pax_global_header00006660000000000000000000000064130007462370014514gustar00rootroot0000000000000052 comment=0e6b46db4ddc1477ceede90002c1c96ca9f497bb motion-release-4.0/000077500000000000000000000000001300074623700143025ustar00rootroot00000000000000motion-release-4.0/.gitignore000066400000000000000000000004151300074623700162720ustar00rootroot00000000000000*.log .svn/ *.o autom4te.cache aclocal.m4 .depend motion config.h config.status config.h.in Makefile configure motion-dist.conf motion.init-Debian motion.init-FreeBSD.sh motion.service motion.spec camera1-dist.conf camera2-dist.conf camera3-dist.conf camera4-dist.conf motion-release-4.0/.travis.yml000066400000000000000000000014261300074623700164160ustar00rootroot00000000000000before_script: - autoreconf -fiv - avplay -version script: ./configure && make matrix: include: - os: linux language: c compiler: gcc addons: apt: packages: - libavformat-dev - libavcodec-dev - libav-tools - libavutil-dev - libswscale-dev - ffmpeg - libjpeg8-dev - libv4l-dev - libzip-dev - os: linux language: c compiler: gcc sudo: rquired dist: trusty addons: apt: sources: - sourceline: 'ppa:mc3man/trusty-media' packages: - libavformat-dev - libavcodec-dev - libav-tools - libavutil-dev - libswscale-dev - ffmpeg - libjpeg8-dev - libv4l-dev - libzip-dev motion-release-4.0/CHANGELOG000066400000000000000000004000761300074623700155230ustar00rootroot00000000000000Summary of Changes Version 4.0 Changes Below * PI Cam Support * Web Interface fixes for preview, IPV6, HTML * Allow for resize of mask file to match image * FFMPEG Revisions (3.0, reporting of messages, FPS, timelapse) * Fix image corruptions across threads * Documentation updates to guide, manual, reference images. * Add camera_name, camera_id, camera_dir options * Configuration fixes (Pi3, JPeg Turbo, SDL, Fedora, BSD, systemd) * travis build testing * remove unused files(pwc-ioctl****.h, README.Axis) * ffmpeg_variable_bitrate to values of 0-100 and ffmpeg fixes * Better messages for pkg-config * Fix timelapse crash when selecting mpeg4 Version 3.4.1 Changes Below * Added suggestion for including pkgconf as part of build requirements * Swap sequence of user ffmpeg path and PKG_CONFIG_PATH * Allow : in non standard locations of netcam_url path. * Eliminated the use of coded_frame (Was a redundant assignment) * Revise HEVC to use H264 for older ffmpeg * Add HEVC codec option for newer ffmpeg (momo-i) * Makefile changes to align with Debian package and fixes from previous changes * Revise description for netcam_url in configuration file. * Change default for ffmpeg in configure.ac (Issue 795002,82) To compile without ffmpeg now requires --without-ffmpeg option * Change prefix to use sysconfdir instead. Install threadX.conf files to threadx-dist.conf * Revise configure.ac to use pkg-config for ffmpeg(Rex Feany) * Revise PIX_FMT to be able to use older ffmpeg versions. * Revise PIX_FMT for newer ffmpeg(Rex Feany) * Add patch for ports on proxy netcams from GVautier (issue 144906) * Add patch from JonGuess(issue 151452) for treatment of partial netcam images. * Consolidate the Sqlite3 options into the code standard. * Draft revised motion_guide.html with new options. * Revise manual to have current options. * Tab/Space cleanup and validation security issue addressed. (issue 071831) * Add preview to webcontrol page(issue 172526) * Additional container fixes(requests/bugs 201900,110304,001656,050731) * Remove depreciated deinterlace option * Remove avformat_network_init call. Call was not needed and was leaking memory * Add extra container options for ffmpeg videos * Remove extra bytes before SOI marker on netcam images. * Updated handling of lastsnap to permit subdirectory specification. * Handle MJPEG streams from some Logitech webcams which send AVI1 instead of JFIF. * Fix missing header for some builds(Jim Dodgen) * Sqlite3 revisions (Jim Dodgen) * Add new config options to the motion-dist.conf * Allow text format specifiers to take a width like printf would. (David Fries) * Allow text format specifiers to take a width like printf would. (David Fries) * Add power_line_frequency configuration item to improve image quality. (David Fries) * Fix webhttpd race condition crash with SIGHUP, add it to running thread counter (David Fries) * Fix the GOP size for the created videos * Fix netcam ftp functionality * Fix Typo on the log level * Fixed the network ftp image option. * Lock thread on ffmpeg/libav init and open_codec * Revise version number generated by script to indicate Unofficial-Git-'hash' * Revised Changelog description for version 3.4 * Merge OpenBSD fixes from sthen pull request * Revised picture.c to allow for modulo 8 pictures and adjusted netcam modules accordingly as well.(closes 135313) * Changes to ffmpeg.h/ffmpeg.c to allow for compiling without ffmpeg/libav or with older versions. * Merge mymalloc, free and casting changes from Alfred Klomp * Merge bug fix for sizeof from Alfred Klomp * Rewrote timelapse so that it works(closes 180501) * Merge tosiara changes for version number * Add copyright file for features added since 3.2.12 * Cleanup ffmpeg.c and plug memory leak * Add support for latest version of ffmpeg/libav * Tidy up packaging changes * Add packaging fixes (infinity0) * Revise version.sh to put out the git commit. * Rollback revision to allow for a formal pull request. * Reimplement changes not to be included in pull request from tosiara commit 9ebee031 * Implement requirement of modulo 16 to avoid seg fault when opening stream * Add debian build files from trusty(14.04) * Revise default values for motion.conf * Revise CHANGELOG to conform with debian format * Revised debian packaging files * Implement new sequence for this file (CHANGELOG) newest to oldest. Summary of Changes that were proposed to maintainer for a version 3.4 Features * Insert Blanking frames http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x12x16x132522 (Dag Erlandsson) * IPV6 for http-control and webcam stream not netcam yet http://www.lavrsen.dk/twiki/bin/view/Motion/IPv6 (Jeroen Massar & Angel Carpintero) * Experimental approach for MJPEG streams (Motion JPEG) for network cameras http://www.lavrsen.dk/twiki/bin/view/Motion/LinksysWVC200SupportPatch ( ... ) * Add draw a RED box around the movement as default (Joerg Weber) * Add write/read nonblock functions in webhttpd( timeout on read/write). (Angel Carpintero) * More changes in option names from http://www.lavrsen.dk/twiki/bin/view/Motion/IntuitiveOptionNamesDiscussion (Angel Carpintero) * motion_locate new parameter names : red , center , redcross instead of only on, off. (Angel Carpintero) * External pipe to allow external video encoders http://www.lavrsen.dk/twiki/bin/view/Motion/DarkwindHackeronMotionPatching (Bill Payne, Angel Carpintero) * Split locate_motion into separate 'mode' and 'style' option to allow all possible combinations. (Joerg Weber) * Implement 'gapless' event mode to allow for recording of movies without 'holes'. (Joerg Weber) * Limit detection rate to 3fps at framerates above 5fps, to reduce CPU load. (Joerg Weber) * Fix warning for syslog() , Added support for some new bayer palettes introduced in kernel 2.6.27. http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2008x10x15x130110 Increased buffer in ffmpeg to allow encoding at 1600x1200 (Angel Carpintero) * Sqlite3 support http://www.lavrsen.dk/twiki/bin/view/Motion/SQLite3Patch (Giacomo Graziosi) * New RPM SPEC file and startup scripts compliant with Fedora 10 guidelines and above (Steven Moix) * Increase write buffer size for writing image files and ffmpegs http://www.lavrsen.dk/foswiki/bin/view/Motion/MotionWriteBuffersPatch (Craig West) * Fixed mask overlay in setup mode is now green instead of white (Joerg Weber) * Add new config option 'ipv6_enabled' to enable/disable IPV6 (Angel Carpintero) * Remove VIDIOC_S_JPEGCOMP support is deprecated. (Angel Carpintero) * Use static memory allocation in ffmpeg_deinterlace() (Peter Holik) http://www.lavrsen.dk/foswiki/bin/view/Motion/FfmpegDeinterlaceStatic * Atom optimizacion in configure.in (Peter Holik) http://www.lavrsen.dk/foswiki/bin/view/Motion/AtomOptimizations * Allow to change Standard method ( PAL / NECAM / SECAM ) (Angel Carpintero) * Add authentication methods 'Basic Authentication' and 'Digest Authentication' to the "Live Stream Server". (Michael Finsterbusch) http://www.lavrsen.dk/foswiki/bin/view/Motion/MotionStreamAuthPatch * Implemented new logging system http://www.lavrsen.dk/foswiki/bin/view/Motion/MotionLog (Angel Carpintero) * Added a macro MOTION_LOG , no need to add __FUNCTION__ anymore. (Angel Carpintero) * Added EXIF feature for jpeg images , http://www.lavrsen.dk/foswiki/bin/view/Motion/ExifTaggingPatch (Wim Lewis) * Improve detection of av_register_protocol() for ffmpeg (Angel Carpintero). * Added support for libjpeg-turbo http://www.lavrsen.dk/foswiki/bin/view/Motion/FeatureRequest2010x04x14x082244 (Angel Carpintero) * Added new log type COR , to filter messages from CORE. (Angel Carpintero) * Added a new starting option -m to disable motion detection. (Angel Carpintero) * Allow image dimension not 4:3 changing a check of modulo 16 by modulo 8. (Jeroen Massar) * Added codec Ogg/Theora as new output format for regular movies. http://www.lavrsen.dk/foswiki/bin/view/Motion/OggTimelapse (Michael Luich) * Added support for ffmpeg 0.11 new API. * Added RSTP support for netcam ( merge https://github.com/hyperbolic2346/motion ) * Merge tosiara/rtsp branch (commit 46cfcf31d, 2014/05/21) (Mr-Dave) * 3fps bugfix from SVN rev559 (tosiara, Joerg Weber) * Buffer overflow vulnerabilities (hyperbolic2346) * Redundand -- boundary prefix (torao) * Removed compiler warnings: (Mr-Dave) logger.c,jpegutils.c,netcam_ftp.c,track.c, picture.c,webhttpd.c,stream.c,ffmpeg.c * Bug fix as part of warnings in webhttpd.c fixed(Mr-Dave) * Removed compiler warning regarding ffmpeg being newer than 0.4 version(Mr-Dave) * New configure script and identification of ffmpeg version and additional libs. (Mr-Dave) * Resolve additional compiler warnings in ffmpeg (Mr-Dave) * Revised INSTALL with samples(Mr-Dave) * Revisions for RTSP and code standard.(Mr-Dave) * Plugged most memory leaks in RTSP. (Mr-Dave) * Undo changes caused by code editor and other unnecessary changes (Mr-Dave) * Moved call to netcam_shutdown_rtsp into netcam_cleanup where it belongs. (Mr-Dave) * Regession fix for memory leaks and reconnection (Mr-Dave) * Eliminated requirement to manually build FFMPEG for RTSP support. (Mr-Dave) * Revised RTSP to support version 53 of libavformat (Mr-Dave) * Revised FFMPEG.c to eliminate warnings and functions no longer supported by Libav/FFMPEG(Mr-Dave) * Revised INSTALL to have sample PI configure option.(Mr-Dave) * Revised configure.ac to generate compiler flag AVFMT_V53.(Mr-Dave) * Revised INSTALL to indicate standard APT packages for RTSP (Mr-Dave) * Revised configure.ac to recognize libavformat version 54 RTSP (Mr-Dave) * Clean up the messaging for RTSP. * Additional validations for RTSP connection and corrected free sequences * Removed seg fault on failure to open first image, comments, isolation of RTSP * Add AC_GNU_SOURCE macro to check for GNU C Library, fix compile when no FFMpeg. * Implement inits of AV functions from bcl fork * Add gray image upon disconnection * Added tcp/udp transport config option from hyperbolic2346(commit 423ef7bb3) * Revised comments to be in line with application standard. * Restructure rtsp to handle rescaling and non YUV420 format, rotate, MJPEG input format Bugfixes * Avoid segfault detecting strerror_r() version GNU or SUSv3. (Angel Carpintero) * Fix Segfault on reload or quit for vloopback (maybe other v4l1 devices too) (Peter Holik) http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2009x06x17x090603 * Allow compile with NetBSD and make LP64 compliant video_freebsd.c (Andreas Wrede) * Avoid compile vloopback in BSD (Angel Carpintero) * V4L2 fourcc GRBG not supported, updated default value for v4l2_palette 17. (Isaac Richter) http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2009x10x29x222753 * Exit when image dimension are not modulo 16. (Angel Carpintero) * Avoid logs flooding using some options of netcam_keepalive and try to discard images with weird header Content-Lenght 0. (Angel Carpintero) * Only use post capture when we setup to record videos with external pipe or ffmpeg. (Angel Carpintero) * Fix introduced bug for Content-Lenght = 0 in svn r476. (Angel Carpintero) * Avoid segfault when motion cannot create a logfile. (Angel Carpintero) * No mysql_close http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2010x11x22x033859 * No PQfinish() (Angel Carpintero) * Input for webcams has to be set to -1 http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2011x01x21x162309 * Added a conditional check for avformat_alloc_context , av_avformat_alloc_context to fix http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2011x10x05x071936 (Angel Carpintero) * Fix issue with JPEG , adding dinfo.do_fancy_upsampling = FALSE; http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2011x08x20x194659 * Made micro-lightswitch optional (see lightwitch option in motion.conf) * Fixed help text for options event_gap and area_detect * Fixed motion.conf-dist , adding text according with config options. * Fixed a bug in post_capture. It was missed under certain conditions. * Fixed configure for SDL. * Replace malloc() by calloc(). (Jeroen Massar) * Free file descriptor buffers on exit. * Avoid segfault when text_left or text_right uses non supported chars. * Fixed leak in vloopback. * Fixed a build of motion for some kernel version with not good videodev.h * Netcam Modulo 8 3.2.12 Summary of Changes Bugfixes * Fixed name space clash with libjpeg8 (Kenneth Lavrsen) http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2010x01x22x084753 * Fixed FFV1 codec encode with ffmpeg (Angel Carpintero) http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2010x04x13x032553 * Fix conv_uyvyto420p segfault ( William M Brack ) * Enhancing the palette selection ( William M Brack ) * Fix zombies on OpenBSD. (Mark Feenstra) http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2010x04x28x054348 3.2.11.1 Summary of Changes Bugfixes * Fix Segfault on reload or quit for vloopback (maybe other v4l1 devices too) (Peter Holik) http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2009x06x17x090603 * Fix fd leaks in external pipe. (Angel Carpintero) * Avoid possible stack smashing in v4l_open_vidpipe(). (Angel Carpintero) * Allow compile with OpenSuse ffmpeg package (15594svn-20081010) http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2008x10x25x070400 (Angel Carpintero) * Fix warning for syslog(). (Angel Carpintero) * Better detection of ffmpeg http://www.lavrsen.dk/foswiki/pub/Motion/ReleaseNoteMotion3x2x11/ffmpeg-detection.diff.gz (Angel Carpintero) * Fix warning for __USE_GNU redefined (Peter Holik) http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2009x06x17x122137 (Peter Holik) * Allow compile with NetBSD and make LP64 compliant video_freebsd.c (Andreas Wrede) * Fix segfault for new libjpeg v7. (Angel Carpintero) 3.2.11 Summary of Changes Features * Added support for ffmpeg-2008-04-09 port version of FreeBSD. * Remove mjpegtools dependencies and integrate only needed functions from library. (Angel Carpintero) * Allow change/setup framerate in FreeBSD using pwcbsd. (Angel Carpintero) * Get rid of ffmpeg-config in configure.in for debian. (Angel Carpintero) Bugfixes * Fix Problem Encoding 1280x1024 resolution videos http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2008x06x11x183727 (Angel Carpintero) * Add a new parameter netcam_tolerant_check, to be less strict with some buggy network cameras firmwares. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x06x19x123218 (Angel Carpintero) * Fix round robin in BSD switching to METEOR_CAP_SINGLE. (Angel Carpintero) * Fix rotate for v4l2 devices using JPEG / MJPEG palettes. (Angel Carpintero) * Fix v4l2_palette http://www.lavrsen.dk/twiki/bin/view/Motion/UvcvideoMjpegPatch (Gerrit Hannaert) * Fix warning for x86_64 in conf.c using pointers LP64 compliant ( Angel Carpintero ). * Fix Segfault on reload or quit for vloopback (maybe other v4l1 devices too) ( Peter Holik ) http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2009x06x17x090603 3.2.10.1 Summary of Changes Bugfixes * Fix a security issue in web control interface http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=484572 (Angel Carpintero) 3.2.10 Summary of Changes Features * Added the pre_capture buffer redesign to throttle load and enhance pre_capture feature. http://www.lavrsen.dk/twiki/bin/view/Motion/PreCaptureRedesign (Dag Erlandsson). * Added preview center feature. http://www.lavrsen.dk/twiki/bin/view/Motion/PreviewCenter (Dag Erlandsson). * Removed low_cpu feature, as it is not really compatible with pre_capture and a lot of other features rely on the pre_capture buffer behind the scenes. (Joerg Weber) * Removed night_compensate feature. This functionality is covered by noise_tune. (Joerg Weber) * Implemented a new reference frame algorithm to improve object recognition and location. (Joerg Weber) * Improved smartmask feature: real moving objects don't trigger the mask anymore. (Joerg Weber) * Added area_detect feature. New config options: area_detect, on_area_detected. (Joerg Weber) * Added help in http control http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x11x19x181541 (Angel Carpintero) * Added Choose V4L2 palette http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x11x19x032318 (Angel Carpintero) * Improved http control ( 'back' link, select box, show current values when are going to be changed ). (Angel Carpintero) * Improved thread handling, single threads can be stopped/restarted (Dag Erlandsson) * Watchdog, restart hang threads (Dag Erlandsson) * Added ON_CAMERA_LOST event (Dag Erlandsson) * Motion start if a camera isn't there at start, retries to connect if lost (Dag Erlandsson) * Netcam Keepalive and HTTP/1.1 http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x01x22x231542 (Simon Walls) * Added mov , Quicktime file format (Andrew Hamilton). * Added to configure.in --with-pwcbsd to allow compile motion in freebsd with webcam support instead of bktr (Angel Carpintero) Bugfixes * Fixed a problem with locate and fixed mask overlay (Dag Erlandsson). * Preview pictures get the timestamp of moment they were captured (Dag Erlandsson). * Fixed http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x10x23x093651 (Angel Carpintero) * Fix process_id_file when is passed from command line (Angel Carpintero) * Fix http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x10x27x150419 (Angel Carpintero) * Fix http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x11x25x102808 (Angel Carpintero) * Avoid random errors , initialising some structs for V4L1 http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x11x26x010755 (Jason Sharpee & Angel Carpintero) * Fix motion segfault because ffmpeg API change http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2007x12x29x175530 (Angel Carpintero) * Little fix in ffmpeg.c comparing version of LIBAVFORMAT_BUILD, since ffmpeg svn -r4486 LIBAVFORMAT_BUILD and LIBAVCODEC_BUILD uses LIBAVFORMAT_VERSION_INT ((49<<16)+(0<<8)+0) and LIBAVCODEC_VERSION_INT ((49<<16)+(0<<8)+0) (Angel Carpintero) * Fix choose v4l2 palette , http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x01x21x043812 (Onakra) * Get current directory to allow write motion.conf properly http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x02x25x013419 (John Bray) * Fix broken PostgreSQL detection for custom location, http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x02x25x025134 ( Angel Carpintero ) * Fixed stepper when is used track_auto on ( Angel Carpintero ). * Better debug in netcam for "Error reading image header" http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x02x27x092849 (Simon Walls) 3.2.9 Formal Release - Summary of Changes Features * Removed debian ( to avoid conflicts with debian package) and FreeBSD ( no needed to deploy BSD port here ) directories. ( Angel Carpintero ) * Added --chuid motion to debian init.d script. ( Angel Carpintero ) * Added Flash video format (FLV) to ffmpeg. http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x07x19x131921 (Timo Taskinen). * Added FFV1 ( FF video codec 1 ) codec , Lossless encoding http://www.lavrsen.dk/twiki/bin/view/Motion/LosslessEncoding (Andrew Hamilton). Bugfixes * Fix segfault in webhttpd.c on motion restart (Angel Carpintero) * Fix segfault in debian http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x09x24x175945 (Angel Carpintero) * Fix http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=391055 , change motion man page , -d requires level. (Angel Carpintero) * Handle mjpeg decoding and fix colour issue adding mjpegtools dependency http://www.lavrsen.dk/twiki/bin/view/Motion/MjpegColorIssue http://www.lavrsen.dk/twiki/bin/view/Motion/MjpegToYUV420pPatch (Marius Rieder, Angel Carpintero). * Add debug level > 5 to get logs from v4l2_select_input, v4l2_set_control and v4l2_set_input. (Angel Carpintero) 3.2.8 Formal Release - Summary of Changes Features * Added connection status for all devices available from http web interface. (Angel Carpintero). http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2006x11x09x050638 * Improved deb packaging, install the init.d script. (Angel Carpintero). * Added swf codec to video creation (Bowser Pete, Andy Brown). http://www.lavrsen.dk/twiki/bin/view/Motion/FFmpegSWFcreation * Added V4L2 support (Krzysztof Blaszkowski, Angel Carpintero). http://www.lavrsen.dk/twiki/bin/view/Motion/VideoForLinuxTwoDiscussion, * Added support for V4L2_PIX_FMT_SBGGR8 ( bayer ), V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_MJPEG and V4L2_PIX_FMT_UYVY (Angel Carpintero). * ucvideo track pan/tilt support ( Michal Licko ,Dirk Wesenberg and Angel Carpintero ) http://www.lavrsen.dk/twiki/bin/view/Motion/LinuxUvcTrackingPatch * Added a FreeBSD directory to allow people from BSD to get a daily version and create a port. (Angel Carpintero). * Removed mysql dependency from debian package and added a note to setup motion to run as daemon to create the pid file. (Angel Carpintero). * Changed the way configure search mysql headers and libs, added 3 parameters to configure --without-mysql to disable support, --with-mysql-include directory of mysql.h and --with-mysql-lib directory of libmysqlclient.a or libmysqlclient.so (Angel Carpintero). * Added MYSQL_OPT_RECONNECT flag for mysql connection (MYSQL 5.x only) and changed default value for mysql_host (Angel Carpintero). Bugfixes * Removed a duplicate call to jpeg_destroy_decompress already is called from netcam_image_conv (Krzysztof Blaszkowski). * Fix http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x10x10x081903, reconnect to mysql if connection dropped (Angel Carpintero). * Fix memory management in ffmpeg.c (Rafis Khayrullin). http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x12x19x062432 * Fix of ffmpeg_avcodec_log code (Alain Guidez). http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x03x25x074612 * Fix a segfault adding correct size to be used for bayer2rgb24() (Damian Wrobel) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x03x30x175913 * Fix an error in FreeBSD, the use of capture even fields depends on height value. (Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x12x03x073610 * Fixed autodetection for VIA cpu, no needed to use --without-optimizecpu. Added many others (Angel Carpintero) * Fix, don't remove pid file when motion reload config file( HUP signal ) (Angel Carpintero). * Fix compilation broken by uvc track type. (Angel Carpintero). * Fixed the thread number assignment which could goof up if netcams started very quickly before all thread were created at startup. (Kenneth Lavrsen) * Fix RoundRobin v4l2 buffers in driver when switching input, http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x07x07x182605 (Dag Erlandsson and Angel Carpintero). * Check EIO for VIDIOC_DQBUF to workaround saa7134 problem. (Dag Erlandsson and Angel Carpintero). * Change bayer2rgb24() to fix a problem with sn9c102 driver http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x06x05x012249 (Jared D and Angel Carpintero). 3.2.7 Formal Release - Summary of Changes Features * Removed the minimum_gap feature which was utterly useless (Kenneth Lavrsen) * Added new feature: minimum_frame_time which enables capturing at a lower rate than 2 frames per second (Kenneth Lavrsen and Angel Carpintero) * Made the creation of reference frame and the decay mechanism depending on how much motion was detected relative to threshold setting (Joerg Weber) http://www.lavrsen.dk/twiki/bin/view/Motion/ReferenceFramePatch * Added process_id_file feature (Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2006x06x06x123003 Bugfixes * Fixed problem related to fetching images from Network camera and error handling when it fails. Motion would end in infinite loops (Bill Brack). http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x03x10x000151 * Improved reporting of thread numbers during startup in setup mode. (Peter Smith and Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/SlightlyImprovedThreadCreationLogging * Ffmpeg code mutex locking fix (Peter Smith) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x04x07x164654 * Ffmpeg avicodec logging improved (Peter Smith and Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/FfmpegAvicodecLogging * Improved upon a few ambiguous log messages which may be emitted by the Event handling code with regards to Ffmpeg (Peter Smith) http://www.lavrsen.dk/twiki/bin/view/Motion/LoggingEventFix * Implemented a fix for the rare problem where some experienced that the move file names would only consist of the extension .mpg or .avi with no name in front. The root cause was the use of sprintf for appending to strings. (Mike Kenney and Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2005x09x05x133031 http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2006x06x19x174238 * Altered the risky use of sprintf to snprintf in all places related to use with config strings that can become very long (Kenneth Lavrsen) * Removed annoying debug messages (v4l_set_input really needed ?) in the FreeBSD version (Angel Carpintero) * Fixed the check for ffmpeg version. In rev 5503 of ffmpeg the FFMPEG_VERSION_INT was removed from libavcodec/avcodec.h. Instead we now use the equivalent LIBAVFORMAT_BUILD >= 4616 which is the 0.4.9pre1 version of ffmpeg. (Kenneth Lavrsen) * Fixed segfault when netcam_url has no service ( http , ftp ) (Angel Carpintero) * Fixed interlace issue with METEOR_GEO_EVEN_ONLY in FreeBSD (Angel Carpintero) * Fixed possible syntax error in configure related to MySQL (Angel Carpintero) * Avoid open file descriptor when connecting to network cameras fails (Peter Holik) http://www.lavrsen.dk/twiki/bin/view/Motion/AvoidOpenfiledescriptors * Fixed http pause feature so that pausing thread 0 now pauses all threads. (GunnarSkjold) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x07x10x111239 * Put a new global mutex around avcodec_close to avoid problems with not thread safe functions in ffmpeg (Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x04x07x164654 * On FreeBSD configure defines a redundant freebsd for motion. Fixed by replacing -D__freebsd_ by BSD macro included in sys/param.h for BSD platforms. (JukkaUkkonen and Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x07x08x070417 * For BSD platforms changed to using native pthreads as default and adding linuxthreads as a optional parameter from configure. (Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x07x08x071646 * Smartmask overlay feature did not set intensity correctly. (Kenneth Lavrsen) 3.2.6 Formal Bugfix Release. * Fixed bug where variables time_last_frame and time_current_frame had been extended to also be used for snapshot feature but declaration was hidden between #ifdef HAVE_FFMPEG. (Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x03x09x012244 * Fixed a bug that only allowed remote control of max 9 cameras. Now Motion can present up to 99 cameras in its http remote control interface (Angel Carpintero based on idea by Chuck Sheehan) http://www.lavrsen.dk/twiki/bin/view/Motion/WebHttpManyThreads * text_changes now shows a '-' when motion detection is paused instead of just showing 0 (Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2006x03x16x095713 3.2.5.1 Doc and man page correction * ffmpeg_filename has changed name to movie_filename to prepare for alternative movie encoding to the current ffmpeg based implementation and ffmpeg_filename will then be a bad name (Kenneth Lavrsen) * Man page corrected. 3.2.5 Formal Release. Summary of changes * 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 separating 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. * Changed CHANGELOG to same bullet format as used by TWiki to make it easier to write release notes (Kenneth Lavrsen) * Changed CREDITS to same bullet format as CHANGELOG (Kenneth Lavrsen) * Fixed sql_mask not initialised correctly (Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x01x09x175603 * Fixed the management of strings from http remote control , setting to NULL when they are set to "blank" and fixes a problem with despeckle , that didn't allow to remove labeling action from http remote control. (Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/FixStringsAndDisableLabeling * Fix many typos in comments ( i ran aspell against the code ). Also there's a fix to free cnt->eventtime_tm when motion exits. (Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/FixTypoInComments * Fix the problem that happens in FreeBSD and Debian Sarge because version of ffmpeg is LIBAVFORMAT_BUILD < 4629. ( Pete Shipley and Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x01x12x120335 * Updated motion.spec. Changing D_FORTIFY_SOURCE=2 by D_FORTIFY_SOURCE=1 to fix problem related to building with ffmpeg. (Angel Carpintero) * Implemented fix for missed snapshots with slow network cameras (Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x02x07x162149 * Added some constants in video.c function v4l_picture_controls() which can help people hack an optimal set of values for controlling auto brightness for their particular camera. For now I am do not want to add all of these to the already too large number of motion config options. Maybe based on feedback we can permanently change the constants and add an additional auto brightness option. Or maybe a combined option that sets more constant based on an algorithm. (Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x02x07x212816 * Fixed a syntax error in picture.c get_pgm() which caused the program to segfault when a mask file size did not match the picture size. Now the program correctly gives an error message and continues without the mask. (Kenneth Lavrsen). http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x10x08x150720 * Added Tilt support to stepper track. ( Angel Carpintero ). * CPU VIA Ezra C3 autodetection support added. (James Van Vleet) http://www.lavrsen.dk/twiki/bin/view/Motion/VIAEzraC3Patch * Fixed mysql configure auto-detection for x64 systems. ( Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2006x03x02x152208 * Added the ffmpeg_deinterlace feature (Andrew Hamilton) http://www.lavrsen.dk/twiki/bin/view/Motion/MotionffmpegDeinterlace 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 dimensions 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 comments 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 algorithms 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 unused 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 unsigned 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 segfault ( 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 dimensions 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 algorithms 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 comments 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 dimensions 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 algorithm 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 algorithm * 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 segfaults) * 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 printf 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 presence 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 occurred 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 algorithm (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 algorithm 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 cleanups (Bill Brack). * Implemented a much easier to use motion_log function which replaces the calls to printf 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: http://www.lavrsen.dk/twiki/bin/view/Motion/ErrorLoggingEnhancementPatch * Populate the motion_log to the whole motion source code. * Fixed FreeBSD compilation. * Added the possibility 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 * 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 Valgrind 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 functions 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 Stability Patch by Christopher Price). * Improvement in the noise-tune algorithm (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 descriptor 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 check 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) http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch * 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. * 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) http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch * 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 redundant code removed. * Consolidated 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. * 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 favourite 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 algorithm. 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 simple 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 algorithm. * 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 * Implemented 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 dimensions/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 algorithm (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 segfault 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 segfault 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 algorithm. It ensures that only the largest area of movement is included in the detection and it prevents noise and wind from making 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: * Forces timelapse to mpeg1 for the time being. * Removes FILE* f declaration in ffmpeg.h since we no longer need it. * 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. * 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 Carpintero 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 completely. 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 optimisation 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 optimisation: 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 exception) 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. Cleaned 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 colour. 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 algorithms. 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 adjust_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. Included 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_compensate 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 separately 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 motion-release-4.0/CODE_STANDARD000066400000000000000000000155701300074623700162270ustar00rootroot00000000000000Formatting rules for Motion code. Version 2.0 - 15 Jul 2008 Note: To understand them you must view this document with spaces and tabs visible. -------------------- RULE 1 Code is generally indented using 4 spaces Example /* 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(EMERG, TYPE_ALL, "%s: Could not allocate %llu bytes of memory!", __FUNCTION__, (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. The alignment is done using spaces making the code on the following lines appear in a natural way below the corresponding code above. Use common sense to enhance readability. Example /* 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(EMERG, TYPE_ALL,"Could not allocate %llu bytes of memory!", __FUNCTION__, (unsigned long long) nbytes); exit(1); } return dummy; } 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; 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" -------------------- RULE 3 Never use TABS to align anything. A tab may be 4 positions in one editor and 8 in another. A space is always a space. -------------------- 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. 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"); } GOOD EXAMPLE switch (expr) { case ABC: case DEF: statement; break; case UVW: statement; break; default: /* default case */ statement; } BAD EXAMPLE switch (expr) { case ABC: case DEF: statement; break; case UVW: statement; break; default: /* default case */ statement; } -------------------- 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. GOOD EXAMPLES int function_name(int *par1, int par2, int par3) { if (var1==2 || var2==3) { BAD 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 developers 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 the developers 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. motion-release-4.0/COPYING000066400000000000000000000430761300074623700153470ustar00rootroot00000000000000 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. motion-release-4.0/CREDITS000066400000000000000000002431341300074623700153310ustar00rootroot00000000000000Thanks 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 completely. 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. Krzysztof Blaszkowski * Removed a duplicate call to jpeg_destroy_decompress already is called from netcam_image_conv. * Added V4L2 support http://www.lavrsen.dk/twiki/bin/view/Motion/VideoForLinuxTwoDiscussion, (Krzysztof Blaszkowski, Angel Carpintero). 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 cleanups. * Implemented a much easier to use motion_log function which replaces the calls to printf 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 Valgrind 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. * Fixed problem related to fetching images from Network camera and error handling when it fails. Motion would end in infinite loops. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x03x10x000151 * Fix conv_uyvyto420p segfault. * Enhancing the palette selection. John Bray * Get current directory to allow write motion.conf properly http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x02x25x013419 Andy Brown * Add swf codec to video creation (on behalf of Bowser Pete). 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. 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 including: * Populate the motion_log to the whole motion source code. * Fixed FreeBSD compilation. * Added the possibility 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'. 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 unsigned 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 segfault ( 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 * Fixed sql_mask not initialised correctly http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x01x09x175603 * Fixed the management of strings from http remote control , setting to NULL when they are set to "blank" and fixes a problem with despeckle , that didn't allow to remove labeling action from http remote control. http://www.lavrsen.dk/twiki/bin/view/Motion/FixStringsAndDisableLabeling * Fix many typos in comments ( i ran aspell against the code ). Also there's a fix to free cnt->eventtime_tm when motion exits. http://www.lavrsen.dk/twiki/bin/view/Motion/FixTypoInComments * Fix the problem that happens in FreeBSD and Debian Sarge because version of ffmpeg is LIBAVFORMAT_BUILD < 4629. ( Pete Shipley and Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x01x12x120335 * Updated motion.spec. Changing D_FORTIFY_SOURCE=2 by D_FORTIFY_SOURCE=1 to fix problem related to building with ffmpeg. (Angel Carpintero) * Added Tilt support to stepper track. * Fixed mysql configure auto-detection for x64 systems. http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2006x03x02x152208 * Fixed a bug that only allowed remote control of max 9 cameras. Now Motion can present up to 99 cameras in its http remote control interface (Angel Carpintero based on idea by Chuck Sheehan) http://www.lavrsen.dk/twiki/bin/view/Motion/WebHttpManyThreads * Removed annoying debug messages (v4l_set_input really needed ?) in the FreeBSD version. * Added new feature: minimum_frame_time which enables capturing at a lower rate than 2 frames per second (Kenneth Lavrsen and Angel Carpintero) * Fix segfault when netcam_url has no service ( http , ftp ) * Fixed interlace issue with METEOR_GEO_EVEN_ONLY in FreeBSD * Fixed possible syntax error in configure related to MySQL * Put a new global mutex around avcodec_close to avoid problems with not thread safe functions in ffmpeg * Put a new global mutex around avcodec_close to avoid problems with not thread safe functions in ffmpeg. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x04x07x164654 * On FreeBSD configure defines a redundant freebsd for motion. Fixed by replacing -D__freebsd_ by BSD macro included in sys/param.h for BSD platforms. (JukkaUkkonen and Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x07x08x070417 * For BSD platforms changed to using native pthreads as default and adding linuxthreads as a optional parameter from configure. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x07x08x071646 * Added process_id_file feature http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2006x06x06x123003 * Add connection status for all devices available from http web interface. http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2006x11x09x050638 * Improved deb packaging , install the init.d script. * Reconnect to mysql if connection dropped. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x10x10x081903 * Track pan/tilt support for uvcvideo ( Michal Licko ,Dirk Wesenberg and Angel Carpintero ) http://www.lavrsen.dk/twiki/bin/view/Motion/LinuxUvcTrackingPatch * Added V4L2 support http://www.lavrsen.dk/twiki/bin/view/Motion/VideoForLinuxTwoDiscussion, (Krzysztof Blaszkowski, Angel Carpintero). * Added support for V4L2_PIX_FMT_SBGGR8 ( bayer ), V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_MJPEG and V4L2_PIX_FMT_UYVY. * Added a FreeBSD directory to allow people from BSD to get a daily version and create a port. * Removed mysql dependency from debian package and added a note to setup motion to run as daemon to create the pid file. * Changed the way configure search mysql headers and libs, added 3 parameters to configure --without-mysql to disable support, --with-mysql-include directory of mysql.h and --with-mysql-lib directory of libmysqlclient.a or libmysqlclient.so * Fix an error in FreeBSD , the use of capture even fields depends on height value. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x12x03x073610 * Fixed autodetection for VIA cpu , no needed to use --without-optimizecpu. Added many others. * Fix , don't remove pid file when motion reload config file( HUP signal ). * Fix compilation broken by uvc track type. * Fix RoundRobin v4l2 buffers in driver when switching input, http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x07x07x182605 (Dag Erlandsson and Angel Carpintero). * Check EIO for VIDIOC_DQBUF to workaround saa7134 problem. (Dag Erlandsson and Angel Carpintero). * Change bayer2rgb24() to fix a problem with sn9c102 driver http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x06x05x012249 (Jared D and Angel Carpintero). * Added MYSQL_OPT_RECONNECT flag for mysql connection (MYSQL 5.x only) and changed default value for mysql_host. * Fix http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=391055 , change motion man page , -d requires level. * Handle mjpeg decoding and fix colour issue adding mjpegtools dependency http://www.lavrsen.dk/twiki/bin/view/Motion/MjpegColorIssue http://www.lavrsen.dk/twiki/bin/view/Motion/MjpegToYUV420pPatch (Marius Rieder, Angel Carpintero). * Fix segfault in webhttpd.c on motion restart. * Fix segfault in debian http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x09x24x175945 * Add debug level > 5 to get logs from v4l2_select_input, v4l2_set_control and v4l2_set_input. * Removed debian ( to avoid conflicts with debian package) and FreeBSD ( no needed to deploy BSD port here ) directories. * Improve debian package, create user/group motion and added --chuid motion to init script. * Added help in http control http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x11x19x181541 * Fixed http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x10x23x093651 * Fix process_id_file when is passed from command line * Fix http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x10x27x150419 * Added Choose V4L2 palette http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x11x19x032318 * Improved in http control ( 'back' link, select box, show current values when are going to be changed ). * Fix http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x11x25x102808 * Avoid random errors , initialising some structs for V4L1 http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x11x26x010755 (Jason Sharpee & Angel Carpintero) * Fix motion segfault because ffmpeg API change http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2007x12x29x17553 * Little fix in ffmpeg.c comparing version of LIBAVFORMAT_BUILD, since ffmpeg svn -r4486 LIBAVFORMAT_BUILD and LIBAVCODEC_BUILD uses LIBAVFORMAT_VERSION_INT ((49<<16)+(0<<8)+0) and LIBAVCODEC_VERSION_INT ((49<<16)+(0<<8)+0) * Fix broken PostgreSQL detection for custom location, http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x02x25x025134 * Fixed stepper when is used track_auto on. * Added to configure.in --with-pwcbsd to allow compile motion in freebsd with webcam support instead of bktr. * IPV6 for http-control and webcam stream not netcam yet http://www.lavrsen.dk/twiki/bin/view/Motion/IPv6 (Jeroen Massar & Angel Carpintero) * Fix a security issue in web control interface http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=484572 * Fix Problem Encoding 1280x1024 resolution videos http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2008x06x11x183727 * Add write/read nonblock functions in webhttpd( timeout on read/write). * Add a new parameter netcam_tolerant_check, to be less strict with some buggy network cameras firmwares. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x06x19x123218 * Remove mjpegtools dependencies and integrate only needed functions from library. * Fix rotate for v4l2 devices using JPEG / MJPEG palettes. * External pipe to allow external video encoders http://www.lavrsen.dk/twiki/bin/view/Motion/DarkwindHackeronMotionPatching (Bill Payne, Angel Carpintero) * Allow change/setup framerate in FreeBSD using pwcbsd * Get rid of ffmpeg-config in configure.in for debian. * Fix warning for x86_64 in conf.c using pointers LP64 compliant. * Fix warning for syslog() , Added support for some new bayer palettes introduced in kernel 2.6.27. http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2008x10x15x130110 Increased buffer in ffmpeg to allow encoding at 1600x1200 * Avoid possible stack smashing in v4l_open_vidpipe(). * Allow compile with OpenSuse ffmpeg package (15594svn-20081010) http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2008x10x25x070400 * Avoid segfault detecting strerror_r() version GNU or SUSv3. * Fix fd leaks in external pipe. * Fix segfault for new libjpeg v7. * Allow to change Standard method ( PAL / NECAM / SECAM ). * Exit when image dimension are not modulo 16. * Avoid logs flooding using some options of netcam_keepalive and try to discard images with weird header Content-Lenght 0. * Only use post capture when we setup to record videos with external pipe or ffmpeg. * Fixed FFV1 codec encode with ffmpeg. http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2010x04x13x032553 * No PQfinish(). * Added a conditional check for avformat_alloc_context , av_avformat_alloc_context to fix http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2011x10x05x071936 * Added a new starting option -m to disable motion detection. Jared D * Change bayer2rgb24() to fix a problem with sn9c102 driver http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x06x05x012249 (Jared D and Angel Carpintero). John Edwards * Added the 'pal-nc' norm. Dag Erlandsson * Fix RoundRobin v4l2 buffers in driver when switching input, http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x07x07x182605 (Dag Erlandsson and Angel Carpintero). * Check EIO for VIDIOC_DQBUF to workaround saa7134 problem. (Dag Erlandsson and Angel Carpintero). * Added the pre_capture buffer redesign to throttle load and enhance pre_capture feature. http://www.lavrsen.dk/twiki/bin/view/Motion/PreCaptureRedesign * Fixed a problem with locate and fixed mask overlay * Added preview center feature. http://www.lavrsen.dk/twiki/bin/view/Motion/PreviewCenter * Fixed timestamp for preview pictures. * Insert Blanking frames http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x12x16x132522 Stephen Farrugia * Fixing the division by zero problem. This makes motion a lot more stable. Michael Finsterbusch * Add authentication methods 'Basic Authentication' and 'Digest Authentication' to the "Live Stream Server". http://www.lavrsen.dk/foswiki/bin/view/Motion/MotionStreamAuthPatch Mark Feenstra * Fix zombies on OpenBSD. http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2010x04x28x054348 Miguel Freitas * Came up with the round robing idea. David Fries * Fix webhttpd race condition crash with SIGHUP, add it to running thread counter * Allow text format specifiers to take a width like printf would. * Add power_line_frequency configuration item to improve image quality. Aaron Gage * Pointed me to the vid_mmap/int problem when calling SYNC in video.c Giacomo Graziosi * Sqlite3 support http://www.lavrsen.dk/twiki/bin/view/Motion/SQLite3Patch 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. Alain Guidez * Fix of ffmpeg_avcodec_log code. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x03x25x074612 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. * Added the ffmpeg_deinterlace feature http://www.lavrsen.dk/twiki/bin/view/Motion/MotionffmpegDeinterlace * Added FFV1 ( FF video codec 1 ) codec , Lossless encoding http://www.lavrsen.dk/twiki/bin/view/Motion/LosslessEncoding * Added mov , Quicktime file format (Andrew Hamilton). Gerrit Hannaert * Fix v4l2_palette http://www.lavrsen.dk/twiki/bin/view/Motion/UvcvideoMjpegPatch Peter Holik * Netcam First Header patch. If an error with jpeg decompression occurred 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 separating frames in an mjpeg stream. From mailing list 23 Dec 2005. * Avoid open file descriptor when connecting to network cameras fails http://www.lavrsen.dk/twiki/bin/view/Motion/AvoidOpenfiledescriptors * Fix Segfault on reload or quit for vloopback (maybe other v4l1 devices too) http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2009x06x17x090603 * Fix warning for __USE_GNU redefined http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2009x06x17x122137 * Use static memory allocation in ffmpeg_deinterlace() http://www.lavrsen.dk/foswiki/bin/view/Motion/FfmpegDeinterlaceStatic * Atom optimizacion in configure.in http://www.lavrsen.dk/foswiki/bin/view/Motion/AtomOptimizations 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 algorithm. * 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 unused cnt by Kenneth Lavrsen). http://www.lavrsen.dk/twiki/bin/view/Motion/ThreadNrTlsPatch * Simplified rotation code based on the fact that images must have dimensions that are a multiple of 16. http://www.lavrsen.dk/twiki/bin/view/Motion/RotateSimplificationPatch Mike Kenney * Implemented a fix for the rare problem where some experienced that the move file names would only consist of the extension .mpg or .avi with no name in front. The root cause was the use of sprintf for appending to strings. (Mike Kenney and Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2005x09x05x133031 http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2006x06x19x174238 Rafis Khayrullin * Fix memory management in ffmpeg.c http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x12x19x062432 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 segfault 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 descriptor 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 algorithm. 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 simple 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 presence 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 unused 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 algorithms 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. * Removed the sql_user_text option that was added in snap 2 * 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. * Change the sequence of events connected with creating files. Data is now written to the databases (if used) before an external comments is on (on_xxxx options) allowing the external program to use the new data in the database * 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 dimensions 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 * Implemented fix for missed snapshots with slow network cameras http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x02x07x162149 * Added some constants in video.c function v4l_picture_controls() which can help people hack an optimal set of values for controlling auto brightness for their particular camera. For now I am do not want to add all of these to the already too large number of motion config options. Maybe based on feedback we can permanently change the constants and add an additional auto brightness option. Or maybe a combined option that sets more constant based on an algorithm. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x02x07x212816 * Fixed a syntax error in picture.c get_pgm() which caused the program to segfault when a mask file size did not match the picture size. Now the program correctly gives an error message and continues without the mask. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2005x10x08x150720 * ffmpeg_filename has changed name to movie_filename to prepare for alternative movie encoding to the current ffmpeg based implementation and ffmpeg_filename will then be a bad name. * Fixed bug where variables time_last_frame and time_current_frame had been extended to also be used for snapshot feature but declaration was hidden between #ifdef HAVE_FFMPEG. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x03x09x012244 * text_changes now shows a '-' when motion detection is paused instead of just showing 0 http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2006x03x16x095713 * Improved reporting of thread numbers during startup in setup mode. (Peter Smith and Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/SlightlyImprovedThreadCreationLogging * Implemented a fix for the rare problem where some experienced that the move file names would only consist of the extension .mpg or .avi with no name in front. The root cause was the use of sprintf for appending to strings. (Mike Kenney and Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2005x09x05x133031 http://www.lavrsen.dk/twiki/bin/view/Motion/SupportQuestion2006x06x19x174238 * Altered the risky use of sprintf to snprintf in all places related to use with config strings that can become very long. * Removed the minimum_gap feature which was utterly useless * Added new feature: minimum_frame_time which enables capturing at a lower rate than 2 frames per second (Kenneth Lavrsen and Angel Carpintero) * Fixed the check for ffmpeg version. In rev 5503 of ffmpeg the FFMPEG_VERSION_INT was removed from libavcodec/avcodec.h. Instead we now use the equivalent LIBAVFORMAT_BUILD >= 4616 which is the 0.4.9pre1 version of ffmpeg. * Smartmask overlay feature did not set intensity correctly. * Fixed the thread number assignment which could goof up if netcams started very quickly before all thread were created at startup. * Fixed name space clash with libjpeg8 http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2010x01x22x084753 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 Wim Lewis * Added EXIF feature for jpeg images , http://www.lavrsen.dk/foswiki/bin/view/Motion/ExifTaggingPatch Michal Licko * Track pan/tilt support for uvcvideo ( Michal Licko ,Dirk Wesenberg and Angel Carpintero ) http://www.lavrsen.dk/twiki/bin/view/Motion/LinuxUvcTrackingPatch Michael Luich * Added codec Ogg/Theora as new output format for regular movies. http://www.lavrsen.dk/foswiki/bin/view/Motion/OggTimelapse Bill Maidment * Fixed bug reporting errors when creating symlink to last snap. Philip Marien * Fixed a problem when compiling with --without-v4l configuration. Jeroen Massar * IPV6 for http-control and webcam stream not netcam yet http://www.lavrsen.dk/twiki/bin/view/Motion/IPv6 (Jeroen Massar & Angel Carpintero) 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 Onakra * Fix choose v4l2 palette , http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x01x21x043812 Mikko Paananen * Changed netcam open to use pipes and fixed authentication. Bill Payne, * External pipe to allow external video encoders http://www.lavrsen.dk/twiki/bin/view/Motion/DarkwindHackeronMotionPatching (Angel Carpintero, Bill Payne) Bowser Pete * Add swf codec to video creation. 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 Stability 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) http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch * 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. * More Netcam Stability Fixes (snap11-post1-4) (Christopher Price) http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch * 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 redundant code removed. * Consolidated 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. * More Netcam Stability Fixes (snap12-post1) (Christopher Price) http://www.lavrsen.dk/twiki/bin/view/Motion/NetcamStabilityPatch * 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. 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 Petter Reinholdtsen * Adding the install option to the makefile. Isaac Richte * V4L2 fourcc GRBG not supported, updated default value for v4l2_palette 17. http://www.lavrsen.dk/foswiki/bin/view/Motion/BugReport2009x10x29x222753 Marius Rieder * Handle mjpeg decoding and fix colour issue adding mjpegtools dependency http://www.lavrsen.dk/twiki/bin/view/Motion/MjpegColorIssue http://www.lavrsen.dk/twiki/bin/view/Motion/MjpegToYUV420pPatch (Marius Rieder, Angel Carpintero). 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. Jason Sharpee * Avoid random errors , initialising some structs for V4L1 http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x11x26x010755 (Jason Sharpee & Angel Carpintero) Pete Shipley * Fix the problem that happens in FreeBSD and Debian Sarge because version of ffmpeg is LIBAVFORMAT_BUILD < 4629. ( Angel Carpintero and Pete Shipley) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x01x12x120335 Gunnar Skjold * Fixed http pause feature so that pausing thread 0 now pauses all threads. http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x07x10x111239 Peter Smith * Improved reporting of thread numbers during startup in setup mode. (Peter Smith and Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/SlightlyImprovedThreadCreationLogging * Ffmpeg code mutex locking fix http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x04x07x164654 * Ffmpeg avicodec logging improved (Peter Smith and Kenneth Lavrsen) http://www.lavrsen.dk/twiki/bin/view/Motion/FfmpegAvicodecLogging * Improved upon a few ambiguous log messages which may be emitted by the Event handling code with regards to Ffmpeg (Peter Smith) http://www.lavrsen.dk/twiki/bin/view/Motion/LoggingEventFix 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 Timo Taskinen * Added Flash video format (FLV) to ffmpeg. http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x07x19x131921 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 Jukka Ukkonen * On FreeBSD configure defines a redundant freebsd for motion. Fixed by replacing -D__freebsd_ by BSD macro included in sys/param.h for BSD platforms. (JukkaUkkonen and Angel Carpintero) http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2006x07x08x070417 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 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 extension 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. James Van Vleet * CPU VIA Ezra C3 autodetection support added. http://www.lavrsen.dk/twiki/bin/view/Motion/VIAEzraC3Patch Simon Walls * Netcam Keepalive and HTTP/1.1 http://www.lavrsen.dk/twiki/bin/view/Motion/FeatureRequest2007x01x22x231542 * Better debug in netcam for "Error reading image header" http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2008x02x27x092849 (Simon Walls) 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 algorithm. * 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 algorithm so that locate feature and tracking features includes all labelled areas above threshold. http://www.lavrsen.dk/twiki/bin/view/Motion/ImprovedLabellingPatch * Make the creation of reference frame and the decay mechanism depending on how much motion was detected relative to threshold setting. http://www.lavrsen.dk/twiki/bin/view/Motion/ReferenceFramePatch * Removed low_cpu feature. * Removed night_compensate feature * Implemented a new reference frame algorithm to improve object recognition and location. * Improved smartmask feature: real moving objects don't trigger the mask anymore. * Added area_detect feature. * Add draw a RED box around the movement as default. * Split locate_motion into separate 'mode' and 'style' option to allow all possible combinations. * Gapless_event mode. * Limit detection rate to 3fps at framerates above 5fps, to reduce CPU load. * Fixed mask overlay in setup mode is now green instead of white. Dirk Wesenberg * Track pan/tilt support for uvcvideo ( Michal Licko ,Dirk Wesenberg and Angel Carpintero ) http://www.lavrsen.dk/twiki/bin/view/Motion/LinuxUvcTrackingPatch Tristan Willy * Wrote Axis 2100 support and added the check for ~/.motion/motion.conf Robert Eugene Wood * Inverse pixels for locate box. Andreas Wrede * Allow compile with NetBSD and make LP64 compliant video_freebsd.c Damian Wrobel * Fix a segfault adding correct size to be used for bayer2rgb24(). http://www.lavrsen.dk/twiki/bin/view/Motion/BugReport2007x03x30x175913 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 motion-release-4.0/FAQ000066400000000000000000000001431300074623700146320ustar00rootroot00000000000000This FAQ is no longer kept up to date Look at the guide in /usr/share/doc/motion/motion_guide.html motion-release-4.0/INSTALL000066400000000000000000000026241300074623700153370ustar00rootroot00000000000000The following is a brief overview of the building and installing instructions. For full instructions on how to build and install Motion, see the motion_guide.html that is distributed with this source code. The building instructions vary based upon the base system and the desired options. For most installs, it will be desired to include either ffmpeg or libav and these will have associated dependencies which dictate the configuration options. Build Packages: autoconf automake pkgconf libtool libjpeg8-dev build-essential libzip-dev The option to include FFMPEG or Libav functionality in motion is a choice of one OR the other not both. The packages and library names change frequently and vary across base operating systems. If ffmpeg or libav are built and installed from source the custom motion configuration options will usually be needed. FFMPEG Packages ffmpeg libavformat-dev libavcodec-dev libavutil-dev libswscale-dev Libav Packages libavformat-dev libavcodec-dev libavutil-dev libav-tools libswscale-dev Once required packages are installed, execute: autoreconf -fiv ./configure make make install Sample custom configuration options: --prefix : Specify the install location for the motion package --with-ffmpeg=[dir] : Specify the location in which ffmpeg/libav is installed. motion-release-4.0/Makefile.in000066400000000000000000000332371300074623700163570ustar00rootroot00000000000000################################################################################ # 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: # # https://motion-project.github.io/ # ################################################################################ CC = @CC@ INSTALL = install INSTALL_DATA = ${INSTALL} -m 644 ################################################################################ # Install locations, controlled by setting configure flags. # ################################################################################ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ mandir = @mandir@ sysconfdir = @sysconfdir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = $(datadir)/doc/@PACKAGE_NAME@ examplesdir = $(datadir)/@PACKAGE_NAME@/examples ################################################################################ # These variables contain compiler flags, object files to build and files to # # install. # ################################################################################ CFLAGS = @CFLAGS@ -Wall -DVERSION=\"@PACKAGE_VERSION@\" -Dsysconfdir=\"$(sysconfdir)\" @FFMPEG_CFLAGS@ @MMAL_CFLAGS@ LDFLAGS = @LDFLAGS@ LIBS = @LIBS@ @MMAL_LIBS@ @FFMPEG_LIBS@ VIDEO_OBJ = @VIDEO@ OBJ = motion.o logger.o conf.o draw.o jpegutils.o vloopback_motion.o $(VIDEO_OBJ) \ netcam.o netcam_ftp.o netcam_jpeg.o netcam_wget.o track.o \ alg.o event.o picture.o rotate.o webhttpd.o \ stream.o md5.o netcam_rtsp.o \ @FFMPEG_OBJ@ @MMAL_OBJ@ @SDL_OBJ@ SRC = $(OBJ:.o=.c) DOC = CHANGELOG COPYING CREDITS README.md motion_guide.html mask1.png normal.jpg outputmotion1.jpg outputnormal1.jpg EXAMPLES = *.conf motion.service EXAMPLES_BIN = motion.init-Debian motion.init-FreeBSD.sh PROGS = motion DEPEND_FILE = .depend ################################################################################ # ALL and PROGS build Motion and, possibly, Motion-control. # ################################################################################ all: progs ifneq (,$(findstring freebsd,$(VIDEO_OBJ))) @echo "Build complete, run \"gmake install\" to install Motion!" else @echo "Build complete, run \"make install\" to install Motion!" endif @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 @echo "Version: @PACKAGE_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) ################################################################################ # DEV, BUILD with developer flags # ################################################################################ dev: distclean autotools all ################################################################################ # DEV-GIT, BUILD with developer flags # ################################################################################ dev-git: distclean autotools-git all ################################################################################ # GIT, BUILD with developer flags # ################################################################################ build-commit-git: distclean set-version-git all ################################################################################ # CURRENT, BUILD current svn trunk. # ################################################################################ current: distclean svn autotools all svn: svn update autotools: @sed -e 's/.\/commit-version.sh/.\/version.sh/g' configure.ac > configure.ac.tmp && mv -f configure.ac.tmp configure.ac autoconf ./configure --with-developer-flags autotools-git: @sed -e 's/.\/git-commit-version.sh/.\/version.sh/g' configure.ac > configure.ac.tmp && mv -f configure.ac.tmp configure.ac autoconf ./configure --with-developer-flags build-commit: distclean svn set-version all set-version: @sed -e 's/.\/version.sh/.\/commit-version.sh/g' configure.ac > configure.ac.tmp && mv -f configure.ac.tmp configure.ac autoconf @sed -e 's/.\/commit-version.sh/.\/version.sh/g' configure.ac > configure.ac.tmp && mv -f configure.ac.tmp configure.ac ./configure --with-developer-flags set-version-git: @sed -e 's/.\/version.sh/.\/git-commit-version.sh/g' configure.ac > configure.ac.tmp && mv -f configure.ac.tmp configure.ac autoconf @sed -e 's/.\/git-commit-version.sh/.\/version.sh/g' configure.ac > configure.ac.tmp && mv -f configure.ac.tmp configure.ac ./configure --with-developer-flags help: @echo "--------------------------------------------------------------------------------" @echo "make Build motion from local copy in your computer" @echo "make current Build last version of motion from svn" @echo "make dev Build motion with dev flags" @echo "make dev-git Build motion with dev flags for git" @echo "make build-commit Build last version of motion and prepare to commit to svn" @echo "make build-commit-git Build last version of motion and prepare to commit to git" @echo "make clean Clean objects" @echo "make distclean Clean everything" @echo "make install Install binary , examples , docs and config files" @echo "make uninstall Uninstall all installed files" @echo "--------------------------------------------------------------------------------" @echo ################################################################################ # INSTALL installs all relevant files. # ################################################################################ install: @echo "Installing files..." @echo "--------------------------------------------------------------------------------" mkdir -p $(DESTDIR)$(bindir) mkdir -p $(DESTDIR)$(mandir)/man1 mkdir -p $(DESTDIR)$(sysconfdir)/motion mkdir -p $(DESTDIR)$(docdir) mkdir -p $(DESTDIR)$(examplesdir) @sed -e 's|$${prefix}|$(prefix)|' motion-dist.conf > motion-dist.conf.tmp && mv -f motion-dist.conf.tmp motion-dist.conf @sed -e 's|$${prefix}|$(prefix)|' camera1-dist.conf > camera1-dist.conf.tmp && mv -f camera1-dist.conf.tmp camera1-dist.conf @sed -e 's|$${prefix}|$(prefix)|' camera2-dist.conf > camera2-dist.conf.tmp && mv -f camera2-dist.conf.tmp camera2-dist.conf @sed -e 's|$${prefix}|$(prefix)|' camera3-dist.conf > camera3-dist.conf.tmp && mv -f camera3-dist.conf.tmp camera3-dist.conf @sed -e 's|$${prefix}|$(prefix)|' camera4-dist.conf > camera4-dist.conf.tmp && mv -f camera4-dist.conf.tmp camera4-dist.conf $(INSTALL_DATA) motion.1 $(DESTDIR)$(mandir)/man1 $(INSTALL_DATA) $(DOC) $(DESTDIR)$(docdir) $(INSTALL_DATA) $(EXAMPLES) $(DESTDIR)$(examplesdir) $(INSTALL) $(EXAMPLES_BIN) $(DESTDIR)$(examplesdir) $(INSTALL_DATA) motion-dist.conf $(DESTDIR)$(sysconfdir)/motion $(INSTALL_DATA) camera1-dist.conf $(DESTDIR)$(sysconfdir)/motion $(INSTALL_DATA) camera2-dist.conf $(DESTDIR)$(sysconfdir)/motion $(INSTALL_DATA) camera3-dist.conf $(DESTDIR)$(sysconfdir)/motion $(INSTALL_DATA) camera4-dist.conf $(DESTDIR)$(sysconfdir)/motion 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)/motion. You need to rename/copy it to 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/motion-dist.conf rm -f $(sysconfdir)/motion/camera1-dist.conf rm -f $(sysconfdir)/motion/camera2-dist.conf rm -f $(sysconfdir)/motion/camera3-dist.conf rm -f $(sysconfdir)/motion/camera4-dist.conf rm -rf $(docdir) rm -rf $(examplesdir) @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 *~ *.o $(PROGS) combine $(DEPEND_FILE) ################################################################################ # DIST restores the directory to distribution state. # ################################################################################ dist: distclean updateguide @chmod -R 644 * @chmod 755 configure @chmod 755 version.sh ################################################################################ # 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.service motion.init-Debian motion.init-FreeBSD.sh @rm -f camera1-dist.conf camera2-dist.conf camera3-dist.conf camera4-dist.conf motion-dist.conf motion-help.conf motion.spec @rm -rf autom4te.cache config.h @echo "You will need to re-run configure if you want to build Motion." @echo motion-release-4.0/README.FreeBSD000066400000000000000000000020371300074623700163750ustar00rootroot00000000000000The following file explains how to build and run Motion on FreeBSD. It was tested on FreeBSD 10.3. To build Motion on FreeBSD, you need the following packages: * autoconf * automake * ffmpeg * gcc * gmake * libjpeg-turbo * pkgconf For database backend support, also install one of the following: * mysql57-client * postgresql95-contrib * sqlite3 For PWC based webcams, also install: * pwcbsd * v4l_compat Then, run the following commands: autoreconf ./configure gmake gmake install To set up your capture device: 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 motion-release-4.0/README.MacOSX000066400000000000000000000020121300074623700162460ustar00rootroot00000000000000 The motion port to MacOSX is only working with network cameras. Tested in latest 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 motion-release-4.0/README.md000066400000000000000000000033471300074623700155700ustar00rootroot00000000000000Motion ============= ## Status The build status from travis-ci for the master branch is: [![Build Status](https://travis-ci.org/Motion-Project/motion.svg?branch=master)](https://travis-ci.org/Motion-Project/motion) ## Description 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. ## Documentation The documentation for Motion is contained within the file motion_guide.html. The offline version of this file is available in the **doc/motion** directory. The online version of the motion_guide.html file is within the master branch and can be viewed [here](http://htmlpreview.github.com/?https://github.com/Motion-Project/motion/blob/master/motion_guide.html) In addition to the detailed building instructions included within the guide, the INSTALL file contains abbreviated building instructions. ## Resources Please join the mailing list [here](https://lists.sourceforge.net/lists/listinfo/motion-user) We prefer support through the mailing list because more people will have the benefit from the answers. A archive of mailing list discussions can be viewed [here](https://sourceforge.net/p/motion/mailman/motion-user/) ## License Motion is mainly distributed under the GNU GENERAL PUBLIC LICENSE (GPL) version 2 or later. See the copyright file for a list of all the licensing terms of the various components of Motion. The file CREDITS lists the many people who have contributed to Motion over the years. ## Contributing Issues and Patches should be submitted via github and include detail descriptions of the issue being addressed as well as any documentation updates that would be needed with the change. motion-release-4.0/alg.c000066400000000000000000001251571300074623700152240ustar00rootroot00000000000000/* 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" #include "alg.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))) /** * alg_locate_center_size * Locates 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; y < height; y++) { for (x = 0; x < width; x++) { if (*(labels++) & 32768) { cent->x += x; cent->y += y; centc++; } } } } else { /* Locate movement */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { if (*(out++)) { cent->x += 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 < height; y++) { for (x = 0; x < width; x++) { if (*(labels++) & 32768) { if (x > 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 < height; y++) { for (x = 0; x < width; x++) { if (*(out++)) { if (x > 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 little 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; /* Align for better locate box handling */ cent->minx += cent->minx % 2; cent->miny += cent->miny % 2; cent->maxx -= cent->maxx % 2; cent->maxy -= cent->maxy % 2; 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; } /** * alg_draw_location * Draws a box around the movement. */ void alg_draw_location(struct coord *cent, struct images *imgs, int width, unsigned char *new, int style, int mode, int process_thisframe) { unsigned char *out = imgs->out; int x, y; out = imgs->out; /* Debug image always gets a 'normal' box. */ if ((mode == LOCATE_BOTH) && process_thisframe) { 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; 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; out[width_minx_y] =~out[width_minx_y]; out[width_maxx_y] =~out[width_maxx_y]; } } if (style == LOCATE_BOX) { /* Draw a box on normal images. */ 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]; } 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]; } } else if (style == LOCATE_CROSS) { /* Draw a cross on normal images. */ int centy = cent->y * width; for (x = cent->x - 10; x <= cent->x + 10; x++) { new[centy + x] =~new[centy + x]; out[centy + x] =~out[centy + x]; } for (y = cent->y - 10; y <= cent->y + 10; y++) { new[cent->x + y * width] =~new[cent->x + y * width]; out[cent->x + y * width] =~out[cent->x + y * width]; } } } /** * alg_draw_red_location * Draws a RED box around the movement. */ void alg_draw_red_location(struct coord *cent, struct images *imgs, int width, unsigned char *new, int style, int mode, int process_thisframe) { unsigned char *out = imgs->out; unsigned char *new_u, *new_v; int x, y, v, cwidth, cblock; cwidth = width / 2; cblock = imgs->motionsize / 4; x = imgs->motionsize; v = x + cblock; out = imgs->out; new_u = new + x; new_v = new + v; /* Debug image always gets a 'normal' box. */ if ((mode == LOCATE_BOTH) && process_thisframe) { 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; 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; out[width_minx_y] =~out[width_minx_y]; out[width_maxx_y] =~out[width_maxx_y]; } } if (style == LOCATE_REDBOX) { /* Draw a red box on normal images. */ int width_miny = width * cent->miny; int width_maxy = width * cent->maxy; int cwidth_miny = cwidth * (cent->miny / 2); int cwidth_maxy = cwidth * (cent->maxy / 2); for (x = cent->minx + 2; x <= cent->maxx - 2; x += 2) { int width_miny_x = x + width_miny; int width_maxy_x = x + width_maxy; int cwidth_miny_x = x / 2 + cwidth_miny; int cwidth_maxy_x = x / 2 + cwidth_maxy; new_u[cwidth_miny_x] = 128; new_u[cwidth_maxy_x] = 128; new_v[cwidth_miny_x] = 255; new_v[cwidth_maxy_x] = 255; new[width_miny_x] = 128; new[width_maxy_x] = 128; new[width_miny_x + 1] = 128; new[width_maxy_x + 1] = 128; new[width_miny_x + width] = 128; new[width_maxy_x + width] = 128; new[width_miny_x + 1 + width] = 128; new[width_maxy_x + 1 + width] = 128; } for (y = cent->miny; y <= cent->maxy; y += 2) { int width_minx_y = cent->minx + y * width; int width_maxx_y = cent->maxx + y * width; int cwidth_minx_y = (cent->minx / 2) + (y / 2) * cwidth; int cwidth_maxx_y = (cent->maxx / 2) + (y / 2) * cwidth; new_u[cwidth_minx_y] = 128; new_u[cwidth_maxx_y] = 128; new_v[cwidth_minx_y] = 255; new_v[cwidth_maxx_y] = 255; new[width_minx_y] = 128; new[width_maxx_y] = 128; new[width_minx_y + width] = 128; new[width_maxx_y + width] = 128; new[width_minx_y + 1] = 128; new[width_maxx_y + 1] = 128; new[width_minx_y + width + 1] = 128; new[width_maxx_y + width + 1] = 128; } } else if (style == LOCATE_REDCROSS) { /* Draw a red cross on normal images. */ int cwidth_maxy = cwidth * (cent->y / 2); for (x = cent->x - 10; x <= cent->x + 10; x += 2) { int cwidth_maxy_x = x / 2 + cwidth_maxy; new_u[cwidth_maxy_x] = 128; new_v[cwidth_maxy_x] = 255; } for (y = cent->y - 10; y <= cent->y + 10; y += 2) { int cwidth_minx_y = (cent->x / 2) + (y / 2) * cwidth; new_u[cwidth_minx_y] = 128; new_v[cwidth_minx_y] = 255; } } } #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))) /** * alg_noise_tune * */ 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 = ABS(*ref - *new); if (mask) diff = ((diff * *mask++) / 255); if (*smartmask) { sum += diff + 1; count++; } ref++; new++; smartmask++; } if (count > 3) /* Avoid divide by zero. */ sum /= count / 3; /* 5: safe, 4: regular, 3: more sensitive */ cnt->noise = 4 + (cnt->noise + sum) / 2; } /** * alg_threshold_tune * */ 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; /** * iflood * */ 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; } /** * alg_labeling * */ 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; /* Keep track of the area just under the threshold. */ int max_under = 0; cnt->current_image->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(*labels)); pixelpos = 0; for (iy = 0; iy < height - 1; iy++) { for (ix = 0; ix < width - 1; ix++, pixelpos++) { /* No motion - no label */ if (out[pixelpos] == 0) { labels[pixelpos] = 1; continue; } /* Already visited by iflood */ if (labels[pixelpos] > 0) continue; labelsize = iflood(ix, iy, width, height, out, labels, current_label, 0); if (labelsize > 0) { MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%s: Label: %i (%i) Size: %i (%i,%i)", current_label, cnt->current_image->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++; } else if(max_under < labelsize) max_under = labelsize; if (imgs->labelsize_max < labelsize) { imgs->labelsize_max = labelsize; imgs->largest_label = current_label; } cnt->current_image->total_labels++; current_label++; } } pixelpos++; /* Compensate for ix < width - 1 */ } MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%s: %i Labels found. Largest connected Area: %i Pixel(s). " "Largest Label: %i", imgs->largest_label, imgs->labelsize_max, cnt->current_image->total_labels); /* Return group of significant labels or if that's none, the next largest * group (which is under the threshold, but especially for setup gives an * idea how close it was). */ return imgs->labelgroup_max ? imgs->labelgroup_max : max_under; } /** * dilate9 * 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. * - width 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; } /** * dilate5 * 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; } /** * erode9 * 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; } /** * erode5 * 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; } /** * alg_despeckle * 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_filter); unsigned char *common_buffer = cnt->imgs.common_buffer; for (i = 0; i < len; i++) { switch (cnt->conf.despeckle_filter[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 = 2; break; } } /* If conf.despeckle_filter contains any valid action EeDdl */ if (done) { if (done != 2) cnt->imgs.labelsize_max = 0; // Disable Labeling return diffs; } else { cnt->imgs.labelsize_max = 0; // Disable Labeling } return olddiffs; } /** * alg_tune_smartmask * Generates 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 < motionsize; i++) { /* Decrease smart_mask sensitivity every 5*speed seconds only. */ if (smartmask[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 /** * alg_diff_standard * */ int alg_diff_standard(struct context *cnt, unsigned char *new) { struct images *imgs = &cnt->imgs; int i, diffs = 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 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 (repeated 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 */ /* AND 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 (cnt->event_nr != cnt->prev_event) { 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 sensitivity 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. */ if (cnt->event_nr != cnt->prev_event) (*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; } /** * alg_diff_fast * Very fast diff function, does not apply 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; } /** * alg_lightswitch * Detects 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; } /** * alg_switchfilter * */ 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; } /** * alg_update_reference_frame * * Called from 'motion_loop' to calculate the reference frame * Moving objects are excluded from the reference frame for a certain * amount of time to improve detection. * * Parameters: * * cnt - current thread's context struct * action - UPDATE_REF_FRAME or RESET_REF_FRAME * */ #define ACCEPT_STATIC_OBJECT_TIME 10 /* Seconds */ #define EXCLUDE_LEVEL_PERCENT 20 void alg_update_reference_frame(struct context *cnt, int action) { int accept_timer = cnt->lastrate * ACCEPT_STATIC_OBJECT_TIME; int i, threshold_ref; int *ref_dyn = cnt->imgs.ref_dyn; unsigned char *image_virgin = cnt->imgs.image_virgin; unsigned char *ref = cnt->imgs.ref; unsigned char *smartmask = cnt->imgs.smartmask_final; unsigned char *out = cnt->imgs.out; if (cnt->lastrate > 5) /* Match rate limit */ accept_timer /= (cnt->lastrate / 3); if (action == UPDATE_REF_FRAME) { /* Black&white only for better performance. */ threshold_ref = cnt->noise * EXCLUDE_LEVEL_PERCENT / 100; for (i = cnt->imgs.motionsize; i > 0; i--) { /* Exclude pixels from ref frame well below noise level. */ if (((int)(abs(*ref - *image_virgin)) > threshold_ref) && (*smartmask)) { if (*ref_dyn == 0) { /* Always give new pixels a chance. */ *ref_dyn = 1; } else if (*ref_dyn > accept_timer) { /* Include static Object after some time. */ *ref_dyn = 0; *ref = *image_virgin; } else if (*out) { (*ref_dyn)++; /* Motionpixel? Keep excluding from ref frame. */ } else { *ref_dyn = 0; /* Nothing special - release pixel. */ *ref = (*ref + *image_virgin) / 2; } } else { /* No motion: copy to ref frame. */ *ref_dyn = 0; /* Reset pixel */ *ref = *image_virgin; } ref++; image_virgin++; smartmask++; ref_dyn++; out++; } /* end for i */ } else { /* action == RESET_REF_FRAME - also used to initialize the frame at startup. */ /* Copy fresh image */ memcpy(cnt->imgs.ref, cnt->imgs.image_virgin, cnt->imgs.size); /* Reset static objects */ memset(cnt->imgs.ref_dyn, 0, cnt->imgs.motionsize * sizeof(*cnt->imgs.ref_dyn)); } } motion-release-4.0/alg.h000066400000000000000000000024461300074623700152240ustar00rootroot00000000000000/* 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, int); void alg_draw_red_location(struct coord *, struct images *, int width, unsigned char *, int, int, 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 *); void alg_update_reference_frame(struct context *, int); #endif /* _INCLUDE_ALG_H */ motion-release-4.0/camera1-dist.conf.in000066400000000000000000000053351300074623700200360ustar00rootroot00000000000000# @sysconfdir@/motion/camera1.conf # # This config file was generated by @PACKAGE_NAME@ @PACKAGE_VERSION@ ########################################################### # Capture device options ############################################################ # Camera Id # Consistent identification number to assign to each camera across multiple # invocations of Motion. # Default: The order when the camera file was read # camera_id = 1 # 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: -1) # Should normally be set to 1 for video/TV cards, and -1 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 1 ############################################################ # Target Directories and filenames For Images And Films # For the options snapshot_, picture_, 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 = camera id 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 /tmp/motion/cam1 # 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 picture_filename CAM1_%v-%Y%m%d%H%M%S-%q ############################################################ # Live Stream Server ############################################################ # The mini-http server listens to this port for requests (default: 0 = disabled) stream_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/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 motion-release-4.0/camera2-dist.conf.in000066400000000000000000000053341300074623700200360ustar00rootroot00000000000000# @sysconfdir@/motion/camera2.conf # # This config file was generated by @PACKAGE_NAME@ @PACKAGE_VERSION@ ########################################################### # Capture device options ############################################################ # Camera Id # Consistent identification number to assign to each camera across multiple # invocations of Motion. # Default: The order when the camera file was read # camera_id = 2 # 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: -1) # Should normally be set to 1 for video/TV cards, and -1 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_, picture_, 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 = camera id 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 /tmp/motion/cam2 # 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 picture_filename CAM2_%v-%Y%m%d%H%M%S-%q ############################################################ # Live Stream Server ############################################################ # The mini-http server listens to this port for requests (default: 0 = disabled) stream_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 motion-release-4.0/camera3-dist.conf.in000066400000000000000000000053351300074623700200400ustar00rootroot00000000000000# @sysconfdir@/motion/camera3.conf # # This config file was generated by @PACKAGE_NAME@ @PACKAGE_VERSION@ ########################################################### # Capture device options ############################################################ # Camera Id # Consistent identification number to assign to each camera across multiple # invocations of Motion. # Default: The order when the camera file was read # camera_id = 3 # 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: -1) # Should normally be set to 1 for video/TV cards, and -1 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 3 ############################################################ # Target Directories and filenames For Images And Films # For the options snapshot_, picture_, 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 = camera id 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 /tmp/motion/cam3 # 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 picture_filename CAM3_%v-%Y%m%d%H%M%S-%q ############################################################ # Live Stream Server ############################################################ # The mini-http server listens to this port for requests (default: 0 = disabled) stream_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/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 motion-release-4.0/camera4-dist.conf.in000066400000000000000000000053351300074623700200410ustar00rootroot00000000000000# @sysconfdir@/motion/camera4.conf # # This config file was generated by @PACKAGE_NAME@ @PACKAGE_VERSION@ ########################################################### # Capture device options ############################################################ # Camera Id # Consistent identification number to assign to each camera across multiple # invocations of Motion. # Default: The order when the camera file was read # camera_id = 4 # Videodevice to be used for capturing (default /dev/video0) # for FreeBSD default is /dev/bktr0 videodevice /dev/video3 # The video input to be used (default: -1) # Should normally be set to 1 for video/TV cards, and -1 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_, picture_, 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 = camera id 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 /tmp/motion/cam4 # 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 picture_filename CAM4_%v-%Y%m%d%H%M%S-%q ############################################################ # Live Stream Server ############################################################ # The mini-http server listens to this port for requests (default: 0 = disabled) stream_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/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 motion-release-4.0/commit-version.sh000077500000000000000000000001161300074623700176120ustar00rootroot00000000000000#!/bin/sh SNV_VERSION=`git show -s --format=%h` echo -n "4.0+git$SNV_VERSION" motion-release-4.0/conf.c000066400000000000000000002411541300074623700154020ustar00rootroot00000000000000/* ** ** conf.c ** ** I originally 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 #include #include "motion.h" #if (defined(BSD) && !defined(PWCBSD)) #include "video_freebsd.h" #else #include "video.h" #endif /* BSD */ #define EXTENSION ".conf" #ifndef HAVE_GET_CURRENT_DIR_NAME char *get_current_dir_name(void) { char *buf = mymalloc(MAXPATHLEN); getwd(buf); return buf; } #endif #define stripnewline(x) {if ((x)[strlen(x)-1]=='\n') (x)[strlen(x) - 1] = 0; } struct config conf_template = { camera_name: NULL, width: DEF_WIDTH, height: DEF_HEIGHT, quality: DEF_QUALITY, camera_id: 0, rotate_deg: 0, max_changes: DEF_CHANGES, threshold_tune: 0, output_pictures: "on", motion_img: 0, emulate_motion: 0, event_gap: DEF_EVENT_GAP, max_movie_time: DEF_MAXMOVIETIME, snapshot_interval: 0, locate_motion_mode: "off", locate_motion_style: "box", input: IN_DEFAULT, norm: 0, frame_limit: DEF_MAXFRAMERATE, quiet: 1, picture_type: "jpeg", noise: DEF_NOISELEVEL, noise_tune: 1, minimum_frame_time: 0, lightswitch: 0, autobright: 0, brightness: 0, contrast: 0, saturation: 0, hue: 0, power_line_frequency: -1, roundrobin_frames: 1, roundrobin_skip: 1, pre_capture: 0, post_capture: 0, switchfilter: 0, ffmpeg_output: 0, extpipe: NULL, useextpipe: 0, ffmpeg_output_debug: 0, ffmpeg_bps: DEF_FFMPEG_BPS, ffmpeg_vbr: DEF_FFMPEG_VBR, ffmpeg_video_codec: DEF_FFMPEG_CODEC, #ifdef HAVE_SDL sdl_threadnr: 0, #endif ipv6_enabled: 0, stream_port: 0, stream_quality: 50, stream_motion: 0, stream_maxrate: 1, stream_localhost: 1, stream_limit: 0, stream_auth_method: 0, stream_authentication: NULL, stream_preview_scale: 25, stream_preview_newline: 0, webcontrol_port: 0, webcontrol_localhost: 1, webcontrol_html_output: 1, webcontrol_authentication: NULL, frequency: 0, tuner_number: 0, timelapse: 0, timelapse_mode: DEF_TIMELAPSE_MODE, #if (defined(BSD)) tuner_device: NULL, #endif video_device: VIDEO_DEVICE, v4l2_palette: DEF_PALETTE, vidpipe: NULL, filepath: NULL, imagepath: DEF_IMAGEPATH, moviepath: DEF_MOVIEPATH, snappath: DEF_SNAPPATH, timepath: DEF_TIMEPATH, on_event_start: NULL, on_event_end: NULL, mask_file: NULL, smart_mask_speed: 0, #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) sql_log_image: 1, sql_log_snapshot: 1, sql_log_movie: 0, sql_log_timelapse: 0, sql_query: DEF_SQL_QUERY, database_type: NULL, database_dbname: NULL, database_host: "localhost", database_user: NULL, database_password: NULL, database_port: 0, database_busy_timeout: 0, #endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || define(HAVE_SQLITE3) */ on_picture_save: NULL, on_motion_detected: NULL, on_area_detected: NULL, on_movie_start: NULL, on_movie_end: NULL, on_camera_lost: NULL, motionvidpipe: NULL, netcam_url: NULL, netcam_userpass: NULL, netcam_keepalive: "off", netcam_proxy: NULL, netcam_tolerant_check: 0, rtsp_uses_tcp: 1, #ifdef HAVE_MMAL mmalcam_name: NULL, mmalcam_control_params: NULL, #endif text_changes: 0, text_left: NULL, text_right: DEF_TIMESTAMP, text_event: DEF_EVENTSTAMP, text_double: 0, despeckle_filter: NULL, area_detect: NULL, minimum_motion_frames: 1, exif_text: NULL, pid_file: NULL, log_file: NULL, log_level: LEVEL_DEFAULT+10, log_type_str: NULL, camera_dir: sysconfdir"/conf.d" }; static struct context **copy_bool(struct context **, const char *, int); static struct context **copy_int(struct context **, const char *, int); static struct context **config_camera(struct context **cnt, const char *str, int val); static struct context **read_camera_dir(struct context **cnt, const char *str, int val); static const char *print_bool(struct context **, char **, int, unsigned int); static const char *print_int(struct context **, char **, int, unsigned int); static const char *print_string(struct context **, char **, int, unsigned int); static const char *print_camera(struct context **, char **, int, unsigned int); /* Deprcated thread config functions */ static struct context **config_thread(struct context **cnt, const char *str, int val); static const char *print_thread(struct context **, char **, int, unsigned int); static void usage(void); /* Pointer magic to determine relative addresses of variables to a struct context pointer */ #define CNT_OFFSET(varname) ((long)&((struct context *)NULL)->varname) #define CONF_OFFSET(varname) ((long)&((struct context *)NULL)->conf.varname) #define TRACK_OFFSET(varname) ((long)&((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)", 1, CNT_OFFSET(daemon), copy_bool, print_bool }, { "process_id_file", "#File to store the process ID, also called pid file. (default: not defined)", 1, CONF_OFFSET(pid_file), copy_string, print_string }, { "setup_mode", "############################################################\n" "# Basic Setup Mode\n" "############################################################\n\n" "# Start in Setup-Mode, daemon disabled. (default: off)", 0, CONF_OFFSET(setup_mode), copy_bool, print_bool }, { "camera_name", "# Name given to a camera. Shown in web interface and may be used with the specifier %$ for filenames and such.\n" "# Default: not defined", 0, CONF_OFFSET(camera_name), copy_string, print_string }, { "logfile", "# Use a file to save logs messages, if not defined stderr and syslog is used. (default: not defined)", 1, CONF_OFFSET(log_file), copy_string, print_string }, { "log_level", "# Level of log messages [1..9] (EMG, ALR, CRT, ERR, WRN, NTC, INF, DBG, ALL). (default: 6 / NTC)", 1, CONF_OFFSET(log_level), copy_int, print_int }, { "log_type", "# Filter to log messages by type (COR, STR, ENC, NET, DBL, EVT, TRK, VID, ALL). (default: ALL)", 1, CONF_OFFSET(log_type_str), copy_string, print_string }, { "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", 0, CONF_OFFSET(video_device), copy_string, print_string }, { "v4l2_palette", "# v4l2_palette allows one to choose preferable palette to be use by motion\n" "# to capture from those supported by your videodevice. (default: 17)\n" "# E.g. if your videodevice supports both V4L2_PIX_FMT_SBGGR8 and\n" "# V4L2_PIX_FMT_MJPEG then motion will by default use V4L2_PIX_FMT_MJPEG.\n" "# Setting v4l2_palette to 2 forces motion to use V4L2_PIX_FMT_SBGGR8\n" "# instead.\n" "#\n" "# Values :\n" "# V4L2_PIX_FMT_SN9C10X : 0 'S910'\n" "# V4L2_PIX_FMT_SBGGR16 : 1 'BYR2'\n" "# V4L2_PIX_FMT_SBGGR8 : 2 'BA81'\n" "# V4L2_PIX_FMT_SPCA561 : 3 'S561'\n" "# V4L2_PIX_FMT_SGBRG8 : 4 'GBRG'\n" "# V4L2_PIX_FMT_SGRBG8 : 5 'GRBG'\n" "# V4L2_PIX_FMT_PAC207 : 6 'P207'\n" "# V4L2_PIX_FMT_PJPG : 7 'PJPG'\n" "# V4L2_PIX_FMT_MJPEG : 8 'MJPEG'\n" "# V4L2_PIX_FMT_JPEG : 9 'JPEG'\n" "# V4L2_PIX_FMT_RGB24 : 10 'RGB3'\n" "# V4L2_PIX_FMT_SPCA501 : 11 'S501'\n" "# V4L2_PIX_FMT_SPCA505 : 12 'S505'\n" "# V4L2_PIX_FMT_SPCA508 : 13 'S508'\n" "# V4L2_PIX_FMT_UYVY : 14 'UYVY'\n" "# V4L2_PIX_FMT_YUYV : 15 'YUYV'\n" "# V4L2_PIX_FMT_YUV422P : 16 '422P'\n" "# V4L2_PIX_FMT_YUV420 : 17 'YU12'\n" "#", 0, CONF_OFFSET(v4l2_palette), copy_int, print_int }, #if (defined(BSD)) { "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", 0, CONF_OFFSET(tuner_device), copy_string, print_string }, #endif { "input", "# The video input to be used (default: -1)\n" "# Should normally be set to 0 or 1 for video/TV cards, and -1 for USB cameras", 0, 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)", 0, CONF_OFFSET(norm), copy_int, print_int }, { "frequency", "# The frequency to set the tuner to (kHz) (only for TV tuner cards) (default: 0)", 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 movies. Valid values: 0 (default = no rotation), 90, 180 and 270.", 0, CONF_OFFSET(rotate_deg), copy_int, print_int }, { "width", "# Image width (pixels). Valid range: Camera dependent, default: 352", 0, CONF_OFFSET(width), copy_int, print_int }, { "height", "# Image height (pixels). Valid range: Camera dependent, default: 288", 0, 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).", 0, CONF_OFFSET(frame_limit), copy_int, print_int }, { "minimum_frame_time", "# Minimum time in seconds between capturing picture frames from the camera.\n" "# Default: 0 = disabled - the capture rate is given by the camera framerate.\n" "# This option is used when you want to capture images at a rate lower than 2 per second.", 0, CONF_OFFSET(minimum_frame_time), copy_int, print_int }, { "netcam_url", "# URL to use if you are using a network camera, size will be autodetected (incl http:// ftp:// mjpg:// or file:///)\n" "# Must be a URL that returns single jpeg pictures or a raw mjpeg stream. Default: Not defined", 0, 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", 0, CONF_OFFSET(netcam_userpass), copy_string, print_string }, { "netcam_keepalive", "# The setting for keep-alive of network socket, should improve performance on compatible net cameras.\n" "# off: The historical implementation using HTTP/1.0, closing the socket after each http request.\n" "# force: Use HTTP/1.0 requests with keep alive header to reuse the same connection.\n" "# on: Use HTTP/1.1 requests that support keep alive as default.\n" "# Default: off", 0, CONF_OFFSET(netcam_keepalive), 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", 0, CONF_OFFSET(netcam_proxy), copy_string, print_string }, { "netcam_tolerant_check", "# Set less strict jpeg checks for network cameras with a poor/buggy firmware.\n" "# Default: off", 0, CONF_OFFSET(netcam_tolerant_check), copy_bool, print_bool }, { "rtsp_uses_tcp", "# RTSP connection uses TCP to communicate to the camera. Can prevent image corruption.\n" "# Default: on", 1, CONF_OFFSET(rtsp_uses_tcp), copy_bool, print_bool }, #ifdef HAVE_MMAL { "mmalcam_name", "# Name of camera to use if you are using a camera accessed through OpenMax/MMAL\n" "# For the raspberry pi official camera, use vc.ril.camera" "# Default: Not defined", 0, CONF_OFFSET(mmalcam_name), copy_string, print_string }, { "mmalcam_control_params", "# Camera control parameters (see raspivid/raspistill tool documentation)\n" "# Default: Not defined", 0, CONF_OFFSET(mmalcam_control_params), copy_string, print_string }, #endif { "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", 0, 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", 0, CONF_OFFSET(brightness), copy_int, print_int }, { "contrast", "# Set the contrast of a video device.\n" "# Valid range 0-255, default 0 = disabled", 0, CONF_OFFSET(contrast), copy_int, print_int }, { "saturation", "# Set the saturation of a video device.\n" "# Valid range 0-255, default 0 = disabled", 0, 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", 0, CONF_OFFSET(hue), copy_int, print_int }, { "power_line_frequency", "# Set the power line frequency to help cancel flicker by compensating\n" "# for light intensity ripple. (default: -1).\n" "# This can help reduce power line light flicker.\n" "# Valuse :\n" "# do not modify the device setting : -1\n" "# V4L2_CID_POWER_LINE_FREQUENCY_DISABLED : 0\n" "# V4L2_CID_POWER_LINE_FREQUENCY_50HZ : 1\n" "# V4L2_CID_POWER_LINE_FREQUENCY_60HZ : 2\n" "# V4L2_CID_POWER_LINE_FREQUENCY_AUTO : 3", 0, CONF_OFFSET(power_line_frequency), copy_int, print_int }, { "roundrobin_frames", "\n############################################################\n" "# Round Robin (multiple inputs on same video device name)\n" "############################################################\n\n" "# Number of frames to capture in each roundrobin step (default: 1)", 0, CONF_OFFSET(roundrobin_frames), copy_int, print_int }, { "roundrobin_skip", "# Number of frames to skip before each roundrobin step (default: 1)", 0, CONF_OFFSET(roundrobin_skip), copy_int, print_int }, { "switchfilter", "# Try to filter out noise generated by roundrobin (default: off)", 0, 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)", 0, CONF_OFFSET(max_changes), copy_int, print_int }, { "threshold_tune", "# Automatically tune the threshold down if possible (default: off)", 0, CONF_OFFSET(threshold_tune), copy_bool, print_bool }, { "noise_level", "# Noise threshold for the motion detection (default: 32)", 0, CONF_OFFSET(noise), copy_int, print_int }, { "noise_tune", "# Automatically tune the noise threshold (default: on)", 0, CONF_OFFSET(noise_tune), copy_bool, print_bool }, { "despeckle_filter", "# 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", 0, CONF_OFFSET(despeckle_filter), copy_string, print_string }, { "area_detect", "# Detect motion in predefined areas (1 - 9). Areas are numbered like that: 1 2 3\n" "# A script (on_area_detected) is started immediately when motion is 4 5 6\n" "# detected in one of the given areas, but only once during an event. 7 8 9\n" "# One or more areas can be specified with this option. Take care: This option\n" "# does NOT restrict detection to these areas! (Default: not defined)", 0, CONF_OFFSET(area_detect), copy_string, print_string }, { "mask_file", "# PGM file to use as a sensitivity mask.\n" "# Full path name to. (Default: not defined)", 0, 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)", 0, 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. If set to 1, motion will do some kind of\n" "# auto-lightswitch. Valid range: 0 - 100 , default: 0 = disabled", 0, 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", 0, 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 movies. To smooth movies use larger values of post_capture instead.", 0, CONF_OFFSET(pre_capture), copy_int, print_int }, { "post_capture", "# Number of frames to capture after motion is no longer detected (default: 0)", 0, CONF_OFFSET(post_capture), copy_int, print_int }, { "event_gap", "# Event 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 -1 is allowed and disables\n" "# events causing all Motion to be written to one single movie file and no pre_capture.\n" "# If set to 0, motion is running in gapless mode. Movies don't have gaps anymore. An\n" "# event ends right after no more motion is detected and post_capture is over.", 0, CONF_OFFSET(event_gap), copy_int, print_int }, { "max_movie_time", "# Maximum length in seconds of a movie\n" "# When value is exceeded a new movie file is created. (Default: 0 = infinite)", 0, CONF_OFFSET(max_movie_time), copy_int, print_int }, { "emulate_motion", "# Always save images even if there was no motion (default: off)", 0, CONF_OFFSET(emulate_motion), copy_bool, print_bool }, { "output_pictures", "\n############################################################\n" "# Image File Output\n" "############################################################\n\n" "# Output 'normal' pictures when motion is detected (default: on)\n" "# Valid values: on, off, first, best, center\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" "# Picture with motion nearest center of picture is saved when set to 'center'.\n" "# Can be used as preview shot for the corresponding movie.", 0, CONF_OFFSET(output_pictures), copy_string, print_string }, { "output_debug_pictures", "# Output pictures with only the pixels moving object (ghost images) (default: off)", 0, CONF_OFFSET(motion_img), copy_bool, print_bool }, { "quality", "# The quality (in percent) to be used by the jpeg compression (default: 75)", 0, CONF_OFFSET(quality), copy_int, print_int }, { "camera_id", "# Id used to label the camera when inserting data into SQL or saving the\n" "# camera image to disk. This is better than using thread ID so that there\n" "# always is a consistent label", 0, CONF_OFFSET(camera_id), copy_int, print_int }, { "picture_type", "# Type of output images\n" "# Valid values: jpeg, ppm (default: jpeg)", 0, CONF_OFFSET(picture_type), copy_string, print_string }, #ifdef HAVE_FFMPEG { "ffmpeg_output_movies", "\n############################################################\n" "# FFMPEG related options\n" "# Film (movie) file output, and deinterlacing of the video input\n" "# The options movie_filename and timelapse_filename are also used\n" "# by the ffmpeg feature\n" "############################################################\n\n" "# Use ffmpeg to encode movies in realtime (default: off)", 0, CONF_OFFSET(ffmpeg_output), copy_bool, print_bool }, { "ffmpeg_output_debug_movies", "# Use ffmpeg to make movies with only the pixels moving\n" "# object (ghost images) (default: off)", 0, CONF_OFFSET(ffmpeg_output_debug), copy_bool, print_bool }, { "ffmpeg_timelapse", "# Use ffmpeg to encode a timelapse movie\n" "# Default value 0 = off - else save frame every Nth second", 0, 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", 0, 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)", 0, 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.", 0, CONF_OFFSET(ffmpeg_vbr), copy_int, print_int }, { "ffmpeg_video_codec", "# Codec to used by ffmpeg for the video compression.\n" "# Timelapse movies 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 - gives 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.\n" "# swf - gives you a flash film with extension .swf\n" "# flv - gives you a flash video with extension .flv\n" "# ffv1 - FF video codec 1 for Lossless Encoding ( experimental )\n" "# mov - QuickTime ( testing )\n" "# ogg - Ogg/Theora ( testing )\n" "# mp4 - MPEG-4 Part 14 H264 encoding\n" "# mkv - Matroska H264 encoding\n" "# hevc - H.265 / HEVC (High Efficiency Video Coding)", 0, CONF_OFFSET(ffmpeg_video_codec), copy_string, print_string }, { "ffmpeg_duplicate_frames", "# True to duplicate frames to achieve \"framerate\" fps, but enough\n" "duplicated frames and the video appears to freeze once a second.", 0, CONF_OFFSET(ffmpeg_duplicate_frames), copy_bool, print_bool }, #endif /* HAVE_FFMPEG */ #ifdef HAVE_SDL { "sdl_threadnr", "\n############################################################\n" "# SDL Window\n" "############################################################\n\n" "# Number of motion thread to show in SDL Window (default: 0 = disabled)", 1, CONF_OFFSET(sdl_threadnr), copy_int, print_int }, #endif /* HAVE_SDL */ { "use_extpipe", "\n############################################################\n" "# External pipe to video encoder\n" "# Replacement for FFMPEG builtin encoder for ffmpeg_output_movies only.\n" "# The options movie_filename and timelapse_filename are also used\n" "# by the ffmpeg feature\n" "############################################################\n\n" "# Bool to enable or disable extpipe (default: off)", 0, CONF_OFFSET(useextpipe), copy_bool, print_bool }, { "extpipe", "# External program (full path and opts) to pipe raw video to\n" "# Generally, use '-' for STDIN...", 0, CONF_OFFSET(extpipe), copy_string, print_string }, { "snapshot_interval", "\n############################################################\n" "# Snapshots (Traditional Periodic Webcam File Output)\n" "############################################################\n\n" "# Make automated snapshot every N seconds (default: 0 = disabled)", 0, CONF_OFFSET(snapshot_interval), copy_int, print_int }, { "locate_motion_mode", "\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 = camera id,\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, preview (default: off)\n" "# Set to 'preview' will only draw a box in preview_shot pictures.", 0, CONF_OFFSET(locate_motion_mode), copy_string, print_string }, { "locate_motion_style", "# Set the look and style of the locate box if enabled.\n" "# Valid values: box, redbox, cross, redcross (default: box)\n" "# Set to 'box' will draw the traditional box.\n" "# Set to 'redbox' will draw a red box.\n" "# Set to 'cross' will draw a little cross to mark center.\n" "# Set to 'redcross' will draw a little red cross to mark center.", 0, CONF_OFFSET(locate_motion_style), 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", 0, 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", 0, 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", 0, CONF_OFFSET(text_changes), copy_bool, print_bool }, { "text_event", "# This option defines the value of the special event conversion specifier %C\n" "# You can use any conversion specifier in this option except %C. Date and time\n" "# 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.", 0, CONF_OFFSET(text_event), copy_string, print_string }, { "text_double", "# Draw characters at twice normal size on images. (default: off)", 0, CONF_OFFSET(text_double), copy_bool, print_bool }, { "exif_text", "# Text to include in a JPEG EXIF comment\n" "# May be any text, including conversion specifiers.\n" "# The EXIF timestamp is included independent of this text.", 0, CONF_OFFSET(exif_text), copy_string, print_string }, { "target_dir", "\n############################################################\n" "# Target Directories and filenames For Images And Films\n" "# For the options snapshot_, picture_, movie_ 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 = camera id,\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)", 0, 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'", 0, CONF_OFFSET(snappath), copy_string, print_string }, { "picture_filename", "# File path for motion triggered images (jpeg or ppm) relative to target_dir\n" "# Default: "DEF_IMAGEPATH"\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", 0, CONF_OFFSET(imagepath), copy_string, print_string }, #ifdef HAVE_FFMPEG { "movie_filename", "# File path for motion triggered ffmpeg films (movies) relative to target_dir\n" "# Default: "DEF_MOVIEPATH"\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\n" "# This option was previously called ffmpeg_filename", 0, CONF_OFFSET(moviepath), copy_string, print_string }, { "timelapse_filename", "# File path for timelapse movies 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", 0, CONF_OFFSET(timepath), copy_string, print_string }, #endif /* HAVE_FFMPEG */ { "ipv6_enabled", "\n############################################################\n" "# Global Network Options\n" "############################################################\n\n" "# Enable IPv6 (default: off)", 0, CONF_OFFSET(ipv6_enabled), copy_bool, print_bool }, { "stream_port", "\n############################################################\n" "# Live Stream Server\n" "############################################################\n\n" "# The mini-http server listens to this port for requests (default: 0 = disabled)", 0, CONF_OFFSET(stream_port), copy_int, print_int }, { "stream_quality", "# Quality of the jpeg (in percent) images produced (default: 50)", 0, CONF_OFFSET(stream_quality), copy_int, print_int }, { "stream_motion", "# Output frames at 1 fps when no motion is detected and increase to the\n" "# rate given by stream_maxrate when motion is detected (default: off)", 0, CONF_OFFSET(stream_motion), copy_bool, print_bool }, { "stream_maxrate", "# Maximum framerate for streams (default: 1)", 0, CONF_OFFSET(stream_maxrate), copy_int, print_int }, { "stream_localhost", "# Restrict stream connections to localhost only (default: on)", 0, CONF_OFFSET(stream_localhost), copy_bool, print_bool }, { "stream_limit", "# Limits the number of images per connection (default: 0 = unlimited)\n" "# Number can be defined by multiplying actual stream rate by desired number of seconds\n" "# Actual stream rate is the smallest of the numbers framerate and stream_maxrate", 0, CONF_OFFSET(stream_limit), copy_int, print_int }, { "stream_auth_method", "# Set the authentication method (default: 0)\n" "# 0 = disabled \n" "# 1 = Basic authentication\n" "# 2 = MD5 digest (the safer authentication)\n", 0, CONF_OFFSET(stream_auth_method), copy_int, print_int }, { "stream_authentication", "# Authentication for the stream. Syntax username:password\n" "# Default: not defined (Disabled)", 1, CONF_OFFSET(stream_authentication), copy_string, print_string }, { "stream_preview_scale", "# Percentage to scale the preview stream image (default: 25)\n", 0, CONF_OFFSET(stream_preview_scale), copy_int, print_int }, { "stream_preview_newline", "# Have stream preview image start on a new line (default: no)\n", 0, CONF_OFFSET(stream_preview_newline), copy_bool, print_bool }, { "webcontrol_port", "\n############################################################\n" "# HTTP Based Control\n" "############################################################\n\n" "# TCP/IP port for the http server to listen on (default: 0 = disabled)", 1, CONF_OFFSET(webcontrol_port), copy_int, print_int }, { "webcontrol_localhost", "# Restrict control connections to localhost only (default: on)", 1, CONF_OFFSET(webcontrol_localhost), copy_bool, print_bool }, { "webcontrol_html_output", "# Output for http server, select off to choose raw text plain (default: on)", 1, CONF_OFFSET(webcontrol_html_output), copy_bool, print_bool }, { "webcontrol_authentication", "# Authentication for the http based control. Syntax username:password\n" "# Default: not defined (Disabled)", 1, CONF_OFFSET(webcontrol_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, 5=uvcvideo, 6=servo)\n" "# The generic type enables the definition of motion center and motion size to\n" "# be used with the conversion specifiers for options like on_motion_detected", 0, TRACK_OFFSET(type), copy_int, print_int }, { "track_auto", "# Enable auto tracking (default: off)", 0, TRACK_OFFSET(active), copy_bool, print_bool }, { "track_port", "# Serial port of motor (default: none)", 0, TRACK_OFFSET(port), copy_string, print_string }, { "track_motorx", "# Motor number for x-axis (default: 0)", 0, TRACK_OFFSET(motorx), copy_int, print_int }, { "track_motorx_reverse", "# Set motorx reverse (default: off)", 0, TRACK_OFFSET(motorx_reverse), copy_bool, print_bool }, { "track_motory", "# Motor number for y-axis (default: 0)", 0, TRACK_OFFSET(motory), copy_int, print_int }, { "track_motory_reverse", "# Set motory reverse (default: off)", 0, TRACK_OFFSET(motory_reverse), copy_bool, print_bool }, { "track_maxx", "# Maximum value on x-axis (default: 0)", 0, TRACK_OFFSET(maxx), copy_int, print_int }, { "track_minx", "# Minimum value on x-axis (default: 0)", 0, TRACK_OFFSET(minx), copy_int, print_int }, { "track_maxy", "# Maximum value on y-axis (default: 0)", 0, TRACK_OFFSET(maxy), copy_int, print_int }, { "track_miny", "# Minimum value on y-axis (default: 0)", 0, TRACK_OFFSET(miny), copy_int, print_int }, { "track_homex", "# Center value on x-axis (default: 0)", 0, TRACK_OFFSET(homex), copy_int, print_int }, { "track_homey", "# Center value on y-axis (default: 0)", 0, TRACK_OFFSET(homey), copy_int, print_int }, { "track_iomojo_id", "# ID of an iomojo camera if used (default: 0)", 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", 0, 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", 0, 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)", 0, TRACK_OFFSET(move_wait), copy_int, print_int }, { "track_speed", "# Speed to set the motor to (stepper motor option) (default: 255)", 0, TRACK_OFFSET(speed), copy_int, print_int }, { "track_stepsize", "# Number of steps to make (stepper motor option) (default: 40)", 0, 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.", 0, 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 event_gap ", 0, 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 event_gap.", 0, 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", 0, CONF_OFFSET(on_picture_save), copy_string, print_string }, { "on_motion_detected", "# Command to be executed when a motion frame is detected (default: none)", 0, CONF_OFFSET(on_motion_detected), copy_string, print_string }, { "on_area_detected", "# Command to be executed when motion in a predefined area is detected\n" "# Check option 'area_detect'. (default: none)", 0, CONF_OFFSET(on_area_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", 0, 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", 0, CONF_OFFSET(on_movie_end), copy_string, print_string }, #endif /* HAVE_FFMPEG */ { "on_camera_lost", "# Command to be executed when a camera can't be opened or if it is lost\n" "# NOTE: There is situations when motion don't detect a lost camera!\n" "# It depends on the driver, some drivers dosn't detect a lost camera at all\n" "# Some hangs the motion thread. Some even hangs the PC! (default: none)", 0, CONF_OFFSET(on_camera_lost), copy_string, print_string }, #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) { "sql_log_picture", "\n############################################################\n" "# Common Options for database features.\n" "# Options require the database options to be active also.\n" "############################################################\n\n" "# Log to the database when creating motion triggered image file (default: on)", 0, CONF_OFFSET(sql_log_image), copy_bool, print_bool }, { "sql_log_snapshot", "# Log to the database when creating a snapshot image file (default: on)", 0, CONF_OFFSET(sql_log_snapshot), copy_bool, print_bool }, { "sql_log_movie", "# Log to the database when creating motion triggered movie file (default: off)", 0, CONF_OFFSET(sql_log_movie), copy_bool, print_bool }, { "sql_log_timelapse", "# Log to the database when creating timelapse movie file (default: off)", 0, 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" "# Create tables :\n" "##\n" "# Mysql\n" "# CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp(14), event_time_stamp timestamp(14));\n" "#\n" "# Postgresql\n" "# CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp without time zone, event_time_stamp timestamp without time zone);\n" "#\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')", 0, CONF_OFFSET(sql_query), copy_string, print_string }, { "database_type", "\n############################################################\n" "# Database Options \n" "############################################################\n\n" "# database type : mysql, postgresql, sqlite3 (default : not defined)", 0, CONF_OFFSET(database_type), copy_string, print_string }, { "database_dbname", "# database to log to (default: not defined)", 0, CONF_OFFSET(database_dbname), copy_string, print_string }, { "database_host", "# The host on which the database is located (default: not defined)", 0, CONF_OFFSET(database_host), copy_string, print_string }, { "database_user", "# User account name for database (default: not defined)", 0, CONF_OFFSET(database_user), copy_string, print_string }, { "database_password", "# User password for database (default: not defined)", 0, CONF_OFFSET(database_password), copy_string, print_string }, { "database_port", "# Port on which the database is located (default: not defined)\n" "# mysql 3306 , postgresql 5432 (default: not defined)", 0, CONF_OFFSET(database_port), copy_int, print_int }, { "database_busy_timeout", "# Database wait for unlock time (default: 0)", 0, CONF_OFFSET(database_busy_timeout), copy_int, print_int }, #endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) */ { "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)", 0, 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)", 0, CONF_OFFSET(motionvidpipe), copy_string, print_string }, { "camera", "\n##############################################################\n" "# Camera 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 camera\n" "# config file for each camera in addition to this config file.\n" "##############################################################\n", 1, 0, config_camera, print_camera }, { "thread", "\n##############################################################\n" "# Deprecated use camera instead of thread.\n" "# Camera 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 camera\n" "# config file for each camera in addition to this config file.\n" "##############################################################\n", 1, 0, config_thread, print_thread }, /* using a conf.d style camera addition */ { "camera_dir", "\n##############################################################\n" "# Camera config directory - One for each camera.\n" "##############################################################\n", 1, CONF_OFFSET(camera_dir), read_camera_dir, print_string }, { NULL, NULL, 0, 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. * * Returns nothing. */ 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, "bc:d:hmns?p:k:l:")) != EOF) switch (c) { case 'c': if (thread == -1) strcpy(cnt->conf_filename, optarg); break; case 'b': cnt->daemon = 1; break; case 'n': cnt->daemon = 0; break; case 's': conf->setup_mode = 1; break; case 'd': /* No validation - just take what user gives. */ if (thread == -1) cnt->log_level = (unsigned int)atoi(optarg); break; case 'k': if (thread == -1) { strncpy(cnt->log_type_str, optarg, sizeof(cnt->log_type_str) - 1); cnt->log_type_str[sizeof(cnt->log_type_str) - 1] = '\0'; } break; case 'p': if (thread == -1) { strncpy(cnt->pid_file, optarg, sizeof(cnt->pid_file) - 1); cnt->pid_file[sizeof(cnt->pid_file) - 1] = '\0'; } break; case 'l': if (thread == -1) { strncpy(cnt->log_file, optarg, sizeof(cnt->log_file) - 1); cnt->log_file[sizeof(cnt->log_file) - 1] = '\0'; } break; case 'm': cnt->pause = 1; 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. * * Returns context struct. */ struct context **conf_cmdparse(struct context **cnt, const char *cmd, const char *arg1) { unsigned 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_param is string we don't want to check arg1. */ if (strcmp(config_type(&config_params[i]), "string")) { 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. * If the option is a camera, config_camera 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(ALR, TYPE_ALL, NO_ERRNO, "%s: 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. * * Returns context struct. */ 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 >= line && (*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 == '\n' || *beg == '\r') 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. * * Returns nothing. */ void conf_print(struct context **cnt) { const char *retval; char *val; unsigned int i, thread; FILE *conffile; for (thread = 0; cnt[thread]; thread++) { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: 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, "; camera %s/motion/camera1.conf\n", sysconfdir); 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"); myfclose(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. * * Returns context struct. */ 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. Command-line * 2. current working directory * 3. $HOME/.motion/motion.conf * 4. sysconfig/motion.conf */ /* Get filename , pid file & log file from Command-line. */ cnt[0]->log_type_str[0] = 0; cnt[0]->conf_filename[0] = 0; cnt[0]->pid_file[0] = 0; cnt[0]->log_file[0] = 0; cnt[0]->log_level = -1; conf_cmdline(cnt[0], -1); if (cnt[0]->conf_filename[0]) { /* User has supplied filename on Command-line. */ strncpy(filename, cnt[0]->conf_filename, PATH_MAX-1); filename[PATH_MAX-1] = '\0'; fp = fopen (filename, "r"); } if (!fp) { /* Command-line didn't work, try current dir. */ char *path = NULL; if (cnt[0]->conf_filename[0]) MOTION_LOG(ALR, TYPE_ALL, SHOW_ERRNO, "%s: Configfile %s not found - trying defaults.", filename); if ((path = get_current_dir_name()) == NULL) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Error get_current_dir_name"); exit(-1); } snprintf(filename, PATH_MAX, "%s/motion.conf", path); fp = fopen (filename, "r"); free(path); } if (!fp) { /* Specified file does not exist... try default file. */ snprintf(filename, PATH_MAX, "%s/.motion/motion.conf", getenv("HOME")); fp = fopen(filename, "r"); if (!fp) { snprintf(filename, PATH_MAX, "%s/motion/motion.conf", sysconfdir); fp = fopen(filename, "r"); if (!fp) /* There is no config file.... use defaults. */ MOTION_LOG(ALR, TYPE_ALL, SHOW_ERRNO, "%s: could not open configfile %s", filename); } } /* Now we process the motion.conf config file and close it. */ if (fp) { strncpy(cnt[0]->conf_filename, filename, sizeof(cnt[0]->conf_filename) - 1); cnt[0]->conf_filename[sizeof(cnt[0]->conf_filename) - 1] = '\0'; MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Processing thread 0 - config file %s", filename); cnt = conf_process(cnt, fp); myfclose(fp); } else { MOTION_LOG(CRT, TYPE_ALL, NO_ERRNO, "%s: Not config file to process using default values"); } /* * 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); /* If pid file was passed from Command-line copy to main thread conf struct. */ if (cnt[0]->pid_file[0]) cnt[0]->conf.pid_file = mystrcpy(cnt[0]->conf.pid_file, cnt[0]->pid_file); /* If log file was passed from Command-line copy to main thread conf struct. */ if (cnt[0]->log_file[0]) cnt[0]->conf.log_file = mystrcpy(cnt[0]->conf.log_file, cnt[0]->log_file); /* If log type string was passed from Command-line copy to main thread conf struct. */ if (cnt[0]->log_type_str[0]) cnt[0]->conf.log_type_str = mystrcpy(cnt[0]->conf.log_type_str, cnt[0]->log_type_str); /* if log level was passed from Command-line copy to main thread conf struct. */ if (cnt[0]->log_level != -1) cnt[0]->conf.log_level = cnt[0]->log_level; 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 * * Returns nothing. */ void malloc_strings(struct context *cnt) { unsigned 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 converted 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. * * Returns context struct. */ 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 integer value. * The integer is given as a string in str which is converted to integer * by the function. * * Returns context struct. */ 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. * * Returns context struct. */ 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 free'd. * * 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 * 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. * * Returns 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. */ char *mystrdup(const char *from) { char *tmp; size_t stringlength; if (from == NULL || !strlen(from)) { tmp = NULL; } else { stringlength = strlen(from); stringlength = (stringlength < PATH_MAX ? stringlength : PATH_MAX); tmp = 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 string length. */ tmp += stringlength; *tmp = '\0'; tmp -= stringlength; } return tmp; } /** * config_type * Returns a pointer to string containing value the type of config parameter passed. * * Returns const char *. */ 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"; } /** * print_bool * Returns a pointer to string containing boolean value 'on' / 'off' or NULL. * * Returns const char *. */ static const char *print_bool(struct context **cnt, char **str ATTRIBUTE_UNUSED, int parm, unsigned 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 thread number is not 0 the string is compared with the value of the same * option in thread 0. * * Returns If the option is not defined NULL is returned. * 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, unsigned 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, unsigned 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, unsigned int threadnr) { MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "thread config option deprecated use camera"); return print_camera(cnt, str, parm, threadnr); } static const char *print_camera(struct context **cnt, char **str, int parm ATTRIBUTE_UNUSED, unsigned int threadnr) { char *retval; unsigned 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_camera"); sprintf(retval + strlen(retval), "camera %s\n", cnt[i]->conf_filename); } *str = retval; return NULL; } /** * config_camera_dir * Read the directory finding all *.conf files in the path * when calls config_camera */ static struct context **read_camera_dir(struct context **cnt, const char *str, int val ATTRIBUTE_UNUSED) { DIR *dp; struct dirent *ep; size_t name_len; char conf_file[PATH_MAX]; dp = opendir(str); if (dp != NULL) { while( (ep = readdir(dp)) ) { name_len = strlen(ep->d_name); if (name_len > strlen(EXTENSION) && (strncmp(EXTENSION, (ep->d_name + name_len - strlen(EXTENSION)), strlen(EXTENSION)) == 0 ) ) { memset(conf_file, '\0', sizeof(conf_file)); snprintf(conf_file, sizeof(conf_file) - 1, "%s/%s", str, ep->d_name); MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Processing config file %s", conf_file ); cnt = config_camera(cnt, conf_file, 0); } } closedir(dp); } else { MOTION_LOG(ALR, TYPE_ALL, SHOW_ERRNO, "%s: Camera directory config " "%s not found", str); } return cnt; } static struct context **config_thread(struct context **cnt, const char *str, int val ATTRIBUTE_UNUSED) { MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "thread config option deprecated use camera"); return config_camera(cnt, str, val); } /** * config_camera * Is called during initial config file loading each time Motion * finds a camera 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_camera(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(ALR, TYPE_ALL, SHOW_ERRNO, "%s: Camera 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_camera"); /* 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(NTC, TYPE_ALL, NO_ERRNO, "%s: Processing camera config file %s", str); conf_process(cnt + i, fp); /* Finally we close the thread config file. */ myfclose(fp); return cnt; } /** * usage * Prints usage and options allowed from Command-line. * * Returns nothing. */ static void usage() { printf("motion Version "VERSION", Copyright 2000-2016 Jeroen Vreeken/Folkert van Heusden/Kenneth Lavrsen/Motion-Project maintainers\n"); printf("\nusage:\tmotion [options]\n"); printf("\n\n"); printf("Possible options:\n\n"); printf("-b\t\t\tRun in background (daemon) mode.\n"); printf("-n\t\t\tRun in non-daemon mode.\n"); printf("-s\t\t\tRun in setup mode.\n"); printf("-c config\t\tFull path and filename of config file.\n"); printf("-d level\t\tLog level (1-9) (EMG, ALR, CRT, ERR, WRN, NTC, INF, DBG, ALL). default: 6 / NTC.\n"); printf("-k type\t\t\tType of log (COR, STR, ENC, NET, DBL, EVT, TRK, VID, ALL). default: ALL.\n"); printf("-p process_id_file\tFull path and filename of process id file (pid file).\n"); printf("-l log file \t\tFull path and filename of log file.\n"); printf("-m\t\t\tDisable motion detection at startup.\n"); printf("-h\t\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/motion.\n", sysconfdir); printf("\n"); } motion-release-4.0/conf.h000066400000000000000000000117471300074623700154120ustar00rootroot00000000000000/* * * 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 { const char *camera_name; unsigned int log_level; char *log_type_str; char *log_file; int setup_mode; int width; int height; int camera_id; int quality; int rotate_deg; int max_changes; int threshold_tune; const char *output_pictures; int ffmpeg_duplicate_frames; int motion_img; int emulate_motion; int event_gap; int max_movie_time; int snapshot_interval; const char *locate_motion_mode; const char *locate_motion_style; int input; int norm; int frame_limit; int quiet; int useextpipe; /* ext_pipe on or off */ const char *extpipe; /* full Command-line for pipe -- must accept YUV420P images */ const char *picture_type; int noise; int noise_tune; int minimum_frame_time; int lightswitch; int autobright; int brightness; int contrast; int saturation; int hue; int power_line_frequency; int roundrobin_frames; int roundrobin_skip; int pre_capture; int post_capture; int switchfilter; int ffmpeg_output; int ffmpeg_output_debug; int ffmpeg_bps; int ffmpeg_vbr; const char *ffmpeg_video_codec; #ifdef HAVE_SDL int sdl_threadnr; #endif int ipv6_enabled; int stream_port; int stream_quality; int stream_motion; int stream_maxrate; int stream_localhost; int stream_limit; int stream_auth_method; const char *stream_authentication; int stream_preview_scale; int stream_preview_newline; int webcontrol_port; int webcontrol_localhost; int webcontrol_html_output; const char *webcontrol_authentication; unsigned long frequency; int tuner_number; int timelapse; const char *timelapse_mode; #if (defined(BSD) || defined(__FreeBSD_kernel__)) const char *tuner_device; #endif const char *video_device; int v4l2_palette; const char *vidpipe; const char *filepath; const char *imagepath; const char *moviepath; 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_movie; int sql_log_timelapse; const char *sql_query; const char *database_type; const char *database_dbname; const char *database_host; const char *database_user; const char *database_password; int database_busy_timeout; int database_port; char *on_picture_save; char *on_area_detected; char *on_motion_detected; char *on_movie_start; char *on_movie_end; char *on_camera_lost; const char *motionvidpipe; const char *netcam_url; const char *netcam_userpass; const char *netcam_keepalive; const char *netcam_proxy; unsigned int netcam_tolerant_check; unsigned int rtsp_uses_tcp; #ifdef HAVE_MMAL const char *mmalcam_name; const char *mmalcam_control_params; #endif int text_changes; const char *text_left; const char *text_right; const char *text_event; int text_double; const char *despeckle_filter; const char *area_detect; const char *camera_dir; int minimum_motion_frames; const char *exif_text; char *pid_file; 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, unsigned 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 */ unsigned int main_thread; /* belong only to main thread when value>0 */ 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); #ifndef HAVE_GET_CURRENT_DIR_NAME char *get_current_dir_name(void); #endif #endif /* _INCLUDE_CONF_H */ motion-release-4.0/configure.ac000066400000000000000000000755651300074623700166120ustar00rootroot00000000000000# Process this file with autoconf to produce a configure script AC_INIT(motion, esyscmd(['./version.sh'])) AC_GNU_SOURCE AC_CONFIG_SRCDIR([motion.c]) AC_CONFIG_HEADERS(config.h) AC_PROG_CC THREAD_CFLAGS="" THREAD_CHECK="/usr/include/pthread.h" Darwin="" FreeBSD="" LINUXTHREADS="no" AC_ARG_WITH(linuxthreads, [ --with-linuxthreads Use linuxthreads in BSD instead of native pthreads ] , LINUXTHREADS="$withval" ) PWCBSD="no" AC_ARG_WITH(pwcbsd, [ --with-pwcbsd Use pwcbsd based webcams ( only BSD ) ] , PWCBSD="$withval" ) 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 "FreeBSD"` if test "${FreeBSD}" = ""; then AC_MSG_RESULT(no) VIDEO="video.o video2.o video_common.o" else AC_MSG_RESULT(yes) if test "${LINUXTHREADS}" = "no"; then AC_MSG_CHECKING(Linuxthreads) AC_MSG_RESULT(skipping) else THREAD_CHECK="/usr/local/include/pthread/linuxthreads/pthread.h" THREAD_LIB_CHECK="/usr/local/lib/liblthread.so" fi if test "${PWCBSD}" != "no"; then VIDEO="video.o video2.o video_common.o" TEMP_CFLAGS="${CFLAGS} -I/usr/local/include -DPWCBSD" else VIDEO="video_freebsd.o" TEMP_CFLAGS="${CFLAGS} -I/usr/local/include" fi TEMP_LDFLAGS="${LDFLAGS} -L/usr/local/lib" TEMP_LIBS="-L/usr/local/lib" AC_MSG_RESULT(Build with PWCBSD support $PWCBSD) fi else TEMP_CFLAGS="${CFLAGS} -I/sw/include" TEMP_LDFLAGS="${LDFLAGS} -L/sw/lib" TEMP_LIBS="-L/sw/lib" VIDEO="video_freebsd.o" FINK_LIB="-L/sw/lib" Darwin="yes" V4L="no" AC_MSG_RESULT($Darwin) fi AC_SUBST(VIDEO) # Checks for programs. AC_PROG_CC TEMP_LIBS="-lm ${TEMP_LIBS}" TEMP_CFLAGS="${TEMP_CFLAGS} ${CFLAGS}" TEMP_LDFLAGS="${TEMP_LDFLAGS} ${LDFLAGS}" if test "${FreeBSD}" != "" && test "${PWCBSD}" = "no"; then AC_MSG_CHECKING(bktr headers in /usr/include/dev/bktr) if test -f /usr/include/dev/bktr/ioctl_meteor.h && test -f /usr/include/dev/bktr/ioctl_bt848.h; then AC_MSG_RESULT(yes) else AC_MSG_RESULT(no) TEMP_CFLAGS="${TEMP_CFLAGS} -DOLD_BKTR" fi # # Check to Exclude BKTR # BKTR="yes" AC_ARG_WITH(bktr, [ --without-bktr Exclude to use bktr subsystem , that usually useful for devices as network cameras ( ONLY used in *BSD). ] , BKTR="$withval" ) if test "${BKTR}" = "no"; then TEMP_CFLAGS="${TEMP_CFLAGS} -DWITHOUT_V4L" fi else # # Check to Exclude V4L # 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 "${V4L}" = "no"; then TEMP_CFLAGS="${TEMP_CFLAGS} -DWITHOUT_V4L" fi if test "${FreeBSD}" != "" && test "${LINUXTHREADS}" != "no" ; then AC_MSG_CHECKING(for linuxthreads) # # Check for thread header # if test -f "${THREAD_CHECK}"; then HEADERS_THREAD_CFLAGS="-I/usr/local/include/pthread/linuxthreads" THREADS="yes" else THREADS="no" fi # # Check for thread lib # if test -f "${THREAD_LIB_CHECK}" ; then THREADS="yes" LIB_THREAD="-llthread -llgcc_r" else THREADS="no" fi # Checks for Library linuxthreads for FreeBSD # # linuxthreads on freeBSD, ports collection # /usr/local/include/pthreads/linuxthreads/pthread.h # #include # /usr/local/lib/libpthread.so # 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 elif test -f "${THREAD_CHECK}"; then AC_MSG_CHECKING(threads) AC_TRY_LINK([#include ], [pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], [PTHREAD_LIB=yes]) if test x$PTHREAD_LIB != xyes; then if test "${FreeBSD}" != ""; then TEMP_LIBS="$TEMP_LIBS -pthread" TEMP_CFLAGS="${TEMP_CFLAGS} -D_REENTRANT -D_THREAD_SAFE" else TEMP_LIBS="$TEMP_LIBS -lpthread" TEMP_CFLAGS="${TEMP_CFLAGS} -D_REENTRANT" fi PTHREAD_SUPPORT="yes" fi AC_MSG_RESULT($PTHREAD_SUPPORT) else echo echo "You do not have threads support" echo fi if test "x$PTHREAD_SUPPORT" = "xyes"; then AC_MSG_CHECKING([[for pthread_setname_np]]) CFLAGS="$TEMP_CFLAGS" LIBS="$TEMP_LIBS" AC_LINK_IFELSE( [AC_LANG_PROGRAM([[#include ]], [[ pthread_setname_np(pthread_self(), "name")]])], [AC_DEFINE([[HAVE_PTHREAD_SETNAME_NP]], [[1]], [Define if you have pthread_setname_np function.]) AC_MSG_RESULT([[yes]])], [AC_MSG_RESULT([[no]])] ) fi # # Check for sdl library # SDL_SUPPORT="no" AC_ARG_WITH(sdl, [ --with-sdl[=DIR] Specify the prefix for the install path for sdl-config to get stream in SDL window (optional). ], [], [withval="no"]) if test "x$withval" = "xno"; then AC_MSG_CHECKING(for sdl) AC_MSG_RESULT(skipped) else AC_PATH_PROG([CONFIG_SDL], [sdl-config], [], [${PATH}:${withval}:${withval}/bin]) if test -z "`($CONFIG_SDL --version) 2>/dev/null`" ;then echo "" echo "****************************************************" echo "* sdl-config could not be found. Please install it *" echo "* and remove the --with-sdl configure argument. *" echo "* libSDL can be found at http://www.libsdl.org *" echo "****************************************************" echo "" else SDL_SUPPORT="yes" TEMP_LIBS="$TEMP_LIBS `${CONFIG_SDL} --libs`" TEMP_CFLAGS="${TEMP_CFLAGS} `${CONFIG_SDL} --cflags`" AC_DEFINE([HAVE_SDL],1,[Define to 1 if you have SDL support]) SDL_OBJ="sdl.o" AC_SUBST(SDL_OBJ) fi fi # # Check for the libjpeg-turbo library # JPEG_TURBO="no" JPEG_TURBO_OK="not_found" AC_ARG_WITH(jpeg-turbo, [ --with-jpeg-turbo[=DIR] Specify the prefix for the install path for jpeg-turbo for optimized jpeg handling (optional). ], JPEG_TURBO="$withval" ) if test "${JPEG_TURBO}" = "no"; then AC_MSG_CHECKING(for libjpeg-turbo) AC_MSG_RESULT(skipping) else AC_MSG_CHECKING(for libjpeg-turbo in -> [${JPEG_TURBO}] <-) if test -f ${JPEG_TURBO}/lib/libjpeg.a -o -f ${JPEG_TURBO}/lib/libjpeg.so; then AC_MSG_RESULT(found) JPEG_TURBO_OK="found" else AC_MSG_RESULT(not found) fi fi if test "${JPEG_TURBO_OK}" = "found"; then saved_CFLAGS="$CFLAGS" saved_LIBS="$LIBS" saved_LDFLAGS="$LDFLAGS" LDFLAGS="-L${JPEG_TURBO}/lib" CFLAGS="$CFLAGS -I${JPEG_TURBO}/include" LIBS="$LIBS -L${JPEG_TURBO}/lib -ljpeg" AC_CHECK_LIB(jpeg, jpeg_start_compress, [ TEMP_LIBS="$TEMP_LIBS -L${JPEG_TURBO}/lib -ljpeg" TEMP_CFLAGS="${TEMP_CFLAGS} -I${JPEG_TURBO}/include" TEMP_LDFLAGS="$TEMP_LDFLAGS $LDFLAGS" JPEG_SUPPORT="yes"],,) LIBS="$saved_LIBS" CFLAGS="$saved_CFLAGS" LDFLAGS="$saved_LDFLAGS" JPEG_SUPPORT_TURBO="yes" fi # # Check for the special mmx accelerated jpeg library # 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" ) # # --without-jpeg-mmx or with-jpeg-mmx=no # if test "${JPEG_MMX}" = "no" || test x$JPEG_SUPPORT != xyes; then AC_MSG_CHECKING(for libjpeg-mmx) AC_MSG_RESULT(skipping) elif test "${JPEG_MMX}" = "yes"; then # 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 saved_CFLAGS="$CFLAGS" saved_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="$saved_LIBS" CFLAGS="$saved_CFLAGS" JPEG_SUPPORT_MMX="yes" fi # # Look for _a_ jpeg lib that will work. # if test x$JPEG_SUPPORT != xyes ; then # 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 # # Check for the pkg-config. # AC_CHECK_PROG([PKGCONFIG],[pkg-config],[yes],[no]) AM_CONDITIONAL([FOUND_PKGCONFIG], [test "x$PKGCONFIG" = xyes]) AM_COND_IF([FOUND_PKGCONFIG],,[AC_MSG_ERROR([Required package 'pkg-config' not found, please check motion_guide.html and install necessary dependencies.])]) # Check for raspberry pi mmal interface # WITHOUT_MMAL="no" AC_ARG_WITH([mmal], AS_HELP_STRING([--without-mmal], [Compile without RaspberyPi mmal camera support]), WITHOUT_MMAL="yes", WITHOUT_MMAL="no") if test "${WITHOUT_MMAL}" = "no"; then HAVE_MMAL="" LIBRASPBERRYPIDEVPATH="/opt/vc/include/interface/mmal" if test -d ${LIBRASPBERRYPIDEVPATH}; then HAVE_MMAL="yes" fi AS_IF([test "${HAVE_MMAL}" = "yes" ], [ AC_SUBST(MMAL_CFLAGS) AC_SUBST(MMAL_OBJ) AC_SUBST(MMAL_LIBS) MMAL_OBJ="mmalcam.o raspicam/RaspiCamControl.o raspicam/RaspiCLI.o" MMAL_CFLAGS="-std=gnu99 -DHAVE_MMAL -Irasppicam -I/opt/vc/include" MMAL_LIBS="-L/opt/vc/lib -lmmal_core -lmmal_util -lmmal_vc_client -lvcos -lvchostif -lvchiq_arm" AC_DEFINE([HAVE_MMAL], 1, [Define to 1 if we want MMAL]) ]) fi # # Check for libavcodec and libavformat from ffmpeg # FFMPEG_OBJ="" AC_ARG_WITH([ffmpeg], AS_HELP_STRING([--with-ffmpeg[=DIR]], [Build with FFMPEG support]), [with_ffmpeg=$withval], [with_ffmpeg=yes]) AS_IF([test "x$with_ffmpeg" != "xno"], [ AS_IF([test "x$with_ffmpeg" != "xyes"], [ PKG_CONFIG_PATH=${with_ffmpeg}/lib/pkgconfig:$PKG_CONFIG_PATH export PKG_CONFIG_PATH ]) AC_SUBST(FFMPEG_LIBS) AC_SUBST(FFMPEG_CFLAGS) FFMPEG_DEPS="libavutil libavformat libavcodec libswscale" if pkg-config $FFMPEG_DEPS; then FFMPEG_CFLAGS=`pkg-config --cflags $FFMPEG_DEPS` FFMPEG_LIBS=`pkg-config --libs $FFMPEG_DEPS` HAVE_FFMPEG="yes" else AC_MSG_ERROR([Required ffmpeg packages 'libavutil-dev libavformat-dev libavcodec-dev libswscale-dev' were not found. Please check motion_guide.html and install necessary dependencies or use the '--without-ffmpeg' configuration option.]) fi ]) AS_IF([test "${HAVE_FFMPEG}" = "yes" ], [ FFMPEG_OBJ="ffmpeg.o" AC_DEFINE([HAVE_FFMPEG], 1, [Define to 1 if FFMPEG is around]) ]) AC_SUBST(FFMPEG_OBJ) # # Check SQLITE3 # SQLITE3_SUPPORT="no" AC_ARG_WITH(sqlite3, [ --without-sqlite3 Disable sqlite3 support in motion. ], SQLITE3="$withval" # if not given argument, assume standard ) if test "${SQLITE3}" = "no"; then AC_MSG_CHECKING(for sqlite3) AC_MSG_RESULT(skipping) else saved_CFLAGS=$CFLAGS saved_LIBS=$LIBS # first we check to see if the sqlite3 amalgamation (sqlite3.c), is in with our source # this is the preferred way to use sqlite if test -f sqlite3.c; then SQLITE3_SUPPORT="yes" VIDEO="$VIDEO sqlite3.o" TEMP_LIBS="$TEMP_LIBS -ldl" AC_DEFINE([HAVE_SQLITE3],1,[Define to 1 if you have SQLITE3]) AC_DEFINE([HAVE_SQLITE3_EMBEDDED],1,[Define to 1 if you have SQLITE3 embedded support]) else # if sqlite3.c is not found then we look for the shared library AC_CHECK_LIB(sqlite3, sqlite3_open, [ TEMP_LIBS="$TEMP_LIBS -lsqlite3" SQLITE3_SUPPORT="yes" AC_DEFINE([HAVE_SQLITE3],1,[Define to 1 if you have SQLITE3 shared library support]) ] ) fi CFLAGS=$saved_CFLAGS LIBS=$saved_LIBS fi # # Check Mysql # MYSQL="yes" MYSQL_SUPPORT="no" MYSQL_HEADERS="yes" MYSQL_LIBS="yes" AC_MSG_CHECKING(for mysql support) AC_ARG_WITH(mysql, [ --without-mysql Disable mysql support in motion. ], MYSQL="$withval" # if not given argument, assume standard ) AC_ARG_WITH(mysql-lib, [ --with-mysql-lib[=DIR] Normally, configure will scan all possible default installation paths for mysql libs. When it fails, use this command to tell configure where mysql libs installation root directory is. ], MYSQL_LIBS="$withval" # if not given argument, assume standard ) AC_ARG_WITH(mysql-include, [ --with-mysql-include[=DIR] Normally, configure will scan all possible default installation paths for mysql include. When it fails, use this command to tell configure where mysql include installation root directory is. ], MYSQL_HEADERS="$withval" # if not given argument, assume standard ) if test "${MYSQL}" = "no"; then AC_MSG_RESULT(skipped) else AC_MSG_RESULT(testing) # ******* Search mysql headers ******* if test "${MYSQL_HEADERS}" = "yes"; then AC_MSG_CHECKING(autodect mysql headers) # 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 elif test "${MYSQL_HEADERS}" = "no"; then AC_MSG_CHECKING(for mysql headers) AC_MSG_RESULT(skipped) else AC_MSG_CHECKING(for mysql headers in $MYSQL_HEADERS) # Manual detection for if test -f $MYSQL_HEADERS/mysql.h; then MYSQL_INCDIR=$MYSQL_HEADERS fi fi if test -z "$MYSQL_INCDIR" ; then MYSQL_HEADERS="no" AC_MSG_RESULT(not found) echo "Invalid MySQL directory - unable to find mysql.h." else AC_MSG_RESULT($MYSQL_INCDIR yes) MYSQL_HEADERS="yes" fi if test "${MYSQL_HEADERS}" = "yes"; then # ******* Search mysql libs ********* if test "${MYSQL_LIBS}" = "yes"; then AC_MSG_CHECKING(autodect mysql libs) # Autodetect for w in /usr/lib64 /usr/lib /usr/local/lib /usr/mysql /usr/local/mysql /usr/local/mysql/lib /opt /opt/mysql /usr/lib/x86_64-linux-gnu; 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 elif test "${MYSQL_LIBS}" = "no"; then AC_MSG_CHECKING(for mysql libs) AC_MSG_RESULT(skipped) else AC_MSG_CHECKING(for mysql libs in $MYSQL_LIBS) # Manual detection for if test -f $MYSQL_LIBS/libmysqlclient.a -o -f $MYSQL_LIBS/libmysqlclient.so; then MYSQL_LIBDIR=$MYSQL_LIBS fi fi if test -z "$MYSQL_LIBDIR" ; then AC_MSG_RESULT(not found) echo "Invalid MySQL directory - unable to find libmysqlclient.a or libmysqlclient.so." else AC_MSG_RESULT($MYSQL_LIBDIR) #LDFLAGS="-L$MYSQL_LIBDIR" saved_CFLAGS=$CFLAGS saved_LIBS=$LIBS CFLAGS="-I$MYSQL_INCDIR" LIBS="-L$MYSQL_LIBDIR" AC_CHECK_LIB(mysqlclient,mysql_init,[ TEMP_LIBS="$TEMP_LIBS -L$MYSQL_LIBDIR -lmysqlclient -lz" TEMP_CFLAGS="$TEMP_CFLAGS -I$MYSQL_INCDIR" MYSQL_SUPPORT="yes" AC_DEFINE([HAVE_MYSQL],1,[Define to 1 if you have MySQL support]) ], AC_MSG_ERROR(MySQL support can't build without MySQL libraries)) CFLAGS=$saved_CFLAGS LIBS=$saved_LIBS fi # end mysql-include , mysql-libs fi # end Mysql detection fi # # Check PostgreSQL # PGSQL="yes" PGSQL_SUPPORT="no" PGSQL_HEADERS="yes" PGSQL_LIBS="yes" AC_DEFUN([PGSQL_INC_CHK],[if test -r $i$1/libpq-fe.h; then PGSQL_DIR=$i; PGSQL_INCDIR=$i$1]) AC_ARG_WITH(pgsql, [ --without-pgsql Disable PostgreSQL support in motion. ], PGSQL="$withval" # if not given argument, assume standard ) AC_ARG_WITH(pgsql-lib, [ --with-pgsql-lib[=DIR] Normally, configure will scan all possible default installation paths for pgsql libs. When it fails, use this command to tell configure where pgsql libs installation root directory is. ], PGSQL_LIBS="$withval" # if not given argument, assume standard ) AC_ARG_WITH(pgsql-include, [ --with-pgsql-include[=DIR] Normally, configure will scan all possible default installation paths for pgsql include. When it fails, use this command to tell configure where pgsql include installation root directory is. ], PGSQL_HEADERS="$withval" # if not given argument, assume standard ) AC_MSG_CHECKING(for PostgreSQL) if test "${PGSQL}" = "no"; then AC_MSG_RESULT(skipped) else AC_MSG_RESULT(testing) # ******* Search pgsql headers ******* if test "${PGSQL_HEADERS}" = "yes"; then AC_MSG_CHECKING(autodect pgsql headers) # Autodetect 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 elif test "${PGSQL_HEADERS}" = "no"; then AC_MSG_CHECKING(for pgsql headers) AC_MSG_RESULT(skipped) else AC_MSG_CHECKING(for pgsql headers in $PGSQL_HEADERS) # Manual detection for if test -f $PGSQL_HEADERS/libpq-fe.h; then PGSQL_INCDIR=$PGSQL_HEADERS fi fi if test -z "$PGSQL_INCDIR" ; then PGSQL_HEADERS="no" AC_MSG_RESULT(not found) echo "Invalid PostgreSQL directory - unable to find libpq-fe.h." else AC_MSG_RESULT(yes [$PGSQL_INCDIR]) PGSQL_HEADERS="yes" fi if test "${PGSQL_HEADERS}" = "yes"; then # ******* Search pgsql libs ********* if test "${PGSQL_LIBS}" = "yes"; then AC_MSG_CHECKING(autodect pgsql libs) # Autodetect PGSQL_INCLUDE="-I$PGSQL_INCDIR" PGSQL_LIBDIR=$PGSQL_DIR/lib if test -f /usr/lib64/libpq.so ; then PGSQL_LIBDIR=/usr/lib64 elif test -f $PGSQL_DIR/lib/pgsql/libpq.so ; then PGSQL_LIBDIR=$PGSQL_DIR/lib/pgsql elif test -f $PGSQL_DIR/lib/postgresql/libpq.so ; then PGSQL_LIBDIR=$PGSQL_DIR/lib/postgresql elif test -f $PGSQL_DIR/lib/libpq.so ; then PGSQL_LIBDIR=$PGSQL_DIR/lib else PGSQL_LIBDIR="" fi AC_MSG_RESULT($PGSQL_LIBDIR) elif test "${PGSQL_LIBS}" = "no"; then AC_MSG_CHECKING(for pgsql libs) AC_MSG_RESULT(skipped) else AC_MSG_CHECKING(for pgsql libs in $PGSQL_LIBS) # Manual detection for if test -f $PGSQL_LIBS/libpq.a -o -f $PGSQL_LIBS/libpq.so; then PGSQL_LIBDIR=$PGSQL_LIBS fi fi if test -z "$PGSQL_LIBDIR" ; then AC_MSG_RESULT(not found) echo "Invalid PostgreSQL directory [$PGSQL_LIBDIR] - unable to find libpq.a or libpq.so." else #LDFLAGS="$TEMP_LDFLAGS -L$PGSQL_LIBDIR" saved_CFLAGS=$CFLAGS saved_LIBS=$LIBS CFLAGS="-I$PGSQL_INCDIR" LIBS="-L$PGSQL_LIBDIR" AC_CHECK_LIB(pq, PQconnectStart, [ PGSQL_SUPPORT="yes" TEMP_LIBS="$TEMP_LIBS -L$PGSQL_LIBDIR -lpq" TEMP_CFLAGS="$TEMP_CFLAGS -I$PGSQL_INCDIR" AC_DEFINE([HAVE_PGSQL],1,[Define to 1 if you have PostgreSQL support]) ], AC_MSG_ERROR(PostgreSQL support can't build without PostgreSQL libraries)) LDFLAGS="" CFLAGS=$saved_CFLAGS LIBS=$saved_LIBS fi fi # end pgsql-include , pgsql-libs # end PostgreSQL detection fi #Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS(stdio.h unistd.h stdint.h fcntl.h time.h signal.h sys/ioctl.h sys/mman.h linux/videodev.h linux/videodev2.h sys/param.h sys/types.h sys/videoio.h) AC_CHECK_FUNCS(get_current_dir_name) # Check if v4l2 is available SUPPORTED_V4L2=false SUPPORTED_V4L2_old=false if test "${V4L}" = "no"; then AC_MSG_CHECKING(for V42L support) AC_MSG_RESULT(skipping) else AC_CHECK_TYPE([struct v4l2_buffer], [SUPPORTED_V4L2=true], [SUPPORTED_V4L2=false], [#include #ifdef HAVE_LINUX_VIDEODEV_H #include #elif HAVE_SYS_VIDEOIO_H #include #endif]) AC_MSG_CHECKING(for V42L support) if test x$SUPPORTED_V4L2 = xtrue; then AC_MSG_RESULT(yes) TEMP_CFLAGS="${TEMP_CFLAGS} -DMOTION_V4L2" else AC_MSG_RESULT(no) fi # linux/videodev.h doesn't include videodev2.h if test x$SUPPORTED_V4L2 = xfalse; then AC_MSG_CHECKING(for V42L *old* support) AC_MSG_RESULT(testing) AC_CHECK_HEADERS(linux/videodev2.h,[SUPPORTED_V4L2_old=true],[], [#include ]) fi if test x$SUPPORTED_V4L2_old = xtrue; then TEMP_CFLAGS="${TEMP_CFLAGS} -DMOTION_V4L2 -DMOTION_V4L2_OLD" SUPPORTED_V4L2=true 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" ) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST if test "${FreeBSD}" != ""; then OPTIMIZECPU="" fi if test "${OPTIMIZECPU}" = "yes"; then # Try to autodetect cpu type CPU_NAME="unknown" CPU_TYPE="unknown" if test -e "/proc/device-tree/model"; then # explicit test for RPI3 as /proc/cpuinfo reports armv7 even though # it is armv8 RPI3=`grep "Raspberry Pi 3 Model" /proc/device-tree/model` if test "x${RPI3}" != "x"; then CPU_NAME="Raspberry Pi 3" CPU_TYPE="Raspberry Pi 3" CPU_OPTIONS="-mcpu=cortex-a53 -mfpu=neon-fp-armv8" fi fi if test "x${CPU_TYPE}" = "xunknown" && 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[[56]]="-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" intel[[154]]="-march=pentium4" intel[[614]]="-march=prescott" intel[[628]]="-march=core2" 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[[510]]="-march=k6-2" amd[[59]]="-march=k6-3" amd[[513]]="-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[[610]]="-march=athlon" amd[[158]]="-march=athlon-xp" amd[[154]]="-march=k8" amd[[155]]="-march=athlon64" amd[[1543]]="-march=athlon64" amd[[1544]]="-march=athlon64" amd[[1565]]="-march=opteron" amd[[1572]]="-march=k8" via[[67]]="-march=c3" via[[68]]="-march=c3" via[[69]]="-march=i686" via[[610]]="-march=i686" 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_VENDOR}" = "xCentaurHauls"; then CPU_OPTIONS=${via[[$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" ;; sse3) CPU_FPU="-msse3" ;; ssse3) CPU_FPU="-mfpmath=sse -msse2 -mssse3" ;; 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" ;; "VIA C3 Ezra") CPU_OPTIONS="-march=c3" CPU_TYPE="known" ;; *) 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="-march=native -mtune=native" ;; esac fi fi echo "Detected CPU: $CPU_NAME" # 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}" = "x0" ; then echo "CPU optimization: $CPU_OPTIONS" else CPU_OPTIONS="" echo "No CPU optimizations will be added" fi else CPU_OPTIONS="" fi 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 -ggdb -g3" fi CFLAGS="${TEMP_CFLAGS} $UNAME_DEFS $CPU_OPTIONS" LIBS="${TEMP_LIBS}" LDFLAGS="${TEMP_LDFLAGS}" # # Add the right exec path for rc scripts # 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_CONFIG_FILES([ camera1-dist.conf camera2-dist.conf camera3-dist.conf camera4-dist.conf motion-dist.conf motion.init-FreeBSD.sh motion.init-Debian motion.service motion.spec Makefile ]) AC_OUTPUT echo "" echo " **************************" echo " Configure status " echo " ${PACKAGE_NAME} ${PACKAGE_VERSION}" 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_TURBO}" = "yes"; then echo "jpeg turbo support: Yes" elif 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 if test "${PWCBSD}" = "yes"; then echo "PWCBSD include: Yes" else echo "PWCBSD include: No" fi else if test "${V4L}" = "yes"; then echo "V4L support: Yes" else echo "V4L support: No" fi if test x$SUPPORTED_V4L2 = xtrue; then echo "V4L2 support: Yes" else echo "V4L2 support: No" fi fi if test "${SDL_SUPPORT}" = "yes"; then echo "SDL support: Yes" else echo "SDL support: No" fi if test "${HAVE_MMAL}" = "yes"; then echo "MMAL support: Yes" echo " ... MMAL_CFLAGS: $MMAL_CFLAGS" echo " ... MMAL_OBJ: $MMAL_OBJ" echo " ... MMAL_LIBS: $MMAL_LIBS" elif test "${WITHOUT_MMAL}" = "yes"; then echo "MMAL support: disabled" else echo "MMAL support: No" echo " ... libraspberrypi-dev package not installed" fi if test "${HAVE_FFMPEG}" = "yes"; then echo "FFmpeg support: Yes" echo " ... FFMPEG_CFLAGS: $FFMPEG_CFLAGS" echo " ... FFMPEG_LIBS: $FFMPEG_LIBS" else echo "FFmpeg support: No" fi if test "${SQLITE3_SUPPORT}" = "yes"; then echo "SQLite3 support: Yes" else echo "SQLite3 support: No" fi if test "${MYSQL_SUPPORT}" = "yes"; then echo "MYSQL support: Yes" else echo "MYSQL support: No" fi if test "${PGSQL_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 motion-release-4.0/copyright000066400000000000000000000030421300074623700162340ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: motion Source: http://motion.sourceforge.net/ License: GPL-2+ Files: * Copyright: 1999-2014 motion authors; see CREDITS for details License: GPL-2+ Files: md5.* Copyright: 1991-1992 RSA Data Security, Inc License: custom-RSA Files: netcam_wget.* Copyright: 1995-2002 Free Software Foundation, Inc. License: GPL-2+ Files: mmx.h Copyright: 1997-2001 H. Dietz and R. Fisher License: GPL-2+ Files: debian/* Copyright: 2000-2014 motion Debian packagers; see debian/changelog for details License: GPL-2+ License: GPL-2+ On Debian systems, the complete text of the GNU General Public License can be found in the file /usr/share/common-licenses/GPL-2'. License: custom-RSA License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. . License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. . RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. . These notices must be retained in any copies of any part of this documentation and/or software. motion-release-4.0/draw.c000066400000000000000000000633471300074623700154200ustar00rootroot00000000000000/* * 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" /** * draw_textn */ static int draw_textn(unsigned char *image, unsigned int startx, unsigned int starty, unsigned int width, const char *text, int len, unsigned 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 + 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++) { int pos_check = (int)text[pos]; char_ptr = char_arr_ptr[pos_check]; for (y = 8 * (factor + 1); y--;) { for (x = 7 * (factor + 1); x--;) { if (pos_check < 0) { image_ptr++; char_ptr++; continue; } 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; } /** * draw_text */ int draw_text(unsigned char *image, unsigned int startx, unsigned int starty, unsigned int width, const char *text, unsigned int factor) { int num_nl = 0; const 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; } /** * initialize_chars */ int initialize_chars(void) { unsigned int i, x, y; size_t draw_table_size; draw_table_size = sizeof(draw_table) / sizeof(struct draw_char); /* Fill the structure 'big_table' with double sized characters. */ for (i = 0; i < draw_table_size; i++) { 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]; } } /* 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 < draw_table_size; 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; } motion-release-4.0/event.c000066400000000000000000001062621300074623700155760ustar00rootroot00000000000000/* 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 "picture.h" /* already includes motion.h */ #include "event.h" #if (!defined(BSD)) #include "video.h" #endif /* Various functions (most doing the actual action) */ const char *eventList[] = { "NULL", "EVENT_FILECREATE", "EVENT_MOTION", "EVENT_FIRSTMOTION", "EVENT_ENDMOTION", "EVENT_STOP", "EVENT_TIMELAPSE", "EVENT_TIMELAPSEEND", "EVENT_STREAM", "EVENT_IMAGE_DETECTED", "EVENT_IMAGEM_DETECTED", "EVENT_IMAGE_SNAPSHOT", "EVENT_IMAGE", "EVENT_IMAGEM", "EVENT_FILECLOSE", "EVENT_DEBUG", "EVENT_CRITICAL", "EVENT_AREA_DETECTED", "EVENT_CAMERA_LOST", "EVENT_FFMPEG_PUT", "EVENT_SDL_PUT", "EVENT_LAST" }; /** * eventToString * * returns string label of the event */ /** * Future use debug / notification function static const char *eventToString(motion_event e) { return eventList[(int)e]; } */ /** * exec_command * 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->current_image->timestamp_tm, filename, filetype); if (!fork()) { int i; /* Detach from parent */ setsid(); /* * Close any file descriptor 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(ALR, TYPE_EVENTS, SHOW_ERRNO, "%s: Unable to start external command '%s'", stamp); exit(1); } MOTION_LOG(DBG, TYPE_EVENTS, NO_ERRNO, "%s: Executing external command '%s'", stamp); } /* * Event handlers */ static void event_newfile(struct context *cnt ATTRIBUTE_UNUSED, motion_event type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, char *filename, void *ftype, struct tm *tm ATTRIBUTE_UNUSED) { MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: File of type %ld saved to: %s", (unsigned long)ftype, filename); } static void event_beep(struct context *cnt, motion_event 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, motion_event 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, motion_event 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) || defined(HAVE_SQLITE3) static void event_sqlnewfile(struct context *cnt, motion_event 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.database_type) || (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]; mystrftime(cnt, sqlquery, sizeof(sqlquery), cnt->conf.sql_query, &cnt->current_image->timestamp_tm, filename, sqltype); #ifdef HAVE_MYSQL if (!strcmp(cnt->conf.database_type, "mysql")) { if (mysql_query(cnt->database, sqlquery) != 0) { int error_code = mysql_errno(cnt->database); MOTION_LOG(ERR, TYPE_DB, SHOW_ERRNO, "%s: Mysql query failed %s error code %d", mysql_error(cnt->database), error_code); /* Try to reconnect ONCE if fails continue and discard this sql query */ if (error_code >= 2000) { // Close connection before start a new connection mysql_close(cnt->database); cnt->database = mymalloc(sizeof(MYSQL)); mysql_init(cnt->database); if (!mysql_real_connect(cnt->database, cnt->conf.database_host, cnt->conf.database_user, cnt->conf.database_password, cnt->conf.database_dbname, 0, NULL, 0)) { MOTION_LOG(ALR, TYPE_DB, NO_ERRNO, "%s: Cannot reconnect to MySQL" " database %s on host %s with user %s MySQL error was %s", cnt->conf.database_dbname, cnt->conf.database_host, cnt->conf.database_user, mysql_error(cnt->database)); } else { MOTION_LOG(INF, TYPE_DB, NO_ERRNO, "%s: Re-Connection to Mysql database '%s' Succeed", cnt->conf.database_dbname); if (mysql_query(cnt->database, sqlquery) != 0) { int error_my = mysql_errno(cnt->database); MOTION_LOG(ERR, TYPE_DB, SHOW_ERRNO, "%s: after re-connection Mysql query failed %s error code %d", mysql_error(cnt->database), error_my); } } } } } #endif /* HAVE_MYSQL */ #ifdef HAVE_PGSQL if (!strcmp(cnt->conf.database_type, "postgresql")) { PGresult *res; res = PQexec(cnt->database_pg, sqlquery); if (PQstatus(cnt->database_pg) == CONNECTION_BAD) { MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: Connection to PostgreSQL database '%s' failed: %s", cnt->conf.database_dbname, PQerrorMessage(cnt->database_pg)); // This function will close the connection to the server and attempt to reestablish a new connection to the same server, // using all the same parameters previously used. This may be useful for error recovery if a working connection is lost PQreset(cnt->database_pg); if (PQstatus(cnt->database_pg) == CONNECTION_BAD) { MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: Re-Connection to PostgreSQL database '%s' failed: %s", cnt->conf.database_dbname, PQerrorMessage(cnt->database_pg)); } else { MOTION_LOG(INF, TYPE_DB, NO_ERRNO, "%s: Re-Connection to PostgreSQL database '%s' Succeed", cnt->conf.database_dbname); } } else if (PQresultStatus(res) != PGRES_COMMAND_OK) { MOTION_LOG(ERR, TYPE_DB, SHOW_ERRNO, "%s: PGSQL query [%s] failed", sqlquery); PQclear(res); } } #endif /* HAVE_PGSQL */ #ifdef HAVE_SQLITE3 if ((!strcmp(cnt->conf.database_type, "sqlite3")) && (cnt->conf.database_dbname)) { int res; char *errmsg = 0; res = sqlite3_exec(cnt->database_sqlite3, sqlquery, NULL, 0, &errmsg); if (res != SQLITE_OK ) { MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: SQLite error was %s", errmsg); sqlite3_free(errmsg); } } #endif /* HAVE_SQLITE3 */ } } #endif /* defined HAVE_MYSQL || defined HAVE_PGSQL || defined(HAVE_SQLITE3) */ static void on_area_command(struct context *cnt, motion_event 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_area_detected) exec_command(cnt, cnt->conf.on_area_detected, NULL, 0); } static void on_event_start_command(struct context *cnt, motion_event 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, motion_event 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_stream(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *dummy1 ATTRIBUTE_UNUSED, char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { if ((cnt->conf.stream_port) && (cnt->stream.socket != -1)) stream_stop(cnt); } static void event_stream_put(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { if (cnt->conf.stream_port) stream_put(cnt, img); } #ifdef HAVE_SDL static void event_sdl_put(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { sdl_put(img, cnt->imgs.width, cnt->imgs.height); } #endif #if defined(HAVE_LINUX_VIDEODEV_H) && !defined(WITHOUT_V4L) && !defined(BSD) static void event_vid_putpipe(struct context *cnt, motion_event 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(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: Failed to put image into video pipe"); } } #endif /* !WITHOUT_V4L && !BSD */ const char *imageext(struct context *cnt) { if (cnt->imgs.picture_type == IMAGE_TYPE_PPM) return "ppm"; return "jpg"; } static void event_image_detect(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *newimg, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) { char fullfilename[PATH_MAX]; char filename[PATH_MAX]; if (cnt->new_img & NEWIMG_ON) { const char *imagepath; /* * conf.imagepath 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.imagepath) imagepath = cnt->conf.imagepath; else imagepath = DEF_IMAGEPATH; mystrftime(cnt, filename, sizeof(filename), imagepath, currenttime_tm, NULL, 0); snprintf(fullfilename, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt)); put_picture(cnt, fullfilename, newimg, FTYPE_IMAGE); } } static void event_imagem_detect(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *newimg ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) { struct config *conf = &cnt->conf; char fullfilenamem[PATH_MAX]; char filename[PATH_MAX]; char filenamem[PATH_MAX]; if (conf->motion_img) { const char *imagepath; /* * conf.imagepath 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.imagepath) imagepath = cnt->conf.imagepath; else imagepath = DEF_IMAGEPATH; mystrftime(cnt, filename, sizeof(filename), imagepath, currenttime_tm, NULL, 0); /* motion images gets same name as normal images plus an appended 'm' */ snprintf(filenamem, PATH_MAX, "%sm", filename); snprintf(fullfilenamem, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filenamem, imageext(cnt)); put_picture(cnt, fullfilenamem, cnt->imgs.out, FTYPE_IMAGE_MOTION); } } static void event_image_snapshot(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) { char fullfilename[PATH_MAX]; char filename[PATH_MAX]; char filepath[PATH_MAX]; int offset = 0; int len = strlen(cnt->conf.snappath); if (len >= 9) offset = len - 8; if (strcmp(cnt->conf.snappath+offset, "lastsnap")) { 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); snprintf(filename, PATH_MAX, "%s.%s", filepath, imageext(cnt)); snprintf(fullfilename, PATH_MAX, "%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. */ snprintf(linkpath, PATH_MAX, "%s/lastsnap.%s", cnt->conf.filepath, imageext(cnt)); remove(linkpath); if (symlink(filename, linkpath)) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: Could not create symbolic link [%s]", filename); return; } } else { mystrftime(cnt, filepath, sizeof(filepath), cnt->conf.snappath, currenttime_tm, NULL, 0); snprintf(filename, PATH_MAX, "%s.%s", filepath, imageext(cnt)); snprintf(fullfilename, PATH_MAX, "%s/%s", cnt->conf.filepath, filename); remove(fullfilename); put_picture(cnt, fullfilename, img, FTYPE_IMAGE_SNAPSHOT); } cnt->snapshot = 0; } static void event_camera_lost(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *img ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm ATTRIBUTE_UNUSED) { if (cnt->conf.on_camera_lost) exec_command(cnt, cnt->conf.on_camera_lost, NULL, 0); } static void on_movie_end_command(struct context *cnt, motion_event 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_extpipe_end(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { if (cnt->extpipe_open) { cnt->extpipe_open = 0; fflush(cnt->extpipe); MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: CLOSING: extpipe file desc %d, error state %d", fileno(cnt->extpipe), ferror(cnt->extpipe)); MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: pclose return: %d", pclose(cnt->extpipe)); event(cnt, EVENT_FILECLOSE, NULL, cnt->extpipefilename, (void *)FTYPE_MPEG, NULL); } } static void event_create_extpipe(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *currenttime_tm) { if ((cnt->conf.useextpipe) && (cnt->conf.extpipe)) { char stamp[PATH_MAX] = ""; const char *moviepath; FILE *fd_dummy = NULL; /* * 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.moviepath) { moviepath = cnt->conf.moviepath; } else { moviepath = DEF_MOVIEPATH; MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: moviepath: %s", moviepath); } mystrftime(cnt, stamp, sizeof(stamp), moviepath, currenttime_tm, NULL, 0); snprintf(cnt->extpipefilename, PATH_MAX - 4, "%s/%s", cnt->conf.filepath, stamp); /* Open a dummy file to check if path is correct */ fd_dummy = myfopen(cnt->extpipefilename, "w"); /* TODO: trigger some warning instead of only log an error message */ if (fd_dummy == NULL) { /* Permission denied */ if (errno == EACCES) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: error opening file %s ..." "check access rights to target directory", cnt->extpipefilename); return ; } else { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: error opening file %s", cnt->extpipefilename); return ; } } myfclose(fd_dummy); unlink(cnt->extpipefilename); mystrftime(cnt, stamp, sizeof(stamp), cnt->conf.extpipe, currenttime_tm, cnt->extpipefilename, 0); MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: pipe: %s", stamp); MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: cnt->moviefps: %d", cnt->movie_fps); event(cnt, EVENT_FILECREATE, NULL, cnt->extpipefilename, (void *)FTYPE_MPEG, NULL); cnt->extpipe = popen(stamp, "w"); if (cnt->extpipe == NULL) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: popen failed"); return; } setbuf(cnt->extpipe, NULL); cnt->extpipe_open = 1; } } static void event_extpipe_put(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { /* Check use_extpipe enabled and ext_pipe not NULL */ if ((cnt->conf.useextpipe) && (cnt->extpipe != NULL)) { MOTION_LOG(DBG, TYPE_EVENTS, NO_ERRNO, "%s:"); /* Check that is open */ if ((cnt->extpipe_open) && (fileno(cnt->extpipe) > 0)) { if (!fwrite(img, cnt->imgs.size, 1, cnt->extpipe)) MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: Error writing in pipe , state error %d", ferror(cnt->extpipe)); } else { MOTION_LOG(ERR, TYPE_EVENTS, NO_ERRNO, "%s: pipe %s not created or closed already ", cnt->extpipe); } } } static void event_new_video(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *dummy ATTRIBUTE_UNUSED, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { cnt->movie_last_shot = -1; cnt->movie_fps = cnt->lastrate; MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s Source FPS %d", cnt->movie_fps); if (cnt->movie_fps < 2) cnt->movie_fps = 2; } #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 event_ffmpeg_newfile(struct context *cnt, motion_event 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; char stamp[PATH_MAX]; const char *moviepath; const char *codec; long codenbr; if (!cnt->conf.ffmpeg_output && !cnt->conf.ffmpeg_output_debug) 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.moviepath) moviepath = cnt->conf.moviepath; else moviepath = DEF_MOVIEPATH; mystrftime(cnt, stamp, sizeof(stamp), moviepath, currenttime_tm, NULL, 0); /* * motion movies get the same name as normal movies plus an appended 'm' * PATH_MAX - 4 to allow for .mpg to be appended without overflow */ /* The following section allows for testing of all the various containers * that Motion permits. The container type is pre-pended to the name of the * file so that we can determine which container type created what movie. * The intent for this is be used for developer testing when the ffmpeg libs * change or the code inside our ffmpeg module changes. For each event, the * container type will change. This way, you can turn on emulate motion, then * specify a maximum movie time and let Motion run for days creating all the * different types of movies checking for crashes, warnings, etc. */ codec = cnt->conf.ffmpeg_video_codec; if (strcmp(codec, "ogg") == 0) { MOTION_LOG(WRN, TYPE_ENCODER, NO_ERRNO, "%s The ogg container is no longer supported. Changing to mpeg4"); codec = "mpeg4"; } if (strcmp(codec, "test") == 0) { MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s Running test of the various output formats."); codenbr = cnt->event_nr % 10; switch (codenbr) { case 1: codec = "mpeg4"; break; case 2: codec = "msmpeg4"; break; case 3: codec = "swf"; break; case 4: codec = "flv"; break; case 5: codec = "ffv1"; break; case 6: codec = "mov"; break; case 7: codec = "mp4"; break; case 8: codec = "mkv"; break; case 9: codec = "hevc"; break; default: codec = "msmpeg4"; break; } snprintf(cnt->motionfilename, PATH_MAX - 4, "%s/%s_%sm", cnt->conf.filepath, codec, stamp); snprintf(cnt->newfilename, PATH_MAX - 4, "%s/%s_%s", cnt->conf.filepath, codec, stamp); } else { snprintf(cnt->motionfilename, PATH_MAX - 4, "%s/%sm", cnt->conf.filepath, stamp); snprintf(cnt->newfilename, PATH_MAX - 4, "%s/%s", cnt->conf.filepath, stamp); } if (cnt->conf.ffmpeg_output) { 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_output = ffmpeg_open(codec, cnt->newfilename, y, u, v, cnt->imgs.width, cnt->imgs.height, cnt->movie_fps, cnt->conf.ffmpeg_bps, cnt->conf.ffmpeg_vbr,TIMELAPSE_NONE)) == NULL) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: ffopen_open error creating (new) file [%s]", cnt->newfilename); cnt->finish = 1; return; } ((struct ffmpeg *)cnt->ffmpeg_output)->udata = convbuf; event(cnt, EVENT_FILECREATE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, NULL); } if (cnt->conf.ffmpeg_output_debug) { 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->ffmpeg_output_debug = ffmpeg_open(codec, cnt->motionfilename, y, u, v, cnt->imgs.width, cnt->imgs.height, cnt->movie_fps, cnt->conf.ffmpeg_bps, cnt->conf.ffmpeg_vbr,TIMELAPSE_NONE)) == NULL) { MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: ffopen_open error creating (motion) file [%s]", cnt->motionfilename); cnt->finish = 1; return; } cnt->ffmpeg_output_debug->udata = convbuf; event(cnt, EVENT_FILECREATE, NULL, cnt->motionfilename, (void *)FTYPE_MPEG_MOTION, NULL); } } static void event_ffmpeg_timelapse(struct context *cnt, motion_event 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; const char *codec_mpg = "mpg"; const char *codec_mpeg = "mpeg4"; /* * 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); /* PATH_MAX - 4 to allow for .mpg to be appended without overflow */ snprintf(cnt->timelapsefilename, PATH_MAX - 4, "%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 ((strcmp(cnt->conf.ffmpeg_video_codec,"mpg") == 0) || (strcmp(cnt->conf.ffmpeg_video_codec,"swf") == 0) ){ if (strcmp(cnt->conf.ffmpeg_video_codec,"swf") == 0) { MOTION_LOG(WRN, TYPE_EVENTS, NO_ERRNO, "%s: The swf container for timelapse no longer supported. Using mpg container."); } MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: Timelapse using mpg codec."); MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: Events will be appended to file"); cnt->ffmpeg_timelapse = ffmpeg_open(codec_mpg,cnt->timelapsefilename, y, u, v ,cnt->imgs.width, cnt->imgs.height, cnt->conf.frame_limit ,cnt->conf.ffmpeg_bps,cnt->conf.ffmpeg_vbr,TIMELAPSE_APPEND); } else { MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: Timelapse using mpeg4 codec."); MOTION_LOG(NTC, TYPE_EVENTS, NO_ERRNO, "%s: Events will be trigger new files"); cnt->ffmpeg_timelapse = ffmpeg_open(codec_mpeg ,cnt->timelapsefilename, y, u, v ,cnt->imgs.width, cnt->imgs.height, cnt->conf.frame_limit ,cnt->conf.ffmpeg_bps,cnt->conf.ffmpeg_vbr,TIMELAPSE_NEW); } if (cnt->ffmpeg_timelapse == NULL){ MOTION_LOG(ERR, TYPE_EVENTS, SHOW_ERRNO, "%s: ffopen_open error creating " "(timelapse) 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; if (ffmpeg_put_other_image(cnt->ffmpeg_timelapse, y, u, v) == -1) { cnt->finish = 1; cnt->restart = 0; } } static void event_ffmpeg_put(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *img, char *dummy1 ATTRIBUTE_UNUSED, void *dummy2 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { if (cnt->ffmpeg_output) { 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_output->udata; else u = y + (width * height); v = u + (width * height) / 4; if (ffmpeg_put_other_image(cnt->ffmpeg_output, y, u, v) == -1) { cnt->finish = 1; cnt->restart = 0; } } if (cnt->ffmpeg_output_debug) { if (ffmpeg_put_image(cnt->ffmpeg_output_debug) == -1) { cnt->finish = 1; cnt->restart = 0; } } } static void event_ffmpeg_closefile(struct context *cnt, motion_event type ATTRIBUTE_UNUSED, unsigned char *dummy1 ATTRIBUTE_UNUSED, char *dummy2 ATTRIBUTE_UNUSED, void *dummy3 ATTRIBUTE_UNUSED, struct tm *tm ATTRIBUTE_UNUSED) { if (cnt->ffmpeg_output) { free(cnt->ffmpeg_output->udata); ffmpeg_close(cnt->ffmpeg_output); cnt->ffmpeg_output = NULL; event(cnt, EVENT_FILECLOSE, NULL, cnt->newfilename, (void *)FTYPE_MPEG, NULL); } if (cnt->ffmpeg_output_debug) { free(cnt->ffmpeg_output_debug->udata); ffmpeg_close(cnt->ffmpeg_output_debug); cnt->ffmpeg_output_debug = NULL; event(cnt, EVENT_FILECLOSE, NULL, cnt->motionfilename, (void *)FTYPE_MPEG_MOTION, NULL); } } static void event_ffmpeg_timelapseend(struct context *cnt, motion_event 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) { 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 { motion_event type; event_handler handler; }; struct event_handlers event_handlers[] = { #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) { 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_AREA_DETECTED, on_area_command }, { EVENT_FIRSTMOTION, on_event_start_command }, { EVENT_ENDMOTION, on_event_end_command }, { EVENT_IMAGE_DETECTED, event_image_detect }, { EVENT_IMAGEM_DETECTED, event_imagem_detect }, { EVENT_IMAGE_SNAPSHOT, event_image_snapshot }, #ifdef HAVE_SDL { EVENT_SDL_PUT, event_sdl_put }, #endif #if defined(HAVE_LINUX_VIDEODEV_H) && !defined(WITHOUT_V4L) && !defined(BSD) { EVENT_IMAGE, event_vid_putpipe }, { EVENT_IMAGEM, event_vid_putpipe }, #endif /* !WITHOUT_V4L && !BSD */ { EVENT_STREAM, event_stream_put }, { EVENT_FIRSTMOTION, event_new_video }, #ifdef HAVE_FFMPEG { EVENT_FIRSTMOTION, event_ffmpeg_newfile }, { EVENT_IMAGE_DETECTED, event_ffmpeg_put }, { EVENT_FFMPEG_PUT, event_ffmpeg_put }, { EVENT_ENDMOTION, event_ffmpeg_closefile }, { EVENT_TIMELAPSE, event_ffmpeg_timelapse }, { EVENT_TIMELAPSEEND, event_ffmpeg_timelapseend }, #endif /* HAVE_FFMPEG */ { EVENT_FILECLOSE, on_movie_end_command }, { EVENT_FIRSTMOTION, event_create_extpipe }, { EVENT_IMAGE_DETECTED, event_extpipe_put }, { EVENT_FFMPEG_PUT, event_extpipe_put }, { EVENT_ENDMOTION, event_extpipe_end }, { EVENT_CAMERA_LOST, event_camera_lost }, { EVENT_STOP, event_stop_stream }, {0, NULL} }; /** * event * 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, motion_event 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); } } motion-release-4.0/event.h000066400000000000000000000020121300074623700155670ustar00rootroot00000000000000/* * 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_ typedef enum { EVENT_FILECREATE = 1, EVENT_MOTION, EVENT_FIRSTMOTION, EVENT_ENDMOTION, EVENT_STOP, EVENT_TIMELAPSE, EVENT_TIMELAPSEEND, EVENT_STREAM, EVENT_IMAGE_DETECTED, EVENT_IMAGEM_DETECTED, EVENT_IMAGE_SNAPSHOT, EVENT_IMAGE, EVENT_IMAGEM, EVENT_FILECLOSE, EVENT_DEBUG, EVENT_CRITICAL, EVENT_AREA_DETECTED, EVENT_CAMERA_LOST, EVENT_FFMPEG_PUT, EVENT_SDL_PUT, EVENT_LAST, } motion_event; typedef void(* event_handler)(struct context *, motion_event, unsigned char *, char *, void *, struct tm *); void event(struct context *, motion_event, unsigned char *, char *, void *, struct tm *); const char * imageext(struct context *); #endif /* _INCLUDE_EVENT_H_ */ motion-release-4.0/ffmpeg-0.4.8-macosx.patch000066400000000000000000000036031300074623700205260ustar00rootroot00000000000000diff -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: motion-release-4.0/ffmpeg.c000066400000000000000000000712751300074623700157260ustar00rootroot00000000000000/* * * 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. * * This file has been modified so that only major versions greater than * 53 are supported. * Note that while the conditions are based upon LIBAVFORMAT, not all of the changes are * specific to libavformat.h. Some changes could be related to other components of ffmpeg. * This is for simplicity. The avformat version has historically changed at the same time * as the other components so it is easier to have a single version number to track rather * than the particular version numbers which are associated with each component. * The libav variant also has different apis with the same major/minor version numbers. * As such, it is occasionally necessary to look at the microversion number. Numbers * greater than 100 for micro version indicate ffmpeg whereas numbers less than 100 * indicate libav */ #include "config.h" #ifdef HAVE_FFMPEG #include "ffmpeg.h" #include "motion.h" #define AVSTREAM_CODEC_PTR(avs_ptr) (avs_ptr->codec) /**************************************************************************** * The section below is the "my" section of functions. * These are designed to be extremely simple version specific * variants of the libav functions. ****************************************************************************/ #if (LIBAVFORMAT_VERSION_MAJOR >= 55) || ((LIBAVFORMAT_VERSION_MAJOR == 54) && (LIBAVFORMAT_VERSION_MINOR > 6)) #define MY_FLAG_READ AVIO_FLAG_READ #define MY_FLAG_WRITE AVIO_FLAG_WRITE #define MY_FLAG_READ_WRITE AVIO_FLAG_READ_WRITE #else //Older versions #define MY_FLAG_READ URL_RDONLY #define MY_FLAG_WRITE URL_WRONLY #define MY_FLAG_READ_WRITE URL_RDWR #endif /*********************************************/ #if (LIBAVFORMAT_VERSION_MAJOR >= 56) #define MY_CODEC_ID_MSMPEG4V2 AV_CODEC_ID_MSMPEG4V2 #define MY_CODEC_ID_FLV1 AV_CODEC_ID_FLV1 #define MY_CODEC_ID_FFV1 AV_CODEC_ID_FFV1 #define MY_CODEC_ID_NONE AV_CODEC_ID_NONE #define MY_CODEC_ID_MPEG2VIDEO AV_CODEC_ID_MPEG2VIDEO #define MY_CODEC_ID_H264 AV_CODEC_ID_H264 #define MY_CODEC_ID_HEVC AV_CODEC_ID_HEVC #else #define MY_CODEC_ID_MSMPEG4V2 CODEC_ID_MSMPEG4V2 #define MY_CODEC_ID_FLV1 CODEC_ID_FLV1 #define MY_CODEC_ID_FFV1 CODEC_ID_FFV1 #define MY_CODEC_ID_NONE CODEC_ID_NONE #define MY_CODEC_ID_MPEG2VIDEO CODEC_ID_MPEG2VIDEO #define MY_CODEC_ID_H264 CODEC_ID_H264 #define MY_CODEC_ID_HEVC CODEC_ID_H264 #endif /*********************************************/ AVFrame *my_frame_alloc(void){ AVFrame *pic; #if (LIBAVFORMAT_VERSION_MAJOR >= 55) pic = av_frame_alloc(); #else pic = avcodec_alloc_frame(); #endif return pic; } /*********************************************/ void my_frame_free(AVFrame *frame){ #if (LIBAVFORMAT_VERSION_MAJOR >= 55) av_frame_free(&frame); #else av_freep(&frame); #endif } /*********************************************/ int my_image_get_buffer_size(enum MyPixelFormat pix_fmt, int width, int height){ int retcd = 0; #if (LIBAVFORMAT_VERSION_MAJOR >= 57) int align = 1; retcd = av_image_get_buffer_size(pix_fmt, width, height, align); #else retcd = avpicture_get_size(pix_fmt, width, height); #endif return retcd; } /*********************************************/ int my_image_copy_to_buffer(AVFrame *frame, uint8_t *buffer_ptr, enum MyPixelFormat pix_fmt,int width, int height,int dest_size){ int retcd = 0; #if (LIBAVFORMAT_VERSION_MAJOR >= 57) int align = 1; retcd = av_image_copy_to_buffer((uint8_t *)buffer_ptr,dest_size ,(const uint8_t * const*)frame,frame->linesize,pix_fmt,width,height,align); #else retcd = avpicture_layout((const AVPicture*)frame,pix_fmt,width,height ,(unsigned char *)buffer_ptr,dest_size); #endif return retcd; } /*********************************************/ int my_image_fill_arrays(AVFrame *frame,uint8_t *buffer_ptr,enum MyPixelFormat pix_fmt,int width,int height){ int retcd = 0; #if (LIBAVFORMAT_VERSION_MAJOR >= 57) int align = 1; retcd = av_image_fill_arrays( frame->data ,frame->linesize ,buffer_ptr ,pix_fmt ,width ,height ,align ); #else retcd = avpicture_fill( (AVPicture *)frame ,buffer_ptr ,pix_fmt ,width ,height); #endif return retcd; } /*********************************************/ void my_packet_unref(AVPacket pkt){ #if (LIBAVFORMAT_VERSION_MAJOR >= 57) av_packet_unref(&pkt); #else av_free_packet(&pkt); #endif } /*********************************************/ /**************************************************************************** **************************************************************************** ****************************************************************************/ /** * timelapse_exists * Determines whether the timelapse file exists * * Returns * 0: File doesn't exist * 1: File exists */ static int timelapse_exists(const char *fname){ FILE *file; file = fopen(fname, "r"); if (file) { fclose(file); return 1; } return 0; } static int timelapse_append(struct ffmpeg *ffmpeg, AVPacket pkt){ FILE *file; file = fopen(ffmpeg->oc->filename, "a"); if (!file) return -1; fwrite(pkt.data,1,pkt.size,file); fclose(file); return 0; } /** locking callback for use with ffmpeg's av_lockmgr_register */ static int ffmpeg_lockmgr_cb(void **arg, enum AVLockOp op) { pthread_mutex_t *mutex = *arg; int err; switch (op) { case AV_LOCK_CREATE: mutex = malloc(sizeof(*mutex)); if (!mutex) return AVERROR(ENOMEM); if ((err = pthread_mutex_init(mutex, NULL))) { free(mutex); return AVERROR(err); } *arg = mutex; return 0; case AV_LOCK_OBTAIN: if ((err = pthread_mutex_lock(mutex))) return AVERROR(err); return 0; case AV_LOCK_RELEASE: if ((err = pthread_mutex_unlock(mutex))) return AVERROR(err); return 0; case AV_LOCK_DESTROY: if (mutex) pthread_mutex_destroy(mutex); free(mutex); *arg = NULL; return 0; } return 1; } /** * ffmpeg_init * Initializes for libavformat. * * Returns * Function returns nothing. */ void ffmpeg_init(void){ int ret; MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s: ffmpeg libavcodec version %d.%d.%d" " libavformat version %d.%d.%d" , LIBAVCODEC_VERSION_MAJOR, LIBAVCODEC_VERSION_MINOR, LIBAVCODEC_VERSION_MICRO , LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO); av_register_all(); avcodec_register_all(); avformat_network_init(); av_log_set_callback((void *)ffmpeg_avcodec_log); ret = av_lockmgr_register(ffmpeg_lockmgr_cb); if (ret < 0) { MOTION_LOG(EMG, TYPE_ALL, SHOW_ERRNO, "%s: av_lockmgr_register failed (%d)", ret); exit(1); } } void ffmpeg_finalise(void) { avformat_network_deinit(); } /** * get_oformat * 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. * * Returns * AVOutputFormat pointer or NULL if any error happens. */ 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. */ if (strcmp(codec, "tlapse") == 0) { ext = ".mpg"; of = av_guess_format ("mpeg2video", NULL, NULL); if (of) of->video_codec = MY_CODEC_ID_MPEG2VIDEO; } else if (strcmp(codec, "mpeg4") == 0) { ext = ".avi"; of = av_guess_format("avi", NULL, NULL); } else if (strcmp(codec, "msmpeg4") == 0) { ext = ".avi"; of = av_guess_format("avi", NULL, NULL); /* Manually override the codec id. */ if (of) of->video_codec = MY_CODEC_ID_MSMPEG4V2; } else if (strcmp(codec, "swf") == 0) { ext = ".swf"; of = av_guess_format("swf", NULL, NULL); } else if (strcmp(codec, "flv") == 0) { ext = ".flv"; of = av_guess_format("flv", NULL, NULL); of->video_codec = MY_CODEC_ID_FLV1; } else if (strcmp(codec, "ffv1") == 0) { ext = ".avi"; of = av_guess_format("avi", NULL, NULL); if (of) of->video_codec = MY_CODEC_ID_FFV1; } else if (strcmp(codec, "mov") == 0) { ext = ".mov"; of = av_guess_format("mov", NULL, NULL); } else if (strcmp (codec, "mp4") == 0){ ext = ".mp4"; of = av_guess_format ("mp4", NULL, NULL); of->video_codec = MY_CODEC_ID_H264; } else if (strcmp (codec, "mkv") == 0){ ext = ".mkv"; of = av_guess_format ("matroska", NULL, NULL); of->video_codec = MY_CODEC_ID_H264; } else if (strcmp (codec, "hevc") == 0){ ext = ".mp4"; of = av_guess_format ("mp4", NULL, NULL); of->video_codec = MY_CODEC_ID_HEVC; } else { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: ffmpeg_video_codec option value" " %s is not supported", codec); return NULL; } if (!of) { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not guess format for %s", codec); return NULL; } /* The 4 allows for ".avi" or ".mpg" to be appended. */ strncat(filename, ext, 4); return of; } /** * ffmpeg_open * 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. * * Returns * A new allocated ffmpeg struct or NULL if any error happens. */ struct ffmpeg *ffmpeg_open(const 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, int tlapse) { AVCodecContext *c; AVCodec *codec; struct ffmpeg *ffmpeg; int retcd; char errstr[128]; AVDictionary *opts = 0; /* * Allocate space for our ffmpeg structure. This structure contains all the * codec and image information we need to generate movies. */ ffmpeg = mymalloc(sizeof(struct ffmpeg)); ffmpeg->tlapse = tlapse; /* 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 = avformat_alloc_context(); if (!ffmpeg->oc) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Could not allocate output context"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Setup output format */ if (ffmpeg->tlapse == TIMELAPSE_APPEND){ ffmpeg->oc->oformat = get_oformat("tlapse", filename); } else { 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); ffmpeg->video_st = NULL; if (ffmpeg->oc->oformat->video_codec != MY_CODEC_ID_NONE) { codec = avcodec_find_encoder(ffmpeg->oc->oformat->video_codec); if (!codec) { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Codec %s not found", ffmpeg_video_codec); ffmpeg_cleanups(ffmpeg); return NULL; } ffmpeg->video_st = avformat_new_stream(ffmpeg->oc, codec); if (!ffmpeg->video_st) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Could not alloc stream"); ffmpeg_cleanups(ffmpeg); return NULL; } } else { /* We did not get a proper video codec. */ MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not get the codec"); ffmpeg_cleanups(ffmpeg); return NULL; } /* Only the newer codec and containers can handle the really fast FPS */ if (((strcmp(ffmpeg_video_codec, "msmpeg4") == 0) || (strcmp(ffmpeg_video_codec, "mpeg4") == 0) || (strcmp(ffmpeg_video_codec, "swf") == 0) ) && (rate >100)){ MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s The frame rate specified is too high for the ffmpeg movie type specified. Choose a different ffmpeg container or lower framerate. "); 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 = AVMEDIA_TYPE_VIDEO; c->bit_rate = bps; c->width = width; c->height = height; c->time_base.num = 1; c->time_base.den = rate; c->gop_size = 12; c->pix_fmt = MY_PIX_FMT_YUV420P; c->max_b_frames = 0; if (vbr > 100) vbr = 100; if (c->codec_id == MY_CODEC_ID_H264 || c->codec_id == MY_CODEC_ID_HEVC){ if (vbr > 0) { ffmpeg->vbr = (int)(( (100-vbr) * 51)/100); } else { ffmpeg->vbr = 28; } av_dict_set(&opts, "preset", "ultrafast", 0); char crf[4]; snprintf(crf, 4, "%d",ffmpeg->vbr); av_dict_set(&opts, "crf", crf, 0); av_dict_set(&opts, "tune", "zerolatency", 0); } else { /* The selection of 8000 in the else is a subjective number based upon viewing output files */ if (vbr > 0){ ffmpeg->vbr =(int)(((100-vbr)*(100-vbr)*(100-vbr) * 8000) / 1000000) + 1; c->flags |= CODEC_FLAG_QSCALE; c->global_quality=ffmpeg->vbr; } } MOTION_LOG(INF, TYPE_ENCODER, NO_ERRNO, "%s vbr/crf for codec: %d", ffmpeg->vbr); if (strcmp(ffmpeg_video_codec, "ffv1") == 0) c->strict_std_compliance = -2; c->flags |= CODEC_FLAG_GLOBAL_HEADER; retcd = avcodec_open2(c, codec, &opts); if (retcd < 0) { if (codec->supported_framerates) { const AVRational *fps = codec->supported_framerates; while (fps->num) { MOTION_LOG(INF, TYPE_ENCODER, NO_ERRNO, "%s Reported FPS Supported %d/%d", fps->num, fps->den); fps++; } } int chkrate = 1; while ((chkrate < 36) && (retcd != 0)) { c->time_base.den = chkrate; retcd = avcodec_open2(c, codec, &opts); chkrate++; } if (retcd < 0){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not open codec %s",errstr); av_dict_free(&opts); ffmpeg_cleanups(ffmpeg); return NULL; } } av_dict_free(&opts); MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s Selected Output FPS %d", c->time_base.den); ffmpeg->last_pts = 0; ffmpeg->video_st->time_base.num = 1; ffmpeg->video_st->time_base.den = 1000; if ((strcmp(ffmpeg_video_codec, "swf") == 0) || (ffmpeg->tlapse != TIMELAPSE_NONE) ) { ffmpeg->video_st->time_base.num = 1; ffmpeg->video_st->time_base.den = rate; if ((rate > 50) && (strcmp(ffmpeg_video_codec, "swf") == 0)){ MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s The FPS could be too high for the SWF container. Consider other choices."); } } ffmpeg->video_outbuf = NULL; if (!(ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE)) { ffmpeg->video_outbuf_size = ffmpeg->c->width * 512; ffmpeg->video_outbuf = mymalloc(ffmpeg->video_outbuf_size); } ffmpeg->picture = my_frame_alloc(); if (!ffmpeg->picture) { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: could not alloc frame"); ffmpeg_cleanups(ffmpeg); return NULL; } /* 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 ((timelapse_exists(filename) == 0) || (ffmpeg->tlapse != TIMELAPSE_APPEND)) { if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { if (avio_open(&ffmpeg->oc->pb, filename, MY_FLAG_WRITE) < 0) { if (errno == ENOENT) { if (create_path(filename) == -1) { ffmpeg_cleanups(ffmpeg); return NULL; } if (avio_open(&ffmpeg->oc->pb, filename, MY_FLAG_WRITE) < 0) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: error opening file %s", filename); ffmpeg_cleanups(ffmpeg); return NULL; } /* Permission denied */ } else if (errno == EACCES) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO,"%s: Permission denied. %s",filename); ffmpeg_cleanups(ffmpeg); return NULL; } else { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error opening file %s", filename); ffmpeg_cleanups(ffmpeg); return NULL; } } } gettimeofday(&ffmpeg->start_time, NULL); /* Write the stream header, For the TIMELAPSE_APPEND * we write the data via standard file I/O so we close the * items here */ retcd = avformat_write_header(ffmpeg->oc, NULL); if (retcd < 0){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Could not write ffmpeg header %s",errstr); ffmpeg_cleanups(ffmpeg); return NULL; } if (ffmpeg->tlapse == TIMELAPSE_APPEND) { av_write_trailer(ffmpeg->oc); avio_close(ffmpeg->oc->pb); } } return ffmpeg; } /** * ffmpeg_cleanups * Clean up ffmpeg struct if something was wrong. * * Returns * Function returns nothing. */ void ffmpeg_cleanups(struct ffmpeg *ffmpeg){ /* Close each codec */ if (ffmpeg->video_st) { avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); } free(ffmpeg->video_outbuf); av_freep(&ffmpeg->picture); avformat_free_context(ffmpeg->oc); free(ffmpeg); } /** * ffmpeg_close * Closes a video file. * * Returns * Function returns nothing. */ void ffmpeg_close(struct ffmpeg *ffmpeg){ if (ffmpeg->tlapse != TIMELAPSE_APPEND) { av_write_trailer(ffmpeg->oc); } /* Close each codec */ if (ffmpeg->video_st) { avcodec_close(AVSTREAM_CODEC_PTR(ffmpeg->video_st)); } av_freep(&ffmpeg->picture); free(ffmpeg->video_outbuf); if (!(ffmpeg->oc->oformat->flags & AVFMT_NOFILE)) { if (ffmpeg->tlapse != TIMELAPSE_APPEND) { avio_close(ffmpeg->oc->pb); } } avformat_free_context(ffmpeg->oc); free(ffmpeg); } /** * ffmpeg_put_image * Puts the image pointed to by ffmpeg->picture. * * Returns * value returned by ffmpeg_put_frame call. */ int ffmpeg_put_image(struct ffmpeg *ffmpeg){ /* A return code of -2 is thrown by the put_frame * when a image is buffered. For timelapse, we absolutely * never want a frame buffered so we keep sending back the * the same pic until it flushes or fails in a different way */ int retcd; int cnt = 0; retcd = ffmpeg_put_frame(ffmpeg, ffmpeg->picture); while ((retcd == -2) && (ffmpeg->tlapse != TIMELAPSE_NONE)) { retcd = ffmpeg_put_frame(ffmpeg, ffmpeg->picture); cnt++; if (cnt > 50){ MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Excessive attempts to clear buffered packet"); retcd = -1; } } //non timelapse buffered is ok if (retcd == -2){ retcd = 0; MOTION_LOG(DBG, TYPE_ENCODER, NO_ERRNO, "%s: Buffered packet"); } return retcd; } /** * ffmpeg_put_other_image * Puts an arbitrary picture defined by y, u and v. * * Returns * Number of bytes written by ffmpeg_put_frame * -1 if any error happens in ffmpeg_put_frame * 0 if error allocating picture. */ int ffmpeg_put_other_image(struct ffmpeg *ffmpeg, unsigned char *y, unsigned char *u, unsigned char *v){ AVFrame *picture; int retcd = 0; int cnt = 0; /* Allocate the encoded raw picture. */ picture = ffmpeg_prepare_frame(ffmpeg, y, u, v); if (picture) { /* A return code of -2 is thrown by the put_frame * when a image is buffered. For timelapse, we absolutely * never want a frame buffered so we keep sending back the * the same pic until it flushes or fails in a different way */ retcd = ffmpeg_put_frame(ffmpeg, picture); while ((retcd == -2) && (ffmpeg->tlapse != TIMELAPSE_NONE)) { retcd = ffmpeg_put_frame(ffmpeg, picture); cnt++; if (cnt > 50){ MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: Excessive attempts to clear buffered packet"); retcd = -1; } } //non timelapse buffered is ok if (retcd == -2){ retcd = 0; MOTION_LOG(DBG, TYPE_ENCODER, NO_ERRNO, "%s: Buffered packet"); } av_free(picture); } return retcd; } /** * ffmpeg_put_frame * 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. * * Returns * Number of bytes written or -1 if any error happens. */ int ffmpeg_put_frame(struct ffmpeg *ffmpeg, AVFrame *pic){ /** * Since the logic,return values and conditions changed so * dramatically between versions, the encoding of the frame * is 100% blocked based upon Libav/FFMpeg version */ #if (LIBAVFORMAT_VERSION_MAJOR >= 55) || ((LIBAVFORMAT_VERSION_MAJOR == 54) && (LIBAVFORMAT_VERSION_MINOR > 6)) int retcd; int got_packet_ptr; AVPacket pkt; char errstr[128]; struct timeval tv1; int64_t pts_interval; gettimeofday(&tv1, NULL); av_init_packet(&pkt); /* Init static structure. */ if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { pkt.stream_index = ffmpeg->video_st->index; pkt.flags |= AV_PKT_FLAG_KEY; pkt.data = (uint8_t *)pic; pkt.size = sizeof(AVPicture); } else { pkt.data = NULL; pkt.size = 0; retcd = avcodec_encode_video2(AVSTREAM_CODEC_PTR(ffmpeg->video_st), &pkt, pic, &got_packet_ptr); if (retcd < 0 ){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error encoding video:%s",errstr); //Packet is freed upon failure of encoding return -1; } if (got_packet_ptr == 0){ //Buffered packet. Throw special return code my_packet_unref(pkt); return -2; } } if (ffmpeg->tlapse == TIMELAPSE_APPEND) { retcd = timelapse_append(ffmpeg, pkt); } else if (ffmpeg->tlapse == TIMELAPSE_NEW) { retcd = av_write_frame(ffmpeg->oc, &pkt); } else { pts_interval = ((1000000L * (tv1.tv_sec - ffmpeg->start_time.tv_sec)) + tv1.tv_usec - ffmpeg->start_time.tv_usec) + 10000; pkt.pts = av_rescale_q(pts_interval,(AVRational){1, 1000000L},ffmpeg->video_st->time_base); if (pkt.pts <= ffmpeg->last_pts) pkt.pts = ffmpeg->last_pts + 1; pkt.dts = pkt.pts; retcd = av_write_frame(ffmpeg->oc, &pkt); ffmpeg->last_pts = pkt.pts; } // MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: pts:%d dts:%d stream:%d interval %d",pkt.pts,pkt.dts,ffmpeg->video_st->time_base.den,pts_interval); my_packet_unref(pkt); if (retcd != 0) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error while writing video frame"); ffmpeg_cleanups(ffmpeg); return -1; } return retcd; #else // Old versions of Libav/FFmpeg int retcd; AVPacket pkt; av_init_packet(&pkt); /* Init static structure. */ pkt.stream_index = ffmpeg->video_st->index; if (ffmpeg->oc->oformat->flags & AVFMT_RAWPICTURE) { // Raw video case. pkt.size = sizeof(AVPicture); pkt.data = (uint8_t *)pic; pkt.flags |= AV_PKT_FLAG_KEY; } else { retcd = avcodec_encode_video(AVSTREAM_CODEC_PTR(ffmpeg->video_st), ffmpeg->video_outbuf, ffmpeg->video_outbuf_size, pic); if (retcd < 0 ){ MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error encoding video"); my_packet_unref(pkt); return -1; } if (retcd == 0 ){ // No bytes encoded => buffered=>special handling my_packet_unref(pkt); return -2; } pkt.size = retcd; pkt.data = ffmpeg->video_outbuf; pkt.pts = AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->pts; if (AVSTREAM_CODEC_PTR(ffmpeg->video_st)->coded_frame->key_frame) pkt.flags |= AV_PKT_FLAG_KEY; } if (ffmpeg->tlapse == TIMELAPSE_APPEND) { retcd = timelapse_append(ffmpeg, pkt); } else { retcd = av_write_frame(ffmpeg->oc, &pkt); } my_packet_unref(pkt); if (retcd != 0) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: Error while writing video frame"); ffmpeg_cleanups(ffmpeg); return -1; } return retcd; #endif } /** * ffmpeg_prepare_frame * 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 = my_frame_alloc(); if (!picture) { MOTION_LOG(ERR, TYPE_ENCODER, SHOW_ERRNO, "%s: 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; picture->format = ffmpeg->c->pix_fmt; picture->width = ffmpeg->c->width; picture->height = ffmpeg->c->height; return picture; } /** * ffmpeg_avcodec_log * Handle any logging output from the ffmpeg library avcodec. * * Parameters * *ignoreme A pointer we will ignore * errno_flag The error number value * fmt Text message to be used for log entry in printf() format. * ap List of variables to be used in formatted message text. * * Returns * Function returns nothing. */ void ffmpeg_avcodec_log(void *ignoreme ATTRIBUTE_UNUSED, int errno_flag, const char *fmt, va_list vl) { char buf[1024]; char *end; /* Flatten the message coming in from avcodec. */ vsnprintf(buf, sizeof(buf), fmt, vl); end = buf + strlen(buf); if (end > buf && end[-1] == '\n') { *--end = 0; } /* If the debug_level is correct then send the message to the motion logging routine. * While it is not really desired to look for specific text in the message, there does * not seem another option. The specific messages indicated are lost camera which we * have our own message and UE golomb is not something that is possible for us to fix. * It is caused by the stream sent from the source camera */ if(strstr(buf, "No route to host") == NULL){ if (strstr(buf, "Invalid UE golomb") != NULL) { MOTION_LOG(DBG, TYPE_ENCODER, NO_ERRNO, "%s: %s", buf); } else if (errno_flag <= AV_LOG_ERROR) { MOTION_LOG(ERR, TYPE_ENCODER, NO_ERRNO, "%s: %s", buf); } else if (errno_flag <= AV_LOG_WARNING) { MOTION_LOG(NTC, TYPE_ENCODER, NO_ERRNO, "%s: %s", buf); } else if (errno_flag < AV_LOG_DEBUG){ MOTION_LOG(INF, TYPE_ENCODER, NO_ERRNO, "%s: %s", buf); } } } #endif /* HAVE_FFMPEG */ motion-release-4.0/ffmpeg.h000066400000000000000000000060431300074623700157220ustar00rootroot00000000000000#ifndef _INCLUDE_FFMPEG_H_ #define _INCLUDE_FFMPEG_H_ #include #include #include #include "config.h" #ifdef HAVE_FFMPEG #include #include #include #include #if (LIBAVFORMAT_VERSION_MAJOR >= 56) #define MY_PIX_FMT_YUV420P AV_PIX_FMT_YUV420P #define MY_PIX_FMT_YUVJ420P AV_PIX_FMT_YUVJ420P #define MyPixelFormat AVPixelFormat #else #define MY_PIX_FMT_YUV420P PIX_FMT_YUV420P #define MY_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P #define MyPixelFormat PixelFormat #endif #endif /* HAVE_FFMPEG */ #define TIMELAPSE_NONE 0 /* No timelapse, regular processing */ #define TIMELAPSE_APPEND 1 /* Use append version of timelapse */ #define TIMELAPSE_NEW 2 /* Use create new file version of timelapse */ struct ffmpeg { #ifdef HAVE_FFMPEG AVFormatContext *oc; AVStream *video_st; AVCodecContext *c; AVFrame *picture; /* contains default image 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 */ int tlapse; int64_t last_pts; struct timeval start_time; #else int dummy; #endif }; /* Initialize FFmpeg stuff. Needs to be called before ffmpeg_open. */ void ffmpeg_init(void); /** Finalise ffmpeg; call only after all threads have finished */ void ffmpeg_finalise(void); struct ffmpeg *ffmpeg_open( const 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 */ int tlapse ); /* Puts the image pointed to by the picture member of struct ffmpeg. */ int ffmpeg_put_image(struct ffmpeg *); /* Puts the image defined by u, y and v (YUV420 format). */ int 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 *); /* Setup an avcodec log handler. */ void ffmpeg_avcodec_log(void *, int, const char *, va_list); #ifdef HAVE_FFMPEG AVFrame *my_frame_alloc(void); void my_frame_free(AVFrame *frame); int ffmpeg_put_frame(struct ffmpeg *, AVFrame *); void ffmpeg_cleanups(struct ffmpeg *); AVFrame *ffmpeg_prepare_frame(struct ffmpeg *, unsigned char *, unsigned char *, unsigned char *); int my_image_get_buffer_size(enum MyPixelFormat pix_fmt, int width, int height); int my_image_copy_to_buffer(AVFrame *frame,uint8_t *buffer_ptr,enum MyPixelFormat pix_fmt,int width,int height,int dest_size); int my_image_fill_arrays(AVFrame *frame,uint8_t *buffer_ptr,enum MyPixelFormat pix_fmt,int width,int height); void my_packet_unref(AVPacket pkt); #endif #endif /* _INCLUDE_FFMPEG_H_ */ motion-release-4.0/git-commit-version.sh000077500000000000000000000001171300074623700203740ustar00rootroot00000000000000#!/bin/sh SNV_VERSION=`git show -s --format=%h` echo -n "4.0+git$SNV_VERSION" motion-release-4.0/jpegutils.c000066400000000000000000001224371300074623700164650ustar00rootroot00000000000000/* * jpegutils.c: Some Utility programs for dealing with * JPEG encoded images * * Copyright (C) 1999 Rainer Johanni * Copyright (C) 2001 pHilipp Zabel * Copyright (C) 2008 Angel Carpintero * * based on jdatasrc.c and jdatadst.c from the Independent * JPEG Group's software by Thomas G. Lane * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "motion.h" #include "jpegutils.h" #include #include #include #include /* * jpeg_data: buffer with input / output jpeg * len: Length of jpeg buffer * itype: 0: Not interlaced * 1: Interlaced, Top field first * 2: Interlaced, Bottom field first * ctype Chroma format for decompression. * Currently only Y4M_CHROMA_{420JPEG,422} are available * raw0 buffer with input / output raw Y channel * raw1 buffer with input / output raw U/Cb channel * raw2 buffer with input / output raw V/Cr channel * width width of Y channel (width of U/V is width/2) * height height of Y channel (height of U/V is height/2) */ static void jpeg_buffer_src(j_decompress_ptr cinfo, unsigned char *buffer, long num); static void jpeg_buffer_dest(j_compress_ptr cinfo, unsigned char *buffer, long len); static void jpeg_skip_ff(j_decompress_ptr cinfo); /******************************************************************* * * * The following routines define a JPEG Source manager which * * just reads from a given buffer (instead of a file as in * * the jpeg library) * * * *******************************************************************/ /* * Initialize source --- called by jpeg_read_header * before any data is actually read. */ static void init_source(j_decompress_ptr cinfo ATTRIBUTE_UNUSED) { /* No work necessary here */ } /* * Fill the input buffer --- called whenever buffer is emptied. * * Should never be called since all data should be allready provided. * Is nevertheless sometimes called - sets the input buffer to data * which is the JPEG EOI marker; * */ static uint8_t EOI_data[2] = { 0xFF, 0xD9 }; static boolean fill_input_buffer(j_decompress_ptr cinfo) { cinfo->src->next_input_byte = EOI_data; cinfo->src->bytes_in_buffer = 2; return TRUE; } /* * Skip data --- used to skip over a potentially large amount of * uninteresting data (such as an APPn marker). * */ static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) { if (num_bytes > 0) { if (num_bytes > (long) cinfo->src->bytes_in_buffer) num_bytes = (long) cinfo->src->bytes_in_buffer; cinfo->src->next_input_byte += (size_t) num_bytes; cinfo->src->bytes_in_buffer -= (size_t) num_bytes; } } /* * Terminate source --- called by jpeg_finish_decompress * after all data has been read. Often a no-op. */ static void term_source(j_decompress_ptr cinfo ATTRIBUTE_UNUSED) { /* No work necessary here */ } /* * Prepare for input from a data buffer. */ static void jpeg_buffer_src(j_decompress_ptr cinfo, unsigned char *buffer, long num) { /* The source object and input buffer are made permanent so that a series * of JPEG images can be read from the same buffer by calling jpeg_buffer_src * only before the first one. (If we discarded the buffer at the end of * one image, we'd likely lose the start of the next one.) * This makes it unsafe to use this manager and a different source * manager serially with the same JPEG object. Caveat programmer. */ if (cinfo->src == NULL) { /* First time for this JPEG object? */ cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof (struct jpeg_source_mgr)); } cinfo->src->init_source = init_source; cinfo->src->fill_input_buffer = fill_input_buffer; cinfo->src->skip_input_data = skip_input_data; cinfo->src->resync_to_restart = jpeg_resync_to_restart; /* Use default method */ cinfo->src->term_source = term_source; cinfo->src->bytes_in_buffer = num; cinfo->src->next_input_byte = (JOCTET *) buffer; } /* * jpeg_skip_ff is not a part of the source manager but it is * particularly useful when reading several images from the same buffer: * It should be called to skip padding 0xff bytes beetween images. */ static void jpeg_skip_ff(j_decompress_ptr cinfo) { while (cinfo->src->bytes_in_buffer > 1 && cinfo->src->next_input_byte[0] == 0xff && cinfo->src->next_input_byte[1] == 0xff) { cinfo->src->bytes_in_buffer--; cinfo->src->next_input_byte++; } } /******************************************************************* * * * The following routines define a JPEG Destination manager * * which just reads from a given buffer (instead of a file * * as in the jpeg library) * * * *******************************************************************/ /* * Initialize destination --- called by jpeg_start_compress * before any data is actually written. */ static void init_destination(j_compress_ptr cinfo ATTRIBUTE_UNUSED) { /* No work necessary here */ } /* * Empty the output buffer --- called whenever buffer fills up. * * Should never be called since all data should be written to the buffer. * If it gets called, the given jpeg buffer was too small. * */ static boolean empty_output_buffer(j_compress_ptr cinfo) { /*FIXME: */ MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Given jpeg buffer was too small"); ERREXIT (cinfo, JERR_BUFFER_SIZE); /* Shouldn't be FILE_WRITE but BUFFER_OVERRUN! */ return TRUE; } /* * Terminate destination --- called by jpeg_finish_compress * after all data has been written. Usually needs to flush buffer. * * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding * application must deal with any cleanup that should happen even * for error exit. */ static void term_destination(j_compress_ptr cinfo ATTRIBUTE_UNUSED) { /* No work necessary here */ } /* * Prepare for output to a stdio stream. * The caller must have already opened the stream, and is responsible * for closing it after finishing compression. */ static void jpeg_buffer_dest(j_compress_ptr cinfo, unsigned char *buf, long len) { /* * The destination object is made permanent so that multiple JPEG images * can be written to the same file without re-executing jpeg_stdio_dest. * This makes it dangerous to use this manager and a different destination * manager serially with the same JPEG object, because their private object * sizes may be different. Caveat programmer. */ if (cinfo->dest == NULL) { /* First time for this JPEG object? */ cinfo->dest = (struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof (struct jpeg_destination_mgr)); } cinfo->dest->init_destination = init_destination; cinfo->dest->empty_output_buffer = empty_output_buffer; cinfo->dest->term_destination = term_destination; cinfo->dest->free_in_buffer = len; cinfo->dest->next_output_byte = (JOCTET *) buf; } /******************************************************************* * * * decode_jpeg_data: Decode a (possibly interlaced) JPEG frame * * * *******************************************************************/ /* * ERROR HANDLING: * * We want in all cases to return to the user. * The following kind of error handling is from the * example.c file in the Independent JPEG Group's JPEG software */ struct my_error_mgr { struct jpeg_error_mgr pub; /* "public" fields */ jmp_buf setjmp_buffer; /* For return to caller */ /* Original emit_message method. */ JMETHOD(void, original_emit_message, (j_common_ptr cinfo, int msg_level)); /* Was a corrupt-data warning seen. */ int warning_seen; }; static void my_error_exit(j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; /* cinfo->err really points to a my_error_mgr struct, so coerce pointer. */ struct my_error_mgr *myerr = (struct my_error_mgr *) cinfo->err; /* * Always display the message. * We could postpone this until after returning, if we chose. */ (*cinfo->err->format_message) (cinfo, buffer); MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: %s", buffer); /* Return control to the setjmp point. */ longjmp (myerr->setjmp_buffer, 1); } static void my_emit_message(j_common_ptr cinfo, int msg_level) { char buffer[JMSG_LENGTH_MAX]; /* cinfo->err really points to a my_error_mgr struct, so coerce pointer. */ struct my_error_mgr *myerr = (struct my_error_mgr *) cinfo->err; if (msg_level < 0) myerr->warning_seen++ ; //msg_level = 3 are the RST markers of the JPG which are not of much interest if (msg_level < 3) { (*cinfo->err->format_message) (cinfo, buffer); MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: msg_level: %d, %s", msg_level, buffer); } } #define MAX_LUMA_WIDTH 4096 #define MAX_CHROMA_WIDTH 2048 static unsigned char buf0[16][MAX_LUMA_WIDTH]; static unsigned char buf1[8][MAX_CHROMA_WIDTH]; static unsigned char buf2[8][MAX_CHROMA_WIDTH]; static unsigned char chr1[8][MAX_CHROMA_WIDTH]; static unsigned char chr2[8][MAX_CHROMA_WIDTH]; #if 1 /* Generation of 'std' Huffman tables... */ static void add_huff_table(j_decompress_ptr dinfo, JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val) /* Define a Huffman table */ { int nsymbols, len; if (*htblptr == NULL) *htblptr = jpeg_alloc_huff_table((j_common_ptr) dinfo); /* Copy the number-of-symbols-of-each-code-length counts. */ memcpy((*htblptr)->bits, bits, sizeof((*htblptr)->bits)); /* * Validate the counts. We do this here mainly so we can copy the right * number of symbols from the val[] array, without risking marching off * the end of memory. jchuff.c will do a more thorough test later. */ nsymbols = 0; for (len = 1; len <= 16; len++) nsymbols += bits[len]; if (nsymbols < 1 || nsymbols > 256) MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Given jpeg buffer was too small"); memcpy((*htblptr)->huffval, val, nsymbols * sizeof(UINT8)); } static void std_huff_tables (j_decompress_ptr dinfo) /* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ /* IMPORTANT: these are only valid for 8-bit data precision! */ { static const UINT8 bits_dc_luminance[17] = { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; static const UINT8 val_dc_luminance[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; static const UINT8 bits_dc_chrominance[17] = { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; static const UINT8 val_dc_chrominance[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; static const UINT8 bits_ac_luminance[17] = { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }; static const UINT8 val_ac_luminance[] = { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; static const UINT8 bits_ac_chrominance[17] = { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }; static const UINT8 val_ac_chrominance[] = { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; add_huff_table(dinfo, &dinfo->dc_huff_tbl_ptrs[0], bits_dc_luminance, val_dc_luminance); add_huff_table(dinfo, &dinfo->ac_huff_tbl_ptrs[0], bits_ac_luminance, val_ac_luminance); add_huff_table(dinfo, &dinfo->dc_huff_tbl_ptrs[1], bits_dc_chrominance, val_dc_chrominance); add_huff_table(dinfo, &dinfo->ac_huff_tbl_ptrs[1], bits_ac_chrominance, val_ac_chrominance); } static void guarantee_huff_tables(j_decompress_ptr dinfo) { if ((dinfo->dc_huff_tbl_ptrs[0] == NULL) && (dinfo->dc_huff_tbl_ptrs[1] == NULL) && (dinfo->ac_huff_tbl_ptrs[0] == NULL) && (dinfo->ac_huff_tbl_ptrs[1] == NULL)) { std_huff_tables(dinfo); } } #endif /* ...'std' Huffman table generation */ /* * jpeg_data: Buffer with jpeg data to decode * len: Length of buffer * itype: 0: Not interlaced * 1: Interlaced, Top field first * 2: Interlaced, Bottom field first * ctype Chroma format for decompression. * Currently only Y4M_CHROMA_{420JPEG,422} are available * returns: * -1 on fatal error * 0 on success * 1 if jpeg lib threw a "corrupt jpeg data" warning. * in this case, "a damaged output image is likely." * */ int decode_jpeg_raw (unsigned char *jpeg_data, int len, int itype, int ctype, unsigned int width, unsigned int height, unsigned char *raw0, unsigned char *raw1, unsigned char *raw2) { int numfields, hsf[3], field, yl, yc; int i, xsl, xsc, xs, hdown; unsigned int x, y = 0, vsf[3], xd; JSAMPROW row0[16] = { buf0[0], buf0[1], buf0[2], buf0[3], buf0[4], buf0[5], buf0[6], buf0[7], buf0[8], buf0[9], buf0[10], buf0[11], buf0[12], buf0[13], buf0[14], buf0[15]}; JSAMPROW row1[8] = { buf1[0], buf1[1], buf1[2], buf1[3], buf1[4], buf1[5], buf1[6], buf1[7]}; JSAMPROW row2[16] = { buf2[0], buf2[1], buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7]}; JSAMPROW row1_444[16], row2_444[16]; JSAMPARRAY scanarray[3] = { row0, row1, row2}; struct jpeg_decompress_struct dinfo; struct my_error_mgr jerr; /* We set up the normal JPEG error routines, then override error_exit. */ dinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; /* Also hook the emit_message routine to note corrupt-data warnings. */ jerr.original_emit_message = jerr.pub.emit_message; jerr.pub.emit_message = my_emit_message; jerr.warning_seen = 0; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ jpeg_destroy_decompress (&dinfo); return 1; } jpeg_create_decompress (&dinfo); jpeg_buffer_src (&dinfo, jpeg_data, len); /* * Read header, make some checks and try to figure out what the * user really wants. */ jpeg_read_header (&dinfo, TRUE); dinfo.raw_data_out = TRUE; #if JPEG_LIB_VERSION >= 70 dinfo.do_fancy_upsampling = FALSE; #endif dinfo.out_color_space = JCS_YCbCr; dinfo.dct_method = JDCT_IFAST; guarantee_huff_tables(&dinfo); jpeg_start_decompress (&dinfo); if (dinfo.output_components != 3) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Output components of JPEG image" " = %d, must be 3", dinfo.output_components); goto ERR_EXIT; } for (i = 0; i < 3; i++) { hsf[i] = dinfo.comp_info[i].h_samp_factor; vsf[i] = dinfo.comp_info[i].v_samp_factor; } if ((hsf[0] != 2 && hsf[0] != 1) || hsf[1] != 1 || hsf[2] != 1 || (vsf[0] != 1 && vsf[0] != 2) || vsf[1] != 1 || vsf[2] != 1) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Unsupported sampling factors," " hsf=(%d, %d, %d) vsf=(%d, %d, %d) !", hsf[0], hsf[1], hsf[2], vsf[0], vsf[1], vsf[2]); goto ERR_EXIT; } if (hsf[0] == 1) { if (height % 8 != 0) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: YUV 4:4:4 sampling, but image" " height %d not dividable by 8 !", height); goto ERR_EXIT; } for (y = 0; y < 16; y++) { // Allocate a special buffer for the extra sampling depth. row1_444[y] = (unsigned char *)malloc(dinfo.output_width * sizeof(char)); row2_444[y] = (unsigned char *)malloc(dinfo.output_width * sizeof(char)); } scanarray[1] = row1_444; scanarray[2] = row2_444; } /* Height match image height or be exact twice the image height. */ if (dinfo.output_height == height) { numfields = 1; } else if (2 * dinfo.output_height == height) { numfields = 2; } else { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Read JPEG: requested height = %d, " "height of image = %d", height, dinfo.output_height); goto ERR_EXIT; } /* Width is more flexible */ if (dinfo.output_width > MAX_LUMA_WIDTH) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Image width of %d exceeds max", dinfo.output_width); goto ERR_EXIT; } if (width < 2 * dinfo.output_width / 3) { /* Downsample 2:1 */ hdown = 1; if (2 * width < dinfo.output_width) xsl = (dinfo.output_width - 2 * width) / 2; else xsl = 0; } else if (width == 2 * dinfo.output_width / 3) { /* Special case of 3:2 downsampling */ hdown = 2; xsl = 0; } else { /* No downsampling */ hdown = 0; if (width < dinfo.output_width) xsl = (dinfo.output_width - width) / 2; else xsl = 0; } /* Make xsl even, calculate xsc */ xsl = xsl & ~1; xsc = xsl / 2; yl = yc = 0; for (field = 0; field < numfields; field++) { if (field > 0) { jpeg_read_header (&dinfo, TRUE); dinfo.raw_data_out = TRUE; #if JPEG_LIB_VERSION >= 70 dinfo.do_fancy_upsampling = FALSE; #endif dinfo.out_color_space = JCS_YCbCr; dinfo.dct_method = JDCT_IFAST; jpeg_start_decompress (&dinfo); } if (numfields == 2) { switch (itype) { case Y4M_ILACE_TOP_FIRST: yl = yc = field; break; case Y4M_ILACE_BOTTOM_FIRST: yl = yc = (1 - field); break; default: MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Input is interlaced but" " no interlacing set"); goto ERR_EXIT; } } else { yl = yc = 0; } while (dinfo.output_scanline < dinfo.output_height) { /* Read raw data */ jpeg_read_raw_data (&dinfo, scanarray, 8 * vsf[0]); for (y = 0; y < 8 * vsf[0]; yl += numfields, y++) { xd = yl * width; xs = xsl; if (hdown == 0) { for (x = 0; x < width; x++) raw0[xd++] = row0[y][xs++]; } else if (hdown == 1) { for (x = 0; x < width; x++, xs += 2) raw0[xd++] = (row0[y][xs] + row0[y][xs + 1]) >> 1; } else { for (x = 0; x < width / 2; x++, xd += 2, xs += 3) { raw0[xd] = (2 * row0[y][xs] + row0[y][xs + 1]) / 3; raw0[xd + 1] = (2 * row0[y][xs + 2] + row0[y][xs + 1]) / 3; } } } /* Horizontal downsampling of chroma */ for (y = 0; y < 8; y++) { xs = xsc; if (hsf[0] == 1) for (x = 0; x < width / 2; x++, xs++) { row1[y][xs] = (row1_444[y][2*x] + row1_444[y][2*x + 1]) >> 1; row2[y][xs] = (row2_444[y][2*x] + row2_444[y][2*x + 1]) >> 1; } xs = xsc; if (hdown == 0) { for (x = 0; x < width / 2; x++, xs++) { chr1[y][x] = row1[y][xs]; chr2[y][x] = row2[y][xs]; } } else if (hdown == 1) { for (x = 0; x < width / 2; x++, xs += 2) { chr1[y][x] = (row1[y][xs] + row1[y][xs + 1]) >> 1; chr2[y][x] = (row2[y][xs] + row2[y][xs + 1]) >> 1; } } else { for (x = 0; x < width / 2; x += 2, xs += 3) { chr1[y][x] = (2 * row1[y][xs] + row1[y][xs + 1]) / 3; chr1[y][x + 1] = (2 * row1[y][xs + 2] + row1[y][xs + 1]) / 3; chr2[y][x] = (2 * row2[y][xs] + row2[y][xs + 1]) / 3; chr2[y][x + 1] = (2 * row2[y][xs + 2] + row2[y][xs + 1]) / 3; } } } /* Vertical resampling of chroma */ switch (ctype) { case Y4M_CHROMA_422: if (vsf[0] == 1) { /* Just copy */ for (y = 0; y < 8 /*&& yc < height */; y++, yc += numfields) { xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { raw1[xd] = chr1[y][x]; raw2[xd] = chr2[y][x]; } } } else { /* upsample */ for (y = 0; y < 8 /*&& yc < height */; y++) { xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { raw1[xd] = chr1[y][x]; raw2[xd] = chr2[y][x]; } yc += numfields; xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { raw1[xd] = chr1[y][x]; raw2[xd] = chr2[y][x]; } yc += numfields; } } break; default: /* * Should be case Y4M_CHROMA_420JPEG: but use default: for compatibility. Some * pass things like '420' in with the expectation that anything other than * Y4M_CHROMA_422 will default to 420JPEG. */ if (vsf[0] == 1) { /* Really downsample */ for (y = 0; y < 8 /*&& yc < height/2*/; y += 2, yc += numfields) { xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { assert(xd < (width * height / 4)); raw1[xd] = (chr1[y][x] + chr1[y + 1][x]) >> 1; raw2[xd] = (chr2[y][x] + chr2[y + 1][x]) >> 1; } } } else { /* Just copy */ for (y = 0; y < 8 /* && yc < height / 2 */; y++, yc += numfields) { xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { raw1[xd] = chr1[y][x]; raw2[xd] = chr2[y][x]; } } } break; } } (void) jpeg_finish_decompress (&dinfo); if (field == 0 && numfields > 1) jpeg_skip_ff (&dinfo); } if (hsf[0] == 1) { for (y = 0; y < 16; y++) { // Allocate a special buffer for the extra sampling depth. free(row1_444[y]); free(row2_444[y]); } } jpeg_destroy_decompress (&dinfo); /** * The 10% was determined by trial. Perhaps a better * threshold for discarding an image would be a function * of the threshold for the trigger of motion. */ if (dinfo.output_height == 0){ return 1; } else { if ( (jerr.warning_seen / dinfo.output_height) > 0.10) return 1; else return 0; } ERR_EXIT: jpeg_destroy_decompress (&dinfo); return -1; } /* * jpeg_data: Buffer with jpeg data to decode, must be grayscale mode * len: Length of buffer * itype: 0: Not interlaced * 1: Interlaced, Top field first * 2: Interlaced, Bottom field first * ctype Chroma format for decompression. * Currently only Y4M_CHROMA_{420JPEG,422} are available */ int decode_jpeg_gray_raw(unsigned char *jpeg_data, int len, int itype, int ctype, unsigned int width, unsigned int height, unsigned char *raw0, unsigned char *raw1, unsigned char *raw2) { int numfields, field, yl, yc, xsl, xsc, xs, xd, hdown; unsigned int x, y, vsf[3]; JSAMPROW row0[16] = { buf0[0], buf0[1], buf0[2], buf0[3], buf0[4], buf0[5], buf0[6], buf0[7], buf0[8], buf0[9], buf0[10], buf0[11], buf0[12], buf0[13], buf0[14], buf0[15]}; JSAMPARRAY scanarray[3] = { row0 }; struct jpeg_decompress_struct dinfo; struct my_error_mgr jerr; /* We set up the normal JPEG error routines, then override error_exit. */ dinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ jpeg_destroy_decompress (&dinfo); return -1; } jpeg_create_decompress (&dinfo); jpeg_buffer_src (&dinfo, jpeg_data, len); /* * Read header, make some checks and try to figure out what the * user really wants. */ jpeg_read_header (&dinfo, TRUE); dinfo.raw_data_out = TRUE; #if JPEG_LIB_VERSION >= 70 dinfo.do_fancy_upsampling = FALSE; #endif dinfo.out_color_space = JCS_GRAYSCALE; dinfo.dct_method = JDCT_IFAST; if (dinfo.jpeg_color_space != JCS_GRAYSCALE) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Expected grayscale colorspace" " for JPEG raw decoding"); goto ERR_EXIT; } guarantee_huff_tables(&dinfo); jpeg_start_decompress (&dinfo); vsf[0] = 1; vsf[1] = 1; vsf[2] = 1; /* Height match image height or be exact twice the image height. */ if (dinfo.output_height == height) { numfields = 1; } else if (2 * dinfo.output_height == height) { numfields = 2; } else { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Read JPEG: requested height = %d, " "height of image = %d", height, dinfo.output_height); goto ERR_EXIT; } /* Width is more flexible */ if (dinfo.output_width > MAX_LUMA_WIDTH) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Image width of %d exceeds max", dinfo.output_width); goto ERR_EXIT; } if (width < 2 * dinfo.output_width / 3) { /* Downsample 2:1 */ hdown = 1; if (2 * width < dinfo.output_width) xsl = (dinfo.output_width - 2 * width) / 2; else xsl = 0; } else if (width == 2 * dinfo.output_width / 3) { /* special case of 3:2 downsampling */ hdown = 2; xsl = 0; } else { /* No downsampling */ hdown = 0; if (width < dinfo.output_width) xsl = (dinfo.output_width - width) / 2; else xsl = 0; } /* Make xsl even, calculate xsc */ xsl = xsl & ~1; xsc = xsl / 2; yl = yc = 0; for (field = 0; field < numfields; field++) { if (field > 0) { jpeg_read_header (&dinfo, TRUE); dinfo.raw_data_out = TRUE; #if JPEG_LIB_VERSION >= 70 dinfo.do_fancy_upsampling = FALSE; #endif dinfo.out_color_space = JCS_GRAYSCALE; dinfo.dct_method = JDCT_IFAST; jpeg_start_decompress (&dinfo); } if (numfields == 2) { switch (itype) { case Y4M_ILACE_TOP_FIRST: yl = yc = field; break; case Y4M_ILACE_BOTTOM_FIRST: yl = yc = (1 - field); break; default: MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Input is interlaced" " but no interlacing set"); goto ERR_EXIT; } } else { yl = yc = 0; } while (dinfo.output_scanline < dinfo.output_height) { jpeg_read_raw_data (&dinfo, scanarray, 16); for (y = 0; y < 8 * vsf[0]; yl += numfields, y++) { xd = yl * width; xs = xsl; if (hdown == 0) { // No horiz downsampling for (x = 0; x < width; x++) raw0[xd++] = row0[y][xs++]; } else if (hdown == 1) { // Half the res for (x = 0; x < width; x++, xs += 2) raw0[xd++] = (row0[y][xs] + row0[y][xs + 1]) >> 1; } else { // 2:3 downsampling for (x = 0; x < width / 2; x++, xd += 2, xs += 3) { raw0[xd] = (2 * row0[y][xs] + row0[y][xs + 1]) / 3; raw0[xd + 1] = (2 * row0[y][xs + 2] + row0[y][xs + 1]) / 3; } } } for (y = 0; y < 8; y++) { xs = xsc; if (hdown == 0) { for (x = 0; x < width / 2; x++, xs++) { chr1[y][x] = 0; //row1[y][xs]; chr2[y][x] = 0; //row2[y][xs]; } } else if (hdown == 1) { for (x = 0; x < width / 2; x++, xs += 2) { chr1[y][x] = 0; //(row1[y][xs] + row1[y][xs + 1]) >> 1; chr2[y][x] = 0; //(row2[y][xs] + row2[y][xs + 1]) >> 1; } } else { for (x = 0; x < width / 2; x += 2, xs += 3) { chr1[y][x] = 0; //(2 * row1[y][xs] + row1[y][xs + 1]) / 3; chr1[y][x + 1] = 0; //(2 * row1[y][xs + 2] + row1[y][xs + 1]) / 3; chr2[y][x] = 0; // (2 * row2[y][xs] + row2[y][xs + 1]) / 3; chr2[y][x + 1] = 0; //(2 * row2[y][xs + 2] + row2[y][xs + 1]) / 3; } } } switch (ctype) { case Y4M_CHROMA_422: if (vsf[0] == 1) { /* Just copy */ for (y = 0; y < 8 /* && yc < height */; y++, yc += numfields) { xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { raw1[xd] = 127; //chr1[y][x]; raw2[xd] = 127; //chr2[y][x]; } } } else { /* upsample */ for (y = 0; y < 8 /* && yc < height */; y++) { xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { raw1[xd] = 127; //chr1[y][x]; raw2[xd] = 127; //chr2[y][x]; } yc += numfields; xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { raw1[xd] = 127; //chr1[y][x]; raw2[xd] = 127; //chr2[y][x]; } yc += numfields; } } break; /* * Should be case Y4M_CHROMA_420JPEG: but use default: for compatibility. Some * pass things like '420' in with the expectation that anything other than * Y4M_CHROMA_422 will default to 420JPEG. */ default: if (vsf[0] == 1) { /* Really downsample */ for (y = 0; y < 8; y += 2, yc += numfields) { xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { raw1[xd] = 127; //(chr1[y][x] + chr1[y + 1][x]) >> 1; raw2[xd] = 127; //(chr2[y][x] + chr2[y + 1][x]) >> 1; } } } else { /* Just copy */ for (y = 0; y < 8; y++, yc += numfields) { xd = yc * width / 2; for (x = 0; x < width / 2; x++, xd++) { raw1[xd] = 127; //chr1[y][x]; raw2[xd] = 127; //chr2[y][x]; } } } break; } } (void) jpeg_finish_decompress (&dinfo); if (field == 0 && numfields > 1) jpeg_skip_ff (&dinfo); } jpeg_destroy_decompress (&dinfo); return 0; ERR_EXIT: jpeg_destroy_decompress (&dinfo); return -1; } /******************************************************************* * * * encode_jpeg_data: Compress raw YCbCr data (output JPEG * * may be interlaced * * * *******************************************************************/ /* * jpeg_data: Buffer to hold output jpeg * len: Length of buffer * itype: 0: Not interlaced * 1: Interlaced, Top field first * 2: Interlaced, Bottom field first * ctype Chroma format for decompression. * Currently only Y4M_CHROMA_{420JPEG,422} are available */ int encode_jpeg_raw(unsigned char *jpeg_data, int len, int quality, int itype, int ctype, unsigned int width, unsigned int height, unsigned char *raw0, unsigned char *raw1, unsigned char *raw2) { int numfields, field, yl, yc, y, i; JSAMPROW row0[16] = { buf0[0], buf0[1], buf0[2], buf0[3], buf0[4], buf0[5], buf0[6], buf0[7], buf0[8], buf0[9], buf0[10], buf0[11], buf0[12], buf0[13], buf0[14], buf0[15]}; JSAMPROW row1[8] = { buf1[0], buf1[1], buf1[2], buf1[3], buf1[4], buf1[5], buf1[6], buf1[7]}; JSAMPROW row2[8] = { buf2[0], buf2[1], buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7]}; JSAMPARRAY scanarray[3] = { row0, row1, row2}; struct jpeg_compress_struct cinfo; struct my_error_mgr jerr; /* We set up the normal JPEG error routines, then override error_exit. */ cinfo.err = jpeg_std_error (&jerr.pub); jerr.pub.error_exit = my_error_exit; /* Establish the setjmp return context for my_error_exit to use. */ if (setjmp (jerr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. */ jpeg_destroy_compress (&cinfo); return -1; } jpeg_create_compress (&cinfo); jpeg_buffer_dest(&cinfo, jpeg_data, len); /* Set some jpeg header fields */ cinfo.input_components = 3; jpeg_set_defaults (&cinfo); jpeg_set_quality (&cinfo, quality, FALSE); cinfo.raw_data_in = TRUE; cinfo.in_color_space = JCS_YCbCr; cinfo.dct_method = JDCT_IFAST; cinfo.input_gamma = 1.0; cinfo.comp_info[0].h_samp_factor = 2; cinfo.comp_info[0].v_samp_factor = 1; /*1||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; /*1||2 */ cinfo.comp_info[2].v_samp_factor = 1; if ((width > 4096) || (height > 4096)) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Image dimensions (%dx%d) exceed" " lavtools' max (4096x4096)", width, height); goto ERR_EXIT; } if ((width % 16) || (height % 16)) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Image dimensions (%dx%d) not" " multiples of 16", width, height); goto ERR_EXIT; } cinfo.image_width = width; switch (itype) { case Y4M_ILACE_TOP_FIRST: case Y4M_ILACE_BOTTOM_FIRST: /* Interlaced */ numfields = 2; break; default: numfields = 1; if (height > 2048) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Image height (%d) exceeds" " lavtools max for non-interlaced frames", height); goto ERR_EXIT; } } cinfo.image_height = height / numfields; yl = yc = 0; /* y luma, chroma */ for (field = 0; field < numfields; field++) { jpeg_start_compress (&cinfo, FALSE); if (numfields == 2) { static const JOCTET marker0[40]; jpeg_write_marker(&cinfo, JPEG_APP0, marker0, 14); jpeg_write_marker(&cinfo, JPEG_APP0 + 1, marker0, 40); switch (itype) { case Y4M_ILACE_TOP_FIRST: /* Top field first */ yl = yc = field; break; case Y4M_ILACE_BOTTOM_FIRST: /* Bottom field first */ yl = yc = (1 - field); break; default: MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Input is interlaced" " but no interlacing set"); goto ERR_EXIT; } } else { yl = yc = 0; } while (cinfo.next_scanline < cinfo.image_height) { for (y = 0; y < 8 * cinfo.comp_info[0].v_samp_factor; yl += numfields, y++) { row0[y] = &raw0[yl * width]; } for (y = 0; y < 8; y++) { row1[y] = &raw1[yc * width / 2]; row2[y] = &raw2[yc * width / 2]; if ((ctype == Y4M_CHROMA_422) || (y % 2)) yc += numfields; } jpeg_write_raw_data (&cinfo, scanarray, 8 * cinfo.comp_info[0].v_samp_factor); } (void) jpeg_finish_compress (&cinfo); } /* FIXME */ i = len - cinfo.dest->free_in_buffer; jpeg_destroy_compress (&cinfo); return i; /* Size of jpeg */ ERR_EXIT: jpeg_destroy_compress (&cinfo); return -1; } motion-release-4.0/jpegutils.h000066400000000000000000000053441300074623700164670ustar00rootroot00000000000000/* * jpegutils.h: Some Utility programs for dealing with * JPEG encoded images * * Copyright (C) 1999 Rainer Johanni * Copyright (C) 2001 pHilipp Zabel * Copyright (C) 2008 Angel Carpintero * */ #ifndef __JPEGUTILS_H__ #define __JPEGUTILS_H__ /* * jpeg_data: buffer with input / output jpeg * len: Length of jpeg buffer * itype: Y4M_ILACE_NONE: Not interlaced * Y4M_ILACE_TOP_FIRST: Interlaced, top-field-first * Y4M_ILACE_BOTTOM_FIRST: Interlaced, bottom-field-first * ctype Chroma format for decompression. * Currently always 420 and hence ignored. * raw0 buffer with input / output raw Y channel * raw1 buffer with input / output raw U/Cb channel * raw2 buffer with input / output raw V/Cr channel * width width of Y channel (width of U/V is width/2) * height height of Y channel (height of U/V is height/2) */ #define Y4M_ILACE_NONE 0 /* non-interlaced, progressive frame */ #define Y4M_ILACE_TOP_FIRST 1 /* interlaced, top-field first */ #define Y4M_ILACE_BOTTOM_FIRST 2 /* interlaced, bottom-field first */ #define Y4M_ILACE_MIXED 3 /* mixed, "refer to frame header" */ #define Y4M_CHROMA_420JPEG 0 /* 4:2:0, H/V centered, for JPEG/MPEG-1 */ #define Y4M_CHROMA_420MPEG2 1 /* 4:2:0, H cosited, for MPEG-2 */ #define Y4M_CHROMA_420PALDV 2 /* 4:2:0, alternating Cb/Cr, for PAL-DV */ #define Y4M_CHROMA_444 3 /* 4:4:4, no subsampling, phew. */ #define Y4M_CHROMA_422 4 /* 4:2:2, H cosited */ #define Y4M_CHROMA_411 5 /* 4:1:1, H cosited */ #define Y4M_CHROMA_MONO 6 /* luma plane only */ #define Y4M_CHROMA_444ALPHA 7 /* 4:4:4 with an alpha channel */ int decode_jpeg_raw(unsigned char *jpeg_data, int len, int itype, int ctype, unsigned int width, unsigned int height, unsigned char *raw0, unsigned char *raw1, unsigned char *raw2); int decode_jpeg_gray_raw(unsigned char *jpeg_data, int len, int itype, int ctype, unsigned int width, unsigned int height, unsigned char *raw0, unsigned char *raw1, unsigned char *raw2); int encode_jpeg_raw(unsigned char *jpeg_data, int len, int quality, int itype, int ctype, unsigned int width, unsigned int height, unsigned char *raw0, unsigned char *raw1, unsigned char *raw2); #endif motion-release-4.0/logger.c000066400000000000000000000153771300074623700157420ustar00rootroot00000000000000/* * logger.c * * Logger for motion * * Copyright 2005, William M. Brack * Copyright 2008 by Angel Carpintero (motiondevelop@gmail.com) * This software is distributed under the GNU Public License Version 2 * See also the file 'COPYING'. * */ #include "logger.h" /* already includes motion.h */ #include static int log_mode = LOGMODE_SYSLOG; static FILE *logfile; static unsigned int log_level = LEVEL_DEFAULT; static unsigned int log_type = TYPE_DEFAULT; static const char *log_type_str[] = {NULL, "COR", "STR", "ENC", "NET", "DBL", "EVT", "TRK", "VID", "ALL"}; static const char *log_level_str[] = {"EMG", "ALR", "CRT", "ERR", "WRN", "NTC", "INF", "DBG", "ALL", NULL}; /** * get_log_type * * * Returns: index of log type or 0 if not valid type. */ int get_log_type(const char *type) { unsigned int i, ret = 0; unsigned int maxtype = sizeof(log_type_str)/sizeof(const char *); for (i = 1;i < maxtype; i++) { if (!strncasecmp(type, log_type_str[i], 3)) { ret = i; break; } } return ret; } /** * get_log_type_str * Gets string value for type log level. * * Returns: name of type log level. */ const char* get_log_type_str(unsigned int type) { return log_type_str[type]; } /** * set_log_type * Sets log type level. * * Returns: nothing. */ void set_log_type(unsigned int type) { log_type = type; //printf("set log type %d\n", type); } /** * get_log_level_str * Gets string value for log level. * * Returns: name of log level. */ const char* get_log_level_str(unsigned int level) { return log_level_str[level]; } /** * set_log_level * Sets log level. * * Returns nothing. */ void set_log_level(unsigned int level) { log_level = level; //printf("set log level %d\n", level); } /** * set_log_mode * Sets mode of logging , could be using syslog or files. * * Returns: nothing. */ void set_log_mode(int mode) { log_mode = mode; //printf("set log mode %d\n", mode); } /** * set_logfile * Sets logfile to be used instead of syslog. * * Returns: pointer to log file. */ FILE * set_logfile(const char *logfile_name) { log_mode = LOGMODE_SYSLOG; /* Setup temporary to let log if myfopen fails */ logfile = myfopen(logfile_name, "a"); /* If logfile was opened correctly */ if (logfile) log_mode = LOGMODE_FILE; return logfile; } /** * str_time * * Return: string with human readable time */ static char *str_time(void) { static char buffer[16]; time_t now = 0; now = time(0); strftime(buffer, 16, "%b %d %H:%M:%S", localtime(&now)); return buffer; } /** * 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 * * type logging type. * * 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, unsigned int type, int errno_flag, const char *fmt, ...) { int errno_save, n; char buf[1024]; /* GNU-specific strerror_r() */ #if (!defined(XSI_STRERROR_R)) char msg_buf[100]; #endif va_list ap; int threadnr; /* Exit if level is greater than log_level */ if ((unsigned int)level > log_level) return; /* Exit if type is not equal to log_type and not TYPE_ALL */ if ((log_type != TYPE_ALL) && (type != log_type)) return; //printf("log_type %d, type %d level %d\n", log_type, type, level); /* * 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; #ifdef HAVE_PTHREAD_SETNAME_NP char threadname[32] = "unknown"; pthread_getname_np(pthread_self(), threadname, sizeof(threadname)); #else /* HAVE_PTHREAD_SETNAME_NP */ const char *threadname = ""; #endif /* HAVE_PTHREAD_SETNAME_NP */ /* * Prefix the message with the log level string, log type string, * time and thread number. e.g. [1] [ERR] [ENC] [Apr 03 00:08:44] blah * */ if (!log_mode) { n = snprintf(buf, sizeof(buf), "[%d:%s] [%s] [%s] [%s] ", threadnr, threadname, get_log_level_str(level), get_log_type_str(type), str_time()); } else { /* * Prefix the message with the log level string, log type string * and thread number. e.g. [1] [DBG] [TRK] blah */ n = snprintf(buf, sizeof(buf), "[%d:%s] [%s] [%s] ", threadnr, threadname, get_log_level_str(level), get_log_type_str(type)); } /* Next add the user's message. */ va_start(ap, fmt); n += vsnprintf(buf + n, sizeof(buf) - n, fmt, ap); buf[1023] = '\0'; /* If errno_flag is set, add on the library error message. */ if (errno_flag) { size_t buf_len = strlen(buf); // just knock off 10 characters if we're that close... if (buf_len + 10 > 1024) { buf[1024 - 10] = '\0'; buf_len = 1024 - 10; } strncat(buf, ": ", 1024 - buf_len); 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. */ #if defined(XSI_STRERROR_R) /* XSI-compliant strerror_r() */ strerror_r(errno_save, buf + n, sizeof(buf) - n); /* 2 for the ': ' */ #else /* GNU-specific strerror_r() */ strncat(buf, strerror_r(errno_save, msg_buf, sizeof(msg_buf)), 1024 - strlen(buf)); #endif } if (!log_mode) { strncat(buf, "\n", 1024 - strlen(buf)); fputs(buf, logfile); fflush(logfile); /* If log_mode, send the message to the syslog. */ } else { syslog(level, "%s", buf); strncat(buf, "\n", 1024 - strlen(buf)); fputs(buf, stderr); fflush(stderr); } /* Clean up the argument list routine. */ va_end(ap); } motion-release-4.0/logger.h000066400000000000000000000052741300074623700157420ustar00rootroot00000000000000/* * logger.h * * Include file for logger.c * * Copyright 2005, William M. Brack * Copyright 2008 by Angel Carpintero (motiondevelop@gmail.com) * This software is distributed under the GNU Public License Version 2 * See also the file 'COPYING'. * */ #ifndef _INCLUDE_LOGGER_H_ #define _INCLUDE_LOGGER_H_ #include "motion.h" #include /* Logging mode */ #define LOGMODE_FILE 0 /* Log messages to file */ #define LOGMODE_SYSLOG 1 /* Log messages to syslog */ #define NO_ERRNO 0 /* Flag to avoid how message associated to errno */ #define SHOW_ERRNO 1 /* Flag to show message associated to errno */ /* Log levels */ #define LOG_ALL 9 #define EMG LOG_EMERG /* syslog 0 motion 1 */ #define ALR LOG_ALERT /* syslog 1 motion 2 */ #define CRT LOG_CRIT /* syslog 2 motion 3 */ #define ERR LOG_ERR /* syslog 3 motion 4 */ #define WRN LOG_WARNING /* syslog 4 motion 5 */ #define NTC LOG_NOTICE /* syslog 5 motion 6 */ #define INF LOG_INFO /* syslog 6 motion 7 */ #define DBG LOG_DEBUG /* syslog 7 motion 8 */ #define ALL LOG_ALL /* syslog 8 motion 9 */ #define LEVEL_DEFAULT NTC /* syslog 5 motion 6 default */ #define SHOW_LEVEL_VALUE(x) (x+1) /* Log types */ #define TYPE_CORE 1 /* Core logs */ #define TYPE_STREAM 2 /* Stream logs */ #define TYPE_ENCODER 3 /* Encoder logs */ #define TYPE_NETCAM 4 /* Netcam logs */ #define TYPE_DB 5 /* Database logs */ #define TYPE_EVENTS 6 /* Events logs */ #define TYPE_TRACK 7 /* Track logs */ #define TYPE_VIDEO 8 /* V4L1/2 Bktr logs */ #define TYPE_ALL 9 /* All type logs */ #define TYPE_DEFAULT TYPE_ALL /* Default type */ #define TYPE_DEFAULT_STR "ALL" /* Default name logs */ #define MOTION_LOG(x, y, z, format, args...) motion_log(x, y, z, format, __FUNCTION__, ##args) int get_log_type(const char* type); const char* get_log_type_str(unsigned int type); void set_log_type(unsigned int type); const char* get_log_level_str(unsigned int level); void set_log_level(unsigned int level); void set_log_mode(int mode); FILE * set_logfile(const char *logfile_name); void motion_log(int, unsigned int, int, const char *, ...); #endif motion-release-4.0/mask1.png000066400000000000000000000015261300074623700160300ustar00rootroot00000000000000PNG  IHDR@TFtIME0PP/ pHYs  ~gAMA aIDATx1nA@]BEHO %KA1[@lKAlohޯ\fffǫZ.}lKݩooz=111111111111%Gx8]ziu껻|e!|Էv7-<\f|a:o0zW#M\;"`+#r:n.mpl7[w }Ɍw.gX_˙S/@g !yǮnv> (32-(n)))) /* * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. * Rotation is separate from addition to prevent recomputation. */ #define FF(a, b, c, d, x, s, ac) { \ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define GG(a, b, c, d, x, s, ac) { \ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define HH(a, b, c, d, x, s, ac) { \ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } #define II(a, b, c, d, x, s, ac) { \ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ (a) = ROTATE_LEFT ((a), (s)); \ (a) += (b); \ } /* * MD5 initialization. Begins an MD5 operation, writing a new context. */ void MD5Init(MD5_CTX *context) { context->count[0] = context->count[1] = 0; /* Load magic initialization constants. */ context->state[0] = 0x67452301; context->state[1] = 0xefcdab89; context->state[2] = 0x98badcfe; context->state[3] = 0x10325476; } /* * MD5 block update operation. Continues an MD5 message-digest * operation, processing another message block, and updating the * context. */ void MD5Update ( MD5_CTX *context, /* context */ unsigned char *input, /* input block */ unsigned int inputLen) /* length of input block */ { unsigned int i, index, partLen; /* Compute number of bytes mod 64 */ index = (unsigned int)((context->count[0] >> 3) & 0x3F); /* Update number of bits */ if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3)) context->count[1]++; context->count[1] += ((UINT4)inputLen >> 29); partLen = 64 - index; /* Transform as many times as possible. */ if (inputLen >= partLen) { MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen); MD5Transform (context->state, context->buffer); for (i = partLen; i + 63 < inputLen; i += 64) MD5Transform (context->state, &input[i]); index = 0; } else i = 0; /* Buffer remaining input */ MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); } /* * MD5 finalization. Ends an MD5 message-digest operation, writing the * the message digest and zeroizing the context. */ void MD5Final ( unsigned char digest[16], /* message digest */ MD5_CTX *context) /* context */ { unsigned char bits[8]; unsigned int index, padLen; /* Save number of bits */ Encode (bits, context->count, 8); /* Pad out to 56 mod 64. */ index = (unsigned int)((context->count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); MD5Update (context, PADDING, padLen); /* Append length (before padding) */ MD5Update (context, bits, 8); /* Store state in digest */ Encode (digest, context->state, 16); /* Zeroize sensitive information. */ MD5_memset ((POINTER)context, 0, sizeof (*context)); } /* * MD5 basic transformation. Transforms state based on block. */ static void MD5Transform (state, block) UINT4 state[4]; unsigned char block[64]; { UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; Decode (x, block, 64); /* Round 1 */ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; /* Zeroize sensitive information. */ MD5_memset ((POINTER)x, 0, sizeof (x)); } /* * Encodes input (UINT4) into output (unsigned char). Assumes len is * a multiple of 4. */ static void Encode (output, input, len) unsigned char *output; UINT4 *input; unsigned int len; { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = (unsigned char)(input[i] & 0xff); output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); } } /* * Decodes input (unsigned char) into output (UINT4). Assumes len is * a multiple of 4. */ static void Decode (output, input, len) UINT4 *output; unsigned char *input; unsigned int len; { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); } /* Note: Replace "for loop" with standard memcpy if possible. */ static void MD5_memcpy (output, input, len) POINTER output; POINTER input; unsigned int len; { unsigned int i; for (i = 0; i < len; i++) output[i] = input[i]; } /* Note: Replace "for loop" with standard memset if possible. */ static void MD5_memset (output, value, len) POINTER output; int value; unsigned int len; { unsigned int i; for (i = 0; i < len; i++) ((char *)output)[i] = (char)value; } void MD5(unsigned char *message,unsigned long message_length,unsigned char *md) { MD5_CTX state; MD5Init(&state); MD5Update(&state,message,message_length); MD5Final(md,&state); return; } motion-release-4.0/md5.h000066400000000000000000000043201300074623700151370ustar00rootroot00000000000000/* * MD5.H - header file for MD5C.C * taken from RFC 1321 */ #ifndef MD5_H #define MD5_H /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ /* GLOBAL.H - RSAREF types and constants */ /* * PROTOTYPES should be set to one if and only if the compiler supports * function argument prototyping. * The following makes PROTOTYPES default to 0 if it has not already * been defined with C compiler flags. */ #ifndef PROTOTYPES #define PROTOTYPES 0 #endif /* POINTER defines a generic pointer type */ typedef unsigned char *POINTER; /* UINT2 defines a two byte word */ typedef unsigned short int UINT2; /* UINT4 defines a four byte word */ typedef unsigned int UINT4; /* * PROTO_LIST is defined depending on how PROTOTYPES is defined above. * If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it * returns an empty list. */ #if PROTOTYPES #define PROTO_LIST(list) list #else #define PROTO_LIST(list) () #endif /* MD5 context. */ typedef struct { UINT4 state[4]; /* state (ABCD) */ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ unsigned char buffer[64]; /* input buffer */ } MD5_CTX; void MD5Init(MD5_CTX *); void MD5Update(MD5_CTX *, unsigned char *, unsigned int); void MD5Final(unsigned char [16], MD5_CTX *); void MD5(unsigned char *message, unsigned long message_length, unsigned char *md); #endif // MD5_H motion-release-4.0/mmalcam.c000066400000000000000000000317321300074623700160630ustar00rootroot00000000000000/* * mmalcam.c * * Raspberry Pi camera module using MMAL API. * * Built upon functionality from the Raspberry Pi userland utility raspivid. * * Copyright 2013 by Nicholas Tuckett * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. * */ #include "interface/vcos/vcos.h" #include "interface/mmal/mmal.h" #include "interface/mmal/mmal_buffer.h" #include "interface/mmal/mmal_port.h" #include "interface/mmal/util/mmal_util.h" #include "interface/mmal/util/mmal_util_params.h" #include "interface/mmal/util/mmal_default_components.h" #include "interface/mmal/util/mmal_connection.h" #include "raspicam/RaspiCamControl.h" #include "motion.h" #include "rotate.h" #define MMALCAM_OK 0 #define MMALCAM_ERROR -1 #define MMAL_CAMERA_PREVIEW_PORT 0 #define MMAL_CAMERA_VIDEO_PORT 1 #define MMAL_CAMERA_CAPTURE_PORT 2 #define VIDEO_FRAME_RATE_NUM 30 #define VIDEO_FRAME_RATE_DEN 1 #define VIDEO_OUTPUT_BUFFERS_NUM 3 const int MAX_BITRATE = 30000000; // 30Mbits/s static void parse_camera_control_params(const char *control_params_str, RASPICAM_CAMERA_PARAMETERS *camera_params) { char *control_params_tok = alloca(strlen(control_params_str) + 1); strcpy(control_params_tok, control_params_str); char *next_param = strtok(control_params_tok, " "); while (next_param != NULL) { char *param_val = strtok(NULL, " "); if (raspicamcontrol_parse_cmdline(camera_params, next_param + 1, param_val) < 2) { next_param = param_val; } else { next_param = strtok(NULL, " "); } } } static void check_disable_port(MMAL_PORT_T *port) { if (port && port->is_enabled) { mmal_port_disable(port); } } static void camera_control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) { if (buffer->cmd != MMAL_EVENT_PARAMETER_CHANGED) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "Received unexpected camera control callback event, 0x%08x", buffer->cmd); } mmal_buffer_header_release(buffer); } static void camera_buffer_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) { mmalcam_context_ptr mmalcam = (mmalcam_context_ptr) port->userdata; mmal_queue_put(mmalcam->camera_buffer_queue, buffer); } static void set_port_format(mmalcam_context_ptr mmalcam, MMAL_ES_FORMAT_T *format) { format->encoding = MMAL_ENCODING_OPAQUE; format->encoding_variant = MMAL_ENCODING_I420; format->es->video.width = mmalcam->width; format->es->video.height = mmalcam->height; format->es->video.crop.x = 0; format->es->video.crop.y = 0; format->es->video.crop.width = mmalcam->width; format->es->video.crop.height = mmalcam->height; } static void set_video_port_format(mmalcam_context_ptr mmalcam, MMAL_ES_FORMAT_T *format) { set_port_format(mmalcam, format); format->es->video.frame_rate.num = mmalcam->framerate; format->es->video.frame_rate.den = VIDEO_FRAME_RATE_DEN; } static int create_camera_component(mmalcam_context_ptr mmalcam, const char *mmalcam_name) { MMAL_STATUS_T status; MMAL_COMPONENT_T *camera_component; MMAL_PORT_T *video_port = NULL; status = mmal_component_create(mmalcam_name, &camera_component); if (status != MMAL_SUCCESS) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "Failed to create MMAL camera component %s", mmalcam_name); goto error; } if (camera_component->output_num == 0) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "MMAL camera %s doesn't have output ports", mmalcam_name); goto error; } video_port = camera_component->output[MMAL_CAMERA_VIDEO_PORT]; status = mmal_port_enable(camera_component->control, camera_control_callback); if (status) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "Unable to enable control port : error %d", status); goto error; } // set up the camera configuration { MMAL_PARAMETER_CAMERA_CONFIG_T cam_config = { { MMAL_PARAMETER_CAMERA_CONFIG, sizeof(cam_config) }, .max_stills_w = mmalcam->width, .max_stills_h = mmalcam->height, .stills_yuv422 = 0, .one_shot_stills = 0, .max_preview_video_w = mmalcam->width, .max_preview_video_h = mmalcam->height, .num_preview_video_frames = 3, .stills_capture_circular_buffer_height = 0, .fast_preview_resume = 0, .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RESET_STC }; mmal_port_parameter_set(camera_component->control, &cam_config.hdr); } set_video_port_format(mmalcam, video_port->format); video_port->format->encoding = MMAL_ENCODING_I420; status = mmal_port_format_commit(video_port); if (status) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "camera video format couldn't be set"); goto error; } // Ensure there are enough buffers to avoid dropping frames if (video_port->buffer_num < VIDEO_OUTPUT_BUFFERS_NUM) { video_port->buffer_num = VIDEO_OUTPUT_BUFFERS_NUM; } status = mmal_component_enable(camera_component); if (status) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "camera component couldn't be enabled"); goto error; } raspicamcontrol_set_all_parameters(camera_component, mmalcam->camera_parameters); mmalcam->camera_component = camera_component; mmalcam->camera_capture_port = video_port; mmalcam->camera_capture_port->userdata = (struct MMAL_PORT_USERDATA_T*) mmalcam; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "MMAL camera component created"); return MMALCAM_OK; error: if (mmalcam->camera_component != NULL ) { mmal_component_destroy(camera_component); mmalcam->camera_component = NULL; } return MMALCAM_ERROR; } static void destroy_camera_component(mmalcam_context_ptr mmalcam) { if (mmalcam->camera_component) { mmal_component_destroy(mmalcam->camera_component); mmalcam->camera_component = NULL; } } static int create_camera_buffer_structures(mmalcam_context_ptr mmalcam) { mmalcam->camera_buffer_pool = mmal_pool_create(mmalcam->camera_capture_port->buffer_num, mmalcam->camera_capture_port->buffer_size); if (mmalcam->camera_buffer_pool == NULL ) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "MMAL camera buffer pool creation failed"); return MMALCAM_ERROR; } mmalcam->camera_buffer_queue = mmal_queue_create(); if (mmalcam->camera_buffer_queue == NULL ) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "MMAL camera buffer queue creation failed"); return MMALCAM_ERROR; } return MMALCAM_OK; } static int send_pooled_buffers_to_port(MMAL_POOL_T *pool, MMAL_PORT_T *port) { int num = mmal_queue_length(pool->queue); for (int i = 0; i < num; i++) { MMAL_BUFFER_HEADER_T *buffer = mmal_queue_get(pool->queue); if (!buffer) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "Unable to get a required buffer %d from pool queue", i); return MMALCAM_ERROR; } if (mmal_port_send_buffer(port, buffer) != MMAL_SUCCESS) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "Unable to send a buffer to port (%d)", i); return MMALCAM_ERROR; } } return MMALCAM_OK; } static void destroy_camera_buffer_structures(mmalcam_context_ptr mmalcam) { if (mmalcam->camera_buffer_queue != NULL ) { mmal_queue_destroy(mmalcam->camera_buffer_queue); mmalcam->camera_buffer_queue = NULL; } if (mmalcam->camera_buffer_pool != NULL ) { mmal_pool_destroy(mmalcam->camera_buffer_pool); mmalcam->camera_buffer_pool = NULL; } } /** * mmalcam_start * * This routine is called from the main motion thread. It's job is * to open up the requested camera device via MMAL and do any required * initialisation. * * Parameters: * * cnt Pointer to the motion context structure for this device. * * Returns: 0 on success * -1 on any failure */ int mmalcam_start(struct context *cnt) { mmalcam_context_ptr mmalcam; cnt->mmalcam = (mmalcam_context*) mymalloc(sizeof(struct mmalcam_context)); memset(cnt->mmalcam, 0, sizeof(mmalcam_context)); mmalcam = cnt->mmalcam; mmalcam->cnt = cnt; MOTION_LOG(ALR, TYPE_VIDEO, NO_ERRNO, "%s: MMAL Camera thread starting... for camera (%s) of %d x %d at %d fps", cnt->conf.mmalcam_name, cnt->conf.width, cnt->conf.height, cnt->conf.frame_limit); mmalcam->camera_parameters = (RASPICAM_CAMERA_PARAMETERS*)malloc(sizeof(RASPICAM_CAMERA_PARAMETERS)); if (mmalcam->camera_parameters == NULL) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "camera params couldn't be allocated"); return MMALCAM_ERROR; } raspicamcontrol_set_defaults(mmalcam->camera_parameters); mmalcam->width = cnt->conf.width; mmalcam->height = cnt->conf.height; mmalcam->framerate = cnt->conf.frame_limit; if (cnt->conf.mmalcam_control_params) { parse_camera_control_params(cnt->conf.mmalcam_control_params, mmalcam->camera_parameters); } cnt->imgs.width = mmalcam->width; cnt->imgs.height = mmalcam->height; cnt->imgs.size = (mmalcam->width * mmalcam->height * 3) / 2; cnt->imgs.motionsize = mmalcam->width * mmalcam->height; cnt->imgs.type = VIDEO_PALETTE_YUV420P; int retval = create_camera_component(mmalcam, cnt->conf.mmalcam_name); if (retval == 0) { retval = create_camera_buffer_structures(mmalcam); } if (retval == 0) { if (mmal_port_enable(mmalcam->camera_capture_port, camera_buffer_callback)) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "MMAL camera capture port enabling failed"); retval = MMALCAM_ERROR; } } if (retval == 0) { if (mmal_port_parameter_set_boolean(mmalcam->camera_capture_port, MMAL_PARAMETER_CAPTURE, 1) != MMAL_SUCCESS) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "MMAL camera capture start failed"); retval = MMALCAM_ERROR; } } if (retval == 0) { retval = send_pooled_buffers_to_port(mmalcam->camera_buffer_pool, mmalcam->camera_capture_port); } return retval; } /** * mmalcam_cleanup * * This routine shuts down any MMAL resources, then releases any allocated data * within the mmalcam context and frees the context itself. * 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: * * mmalcam Pointer to a mmalcam context * * Returns: Nothing. * */ void mmalcam_cleanup(struct mmalcam_context *mmalcam) { MOTION_LOG(ALR, TYPE_VIDEO, NO_ERRNO, "MMAL Camera cleanup"); if (mmalcam != NULL ) { if (mmalcam->camera_component) { check_disable_port(mmalcam->camera_capture_port); mmal_component_disable(mmalcam->camera_component); destroy_camera_buffer_structures(mmalcam); destroy_camera_component(mmalcam); } if (mmalcam->camera_parameters) { free(mmalcam->camera_parameters); } free(mmalcam); } } /** * mmalcam_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 Pi camera already in 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 mmalcam_next(struct context *cnt, unsigned char *map) { mmalcam_context_ptr mmalcam; if ((!cnt) || (!cnt->mmalcam)) return NETCAM_FATAL_ERROR; mmalcam = cnt->mmalcam; MMAL_BUFFER_HEADER_T *camera_buffer = mmal_queue_wait(mmalcam->camera_buffer_queue); if (camera_buffer->cmd == 0 && (camera_buffer->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) && camera_buffer->length == cnt->imgs.size) { mmal_buffer_header_mem_lock(camera_buffer); memcpy(map, camera_buffer->data, cnt->imgs.size); mmal_buffer_header_mem_unlock(camera_buffer); } else { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: cmd %d flags %08x size %d/%d at %08x", camera_buffer->cmd, camera_buffer->flags, camera_buffer->length, camera_buffer->alloc_size, camera_buffer->data); } mmal_buffer_header_release(camera_buffer); if (mmalcam->camera_capture_port->is_enabled) { MMAL_STATUS_T status; MMAL_BUFFER_HEADER_T *new_buffer = mmal_queue_get(mmalcam->camera_buffer_pool->queue); if (new_buffer) { status = mmal_port_send_buffer(mmalcam->camera_capture_port, new_buffer); } if (!new_buffer || status != MMAL_SUCCESS) MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "Unable to return a buffer to the camera video port"); } if (cnt->rotate_data.degrees > 0) rotate_map(cnt, map); return 0; } motion-release-4.0/mmalcam.h000066400000000000000000000016431300074623700160660ustar00rootroot00000000000000/* * mmalcam.h * * Include file for mmalcam.c * * Copyright 2013 by Nicholas Tuckett * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. */ #ifndef MMALCAM_H_ #define MMALCAM_H_ typedef struct mmalcam_context *mmalcam_context_ptr; typedef struct mmalcam_context { struct context *cnt; /* pointer to parent motion context structure */ int width; int height; int framerate; struct MMAL_COMPONENT_T *camera_component; struct MMAL_PORT_T *camera_capture_port; struct MMAL_POOL_T *camera_buffer_pool; struct MMAL_QUEUE_T *camera_buffer_queue; struct raspicam_camera_parameters_s *camera_parameters; } mmalcam_context; int mmalcam_start (struct context *); int mmalcam_next (struct context *, unsigned char *); void mmalcam_cleanup (struct mmalcam_context *); #endif /* MMALCAM_H_ */ motion-release-4.0/mmx.h000066400000000000000000000300411300074623700152520ustar00rootroot00000000000000/* * 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 */ motion-release-4.0/motion-dist.conf.in000066400000000000000000000706411300074623700200340ustar00rootroot00000000000000# Rename this distribution example file to motion.conf # # This config file was generated by @PACKAGE_NAME@ @PACKAGE_VERSION@ ############################################################ # Daemon ############################################################ # Start in daemon (background) mode and release terminal (default: off) daemon off # File to store the process ID, also called pid file. (default: not defined) process_id_file /var/run/motion/motion.pid ############################################################ # Basic Setup Mode ############################################################ # Start in Setup-Mode, daemon disabled. (default: off) setup_mode off # Use a file to save logs messages, if not defined stderr and syslog is used. (default: not defined) ;logfile /tmp/motion.log # Level of log messages [1..9] (EMG, ALR, CRT, ERR, WRN, NTC, INF, DBG, ALL). (default: 6 / NTC) log_level 6 # Filter to log messages by type (COR, STR, ENC, NET, DBL, EVT, TRK, VID, ALL). (default: ALL) log_type all ########################################################### # Capture device options ############################################################ # Videodevice to be used for capturing (default /dev/video0) # for FreeBSD default is /dev/bktr0 videodevice /dev/video0 # v4l2_palette allows one to choose preferable palette to be use by motion # to capture from those supported by your videodevice. (default: 17) # E.g. if your videodevice supports both V4L2_PIX_FMT_SBGGR8 and # V4L2_PIX_FMT_MJPEG then motion will by default use V4L2_PIX_FMT_MJPEG. # Setting v4l2_palette to 2 forces motion to use V4L2_PIX_FMT_SBGGR8 # instead. # # Values : # V4L2_PIX_FMT_SN9C10X : 0 'S910' # V4L2_PIX_FMT_SBGGR16 : 1 'BYR2' # V4L2_PIX_FMT_SBGGR8 : 2 'BA81' # V4L2_PIX_FMT_SPCA561 : 3 'S561' # V4L2_PIX_FMT_SGBRG8 : 4 'GBRG' # V4L2_PIX_FMT_SGRBG8 : 5 'GRBG' # V4L2_PIX_FMT_PAC207 : 6 'P207' # V4L2_PIX_FMT_PJPG : 7 'PJPG' # V4L2_PIX_FMT_MJPEG : 8 'MJPEG' # V4L2_PIX_FMT_JPEG : 9 'JPEG' # V4L2_PIX_FMT_RGB24 : 10 'RGB3' # V4L2_PIX_FMT_SPCA501 : 11 'S501' # V4L2_PIX_FMT_SPCA505 : 12 'S505' # V4L2_PIX_FMT_SPCA508 : 13 'S508' # V4L2_PIX_FMT_UYVY : 14 'UYVY' # V4L2_PIX_FMT_YUYV : 15 'YUYV' # V4L2_PIX_FMT_YUV422P : 16 '422P' # V4L2_PIX_FMT_YUV420 : 17 'YU12' # v4l2_palette 17 # 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: -1) # Should normally be set to 0 or 1 for video/TV cards, and -1 for USB cameras # Set to 0 for uvideo(4) on OpenBSD input -1 # 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 # Override the power line frequency for the webcam. (normally not necessary) # Values: # -1 : Do not modify device setting # 0 : Power line frequency Disabled # 1 : 50hz # 2 : 60hz # 3 : Auto power_line_frequency -1 # Rotate image this number of degrees. The rotation affects all saved images as # well as 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 # Minimum time in seconds between capturing picture frames from the camera. # Default: 0 = disabled - the capture rate is given by the camera framerate. # This option is used when you want to capture images at a rate lower than 2 per second. minimum_frame_time 0 # URL to use if you are using a network camera, size will be autodetected (incl http:// ftp:// mjpg:// rtsp:// mjpeg:// or file:///) # Must be a URL that returns single jpeg pictures or a raw mjpeg stream. A trailing slash may be required for some cameras. # 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 # The setting for keep-alive of network socket, should improve performance on compatible net cameras. # off: The historical implementation using HTTP/1.0, closing the socket after each http request. # force: Use HTTP/1.0 requests with keep alive header to reuse the same connection. # on: Use HTTP/1.1 requests that support keep alive as default. # Default: off netcam_keepalive off # 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 # Set less strict jpeg checks for network cameras with a poor/buggy firmware. # Default: off netcam_tolerant_check off # RTSP connection uses TCP to communicate to the camera. Can prevent image corruption. # Default: on rtsp_uses_tcp on # Name of camera to use if you are using a camera accessed through OpenMax/MMAL # Default: Not defined ; mmalcam_name vc.ril.camera # Camera control parameters (see raspivid/raspistill tool documentation) # Default: Not defined ; mmalcam_control_params -hf # 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 # 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_filter EedDl # Detect motion in predefined areas (1 - 9). Areas are numbered like that: 1 2 3 # A script (on_area_detected) is started immediately when motion is 4 5 6 # detected in one of the given areas, but only once during an event. 7 8 9 # One or more areas can be specified with this option. Take care: This option # does NOT restrict detection to these areas! (Default: not defined) ; area_detect value # 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-5 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 movies. To smooth movies 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 # Event 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 -1 is allowed and disables # events causing all Motion to be written to one single movie file and no pre_capture. # If set to 0, motion is running in gapless mode. Movies don't have gaps anymore. An # event ends right after no more motion is detected and post_capture is over. event_gap 60 # Maximum length in seconds of a movie # When value is exceeded a new movie file is created. (Default: 0 = infinite) max_movie_time 0 # Always save images even if there was no motion (default: off) emulate_motion off ############################################################ # Image File Output ############################################################ # Output 'normal' pictures when motion is detected (default: on) # Valid values: on, off, first, best, center # 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'. # Picture with motion nearest center of picture is saved when set to 'center'. # Can be used as preview shot for the corresponding movie. output_pictures on # Output pictures with only the pixels moving object (ghost images) (default: off) output_debug_pictures off # The quality (in percent) to be used by the jpeg compression (default: 75) quality 75 # Type of output images # Valid values: jpeg, ppm (default: jpeg) picture_type jpeg ############################################################ # FFMPEG related options # Film (movies) file output, and deinterlacing of the video input # The options movie_filename and timelapse_filename are also used # by the ffmpeg feature ############################################################ # Use ffmpeg to encode movies in realtime (default: off) ffmpeg_output_movies on # Use ffmpeg to make movies with only the pixels moving # object (ghost images) (default: off) ffmpeg_output_debug_movies 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 400000 # 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 1 - 100 where 1 means worst quality and 100 is best. ffmpeg_variable_bitrate 0 # Codec to used by ffmpeg for the video compression. # Timelapse videos have two options. # mpg - Creates mpg file with mpeg-2 encoding. # If motion is shutdown and restarted, new pics will be appended # to any previously created file with name indicated for timelapse. # mpeg4 - Creates avi file with the default encoding. # If motion is shutdown and restarted, new pics will create a # new file with the name indicated for timelapse. # Supported formats are: # mpeg4 or msmpeg4 - gives 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. # swf - gives you a flash film with extension .swf # flv - gives you a flash video with extension .flv # ffv1 - FF video codec 1 for Lossless Encoding # mov - QuickTime # mp4 - MPEG-4 Part 14 H264 encoding # mkv - Matroska H264 encoding # hevc - H.265 / HEVC (High Efficiency Video Coding) ffmpeg_video_codec mpeg4 # When creating videos, should frames be duplicated in order # to keep up with the requested frames per second # (default: true) ffmpeg_duplicate_frames true ############################################################ # SDL Window ############################################################ # Number of motion thread to show in SDL Window (default: 0 = disabled) #sdl_threadnr 0 ############################################################ # External pipe to video encoder # Replacement for FFMPEG builtin encoder for ffmpeg_output_movies only. # The options movie_filename and timelapse_filename are also used # by the ffmpeg feature ############################################################# # Bool to enable or disable extpipe (default: off) use_extpipe off # External program (full path and opts) to pipe raw video to # Generally, use '-' for STDIN... ;extpipe mencoder -demuxer rawvideo -rawvideo w=%w:h=%h:i420 -ovc x264 -x264encopts bframes=4:frameref=1:subq=1:scenecut=-1:nob_adapt:threads=1:keyint=1000:8x8dct:vbv_bufsize=4000:crf=24:partitions=i8x8,i4x4:vbv_maxrate=800:no-chroma-me -vf denoise3d=16:12:48:4,pp=lb -of avi -o %f.avi - -fps %fps ;extpipe x264 - --input-res %wx%h --fps %fps --bitrate 2000 --preset ultrafast --quiet -o %f.mp4 ;extpipe mencoder -demuxer rawvideo -rawvideo w=%w:h=%h:fps=%fps -ovc x264 -x264encopts preset=ultrafast -of lavf -o %f.mp4 - -fps %fps ;extpipe ffmpeg -y -f rawvideo -pix_fmt yuv420p -video_size %wx%h -framerate %fps -i pipe:0 -vcodec libx264 -preset ultrafast -f mp4 %f.mp4 ############################################################ # 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 = camera id 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, preview (default: off) # Set to 'preview' will only draw a box in preview_shot pictures. locate_motion_mode off # Set the look and style of the locate box if enabled. # Valid values: box, redbox, cross, redcross (default: box) # Set to 'box' will draw the traditional box. # Set to 'redbox' will draw a red box. # Set to 'cross' will draw a little cross to mark center. # Set to 'redcross' will draw a little red cross to mark center. locate_motion_style box # 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 special 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 # Text to include in a JPEG EXIF comment # May be any text, including conversion specifiers. # The EXIF timestamp is included independent of this text. ;exif_text %i%J/%K%L ############################################################ # Target Directories and filenames For Images And Films # For the options snapshot_, picture_, movie_ 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 = camera id 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/motion # 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 picture_filename %v-%Y%m%d%H%M%S-%q # File path for motion triggered ffmpeg films (movies) relative to target_dir # Default: %v-%Y%m%d%H%M%S # File extensions(.mpg .avi) are automatically added so do not include them movie_filename %v-%Y%m%d%H%M%S # File path for timelapse movies relative to target_dir # Default: %Y%m%d-timelapse # File extensions(.mpg .avi) are automatically added so do not include them timelapse_filename %Y%m%d-timelapse ############################################################ # Global Network Options ############################################################ # Enable IPv6 (default: off) ipv6_enabled off ############################################################ # Live Stream Server ############################################################ # The mini-http server listens to this port for requests (default: 0 = disabled) stream_port 8081 # Quality of the jpeg (in percent) images produced (default: 50) stream_quality 50 # Output frames at 1 fps when no motion is detected and increase to the # rate given by stream_maxrate when motion is detected (default: off) stream_motion off # Maximum framerate for stream streams (default: 1) stream_maxrate 1 # Restrict stream connections to localhost only (default: on) stream_localhost on # Limits the number of images per connection (default: 0 = unlimited) # Number can be defined by multiplying actual stream rate by desired number of seconds # Actual stream rate is the smallest of the numbers framerate and stream_maxrate stream_limit 0 # Set the authentication method (default: 0) # 0 = disabled # 1 = Basic authentication # 2 = MD5 digest (the safer authentication) stream_auth_method 0 # Authentication for the stream. Syntax username:password # Default: not defined (Disabled) ; stream_authentication username:password # Percentage to scale the stream image for preview # Default: 25 ; stream_preview_scale 25 # Have stream preview image start on a new line # Default: no ; stream_preview_newline no ############################################################ # HTTP Based Control ############################################################ # TCP/IP port for the http server to listen on (default: 0 = disabled) webcontrol_port 8080 # Restrict control connections to localhost only (default: on) webcontrol_localhost on # Output for http server, select off to choose raw text plain (default: on) webcontrol_html_output on # Authentication for the http based control. Syntax username:password # Default: not defined (Disabled) ; webcontrol_authentication username:password ############################################################ # Tracking (Pan/Tilt) ############################################################# # Type of tracker (0=none (default), 1=stepper, 2=iomojo, 3=pwc, 4=generic, 5=uvcvideo, 6=servo) # The generic type enables the definition of motion center and motion size to # be used with the conversion 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 /dev/ttyS0 # Motor number for x-axis (default: 0) ;track_motorx 0 # Set motorx reverse (default: 0) ;track_motorx_reverse 0 # Motor number for y-axis (default: 0) ;track_motory 1 # Set motory reverse (default: 0) ;track_motory_reverse 0 # Maximum value on x-axis (default: 0) ;track_maxx 200 # Minimum value on x-axis (default: 0) ;track_minx 50 # Maximum value on y-axis (default: 0) ;track_maxy 200 # Minimum value on y-axis (default: 0) ;track_miny 50 # Center value on x-axis (default: 0) ;track_homex 128 # Center value on y-axis (default: 0) ;track_homey 128 # 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 tracking movement as number # of picture frames (default: 10) track_move_wait 10 # Speed to set the motor to (stepper motor option) (default: 255) track_speed 255 # Number of steps to make (stepper motor option) (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 = camera id 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 event_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 event_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 motion in a predefined area is detected # Check option 'area_detect'. (default: none) ; on_area_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 # Command to be executed when a camera can't be opened or if it is lost # NOTE: There is situations when motion don't detect a lost camera! # It depends on the driver, some drivers dosn't detect a lost camera at all # Some hangs the motion thread. Some even hangs the PC! (default: none) ; on_camera_lost value ##################################################################### # Common Options for database features. # Options require database options to be active also. ##################################################################### # Log to the database when creating motion triggered picture file (default: on) ; sql_log_picture 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 movie file (default: off) ; sql_log_movie off # Log to the database when creating timelapse movies 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: # Create tables : ## # Mysql # CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp(14), event_time_stamp timestamp(14)); # # Postgresql # CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp without time zone, event_time_stamp timestamp without time zone); # # 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 ############################################################ # database type : mysql, postgresql, sqlite3 (default : not defined) ; database_type value # database to log to (default: not defined) # for sqlite3, the full path and name for the database. ; database_dbname value # The host on which the database is located (default: localhost) ; database_host value # User account name for database (default: not defined) ; database_user value # User password for database (default: not defined) ; database_password value # Port on which the database is located # mysql 3306 , postgresql 5432 (default: not defined) ; database_port value # Database wait time in milliseconds for locked database to # be unlocked before returning database locked error (default 0) ; database_busy_timeout 0 ############################################################ # 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 ############################################################## # camera 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 camera # config file for each camera in addition to this config file. ############################################################## # Remember: If you have more than one camera you must have one # camera file for each camera. E.g. 2 cameras requires 3 files: # This motion.conf file AND camera1.conf and camera2.conf. # Only put the options that are unique to each camera in the # camera config files. ; camera @sysconfdir@/motion/camera1.conf ; camera @sysconfdir@/motion/camera2.conf ; camera @sysconfdir@/motion/camera3.conf ; camera @sysconfdir@/motion/camera4.conf ############################################################## # Camera config directory - One for each camera. ############################################################## # ; camera_dir @sysconfdir@/motion/conf.d motion-release-4.0/motion.1000066400000000000000000001225241300074623700156770ustar00rootroot00000000000000.TH MOTION 1 2016-09-28 "Motion" "Motion Options and Config Files" .SH NAME motion \- Detect motion using a video4linux device or network camera .SH SYNOPSIS .B motion [ \-hbnsm ] [ \-c config file path ] [ \-d level ] [ \-k level ] [ \-p process_id_file ][ \-l log_file ] .SH DESCRIPTION .I Motion uses a video4linux device or network camera to detect motion. If motion is detected both normal and motion pictures can 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. For example: \fI /home/kurt/motion.conf \fR The default is \fI /usr/local/etc/motion\fR unless specified differently when building Motion. Many RPMs and Debian packages will use \fI/etc\fR or \fI/etc/motion\fR as the default. .TP .B \-h Show help screen. .TP .B \-b Run in daemon mode. .TP .B \-n Run in non-daemon mode. .TP .B \-s Run in setup mode. Also forces non-daemon mode .TP .B \-d Run with message log level 1-9. .TP .B \-k Run with message log type 1-9 .TP .B \-l Full path and file name for the log file .TP .B \-p Full path and filename for process id file (pid file). E.g /var/run/motion.pid. Default is not defined. Pid file is only created when Motion is started in daemon mode. .TP .B \-m Start in pause 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 daemon .RS .nf Values: on/off Default: off Description: .fi .RS When specified as ON, Motion goes into daemon (background process) mode and releases the terminal. .RE .RE .TP .B process_id_file .RS .nf Values: User specified string Default: Not defined Description: .fi .RS File to store the process ID, also called pid file. .RE .RE .TP .B setup_mode .RS .nf Values: on/off Default: off Description: .fi .RS Start in Setup-Mode, daemon disabled. .RE .RE .TP .B logfile .RS .nf Values: User specified string Default: Not Defined Description: .fi .RS File to save logs messages, if not defined stderr and syslog is used. .RE .RE .TP .B log_level .RS .nf Values: 1 to 9 (EMG, ALR, CRT, ERR, WRN, NTC, INF, DBG, ALL) Default: 6 / NTC Description: .fi .RS Specify the level of verbosity in the messages sent to the log. .RE .RE .TP .B log_type .RS .nf Values: COR, STR, ENC, NET, DBL, EVT, TRK, VID, ALL Default: ALL Description: .fi .RS Filter to log messages by type .RE .RE .TP .B camera_id .RS .nf Values: Numeric identification number for the camera Default: The order in which the camera is opened by Motion Description: .fi .RS Use this option to assign a number to each camera that is consistent every time that Motion is started. By default Motion will assign a number based upon the sequence in which it reads the configuration and this sequence may not be the same every time that Motion starts. .RE .RE .TP .B camera_name .RS .nf Values: User specified string Default: Not Defined Description: .fi .RS User specified string to describe the camera. .RE .RE .TP .B videodevice .RS .nf Values: User specified string Default: /dev/video0 Description: .fi .RS String to specify the videodevice to be used for capturing. For Linux, the format is usually /dev/videoX where X varies depending upon the video devices connected to the computer. For FreeBSD the default device would be /dev/bktr0. .RE .RE .TP .B v4l2_palette .RS .nf Values: 0 to 17 .RS V4L2_PIX_FMT_SN9C10X : 0 'S910' V4L2_PIX_FMT_SBGGR16 : 1 'BYR2' V4L2_PIX_FMT_SBGGR8 : 2 'BA81' V4L2_PIX_FMT_SPCA561 : 3 'S561' V4L2_PIX_FMT_SGBRG8 : 4 'GBRG' V4L2_PIX_FMT_SGRBG8 : 5 'GRBG' V4L2_PIX_FMT_PAC207 : 6 'P207' V4L2_PIX_FMT_PJPG : 7 'PJPG' V4L2_PIX_FMT_MJPEG : 8 'MJPEG' V4L2_PIX_FMT_JPEG : 9 'JPEG' V4L2_PIX_FMT_RGB24 : 10 'RGB3' V4L2_PIX_FMT_SPCA501 : 11 'S501' V4L2_PIX_FMT_SPCA505 : 12 'S505' V4L2_PIX_FMT_SPCA508 : 13 'S508' V4L2_PIX_FMT_UYVY : 14 'UYVY' V4L2_PIX_FMT_YUYV : 15 'YUYV' V4L2_PIX_FMT_YUV422P : 16 '422P' V4L2_PIX_FMT_YUV420 : 17 'YU12' .RE Default: 17 Description: .fi .RS The v4l2_palette option allows users to choose the preferred palette to be use by motion to capture from the video device. If the preferred palette is not available from the video device, Motion will attempt to use palettes that are supported. .RE .RE .TPq .B tunerdevice .RS .nf Values: User Specified String Default: /dev/tuner0 Description: .fi .RS Tuner device to be used for capturing images. This is ONLY used for FreeBSD. .RE .RE .TP .B input .RS .nf Values: .RS \-1 : USB Cameras 0 : video/TV cards or uvideo(4) on OpenBSD 1 : video/TV cards .RE Default: \-1 Description: .fi .RS The video input to be used. .RE .RE .TP .B norm .RS .nf Values: .RS 0 (PAL) 1 (NTSC) 2 (SECAM) 3 (PAL NC no colour) .RE Default: 0 (PAL) Description: .fi .RS The video norm to use when capturing from TV tuner cards .RE .RE .TP .B frequency .RS .nf Values: Dependent upon video device Default: 0 Description: .fi .RS The frequency to set the tuner in kHz when using a TV tuner card. .RE .RE .TP .B power_line_frequency .RS .nf Values: .RS -1 : Do not modify device setting 0 : Power line frequency Disabled 1 : 50hz 2 : 60hz 3 : Auto .RE Default: -1 Description: .fi .RS Override the power line frequency for the video device. .RE .RE .TP .B rotate .RS .nf Values: 0, 90, 180, 270 Default: 0 Description: .fi .RS Rotate image this number of degrees. The rotation affects all saved images as well as movies. .RE .RE .TP .B width .RS .nf Values: Dependent upon video device Default: 352 Description: .fi .RS Image width in pixels for the video device. .RE .RE .TP .B height .RS .nf Values: Dependent upon video device Default: off Description: .fi .RS Image height in pixels for the video device .RE .RE .TP .B framerate .RS .nf Values: 2 - 100 Default: 100 Description: .fi .RS The maximum number of frames to capture in 1 second. The default of 100 will normally be limited by the capabilities of the video device. Typical video devices have a maximum rate of 30. .RE .RE .TP .B minimum_frame_time .RS .nf Values: 0 to unlimited Default: 0 Description: .fi .RS The minimum time in seconds between capturing picture frames from the camera. The default of 0 disables this option and relies upon the capture rate of the camera. This option is used when you want to capture images at a rate lower than 2 per second. .RE .RE .TP .B netcam_url .RS .nf Values: User specified string Default: None Description: .fi .RS Full connection URL string to use to connect to a network camera. The URL must provide a stream of images instead of only a static image. The following prefixes are recognized .RS http:// ftp:// mjpg:// rtsp:// mjpeg:// file:// .RE The connection string is camera specific. It is usually the same as what other video playing applications would use to connect to the camera stream. Motion currently only supports basic authentication for the cameras. Digest is not currently supported. Basic authentication can be specified in the URL or via the netcam_userpass option. .RE .RE .TP .B netcam_userpass .RS .nf Values: User specified string Default: Not Defined Description: .fi .RS The user id and password required to access the network camera string. Only basic authentication is supported at this time. Format is in user:password format when both a user name and password are required. .RE .RE .TP .B netcam_keepalive .RS .nf Values: .RS .fi off: The historical implementation using HTTP/1.0, closing the socket after each http request. .nf .fi force: Use HTTP/1.0 requests with keep alive header to reuse the same connection. .nf .fi on: Use HTTP/1.1 requests that support keep alive as default. .nf .RE Default: off Description: .fi .RS This setting is to keep-alive (open) the network socket between requests. When used, this option should improve performance on compatible net cameras. This option is not applicable for the rtsp:// and mjpeg:// formats. .RE .RE .TP .B netcam_proxy .RS .nf Values: User specified string Default: Not defined Description: .fi .RS If required, the URL to use for a netcam proxy server. For example, "http://myproxy". If a port number other than 80 is needed, append to the specification. For examplet, "http://myproxy:1234". .RE .RE .TP .B netcam_tolerant_check .RS .nf Values: on/off Default: off Description: .fi .RS Use a less strict jpeg validation for network cameras. This can assist with cameras that have poor or buggy firmware. .RE .RE .TP .B rtsp_uses_tcp .RS .nf Values: on/off Default: on Description: .fi .RS When using a RTSP connection for a network camera, use a TCP transport instead of UDP. The UDP transport frequently results in "smeared" corrupt images. .RE .RE .TP .B mmalcam_name .RS .nf Values: User specified string Default: Not defined Description: .fi .RS Name of camera to use if you are using a camera accessed through OpenMax/MMAL. This value is used to specify the use of the PI camera. The typical value for the PI camera is vc.ril.camera .RE .RE .TP .B mmalcam_control_params .RS .nf Values: User specified string Default: Not defined Description: .fi .RS Camera configuration options to use for the OpenMax/MMAL camera. See the raspivid/raspistill tool documentation for full list of options. Typical value for the PI camera is -hf .RE .RE .TP .B auto_brightness .RS .nf Values: on/off Default: off Description: .fi .RS 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. .RE .RE .TP .B brightness .RS .nf Values: 0 to 255 Default: 0 (Disabled) Description: .fi .RS 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. .RE .RE .TP .B contrast .RS .nf Values: 0 to 255 Default: 0 (Disabled) Description: .fi .RS The contrast value to set for the video device. .RE .RE .TP .B saturation .RS .nf Values: 0 to 255 Default: 0 (Disabled) Description: .fi .RS The saturation value to set for the video device. .RE .RE .TP .B hue .RS .nf Values: 0 to 255 Default: 0 (Disabled) Description: .fi .RS The hue value to set for the video device. .RE .RE .TP .B roundrobin_frames .RS .nf Values: 1 to unlimited Default: 1 Description: .fi .RS Number of frames to capture in each roundrobin step .RE .RE .TP .B roundrobin_skip .RS .nf Values: 1 to unlimited Default: 1 Description: .fi .RS Number of frames to skip before each roundrobin step .RE .RE .TP .B switchfilter .RS .nf Values: on/off Default: off Description: .fi .RS Filter out noise generated by roundrobin .RE .RE .TP .B threshold .RS .nf Values: 1 to unlimited Default: 1500 Description: .fi .RS Threshold for number of changed pixels in an image that triggers motion detection .RE .RE .TP .B threshold_tune .RS .nf Values: on/off Default: off Description: .fi .RS Automatically tune the threshold down if possible. .RE .RE .TP .B noise_level .RS .nf Values: 1 to unlimited Default: 32 Description: .fi .RS Noise threshold for the motion detection. .RE .RE .TP .B noise_tune .RS .nf Values: on/off Default: on Description: .fi .RS Automatically tune the noise threshold .RE .RE .TP .B despeckle_filter .RS .nf Values: .RS e/E : erode d/D : dilate l : label .RE Default: EedDl Description: .fi .RS Despeckle motion image using (e)rode or (d)ilate or (l)abel. The 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 .RE .RE .TP .B area_detect .RS .nf Values: 1 to 9 Default: Not Defined Description: .fi .RS When motion is detected in the predefined areas indicated below, trigger the script indicated by the on_area_detected. The trigger is only activated once during an event. one or more areas can be specified with this option. Note that this option is only used to trigger the indicated script. It does not limit all motion detection events to only the area indicated. .RS Image Areas .RE .RS 123 .RE .RS 456 .RE .RS 789 .RE .RE .RE .TP .B mask_file .RS .nf Values: User specified string Default: Not defined Description: .fi .RS When particular area should be ignored for motion, it can be accomplished using a PGM mask file. The PGM mask file is a specially constructed mask file that allows the user to indicate the areas for which motion should be monitored. This option specifies the full path and name for the mask file. .RE .RE .TP .B smart_mask_speed .RS .nf Values: 0 to 10 Default: 0 (off) Description: .fi .RS Speed of mask changes when creating a dynamic mask file. .RE .RE .TP .B lightswitch .RS .nf Values: 0 to 100 Default: 0 Description: .fi .RS Ignore sudden massive light intensity changes. Value is a percentage of the picture area that changed intensity. .RE .RE .TP .B minimum_motion_frames .RS .nf Values: 1 to unlimited Default: 1 Description: .fi .RS The minimum number of picture frames in a row that must contain motion before a event is triggered. The default of 1 means that all motion is detected. The recommended range is 1 to 5. .RE .RE .TP .B pre_capture .RS .nf Values: 0 to unlimited Default: 0 Description: .fi .RS The number of pre-captured (buffered) pictures from before motion was detected that will be output upon motion detection. The recommended range is 0 to 5. It is not recommended to use large values since it will cause Motion to skip frames. To smooth movies use larger values of post_capture instead. .RE .RE .TP .B post_capture .RS .nf Values: 0 to unlimited Default: 0 Description: .fi .RS Number of frames to capture after motion is no longer detected. .RE .RE .TP .B event_gap .RS .nf Values: -1 to unlimited Default: 60 Description: .fi .RS The number of seconds of no motion that triggers the end of an event. An event is defined as a series of motion images taken within a short timeframe. The recommended value is 60 seconds. The value -1 is allowed and disables events causing all Motion to be written to one single movie file and no pre_capture. If set to 0, motion is running in gapless mode. Movies don't have gaps anymore. An event ends right after no more motion is detected and post_capture is over. .RE .RE .TP .B max_movie_time .RS .nf Values: 0 to unlimited Default: 0 Description: .fi .RS Maximum length in seconds of a movie. When value is exceeded a new movie file is created. The value of 0 means that there is no limit. .RE .RE .TP .B emulate_motion .RS .nf Values: on/off Default: off Description: .fi .RS Always save images even if there was no motion. .RE .RE .TP .B output_pictures .RS .nf Values: on, off, first, best, center Default: on Description: .fi .RS Output pictures when motion is detected. 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'. Picture with motion nearest center of picture is saved when set to 'center'. Can be used as preview shot for the corresponding movie. .RE .RE .TP .B output_debug_pictures .RS .nf Values: on/off Default: off Description: .fi .RS Output pictures with only the pixels moving object (ghost images) .RE .RE .TP .B quality .RS .nf Values: 1 to 100 Default: 75 Description: .fi .RS The quality (in percent) to be used by the jpeg compression .RE .RE .TP .B picture_type .RS .nf Values: jpeg/ppm Default: jpeg Description: .fi .RS The file type of output images .RE .RE .TP .B ffmpeg_output_movies .RS .nf Values: on/off Default: off Description: .fi .RS Use ffmpeg to encode movies of the motion. .RE .RE .TP .B ffmpeg_output_debug_movies .RS .nf Values: on/off Default: off Description: .fi .RS Use ffmpeg to encode movies with only the pixels moving object (ghost images) .RE .RE .TP .B ffmpeg_timelapse .RS .nf Values: 0 to unlimited Default: 0 Description: .fi .RS Number of seconds between frame captures for a timelapse movie. Specify 0 to disable the timelapse. .RE .RE .TP .B ffmpeg_timelapse_mode .RS .nf Values: .RS hourly .br daily .br weekly-sunday .br weekly-monday .br monthly .br manual .RE Default: daily Description: .fi .RS File rollover mode for the timelapse video. .RE .RE .TP .B ffmpeg_bps .RS .nf Values: 0 to unlimited Default: 400000 Description: .fi .RS Bitrate to be used by the ffmpeg encoder. This option is ignored if ffmpeg_variable_bitrate is not 0. .RE .RE .TP .B ffmpeg_variable_bitrate .RS .nf Values: 0 to 100 Default: 0 Description: .fi .RS Enable and define the variable bitrate for the ffmpeg encoder. ffmpeg_bps is ignored if variable bitrate is enabled. When specified as 0, use the fixed bitrate defined by ffmpeg_bps. When defined as 1 - 100 varies the quality of the movie. A value of 1 is worst quality versus a value of 100 is best quality. .RE .RE .TP .B ffmpeg_video_codec .RS .nf Values: .RS Timelapse videos: .RS mpg - Creates mpg file with mpeg-2 encoding. mpeg4 - Creates avi file with the default encoding. .RE Motion videos: .RS mpeg4 - Creates .avi file msmpeg4 - Creates .avi file swf - Flash film with extension .swf flv - Flash video with extension .flv ffv1 - FF video codec 1 for Lossless Encoding mov - QuickTime mp4 - MPEG-4 Part 14 H264 encoding mkv - Matroska H264 encoding hevc - H.265 / HEVC (High Efficiency Video Coding) .RE .RE Default: mpeg4 Description: .fi .RS The container and codec to use when creating videos. When creating timelapse videos, there are only two options and the processing varies due to container/codec limitations. For mpg timelapse videos, if motion is shutdown and restarted, new pics will be appended to any previously created file with name indicated for timelapse. For mpeg4 timelapse videos, if motion is shutdown and restarted, new pics will create a new file with the name indicated for timelapse. For regular motion videos, the container/codec must be available in the ffmpeg installed on the computer. .RE .RE .TP .B ffmpeg_duplicate_frames .RS .nf Values: on/off Default: off Description: .fi .RS When creating videos, should frames be duplicated in order to keep up with the requested frames per second .RE .RE .TP .B sdl_threadnr .RS .nf Values: 0 to unlimited Default: 0 Description: .fi .RS Number of motion thread to show in SDL Window .RE .RE .TP .B use_extpipe .RS .nf Values: on/off Default: off Description: .fi .RS Use the external pipe in order to encode videos. This is a replacement option for the FFMPEG builtin encoder for ffmpeg_output_movies only. The options movie_filename and timelapse_filename are also used from the ffmpeg feature .RE .RE .TP .B extpipe .RS .nf Values: User specified string Default: Not defined Description: .fi .RS Command line string to receive and process a pipe of images to encode. Generally, use '-' for STDIN .RE .RE .TP .B snapshot_interval .RS .nf Values: 0 to unlimited Default: 0 Description: .fi .RS When specified as 0, the snapshot feature is disabled. When a value is specified, the value indicates the number of seconds between snapshots. .RE .RE .TP .B locate_motion_mode .RS .nf Values: on/off/preview Default: off Description: .fi .RS When specified as 'on', locate and draw a box around the moving object. When set 'preview', only draw a box in preview_shot pictures. .RE .RE .TP .B locate_motion_style .RS .nf Values: .RS .fi box : Draw traditional box around the part of the image generating the motion .nf .fi redbox : Draw a red box around the part of the image generating the motion .nf .fi cross : Draw a cross on the part of the image generating the motion .nf .fi redcross : Draw a red cross on the part of the image generating the motion .nf .RE Default: box Description: .fi .RS When locate_motion_mode is enable, this option specifies how the motion will be indicated on the image. .RE .RE .TP .B text_right .RS .nf Values: User specified string Default: %Y-%m-%d\n%T-%q Description: .fi .RS Text to place in lower right corner of image. Format specifiers follow C function strftime(3) .RE .RE .TP .B text_left .RS .nf Values: User specified string Default: CAMERA %t Description: .fi .RS Text to place in lower left corner of image. Format specifiers follow C function strftime(3) .RE .RE .TP .B text_changes .RS .nf Values: on/off Default: off Description: .fi .RS When specified, draw the number of changed pixed on the images. This option will normally be set to off except when you setup and adjust the motion settings. The text is placed in upper right corner of the image. .RE .RE .TP .B text_event .RS .nf Values: User specified string Default: %Y%m%d%H%M%S Description: .fi .RS Define the value of the special event conversion specifier %C. The user 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 %C can be used filenames and text_left/right for creating a unique identifier for each event. .RE .RE .TP .B text_double .RS .nf Values: on/off Default: off Description: .fi .RS Draw characters at twice normal size on images. .RE .RE .TP .B exif_text .RS .nf Values: User specified string Default: Not defined Description: .fi .RS Text to include in a JPEG EXIF comment .RE .RE .TP .B target_dir .RS .nf Values: User specified string Default: Not defined Description: .fi .RS Target base directory for pictures and films. It is recommended to use an absolute path. If this option is not defined, the current working directory is used. This option accepts the conversion specifiers included at the end of this manual. .RE .RE .TP .B snapshot_filename .RS .nf Values: User specified string Default: %v-%Y%m%d%H%M%S-snapshot Description: .fi .RS The file path for snapshots relative to target_dir. The file extension .jpg or .ppm is automatically added so do not include this. A symbolic link called lastsnap.jpg created in the target_dir will always point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap' This option accepts the conversion specifiers included at the end of this manual. .RE .RE .TP .B picture_filename .RS .nf Values: User specified string Default: %v-%Y%m%d%H%M%S-%q Description: .fi .RS The file path for motion triggered images (jpeg or ppm) relative to target_dir. The file extension of .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. This option accepts the conversion specifiers included at the end of this manual. .RE .RE .TP .B movie_filename .RS .nf Values: User specified string Default: %v-%Y%m%d%H%M%S Description: .fi .RS File path for motion triggered ffmpeg films (movies) relative to target_dir. The extensions(.swf, .avi, etc) are automatically added so do not include them This option accepts the conversion specifiers included at the end of this manual. .RE .RE .TP .B timelapse_filename .RS .nf Values: User specified string Default: %Y%m%d-timelapse Description: .fi .RS File path for timelapse movies relative to target_dir. The file extensions(.mpg .avi) are automatically added so do not include them This option accepts the conversion specifiers included at the end of this manual. .RE .RE .TP .B ipv6_enabled .RS .nf Values: on/off Default: off Description: .fi .RS Listen to IPv6 localhost instead of IPv4 .RE .RE .TP .B stream_port .RS .nf Values: 0 to port number limit Default: 0 Description: .fi .RS This option is the port number that the mini-http server listens on for streams of the pictures. .RE .RE .TP .B stream_quality .RS .nf Values: 1 to 100 Default: 50 Description: .fi .RS The quality in percent for the jpg images streamed. .RE .RE .TP .B stream_motion .RS .nf Values: on/off Default: off Description: .fi .RS Output frames at 1 fps when no motion is detected and increase to the rate given by stream_maxrate when motion is detected .RE .RE .TP .B stream_maxrate .RS .nf Values: 1 to unlimited Default: 1 Description: .fi .RS Maximum frame rate to send to stream .RE .RE .TP .B stream_localhost .RS .nf Values: on/off Default: off Description: .fi .RS Restrict stream connections to localhost only .RE .RE .TP .B stream_limit .RS .nf Values: 0 to unlimited Default: 0 Description: .fi .RS Limits the number of images per connection. The default value of 0 means unlimited. The number can be defined by multiplying actual stream rate by the desired number of seconds. The actual stream rate is the smallest of the framerate and stream_maxrate. .RE .RE .TP .B stream_auth_method .RS .nf Values: .RS 0 = disabled 1 = Basic authentication 2 = MD5 digest (the safer authentication) .RE Default: 0 Description: .fi .RS The authentication method to use for viewing the stream. .RE .RE .TP .B stream_authentication .RS .nf Values: User specified string Default: Not defined Description: .fi .RS The username and password to use for authentication of the stream. The format is Username:Password .RE .RE .TP .B stream_preview_scale .RS .nf Values: 1 to 100 Default: 25 Description: .fi .RS This defines what percentage the stream image should be scaled to for the preview page .RE .RE .TP .B stream_preview_newline .RS .nf Values: yes/no Default: no Description: .fi .RS When the image is put on the preview page, should the image start on a new line. This option allows the user to specify whether the preview images should be side by side or stacked on the page. .RE .RE .TP .B webcontrol_port .RS .nf Values: 0 to maximum port number Default: 0 Description: .fi .RS Port number for the web control / preview page. .RE .RE .TP .B webcontrol_localhost .RS .nf Values: on/off Default: off Description: .fi .RS Restrict control connections to localhost only .RE .RE .TP .B webcontrol_html_output .RS .nf Values: on/off Default: on Description: .fi .RS Specified whether the web control/preview page should be raw or html format. Preview page is always html. .RE .RE .TP .B webcontrol_authentication .RS .nf Values: .RS 0 = disabled 1 = Basic authentication 2 = MD5 digest (the safer authentication) .RE Default: 0 Description: .fi .RS The authentication method to use for viewing the web control. .RE .RE .TP .B track_type .RS .nf Values: .RS 0 = none 1 = stepper 2 = iomojo 3 = pwc 4 = generic 5 = uvcvideo 6 = servo .RE Default: 0 Description: .fi .RS This option specifies the type of tracker. The generic type enables the definition of motion center and motion size to be used with the conversion specifiers for options like on_motion_detected .RE .RE .TP .B track_auto .RS .nf Values: on/off Default: off Description: .fi .RS Enables/disables the automatic tracking. .RE .RE .TP .B track_port .RS .nf Values: User specified string Default: Not defined Description: .fi .RS The serial port of the motor. For example /dev/ttyS0 .RE .RE .TP .B Tracking options .RS .nf track_motorx, track_motorx_reverse, track_motory, track_motory_reverse track_maxx, track_minx, track_maxy, track_miny, track_homex, track_homey track_iomojo_id, track_step_angle_x, track_step_angle_y, track_move_wait track_speed, track_stepsize .fi .RE .RS .nf Values: device dependent Default: 0 Description: .fi .RS These options specify the parameters for cameras with tracking capabilities. .RE .RE .TP .B quiet .RS .nf Values: on/off Default: on Description: .fi .RS Do not sound beeps when detecting motion .RE .RE .TP .B Script Options .RS .nf on_event_start, on_event_end, on_picture_save on_motion_detected, on_area_detected, on_movie_start on_movie_end, on_camera_lost .fi .RE .RS .nf Values: User defined string Default: Not defined Description: .fi .RS Specify the full path and file name for the script to execute when the indicated event occurs. When a file name is required for the script, append a %f to the script string. .RE .RE .TP .B sql_log_picture .RS .nf Values: on/off Default: on Description: .fi .RS Log to the database when creating motion triggered picture file .RE .RE .TP .B sql_log_snapshot .RS .nf Values: on/off Default: on Description: .fi .RS Log to the database when creating a snapshot image file .RE .RE .TP .B sql_log_movie .RS .nf Values: on/off Default: off Description: .fi .RS Log to the database when creating motion triggered movie file .RE .RE .TP .B sql_log_timelapse .RS .nf Values: on/off Default: off Description: .fi .RS Log to the database when creating timelapse movies file .RE .RE .TP .B sql_query .RS .nf Values: User defined string Default: Not defined Description: .fi .RS SQL statement to execute when a event occurs. Use same conversion specifiers as for text features Additional special conversion specifiers are .RS %n = the number representing the file_type %f = filename with full path .RE Sample table set up (not sql_query): .RS Mysql: CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp(14), event_time_stamp timestamp(14)); .RE .RS Postgresql: CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp without time zone, event_time_stamp timestamp without time zone); .RE Sample sql_query .RS insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') .RE .RE .RE .TP .B database_type .RS .nf Values: mysql, postgresql, sqlite3 Default: Not defined Description: .fi .RS The type of database being used. .RE .RE .TP .B database_dbname .RS .nf Values: User defined string Default: Not defined Description: .fi .RS The name of the database being used (dbname). For Sqlite3, the full path to the database. .RE .RE .TP .B database_host .RS .nf Values: User defined string Default: Not Defined Description: .fi .RS The name of the host on which the database is running. .RE .RE .TP .B database_user .RS .nf Values: User defined string Default: Not Defined Description: .fi .RS The username to access the database .RE .RE .TP .B database_password .RS .nf Values: User defined string Default: Not Defined Description: .fi .RS The database password for the user to access the database. .RE .RE .TP .B database_port .RS .nf Values: 0 to maximum port number Default: Not defined Description: .fi .RS The port to use in order to access the database. Default ports: mysql 3306 , postgresql 5432 .RE .RE .TP .B database_busy_timeout .RS .nf Values: 0 to unlimited Default: 0 Description: .fi .RS Database wait time in milliseconds for locked database to be unlocked before returning database locked error .RE .RE .TP .B video_pipe .RS .nf Values: User specified string Default: Not Defined Description: .fi .RS Output images to a video4linux loopback device. The value '-' means next available .RE .RE .TP .B motion_video_pipe .RS .nf Values: User specified string Default: Not Defined Description: .fi .RS Output motion images to a video4linux loopback device. The value '-' means next available .RE .RE .TP .B camera_dir .RS .nf Values: User specified string Default: Not Defined Description: .fi .RS This option specifies the optional subdirectory that contains the camera config files. .RE .RE .TP .B camera .RS .nf Values: User specified string Default: Not Defined Description: .fi .RS This option specifies the full path and file name to individual camera files. This option can be listed multiple times. Each camera file should contain the options that are unique to that camera/video device. Common options are obtained from the motion.conf file and values are overwritten from each camera file. While the motion.conf includes four sample camera files, the actual limit of cameras is only dependent upon the machine capabilities. Remember: If you have more than one camera you must have one camera file for each one. For example, 2 cameras would require 3 files: The motion.conf file AND camera1.conf and camera2.conf. only put the options that are unique to each camera in the camera config files. .RE .RE .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 movie file of the last event and exit .TP .B SIGUSR1 Motion will create an movie 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/movie 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 Cameras 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 motion/ subdirectory inside 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/motion then the editor recommends having only one motion.conf file in the default /usr/local/etc/motion 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/motion 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 camera 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 camera config file per camera besides the motion.conf file. .br So if you have for example two cameras you need motion.conf and two camera 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 camera 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 camera file. .br The first camera is defined in the first camera file called from motion.conf. The 2nd camera is defined in the 2nd camera 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 camera 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 "camera" is defined in motion.conf, the camera 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 camera config file. .br 4. Motion reads the command line option again overruling any previously defined options. .br So always call the camera config files in the end of the motion.conf file. If you define options in motion.conf AFTER the camera file calls, the same options in the camera files will never be used. So always put the camera 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 camera.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 Camera ID 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 %$ The camera name. .TP .B More information Motion homepage: https://motion-project.github.io/ Motion Guide (user and installation guide): .br /usr/share/doc/motion/motion_guide.html .SH AUTHORS Jeroen Vreeken (pe1rxq@amsat.org), Folkert van Heusden, Kenneth Lavrsen (kenneth@lavrsen.dk), Juan Angulo Moreno , and many others motion-release-4.0/motion.c000066400000000000000000003666511300074623700157740ustar00rootroot00000000000000/* 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" #if (defined(BSD) && !defined(PWCBSD)) #include "video_freebsd.h" #else #include "video.h" #endif /* BSD */ #include "conf.h" #include "alg.h" #include "track.h" #include "event.h" #include "picture.h" #include "rotate.h" /* Forward declarations */ static int motion_init(struct context *cnt); static void motion_cleanup(struct context *cnt); static void setup_signals(struct sigaction *, struct sigaction *); /** * 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; /* Set this when we want main to end or restart */ volatile unsigned int finish = 0; /* Log file used instead of stderr and syslog */ FILE *ptr_logfile = NULL; /** * 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). */ unsigned int restart = 0; /** * image_ring_resize * * This routine is called from motion_loop to resize the image precapture ringbuffer * NOTE: This function clears all images in the old ring buffer * Parameters: * * cnt Pointer to the motion context structure * new_size The new size of the ring buffer * * Returns: nothing */ static void image_ring_resize(struct context *cnt, int new_size) { /* * Only resize if : * Not in an event and * decreasing at last position in new buffer * increasing at last position in old buffer * e.g. at end of smallest buffer */ if (cnt->event_nr != cnt->prev_event) { int smallest; if (new_size < cnt->imgs.image_ring_size) /* Decreasing */ smallest = new_size; else /* Increasing */ smallest = cnt->imgs.image_ring_size; if (cnt->imgs.image_ring_in == smallest - 1 || smallest == 0) { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Resizing pre_capture buffer to %d items", new_size); /* Create memory for new ring buffer */ struct image_data *tmp; tmp = mymalloc(new_size * sizeof(struct image_data)); /* * Copy all information from old to new * Smallest is 0 at initial init */ if (smallest > 0) memcpy(tmp, cnt->imgs.image_ring, sizeof(struct image_data) * smallest); /* In the new buffers, allocate image memory */ { int i; for(i = smallest; i < new_size; i++) { tmp[i].image = mymalloc(cnt->imgs.size); memset(tmp[i].image, 0x80, cnt->imgs.size); /* initialize to grey */ } } /* Free the old ring */ free(cnt->imgs.image_ring); /* Point to the new ring */ cnt->imgs.image_ring = tmp; cnt->current_image = NULL; cnt->imgs.image_ring_size = new_size; } } } /** * image_ring_destroy * * This routine is called when we want to free the ring * * Parameters: * * cnt Pointer to the motion context structure * * Returns: nothing */ static void image_ring_destroy(struct context *cnt) { int i; /* Exit if don't have any ring */ if (cnt->imgs.image_ring == NULL) return; /* Free all image buffers */ for (i = 0; i < cnt->imgs.image_ring_size; i++) free(cnt->imgs.image_ring[i].image); /* Free the ring */ free(cnt->imgs.image_ring); cnt->imgs.image_ring = NULL; cnt->current_image = NULL; cnt->imgs.image_ring_size = 0; } /** * image_save_as_preview * * This routine is called when we detect motion and want to save an image in the preview buffer * * Parameters: * * cnt Pointer to the motion context structure * img Pointer to the image_data structure we want to set as preview image * * Returns: nothing */ static void image_save_as_preview(struct context *cnt, struct image_data *img) { void * image; /* Save preview image pointer */ image = cnt->imgs.preview_image.image; /* Copy all info */ memcpy(&cnt->imgs.preview_image.image, img, sizeof(struct image_data)); /* restore image pointer */ cnt->imgs.preview_image.image = image; /* Copy image */ memcpy(cnt->imgs.preview_image.image, img->image, cnt->imgs.size); /* * If we set output_all to yes and during the event * there is no image with motion, diffs is 0, we are not going to save the preview event */ if (cnt->imgs.preview_image.diffs == 0) cnt->imgs.preview_image.diffs = 1; /* draw locate box here when mode = LOCATE_PREVIEW */ if (cnt->locate_motion_mode == LOCATE_PREVIEW) { if (cnt->locate_motion_style == LOCATE_BOX) { alg_draw_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image, LOCATE_BOX, LOCATE_NORMAL, cnt->process_thisframe); } else if (cnt->locate_motion_style == LOCATE_REDBOX) { alg_draw_red_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image, LOCATE_REDBOX, LOCATE_NORMAL, cnt->process_thisframe); } else if (cnt->locate_motion_style == LOCATE_CROSS) { alg_draw_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image, LOCATE_CROSS, LOCATE_NORMAL, cnt->process_thisframe); } else if (cnt->locate_motion_style == LOCATE_REDCROSS) { alg_draw_red_location(&img->location, &cnt->imgs, cnt->imgs.width, cnt->imgs.preview_image.image, LOCATE_REDCROSS, LOCATE_NORMAL, cnt->process_thisframe); } } } /** * 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; } /** * 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) { unsigned int j; /* 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; cnt_list[i]->webcontrol_finish = 1; /* * Don't restart thread when it ends, * all threads restarts if global restart is set */ cnt_list[i]->restart = 0; } } /* * Set flag we want to quit main check threads loop * if restart is set (above) we start up again */ finish = 1; break; case SIGSEGV: exit(0); case SIGVTALRM: printf("SIGVTALRM went off\n"); break; } } /** * 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_remove_pid * * This function remove the process id file ( pid file ) before motion exit. */ static void motion_remove_pid(void) { if ((cnt_list[0]->daemon) && (cnt_list[0]->conf.pid_file) && (restart == 0)) { if (!unlink(cnt_list[0]->conf.pid_file)) MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Removed process id file (pid file)."); else MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Error removing pid file"); } if (ptr_logfile) { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Closing logfile (%s).", cnt_list[0]->conf.log_file); myfclose(ptr_logfile); set_log_mode(LOGMODE_SYSLOG); ptr_logfile = NULL; } } /** * motion_detected * * Called from 'motion_loop' when motion is detected * Can be called when no motion if emulate_motion is set! * * Parameters: * * cnt - current thread's context struct * dev - video device file descriptor * img - pointer to the captured image_data with detected motion */ static void motion_detected(struct context *cnt, int dev, struct image_data *img) { struct config *conf = &cnt->conf; struct images *imgs = &cnt->imgs; struct coord *location = &img->location; /* Draw location */ if (cnt->locate_motion_mode == LOCATE_ON) { if (cnt->locate_motion_style == LOCATE_BOX) { alg_draw_location(location, imgs, imgs->width, img->image, LOCATE_BOX, LOCATE_BOTH, cnt->process_thisframe); } else if (cnt->locate_motion_style == LOCATE_REDBOX) { alg_draw_red_location(location, imgs, imgs->width, img->image, LOCATE_REDBOX, LOCATE_BOTH, cnt->process_thisframe); } else if (cnt->locate_motion_style == LOCATE_CROSS) { alg_draw_location(location, imgs, imgs->width, img->image, LOCATE_CROSS, LOCATE_BOTH, cnt->process_thisframe); } else if (cnt->locate_motion_style == LOCATE_REDCROSS) { alg_draw_red_location(location, imgs, imgs->width, img->image, LOCATE_REDCROSS, LOCATE_BOTH, cnt->process_thisframe); } } /* Calculate how centric motion is if configured preview center*/ if (cnt->new_img & NEWIMG_CENTER) { unsigned int distX = abs((imgs->width / 2) - location->x); unsigned int distY = abs((imgs->height / 2) - location->y); img->cent_dist = distX * distX + distY * distY; } /* Do things only if we have got minimum_motion_frames */ if (img->flags & IMAGE_TRIGGER) { /* Take action if this is a new event and we have a trigger image */ if (cnt->event_nr != cnt->prev_event) { /* * 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 = img->timestamp; 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, img->image, NULL, NULL, &img->timestamp_tm); MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Motion detected - starting event %d", cnt->event_nr); /* always save first motion frame as preview-shot, may be changed to an other one later */ if (cnt->new_img & (NEWIMG_FIRST | NEWIMG_BEST | NEWIMG_CENTER)) image_save_as_preview(cnt, img); } /* EVENT_MOTION triggers event_beep and on_motion_detected_command */ event(cnt, EVENT_MOTION, NULL, NULL, NULL, &img->timestamp_tm); } /* Limit framerate */ if (img->shot < conf->frame_limit) { /* * If config option stream_motion is enabled, send the latest motion detected image * to the stream 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 stream. * We also disable this in setup_mode. */ if (conf->stream_motion && !conf->setup_mode && img->shot != 1) event(cnt, EVENT_STREAM, img->image, NULL, NULL, &img->timestamp_tm); /* * Save motion jpeg, if configured * Output the image_out (motion) picture. */ if (conf->motion_img) event(cnt, EVENT_IMAGEM_DETECTED, NULL, NULL, NULL, &img->timestamp_tm); } /* if track enabled and auto track on */ if (cnt->track.type && cnt->track.active) cnt->moved = track_move(cnt, dev, location, imgs, 0); } /** * process_image_ring * * Called from 'motion_loop' to save images / send images to movie * * Parameters: * * cnt - current thread's context struct * max_images - Max number of images to process * Set to IMAGE_BUFFER_FLUSH to send/save all images in buffer */ #define IMAGE_BUFFER_FLUSH ((unsigned int)-1) static void process_image_ring(struct context *cnt, unsigned int max_images) { /* * We are going to send an event, in the events there is still * some code that use cnt->current_image * so set it temporary to our image */ struct image_data *saved_current_image = cnt->current_image; /* If image is flaged to be saved and not saved yet, process it */ do { /* Check if we should save/send this image, breakout if not */ if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & (IMAGE_SAVE | IMAGE_SAVED)) != IMAGE_SAVE) break; /* Set inte global cotext that we are working with this image */ cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_out]; if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot < cnt->conf.frame_limit) { if (cnt->log_level >= DBG) { char tmp[32]; const char *t; if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_TRIGGER) t = "Trigger"; else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION) t = "Motion"; else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_PRECAP) t = "Precap"; else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_POSTCAP) t = "Postcap"; else t = "Other"; mystrftime(cnt, tmp, sizeof(tmp), "%H%M%S-%q", &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm, NULL, 0); draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 20, cnt->imgs.width, tmp, cnt->conf.text_double); draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 30, cnt->imgs.width, t, cnt->conf.text_double); } /* Output the picture to jpegs and ffmpeg */ event(cnt, EVENT_IMAGE_DETECTED, cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); /* * Check if we must add any "filler" frames into movie to keep up fps * Only if we are recording videos ( ffmpeg or extenal pipe ) * While the overall elapsed time might be correct, if there are * many duplicated frames, say 10 fps, 5 duplicated, the video will * look like it is frozen every second for half a second. */ if (!cnt->conf.ffmpeg_duplicate_frames) { /* don't duplicate frames */ } else if ((cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot == 0) && #ifdef HAVE_FFMPEG (cnt->ffmpeg_output || (cnt->conf.useextpipe && cnt->extpipe))) { #else (cnt->conf.useextpipe && cnt->extpipe)) { #endif /* * movie_last_shoot is -1 when file is created, * we don't know how many frames there is in first sec */ if (cnt->movie_last_shot >= 0) { if (cnt_list[0]->log_level >= DBG) { int frames = cnt->movie_fps - (cnt->movie_last_shot + 1); if (frames > 0) { char tmp[15]; MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%s: Added %d fillerframes into movie", frames); sprintf(tmp, "Fillerframes %d", frames); draw_text(cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, 10, 40, cnt->imgs.width, tmp, cnt->conf.text_double); } } /* Check how many frames it was last sec */ while ((cnt->movie_last_shot + 1) < cnt->movie_fps) { /* Add a filler frame into encoder */ event(cnt, EVENT_FFMPEG_PUT, cnt->imgs.image_ring[cnt->imgs.image_ring_out].image, NULL, NULL, &cnt->imgs.image_ring[cnt->imgs.image_ring_out].timestamp_tm); cnt->movie_last_shot++; } } cnt->movie_last_shot = 0; } else if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot != (cnt->movie_last_shot + 1)) { /* We are out of sync! Propably we got motion - no motion - motion */ cnt->movie_last_shot = -1; } /* * Save last shot added to movie * only when we not are within first sec */ if (cnt->movie_last_shot >= 0) cnt->movie_last_shot = cnt->imgs.image_ring[cnt->imgs.image_ring_out].shot; } /* Mark the image as saved */ cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags |= IMAGE_SAVED; /* Store it as a preview image, only if it have motion */ if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].flags & IMAGE_MOTION) { /* Check for most significant preview-shot when output_pictures=best */ if (cnt->new_img & NEWIMG_BEST) { if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].diffs > cnt->imgs.preview_image.diffs) { image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]); } } /* Check for most significant preview-shot when output_pictures=center */ if (cnt->new_img & NEWIMG_CENTER) { if (cnt->imgs.image_ring[cnt->imgs.image_ring_out].cent_dist < cnt->imgs.preview_image.cent_dist) { image_save_as_preview(cnt, &cnt->imgs.image_ring[cnt->imgs.image_ring_out]); } } } /* Increment to image after last sended */ if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size) cnt->imgs.image_ring_out = 0; if (max_images != IMAGE_BUFFER_FLUSH) { max_images--; /* breakout if we have done max_images */ if (max_images == 0) break; } /* loop until out and in is same e.g. buffer empty */ } while (cnt->imgs.image_ring_out != cnt->imgs.image_ring_in); /* restore global context values */ cnt->current_image = saved_current_image; } /** * 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: 0 OK * -1 Fatal error, open loopback error * -2 Fatal error, open SQL database error * -3 Fatal error, image dimensions are not modulo 8 */ static int motion_init(struct context *cnt) { FILE *picture; /* Store thread number in TLS. */ pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)cnt->threadnr)); cnt->currenttime_tm = mymalloc(sizeof(struct tm)); cnt->eventtime_tm = mymalloc(sizeof(struct tm)); /* Init frame time */ cnt->currenttime = time(NULL); localtime_r(&cnt->currenttime, cnt->currenttime_tm); cnt->smartmask_speed = 0; /* * We initialize cnt->event_nr to 1 and cnt->prev_event to 0 (not really needed) so * that certain code below does not run until motion has been detected the first time */ cnt->event_nr = 1; cnt->prev_event = 0; cnt->lightswitch_framecounter = 0; cnt->detecting_motion = 0; cnt->makemovie = 0; MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Camera %d started: motion detection %s", cnt->conf.camera_id, cnt->pause ? "Disabled":"Enabled"); if (!cnt->conf.filepath) cnt->conf.filepath = mystrdup("."); /* set the device settings */ cnt->video_dev = vid_start(cnt); /* * We failed to get an initial image from a camera * So we need to guess height and width based on the config * file options. */ if (cnt->video_dev == -1) { MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Could not fetch initial image from camera " "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; } else if (cnt->video_dev == -2) { MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Could not fetch initial image from camera " "Motion only supports width and height modulo 8"); return -3; } image_ring_resize(cnt, 1); /* Create a initial precapture ring buffer with 1 frame */ cnt->imgs.ref = mymalloc(cnt->imgs.size); cnt->imgs.out = mymalloc(cnt->imgs.size); /* contains the moving objects of ref. frame */ cnt->imgs.ref_dyn = mymalloc(cnt->imgs.motionsize * sizeof(*cnt->imgs.ref_dyn)); cnt->imgs.image_virgin = mymalloc(cnt->imgs.size); cnt->imgs.smartmask = mymalloc(cnt->imgs.motionsize); cnt->imgs.smartmask_final = mymalloc(cnt->imgs.motionsize); cnt->imgs.smartmask_buffer = mymalloc(cnt->imgs.motionsize * sizeof(*cnt->imgs.smartmask_buffer)); cnt->imgs.labels = mymalloc(cnt->imgs.motionsize * sizeof(*cnt->imgs.labels)); cnt->imgs.labelsize = mymalloc((cnt->imgs.motionsize/2+1) * sizeof(*cnt->imgs.labelsize)); /* Set output picture type */ if (!strcmp(cnt->conf.picture_type, "ppm")) cnt->imgs.picture_type = IMAGE_TYPE_PPM; else cnt->imgs.picture_type = IMAGE_TYPE_JPEG; /* allocate buffer here for preview buffer */ cnt->imgs.preview_image.image = mymalloc(cnt->imgs.size); /* * Allocate a buffer for temp. usage in some places * Only despeckle & bayer2rgb24() for now for now... */ cnt->imgs.common_buffer = mymalloc(3 * cnt->imgs.width * cnt->imgs.height); /* * 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 */ /* Capture first image, or we will get an alarm on start */ if (cnt->video_dev > 0) { int i; for (i = 0; i < 5; i++) { if (vid_next(cnt, cnt->imgs.image_virgin) == 0) break; SLEEP(2, 0); } if (i >= 5) { memset(cnt->imgs.image_virgin, 0x80, cnt->imgs.size); /* initialize to grey */ draw_text(cnt->imgs.image_virgin, 10, 20, cnt->imgs.width, "Error capturing first image", cnt->conf.text_double); MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Error capturing first image"); } } /* create a reference frame */ alg_update_reference_frame(cnt, RESET_REF_FRAME); #if defined(HAVE_LINUX_VIDEODEV_H) && !defined(WITHOUT_V4L) && !defined(BSD) /* open video loopback devices if enabled */ if (cnt->conf.vidpipe) { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: 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(ERR, TYPE_ALL, NO_ERRNO, "%s: Failed to open video loopback for normal pictures"); return -1; } } if (cnt->conf.motionvidpipe) { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: 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(ERR, TYPE_ALL, NO_ERRNO, "%s: Failed to open video loopback for motion pictures"); return -1; } } #endif /* !WITHOUT_V4L && !BSD */ #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) if (cnt->conf.database_type) { MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, "%s: Database backend %s", cnt->conf.database_type); #ifdef HAVE_SQLITE3 /* if database_sqlite3 is NULL then we are using a non threaded version of * sqlite3 and will need a seperate connection for each thread */ if (cnt->database_sqlite3) { MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, "%s: SQLite3 using shared handle"); } else if ((!strcmp(cnt->conf.database_type, "sqlite3")) && cnt->conf.database_dbname) { MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, "%s: SQLite3 Database filename %s", cnt->conf.database_dbname); if (sqlite3_open(cnt->conf.database_dbname, &cnt->database_sqlite3) != SQLITE_OK) { MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: Can't open database %s : %s", cnt->conf.database_dbname, sqlite3_errmsg(cnt->database_sqlite3)); sqlite3_close(cnt->database_sqlite3); exit(1); } MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, "%s: database_busy_timeout %d msec", cnt->conf.database_busy_timeout); if (sqlite3_busy_timeout(cnt->database_sqlite3, cnt->conf.database_busy_timeout) != SQLITE_OK) MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: database_busy_timeout failed %s", sqlite3_errmsg(cnt->database_sqlite3)); } #endif /* HAVE_SQLITE3 */ #ifdef HAVE_MYSQL if ((!strcmp(cnt->conf.database_type, "mysql")) && (cnt->conf.database_dbname)) { // close database to be sure that we are not leaking mysql_close(cnt->database); cnt->database = mymalloc(sizeof(MYSQL)); mysql_init(cnt->database); if (!mysql_real_connect(cnt->database, cnt->conf.database_host, cnt->conf.database_user, cnt->conf.database_password, cnt->conf.database_dbname, 0, NULL, 0)) { MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: Cannot connect to MySQL database %s on host %s with user %s", cnt->conf.database_dbname, cnt->conf.database_host, cnt->conf.database_user); MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: MySQL error was %s", mysql_error(cnt->database)); return -2; } #if (defined(MYSQL_VERSION_ID)) && (MYSQL_VERSION_ID > 50012) my_bool my_true = TRUE; mysql_options(cnt->database, MYSQL_OPT_RECONNECT, &my_true); #endif } #endif /* HAVE_MYSQL */ #ifdef HAVE_PGSQL if ((!strcmp(cnt->conf.database_type, "postgresql")) && (cnt->conf.database_dbname)) { 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.database_dbname, /* dbname */ (cnt->conf.database_host ? cnt->conf.database_host : ""), /* host (may be blank) */ (cnt->conf.database_user ? cnt->conf.database_user : ""), /* user (may be blank) */ (cnt->conf.database_password ? cnt->conf.database_password : ""), /* password (may be blank) */ cnt->conf.database_port ); cnt->database_pg = PQconnectdb(connstring); if (PQstatus(cnt->database_pg) == CONNECTION_BAD) { MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: Connection to PostgreSQL database '%s' failed: %s", cnt->conf.database_dbname, PQerrorMessage(cnt->database_pg)); return -2; } } #endif /* HAVE_PGSQL */ /* Set the sql mask file according to the SQL config options*/ 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_movie * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE; } #endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) */ /* Load the mask file if any */ if (cnt->conf.mask_file) { if ((picture = myfopen(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); myfclose(picture); } else { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: 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(ERR, TYPE_ALL, NO_ERRNO, "%s: Failed to read mask image. Mask feature disabled."); } else { MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, "%s: 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(*cnt->imgs.smartmask_buffer)); /* Set noise level */ cnt->noise = cnt->conf.noise; /* Set threshold value */ cnt->threshold = cnt->conf.max_changes; /* Initialize stream server if stream port is specified to not 0 */ if (cnt->conf.stream_port) { if (stream_init(cnt) == -1) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Problem enabling motion-stream server in port %d", cnt->conf.stream_port); cnt->finish = 1; } else { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Started motion-stream server on port %d (auth %s)", cnt->conf.stream_port, cnt->conf.stream_auth_method ? "Enabled":"Disabled"); } } /* Prevent first few frames from triggering motion... */ cnt->moved = 8; /* 2 sec startup delay so FPS is calculated correct */ cnt->startup_frames = cnt->conf.frame_limit * 2; return 0; } /** * motion_cleanup * * This routine is called from motion_loop when thread ends to * cleanup all memory etc. that motion_init did. * * Parameters: * * cnt Pointer to the motion context structure * * Returns: nothing */ static void motion_cleanup(struct context *cnt) { /* Stop stream */ event(cnt, EVENT_STOP, NULL, NULL, NULL, NULL); #ifdef HAVE_FFMPEG event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, NULL); event(cnt, EVENT_ENDMOTION, NULL, NULL, NULL, NULL); #endif // HAVE_FFMPEG if (cnt->video_dev >= 0) { MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, "%s: Calling vid_close() from motion_cleanup"); vid_close(cnt); } free(cnt->imgs.out); cnt->imgs.out = NULL; free(cnt->imgs.ref); cnt->imgs.ref = NULL; free(cnt->imgs.ref_dyn); cnt->imgs.ref_dyn = NULL; free(cnt->imgs.image_virgin); cnt->imgs.image_virgin = NULL; free(cnt->imgs.labels); cnt->imgs.labels = NULL; free(cnt->imgs.labelsize); cnt->imgs.labelsize = NULL; free(cnt->imgs.smartmask); cnt->imgs.smartmask = NULL; free(cnt->imgs.smartmask_final); cnt->imgs.smartmask_final = NULL; free(cnt->imgs.smartmask_buffer); cnt->imgs.smartmask_buffer = NULL; if (cnt->imgs.mask) free(cnt->imgs.mask); cnt->imgs.mask = NULL; free(cnt->imgs.common_buffer); cnt->imgs.common_buffer = NULL; free(cnt->imgs.preview_image.image); cnt->imgs.preview_image.image = NULL; image_ring_destroy(cnt); /* Cleanup the precapture ring buffer */ rotate_deinit(cnt); /* cleanup image rotation data */ if (cnt->pipe != -1) { close(cnt->pipe); cnt->pipe = -1; } if (cnt->mpipe != -1) { close(cnt->mpipe); cnt->mpipe = -1; } /* Cleanup the current time structure */ free(cnt->currenttime_tm); cnt->currenttime_tm = NULL; /* Cleanup the event time structure */ free(cnt->eventtime_tm); cnt->eventtime_tm = NULL; if (cnt->conf.database_type) { #ifdef HAVE_MYSQL if ( (!strcmp(cnt->conf.database_type, "mysql")) && (cnt->conf.database_dbname)) { mysql_close(cnt->database); } #endif /* HAVE_MYSQL */ #ifdef HAVE_PGSQL if ((!strcmp(cnt->conf.database_type, "postgresql")) && (cnt->conf.database_dbname)) { PQfinish(cnt->database_pg); } #endif /* HAVE_PGSQL */ #ifdef HAVE_SQLITE3 /* Close the SQLite database */ if ((!strcmp(cnt->conf.database_type, "sqlite3")) && (cnt->conf.database_dbname)) { sqlite3_close(cnt->database_sqlite3); } #endif /* HAVE_SQLITE3 */ } } /** * motion_loop * * Thread function for the motion handling threads. * */ static void *motion_loop(void *arg) { struct context *cnt = arg; int i, j, z = 0; time_t lastframetime = 0; int frame_buffer_size; unsigned int rate_limit = 0; int area_once = 0; int area_minx[9], area_miny[9], area_maxx[9], area_maxy[9]; int smartmask_ratio = 0; int smartmask_count = 20; unsigned int smartmask_lastrate = 0; int olddiffs = 0; int previous_diffs = 0, previous_location_x = 0, previous_location_y = 0; unsigned int text_size_factor; unsigned int passflag = 0; long int *rolling_average_data = NULL; long int rolling_average_limit, required_frame_time, frame_delay, delay_time_nsec; int rolling_frame = 0; struct timeval tv1, tv2; unsigned long int rolling_average, elapsedtime; unsigned long long int timenow = 0, timebefore = 0; int vid_return_code = 0; /* Return code used when calling vid_next */ int minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; /* time in seconds to skip between capturing images */ unsigned int get_image = 1; /* Flag used to signal that we capture new image when we run the loop */ struct image_data *old_image; #ifdef HAVE_PTHREAD_SETNAME_NP { char tname[16]; snprintf(tname, sizeof(tname), "ml%d%s%s", cnt->threadnr, cnt->conf.camera_name ? ":" : "", cnt->conf.camera_name ? cnt->conf.camera_name : ""); pthread_setname_np(pthread_self(), tname); } #endif /* * Next two variables are used for snapshot and timelapse feature * time_last_frame is set to 1 so that first coming timelapse or second = 0 * is acted upon. */ unsigned long int time_last_frame = 1, time_current_frame; if (motion_init(cnt) < 0) goto err; /* Initialize the double sized characters if needed. */ if (cnt->conf.text_double) text_size_factor = 2; else text_size_factor = 1; /* Initialize area detection */ area_minx[0] = area_minx[3] = area_minx[6] = 0; area_miny[0] = area_miny[1] = area_miny[2] = 0; area_minx[1] = area_minx[4] = area_minx[7] = cnt->imgs.width / 3; area_maxx[0] = area_maxx[3] = area_maxx[6] = cnt->imgs.width / 3; area_minx[2] = area_minx[5] = area_minx[8] = cnt->imgs.width / 3 * 2; area_maxx[1] = area_maxx[4] = area_maxx[7] = cnt->imgs.width / 3 * 2; area_miny[3] = area_miny[4] = area_miny[5] = cnt->imgs.height / 3; area_maxy[0] = area_maxy[1] = area_maxy[2] = cnt->imgs.height / 3; area_miny[6] = area_miny[7] = area_miny[8] = cnt->imgs.height / 3 * 2; area_maxy[3] = area_maxy[4] = area_maxy[5] = cnt->imgs.height / 3 * 2; area_maxx[2] = area_maxx[5] = area_maxx[8] = cnt->imgs.width; area_maxy[6] = area_maxy[7] = area_maxy[8] = cnt->imgs.height; /* Work out expected frame rate based on config setting */ if (cnt->conf.frame_limit < 2) cnt->conf.frame_limit = 2; required_frame_time = 1000000L / cnt->conf.frame_limit; 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(rolling_average_data) * 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; if (cnt->track.type) cnt->moved = track_center(cnt, cnt->video_dev, 0, 0, 0); #ifdef __OpenBSD__ /* * FIXMARK * Fixes zombie issue on OpenBSD 4.6 */ struct sigaction sig_handler_action; struct sigaction sigchild_action; setup_signals(&sig_handler_action, &sigchild_action); #endif /* * 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 *****/ cnt->watchdog = WATCHDOG_TMO; /* Get current time and preserver last time for frame interval calc. */ timebefore = timenow; gettimeofday(&tv1, NULL); timenow = tv1.tv_usec + 1000000L * tv1.tv_sec; /* * Calculate detection rate limit. Above 5fps we limit the detection * rate to 3fps to reduce load at higher framerates. */ cnt->process_thisframe = 0; rate_limit++; if (rate_limit >= (cnt->lastrate / 3)) { rate_limit = 0; cnt->process_thisframe = 1; } /* * 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; if (cnt->imgs.image_ring_size != frame_buffer_size) image_ring_resize(cnt, 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; if (cnt->conf.minimum_frame_time) { minimum_frame_time_downcounter--; if (minimum_frame_time_downcounter == 0) get_image = 1; } else { get_image = 1; } } /* Increase the shots variable for each frame captured within this second */ cnt->shots++; if (cnt->startup_frames > 0) cnt->startup_frames--; if (get_image) { if (cnt->conf.minimum_frame_time) { minimum_frame_time_downcounter = cnt->conf.minimum_frame_time; get_image = 0; } /* ring_buffer_in is pointing to current pos, update before put in a new image */ if (++cnt->imgs.image_ring_in >= cnt->imgs.image_ring_size) cnt->imgs.image_ring_in = 0; /* Check if we have filled the ring buffer, throw away last image */ if (cnt->imgs.image_ring_in == cnt->imgs.image_ring_out) { if (++cnt->imgs.image_ring_out >= cnt->imgs.image_ring_size) cnt->imgs.image_ring_out = 0; } /* cnt->current_image points to position in ring where to store image, diffs etc. */ old_image = cnt->current_image; cnt->current_image = &cnt->imgs.image_ring[cnt->imgs.image_ring_in]; /* Init/clear current_image */ if (cnt->process_thisframe) { /* set diffs to 0 now, will be written after we calculated diffs in new image */ cnt->current_image->diffs = 0; /* Set flags to 0 */ cnt->current_image->flags = 0; cnt->current_image->cent_dist = 0; /* Clear location data */ memset(&cnt->current_image->location, 0, sizeof(cnt->current_image->location)); cnt->current_image->total_labels = 0; } else if (cnt->current_image && old_image) { /* not processing this frame: save some important values for next image */ cnt->current_image->diffs = old_image->diffs; cnt->current_image->timestamp = old_image->timestamp; cnt->current_image->timestamp_tm = old_image->timestamp_tm; cnt->current_image->shot = old_image->shot; cnt->current_image->cent_dist = old_image->cent_dist; cnt->current_image->flags = old_image->flags & (~IMAGE_SAVED); cnt->current_image->location = old_image->location; cnt->current_image->total_labels = old_image->total_labels; } /* Store time with pre_captured image */ cnt->current_image->timestamp = cnt->currenttime; localtime_r(&cnt->current_image->timestamp, &cnt->current_image->timestamp_tm); /* Store shot number with pre_captured image */ cnt->current_image->shot = cnt->shots; /***** MOTION LOOP - RETRY INITIALIZING SECTION *****/ /* * If a camera is not available we keep on retrying every 10 seconds * until it shows up. */ if (cnt->video_dev < 0 && cnt->currenttime % 10 == 0 && cnt->shots == 0) { MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Retrying until successful connection with camera"); cnt->video_dev = vid_start(cnt); /* * If the netcam has different dimensions than in the config file * we need to restart Motion to re-allocate all the buffers */ if (cnt->imgs.width != cnt->conf.width || cnt->imgs.height != cnt->conf.height) { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Camera has finally become available\n" "Camera image has different width and height" "from what is in the config file. You should fix that\n" "Restarting Motion thread to reinitialize all " "image buffers to new picture dimensions"); cnt->conf.width = cnt->imgs.width; cnt->conf.height = cnt->imgs.height; /* * Break out of main loop terminating thread * watchdog will start us again */ break; } } /***** 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 */ if (cnt->video_dev >= 0) vid_return_code = vid_next(cnt, cnt->current_image->image); else vid_return_code = 1; /* Non fatal error */ // VALID PICTURE if (vid_return_code == 0) { cnt->lost_connection = 0; cnt->connectionlosttime = 0; /* If all is well reset missing_frame_counter */ if (cnt->missing_frame_counter >= MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) { /* If we previously logged starting a grey image, now log video re-start */ MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: 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, cnt->current_image->image, 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; } // FATAL ERROR - leave the thread by breaking out of the main loop } else if (vid_return_code < 0) { /* Fatal error - Close video device */ MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Video device fatal error - Closing video device"); vid_close(cnt); /* * Use virgin image, if we are not able to open it again next loop * a gray image with message is applied * flag lost_connection */ memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size); cnt->lost_connection = 1; /* NO FATAL ERROR - * copy last image or show grey image with message * flag on lost_connection if : * vid_return_code == NETCAM_RESTART_ERROR * cnt->video_dev < 0 * cnt->missing_frame_counter > (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit) */ } else { MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%s: 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(NTC, TYPE_ALL, NO_ERRNO, "%s: Restarting Motion thread to reinitialize all " "image buffers"); /* * Break out of main loop terminating thread * watchdog will start us again * Set lost_connection flag on */ cnt->lost_connection = 1; break; } /* * First missed frame - store timestamp * Don't reset time when thread restarts */ if (cnt->connectionlosttime == 0) cnt->connectionlosttime = cnt->currenttime; /* * Increase missing_frame_counter * The first MISSING_FRAMES_TIMEOUT seconds we copy previous virgin image * After MISSING_FRAMES_TIMEOUT seconds we put a grey error image in the buffer * If we still have not yet received the initial image from a camera * we go straight for the grey error image. */ ++cnt->missing_frame_counter; if (cnt->video_dev >= 0 && cnt->missing_frame_counter < (MISSING_FRAMES_TIMEOUT * cnt->conf.frame_limit)) { memcpy(cnt->current_image->image, cnt->imgs.image_virgin, cnt->imgs.size); } else { const char *tmpin; char tmpout[80]; struct tm tmptime; cnt->lost_connection = 1; if (cnt->video_dev >= 0) tmpin = "CONNECTION TO CAMERA LOST\\nSINCE %Y-%m-%d %T"; else tmpin = "UNABLE TO OPEN VIDEO DEVICE\\nSINCE %Y-%m-%d %T"; localtime_r(&cnt->connectionlosttime, &tmptime); memset(cnt->current_image->image, 0x80, cnt->imgs.size); mystrftime(cnt, tmpout, sizeof(tmpout), tmpin, &tmptime, NULL, 0); draw_text(cnt->current_image->image, 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(NTC, TYPE_ALL, NO_ERRNO, "%s: Video signal lost - Adding grey image"); // Event for lost video signal can be called from here event(cnt, EVENT_CAMERA_LOST, NULL, NULL, NULL, cnt->currenttime_tm); } /* * If we don't get a valid frame for a long time, try to close/reopen device * Only try this when a device is open */ if ((cnt->video_dev > 0) && (cnt->missing_frame_counter == (MISSING_FRAMES_TIMEOUT * 4) * cnt->conf.frame_limit)) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Video signal still lost - " "Trying to close video device"); vid_close(cnt); } } } /***** 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->process_thisframe) { 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 (cnt->detecting_motion || cnt->conf.setup_mode) cnt->current_image->diffs = alg_diff_standard(cnt, cnt->imgs.image_virgin); else cnt->current_image->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. * Don't check if we have lost connection, we detect "Lost signal" frame as lightswitch */ if (cnt->conf.lightswitch > 1 && !cnt->lost_connection) { if (alg_lightswitch(cnt, cnt->current_image->diffs)) { MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, "%s: Lightswitch detected"); if (cnt->moved < 5) cnt->moved = 5; cnt->current_image->diffs = 0; alg_update_reference_frame(cnt, RESET_REF_FRAME); } } /* * 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 algorithm is not very safe. * The algorithm 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 lightswitch * because with Round Robin this is controlled by roundrobin_skip. */ if (cnt->conf.switchfilter && cnt->current_image->diffs > cnt->threshold) { cnt->current_image->diffs = alg_switchfilter(cnt, cnt->current_image->diffs, cnt->current_image->image); if (cnt->current_image->diffs <= cnt->threshold) { cnt->current_image->diffs = 0; MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, "%s: Switchfilter detected"); } } /* * Despeckle feature * First we run (as given by the despeckle_filter option iterations * of erode and dilate algorithms. * Finally we run the labelling feature. * All this is done in the alg_despeckle code. */ cnt->current_image->total_labels = 0; cnt->imgs.largest_label = 0; olddiffs = 0; if (cnt->conf.despeckle_filter && cnt->current_image->diffs > 0) { olddiffs = cnt->current_image->diffs; cnt->current_image->diffs = alg_despeckle(cnt, olddiffs); } else if (cnt->imgs.labelsize_max) { cnt->imgs.labelsize_max = 0; /* Disable labeling if enabled */ } } else if (!cnt->conf.setup_mode) { cnt->current_image->diffs = 0; } } /* Manipulate smart_mask sensitivity (only every smartmask_ratio seconds) */ if ((cnt->smartmask_speed && (cnt->event_nr != cnt->prev_event)) && (!--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->current_image->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) && (!cnt->detecting_motion && (cnt->current_image->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->process_thisframe) { 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->current_image->diffs, cnt->detecting_motion); else cnt->threshold = cnt->conf.max_changes; /* * If motion is detected (cnt->current_image->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->current_image->diffs > cnt->threshold) alg_locate_center_size(&cnt->imgs, cnt->imgs.width, cnt->imgs.height, &cnt->current_image->location); /* * Update reference frame. * micro-lighswitch: trying to auto-detect lightswitch events. * frontdoor illumination. Updates are rate-limited to 3 per second at * framerates above 5fps to save CPU resources and to keep sensitivity * at a constant level. */ if ((cnt->current_image->diffs > cnt->threshold) && (cnt->conf.lightswitch == 1) && (cnt->lightswitch_framecounter < (cnt->lastrate * 2)) && /* two seconds window only */ /* number of changed pixels almost the same in two consecutive frames and */ ((abs(previous_diffs - cnt->current_image->diffs)) < (previous_diffs / 15)) && /* center of motion in about the same place ? */ ((abs(cnt->current_image->location.x - previous_location_x)) <= (cnt->imgs.width / 150)) && ((abs(cnt->current_image->location.y - previous_location_y)) <= (cnt->imgs.height / 150))) { alg_update_reference_frame(cnt, RESET_REF_FRAME); cnt->current_image->diffs = 0; cnt->lightswitch_framecounter = 0; MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, "%s: micro-lightswitch!"); } else { alg_update_reference_frame(cnt, UPDATE_REF_FRAME); } previous_diffs = cnt->current_image->diffs; previous_location_x = cnt->current_image->location.x; previous_location_y = cnt->current_image->location.y; } /***** 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. */ /* Smartmask overlay */ if (cnt->smartmask_speed && (cnt->conf.motion_img || cnt->conf.ffmpeg_output_debug || 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_output_debug || cnt->conf.setup_mode)) overlay_largest_label(cnt, cnt->imgs.out); /* Fixed mask overlay */ if (cnt->imgs.mask && (cnt->conf.motion_img || cnt->conf.ffmpeg_output_debug || cnt->conf.setup_mode)) overlay_fixed_mask(cnt, cnt->imgs.out); /* 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]; if (!cnt->pause) sprintf(tmp, "%d", cnt->current_image->diffs); else sprintf(tmp, "-"); draw_text(cnt->current_image->image, cnt->imgs.width - 10, 10, cnt->imgs.width, tmp, cnt->conf.text_double); } /* * Add changed pixels to motion-images (for stream) 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->current_image->diffs, cnt->current_image->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->current_image->timestamp_tm, NULL, 0); draw_text(cnt->current_image->image, 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->current_image->timestamp_tm, NULL, 0); draw_text(cnt->current_image->image, 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 (cnt->current_image->diffs > cnt->threshold) { /* flag this image, it have motion */ cnt->current_image->flags |= IMAGE_MOTION; cnt->lightswitch_framecounter++; /* micro lightswitch */ } else { cnt->lightswitch_framecounter = 0; } /* * If motion has been detected we take action and start saving * pictures and movies etc by calling motion_detected(). * Is emulate_motion 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.emulate_motion && (cnt->startup_frames == 0)) { cnt->detecting_motion = 1; MOTION_LOG(INF, TYPE_ALL, NO_ERRNO, "%s: Emulating motion"); #ifdef HAVE_FFMPEG if (cnt->ffmpeg_output || (cnt->conf.useextpipe && cnt->extpipe)) { #else if (cnt->conf.useextpipe && cnt->extpipe) { #endif /* Setup the postcap counter */ cnt->postcap = cnt->conf.post_capture; MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%s: (Em) Init post capture %d", cnt->postcap); } cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE); motion_detected(cnt, cnt->video_dev, cnt->current_image); } else if ((cnt->current_image->flags & IMAGE_MOTION) && (cnt->startup_frames == 0)) { /* * Did we detect motion (like the cat just walked in :) )? * If so, ensure the motion is sustained if minimum_motion_frames */ /* Count how many frames with motion there is in the last minimum_motion_frames in precap buffer */ int frame_count = 0; int pos = cnt->imgs.image_ring_in; for (i = 0; i < cnt->conf.minimum_motion_frames; i++) { if (cnt->imgs.image_ring[pos].flags & IMAGE_MOTION) frame_count++; if (pos == 0) pos = cnt->imgs.image_ring_size-1; else pos--; } if (frame_count >= cnt->conf.minimum_motion_frames) { cnt->current_image->flags |= (IMAGE_TRIGGER | IMAGE_SAVE); cnt->detecting_motion = 1; /* Setup the postcap counter */ cnt->postcap = cnt->conf.post_capture; MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%s: Setup post capture %d", cnt->postcap); /* Mark all images in image_ring to be saved */ for (i = 0; i < cnt->imgs.image_ring_size; i++) cnt->imgs.image_ring[i].flags |= IMAGE_SAVE; } else if ((cnt->postcap) && #ifdef HAVE_FFMPEG (cnt->ffmpeg_output || (cnt->conf.useextpipe && cnt->extpipe))) { #else (cnt->conf.useextpipe && cnt->extpipe)) { #endif /* we have motion in this frame, but not enought frames for trigger. Check postcap */ cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE); cnt->postcap--; MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%s: post capture %d", cnt->postcap); } else { cnt->current_image->flags |= IMAGE_PRECAP; } /* Always call motion_detected when we have a motion image */ motion_detected(cnt, cnt->video_dev, cnt->current_image); } else if ((cnt->postcap) && #ifdef HAVE_FFMPEG (cnt->ffmpeg_output || (cnt->conf.useextpipe && cnt->extpipe))) { #else (cnt->conf.useextpipe && cnt->extpipe)) { #endif /* No motion, doing postcap */ cnt->current_image->flags |= (IMAGE_POSTCAP | IMAGE_SAVE); cnt->postcap--; MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%s: post capture %d", cnt->postcap); } else { /* Done with postcap, so just have the image in the precap buffer */ cnt->current_image->flags |= IMAGE_PRECAP; /* gapless movie feature */ if ((cnt->conf.event_gap == 0) && (cnt->detecting_motion == 1)) cnt->makemovie = 1; cnt->detecting_motion = 0; } /* Update last frame saved time, so we can end event after gap time */ if (cnt->current_image->flags & IMAGE_SAVE) cnt->lasttime = cnt->current_image->timestamp; /* * Simple hack to recognize motion in a specific area * Do we need a new coversion specifier as well?? */ if ((cnt->conf.area_detect) && (cnt->event_nr != area_once) && (cnt->current_image->flags & IMAGE_TRIGGER)) { j = strlen(cnt->conf.area_detect); for (i = 0; i < j; i++) { z = cnt->conf.area_detect[i] - 49; /* 1 becomes 0 */ if ((z >= 0) && (z < 9)) { if (cnt->current_image->location.x > area_minx[z] && cnt->current_image->location.x < area_maxx[z] && cnt->current_image->location.y > area_miny[z] && cnt->current_image->location.y < area_maxy[z]) { event(cnt, EVENT_AREA_DETECTED, NULL, NULL, NULL, cnt->currenttime_tm); area_once = cnt->event_nr; /* Fire script only once per event */ MOTION_LOG(DBG, TYPE_ALL, NO_ERRNO, "%s: Motion in area %d detected.", z + 1); break; } } } } /* * Is the movie too long? Then make movies * First test for max_movie_time */ if ((cnt->conf.max_movie_time && cnt->event_nr == cnt->prev_event) && (cnt->currenttime - cnt->eventtime >= cnt->conf.max_movie_time)) 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.event_gap) && cnt->conf.event_gap > 0) || cnt->makemovie) { if (cnt->event_nr == cnt->prev_event || cnt->makemovie) { /* Flush image buffer */ process_image_ring(cnt, IMAGE_BUFFER_FLUSH); /* Save preview_shot here at the end of event */ if (cnt->imgs.preview_image.diffs) { preview_save(cnt); cnt->imgs.preview_image.diffs = 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); MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: End of event %d", cnt->event_nr); cnt->makemovie = 0; /* Reset post capture */ cnt->postcap = 0; /* Finally we increase the event number */ cnt->event_nr++; cnt->lightswitch_framecounter = 0; /* * And we unset the text_event_string to avoid that buffered * images get a timestamp from previous event. */ cnt->text_event_string[0] = '\0'; } } /* Save/send to movie some images */ process_image_ring(cnt, 2); /***** MOTION LOOP - SETUP MODE CONSOLE OUTPUT SECTION *****/ /* If CAMERA_VERBOSE enabled output some numbers to console */ if (cnt->conf.setup_mode) { char msg[1024] = "\0"; char part[100]; if (cnt->conf.despeckle_filter) { snprintf(part, 99, "Raw changes: %5d - changes after '%s': %5d", olddiffs, cnt->conf.despeckle_filter, cnt->current_image->diffs); strcat(msg, part); if (strchr(cnt->conf.despeckle_filter, 'l')) { sprintf(part, " - labels: %3d", cnt->current_image->total_labels); strcat(msg, part); } } else { sprintf(part, "Changes: %5d", cnt->current_image->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(INF, TYPE_ALL, NO_ERRNO, "%s: %s", msg); } } /* get_image end */ /***** 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 * We actually allow the time to run over the interval in case we have a delay * from slow camera. * Note: Negative value means SIGALRM snaps are enabled * httpd-control snaps are always enabled. */ /* time_current_frame is used both for snapshot and timelapse features */ time_current_frame = cnt->currenttime; if ((cnt->conf.snapshot_interval > 0 && cnt->shots == 0 && time_current_frame % cnt->conf.snapshot_interval <= time_last_frame % cnt->conf.snapshot_interval) || cnt->snapshot) { event(cnt, EVENT_IMAGE_SNAPSHOT, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); cnt->snapshot = 0; } /***** MOTION LOOP - TIMELAPSE FEATURE SECTION *****/ #ifdef HAVE_FFMPEG if (cnt->conf.timelapse) { /* * Check to see if we should start a new timelapse file. We start one when * 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->current_image->timestamp_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->current_image->timestamp_tm.tm_hour == 0) event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); /* handle the hourly case */ } else if (strcasecmp(cnt->conf.timelapse_mode, "hourly") == 0) { event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_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->current_image->timestamp_tm.tm_wday == 0 && cnt->current_image->timestamp_tm.tm_hour == 0) event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_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->current_image->timestamp_tm.tm_wday == 1 && cnt->current_image->timestamp_tm.tm_hour == 0) event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_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->current_image->timestamp_tm.tm_mday == 1 && cnt->current_image->timestamp_tm.tm_hour == 0) event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, &cnt->current_image->timestamp_tm); /* If invalid we report in syslog once and continue in manual mode */ } else { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Invalid timelapse_mode argument '%s'", cnt->conf.timelapse_mode); MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%:s 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 movie. */ if (cnt->shots == 0 && time_current_frame % cnt->conf.timelapse <= time_last_frame % cnt->conf.timelapse) event(cnt, EVENT_TIMELAPSE, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); } else if (cnt->ffmpeg_timelapse) { /* * If timelapse movie 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 the http * remote control via a cron job. */ event(cnt, EVENT_TIMELAPSEEND, NULL, NULL, NULL, cnt->currenttime_tm); } #endif /* HAVE_FFMPEG */ time_last_frame = time_current_frame; /***** MOTION LOOP - VIDEO LOOPBACK SECTION *****/ /* * Feed last image and motion image to video device pipes and the stream clients * In setup mode we send the special setup mode image to both stream and vloopback pipe * In normal mode we feed the latest image to vloopback device and we send * the image to the stream. We always send the first image in a second to the stream. * Other image are sent only when the config option stream_motion is off * The result is that with stream_motion on the stream 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 stream 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_STREAM, cnt->imgs.out, NULL, NULL, cnt->currenttime_tm); #ifdef HAVE_SDL if (cnt_list[0]->conf.sdl_threadnr == cnt->threadnr) event(cnt, EVENT_SDL_PUT, cnt->imgs.out, NULL, NULL, cnt->currenttime_tm); #endif } else { event(cnt, EVENT_IMAGE, cnt->current_image->image, NULL, &cnt->pipe, &cnt->current_image->timestamp_tm); if (!cnt->conf.stream_motion || cnt->shots == 1) event(cnt, EVENT_STREAM, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); #ifdef HAVE_SDL if (cnt_list[0]->conf.sdl_threadnr == cnt->threadnr) event(cnt, EVENT_SDL_PUT, cnt->current_image->image, NULL, NULL, &cnt->current_image->timestamp_tm); #endif } 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_pictures, "on") == 0) cnt->new_img = NEWIMG_ON; else if (strcasecmp(cnt->conf.output_pictures, "first") == 0) cnt->new_img = NEWIMG_FIRST; else if (strcasecmp(cnt->conf.output_pictures, "best") == 0) cnt->new_img = NEWIMG_BEST; else if (strcasecmp(cnt->conf.output_pictures, "center") == 0) cnt->new_img = NEWIMG_CENTER; else cnt->new_img = NEWIMG_OFF; if (strcasecmp(cnt->conf.locate_motion_mode, "on") == 0) cnt->locate_motion_mode = LOCATE_ON; else if (strcasecmp(cnt->conf.locate_motion_mode, "preview") == 0) cnt->locate_motion_mode = LOCATE_PREVIEW; else cnt->locate_motion_mode = LOCATE_OFF; if (strcasecmp(cnt->conf.locate_motion_style, "box") == 0) cnt->locate_motion_style = LOCATE_BOX; else if (strcasecmp(cnt->conf.locate_motion_style, "redbox") == 0) cnt->locate_motion_style = LOCATE_REDBOX; else if (strcasecmp(cnt->conf.locate_motion_style, "cross") == 0) cnt->locate_motion_style = LOCATE_CROSS; else if (strcasecmp(cnt->conf.locate_motion_style, "redcross") == 0) cnt->locate_motion_style = LOCATE_REDCROSS; else cnt->locate_motion_style = LOCATE_BOX; /* 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) || defined(HAVE_SQLITE3) /* * 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_movie * (FTYPE_MPEG + FTYPE_MPEG_MOTION) + cnt->conf.sql_log_timelapse * FTYPE_MPEG_TIMELAPSE; #endif /* defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) */ } /***** 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); } } /* * END OF MOTION MAIN LOOP * If code continues here it is because the thread is exiting or restarting */ err: free(rolling_average_data); cnt->lost_connection = 1; MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Thread exiting"); motion_cleanup(cnt); pthread_mutex_lock(&global_lock); threads_running--; pthread_mutex_unlock(&global_lock); if (!cnt->restart) cnt->watchdog = WATCHDOG_OFF; cnt->running = 0; cnt->finish = 0; 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; FILE *pidf = NULL; 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); /* fork */ if (fork()) { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Motion going to daemon mode"); exit(0); } /* * Create the pid file if defined, if failed exit * If we fail we report it. If we succeed we postpone the log entry till * later when we have closed stdout. Otherwise Motion hangs in the terminal waiting * for an enter. */ if (cnt_list[0]->conf.pid_file) { pidf = myfopen(cnt_list[0]->conf.pid_file, "w+"); if (pidf) { (void)fprintf(pidf, "%d\n", getpid()); myfclose(pidf); } else { MOTION_LOG(EMG, TYPE_ALL, SHOW_ERRNO, "%s: Exit motion, cannot create process" " id file (pid file) %s", cnt_list[0]->conf.pid_file); if (ptr_logfile) myfclose(ptr_logfile); exit(0); } } /* * Changing dir to root enables people to unmount a disk * without having to stop Motion */ if (chdir("/")) MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Could not change directory"); #if (defined(BSD)) setpgrp(0, getpid()); #else setpgrp(); #endif /* BSD */ 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); } /* Now it is safe to add the PID creation to the logs */ if (pidf) MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Created process id file %s. Process ID is %d", cnt_list[0]->conf.pid_file, getpid()); 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; motion_remove_pid(); while (cnt_list[++i]) context_destroy(cnt_list[i]); free(cnt_list); cnt_list = NULL; #ifndef WITHOUT_V4L 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); if ((cnt_list[0]->conf.log_level > ALL) || (cnt_list[0]->conf.log_level == 0)) { cnt_list[0]->conf.log_level = LEVEL_DEFAULT; cnt_list[0]->log_level = cnt_list[0]->conf.log_level; MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Using default log level (%s) (%d)", get_log_level_str(cnt_list[0]->log_level), SHOW_LEVEL_VALUE(cnt_list[0]->log_level)); } else { cnt_list[0]->log_level = cnt_list[0]->conf.log_level - 1; // Let's make syslog compatible } //set_log_level(cnt_list[0]->log_level); #ifdef HAVE_SDL MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Motion "VERSION" Started with SDL support"); #else MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Motion "VERSION" Started"); #endif if ((cnt_list[0]->conf.log_file) && (strncmp(cnt_list[0]->conf.log_file, "syslog", 6))) { set_log_mode(LOGMODE_FILE); ptr_logfile = set_logfile(cnt_list[0]->conf.log_file); if (ptr_logfile) { set_log_mode(LOGMODE_SYSLOG); MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Logging to file (%s)", cnt_list[0]->conf.log_file); set_log_mode(LOGMODE_FILE); } else { MOTION_LOG(EMG, TYPE_ALL, SHOW_ERRNO, "%s: Exit motion, cannot create log file %s", cnt_list[0]->conf.log_file); exit(0); } } else { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Logging to syslog"); } if ((cnt_list[0]->conf.log_type_str == NULL) || !(cnt_list[0]->log_type = get_log_type(cnt_list[0]->conf.log_type_str))) { cnt_list[0]->log_type = TYPE_DEFAULT; cnt_list[0]->conf.log_type_str = mystrcpy(cnt_list[0]->conf.log_type_str, "ALL"); MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Using default log type (%s)", get_log_type_str(cnt_list[0]->log_type)); } MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Using log type (%s) log level (%s)", get_log_type_str(cnt_list[0]->log_type), get_log_level_str(cnt_list[0]->log_level)); set_log_level(cnt_list[0]->log_level); set_log_type(cnt_list[0]->log_type); 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(NTC, TYPE_ALL, NO_ERRNO, "%s: Motion running as daemon process"); } } #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); /* use SIGVTALRM as a way to break out of the ioctl, don't restart */ sig_handler_action->sa_flags = 0; sigaction(SIGVTALRM, sig_handler_action, NULL); } /** * start_motion_thread * * Called from main when start a motion thread * * Parameters: * * cnt - Thread context pointer * thread_attr - pointer to thread attributes * * Returns: nothing */ static void start_motion_thread(struct context *cnt, pthread_attr_t *thread_attr) { int i; /* * Check the stream 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 stream. If a duplicate port is found the stream feature gets disabled (port = 0) * for this thread and a warning is written to console and syslog. */ if (cnt->conf.stream_port != 0) { /* Compare against the control port. */ if (cnt_list[0]->conf.webcontrol_port == cnt->conf.stream_port) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Stream port number %d for thread %d conflicts with the control port", cnt->conf.stream_port, cnt->threadnr); MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Stream feature for thread %d is disabled.", cnt->threadnr); cnt->conf.stream_port = 0; } /* Compare against stream ports of other threads. */ for (i = 1; cnt_list[i]; i++) { if (cnt_list[i] == cnt) continue; if (cnt_list[i]->conf.stream_port == cnt->conf.stream_port) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Stream port number %d for thread %d conflicts with thread %d", cnt->conf.stream_port, cnt->threadnr, cnt_list[i]->threadnr); MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Stream feature for thread %d is disabled.", cnt->threadnr); cnt->conf.stream_port = 0; } } } /* * Update how many threads we have running. This is done within a * mutex lock to prevent multiple simultaneous updates to * 'threads_running'. */ pthread_mutex_lock(&global_lock); threads_running++; pthread_mutex_unlock(&global_lock); /* Set a flag that we want this thread running */ cnt->restart = 1; /* Give the thread WATCHDOG_TMO to start */ cnt->watchdog = WATCHDOG_TMO; /* Flag it as running outside of the thread, otherwise if the main loop * checked if it is was running before the thread set it to 1, it would * start another thread for this device. */ cnt->running = 1; /* * Create the actual thread. Use 'motion_loop' as the thread * function. */ if (pthread_create(&cnt->thread_id, thread_attr, &motion_loop, cnt)) { /* thread create failed, undo running state */ cnt->running = 0; pthread_mutex_lock(&global_lock); threads_running--; pthread_mutex_unlock(&global_lock); } } /** * 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; 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 */ #ifdef HAVE_MYSQL if (mysql_library_init(0, NULL, NULL)) { fprintf(stderr, "could not initialize MySQL library\n"); exit(1); } #endif /* HAVE_MYSQL */ #ifdef HAVE_SQLITE3 /* database_sqlite3 == NULL if not changed causes each thread to creat their own * sqlite3 connection this will only happens when using a non-threaded sqlite version */ cnt_list[0]->database_sqlite3=NULL; if (cnt_list[0]->conf.database_type && ((!strcmp(cnt_list[0]->conf.database_type, "sqlite3")) && cnt_list[0]->conf.database_dbname)) { MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, "%s: SQLite3 Database filename %s", cnt_list[0]->conf.database_dbname); int thread_safe = sqlite3_threadsafe(); if (thread_safe > 0) { MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, "%s: SQLite3 is threadsafe"); MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, "%s: SQLite3 serialized %s", (sqlite3_config(SQLITE_CONFIG_SERIALIZED)?"FAILED":"SUCCESS")); if (sqlite3_open( cnt_list[0]->conf.database_dbname, &cnt_list[0]->database_sqlite3) != SQLITE_OK) { MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: Can't open database %s : %s", cnt_list[0]->conf.database_dbname, sqlite3_errmsg( cnt_list[0]->database_sqlite3)); sqlite3_close( cnt_list[0]->database_sqlite3); exit(1); } MOTION_LOG(NTC, TYPE_DB, NO_ERRNO, "%s: database_busy_timeout %d msec", cnt_list[0]->conf.database_busy_timeout); if (sqlite3_busy_timeout( cnt_list[0]->database_sqlite3, cnt_list[0]->conf.database_busy_timeout) != SQLITE_OK) MOTION_LOG(ERR, TYPE_DB, NO_ERRNO, "%s: database_busy_timeout failed %s", sqlite3_errmsg( cnt_list[0]->database_sqlite3)); } } #endif /* HAVE_SQLITE3 */ /* * 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(NTC, TYPE_ALL, NO_ERRNO, "%s: 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_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Restarting motion."); 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 */ MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Motion restarted"); } /* * 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 i is 0 it means no thread files and we then set the thread number to 1 */ cnt_list[i]->threadnr = i ? i : 1; /* camera_id is not defined in the config, then the camera id needs to be generated. * the load order will generate a # that will become the camera_id */ cnt_list[i]->conf.camera_id = cnt_list[i]->conf.camera_id ? cnt_list[i]->conf.camera_id: i; if (strcmp(cnt_list[i]->conf_filename, "")) { cnt_list[i]->conf_filename[sizeof(cnt_list[i]->conf_filename) - 1] = '\0'; MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Camera %d is from %s", cnt_list[i]->conf.camera_id, cnt_list[i]->conf_filename); } MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Camera %d is device: %s input %d", cnt_list[i]->conf.camera_id, 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); MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Stream port %d", cnt_list[i]->conf.stream_port); #ifdef HAVE_SQLITE /* this is done to share the seralized handle * and supress creation of new handles in the threads */ cnt_list[i]->database_sqlite3=cnt_list[0]->database_sqlite3; #endif start_motion_thread(cnt_list[i], &thread_attr); } #ifdef HAVE_SDL if (cnt_list[0]->conf.sdl_threadnr > 0) sdl_start(cnt_list[cnt_list[1] != NULL ? cnt_list[0]->conf.sdl_threadnr : 0]->conf.width, cnt_list[cnt_list[1] != NULL ? cnt_list[0]->conf.sdl_threadnr : 0]->conf.height); #endif /* * Create a thread for the control interface if requested. Create it * detached and with 'motion_web_control' as the thread function. */ if (cnt_list[0]->conf.webcontrol_port) { pthread_mutex_lock(&global_lock); threads_running++; /* set outside the loop to avoid thread set vs main thread check */ cnt_list[0]->webcontrol_running = 1; pthread_mutex_unlock(&global_lock); if (pthread_create(&thread_id, &thread_attr, &motion_web_control, cnt_list)) { /* thread create failed, undo running state */ pthread_mutex_lock(&global_lock); threads_running--; cnt_list[0]->webcontrol_running = 0; pthread_mutex_unlock(&global_lock); } } MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Waiting for threads to finish, pid: %d", getpid()); /* * Crude way of waiting for all threads to finish - check the thread * counter (because we cannot do join on the detached threads). */ while (1) { SLEEP(1, 0); /* * Calculate how many threads are running or wants to run * if zero and we want to finish, break out */ int motion_threads_running = 0; for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) { if (cnt_list[i]->running || cnt_list[i]->restart) motion_threads_running++; } if (cnt_list[0]->conf.webcontrol_port && cnt_list[0]->webcontrol_running) motion_threads_running++; if (((motion_threads_running == 0) && finish) || ((motion_threads_running == 0) && (threads_running == 0))) { MOTION_LOG(ALL, TYPE_ALL, NO_ERRNO, "%s: DEBUG-1 threads_running %d motion_threads_running %d " ", finish %d", threads_running, motion_threads_running, finish); break; } for (i = (cnt_list[1] != NULL ? 1 : 0); cnt_list[i]; i++) { /* Check if threads wants to be restarted */ if ((!cnt_list[i]->running) && (cnt_list[i]->restart)) { MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: Motion thread %d restart", cnt_list[i]->threadnr); start_motion_thread(cnt_list[i], &thread_attr); } if (cnt_list[i]->watchdog > WATCHDOG_OFF) { if (cnt_list[i]->watchdog == WATCHDOG_KILL) { /* if 0 then it finally did clean up (and will restart without any further action here) * kill(, 0) == ESRCH means the thread is no longer running * if it is no longer running with running set, then cleanup here so it can restart */ if(cnt_list[i]->running && pthread_kill(cnt_list[i]->thread_id, 0) == ESRCH) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: cleaning Thread %d", cnt_list[i]->threadnr); pthread_mutex_lock(&global_lock); threads_running--; pthread_mutex_unlock(&global_lock); motion_cleanup(cnt_list[i]); cnt_list[i]->running = 0; cnt_list[i]->finish = 0; } else { /* keep sending signals so it doesn't get stuck in a blocking call */ pthread_kill(cnt_list[i]->thread_id, SIGVTALRM); } } else { cnt_list[i]->watchdog--; if (cnt_list[i]->watchdog == 0) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Thread %d - Watchdog timeout, trying to do " "a graceful restart", cnt_list[i]->threadnr); cnt_list[i]->finish = 1; } if (cnt_list[i]->watchdog == WATCHDOG_KILL) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Thread %d - Watchdog timeout, did NOT restart graceful," "killing it!", cnt_list[i]->threadnr); /* The problem is pthread_cancel might just wake up the thread so it runs to completion * or it might not. In either case don't rip the carpet out under it by * doing motion_cleanup until it no longer is running. */ pthread_cancel(cnt_list[i]->thread_id); } } } } MOTION_LOG(ALL, TYPE_ALL, NO_ERRNO, "%s: DEBUG-2 threads_running %d motion_threads_running %d finish %d", threads_running, motion_threads_running, finish); } /* Reset end main loop flag */ finish = 0; MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: 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 */ #ifdef HAVE_SDL sdl_stop(); #endif #ifdef HAVE_FFMPEG ffmpeg_finalise(); #endif /* HAVE_FFMPEG */ // Be sure that http control exits fine cnt_list[0]->webcontrol_finish = 1; SLEEP(1, 0); MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: 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 = calloc(nbytes, 1); if (!dummy) { MOTION_LOG(EMG, TYPE_ALL, SHOW_ERRNO, "%s: Could not allocate %llu bytes of memory!", (unsigned long long)nbytes); motion_remove_pid(); 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(WRN, TYPE_ALL, NO_ERRNO, "%s: Warning! Function %s tries to resize memoryblock at %p to 0 bytes!", desc, ptr); } else { dummy = realloc(ptr, size); if (!dummy) { MOTION_LOG(EMG, TYPE_ALL, NO_ERRNO, "%s: Could not resize memory-block at offset %p to %llu bytes (function %s)!", ptr, (unsigned long long)size, desc); motion_remove_pid(); 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 = mystrdup(path); buffer[start-path] = 0x00; if (mkdir(buffer, mode) == -1 && errno != EEXIST) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Problem creating directory %s", buffer); free(buffer); return -1; } start = strchr(start + 1, '/'); if (!start) MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: creating directory %s", buffer); free(buffer); } 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); if (dummy) return dummy; /* could not open file... */ /* path did not exist? */ if (errno == ENOENT) { /* create path for file... */ if (create_path(path) == -1) return NULL; /* and retry opening the file */ dummy = fopen(path, mode); } if (!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(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Error opening file %s with mode %s", path, mode); return NULL; } return dummy; } /** * myfclose * * Motion-specific variant of fclose() * * Returns: fclose() return value */ int myfclose(FILE* fh) { int rval = fclose(fh); if (rval != 0) MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Error closing file"); return rval; } /** * 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(const 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[PATH_MAX] = ""; char *format, *tempstr; const char *pos_userformat; int width; 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'; width = 0; while ('0' <= pos_userformat[1] && pos_userformat[1] <= '9') { width *= 10; width += pos_userformat[1] - '0'; ++pos_userformat; } switch (*++pos_userformat) { case '\0': // end of string --pos_userformat; break; case 'v': // event sprintf(tempstr, "%0*d", width ? width : 2, cnt->event_nr); break; case 'q': // shots sprintf(tempstr, "%0*d", width ? width : 2, cnt->current_image->shot); break; case 'D': // diffs sprintf(tempstr, "%*d", width, cnt->current_image->diffs); break; case 'N': // noise sprintf(tempstr, "%*d", width, cnt->noise); break; case 'i': // motion width sprintf(tempstr, "%*d", width, cnt->current_image->location.width); break; case 'J': // motion height sprintf(tempstr, "%*d", width, cnt->current_image->location.height); break; case 'K': // motion center x sprintf(tempstr, "%*d", width, cnt->current_image->location.x); break; case 'L': // motion center y sprintf(tempstr, "%*d", width, cnt->current_image->location.y); break; case 'o': // threshold sprintf(tempstr, "%*d", width, cnt->threshold); break; case 'Q': // number of labels sprintf(tempstr, "%*d", width, cnt->current_image->total_labels); break; case 't': // camera id sprintf(tempstr, "%*d", width, cnt->conf.camera_id); break; case 'C': // text_event if (cnt->text_event_string && cnt->text_event_string[0]) snprintf(tempstr, PATH_MAX, "%*s", width, cnt->text_event_string); else ++pos_userformat; break; case 'w': // picture width sprintf(tempstr, "%*d", width, cnt->imgs.width); break; case 'h': // picture height sprintf(tempstr, "%*d", width, cnt->imgs.height); break; case 'f': // filename -- or %fps if ((*(pos_userformat+1) == 'p') && (*(pos_userformat+2) == 's')) { sprintf(tempstr, "%*d", width, cnt->movie_fps); pos_userformat += 2; break; } if (filename) snprintf(tempstr, PATH_MAX, "%*s", width, filename); else ++pos_userformat; break; case 'n': // sqltype if (sqltype) sprintf(tempstr, "%*d", width, sqltype); else ++pos_userformat; break; case '$': // thread name if (cnt->conf.camera_name && cnt->conf.camera_name[0]) snprintf(tempstr, PATH_MAX, "%s", cnt->conf.camera_name); 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-release-4.0/motion.h000066400000000000000000000347641300074623700157760ustar00rootroot00000000000000/* 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 #include "config.h" #if defined(__FreeBSD__) || defined(__NetBSD__) #define BSD #endif /* Includes */ #ifdef HAVE_MYSQL #include #endif #ifdef HAVE_SQLITE3 #ifdef HAVE_SQLITE3_EMBEDDED #include "sqlite3.h" #else #include #endif #endif #ifdef HAVE_PGSQL #include #endif #include #include #ifndef __USE_GNU #define __USE_GNU #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _LINUX_TIME_H 1 #if defined(HAVE_LINUX_VIDEODEV_H) && (!defined(WITHOUT_V4L)) && (!defined(BSD)) #include #endif #include #include "logger.h" #include "conf.h" #include "stream.h" #include "webhttpd.h" #ifdef HAVE_SDL #include "sdl.h" #endif /** * 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 /* strerror_r() XSI vs GNU */ #if (defined(BSD)) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE) #define XSI_STRERROR_R #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); \ } #define CLEAR(x) memset(&(x), 0, sizeof(x)) #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 */ #define DEF_PALETTE 17 /* 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_EVENT_GAP 60 /* 1 minutes */ #define DEF_MAXMOVIETIME 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 WATCHDOG_TMO 30 /* 30 sec max motion_loop interval */ #define WATCHDOG_KILL -60 /* -60 sec grace period before calling thread cancel */ #define WATCHDOG_OFF -127 /* Turn off watchdog, used when we wants to quit a thread */ #define CONNECTION_KO "Lost connection" #define CONNECTION_OK "Connection OK" #define DEF_MAXSTREAMS 10 /* Maximum number of stream clients per camera */ #define DEF_MAXWEBQUEUE 10 /* Maximum number of stream 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_IMAGEPATH "%v-%Y%m%d%H%M%S-%q" #define DEF_MOVIEPATH "%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')" /* OUTPUT Image types */ #define IMAGE_TYPE_JPEG 0 #define IMAGE_TYPE_PPM 1 /* 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) /* What types of images files do we want to have */ #define NEWIMG_OFF 0 #define NEWIMG_ON 1 #define NEWIMG_FIRST 2 #define NEWIMG_BEST 4 #define NEWIMG_CENTER 8 #define LOCATE_OFF 0 #define LOCATE_ON 1 #define LOCATE_PREVIEW 2 #define LOCATE_BOX 1 #define LOCATE_REDBOX 2 #define LOCATE_CROSS 4 #define LOCATE_REDCROSS 8 #define LOCATE_NORMAL 1 #define LOCATE_BOTH 2 #define UPDATE_REF_FRAME 1 #define RESET_REF_FRAME 2 /* Forward declaration, used in track.h */ struct images; #include "track.h" #include "netcam.h" #ifdef HAVE_MMAL #include "mmalcam.h" #endif /* * Structure to hold images information * The idea is that this should have all information about a picture e.g. diffs, timestamp etc. * The exception is the label information, it uses a lot of memory * When the image is stored all texts motion marks etc. is written to the image * so we only have to send it out when/if we want. */ /* A image can have detected motion in it, but dosn't trigger an event, if we use minimum_motion_frames */ #define IMAGE_MOTION 1 #define IMAGE_TRIGGER 2 #define IMAGE_SAVE 4 #define IMAGE_SAVED 8 #define IMAGE_PRECAP 16 #define IMAGE_POSTCAP 32 struct image_data { unsigned char *image; int diffs; time_t timestamp; /* Timestamp when image was captured */ struct tm timestamp_tm; int shot; /* Sub second timestamp count */ /* * Movement center to img center distance * Note: Dist is calculated distX*distX + distY*distY */ unsigned long cent_dist; unsigned int flags; /* Se IMAGE_* defines */ struct coord location; /* coordinates for center and size of last motion detection*/ int total_labels; }; /* * 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 http remote control, * 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, unsigned int startx, unsigned int starty, unsigned int width, const char *text, unsigned int factor); int initialize_chars(void); struct images { struct image_data *image_ring; /* The base address of the image ring buffer */ int image_ring_size; int image_ring_in; /* Index in image ring buffer we last added a image into */ int image_ring_out; /* Index in image ring buffer we want to process next time */ unsigned char *ref; /* The reference frame */ unsigned char *out; /* Picture buffer for motion images */ int *ref_dyn; /* Dynamic objects to be excluded from reference frame */ unsigned char *image_virgin; /* Last picture frame with no text or locate overlay */ struct image_data preview_image; /* 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 picture_type; /* Output picture type IMAGE_JPEG, IMAGE_PPM */ int size; int motionsize; int labelgroup_max; int labels_above; int labelsize_max; int largest_label; }; /* 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; }; /* * These used to be global variables but now each thread will have its * own context */ struct context { FILE *extpipe; int extpipe_open; char conf_filename[PATH_MAX]; int threadnr; unsigned int daemon; char pid_file[PATH_MAX]; char log_file[PATH_MAX]; char log_type_str[6]; int log_level; unsigned int log_type; struct config conf; struct images imgs; struct trackoptions track; struct netcam_context *netcam; #ifdef HAVE_MMAL struct mmalcam_context *mmalcam; #endif struct image_data *current_image; /* Pointer to a structure where the image, diffs etc is stored */ unsigned int new_img; int locate_motion_mode; int locate_motion_style; int process_thisframe; struct rotdata rotate_data; /* rotation data is thread-specific */ int noise; int threshold; int diffs_last[THRESHOLD_TUNE_LENGTH]; int smartmask_speed; /* Commands to the motion thread */ volatile unsigned int snapshot; /* Make a snapshot */ volatile unsigned int makemovie; /* End a movie */ volatile unsigned int finish; /* End the thread */ volatile unsigned int restart; /* Restart the thread when it ends */ /* Is the motion thread running */ volatile unsigned int running; /* Is the web control thread running */ volatile unsigned int webcontrol_running; volatile unsigned int webcontrol_finish; /* End the thread */ volatile int watchdog; pthread_t thread_id; int event_nr; int prev_event; unsigned int lightswitch_framecounter; char text_event_string[PATH_MAX]; /* The text for conv. spec. %C - we assume PATH_MAX normally 4096 characters is fine */ int postcap; /* downcounter, frames left to to send post event */ int shots; unsigned int detecting_motion; struct tm *currenttime_tm; struct tm *eventtime_tm; time_t currenttime; time_t lasttime; time_t eventtime; time_t connectionlosttime; /* timestamp from connection lost */ unsigned int lastrate; unsigned int startup_frames; unsigned int moved; unsigned int pause; int missing_frame_counter; /* counts failed attempts to fetch picture frame from camera */ unsigned int lost_connection; #if (defined(BSD)) int tuner_dev; #endif int video_dev; int pipe; int mpipe; struct stream stream; int stream_count; #if defined(HAVE_MYSQL) || defined(HAVE_PGSQL) || defined(HAVE_SQLITE3) int sql_mask; #endif #ifdef HAVE_SQLITE3 sqlite3 *database_sqlite3; #endif #ifdef HAVE_MYSQL MYSQL *database; #endif #ifdef HAVE_PGSQL PGconn *database_pg; #endif int movie_fps; char newfilename[PATH_MAX]; char extpipefilename[PATH_MAX]; int movie_last_shot; #ifdef HAVE_FFMPEG struct ffmpeg *ffmpeg_output; struct ffmpeg *ffmpeg_output_debug; struct ffmpeg *ffmpeg_timelapse; struct ffmpeg *ffmpeg_smartmask; char timelapsefilename[PATH_MAX]; char motionfilename[PATH_MAX]; #endif }; extern pthread_mutex_t global_lock; extern volatile int threads_running; extern FILE *ptr_logfile; /* TLS keys below */ extern pthread_key_t tls_key_threadnr; /* key for thread number */ int http_bindsock(int, int, int); void * mymalloc(size_t); void * myrealloc(void *, size_t, const char *); FILE * myfopen(const char *, const char *); int myfclose(FILE *); size_t mystrftime(const struct context *, char *, size_t, const char *, const struct tm *, const char *, int); int create_path(const char *); #endif /* _INCLUDE_MOTION_H */ motion-release-4.0/motion.init-Debian.in000066400000000000000000000025571300074623700202720ustar00rootroot00000000000000#! /bin/bash # # @PACKAGE_NAME@ @PACKAGE_VERSION@ # Start the motion detection . # NAME=@PACKAGE_NAME@ PATH=/bin:/usr/bin:/sbin:/usr/sbin DAEMON=@BIN_PATH@/@PACKAGE_NAME@ PIDFILE=/var/run/@PACKAGE_NAME@/$NAME.pid trap "" 1 export LANG=C export PATH test -f $DAEMON || exit 0 case "$1" in start) echo "Starting @PACKAGE_NAME@ detection : $NAME" start-stop-daemon --start --pidfile $PIDFILE --exec $DAEMON --chuid motion ;; stop) echo "Stopping @PACKAGE_NAME@ detection : $NAME" start-stop-daemon --stop --pidfile $PIDFILE --oknodo --exec $DAEMON --retry 30 ;; status) echo "Status @PACKAGE_NAME@ 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 start-stop-daemon --start --pidfile $PIDFILE --exec $DAEMON --chuid motion ;; 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 motion-release-4.0/motion.init-FreeBSD.sh.in000066400000000000000000000007421300074623700207250ustar00rootroot00000000000000#!/bin/sh # # motion.sh for rc.d usage 2006 Angel Carpintero # # Add the following line in /etc/rc.conf to enable @PACKAGE_NAME@ at startup # # motion_enable="YES" # # PROVIDE: @PACKAGE_NAME@ # REQUIRE: DAEMON # KEYWORD: shutdown . /etc/rc.subr motion_enable="${motion_enable-NO}" name="@PACKAGE_NAME@" rcvar=`set_rcvar` command="@BIN_PATH@/${name}" pidfile="/var/run/${name}.pid" required_files="/usr/local/etc/motion/${name}.conf" load_rc_config $name run_rc_command "$1" motion-release-4.0/motion.service.in000066400000000000000000000003161300074623700175760ustar00rootroot00000000000000[Unit] Description=Motion daemon After=local-fs.target network.target [Service] PIDFile=/var/run/motion.pid ExecStart=@BIN_PATH@/motion Type=simple StandardError=null [Install] WantedBy=multi-user.target motion-release-4.0/motion.spec.in000066400000000000000000000133251300074623700170740ustar00rootroot00000000000000Name: @PACKAGE_NAME@ Version: @PACKAGE_VERSION@ Release: 1%{?dist} Summary: A motion detection system Group: Applications/Multimedia License: GPLv2+ URL: https://motion-project.github.io/ Source0: https://github.com/Motion-Project/motion BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: libjpeg-devel ffmpeg-devel zlib-devel #This requires comes from the startup script, it will be there until motion supports libv4l calls in the code Requires: libv4l Requires(post): chkconfig Requires(preun): chkconfig initscripts Requires(postun): initscripts %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. This version is built with ffmpeg support but without MySQL and PostgreSQL support. %prep %setup -q %build %configure --sysconfdir=%{_sysconfdir}/%{name} --without-optimizecpu --with-ffmpeg --without-mysql --without-pgsql make %{?_smp_mflags} %install rm -rf %{buildroot} make install DESTDIR=%{buildroot} #We rename the configuration file mv %{buildroot}%{_sysconfdir}/%{name}/motion-dist.conf %{buildroot}%{_sysconfdir}/%{name}/motion.conf #We change the PID file path to match the one in the startup script sed -i 's|/var/run/motion/motion.pid|/var/run/motion.pid|g' %{buildroot}%{_sysconfdir}/%{name}/motion.conf #We remove SQL directives in the configuration file, as we don't use them sed -i 's|sql_log_image|; sql_log_image|g' %{buildroot}%{_sysconfdir}/%{name}/motion.conf sed -i 's|sql_log_snapshot|; sql_log_snapshot|g' %{buildroot}%{_sysconfdir}/%{name}/motion.conf sed -i 's|sql_log_mpeg|; sql_log_mpeg|g' %{buildroot}%{_sysconfdir}/%{name}/motion.conf sed -i 's|sql_log_timelapse|; sql_log_timelapse|g' %{buildroot}%{_sysconfdir}/%{name}/motion.conf sed -i 's|sql_query|; sql_query|g' %{buildroot}%{_sysconfdir}/%{name}/motion.conf #We set the log file and target directory - logging is for 3.3 branch sed -i 's|;logfile|logfile /var/log/motion.log|g' %{buildroot}%{_sysconfdir}/%{name}/motion.conf sed -i 's|target_dir /usr/local/apache2/htdocs/cam1|target_dir /var/motion|g' %{buildroot}%{_sysconfdir}/%{name}/motion.conf #We install our startup script install -D -m 0755 motion.init-Fedora %{buildroot}%{_initrddir}/%{name} %post #We add the motion init script to the services when installing /sbin/chkconfig --add %{name} %preun #We stop the service and remove it from init scripts when erasing if [ $1 = 0 ] ; then /sbin/service %{name} stop >/dev/null 2>&1 /sbin/chkconfig --del %{name} fi %postun #We restart the service during an upgrade if [ "$1" -ge "1" ] ; then /sbin/service %{name} condrestart >/dev/null 2>&1 fi %clean rm -rf %{buildroot} %files #Permissions are bogus upstream, we need to be sure to set them here %defattr (-,root,root,-) %dir %{_sysconfdir}/%{name} %dir %{_datadir}/%{name}-%{version} %dir %{_datadir}/%{name}-%{version}/examples %doc CHANGELOG COPYING CREDITS INSTALL README motion_guide.html %attr(0644,root,root) %{_datadir}/%{name}-%{version}/examples/motion-dist.conf %attr(0755,root,root) %{_datadir}/%{name}-%{version}/examples/motion.init-Debian %attr(0755,root,root) %{_datadir}/%{name}-%{version}/examples/motion.init-FreeBSD.sh %attr(0755,root,root) %{_datadir}/%{name}-%{version}/examples/motion.init-Fedora %attr(0644,root,root) %{_datadir}/%{name}-%{version}/examples/thread1.conf %attr(0644,root,root) %{_datadir}/%{name}-%{version}/examples/thread2.conf %attr(0644,root,root) %{_datadir}/%{name}-%{version}/examples/thread3.conf %attr(0644,root,root) %{_datadir}/%{name}-%{version}/examples/thread4.conf %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/%{name}/motion.conf %attr(0755,root,root) %{_bindir}/motion %attr(0644,root,root) %{_mandir}/man1/motion.1* %attr(0755,root,root) %{_initrddir}/%{name} %changelog * Thu Mar 06 2010 Steven Moix - 3.2.12-1 - New upstream release, important bugfixes only * Wed Oct 21 2009 Thorsten Leemhuis - 3.2.11.1-3 - rebuild for new ffmpeg * Tue Aug 11 2009 Steven Moix - 3.2.11.1-1 - Drop patch for ffmpeg 0.5 compatibility - Drop ffmpeg detection patch - Moved default output directory to /var/motion - New startup script with added v4l2convert to support more cameras - https://bugzilla.rpmfusion.org/show_bug.cgi?id=681 - Fix Segfault on reload or quit for vloopback (maybe other v4l1 devices too) - Fix fd leaks in external pipe - Avoid possible stack smashing in v4l_open_vidpipe() - Fix segfault for new libjpeg v7 * Mon Jul 06 2009 Steven Moix - 3.3.0-1 - SPEC Preparation for the 3.3 branch * Sun Jun 05 2009 Steven Moix - 3.2.11-5 - Patch and rebuild for ffmpeg 0.5 * Sun Mar 29 2009 Thorsten Leemhuis - 3.2.11-4 - rebuild for new F11 features * Wed Mar 18 2009 Steven Moix - 3.2.11-3 - Even more corrected init script thanks to Stewart Adam * Sun Mar 15 2009 Steven Moix - 3.2.11-2 - Removed the ffmpeg requires - Corrected the spec file - New init script with a corrected start() function and LSB header support * Tue Mar 03 2009 Steven Moix - 3.2.11-1 - Updated to Fedora 10 standard * 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 motion-release-4.0/motion_guide.html000066400000000000000000006347061300074623700176720ustar00rootroot00000000000000

Motion Guide

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 was made for the Linux operating system. Recent revisions have also made it available for BSD.

    Motion is a command line based tool with only limited support for configuration via a http interface. The many configuration options are set via the command line or configuration files.

    The output from motion can be:

    • jpg files
    • ppm format files
    • movie 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.

    On the Releases page you will find links to the latest stable version binaries for some of the most popular Linux distributions. p>

    Motion was originally written by Jeroen Vreeken and later Folkert van Heusden continued as a lead programmer with Kenneth Lavrsen responsible for Motion Guide, website and releases on Sourceforge.

    Staring with version 3.4.1 Motion is managed on github with many maintainers and contributors.

    For support we encourage you to join the mailing list instead of writing to the maintainers directly. Newbies and stupid questions are welcome on the list. Contributions in the form of pull requests to the github repository are also very welcome.

    Which version to download and use?

    The most current release version can be found on the github releases page here. The master branch of the github repository represents the official development branch of Motion. Additional independent development forks exist in other Github repos that contain additional bug fixes, enhanced features and updated documentation. This guide is based upon the features and options included in the source code with which it is distributed. It was re-written in September 2015 to reflect the changes and options in the code at that time. The majority of the new options and changed names represent differences between the 3.2.12 version and the legacy SVN trunk. Since 3.2.12 many options have been renamed to make setting up Motion easier. A cross reference of the old 3.2.12 option names to the new names can be reviewed here

    Motion is included in Debian and Ubuntu while RPM users currently must build from source.

    What features does Motion have?

    • 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
    • Real time creation of movies using libraries from ffmpeg
    • Take automated snapshots on regular intervals
    • Take automated snapshots at irregular intervals using cron
    • Executing external program when a particular event occurs
    • Motion tracking
    • Feed events to an MySQL, PostgreSQL or SQLite3 database.
    • Feed video back to a video4linux loopback for real time viewing
    • Control via simple web interface.
    • Automatic noise and threshold control
    • Ability to control the pan/tilt of a tracking camera.
    • Highly configurable display of text on images.
    • Highly configurable definition of path and file names of the stored images and movies.

    FreeBSD

    Motion was originally developed for Linux and it is still mainly developed and supported for this platform. Thanks to contributions of Angel Carpintero, Motion now builds on BSD platforms. See the file README.FreeBSD for instructions that are unique to the FreeBSD distribution.

    MacOSX

    As of this writing, Motion currently is not able to be built on MacOSX due to lack of a required header file. See Issues for tracking of the status for MacOSX.

    Documentation

    You have the following sources of information:

    Supported Hardware

    Motion supports video input from three kinds of sources.

    Standard video4linux devices (e.g. /dev/video0), network cameras and the PI camera. Motion has no drivers for cameras. Installing or configuring the camera itself is outside the scope of this document. Generally, if the device works with other common video player software, it will work with Motion (and vice versa). As a result, it is often convenient to first get the device working with other software and then use those connection options with Motion.

    Known Problems

    See also the Frequently Asked Questions and Issues for known open issues.

Installing with apt / Building from source

    Installing with apt

      Motion is part of the Ubuntu and Debian repositories and can be installed with the apt tool.

      Open up a terminal window and type: sudo apt-get install motion

      Before we start configuring Motion, we will copy the config file to our Home folder so that the master copy won't be affected. Open a terminal and copy the configuration file to your Home folder with following commands:

      mkdir .motion

      (Note: This will create a hidden folder .motion in your Home directory.)

      cp /etc/motion/motion-dist.conf ~/.motion/motion.conf

      (Note: This command will copy the original motion configuration file to its location.)

      Now can open the configuration file for editing:

      sudo nano ~/.motion/motion.conf

      After you you have made all the appropriate adjustments to the configuration file, close it. Then start motion in the terminal simply by typing:

      motion

    Abbreviated Building Guide

      If you are familiar with the building of applications, then the following is a basic build / install script for Ubuntu. If errors occur during the process or you wish to customize the build, please review the extended building instructions further below.

      sudo apt-get install autoconf automake build-essential pkgconf libtool libzip-dev libjpeg62 libjpeg62-dev git libavformat-dev libavcodec-dev libavutil-dev libswscale-dev

      cd ~
      git clone https://github.com/Motion-Project/motion.git
      cd motion
      autoreconf -fiv
      ./configure
      make
      make install

    Preparation For Building

      In order to build Motion from source many shared libraries must be installed. The particular libraries needed will vary depending upon the features desired.

      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 programs from source that use these 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 contain the header files (xxx.h) that Motion needs to build. 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.

      Open a terminal and run the following commands to install the packages.

      Ubuntu / Debian Packages
      • Required
        • sudo apt-get install autoconf automake build-essential pkgconf libtool git libzip-dev libjpeg62 libjpeg62-dev

      • Optional Packages
        • FFMpeg Functionality(Required for creating movies, using network cameras, etc. See note below!)
          • sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev libswscale-dev

        • MySQL database functionality
          • sudo apt-get install mysql-server libmysqlclient-dev

        • PostgreSQL database functionality
          • sudo apt-get install postgresql libpq-dev

        • SQLite3 database functionality
          • sudo apt-get install sqlite3

        • JPEG Turbo
          • sudo apt-get install libjpeg-turbo8 libjpeg-turbo8-dev

        • SDL
          • sudo apt-get install libsdl1.2-dev

        Important ffmpeg/libav note: Motion has been built to use either the libav or the ffmpeg set of libraries. Which set of libraries to use is a user selection and possibly dependent upon which is available in the Ubuntu/Debian repositories. The packages listed above are those that are available within Ubuntu 16.04. The package names in earlier versions or on other distributions will vary. The configure option can also be used to remove the ffmpeg/libav functionality and the dependency on these libraries.

      openSUSE Packages
      • Required
        • sudo zypper install autoconf automake libtool git

          sudo zypper install --type pattern devel_basis

          sudo zypper install libjpeg8-devel

          sudo zypper install -t pattern devel_C_C++

      • Optional Packages
        • FFMpeg Functionality(Required for creating movies, using network cameras, etc. SEE NOTE BELOW!)
          • sudo zypper ar -f -n packman-essentials http://packman.inode.at/suse/openSUSE_13.1/Essentials/ packman-essentials

            sudo zypper ar -f -n packman-multimedia http://packman.inode.at/suse/openSUSE_13.1/Multimedia/ packman-multimedia

            sudo zypper install libffmpeg-devel

        • MySQL database functionality
          • Not known by author

        • PostgreSQL database functionality
          • Not known by author

        • SQLite3 database functionality
          • Not known by author

        Important ffmpeg note: The ffmpeg libraries indicated above are provided by a external repository. This may change in the future. Validate that the repository is still valid when doing the install on openSUSE systems. The default for the configure is to require that ffmpeg or Libav is installed. Use the configure option to compile without the ffmpeg / libav functionality.

      FreeBSD
      • Required packages
        • autoconf
        • automake
        • gcc
        • gmake
        • libjpeg-turbo
        • pkgconf
        • ffmpeg
      • Optional database packages
        • mysql57-client
        • postgresql95-contrib
        • sqlite3
      • PWC based webcams packages
        • pwcbsd
        • v4l_compat

    Configure Script

      Motion uses a set tools called the "autotools" in order to generate the required scripts in order to compile and install Motion. The next step after installing all of the required and desired libraries is to have the autotools create a configure script. To do this open a terminal and change to the directory with the source code and type

      autoreconf

      If the 'configure' file exists and contains a valid script, the tool will return immediately since no additional work needs to be completed. If the script needs updating, then it will take a moment to return. Once it has been executed, a file called 'configure' will exist in the directory. Note that if the command is run as

      autoreconf -f

      The -f parameter instructs it to force a new configure file to be created. This can be preferable in certain situations so that the configure script gets updated with the correct version number. Once the 'configure' file is created, we can execute it. What the script does is interrogate the system and look for all the needed items in order to compile Motion. In this process it looks to determine which optional components have been installed on the system and if found sets flags to indicate for them to be included. If a particular library is required by Motion and is not found, the configure script will issue an error. The error means that the library was not found because it was either not installed or that it was installed into a location that the script could not find. With the Motion configure script, once it has ended it also lists out all of the optional components that were located. Note that if you KNOW that a particular component is installed yet the configure script reports it as not installed, then it may be necessary to use one or more of the configure options described below to tell the script where to find the particular component.

      To run the 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.

      When the configure script finishes you should validate that the options desired were correctly identified by the configure. In particular, the ffmpeg option is occasionally not found even if it is actually installed. Various users have indicated this to be a particular problem with the PI. If using a PI and have this issue, you can use the following option

      ./configure --with-ffmpeg=/usr/lib/arm-linux-gnueabihf

      For a long term option, you can edit the file $HOME/.bashrc and within it place the following two lines at the end PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf:$PKG_CONFIG_PATH export PKG_CONFIG_PATH This option will however only become effective the next time you get into the terminal shell.

      The following options can be specified with the configure script to customize how Motion is built.
      Options 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.
      --help=recursive display the short help of all the included packages
      -V, --version display version information and exit Provides the version number of the source code and autotools
      -q, --quiet, --silent do not print `checking...' messages Illustrates only the results of the script.
      --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.
      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
      The configuration file in /usr/local/etc/motion
      The example config files in /usr/local/motion/examples

      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.
      --libexecdir=DIR program executables [EPREFIX/libexec] Not used by motion.
      --datadir=DIR read-only architecture-independent data [PREFIX/share] Not used by motion.
      --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 motion/ subdirectory inside 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-dist.conf file that you may have edited.
      --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] Not used by motion.
      --localstatedir=DIR modifiable single-machine data [PREFIX/var] Not used by motion.
      --libdir=DIR object code libraries [EPREFIX/lib] Not used by motion.
      --includedir=DIR C header files [PREFIX/include] Not used by motion.
      --oldincludedir=DIR C header files for non-gcc [/usr/include] Not used by motion.
      --infodir=DIR info documentation [PREFIX/info] Not used by motion.
      --mandir=DIR man documentation [PREFIX/man] Editor recommends the default.
      Optional Packages:
      --with-linuxthreads Use linuxthreads in BSD instead of native phtreads Only relevant for BSD. In Linux we always use this per default.
      --with-pwcbsd Use pwcbsd based webcams ( only BSD ) This option allow to build motion to support V4L/V4L2 in BSD.
      --without-bktr Exclude to use bktr subsystem , that usually useful for devices as network cameras ONLY used in *BSD
      --without-v4l Exclude using v4l (video4linux) subsystem. Makes Motion so it only supports network cameras. Can be used if you do not need support and maybe lack some of the libraries for it.
      --with-jpeg-mmx=DIR Specify the prefix for the install path for jpeg-mmx for optimized jpeg handling
      --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 may not need to specify the directory prefix. Configure should 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) or export that location to be included in the PKG_CONFIG_PATH The option of --with-ffmpeg is the default for Motion. If the required libraries are not located, the configure script will stop at the ffmpeg section and report which libraries need to be installed. Once the required libraries are installed, run the script again. As noted previously, make sure to install the -dev versions. For more information on FFmpeg see the documentation for the FFmpeg project.
      --without-ffmpeg Do not compile with ffmpeg Use this if you do not want to compile with ffmpeg. If ffmpeg is not installed you must specify this option for Motion to build without ffmpeg.
      --with-mysql-lib=DIR Lib directory of MySQL Normally, configure will scan all possible default installation paths for MySQL libs. When its fail, use this command to tell configure where MySQL libs installation root directory is.
      --with-mysql-include=DIR Include directory with headers for MySQL Normally, configure will scan all possible default installation paths for MySQL include. When its fail, use this command to tell configure where MySQL include installation directory is. This is the directory with the MySQL header files.
      --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-lib=DIR Normally, configure will scan all possible default installation paths for pgsql libs. When it fails, use
      this command to tell configure where pgsql libs installation root directory is.
      --with-pgsql-include=DIR Normally, configure will scan all possible default installation paths for pgsql include. When it fails, use this command to tell configure where pgsql include installation root directory is.
      --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-sqlite3 Disable sqlite3 support in motion. Use this if you do not want to include SQLite3 support in the package.
      This can also be useful if you get compilation errors related to SQLite4 and you actually do not need the feature anyway.
      --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.
      --with-sdl=DIR Compile with sdl support.
      --with-jpeg-turbo=DIR Specify the prefix for the install path for jpeg-turbo for optimized jpeg handling (optional).
      --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.

    Make

      Once the configure script has successfully finished and indicates all options desired, a 'makefile' is created. The makefile builds the Motion program and links in all of the required libraries. To run the makefile on most distributions, type: make For FreeBSD distributions use gmake

      The makefile will go through each of the files and compile it. Depending upon the source code obtained, there may be many warnings or possibly none.

      If the notifications indicate undefined references, then it is most likely that an additional library needs to be added in via the configure switches. Many of these additional missing libraries issues are related to the version of ffmpeg and how it was built or installed. The following is a sample of some of the extra ffmpeg libraries that may need to be added.

      -lavformat -lswscale -lavcodec -lavutil -lfdk-aac -lswresample -lm -lopus -lz -lva -lvpx -lx264 -lmp3lame -lbz2 -ldl -lvorbisenc -lvorbis -ltheoraenc -ltheoradec

      Once the makefile has completed correctly, it will report 'build complete'.

      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

      For most distributions type make install to install the files. For FreeBSD systems, use gmake install

      These commands create the required directories and copy the files into the following locations. (default directories):
      • /usr/local/bin
      • usr/local/man/man1
      • /usr/local/etc/motion
      • /usr/local/share/doc/motion
      • /usr/local/share/motion/examples

      The contents of what gets copied into each directory is as follows: (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
      • Example configuration files "*.conf" to /usr/local/share/motion/examples
      • Configuration file "motion-dist.conf" to /usr/local/etc/motion
      Note that 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.

    Upgrading From Older Version

      If you are upgrading from an older version of Motion many options have been renamed, added or removed. The author recommends that you start by copying the configuration files from the older version to a safe location for reference only. Then start with a clean copy of the new motion-dist.conf installed and make changes to it.

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 options.

    A few important definitions.
    • A snapshot is a picture taken at regular intervals independently of any movement in the picture.
    • A "motion" image/movie 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 red.
    • A "normal" image is the real image taken by the camera with text overlayed.

The Configuration 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. Then in a directory called '.motion' in the current users home directory (shell environment variable $HOME). E.g. /home/goofy/.motion/motion.conf
    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/motion)
    If you have write access to /usr/local/etc/motion then the editor recommends having only one motion.conf file in the default /usr/local/etc/motion directory.

    Motion has a configuration file in the distribution package called motion-dist.conf. When you run 'make install' this file gets copied to the /usr/local/etc/motion 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 at the same time. The motion.conf file establishes global parameters that apply to all cameras and separate camera.conf files that specify the parameters that apply to each specific camera. The camera.conf files are specified at the bottom of the motion.conf file. If you only have one camera you only need the motion.conf file but if you have more than one camera, then you will need to have one camera config file per camera plus the motion.conf file.

    So if you have for example two cameras you need motion.conf and two camera config files. So a total of three config files.

    An option that is common to all cameras can be placed in motion.conf. (You can also put all parameters in the camera 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 camera file.

    It is often seen that people copy the entire motion.conf into the camera 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 camera config files

    The first camera is defined in the first camera file called from motion.conf. The 2nd camera is defined in the 2nd camera 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 camera config file.

    To make it clear, the camera files format and syntax is the same as motion.conf. An example of what you might want in a camera file as follows: assume you have two cameras, attached to one system. Create files camera0.conf and camera1.conf. At the end of motion.conf, uncomment out the lines that refer to them. The full contents of the camera files can be as simple as

    camera0.conf:
    videodevice /dev/video0
    stream_port 8081

    camera1.conf:
    videodevice /dev/video1
    stream_port 8082

    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. If the option "camera" is defined in motion.conf, the camera configuration file(s) is/(are) read.
    3. Motion continues reading the rest of the motion.conf file. Any options from here will overrule the same option previously defines in a camera config file.
    4. Motion reads the command line option again overruling any previously defined options.
    So always call the camera config files in the end of the motion.conf file. If you define options in motion.conf AFTER the camera file calls, the same options in the camera files will never be used. So always put the camera file call at the end of motion.conf.

    Nearly all config options can be unique for a specific camera and placed in a camera config file. There are a few options that must be in motion.conf and cannot be in a camera config file: stream_auth_method, webcontrol_* , daemon, and camera.

    If motion is built without specific features such as ffmpeg, mysql etc it will ignore the options that belong to those features.

    If you run the http control command http://host:port/0/config/writeyes, motion will overwrite motion.conf and all the camera.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

    SYNOPSIS
    motion [ -hbnsm ] [ -c config file path ] [ -d level ] [ -k level ] [ -p process_id_file ] [ -l log_file ]
    

    • -c : Full path and filename of config file.
    • -h : Show help screen
    • -b : Run in daemon mode
    • -n : Run in non-daemon mode
    • -s : Run in setup mode. Also forces non-daemon mode.
    • -d : Run with message log level 1 - 9
    • -k : Run with message log type 1 - 9
    • -l : Full path and file name for log file
    • -p : Full path and file name for the process id file
    • -m : Start in pause mode

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 movie file of the last event and exit
    SIGUSR1 Motion will create an movie 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 or to the user specified log file.

    The log 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).

Basic Setup

    Before you can start using Motion you need to know some basics about your camera(s).

    For video4linux devices, the camera must be installed per the requirements of the camera. It is out of the scope of this document to tell you how to do this and it depends on which type of camera. Once the camera is installed it will have the device name(s) of /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 USB2 port or hub consumes virtually all the bandwidth that the port can handle. Even with a small framesize and low framerate you should not expect to have more than one camera per USB controller. If you need more than one USB camera it is possible to add extra USB PCI cards to your computer. It is also possible that your computer has more than one controller for different USB slots. For example, if the computer has six USB ports, the three on the left may be on controller A versus the three on the right may be on controller B. In this situation, connecting two cameras to ports only on the left would not work due to excessive bandwidth. However connecting one camera to port on the right and the other to the port on the left may work since they are on different controllers.

    Motion permits the use of video cards that have discreet input channels. Since USB cameras do not have input channels, the option input must be set to the value -1 for USB cameras.

    Network cameras are set up via the netcam_url parameter. The latest versions of Motion support rtsp format which many cameras now stream. The URL connection string to enter is specific to the camera and is usually provided by the manufacturer. The connection string is the same as what would be used by other video playing software such as VLC. If the camera does not stream via RTSP and instead uses a MJPEG, then Motion can also view that format. See the option netcam_url for additional options.

    Raspberry Pi cameras are set up via the mmalcam_name parameter. Note that name for this parameter derives from the MMAL/OpenMax software. The most common use of this option is to use the Rasberry PI camera.

    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 -1 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 camera config files with the same videodevice name but different input numbers Motion automatically goes into round robin mode.

    Many TV tuner cards have the input channels: TV Tuner = 0, Standard composite video = 1, S-VHS = 3. Others 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 and perhaps also a S-Video 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 VLC 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).

Configuration Options-Listed Alphabetically

Configuration Options-Listed by Topic

Configuration Options-Detail Descriptions

    The following section provides detailed descriptions of each of the configuration options.

    Conversion Specifiers

      %Y year %m month %d date
      %H hour %M minute %S second
      %T HH:MM:SS %v event %q frame number
      %t camera id number %D changed pixels %N noise level
      %i width of motion area, %J height of motion area, %J height of motion area,
      %K X coordinates of motion center %L Y coordinates of motion center %C value defined by text_event
      %f filename with full path %n number indicating filetype %$ camera name

      The use of quotation marks around string is permitted. The conversion specifiers used have the same options as for the C function strftime (3) plus the few that are unique to Motion.

    System Processing

      daemon

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

      When Motion is started, immediately go to daemon mode and release the terminal.

      process_id_file

      • Type: String
      • Range / Valid values:
      • Default: Not Defined

      Specify the full path and file name in order to store the pid for processing.

      setup_mode

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

      When this option is turned on, Motion starts in setup mode so that the parameters can be set more easily. In setup mode two things happen: 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 id number. When you look at the 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 labeled 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. 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 Iceweasel or with Firefox by entering in the address of http://localhost:8080/
      • Open a new tab and connect to the http interface. http://localhost:8080/ . You can now control and change almost anything while Motion is running. 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.
      • Set the threshold to what you want to trigger Motion.
      In normal mode you can use the same setting with two browser tabs 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 camera config files).

      logfile

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default: Not Defined

      Use this option to specify the full path and filename to use for logging of the messages generated from Motion. If this option is not defined, the stderr and syslog is used. Note that Motion can generate a LOT of messages and as a result, this option should be considered if the log_level is at any of the higher levels.

      log_level

      • Type: Integer
      • Range / Valid values: 1 - 9
      • Default: 6

      This option specifies the level of verbosity of the messages sent from Motion. At a level of 8(DBG), there are a LOT of messages. At a level of 1(EMR) virtually no messages will be output.

      The various levels are [1..9] (EMR, ALR, CRT, ERR, WRN, NTC, INF, DBG, ALL).

      log_type

      • Type: Discreet Strings
      • Range / Valid values: See Below
      • Default: ALL

      The different components of Motion use different log types. This option allows the user to only show the messages from particular components. The choices for this option are: COR, STR, ENC, NET, DBL, EVT, TRK, VID, ALL

      sdl_threadnr

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      The SDL option is optional and unusual. When SDL is included in the building of Motion, there is the ability for Motion to create a SDL preview window for the user. The author believes this option to be more of a proof of concept on how to create a SDL window and show the image. (This same functionality can be achieved via the stream options) To activate the SDL window, include SDL support in the building of the Motion application. Start Motion and note the thread number indicated. Once that is noted, specify that thread number (or 1 more than that number) for this option. When Motion is started again, it will then create a SDL window to preview the image. To close the window, press X. Author is not aware of any method to restart the SDL window after it has been closed.

      camera

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default: Not Defined

      The full path and file name for the camera file. Motion allows for this line to be listed in the motion.conf file multiple times. These specifications should be the LAST lines in the motion.conf file. See the Configuration Files section within this guide for a completed discussion of what to put into the camera0.conf, camera1.conf, cameraX.conf files that would be specified in this option.

      camera_id

      • Type: Integer
      • Range / Valid values:
      • Default: The sequence that the camera file is read

      This option is useful for situations where there are multiple cameras being used by Motion. This option allows for the assignment of a numeric id number that can be used in database queries as well as format specifiers.

      camera_name

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      This option specifies a camera name to be used in the format specifiers as well as on the web interface.

      camera_dir

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      This option specifies a alternative directory to contain the individual camera configuration files.

    Video4Linux Device

      videodevice

      • Type: String
      • Range / Valid 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. See the Basic Setup section of this guide for a additional discussion of this option.

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

      v4l2_palette

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      The v4l2_palette allows the user to choose preferable palette to be use by Motion. Note that this is only the preferred option. If the video device does not support the requested format, Motion will loop through the available palettes to try to find one that is supported by both Motion and the device. Motion will report the supported palettes of the device when Motion starts when the log_level is specified as NTC or higher. The default of 17 is highly preferred since this the native format that Motion uses internally.
      • V4L2_PIX_FMT_SN9C10X : 0 'S910'
      • V4L2_PIX_FMT_SBGGR16 : 1 'BYR2'
      • V4L2_PIX_FMT_SBGGR8 : 2 'BA81'
      • V4L2_PIX_FMT_SPCA561 : 3 'S561'
      • V4L2_PIX_FMT_SGBRG8 : 4 'GBRG'
      • V4L2_PIX_FMT_SGRBG8 : 5 'GRBG'
      • V4L2_PIX_FMT_PAC207 : 6 'P207'
      • V4L2_PIX_FMT_PJPG : 7 'PJPG'
      • V4L2_PIX_FMT_MJPEG : 8 'MJPEG'
      • V4L2_PIX_FMT_JPEG : 9 'JPEG'
      • V4L2_PIX_FMT_RGB24 : 10 'RGB3'
      • V4L2_PIX_FMT_SPCA501 : 11 'S501'
      • V4L2_PIX_FMT_SPCA505 : 12 'S505'
      • V4L2_PIX_FMT_SPCA508 : 13 'S508'
      • V4L2_PIX_FMT_UYVY : 14 'UYVY'
      • V4L2_PIX_FMT_YUYV : 15 'YUYV'
      • V4L2_PIX_FMT_YUV422P : 16 '422P'
      • V4L2_PIX_FMT_YUV420 : 17 'YU12'

      tunerdevice

      • Type: String
      • Range / Valid 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. Make sure to remove or comment out this option when running Motion under Linux. See the Basic Setup section of this guide for a additional discussion of this option.

      input

      • Type: Integer
      • Range / Valid values: -1, 0 - 7
      • Default: 1 (Disabled) /li>

      Input channel to use expressed as an integer number starting from 0. This option should normally be set to 1 for video/TV cards, and -1 for USB cameras. This parameter is 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 Motion writes an error message back. If you set it to -1 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 -1. 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 -1 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)

      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.

      frequency

      • Type: Integer
      • Range / Valid values: 0 - 999999
      • Default: 0 (Not set)

      The frequency to set the tuner to (kHz) per the tuner specifications. The default is 0 meaning not set. 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.

      power_line_frequency

      • Type: Integer
      • Range / Valid values: -1 to 3
      • Default: -1

      This option allows the user to specify the power line frequency that is applicable to the user. This option can help stabilize the images of some webcams that are sensitive to this frequency. This is not normally necessary.
      • -1 : Do not modify device setting
      • 0 : Power line frequency Disabled
      • 1 : 50hz
      • 2 : 60hz
      • 3 : Auto

      auto_brightness

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

      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 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)

      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: Integer
      • Range / Valid values: 0 - 255
      • Default: 0 (disabled)

      The contrast level for the video device. Disabled (Value 0) means that Motion does not set the contrast value.

      saturation

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

      The color saturation level for the video device.

      hue

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

      The hue level for the video device.

      roundrobin_frames

      • Type: Integer
      • Range / Valid 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. The Round Robin feature is automatically activated where multiple cameras are sharing the same video device. Each camera can then set different input or frequency options to change camera. If multiple cameras use the same video device, they each can capture roundrobin_frames number of frames before having to share the device with the other cameras.

      Round Robin is not relevant for Network cameras or standard USB web cameras. It is used with video capture cards which have multiple inputs per video chip. This is not the ideal way to run multiple cameras. When the capture card changes input it takes some time 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 cameras 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 afford 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. (or better yet, it multiple cheap network cameras) If multiple cameras use the same video device, they each can capture roundrobin_frames number of frames before having to share the device with the other cameras. When another camera 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. Its function is not perfect and sometimes prevents detection of real motion. You should start with having the option disabled and then try with the option enabled to see if you can skip less frames without loosing the detection of the type of motion you normally want to detect.

      roundrobin_skip

      • Type: Integer
      • Range / Valid 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). The Round Robin feature is automatically activated where multiple cameras are sharing the same video device. Each camera can then set different input channels or frequencies to change camera. When another camera 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

      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.

    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, a mjpeg stream a rtsp stream, file or ftp. When getting a still image, make sure to validate that the camera is serving up a actual raw JPG file and not a HTML page with an embedded JPG which is the current standard. When the netcam_url is defined 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.

      netcam_url

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

      URL to use if you are using a network camera, size will be autodetected Available prefixes to the URL:
      • http://
      • ftp://
      • mjpg://
      • rtsp://
      • mjpeg://
      • file:///
      The prefixes of mjpg and mjpeg are not actual protocols and allow the user to specify different formats and methods to access the network stream. They are internally translated into http. For options such as rtsp, it is recommended that the connection string be validated with other applications such as ffplay or VLC.

      When the netcam_url is defined the video4linux options are ignored.

      netcam_userpass

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

      The Username and password for the network camera. For http protocols, this option is for HTTP 1.1 Basic authentication only. The string is specified as username:password. To use no authentication simply remove this option. Digest authentication is only available for rtsp cameras.

      netcam_keepalive

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      The setting for keep-alive of network socket, should improve performance on compatible net cameras.
      • off: The historical implementation using HTTP/1.0, closing the socket after each http request.
      • force: Use HTTP/1.0 requests with keep alive header to reuse the same connection.
      • on: Use HTTP/1.1 requests that support keep alive as default.

      netcam_proxy

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

      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_tolerant_check

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      Use less strict jpeg checks for network cameras

      rtsp_uses_tcp

      • Type: String
      • Range / Valid values: on off
      • Default: on

      This option specifies the transport method for rtsp cameras. The TCP transport is highly preferred because without this option the rtsp images are frequently corrupted and result in many false positive values and images that appear to be smeared. Off indicates that UDP will be used.

    Raspberry Pi Camera

      Motion can use the Raspberry Pi camera when connected. Before setting up Motion with the camera, it is recommended that the user first validate that the camera is functional with the raspistill/raspivid applications.

      mmalcam_name

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

      Name of camera to use if you are using a camera accessed through OpenMax/MMAL. The standard Raspberry Pi camera device name is "vc.ril.camera" without the quotes.

      mmalcam_control_params

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

      This option allows the user to specify control parameters to the Pi camera. The documentation of all of the options available to the Raspberry Camera module is beyond the scope of this guide. In general, the command line options that are available via the raspistill / raspivid applications are the options that can be specified in this Motion configuration option. Some of the more frequently used options are:
      • Horizontal Flip: -hf
      • Vertical Flip: -vf
      • Rotation: -rot

    Image Processing

      rotate

      • Type: Discrete Strings
      • Range / Valid values: 0, 90, 180, 270
      • Default: 0 (not rotated)

      Rotate image the given number of degrees. The rotation affects all saved images as well as 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.

      width

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

      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 except for rtsp streams which does rescale the network camera image to the requested dimensions. Note that this rescaling comes at a very high CPU cost so it is recommended that the network camera send the image in the same dimensions as included in the configuration file. 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. Older versions of Motion required that dimensions of image must have both height and width that are a multiple of 16. The latest versions have eliminated this requirement.

      height

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

      The height of the image in pixels. Motion does not scale so should be set to the actual size of the device. In case of a net camera motion sets the height to the height of the first image read except for rtsp streams which does rescale the network camera image to the requested dimensions. Note that this rescaling comes at a very high CPU cost so it is recommended that the network camera send the image in the same dimensions as included in the configuration file. Motion actually set the size of the image coming from the 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. Older versions of Motion required that dimensions of image must have both height and width that are a multiple of 16. The latest versions have eliminated this requirement.

      framerate

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

      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 movies. To set intervals longer than one second use the 'minimum_gap' option instead.

      minimum_frame_time

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

      Minimum time in seconds between the capturing picture frames from the camera. Default: 0 = disabled - the capture rate is given by the camera framerate. This option is used when you want to capture images at a rate lower than 2 per second. When this is enabled the framerate option is used only to set the pace the Motion service the webcam port etc.

      despeckle

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

      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

      locate_motion_mode

      • Type: Boolean
      • Range / Valid 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 movie.

      locate_motion_style

      • Type: String
      • Range / Valid values: box, redbox, cross, redcross
      • Default:

      Set the look and style of the locate box if enabled.

      text_left

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:
      User defined text overlayed on each in the lower left corner. Use A-Z, a-z, 0-9, " / ( ) @ ~ # < > \ , . : - + _ \n and conversion specifiers

      If the option is not defined no text is displayed at this position.

      You can use Conversion Specifiers to define this field and also include a new line specifier as \n and spaces if this option is enclosed in quotes.

      By combining spaces and new lines '\n' you can place your text anywhere on the picture. 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.

      This is how the overlayed text is located.

        





       

       CHANGES





       



      TEXT_LEFT

      TEXT_RIGHT
      YYYY-MM-DD
      HH:MM:SS 

      text_right

      • Type: String
      • Range / Valid 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

      If the option is not defined no text is displayed at this position.

      You can use Conversion Specifiers to define this field and also include a new line specifier as \n and spaces if this option is enclosed in quotes.

      By combining spaces and new lines '\n' you can place your text anywhere on the picture. 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.

      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.

      This is how the overlayed text is located.

        





       

       CHANGES





       



      TEXT_LEFT

      TEXT_RIGHT
      YYYY-MM-DD
      HH:MM:SS 

      text_changes

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

      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 testing. This is how the overlayed text is located.

        





       

       CHANGES





       



      TEXT_LEFT

      TEXT_RIGHT
      YYYY-MM-DD
      HH:MM:SS 

      text_event

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

      This option is special in that it defines the conversion specifier %C which can be used both for text display and for filenames. This option defines the value of the special 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. You can use Conversion Specifiers to define this field (except for this option itself)

      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 1280 x 720.

    Motion Detection

      emulate_motion

      • Type: String
      • Range / Valid values: on off
      • Default: off

      Always save images even if there was no motion

      threshold

      • Type: Integer
      • Range / Valid 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. 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. (Unless of course you are one of the many many users who use Motion to bird watch!) Practical values would be from a few hundred to thousands.

      threshold_tune

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

      Activate 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.

      noise_level

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

      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

      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. It may give different results depending on camera and light conditions.

      area_detect

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      Detect motion in predefined areas (1 - 9) and when Motion is detected in the area, execute the script. All of motion detection still continue. This option is only to execute the on_area_detect script.

      Areas are numbered like
      • 1 2 3
      • 4 5 6
      • 7 8 9

      mask_file

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

      The full path and filename for the masking pgm file. If needed, the mask will be resized to match the width and height of the frames being captured. 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. 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. 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.

      To use this feature create an image of exact the same size as the ones you get from your camera. Then make it purely white for the areas you want detected and black for the areas you want ignored. 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 method for generating the mask file is as follows.

      Take a motion captured picture, edit it with black and white for the mask and export it as a pgm file. with a program such as gimp. 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

      smart_mask_speed

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

      Speed for 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.

      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.

      The mask_file option provides a static mask to turn off sensitivity in certain areas. This is very useful 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 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.

      lightswitch

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

      Ignore sudden massive light intensity changes given as a percentage of the picture area that changed intensity. 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.

      minimum_motion_frames

      • Type: Integer
      • Range / Valid 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-5. 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. 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.

      event_gap

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

      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. The value -1 is allowed and disables events causing all Motion to be written to one single movie file and no pre_capture. If set to 0, motion is running in gapless mode. Movies don't have gaps anymore. An event ends right after no more motion is detected and post_capture is over. Disabling events has bad side effects on noise_tune and smartmask. Both features can only work properly outside an event. When event_gap is set to -1, both features don't work properly anymore. 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 timer starts after the last motion is detected and post_capture images have been saved and appended to open movie files. Any motion detected before the gap timer times out resets the gap timer so it starts counting over again. The option 'event_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:
      • A timer that timeouts 'event_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 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 'event_gap' value pre_capture is not working very often.
      • When you make movies using the ffmpeg features a new movie file is started at the beginning of an event when the first motion is detected. When 'event_gap' seconds has passed without motion (and post_captured frames saved) the movie files are completed and closed.
      • Do not use large event_gap values to generate one large movie file. If Motion stops working this movie file never gets properly completed and closed 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 'event_gap' and 'minimum_gap' have nothing to do with each other.

    Script Execution

      Motion can execute external commands based on the motion detection and related events. They are described in the sections below.

      Security Warning!

      These features mean 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!!

      on_event_start

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

      The full path and file name of the program/script to be executed at the start of an event. An event starts at first motion detected after a period of no motion defined by gap.

      You can use Conversion Specifiers and spaces as part of the command. 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.

      on_event_end

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

      The full path and file name of the program/script to be executed when a event ends. An event ends after the event_gap has expired.

      You can use Conversion Specifiers and spaces as part of the command. 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.

      on_picture_save

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

      The full path and file name of the program/script to be executed when a picture is saved.

      You can use Conversion Specifiers and spaces as part of the command. 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.

      Use %f for passing filename (with full path) to the command.

      on_motion_detected

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

      The full path and file name of the program/script to be executed when motion is detected.

      You can use Conversion Specifiers and spaces as part of the command. 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.

      on_area_detected

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      The full path and file name of the program/script to be executed when motion is detected in the predefined area indicated in the area_detect option.

      You can use Conversion Specifiers and spaces as part of the command. 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.

      on_movie_start

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

      The full path and file name of the program/script to be executed when a new movie is being created.

      You can use Conversion Specifiers and spaces as part of the command. 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.

      Use %f for passing filename (with full path) to the command.

      on_movie_end

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

      The full path and file name of the program/script to be executed after a new movie was created.

      You can use Conversion Specifiers and spaces as part of the command. 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.

      Use %f for passing filename (with full path) to the command.

      on_camera_lost

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      The full path and file name of the command to be executed when a camera can't be opened or if it is lost Note that there are situations when motion doesn't detect a lost camera. It is dependent upon the camera and driver and it is advised that this option be tested for each configuration. It has also been observed that there are also situations in which the disconnection of the camera even hangs the PC in which case this script will not be executed.

      You can use Conversion Specifiers and spaces as part of the command. 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.

    Output - General Options

      quiet

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

      Be quiet, don't output beeps when detecting motion. Only works in non-daemon mode.

      pre_capture

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

      Specifies the number of pre-captured (buffered) pictures from before motion was detected that will be output at motion detection. The recommended range is 0 to 5. It is not recommended to use large values. Large values will cause Motion to skip video frames and cause unsmooth movies. To smooth movies 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 movie. 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. 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. 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 the movie, 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 probably miss many frames of Motion.

      It is therefore recommended to use relatively 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 a smooth movie 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 movie use a large value for post_capture which does not cost any performance hit or RAM space.

      post_capture

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

      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 movies and do not have output_normal on, then the recommended post_capture value is what is equivalent to 1-5 seconds of movie.

      target_dir

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

      The full path for the target directory for picture and movie files to be saved. The default is the current working directory. This is the target directory for all snapshots, images files and movie files. You will normally always want to specify this parameter as an absolute path.

      Note that the file name options are all saved relative to this target_dir This means in principle that you can specify target_dir as '/' and be 100% flexible. But this is NOT recommended. It is recommended that this directory be specified as deep as possible.

    Output - Picture Options

      Motion can output different types of pictures. The normal picture is indicated below.

      outputnormal1.jpg

      The motion type picture or also referred to as a debug picture is shown below. 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 mathematical calculation of the past images. This enlarges real Motion and ensures that it is not easy to sneak in slowly.

      outputmotion1.jpg

      output_pictures

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

      This option controls the output of the normal image.

      'on' is the usual selection.

      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 may be useful if you store movies on a server and want to present a jpeg to show the content of the movie on a webpage.

      "best" requires a little more CPU power and resources compared to "first".

      'off' to don't write pictures

      output_debug_pictures

      • Type: Boolean
      • Range / Valid 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.

      If a Smartmask is specified, it 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 these 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.

      quality

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

      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.

      picture_type

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      This option specifies the type of picture file to output. The recommendation is to always use jpg except if you have a specific need to store high quality pictures without any quality loss.

      snapshot_interval

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

      This parameter specifies the number of seconds between each snapshot

      snapshot_filename

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

      This option indicates the file name and optionally the path for the snapshots (jpeg or ppm) relative to target_dir.

      The file extension .jpg or .ppm is automatically added.

      A symbolic link called lastsnap.jpg (or lastsnap.ppm) is created in the target_dir and will always point to the latest snapshot, unless snapshot_filename is exactly 'lastsnap'

      You can use Conversion Specifiers in this option.

      picture_filename

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

      This option indicates the file name and optionally the path for the pictures (jpeg or ppm) relative to target_dir.

      The file extension .jpg or .ppm is automatically added.

      You can use Conversion Specifiers in this option.

      exif_text

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      Use this option to specify the text to include in a JPEG EXIF comment The EXIF timestamp is included independent of this text.

      You can use Conversion Specifiers in this option.

    Output - Movie Options

      max_movie_time

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

      The maximum length of a movie in seconds. Set this to zero for unlimited length.

      ffmpeg_output_movies

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

      Use ffmpeg libraries to encode movies of the motion. This option generates a new movie at the beginning of each new event and appends to the movie for each motion detected within the same event. The current event ends when the time defined by the 'event_gap' option has passed with no motion detected. At the next detection of motion a new movie is started.

      To use this feature you need to install ffmpeg or Libav.

      ffmpeg_output_debug_movies

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

      Use ffmpeg libraries to encode motion type movies where you only see the pixels that changes. Works like ffmpeg_output_movies 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 labeling is enabled you see the largest area in blue. Smartmask is shown in red. The filename given is the same as the normal movies except they have an 'm' appended after the filename.

      ffmpeg_timelapse

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

      Create a timelapse movie saving a picture frame at the interval in seconds set by this parameter.

      ffmpeg_timelapse_mode

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

      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_bps

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

      Bitrate of movies produced by ffmpeg. Bitrate is bits per second. Higher value mans better quality and larger files if the camera is on a fixed bitrate setting. 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_variable_bitrate

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

      Enables and defines variable bitrate for the ffmpeg encoder. The option of ffmpeg_bps is ignored if variable bitrate is enabled. A value of 0 disables this option while values 1 - 100 varies the quality of the movie. The value of 1 means worst quality and 100 is the best quality.

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

      ffmpeg_video_codec

      • Type: Discrete Strings
      • Range / Valid values: mpeg1, mpeg4, msmpeg4, swf, flv, ffv1, mov, ogg, mp4, mkv, hevc
      • Default: mpeg4

      Codec to be used by ffmpeg for the video compression.

      Timelapse videos have two options.
      • mpg - Creates mpg file with mpeg-2 encoding. If motion is shutdown and restarted, new pics will be appended to any previously created file with name indicated for timelapse.
      • mpeg4 - Creates avi file with the default encoding. If motion is shutdown and restarted, new pics will create a new file with the name indicated for timelapse.

      Movie files have the following options
      • mpeg4 or msmpeg4 - gives you files with extension .avi
      • swf - gives you a flash film with extension .swf
      • flv - gives you a flash video with extension .flv
      • ffv1 - FF video codec 1 for Lossless Encoding
      • mov - QuickTime
      • ogg - Ogg/Theora
      • mp4 - MPEG-4 Part 14 H264 encoding
      • mkv - Matroska H264 encoding
      • hevc - H.265 / HEVC (High Efficiency Video Coding)

      ffmpeg_duplicate_frames

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      If the CPU can not keep up with the requested frame rate for the movies, should frames be duplicated in order to keep up or should it be skipped.

      movie_filename

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

      File path for motion triggered movies relative to target_dir. Note that the file extension is automatically added to the name based upon the codec selected.

      You can use Conversion Specifiers in this option.

      timelapse_filename

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

      File path for the timelapse movies relative to target_dir. Note that the file extension is automatically added to the name based upon the codec selected.

      You can use Conversion Specifiers in this option.

    Output - Pipe Options

      The video4linux driver is written by the same author that first created Motion. You can find the source within apt packages.

      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 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.

      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 via the apt packages and 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-1.1-rc1.tar.gz

      You now have a directory called vloopback-1.1-rc1. You can rename it to vloopback (mv vloopback-1.1-rc1 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-1.1-rc1 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 camera uses. Then you need to define the specific device name in motion.conf for the first camera and in each camera 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 camera config files, motion automatically assign video devices in the same sequence as the camera config files 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

      video_pipe

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      if a dash '-' is given motion will use /proc/video/vloopback/vloopbacks to locate a free pipe. Default: not set 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.

      motion_video_pipe

      • Type: String
      • Range / Valid 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

      use_extpipe

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      This option specifies whether to send the output to a pipe for external encoding into a movie.

      Piping raw video to stdout has some advantages comparing to using built-in ffmpeg encoder. First, this way you can use any encoder that supports RAW frames from stdin so you are not limited to the formats that are currently implemented in motion. See examples in extpipe Second, external encoder utilizes separate cpu core on a multi-core system so movie encoding is offloaded from main motion thread to a separate core(s) giving noticeable performance boost

      Note that this option does not require the install or configure of the videoloop back

      extpipe

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      This option specifies the program name and options for the program that will receive and process the images during a movie event.

      Note that this option does not require the install or configure of the videoloop back

      Sample:

      extpipe mencoder -demuxer rawvideo -rawvideo w=%w:h=%h:i420 -ovc x264 -x264encopts bframes=4:frameref=1:subq=1:scenecut=-1:nob_adapt:threads=1:keyint=1000:8x8dct:vbv_bufsize=4000:crf=24:partitions=i8x8,i4x4:vbv_maxrate=800:no-chroma-me -vf denoise3d=16:12:48:4,pp=lb -of avi -o %f.avi - -fps %fps

      extpipe x264 - --input-res %wx%h --fps %fps --bitrate 2000 --preset ultrafast --quiet -o %f.mp4

      extpipe mencoder -demuxer rawvideo -rawvideo w=%w:h=%h:fps=%fps -ovc x264 -x264encopts preset=ultrafast -of lavf -o %f.mp4 - -fps %fps

      extpipe ffmpeg -y -f rawvideo -pix_fmt yuv420p -video_size %wx%h -framerate %fps -i pipe:0 -vcodec libx264 -preset ultrafast -f mp4 %f.mp4

    Steam and Webcontrol

      Motion has simple webcam server built in. The server can be configured to stream both the images as well allow for a very basic http control of these Motion parameters.

      Each camera can have its own stream and it is enabled by specifying the port number. If there are multiple cameras, each camera must have its own unique port number.

      The webserver generates a stream in "multipart jpeg" format (mjpeg). Some browsers can not display images fed in this method. Alternatives are being considered for implementation within Motion to resolve this limitation. The most straight forward around this limitation is to create a local HTML page that references the raw stream.

      Open up a text editor and paste in the following. <html> <body bgcolor=000000> <img src=http://yourmotionpc:yourstreamport/ border="0" width=25%></a> </body> </html>

      Change the yourmotionpc with the IP or name of the computer running Motion. Also revise the port number to be the one referenced for the stream.

      Save the file and then open it in your browser.

      It may also be possible to view the stream via regular stream players such as VLC, mplayer, ffplay, avplay, etc. by specifying the network stream as http://localhost:myportnumber/stream.mjpg

      Motion can be remote controlled via a simple http interface.

      Most Motion configuration 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.

      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 , 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.

      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 webcontrol_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 webcontrol_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.

      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');

      ALERT! Security Warning! 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.
      • Run Motion as a harmless user. DO NOT RUN AS ROOT!!

      ipv6_enabled

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      # Listen to IPv6 localhost instead of IPv4 (default: off)

      stream_port

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

      # The mini-http server listens to this port for requests (default: 0 = disabled) TCP port on which motion will listen for incoming connects with its webcam server. Note that each camera 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. This must be placed in motion.conf and not in a camera config file.

      stream_quality

      • Type: Integer
      • Range / Valid 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. 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.

      stream_motion

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

      When set to 'on' slow down the stream to 1 picture per second when no motion is detected. When motion is detected the stream runs as defined by stream_maxrate When 'off' the stream always runs as defined by stream_maxrate. Use this option to save bandwidth when there is not anything important to see from the camera anyway.

      stream_maxrate

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

      Limit the framerate of the stream in frames per second. Set the value to 100 for practically unlimited. Don't set this parameter too high unless you only use it on the localhost or on an internal LAN.

      stream_localhost

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

      Limits the access to the stream to the localhost. By setting this to on, the stream can only be accessed on the same machine on which Motion is running.

      stream_limit

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      This parameter limits the maximum number of images per connection and is determined by multiplying actual stream rate by desired number of seconds

      stream_auth_method

      • Type: Integer
      • Range / Valid values: 0 to 2
      • Default: 0

      This parameter establishes desired authentication method for the stream and web control ports. The parameters have the following meaning.
      • 0 = disabled
      • 1 = Basic authentication
      • 2 = MD5 digest (the safer authentication)

      Note that if you are enabling the webcontrol feature of Motion, you really really really ... should enable security authentications. No. Seriously. You really should. See the security warnings in this document regarding how it completely opens up your system.

      stream_authentication

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default: Not Defined

      This parameter establishes the username and password to use for the stream. The syntax is username:password

      stream_preview_scale

      • Type: Integer
      • Range / Valid values:
      • Default: 25

      If the webcontrol page has HTML enabled, Motion displays all of the streams on the home webcontrol page in HTML format so that all the images can be viewed by standard browsers.

      This parameter indicates the percentage to scale the stream image when it is placed on the page. Numbers greater than 100 are permitted.

      stream_preview_newline

      • Type: string
      • Range / Valid values:on / off
      • Default: on

      If the webcontrol page has HTML enabled, Motion displays all of the streams on the home webcontrol page in HTML format so that all the images can be viewed by standard browsers.

      This parameter determines whether the image is placed on a new line in the webcontrol web page.

      Preview images are placed on to the webcontrol home page in camera id number order. This This parameter allows the user some flexibility in organizing the images on the page.

      Setting this parameter to off will set the image to the right of any image from a lower numbered thread number. Setting it to 'on' will place the image on the start of the next line(below).

      Full customization of the webcontrol page is NOT planned in the Motion development. Users that require a more polished and customized preview page are encouraged to create their own local HTML page that references the streams.

      webcontrol_port

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

      Sets the port number for the http based control of the Motion parameters. This option must be placed in the motion.conf file and not in a camera config file. Port numbers below 1024 normally require that you have root privileges. The port 8080 is the typical selection of the port for this purpose.

      webcontrol_localhost

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

      This option restricts the control of the Motion parameters to the localhost. This option must be placed in motion.conf and not in a camera 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.

      webcontrol_html_output

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

      Enable HTML in the answer sent back to a browser connecting to the webcontrol_port. This option must be placed in motion.conf and not in a camera 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.

      webcontrol_authentication

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      This parameter establishes the username and password to use for the stream. The syntax is username:password

    Database

      Motion can be compiled with MySQL, PostgreSQL or SQLite3 database support. When enabled Motion can be configured to add a record to a table in the database as specified by the sql_query. The query can contain 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 are sample create table queries for different databases.

      Mysql : CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp(14), event_time_stamp timestamp(14));

      Postgresql : CREATE TABLE security (camera int, filename char(80) not null, frame int, file_type int, time_stamp timestamp without time zone, event_time_stamp timestamp without time zone);

      sql_log_picture

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

      Log to the database when a motion triggered image file is created.

      sql_log_snapshot

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

      Log to the database when creating a snapshot image file.

      sql_log_movie

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

      Log to the database when creating motion triggered movie file.

      sql_log_timelapse

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

      Log to the database when creating timelapse movie file

      sql_query

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default: Not Defined

      SQL query string that is sent to the database when the sql_log_* item is triggered.

      You can use Conversion Specifiers within the query.

      Sample Query

      insert into security(camera, filename, frame, file_type, time_stamp, text_event) values('%t', '%f', '%q', '%n', '%Y-%m-%d %T', '%C') insert or ignore into images (camera_nbr, file_name, year, month, day, hour, minute) values (8, '%f', '%Y','%m','%d','%H','%M')

      database_type

      • Type: String
      • Range / Valid values: mysql, postgresql, sqlite3
      • Default: Not Defined

      This option specifies the database type.

      database_dbname

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default: Not Defined

      The name of the database. For Sqlite3, the full path and name to the database.

      database_host

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default: localhost

      The host on which the database is located

      database_user

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      The user account name for database

      database_password

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      The user password for database

      database_port

      • Type: Integer
      • Range / Valid values:
      • Default:

      The port number that is used for the database. Typical values are: mysql=3306 and postgresql=5432

      database_busy_timeout

      • Type: Integer
      • Range / Valid values:
      • Default: 0

      If the database is busy when the request is issued, this parameter indicates the time to wait before issuing a timeout message.

    Tracking

      This is still at the experimental stage.

      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. Search the Motion home website or community development pages for additional information regarding tracking if these features are required.

      track_type

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

      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 pwc type USB tracking cameras such as 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.

      Value 5 is for uvcvideo type USB tracking cameras such as the Logitech Quickcam Sphere/Orbit MP which is driven by the uvcvideo driver.

      track_auto

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

      Enable auto tracking Requires a tracking camera type supported by Motion.

      track_port

      • Type: String
      • Range / Valid values: Max 4095 characters
      • Default:

      This is the device name of the serial port to which the stepper motor interface is connected. Only used for stepper motor tracking.

      track_motorx

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

      The motor number that is used for controlling the x-axis. Only used for stepper motor tracking.

      track_motorx_reverse

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

      Description

      track_motory

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

      The motor number that is used for controlling the y-axis. Only used for stepper motor tracking.

      track_motory_reverse

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

      track_maxx

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

      The maximum position for servo x. Only used for stepper motor tracking.

      track_minx

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

      track_maxy

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

      The maximum position for servo y. Only used for stepper motor tracking.

      track_miny

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

      track_homex

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

      track_homey

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

      track_iomojo_id

      • Type: Integer
      • Range / Valid 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. Only used for iomojo camera.

      track_step_angle_x

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

      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 - 2147483647
      • Default: 0

      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_move_wait

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

      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_speed

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

      Speed to set the motor to. Only used for stepper motor tracking.

      track_stepsize

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

      Number of steps to make. Only used for stepper motor tracking.

motion-release-4.0/netcam.c000066400000000000000000003062241300074623700157240ustar00rootroot00000000000000/* * 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 routine 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 "netcam_ftp.h" #include "netcam_rtsp.h" #define CONNECT_TIMEOUT 10 /* Timeout on remote connection attempt */ #define READ_TIMEOUT 5 /* Default timeout on recv requests */ #define POLLING_TIMEOUT READ_TIMEOUT /* File polling timeout [s] */ #define POLLING_TIME 500*1000*1000 /* File polling time quantum [ns] (500ms) */ #define MAX_HEADER_RETRIES 5 /* Max tries to find a header record */ #define MINVAL(x, y) ((x) < (y) ? (x) : (y)) tfile_context *file_new_context(void); void file_free_context(tfile_context* ctxt); /* These strings are used for the HTTP connection. */ static const char *connect_req; static const char *connect_req_http10 = "GET %s HTTP/1.0\r\n" "Host: %s\r\n" "User-Agent: Motion-netcam/" VERSION "\r\n"; static const char *connect_req_http11 = "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: Motion-netcam/" VERSION "\r\n"; static const char *connect_req_close = "Connection: close\r\n"; static const char *connect_req_keepalive = "Connection: Keep-Alive\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 = mymalloc(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|mjpg|mjpeg|rtsp)://(((.*):(.*))@)?" "([^/:]|[-.a-z0-9]+)(:([0-9]+))?($|(/[^*]*))"; regex_t pattbuf; regmatch_t matches[10]; if (!strncmp(text_url, "file", 4)) re = "(file)://(((.*):(.*))@)?" "([^/:]|[-.a-z0-9]*)(:([0-9]*))?($|(/[^:][/-_.a-z0-9]+))"; MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: Entry netcam_url_parse data %s", text_url); 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) { MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: Parse case %d" " data %s", i, s); 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) && (parse_url->service)) { if (!strcmp(parse_url->service, "http")) parse_url->port = 80; else if (!strcmp(parse_url->service, "ftp")) parse_url->port = 21; else if (!strcmp(parse_url->service, "rtsp") && parse_url->port == 0) parse_url->port = 554; } 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 * */ void netcam_url_free(struct url_t *parse_url) { free(parse_url->service); parse_url->service = NULL; free(parse_url->userpass); parse_url->userpass = NULL; free(parse_url->host); parse_url->host = NULL; 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) MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: malformed token" " Content-Length but value %ld", length); } MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Content-Length %ld", length); return length; } /** * netcam_check_keepalive * * Analyse an HTTP-header line to see if it is a Keep-Alive. * * Parameters: * * header Pointer to a string containing the header line. * * Returns: * -1 Not a Keep-Alive line. * 1 Is a Keep-Alive line. * */ static int netcam_check_keepalive(char *header) { char *content_type = NULL; if (!header_process(header, "Keep-Alive", http_process_type, &content_type)) return -1; /* We do not detect the second field or other case mixes at present. */ free(content_type); return 1; } /** * netcam_check_close * * Analyse an HTTP-header line to see if it is a Connection: close. * * Parameters: * * header Pointer to a string containing the header line. * * Returns: * -1 Not a Connection: close. * 1 Is a Connection: close. * */ static int netcam_check_close(char *header) { char *type = NULL; int ret = -1; if (!header_process(header, "Connection", http_process_type, &type)) return -1; if (!strcmp(type, "close")) /* strcmp returns 0 for match. */ ret = 1; free(type); return ret; } /** * 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 * 3 application/octet-stream (used by WVC200 Linksys IP Camera) * */ 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; MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Content-type %s", content_type); 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 if (!strcmp(content_type, "application/octet-stream")) { ret = 3; } else { ret = 0; } 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 == NCS_MULTIPART) { while (1) { retval = header_get(netcam, &header, HG_NONE); if (retval != HG_OK) { /* Header reported as not-OK, check to see if it's null. */ if (strlen(header) == 0) { MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: Error reading image header, " "streaming mode (1). Null header."); } else { /* Header is not null. Output it in case it's a new camera with unknown headers. */ MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: Error reading image header, " "streaming mode (1). Unknown header '%s'", 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(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error reading image header (2)"); free(header); return -1; } if (*header == 0) break; if ((retval = netcam_check_content_type(header)) >= 0) { if (retval != 1) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Header not JPEG"); free(header); return -1; } } if ((retval = (int) netcam_check_content_length(header)) >= 0) { if (retval > 0) { netcam->caps.content_length = 1; /* Set flag */ netcam->receiving->content_length = retval; } else { netcam->receiving->content_length = 0; MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Content-Length 0"); free(header); return -1; } } free(header); } MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: 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 * -2 if Content-length = 0 */ static int netcam_read_first_header(netcam_context_ptr netcam) { int retval = -3; /* "Unknown err" */ int ret; int firstflag = 1; int aliveflag = 0; /* If we have seen a Keep-Alive header from cam. */ int closeflag = 0; /* If we have seen a Connection: close header from cam. */ char *header; char *boundary; /* Send the initial command to the camera. */ if (send(netcam->sock, netcam->connect_request, strlen(netcam->connect_request), 0) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Received first header ('%s')", header); if (ret != HG_OK) { MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: Error reading first header (%s)", header); free(header); return -1; } if (firstflag) { if ((ret = http_result_code(header)) != 200) { MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: HTTP Result code %d", ret); free(header); if (netcam->connect_keepalive) { /* * Cannot unset netcam->cnt->conf.netcam_keepalive as it is assigned const * But we do unset the netcam keepalive flag which was set in netcam_start * This message is logged as Information as it would be useful to know * if your netcam often returns bad HTTP result codes. */ netcam->connect_keepalive = FALSE; free((void *)netcam->cnt->conf.netcam_keepalive); netcam->cnt->conf.netcam_keepalive = strdup("off"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Removed netcam Keep-Alive flag" "due to apparent closed HTTP connection."); } return ret; } firstflag = 0; free(header); continue; } if (*header == 0) /* Blank line received */ break; /* 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 (netcam->connect_keepalive) MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Non-streaming camera " "(keep-alive set)"); else MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Non-streaming camera " "(keep-alive not set)"); netcam->caps.streaming = NCS_UNSUPPORTED; break; case 2: /* Streaming */ MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Streaming camera"); netcam->caps.streaming = NCS_MULTIPART; if ((boundary = strstr(header, "boundary="))) { /* On error recovery this may already be set. */ free(netcam->boundary); netcam->boundary = mystrdup(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); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Boundary string [%s]", netcam->boundary); } break; case 3: /* MJPG-Block style streaming. */ MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Streaming camera probably using MJPG-blocks," " consider using mjpg:// netcam_url."); break; default: /* Error */ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Unrecognized content type"); free(header); return -1; } } else if ((ret = (int) netcam_check_content_length(header)) >= 0) { MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Content-length present"); if (ret > 0) { netcam->caps.content_length = 1; /* Set flag */ netcam->receiving->content_length = ret; } else { netcam->receiving->content_length = 0; MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Content-length 0"); retval = -2; } } else if (netcam_check_keepalive(header) == TRUE) { /* Note that we have received a Keep-Alive header, and thus the socket can be left open. */ aliveflag = TRUE; netcam->keepalive_thisconn = TRUE; /* * This flag will not be set when a Streaming cam is in use, but that * does not matter as the test below looks at Streaming state also. */ } else if (netcam_check_close(header) == TRUE) { /* Note that we have received a Connection: close header. */ closeflag = TRUE; /* * This flag is acted upon below. * Changed criterion and moved up from below to catch headers that cause returns. */ MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Found Conn: close header ('%s')", header); } free(header); } free(header); if (netcam->caps.streaming == NCS_UNSUPPORTED && netcam->connect_keepalive) { /* If we are a non-streaming (ie. Jpeg) netcam and keepalive is configured. */ if (aliveflag) { if (closeflag) { netcam->warning_count++; if (netcam->warning_count > 3) { netcam->warning_count = 0; MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Both 'Connection: Keep-Alive' and " "'Connection: close' header received. Motion removes keepalive."); netcam->connect_keepalive = FALSE; free((void *)netcam->cnt->conf.netcam_keepalive); netcam->cnt->conf.netcam_keepalive = strdup("off"); } else { /* * If not a streaming cam, and keepalive is set, and the flag shows we * did not see a Keep-Alive field returned from netcam and a Close field. * Not quite sure what the correct course of action is here. In for testing. */ MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Both 'Connection: Keep-Alive' and " "'Connection: close' header received. Motion continues unchanged."); } } else { /* * aliveflag && !closeflag * * If not a streaming cam, and keepalive is set, and the flag shows we * just got a Keep-Alive field returned from netcam and no Close field. * No action, as this is the normal case. In debug we print a notification. */ MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Received a Keep-Alive field in this" "set of headers."); } } else { /* !aliveflag */ if (!closeflag) { netcam->warning_count++; if (netcam->warning_count > 3) { netcam->warning_count = 0; MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: No 'Connection: Keep-Alive' nor 'Connection: close'" " header received.\n Motion removes keepalive."); netcam->connect_keepalive = FALSE; free((void *)netcam->cnt->conf.netcam_keepalive); netcam->cnt->conf.netcam_keepalive = strdup("off"); } else { /* * If not a streaming cam, and keepalive is set, and the flag shows we * did not see a Keep-Alive field returned from netcam nor a Close field. * Not quite sure what the correct course of action is here. In for testing. */ MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: No 'Connection: Keep-Alive' nor 'Connection: close'" " header received.\n Motion continues unchanged."); } } else { /* * !aliveflag & closeflag * If not a streaming cam, and keepalive is set, and the flag shows we * received a 'Connection: close' field returned from netcam. It is not likely * we will get a Keep-Alive and Close header together - this is picked up by * the test code above. * If we receive a Close header, then we want to cease keep-alive for this cam. * This situation will occur in 2 situations: * (a) in HTTP 1.1 when the client wants to stop the keep-alive * (and in this case it would be correct to close connection and then * make a new one, with keep-alive set again). * (b) in HTTP 1.0 with keepalive, when the client does not support it. * In this case we should not attempt to re-start Keep-Alive. * Due to that, we accept a Connection: close header in HTTP 1.0 & 1.1 modes * * To tell between the sitation where a camera has been in Keep-Alive mode and * is now finishing (and will want to be re-started in Keep-Alive) and the other * case when a cam does not support it, we have a flag which says if the netcam * has returned a Keep-Alive flag during this connection. If that's set, we * set ourselves up to re-connect with Keep-Alive after the socket is closed. * If it's not set, then we will not try again to use Keep-Alive. */ if (!netcam->keepalive_thisconn) { netcam->connect_keepalive = FALSE; /* No further attempts at keep-alive */ free((void *)netcam->cnt->conf.netcam_keepalive); netcam->cnt->conf.netcam_keepalive = strdup("off"); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Removed netcam Keep-Alive flag because" " 'Connection: close' header received.\n Netcam does not support " "Keep-Alive. Motion continues in non-Keep-Alive."); } else { netcam->keepalive_timeup = TRUE; /* We will close and re-open keep-alive */ MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "Keep-Alive has reached end of valid period.\n" "Motion will close netcam, then resume Keep-Alive with a new socket."); } } } } 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: netcam_disconnect"); netcam->sock = -1; } } /** * netcam_connect * * Attempt to open the network camera as a stream device. * Keep-alive is supported, ie. if netcam->connect_keepalive is TRUE, we * re-use netcam->sock unless it has value -1, meaning it is invalid. * * Parameters: * * netcam pointer to netcam_context structure * err_flag flag to suppress error printout (1 => suppress) * Note that errors which indicate something other than * a network connection problem are not suppressed. * * Returns: 0 for success, -1 for error * */ static int netcam_connect(netcam_context_ptr netcam, int err_flag) { struct sockaddr_in6 server; /* For connect */ struct addrinfo *ai; /* For getaddrinfo */ int ret; int saveflags; int back_err; int optval; socklen_t optlen = sizeof(optval); socklen_t len; fd_set fd_w; struct timeval selecttime; /* Lookup the hostname given in the netcam URL. */ if ((ret = getaddrinfo(netcam->connect_host, NULL, NULL, &ai)) != 0) { if (!err_flag) MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: getaddrinfo() failed (%s): %s", netcam->connect_host, gai_strerror(ret)); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: disconnecting netcam (1)"); netcam_disconnect(netcam); return -1; } /* Assure any previous connection has been closed - IF we are not in keepalive. */ if (!netcam->connect_keepalive) { MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: disconnecting netcam " "since keep-alive not set."); netcam_disconnect(netcam); /* Create a new socket. */ if ((netcam->sock = socket(ai->ai_family, SOCK_STREAM, 0)) < 0) { MOTION_LOG(WRN, TYPE_NETCAM, SHOW_ERRNO, "%s: with no keepalive, attempt " "to create socket failed."); return -1; } MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: with no keepalive, " "new socket created fd %d", netcam->sock); } else if (netcam->sock == -1) { /* We are in keepalive mode, check for invalid socket. */ /* Must be first time, or closed, create a new socket. */ if ((netcam->sock = socket(ai->ai_family, SOCK_STREAM, 0)) < 0) { MOTION_LOG(WRN, TYPE_NETCAM, SHOW_ERRNO, "%s: with keepalive set, invalid socket." "This could be the first time. Creating a new one failed."); return -1; } MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: with keepalive set, invalid socket." "This could be first time, created a new one with fd %d", netcam->sock); /* Record that this connection has not yet received a Keep-Alive header. */ netcam->keepalive_thisconn = FALSE; /* Check the socket status for the keepalive option. */ if (getsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: getsockopt()"); return -1; } MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: SO_KEEPALIVE is %s", optval ? "ON":"OFF"); /* Set the option active. */ optval = 1; optlen = sizeof(optval); if (setsockopt(netcam->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: setsockopt()"); return -1; } MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: SO_KEEPALIVE set on socket."); } MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: re-using socket %d since keepalive is set.", netcam->sock); /* * Fill the hostname details into the 'server' structure and * attempt to connect to the remote server. */ memset(&server, 0, sizeof(server)); memcpy(&server, ai->ai_addr, sizeof(server)); server.sin6_family = ai->ai_family; server.sin6_port = htons(netcam->connect_port); freeaddrinfo(ai); /* * 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: fcntl(1) on socket"); netcam_disconnect(netcam); return -1; } /* Set the socket non-blocking. */ if (fcntl(netcam->sock, F_SETFL, saveflags | O_NONBLOCK) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: fcntl(2) on socket"); netcam_disconnect(netcam); return -1; } /* Now the connect call will return immediately. */ ret = connect(netcam->sock, &server, sizeof(server)); back_err = errno; /* Save the errno from connect */ /* If the connect failed with anything except EINPROGRESS, error. */ if ((ret < 0) && (back_err != EINPROGRESS)) { if (!err_flag) MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: connect() failed (%d)", back_err); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: disconnecting netcam (4)"); netcam_disconnect(netcam); 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. */ if (!err_flag) MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: timeout on connect()"); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: disconnecting netcam (2)"); netcam_disconnect(netcam); 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(ERR, TYPE_NETCAM, NO_ERRNO, "%s: getsockopt after connect"); netcam_disconnect(netcam); return -1; } /* If the return code is anything except 0, error on connect. */ if (ret) { if (!err_flag) MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: connect returned error"); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: disconnecting netcam (3)"); netcam_disconnect(netcam); return -1; } /* The socket info is stored in the rbuf structure of our context. */ rbuf_initialize(netcam); return 0; /* Success */ } void netcam_check_buffsize(netcam_buff_ptr buff, size_t numbytes) { int min_size_to_alloc; int real_alloc; int new_size; if ((buff->size - buff->used) >= numbytes) return; min_size_to_alloc = numbytes - (buff->size - buff->used); real_alloc = ((min_size_to_alloc / NETCAM_BUFFSIZE) * NETCAM_BUFFSIZE); if ((min_size_to_alloc - real_alloc) > 0) real_alloc += NETCAM_BUFFSIZE; new_size = buff->size + real_alloc; MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: expanding buffer from [%d/%d] to [%d/%d] bytes.", (int) buff->used, (int) buff->size, (int) buff->used, new_size); buff->ptr = myrealloc(buff->ptr, new_size, "netcam_check_buf_size"); buff->size = new_size; } /** * Publish new image * * Moves the image in 'receiving' into 'latest' and updates last frame time */ void netcam_image_read_complete(netcam_context_ptr netcam) { struct timeval curtime; netcam_buff *xchg; if (gettimeofday(&curtime, NULL) < 0) MOTION_LOG(WRN, TYPE_NETCAM, SHOW_ERRNO, "%s: gettimeofday"); 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; MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: 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); } /** * 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 * Note: Keep-Alive is supported for non-streaming cameras, * if enabled in the netcam's config structure. * 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. * WARNING !!! Content-Length *must* to be greater than 0, even more * a jpeg image cannot be less than 300 bytes or so. * 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; /* * 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 = 9999999; /* 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! */ MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: 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); if (retval <= 0) { /* This is a fatal error. */ MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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; } } /* Fix starting of JPEG if needed , some cameras introduce thrash before * SOI 0xFFD8 Start Of Image */ netcam_fix_jpeg_header(netcam); netcam_image_read_complete(netcam); if (netcam->caps.streaming == NCS_UNSUPPORTED) { if (!netcam->connect_keepalive) { MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: disconnecting " "netcam since keep-alive not set."); netcam_disconnect(netcam); } MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: leaving netcam connected."); } return 0; } /** * netcam_http_request * * This routine initiates a connection on the specified netcam, * for which every parameter has already been set (url, etc). * It uses the HTTP protocol, which is what many IP cameras use. * If this function succeeds, the HTTP response along with the * headers are already processed, and you can start reading contents * from here. * * Parameters: * netcam Pointer to a netcam_context structure * * Returns: 0 on success, -1 if an error occurs. */ static int netcam_http_request(netcam_context_ptr netcam) { int ix; /* * 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 * (Not always true now Keep-Alive is implemented). */ MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: about to try to connect, time #%d", ix); if (netcam_connect(netcam, 0) != 0) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "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(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error reading first header - re-trying"); } if (ix == MAX_HEADER_RETRIES) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Failed to read first camera header " "- giving up for now"); return -1; } return 0; } /** * netcam_mjpg_buffer_refill * * This routing reads content from the MJPG-camera until the response * buffer of the specified netcam_context is full. If the connection is * lost during this operation, it tries to re-connect. * * Parameters: * netcam Pointer to a netcam_context structure * * Returns: The number of read bytes, * or -1 if an fatal connection error occurs. */ static int netcam_mjpg_buffer_refill(netcam_context_ptr netcam) { int retval; if (netcam->response->buffer_left > 0) return netcam->response->buffer_left; while (1) { retval = rbuf_read_bufferful(netcam); if (retval <= 0) { /* If we got 0, we timeoutted. */ MOTION_LOG(ALR, TYPE_NETCAM, NO_ERRNO, "%s: Read error," " trying to reconnect.."); /* We may have lost the connexion */ if (netcam_http_request(netcam) < 0) { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: lost the cam."); return -1; /* We REALLY lost the cam... bail out for now. */ } } if (retval > 0) break; } netcam->response->buffer_left = retval; netcam->response->buffer_pos = netcam->response->buffer; MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Refilled buffer with [%d]" " bytes from the network.", retval); return retval; } /** * netcam_read_mjpg_jpeg * * This routine reads from a netcam using a MJPG-chunk based * protocol, used by Linksys WVC200 for example. * This implementation has been made by reverse-engineering * the protocol, so it may contain bugs and should be considered as * experimental. * * Protocol explanation: * * The stream consists of JPG pictures, spanned across multiple * MJPG chunks (in general 3 chunks, altough that's not guaranteed). * * Each data chunk can range from 1 to 65535 bytes + a header, altough * i have not seen anything bigger than 20000 bytes + a header. * * One MJPG chunk is constituted by a header plus the chunk data. * The chunk header is of fixed size, and the following data size * and position in the frame is specified in the chunk header. * * From what i have seen on WVC200 cameras, the stream always begins * on JPG frame boundary, so you don't have to worry about beginning * in the middle of a frame. * * See netcam.h for the mjpg_header structure and more details. * * Parameters: * netcam Pointer to a netcam_context structure * * Returns: 0 if an image was obtained from the camera, * or -1 if an error occurred. */ static int netcam_read_mjpg_jpeg(netcam_context_ptr netcam) { netcam_buff_ptr buffer; mjpg_header mh; size_t read_bytes; int retval; /* * Initialisation - set our local pointers to the context * information. */ buffer = netcam->receiving; /* Assure the target buffer is empty. */ buffer->used = 0; if (netcam_mjpg_buffer_refill(netcam) < 0) return -1; /* Loop until we have a complete JPG. */ while (1) { read_bytes = 0; while (read_bytes < sizeof(mh)) { /* Transfer what we have in buffer in the header structure. */ retval = rbuf_flush(netcam, ((char *)&mh) + read_bytes, sizeof(mh) - read_bytes); read_bytes += retval; MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Read [%d/%d] header bytes.", read_bytes, sizeof(mh)); /* If we don't have received a full header, refill our buffer. */ if (read_bytes < sizeof(mh)) { if (netcam_mjpg_buffer_refill(netcam) < 0) return -1; } } /* Now check the validity of our header. */ if (strncmp(mh.mh_magic, MJPG_MH_MAGIC, MJPG_MH_MAGIC_SIZE)) { MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: Invalid header received," " reconnecting"); /* * We shall reconnect to restart the stream, and get a chance * to resync. */ if (netcam_http_request(netcam) < 0) return -1; /* We lost the cam... bail out. */ /* Even there, we need to resync. */ buffer->used = 0; continue ; } /* Make room for the chunk. */ netcam_check_buffsize(buffer, (int) mh.mh_chunksize); read_bytes = 0; while (read_bytes < mh.mh_chunksize) { retval = rbuf_flush(netcam, buffer->ptr + buffer->used + read_bytes, mh.mh_chunksize - read_bytes); read_bytes += retval; MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: Read [%d/%d] chunk bytes," " [%d/%d] total", read_bytes, mh.mh_chunksize, buffer->used + read_bytes, mh.mh_framesize); if (retval < (int) (mh.mh_chunksize - read_bytes)) { /* MOTION_LOG(EMG, TYPE_NETCAM, NO_ERRNO, "Chunk incomplete, going to refill."); */ if (netcam_mjpg_buffer_refill(netcam) < 0) return -1; } } buffer->used += read_bytes; MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: Chunk complete," " buffer used [%d] bytes.", buffer->used); /* Is our JPG image complete ? */ if (mh.mh_framesize == buffer->used) { MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: Image complete," " buffer used [%d] bytes.", buffer->used); break; } /* MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: Rlen now at [%d] bytes", rlen); */ } netcam_image_read_complete(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; /* 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(ERR, TYPE_NETCAM, NO_ERRNO, "%s: ftp_get_socket failed"); 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); netcam_image_read_complete(netcam); return 0; } /** * netcam_read_file_jpeg * * This routine reads local image file. ( netcam_url file:///path/image.jpg ) * The current implementation is still a little experimental, * and needs some additional code for error detection and * recovery. */ static int netcam_read_file_jpeg(netcam_context_ptr netcam) { int loop_counter = 0; MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: Begin"); netcam_buff_ptr buffer; int len; struct stat statbuf; /* Point to our working buffer. */ buffer = netcam->receiving; buffer->used = 0; /*int fstat(int filedes, struct stat *buf);*/ do { if (stat(netcam->file->path, &statbuf)) { MOTION_LOG(CRT, TYPE_NETCAM, SHOW_ERRNO, "%s: stat(%s) error", netcam->file->path); return -1; } MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: statbuf.st_mtime[%d]" " != last_st_mtime[%d]", statbuf.st_mtime, netcam->file->last_st_mtime); /* its waits POLLING_TIMEOUT */ if (loop_counter>((POLLING_TIMEOUT*1000*1000)/(POLLING_TIME/1000))) { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: waiting new file image" " timeout"); return -1; } MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: delay waiting new" " file image "); //its waits 5seconds - READ_TIMEOUT //SLEEP(netcam->timeout.tv_sec, netcam->timeout.tv_usec*1000); SLEEP(0, POLLING_TIME); // its waits 500ms /*return -1;*/ loop_counter++; } while (statbuf.st_mtime == netcam->file->last_st_mtime); netcam->file->last_st_mtime = statbuf.st_mtime; MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: processing new file image -" " st_mtime %d", netcam->file->last_st_mtime); /* Assure there's enough room in the buffer. */ while (buffer->size < (size_t)statbuf.st_size) netcam_check_buffsize(buffer, statbuf.st_size); /* Do the read */ netcam->file->control_file_desc = open(netcam->file->path, O_RDONLY); if (netcam->file->control_file_desc < 0) { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: open(%s) error: %d", netcam->file->path, netcam->file->control_file_desc); return -1; } if ((len = read(netcam->file->control_file_desc, buffer->ptr + buffer->used, statbuf.st_size)) < 0) { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: read(%s) error: %d", netcam->file->control_file_desc, len); return -1; } buffer->used += len; close(netcam->file->control_file_desc); netcam_image_read_complete(netcam); MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: End"); return 0; } tfile_context *file_new_context(void) { /* Note that mymalloc will exit on any problem. */ return mymalloc(sizeof(tfile_context)); } void file_free_context(tfile_context* ctxt) { if (ctxt == NULL) return; free(ctxt->path); free(ctxt); } static int netcam_setup_file(netcam_context_ptr netcam, struct url_t *url) { if ((netcam->file = file_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->file->path = url->path; url->path = NULL; MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: netcam->file->path %s", netcam->file->path); netcam_url_free(url); netcam->get_image = netcam_read_file_jpeg; 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; int open_error = 0; netcam_context_ptr netcam = arg; struct context *cnt = netcam->cnt; /* Needed for the SETUP macro :-( */ #ifdef HAVE_PTHREAD_SETNAME_NP { char tname[16]; snprintf(tname, sizeof(tname), "nc%d%s%s", cnt->threadnr, cnt->conf.camera_name ? ":" : "", cnt->conf.camera_name ? cnt->conf.camera_name : ""); pthread_setname_np(pthread_self(), tname); } #endif /* 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)); MOTION_LOG(ALR, TYPE_NETCAM, NO_ERRNO, "%s: 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 == NCS_UNSUPPORTED) { /* Non-streaming ie. jpeg */ if (!netcam->connect_keepalive || (netcam->connect_keepalive && netcam->keepalive_timeup)) { /* If keepalive flag set but time up, time to close this socket. */ if (netcam->connect_keepalive && netcam->keepalive_timeup) { MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: Closing netcam socket" " as Keep-Alive time is up (camera sent Close field). A reconnect" " should happen."); netcam_disconnect(netcam); netcam->keepalive_timeup = FALSE; } /* And the netcam_connect call below will open a new one. */ if (netcam_connect(netcam, open_error) < 0) { if (!open_error) { /* Log first error. */ MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: re-opening camera (non-streaming)"); open_error = 1; } /* Need to have a dynamic delay here. */ SLEEP(5, 0); continue; } if (open_error) { /* Log re-connection */ MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: camera re-connected"); open_error = 0; } } /* Send our request and look at the response. */ if ((retval = netcam_read_first_header(netcam)) != 1) { if (retval > 0) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Unrecognized image" " header (%d)", retval); } else if (retval != -1) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error in header (%d)", retval); } /* Need to have a dynamic delay here. */ continue; } } else if (netcam->caps.streaming == NCS_MULTIPART) { /* Multipart Streaming */ if (netcam_read_next_header(netcam) < 0) { if (netcam_connect(netcam, open_error) < 0) { if (!open_error) { /* Log first error */ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: re-opening camera (streaming)"); open_error = 1; } SLEEP(5, 0); continue; } if ((retval = netcam_read_first_header(netcam) != 2)) { if (retval > 0) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Unrecognized image header (%d)", retval); } else if (retval != -1) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error in header (%d)", retval); } /* FIXME need some limit. */ continue; } } if (open_error) { /* Log re-connection */ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: camera re-connected"); open_error = 0; } } else if (netcam->caps.streaming == NCS_BLOCK) { /* MJPG-Block streaming */ /* * Since we cannot move in the stream here, because we will read past the * MJPG-block-header, error handling is done while reading MJPG blocks. */ } } if (netcam->caps.streaming == NCS_RTSP) { if (netcam->rtsp->format_context == NULL) { // We must have disconnected. Try to reconnect if ((netcam->rtsp->status == RTSP_CONNECTED) || (netcam->rtsp->status == RTSP_READINGIMAGE)){ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Reconnecting with camera...."); } netcam->rtsp->status = RTSP_RECONNECTING; netcam_connect_rtsp(netcam); continue; } else { // We think we are connected... if (netcam->get_image(netcam) < 0) { if ((netcam->rtsp->status == RTSP_CONNECTED) || (netcam->rtsp->status == RTSP_READINGIMAGE)){ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Bad image. Reconnecting with camera...."); } //Nope. We are not or got bad image. Reconnect netcam->rtsp->status = RTSP_RECONNECTING; netcam_connect_rtsp(netcam); continue; } } } if (netcam->caps.streaming != NCS_RTSP) { if (netcam->get_image(netcam) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: 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(ERR, TYPE_NETCAM, NO_ERRNO, "%s: 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 == NCS_UNSUPPORTED) { 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(ALR, TYPE_NETCAM, NO_ERRNO, "%s: 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); } /** * netcam_http_build_url * * This routing takes care of the url-processing part of the http protocol. * This includes url scheme and parsing, proxy handling, http-authentication * preparation, response buffer allocation and so on. At the end of this * routine, we are ready to call netcam_http_request(). * * Parameters: * netcam Pointer to a netcam_context structure * url Pointer to a netcam url structure * * Returns: 0 on success, * or -1 if an fatal error occurs. */ static int netcam_http_build_url(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 = mymalloc(sizeof(struct rbuf)); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Netcam has flags:" " HTTP/1.0: %s HTTP/1.1: %s Keep-Alive %s.", netcam->connect_http_10 ? "1":"0", netcam->connect_http_11 ? "1":"0", netcam->connect_keepalive ? "ON":"OFF"); /* * 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 * if a Keep-Alive header is to be included (or just 'close'), then * 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 Keep-Alive connection rather than a close connection, or * 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. * Note: Keep-Alive (but not HTTP 1.1) is disabled if a proxy URL * is set, since HTTP 1.0 Keep-alive cannot be transferred through. */ if (cnt->conf.netcam_proxy) { /* * Allocate space for a working string to contain the path. * The extra 4 is for "://" and string terminator. */ if (url->port != 0) { ptr = mymalloc(strlen(url->service) + strlen(url->host) + 7 + strlen(url->path) + 4); sprintf((char *)ptr, "http://%s:%d%s", url->host, url->port , url->path); }else { ptr = mymalloc(strlen(url->service) + strlen(url->host) + strlen(url->path) + 4); sprintf((char *)ptr, "http://%s%s", url->host, url->path); } netcam->connect_keepalive = FALSE; /* Disable Keepalive if proxy */ free((void *)netcam->cnt->conf.netcam_keepalive); netcam->cnt->conf.netcam_keepalive = strdup("off"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: " "Removed netcam_keepalive flag due to proxy set." "Proxy is incompatible with Keep-Alive."); } 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 add the required number of characters for the close header * or Keep-Alive header. We test the flag which can be unset if * there is a problem (rather than the flag in the conf structure * which is read-only. */ if (netcam->connect_keepalive) ix += strlen(connect_req_keepalive); else ix += strlen(connect_req_close); /* * Point to either the HTTP 1.0 or 1.1 request header set * If the configuration is anything other than 1.1, use 1.0 * as a default. This avoids a chance of being left with none. */ if (netcam->connect_http_11 == TRUE) connect_req = connect_req_http11; else connect_req = connect_req_http10; /* * 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 (netcam->connect_keepalive) strcat(netcam->connect_request, connect_req_keepalive); else strcat(netcam->connect_request, connect_req_close); 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. */ MOTION_LOG(INF , TYPE_NETCAM, NO_ERRNO, "%s: Camera connect" " string is ''%s'' End of camera connect string.", netcam->connect_request); return 0; } /** * netcam_setup_html * This function will parse the netcam url, connect to the camera, * set its type to jpeg-based, detect multipart and keep-alive, * and the get_image method accordingly. The cam can be non-streaming * or multipart-streaming. * * Parameters * * netcam Pointer to the netcam_context for the camera * url Pointer to the url of the camera * * Returns: 0 on success (camera link ok) or -1 if an error occurred. * */ static int netcam_setup_html(netcam_context_ptr netcam, struct url_t *url) { /* * This netcam is http-based, so build the required URL and * structures, like the connection-string and so on. */ if (netcam_http_build_url(netcam, url) < 0) return -1; /* * Then we will send our http request and get headers. */ if (netcam_http_request(netcam) < 0) 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 == NCS_MULTIPART) { if (netcam_read_next_header(netcam) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Failed " "to read first stream header - " "giving up for now"); return -1; } } MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: connected," " going on to read image."); netcam->get_image = netcam_read_html_jpeg; return 0; } /** * netcam_setup_mjpg * This function will parse the netcam url, connect to the camera, * set its type to MJPG-Streaming, and the get_image method accordingly. * * Parameters * * netcam Pointer to the netcam_context for the camera * url Pointer to the url of the camera * * Returns: 0 on success (camera link ok) or -1 if an error occurred. * */ static int netcam_setup_mjpg(netcam_context_ptr netcam, struct url_t *url) { /* * This netcam is http-based, so build the required URL and * structures, like the connection-string and so on. */ if (netcam_http_build_url(netcam, url) != 0) return -1; /* Then we will send our http request and get headers. */ if (netcam_http_request(netcam) < 0) return -1; /* We have a special type of streaming camera. */ netcam->caps.streaming = NCS_BLOCK; /* * We are positionned right just at the start of the first MJPG * header, so don't move anymore, initialization complete. */ MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: connected," " going on to read and decode MJPG chunks."); netcam->get_image = netcam_read_mjpg_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). */ if (strcmp(url->path,"/")){ netcam->ftp->path = mystrdup(url->path + 1); } else { netcam->ftp->path = mystrdup(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 = mystrdup(ptr); } else { netcam->ftp->user = mymalloc((cptr - ptr)); memcpy(netcam->ftp->user, ptr,(cptr - ptr)); netcam->ftp->passwd = mystrdup(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); netcam->ftp = NULL; return -1; } if (ftp_send_type(netcam->ftp, 'I') < 0) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: 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 * * 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) { ssize_t retval; fd_set fd_r; struct timeval selecttime; if (netcam->sock < 0) return -1; /* We are not connected, it's impossible to receive data. */ FD_ZERO(&fd_r); FD_SET(netcam->sock, &fd_r); selecttime = netcam->timeout; retval = select(FD_SETSIZE, &fd_r, NULL, NULL, &selecttime); if (retval == 0) /* 0 means timeout */ return -1; return recv(netcam->sock, buffptr, buffsize, 0); } /** * 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 == NCS_UNSUPPORTED) 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(ERR, TYPE_NETCAM, NO_ERRNO, "%s: 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. */ free(netcam->connect_host); free(netcam->connect_request); free(netcam->boundary); if (netcam->latest != NULL) { free(netcam->latest->ptr); free(netcam->latest); } if (netcam->receiving != NULL) { free(netcam->receiving->ptr); free(netcam->receiving); } if (netcam->jpegbuf != NULL) { free(netcam->jpegbuf->ptr); free(netcam->jpegbuf); } if (netcam->ftp != NULL) { ftp_free_context(netcam->ftp); netcam->ftp = NULL; } else { netcam_disconnect(netcam); } free(netcam->response); if (netcam->caps.streaming == NCS_RTSP) netcam_shutdown_rtsp(netcam); 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) { MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: 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 == NCS_UNSUPPORTED) { pthread_mutex_lock(&netcam->mutex); netcam->start_capture = 1; pthread_cond_signal(&netcam->cap_cond); pthread_mutex_unlock(&netcam->mutex); } if (netcam->caps.streaming == NCS_RTSP) { if (netcam->rtsp->status == RTSP_RECONNECTING) return NETCAM_NOTHING_NEW_ERROR; if (netcam_next_rtsp(image , netcam) < 0) return NETCAM_GENERAL_ERROR | NETCAM_JPEG_CONV_ERROR; return 0; } /* * 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 * -3 image dimensions are not modulo 8 */ 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. */ memset(&url, 0, sizeof(url)); MOTION_LOG(ALR, TYPE_NETCAM, NO_ERRNO, "%s: Network Camera thread" " starting... for url (%s)", cnt->conf.netcam_url); /* * Create a new netcam_context for this camera * and clear all the entries. */ cnt->netcam = mymalloc(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)); netcam->receiving->ptr = mymalloc(NETCAM_BUFFSIZE); netcam->jpegbuf = mymalloc(sizeof(netcam_buff)); netcam->jpegbuf->ptr = mymalloc(NETCAM_BUFFSIZE); netcam->latest = mymalloc(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(CRT, TYPE_NETCAM, NO_ERRNO, "%s: Invalid netcam_proxy (%s)", cnt->conf.netcam_proxy); netcam_url_free(&url); return -1; } if (url.userpass) { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: 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(CRT, TYPE_NETCAM, NO_ERRNO, "%s: 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; } /* Get HTTP Mode (1.0 default, 1.0 Keep-Alive, 1.1) flag from config * and report its stata for debug reasons. * The flags in the conf structure is read only and cannot be * unset if the Keep-Alive needs to be switched off (ie. netcam does * not turn out to support it. That is handled by unsetting the flags * in the context structures (cnt->...) only. */ if (!strcmp(cnt->conf.netcam_keepalive, "force")) { netcam->connect_http_10 = TRUE; netcam->connect_http_11 = FALSE; netcam->connect_keepalive = TRUE; } else if (!strcmp(cnt->conf.netcam_keepalive, "off")) { netcam->connect_http_10 = TRUE; netcam->connect_http_11 = FALSE; netcam->connect_keepalive = FALSE; } else if (!strcmp(cnt->conf.netcam_keepalive, "on")) { netcam->connect_http_10 = FALSE; netcam->connect_http_11 = TRUE; netcam->connect_keepalive = TRUE; /* HTTP 1.1 has keepalive by default. */ } MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: Netcam_http parameter '%s'" " converts to flags: HTTP/1.0: %s HTTP/1.1: %s Keep-Alive %s.", cnt->conf.netcam_keepalive, netcam->connect_http_10 ? "1":"0", netcam->connect_http_11 ? "1":"0", netcam->connect_keepalive ? "ON":"OFF"); /* Initialise the netcam socket to -1 to trigger a connection by the keep-alive logic. */ netcam->sock = -1; if ((url.service) && (!strcmp(url.service, "http"))) { MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: now calling" " netcam_setup_html()"); retval = netcam_setup_html(netcam, &url); } else if ((url.service) && (!strcmp(url.service, "ftp"))) { MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: now calling" " netcam_setup_ftp"); retval = netcam_setup_ftp(netcam, &url); } else if ((url.service) && (!strcmp(url.service, "file"))) { MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: now calling" " netcam_setup_file()"); retval = netcam_setup_file(netcam, &url); } else if ((url.service) && (!strcmp(url.service, "mjpg"))) { MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: now calling" " netcam_setup_mjpg()"); strcpy(url.service, "http"); /* Put back a real URL service. */ retval = netcam_setup_mjpg(netcam, &url); } else if ((url.service) && (!strcmp(url.service, "mjpeg"))) { MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: now calling" " netcam_setup_mjpeg()"); strcpy(url.service, "http"); /* Put back a real URL service. */ retval = netcam_setup_rtsp(netcam, &url); } else if ((url.service) && (!strcmp(url.service, "rtsp"))) { MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: now calling" " netcam_setup_rtsp()"); retval = netcam_setup_rtsp(netcam, &url); } else { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: Invalid netcam service '%s' - " "must be http, ftp, mjpg, mjpeg or file.", 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(CRT, TYPE_NETCAM, NO_ERRNO, "%s: Failed trying to " "read first image - retval:%d", retval); if (netcam->caps.streaming == NCS_RTSP) netcam->rtsp->status = RTSP_NOTCONNECTED; return -1; } if (netcam->caps.streaming != NCS_RTSP) { /* * 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(CRT, TYPE_NETCAM, NO_ERRNO, "%s: libjpeg decompression failure " "on first frame - giving up!"); return -1; } netcam->netcam_tolerant_check = cnt->conf.netcam_tolerant_check; netcam->JFIF_marker = 0; netcam_get_dimensions(netcam); } /* * Motion currently requires that image height and width is a * multiple of 8. So we check for this. */ if (netcam->width % 8) { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: netcam image width (%d)" " is not modulo 8", netcam->width); return -2; } if (netcam->height % 8) { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: netcam image height (%d)" " is not modulo 8", netcam->height); return -2; } /* 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(ALR, TYPE_NETCAM, SHOW_ERRNO, "%s: Starting camera" " handler thread [%d]", netcam->threadnr); return -1; } return 0; } motion-release-4.0/netcam.h000066400000000000000000000323331300074623700157260ustar00rootroot00000000000000/* * 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 #undef HAVE_STDLIB_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 prototypes 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; typedef struct file_context { char *path; /* the path within the URL */ int control_file_desc; /* file descriptor for the control socket */ time_t last_st_mtime; /* time this image was modified */ } tfile_context; #define NCS_UNSUPPORTED 0 /* streaming is not supported */ #define NCS_MULTIPART 1 /* streaming is done via multipart */ #define NCS_BLOCK 2 /* streaming is done via MJPG-block */ #define NCS_RTSP 3 /* streaming is done via RTSP */ enum RTSP_STATUS { RTSP_NOTCONNECTED, /* The camera has never connected */ RTSP_CONNECTED, /* The camera is currently connected */ RTSP_RECONNECTING, /* Motion is trying to reconnect to camera */ RTSP_READINGIMAGE /* Motion is reading a image from camera */ }; /* * 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 acknowledge 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 */ int connect_http_10; /* set to TRUE if HTTP 1.0 connection (netcam_keepalive off) */ int connect_http_11; /* set to TRUE if HTTP 1.1 connection (netcam_keepalive on) */ int connect_keepalive; /* set to TRUE if connection maintained after a request, otherwise FALSE to close down the socket each time (netcam_keealive force) */ int keepalive_thisconn; /* set to TRUE if cam has sent 'Keep-Alive' in this connection */ int keepalive_timeup; /* set to TRUE if it is time to close netcam's socket, and then re-open it with Keep-Alive set again. Even Keep-Alive netcams need a close/open sometimes. */ 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 */ struct file_context *file; /* this structure contains the context for FILE connection */ struct rtsp_context *rtsp; /* this structure contains the context for RTSP connection */ int (*get_image)(netcam_context_ptr); /* Function to fetch the image from the netcam. It is initialised in netcam_setup depending upon whether the picture source is from an http server or from an ftp server */ struct netcam_caps { /* netcam capabilities: */ unsigned char streaming; /* See the NCS_* defines */ 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 decompression */ int imgcnt; /* count for # of received jpegs */ int imgcnt_last; /* remember last count to check if a new image arrived */ int warning_count; /* simple count of number of warnings since last good frame was received */ int error_count; /* simple count of number of errors since last good frame was received */ unsigned int width; /* info for decompression */ unsigned int height; int JFIF_marker; /* Debug to know if JFIF was present or not */ unsigned int netcam_tolerant_check; /* For network cameras with buggy firmwares */ 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; #define MJPG_MH_MAGIC "MJPG" #define MJPG_MH_MAGIC_SIZE 4 /* * MJPG Chunk header for MJPG streaming. * Little-endian data is read from the network. */ typedef struct { char mh_magic[MJPG_MH_MAGIC_SIZE]; /* must contain the string MJP not null-terminated. */ unsigned int mh_framesize; /* Total size of the current frame in bytes (~45kb on WVC200) */ unsigned short mh_framewidth; /* Frame width in pixels */ unsigned short mh_frameheight; /* Frame height in pixels */ unsigned int mh_frameoffset; /* Offset of this chunk relative to the beginning of frame. */ unsigned short mh_chunksize; /* The size of the chunk data following this header. */ char mh_reserved[30]; /* Unknown data, seems to be constant between all headers */ } mjpg_header; /* * Declare prototypes for our external entry points */ /* Within netcam_jpeg.c */ int netcam_proc_jpeg (struct netcam_context *, unsigned char *); void netcam_fix_jpeg_header(struct netcam_context *); 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); void netcam_url_free(struct url_t *parse_url); /** * Publish new image * * Moves the image in 'receiving' into 'latest' and updates last frame time */ void netcam_image_read_complete(netcam_context_ptr netcam); /** * 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 */ void netcam_check_buffsize(netcam_buff_ptr buff, size_t numbytes); #endif motion-release-4.0/netcam_ftp.c000066400000000000000000000532251300074623700165750ustar00rootroot00000000000000/* * 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 "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; free(ctxt->path); free(ctxt->user); 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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; MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: Server Response: %s",ctxt->control_buffer); return (res / 100); } /** * ftp_send_user * Sends 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: send failed in ftp_send_user"); return res; } return 0; } /** * ftp_send_passwd * Sends 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: gethostbyname failed in ftp_connect"); return -1; } if ((unsigned int) hp->h_length > sizeof(((struct sockaddr_in *)&ctxt->ftp_address)->sin_addr)) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: socket failed"); return -1; } /* Do the connect. */ if (connect(ctxt->control_file_desc, (struct sockaddr *) &ctxt->ftp_address, addrlen) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(WRN, TYPE_NETCAM, NO_ERRNO, "%s: 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 in case 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: socket failed"); return -1; } on = 1; if (setsockopt(ctxt->data_file_desc, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(WRN, TYPE_NETCAM, NO_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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); snprintf(buf, sizeof(buf), "TYPE %c\r\n", utype); len = strlen(buf); res = send(ctxt->control_file_desc, buf, len, 0); if (res < 0) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: 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; } motion-release-4.0/netcam_ftp.h000066400000000000000000000033121300074623700165720ustar00rootroot00000000000000/* * 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 motion-release-4.0/netcam_jpeg.c000066400000000000000000000407021300074623700167250ustar00rootroot00000000000000/* * netcam_jpeg.c * * Module for handling JPEG decompression for network 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 "rotate.h" /* already includes motion.h */ #include #include /* * 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 calls ERREXIT(). */ if (src->start_of_file) { nbytes = src->length; src->buffer = (JOCTET *) src->data; } else { nbytes = 2; MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: Not enough data from netcam."); ERREXIT(cinfo, JERR_INPUT_EOF); } 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 throw 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); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: netcam->jpeg_error %d", netcam->jpeg_error); /* 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) && (cinfo->err->msg_code == JWRN_NOT_SEQUENTIAL) && (!netcam->netcam_tolerant_check)) netcam->jpeg_error |= 2; /* Set flag to show problem */ /* * Format the message according to library standards. * Write it out to the motion log. */ (*cinfo->err->format_message)(cinfo, buffer); MOTION_LOG(DBG, TYPE_NETCAM, NO_ERRNO, "%s: %s", 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); MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: no new pic, no signal rcvd"); return NETCAM_GENERAL_ERROR | NETCAM_NOTHING_NEW_ERROR; } MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: ***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); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: jpeg_error %d", netcam->jpeg_error); return netcam->jpeg_error; } /** * netcam_image_conv * * Parameters: * netcam pointer to netcam_context * cinfo pointer to JPEG decompression context * image pointer to buffer of destination image (yuv420) * * Returns : 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(WRN, TYPE_NETCAM, NO_ERRNO, "%s: 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); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: jpeg_error %d", netcam->jpeg_error); 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. * We need to "protect" the "latest" image while we * decompress it. netcam_init_jpeg uses * netcam->mutex to do this. */ MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: processing jpeg image" " - content length %d", netcam->latest->content_length); ret = netcam_init_jpeg(netcam, &cinfo); if (ret != 0) { MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: ret %d", ret); return ret; } /* * Do a sanity check on dimensions * If dimensions have changed we throw an * error message 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)) { retval = NETCAM_RESTART_ERROR; MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Camera width/height mismatch " "with JPEG image - expected %dx%d, JPEG %dx%d", " retval %d", netcam->width, netcam->height, cinfo.output_width, cinfo.output_height, retval); return retval; } } /* Do the conversion */ ret = netcam_image_conv(netcam, &cinfo, image); if (ret != 0) { retval |= NETCAM_JPEG_CONV_ERROR; MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: ret %d retval %d", ret, retval); } return retval; } /** * netcam_fix_jpeg_header * * 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 * * Returns: Nothing * */ void netcam_fix_jpeg_header(netcam_context_ptr netcam) { char *ptr_buffer; ptr_buffer = memmem(netcam->receiving->ptr, netcam->receiving->used, "\xff\xd8", 2); if (ptr_buffer != NULL) { size_t soi_position = 0; soi_position = ptr_buffer - netcam->receiving->ptr; if (soi_position > 0) { memmove(netcam->receiving->ptr, netcam->receiving->ptr + soi_position, netcam->receiving->used - soi_position); netcam->receiving->used -= soi_position; } // if (debug_level > CAMERA_INFO) // motion_log(LOG_INFO, 0, "%s: SOI found , position %d", // __FUNCTION__, soi_position); } } /** * 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. */ int ret; ret = netcam_init_jpeg(netcam, &cinfo); netcam->width = cinfo.output_width; netcam->height = cinfo.output_height; netcam->JFIF_marker = cinfo.saw_JFIF_marker; jpeg_destroy_decompress(&cinfo); MOTION_LOG(INF, TYPE_NETCAM, NO_ERRNO, "%s: JFIF_marker %s PRESENT ret %d", netcam->JFIF_marker ? "IS" : "NOT", ret); } motion-release-4.0/netcam_rtsp.c000066400000000000000000000733551300074623700170020ustar00rootroot00000000000000/*********************************************************** * In the top section are the functions that are used * when processing the RTSP camera feed. Since these functions * are internal to the RTSP module, and many require FFmpeg * structures in their declarations, they are within the * HAVE_FFMPEG block that eliminates them entirely when * FFmpeg is not present. * * The functions: * netcam_setup_rtsp * netcam_connect_rtsp * netcam_shutdown_rtsp * netcam_next_rtsp * are called from netcam.c therefore must be defined even * if FFmpeg is not present. They must also not have FFmpeg * structures in the declarations. Simple error * messages are raised if called when no FFmpeg is found. * ***********************************************************/ #include #include "rotate.h" /* already includes motion.h */ #include "netcam_rtsp.h" #ifdef HAVE_FFMPEG #include "ffmpeg.h" static int netcam_rtsp_resize(netcam_context_ptr netcam); static int netcam_rtsp_open_sws(netcam_context_ptr netcam); /** * netcam_check_pixfmt * * Determine whether pix_format is YUV420P */ static int netcam_check_pixfmt(netcam_context_ptr netcam){ int retcd; retcd = -1; if ((netcam->rtsp->codec_context->pix_fmt == MY_PIX_FMT_YUV420P) || (netcam->rtsp->codec_context->pix_fmt == MY_PIX_FMT_YUVJ420P)) retcd = 0; return retcd; } /** * netcam_rtsp_null_context * * Null all the context */ static void netcam_rtsp_null_context(netcam_context_ptr netcam){ netcam->rtsp->swsctx = NULL; netcam->rtsp->swsframe_in = NULL; netcam->rtsp->swsframe_out = NULL; netcam->rtsp->frame = NULL; netcam->rtsp->codec_context = NULL; netcam->rtsp->format_context = NULL; } /** * netcam_rtsp_close_context * * Close all the context that could be open */ static void netcam_rtsp_close_context(netcam_context_ptr netcam){ if (netcam->rtsp->swsctx != NULL) sws_freeContext(netcam->rtsp->swsctx); if (netcam->rtsp->swsframe_in != NULL) my_frame_free(netcam->rtsp->swsframe_in); if (netcam->rtsp->swsframe_out != NULL) my_frame_free(netcam->rtsp->swsframe_out); if (netcam->rtsp->frame != NULL) my_frame_free(netcam->rtsp->frame); if (netcam->rtsp->codec_context != NULL) avcodec_close(netcam->rtsp->codec_context); if (netcam->rtsp->format_context != NULL) avformat_close_input(&netcam->rtsp->format_context); netcam_rtsp_null_context(netcam); } /** * decode_packet * * This routine takes in the packet from the read and decodes it into * the frame. It then takes the frame and copies it into the netcam * buffer * * Parameters: * packet The packet that was read from av_read * buffer The buffer that is the final destination * frame The frame into which we decode the packet * * * Returns: * Failure 0(zero) * Success The size of the frame decoded */ static int decode_packet(AVPacket *packet, netcam_buff_ptr buffer, AVFrame *frame, AVCodecContext *cc){ int check = 0; int frame_size = 0; int retcd = 0; retcd = avcodec_decode_video2(cc, frame, &check, packet); if (retcd < 0) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error decoding video packet"); return 0; } if (check == 0) { return 0; } frame_size = my_image_get_buffer_size(cc->pix_fmt, cc->width, cc->height); netcam_check_buffsize(buffer, frame_size); retcd = my_image_copy_to_buffer(frame, (uint8_t *)buffer->ptr,cc->pix_fmt,cc->width,cc->height, frame_size); if (retcd < 0) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error decoding video packet: Copying to buffer"); return 0; } buffer->used = frame_size; return frame_size; } /** * netcam_open_codec * * This routine opens the codec context for the indicated stream * * Parameters: * stream_idx The index of the stream that was found as "best" * fmt_ctx The format context that was created upon opening the stream * type The type of media type (This is a constant) * * * Returns: * Failure Error code from FFmpeg (Negative number) * Success 0(Zero) */ static int netcam_open_codec(int *stream_idx, AVFormatContext *fmt_ctx, enum AVMediaType type){ int ret; char errstr[128]; AVStream *st; AVCodecContext *dec_ctx = NULL; AVCodec *dec = NULL; ret = av_find_best_stream(fmt_ctx, type, -1, -1, NULL, 0); if (ret < 0) { av_strerror(ret, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Could not find stream in input!: %s",errstr); return ret; } *stream_idx = ret; st = fmt_ctx->streams[*stream_idx]; /* find decoder for the stream */ dec_ctx = st->codec; dec = avcodec_find_decoder(dec_ctx->codec_id); if (dec == NULL) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Failed to find codec!"); return -1; } /* Open the codec */ ret = avcodec_open2(dec_ctx, dec, NULL); if (ret < 0) { av_strerror(ret, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Failed to open codec!: %s", errstr); return ret; } return 0; } /** * rtsp_new_context * * Create a new RTSP context structure. * * Parameters * * None * * Returns: Pointer to the newly-created structure, NULL if error. * */ struct rtsp_context *rtsp_new_context(void){ struct rtsp_context *ret; /* Note that mymalloc will exit on any problem. */ ret = mymalloc(sizeof(struct rtsp_context)); memset(ret, 0, sizeof(struct rtsp_context)); return ret; } /** * netcam_interrupt_rtsp * * This function is called during the FFmpeg blocking functions. * These include the opening of the format context as well as the * reading of the packets from the stream. Since this is called * during all blocking functions, the process uses the readingframe * flag to determine whether to timeout the process. * * Parameters * * ctx We pass in the rtsp context to use it to look for the * readingframe flag as well as the time that we started * the read attempt. * * Returns: * Failure -1(which triggers an interupt) * Success 0(zero which indicates to let process continue) * */ static int netcam_interrupt_rtsp(void *ctx){ struct rtsp_context *rtsp = (struct rtsp_context *)ctx; if (rtsp->status == RTSP_CONNECTED) { return 0; } else if (rtsp->status == RTSP_READINGIMAGE) { struct timeval interrupttime; if (gettimeofday(&interrupttime, NULL) < 0) { MOTION_LOG(WRN, TYPE_NETCAM, SHOW_ERRNO, "%s: get interrupt time failed"); } if ((interrupttime.tv_sec - rtsp->startreadtime.tv_sec ) > 10){ MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: Camera timed out for %s",rtsp->path); return 1; } else{ return 0; } } else { /* This is for NOTCONNECTED and RECONNECTING status. We give these * options more time because all the ffmpeg calls that are inside the * rtsp_connect function will use the same start time. Otherwise we * would need to reset the time before each call to a ffmpeg function. */ struct timeval interrupttime; if (gettimeofday(&interrupttime, NULL) < 0) { MOTION_LOG(WRN, TYPE_NETCAM, SHOW_ERRNO, "%s: get interrupt time failed"); } if ((interrupttime.tv_sec - rtsp->startreadtime.tv_sec ) > 30){ MOTION_LOG(WRN, TYPE_NETCAM, NO_ERRNO, "%s: Camera timed out for %s",rtsp->path); return 1; } else{ return 0; } } //should not be possible to get here return 0; } /** * netcam_read_rtsp_image * * This function reads the packet from the camera. * It is called extensively so only absolutely essential * functions and allocations are performed. * * Parameters * * netcam The netcam context to read from * * Returns: * Failure -1 * Success 0(zero) * */ int netcam_read_rtsp_image(netcam_context_ptr netcam){ struct timeval curtime; netcam_buff_ptr buffer; AVPacket packet; int size_decoded; /* Point to our working buffer. */ buffer = netcam->receiving; buffer->used = 0; av_init_packet(&packet); packet.data = NULL; packet.size = 0; size_decoded = 0; if (gettimeofday(&curtime, NULL) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: gettimeofday"); } netcam->rtsp->startreadtime = curtime; netcam->rtsp->status = RTSP_READINGIMAGE; while (size_decoded == 0 && av_read_frame(netcam->rtsp->format_context, &packet) >= 0) { if(packet.stream_index != netcam->rtsp->video_stream_index) { my_packet_unref(packet); av_init_packet(&packet); packet.data = NULL; packet.size = 0; // not our packet, skip continue; } size_decoded = decode_packet(&packet, buffer, netcam->rtsp->frame, netcam->rtsp->codec_context); my_packet_unref(packet); av_init_packet(&packet); packet.data = NULL; packet.size = 0; } netcam->rtsp->status = RTSP_CONNECTED; // at this point, we are finished with the packet my_packet_unref(packet); if (size_decoded == 0) { // something went wrong, end of stream? Interupted? netcam_rtsp_close_context(netcam); return -1; } if ((netcam->width != (unsigned)netcam->rtsp->codec_context->width) || (netcam->height != (unsigned)netcam->rtsp->codec_context->height) || (netcam_check_pixfmt(netcam) != 0) ){ if (netcam_rtsp_resize(netcam) < 0) return -1; } netcam_image_read_complete(netcam); return 0; } /** * netcam_rtsp_resize_ntc * * This function notifies the user of the need to transcode * the netcam image which uses a lot of CPU resources * * Parameters * * netcam The netcam context to read from * * Returns: * Failure -1 * Success 0(zero) * */ static int netcam_rtsp_resize_ntc(netcam_context_ptr netcam){ if ((netcam->width != (unsigned)netcam->rtsp->codec_context->width) || (netcam->height != (unsigned)netcam->rtsp->codec_context->height) || (netcam_check_pixfmt(netcam) != 0) ){ MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: "); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: ****************************************************************"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: The network camera is sending pictures in a different"); if ((netcam->width != (unsigned)netcam->rtsp->codec_context->width) || (netcam->height != (unsigned)netcam->rtsp->codec_context->height)) { if (netcam_check_pixfmt(netcam) != 0) { MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: size than specified in the config and also a "); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: different picture format. The picture is being"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: transcoded to YUV420P and into the size requested"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: in the config file. If possible change netcam to"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: be in YUV420P format and the size requested in the"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: config to possibly lower CPU usage."); } else { MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: size than specified in the configuration file."); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: The picture is being transcoded into the size "); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: requested in the configuration. If possible change"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: netcam or configuration to indicate the same size"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: to possibly lower CPU usage."); } MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Netcam: %d x %d => Config: %d x %d" ,netcam->rtsp->codec_context->width,netcam->rtsp->codec_context->height ,netcam->width,netcam->height); } else { MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: format than YUV420P. The image sent is being "); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: trancoded to YUV420P. If possible change netcam "); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: picture format to YUV420P to possibly lower CPU usage."); } MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: ****************************************************************"); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: "); } return 0; } /** * netcam_rtsp_open_context * * This function opens the format context for the camera. * * Parameters * * netcam The netcam context to read from * * Returns: * Failure -1 * Success 0(zero) * */ static int netcam_rtsp_open_context(netcam_context_ptr netcam){ int retcd; char errstr[128]; if (netcam->rtsp->path == NULL) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Null path passed to connect (%s)", netcam->rtsp->path); } return -1; } // open the network connection AVDictionary *opts = 0; netcam->rtsp->format_context = avformat_alloc_context(); netcam->rtsp->format_context->interrupt_callback.callback = netcam_interrupt_rtsp; netcam->rtsp->format_context->interrupt_callback.opaque = netcam->rtsp; if (gettimeofday(&netcam->rtsp->startreadtime, NULL) < 0) { MOTION_LOG(ERR, TYPE_NETCAM, SHOW_ERRNO, "%s: gettimeofday"); } if (strncmp(netcam->rtsp->path, "http", 4) == 0 ){ netcam->rtsp->format_context->iformat = av_find_input_format("mjpeg"); } else { if (netcam->cnt->conf.rtsp_uses_tcp) { av_dict_set(&opts, "rtsp_transport", "tcp", 0); if (netcam->rtsp->status == RTSP_NOTCONNECTED) MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Using tcp transport"); } else { av_dict_set(&opts, "rtsp_transport", "udp", 0); av_dict_set(&opts, "max_delay", "500000", 0); //100000 is the default if (netcam->rtsp->status == RTSP_NOTCONNECTED) MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Using udp transport"); } } retcd = avformat_open_input(&netcam->rtsp->format_context, netcam->rtsp->path, NULL, &opts); if (retcd < 0) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: unable to open input(%s): %s", netcam->rtsp->path,errstr); } av_dict_free(&opts); //The format context gets freed upon any error from open_input. return retcd; } av_dict_free(&opts); // fill out stream information retcd = avformat_find_stream_info(netcam->rtsp->format_context, NULL); if (retcd < 0) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: unable to find stream info: %s", errstr); } netcam_rtsp_close_context(netcam); return -1; } #ifdef HAVE_PTHREAD_SETNAME_NP /* there is no way to set the avcodec thread names, but they inherit * our thread name - so temporarily change our thread name to the * desired name */ { char curtname[16]; char newtname[16]; pthread_getname_np(pthread_self(), curtname, sizeof(curtname)); snprintf(newtname, sizeof(newtname), "av%d%s%s", netcam->cnt->threadnr, netcam->cnt->conf.camera_name ? ":" : "", netcam->cnt->conf.camera_name ? netcam->cnt->conf.camera_name : ""); pthread_setname_np(pthread_self(), newtname); #endif retcd = netcam_open_codec(&netcam->rtsp->video_stream_index, netcam->rtsp->format_context, AVMEDIA_TYPE_VIDEO); #ifdef HAVE_PTHREAD_SETNAME_NP pthread_setname_np(pthread_self(), curtname); } #endif if (retcd < 0) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: unable to open codec context: %s", errstr); } netcam_rtsp_close_context(netcam); return -1; } netcam->rtsp->codec_context = netcam->rtsp->format_context->streams[netcam->rtsp->video_stream_index]->codec; if (netcam->rtsp->codec_context->width <= 0 || netcam->rtsp->codec_context->height <= 0) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Camera image size is invalid"); netcam_rtsp_close_context(netcam); return -1; } netcam->rtsp->frame = my_frame_alloc(); if (netcam->rtsp->frame == NULL) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: unable to allocate frame. Fatal error. Check FFmpeg/Libav configuration"); } netcam_rtsp_close_context(netcam); return -1; } if (netcam_rtsp_open_sws(netcam) < 0) return -1; /* * Validate that the previous steps opened the camera */ retcd = netcam_read_rtsp_image(netcam); if (retcd < 0) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Failed to read first image"); } netcam_rtsp_close_context(netcam); return -1; } return 0; } /** * netcam_rtsp_open_sws * * This function opens the rescaling context components. * * Parameters * * netcam The netcam context to read from * * Returns: * Failure -1 * Success 0(zero) * */ static int netcam_rtsp_open_sws(netcam_context_ptr netcam){ netcam->width = ((netcam->cnt->conf.width / 8) * 8); netcam->height = ((netcam->cnt->conf.height / 8) * 8); netcam->rtsp->swsframe_in = my_frame_alloc(); if (netcam->rtsp->swsframe_in == NULL) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: unable to allocate frame. Fatal error. Check FFmpeg/Libav configuration"); } netcam_rtsp_close_context(netcam); return -1; } netcam->rtsp->swsframe_out = my_frame_alloc(); if (netcam->rtsp->swsframe_out == NULL) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: unable to allocate frame. Fatal error. Check FFmpeg/Libav configuration"); } netcam_rtsp_close_context(netcam); return -1; } /* * The scaling context is used to change dimensions to config file and * also if the format sent by the camera is not YUV420. */ netcam->rtsp->swsctx = sws_getContext( netcam->rtsp->codec_context->width ,netcam->rtsp->codec_context->height ,netcam->rtsp->codec_context->pix_fmt ,netcam->width ,netcam->height ,MY_PIX_FMT_YUV420P ,SWS_BICUBIC,NULL,NULL,NULL); if (netcam->rtsp->swsctx == NULL) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: unable to allocate scaling context. Fatal error. Check FFmpeg/Libav configuration"); } netcam_rtsp_close_context(netcam); return -1; } netcam->rtsp->swsframe_size = my_image_get_buffer_size( MY_PIX_FMT_YUV420P ,netcam->width ,netcam->height); if (netcam->rtsp->swsframe_size <= 0) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error determining size of frame out"); } netcam_rtsp_close_context(netcam); return -1; } /* the image buffers must be big enough to hold the final frame after resizing */ netcam_check_buffsize(netcam->receiving, netcam->rtsp->swsframe_size); netcam_check_buffsize(netcam->latest, netcam->rtsp->swsframe_size); return 0; } /** * netcam_rtsp_resize * * This function reencodes the image to yuv420p with the desired size * * Parameters * * netcam The netcam context to read from * image The destination image. * * Returns: * Failure -1 * Success 0(zero) * */ static int netcam_rtsp_resize(netcam_context_ptr netcam){ int retcd; char errstr[128]; uint8_t *buffer_out; retcd=my_image_fill_arrays( netcam->rtsp->swsframe_in ,(uint8_t*)netcam->receiving->ptr ,netcam->rtsp->codec_context->pix_fmt ,netcam->rtsp->codec_context->width ,netcam->rtsp->codec_context->height); if (retcd < 0) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error allocating picture in: %s", errstr); } netcam_rtsp_close_context(netcam); return -1; } buffer_out=(uint8_t *)av_malloc(netcam->rtsp->swsframe_size*sizeof(uint8_t)); retcd=my_image_fill_arrays( netcam->rtsp->swsframe_out ,buffer_out ,MY_PIX_FMT_YUV420P ,netcam->width ,netcam->height); if (retcd < 0) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error allocating picture out: %s", errstr); } netcam_rtsp_close_context(netcam); return -1; } retcd = sws_scale( netcam->rtsp->swsctx ,(const uint8_t* const *)netcam->rtsp->swsframe_in->data ,netcam->rtsp->swsframe_in->linesize ,0 ,netcam->rtsp->codec_context->height ,netcam->rtsp->swsframe_out->data ,netcam->rtsp->swsframe_out->linesize); if (retcd < 0) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error resizing/reformatting: %s", errstr); } netcam_rtsp_close_context(netcam); return -1; } retcd=my_image_copy_to_buffer( netcam->rtsp->swsframe_out ,(uint8_t *)netcam->receiving->ptr ,MY_PIX_FMT_YUV420P ,netcam->width ,netcam->height ,netcam->rtsp->swsframe_size); if (retcd < 0) { if (netcam->rtsp->status == RTSP_NOTCONNECTED){ av_strerror(retcd, errstr, sizeof(errstr)); MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: Error putting frame into output buffer: %s", errstr); } netcam_rtsp_close_context(netcam); return -1; } netcam->receiving->used = netcam->rtsp->swsframe_size; av_free(buffer_out); return 0; } /********************************************************* * This ends the section of functions that rely upon FFmpeg ***********************************************************/ #endif /* End HAVE_FFMPEG */ /** * netcam_connect_rtsp * * This function initiates the connection to the rtsp camera. * * Parameters * * netcam The netcam context to open. * * Returns: * Failure -1 * Success 0(zero) * */ int netcam_connect_rtsp(netcam_context_ptr netcam){ #ifdef HAVE_FFMPEG if (netcam_rtsp_open_context(netcam) < 0) return -1; if (netcam_rtsp_resize_ntc(netcam) < 0 ) return -1; if (netcam_read_rtsp_image(netcam) < 0) return -1; netcam->rtsp->status = RTSP_CONNECTED; MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO, "%s: Camera connected"); return 0; #else /* No FFmpeg/Libav */ netcam->rtsp->status = RTSP_NOTCONNECTED; netcam->rtsp->format_context = NULL; MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: FFmpeg/Libav not found on computer. No RTSP support"); return -1; #endif /* End #ifdef HAVE_FFMPEG */ } /** * netcam_shutdown_rtsp * * This function closes and frees all the items for rtsp * * Parameters * * netcam The netcam context to free. * * Returns: * Failure nothing * Success nothing * */ void netcam_shutdown_rtsp(netcam_context_ptr netcam){ #ifdef HAVE_FFMPEG if (netcam->rtsp->status == RTSP_CONNECTED || netcam->rtsp->status == RTSP_READINGIMAGE) { netcam_rtsp_close_context(netcam); MOTION_LOG(NTC, TYPE_NETCAM, NO_ERRNO,"%s: netcam shut down"); } free(netcam->rtsp->path); free(netcam->rtsp->user); free(netcam->rtsp->pass); free(netcam->rtsp); netcam->rtsp = NULL; #else /* No FFmpeg/Libav */ /* Stop compiler warnings */ netcam->rtsp->status = RTSP_NOTCONNECTED; MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: FFmpeg/Libav not found on computer. No RTSP support"); #endif /* End #ifdef HAVE_FFMPEG */ } /** * netcam_setup_rtsp * * This function sets up all the necessary items for the * rtsp camera. * * Parameters * * netcam The netcam context to free. * url The URL of the camera * * Returns: * Failure -1 * Success 0(zero) * */ int netcam_setup_rtsp(netcam_context_ptr netcam, struct url_t *url){ #ifdef HAVE_FFMPEG struct context *cnt = netcam->cnt; const char *ptr; int ret = -1; netcam->caps.streaming = NCS_RTSP; netcam->rtsp = rtsp_new_context(); netcam_rtsp_null_context(netcam); if (netcam->rtsp == NULL) { MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: unable to create rtsp context"); netcam_shutdown_rtsp(netcam); return -1; } /* * Allocate space for a working string to contain the path. * The extra 5 is for "://", ":" and string terminator. */ // force port to a sane value if (netcam->connect_port > 65536) { netcam->connect_port = 65536; } else if (netcam->connect_port < 0) { netcam->connect_port = 0; } 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->rtsp->user = mystrdup(ptr); } else { netcam->rtsp->user = mymalloc((cptr - ptr)+2); //+2 for string terminator memcpy(netcam->rtsp->user, ptr,(cptr - ptr)); netcam->rtsp->pass = mystrdup(cptr + 1); } } /* * Need a method to query the path and * determine the authentication type */ if ((netcam->rtsp->user != NULL) && (netcam->rtsp->pass != NULL)) { ptr = mymalloc(strlen(url->service) + strlen(netcam->connect_host) + 5 + strlen(url->path) + 5 + strlen(netcam->rtsp->user) + strlen(netcam->rtsp->pass) + 4 ); sprintf((char *)ptr, "%s://%s:%s@%s:%d%s", url->service,netcam->rtsp->user,netcam->rtsp->pass, netcam->connect_host, netcam->connect_port, url->path); } else { ptr = mymalloc(strlen(url->service) + strlen(netcam->connect_host) + 5 + strlen(url->path) + 5); sprintf((char *)ptr, "%s://%s:%d%s", url->service, netcam->connect_host, netcam->connect_port, url->path); } netcam->rtsp->path = (char *)ptr; netcam_url_free(url); /* * Now we need to set some flags */ netcam->rtsp->status = RTSP_NOTCONNECTED; /* * Warn and fix dimensions as needed. */ if (netcam->cnt->conf.width % 8) { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: Image width (%d) requested is not modulo 8.", netcam->cnt->conf.width); netcam->cnt->conf.width = netcam->cnt->conf.width - (netcam->cnt->conf.width % 8) + 8; MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: Adjusting width to next higher multiple of 8 (%d).", netcam->cnt->conf.width); } if (netcam->cnt->conf.height % 8) { MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: Image height (%d) requested is not modulo 8.", netcam->cnt->conf.height); netcam->cnt->conf.height = netcam->cnt->conf.height - (netcam->cnt->conf.height % 8) + 8; MOTION_LOG(CRT, TYPE_NETCAM, NO_ERRNO, "%s: Adjusting height to next higher multiple of 8 (%d).", netcam->cnt->conf.height); } /* * The RTSP context should be all ready to attempt a connection with * the server, so we try .... */ ret = netcam_connect_rtsp(netcam); if (ret < 0){ return ret; } netcam->get_image = netcam_read_rtsp_image; return 0; #else /* No FFmpeg/Libav */ /* Stop compiler warnings */ if (url->port == url->port) netcam->rtsp->status = RTSP_NOTCONNECTED; MOTION_LOG(ERR, TYPE_NETCAM, NO_ERRNO, "%s: FFmpeg/Libav not found on computer. No RTSP support"); return -1; #endif /* End #ifdef HAVE_FFMPEG */ } /** * netcam_next_rtsp * * This function moves the picture to the image buffer. * If the picture is not in the correct format for size * it will put it into the requested format * * Parameters * * netcam The netcam context to free. * url The URL of the camera * * Returns: * Failure -1 * Success 0(zero) * */ int netcam_next_rtsp(unsigned char *image , netcam_context_ptr netcam){ /* This function is running from the motion_loop thread - generally the * rest of the functions in this file are running from the * netcam_handler_loop thread - this means you generally cannot access * or call anything else without taking care of thread safety. * The netcam mutex *only* protects netcam->latest, it cannot be * used to safely call other netcam functions. */ pthread_mutex_lock(&netcam->mutex); memcpy(image, netcam->latest->ptr, netcam->latest->used); pthread_mutex_unlock(&netcam->mutex); if (netcam->cnt->rotate_data.degrees > 0) /* Rotate as specified */ rotate_map(netcam->cnt, image); return 0; } motion-release-4.0/netcam_rtsp.h000066400000000000000000000025141300074623700167740ustar00rootroot00000000000000#ifndef _INCLUDE_NETCAM_RTSP_H #define _INCLUDE_NETCAM_RTSP_H #include "netcam.h" #ifdef HAVE_FFMPEG #include #include #include #include #include #include struct rtsp_context { AVFormatContext* format_context; AVCodecContext* codec_context; AVFrame* frame; AVFrame* swsframe_in; AVFrame* swsframe_out; int swsframe_size; int video_stream_index; char* path; char* user; char* pass; enum RTSP_STATUS status; struct timeval startreadtime; struct SwsContext* swsctx; }; #else /* Do not have FFmpeg */ struct rtsp_context { int* format_context; enum RTSP_STATUS status; }; #endif /* end HAVE_FFMPEG */ struct rtsp_context *rtsp_new_context(void); void netcam_shutdown_rtsp(netcam_context_ptr netcam); int netcam_connect_rtsp(netcam_context_ptr netcam); int netcam_read_rtsp_image(netcam_context_ptr netcam); int netcam_setup_rtsp(netcam_context_ptr netcam, struct url_t *url); int netcam_next_rtsp(unsigned char *image , netcam_context_ptr netcam); #endif /* _INCLUDE_NETCAM_RTSP_H */ motion-release-4.0/netcam_wget.c000066400000000000000000000237071300074623700167540ustar00rootroot00000000000000/* 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 #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 = 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; } /** * header_process * * 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(). */ /** * header_extract_number * * 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; } /** * header_strdup * * Strdup HEADER, and place the pointer to CLOSURE. */ int header_strdup(const char *header, void *closure) { *(char **)closure = mystrdup(header); return 1; } /** * skip_lws * 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; } /** * base64_encode * * 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'; } /** * strdupdelim */ char *strdupdelim(const char *beg, const char *end) { char *res = mymalloc(end - beg + 1); memcpy (res, beg, end - beg); res[end - beg] = '\0'; return res; } /** * http_process_type */ 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; } /** * rbuf_initialize * * 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)); } /** * rbuf_peek * * 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; rbuf_initialize(netcam); res = netcam_recv (netcam, netcam->response->buffer, sizeof (netcam->response->buffer)); if (res <= 0) { *store = '\0'; return res; } netcam->response->buffer_left = res; } *store = *netcam->response->buffer_pos; return 1; } /** * rbuf_flush * * 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; } } /** * http_result_code * * 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); } motion-release-4.0/netcam_wget.h000066400000000000000000000070071300074623700167540ustar00rootroot00000000000000/* 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); /* 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 */ motion-release-4.0/normal.jpg000066400000000000000000000337161300074623700163060ustar00rootroot00000000000000JFIFC    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO@" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?HĜqdrw+.x`. c@ؚGI,*3ΐ! x[#ī"8$XdYXVhd"bPs9(, \6=xhʆ(f?(+/LX>jflO,{hQ(@X〣({AV.\,{6Ui+$ll'm?SQ"altHNiY="e y#?:``kF@d+TbBŹ9OTlc$QÓӆWǿK;>&Տ zbIَtbcӖuPH,q?\ܲIɬ`FIaL>X9I7u B>EaYDhF>SȢ2[s18cXٶ6gS8 wܤ~pyǵde`6ԋ;&ᷣzp, 0y:SW Ojh,pFGLc,}$#H'=@FO u4 ămj$cCRNS="DQ& ס0hJ힤L snc0j$a6sI~JE*3iǿ=jca@+TQhr7ip@ZDo˖(sjݸ,K-с- 3֧I0WMFI01MFM98/vb -,66i5xȌ9 `OcM"[c)8ncVy,BZh aXl} !\ 5UR %^}H,dQUܼ)+ "`̫pG4H-`3 XցiB:y~\ nd@)8L&xm\*+[( 0>P=~pζMl#X3>Ua /[haEȒ"?X??[$Em'*F8֩^ȋb֊Ş<=/sV4 ]\,亶iQI*?ơ2:t?Ι Gۆd?xp1اhV3ҳHls ;Y"*'*s~KnA?ۂz˸~hjWh$zqq+;I>攪\Hu&k8]J C!a!hsْ0R. }C`V]>V 7a>cd,Ǩt$p{v7Sqjej̅\8]'-( DH6( ¡VBU.GN^5,=q@#֘P$)0 Q,w$ae\5lty^vlbcpтHkR90[}kV_%7.]Gzb Fkʃ"fNLOF#"$;RiYv^ԑb5'X]%$8":1ܨ1b s=8㊚ ږ`}12d,Y,pA@#Q( ,tϭA7f*VV1xe762`0l6A+ӚuY8ܧbW E&,,< 'RyN bG(^I4#$r*C"px@G': P\\T58b:GjzUw:SFgoN쓄j63s׭J(%5e緭=g'֚H-.LrsK*Fq"ʁQm ȁ\p<aI wս0mIg]XsHʴ`.]h#z6y$&~LNzԖ(IERI E{hBZΧRQVF vKƝs}&6+(E2lFIv{b%ȶ6wxA*6>S4Zhn`'a@ W5wmqWzT23p^nZA9# Fޝi =j;OH66d Ԗ'W ETHId[MxmHy|N*Qȓy, [oq]&3$0/r) ^E"Iyi#'$emEg} !%,[V".ֺ+q&[$o3ih6W%#;S`i%;*±8B~>}@t-."=I=֚MD3\Krj1`C9=:wZryacYuu$]T=G5`aQ_r1^ɢйXxs: BW-x;kԲ

[3{ Z%YY"RFr+3&t_D]"0wrm;b%Ռ{n@ϿZt5HEjh~Qǽ?j,zzn7/DJ8N9$Sq6y9" JX Aw?ꊡsSC6܎ci@)X ԍp\$g!$sN $fnj#RFQ;āq7>,߃OXIqhd+@ʛ7 g%D`OZvSɼMkr GhhK`!~6SOX& g"Ylt 22E!,G @{TD2F>1ޜV<)K+ "lw!8k><(:2ƪn4E>諎rPONҨ8r#P2szPM&qǓUwpq*6A =3L1A;m(8*sy 秽"ǘ>b3@稦d:T}*B$f@]ۈSȫښq4`sQȬ9~n琂8;OVi]5꺳9}$ոY贆NWL>-sn$jxkdW=Y7B)=hq9v8Z[,,Ee qM,3p5lk1Iߊax֜KJGea]UAQOoQHT$Azw  9}i)"ϚF眚md`UC~j9Y-2.c~nsbW)ޫ!Jq/ENMY20MU%@&7QJs4M2zDuXg H?#UB_*J?hSn1 d}:d4ic'ߕ 2ug|3/eŮrŹcm\ +ӹf\O*I' [Y1X;b~`NxC28kWH)ē9B6ɩVBFO1T-f&ʐ ? gڱ`33nz6#rxު|.8NTVd!1|t9Ʈ 2EWhb #vN Sr4r2' 1V`e2v۷i#bNy+!lS5pHV߂@_ʪFH=?JՎJ9UKAyzҔ9TviE63 "hE\@z`%i(m,Grt>̬p j{@$zd-qe\ʮ70/WsWyU)-?Z(L~Ti.d.zyaW2bg\5m i0 z ѳo(r#{{`Cj$%,Whp`fRMRo3Ja/)$}*y "vw{V76\3F:~ Ԗz+oCsJ#6ofpB  R9#w@GJr;UK@\fF#88K!fxUGemwH=jM (6)sckG!${sхEI%mstTXe׭VISRJfmr+ O),H޲5LFҌ2nF+M}l\qM-I%bu=Sui|a\˂d%fZ=M~nAimZP = 2@0 }1,'AWjC XGqQF;P Ҙ0@#ךkvPw)8K(%I 4$f(#=am2-cn2CVն5^Jj{OH@֘dUp1aQ*B@Q]}N3J9`N|hQcRb/gmk:6~sXG+zfẖ9;e(7QMt8"Ըs23ޑ&}3K,P0G" ːH{3!dr$Y(~@t5$X"NV>lM,71TDE>;cV]E{^jA;a@H9JHR٘`g_i)t>JD6!=rGRBp1Њfp;w}Ɇ1p[AGZnt/?t`vZ%EE ɷ\ӰypI/$xmdtpz"c5\w3 t+G !W'ѶJqT=d*)t p8z}i6=5k f$+2o"#@ u'cV;4 2j#.s٩j"qhp@.=#DimǨ֛w@2;^EQO\s,$YT'ƛTeɤU Pٻ2siz]q۰O;vAqTraZ()@X}IXr'96^zTgk?@QGEYs;IiJPERc8MRؤP[v ˗1PH-c51UtI*4E-S](E޴b;KEWY >7njVm&lvucݩyƧkEpGɜwYZf<~ DuУ.ri:<Ȑ{ Qel`,έDw!5~\+8ێ!cM0*b{HBI ͍Ñ~Gݻ$2!i g9F^7鎵.{" :l. ưm.-O% qRZCCMI0-d.'9$ p9P8yFR3.͈~W D! '#He:wH JpX!?*)$'FzY@#w;#'zbOozF;R/2A,)gfŒgL>F* b6N~;8p02~)Fzr;D?^})5$f{Q#Lc%Aj:!۱Tl#|Cj4|0=sRM RlV%@F\qjF$:1=j& c\%W#9皑[# M1d5" ~R02)tHp2~,:N`ʽ=yp 1ҝ!o=*6ڤC߽IxA91҂р@^ѲFB60[օx: b-8c1f~p76ּuF階By,U)TˀO qNZs\59 8Qbk Vj N*ejcwN~-$3ڙ 'L~գaISSfC$g@ŷ0N]5Sy6,8Lna mQz ?γ' =R].l#u̒I=u 퍽Hi7t/+]X |I3YQZ'.!#pAFR3ƚyfu 8>ճF۞eT+ 4ڪ8)3<{t&Q,8P|QR+H" 7n8>E6N;/Sf*ě9Rj慺9.8((з$pTubaL/uIɀyjJ2r0)!jA" n«!iAe2 laRe Ո]1x<֑Dv–t^jHcY$X%7F)'*BOBji4.i$+0T 1U#+UK 6b_jׅP6#-ՇW2$])-ZjWq@~gy252_RldycP ge11,F <&"q@l N+*x2Ojw8ži67$SWp`Ž~x- |:yo•lvd-݁|Jckc W rNHҟ#8glFp;` ،Pr8s3T"2Kg'8|8Xwq9'yOP)fЏD'8v$r9W'88ҝ @O֚rs?+JԆR;0je;Fuhk"&q%G>8 ZfG=kKXaخU чP@E qMnWp eGӚH!` gpQUQZb- 0@۞=~PK?@*K[ya[ *>}i HG ø)zkjt-2f%t[ }O^y$!1@IjU,۸x+/N+$猄$i ;r{VebhnsǥKoq^Hmby-1Xņq5i&N%A&$P)6[[Pl- cOʊY"m0m銎DѫmC?lGɣ+ XGK`X0}~L4RI*G70ƭ]2 }rN~ᦂ$12ǍIe&!@fIRWZ =LDrVӼ%;?+wA$Ep?~ :+qo?>ޟcA*'89 n}iͻ TFqs)fțHsTyޢ70:<)g*yXJ|"F@ } NF4aDɒ\p2)6'd-\S{ss XX*RS"B[" Ab'dfa!PU W I'y \I)x78ozPLu \ 8LY Aߌ<&- SJSFLc@@@׭|CԄGo)/Vk4h?fp˸ΪxӜO=0EPEP|G_e jVo:ʫ6!915VzvfҸ]-^x扶SLJudN-+:]Zþe)! #zyz(^.go(`qhu{_}7u+(((((((((((((((((c)/Z.{ip (.JV~+?eogg^G (<-c #4.CAϦ:Uo 8s~Y>b7}8u7QI9| S{@8/=OR{;4ɧf# 0lDAe1$q.靹]H;*1]׆Bm&],-0SS V߂8Ysga8oʛ|a }SֱѾ4Da3]$9+K< mtXv9B k.☧+hY '#{Wxx*VB%pG[Q x{icyȻLs}ާm <_e٫LǺ77<NNyx7N櫩Y\yHPw܂= 9"^)Q #hQEQEQEQEQEQEQEQEQEQEQE~(+x_+? Kډ $#wTA,h~p^6\+e+YS^y HMUuqu]:1X882(#A ^_Žsڳ%B_)+oص>7P,|9bx<'mm62ٮQ,lݑ׮?@}(YtfƙKhʂO@lݍTgJj=W(!#ϱ('šumw`"2pFPB3ak/.L242c%xz h(YYn%.Mg/cF춿-N8)>8h袊(((((((((R\U*sWNQOѰ (f(+XYeFַ+;;C=E`^c傃z#9+igi&d#\F rF{}tgntIk-ʪLr( _r<bNGJrבkEnlZg-,` v5~"-cg4P7XĒ>c{:T(玵_HԮ5 r̋rqHI5\bu6JɲDaݓN+v?6#2JqOۙo'TN@Lw(?m|tӬf[d ޹,0*Np:O!@\}y֮ Wv-3Dclcr:p8 (>"MnR9RT/ W\c5,()IJ((((((((((+ 4Ge{֩|ݐQ_QEQEO^2Sk.iZ EgejfIXޡOqdr2<ҽOqe4TDn *T3>[Ⲵeu+)%0.'8s֝6Q7J-mD\;lH0o^wKTNBYHlx'5x6 b4L!r)99<(8qph>3Wچgouap,3n:ƶ NKh]ۨ;uSz郞㺍w8Q53{d]YLjL#91rsbI ySy,4`p1=}|űrIx@}31(%8$d{+k怭-}1|D:`$}r10袊((((((((((()gciߧQ^Q_TFZ&ߓ{-s# |w1T±+]m<=,$v?0=TLbq={vrZmluKYRgfmQV9n~%ʿO2\ˡiovI;Cm ǒ0XΟqku5\ #v@%NAZEzGğxgĺn60;E8IF'wn >oEKڟa{[xm,JJ`x 08랛_lG4)ge3r +BGQ@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q@Q^OJU1cw̟;QEQ_U*IXQ]fg[/)_`QEuR 9xݵp>os-&[D£(T 4x^XdhQшe!$ ؊hAضlH$8V:I4;Ƿ_[?Z4sʊNjq:'9aWe*/M9M Ǚa|0?<Ԑr5u< kpqrY-9>Wx@ޮ z:@8x#'藼xdI@]AGQ\'3.o͘O.8랙 rh袬Kcw\Mk[d^Npq3EYMJ"xg0FHv w5Z((((((((((+8cJ+_` (8&~Zy_nEWQEQE~؉ZeK'^:9RXD`pTƻx%sC"&F FGy88Lb2FTwri[S'X2@l-"'c,cM,onxaS5~i5ؼga<k. &A0yU?f[yغ%))ݏC><̅:Fqsl%\jz !GSY3jʊ][msF9y&FUڋ;Ā $02>}Y%xoSS50|D2=ZRlaQH̶ȥ>jcUտtffG(|2=@桖6Z]OBD*{.b.ʰ@vzdӢ7GEvl`R\KTrj B$ НM#;q# {w{QȤ2&|3b}篥+rx錊p~  9c A<$P(D$/cr=ndycLqZ^IJ=H>WÑM!*=]:!іxf`J\ūet %c]O.fGs';AzFGxt_8pQuXyN= W3 k C>CFs_]{6(fG?ýDm C{i2k0E%S랴7MrMW.b_ >b NL>0 w |}yKdyP%,PG@nOʬj—fxBHkkbO[csPEp?kTgo?vUR`t8Z]jݣ7g$Gԯ9-4;[$8ypqT43ڄnew ~_ֺ"+}6R֗DH?C\A5J n)٫  xS'"}M$@ ^.%HgYDGMgJvjI.ўޕ5bʆ&Ou<4e+IS9QJ7>a>̔+ *vS9U$/r@Qn'-> )!OޠSNx犖GaTx 6q^EJew7JP}À*9ddsU14xjX§/߁@cFT$8(=f.fg9DtpU@ǯ4)lՆ)!ïA*yskuLwKJ8NQ.[P7g1t+?} ify'fpʹH#< |cŏu$]zT#_yjaU c5xy<#+-b5]8ЦX+-KF} JH]GX~m-3a*%}GV6sHl[qO}Zc|5aylikٚt='M$ >LH(#[NO;siBKG8VG#ڟk='XIi'>n}5v:fl/heņ0]ڂl*H[|y.#@J">iq R sƞizeec)xrx mݣ*HUob2I G?Z콽9#0\]Z$b7t*N5o{[heiF_,hGr.Odwҝ6g Ǩx n| C$a5G_x /z%~gKo[ƙ.˰뵷mѾ: v&rZ7ۤ^8Jl2T:tb7/;5ϭ?$rjѧ(I1RZޗg K!Dkf pnYh .]Td%k*u܅t2($βn+1@XYif bO҉@:pZVeD ebO E7WD-XV6g|zI U !ԯmģ3yAK$XЃ8 0R di$Nl+yq!^9|+6+;Rh>{I'DH'9?4.#y|5ÞW-J"g-6ws!g5"QX)CS "!XnFSv-Ź 5 ;A=R۲(:V!N[=i& 9QpW RJl&A֍c EU\֞ܩ1$p$(^[PF*L -;ѿ =2@\lI唩j=4\j^$wA(=8$>]jq,(ΔL1cok% ,G\ZNqQc${Z2E$Z.XCr {5o$ku{[u2bx|_:g7/`{~-VXV.`q>Lz纅Ԛ!gnуw֖͜͞-9cTPwcC:=R< `??қD ԗZm4[w2lI;}_X5v8F1UGh5KAgw4CXL+\x uoߵ "1X{onmSF/nLj#ӭw7SYΫưFvfY|8ʯąr s\قݶas#@Uc}RrwgL}du)yc<L2)B"a8 3w#kWscwE9뾧9QW!ČqZ%@OX'g[͒'f8 VKO<6O?he% lH|gHSRX68(3򱯩>Q* X G:TF%z.0* TknX#sI53y0 0g*U܀IazUYMp Ҷ.u6;VĒ8b2IEZ8i=jm6qTgTs5bXSrFs[l`p3Uq)f[:Zs)8jm1'EW38*UyzdBB= X1Q68 '#"9j9ݏJ´SP 7|%};[$mҴ#"M1т~?۫L[W<$>e}U( )ޢ 0YtJ(V ðL1ڪNzVS1I$4@ 2z#2oRlqRF^0W'9B9 huSj'\. )+VXs~mj|ʑ1c?ҹ-BW /$ u]tAZ%W=q`02x<d`5#:ZT^Őݟ p]|M r+(ߑG֮I[S$v|k0 LjYex'j$ NOJqS̤2qjq)Ug_1Te|긠۲wm<`ըjPDJ<)[NP9Z&lFW=۶+)<+DZ!2 HϷlSB/Oh5AA-H\B0AiL!_*$A 2kC.aeP<׊"LW)c.:A=5ـXdqޥ1(HRHPN@><Ҭ Ԝ{RQOqF+,*VhG@&gxUf@yRكc8 U7¶ \T*lqȦDd]TOUT '@qxؔOL#T1Ȫ23նLUn- _Je9>v9Uu2U_,)ܽTD9ߎXR7Oٴ`TbW 5bT[Qƛi䌜`sE!/-E]Tgxh (#Ni7)SHdJiBQܤqI^{ TŃ)@*F+eŮ$rrw+wy_fDw5(q[} xЍ:'pzT'ڣg,=)8>(ou8#J*ͤ< 鑚 HߑFr~ȼU,2N++6eSb"YO5 i2DYq´$l#Ɣy=2k҅DbbXJ>.T%gwb`2zMm帷ORx'5<%"Pv jQ0|95i!x3iK ̾y kEs>]x)./-HN:dI(M[QK;Z =Zo m (&#C|+9p4&W6!l7J&23ն|UI$cZFXQmE>%'9K#IN#4+ӊPxRsc$jTqb!<\ ʹBE^ _*򁴱bG rO'56E@q ӕ%]sۊwC--j9'w$DgtTfTњpTr*OLV*ԅdcH } li|T58j;qc0✱R8=C mjJ1R2:`)U\zw]Nc#B0\+ 7~"J6ʎ%gSyYURH트.D uYKE<`3T jrlF T^އ$HѭpϞaixg`HkZS1ֲh}t۠aSM$c@B@6!FW8ldHI{R#7B <ZH#PvJ$tt PMA$~`T-(GV *6#h_y`pG0>rqX&VcF TIHfV-2#SS.悑 })l&bwgoW{^2(YdfLQ c'ޣ\JG8@a c4"nX'JH\uUAT t}7n;Q\|nZ6>mW28R$ ա}MF3hoAbȄ1֝$qu${I>IvwظMDd! 3,aD{Ҟ Q#LWei޵pWD>:̻ ;W5ZY->GQq1HK#WG -A4(.w6K dsҼn,{RE$N ##p)m~O5+[2qڸ4}sDpu;T2@?$= ᕕ$$T&Hvs'P8&+n߻`hOE˖9Z72]Ѝ&y?H˵U+.7`H~`ٮbC4sʹSM}O|jHy$qצZĊE{$N轿 쌹Il'*V>5,?ZR$jFhgmcDHNJ,ꄨ|q4#XPDpAzbo.%agIK )=aj0/4$^MiDsR1M7s,Z'XBHu ֤f#i# 6XʄdC!HQc%9[=ȬPa#ָIr]sNlOV;cEDqYr<Jנ?6=2)"PK7 9cfBަS"&'yjFENIǠYW8Q%Ԍ(aGOZ [n·+6n5"Xh1BW5gwwN |wQ1extT9'* [L&xRx$X‚]5 1]WZAWRZmkfH̐ ƒ5k`qZK6㴃G\TVrzq^#w{^H%.7w`xc:W޽bّs[1 1=Mn|v FwSẗqoJvӯbKgA$ rׁ$ؽ laT隥sPwtiMUU~`ܐqRِ#N8SnIE87H 8Uw=l ǂzS&Ui4ZY)!Okéo ^ίkpgҵZPhh:HlL*'kvXqww]*޵ BdU=,쑕V\1{Qpq[2y R&ʻfRq=*' (!ץG#ZCH6mܚ%ʙ[f00B{9?T^>F\Z#-Ħ0T%I>TNhfx;b [A$qȣosʂ{ ɘ匀t4 h>r " ȫtXNɤ!JO؊ʯ 81*Ϡnir`9,3P}*mvK@ \`fG8'=($uLlҨȋ Os\-}Ml=F1qk_$z4+{8+J7guk&eea]W&|+ ͸O#F|2t&mʧlUoi.k+Yu-9$ P+U6}ddJU`W5\ܬeYb{aI88FUdc%U^;f!?7ҥUI?ZB;@1AzSUTu#^Zu #5I2Z1GM { mi!$*C0+ :rd>vU&Gx5FI~iŰaԚ.@fH݅jʊX8r|5U杇sY.AJG\⪙Uш^{ %a*|*@Z#Ƭ29A3.~B %g\M+c ]G哴t5xWKT#SL\浴%;<VGjj*TՔ_azG͜UEFwMM:{Wc;PI9$*dVcBKc9;ES {.T)>U JnOs q)A,a c; 8X2)#q܎wfT+p@SH@wqzU`lrI|M@`ҳfti<3rW< WY,ry{܁zf+ d𣿥'!_\SrryRCWk LtE]Ի+;-I|ԥryDPpu?{rzSy9u̧s>4Dyeg`늬e E0 [»E^"O ;$õ(ۃ2Ubʱ+?p>^2 =ZC!G8lTd8Szqe qՆl xUibYz|r[c5|)(۴9QLp) g}rNerv6Tx' S3l$݌sU>7M=I{m$'nr0SbbFUNI<$ SOBPW9^9=*M Cc(,xʌT/vLK3 ہR&ˊ_>Y_*2}7ɂI.wqڟG#5J]qIݩ6+0^GӼ[<}zc5V\FA8$z{ Dy$NdBXI#}>Y#(VAE9DU7@Ar# A%0y zLquSHWzq֡ʒrI(H។SMX^"C`g*%$YOzQ H~u.$YYlZexE Wr})sa틍*(ܹSy?{}]>chr7}\ڭź.Re+7yg :v)G.N&@lw1lI,?hpQZ5+|'ǂ1I2\8'ip@JZ%mh9/Wnfk9JmRW[h' HT#.%|ũ45.TFbO k3k+\Eoe#|TCNJ` W!nV]Ҭ8~bn{JTe\Q)$5_94S-nnf+!/RɑKt } s>wsƘQUWb^Ֆ4gnV`ҾN7̼|˕]ON)EngG k$uq´l>o|Q2-GomLi$c!Uݽ(w݆Rd)v>q$vW&vB>PpK9&ExD3ʿrR0+aӈ_YUlі c"n]& ޻oy}kasc iew,-fUX;I`NL2{kwK5ZZ[hPHUxnKA4V1eBKG16;ŕO$6ʒz#I<vy%T񳔂Aǘ^9]#nG,D0, <(\`nvl  Fma)R2f,E2L9g5H.&ěJ(%:[HVAm `qԊ!0fp73hANr8ˠ&?);!3= :l9,80cPU+1.%q^hl[q5s+3P!ۭJ3H@pzHcshIsIn=QlC=(!߹\d }'9ЛH.UVx*{UmՎs TpK}jI l(ICZ]ʹ-M!rѼ@856cEj ^ʢA x> ` p4wN>PS8=)g4I*9e~8PSG9*;Kn4ͼ8SKU% ?ڷb4X˶&~OJaCf aբ-OYm+i[h 1k1| dTQ+S|p {qq!ukmuf6"q:yq9Cb*4(m R݋l#tŰAVrIJD _*3ɨjv[xdN$I"*i2 D@jӴ7]\Mb%6 rۭH*oY$:̚Jhא[i5fsm)h|Mٓ%_yM!Xx^ie>pRH,t$i\3*i,w cii<Э% nb$"mdRLf)XI1l3Dˮ9,/sy.lΏw,#+yhm "*~))ǭ]֍Ώ,%-㶒Q>P\E.HbAn=Qmotion-release-4.0/picture.c000066400000000000000000001060021300074623700161200ustar00rootroot00000000000000/* 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 * Portions of this file are Copyright 2010 by Wim Lewis (wiml@hhhh.org) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. * */ #include "picture.h" #include "event.h" #include #undef HAVE_STDLIB_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; } /* EXIF image data is always in TIFF format, even if embedded in another * file type. This consists of a constant header (TIFF file header, * IFD header) followed by the tags in the IFD and then the data * from any tags which do not fit inline in the IFD. * * The tags we write in the main IFD are: * 0x010E Image description * 0x8769 Exif sub-IFD * 0x882A Time zone of time stamps * and in the Exif sub-IFD: * 0x9000 Exif version * 0x9003 File date and time * 0x9291 File date and time subsecond info * But we omit any empty IFDs. */ #define TIFF_TAG_IMAGE_DESCRIPTION 0x010E #define TIFF_TAG_DATETIME 0x0132 #define TIFF_TAG_EXIF_IFD 0x8769 #define TIFF_TAG_TZ_OFFSET 0x882A #define EXIF_TAG_EXIF_VERSION 0x9000 #define EXIF_TAG_ORIGINAL_DATETIME 0x9003 #define EXIF_TAG_SUBJECT_AREA 0x9214 #define EXIF_TAG_TIFF_DATETIME_SS 0x9290 #define EXIF_TAG_ORIGINAL_DATETIME_SS 0x9291 #define TIFF_TYPE_ASCII 2 /* ASCII text */ #define TIFF_TYPE_USHORT 3 /* Unsigned 16-bit int */ #define TIFF_TYPE_LONG 4 /* Unsigned 32-bit int */ #define TIFF_TYPE_UNDEF 7 /* Byte blob */ #define TIFF_TYPE_SSHORT 8 /* Signed 16-bit int */ static const char exif_marker_start[14] = { 'E', 'x', 'i', 'f', 0, 0, /* EXIF marker signature */ 'M', 'M', 0, 42, /* TIFF file header (big-endian) */ 0, 0, 0, 8, /* Offset to first toplevel IFD */ }; static const char exif_version_tag[12] = { 0x90, 0x00, /* EXIF version tag, 0x9000 */ 0x00, 0x07, /* Data type 7 = "unknown" (raw byte blob) */ 0x00, 0x00, 0x00, 0x04, /* Data length */ 0x30, 0x32, 0x32, 0x30 /* Inline data, EXIF version 2.2 */ }; static const char exif_subifd_tag[8] = { 0x87, 0x69, /* EXIF Sub-IFD tag */ 0x00, 0x04, /* Data type 4 = uint32 */ 0x00, 0x00, 0x00, 0x01, /* Number of values */ }; static const char exif_tzoffset_tag[12] = { 0x88, 0x2A, /* TIFF/EP time zone offset tag */ 0x00, 0x08, /* Data type 8 = sint16 */ 0x00, 0x00, 0x00, 0x01, /* Number of values */ 0, 0, 0, 0 /* Dummy data */ }; static void put_uint16(JOCTET *buf, unsigned value) { buf[0] = ( value & 0xFF00 ) >> 8; buf[1] = ( value & 0x00FF ); } static void put_sint16(JOCTET *buf, int value) { buf[0] = ( value & 0xFF00 ) >> 8; buf[1] = ( value & 0x00FF ); } static void put_uint32(JOCTET *buf, unsigned value) { buf[0] = ( value & 0xFF000000 ) >> 24; buf[1] = ( value & 0x00FF0000 ) >> 16; buf[2] = ( value & 0x0000FF00 ) >> 8; buf[3] = ( value & 0x000000FF ); } struct tiff_writing { JOCTET * const base; JOCTET *buf; unsigned data_offset; }; static void put_direntry(struct tiff_writing *into, const char *data, unsigned length) { if (length <= 4) { /* Entries that fit in the directory entry are stored there */ memset(into->buf, 0, 4); memcpy(into->buf, data, length); } else { /* Longer entries are stored out-of-line */ unsigned offset = into->data_offset; while ((offset & 0x03) != 0) { /* Alignment */ into->base[offset] = 0; offset ++; } put_uint32(into->buf, offset); memcpy(into->base + offset, data, length); into->data_offset = offset + length; } } static void put_stringentry(struct tiff_writing *into, unsigned tag, const char *str, int with_nul) { unsigned stringlength = strlen(str) + (with_nul?1:0); put_uint16(into->buf, tag); put_uint16(into->buf + 2, TIFF_TYPE_ASCII); put_uint32(into->buf + 4, stringlength); into->buf += 8; put_direntry(into, str, stringlength); into->buf += 4; } static void put_subjectarea(struct tiff_writing *into, const struct coord *box) { put_uint16(into->buf , EXIF_TAG_SUBJECT_AREA); put_uint16(into->buf + 2, TIFF_TYPE_USHORT); put_uint32(into->buf + 4, 4 /* Four USHORTs */); put_uint32(into->buf + 8, into->data_offset); into->buf += 12; JOCTET *ool = into->base + into->data_offset; put_uint16(ool , box->x); /* Center.x */ put_uint16(ool+2, box->y); /* Center.y */ put_uint16(ool+4, box->width); put_uint16(ool+6, box->height); into->data_offset += 8; } /* * put_jpeg_exif writes the EXIF APP1 chunk to the jpeg file. * It must be called after jpeg_start_compress() but before * any image data is written by jpeg_write_scanlines(). */ static void put_jpeg_exif(j_compress_ptr cinfo, const struct context *cnt, const struct tm *timestamp, const struct coord *box) { /* description, datetime, and subtime are the values that are actually * put into the EXIF data */ char *description, *datetime, *subtime; char datetime_buf[22]; if (timestamp) { /* Exif requires this exact format */ snprintf(datetime_buf, 21, "%04d:%02d:%02d %02d:%02d:%02d", timestamp->tm_year + 1900, timestamp->tm_mon + 1, timestamp->tm_mday, timestamp->tm_hour, timestamp->tm_min, timestamp->tm_sec); datetime = datetime_buf; } else { datetime = NULL; } // TODO: Extract subsecond timestamp from somewhere, but only // use as much of it as is indicated by conf->frame_limit subtime = NULL; if (cnt->conf.exif_text) { description = malloc(PATH_MAX); mystrftime(cnt, description, PATH_MAX-1, cnt->conf.exif_text, timestamp, NULL, 0); } else { description = NULL; } /* Calculate an upper bound on the size of the APP1 marker so * we can allocate a buffer for it. */ /* Count up the number of tags and max amount of OOL data */ int ifd0_tagcount = 0; int ifd1_tagcount = 0; unsigned datasize = 0; if (description) { ifd0_tagcount ++; datasize += 5 + strlen(description); /* Add 5 for NUL and alignment */ } if (datetime) { /* We write this to both the TIFF datetime tag (which most programs * treat as "last-modified-date") and the EXIF "time of creation of * original image" tag (which many programs ignore). This is * redundant but seems to be the thing to do. */ ifd0_tagcount++; ifd1_tagcount++; /* We also write the timezone-offset tag in IFD0 */ ifd0_tagcount++; /* It would be nice to use the same offset for both tags' values, * but I don't want to write the bookkeeping for that right now */ datasize += 2 * (5 + strlen(datetime)); } if (subtime) { ifd1_tagcount++; datasize += 5 + strlen(subtime); } if (box) { ifd1_tagcount++; datasize += 2 * 4; /* Four 16-bit ints */ } if (ifd1_tagcount > 0) { /* If we're writing the Exif sub-IFD, account for the * two tags that requires */ ifd0_tagcount ++; /* The tag in IFD0 that points to IFD1 */ ifd1_tagcount ++; /* The EXIF version tag */ } /* Each IFD takes 12 bytes per tag, plus six more (the tag count and the * pointer to the next IFD, always zero in our case) */ unsigned int ifds_size = ( ifd1_tagcount > 0 ? ( 12 * ifd1_tagcount + 6 ) : 0 ) + ( ifd0_tagcount > 0 ? ( 12 * ifd0_tagcount + 6 ) : 0 ); if (ifds_size == 0) { /* We're not actually going to write any information. */ return; } unsigned int buffer_size = 6 /* EXIF marker signature */ + 8 /* TIFF file header */ + ifds_size /* the tag directories */ + datasize; JOCTET *marker = malloc(buffer_size); memcpy(marker, exif_marker_start, 14); /* EXIF and TIFF headers */ struct tiff_writing writing = (struct tiff_writing) { .base = marker + 6, /* base address for intra-TIFF offsets */ .buf = marker + 14, /* current write position */ .data_offset = 8 + ifds_size, /* where to start storing data */ }; /* Write IFD 0 */ /* Note that tags are stored in numerical order */ put_uint16(writing.buf, ifd0_tagcount); writing.buf += 2; if (description) put_stringentry(&writing, TIFF_TAG_IMAGE_DESCRIPTION, description, 0); if (datetime) put_stringentry(&writing, TIFF_TAG_DATETIME, datetime, 1); if (ifd1_tagcount > 0) { /* Offset of IFD1 - TIFF header + IFD0 size. */ unsigned ifd1_offset = 8 + 6 + ( 12 * ifd0_tagcount ); memcpy(writing.buf, exif_subifd_tag, 8); put_uint32(writing.buf + 8, ifd1_offset); writing.buf += 12; } if (datetime) { memcpy(writing.buf, exif_tzoffset_tag, 12); put_sint16(writing.buf+8, timestamp->tm_gmtoff / 3600); writing.buf += 12; } put_uint32(writing.buf, 0); /* Next IFD offset = 0 (no next IFD) */ writing.buf += 4; /* Write IFD 1 */ if (ifd1_tagcount > 0) { /* (remember that the tags in any IFD must be in numerical order * by tag) */ put_uint16(writing.buf, ifd1_tagcount); memcpy(writing.buf + 2, exif_version_tag, 12); /* tag 0x9000 */ writing.buf += 14; if (datetime) put_stringentry(&writing, EXIF_TAG_ORIGINAL_DATETIME, datetime, 1); if (box) put_subjectarea(&writing, box); if (subtime) put_stringentry(&writing, EXIF_TAG_ORIGINAL_DATETIME_SS, subtime, 0); put_uint32(writing.buf, 0); /* Next IFD = 0 (no next IFD) */ writing.buf += 4; } /* We should have met up with the OOL data */ assert( (writing.buf - writing.base) == 8 + ifds_size ); /* The buffer is complete; write it out */ unsigned marker_len = 6 + writing.data_offset; /* assert we didn't underestimate the original buffer size */ assert(marker_len <= buffer_size); /* EXIF data lives in a JPEG APP1 marker */ jpeg_write_marker(cinfo, JPEG_APP0 + 1, marker, marker_len); free(description); free(marker); } /** * 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 dimensions 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, struct context *cnt, struct tm *tm, struct coord *box) { 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 #if JPEG_LIB_VERSION >= 70 cinfo.do_fancy_downsampling = FALSE; // Fix segfault with v7 #endif 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); put_jpeg_exif(&cinfo, cnt, tm, box); /* If the image is not a multiple of 16, this overruns the buffers * we'll just pad those last bytes with zeros */ for (j = 0; j < height; j += 16) { for (i = 0; i < 16; i++) { if ((width * (i + j)) < (width * height)) { y[i] = input_image + width * (i + j); if (i % 2 == 0) { cb[i / 2] = input_image + width * height + width / 2 * ((i + j) /2); cr[i / 2] = input_image + width * height + width * height / 4 + width / 2 * ((i + j) / 2); } } else { y[i] = 0x00; cb[i] = 0x00; cr[i] = 0x00; } } jpeg_write_raw_data(&cinfo, data, 16); } jpeg_finish_compress(&cinfo); jpeg_image_size = _jpeg_mem_size(&cinfo); jpeg_destroy_compress(&cinfo); return jpeg_image_size; } /** * put_jpeg_grey_memory * Converts an input image in the grayscale format into a jpeg image. * * Inputs: * - image_size is the size of the input image buffer. * - input_image is the image in grayscale format. * - width and height are the dimensions 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_grey_memory(unsigned char *dest_image, int image_size, unsigned char *input_image, int width, int height, int quality) { int y, dest_image_size; JSAMPROW row_ptr[1]; struct jpeg_compress_struct cjpeg; struct jpeg_error_mgr jerr; cjpeg.err = jpeg_std_error(&jerr); jpeg_create_compress(&cjpeg); cjpeg.image_width = width; cjpeg.image_height = height; cjpeg.input_components = 1; /* One colour component */ cjpeg.in_color_space = JCS_GRAYSCALE; jpeg_set_defaults(&cjpeg); jpeg_set_quality(&cjpeg, quality, TRUE); cjpeg.dct_method = JDCT_FASTEST; _jpeg_mem_dest(&cjpeg, dest_image, image_size); // Data written to mem jpeg_start_compress (&cjpeg, TRUE); put_jpeg_exif(&cjpeg, NULL, NULL, NULL); row_ptr[0] = input_image; for (y = 0; y < height; y++) { jpeg_write_scanlines(&cjpeg, row_ptr, 1); row_ptr[0] += width; } jpeg_finish_compress(&cjpeg); dest_image_size = _jpeg_mem_size(&cjpeg); jpeg_destroy_compress(&cjpeg); return dest_image_size; } /** * put_jpeg_yuv420p_file * Converts an YUV420P coded image to a jpeg image and writes * it to an already open file. * * Inputs: * - image is the image in YUV420P format. * - width and height are the dimensions of the image * - quality is the jpeg encoding quality 0-100% * * Output: * - The jpeg is written directly to the file given by the file pointer fp * * Returns nothing */ static void put_jpeg_yuv420p_file(FILE *fp, unsigned char *image, int width, int height, int quality, struct context *cnt, struct tm *tm, struct coord *box) { int i, j; 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 #if JPEG_LIB_VERSION >= 70 cinfo.do_fancy_downsampling = FALSE; // Fix segfault with v7 #endif 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_stdio_dest(&cinfo, fp); // Data written to file jpeg_start_compress(&cinfo, TRUE); put_jpeg_exif(&cinfo, cnt, tm, box); for (j = 0; j < height; j += 16) { for (i = 0; i < 16; i++) { if ((width * (i + j)) < (width * height)) { y[i] = image + width * (i + j); if (i % 2 == 0) { cb[i / 2] = image + width * height + width / 2 * ((i + j) / 2); cr[i / 2] = image + width * height + width * height / 4 + width / 2 * ((i + j) / 2); } } else { y[i] = 0x00; cb[i] = 0x00; cr[i] = 0x00; } } jpeg_write_raw_data(&cinfo, data, 16); } jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); } /** * put_jpeg_grey_file * Converts an greyscale image to a jpeg image and writes * it to an already open file. * * Inputs: * - image is the image in greyscale format. * - width and height are the dimensions of the image * - quality is the jpeg encoding quality 0-100% * Output: * - The jpeg is written directly to the file given by the file pointer fp * * Returns nothing */ static void put_jpeg_grey_file(FILE *picture, unsigned char *image, int width, int height, int quality) { int y; JSAMPROW row_ptr[1]; struct jpeg_compress_struct cjpeg; struct jpeg_error_mgr jerr; cjpeg.err = jpeg_std_error(&jerr); jpeg_create_compress(&cjpeg); cjpeg.image_width = width; cjpeg.image_height = height; cjpeg.input_components = 1; /* One colour component */ cjpeg.in_color_space = JCS_GRAYSCALE; jpeg_set_defaults(&cjpeg); jpeg_set_quality(&cjpeg, quality, TRUE); cjpeg.dct_method = JDCT_FASTEST; jpeg_stdio_dest(&cjpeg, picture); jpeg_start_compress(&cjpeg, TRUE); put_jpeg_exif(&cjpeg, NULL, NULL, NULL); row_ptr[0] = image; for (y = 0; y < height; y++) { jpeg_write_scanlines(&cjpeg, row_ptr, 1); row_ptr[0] += width; } jpeg_finish_compress(&cjpeg); jpeg_destroy_compress(&cjpeg); } /** * put_ppm_bgr24_file * Converts an greyscale image to a PPM image and writes * it to an already open file. * Inputs: * - image is the image in YUV420P format. * - width and height are the dimensions of the image * * Output: * - The PPM is written directly to the file given by the file pointer fp * * Returns nothing */ static void put_ppm_bgr24_file(FILE *picture, unsigned char *image, int width, int height) { int x, y; unsigned char *l = image; unsigned char *u = image + width * height; unsigned char *v = u + (width * height) / 4; int r, g, b; unsigned char rgb[3]; /* * ppm header * width height * maxval */ fprintf(picture, "P6\n"); fprintf(picture, "%d %d\n", width, height); fprintf(picture, "%d\n", 255); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { r = 76283 * (((int)*l) - 16)+104595*(((int)*u) - 128); g = 76283 * (((int)*l) - 16)- 53281*(((int)*u) - 128) - 25625 * (((int)*v) - 128); b = 76283 * (((int)*l) - 16) + 132252 * (((int)*v) - 128); r = r >> 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 */ fwrite(rgb, 1, 3, picture); } if (y & 1) { u -= width / 2; v -= width / 2; } } } /** * overlay_smartmask * Copies smartmask as an overlay into motion images and movies. * * Returns nothing. */ 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; i < height; i += 2) { line = i * width; for (x = 0; x < width; x += 2) { if (smartmask[line + x] == 0 || smartmask[line + x + 1] == 0 || smartmask[line + width + x] == 0 || smartmask[line + width + x + 1] == 0) { *out_v = 255; *out_u = 128; } out_v++; out_u++; } } out_y = out; /* Set colour intensity for smartmask. */ for (i = 0; i < imgs->motionsize; i++) { if (smartmask[i] == 0) *out_y = 0; out_y++; } } /** * overlay_fixed_mask * Copies fixed mask as green overlay into motion images and movies. * * Returns nothing. */ void overlay_fixed_mask(struct context *cnt, unsigned char *out) { int i, x, v, width, height, line; struct images *imgs = &cnt->imgs; unsigned char *mask = imgs->mask; unsigned char *out_y, *out_u, *out_v; i = imgs->motionsize; v = i + ((imgs->motionsize) / 4); width = imgs->width; height = imgs->height; /* Set U and V to 0 to make fixed mask appear green. */ out_v = out + v; out_u = out + i; for (i = 0; i < height; i += 2) { line = i * width; for (x = 0; x < width; x += 2) { if (mask[line + x] == 0 || mask[line + x + 1] == 0 || mask[line + width + x] == 0 || mask[line + width + x + 1] == 0) { *out_v = 0; *out_u = 0; } out_v++; out_u++; } } out_y = out; /* Set colour intensity for mask. */ for (i = 0; i < imgs->motionsize; i++) { if (mask[i] == 0) *out_y = 0; out_y++; } } /** * overlay_largest_label * Copies largest label as an overlay into motion images and movies. * * Returns nothing. */ 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; i < height; i += 2) { line = i * width; for (x = 0; x < width; x += 2) { if (labels[line + x] & 32768 || labels[line + x + 1] & 32768 || labels[line + width + x] & 32768 || labels[line + width + x + 1] & 32768) { *out_u = 255; *out_v = 128; } out_u++; out_v++; } } out_y = out; /* Set intensity for coloured label to have better visibility. */ for (i = 0; i < imgs->motionsize; 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 * * 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, cnt, &(cnt->current_image->timestamp_tm), &(cnt->current_image->location)); case VIDEO_PALETTE_GREY: return put_jpeg_grey_memory(dest_image, image_size, image, cnt->imgs.width, cnt->imgs.height, quality); default: MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Unknown image type %d", cnt->imgs.type); } return 0; } void put_picture_fd(struct context *cnt, FILE *picture, unsigned char *image, int quality) { if (cnt->imgs.picture_type == IMAGE_TYPE_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, cnt, &(cnt->current_image->timestamp_tm), &(cnt->current_image->location)); break; case VIDEO_PALETTE_GREY: put_jpeg_grey_file(picture, image, cnt->imgs.width, cnt->imgs.height, quality); break; default: MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Unknown image type %d", cnt->imgs.type); } } } 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(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Can't write picture to file %s - check access rights to target directory\n" "Thread is going to finish due to this fatal error", file); cnt->finish = 1; cnt->restart = 0; return; } else { /* If target dir is temporarily unavailable we may survive. */ MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Can't write picture to file %s", file); return; } } put_picture_fd(cnt, picture, image, cnt->conf.quality); myfclose(picture); event(cnt, EVENT_FILECREATE, NULL, file, (void *)(unsigned long)ftype, NULL); } /** * get_pgm * Get the pgm file used as fixed mask * */ unsigned char *get_pgm(FILE *picture, int width, int height) { int x, y, mask_width, mask_height, maxval; char line[256]; unsigned char *image, *resized_image; line[255] = 0; if (!fgets(line, 255, picture)) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Could not read from ppm file"); return NULL; } if (strncmp(line, "P5", 2)) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: 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; /* Read image size */ if (sscanf(line, "%d %d", &mask_width, &mask_height) != 2) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Failed reading size in pgm file"); return NULL; } /* Maximum value */ line[0] = '#'; while (line[0] == '#') if (!fgets(line, 255, picture)) return NULL; if (sscanf(line, "%d", &maxval) != 1) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Failed reading maximum value in pgm file"); return NULL; } /* Read data */ image = mymalloc(mask_width * mask_height); for (y = 0; y < mask_height; y++) { if ((int)fread(&image[y * mask_width], 1, mask_width, picture) != mask_width) MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Failed reading image data from pgm file"); for (x = 0; x < mask_width; x++) image[y * mask_width + x] = (int)image[y * mask_width + x] * 255 / maxval; } /* Resize mask if required */ if (mask_width != width || mask_height != height) { MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: The mask file specified is not the same size as image from camera."); MOTION_LOG(WRN, TYPE_ALL, NO_ERRNO, "%s: Attempting to resize mask image from %dx%d to %dx%d", mask_width, mask_height, width, height); resized_image = mymalloc(width * height); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { resized_image[y * width + x] = image[ (mask_height - 1) * y / (height - 1) * mask_width + (mask_width - 1) * x / (width - 1)]; } } free(image); image = resized_image; } return image; } /** * put_fixed_mask * If a mask file is asked for but does not exist this function * creates an empty mask file in the right binary pgm format and * and the right size - easy to edit with Gimp or similar tool. * * Returns nothing. */ void put_fixed_mask(struct context *cnt, const char *file) { 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(ERR, TYPE_ALL, SHOW_ERRNO, "%s: can't write mask file %s - check access rights to target directory", file); } else { /* If target dir is temporarily unavailable we may survive. */ MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: can't write mask file %s", file); } return; } memset(cnt->imgs.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(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Failed writing default mask as pgm file"); return; } myfclose(picture); MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: Creating empty mask %s\nPlease edit this file and " "re-run motion to enable mask feature", cnt->conf.mask_file); } /** * preview_save * save preview_shot * * Returns nothing. */ void preview_save(struct context *cnt) { int use_imagepath; int basename_len; const char *imagepath; char previewname[PATH_MAX]; char filename[PATH_MAX]; struct image_data *saved_current_image; if (cnt->imgs.preview_image.diffs) { /* Save current global context. */ saved_current_image = cnt->current_image; /* Set global context to the image we are processing. */ cnt->current_image = &cnt->imgs.preview_image; /* Use filename of movie i.o. jpeg_filename when set to 'preview'. */ use_imagepath = strcmp(cnt->conf.imagepath, "preview"); #ifdef HAVE_FFMPEG if ((cnt->ffmpeg_output || (cnt->conf.useextpipe && cnt->extpipe)) && !use_imagepath) { #else if ((cnt->conf.useextpipe && cnt->extpipe) && !use_imagepath) { #endif if (cnt->conf.useextpipe && cnt->extpipe) { basename_len = strlen(cnt->extpipefilename) + 1; strncpy(previewname, cnt->extpipefilename, basename_len); previewname[basename_len - 1] = '.'; } else { /* Replace avi/mpg with jpg/ppm and keep the rest of the filename. */ basename_len = strlen(cnt->newfilename) - 3; strncpy(previewname, cnt->newfilename, basename_len); } previewname[basename_len] = '\0'; strcat(previewname, imageext(cnt)); put_picture(cnt, previewname, cnt->imgs.preview_image.image , FTYPE_IMAGE); } else { /* * Save best preview-shot also when no movies are recorded or imagepath * is used. Filename has to be generated - nothing available to reuse! */ MOTION_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s: different filename or picture only!"); /* * conf.imagepath 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.imagepath) imagepath = cnt->conf.imagepath; else imagepath = (char *)DEF_IMAGEPATH; mystrftime(cnt, filename, sizeof(filename), imagepath, &cnt->imgs.preview_image.timestamp_tm, NULL, 0); snprintf(previewname, PATH_MAX, "%s/%s.%s", cnt->conf.filepath, filename, imageext(cnt)); put_picture(cnt, previewname, cnt->imgs.preview_image.image, FTYPE_IMAGE); } /* Restore global context values. */ cnt->current_image = saved_current_image; } } motion-release-4.0/picture.h000066400000000000000000000016241300074623700161310ustar00rootroot00000000000000/* * 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_save(struct context *); #endif /* _INCLUDE_PICTURE_H_ */ motion-release-4.0/pwc-ioctl.h000066400000000000000000000265621300074623700163670ustar00rootroot00000000000000#ifndef PWC_IOCTL_H #define PWC_IOCTL_H /* (C) 2001-2004 Nemosoft Unv. (C) 2004-2006 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 10.0.10 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 2006/01/01 Luc Saillard Add raw format definition */ /* 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 */ #if defined(__linux__) #include #include #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 10) /* Compatibility for older kernel */ typedef __u16 __le16; #endif #endif /* ( !BSD ) */ /* 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. This value can also be passing using the private flag when using v4l2 and VIDIOC_S_FMT ioctl. 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 #define PWC_QLT_MASK 0x03000000 #define PWC_QLT_SHIFT 24 /* structure for transferring 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) /* * This is private command used when communicating with v4l2. * In the future all private ioctl will be remove/replace to * use interface offer by v4l2. */ #if (defined(MOTION_V4L2)) && defined(__linux__) #define V4L2_CID_PRIVATE_SAVE_USER (V4L2_CID_PRIVATE_BASE + 0) #define V4L2_CID_PRIVATE_RESTORE_USER (V4L2_CID_PRIVATE_BASE + 1) #define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2) #define V4L2_CID_PRIVATE_COLOUR_MODE (V4L2_CID_PRIVATE_BASE + 3) #define V4L2_CID_PRIVATE_AUTOCONTOUR (V4L2_CID_PRIVATE_BASE + 4) #define V4L2_CID_PRIVATE_CONTOUR (V4L2_CID_PRIVATE_BASE + 5) #define V4L2_CID_PRIVATE_BACKLIGHT (V4L2_CID_PRIVATE_BASE + 6) #define V4L2_CID_PRIVATE_FLICKERLESS (V4L2_CID_PRIVATE_BASE + 7) #define V4L2_CID_PRIVATE_NOISE_REDUCTION (V4L2_CID_PRIVATE_BASE + 8) struct pwc_raw_frame { __le16 type; /* type of the webcam */ __le16 vbandlength; /* Size of 4lines compressed (used by the decompressor) */ __u8 cmd[4]; /* the four byte of the command (in case of nala, only the first 3 bytes is filled) */ __u8 rawframe[0]; /* frame_size = H/4*vbandlength */ } __attribute__ ((packed)); #endif /* MOTION_V4L2 && (! BSD ) */ #endif motion-release-4.0/raspicam/000077500000000000000000000000001300074623700161015ustar00rootroot00000000000000motion-release-4.0/raspicam/README.txt000066400000000000000000000006261300074623700176030ustar00rootroot00000000000000The files in this directory are used in the MMAL/RaspberryPI camera support code. The files were taken from the Raspberry PI userland git repository: https://github.com/raspberrypi/userland The files are unchanged from the userland versions. They are used to parse an options string and setup the camera parameters appropriately. The format of the string is the same as other raspberry pi camera tools. motion-release-4.0/raspicam/RaspiCLI.c000066400000000000000000000103631300074623700176560ustar00rootroot00000000000000/* Copyright (c) 2013, Broadcom Europe Ltd Copyright (c) 2013, James Hughes All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * \file RaspiCLI.c * Code for handling command line parameters * * \date 4th March 2013 * \Author: James Hughes * * Description * * Some functions/structures for command line parameter parsing * */ #include #include #include #include #include "interface/vcos/vcos.h" #include "RaspiCLI.h" /** * Convert a string from command line to a comand_id from the list * * @param commands Array of command to check * @param num_command Number of commands in the array * @param arg String to search for in the list * @param num_parameters Returns the number of parameters used by the command * * @return command ID if found, -1 if not found * */ int raspicli_get_command_id(const COMMAND_LIST *commands, const int num_commands, const char *arg, int *num_parameters) { int command_id = -1; int j; vcos_assert(commands); vcos_assert(num_parameters); vcos_assert(arg); if (!commands || !num_parameters || !arg) return -1; for (j = 0; j < num_commands; j++) { if (!strcmp(arg, commands[j].command) || !strcmp(arg, commands[j].abbrev)) { // match command_id = commands[j].id; *num_parameters = commands[j].num_parameters; break; } } return command_id; } /** * Display the list of commands in help format * * @param commands Array of command to check * @param num_command Number of commands in the arry * * */ void raspicli_display_help(const COMMAND_LIST *commands, const int num_commands) { int i; vcos_assert(commands); if (!commands) return; for (i = 0; i < num_commands; i++) { fprintf(stdout, "-%s, -%s\t: %s\n", commands[i].abbrev, commands[i].command, commands[i].help); } } /** * Function to take a string, a mapping, and return the int equivalent * @param str Incoming string to match * @param map Mapping data * @param num_refs The number of items in the mapping data * @return The integer match for the string, or -1 if no match */ int raspicli_map_xref(const char *str, const XREF_T *map, int num_refs) { int i; for (i=0;i #include #include #include "interface/vcos/vcos.h" #include "interface/vmcs_host/vc_vchi_gencmd.h" #include "interface/mmal/mmal.h" #include "interface/mmal/mmal_logging.h" #include "interface/mmal/util/mmal_util.h" #include "interface/mmal/util/mmal_util_params.h" #include "interface/mmal/util/mmal_default_components.h" #include "RaspiCamControl.h" #include "RaspiCLI.h" /// Structure to cross reference exposure strings against the MMAL parameter equivalent static XREF_T exposure_map[] = { {"off", MMAL_PARAM_EXPOSUREMODE_OFF}, {"auto", MMAL_PARAM_EXPOSUREMODE_AUTO}, {"night", MMAL_PARAM_EXPOSUREMODE_NIGHT}, {"nightpreview", MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW}, {"backlight", MMAL_PARAM_EXPOSUREMODE_BACKLIGHT}, {"spotlight", MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT}, {"sports", MMAL_PARAM_EXPOSUREMODE_SPORTS}, {"snow", MMAL_PARAM_EXPOSUREMODE_SNOW}, {"beach", MMAL_PARAM_EXPOSUREMODE_BEACH}, {"verylong", MMAL_PARAM_EXPOSUREMODE_VERYLONG}, {"fixedfps", MMAL_PARAM_EXPOSUREMODE_FIXEDFPS}, {"antishake", MMAL_PARAM_EXPOSUREMODE_ANTISHAKE}, {"fireworks", MMAL_PARAM_EXPOSUREMODE_FIREWORKS} }; static const int exposure_map_size = sizeof(exposure_map) / sizeof(exposure_map[0]); /// Structure to cross reference awb strings against the MMAL parameter equivalent static XREF_T awb_map[] = { {"off", MMAL_PARAM_AWBMODE_OFF}, {"auto", MMAL_PARAM_AWBMODE_AUTO}, {"sun", MMAL_PARAM_AWBMODE_SUNLIGHT}, {"cloud", MMAL_PARAM_AWBMODE_CLOUDY}, {"shade", MMAL_PARAM_AWBMODE_SHADE}, {"tungsten", MMAL_PARAM_AWBMODE_TUNGSTEN}, {"fluorescent", MMAL_PARAM_AWBMODE_FLUORESCENT}, {"incandescent", MMAL_PARAM_AWBMODE_INCANDESCENT}, {"flash", MMAL_PARAM_AWBMODE_FLASH}, {"horizon", MMAL_PARAM_AWBMODE_HORIZON} }; static const int awb_map_size = sizeof(awb_map) / sizeof(awb_map[0]); /// Structure to cross reference image effect against the MMAL parameter equivalent static XREF_T imagefx_map[] = { {"none", MMAL_PARAM_IMAGEFX_NONE}, {"negative", MMAL_PARAM_IMAGEFX_NEGATIVE}, {"solarise", MMAL_PARAM_IMAGEFX_SOLARIZE}, {"sketch", MMAL_PARAM_IMAGEFX_SKETCH}, {"denoise", MMAL_PARAM_IMAGEFX_DENOISE}, {"emboss", MMAL_PARAM_IMAGEFX_EMBOSS}, {"oilpaint", MMAL_PARAM_IMAGEFX_OILPAINT}, {"hatch", MMAL_PARAM_IMAGEFX_HATCH}, {"gpen", MMAL_PARAM_IMAGEFX_GPEN}, {"pastel", MMAL_PARAM_IMAGEFX_PASTEL}, {"watercolour", MMAL_PARAM_IMAGEFX_WATERCOLOUR}, {"film", MMAL_PARAM_IMAGEFX_FILM}, {"blur", MMAL_PARAM_IMAGEFX_BLUR}, {"saturation", MMAL_PARAM_IMAGEFX_SATURATION}, {"colourswap", MMAL_PARAM_IMAGEFX_COLOURSWAP}, {"washedout", MMAL_PARAM_IMAGEFX_WASHEDOUT}, {"posterise", MMAL_PARAM_IMAGEFX_POSTERISE}, {"colourpoint", MMAL_PARAM_IMAGEFX_COLOURPOINT}, {"colourbalance", MMAL_PARAM_IMAGEFX_COLOURBALANCE}, {"cartoon", MMAL_PARAM_IMAGEFX_CARTOON} }; static const int imagefx_map_size = sizeof(imagefx_map) / sizeof(imagefx_map[0]); static XREF_T metering_mode_map[] = { {"average", MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE}, {"spot", MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT}, {"backlit", MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT}, {"matrix", MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX} }; static const int metering_mode_map_size = sizeof(metering_mode_map)/sizeof(metering_mode_map[0]); static XREF_T drc_mode_map[] = { {"off", MMAL_PARAMETER_DRC_STRENGTH_OFF}, {"low", MMAL_PARAMETER_DRC_STRENGTH_LOW}, {"med", MMAL_PARAMETER_DRC_STRENGTH_MEDIUM}, {"high", MMAL_PARAMETER_DRC_STRENGTH_HIGH} }; static const int drc_mode_map_size = sizeof(drc_mode_map)/sizeof(drc_mode_map[0]); static XREF_T stereo_mode_map[] = { {"off", MMAL_STEREOSCOPIC_MODE_NONE}, {"sbs", MMAL_STEREOSCOPIC_MODE_SIDE_BY_SIDE}, {"tb", MMAL_STEREOSCOPIC_MODE_TOP_BOTTOM}, }; static const int stereo_mode_map_size = sizeof(stereo_mode_map)/sizeof(stereo_mode_map[0]); #define CommandSharpness 0 #define CommandContrast 1 #define CommandBrightness 2 #define CommandSaturation 3 #define CommandISO 4 #define CommandVideoStab 5 #define CommandEVComp 6 #define CommandExposure 7 #define CommandAWB 8 #define CommandImageFX 9 #define CommandColourFX 10 #define CommandMeterMode 11 #define CommandRotation 12 #define CommandHFlip 13 #define CommandVFlip 14 #define CommandROI 15 #define CommandShutterSpeed 16 #define CommandAwbGains 17 #define CommandDRCLevel 18 #define CommandStatsPass 19 #define CommandAnnotate 20 #define CommandStereoMode 21 #define CommandStereoDecimate 22 #define CommandStereoSwap 23 #define CommandAnnotateExtras 24 static COMMAND_LIST cmdline_commands[] = { {CommandSharpness, "-sharpness", "sh", "Set image sharpness (-100 to 100)", 1}, {CommandContrast, "-contrast", "co", "Set image contrast (-100 to 100)", 1}, {CommandBrightness, "-brightness","br", "Set image brightness (0 to 100)", 1}, {CommandSaturation, "-saturation","sa", "Set image saturation (-100 to 100)", 1}, {CommandISO, "-ISO", "ISO","Set capture ISO", 1}, {CommandVideoStab, "-vstab", "vs", "Turn on video stabilisation", 0}, {CommandEVComp, "-ev", "ev", "Set EV compensation - steps of 1/6 stop", 1}, {CommandExposure, "-exposure", "ex", "Set exposure mode (see Notes)", 1}, {CommandAWB, "-awb", "awb","Set AWB mode (see Notes)", 1}, {CommandImageFX, "-imxfx", "ifx","Set image effect (see Notes)", 1}, {CommandColourFX, "-colfx", "cfx","Set colour effect (U:V)", 1}, {CommandMeterMode, "-metering", "mm", "Set metering mode (see Notes)", 1}, {CommandRotation, "-rotation", "rot","Set image rotation (0-359)", 1}, {CommandHFlip, "-hflip", "hf", "Set horizontal flip", 0}, {CommandVFlip, "-vflip", "vf", "Set vertical flip", 0}, {CommandROI, "-roi", "roi","Set region of interest (x,y,w,d as normalised coordinates [0.0-1.0])", 1}, {CommandShutterSpeed,"-shutter", "ss", "Set shutter speed in microseconds", 1}, {CommandAwbGains, "-awbgains", "awbg", "Set AWB gains - AWB mode must be off", 1}, {CommandDRCLevel, "-drc", "drc", "Set DRC Level (see Notes)", 1}, {CommandStatsPass, "-stats", "st", "Force recomputation of statistics on stills capture pass"}, {CommandAnnotate, "-annotate", "a", "Enable/Set annotate flags or text", 1}, {CommandStereoMode, "-stereo", "3d", "Select stereoscopic mode", 1}, {CommandStereoDecimate,"-decimate","dec", "Half width/height of stereo image"}, {CommandStereoSwap, "-3dswap", "3dswap", "Swap camera order for stereoscopic"}, {CommandAnnotateExtras,"-annotateex","ae", "Set extra annotation parameters (text size, text colour(hex YUV), bg colour(hex YUV))", 2}, }; static int cmdline_commands_size = sizeof(cmdline_commands) / sizeof(cmdline_commands[0]); #define parameter_reset -99999 /** * Update the passed in parameter according to the rest of the parameters * passed in. * * * @return 0 if reached end of cycle for this parameter, !0 otherwise */ static int update_cycle_parameter(int *option, int min, int max, int increment) { vcos_assert(option); if (!option) return 0; if (*option == parameter_reset) *option = min - increment; *option += increment; if (*option > max) { *option = parameter_reset; return 0; } else return 1; } /** * Test/Demo code to cycle through a bunch of camera settings * This code is pretty hacky so please don't complain!! * It only does stuff that should have a visual impact (hence demo!) * This will override any user supplied parameters * * Each call of this function will move on to the next setting * * @param camera Pointer to the camera to change settings on. * @return 0 if reached end of complete sequence, !0 otherwise */ int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera) { static int parameter = 0; static int parameter_option = parameter_reset; // which value the parameter currently has vcos_assert(camera); // We are going to cycle through all the relevant entries in the parameter block // and send options to the camera. if (parameter == 0) { // sharpness if (update_cycle_parameter(¶meter_option, -100, 100, 10)) raspicamcontrol_set_sharpness(camera, parameter_option); else { raspicamcontrol_set_sharpness(camera, 0); parameter++; } } else if (parameter == 1) { // contrast if (update_cycle_parameter(¶meter_option, -100, 100, 10)) raspicamcontrol_set_contrast(camera, parameter_option); else { raspicamcontrol_set_contrast(camera, 0); parameter++; } } else if (parameter == 2) { // brightness if (update_cycle_parameter(¶meter_option, 0, 100, 10)) raspicamcontrol_set_brightness(camera, parameter_option); else { raspicamcontrol_set_brightness(camera, 50); parameter++; } } else if (parameter == 3) { // contrast if (update_cycle_parameter(¶meter_option, -100, 100, 10)) raspicamcontrol_set_saturation(camera, parameter_option); else { parameter++; raspicamcontrol_set_saturation(camera, 0); } } else if (parameter == 4) { // EV if (update_cycle_parameter(¶meter_option, -10, 10, 4)) raspicamcontrol_set_exposure_compensation(camera, parameter_option); else { raspicamcontrol_set_exposure_compensation(camera, 0); parameter++; } } else if (parameter == 5) { // MMAL_PARAM_EXPOSUREMODE_T if (update_cycle_parameter(¶meter_option, 0, exposure_map_size, 1)) raspicamcontrol_set_exposure_mode(camera, exposure_map[parameter_option].mmal_mode); else { raspicamcontrol_set_exposure_mode(camera, MMAL_PARAM_EXPOSUREMODE_AUTO); parameter++; } } else if (parameter == 6) { // MMAL_PARAM_AWB_T if (update_cycle_parameter(¶meter_option, 0, awb_map_size, 1)) raspicamcontrol_set_awb_mode(camera, awb_map[parameter_option].mmal_mode); else { raspicamcontrol_set_awb_mode(camera, MMAL_PARAM_AWBMODE_AUTO); parameter++; } } if (parameter == 7) { // MMAL_PARAM_IMAGEFX_T if (update_cycle_parameter(¶meter_option, 0, imagefx_map_size, 1)) raspicamcontrol_set_imageFX(camera, imagefx_map[parameter_option].mmal_mode); else { raspicamcontrol_set_imageFX(camera, MMAL_PARAM_IMAGEFX_NONE); parameter++; } } if (parameter == 8) { MMAL_PARAM_COLOURFX_T colfx = {0,0,0}; switch (parameter_option) { case parameter_reset : parameter_option = 1; colfx.u = 128; colfx.v = 128; break; case 1 : parameter_option = 2; colfx.u = 100; colfx.v = 200; break; case 2 : parameter_option = parameter_reset; colfx.enable = 0; parameter++; break; } raspicamcontrol_set_colourFX(camera, &colfx); } // Orientation if (parameter == 9) { switch (parameter_option) { case parameter_reset: raspicamcontrol_set_rotation(camera, 90); parameter_option = 1; break; case 1 : raspicamcontrol_set_rotation(camera, 180); parameter_option = 2; break; case 2 : raspicamcontrol_set_rotation(camera, 270); parameter_option = 3; break; case 3 : { raspicamcontrol_set_rotation(camera, 0); raspicamcontrol_set_flips(camera, 1,0); parameter_option = 4; break; } case 4 : { raspicamcontrol_set_flips(camera, 0,1); parameter_option = 5; break; } case 5 : { raspicamcontrol_set_flips(camera, 1, 1); parameter_option = 6; break; } case 6 : { raspicamcontrol_set_flips(camera, 0, 0); parameter_option = parameter_reset; parameter++; break; } } } if (parameter == 10) { parameter = 1; return 0; } return 1; } /** * Convert string to the MMAL parameter for exposure mode * @param str Incoming string to match * @return MMAL parameter matching the string, or the AUTO option if no match found */ static MMAL_PARAM_EXPOSUREMODE_T exposure_mode_from_string(const char *str) { int i = raspicli_map_xref(str, exposure_map, exposure_map_size); if( i != -1) return (MMAL_PARAM_EXPOSUREMODE_T)i; vcos_log_error("Unknown exposure mode: %s", str); return MMAL_PARAM_EXPOSUREMODE_AUTO; } /** * Convert string to the MMAL parameter for AWB mode * @param str Incoming string to match * @return MMAL parameter matching the string, or the AUTO option if no match found */ static MMAL_PARAM_AWBMODE_T awb_mode_from_string(const char *str) { int i = raspicli_map_xref(str, awb_map, awb_map_size); if( i != -1) return (MMAL_PARAM_AWBMODE_T)i; vcos_log_error("Unknown awb mode: %s", str); return MMAL_PARAM_AWBMODE_AUTO; } /** * Convert string to the MMAL parameter for image effects mode * @param str Incoming string to match * @return MMAL parameter matching the strong, or the AUTO option if no match found */ MMAL_PARAM_IMAGEFX_T imagefx_mode_from_string(const char *str) { int i = raspicli_map_xref(str, imagefx_map, imagefx_map_size); if( i != -1) return (MMAL_PARAM_IMAGEFX_T)i; vcos_log_error("Unknown image fx: %s", str); return MMAL_PARAM_IMAGEFX_NONE; } /** * Convert string to the MMAL parameter for exposure metering mode * @param str Incoming string to match * @return MMAL parameter matching the string, or the AUTO option if no match found */ static MMAL_PARAM_EXPOSUREMETERINGMODE_T metering_mode_from_string(const char *str) { int i = raspicli_map_xref(str, metering_mode_map, metering_mode_map_size); if( i != -1) return (MMAL_PARAM_EXPOSUREMETERINGMODE_T)i; vcos_log_error("Unknown metering mode: %s", str); return MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; } /** * Convert string to the MMAL parameter for DRC level * @param str Incoming string to match * @return MMAL parameter matching the string, or the AUTO option if no match found */ static MMAL_PARAMETER_DRC_STRENGTH_T drc_mode_from_string(const char *str) { int i = raspicli_map_xref(str, drc_mode_map, drc_mode_map_size); if( i != -1) return (MMAL_PARAMETER_DRC_STRENGTH_T)i; vcos_log_error("Unknown DRC level: %s", str); return MMAL_PARAMETER_DRC_STRENGTH_OFF; } /** * Convert string to the MMAL parameter for exposure metering mode * @param str Incoming string to match * @return MMAL parameter matching the string, or the AUTO option if no match found */ static MMAL_STEREOSCOPIC_MODE_T stereo_mode_from_string(const char *str) { int i = raspicli_map_xref(str, stereo_mode_map, stereo_mode_map_size); if( i != -1) return (MMAL_STEREOSCOPIC_MODE_T)i; vcos_log_error("Unknown metering mode: %s", str); return MMAL_STEREOSCOPIC_MODE_NONE; } /** * Parse a possible command pair - command and parameter * @param arg1 Command * @param arg2 Parameter (could be NULL) * @return How many parameters were used, 0,1,2 */ int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2) { int command_id, used = 0, num_parameters; if (!arg1) return 0; command_id = raspicli_get_command_id(cmdline_commands, cmdline_commands_size, arg1, &num_parameters); // If invalid command, or we are missing a parameter, drop out if (command_id==-1 || (command_id != -1 && num_parameters > 0 && arg2 == NULL)) return 0; switch (command_id) { case CommandSharpness : // sharpness - needs single number parameter sscanf(arg2, "%d", ¶ms->sharpness); used = 2; break; case CommandContrast : // contrast - needs single number parameter sscanf(arg2, "%d", ¶ms->contrast); used = 2; break; case CommandBrightness : // brightness - needs single number parameter sscanf(arg2, "%d", ¶ms->brightness); used = 2; break; case CommandSaturation : // saturation - needs single number parameter sscanf(arg2, "%d", ¶ms->saturation); used = 2; break; case CommandISO : // ISO - needs single number parameter sscanf(arg2, "%d", ¶ms->ISO); used = 2; break; case CommandVideoStab : // video stabilisation - if here, its on params->videoStabilisation = 1; used = 1; break; case CommandEVComp : // EV - needs single number parameter sscanf(arg2, "%d", ¶ms->exposureCompensation); used = 2; break; case CommandExposure : // exposure mode - needs string params->exposureMode = exposure_mode_from_string(arg2); used = 2; break; case CommandAWB : // AWB mode - needs single number parameter params->awbMode = awb_mode_from_string(arg2); used = 2; break; case CommandImageFX : // Image FX - needs string params->imageEffect = imagefx_mode_from_string(arg2); used = 2; break; case CommandColourFX : // Colour FX - needs string "u:v" sscanf(arg2, "%d:%d", ¶ms->colourEffects.u, ¶ms->colourEffects.v); params->colourEffects.enable = 1; used = 2; break; case CommandMeterMode: params->exposureMeterMode = metering_mode_from_string(arg2); used = 2; break; case CommandRotation : // Rotation - degree sscanf(arg2, "%d", ¶ms->rotation); used = 2; break; case CommandHFlip : params->hflip = 1; used = 1; break; case CommandVFlip : params->vflip = 1; used = 1; break; case CommandROI : { double x,y,w,h; int args; args = sscanf(arg2, "%lf,%lf,%lf,%lf", &x,&y,&w,&h); if (args != 4 || x > 1.0 || y > 1.0 || w > 1.0 || h > 1.0) { return 0; } // Make sure we stay within bounds if (x + w > 1.0) w = 1 - x; if (y + h > 1.0) h = 1 - y; params->roi.x = x; params->roi.y = y; params->roi.w = w; params->roi.h = h; used = 2; break; } case CommandShutterSpeed : // Shutter speed needs single number parameter { sscanf(arg2, "%d", ¶ms->shutter_speed); used = 2; break; } case CommandAwbGains : { double r,b; int args; args = sscanf(arg2, "%lf,%lf", &r,&b); if (args != 2 || r > 8.0 || b > 8.0) { return 0; } params->awb_gains_r = r; params->awb_gains_b = b; used = 2; break; } case CommandDRCLevel: { params->drc_level = drc_mode_from_string(arg2); used = 2; break; } case CommandStatsPass: { params->stats_pass = MMAL_TRUE; used = 1; break; } case CommandAnnotate: { char dummy; unsigned int bitmask; // If parameter is a number, assume its a bitmask, otherwise a string if (sscanf(arg2, "%u%c", &bitmask, &dummy) == 1) { params->enable_annotate |= bitmask; } else { params->enable_annotate |= ANNOTATE_USER_TEXT; //copy string char by char and replace "\n" with newline character unsigned char c; char const *s = arg2; char *t = ¶ms->annotate_string[0]; int n=0; while ((c = *s++) && n < MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1) { if (c == '\\' && *s) { switch (c = *s++) { case 'n': c = '\n'; break; default: c = '\\'; s--; break; } } *(t++) = c; n++; } *t='\0'; //params->annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1] = '\0'; } used=2; break; } case CommandAnnotateExtras: { // 3 parameters - text size (6-80), text colour (Hex VVUUYY) and background colour (Hex VVUUYY) sscanf(arg2, "%u,%X,%X", ¶ms->annotate_text_size, ¶ms->annotate_text_colour, ¶ms->annotate_bg_colour); used=2; break; } case CommandStereoMode: { params->stereo_mode.mode = stereo_mode_from_string(arg2); used = 2; break; } case CommandStereoDecimate: { params->stereo_mode.decimate = MMAL_TRUE; used = 1; break; } case CommandStereoSwap: { params->stereo_mode.swap_eyes = MMAL_TRUE; used = 1; break; } } return used; } /** * Display help for command line options */ void raspicamcontrol_display_help() { int i; fprintf(stdout, "\nImage parameter commands\n\n"); raspicli_display_help(cmdline_commands, cmdline_commands_size); fprintf(stdout, "\n\nNotes\n\nExposure mode options :\n%s", exposure_map[0].mode ); for (i=1;iexposureMode, exposure_map, exposure_map_size); const char *awb_mode = raspicli_unmap_xref(params->awbMode, awb_map, awb_map_size); const char *image_effect = raspicli_unmap_xref(params->imageEffect, imagefx_map, imagefx_map_size); const char *metering_mode = raspicli_unmap_xref(params->exposureMeterMode, metering_mode_map, metering_mode_map_size); fprintf(stderr, "Sharpness %d, Contrast %d, Brightness %d\n", params->sharpness, params->contrast, params->brightness); fprintf(stderr, "Saturation %d, ISO %d, Video Stabilisation %s, Exposure compensation %d\n", params->saturation, params->ISO, params->videoStabilisation ? "Yes": "No", params->exposureCompensation); fprintf(stderr, "Exposure Mode '%s', AWB Mode '%s', Image Effect '%s'\n", exp_mode, awb_mode, image_effect); fprintf(stderr, "Metering Mode '%s', Colour Effect Enabled %s with U = %d, V = %d\n", metering_mode, params->colourEffects.enable ? "Yes":"No", params->colourEffects.u, params->colourEffects.v); fprintf(stderr, "Rotation %d, hflip %s, vflip %s\n", params->rotation, params->hflip ? "Yes":"No",params->vflip ? "Yes":"No"); fprintf(stderr, "ROI x %lf, y %f, w %f h %f\n", params->roi.x, params->roi.y, params->roi.w, params->roi.h); } /** * Convert a MMAL status return value to a simple boolean of success * ALso displays a fault if code is not success * * @param status The error code to convert * @return 0 if status is success, 1 otherwise */ int mmal_status_to_int(MMAL_STATUS_T status) { if (status == MMAL_SUCCESS) return 0; else { switch (status) { case MMAL_ENOMEM : vcos_log_error("Out of memory"); break; case MMAL_ENOSPC : vcos_log_error("Out of resources (other than memory)"); break; case MMAL_EINVAL: vcos_log_error("Argument is invalid"); break; case MMAL_ENOSYS : vcos_log_error("Function not implemented"); break; case MMAL_ENOENT : vcos_log_error("No such file or directory"); break; case MMAL_ENXIO : vcos_log_error("No such device or address"); break; case MMAL_EIO : vcos_log_error("I/O error"); break; case MMAL_ESPIPE : vcos_log_error("Illegal seek"); break; case MMAL_ECORRUPT : vcos_log_error("Data is corrupt \attention FIXME: not POSIX"); break; case MMAL_ENOTREADY :vcos_log_error("Component is not ready \attention FIXME: not POSIX"); break; case MMAL_ECONFIG : vcos_log_error("Component is not configured \attention FIXME: not POSIX"); break; case MMAL_EISCONN : vcos_log_error("Port is already connected "); break; case MMAL_ENOTCONN : vcos_log_error("Port is disconnected"); break; case MMAL_EAGAIN : vcos_log_error("Resource temporarily unavailable. Try again later"); break; case MMAL_EFAULT : vcos_log_error("Bad address"); break; default : vcos_log_error("Unknown status error"); break; } return 1; } } /** * Give the supplied parameter block a set of default values * @params Pointer to parameter block */ void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params) { vcos_assert(params); params->sharpness = 0; params->contrast = 0; params->brightness = 50; params->saturation = 0; params->ISO = 0; // 0 = auto params->videoStabilisation = 0; params->exposureCompensation = 0; params->exposureMode = MMAL_PARAM_EXPOSUREMODE_AUTO; params->exposureMeterMode = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; params->awbMode = MMAL_PARAM_AWBMODE_AUTO; params->imageEffect = MMAL_PARAM_IMAGEFX_NONE; params->colourEffects.enable = 0; params->colourEffects.u = 128; params->colourEffects.v = 128; params->rotation = 0; params->hflip = params->vflip = 0; params->roi.x = params->roi.y = 0.0; params->roi.w = params->roi.h = 1.0; params->shutter_speed = 0; // 0 = auto params->awb_gains_r = 0; // Only have any function if AWB OFF is used. params->awb_gains_b = 0; params->drc_level = MMAL_PARAMETER_DRC_STRENGTH_OFF; params->stats_pass = MMAL_FALSE; params->enable_annotate = 0; params->annotate_string[0] = '\0'; params->annotate_text_size = 0; //Use firmware default params->annotate_text_colour = -1; //Use firmware default params->annotate_bg_colour = -1; //Use firmware default params->stereo_mode.mode = MMAL_STEREOSCOPIC_MODE_NONE; params->stereo_mode.decimate = MMAL_FALSE; params->stereo_mode.swap_eyes = MMAL_FALSE; } /** * Get all the current camera parameters from specified camera component * @param camera Pointer to camera component * @param params Pointer to parameter block to accept settings * @return 0 if successful, non-zero if unsuccessful */ int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params) { vcos_assert(camera); vcos_assert(params); if (!camera || !params) return 1; /* TODO : Write these get functions params->sharpness = raspicamcontrol_get_sharpness(camera); params->contrast = raspicamcontrol_get_contrast(camera); params->brightness = raspicamcontrol_get_brightness(camera); params->saturation = raspicamcontrol_get_saturation(camera); params->ISO = raspicamcontrol_get_ISO(camera); params->videoStabilisation = raspicamcontrol_get_video_stabilisation(camera); params->exposureCompensation = raspicamcontrol_get_exposure_compensation(camera); params->exposureMode = raspicamcontrol_get_exposure_mode(camera); params->awbMode = raspicamcontrol_get_awb_mode(camera); params->imageEffect = raspicamcontrol_get_image_effect(camera); params->colourEffects = raspicamcontrol_get_colour_effect(camera); params->thumbnailConfig = raspicamcontrol_get_thumbnail_config(camera); */ return 0; } /** * Set the specified camera to all the specified settings * @param camera Pointer to camera component * @param params Pointer to parameter block containing parameters * @return 0 if successful, none-zero if unsuccessful. */ int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params) { int result; result = raspicamcontrol_set_saturation(camera, params->saturation); result += raspicamcontrol_set_sharpness(camera, params->sharpness); result += raspicamcontrol_set_contrast(camera, params->contrast); result += raspicamcontrol_set_brightness(camera, params->brightness); result += raspicamcontrol_set_ISO(camera, params->ISO); result += raspicamcontrol_set_video_stabilisation(camera, params->videoStabilisation); result += raspicamcontrol_set_exposure_compensation(camera, params->exposureCompensation); result += raspicamcontrol_set_exposure_mode(camera, params->exposureMode); result += raspicamcontrol_set_metering_mode(camera, params->exposureMeterMode); result += raspicamcontrol_set_awb_mode(camera, params->awbMode); result += raspicamcontrol_set_awb_gains(camera, params->awb_gains_r, params->awb_gains_b); result += raspicamcontrol_set_imageFX(camera, params->imageEffect); result += raspicamcontrol_set_colourFX(camera, ¶ms->colourEffects); //result += raspicamcontrol_set_thumbnail_parameters(camera, ¶ms->thumbnailConfig); TODO Not working for some reason result += raspicamcontrol_set_rotation(camera, params->rotation); result += raspicamcontrol_set_flips(camera, params->hflip, params->vflip); result += raspicamcontrol_set_ROI(camera, params->roi); result += raspicamcontrol_set_shutter_speed(camera, params->shutter_speed); result += raspicamcontrol_set_DRC(camera, params->drc_level); result += raspicamcontrol_set_stats_pass(camera, params->stats_pass); result += raspicamcontrol_set_annotate(camera, params->enable_annotate, params->annotate_string, params->annotate_text_size, params->annotate_text_colour, params->annotate_bg_colour); return result; } /** * Adjust the saturation level for images * @param camera Pointer to camera component * @param saturation Value to adjust, -100 to 100 * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation) { int ret = 0; if (!camera) return 1; if (saturation >= -100 && saturation <= 100) { MMAL_RATIONAL_T value = {saturation, 100}; ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SATURATION, value)); } else { vcos_log_error("Invalid saturation value"); ret = 1; } return ret; } /** * Set the sharpness of the image * @param camera Pointer to camera component * @param sharpness Sharpness adjustment -100 to 100 */ int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness) { int ret = 0; if (!camera) return 1; if (sharpness >= -100 && sharpness <= 100) { MMAL_RATIONAL_T value = {sharpness, 100}; ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_SHARPNESS, value)); } else { vcos_log_error("Invalid sharpness value"); ret = 1; } return ret; } /** * Set the contrast adjustment for the image * @param camera Pointer to camera component * @param contrast Contrast adjustment -100 to 100 * @return */ int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast) { int ret = 0; if (!camera) return 1; if (contrast >= -100 && contrast <= 100) { MMAL_RATIONAL_T value = {contrast, 100}; ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_CONTRAST, value)); } else { vcos_log_error("Invalid contrast value"); ret = 1; } return ret; } /** * Adjust the brightness level for images * @param camera Pointer to camera component * @param brightness Value to adjust, 0 to 100 * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness) { int ret = 0; if (!camera) return 1; if (brightness >= 0 && brightness <= 100) { MMAL_RATIONAL_T value = {brightness, 100}; ret = mmal_status_to_int(mmal_port_parameter_set_rational(camera->control, MMAL_PARAMETER_BRIGHTNESS, value)); } else { vcos_log_error("Invalid brightness value"); ret = 1; } return ret; } /** * Adjust the ISO used for images * @param camera Pointer to camera component * @param ISO Value to set TODO : * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO) { if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_ISO, ISO)); } /** * Adjust the metering mode for images * @param camera Pointer to camera component * @param saturation Value from following * - MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, * - MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, * - MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, * - MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T m_mode ) { MMAL_PARAMETER_EXPOSUREMETERINGMODE_T meter_mode = {{MMAL_PARAMETER_EXP_METERING_MODE,sizeof(meter_mode)}, m_mode}; if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set(camera->control, &meter_mode.hdr)); } /** * Set the video stabilisation flag. Only used in video mode * @param camera Pointer to camera component * @param saturation Flag 0 off 1 on * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation) { if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_VIDEO_STABILISATION, vstabilisation)); } /** * Adjust the exposure compensation for images (EV) * @param camera Pointer to camera component * @param exp_comp Value to adjust, -10 to +10 * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp) { if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set_int32(camera->control, MMAL_PARAMETER_EXPOSURE_COMP , exp_comp)); } /** * Set exposure mode for images * @param camera Pointer to camera component * @param mode Exposure mode to set from * - MMAL_PARAM_EXPOSUREMODE_OFF, * - MMAL_PARAM_EXPOSUREMODE_AUTO, * - MMAL_PARAM_EXPOSUREMODE_NIGHT, * - MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, * - MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, * - MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, * - MMAL_PARAM_EXPOSUREMODE_SPORTS, * - MMAL_PARAM_EXPOSUREMODE_SNOW, * - MMAL_PARAM_EXPOSUREMODE_BEACH, * - MMAL_PARAM_EXPOSUREMODE_VERYLONG, * - MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, * - MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, * - MMAL_PARAM_EXPOSUREMODE_FIREWORKS, * * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode) { MMAL_PARAMETER_EXPOSUREMODE_T exp_mode = {{MMAL_PARAMETER_EXPOSURE_MODE,sizeof(exp_mode)}, mode}; if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set(camera->control, &exp_mode.hdr)); } /** * Set the aWB (auto white balance) mode for images * @param camera Pointer to camera component * @param awb_mode Value to set from * - MMAL_PARAM_AWBMODE_OFF, * - MMAL_PARAM_AWBMODE_AUTO, * - MMAL_PARAM_AWBMODE_SUNLIGHT, * - MMAL_PARAM_AWBMODE_CLOUDY, * - MMAL_PARAM_AWBMODE_SHADE, * - MMAL_PARAM_AWBMODE_TUNGSTEN, * - MMAL_PARAM_AWBMODE_FLUORESCENT, * - MMAL_PARAM_AWBMODE_INCANDESCENT, * - MMAL_PARAM_AWBMODE_FLASH, * - MMAL_PARAM_AWBMODE_HORIZON, * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode) { MMAL_PARAMETER_AWBMODE_T param = {{MMAL_PARAMETER_AWB_MODE,sizeof(param)}, awb_mode}; if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); } int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain) { MMAL_PARAMETER_AWB_GAINS_T param = {{MMAL_PARAMETER_CUSTOM_AWB_GAINS,sizeof(param)}, {0,0}, {0,0}}; if (!camera) return 1; if (!r_gain || !b_gain) return 0; param.r_gain.num = (unsigned int)(r_gain * 65536); param.b_gain.num = (unsigned int)(b_gain * 65536); param.r_gain.den = param.b_gain.den = 65536; return mmal_status_to_int(mmal_port_parameter_set(camera->control, ¶m.hdr)); } /** * Set the image effect for the images * @param camera Pointer to camera component * @param imageFX Value from * - MMAL_PARAM_IMAGEFX_NONE, * - MMAL_PARAM_IMAGEFX_NEGATIVE, * - MMAL_PARAM_IMAGEFX_SOLARIZE, * - MMAL_PARAM_IMAGEFX_POSTERIZE, * - MMAL_PARAM_IMAGEFX_WHITEBOARD, * - MMAL_PARAM_IMAGEFX_BLACKBOARD, * - MMAL_PARAM_IMAGEFX_SKETCH, * - MMAL_PARAM_IMAGEFX_DENOISE, * - MMAL_PARAM_IMAGEFX_EMBOSS, * - MMAL_PARAM_IMAGEFX_OILPAINT, * - MMAL_PARAM_IMAGEFX_HATCH, * - MMAL_PARAM_IMAGEFX_GPEN, * - MMAL_PARAM_IMAGEFX_PASTEL, * - MMAL_PARAM_IMAGEFX_WATERCOLOUR, * - MMAL_PARAM_IMAGEFX_FILM, * - MMAL_PARAM_IMAGEFX_BLUR, * - MMAL_PARAM_IMAGEFX_SATURATION, * - MMAL_PARAM_IMAGEFX_COLOURSWAP, * - MMAL_PARAM_IMAGEFX_WASHEDOUT, * - MMAL_PARAM_IMAGEFX_POSTERISE, * - MMAL_PARAM_IMAGEFX_COLOURPOINT, * - MMAL_PARAM_IMAGEFX_COLOURBALANCE, * - MMAL_PARAM_IMAGEFX_CARTOON, * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX) { MMAL_PARAMETER_IMAGEFX_T imgFX = {{MMAL_PARAMETER_IMAGE_EFFECT,sizeof(imgFX)}, imageFX}; if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set(camera->control, &imgFX.hdr)); } /* TODO :what to do with the image effects parameters? MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {{MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS,sizeof(imfx_param)}, imageFX, 0, {0}}; mmal_port_parameter_set(camera->control, &imfx_param.hdr); */ /** * Set the colour effect for images (Set UV component) * @param camera Pointer to camera component * @param colourFX Contains enable state and U and V numbers to set (e.g. 128,128 = Black and white) * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX) { MMAL_PARAMETER_COLOURFX_T colfx = {{MMAL_PARAMETER_COLOUR_EFFECT,sizeof(colfx)}, 0, 0, 0}; if (!camera) return 1; colfx.enable = colourFX->enable; colfx.u = colourFX->u; colfx.v = colourFX->v; return mmal_status_to_int(mmal_port_parameter_set(camera->control, &colfx.hdr)); } /** * Set the rotation of the image * @param camera Pointer to camera component * @param rotation Degree of rotation (any number, but will be converted to 0,90,180 or 270 only) * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation) { int ret; int my_rotation = ((rotation % 360 ) / 90) * 90; ret = mmal_port_parameter_set_int32(camera->output[0], MMAL_PARAMETER_ROTATION, my_rotation); mmal_port_parameter_set_int32(camera->output[1], MMAL_PARAMETER_ROTATION, my_rotation); mmal_port_parameter_set_int32(camera->output[2], MMAL_PARAMETER_ROTATION, my_rotation); return ret; } /** * Set the flips state of the image * @param camera Pointer to camera component * @param hflip If true, horizontally flip the image * @param vflip If true, vertically flip the image * * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip) { MMAL_PARAMETER_MIRROR_T mirror = {{MMAL_PARAMETER_MIRROR, sizeof(MMAL_PARAMETER_MIRROR_T)}, MMAL_PARAM_MIRROR_NONE}; if (hflip && vflip) mirror.value = MMAL_PARAM_MIRROR_BOTH; else if (hflip) mirror.value = MMAL_PARAM_MIRROR_HORIZONTAL; else if (vflip) mirror.value = MMAL_PARAM_MIRROR_VERTICAL; mmal_port_parameter_set(camera->output[0], &mirror.hdr); mmal_port_parameter_set(camera->output[1], &mirror.hdr); return mmal_port_parameter_set(camera->output[2], &mirror.hdr); } /** * Set the ROI of the sensor to use for captures/preview * @param camera Pointer to camera component * @param rect Normalised coordinates of ROI rectangle * * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect) { MMAL_PARAMETER_INPUT_CROP_T crop = {{MMAL_PARAMETER_INPUT_CROP, sizeof(MMAL_PARAMETER_INPUT_CROP_T)}}; crop.rect.x = (65536 * rect.x); crop.rect.y = (65536 * rect.y); crop.rect.width = (65536 * rect.w); crop.rect.height = (65536 * rect.h); return mmal_port_parameter_set(camera->control, &crop.hdr); } /** * Adjust the exposure time used for images * @param camera Pointer to camera component * @param shutter speed in microseconds * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed) { if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set_uint32(camera->control, MMAL_PARAMETER_SHUTTER_SPEED, speed)); } /** * Adjust the Dynamic range compression level * @param camera Pointer to camera component * @param strength Strength of DRC to apply * MMAL_PARAMETER_DRC_STRENGTH_OFF * MMAL_PARAMETER_DRC_STRENGTH_LOW * MMAL_PARAMETER_DRC_STRENGTH_MEDIUM * MMAL_PARAMETER_DRC_STRENGTH_HIGH * * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength) { MMAL_PARAMETER_DRC_T drc = {{MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, sizeof(MMAL_PARAMETER_DRC_T)}, strength}; if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set(camera->control, &drc.hdr)); } int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass) { if (!camera) return 1; return mmal_status_to_int(mmal_port_parameter_set_boolean(camera->control, MMAL_PARAMETER_CAPTURE_STATS_PASS, stats_pass)); } /** * Set the annotate data * @param camera Pointer to camera component * @param Bitmask of required annotation data. 0 for off. * @param If set, a pointer to text string to use instead of bitmask, max length 32 characters * * @return 0 if successful, non-zero if any parameters out of range */ int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int settings, const char *string, const int text_size, const int text_colour, const int bg_colour) { MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T annotate = {{MMAL_PARAMETER_ANNOTATE, sizeof(MMAL_PARAMETER_CAMERA_ANNOTATE_V3_T)}}; if (settings) { time_t t = time(NULL); struct tm tm = *localtime(&t); char tmp[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3]; int process_datetime = 1; annotate.enable = 1; if (settings & (ANNOTATE_APP_TEXT | ANNOTATE_USER_TEXT)) { if ((settings & (ANNOTATE_TIME_TEXT | ANNOTATE_DATE_TEXT)) && strchr(string,'%') != NULL) { //string contains strftime parameter? strftime(annotate.text, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3, string, &tm ); process_datetime = 0; }else{ strncpy(annotate.text, string, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3); } annotate.text[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3-1] = '\0'; } if (process_datetime && (settings & ANNOTATE_TIME_TEXT)) { if(strlen(annotate.text)){ strftime(tmp, 32, " %X", &tm ); }else{ strftime(tmp, 32, "%X", &tm ); } strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); } if (process_datetime && (settings & ANNOTATE_DATE_TEXT)) { if(strlen(annotate.text)){ strftime(tmp, 32, " %x", &tm ); }else{ strftime(tmp, 32, "%x", &tm ); } strncat(annotate.text, tmp, MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V3 - strlen(annotate.text) - 1); } if (settings & ANNOTATE_SHUTTER_SETTINGS) annotate.show_shutter = MMAL_TRUE; if (settings & ANNOTATE_GAIN_SETTINGS) annotate.show_analog_gain = MMAL_TRUE; if (settings & ANNOTATE_LENS_SETTINGS) annotate.show_lens = MMAL_TRUE; if (settings & ANNOTATE_CAF_SETTINGS) annotate.show_caf = MMAL_TRUE; if (settings & ANNOTATE_MOTION_SETTINGS) annotate.show_motion = MMAL_TRUE; if (settings & ANNOTATE_FRAME_NUMBER) annotate.show_frame_num = MMAL_TRUE; if (settings & ANNOTATE_BLACK_BACKGROUND) annotate.enable_text_background = MMAL_TRUE; annotate.text_size = text_size; if (text_colour != -1) { annotate.custom_text_colour = MMAL_TRUE; annotate.custom_text_Y = text_colour&0xff; annotate.custom_text_U = (text_colour>>8)&0xff; annotate.custom_text_V = (text_colour>>16)&0xff; } else annotate.custom_text_colour = MMAL_FALSE; if (bg_colour != -1) { annotate.custom_background_colour = MMAL_TRUE; annotate.custom_background_Y = bg_colour&0xff; annotate.custom_background_U = (bg_colour>>8)&0xff; annotate.custom_background_V = (bg_colour>>16)&0xff; } else annotate.custom_background_colour = MMAL_FALSE; } else annotate.enable = 0; return mmal_status_to_int(mmal_port_parameter_set(camera->control, &annotate.hdr)); } int raspicamcontrol_set_stereo_mode(MMAL_PORT_T *port, MMAL_PARAMETER_STEREOSCOPIC_MODE_T *stereo_mode) { MMAL_PARAMETER_STEREOSCOPIC_MODE_T stereo = { {MMAL_PARAMETER_STEREOSCOPIC_MODE, sizeof(stereo)}, MMAL_STEREOSCOPIC_MODE_NONE, MMAL_FALSE, MMAL_FALSE }; if (stereo_mode->mode != MMAL_STEREOSCOPIC_MODE_NONE) { stereo.mode = stereo_mode->mode; stereo.decimate = stereo_mode->decimate; stereo.swap_eyes = stereo_mode->swap_eyes; } return mmal_status_to_int(mmal_port_parameter_set(port, &stereo.hdr)); } /** * Asked GPU how much memory it has allocated * * @return amount of memory in MB */ static int raspicamcontrol_get_mem_gpu(void) { char response[80] = ""; int gpu_mem = 0; if (vc_gencmd(response, sizeof response, "get_mem gpu") == 0) vc_gencmd_number_property(response, "gpu", &gpu_mem); return gpu_mem; } /** * Ask GPU about its camera abilities * @param supported None-zero if software supports the camera * @param detected None-zero if a camera has been detected */ static void raspicamcontrol_get_camera(int *supported, int *detected) { char response[80] = ""; if (vc_gencmd(response, sizeof response, "get_camera") == 0) { if (supported) vc_gencmd_number_property(response, "supported", supported); if (detected) vc_gencmd_number_property(response, "detected", detected); } } /** * Check to see if camera is supported, and we have allocated enough meooryAsk GPU about its camera abilities * @param supported None-zero if software supports the camera * @param detected None-zero if a camera has been detected */ void raspicamcontrol_check_configuration(int min_gpu_mem) { int gpu_mem = raspicamcontrol_get_mem_gpu(); int supported = 0, detected = 0; raspicamcontrol_get_camera(&supported, &detected); if (!supported) vcos_log_error("Camera is not enabled in this build. Try running \"sudo raspi-config\" and ensure that \"camera\" has been enabled\n"); else if (gpu_mem < min_gpu_mem) vcos_log_error("Only %dM of gpu_mem is configured. Try running \"sudo raspi-config\" and ensure that \"memory_split\" has a value of %d or greater\n", gpu_mem, min_gpu_mem); else if (!detected) vcos_log_error("Camera is not detected. Please check carefully the camera module is installed correctly\n"); else vcos_log_error("Failed to run camera app. Please check for firmware updates\n"); } motion-release-4.0/raspicam/RaspiCamControl.h000066400000000000000000000235771300074623700213300ustar00rootroot00000000000000/* Copyright (c) 2013, Broadcom Europe Ltd Copyright (c) 2013, James Hughes All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef RASPICAMCONTROL_H_ #define RASPICAMCONTROL_H_ /* Various parameters * * Exposure Mode * MMAL_PARAM_EXPOSUREMODE_OFF, MMAL_PARAM_EXPOSUREMODE_AUTO, MMAL_PARAM_EXPOSUREMODE_NIGHT, MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, MMAL_PARAM_EXPOSUREMODE_SPORTS, MMAL_PARAM_EXPOSUREMODE_SNOW, MMAL_PARAM_EXPOSUREMODE_BEACH, MMAL_PARAM_EXPOSUREMODE_VERYLONG, MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, MMAL_PARAM_EXPOSUREMODE_FIREWORKS, * * AWB Mode * MMAL_PARAM_AWBMODE_OFF, MMAL_PARAM_AWBMODE_AUTO, MMAL_PARAM_AWBMODE_SUNLIGHT, MMAL_PARAM_AWBMODE_CLOUDY, MMAL_PARAM_AWBMODE_SHADE, MMAL_PARAM_AWBMODE_TUNGSTEN, MMAL_PARAM_AWBMODE_FLUORESCENT, MMAL_PARAM_AWBMODE_INCANDESCENT, MMAL_PARAM_AWBMODE_FLASH, MMAL_PARAM_AWBMODE_HORIZON, * * Image FX MMAL_PARAM_IMAGEFX_NONE, MMAL_PARAM_IMAGEFX_NEGATIVE, MMAL_PARAM_IMAGEFX_SOLARIZE, MMAL_PARAM_IMAGEFX_POSTERIZE, MMAL_PARAM_IMAGEFX_WHITEBOARD, MMAL_PARAM_IMAGEFX_BLACKBOARD, MMAL_PARAM_IMAGEFX_SKETCH, MMAL_PARAM_IMAGEFX_DENOISE, MMAL_PARAM_IMAGEFX_EMBOSS, MMAL_PARAM_IMAGEFX_OILPAINT, MMAL_PARAM_IMAGEFX_HATCH, MMAL_PARAM_IMAGEFX_GPEN, MMAL_PARAM_IMAGEFX_PASTEL, MMAL_PARAM_IMAGEFX_WATERCOLOUR, MMAL_PARAM_IMAGEFX_FILM, MMAL_PARAM_IMAGEFX_BLUR, MMAL_PARAM_IMAGEFX_SATURATION, MMAL_PARAM_IMAGEFX_COLOURSWAP, MMAL_PARAM_IMAGEFX_WASHEDOUT, MMAL_PARAM_IMAGEFX_POSTERISE, MMAL_PARAM_IMAGEFX_COLOURPOINT, MMAL_PARAM_IMAGEFX_COLOURBALANCE, MMAL_PARAM_IMAGEFX_CARTOON, */ /// Annotate bitmask options /// Supplied by user on command line #define ANNOTATE_USER_TEXT 1 /// Supplied by app using this module #define ANNOTATE_APP_TEXT 2 /// Insert current date #define ANNOTATE_DATE_TEXT 4 // Insert current time #define ANNOTATE_TIME_TEXT 8 #define ANNOTATE_SHUTTER_SETTINGS 16 #define ANNOTATE_CAF_SETTINGS 32 #define ANNOTATE_GAIN_SETTINGS 64 #define ANNOTATE_LENS_SETTINGS 128 #define ANNOTATE_MOTION_SETTINGS 256 #define ANNOTATE_FRAME_NUMBER 512 #define ANNOTATE_BLACK_BACKGROUND 1024 // There isn't actually a MMAL structure for the following, so make one typedef struct mmal_param_colourfx_s { int enable; /// Turn colourFX on or off int u,v; /// U and V to use } MMAL_PARAM_COLOURFX_T; typedef struct mmal_param_thumbnail_config_s { int enable; int width,height; int quality; } MMAL_PARAM_THUMBNAIL_CONFIG_T; typedef struct param_float_rect_s { double x; double y; double w; double h; } PARAM_FLOAT_RECT_T; /// struct contain camera settings typedef struct raspicam_camera_parameters_s { int sharpness; /// -100 to 100 int contrast; /// -100 to 100 int brightness; /// 0 to 100 int saturation; /// -100 to 100 int ISO; /// TODO : what range? int videoStabilisation; /// 0 or 1 (false or true) int exposureCompensation; /// -10 to +10 ? MMAL_PARAM_EXPOSUREMODE_T exposureMode; MMAL_PARAM_EXPOSUREMETERINGMODE_T exposureMeterMode; MMAL_PARAM_AWBMODE_T awbMode; MMAL_PARAM_IMAGEFX_T imageEffect; MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imageEffectsParameters; MMAL_PARAM_COLOURFX_T colourEffects; int rotation; /// 0-359 int hflip; /// 0 or 1 int vflip; /// 0 or 1 PARAM_FLOAT_RECT_T roi; /// region of interest to use on the sensor. Normalised [0,1] values in the rect int shutter_speed; /// 0 = auto, otherwise the shutter speed in ms float awb_gains_r; /// AWB red gain float awb_gains_b; /// AWB blue gain MMAL_PARAMETER_DRC_STRENGTH_T drc_level; // Strength of Dynamic Range compression to apply MMAL_BOOL_T stats_pass; /// Stills capture statistics pass on/off int enable_annotate; /// Flag to enable the annotate, 0 = disabled, otherwise a bitmask of what needs to be displayed char annotate_string[MMAL_CAMERA_ANNOTATE_MAX_TEXT_LEN_V2]; /// String to use for annotate - overrides certain bitmask settings int annotate_text_size; // Text size for annotation int annotate_text_colour; // Text colour for annotation int annotate_bg_colour; // Background colour for annotation MMAL_PARAMETER_STEREOSCOPIC_MODE_T stereo_mode; } RASPICAM_CAMERA_PARAMETERS; void raspicamcontrol_check_configuration(int min_gpu_mem); int raspicamcontrol_parse_cmdline(RASPICAM_CAMERA_PARAMETERS *params, const char *arg1, const char *arg2); void raspicamcontrol_display_help(); int raspicamcontrol_cycle_test(MMAL_COMPONENT_T *camera); int raspicamcontrol_set_all_parameters(MMAL_COMPONENT_T *camera, const RASPICAM_CAMERA_PARAMETERS *params); int raspicamcontrol_get_all_parameters(MMAL_COMPONENT_T *camera, RASPICAM_CAMERA_PARAMETERS *params); void raspicamcontrol_dump_parameters(const RASPICAM_CAMERA_PARAMETERS *params); void raspicamcontrol_set_defaults(RASPICAM_CAMERA_PARAMETERS *params); void raspicamcontrol_check_configuration(int min_gpu_mem); // Individual setting functions int raspicamcontrol_set_saturation(MMAL_COMPONENT_T *camera, int saturation); int raspicamcontrol_set_sharpness(MMAL_COMPONENT_T *camera, int sharpness); int raspicamcontrol_set_contrast(MMAL_COMPONENT_T *camera, int contrast); int raspicamcontrol_set_brightness(MMAL_COMPONENT_T *camera, int brightness); int raspicamcontrol_set_ISO(MMAL_COMPONENT_T *camera, int ISO); int raspicamcontrol_set_metering_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMETERINGMODE_T mode); int raspicamcontrol_set_video_stabilisation(MMAL_COMPONENT_T *camera, int vstabilisation); int raspicamcontrol_set_exposure_compensation(MMAL_COMPONENT_T *camera, int exp_comp); int raspicamcontrol_set_exposure_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_EXPOSUREMODE_T mode); int raspicamcontrol_set_awb_mode(MMAL_COMPONENT_T *camera, MMAL_PARAM_AWBMODE_T awb_mode); int raspicamcontrol_set_awb_gains(MMAL_COMPONENT_T *camera, float r_gain, float b_gain); int raspicamcontrol_set_imageFX(MMAL_COMPONENT_T *camera, MMAL_PARAM_IMAGEFX_T imageFX); int raspicamcontrol_set_colourFX(MMAL_COMPONENT_T *camera, const MMAL_PARAM_COLOURFX_T *colourFX); int raspicamcontrol_set_rotation(MMAL_COMPONENT_T *camera, int rotation); int raspicamcontrol_set_flips(MMAL_COMPONENT_T *camera, int hflip, int vflip); int raspicamcontrol_set_ROI(MMAL_COMPONENT_T *camera, PARAM_FLOAT_RECT_T rect); int raspicamcontrol_set_shutter_speed(MMAL_COMPONENT_T *camera, int speed_ms); int raspicamcontrol_set_DRC(MMAL_COMPONENT_T *camera, MMAL_PARAMETER_DRC_STRENGTH_T strength); int raspicamcontrol_set_stats_pass(MMAL_COMPONENT_T *camera, int stats_pass); int raspicamcontrol_set_annotate(MMAL_COMPONENT_T *camera, const int bitmask, const char *string, const int text_size, const int text_colour, const int bg_colour); int raspicamcontrol_set_stereo_mode(MMAL_PORT_T *port, MMAL_PARAMETER_STEREOSCOPIC_MODE_T *stereo_mode); //Individual getting functions int raspicamcontrol_get_saturation(MMAL_COMPONENT_T *camera); int raspicamcontrol_get_sharpness(MMAL_COMPONENT_T *camera); int raspicamcontrol_get_contrast(MMAL_COMPONENT_T *camera); int raspicamcontrol_get_brightness(MMAL_COMPONENT_T *camera); int raspicamcontrol_get_ISO(MMAL_COMPONENT_T *camera); MMAL_PARAM_EXPOSUREMETERINGMODE_T raspicamcontrol_get_metering_mode(MMAL_COMPONENT_T *camera); int raspicamcontrol_get_video_stabilisation(MMAL_COMPONENT_T *camera); int raspicamcontrol_get_exposure_compensation(MMAL_COMPONENT_T *camera); MMAL_PARAM_THUMBNAIL_CONFIG_T raspicamcontrol_get_thumbnail_parameters(MMAL_COMPONENT_T *camera); MMAL_PARAM_EXPOSUREMODE_T raspicamcontrol_get_exposure_mode(MMAL_COMPONENT_T *camera); MMAL_PARAM_AWBMODE_T raspicamcontrol_get_awb_mode(MMAL_COMPONENT_T *camera); MMAL_PARAM_IMAGEFX_T raspicamcontrol_get_imageFX(MMAL_COMPONENT_T *camera); MMAL_PARAM_COLOURFX_T raspicamcontrol_get_colourFX(MMAL_COMPONENT_T *camera); #endif /* RASPICAMCONTROL_H_ */ motion-release-4.0/rotate.c000066400000000000000000000250111300074623700157430ustar00rootroot00000000000000/* * 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" #include #if defined(__APPLE__) #include #define bswap_32(x) OSSwapInt32(x) #elif defined(BSD) #include #define bswap_32(x) bswap32(x) #else #include #endif /** * reverse_inplace_quad * * Reverses a block of memory in-place, 4 bytes at a time. This function * requires the uint32_t 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_t *nsrc = (uint32_t *)src; /* first quad */ uint32_t *ndst = (uint32_t *)(src + size - 4); /* last quad */ register uint32_t tmp; while (nsrc < ndst) { tmp = bswap_32(*ndst); *ndst-- = bswap_32(*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(WRN, TYPE_ALL, NO_ERRNO, "%s: 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(WRN, TYPE_ALL, NO_ERRNO, "%s: 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; } motion-release-4.0/rotate.h000066400000000000000000000031431300074623700157520ustar00rootroot00000000000000/* * 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 motion-release-4.0/sdl.c000066400000000000000000000132731300074623700152360ustar00rootroot00000000000000/* * sdl.c * * sdl functions for motion. * Copyright 2009 by Peter Holik (peter@holik.at) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. */ #include "sdl.h" #include static int cur_width; static int cur_height; static int is_full_screen; static int fs_screen_width; static int fs_screen_height; static SDL_Surface *screen; static SDL_Overlay *overlay; static int sdl_video_open(int width, int height) { int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL; int w,h; if (is_full_screen) flags |= SDL_FULLSCREEN; else flags |= SDL_RESIZABLE; if (is_full_screen && fs_screen_width) { w = fs_screen_width; h = fs_screen_height; } else if (width > fs_screen_width || height > fs_screen_height) { w = fs_screen_width; h = fs_screen_height; } else { w = width; h = height; } /* 32 because framebuffer is usually initalized to 8 and you have to use fbset with -depth to make it working */ screen = SDL_SetVideoMode(w, h, 32, flags); if (!screen) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO,"%s: Unable to set video mode: %s", SDL_GetError()); return -1; } MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: SDL dimension %d x %d fullscreen %d BytesPerPixel %d", screen->w, screen->h, is_full_screen, screen->format->BytesPerPixel); SDL_WM_SetCaption("motion", "motion"); SDL_ShowCursor(SDL_DISABLE); if (cur_width != width || cur_height != height) { cur_width = width; cur_height = height; if (overlay) SDL_FreeYUVOverlay(overlay); overlay = SDL_CreateYUVOverlay(cur_width, cur_height, SDL_YV12_OVERLAY, screen); if (!overlay) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Could not create overlay: %s", SDL_GetError()); sdl_stop(); } else MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: SDL created %dx%dx%d %s overlay", overlay->w,overlay->h,overlay->planes, overlay->hw_overlay?"hardware":"software"); } return overlay == NULL; } int sdl_start(int width, int height) { //putenv("SDL_NOMOUSE=1"); setenv("SDL_NOMOUSE", "1", 1); if (screen) return 0; MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: SDL start"); if (SDL_Init(SDL_INIT_VIDEO)) { MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: Could not initialize SDL - %s", SDL_GetError()); return -1; } const SDL_VideoInfo *vi = SDL_GetVideoInfo(); fs_screen_width = vi->current_w; fs_screen_height = vi->current_h; if (sdl_video_open(width, height)) return -1; SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE); SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE); SDL_EventState(SDL_USEREVENT, SDL_IGNORE); SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE); SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE); SDL_EventState(SDL_KEYUP, SDL_IGNORE); SDL_EventState(SDL_JOYBUTTONDOWN, SDL_IGNORE); SDL_EventState(SDL_JOYBUTTONUP, SDL_IGNORE); SDL_EventState(SDL_JOYAXISMOTION, SDL_IGNORE); SDL_EventState(SDL_JOYBALLMOTION, SDL_IGNORE); SDL_EventState(SDL_JOYHATMOTION, SDL_IGNORE); SDL_EventState(SDL_VIDEORESIZE, SDL_IGNORE); return 0; } void sdl_put(unsigned char *image, int width, int height) { SDL_Event event; if (screen && overlay) { SDL_Rect rect; float aspect_ratio = (float)width / height; int pic_width, pic_height; if (width != cur_width || height != cur_height) sdl_video_open(width, height); if (SDL_MUSTLOCK(screen)) if (SDL_LockSurface(screen) < 0) return; SDL_LockYUVOverlay(overlay); memcpy(overlay->pixels[0], image, width * height); memcpy(overlay->pixels[2], image + (width * height), (width * height / 4)); memcpy(overlay->pixels[1], image + (width * height * 5 / 4), (width * height / 4)); SDL_UnlockYUVOverlay(overlay); if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); pic_height = screen->h; pic_width = pic_height * aspect_ratio; if (pic_width > screen->w) { pic_width = screen->w; pic_height = pic_width / aspect_ratio; } rect.x = (screen->w - pic_width) / 2; rect.y = (screen->h - pic_height) / 2; rect.w = pic_width; rect.h = pic_height; if (SDL_DisplayYUVOverlay(overlay, &rect)) MOTION_LOG(ERR, TYPE_ALL, SHOW_ERRNO, "%s: SDL_DisplayYUVOverlay: %s", SDL_GetError()); if (SDL_PollEvent(&event)) { if ((event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE))) sdl_stop(); else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_f) { is_full_screen = !is_full_screen; sdl_video_open(width, height); } else if (event.type == SDL_VIDEORESIZE) screen = SDL_SetVideoMode(event.resize.w, event.resize.h, screen->format->BitsPerPixel, screen->flags); } } } void sdl_stop(void) { if (screen) { MOTION_LOG(ERR, TYPE_ALL, NO_ERRNO, "%s: SDL quit"); SDL_ShowCursor(SDL_ENABLE); if (overlay) { SDL_FreeYUVOverlay(overlay); overlay = NULL; } SDL_Quit(); screen = NULL; } } motion-release-4.0/sdl.h000066400000000000000000000006261300074623700152410ustar00rootroot00000000000000/* sdl.h * * Include file for sdl.c * Copyright 2009 by Peter Holik (peter@holik.at) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. */ #ifndef _INCLUDE_SDL_H #define _INCLUDE_SDL_H #include "motion.h" int sdl_start(int width, int height); void sdl_put(unsigned char *image, int width, int height); void sdl_stop(void); #endif motion-release-4.0/stream.c000066400000000000000000001071051300074623700157450ustar00rootroot00000000000000/* * stream.c (based in webcam.c) * Streaming 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 "md5.h" #include "picture.h" #include #include #include #include #include #include #define STREAM_REALM "Motion Stream Security Access" #define KEEP_ALIVE_TIMEOUT 100 typedef void* (*auth_handler)(void*); struct auth_param { struct context *cnt; int sock; int sock_flags; int* thread_count; struct config *conf; }; pthread_mutex_t stream_auth_mutex; /** * set_sock_timeout * * Returns : 0 or 1 on timeout */ static int set_sock_timeout(int sock, int sec) { struct timeval tv; tv.tv_sec = sec; tv.tv_usec = 0; if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*) &tv, sizeof(tv))) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: set socket timeout failed"); return 1; } return 0; } /** * read_http_request * * * Returns : 1 on success or 0 if any error happens */ static int read_http_request(int sock, char* buffer, int buflen, char* uri, int uri_len) { int nread = 0; int ret,readb = 1; char method[10] = {'\0'}; char url[512] = {'\0'}; char protocol[10] = {'\0'}; 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\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 *timeout_response_template_raw = "HTTP/1.0 408 Request Timeout\r\n" "Content-type: text/plain\r\n\r\n" "Request Timeout\n"; buffer[0] = '\0'; while ((strstr(buffer, "\r\n\r\n") == NULL) && (readb != 0) && (nread < buflen)) { readb = read(sock, buffer+nread, buflen - nread); if (readb == -1) { nread = -1; break; } nread += readb; if (nread > buflen) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: motion-stream 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) { if(errno == EAGAIN) { // Timeout ret = write(sock, timeout_response_template_raw, strlen(timeout_response_template_raw)); return 0; } MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: motion-stream READ give up!"); return 0; } ret = sscanf(buffer, "%9s %511s %9s", method, url, protocol); if (ret != 3) { ret = write(sock, bad_request_response_raw, sizeof(bad_request_response_raw)); return 0; } /* Check Protocol */ if (strcmp(protocol, "HTTP/1.0") && strcmp (protocol, "HTTP/1.1")) { /* We don't understand this protocol. Report a bad response. */ ret = write(sock, bad_request_response_raw, sizeof(bad_request_response_raw)); return 0; } if (strcmp(method, "GET")) { /* * This server only implements the GET method. If client * uses other method, report the failure. */ char response[1024]; snprintf(response, sizeof(response), bad_method_response_template_raw, method); ret = write(sock, response, strlen (response)); return 0; } if(uri) strncpy(uri, url, uri_len); return 1; } static void stream_add_client(struct stream *list, int sc); /** * handle_basic_auth * * */ static void* handle_basic_auth(void* param) { struct auth_param *p = (struct auth_param*)param; char buffer[1024] = {'\0'}; ssize_t length = 1023; char *auth, *h, *authentication; static const char *request_auth_response_template= "HTTP/1.0 401 Authorization Required\r\n" "Server: Motion/"VERSION"\r\n" "Max-Age: 0\r\n" "Expires: 0\r\n" "Cache-Control: no-cache, private\r\n" "Pragma: no-cache\r\n" "WWW-Authenticate: Basic realm=\""STREAM_REALM"\"\r\n\r\n"; #ifdef HAVE_PTHREAD_SETNAME_NP pthread_setname_np(pthread_self(), "handle_basic_auth"); #endif pthread_mutex_lock(&stream_auth_mutex); p->thread_count++; pthread_mutex_unlock(&stream_auth_mutex); if (!read_http_request(p->sock,buffer, length, NULL, 0)) goto Invalid_Request; auth = strstr(buffer, "Authorization: Basic"); if (!auth) goto Error; auth += sizeof("Authorization: Basic"); h = strstr(auth, "\r\n"); if(!h) goto Error; *h='\0'; if (p->conf->stream_authentication != NULL) { char *userpass = NULL; size_t auth_size = strlen(p->conf->stream_authentication); authentication = 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, p->conf->stream_authentication); base64_encode(userpass, authentication, auth_size); free(userpass); if (strcmp(auth, authentication)) { free(authentication); goto Error; } free(authentication); } // OK - Access /* Set socket to non blocking */ if (fcntl(p->sock, F_SETFL, p->sock_flags) < 0) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: fcntl"); goto Error; } /* Lock the mutex */ pthread_mutex_lock(&stream_auth_mutex); stream_add_client(&p->cnt->stream, p->sock); p->cnt->stream_count++; p->thread_count--; /* Unlock the mutex */ pthread_mutex_unlock(&stream_auth_mutex); free(p); pthread_exit(NULL); Error: if (write(p->sock, request_auth_response_template, strlen (request_auth_response_template)) < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write failure 1:handle_basic_auth"); Invalid_Request: close(p->sock); pthread_mutex_lock(&stream_auth_mutex); p->thread_count--; pthread_mutex_unlock(&stream_auth_mutex); free(p); pthread_exit(NULL); } #define HASHLEN 16 typedef char HASH[HASHLEN]; #define HASHHEXLEN 32 typedef char HASHHEX[HASHHEXLEN+1]; #define IN #define OUT /** * CvtHex * Calculates H(A1) as per HTTP Digest spec -- taken from RFC 2617. */ static void CvtHex(IN HASH Bin, OUT HASHHEX Hex) { unsigned short i; unsigned char j; for (i = 0; i < HASHLEN; i++) { j = (Bin[i] >> 4) & 0xf; if (j <= 9) Hex[i*2] = (j + '0'); else Hex[i*2] = (j + 'a' - 10); j = Bin[i] & 0xf; if (j <= 9) Hex[i*2+1] = (j + '0'); else Hex[i*2+1] = (j + 'a' - 10); }; Hex[HASHHEXLEN] = '\0'; }; /** * DigestCalcHA1 * Calculates H(A1) as per spec. */ static void DigestCalcHA1( IN char * pszAlg, IN char * pszUserName, IN char * pszRealm, IN char * pszPassword, IN char * pszNonce, IN char * pszCNonce, OUT HASHHEX SessionKey ) { MD5_CTX Md5Ctx; HASH HA1; MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, (unsigned char *)pszUserName, strlen(pszUserName)); MD5Update(&Md5Ctx, (unsigned char *)":", 1); MD5Update(&Md5Ctx, (unsigned char *)pszRealm, strlen(pszRealm)); MD5Update(&Md5Ctx, (unsigned char *)":", 1); MD5Update(&Md5Ctx, (unsigned char *)pszPassword, strlen(pszPassword)); MD5Final((unsigned char *)HA1, &Md5Ctx); if (strcmp(pszAlg, "md5-sess") == 0) { MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, (unsigned char *)HA1, HASHLEN); MD5Update(&Md5Ctx, (unsigned char *)":", 1); MD5Update(&Md5Ctx, (unsigned char *)pszNonce, strlen(pszNonce)); MD5Update(&Md5Ctx, (unsigned char *)":", 1); MD5Update(&Md5Ctx, (unsigned char *)pszCNonce, strlen(pszCNonce)); MD5Final((unsigned char *)HA1, &Md5Ctx); }; CvtHex(HA1, SessionKey); }; /** * DigestCalcResponse * Calculates request-digest/response-digest as per HTTP Digest spec. */ static void DigestCalcResponse( IN HASHHEX HA1, /* H(A1) */ IN char * pszNonce, /* nonce from server */ IN char * pszNonceCount, /* 8 hex digits */ IN char * pszCNonce, /* client nonce */ IN char * pszQop, /* qop-value: "", "auth", "auth-int" */ IN char * pszMethod, /* method from the request */ IN char * pszDigestUri, /* requested URL */ IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */ OUT HASHHEX Response /* request-digest or response-digest */ ) { MD5_CTX Md5Ctx; HASH HA2; HASH RespHash; HASHHEX HA2Hex; // Calculate H(A2) MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, (unsigned char *)pszMethod, strlen(pszMethod)); MD5Update(&Md5Ctx, (unsigned char *)":", 1); MD5Update(&Md5Ctx, (unsigned char *)pszDigestUri, strlen(pszDigestUri)); if (strcmp(pszQop, "auth-int") == 0) { MD5Update(&Md5Ctx, (unsigned char *)":", 1); MD5Update(&Md5Ctx, (unsigned char *)HEntity, HASHHEXLEN); } MD5Final((unsigned char *)HA2, &Md5Ctx); CvtHex(HA2, HA2Hex); // Calculate response MD5Init(&Md5Ctx); MD5Update(&Md5Ctx, (unsigned char *)HA1, HASHHEXLEN); MD5Update(&Md5Ctx, (unsigned char *)":", 1); MD5Update(&Md5Ctx, (unsigned char *)pszNonce, strlen(pszNonce)); MD5Update(&Md5Ctx, (unsigned char *)":", 1); if (*pszQop) { MD5Update(&Md5Ctx, (unsigned char *)pszNonceCount, strlen(pszNonceCount)); MD5Update(&Md5Ctx, (unsigned char *)":", 1); MD5Update(&Md5Ctx, (unsigned char *)pszCNonce, strlen(pszCNonce)); MD5Update(&Md5Ctx, (unsigned char *)":", 1); MD5Update(&Md5Ctx, (unsigned char *)pszQop, strlen(pszQop)); MD5Update(&Md5Ctx, (unsigned char *)":", 1); } MD5Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN); MD5Final((unsigned char *)RespHash, &Md5Ctx); CvtHex(RespHash, Response); }; /** * handle_md5_digest * * */ static void* handle_md5_digest(void* param) { struct auth_param *p = (struct auth_param*)param; char buffer[1024] = {'\0'}; ssize_t length = 1023; char *auth, *h, *username, *realm, *uri, *nonce, *response; int username_len, realm_len, uri_len, nonce_len, response_len; #define SERVER_NONCE_LEN 17 char server_nonce[SERVER_NONCE_LEN]; #define SERVER_URI_LEN 512 char server_uri[SERVER_URI_LEN]; char* server_user = NULL, *server_pass = NULL; unsigned int rand1,rand2; HASHHEX HA1; HASHHEX HA2 = ""; HASHHEX server_response; static const char *request_auth_response_template= "HTTP/1.0 401 Authorization Required\r\n" "Server: Motion/"VERSION"\r\n" "Max-Age: 0\r\n" "Expires: 0\r\n" "Cache-Control: no-cache, private\r\n" "Pragma: no-cache\r\n" "WWW-Authenticate: Digest"; static const char *auth_failed_html_template= "\n" "\n" "401 Authorization Required\n" "\n" "

Authorization Required

\n" "

This server could not verify that you are authorized to access the document " "requested. Either you supplied the wrong credentials (e.g., bad password), " "or your browser doesn't understand how to supply the credentials required.

\n" "\n" "\n"; static const char *internal_error_template= "HTTP/1.0 500 Internal Server Error\r\n" "Server: Motion/"VERSION"\r\n" "Content-Type: text/html\r\n" "Connection: Close\r\n\r\n" "\n" "\n" "500 Internal Server Error\n" "\n" "

500 Internal Server Error

\n" "\n" "\n"; #ifdef HAVE_PTHREAD_SETNAME_NP pthread_setname_np(pthread_self(), "handle_md5_digest"); #endif pthread_mutex_lock(&stream_auth_mutex); p->thread_count++; pthread_mutex_unlock(&stream_auth_mutex); set_sock_timeout(p->sock, KEEP_ALIVE_TIMEOUT); srand(time(NULL)); rand1 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0)); rand2 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0)); snprintf(server_nonce, SERVER_NONCE_LEN, "%08x%08x", rand1, rand2); if (!p->conf->stream_authentication) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: Error no authentication data"); goto InternalError; } h = strstr(p->conf->stream_authentication, ":"); if (!h) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: Error no authentication data (no ':' found)"); goto InternalError; } server_user = (char*)malloc((h - p->conf->stream_authentication) + 1); server_pass = (char*)malloc(strlen(h) + 1); if (!server_user || !server_pass) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: Error malloc failed"); goto InternalError; } strncpy(server_user, p->conf->stream_authentication, h-p->conf->stream_authentication); server_user[h - p->conf->stream_authentication] = '\0'; strncpy(server_pass, h + 1, strlen(h + 1)); server_pass[strlen(h + 1)] = '\0'; while(1) { if(!read_http_request(p->sock, buffer, length, server_uri, SERVER_URI_LEN - 1)) goto Invalid_Request; auth = strstr(buffer, "Authorization: Digest"); if(!auth) goto Error; auth += sizeof("Authorization: Digest"); h = strstr(auth, "\r\n"); if (!h) goto Error; *h = '\0'; // Username h=strstr(auth, "username=\""); if (!h) goto Error; username = h + 10; h = strstr(username + 1, "\""); if (!h) goto Error; username_len = h - username; // Realm h = strstr(auth, "realm=\""); if (!h) goto Error; realm = h + 7; h = strstr(realm + 1, "\""); if (!h) goto Error; realm_len = h - realm; // URI h = strstr(auth, "uri=\""); if (!h) goto Error; uri = h + 5; h = strstr(uri + 1, "\""); if (!h) goto Error; uri_len = h - uri; // Nonce h = strstr(auth, "nonce=\""); if (!h) goto Error; nonce = h + 7; h = strstr(nonce + 1, "\""); if (!h) goto Error; nonce_len = h - nonce; // Response h = strstr(auth, "response=\""); if (!h) goto Error; response = h + 10; h = strstr(response + 1, "\""); if (!h) goto Error; response_len = h - response; username[username_len] = '\0'; realm[realm_len] = '\0'; uri[uri_len] = '\0'; nonce[nonce_len] = '\0'; response[response_len] = '\0'; DigestCalcHA1((char*)"md5", server_user, (char*)STREAM_REALM, server_pass, (char*)server_nonce, (char*)NULL, HA1); DigestCalcResponse(HA1, server_nonce, NULL, NULL, (char*)"", (char*)"GET", server_uri, HA2, server_response); if (strcmp(server_response, response) == 0) break; Error: rand1 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0)); rand2 = (unsigned int)(42000000.0 * rand() / (RAND_MAX + 1.0)); snprintf(server_nonce, SERVER_NONCE_LEN, "%08x%08x", rand1, rand2); snprintf(buffer, length, "%s realm=\""STREAM_REALM"\", nonce=\"%s\"\r\n" "Content-Type: text/html\r\n" "Keep-Alive: timeout=%i\r\n" "Connection: keep-alive\r\n" "Content-Length: %Zu\r\n\r\n", request_auth_response_template, server_nonce, KEEP_ALIVE_TIMEOUT, strlen(auth_failed_html_template)); if (write(p->sock, buffer, strlen(buffer)) < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write failure 1:handle_md5_digest"); if (write(p->sock, auth_failed_html_template, strlen(auth_failed_html_template)) < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write failure 2:handle_md5_digest"); } // OK - Access /* Set socket to non blocking */ if (fcntl(p->sock, F_SETFL, p->sock_flags) < 0) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: fcntl"); goto Error; } free(server_user); free(server_pass); /* Lock the mutex */ pthread_mutex_lock(&stream_auth_mutex); stream_add_client(&p->cnt->stream, p->sock); p->cnt->stream_count++; p->thread_count--; /* Unlock the mutex */ pthread_mutex_unlock(&stream_auth_mutex); free(p); pthread_exit(NULL); InternalError: free(server_user); free(server_pass); if (write(p->sock, internal_error_template, strlen(internal_error_template)) < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write failure 3:handle_md5_digest"); Invalid_Request: close(p->sock); pthread_mutex_lock(&stream_auth_mutex); p->thread_count--; pthread_mutex_unlock(&stream_auth_mutex); free(p); pthread_exit(NULL); } /** * do_client_auth * * */ static void do_client_auth(struct context *cnt, int sc) { pthread_t thread_id; pthread_attr_t attr; auth_handler handle_func; struct auth_param* handle_param = NULL; int flags; static int first_call = 0; static int thread_count = 0; if(first_call == 0) { first_call = 1; /* Initialize the mutex */ pthread_mutex_init(&stream_auth_mutex, NULL); } switch(cnt->conf.stream_auth_method) { case 1: // Basic handle_func = handle_basic_auth; break; case 2: // MD5 Digest handle_func = handle_md5_digest; break; default: MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: Error unknown stream authentication method"); goto Error; break; } handle_param = mymalloc(sizeof(struct auth_param)); handle_param->cnt = cnt; handle_param->sock = sc; handle_param->conf = &cnt->conf; handle_param->thread_count = &thread_count; /* Set socket to blocking */ if ((flags = fcntl(sc, F_GETFL, 0)) < 0) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: fcntl"); goto Error; } handle_param->sock_flags = flags; if (fcntl(sc, F_SETFL, flags & (~O_NONBLOCK)) < 0) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: fcntl"); goto Error; } if (thread_count >= DEF_MAXSTREAMS) goto Error; if (pthread_attr_init(&attr)) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: Error pthread_attr_init"); goto Error; } if (pthread_create(&thread_id, &attr, handle_func, handle_param)) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: Error pthread_create"); goto Error; } pthread_detach(thread_id); if (pthread_attr_destroy(&attr)) MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: Error pthread_attr_destroy"); return; Error: close(sc); free(handle_param); } /** * http_bindsock * Sets up a TCP/IP socket for incoming requests. It is called only during * initialisation of Motion from the function stream_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. * * Returns: socket descriptor or -1 if any error happens */ int http_bindsock(int port, int local, int ipv6_enabled) { int sd = socket(ipv6_enabled?AF_INET6:AF_INET, SOCK_STREAM, 0); int yes = 1, no = 0; setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); if (ipv6_enabled) setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no)); const char *addr_str; struct sockaddr_storage sin; bzero(&sin, sizeof(struct sockaddr_storage)); sin.ss_family = ipv6_enabled?AF_INET6:AF_INET; if (ipv6_enabled) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&sin; sin6->sin6_family = AF_INET6; sin6->sin6_port = htons(port); if(local) { addr_str = "::1"; sin6->sin6_addr = in6addr_loopback; } else { addr_str = "any IPv4/IPv6 address"; sin6->sin6_addr = in6addr_any; } } else { struct sockaddr_in *sin4 = (struct sockaddr_in*)&sin; sin4->sin_family = AF_INET; sin4->sin_port = htons(port); if(local) { addr_str = "127.0.0.1"; sin4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); } else { addr_str = "any IPv4 address"; sin4->sin_addr.s_addr = htonl(INADDR_ANY); } } if (bind(sd, (struct sockaddr*)&sin, sizeof(sin)) != 0) { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: error binding on %s port %d", addr_str, port); close(sd); return -1; } if (listen(sd, DEF_MAXWEBQUEUE) != 0) { MOTION_LOG(CRT, TYPE_STREAM, SHOW_ERRNO, "%s: error listening"); close(sd); return -1; } MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: listening on %s port %d", addr_str, port); return sd; } /** * http_acceptsock * * * Returns: socket descriptor or -1 if any error happens. */ static int http_acceptsock(int sl) { int sc; struct sockaddr_storage addr; socklen_t addr_len = sizeof(addr); sc = accept(sl, (struct sockaddr*)&addr, &addr_len); if (sc < 0) { MOTION_LOG(CRT, TYPE_STREAM, SHOW_ERRNO, "%s: motion-stream accept()"); return -1; } unsigned long i = 1; ioctl(sc, FIONBIO, &i); return sc; } /** * stream_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 stream_flush(struct stream *list, int *stream_count, int lim) { int written; /* The number of bytes actually written. */ struct stream *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 stream 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) */ } /** * stream_tmpbuffer * Routine to create a new "tmpbuffer", which is a common * object used by all clients connected to a single camera. * * Returns: new allocated stream_buffer. */ static struct stream_buffer *stream_tmpbuffer(int size) { struct stream_buffer *tmpbuffer = mymalloc(sizeof(struct stream_buffer)); tmpbuffer->ref = 0; tmpbuffer->ptr = mymalloc(size); return tmpbuffer; } /** * stream_add_client * * */ static void stream_add_client(struct stream *list, int sc) { struct stream *new = mymalloc(sizeof(struct stream)); 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 stream)); new->socket = sc; if ((new->tmpbuffer = stream_tmpbuffer(sizeof(header))) == NULL) { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: Error creating tmpbuffer in stream_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; } /** * stream_add_write * * */ static void stream_add_write(struct stream *list, struct stream_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); } } /** * stream_check_write * We walk through the chain of stream 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. * * Returns: */ static int stream_check_write(struct stream *list) { while (list->next) { list = list->next; if (list->tmpbuffer == NULL) return 1; } return 0; } /** * stream_init * This function is called from motion.c for each motion thread starting up. * The function setup the incoming tcp socket that the clients connect to. * The function returns an integer representing the socket. * * Returns: stream socket descriptor. */ int stream_init(struct context *cnt) { cnt->stream.socket = http_bindsock(cnt->conf.stream_port, cnt->conf.stream_localhost, cnt->conf.ipv6_enabled); cnt->stream.next = NULL; cnt->stream.prev = NULL; return cnt->stream.socket; } /** * stream_stop * This function is called from the motion_loop when it ends * and motion is terminated or restarted. */ void stream_stop(struct context *cnt) { struct stream *list; struct stream *next = cnt->stream.next; MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: Closing motion-stream listen socket" " & active motion-stream sockets"); close(cnt->stream.socket); cnt->stream.socket = -1; while (next) { list = next; next = list->next; if (list->tmpbuffer) { free(list->tmpbuffer->ptr); free(list->tmpbuffer); } close(list->socket); free(list); } MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: Closed motion-stream listen socket" " & active motion-stream sockets"); } /* * stream_put * Is the starting point of the stream loop. It is called from * the motion_loop with the argument 'image' pointing to the latest frame. * If config option 'stream_motion' is 'on' this function is called once * per second (frame 0) and when Motion is detected excl pre_capture. * If config option 'stream_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 stream_flush() * function. */ void stream_put(struct context *cnt, unsigned char *image) { struct timeval timeout; struct stream_buffer *tmpbuffer; fd_set fdread; int sl = cnt->stream.socket; int sc; /* Tthe 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->stream.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 stream struct and * add this to the end of the chain of stream 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); if (cnt->conf.stream_auth_method == 0) { stream_add_client(&cnt->stream, sc); cnt->stream_count++; } else { do_client_auth(cnt, sc); } } /* Lock the mutex */ if (cnt->conf.stream_auth_method != 0) pthread_mutex_lock(&stream_auth_mutex); /* Call flush to send any previous partial-sends which are waiting. */ stream_flush(&cnt->stream, &cnt->stream_count, cnt->conf.stream_limit); /* Check if any clients have available buffers. */ if (stream_check_write(&cnt->stream)) { /* * 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 = stream_tmpbuffer(cnt->imgs.size); /* Check if allocation was 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.stream_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. */ stream_add_write(&cnt->stream, tmpbuffer, cnt->conf.stream_maxrate); } else { MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: 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. */ stream_flush(&cnt->stream, &cnt->stream_count, cnt->conf.stream_limit); /* Unlock the mutex */ if (cnt->conf.stream_auth_method != 0) pthread_mutex_unlock(&stream_auth_mutex); return; } motion-release-4.0/stream.h000066400000000000000000000025341300074623700157520ustar00rootroot00000000000000/* * stream.h * * Include file for stream.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_STREAM_H_ #define _INCLUDE_STREAM_H_ struct stream_buffer { unsigned char *ptr; int ref; long size; }; struct stream { int socket; FILE *fwrite; struct stream_buffer *tmpbuffer; long filepos; int nr; unsigned long int last; struct stream *prev; struct stream *next; }; int stream_init(struct context *); void stream_put(struct context *, unsigned char *); void stream_stop(struct context *); #endif /* _INCLUDE_STREAM_H_ */ motion-release-4.0/track.c000066400000000000000000001230141300074623700155530ustar00rootroot00000000000000/* track.c * * Experimental motion tracking. * * Copyright 2000, Jeroen Vreeken * This program is published under the GNU Public license */ #ifdef MOTION_V4L2 #include #endif /* MOTION_V4L2 */ #include #include "motion.h" #if (defined(HAVE_LINUX_VIDEODEV_H) || defined(HAVE_SYS_VIDEOIO_H)) && (!defined(WITHOUT_V4L)) #include "pwc-ioctl.h" #endif struct trackoptions track_template = { dev: -1, /* dev open */ port: NULL, /* char *port */ motorx: 0, /* int motorx */ motory: 0, /* int motory */ maxx: 0, /* int maxx; */ maxy: 0, /* int maxy; */ minx: 0, /* int minx; */ miny: 0, /* int miny; */ homex: 128, /* int homex; */ homey: 128, /* int homey; */ motorx_reverse: 0, /* int reversed x servo; */ motory_reverse: 0, /* int reversed y servo; */ 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, /* UVC step angle in degrees X-axis that camera moves during auto tracking */ step_angle_y: 10, /* UVC 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 unsigned int servo_position(struct context *cnt, unsigned int motor); static unsigned int servo_center(struct context *cnt, int xoff, int yoff ATTRIBUTE_UNUSED); static unsigned int stepper_center(struct context *cnt, int xoff, int yoff ATTRIBUTE_UNUSED); static unsigned int iomojo_center(struct context *cnt, int xoff, int yoff); static unsigned int stepper_move(struct context *cnt, struct coord *cent, struct images *imgs); static unsigned int servo_move(struct context *cnt, struct coord *cent, struct images *imgs, unsigned int manual); static unsigned int iomojo_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs); #if defined(HAVE_LINUX_VIDEODEV_H) && (!defined(WITHOUT_V4L)) static unsigned int lqos_center(struct context *cnt, int dev, int xoff, int yoff); static unsigned int lqos_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, unsigned int manual); #ifdef MOTION_V4L2 static unsigned int uvc_center(struct context *cnt, int dev, int xoff, int yoff); static unsigned int uvc_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, unsigned int manual); #endif /* MOTION_V4L2 */ #endif /* WITHOUT_V4L */ /* Add a call to your functions here: */ unsigned int track_center(struct context *cnt, int dev ATTRIBUTE_UNUSED, unsigned int manual, int xoff, int yoff) { if (!manual && !cnt->track.active) return 0; if (cnt->track.type == TRACK_TYPE_STEPPER) { unsigned int ret; ret = stepper_center(cnt, xoff, yoff); if (!ret) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: internal error"); return 0; } else return ret; } else if (cnt->track.type == TRACK_TYPE_SERVO) { return servo_center(cnt, xoff, yoff); } #if defined(HAVE_LINUX_VIDEODEV_H) && (!defined(WITHOUT_V4L)) else if (cnt->track.type == TRACK_TYPE_PWC) return lqos_center(cnt, dev, xoff, yoff); #ifdef MOTION_V4L2 else if (cnt->track.type == TRACK_TYPE_UVC) return uvc_center(cnt, dev, xoff, yoff); #endif /* MOTION_V4L2 */ #endif /* WITHOUT_V4L */ 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(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: internal error, %hu is not a known track-type", cnt->track.type); return 0; } /* Add a call to your functions here: */ unsigned int track_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, unsigned int manual) { if (!manual && !cnt->track.active) return 0; if (cnt->track.type == TRACK_TYPE_STEPPER) return stepper_move(cnt, cent, imgs); else if (cnt->track.type == TRACK_TYPE_SERVO) return servo_move(cnt, cent, imgs, manual); #if defined(HAVE_LINUX_VIDEODEV_H) && (!defined(WITHOUT_V4L)) else if (cnt->track.type == TRACK_TYPE_PWC) return lqos_move(cnt, dev, cent, imgs, manual); #ifdef MOTION_V4L2 else if (cnt->track.type == TRACK_TYPE_UVC) return uvc_move(cnt, dev, cent, imgs, manual); #endif /* MOTION_V4L2 */ #endif /* WITHOUT_V4L */ 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(WRN, TYPE_TRACK, SHOW_ERRNO, "%s: internal error, %hu is not a known track-type", cnt->track.type); return 0; } /****************************************************************************** Stepper motor on serial port http://www.lavrsen.dk/twiki/bin/view/Motion/MotionTracking http://www.lavrsen.dk/twiki/bin/view/Motion/MotionTrackerAPI ******************************************************************************/ static unsigned int stepper_command(struct context *cnt, unsigned int motor, unsigned int command, unsigned int data) { char buffer[3]; time_t timeout = time(NULL); buffer[0] = motor; buffer[1] = command; buffer[2] = data; if (write(cnt->track.dev, buffer, 3) != 3) { MOTION_LOG(NTC, TYPE_TRACK, SHOW_ERRNO, "%s: port %s dev fd %i, motor %hu command %hu data %hu", cnt->track.port, cnt->track.dev, motor, command, data); return 0; } while (read(cnt->track.dev, buffer, 1) != 1 && time(NULL) < timeout + 1); if (time(NULL) >= timeout + 2) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Status byte timeout!"); return 0; } return buffer[0]; } static unsigned int stepper_status(struct context *cnt, unsigned int motor) { return stepper_command(cnt, motor, STEPPER_COMMAND_STATUS, 0); } static unsigned int stepper_center(struct context *cnt, int x_offset, int y_offset) { struct termios adtio; if (cnt->track.dev < 0) { MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Try to open serial device %s", cnt->track.port); if ((cnt->track.dev = open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Unable to open serial device %s", cnt->track.port); return 0; } 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(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Unable to initialize serial device %s", cnt->track.port); cnt->track.dev = -1; return 0; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Opened serial device %s and initialize, fd %i", cnt->track.port, cnt->track.dev); } /* x-axis */ 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); /* y-axis */ stepper_command(cnt, cnt->track.motory, STEPPER_COMMAND_SPEED, cnt->track.speed); stepper_command(cnt, cnt->track.motory, STEPPER_COMMAND_UP_N, cnt->track.maxy); while (stepper_status(cnt, cnt->track.motory) & STEPPER_STATUS_UP) stepper_command(cnt, cnt->track.motory, STEPPER_COMMAND_DOWN_N, cnt->track.maxy / 2 + y_offset * cnt->track.stepsize); while (stepper_status(cnt, cnt->track.motory) & STEPPER_STATUS_DOWN); return cnt->track.move_wait; } static unsigned int stepper_move(struct context *cnt, struct coord *cent, struct images *imgs) { unsigned int command = 0, data = 0; if (cnt->track.dev < 0) { MOTION_LOG(WRN, TYPE_TRACK, NO_ERRNO, "%s: No device %s started yet , trying stepper_center()", cnt->track.port); if (!stepper_center(cnt, 0, 0)) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: failed to initialize stepper device on %s , fd [%i].", cnt->track.port, cnt->track.dev); return 0; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: succeed , device started %s , fd [%i]", cnt->track.port, cnt->track.dev); } /* x-axis */ if (cent->x < imgs->width / 2) { command = STEPPER_COMMAND_LEFT_N; data = imgs->width / 2 - cent->x; } if (cent->x > imgs->width / 2) { command = STEPPER_COMMAND_RIGHT_N; data = cent->x - imgs->width / 2; } data = data * cnt->track.stepsize / imgs->width; if (data) stepper_command(cnt, cnt->track.motorx, command, data); /* y-axis */ if (cent->y < imgs->height / 2) { command = STEPPER_COMMAND_UP_N; data = imgs->height / 2 - cent->y; } if (cent->y > imgs->height / 2) { command = STEPPER_COMMAND_DOWN_N; data = cent->y - imgs->height / 2; } data = data * cnt->track.stepsize / imgs->height; if (data) stepper_command(cnt, cnt->track.motory, command, data); return cnt->track.move_wait; } /****************************************************************************** * Servo motor on serial port * http://www.lavrsen.dk/twiki/bin/view/Motion/MotionTracking * http://www.lavrsen.dk/twiki/bin/view/Motion/MotionTrackerServoAPI ******************************************************************************/ static int servo_open(struct context *cnt) { struct termios adtio; if ((cnt->track.dev = open(cnt->track.port, O_RDWR | O_NOCTTY)) < 0) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Unable to open serial device %s", cnt->track.port); return 0; } bzero (&adtio, sizeof(adtio)); adtio.c_cflag= SERVO_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(ERR, TYPE_TRACK, NO_ERRNO, "%s: Unable to initialize serial device %s", cnt->track.port); cnt->track.dev = -1; return 0; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Opened serial device %s and initialize, fd %i", cnt->track.port, cnt->track.dev); return 1; } static unsigned int servo_command(struct context *cnt, unsigned int motor, unsigned int command, unsigned int data) { unsigned char buffer[3]; time_t timeout = time(NULL); buffer[0] = motor; buffer[1] = command; buffer[2] = data; MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: SENDS port %s dev fd %i, motor %hu command %hu data %hu", cnt->track.port, cnt->track.dev, buffer[0], buffer[1], buffer[2]); if (write(cnt->track.dev, buffer, 3) != 3) { MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: port %s dev fd %i, motor %hu command %hu data %hu", cnt->track.port, cnt->track.dev, motor, command, data); return 0; } while (read(cnt->track.dev, buffer, 1) != 1 && time(NULL) < timeout + 1); if (time(NULL) >= timeout + 2) { MOTION_LOG(ERR, TYPE_TRACK, NO_ERRNO, "%s: Status byte timeout!"); return 0; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Command return %d", buffer[0]); return buffer[0]; } static unsigned int servo_position(struct context *cnt, unsigned int motor) { unsigned int ret = 0; ret = servo_command(cnt, motor, SERVO_COMMAND_POSITION, 0); return ret; } /** * servo_move * Does relative movements to current position. * */ static unsigned int servo_move(struct context *cnt, struct coord *cent, struct images *imgs, unsigned int manual) { unsigned int command = 0; unsigned int data = 0; unsigned int position; /* If device is not open yet , open and center */ if (cnt->track.dev < 0) { if (!servo_center(cnt, 0, 0)) { MOTION_LOG(ERR, TYPE_TRACK, NO_ERRNO, "%s: Problem opening servo!"); return 0; } } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: cent->x %d, cent->y %d, reversex %d," "reversey %d manual %d", cent->x , cent->y, cnt->track.motorx_reverse, cnt->track.motory_reverse, manual); if (manual) { int offset; if (cent->x) { position = servo_position(cnt, cnt->track.motorx); offset = cent->x * cnt->track.stepsize; if ((cnt->track.motorx_reverse && (offset > 0)) || (!cnt->track.motorx_reverse && (offset < 0))) command = SERVO_COMMAND_LEFT_N; else command = SERVO_COMMAND_RIGHT_N; data = abs(offset); if ((data + position > (unsigned)cnt->track.maxx) || (position - offset < (unsigned)cnt->track.minx)) { MOTION_LOG(ERR, TYPE_TRACK, NO_ERRNO, "%s: x %d value out of range! (%d - %d)", data, cnt->track.minx, cnt->track.maxx); return 0; } /* Set Speed , TODO : it should be done only when speed changes */ servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_SPEED, cnt->track.speed); servo_command(cnt, cnt->track.motorx, command, data); } if (cent->y) { position = servo_position(cnt, cnt->track.motory); offset = cent->y * cnt->track.stepsize; if ((cnt->track.motory_reverse && (offset > 0)) || (!cnt->track.motory_reverse && (offset < 0))) command = SERVO_COMMAND_UP_N; else command = SERVO_COMMAND_DOWN_N; data = abs(offset); if ((data + position > (unsigned)cnt->track.maxy) || (position - offset < (unsigned)cnt->track.miny)) { MOTION_LOG(ERR, TYPE_TRACK, NO_ERRNO, "%s: y %d value out of range! (%d - %d)", data, cnt->track.miny, cnt->track.maxy); return 0; } /* Set Speed , TODO : it should be done only when speed changes */ servo_command(cnt, cnt->track.motory, SERVO_COMMAND_SPEED, cnt->track.speed); servo_command(cnt, cnt->track.motory, command, data); } } else { /***** x-axis *****/ /* Move left */ if (cent->x < imgs->width / 2) { if (cnt->track.motorx_reverse) command = SERVO_COMMAND_RIGHT_N; else command = SERVO_COMMAND_LEFT_N; data = imgs->width / 2 - cent->x; } /* Move right */ if (cent->x > imgs->width / 2) { if (cnt->track.motorx_reverse) command = SERVO_COMMAND_LEFT_N; else command = SERVO_COMMAND_RIGHT_N; data = cent->x - imgs->width / 2; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: X offset %d", data); data = data * cnt->track.stepsize / imgs->width; if (data && command) { // TODO: need to get position to avoid overflow limits position = servo_position(cnt, cnt->track.motorx); if ((position + data > (unsigned)cnt->track.maxx) || (position - data < (unsigned)cnt->track.minx)) { MOTION_LOG(ERR, TYPE_TRACK, NO_ERRNO, "%s: x %d value out of range! (%d - %d)", data, cnt->track.minx, cnt->track.maxx); return 0; } /* Set Speed , TODO : it should be done only when speed changes */ servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_SPEED, cnt->track.speed); servo_command(cnt, cnt->track.motorx, command, data); MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: X cent->x %d, cent->y %d, reversex %d," "reversey %d motorx %d data %d command %d", cent->x, cent->y, cnt->track.motorx_reverse, cnt->track.motory_reverse, cnt->track.motorx, data, command); } /***** y-axis *****/ /* Move down */ if (cent->y < imgs->height / 2) { if (cnt->track.motory_reverse) command = SERVO_COMMAND_UP_N; else command = SERVO_COMMAND_DOWN_N; data = imgs->height / 2 - cent->y; } /* Move up */ if (cent->y > imgs->height / 2) { if (cnt->track.motory_reverse) command = SERVO_COMMAND_DOWN_N; else command = SERVO_COMMAND_UP_N; data = cent->y - imgs->height / 2; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Y offset %d", data); data = data * cnt->track.stepsize / imgs->height; if (data && command) { // TODO: need to get position to avoid overflow limits position = servo_position(cnt, cnt->track.motory); if ((position + data > (unsigned)cnt->track.maxy) || (position - data < (unsigned)cnt->track.miny)) { MOTION_LOG(ERR, TYPE_TRACK, NO_ERRNO, "%s: y %d value out of range! (%d - %d)", data, cnt->track.miny, cnt->track.maxy); return 0; } /* Set Speed , TODO : it should be done only when speed changes */ servo_command(cnt, cnt->track.motory, SERVO_COMMAND_SPEED, cnt->track.speed); servo_command(cnt, cnt->track.motory, command, data); MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Y cent->x %d, cent->y %d, reversex %d," "reversey %d motory %d data %d command %d", cent->x, cent->y, cnt->track.motorx_reverse, cnt->track.motory_reverse, cnt->track.motory, command); } } return cnt->track.move_wait; } #if 0 static unsigned int servo_status(struct context *cnt, unsigned int motor) { return servo_command(cnt, motor, SERVO_COMMAND_STATUS, 0); } #endif /** * servo_center * Moves servo to home position. * Does absolute movements ( offsets relative to home position ). * * Note : Using Clockwise as a convention for right , left , up , down * so left minx , right maxx , down miny , up maxy * */ static unsigned int servo_center(struct context *cnt, int x_offset, int y_offset) { int x_offset_abs; int y_offset_abs; /* If device is not open yet */ if (cnt->track.dev < 0) { if (!servo_open(cnt)) { MOTION_LOG(ERR, TYPE_TRACK, NO_ERRNO, "%s: Problem opening servo!"); return 0; } } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: X-offset %d, Y-offset %d, x-position %d. y-position %d," "reversex %d, reversey %d , stepsize %d", x_offset, y_offset, cnt->track.homex + (x_offset * cnt->track.stepsize), cnt->track.homey + (y_offset * cnt->track.stepsize), cnt->track.motorx_reverse, cnt->track.motory_reverse, cnt->track.stepsize); /* x-axis */ if (cnt->track.motorx_reverse) x_offset_abs = (128 - cnt->track.homex) - (x_offset * cnt->track.stepsize) + 128; else x_offset_abs = cnt->track.homex + (x_offset * cnt->track.stepsize); if (x_offset_abs <= cnt->track.maxx && x_offset_abs >= cnt->track.minx) { /* Set Speed , TODO : it should be done only when speed changes */ servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_SPEED, cnt->track.speed); servo_command(cnt, cnt->track.motorx, SERVO_COMMAND_ABSOLUTE, x_offset_abs); } /* y-axis */ if (cnt->track.motory_reverse) y_offset_abs = (128 - cnt->track.homey) - (y_offset * cnt->track.stepsize) + 128; else y_offset_abs = cnt->track.homey + (y_offset * cnt->track.stepsize); if (y_offset_abs <= cnt->track.maxy && y_offset_abs >= cnt->track.minx) { /* Set Speed , TODO : it should be done only when speed changes */ servo_command(cnt, cnt->track.motory, SERVO_COMMAND_SPEED, cnt->track.speed); servo_command(cnt, cnt->track.motory, SERVO_COMMAND_ABSOLUTE, y_offset_abs); } return cnt->track.move_wait; } /****************************************************************************** Iomojo Smilecam on serial port ******************************************************************************/ static char iomojo_command(struct context *cnt, char *command, int len, unsigned 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(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Return byte timeout!"); return 0; } } /* range values ? */ return buffer[0]; } static void iomojo_setspeed(struct context *cnt, unsigned 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(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: 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 unsigned 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(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: 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(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: 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); } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: succeed"); return cnt->track.move_wait; } static unsigned 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)) 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 ******************************************************************************/ #if defined(HAVE_LINUX_VIDEODEV_H) && (!defined(WITHOUT_V4L)) static unsigned 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(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Failed to reset pwc camera to starting position! Reason"); return 0; } SLEEP(6, 0); if (ioctl(dev, VIDIOCPWCMPTGRANGE, &pmr) == -1) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: failed VIDIOCPWCMPTGRANGE"); return 0; } cnt->track.dev = dev; cnt->track.minmaxfound = 1; cnt->track.minx = pmr.pan_min; cnt->track.maxx = pmr.pan_max; cnt->track.miny = pmr.tilt_min; cnt->track.maxy = pmr.tilt_max; } if (ioctl(dev, VIDIOCPWCMPTGANGLE, &pma) == -1) MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: ioctl VIDIOCPWCMPTGANGLE"); pma.absolute = 1; if (x_angle * 100 < cnt->track.maxx && x_angle * 100 > cnt->track.minx) pma.pan = x_angle * 100; if (y_angle * 100 < cnt->track.maxy && y_angle * 100 > cnt->track.miny) pma.tilt = y_angle * 100; if (ioctl(dev, VIDIOCPWCMPTSANGLE, &pma) == -1) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Failed to pan/tilt pwc camera! Reason"); return 0; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: succeed"); return cnt->track.move_wait; } static unsigned int lqos_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, unsigned 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(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: failed VIDIOCPWCMPTGRANGE"); return 0; } cnt->track.minmaxfound = 1; cnt->track.minx = pmr.pan_min; cnt->track.maxx = pmr.pan_max; cnt->track.miny = pmr.tilt_min; cnt->track.maxy = pmr.tilt_max; } /* Get current camera position */ if (ioctl(dev, VIDIOCPWCMPTGANGLE, &pma) == -1) MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: 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.minx - pma.pan) > move_x_degrees) move_x_degrees = (cnt->track.minx - pma.pan); if (move_x_degrees > 0 && (cnt->track.maxx - pma.pan) < move_x_degrees) move_x_degrees = (cnt->track.maxx - pma.pan); if (move_y_degrees < 0 && (cnt->track.miny - pma.tilt) > move_y_degrees) move_y_degrees = (cnt->track.miny - pma.tilt); if (move_y_degrees > 0 && (cnt->track.maxy - pma.tilt) < move_y_degrees) move_y_degrees = (cnt->track.maxy - 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(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Failed to pan/tilt pwc camera! Reason"); return 0; } return cnt->track.move_wait; } /****************************************************************************** Logitech QuickCam Sphere camera tracking code by oBi Modify by Dirk Wesenberg(Munich) 30.03.07 - for new API in uvcvideo - add Trace-steps for investigation ******************************************************************************/ #ifdef MOTION_V4L2 static unsigned int uvc_center(struct context *cnt, int dev, int x_angle, int y_angle) { /* CALC ABSOLUTE MOVING : Act.Position +/- delta to request X and Y */ int move_x_degrees = 0, move_y_degrees = 0; union pantilt { struct { short pan; short tilt; } s16; int value; }; union pantilt pan; if (cnt->track.dev == -1) { int reset = 3; //0-non reset, 1-reset pan, 2-reset tilt, 3-reset pan&tilt struct v4l2_control control_s; control_s.id = V4L2_CID_PANTILT_RESET; control_s.value = (unsigned char) reset; if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Failed to reset UVC camera to starting position! Reason"); return 0; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Reseting UVC camera to starting position"); SLEEP(8, 0); /* Get camera range */ struct v4l2_queryctrl queryctrl; queryctrl.id = V4L2_CID_PAN_RELATIVE; if (ioctl(dev, VIDIOC_QUERYCTRL, &queryctrl) < 0) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: ioctl querycontrol"); return 0; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Getting camera range"); /* DWe 30.03.07 The orig request failed : * must be VIDIOC_G_CTRL separate for pan and tilt or via VIDIOC_G_EXT_CTRLS - now for 1st manual * Range X = -70 to +70 degrees * Y = -30 to +30 degrees */ //get mininum //pan.value = queryctrl.minimum; cnt->track.minx = -4480 / INCPANTILT; cnt->track.miny = -1920 / INCPANTILT; //get maximum cnt->track.maxx = 4480 / INCPANTILT; cnt->track.maxy = 1920 / INCPANTILT; //pan.value = queryctrl.maximum; cnt->track.dev = dev; cnt->track.pan_angle = 0; cnt->track.tilt_angle = 0; cnt->track.minmaxfound = 1; } struct v4l2_control control_s; MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "%s: INPUT_PARAM_ABS pan_min %d,pan_max %d,tilt_min %d,tilt_max %d ", cnt->track.minx, cnt->track.maxx, cnt->track.miny, cnt->track.maxy); MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "%s: INPUT_PARAM_ABS X_Angel %d, Y_Angel %d ", x_angle, y_angle); if (x_angle <= cnt->track.maxx && x_angle >= cnt->track.minx) move_x_degrees = x_angle - (cnt->track.pan_angle); if (y_angle <= cnt->track.maxy && y_angle >= cnt->track.miny) move_y_degrees = y_angle - (cnt->track.tilt_angle); /* * tilt up: - value * tilt down: + value * pan left: - value * pan right: + value */ pan.s16.pan = -move_x_degrees * INCPANTILT; pan.s16.tilt = -move_y_degrees * INCPANTILT; MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "%s: For_SET_ABS move_X %d,move_Y %d", move_x_degrees, move_y_degrees); /* DWe 30.03.07 Must be broken in diff calls, because * one call for both is not accept via VIDIOC_S_CTRL -> maybe via VIDIOC_S_EXT_CTRLS * The Webcam or uvcvideo does not like a call with a zero-move */ if (move_x_degrees != 0) { control_s.id = V4L2_CID_PAN_RELATIVE; //control_s.value = pan.value; control_s.value = pan.s16.pan; if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Failed to move UVC camera!"); return 0; } } /* DWe 30.03.07 We must wait a little,before we set the next CMD, otherwise PAN is mad ... */ if ((move_x_degrees != 0) && (move_y_degrees != 0)) SLEEP(1, 0); if (move_y_degrees != 0) { control_s.id = V4L2_CID_TILT_RELATIVE; //control_s.value = pan.value; control_s.value = pan.s16.tilt; if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Failed to move UVC camera!"); return 0; } } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Found MINMAX = %d", cnt->track.minmaxfound); if (cnt->track.dev != -1) { MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "%s: Before_ABS_Y_Angel : x= %d , Y= %d, ", cnt->track.pan_angle, cnt->track.tilt_angle); if (move_x_degrees != -1) { cnt->track.pan_angle += move_x_degrees; } if (move_x_degrees != -1) { cnt->track.tilt_angle += move_y_degrees; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: After_ABS_Y_Angel : x= %d , Y= %d", cnt->track.pan_angle, cnt->track.tilt_angle); } return cnt->track.move_wait; } static unsigned int uvc_move(struct context *cnt, int dev, struct coord *cent, struct images *imgs, unsigned int manual) { /* RELATIVE MOVING : Act.Position +/- X and Y */ int delta_x = cent->x - (imgs->width / 2); int delta_y = cent->y - (imgs->height / 2); int move_x_degrees, move_y_degrees; /* * DWe 30.03.07 Does the request of act.position from WebCam work ? luvcview shows at every position 180 :( * Now we init the Web by call Reset, so we can sure, that we are at x/y = 0,0 * Don't worry, if the WebCam make a sound - over End at PAN - hmmm, should it be normal ...? * PAN Value 7777 in relative will init also a want reset for CAM - it will be "0" after that */ if ((cnt->track.minmaxfound != 1) || (cent->x == 7777)) { unsigned int reset = 3; //0-non reset, 1-reset pan, 2-reset tilt, 3-reset pan&tilt struct v4l2_control control_s; control_s.id = V4L2_CID_PANTILT_RESET; control_s.value = (unsigned char) reset; if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Failed to reset UVC camera to starting position! Reason"); return 0; } MOTION_LOG(NTC, TYPE_TRACK, NO_ERRNO, "%s: Reseting UVC camera to starting position"); /* set the "helpvalue" back to null because after reset CAM should be in x=0 and not 70 */ cent->x = 0; SLEEP(8, 0); /* * DWe 30.03.07 The orig request failed : * must be VIDIOC_G_CTRL separate for pan and tilt or via VIDIOC_G_EXT_CTRLS - now for 1st manual * Range X = -70 to +70 degrees * Y = -30 to +30 degrees */ cnt->track.minx = -4480 / INCPANTILT; cnt->track.miny = -1920 / INCPANTILT; cnt->track.maxx = 4480 / INCPANTILT; cnt->track.maxy = 1920 / INCPANTILT; cnt->track.dev = dev; cnt->track.pan_angle = 0; cnt->track.tilt_angle = 0; cnt->track.minmaxfound = 1; } /* If we are on auto track we calculate delta, otherwise we use user input in degrees */ 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 / (imgs->width / 2); move_y_degrees = -delta_y * cnt->track.step_angle_y / (imgs->height / 2); } else { move_x_degrees = cent->x; move_y_degrees = cent->y; } union pantilt { struct { short pan; short tilt; } s16; int value; }; struct v4l2_control control_s; union pantilt pan; if (cnt->track.minmaxfound == 1) { /* * 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.minx - cnt->track.pan_angle) > move_x_degrees) move_x_degrees = cnt->track.minx - cnt->track.pan_angle; if (move_x_degrees > 0 && (cnt->track.maxx - cnt->track.pan_angle) < move_x_degrees) move_x_degrees = cnt->track.maxx - cnt->track.pan_angle; if (move_y_degrees < 0 && (cnt->track.miny - cnt->track.tilt_angle) > move_y_degrees) move_y_degrees = cnt->track.miny - cnt->track.tilt_angle; if (move_y_degrees > 0 && (cnt->track.maxy - cnt->track.tilt_angle) < move_y_degrees) move_y_degrees = cnt->track.maxy - cnt->track.tilt_angle; } MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "For_SET_REL pan_min %d,pan_max %d,tilt_min %d,tilt_max %d", cnt->track.minx, cnt->track.maxx, cnt->track.miny, cnt->track.maxy); MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "For_SET_REL track_pan_Angel %d, track_tilt_Angel %d", cnt->track.pan_angle, cnt->track.tilt_angle); MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "For_SET_REL move_X %d,move_Y %d", move_x_degrees, move_y_degrees); /* * tilt up: - value * tilt down: + value * pan left: - value * pan right: + value */ pan.s16.pan = -move_x_degrees * INCPANTILT; pan.s16.tilt = -move_y_degrees * INCPANTILT; /* DWe 30.03.07 Must be broken in diff calls, because * one call for both is not accept via VIDIOC_S_CTRL -> maybe via VIDIOC_S_EXT_CTRLS * The Webcam or uvcvideo does not like a call with a zero-move */ if (move_x_degrees != 0) { control_s.id = V4L2_CID_PAN_RELATIVE; control_s.value = pan.s16.pan; MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, " dev %d, addr= %d, control_S= %d, Wert= %d", dev, VIDIOC_S_CTRL, &control_s, pan.s16.pan); if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Failed to move UVC camera!"); return 0; } } /* DWe 30.03.07 We must wait a little,before we set the next CMD, otherwise PAN is mad ... */ if ((move_x_degrees != 0) && (move_y_degrees != 0)) SLEEP (1, 0); if (move_y_degrees != 0) { control_s.id = V4L2_CID_TILT_RELATIVE; control_s.value = pan.s16.tilt; MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, " dev %d,addr= %d, control_S= %d, Wert= %d", dev, VIDIOC_S_CTRL, &control_s, pan.s16.tilt); if (ioctl(dev, VIDIOC_S_CTRL, &control_s) < 0) { MOTION_LOG(ERR, TYPE_TRACK, SHOW_ERRNO, "%s: Failed to move UVC camera!"); return 0; } } MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "%s: Found MINMAX = %d", cnt->track.minmaxfound); if (cnt->track.minmaxfound == 1) { MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "Before_REL_Y_Angel : x= %d , Y= %d", cnt->track.pan_angle, cnt->track.tilt_angle); if (move_x_degrees != 0) cnt->track.pan_angle += -pan.s16.pan / INCPANTILT; if (move_y_degrees != 0) cnt->track.tilt_angle += -pan.s16.tilt / INCPANTILT; MOTION_LOG(DBG, TYPE_TRACK, NO_ERRNO, "After_REL_Y_Angel : x= %d , Y= %d", cnt->track.pan_angle, cnt->track.tilt_angle); } return cnt->track.move_wait; } #endif /* MOTION_V4L2 */ #endif /* WITHOUT_V4L */ motion-release-4.0/track.h000066400000000000000000000114051300074623700155600ustar00rootroot00000000000000/* 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" #include struct trackoptions { int dev; /* Config options: */ unsigned int type; char *port; unsigned int motorx; unsigned int motory; int maxx; int maxy; int minx; int miny; unsigned int stepsize; unsigned int speed; unsigned int homex; unsigned int homey; unsigned int iomojo_id; unsigned int active; unsigned int motorx_reverse; unsigned int motory_reverse; unsigned int minmaxfound; unsigned int step_angle_x; unsigned int step_angle_y; unsigned int move_wait; /* UVC */ int pan_angle; // degrees int tilt_angle; // degrees }; extern struct trackoptions track_template; unsigned int track_center(struct context *, int, unsigned int, int, int); unsigned int track_move(struct context *, int, struct coord *, struct images *, unsigned 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 #define TRACK_TYPE_UVC 5 #define TRACK_TYPE_SERVO 6 /* * 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_STATUS_UP 1 #define STEPPER_STATUS_DOWN 2 #define STEPPER_STATUS_SAFETYU 4 #define STEPPER_STATUS_SAFETYD 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 #define STEPPER_COMMAND_UP_N 1 #define STEPPER_COMMAND_DOWN_N 2 #define STEPPER_COMMAND_UP 3 #define STEPPER_COMMAND_DOWN 4 /* * Some defines for the Serial servo motor: */ /* * Controlling: * Three bytes are sent to the servo - BYTE1=SERVO_COMMAND BYTE2=COMMAND BYTE3=DATA * eg, sending the command 01 02 08 would Command SERVO_COMMAND1 to move LEFT a total of 8 STEPS * * An extra command 0x08 has been added but here is the basic command set. * * 0x00 STATUS - Current status byte will be returned, data byte ignored * 0x01 LEFT_N - Servo will take N Steps to the Left until it reaches the Servos safety limit * 0x02 RIGHT_N - Servo will take N Steps to the Right until it reaches the Servos safety limit * 0x03 LEFT - Servo will move to Left most position, data byte ignored. * 0x04 RIGHT - Servo will move to Right most position, data byte ignored. * 0x05 SWEEP - Servo will sweep between its extremes, data byte ignored. * 0x06 STOP - Servo will Stop, data byte ignored * 0x07 SPEED - Set servos speed between 0 and 255. * 0x08 ABSOLUTE - Set servo to absolute position between 0 and 255 * 0x09 POSITION - Get servo to absolute position between 0 and 255 * */ #define SERVO_BAUDRATE B9600 #define SERVO_COMMAND_STATUS 0 #define SERVO_COMMAND_LEFT_N 1 #define SERVO_COMMAND_RIGHT_N 2 #define SERVO_COMMAND_LEFT 3 #define SERVO_COMMAND_RIGHT 4 #define SERVO_COMMAND_SWEEP 5 #define SERVO_COMMAND_STOP 6 #define SERVO_COMMAND_SPEED 7 #define SERVO_COMMAND_ABSOLUTE 8 #define SERVO_COMMAND_POSITION 9 #define SERVO_COMMAND_UP_N 1 #define SERVO_COMMAND_DOWN_N 2 #define SERVO_COMMAND_UP 3 #define SERVO_COMMAND_DOWN 4 /* * 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 #ifndef WITHOUT_V4L /* * Defines for the Logitech QuickCam Orbit/Sphere USB webcam */ #define LQOS_VERTICAL_DEGREES 180 #define LQOS_HORIZONAL_DEGREES 120 /* * UVC */ #ifdef MOTION_V4L2 #ifndef V4L2_CID_PAN_RELATIVE #define V4L2_CID_PAN_RELATIVE (V4L2_CID_PRIVATE_BASE+7) #endif #ifndef V4L2_CID_TILT_RELATIVE #define V4L2_CID_TILT_RELATIVE (V4L2_CID_PRIVATE_BASE+8) #endif #ifndef V4L2_CID_PANTILT_RESET #define V4L2_CID_PANTILT_RESET (V4L2_CID_PRIVATE_BASE+9) #endif #define INCPANTILT 64 // 1 degree #endif /* MOTION_V4L2 */ #endif /* WITHOUT_V4L */ #endif /* _INCLUDE_TRACK_H */ motion-release-4.0/version.sh000077500000000000000000000001161300074623700163240ustar00rootroot00000000000000#!/bin/sh SNV_VERSION=`git show -s --format=%h` echo -n "4.0+git$SNV_VERSION" motion-release-4.0/video.c000066400000000000000000000373641300074623700155710ustar00rootroot00000000000000/* * 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 "rotate.h" /* already includes motion.h */ #include "video.h" #if defined(HAVE_LINUX_VIDEODEV_H) && !defined(WITHOUT_V4L) /** * v4l_picture_controls */ static void v4l_picture_controls(struct context *cnt, struct video_dev *viddev) { int dev = viddev->fd; 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCGPICT)"); } make_change = 1; vid_pic.hue = cnt->conf.hue * 256; viddev->hue = cnt->conf.hue; } /* Only tested with PWCBSD in FreeBSD */ #if defined(PWCBSD) if (cnt->conf.frame_limit != viddev->fps) { struct video_window vw; int fps; if (ioctl(dev, VIDIOCGWIN, &vw) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl VIDIOCGWIN"); } else { fps = vw.flags >> PWC_FPS_SHIFT; MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, "%s: Get Current framerate %d .. trying %d", fps, cnt->conf.frame_limit); } fps = cnt->conf.frame_limit; vw.flags = fps << PWC_FPS_SHIFT; if (ioctl(dev, VIDIOCSWIN, &vw) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl VIDIOCSWIN"); } else if (ioctl(dev, VIDIOCGWIN, &vw) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl VIDIOCGWIN"); } else { fps = vw.flags >> PWC_FPS_SHIFT; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set new framerate %d", fps); } viddev->fps = fps; } #endif if (cnt->conf.autobright) { if (vid_do_autobright(cnt, viddev)) { /* If we already read the VIDIOGPICT - we should not do it again. */ if (!make_change) { if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCGPICT)"); } vid_pic.brightness = viddev->brightness * 256; make_change = 1; } } else if (cnt->conf.brightness && cnt->conf.brightness != viddev->brightness) { if ((!make_change) && (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1)) MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCSPICT)"); } } /******************************************************************************* Video4linux capture routines ********************************************************************************/ /** * v4l_start * Initialize video device to start capturing and allocates memory map * for video device. * * Returns mmapped buffer for video device or NULL if any error happens. * */ unsigned char *v4l_start(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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCGCHAN) Input %d", input); } else { vid_chnl.channel = input; vid_chnl.norm = norm; if (ioctl (dev, VIDIOCSCHAN, &vid_chnl) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCSCHAN) Input %d" " Standard method %d", input, norm); return NULL; } } } if (freq) { memset(&vid_tuner, 0, sizeof(struct video_tuner)); vid_tuner.tuner = tuner_number; if (ioctl (dev, VIDIOCGTUNER, &vid_tuner) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCGTUNER) tuner %d", tuner_number); } 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCSFREQ)" " Frequency %ul", freq); return NULL; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set Tuner to %d Frequency set to %ul", tuner_number, freq); } } if (ioctl (dev, VIDIOCGMBUF, &vid_buf) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: ioctl(VIDIOCGMBUF) - Error device" " does not support memory map\n V4L capturing using read is deprecated!\n" "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) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: MAP_FAILED"); 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(WRN, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(WRN, TYPE_VIDEO, SHOW_ERRNO, "%s: Failed with YUV422," " trying YUYV palette"); viddev->v4l_fmt = VIDEO_PALETTE_YUYV; vid_mmap.format = viddev->v4l_fmt; if (ioctl(dev, VIDIOCMCAPTURE, &vid_mmap) == -1) { MOTION_LOG(WRN, TYPE_VIDEO, SHOW_ERRNO, "%s: Failed with YUYV, 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(WRN, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s: Failed with all supported palettes " "- giving up"); return NULL; } } } } } } switch (viddev->v4l_fmt) { case VIDEO_PALETTE_YUV420P: viddev->v4l_bufsize = (width * height * 3) / 2; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using VIDEO_PALETTE_YUV420P palette"); break; case VIDEO_PALETTE_YUV422: viddev->v4l_bufsize = (width * height * 2); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using VIDEO_PALETTE_YUV422 palette"); break; case VIDEO_PALETTE_YUYV: viddev->v4l_bufsize = (width * height * 2); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using VIDEO_PALETTE_YUYV palette"); break; case VIDEO_PALETTE_RGB24: viddev->v4l_bufsize = (width * height * 3); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using VIDEO_PALETTE_RGB24 palette"); break; case VIDEO_PALETTE_GREY: viddev->v4l_bufsize = width * height; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using VIDEO_PALETTE_GREY palette"); break; } return map; } /** * v4l_next * Fetches a video frame from a v4l device * * Parameters: * 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) */ 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(ALR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(ALR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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: conv_rgb24toyuv420p(map, cap_map, width, height); break; case VIDEO_PALETTE_YUYV: case VIDEO_PALETTE_YUV422: conv_yuv422to420p(map, cap_map, width, height); break; default: memcpy(map, cap_map, viddev->v4l_bufsize); } return 0; } /** * v4l_set_input * Sets input for video device, adjust picture controls. * If needed skip frames for round robin. * * Parameters: * cnt Pointer to context struct * 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 * conf Pointer to config struct * * Returns nothing */ void v4l_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, struct config *conf) { int dev = viddev->fd; struct video_channel vid_chnl; struct video_tuner vid_tuner; unsigned long frequnits , freq; int input = conf->input; int norm = conf->norm; int tuner_number = conf->tuner_number; frequnits = freq = conf->frequency; if (input != viddev->input || width != viddev->width || height != viddev->height || freq != viddev->freq || tuner_number != viddev->tuner_number || norm != viddev->norm) { unsigned int skip = conf->roundrobin_skip, i; if (freq) { memset(&vid_tuner, 0, sizeof(struct video_tuner)); vid_tuner.tuner = tuner_number; if (ioctl (dev, VIDIOCGTUNER, &vid_tuner) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCGTUNER) tuner number %d", tuner_number); } 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCSFREQ) Frequency %ul", frequnits); return; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set Tuner to %d Frequency to %ul", tuner_number, frequnits); } } memset(&vid_chnl, 0, sizeof(struct video_channel)); vid_chnl.channel = input; if (ioctl (dev, VIDIOCGCHAN, &vid_chnl) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCGCHAN) Input %d", input); } else { vid_chnl.channel = input; vid_chnl.norm = norm; if (ioctl (dev, VIDIOCSCHAN, &vid_chnl) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCSCHAN) Input %d" " Standard method %d", input, norm); return; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set Input to %d Standard method to %d", input, norm); } v4l_picture_controls(cnt, viddev); conf->input = viddev->input = input; conf->width = viddev->width = width; conf->height = viddev->height = height; conf->frequency = viddev->freq = freq; conf->tuner_number = viddev->tuner_number = tuner_number; conf->norm = viddev->norm = norm; /* skip a few frames if needed */ for (i = 0; i < skip; i++) v4l_next(viddev, map, width, height); } else { /* No round robin - we only adjust picture controls */ v4l_picture_controls(cnt, viddev); } } #endif /* !WITHOUT_V4L */ motion-release-4.0/video.h000066400000000000000000000075001300074623700155630ustar00rootroot00000000000000/* 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 #include #if !defined(WITHOUT_V4L) #if defined(HAVE_LINUX_VIDEODEV_H) #include #elif defined(HAVE_SYS_VIDEOIO_H) #include #endif #include "vloopback_motion.h" #include "pwc-ioctl.h" #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 -1 #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" typedef struct video_image_buff { unsigned char *ptr; int content_length; size_t size; /* total allocated size */ size_t used; /* bytes already used */ struct timeval image_time; /* time this image was received */ } video_buff; struct video_dev { struct video_dev *next; int usage_count; int fd; const char *video_device; int input; int norm; int width; int height; int brightness; int contrast; int saturation; int hue; int power_line_frequency; unsigned long freq; int tuner_number; int fps; pthread_mutex_t mutex; pthread_mutexattr_t attr; int owner; int frames; /* Device type specific stuff: */ #ifndef WITHOUT_V4L /* v4l */ int v4l2; void *v4l2_private; int size_map; int v4l_fmt; unsigned char *v4l_buffers[2]; int v4l_curbuffer; int v4l_maxbuffer; int v4l_bufsize; #endif }; /* video functions, video_common.c */ int vid_start(struct context *cnt); int vid_next(struct context *cnt, unsigned char *map); void vid_close(struct context *cnt); void vid_cleanup(void); void vid_init(void); void conv_yuv422to420p(unsigned char *map, unsigned char *cap_map, int width, int height); void conv_uyvyto420p(unsigned char *map, unsigned char *cap_map, unsigned int width, unsigned int height); void conv_rgb24toyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height); int sonix_decompress(unsigned char *outp, unsigned char *inp, int width, int height); void bayer2rgb24(unsigned char *dst, unsigned char *src, long int width, long int height); int vid_do_autobright(struct context *cnt, struct video_dev *viddev); int mjpegtoyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height, unsigned int size); #ifndef WITHOUT_V4L /* video functions, video.c */ unsigned char *v4l_start(struct video_dev *viddev, int width, int height, int input, int norm, unsigned long freq, int tuner_number); void v4l_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, struct config *conf); int v4l_next(struct video_dev *viddev, unsigned char *map, int width, int height); /* video2.c */ unsigned char *v4l2_start(struct context *cnt, struct video_dev *viddev, int width, int height, int input, int norm, unsigned long freq, int tuner_number); void v4l2_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, struct config *conf); int v4l2_next(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height); void v4l2_close(struct video_dev *viddev); void v4l2_cleanup(struct video_dev *viddev); #endif /* WITHOUT_V4L */ #endif /* _INCLUDE_VIDEO_H */ motion-release-4.0/video2.c000066400000000000000000001131621300074623700156420ustar00rootroot00000000000000/* * video2.c * * V4L2 interface with basically JPEG decompression support and even more ... * Copyright 2006 Krzysztof Blaszkowski (kb@sysmikro.com.pl) * 2007 Angel Carpintero (motiondevelop@gmail.com) * Supported features and TODO - preferred palette is JPEG which seems to be very popular for many 640x480 usb cams - other supported palettes (NOT TESTED) V4L2_PIX_FMT_SN9C10X (sonix) V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_SBGGR8, (sonix) V4L2_PIX_FMT_SPCA561, V4L2_PIX_FMT_SGBRG8, V4L2_PIX_FMT_SGRBG8, V4L2_PIX_FMT_PAC207, V4L2_PIX_FMT_PJPG, V4L2_PIX_FMT_MJPEG, (tested) V4L2_PIX_FMT_JPEG, (tested) V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_SPCA501, V4L2_PIX_FMT_SPCA505, V4L2_PIX_FMT_SPCA508, V4L2_PIX_FMT_UYVY, (tested) V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420, (tested) V4L2_PIX_FMT_YUYV (tested) * - setting tuner - NOT TESTED * - access to V4L2 device controls is missing. Partially added but requires some improvements likely. * - changing resolution at run-time may not work. * - ucvideo svn r75 or above to work with MJPEG ( e.g. Logitech 5000 pro ) * This work is inspired by fswebcam and current design of motion. * This interface has been tested with ZC0301 driver from kernel 2.6.17.3 and Labtec's usb camera (PAS202 sensor) * I'm very pleased by achieved image quality and cpu usage comparing to junky v4l1 spca5xx driver with * it nonsensical kernel messy jpeg decompressor. * Default sensor settings used by ZC0301 driver are very reasonable choosen. * apparently brigthness should be controlled automatically by motion still for light compensation. * it can be done by adjusting ADC gain and also exposure time. * Kernel 2.6.27 V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') YUYV per line V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S', '5', '0', '5') YYUV per line V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') YUVY per line V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') 8 GBGB.. RGRG.. V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') 8 GRGR.. BGBG.. V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') 16 BGBG.. GRGR.. V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') compressed GBRG bayer V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') Pixart 73xx JPEG V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') compressed BGGR bayer * * 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. let's go :) */ #if !defined(WITHOUT_V4L) && defined(MOTION_V4L2) #include "motion.h" #include "video.h" #ifdef MOTION_V4L2_OLD // Seems that is needed for some system #include #include #endif #define u8 unsigned char #define u16 unsigned short #define u32 unsigned int #define s32 signed int #define MMAP_BUFFERS 4 #define MIN_MMAP_BUFFERS 2 #ifndef V4L2_PIX_FMT_SBGGR8 /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ #endif #ifndef V4L2_PIX_FMT_MJPEG #define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M','J','P','G') /* Motion-JPEG */ #endif #ifndef V4L2_PIX_FMT_SN9C10X #define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S','9','1','0') /* SN9C10x compression */ #endif #ifndef V4L2_PIX_FMT_SGBRG8 #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */ #endif #ifndef V4L2_PIX_FMT_SGRBG8 #define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') /* 8 GRGR.. BGBG.. */ #endif #ifndef V4L2_PIX_FMT_SBGGR16 #define V4L2_PIX_FMT_SBGGR16 v4l2_fourcc('B', 'Y', 'R', '2') /* 16 BGBG.. GRGR.. */ #endif #ifndef V4L2_PIX_FMT_SPCA561 #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #endif #ifndef V4L2_PIX_FMT_PJPG #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ #endif #ifndef V4L2_PIX_FMT_PAC207 #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ #endif #ifndef V4L2_PIX_FMT_SPCA501 #define V4L2_PIX_FMT_SPCA501 v4l2_fourcc('S', '5', '0', '1') /* YUYV per line */ #endif #ifndef V4L2_PIX_FMT_SPCA505 #define V4L2_PIX_FMT_SPCA505 v4l2_fourcc('S', '5', '0', '5') /* YYUV per line */ #endif #ifndef V4L2_PIX_FMT_SPCA508 #define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */ #endif #define ZC301_V4L2_CID_DAC_MAGN V4L2_CID_PRIVATE_BASE #define ZC301_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE+1) static const u32 queried_ctrls[] = { V4L2_CID_BRIGHTNESS, V4L2_CID_CONTRAST, V4L2_CID_SATURATION, V4L2_CID_HUE, /* first added in Linux kernel v2.6.26 */ #ifdef V4L2_CID_POWER_LINE_FREQUENCY V4L2_CID_POWER_LINE_FREQUENCY, #endif V4L2_CID_RED_BALANCE, V4L2_CID_BLUE_BALANCE, V4L2_CID_GAMMA, V4L2_CID_EXPOSURE, V4L2_CID_AUTOGAIN, V4L2_CID_GAIN, ZC301_V4L2_CID_DAC_MAGN, ZC301_V4L2_CID_GREEN_BALANCE, 0 }; typedef struct { int fd; u32 fps; struct v4l2_capability cap; struct v4l2_format src_fmt; struct v4l2_format dst_fmt; struct v4l2_requestbuffers req; struct v4l2_buffer buf; video_buff *buffers; s32 pframe; u32 ctrl_flags; struct v4l2_queryctrl *controls; volatile unsigned int *finish; /* End the thread */ } src_v4l2_t; /** * xioctl */ #ifdef __OpenBSD__ static int xioctl(src_v4l2_t *vid_source, unsigned long request, void *arg) #else static int xioctl(src_v4l2_t *vid_source, int request, void *arg) #endif { int ret; do ret = ioctl(vid_source->fd, request, arg); while (-1 == ret && EINTR == errno && !vid_source->finish); return ret; } /** * v4l2_get_capability */ static int v4l2_get_capability(src_v4l2_t * vid_source) { if (xioctl(vid_source, VIDIOC_QUERYCAP, &vid_source->cap) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: Not a V4L2 device?"); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: \n------------------------\n" "cap.driver: \"%s\"\n" "cap.card: \"%s\"\n" "cap.bus_info: \"%s\"\n" "cap.capabilities=0x%08X\n------------------------", vid_source->cap.driver, vid_source->cap.card, vid_source->cap.bus_info, vid_source->cap.capabilities); if (vid_source->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VIDEO_CAPTURE"); if (vid_source->cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VIDEO_OUTPUT"); if (vid_source->cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VIDEO_OVERLAY"); if (vid_source->cap.capabilities & V4L2_CAP_VBI_CAPTURE) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VBI_CAPTURE"); if (vid_source->cap.capabilities & V4L2_CAP_VBI_OUTPUT) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - VBI_OUTPUT"); if (vid_source->cap.capabilities & V4L2_CAP_RDS_CAPTURE) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - RDS_CAPTURE"); if (vid_source->cap.capabilities & V4L2_CAP_TUNER) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - TUNER"); if (vid_source->cap.capabilities & V4L2_CAP_AUDIO) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - AUDIO"); if (vid_source->cap.capabilities & V4L2_CAP_READWRITE) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - READWRITE"); if (vid_source->cap.capabilities & V4L2_CAP_ASYNCIO) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - ASYNCIO"); if (vid_source->cap.capabilities & V4L2_CAP_STREAMING) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - STREAMING"); if (vid_source->cap.capabilities & V4L2_CAP_TIMEPERFRAME) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - TIMEPERFRAME"); if (!(vid_source->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: Device does not support capturing."); return -1; } return 0; } /** * v4l2_select_input */ static int v4l2_select_input(struct config *conf, struct video_dev *viddev, src_v4l2_t * vid_source, int in, int norm, unsigned long freq_, int tuner_number ATTRIBUTE_UNUSED) { struct v4l2_input input; struct v4l2_standard standard; v4l2_std_id std_id; /* Set the input. */ memset(&input, 0, sizeof (input)); if (in == IN_DEFAULT) input.index = IN_TV; else input.index = in; if (xioctl(vid_source, VIDIOC_ENUMINPUT, &input) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Unable to query input %d." " VIDIOC_ENUMINPUT, if you use a WEBCAM change input value in conf by -1", input.index); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: name = \"%s\", type 0x%08X," " status %08x", input.name, input.type, input.status); if (input.type & V4L2_INPUT_TYPE_TUNER) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - TUNER"); if (input.type & V4L2_INPUT_TYPE_CAMERA) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - CAMERA"); if (xioctl(vid_source, VIDIOC_S_INPUT, &input.index) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error selecting input %d" " VIDIOC_S_INPUT", input.index); return -1; } viddev->input = conf->input = in; /* * Set video standard usually webcams doesn't support the ioctl or * return V4L2_STD_UNKNOWN */ if (xioctl(vid_source, VIDIOC_G_STD, &std_id) == -1) { MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: Device doesn't support VIDIOC_G_STD"); norm = std_id = 0; // V4L2_STD_UNKNOWN = 0 } if (std_id) { memset(&standard, 0, sizeof(standard)); standard.index = 0; while (xioctl(vid_source, VIDIOC_ENUMSTD, &standard) == 0) { if (standard.id & std_id) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: - video standard %s", standard.name); standard.index++; } switch (norm) { case 1: std_id = V4L2_STD_NTSC; break; case 2: std_id = V4L2_STD_SECAM; break; default: std_id = V4L2_STD_PAL; } if (xioctl(vid_source, VIDIOC_S_STD, &std_id) == -1) MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error selecting standard" " method %d VIDIOC_S_STD", (int)std_id); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set standard method %d", (int)std_id); } viddev->norm = conf->norm = norm; /* If this input is attached to a tuner, set the frequency. */ if (input.type & V4L2_INPUT_TYPE_TUNER) { struct v4l2_tuner tuner; struct v4l2_frequency freq; /* Query the tuners capabilities. */ memset(&tuner, 0, sizeof(struct v4l2_tuner)); tuner.index = input.tuner; if (xioctl(vid_source, VIDIOC_G_TUNER, &tuner) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: tuner %d VIDIOC_G_TUNER", tuner.index); return 0; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set tuner %d", tuner.index); /* Set the frequency. */ memset(&freq, 0, sizeof(struct v4l2_frequency)); freq.tuner = input.tuner; freq.type = V4L2_TUNER_ANALOG_TV; freq.frequency = (freq_ / 1000) * 16; if (xioctl(vid_source, VIDIOC_S_FREQUENCY, &freq) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: freq %ul VIDIOC_S_FREQUENCY", freq.frequency); return 0; } viddev->freq = conf->frequency = freq_; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Set Frequency to %ul", freq.frequency); } else { viddev->freq = conf->frequency = 0; } return 0; } /* * * v4l2_do_set_pix_format * * This routine does the actual request to the driver * * Returns: 0 Ok * -1 Problems setting palette or not supported * * Our algorithm for setting the picture format for the data which the * driver returns to us will be as follows: * * First, we request that the format be set to whatever is in the config * file (which is either the motion default, or a value chosen by the user). * If that request is successful, we are finished. * * If the driver responds that our request is not accepted, we then enumerate * the formats which the driver claims to be able to supply. From this list, * we choose whichever format is "most efficient" for motion. The enumerated * list is also printed to the motion log so that the user can consider * choosing a different value for the config file. * * We then request the driver to set the format we have chosen. That request * should never fail, so if it does we log the fact and give up. */ static int v4l2_do_set_pix_format(u32 pixformat, src_v4l2_t * vid_source, int *width, int *height) { CLEAR(vid_source->dst_fmt); vid_source->dst_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vid_source->dst_fmt.fmt.pix.width = *width; vid_source->dst_fmt.fmt.pix.height = *height; vid_source->dst_fmt.fmt.pix.pixelformat = pixformat; vid_source->dst_fmt.fmt.pix.field = V4L2_FIELD_ANY; if (xioctl(vid_source, VIDIOC_TRY_FMT, &vid_source->dst_fmt) != -1 && vid_source->dst_fmt.fmt.pix.pixelformat == pixformat) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Testing palette %c%c%c%c (%dx%d)", pixformat >> 0, pixformat >> 8, pixformat >> 16, pixformat >> 24, *width, *height); if (vid_source->dst_fmt.fmt.pix.width != (unsigned int) *width || vid_source->dst_fmt.fmt.pix.height != (unsigned int) *height) { MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: Adjusting resolution " "from %ix%i to %ix%i.", *width, *height, vid_source->dst_fmt.fmt.pix.width, vid_source->dst_fmt.fmt.pix.height); *width = vid_source->dst_fmt.fmt.pix.width; *height = vid_source->dst_fmt.fmt.pix.height; } if (xioctl(vid_source, VIDIOC_S_FMT, &vid_source->dst_fmt) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error setting pixel " "format.\nVIDIOC_S_FMT: "); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using palette %c%c%c%c (%dx%d)" " bytesperlines %d sizeimage %d colorspace %08x", pixformat >> 0, pixformat >> 8, pixformat >> 16, pixformat >> 24, *width, *height, vid_source->dst_fmt.fmt.pix.bytesperline, vid_source->dst_fmt.fmt.pix.sizeimage, vid_source->dst_fmt.fmt.pix.colorspace); return 0; } return -1; } /** * v4l2_set_pix_format * * Returns: 0 Ok * -1 Problems setting palette or not supported */ static int v4l2_set_pix_format(struct context *cnt, src_v4l2_t * vid_source, int *width, int *height) { struct v4l2_fmtdesc fmtd; int v4l2_pal; /* * Note that this array MUST exactly match the config file list. * A higher index means better chance to be used */ static const u32 supported_formats[] = { V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SPCA561, V4L2_PIX_FMT_SGBRG8, V4L2_PIX_FMT_SGRBG8, V4L2_PIX_FMT_PAC207, V4L2_PIX_FMT_PJPG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_SPCA501, V4L2_PIX_FMT_SPCA505, V4L2_PIX_FMT_SPCA508, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420 /* most efficient for motion */ }; int array_size = sizeof(supported_formats) / sizeof(supported_formats[0]); int index_format = -1; /* -1 says not yet chosen */ CLEAR(fmtd); fmtd.index = v4l2_pal = 0; fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* First we try a shortcut of just setting the config file value */ if (cnt->conf.v4l2_palette >= 0) { char name[5] = {supported_formats[cnt->conf.v4l2_palette] >> 0, supported_formats[cnt->conf.v4l2_palette] >> 8, supported_formats[cnt->conf.v4l2_palette] >> 16, supported_formats[cnt->conf.v4l2_palette] >> 24, 0}; if (v4l2_do_set_pix_format(supported_formats[cnt->conf.v4l2_palette], vid_source, width, height) >= 0) return 0; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Config palette index %d (%s)" " doesn't work.", cnt->conf.v4l2_palette, name); } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Supported palettes:"); while (xioctl(vid_source, VIDIOC_ENUM_FMT, &fmtd) != -1) { int i; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: (%i) %c%c%c%c (%s)", v4l2_pal, fmtd.pixelformat >> 0, fmtd.pixelformat >> 8, fmtd.pixelformat >> 16, fmtd.pixelformat >> 24, fmtd.description); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: %d - %s (compressed : %d) (%#x)", fmtd.index, fmtd.description, fmtd.flags, fmtd.pixelformat); /* Adjust index_format if larger value found */ for (i = index_format + 1; i < array_size; i++) if (supported_formats[i] == fmtd.pixelformat) index_format = i; CLEAR(fmtd); fmtd.index = ++v4l2_pal; fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; } if (index_format >= 0) { char name[5] = {supported_formats[index_format] >> 0, supported_formats[index_format] >> 8, supported_formats[index_format] >> 16, supported_formats[index_format] >> 24, 0}; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s Selected palette %s", name); if (v4l2_do_set_pix_format(supported_formats[index_format], vid_source, width, height) >= 0) return 0; MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "VIDIOC_TRY_FMT failed for " "format %s", name); } MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: Unable to find a compatible" " palette format."); return -1; } #if 0 static void v4l2_set_fps(src_v4l2_t * vid_source) { struct v4l2_streamparm* setfps; setfps = (struct v4l2_streamparm *) calloc(1, sizeof(struct v4l2_streamparm)); memset(setfps, 0, sizeof(struct v4l2_streamparm)); setfpvid_source->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; setfpvid_source->parm.capture.timeperframe.numerator = 1; setfpvid_source->parm.capture.timeperframe.denominator = vid_source->fps; if (xioctl(vid_source, VIDIOC_S_PARM, setfps) == -1) MOTION_LOG(ERR, 1, "%s: v4l2_set_fps VIDIOC_S_PARM"); } #endif /** * v4l2_set_mmap */ static int v4l2_set_mmap(src_v4l2_t * vid_source) { enum v4l2_buf_type type; u32 buffer_index; /* Does the device support streaming? */ if (!(vid_source->cap.capabilities & V4L2_CAP_STREAMING)) return -1; memset(&vid_source->req, 0, sizeof(struct v4l2_requestbuffers)); vid_source->req.count = MMAP_BUFFERS; vid_source->req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vid_source->req.memory = V4L2_MEMORY_MMAP; if (xioctl(vid_source, VIDIOC_REQBUFS, &vid_source->req) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error requesting buffers" " %d for memory map. VIDIOC_REQBUFS", vid_source->req.count); return -1; } MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: mmap information: frames=%d", vid_source->req.count); if (vid_source->req.count < MIN_MMAP_BUFFERS) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Insufficient buffer memory" " %d < MIN_MMAP_BUFFERS.", vid_source->req.count); return -1; } vid_source->buffers = calloc(vid_source->req.count, sizeof(video_buff)); if (!vid_source->buffers) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Out of memory."); return -1; } for (buffer_index = 0; buffer_index < vid_source->req.count; buffer_index++) { struct v4l2_buffer buf; memset(&buf, 0, sizeof(struct v4l2_buffer)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = buffer_index; if (xioctl(vid_source, VIDIOC_QUERYBUF, &buf) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error querying buffer" " %i\nVIDIOC_QUERYBUF: ", buffer_index); free(vid_source->buffers); return -1; } vid_source->buffers[buffer_index].size = buf.length; vid_source->buffers[buffer_index].ptr = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, vid_source->fd, buf.m.offset); if (vid_source->buffers[buffer_index].ptr == MAP_FAILED) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error mapping buffer %i mmap", buffer_index); free(vid_source->buffers); return -1; } MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: %i length=%d Address (%x)", buffer_index, buf.length, vid_source->buffers[buffer_index].ptr); } for (buffer_index = 0; buffer_index < vid_source->req.count; buffer_index++) { memset(&vid_source->buf, 0, sizeof(struct v4l2_buffer)); vid_source->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vid_source->buf.memory = V4L2_MEMORY_MMAP; vid_source->buf.index = buffer_index; if (xioctl(vid_source, VIDIOC_QBUF, &vid_source->buf) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_QBUF"); return -1; } } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (xioctl(vid_source, VIDIOC_STREAMON, &type) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error starting stream." " VIDIOC_STREAMON"); return -1; } return 0; } /** * v4l2_scan_controls */ static int v4l2_scan_controls(src_v4l2_t * vid_source) { int count, i; struct v4l2_queryctrl queryctrl; memset(&queryctrl, 0, sizeof(struct v4l2_queryctrl)); for (i = 0, count = 0; queried_ctrls[i]; i++) { queryctrl.id = queried_ctrls[i]; if (xioctl(vid_source, VIDIOC_QUERYCTRL, &queryctrl)) continue; count++; vid_source->ctrl_flags |= 1 << i; } if (count) { struct v4l2_queryctrl *ctrl = vid_source->controls = calloc(count, sizeof(struct v4l2_queryctrl)); if (!ctrl) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Insufficient buffer memory."); return -1; } for (i = 0; queried_ctrls[i]; i++) { if (vid_source->ctrl_flags & (1 << i)) { struct v4l2_control control; queryctrl.id = queried_ctrls[i]; if (xioctl(vid_source, VIDIOC_QUERYCTRL, &queryctrl)) continue; memcpy(ctrl, &queryctrl, sizeof(struct v4l2_queryctrl)); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: found control 0x%08x, \"%s\"," " range %d,%d %s", ctrl->id, ctrl->name, ctrl->minimum, ctrl->maximum, ctrl->flags & V4L2_CTRL_FLAG_DISABLED ? "!DISABLED!" : ""); memset(&control, 0, sizeof (control)); control.id = queried_ctrls[i]; xioctl(vid_source, VIDIOC_G_CTRL, &control); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: \t\"%s\", default %d, current %d", ctrl->name, ctrl->default_value, control.value); ctrl++; } } } return 0; } /** * v4l2_set_control */ static int v4l2_set_control(src_v4l2_t * vid_source, u32 cid, int value) { int i, count; if (!vid_source->controls) return -1; for (i = 0, count = 0; queried_ctrls[i]; i++) { if (vid_source->ctrl_flags & (1 << i)) { if (cid == queried_ctrls[i]) { struct v4l2_queryctrl *ctrl = vid_source->controls + count; struct v4l2_control control; int ret; memset(&control, 0, sizeof (control)); control.id = queried_ctrls[i]; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: value = control.value = (value * (ctrl->maximum - ctrl->minimum) / 256) + ctrl->minimum; ret = xioctl(vid_source, VIDIOC_S_CTRL, &control); break; case V4L2_CTRL_TYPE_BOOLEAN: value = control.value = value ? 1 : 0; ret = xioctl(vid_source, VIDIOC_S_CTRL, &control); break; case V4L2_CTRL_TYPE_MENU: /* set as is, no adjustments */ control.value = value; ret = xioctl(vid_source, VIDIOC_S_CTRL, &control); break; default: MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: control type not supported yet"); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: setting control \"%s\" to %d" " (ret %d %s) %s", ctrl->name, value, ret, ret ? strerror(errno) : "", ctrl->flags & V4L2_CTRL_FLAG_DISABLED ? "Control is DISABLED!" : ""); return 0; } count++; } } return -1; } /** * v4l2_picture_controls */ static void v4l2_picture_controls(struct context *cnt, struct video_dev *viddev) { src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private; if (cnt->conf.contrast && cnt->conf.contrast != viddev->contrast) { viddev->contrast = cnt->conf.contrast; v4l2_set_control(vid_source, V4L2_CID_CONTRAST, viddev->contrast); } if (cnt->conf.saturation && cnt->conf.saturation != viddev->saturation) { viddev->saturation = cnt->conf.saturation; v4l2_set_control(vid_source, V4L2_CID_SATURATION, viddev->saturation); } if (cnt->conf.hue && cnt->conf.hue != viddev->hue) { viddev->hue = cnt->conf.hue; v4l2_set_control(vid_source, V4L2_CID_HUE, viddev->hue); } #ifdef V4L2_CID_POWER_LINE_FREQUENCY /* -1 is don't modify as 0 is an option to disable the power line filter */ if (cnt->conf.power_line_frequency != -1 && cnt->conf.power_line_frequency != viddev->power_line_frequency) { viddev->power_line_frequency = cnt->conf.power_line_frequency; v4l2_set_control(vid_source, V4L2_CID_POWER_LINE_FREQUENCY, viddev->power_line_frequency); } #endif if (cnt->conf.autobright) { if (vid_do_autobright(cnt, viddev)) { if (v4l2_set_control(vid_source, V4L2_CID_BRIGHTNESS, viddev->brightness)) v4l2_set_control(vid_source, V4L2_CID_GAIN, viddev->brightness); } } else { if (cnt->conf.brightness && cnt->conf.brightness != viddev->brightness) { viddev->brightness = cnt->conf.brightness; if (v4l2_set_control(vid_source, V4L2_CID_BRIGHTNESS, viddev->brightness)) v4l2_set_control(vid_source, V4L2_CID_GAIN, viddev->brightness); } } } /* public functions */ /** * v4l2_start */ unsigned char *v4l2_start(struct context *cnt, struct video_dev *viddev, int width, int height, int input, int norm, unsigned long freq, int tuner_number) { src_v4l2_t *vid_source; /* Allocate memory for the state structure. */ if (!(vid_source = calloc(sizeof(src_v4l2_t), 1))) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Out of memory."); goto err; } viddev->v4l2_private = vid_source; vid_source->fd = viddev->fd; vid_source->fps = cnt->conf.frame_limit; vid_source->pframe = -1; vid_source->finish = &cnt->finish; struct config *conf = &cnt->conf; if (v4l2_get_capability(vid_source)) goto err; if (v4l2_select_input(conf, viddev, vid_source, input, norm, freq, tuner_number)) goto err; if (v4l2_set_pix_format(cnt, vid_source, &width, &height)) goto err; if (v4l2_scan_controls(vid_source)) goto err; #if 0 v4l2_set_fps(vid_source); #endif if (v4l2_set_mmap(vid_source)) goto err; viddev->size_map = 0; viddev->v4l_buffers[0] = NULL; viddev->v4l_maxbuffer = 1; viddev->v4l_curbuffer = 0; viddev->v4l_fmt = VIDEO_PALETTE_YUV420P; viddev->v4l_bufsize = (width * height * 3) / 2; /* Update width and height with supported values from camera driver */ viddev->width = width; viddev->height = height; return (void *) 1; err: free(vid_source); viddev->v4l2_private = NULL; viddev->v4l2 = 0; return NULL; } /** * v4l2_set_input */ void v4l2_set_input(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height, struct config *conf) { int input = conf->input; int norm = conf->norm; unsigned long freq = conf->frequency; int tuner_number = conf->tuner_number; if (input != viddev->input || width != viddev->width || height != viddev->height || freq != viddev->freq || tuner_number != viddev->tuner_number || norm != viddev->norm) { unsigned int i; struct timeval switchTime; unsigned int skip = conf->roundrobin_skip; if (conf->roundrobin_skip < 0) skip = 1; v4l2_select_input(conf, viddev, (src_v4l2_t *) viddev->v4l2_private, input, norm, freq, tuner_number); gettimeofday(&switchTime, NULL); v4l2_picture_controls(cnt, viddev); viddev->width = width; viddev->height = height; /* viddev->input = input; viddev->norm = norm; viddev->width = width; viddev->height = height; viddev->freq = freq; viddev->tuner_number = tuner_number; */ /* Skip all frames captured before switchtime, capture 1 after switchtime */ { src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private; unsigned int counter = 0; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: set_input_skip_frame " "switch_time=%ld:%ld", switchTime.tv_sec, switchTime.tv_usec); /* Avoid hang using the number of mmap buffers */ while(counter < vid_source->req.count) { counter++; if (v4l2_next(cnt, viddev, map, width, height)) break; if (vid_source->buf.timestamp.tv_sec > switchTime.tv_sec || (vid_source->buf.timestamp.tv_sec == switchTime.tv_sec && vid_source->buf.timestamp.tv_usec > switchTime.tv_usec)) break; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: got frame before " " switch timestamp=%ld:%ld", vid_source->buf.timestamp.tv_sec, vid_source->buf.timestamp.tv_usec); } } /* skip a few frames if needed */ for (i = 1; i < skip; i++) v4l2_next(cnt, viddev, map, width, height); } else { /* No round robin - we only adjust picture controls */ v4l2_picture_controls(cnt, viddev); } } /** * v4l2_next */ int v4l2_next(struct context *cnt, struct video_dev *viddev, unsigned char *map, int width, int height) { sigset_t set, old; src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private; if (viddev->v4l_fmt != VIDEO_PALETTE_YUV420P) return V4L_FATAL_ERROR; /* 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); MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: 1) vid_source->pframe %i", vid_source->pframe); if (vid_source->pframe >= 0) { if (xioctl(vid_source, VIDIOC_QBUF, &vid_source->buf) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_QBUF"); pthread_sigmask(SIG_UNBLOCK, &old, NULL); return -1; } } memset(&vid_source->buf, 0, sizeof(struct v4l2_buffer)); vid_source->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vid_source->buf.memory = V4L2_MEMORY_MMAP; vid_source->buf.bytesused = 0; if (xioctl(vid_source, VIDIOC_DQBUF, &vid_source->buf) == -1) { int ret; /* * Some drivers return EIO when there is no signal, * driver might dequeue an (empty) buffer despite * returning an error, or even stop capturing. */ if (errno == EIO) { vid_source->pframe++; if ((u32)vid_source->pframe >= vid_source->req.count) vid_source->pframe = 0; vid_source->buf.index = vid_source->pframe; MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_DQBUF: EIO " "(vid_source->pframe %d)", vid_source->pframe); ret = 1; } else if (errno == EAGAIN) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_DQBUF: EAGAIN" " (vid_source->pframe %d)", vid_source->pframe); ret = 1; } else { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: VIDIOC_DQBUF"); ret = -1; } pthread_sigmask(SIG_UNBLOCK, &old, NULL); return ret; } MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: 2) vid_source->pframe %i", vid_source->pframe); vid_source->pframe = vid_source->buf.index; vid_source->buffers[vid_source->buf.index].used = vid_source->buf.bytesused; vid_source->buffers[vid_source->buf.index].content_length = vid_source->buf.bytesused; MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: 3) vid_source->pframe %i " "vid_source->buf.index %i", vid_source->pframe, vid_source->buf.index); MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: vid_source->buf.bytesused %i", vid_source->buf.bytesused); pthread_sigmask(SIG_UNBLOCK, &old, NULL); /*undo the signal blocking */ { video_buff *the_buffer = &vid_source->buffers[vid_source->buf.index]; MOTION_LOG(DBG, TYPE_VIDEO, NO_ERRNO, "%s: the_buffer index %d Address (%x)", vid_source->buf.index, the_buffer->ptr); switch (vid_source->dst_fmt.fmt.pix.pixelformat) { case V4L2_PIX_FMT_RGB24: conv_rgb24toyuv420p(map, the_buffer->ptr, width, height); return 0; case V4L2_PIX_FMT_UYVY: conv_uyvyto420p(map, the_buffer->ptr, (unsigned)width, (unsigned)height); return 0; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_YUV422P: conv_yuv422to420p(map, the_buffer->ptr, width, height); return 0; case V4L2_PIX_FMT_YUV420: memcpy(map, the_buffer->ptr, viddev->v4l_bufsize); return 0; case V4L2_PIX_FMT_PJPG: case V4L2_PIX_FMT_JPEG: case V4L2_PIX_FMT_MJPEG: return mjpegtoyuv420p(map, the_buffer->ptr, width, height, vid_source->buffers[vid_source->buf.index].content_length); /* FIXME: quick hack to allow work all bayer formats */ case V4L2_PIX_FMT_SBGGR16: case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: /* case V4L2_PIX_FMT_SPCA561: */ case V4L2_PIX_FMT_SBGGR8: /* bayer */ bayer2rgb24(cnt->imgs.common_buffer, the_buffer->ptr, width, height); conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height); return 0; case V4L2_PIX_FMT_SPCA561: case V4L2_PIX_FMT_SN9C10X: sonix_decompress(map, the_buffer->ptr, width, height); bayer2rgb24(cnt->imgs.common_buffer, map, width, height); conv_rgb24toyuv420p(map, cnt->imgs.common_buffer, width, height); return 0; } } return 1; } /** * v4l2_close */ void v4l2_close(struct video_dev *viddev) { src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private; enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; xioctl(vid_source, VIDIOC_STREAMOFF, &type); close(vid_source->fd); vid_source->fd = -1; } /** * v4l2_cleanup */ void v4l2_cleanup(struct video_dev *viddev) { src_v4l2_t *vid_source = (src_v4l2_t *) viddev->v4l2_private; if (vid_source->buffers) { unsigned int i; for (i = 0; i < vid_source->req.count; i++) munmap(vid_source->buffers[i].ptr, vid_source->buffers[i].size); free(vid_source->buffers); vid_source->buffers = NULL; } free(vid_source->controls); vid_source->controls = NULL; free(vid_source); viddev->v4l2_private = NULL; } #endif /* !WITHOUT_V4L && MOTION_V4L2 */ motion-release-4.0/video_common.c000066400000000000000000000707461300074623700171420ustar00rootroot00000000000000/* video_common.c * * Video stream functions for motion. * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) * 2006 by Krzysztof Blaszkowski (kb@sysmikro.com.pl) * 2007 by Angel Carpintero (motiondevelop@gmail.com) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. * */ /* For rotation */ #include "rotate.h" /* Already includes motion.h */ #include "video.h" #include "jpegutils.h" typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; #define CLAMP(x) ((x) < 0 ? 0 : ((x) > 255) ? 255 : (x)) typedef struct { int is_abs; int len; int val; } code_table_t; /** * sonix_decompress_init * pre-calculates a locally stored table for efficient huffman-decoding. * * Each entry at index x in the table represents the codeword * present at the MSB of byte x. * */ static void sonix_decompress_init(code_table_t * table) { int i; int is_abs, val, len; for (i = 0; i < 256; i++) { is_abs = 0; val = 0; len = 0; if ((i & 0x80) == 0) { /* code 0 */ val = 0; len = 1; } else if ((i & 0xE0) == 0x80) { /* code 100 */ val = +4; len = 3; } else if ((i & 0xE0) == 0xA0) { /* code 101 */ val = -4; len = 3; } else if ((i & 0xF0) == 0xD0) { /* code 1101 */ val = +11; len = 4; } else if ((i & 0xF0) == 0xF0) { /* code 1111 */ val = -11; len = 4; } else if ((i & 0xF8) == 0xC8) { /* code 11001 */ val = +20; len = 5; } else if ((i & 0xFC) == 0xC0) { /* code 110000 */ val = -20; len = 6; } else if ((i & 0xFC) == 0xC4) { /* code 110001xx: unknown */ val = 0; len = 8; } else if ((i & 0xF0) == 0xE0) { /* code 1110xxxx */ is_abs = 1; val = (i & 0x0F) << 4; len = 8; } table[i].is_abs = is_abs; table[i].val = val; table[i].len = len; } } /** * sonix_decompress * Decompresses an image encoded by a SN9C101 camera controller chip. * * IN width * height * inp pointer to compressed frame (with header already stripped) * OUT outp pointer to decompressed frame * * Returns 0 if the operation was successful. * Returns <0 if operation failed. * */ int sonix_decompress(unsigned char *outp, unsigned char *inp, int width, int height) { int row, col; int val; int bitpos; unsigned char code; unsigned char *addr; /* Local storage */ static code_table_t table[256]; static int init_done = 0; if (!init_done) { init_done = 1; sonix_decompress_init(table); /* Do sonix_decompress_init first! */ //return -1; // so it has been done and now fall through } bitpos = 0; for (row = 0; row < height; row++) { col = 0; /* First two pixels in first two rows are stored as raw 8-bit. */ if (row < 2) { addr = inp + (bitpos >> 3); code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); bitpos += 8; *outp++ = code; addr = inp + (bitpos >> 3); code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); bitpos += 8; *outp++ = code; col += 2; } while (col < width) { /* Get bitcode from bitstream. */ addr = inp + (bitpos >> 3); code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7))); /* Update bit position. */ bitpos += table[code].len; /* Calculate pixel value. */ val = table[code].val; if (!table[code].is_abs) { /* Value is relative to top and left pixel. */ if (col < 2) { /* Left column: relative to top pixel. */ val += outp[-2 * width]; } else if (row < 2) { /* Top row: relative to left pixel. */ val += outp[-2]; } else { /* Main area: average of left pixel and top pixel. */ val += (outp[-2] + outp[-2 * width]) / 2; } } /* Store pixel */ *outp++ = CLAMP(val); col++; } } return 0; } /** * bayer2rgb24 * BAYER2RGB24 ROUTINE TAKEN FROM: * * Sonix SN9C10x based webcam basic I/F routines * Takafumi Mizuno * */ void bayer2rgb24(unsigned char *dst, unsigned char *src, long int width, long int height) { long int i; unsigned char *rawpt, *scanpt; long int size; rawpt = src; scanpt = dst; size = width * height; for (i = 0; i < size; i++) { if (((i / width) & 1) == 0) { // %2 changed to & 1 if ((i & 1) == 0) { /* B */ if ((i > width) && ((i % width) > 0)) { *scanpt++ = *rawpt; /* B */ *scanpt++ = (*(rawpt - 1) + *(rawpt + 1) + *(rawpt + width) + *(rawpt - width)) / 4; /* G */ *scanpt++ = (*(rawpt - width - 1) + *(rawpt - width + 1) + *(rawpt + width - 1) + *(rawpt + width + 1)) / 4; /* R */ } else { /* First line or left column. */ *scanpt++ = *rawpt; /* B */ *scanpt++ = (*(rawpt + 1) + *(rawpt + width)) / 2; /* G */ *scanpt++ = *(rawpt + width + 1); /* R */ } } else { /* (B)G */ if ((i > width) && ((i % width) < (width - 1))) { *scanpt++ = (*(rawpt - 1) + *(rawpt + 1)) / 2; /* B */ *scanpt++ = *rawpt; /* G */ *scanpt++ = (*(rawpt + width) + *(rawpt - width)) / 2; /* R */ } else { /* First line or right column. */ *scanpt++ = *(rawpt - 1); /* B */ *scanpt++ = *rawpt; /* G */ *scanpt++ = *(rawpt + width); /* R */ } } } else { if ((i & 1) == 0) { /* G(R) */ if ((i < (width * (height - 1))) && ((i % width) > 0)) { *scanpt++ = (*(rawpt + width) + *(rawpt - width)) / 2; /* B */ *scanpt++ = *rawpt; /* G */ *scanpt++ = (*(rawpt - 1) + *(rawpt + 1)) / 2; /* R */ } else { /* Bottom line or left column. */ *scanpt++ = *(rawpt - width); /* B */ *scanpt++ = *rawpt; /* G */ *scanpt++ = *(rawpt + 1); /* R */ } } else { /* R */ if (i < (width * (height - 1)) && ((i % width) < (width - 1))) { *scanpt++ = (*(rawpt - width - 1) + *(rawpt - width + 1) + *(rawpt + width - 1) + *(rawpt + width + 1)) / 4; /* B */ *scanpt++ = (*(rawpt - 1) + *(rawpt + 1) + *(rawpt - width) + *(rawpt + width)) / 4; /* G */ *scanpt++ = *rawpt; /* R */ } else { /* Bottom line or right column. */ *scanpt++ = *(rawpt - width - 1); /* B */ *scanpt++ = (*(rawpt - 1) + *(rawpt - width)) / 2; /* G */ *scanpt++ = *rawpt; /* R */ } } } rawpt++; } } /** * conv_yuv422to420p * * */ void conv_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 > 0; 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 > 0; i--) { for (j = width / 2; j > 0; 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; } } /** * conv_uyvyto420p * * */ void conv_uyvyto420p(unsigned char *map, unsigned char *cap_map, unsigned int width, unsigned int height) { uint8_t *pY = map; uint8_t *pU = pY + (width * height); uint8_t *pV = pU + (width * height) / 4; uint32_t uv_offset = width * 2 * sizeof(uint8_t); uint32_t ix, jx; for (ix = 0; ix < height; ix++) { for (jx = 0; jx < width; jx += 2) { uint16_t calc; if ((ix&1) == 0) { calc = *cap_map; calc += *(cap_map + uv_offset); calc /= 2; *pU++ = (uint8_t) calc; } cap_map++; *pY++ = *cap_map++; if ((ix&1) == 0) { calc = *cap_map; calc += *(cap_map + uv_offset); calc /= 2; *pV++ = (uint8_t) calc; } cap_map++; *pY++ = *cap_map++; } } } /** * conv_rgb24toyuv420p * * */ void conv_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 < height; loop++) { for (i = 0; i < width; i += 2) { *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; *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; } } } /** * mjpegtoyuv420p * * Return values * -1 on fatal error * 0 on success * 2 if jpeg lib threw a "corrupt jpeg data" warning. * in this case, "a damaged output image is likely." */ int mjpegtoyuv420p(unsigned char *map, unsigned char *cap_map, int width, int height, unsigned int size) { unsigned char *ptr_buffer; size_t soi_pos = 0; int ret = 0; ptr_buffer = memmem(cap_map, size, "\xff\xd8", 2); if (ptr_buffer != NULL) { /** Some cameras are sending multiple SOIs in the buffer. Move the pointer to the last SOI in the buffer and proceed. */ while (ptr_buffer != NULL && ((size - soi_pos - 1) > 2) ){ soi_pos = ptr_buffer - cap_map; ptr_buffer = memmem(cap_map + soi_pos + 1, size - soi_pos - 1, "\xff\xd8", 2); } if (soi_pos != 0){ MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, "%s: SOI position adjusted by %d bytes.", soi_pos); } memmove(cap_map, cap_map + soi_pos, size - soi_pos); size -= soi_pos; ret = decode_jpeg_raw(cap_map, size, 0, 420, width, height, map, map + (width * height), map + (width * height) + (width * height) / 4); } else { //Buffer does not have a SOI ret = 1; } if (ret == 1) { MOTION_LOG(CRT, TYPE_VIDEO, NO_ERRNO, "%s: Corrupt image ... continue"); ret = 2; } return ret; } #define MAX2(x, y) ((x) > (y) ? (x) : (y)) #define MIN2(x, y) ((x) < (y) ? (x) : (y)) /* Constants used by auto brightness feature * Defined as constant to make it easier for people to tweak code for a * difficult camera. * The experience gained from people could help improving the feature without * adding too many new options. * AUTOBRIGHT_HYSTERESIS sets the minimum the light intensity must change before * we adjust brigtness. * AUTOBRIGHTS_DAMPER damps the speed with which we adjust the brightness * When the brightness changes a lot we step in large steps and as we approach the * target value we slow down to avoid overshoot and oscillations. If the camera * adjusts too slowly decrease the DAMPER value. If the camera oscillates try * increasing the DAMPER value. DAMPER must be minimum 1. * MAX and MIN are the max and min values of brightness setting we will send to * the camera device. */ #define AUTOBRIGHT_HYSTERESIS 10 #define AUTOBRIGHT_DAMPER 5 #define AUTOBRIGHT_MAX 255 #define AUTOBRIGHT_MIN 0 /** * vid_do_autobright */ int vid_do_autobright(struct context *cnt, struct video_dev *viddev) { int brightness_window_high; int brightness_window_low; int brightness_target; int i, j = 0, avg = 0, step = 0; unsigned char *image = cnt->imgs.image_virgin; /* Or cnt->current_image ? */ int make_change = 0; if (cnt->conf.brightness) brightness_target = cnt->conf.brightness; else brightness_target = 128; brightness_window_high = MIN2(brightness_target + AUTOBRIGHT_HYSTERESIS, 255); brightness_window_low = MAX2(brightness_target - AUTOBRIGHT_HYSTERESIS, 1); for (i = 0; i < cnt->imgs.motionsize; i += 101) { avg += image[i]; j++; } avg = avg / j; /* Average is above window - turn down brightness - go for the target. */ if (avg > brightness_window_high) { step = MIN2((avg - brightness_target) / AUTOBRIGHT_DAMPER + 1, viddev->brightness - AUTOBRIGHT_MIN); if (viddev->brightness > step + 1 - AUTOBRIGHT_MIN) { viddev->brightness -= step; make_change = 1; } } else if (avg < brightness_window_low) { /* Average is below window - turn up brightness - go for the target. */ step = MIN2((brightness_target - avg) / AUTOBRIGHT_DAMPER + 1, AUTOBRIGHT_MAX - viddev->brightness); if (viddev->brightness < AUTOBRIGHT_MAX - step) { viddev->brightness += step; make_change = 1; } } return make_change; } /***************************************************************************** Wrappers calling the actual capture routines *****************************************************************************/ #ifndef WITHOUT_V4L /* * Big lock for vid_start to ensure exclusive access to viddevs while adding * devices during initialization of each thread. */ static pthread_mutex_t vid_mutex; /* * Here we setup the viddevs structure which is used globally in the vid_* * functions. */ static struct video_dev *viddevs = NULL; /** * vid_init * * Called from motion.c at the very beginning before setting up the threads. * Function prepares the vid_mutex. */ void vid_init(void) { pthread_mutex_init(&vid_mutex, NULL); } /** * vid_cleanup * * vid_cleanup is called from motion.c when Motion is stopped or restarted. */ void vid_cleanup(void) { pthread_mutex_destroy(&vid_mutex); } #endif /* WITHOUT_V4L */ /** * vid_close * * vid_close is called from motion.c when a Motion thread is stopped or restarted. */ void vid_close(struct context *cnt) { #ifndef WITHOUT_V4L struct video_dev *dev = viddevs; struct video_dev *prev = NULL; #endif /* WITHOUT_V4L */ /* Cleanup the netcam part */ #ifdef HAVE_MMAL if (cnt->mmalcam) { MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, "%s: calling mmalcam_cleanup"); mmalcam_cleanup(cnt->mmalcam); cnt->mmalcam = NULL; return; } else #endif if (cnt->netcam) { MOTION_LOG(INF, TYPE_VIDEO, NO_ERRNO, "%s: calling netcam_cleanup"); netcam_cleanup(cnt->netcam, 0); cnt->netcam = NULL; return; } #ifndef WITHOUT_V4L /* Cleanup the v4l part */ pthread_mutex_lock(&vid_mutex); while (dev) { if (dev->fd == cnt->video_dev) break; prev = dev; dev = dev->next; } pthread_mutex_unlock(&vid_mutex); /* Set it as closed in thread context. */ cnt->video_dev = -1; if (dev == NULL) { MOTION_LOG(CRT, TYPE_VIDEO, NO_ERRNO, "%s: Unable to find video device"); return; } if (--dev->usage_count == 0) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Closing video device %s", dev->video_device); #ifdef MOTION_V4L2 if (dev->v4l2) { v4l2_close(dev); v4l2_cleanup(dev); } else { #endif close(dev->fd); munmap(viddevs->v4l_buffers[0], dev->size_map); #ifdef MOTION_V4L2 } #endif dev->fd = -1; pthread_mutex_lock(&vid_mutex); /* Remove from list */ if (prev == NULL) viddevs = dev->next; else prev->next = dev->next; pthread_mutex_unlock(&vid_mutex); pthread_mutexattr_destroy(&dev->attr); pthread_mutex_destroy(&dev->mutex); free(dev); } else { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Still %d users of video device %s, so we don't close it now", dev->usage_count, dev->video_device); /* * There is still at least one thread using this device * If we own it, release it. */ if (dev->owner == cnt->threadnr) { dev->frames = 0; dev->owner = -1; pthread_mutex_unlock(&dev->mutex); } } #endif /* !WITHOUT_V4L */ } #ifndef WITHOUT_V4L /** * vid_v4lx_start * * Called from vid_start setup the V4L/V4L2 capture device * The function does the following: * * - Setup basic V4L/V4L2 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 during startup calling vid_start * * Returns * device number * -1 if failed to open device. * -3 image dimensions are not modulo 8 */ static int vid_v4lx_start(struct context *cnt) { struct config *conf = &cnt->conf; int fd = -1; struct video_dev *dev; 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 8 so we check * for this first. */ if (conf->width % 8) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: config image width (%d) is not modulo 8", conf->width); return -3; } if (conf->height % 8) { MOTION_LOG(ERR, TYPE_VIDEO, NO_ERRNO, "%s: config image height (%d) is not modulo 8", conf->height); return -3; } 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. */ dev = viddevs; while (dev) { if (!strcmp(conf->video_device, dev->video_device)) { dev->usage_count++; cnt->imgs.type = dev->v4l_fmt; switch (cnt->imgs.type) { case VIDEO_PALETTE_GREY: cnt->imgs.motionsize = width * height; cnt->imgs.size = width * height; break; case VIDEO_PALETTE_YUYV: case VIDEO_PALETTE_RGB24: case VIDEO_PALETTE_YUV422: cnt->imgs.type = VIDEO_PALETTE_YUV420P; case VIDEO_PALETTE_YUV420P: cnt->imgs.motionsize = width * height; cnt->imgs.size = (width * height * 3) / 2; break; } pthread_mutex_unlock(&vid_mutex); return dev->fd; } dev = dev->next; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using videodevice %s and input %d", conf->video_device, conf->input); dev = mymalloc(sizeof(struct video_dev)); dev->video_device = conf->video_device; fd = open(dev->video_device, O_RDWR); if (fd < 0) { MOTION_LOG(ALR, TYPE_VIDEO, SHOW_ERRNO, "%s: Failed to open video device %s", conf->video_device); free(dev); pthread_mutex_unlock(&vid_mutex); return -1; } pthread_mutexattr_init(&dev->attr); pthread_mutex_init(&dev->mutex, &dev->attr); dev->usage_count = 1; dev->fd = fd; dev->input = input; dev->norm = norm; dev->height = height; dev->width = width; dev->freq = frequency; dev->tuner_number = tuner_number; /* * We set brightness, contrast, saturation and hue = 0 so that they only get * set if the config is not zero. */ dev->brightness = 0; dev->contrast = 0; dev->saturation = 0; dev->hue = 0; /* -1 is don't modify, (0 is a valid value) */ dev->power_line_frequency = -1; dev->owner = -1; dev->v4l_fmt = VIDEO_PALETTE_YUV420P; dev->fps = 0; #ifdef MOTION_V4L2 /* First lets try V4L2 and if it's not supported V4L1. */ dev->v4l2 = 1; if (!v4l2_start(cnt, dev, width, height, input, norm, frequency, tuner_number)) { /* * Restore width & height before test with v4l * because could be changed in v4l2_start(). */ dev->width = width; dev->height = height; #endif #if defined(HAVE_LINUX_VIDEODEV_H) && (!defined(WITHOUT_V4L)) if (!v4l_start(dev, width, height, input, norm, frequency, tuner_number)) { close(dev->fd); pthread_mutexattr_destroy(&dev->attr); pthread_mutex_destroy(&dev->mutex); free(dev); pthread_mutex_unlock(&vid_mutex); return -1; } #endif #ifdef MOTION_V4L2 dev->v4l2 = 0; } #endif if (dev->v4l2 == 0) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using V4L1"); } else { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Using V4L2"); /* Update width & height because could be changed in v4l2_start(). */ width = dev->width; height = dev->height; cnt->imgs.width = width; cnt->imgs.height = height; } cnt->imgs.type = dev->v4l_fmt; switch (cnt->imgs.type) { case VIDEO_PALETTE_GREY: cnt->imgs.size = width * height; cnt->imgs.motionsize = width * height; break; case VIDEO_PALETTE_YUYV: case VIDEO_PALETTE_RGB24: case VIDEO_PALETTE_YUV422: cnt->imgs.type = VIDEO_PALETTE_YUV420P; case VIDEO_PALETTE_YUV420P: cnt->imgs.size = (width * height * 3) / 2; cnt->imgs.motionsize = width * height; break; } /* Insert into linked list. */ dev->next = viddevs; viddevs = dev; pthread_mutex_unlock(&vid_mutex); return fd; } #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 8) * - 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 later be changed via http remote control as this would * require major re-memory allocations of all image buffers. * * - if the camera is V4L/V4L2 vid_v4lx_start is called * * Parameters: * cnt Pointer to the context for this thread * * Returns * device number * -1 if failed to open device. * -3 image dimensions are not modulo 8 */ int vid_start(struct context *cnt) { struct config *conf = &cnt->conf; int dev = -1; #ifdef HAVE_MMAL if (conf->mmalcam_name) { dev = mmalcam_start(cnt); if (dev < 0) { mmalcam_cleanup(cnt->mmalcam); cnt->mmalcam = NULL; } } else #endif if (conf->netcam_url) { dev = netcam_start(cnt); if (dev < 0) { netcam_cleanup(cnt->netcam, 1); cnt->netcam = NULL; } } #ifdef WITHOUT_V4L else MOTION_LOG(CRT, TYPE_VIDEO, NO_ERRNO, "%s: You must setup netcam_url"); #else else dev = vid_v4lx_start(cnt); #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 (copy grey image and discard this image) * with bit 1 set Non fatal Netcam error */ int vid_next(struct context *cnt, unsigned char *map) { int ret = -2; struct config *conf = &cnt->conf; #ifdef HAVE_MMAL if (conf->mmalcam_name) { if (cnt->mmalcam == NULL) { return NETCAM_GENERAL_ERROR; } return mmalcam_next(cnt, map); } else #endif if (conf->netcam_url) { if (cnt->video_dev == -1) return NETCAM_GENERAL_ERROR; return netcam_next(cnt, map); } #ifndef WITHOUT_V4L /* * We start a new block so we can make declarations without breaking * gcc 2.95 or older. */ { struct video_dev *dev; int width, height; /* NOTE: Since this is a capture, we need to use capture dimensions. */ width = cnt->rotate_data.cap_width; height = cnt->rotate_data.cap_height; pthread_mutex_lock(&vid_mutex); dev = viddevs; while (dev) { if (dev->fd == cnt->video_dev) break; dev = dev->next; } pthread_mutex_unlock(&vid_mutex); if (dev == NULL) return V4L_FATAL_ERROR; if (dev->owner != cnt->threadnr) { pthread_mutex_lock(&dev->mutex); dev->owner = cnt->threadnr; dev->frames = conf->roundrobin_frames; } #ifdef MOTION_V4L2 if (dev->v4l2) { v4l2_set_input(cnt, dev, map, width, height, conf); ret = v4l2_next(cnt, dev, map, width, height); } else { #endif #if defined(HAVE_LINUX_VIDEODEV_H) && (!defined(WITHOUT_V4L)) v4l_set_input(cnt, dev, map, width, height, conf); ret = v4l_next(dev, map, width, height); #endif #ifdef MOTION_V4L2 } #endif if (--dev->frames <= 0) { dev->owner = -1; dev->frames = 0; pthread_mutex_unlock(&dev->mutex); } /* Rotate the image as specified. */ if (cnt->rotate_data.degrees > 0) rotate_map(cnt, map); } #endif /*WITHOUT_V4L */ return ret; } motion-release-4.0/video_freebsd.c000066400000000000000000001156731300074623700172630ustar00rootroot00000000000000/* video_freebsd.c * * BSD Video stream functions for motion. * Copyright 2004 by Angel Carpintero (motiondevelop@gmail.com) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. * */ /* For rotation */ #include "rotate.h" /* Already includes motion.h */ #include "video_freebsd.h" #ifndef WITHOUT_V4L /* For the v4l stuff: */ #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 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 volatile sig_atomic_t bktr_frame_waiting; //sigset_t sa_mask; static void catchsignal(int sig) { bktr_frame_waiting++; } /* 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; } } /** * rgb24toyuv420p * 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 < height; loop++) { for (i = 0; i < width; i += 2) { *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; *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; } } } /********************************************************************************************* 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORSHUE Error setting hue [%d]", new_hue); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d]", ioctlval); return ioctlval; } static int get_hue(int viddev , int *hue) { signed char ioctlval; if (ioctl(viddev, METEORGHUE, &ioctlval) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORGHUE Error getting hue"); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d]", ioctlval); *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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORSCSAT Error setting saturation [%d]", new_saturation); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d]", ioctlval); return ioctlval; } static int get_saturation(int viddev , int *saturation) { unsigned char ioctlval; if (ioctl(viddev, METEORGCSAT, &ioctlval) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORGCSAT Error getting saturation"); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d]", ioctlval); *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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORSCONT Error setting contrast [%d]", new_contrast); return 0; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d]", ioctlval); return ioctlval; } static int get_contrast(int viddev, int *contrast) { unsigned char ioctlval; if (ioctl(viddev, METEORGCONT, &ioctlval) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORGCONT Error getting contrast"); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d]", ioctlval); *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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORSBRIG brightness [%d]", new_bright); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d]", ioctlval); return ioctlval; } static int get_brightness(int viddev, int *brightness) { unsigned char ioctlval; if (ioctl(viddev, METEORGBRIG, &ioctlval) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORGBRIG getting brightness"); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d]", ioctlval); *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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Error channel %d", ioctlval); return -1; } else { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: channel set to %d", ioctlval); } viddev->channel = new_channel; return 0; } */ /** * set_freq * Sets frequency to tuner */ static int set_freq(struct video_dev *viddev, unsigned long freq) { int tuner_fd = viddev->fd_tuner; int old_audio; MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: Not implemented"); return 0; /* 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: BT848_GAUDIO"); return -1; } if (ioctl(tuner_fd, TVTUNER_SETFREQ, &freq) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: BT848_SAUDIO %i", old_audio); return -1; } } return 0; } /** * set_input * Sets 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 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(ERR, TYPE_VIDEO, NO_ERRNO, "%s: Channel Port %d out of range (0-4)", input); return -1; } actport = portdata[ input ]; if (ioctl(viddev->fd_bktr, METEORSINPUT, &actport) < 0) { if (input != IN_DEFAULT) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORSINPUT %d init", input); return -1; } } else { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORSINPUT %d init", input); return -1; } } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d]", input); return input; } static int set_geometry(struct video_dev *viddev, int width, int height) { struct meteor_geomet geom; int h_max; geom.columns = width; geom.rows = height; geom.oformat = METEOR_GEO_YUV_422 | METEOR_GEO_YUV_12; switch (viddev->norm) { case PAL: h_max = PAL_HEIGHT; break; case NTSC: h_max = NTSC_HEIGHT; break; case SECAM: h_max = SECAM_HEIGHT; break; default: h_max = PAL_HEIGHT; } if (height <= h_max / 2) geom.oformat |= METEOR_GEO_EVEN_ONLY; geom.frames = 1; if (ioctl(viddev->fd_bktr, METEORSETGEO, &geom) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Couldn't set the geometry"); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to [%d/%d] Norm %d", width, height, viddev->norm); return 0; } /** * set_input_format * Sets input format ( PAL, NTSC, SECAM, etc ... ) */ static int set_input_format(struct video_dev *viddev, unsigned 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(WRN, TYPE_VIDEO, NO_ERRNO, "%s: Input format %d out of range (0-2)", newformat); return -1; } format = input_format[newformat]; if (ioctl(viddev->fd_bktr, BT848SFMT, &format) < 0) { MOTION_LOG(WRN, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: BT848SFMT, Couldn't set the input format " "either default"); return -1; } } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: to %d", newformat); return newformat; } /* 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORGSUPPIXFMT getting pixformat %d", i); return -1; } // Hack from xawtv 4.x switch (p.type) { case METEOR_PIXTYPE_RGB: MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: 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(NTC, TYPE_VIDEO, NO_ERRNO, "%s: setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB15"); break; case 63488: // 16 bpp format = p.swap_bytes ? VIDEO_RGB16_LE : VIDEO_RGB16_BE; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: 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(NTC, TYPE_VIDEO, NO_ERRNO, "%s: 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(NTC, TYPE_VIDEO, NO_ERRNO, "%s: 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(EMG, TYPE_VIDEO, NO_ERRNO, "%s: setup_pixelformat METEOR_PIXTYPE_RGB VIDEO_RGB32"); } } break; case METEOR_PIXTYPE_YUV: format = VIDEO_YUV422P; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: setup_pixelformat METEOR_PIXTYPE_YUV"); break; case METEOR_PIXTYPE_YUV_12: format = VIDEO_YUV422P; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: setup_pixelformat METEOR_PIXTYPE_YUV_12"); break; case METEOR_PIXTYPE_YUV_PACKED: format = VIDEO_YUV422P; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: 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(WRN, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORSACTPIXFMT etting pixformat METEOR_PIXTYPE_RGB Bpp == 3"); // Not immediately fatal } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: 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(WRN, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORSACTPIXFMT setting pixformat METEOR_PIXTYPE_YUV_PACKED"); // Not immediately fatal } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: 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 video_dev *viddev, int width, int height, unsigned input, unsigned norm, unsigned long freq) { int dev_bktr = viddev->fd_bktr; struct sigaction act, old; //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; 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(WRN, TYPE_VIDEO, NO_ERRNO, "%s: Not valid Frequency [%lu] for " "Source input [%i]", freq, input); return NULL; } else if (set_freq(viddev, freq) == -1) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: 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 ((dummy = set_input(viddev, input)) == -1) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: set input [%d]", input); return NULL; } viddev->input = dummy; if ((dummy = set_input_format(viddev, norm)) == -1) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: set input format [%d]", norm); return NULL; } viddev->norm = dummy; if (set_geometry(viddev, width, height) == -1) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: set geometry [%d]x[%d]", width, height); return NULL; } /* if (ioctl(dev_bktr, METEORSACTPIXFMT, &pixelformat) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: set encoding method BSD_VIDFMT_I420"); return NULL; } NEEDED !? FIXME if (setup_pixelformat(viddev) == -1) return NULL; */ if (freq) { MOTION_LOG(WRN, TYPE_VIDEO, NO_ERRNO, "%s: 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: BT848SCBUF"); return NULL; } /* Signal handler to know when data is ready to be read() */ memset(&act, 0, sizeof(act)); sigemptyset(&act.sa_mask); act.sa_handler = catchsignal; sigaction(SIGUSR2, &act, &old); dummy = SIGUSR2; //viddev->capture_method = METEOR_CAP_CONTINOUS; //viddev->capture_method = METEOR_CAP_SINGLE; if ((viddev->capture_method == METEOR_CAP_CONTINOUS) && (ioctl(dev_bktr, METEORSSIGNAL, &dummy) < 0)) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: METEORSSIGNAL"); viddev->capture_method = METEOR_CAP_SINGLE; if (ioctl(dev_bktr, METEORCAPTUR, &viddev->capture_method) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORCAPTUR using single method " "Error capturing"); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: METEORCAPTUR using single method " "Error capturing"); } } else { if (ioctl(dev_bktr, METEORCAPTUR, &viddev->capture_method) < 0) { viddev->capture_method = METEOR_CAP_SINGLE; if (ioctl(dev_bktr, METEORCAPTUR, &viddev->capture_method) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: METEORCAPTUR using single method " "Error capturing"); } } } if (viddev->capture_method == METEOR_CAP_CONTINOUS) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: METEORCAPTUR METEOR_CAP_CONTINOUS"); else MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: METEORCAPTUR METEOR_CAP_SINGLE"); // settle , sleep(1) replaced SLEEP(1, 0); /* FIXME */ 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; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: HUE [%d]", get_hue(dev_bktr, &dummy)); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: SATURATION [%d]", get_saturation(dev_bktr, &dummy)); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: BRIGHTNESS [%d]", get_brightness(dev_bktr, &dummy)); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: CONTRAST [%d]", get_contrast(dev_bktr, &dummy)); 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; sigset_t set, old; /* ONLY MMAP method is used to Capture */ /* * Allocates 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 == METEOR_CAP_CONTINOUS) { if (bktr_frame_waiting) bktr_frame_waiting = 0; } else if (ioctl(dev_bktr, METEORCAPTUR, &single) < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: 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; } /** * v4l_set_input * Sets 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 input, unsigned norm, int skip, unsigned long freq) { if (input != viddev->input || norm != viddev->norm || freq != viddev->freq) { int dummy; unsigned long frequnits = freq; if ((dummy = set_input(viddev, input)) == -1) return; viddev->input = dummy; if ((dummy = set_input_format(viddev, norm)) == -1) return; viddev->norm = dummy; if ((viddev->tuner_device != NULL) && (viddev->input == IN_TV) && (frequnits > 0)) { if (set_freq(viddev, freq) == -1) return; } // FIXME /* if (setup_pixelformat(viddev) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCSFREQ)"); return } if (set_geometry(viddev, width, height) == -1) return; */ v4l_picture_controls(cnt, viddev); viddev->freq = freq; /* skip a few frames if needed */ for (dummy = 0; dummy < skip; dummy++) v4l_next(viddev, map, width, height); } else { /* No round robin - we only adjust picture controls */ v4l_picture_controls(cnt, viddev); } } /***************************************************************************** Wrappers calling the current capture routines *****************************************************************************/ /* vid_init - Initi vid_mutex. vid_start - Setup Device parameters ( device , channel , freq , contrast , hue , saturation , brightness ) and open it. vid_next - Capture a frame and set input , contrast , hue , saturation and brightness if necessary. vid_close - close devices. vid_cleanup - Destroy vid_mutex. */ /* * Big lock for vid_start to ensure exclusive access to viddevs while adding * devices during initialization of each thread. */ static pthread_mutex_t vid_mutex; /* * Here we setup the viddevs structure which is used globally in the vid_* * functions. */ static struct video_dev *viddevs = NULL; /** * vid_init * * Called from motion.c at the very beginning before setting up the threads. * Function prepares the vid_mutex. */ void vid_init(void) { pthread_mutex_init(&vid_mutex, NULL); } /** * vid_cleanup * * vid_cleanup is called from motion.c when Motion is stopped or restarted. */ void vid_cleanup(void) { pthread_mutex_destroy(&vid_mutex); } #endif /*WITHOUT_V4L*/ /** * vid_close * * vid_close is called from motion.c when a Motion thread is stopped or restarted. */ void vid_close(struct context *cnt) { #ifndef WITHOUT_V4L struct video_dev *dev = viddevs; struct video_dev *prev = NULL; #endif /* Cleanup the netcam part */ if (cnt->netcam) { netcam_cleanup(cnt->netcam, 0); cnt->netcam = NULL; return; } #ifndef WITHOUT_V4L /* Cleanup the v4l part */ pthread_mutex_lock(&vid_mutex); while (dev) { if (dev->fd_bktr == cnt->video_dev) break; prev = dev; dev = dev->next; } pthread_mutex_unlock(&vid_mutex); /* Set it as closed in thread context. */ cnt->video_dev = -1; if (dev == NULL) { MOTION_LOG(CRT, TYPE_VIDEO, NO_ERRNO, "%s: Unable to find video device"); return; } if (--dev->usage_count == 0) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Closing video device %s", dev->video_device); if (dev->fd_tuner > 0) close(dev->fd_tuner); if (dev->fd_bktr > 0) { if (dev->capture_method == METEOR_CAP_CONTINOUS) { dev->fd_tuner = METEOR_CAP_STOP_CONT; ioctl(dev->fd_bktr, METEORCAPTUR, &dev->fd_tuner); } close(dev->fd_bktr); dev->fd_tuner = -1; } munmap(viddevs->v4l_buffers[0], viddevs->v4l_bufsize); viddevs->v4l_buffers[0] = MAP_FAILED; dev->fd_bktr = -1; pthread_mutex_lock(&vid_mutex); /* Remove from list */ if (prev == NULL) viddevs = dev->next; else prev->next = dev->next; pthread_mutex_unlock(&vid_mutex); pthread_mutexattr_destroy(&dev->attr); pthread_mutex_destroy(&dev->mutex); free(dev); } else { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Still %d users of video device %s, " "so we don't close it now", dev->usage_count, dev->video_device); /* * There is still at least one thread using this device * If we own it, release it. */ if (dev->owner == cnt->threadnr) { dev->frames = 0; dev->owner = -1; pthread_mutex_unlock(&dev->mutex); } } #endif /* !WITHOUT_V4L */ } /** * vid_start * */ int vid_start(struct context *cnt) { struct config *conf = &cnt->conf; int fd_bktr = -1; if (conf->netcam_url) { fd_bktr = netcam_start(cnt); if (fd_bktr < 0) { netcam_cleanup(cnt->netcam, 1); cnt->netcam = NULL; } } #ifdef WITHOUT_V4L else MOTION_LOG(CRT, TYPE_VIDEO, NO_ERRNO, "%s: You must setup netcam_url"); #else else { struct video_dev *dev; int fd_tuner = -1; int width, height, capture_method; unsigned input, norm; unsigned long frequency; MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: [%s]", conf->video_device); /* * 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 8 so we check for this. */ if (conf->width % 8) { MOTION_LOG(CRT, TYPE_VIDEO, NO_ERRNO, "%s: config image width (%d) is not modulo 8", conf->width); return -1; } if (conf->height % 8) { MOTION_LOG(CRT, TYPE_VIDEO, NO_ERRNO, "%s: config image height (%d) is not modulo 8", conf->height); return -1; } width = conf->width; height = conf->height; input = conf->input; norm = conf->norm; frequency = conf->frequency; capture_method = METEOR_CAP_CONTINOUS; 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. */ dev = viddevs; while (dev) { if (!strcmp(conf->video_device, dev->video_device)) { int dummy = METEOR_CAP_STOP_CONT; dev->usage_count++; cnt->imgs.type = dev->v4l_fmt; if (ioctl(dev->fd_bktr, METEORCAPTUR, &dummy) < 0) { MOTION_LOG(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s Stopping capture"); return -1; } MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s Reusing [%s] inputs [%d,%d] Change " "capture method METEOR_CAP_SINGLE", dev->video_device, dev->input, conf->input); dev->capture_method = METEOR_CAP_SINGLE; 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(NTC, TYPE_VIDEO, NO_ERRNO, "%s VIDEO_PALETTE_YUV420P setting" " imgs.size and imgs.motionsize"); cnt->imgs.motionsize = width * height; cnt->imgs.size = (width * height * 3) / 2; break; } pthread_mutex_unlock(&vid_mutex); return dev->fd_bktr; // FIXME return fd_tuner ?! } dev = dev->next; } dev = mymalloc(sizeof(struct video_dev)); fd_bktr = open(conf->video_device, O_RDWR); if (fd_bktr < 0) { MOTION_LOG(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s: open video device %s", conf->video_device); free(dev); pthread_mutex_unlock(&vid_mutex); 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(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s: open tuner device %s", conf->tuner_device); free(dev); pthread_mutex_unlock(&vid_mutex); return -1; } } pthread_mutexattr_init(&dev->attr); pthread_mutex_init(&dev->mutex, &dev->attr); dev->usage_count = 1; dev->video_device = conf->video_device; dev->tuner_device = conf->tuner_device; dev->fd_bktr = fd_bktr; dev->fd_tuner = fd_tuner; dev->input = input; dev->height = height; dev->width = width; dev->freq = frequency; dev->owner = -1; dev->capture_method = capture_method; /* * We set brightness, contrast, saturation and hue = 0 so that they only get * set if the config is not zero. */ dev->brightness = 0; dev->contrast = 0; dev->saturation = 0; dev->hue = 0; dev->owner = -1; /* Default palette */ dev->v4l_fmt = VIDEO_PALETTE_YUV420P; dev->v4l_curbuffer = 0; dev->v4l_maxbuffer = 1; if (!v4l_start(dev, width, height, input, norm, frequency)) { close(dev->fd_bktr); pthread_mutexattr_destroy(&dev->attr); pthread_mutex_destroy(&dev->mutex); free(dev); pthread_mutex_unlock(&vid_mutex); return -1; } cnt->imgs.type = dev->v4l_fmt; switch (cnt->imgs.type) { case VIDEO_PALETTE_GREY: cnt->imgs.size = width * height; cnt->imgs.motionsize = width * height; break; case VIDEO_PALETTE_RGB24: case VIDEO_PALETTE_YUV422: cnt->imgs.type = VIDEO_PALETTE_YUV420P; case VIDEO_PALETTE_YUV420P: MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: VIDEO_PALETTE_YUV420P imgs.type"); cnt->imgs.size = (width * height * 3) / 2; cnt->imgs.motionsize = width * height; break; } /* Insert into linked list */ dev->next = viddevs; viddevs = dev; 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 struct video_dev *dev; 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; pthread_mutex_lock(&vid_mutex); dev = viddevs; while (dev) { if (dev->fd_bktr == dev_bktr) break; dev = dev->next; } pthread_mutex_unlock(&vid_mutex); if (dev == NULL) return V4L_FATAL_ERROR; if (dev->owner != cnt->threadnr) { pthread_mutex_lock(&dev->mutex); dev->owner = cnt->threadnr; dev->frames = conf->roundrobin_frames; } v4l_set_input(cnt, dev, map, width, height, conf->input, conf->norm, conf->roundrobin_skip, conf->frequency); ret = v4l_next(dev, map, width, height); if (--dev->frames <= 0) { dev->owner = -1; dev->frames = 0; pthread_mutex_unlock(&dev->mutex); } /* Rotate the image as specified */ if (cnt->rotate_data.degrees > 0) rotate_map(cnt, map); #endif /* !WITHOUT_V4L */ return ret; } motion-release-4.0/video_freebsd.h000066400000000000000000000067511300074623700172640ustar00rootroot00000000000000/* * video_freebsd.h * * Include file for video_freebsd.c * Copyright 2004 by Angel Carpintero (motiondevelop@gmail.com) * 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 #ifdef __NetBSD__ #include #elif __OpenBSD__ #include #elif defined(OLD_BKTR) #include #include #else #include #include #endif #endif /* !WITHOUT_V4L */ /* 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 PAL 0 #define NTSC 1 #define SECAM 2 #define PAL_NC 3 #define PAL_HEIGHT 576 #define SECAM_HEIGHT 576 #define NTSC_HEIGHT 480 #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/tuner0" struct video_dev { struct video_dev *next; int usage_count; int fd_bktr; int fd_tuner; const char *video_device; const char *tuner_device; unsigned input; unsigned 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 *); void vid_close(struct context *); #ifndef WITHOUT_V4L void vid_init(void); void vid_cleanup(void); #endif #endif /* _INCLUDE_VIDEO_FREEBSD_H */ motion-release-4.0/vloopback_motion.c000066400000000000000000000155301300074623700200170ustar00rootroot00000000000000/* * vloopback_motion.c * * Video loopback functions for motion. * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) * Copyright 2008 by Angel Carpintero (motiondevelop@gmail.com) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. * */ #include "vloopback_motion.h" #if defined(HAVE_LINUX_VIDEODEV_H) && (!defined(WITHOUT_V4L)) && (!defined(BSD)) #include #include /** * v4l_open_vidpipe * */ static int v4l_open_vidpipe(void) { int pipe_fd = -1; char pipepath[255]; char buffer[255]; char *major; char *minor; struct utsname uts; if (uname(&uts) < 0) { MOTION_LOG(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s: Unable to execute uname"); return -1; } major = strtok(uts.release, "."); minor = strtok(NULL, "."); if ((major == NULL) || (minor == NULL) || (strcmp(major, "2"))) { MOTION_LOG(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s: Unable to decipher OS version"); return -1; } if (strcmp(minor, "5") < 0) { FILE *vloopbacks; char *input; char *istatus; char *output; vloopbacks = fopen("/proc/video/vloopback/vloopbacks", "r"); if (!vloopbacks) { MOTION_LOG(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s: Failed to open " "'/proc/video/vloopback/vloopbacks'"); return -1; } /* Read vloopback version*/ if (!fgets(buffer, sizeof(buffer), vloopbacks)) { MOTION_LOG(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s: Unable to read vloopback version"); myfclose(vloopbacks); return -1; } fprintf(stderr, "\t%s", buffer); /* Read explanation line */ if (!fgets(buffer, sizeof(buffer), vloopbacks)) { MOTION_LOG(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s: Unable to read vloopback" " explanation line"); myfclose(vloopbacks); return -1; } while (fgets(buffer, sizeof(buffer), vloopbacks)) { if (strlen(buffer) > 1) { buffer[strlen(buffer)-1] = 0; input = strtok(NULL, "\t"); istatus = strtok(NULL, "\t"); output = strtok(NULL, "\t"); if (istatus[0] == '-') { snprintf(pipepath, sizeof(pipepath), "/dev/%s", input); pipe_fd = open(pipepath, O_RDWR); if (pipe_fd >= 0) { MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: \tInput: /dev/%s " "\tOutput: /dev/%s", input, output); break; } } } } myfclose(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(CRT, TYPE_VIDEO, SHOW_ERRNO, "%s: Failed to open '%s'", prefix); return -1; } while ((dirp = readdir(dir)) != NULL) { if (!strncmp(dirp->d_name, "video", 5)) { strncpy(buffer, prefix, sizeof(buffer)); strncat(buffer, dirp->d_name, sizeof(buffer) - strlen(buffer)); strncat(buffer, "/name", sizeof(buffer) - strlen(buffer)); if ((fd = open(buffer, O_RDONLY)) >= 0) { if ((read(fd, buffer, sizeof(buffer)-1)) < 0) { close(fd); continue; } ptr = strtok(buffer, " "); if (strcmp(ptr, "Video")) { close(fd); continue; } major = strtok(NULL, " "); minor = strtok(NULL, " "); io = strtok(NULL, " \n"); if (strcmp(major, "loopback") || strcmp(io, "input")) { close(fd); continue; } if ((ptr = strtok(buffer, " ")) == NULL) { close(fd); continue; } tnum = atoi(minor); if (tnum < low) { mystrcpy(buffer, "/dev/"); strncat(buffer, dirp->d_name, sizeof(buffer) - strlen(buffer)); if ((tfd = open(buffer, O_RDWR)) >= 0) { strncpy(pipepath, buffer, sizeof(pipepath)); if (pipe_fd >= 0) close(pipe_fd); pipe_fd = tfd; low = tnum; } } close(fd); } } } closedir(dir); if (pipe_fd >= 0) MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Opened %s as input", pipepath); } return pipe_fd; } /** * v4l_startpipe * */ static int v4l_startpipe(const char *dev_name, int width, int height, int type) { int dev; struct video_picture vid_pic; struct video_window vid_win; if (!strcmp(dev_name, "-")) { dev = v4l_open_vidpipe(); } else { dev = open(dev_name, O_RDWR); MOTION_LOG(NTC, TYPE_VIDEO, NO_ERRNO, "%s: Opened %s as input", dev_name); } if (dev < 0) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: Opening %s as input failed", dev_name); return -1; } if (ioctl(dev, VIDIOCGPICT, &vid_pic) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCGPICT)"); return -1; } vid_pic.palette = type; if (ioctl(dev, VIDIOCSPICT, &vid_pic) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCSPICT)"); return -1; } if (ioctl(dev, VIDIOCGWIN, &vid_win) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCGWIN)"); return -1; } vid_win.height = height; vid_win.width = width; if (ioctl(dev, VIDIOCSWIN, &vid_win) == -1) { MOTION_LOG(ERR, TYPE_VIDEO, SHOW_ERRNO, "%s: ioctl (VIDIOCSWIN)"); return -1; } return dev; } /** * v4l_putpipe * */ static int v4l_putpipe(int dev, unsigned char *image, int size) { return write(dev, image, size); } /** * vid_startpipe * */ int vid_startpipe(const char *dev_name, int width, int height, int type) { return v4l_startpipe(dev_name, width, height, type); } /** * vid_putpipe * */ int vid_putpipe (int dev, unsigned char *image, int size) { return v4l_putpipe(dev, image, size); } #endif /* !WITHOUT_V4L && !BSD */ motion-release-4.0/vloopback_motion.h000066400000000000000000000010321300074623700200140ustar00rootroot00000000000000/* vloopback_motion.h * * Include file for vloopback_motion.c * Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org) * Copyright 2008 by Angel Carpintero (motiondevelop@gmail.com) * This software is distributed under the GNU public license version 2 * See also the file 'COPYING'. * */ #ifndef _INCLUDE_VLOOPBACK_MOTION_H #define _INCLUDE_VLOOPBACK_MOTION_H #include "motion.h" int vid_startpipe(const char *dev_name, int width, int height, int); int vid_putpipe(int dev, unsigned char *image, int); #endif motion-release-4.0/webhttpd.c000066400000000000000000003563141300074623700163030ustar00rootroot00000000000000/* * webhttpd.c * * HTTP Control interface for motion. * * Specs : http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpAPI * * Copyright 2004-2005 by Angel Carpintero (motiondevelop@gmail.com) * This software is distributed under the GNU Public License Version 2 * See also the file 'COPYING'. * */ #include "webhttpd.h" /* already includes motion.h */ #include #include #include #include #include /* Timeout in seconds, used for read and write */ const int NONBLOCK_TIMEOUT = 1; pthread_mutex_t httpd_mutex; // This is a dummy variable use to kill warnings when not checking sscanf and similar functions int warningkill; static const char *ini_template = "\n" "\n" "Motion "VERSION"\n" "\n"; static const char *set_template = "\n" "\n" "\n" "Motion "VERSION"\n" "\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" "\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" "\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" "\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" "\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" "\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" "\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" "\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" "\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" "\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"; /** * write_nonblock */ static ssize_t write_nonblock(int fd, const void *buf, size_t size) { ssize_t nwrite = -1; struct timeval tm; fd_set fds; tm.tv_sec = NONBLOCK_TIMEOUT; tm.tv_usec = 0; FD_ZERO(&fds); FD_SET(fd, &fds); if (select(fd + 1, NULL, &fds, NULL, &tm) > 0) { if (FD_ISSET(fd, &fds)) { if ((nwrite = write(fd , buf, size)) < 0) { if (errno != EWOULDBLOCK) return -1; } } } return nwrite; } /** * read_nonblock */ static ssize_t read_nonblock(int fd ,void *buf, ssize_t size) { ssize_t nread = -1; struct timeval tm; fd_set fds; tm.tv_sec = NONBLOCK_TIMEOUT; /* Timeout in seconds */ tm.tv_usec = 0; FD_ZERO(&fds); FD_SET(fd, &fds); if (select(fd + 1, &fds, NULL, NULL, &tm) > 0) { if (FD_ISSET(fd, &fds)) { if ((nread = read(fd , buf, size)) < 0) { if (errno != EWOULDBLOCK) return -1; } } } return nread; } /** * send_template_ini_client */ static void send_template_ini_client(int client_socket, const char *template) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, ok_response, strlen(ok_response)); nwrite += write_nonblock(client_socket, template, strlen(template)); if (nwrite != (ssize_t)(strlen(ok_response) + strlen(template))) MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: failure write"); } /** * send_template_ini_client_raw */ static void send_template_ini_client_raw(int client_socket) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, ok_response_raw, strlen(ok_response_raw)); if (nwrite != (ssize_t)strlen(ok_response_raw)) MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: failure write"); } /** * send_template */ static void send_template(int client_socket, char *res) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, res, strlen(res)); if (nwrite != (ssize_t)strlen(res)) MOTION_LOG(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: failure write"); } /** * send_template_raw */ static void send_template_raw(int client_socket, char *res) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, res, strlen(res)); if (nwrite < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write_nonblock returned value less than zero."); } /** * send_template_end_client */ static void send_template_end_client(int client_socket) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, end_template, strlen(end_template)); if (nwrite < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write_nonblock returned value less than zero."); } /** * response_client */ static void response_client(int client_socket, const char *template, char *back) { ssize_t nwrite = 0; nwrite = write_nonblock(client_socket, template, strlen(template)); if (back != NULL) { send_template(client_socket, back); send_template_end_client(client_socket); } if (nwrite < 0) MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: write_nonblock returned value less than zero."); } /** * replace */ static char *replace(const char *str, const char *old, const char *new) { char *ret, *r; const char *p, *q; size_t oldlen = strlen(old); size_t count, retlen, newlen = strlen(new); if (oldlen != newlen) { for (count = 0, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) count++; /* this is undefined if p - str > PTRDIFF_MAX */ retlen = p - str + strlen(p) + count * (newlen - oldlen); } else retlen = strlen(str); ret = mymalloc(retlen + 1); for (r = ret, p = str; (q = strstr(p, old)) != NULL; p = q + oldlen) { /* this is undefined if q - p > PTRDIFF_MAX */ ptrdiff_t l = q - p; memcpy(r, p, l); r += l; memcpy(r, new, newlen); r += newlen; } strcpy(r, p); return ret; } /** * url_decode * This function decode the values from GET request following the http RFC. * * Returns nothing. */ static void url_decode(char *urlencoded, size_t 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 == '<' || *data == '+' || *data == '>') { *urldecoded++ = ' '; } else { *urldecoded++ = *data; } data++; length--; } *urldecoded = '\0'; } /** * config * Manages/parses the config action for motion ( set , get , write , list ). * * Returns 1 to exit from function. */ static unsigned int config(char *pointer, char *res, unsigned int length_uri, unsigned int thread, int client_socket, void *userdata) { char question='\0'; char command[256] = {'\0'}; unsigned int i; struct context **cnt = userdata; warningkill = sscanf(pointer, "%255[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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s\n
    ", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); for (i=0; config_params[i].param_name != NULL; i++) { if ((thread != 0) && (config_params[i].main_thread)) continue; 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 = mystrdup("No threads"); } else { char *temp = retval; size_t retval_miss = 0; size_t retval_len = strlen(retval); unsigned 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 = mystrdup(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.webcontrol_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,"%255[-0-9a-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 ((thread != 0) && (config_params[i].main_thread)) { i++; continue; } 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,"%1023s", Value); length_uri = length_uri - strlen(Value); if ((length_uri == 0) && (strlen(Value) > 0)) { /* FIXME need to assure that is a valid value */ url_decode(Value, strlen(Value)); conf_cmdparse(cnt + thread, config_params[i].param_name, Value); if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back" "

\nCamera %d%s%s\n" "
  • %s = %s" "
Done", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", thread, config_params[i].param_name, config_params[i].param_name, Value); 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.webcontrol_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 = mystrdup(config_type(&config_params[i])); if (!strcmp(type, "string")) { char *value = NULL; conf_cmdparse(cnt+thread, config_params[i].param_name, value); free(type); type = mystrdup("(null)"); } else if (!strcmp(type, "int")) { free(type); type = mystrdup("0"); conf_cmdparse(cnt+thread, config_params[i].param_name, type); } else if (!strcmp(type, "bool")) { free(type); type = mystrdup("off"); conf_cmdparse(cnt+thread, config_params[i].param_name, type); } else { free(type); type = mystrdup("unknown"); } if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

\n" "Camera %d%s%s\n
Done", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", thread, config_params[i].param_name, config_params[i].param_name, type); 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.webcontrol_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.webcontrol_html_output) && (command[0] != '\0') && (((length_uri = length_uri - strlen(command)) == 0))) { i=0; while (config_params[i].param_name != NULL) { if ((thread != 0) && (config_params[i].main_thread)) { i++; continue; } if (!strcasecmp(command, config_params[i].param_name)) break; i++; } /* param_name exists */ if (config_params[i].param_name) { const char *value = NULL; char *text_help = NULL; char *sharp = NULL; value = config_params[i].print(cnt, NULL, i, thread); sharp = strstr(config_params[i].param_help, "#\n\n#"); if (sharp == NULL) sharp = strstr(config_params[i].param_help, "#"); sharp++; text_help = replace(sharp, "\n#", "
"); send_template_ini_client(client_socket, ini_template); if (!strcmp ("bool", config_type(&config_params[i]))) { char option[80] = {'\0'}; if ((value == NULL) && (thread != 0)) value = config_params[i].print(cnt, NULL, i, 0); if (!strcmp ("on", value)) sprintf(option, "\n" "\n"); else sprintf(option, "\n" "\n"); sprintf(res, "<– back

\n" "Camera %d%s%s\n" "
\n" "%s \n" "    " "[help]" "
\n
%s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", config_params[i].param_name, config_params[i].param_name, option, TWIKI_URL, config_params[i].param_name, text_help); } else { if (value == NULL) { if (thread != 0) /* get the value from main thread for the rest of threads */ value = config_params[i].print(cnt, NULL, i, 0); if (value == NULL) value = ""; } sprintf(res, "<– back

\n" "Camera %d%s%s\n
\n" "%s \n" "\n" "    " "[help]" "
\n
%s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", config_params[i].param_name, config_params[i].param_name, value, TWIKI_URL, config_params[i].param_name, text_help); } send_template(client_socket, res); send_template_end_client(client_socket); free(text_help); } else { if (cnt[0]->conf.webcontrol_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.webcontrol_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.webcontrol_html_output) { send_template_ini_client(client_socket, set_template); sprintf(res, "<– back

\nCamera %d%s%s\n" "
\n\n
\n" "
\n" "\n" "\n" "
\n"); 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.webcontrol_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 minimum length param_name */ pointer++; length_uri--; warningkill = sscanf(pointer,"%255[-0-9a-z]%c", command, &question); if ((question == '=') && (!strcmp(command, "query"))) { pointer = pointer + 6; length_uri = length_uri - 6; warningkill = sscanf(pointer, "%255[-0-9a-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 ((thread != 0) && (config_params[i].main_thread)) { i++; continue; } 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.webcontrol_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 { char *text_help = NULL; char *sharp = NULL; value = config_params[i].print(cnt, NULL, i, thread); sharp = strstr(config_params[i].param_help, "#\n\n#"); if (sharp == NULL) sharp = strstr(config_params[i].param_help, "#"); sharp++; text_help = replace(sharp, "\n#", "
"); if (value == NULL) value = config_params[i].print(cnt, NULL, i, 0); if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n
%s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", config_params[i].param_name, value, TWIKI_URL, config_params[i].param_name, text_help); send_template(client_socket, res); send_template_end_client(client_socket); free(text_help); } 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.webcontrol_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.webcontrol_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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s
\n" "
\n" "\n" "\n" "
\n"); 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.webcontrol_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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Are you sure? Yes\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, "Camera %d write\nDone\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s write done !\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d write\nDone\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_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.webcontrol_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; } /** * action * manages/parses the actions for motion ( makemovie , snapshot , restart , quit ). * * Returns * 0 for restart & quit * 1 for makemovie & snaphost */ static unsigned int action(char *pointer, char *res, unsigned int length_uri, unsigned int thread, int client_socket, void *userdata) { /* parse action commands */ char command[256] = {'\0'}; struct context **cnt = userdata; unsigned int i = 0; warningkill = sscanf(pointer, "%255[a-z]" , command); if (!strcmp(command, "makemovie")) { pointer = pointer + 9; length_uri = length_uri - 9; if (length_uri == 0) { /* call makemovie */ if (thread == 0) { while (cnt[++i]) cnt[i]->makemovie = 1; } else { cnt[thread]->makemovie = 1; } if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "makemovie for thread %hu done
\n", thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "makemovie for thread %hu\nDone\n", thread); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_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) { while (cnt[++i]) cnt[i]->snapshot = 1; } else { cnt[thread]->snapshot = 1; } cnt[thread]->snapshot = 1; if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "snapshot for thread %hu done
\n", thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "snapshot for thread %hu\nDone\n", thread); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_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) { /* call restart */ if (thread == 0) { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: httpd is going to restart"); kill(getpid(),SIGHUP); if (cnt[0]->conf.webcontrol_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 { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: httpd is going to restart thread %d", thread); if (cnt[thread]->running) { cnt[thread]->makemovie = 1; cnt[thread]->finish = 1; } cnt[thread]->restart = 1; if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "restart for thread %hu done
\n", thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "restart for thread %hu\nDone\n", thread); send_template_raw(client_socket, res); } } } else { if (cnt[0]->conf.webcontrol_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) { /* call quit */ if (thread == 0) { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: httpd quits"); kill(getpid(),SIGQUIT); if (cnt[0]->conf.webcontrol_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 { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: httpd quits thread %d", thread); cnt[thread]->restart = 0; cnt[thread]->makemovie = 1; cnt[thread]->finish = 1; cnt[thread]->watchdog = WATCHDOG_OFF; if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "quit for thread %hu done
\n", thread, thread); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "quit for thread %hu\nDone\n", thread); send_template_raw(client_socket, res); } } } else { /* error */ if (cnt[0]->conf.webcontrol_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.webcontrol_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; } /** * detection * manages/parses the detection actions for motion ( status , start , pause ). * * Returns * 1 to exit from function. */ static unsigned int detection(char *pointer, char *res, unsigned int length_uri, unsigned int thread, int client_socket, void *userdata) { char command[256] = {'\0'}; struct context **cnt = userdata; unsigned int i = 0; warningkill = sscanf(pointer, "%255[a-z]" , command); if (!strcmp(command, "status")) { pointer = pointer + 6; length_uri = length_uri - 6; if (length_uri == 0) { /* call status */ if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

Camera %d%s%s" " Detection status %s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (!cnt[thread]->running)? "NOT RUNNING": (cnt[thread]->pause)? "PAUSE":"ACTIVE"); send_template(client_socket, res); send_template_end_client(client_socket); } else { sprintf(res, "Camera %d Detection status %s\n", cnt[thread]->conf.camera_id, (!cnt[thread]->running)? "NOT RUNNING": (cnt[thread]->pause)? "PAUSE":"ACTIVE"); send_template_ini_client_raw(client_socket); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_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 */ if (thread == 0) { do { cnt[i]->pause = 0; } while (cnt[++i]); } else { cnt[thread]->pause = 0; } if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s" " Detection resumed\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d Detection resumed\nDone\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_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 */ if (thread == 0) { do { cnt[i]->pause = 1; } while (cnt[++i]); } else { cnt[thread]->pause = 1; } if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s" " Detection paused\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Camera %d%s%s Detection paused\nDone\n", cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template_raw(client_socket, res); } } else { /* error */ if (cnt[0]->conf.webcontrol_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, "connection")) { pointer = pointer + 10; length_uri = length_uri - 10; if (length_uri == 0) { /* call connection */ if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n", thread); send_template(client_socket, res); if (thread == 0) { do{ sprintf(res, "Camera %d%s%s %s
\n", cnt[i]->conf.camera_id, cnt[i]->conf.camera_name ? " -- " : "", cnt[i]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (!cnt[i]->running)? "NOT RUNNING" : (cnt[i]->lost_connection)?CONNECTION_KO:CONNECTION_OK); send_template(client_socket, res); } while (cnt[++i]); } else { sprintf(res, "Camera %d%s%s %s\n", cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (!cnt[thread]->running)? "NOT RUNNING" : (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK); send_template(client_socket, res); } send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); if (thread == 0) { do { sprintf(res, "Camera %d %s\n", cnt[i]->conf.camera_id, (!cnt[i]->running)? "NOT RUNNING" : (cnt[i]->lost_connection)? CONNECTION_KO: CONNECTION_OK); send_template_raw(client_socket, res); } while (cnt[++i]); } else { sprintf(res, "Camera %d %s\n", cnt[thread]->conf.camera_id, (!cnt[thread]->running)? "NOT RUNNING" : (cnt[thread]->lost_connection)? CONNECTION_KO: CONNECTION_OK); send_template_raw(client_socket, res); } } } else { /* error */ if (cnt[0]->conf.webcontrol_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.webcontrol_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; } /** * track * manages/parses the track action for motion ( set , pan , tilt , auto ). * * Returns * 1 to exit from function. */ static unsigned int track(char *pointer, char *res, unsigned int length_uri, unsigned int thread, int client_socket, void *userdata) { char question='\0'; char command[256] = {'\0'}; struct context **cnt = userdata; warningkill = sscanf(pointer, "%255[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, "%255[a-z]%c" , command, &question); if ((question != '=') || (command[0] == '\0')) { /* no valid syntax */ MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 1"); if (cnt[0]->conf.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 2"); if (cnt[0]->conf.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 3"); /* error value */ if (cnt[0]->conf.webcontrol_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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track set relative pan=%s
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", panvalue); 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.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); 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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track set relative tilt=%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", tiltvalue); 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.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); 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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track set absolute x=%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", x_value); 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.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); 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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track set absolute y=%s
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", y_value); 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.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); 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%255[a-z]" , &question, command); if ((question != '&') || (command[0] == '\0')) { MOTION_LOG(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 4"); if (strstr(pointer, "&")) { if (cnt[0]->conf.webcontrol_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.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 5"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 6"); if (cnt[0]->conf.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 7"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 8"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 9"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 10"); /* no valid syntax */ if (cnt[0]->conf.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 11"); if (cnt[0]->conf.webcontrol_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(INF, TYPE_STREAM, NO_ERRNO, "%s: httpd debug race 12"); if (cnt[0]->conf.webcontrol_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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track absolute set x=%s y=%s
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", x_value, y_value); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "track absolute 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.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); 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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
\n" "track relative pan=%s tilt=%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", panvalue, tiltvalue); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "track relative 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.webcontrol_html_output) { sprintf(res, "<– back

" "Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); response_client(client_socket, track_error, res); } else { response_client(client_socket, track_error_raw, NULL); } } } /* error in track pan */ if (cnt[0]->conf.webcontrol_html_output) { sprintf(res, "<– back

Camera %d%s%s\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); 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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s
\n" "
\n" "Pan\n" "Tilt\n" "\n" "
\n" "
\n" "X\n" "Y\n" "\n" "
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); 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.webcontrol_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, "center")) { pointer = pointer+6; length_uri = length_uri-6; if (length_uri==0) { struct context *setcnt; setcnt = cnt[thread]; // 1000 is out of range for pwc cnt[thread]->moved = track_center(setcnt, setcnt->video_dev, 1, 0, 0); if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s" "
track set center", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : ""); send_template(client_socket, res); send_template_end_client(client_socket); } else { sprintf(res, "Camera %d\n track set center\nDone\n", cnt[thread]->conf.camera_id); send_template_ini_client_raw(client_socket); send_template_raw(client_socket, res); } } else { /* error not valid command */ if (cnt[0]->conf.webcontrol_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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s" "
track auto %s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (cnt[thread]->track.active)? "enabled":"disabled"); send_template(client_socket, res); send_template_end_client(client_socket); } else { sprintf(res, "Camera %d\n track auto %s\nDone\n", cnt[thread]->conf.camera_id, (cnt[thread]->track.active)? "enabled":"disabled"); send_template_ini_client_raw(client_socket); send_template_raw(client_socket, res); } } else { /* error not valid command */ if (cnt[0]->conf.webcontrol_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, "%255[a-z]%c", query, &question); if ((question == '=') && (!strcmp(query, "value"))) { pointer = pointer + 6; length_uri = length_uri - 6; warningkill = sscanf(pointer, "%255[-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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s
" "track auto %s", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (cnt[thread]->track.active)? "enabled":"disabled"); send_template(client_socket, res); send_template_end_client(client_socket); } else { sprintf(res, "Camera %d\n track auto %s\nDone\n", cnt[thread]->conf.camera_id, (cnt[thread]->track.active)? "enabled":"disabled"); send_template_ini_client_raw(client_socket); send_template_raw(client_socket, res); } } else { int active; active = atoi(command); /* CHECK */ if (active > -1 && active < 2) { autocnt = cnt[thread]; autocnt->track.active = active; if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

" "Camera %d%s%s" "
track auto %s
", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", active ? "enabled":"disabled"); 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", active ? "enabled":"disabled"); send_template_raw(client_socket, res); } } else { if (cnt[0]->conf.webcontrol_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.webcontrol_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.webcontrol_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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d%s%s\n" "
\n" "
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", (cnt[thread]->track.active) ? "selected":"", (cnt[thread]->track.active) ? "selected":""); send_template(client_socket, res); send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "auto accepts only 0,1 or status as valid value\n"); send_template_raw(client_socket, res); } } else { if (cnt[0]->conf.webcontrol_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.webcontrol_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; } /** * handle_get * parses the action requested for motion ( config , action , detection , track ) * and call to action function if needed. * * Returns * 0 on action restart or quit * 1 on success */ static unsigned int handle_get(int client_socket, const char *url, void *userdata, const char *httphostname) { struct context **cnt = userdata; if (*url == '/') { int i = 0; char *res=NULL; res = mymalloc(2048); /* get the number of threads */ while (cnt[++i]); /* ROOT_URI -> GET / */ if (!strcmp(url, "/")) { int y; int counter; char hostname[1024]; //Send the webcontrol section if applicable if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "Motion "VERSION" Running [%hu] Cameras
\n" "All\n", i); send_template(client_socket, res); counter = 0; for (y = 1; y < i; y++) { counter++; if (counter == 6){ sprintf(res, "
"); send_template(client_socket, res); counter = 0; } sprintf(res, "Camera %d%s%s\n", y, cnt[y]->conf.camera_id, cnt[y]->conf.camera_name ? " -- " : "", cnt[y]->conf.camera_name ? cnt[y]->conf.camera_name : ""); send_template(client_socket, res); } sprintf(res, "
"); send_template(client_socket, res); if (!httphostname) { hostname[1023] = '\0'; gethostname(hostname, 1023); httphostname = hostname; } //Send the preview section for (y = 0; y < i; y++) { if (cnt[y]->conf.stream_port) { if (cnt[y]->conf.stream_preview_newline) { sprintf(res, "
"); send_template(client_socket, res); } sprintf(res, " " "\n" ,httphostname,cnt[y]->conf.stream_port ,httphostname,cnt[y]->conf.stream_port ,cnt[y]->conf.stream_preview_scale); send_template(client_socket, res); } } send_template_end_client(client_socket); } else { send_template_ini_client_raw(client_socket); sprintf(res, "Motion "VERSION" Running [%hu] Cameras\n0\n", i); send_template_raw(client_socket, res); for (y = 1; y < i; y++) { sprintf(res, "%hu\n", y); send_template_raw(client_socket, res); } } } else { char command[256] = {'\0'}; char slash; int thread = -1; size_t length_uri = 0; char *pointer = (char *)url; length_uri = strlen(url); /* Check for Thread number first -> GET /2 */ pointer++; length_uri--; warningkill = sscanf(pointer, "%d%c", &thread, &slash); if ((thread != -1) && (thread < i)) { /* thread_number found */ if (thread > 9) { pointer = pointer + 2; length_uri = length_uri - 2; } else { pointer++; length_uri--; } if (slash == '/') { /* slash found /2/ */ pointer++; length_uri--; } if (length_uri != 0) { warningkill = sscanf(pointer, "%255[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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n" "list
\n" "write
\n" "set
\n" "get
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", 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, "Camera %d\nlist\nwrite\nset\nget\n", cnt[thread]->conf.camera_id); 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.webcontrol_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, "action")) { /* action */ pointer = pointer + 6; length_uri = length_uri - 6; /* call action() */ if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n" "makemovie
\n" "snapshot
\n" "restart
\n" "quit
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", 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, "Camera %d\nmakemovie\nsnapshot\nrestart\nquit\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } else if ((slash == '/') && (length_uri > 4)) { unsigned 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.webcontrol_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, "detection")) { /* detection */ pointer = pointer + 9; length_uri = length_uri - 9; if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n" "status
\n" "start
\n" "pause
\n" "connection
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", 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, "Camera %d\nstatus\nstart\npause\nconnection\n", cnt[thread]->conf.camera_id); 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.webcontrol_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,"track")) { /* track */ pointer = pointer + 5; length_uri = length_uri - 5; if (length_uri == 0) { if (cnt[0]->conf.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\n" "Camera %d%s%s
\n" "track set pan/tilt
\n" "track center
\n" "track auto
\n" "track status
\n", thread, cnt[thread]->conf.camera_id, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", 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, "Camera %d\nset pan/tilt\ncenter\nauto\nstatus\n", cnt[thread]->conf.camera_id); 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.webcontrol_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.webcontrol_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.webcontrol_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.webcontrol_html_output) { send_template_ini_client(client_socket, ini_template); sprintf(res, "<– back

\nCamera %d -- Thread %hd%s%s
\n" "config
\n" "action
\n" "detection
\n" "track
\n", cnt[thread]->conf.camera_id, thread, cnt[thread]->conf.camera_name ? " -- " : "", cnt[thread]->conf.camera_name ? cnt[thread]->conf.camera_name : "", 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, "Camera %d\nconfig\naction\ndetection\ntrack\n", cnt[thread]->conf.camera_id); send_template_raw(client_socket, res); } } } else { if (cnt[0]->conf.webcontrol_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.webcontrol_html_output) response_client(client_socket, not_found_response_template,NULL); else response_client(client_socket, not_found_response_template_raw,NULL); } return 1; } /** * read_client * As usually web clients uses nonblocking connect/read * read_client should handle nonblocking sockets. * * Returns * 0 to quit or restart * 1 on success */ static unsigned int read_client(int client_socket, void *userdata, char *auth) { unsigned int alive = 1; unsigned int ret = 1; char buffer[1024] = {'\0'}; ssize_t length = 1023; struct context **cnt = userdata; /* lock the mutex */ pthread_mutex_lock(&httpd_mutex); while (alive) { ssize_t nread = 0, readb = -1; nread = read_nonblock(client_socket, buffer, length); if (nread <= 0) { MOTION_LOG(DBG, TYPE_STREAM, SHOW_ERRNO, "%s: motion-httpd First Read Error"); pthread_mutex_unlock(&httpd_mutex); return 1; } else { char method[10]={'\0'}; char url[512]={'\0'}; char protocol[10]={'\0'}; char *authentication=NULL; char *hostname=NULL; buffer[nread] = '\0'; warningkill = sscanf(buffer, "%9s %511s %9s", method, url, protocol); if (warningkill != 3) { if (cnt[0]->conf.webcontrol_html_output) warningkill = write_nonblock(client_socket, bad_request_response, sizeof (bad_request_response)); else warningkill = write_nonblock(client_socket, bad_request_response_raw, sizeof (bad_request_response_raw)); pthread_mutex_unlock(&httpd_mutex); return 1; } while ((strstr(buffer, "\r\n\r\n") == NULL) && (readb != 0) && (nread < length)) { readb = read_nonblock(client_socket, buffer+nread, sizeof (buffer) - nread); if (readb == -1) { nread = -1; break; } nread += readb; if (nread > length) { MOTION_LOG(WRN, TYPE_STREAM, SHOW_ERRNO, "%s: motion-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(ERR, TYPE_STREAM, SHOW_ERRNO, "%s: motion-httpd READ give up!"); 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.webcontrol_html_output) warningkill = write_nonblock(client_socket, bad_request_response, sizeof (bad_request_response)); else warningkill = write_nonblock(client_socket, bad_request_response_raw, sizeof (bad_request_response_raw)); pthread_mutex_unlock(&httpd_mutex); return 1; } 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.webcontrol_html_output) snprintf(response, sizeof (response), bad_method_response_template, method); else snprintf(response, sizeof (response), bad_method_response_template_raw, method); warningkill = write_nonblock(client_socket, response, strlen(response)); pthread_mutex_unlock(&httpd_mutex); return 1; } if ((hostname = strstr(buffer,"Host:"))) { /* use the hostname the browser used to connect to us when * constructing links to the stream ports. If available * (which it is in all modern browsers) it is more likely to * work that the result of gethostname(), which is reliant on * the machine we're running on having it's hostname setup * correctly and corresponding DNS in place. */ hostname += strlen("Host:"); char *end_host = strstr(hostname,"\r\n"); if (end_host) { while (hostname < end_host && isspace(hostname[0])) hostname++; while (hostname < end_host && isspace(end_host[-1])) end_host--; /* we have a string that is the hostname followed by * optionally a colon and a port number - strip off any * port number & colon */ char *colon = NULL; if (hostname[0] == '[') { // hostname is a IPv6 address like "[::1]" char *end_bracket = memchr(hostname, ']', end_host-hostname); // look for the colon after the "]" colon = memchr(end_bracket, ':', end_host-end_bracket); } else { colon = memchr(hostname, ':', end_host-hostname); } if (colon) end_host = colon; hostname = strndup(hostname, end_host-hostname); } else { hostname = NULL; } } 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_nonblock(client_socket, response, strlen(response)); free(hostname); pthread_mutex_unlock(&httpd_mutex); return 1; } if (strcmp(auth, authentication)) { char response[1024] = {'\0'}; snprintf(response, sizeof (response), request_auth_response_template, method); warningkill = write_nonblock(client_socket, response, strlen(response)); free(hostname); pthread_mutex_unlock(&httpd_mutex); return 1; } else { ret = handle_get(client_socket, url, cnt, hostname); /* A valid auth request. Process it. */ } } else { // Request Authorization char response[1024] = {'\0'}; snprintf(response, sizeof (response), request_auth_response_template, method); warningkill = write_nonblock(client_socket, response, strlen(response)); free(hostname); pthread_mutex_unlock(&httpd_mutex); return 1; } } else { ret = handle_get(client_socket, url, cnt, hostname); /* A valid request. Process it. */ } free(hostname); } } pthread_mutex_unlock(&httpd_mutex); return ret; } /** * acceptnonblocking * 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; struct sockaddr_in6 client; socklen_t client_len = sizeof(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, &client, &client_len)) > 0) return curfd; } } return -1; } /** * httpd_run * Creates the listening socket and waits client requests. */ void httpd_run(struct context **cnt) { unsigned int client_sent_quit_message = 1, closehttpd = 0; 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); int sd = http_bindsock(cnt[0]->conf.webcontrol_port, cnt[0]->conf.webcontrol_localhost, cnt[0]->conf.ipv6_enabled); if (sd < 0) { pthread_mutex_destroy(&httpd_mutex); return; } MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO,"%s: Started motion-httpd server on port %d (auth %s)", cnt[0]->conf.webcontrol_port, cnt[0]->conf.webcontrol_authentication ? "Enabled":"Disabled"); if (cnt[0]->conf.webcontrol_authentication != NULL) { char *userpass = NULL; size_t auth_size = strlen(cnt[0]->conf.webcontrol_authentication); authentication = 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.webcontrol_authentication); base64_encode(userpass, authentication, auth_size); free(userpass); } while ((client_sent_quit_message) && (!closehttpd)) { int client_socket_fd = acceptnonblocking(sd, NONBLOCK_TIMEOUT); if (client_socket_fd < 0) { if ((!cnt[0]) || (cnt[0]->webcontrol_finish)) { MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd - Finishing"); closehttpd = 1; } } else { /* Get the Client request */ client_sent_quit_message = read_client(client_socket_fd, cnt, authentication); MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd - Read from client"); /* Close Connection */ if (client_socket_fd) close(client_socket_fd); } } free(authentication); close(sd); MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd Closing"); pthread_mutex_destroy(&httpd_mutex); } /** * motion_web_control * Calls main function httpd_run */ void *motion_web_control(void *arg) { struct context **cnt = arg; #ifdef HAVE_PTHREAD_SETNAME_NP pthread_setname_np(pthread_self(), "web_control"); #endif httpd_run(cnt); /* * Update how many threads we have running. This is done within a * mutex lock to prevent multiple simultaneous updates to * 'threads_running'. */ pthread_mutex_lock(&global_lock); threads_running--; cnt[0]->webcontrol_running = 0; pthread_mutex_unlock(&global_lock); MOTION_LOG(NTC, TYPE_STREAM, NO_ERRNO, "%s: motion-httpd thread exit"); pthread_exit(NULL); } motion-release-4.0/webhttpd.h000066400000000000000000000011371300074623700162760ustar00rootroot00000000000000/* * webhttpd.h * * Include file for webhttpd.c * * Specs : http://www.lavrsen.dk/twiki/bin/view/Motion/MotionHttpAPI * * Copyright 2004-2005 by Angel Carpintero (motiondevelop@gmail.com) * This software is distributed under the GNU Public License Version 2 * See also the file 'COPYING'. * */ #ifndef _INCLUDE_WEBHTTPD_H_ #define _INCLUDE_WEBHTTPD_H_ #include "motion.h" #define TWIKI_URL "http://www.lavrsen.dk/twiki/bin/view/Motion/MotionGuideAlphabeticalOptionReferenceManual" void * motion_web_control(void *arg); void httpd_run(struct context **); #endif