diff --git a/.gitignore b/.gitignore index ec021d210..bd66e684a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ configure config.h.in +config.h.in~ autom4te.cache aclocal.m4 depcomp @@ -12,7 +13,7 @@ scripts/ZoneMinder/blib Makefile.in Makefile docs/_build - +compile config.guess config.h config.log @@ -24,6 +25,12 @@ misc/com.zoneminder.systemctl.policy misc/com.zoneminder.systemctl.rules misc/logrotate.conf misc/syslog.conf +onvif/modules/MYMETA.json +onvif/modules/MYMETA.yml +onvif/proxy/MYMETA.json +onvif/proxy/MYMETA.yml +scripts/ZoneMinder/Makefile.old +scripts/ZoneMinder/MYMETA.json scripts/ZoneMinder/MYMETA.yml scripts/ZoneMinder/lib/ZoneMinder/Base.pm scripts/ZoneMinder/lib/ZoneMinder/Config.pm @@ -56,7 +63,6 @@ src/zma src/zmc src/zmf src/zms -src/zmstreamer src/zmu web/includes/config.php zm.conf diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..0fa8df21a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "web/api/app/Plugin/Crud"] + path = web/api/app/Plugin/Crud + url = https://github.com/FriendsOfCake/crud.git + branch = 3.0 diff --git a/.travis.yml b/.travis.yml index 94c50c4b1..183d22e57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,15 @@ language: cpp +sudo: required +dist: trusty notifications: irc: "chat.freenode.net#zoneminder-dev" branches: except: - modern +addons: + sauce_connect: + username: "zoneminder" + access_key: "046ec7c1-c598-4e7e-949a-f86e725d1722" env: global: - LD_LIBRARY_PATH="/usr/local/lib:/opt/libjpeg-turbo/lib:$LD_LIBRARY_PATH" @@ -13,14 +19,14 @@ env: - CXXFLAGS="$CFLAGS" matrix: - ZM_BUILDMETHOD=cmake - - ZM_BUILDMETHOD=autotools compiler: - gcc + - clang before_install: - sudo apt-get update -qq - - sudo apt-get install -y -qq libpolkit-gobject-1-dev zlib1g-dev apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf cmake libjpeg-turbo8-dev apache2-mpm-prefork libapache2-mod-php5 php5-cli libtheora-dev libvorbis-dev libvpx-dev libx264-dev libvlccore-dev libvlc-dev libvlccore5 libvlc5 2>&1 > /dev/null + - sudo apt-get install -y -qq libpolkit-gobject-1-dev zlib1g-dev apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf cmake libjpeg-turbo8-dev apache2-mpm-prefork libapache2-mod-php5 php5-cli libtheora-dev libvorbis-dev libvpx-dev libx264-dev libvlccore-dev libvlc-dev 2>&1 > /dev/null install: - - git clone -b n2.4.2 --depth=1 git://source.ffmpeg.org/ffmpeg.git + - git clone -b n3.0 --depth=1 git://source.ffmpeg.org/ffmpeg.git - cd ffmpeg - ./configure --enable-shared --enable-swscale --enable-gpl --enable-libx264 --enable-libvpx --enable-libvorbis --enable-libtheora - make -j `grep processor /proc/cpuinfo|wc -l` @@ -28,16 +34,10 @@ install: - sudo make install-libs before_script: - cd $TRAVIS_BUILD_DIR - - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then libtoolize --force; fi - - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then aclocal; fi - - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then autoheader; fi - - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then automake --force-missing --add-missing; fi - - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then autoconf; fi - mysql -uroot -e "CREATE DATABASE IF NOT EXISTS zm" - mysql -uroot -e "GRANT ALL ON zm.* TO 'zmuser'@'localhost' IDENTIFIED BY 'zmpass'"; - mysql -uroot -e "FLUSH PRIVILEGES" script: - - if [ "$ZM_BUILDMETHOD" = "autotools" ]; then ./configure --prefix=/usr --with-libarch=lib/$DEB_HOST_GNU_TYPE --host=$DEB_HOST_GNU_TYPE --build=$DEB_BUILD_GNU_TYPE --with-mysql=/usr --with-ffmpeg=/usr --with-webdir=/usr/share/zoneminder/www --with-cgidir=/usr/libexec/zoneminder/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-crashtrace=yes --disable-debug --enable-mmap=yes ZM_SSL_LIB=openssl; fi - if [ "$ZM_BUILDMETHOD" = "cmake" ]; then cmake -DCMAKE_INSTALL_PREFIX="/usr"; fi - make - sudo make install diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..471066ac3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,487 @@ +# Change Log + +## [v1.29.0](https://github.com/ZoneMinder/ZoneMinder/tree/v1.29.0) (2016-02-03) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.29.0-rc2...v1.29.0) + +**Merged pull requests:** + +- 1253 zms quit cmd [\#1254](https://github.com/ZoneMinder/ZoneMinder/pull/1254) ([pliablepixels](https://github.com/pliablepixels)) +- Add Debug/Info lines reporting multi-server status [\#1252](https://github.com/ZoneMinder/ZoneMinder/pull/1252) ([connortechnology](https://github.com/connortechnology)) +- Do debian package mods [\#1244](https://github.com/ZoneMinder/ZoneMinder/pull/1244) ([pliablepixels](https://github.com/pliablepixels)) +- zmtrigger - process off+time delay condition [\#1240](https://github.com/ZoneMinder/ZoneMinder/pull/1240) ([knnniggett](https://github.com/knnniggett)) +- Multi server [\#1233](https://github.com/ZoneMinder/ZoneMinder/pull/1233) ([connortechnology](https://github.com/connortechnology)) +- remove Google open Sans external import [\#1232](https://github.com/ZoneMinder/ZoneMinder/pull/1232) ([connortechnology](https://github.com/connortechnology)) +- PTZ - Added autostop to Down command on FI8918W [\#1189](https://github.com/ZoneMinder/ZoneMinder/pull/1189) ([marcolino7](https://github.com/marcolino7)) + +## [v1.29.0-rc2](https://github.com/ZoneMinder/ZoneMinder/tree/v1.29.0-rc2) (2016-01-05) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.29.0-rc1...v1.29.0-rc2) + +**Merged pull requests:** + +- Multi server [\#1223](https://github.com/ZoneMinder/ZoneMinder/pull/1223) ([connortechnology](https://github.com/connortechnology)) +- Multi server [\#1222](https://github.com/ZoneMinder/ZoneMinder/pull/1222) ([connortechnology](https://github.com/connortechnology)) +- Multi server [\#1217](https://github.com/ZoneMinder/ZoneMinder/pull/1217) ([connortechnology](https://github.com/connortechnology)) +- Multi server [\#1215](https://github.com/ZoneMinder/ZoneMinder/pull/1215) ([connortechnology](https://github.com/connortechnology)) +- Change log updates [\#1172](https://github.com/ZoneMinder/ZoneMinder/pull/1172) ([SteveGilvarry](https://github.com/SteveGilvarry)) + +## [v1.29.0-rc1](https://github.com/ZoneMinder/ZoneMinder/tree/v1.29.0-rc1) (2016-01-01) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.28.1...v1.29.0-rc1) + +**Merged pull requests:** + +- Bump version to 1.29.0 [\#1213](https://github.com/ZoneMinder/ZoneMinder/pull/1213) ([knnniggett](https://github.com/knnniggett)) +- Missing rtd theme [\#1202](https://github.com/ZoneMinder/ZoneMinder/pull/1202) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Skip directories that have non-digits in them [\#1201](https://github.com/ZoneMinder/ZoneMinder/pull/1201) ([connortechnology](https://github.com/connortechnology)) +- updated mobile app info [\#1200](https://github.com/ZoneMinder/ZoneMinder/pull/1200) ([pliablepixels](https://github.com/pliablepixels)) +- Api more security [\#1196](https://github.com/ZoneMinder/ZoneMinder/pull/1196) ([pliablepixels](https://github.com/pliablepixels)) +- Documentation [\#1194](https://github.com/ZoneMinder/ZoneMinder/pull/1194) ([pliablepixels](https://github.com/pliablepixels)) +- Documentation updated for ubuntu [\#1193](https://github.com/ZoneMinder/ZoneMinder/pull/1193) ([pliablepixels](https://github.com/pliablepixels)) +- Fixes \#1179 Libvlc Live555 Segmentation Fault [\#1190](https://github.com/ZoneMinder/ZoneMinder/pull/1190) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Add code to detect the change in REALM from older to newer firmware [\#1184](https://github.com/ZoneMinder/ZoneMinder/pull/1184) ([connortechnology](https://github.com/connortechnology)) +- add a 1/8th scale option, which is useful for 1920x1080 streams [\#1182](https://github.com/ZoneMinder/ZoneMinder/pull/1182) ([connortechnology](https://github.com/connortechnology)) +- Zms socket lock [\#1180](https://github.com/ZoneMinder/ZoneMinder/pull/1180) ([connortechnology](https://github.com/connortechnology)) +- Check for the presence of CrudControllerTrait.php instead of .git [\#1178](https://github.com/ZoneMinder/ZoneMinder/pull/1178) ([knnniggett](https://github.com/knnniggett)) +- Partial fix for \#1167 [\#1176](https://github.com/ZoneMinder/ZoneMinder/pull/1176) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Error on missing submodules [\#1173](https://github.com/ZoneMinder/ZoneMinder/pull/1173) ([knnniggett](https://github.com/knnniggett)) +- fix mem alloc fault in zm\_monitor.cpp [\#1168](https://github.com/ZoneMinder/ZoneMinder/pull/1168) ([knnniggett](https://github.com/knnniggett)) +- compat for php 5.4 [\#1164](https://github.com/ZoneMinder/ZoneMinder/pull/1164) ([abishai](https://github.com/abishai)) +- remove comment at end of line [\#1157](https://github.com/ZoneMinder/ZoneMinder/pull/1157) ([connortechnology](https://github.com/connortechnology)) +- Reorder RTSPDescribe to avoid -wreorder warnings [\#1147](https://github.com/ZoneMinder/ZoneMinder/pull/1147) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Update to \#1137 for backward compatibility. [\#1142](https://github.com/ZoneMinder/ZoneMinder/pull/1142) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Update Travis to ffmpeg 2.8.1 for testing [\#1139](https://github.com/ZoneMinder/ZoneMinder/pull/1139) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Replace deprecated FFmpeg API [\#1137](https://github.com/ZoneMinder/ZoneMinder/pull/1137) ([onlyjob](https://github.com/onlyjob)) +- added prev/next event ids [\#1136](https://github.com/ZoneMinder/ZoneMinder/pull/1136) ([pliablepixels](https://github.com/pliablepixels)) +- Install nph-zms with FILES so it is listed in install\_manifest.txt [\#1124](https://github.com/ZoneMinder/ZoneMinder/pull/1124) ([baffo32](https://github.com/baffo32)) +- Stray semicolon causes SQL error [\#1123](https://github.com/ZoneMinder/ZoneMinder/pull/1123) ([baffo32](https://github.com/baffo32)) +- Use relative URL's instead of absolute [\#1121](https://github.com/ZoneMinder/ZoneMinder/pull/1121) ([knnniggett](https://github.com/knnniggett)) +- Update version check URL [\#1120](https://github.com/ZoneMinder/ZoneMinder/pull/1120) ([kylejohnson](https://github.com/kylejohnson)) +- Add index to frames [\#1116](https://github.com/ZoneMinder/ZoneMinder/pull/1116) ([kylejohnson](https://github.com/kylejohnson)) +- Fatal if content dirs are unwritable [\#1113](https://github.com/ZoneMinder/ZoneMinder/pull/1113) ([baffo32](https://github.com/baffo32)) +- Fatal error if date.timezone is unset [\#1111](https://github.com/ZoneMinder/ZoneMinder/pull/1111) ([baffo32](https://github.com/baffo32)) +- Fix faulty zm.conf.new install line [\#1107](https://github.com/ZoneMinder/ZoneMinder/pull/1107) ([baffo32](https://github.com/baffo32)) +- Update preinst [\#1103](https://github.com/ZoneMinder/ZoneMinder/pull/1103) ([seebaer1976](https://github.com/seebaer1976)) +- Update apache.conf [\#1102](https://github.com/ZoneMinder/ZoneMinder/pull/1102) ([seebaer1976](https://github.com/seebaer1976)) +- Update rules [\#1101](https://github.com/ZoneMinder/ZoneMinder/pull/1101) ([seebaer1976](https://github.com/seebaer1976)) +- Update links [\#1100](https://github.com/ZoneMinder/ZoneMinder/pull/1100) ([seebaer1976](https://github.com/seebaer1976)) +- Update preinst [\#1099](https://github.com/ZoneMinder/ZoneMinder/pull/1099) ([seebaer1976](https://github.com/seebaer1976)) +- Fix zmaudit [\#1095](https://github.com/ZoneMinder/ZoneMinder/pull/1095) ([connortechnology](https://github.com/connortechnology)) +- fixed version compare logic [\#1094](https://github.com/ZoneMinder/ZoneMinder/pull/1094) ([pliablepixels](https://github.com/pliablepixels)) +- Don't install zm.conf if it already exists [\#1090](https://github.com/ZoneMinder/ZoneMinder/pull/1090) ([connortechnology](https://github.com/connortechnology)) +- change types and move things around to remove compile warnings [\#1089](https://github.com/ZoneMinder/ZoneMinder/pull/1089) ([connortechnology](https://github.com/connortechnology)) +- Tz [\#1084](https://github.com/ZoneMinder/ZoneMinder/pull/1084) ([connortechnology](https://github.com/connortechnology)) +- fixed orange display for monitor mode [\#1083](https://github.com/ZoneMinder/ZoneMinder/pull/1083) ([pliablepixels](https://github.com/pliablepixels)) +- use deleteAll\(\) vs. delete\(\) when deleting an Event's Frames [\#1080](https://github.com/ZoneMinder/ZoneMinder/pull/1080) ([kylejohnson](https://github.com/kylejohnson)) +- Added control script for SunEyes SP-P1802SWPTZ [\#1079](https://github.com/ZoneMinder/ZoneMinder/pull/1079) ([bofhdk](https://github.com/bofhdk)) +- Use the 3.0 branch of crud, compatible with cakephp 2.x [\#1078](https://github.com/ZoneMinder/ZoneMinder/pull/1078) ([kylejohnson](https://github.com/kylejohnson)) +- 663 frames primary key [\#1075](https://github.com/ZoneMinder/ZoneMinder/pull/1075) ([kylejohnson](https://github.com/kylejohnson)) +- Delete fixes for Events [\#1073](https://github.com/ZoneMinder/ZoneMinder/pull/1073) ([pliablepixels](https://github.com/pliablepixels)) +- restart monitor when edited via APIs [\#1070](https://github.com/ZoneMinder/ZoneMinder/pull/1070) ([pliablepixels](https://github.com/pliablepixels)) +- add debug statements for when LastWriteTime is not defined. [\#1067](https://github.com/ZoneMinder/ZoneMinder/pull/1067) ([connortechnology](https://github.com/connortechnology)) +- fixed recaptcha showing up pre DB update [\#1066](https://github.com/ZoneMinder/ZoneMinder/pull/1066) ([pliablepixels](https://github.com/pliablepixels)) +- fixed security instructions for curl [\#1062](https://github.com/ZoneMinder/ZoneMinder/pull/1062) ([pliablepixels](https://github.com/pliablepixels)) +- header typo corrections [\#1058](https://github.com/ZoneMinder/ZoneMinder/pull/1058) ([onlyjob](https://github.com/onlyjob)) +- quick fix for \#1055: make sure our mmap fd is \> 2 [\#1057](https://github.com/ZoneMinder/ZoneMinder/pull/1057) ([connortechnology](https://github.com/connortechnology)) +- Fix sgfault caused by the privacy mask stuff [\#1056](https://github.com/ZoneMinder/ZoneMinder/pull/1056) ([connortechnology](https://github.com/connortechnology)) +- link to cambozola pacakge, rather than download during build [\#1054](https://github.com/ZoneMinder/ZoneMinder/pull/1054) ([knnniggett](https://github.com/knnniggett)) +- redhat rpm packaging modifications [\#1052](https://github.com/ZoneMinder/ZoneMinder/pull/1052) ([knnniggett](https://github.com/knnniggett)) +- remove core.php, modify core.php.default [\#1049](https://github.com/ZoneMinder/ZoneMinder/pull/1049) ([knnniggett](https://github.com/knnniggett)) +- Google recaptcha [\#1048](https://github.com/ZoneMinder/ZoneMinder/pull/1048) ([pliablepixels](https://github.com/pliablepixels)) +- enable/disable RTSP Describe Header [\#1045](https://github.com/ZoneMinder/ZoneMinder/pull/1045) ([knnniggett](https://github.com/knnniggett)) +- Add Documentation for Privacy zones [\#1044](https://github.com/ZoneMinder/ZoneMinder/pull/1044) ([schrorg](https://github.com/schrorg)) +- added note about potential Perl and PHP time translation conflict wit… [\#1043](https://github.com/ZoneMinder/ZoneMinder/pull/1043) ([pliablepixels](https://github.com/pliablepixels)) +- Multi server [\#1040](https://github.com/ZoneMinder/ZoneMinder/pull/1040) ([connortechnology](https://github.com/connortechnology)) +- 1038 fixing state mgmt 1030 is active fix [\#1039](https://github.com/ZoneMinder/ZoneMinder/pull/1039) ([pliablepixels](https://github.com/pliablepixels)) +- Grey color for disabled buttons [\#1037](https://github.com/ZoneMinder/ZoneMinder/pull/1037) ([pliablepixels](https://github.com/pliablepixels)) +- Update filterevents.rst [\#1035](https://github.com/ZoneMinder/ZoneMinder/pull/1035) ([tikismoke](https://github.com/tikismoke)) +- add warning and help text for maxfps fields [\#1033](https://github.com/ZoneMinder/ZoneMinder/pull/1033) ([knnniggett](https://github.com/knnniggett)) +- update doc [\#1032](https://github.com/ZoneMinder/ZoneMinder/pull/1032) ([tikismoke](https://github.com/tikismoke)) +- Remove full path from Logger filename [\#1029](https://github.com/ZoneMinder/ZoneMinder/pull/1029) ([knnniggett](https://github.com/knnniggett)) +- Typo in README.md [\#1027](https://github.com/ZoneMinder/ZoneMinder/pull/1027) ([tikismoke](https://github.com/tikismoke)) +- Add new zone type - privacy zones [\#1026](https://github.com/ZoneMinder/ZoneMinder/pull/1026) ([schrorg](https://github.com/schrorg)) +- Send login activity to the zoneminder event log [\#1021](https://github.com/ZoneMinder/ZoneMinder/pull/1021) ([knnniggett](https://github.com/knnniggett)) +- Small dark CSS fixes in frames and timeline view [\#1019](https://github.com/ZoneMinder/ZoneMinder/pull/1019) ([schrorg](https://github.com/schrorg)) +- New User Permission "Groups" [\#1018](https://github.com/ZoneMinder/ZoneMinder/pull/1018) ([knnniggett](https://github.com/knnniggett)) +- 1013 document migration [\#1017](https://github.com/ZoneMinder/ZoneMinder/pull/1017) ([pliablepixels](https://github.com/pliablepixels)) +- Fix issue with score values less than 0 [\#1016](https://github.com/ZoneMinder/ZoneMinder/pull/1016) ([knnniggett](https://github.com/knnniggett)) +- Explained a caveat with using relative times [\#1012](https://github.com/ZoneMinder/ZoneMinder/pull/1012) ([pliablepixels](https://github.com/pliablepixels)) +- Included logic to not enforce authentication in API layer if ZM auth is off [\#1008](https://github.com/ZoneMinder/ZoneMinder/pull/1008) ([pliablepixels](https://github.com/pliablepixels)) +- Update to ffmpeg 2.7.2 in travis build [\#1007](https://github.com/ZoneMinder/ZoneMinder/pull/1007) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- I was using the wrong field to check for portal authentication [\#1006](https://github.com/ZoneMinder/ZoneMinder/pull/1006) ([pliablepixels](https://github.com/pliablepixels)) +- Demote user auth info message to debug [\#1003](https://github.com/ZoneMinder/ZoneMinder/pull/1003) ([Linwood-F](https://github.com/Linwood-F)) +- Add scale as optional feature to image.php [\#1001](https://github.com/ZoneMinder/ZoneMinder/pull/1001) ([Linwood-F](https://github.com/Linwood-F)) +- 995 events count per api \(bumps up \# of events reported per API call\) [\#996](https://github.com/ZoneMinder/ZoneMinder/pull/996) ([pliablepixels](https://github.com/pliablepixels)) +- APIs will be served only if user is logged into the ZM portal [\#994](https://github.com/ZoneMinder/ZoneMinder/pull/994) ([pliablepixels](https://github.com/pliablepixels)) +- Add option to make TimeStamp larger [\#992](https://github.com/ZoneMinder/ZoneMinder/pull/992) ([schrorg](https://github.com/schrorg)) +- Implemented \#989 \(highlight current row in tables\) for dark CSS [\#990](https://github.com/ZoneMinder/ZoneMinder/pull/990) ([schrorg](https://github.com/schrorg)) +- CSS\[skins/classic\]: highlight current row in tables. [\#989](https://github.com/ZoneMinder/ZoneMinder/pull/989) ([onlyjob](https://github.com/onlyjob)) +- quiet error when no Servers in Servers table [\#986](https://github.com/ZoneMinder/ZoneMinder/pull/986) ([connortechnology](https://github.com/connortechnology)) +- fix \#948 1 [\#985](https://github.com/ZoneMinder/ZoneMinder/pull/985) ([connortechnology](https://github.com/connortechnology)) +- Remove shared data warning for purpose query only [\#984](https://github.com/ZoneMinder/ZoneMinder/pull/984) ([Linwood-F](https://github.com/Linwood-F)) +- Change from info to debug [\#983](https://github.com/ZoneMinder/ZoneMinder/pull/983) ([Linwood-F](https://github.com/Linwood-F)) +- Fix image dimensions check [\#980](https://github.com/ZoneMinder/ZoneMinder/pull/980) ([connortechnology](https://github.com/connortechnology)) +- Apache.conf modifications [\#968](https://github.com/ZoneMinder/ZoneMinder/pull/968) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Dark CSS for classic theme [\#967](https://github.com/ZoneMinder/ZoneMinder/pull/967) ([schrorg](https://github.com/schrorg)) +- Auto generated changelog [\#966](https://github.com/ZoneMinder/ZoneMinder/pull/966) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- 959 add exif date time to images [\#962](https://github.com/ZoneMinder/ZoneMinder/pull/962) ([Linwood-F](https://github.com/Linwood-F)) +- Add analysis interval parameter to monitors settings [\#956](https://github.com/ZoneMinder/ZoneMinder/pull/956) ([manupap1](https://github.com/manupap1)) +- Fixed Configs API to return all values [\#955](https://github.com/ZoneMinder/ZoneMinder/pull/955) ([pliablepixels](https://github.com/pliablepixels)) +- Change encoding of german language file to UTF-8 [\#952](https://github.com/ZoneMinder/ZoneMinder/pull/952) ([schrorg](https://github.com/schrorg)) +- Show correct part of URL \(hostname\) for ffmpeg sources in console [\#951](https://github.com/ZoneMinder/ZoneMinder/pull/951) ([schrorg](https://github.com/schrorg)) +- add php-gd to list of dependencies for debian and ubuntu builds [\#944](https://github.com/ZoneMinder/ZoneMinder/pull/944) ([connortechnology](https://github.com/connortechnology)) +- rpm packaging - require php-gd [\#943](https://github.com/ZoneMinder/ZoneMinder/pull/943) ([knnniggett](https://github.com/knnniggett)) +- add some utility db functions [\#942](https://github.com/ZoneMinder/ZoneMinder/pull/942) ([connortechnology](https://github.com/connortechnology)) +- add space instead of + to handle old Axis cameras [\#941](https://github.com/ZoneMinder/ZoneMinder/pull/941) ([connortechnology](https://github.com/connortechnology)) +- zmtrigger: POD documentation [\#938](https://github.com/ZoneMinder/ZoneMinder/pull/938) ([onlyjob](https://github.com/onlyjob)) +- improve log [\#937](https://github.com/ZoneMinder/ZoneMinder/pull/937) ([connortechnology](https://github.com/connortechnology)) +- add error handling on failure to open serial port [\#936](https://github.com/ZoneMinder/ZoneMinder/pull/936) ([connortechnology](https://github.com/connortechnology)) +- fix utf8 ' characters [\#934](https://github.com/ZoneMinder/ZoneMinder/pull/934) ([connortechnology](https://github.com/connortechnology)) +- roudn up when calculating buffer size for scaled image. Fixes \#932 [\#933](https://github.com/ZoneMinder/ZoneMinder/pull/933) ([connortechnology](https://github.com/connortechnology)) +- Added API routing [\#931](https://github.com/ZoneMinder/ZoneMinder/pull/931) ([pliablepixels](https://github.com/pliablepixels)) +- don't include .cpp in man [\#930](https://github.com/ZoneMinder/ZoneMinder/pull/930) ([connortechnology](https://github.com/connortechnology)) +- fix pod2man generation for out-of-source builds [\#928](https://github.com/ZoneMinder/ZoneMinder/pull/928) ([knnniggett](https://github.com/knnniggett)) +- Version to 1.28.99 [\#926](https://github.com/ZoneMinder/ZoneMinder/pull/926) ([connortechnology](https://github.com/connortechnology)) +- Introduce a read\_into function in the Buffer. [\#923](https://github.com/ZoneMinder/ZoneMinder/pull/923) ([connortechnology](https://github.com/connortechnology)) +- Added "RewriteBase /zm/api" for API routing [\#921](https://github.com/ZoneMinder/ZoneMinder/pull/921) ([pliablepixels](https://github.com/pliablepixels)) +- Zms no crash [\#920](https://github.com/ZoneMinder/ZoneMinder/pull/920) ([connortechnology](https://github.com/connortechnology)) +- add check for gettime in librt, needed for building on pi [\#919](https://github.com/ZoneMinder/ZoneMinder/pull/919) ([connortechnology](https://github.com/connortechnology)) +- Add ServerId to Monitors [\#918](https://github.com/ZoneMinder/ZoneMinder/pull/918) ([connortechnology](https://github.com/connortechnology)) +- Dumb down Crud from 4.0 -\> 3.0.10 [\#915](https://github.com/ZoneMinder/ZoneMinder/pull/915) ([knnniggett](https://github.com/knnniggett)) +- Add Servers Table and add Id PRIMARY KEY to States [\#910](https://github.com/ZoneMinder/ZoneMinder/pull/910) ([connortechnology](https://github.com/connortechnology)) +- fix montage view issue in mobile skin [\#909](https://github.com/ZoneMinder/ZoneMinder/pull/909) ([knnniggett](https://github.com/knnniggett)) +- Solaris cmake [\#906](https://github.com/ZoneMinder/ZoneMinder/pull/906) ([knnniggett](https://github.com/knnniggett)) +- Fix el7 build [\#902](https://github.com/ZoneMinder/ZoneMinder/pull/902) ([bill-mcgonigle](https://github.com/bill-mcgonigle)) +- 898 is running states [\#899](https://github.com/ZoneMinder/ZoneMinder/pull/899) ([pliablepixels](https://github.com/pliablepixels)) +- Fixed events API to remove thumbnail code [\#897](https://github.com/ZoneMinder/ZoneMinder/pull/897) ([pliablepixels](https://github.com/pliablepixels)) +- Generate man pages for perl scripts & C Binaries in the bin folder [\#896](https://github.com/ZoneMinder/ZoneMinder/pull/896) ([knnniggett](https://github.com/knnniggett)) +- 893 foscam 9831 w and other foscams [\#895](https://github.com/ZoneMinder/ZoneMinder/pull/895) ([pliablepixels](https://github.com/pliablepixels)) +- 893 foscam 9831 w and other foscams [\#894](https://github.com/ZoneMinder/ZoneMinder/pull/894) ([pliablepixels](https://github.com/pliablepixels)) +- Zmwatch cleanup2 [\#891](https://github.com/ZoneMinder/ZoneMinder/pull/891) ([connortechnology](https://github.com/connortechnology)) +- reverse the if statement to reduce indenting [\#890](https://github.com/ZoneMinder/ZoneMinder/pull/890) ([connortechnology](https://github.com/connortechnology)) +- Updated API document [\#886](https://github.com/ZoneMinder/ZoneMinder/pull/886) ([pliablepixels](https://github.com/pliablepixels)) +- Use avconv as alternative to ffmpeg executable [\#884](https://github.com/ZoneMinder/ZoneMinder/pull/884) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- 881 bootstrap loading config [\#883](https://github.com/ZoneMinder/ZoneMinder/pull/883) ([pliablepixels](https://github.com/pliablepixels)) +- Merged Angular UI branch API to master [\#882](https://github.com/ZoneMinder/ZoneMinder/pull/882) ([pliablepixels](https://github.com/pliablepixels)) +- Add version to the startup log line [\#875](https://github.com/ZoneMinder/ZoneMinder/pull/875) ([connortechnology](https://github.com/connortechnology)) +- German translation update [\#874](https://github.com/ZoneMinder/ZoneMinder/pull/874) ([seeebek](https://github.com/seeebek)) +- reduce the wait to 2/10ths instead of a whole second [\#873](https://github.com/ZoneMinder/ZoneMinder/pull/873) ([connortechnology](https://github.com/connortechnology)) +- alter the logic of ReadData. New behaviour is documented. [\#870](https://github.com/ZoneMinder/ZoneMinder/pull/870) ([connortechnology](https://github.com/connortechnology)) +- analysis optimisations [\#867](https://github.com/ZoneMinder/ZoneMinder/pull/867) ([connortechnology](https://github.com/connortechnology)) +- Don't die if db goes away during logging [\#866](https://github.com/ZoneMinder/ZoneMinder/pull/866) ([connortechnology](https://github.com/connortechnology)) +- Move iostream inclusion in zm.h and declare explicitely the namespace [\#859](https://github.com/ZoneMinder/ZoneMinder/pull/859) ([manupap1](https://github.com/manupap1)) +- Fix detection of deprecated libav / ffmpeg functions [\#858](https://github.com/ZoneMinder/ZoneMinder/pull/858) ([manupap1](https://github.com/manupap1)) +- Correct bareword config entries with newer {} style [\#856](https://github.com/ZoneMinder/ZoneMinder/pull/856) ([connortechnology](https://github.com/connortechnology)) +- update german translation [\#854](https://github.com/ZoneMinder/ZoneMinder/pull/854) ([seeebek](https://github.com/seeebek)) +- ubuntu 15.04 [\#850](https://github.com/ZoneMinder/ZoneMinder/pull/850) ([seeebek](https://github.com/seeebek)) +- faster shutdown [\#847](https://github.com/ZoneMinder/ZoneMinder/pull/847) ([connortechnology](https://github.com/connortechnology)) +- Additional SLANG changes [\#845](https://github.com/ZoneMinder/ZoneMinder/pull/845) ([knnniggett](https://github.com/knnniggett)) +- Cmake hostos [\#844](https://github.com/ZoneMinder/ZoneMinder/pull/844) ([knnniggett](https://github.com/knnniggett)) +- Port to OmniOS/Solaris [\#842](https://github.com/ZoneMinder/ZoneMinder/pull/842) ([whorfin](https://github.com/whorfin)) +- Zmaudit update1: Make MIN\_AGE Configurable [\#838](https://github.com/ZoneMinder/ZoneMinder/pull/838) ([connortechnology](https://github.com/connortechnology)) +- Update to FI982821W\_Y2k [\#836](https://github.com/ZoneMinder/ZoneMinder/pull/836) ([connortechnology](https://github.com/connortechnology)) +- add translate function [\#833](https://github.com/ZoneMinder/ZoneMinder/pull/833) ([knnniggett](https://github.com/knnniggett)) +- Separate css window sizes [\#829](https://github.com/ZoneMinder/ZoneMinder/pull/829) ([connortechnology](https://github.com/connortechnology)) +- Fix fast forward/reverse in event playback \(\#688\) [\#825](https://github.com/ZoneMinder/ZoneMinder/pull/825) ([rwg0](https://github.com/rwg0)) +- Fix: typo in options\_libvlc [\#824](https://github.com/ZoneMinder/ZoneMinder/pull/824) ([Lihis](https://github.com/Lihis)) +- close the session before requiring the page contents to fix the concurre... [\#823](https://github.com/ZoneMinder/ZoneMinder/pull/823) ([connortechnology](https://github.com/connortechnology)) +- Fix build issues on kFreeBSD. Fixes \#771 [\#822](https://github.com/ZoneMinder/ZoneMinder/pull/822) ([connortechnology](https://github.com/connortechnology)) +- beautifying \*.pm [\#821](https://github.com/ZoneMinder/ZoneMinder/pull/821) ([onlyjob](https://github.com/onlyjob)) +- Remove hardcoded localized strings in php files and update lang files [\#820](https://github.com/ZoneMinder/ZoneMinder/pull/820) ([manupap1](https://github.com/manupap1)) +- Fix french lang file [\#818](https://github.com/ZoneMinder/ZoneMinder/pull/818) ([manupap1](https://github.com/manupap1)) +- more perlcritic/PBP corrections [\#816](https://github.com/ZoneMinder/ZoneMinder/pull/816) ([onlyjob](https://github.com/onlyjob)) +- last batch of POD and readability conversions for \*.pl scripts [\#815](https://github.com/ZoneMinder/ZoneMinder/pull/815) ([onlyjob](https://github.com/onlyjob)) +- Fixes \#760 in part Clean up CMakeLists.txt [\#812](https://github.com/ZoneMinder/ZoneMinder/pull/812) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- upgrade bundled jQuery \(Closes: \#785\) [\#809](https://github.com/ZoneMinder/ZoneMinder/pull/809) ([onlyjob](https://github.com/onlyjob)) +- Update Mootools [\#803](https://github.com/ZoneMinder/ZoneMinder/pull/803) ([knnniggett](https://github.com/knnniggett)) +- hide USE\_DEEP\_STORAGE [\#802](https://github.com/ZoneMinder/ZoneMinder/pull/802) ([knnniggett](https://github.com/knnniggett)) +- link zms to nph-zms, rather than build identical [\#801](https://github.com/ZoneMinder/ZoneMinder/pull/801) ([knnniggett](https://github.com/knnniggett)) +- \* use pthread\_join instead of pthread\_tryjoin\_np [\#800](https://github.com/ZoneMinder/ZoneMinder/pull/800) ([Sune1337](https://github.com/Sune1337)) +- zmcontrol.pl, zmfilter.pl: pod2usage + readability improvements. [\#798](https://github.com/ZoneMinder/ZoneMinder/pull/798) ([onlyjob](https://github.com/onlyjob)) +- one small fix for a log line where the 4th parameter wasn't included. T... [\#796](https://github.com/ZoneMinder/ZoneMinder/pull/796) ([connortechnology](https://github.com/connortechnology)) +- zmaudit.pl, zmcamtool.pl: pod2usage, PBP/5 + readability [\#795](https://github.com/ZoneMinder/ZoneMinder/pull/795) ([onlyjob](https://github.com/onlyjob)) +- as discussed... [\#794](https://github.com/ZoneMinder/ZoneMinder/pull/794) ([onlyjob](https://github.com/onlyjob)) +- Leftover short open tags [\#793](https://github.com/ZoneMinder/ZoneMinder/pull/793) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- cmake - use perl INSTALLDIRS [\#792](https://github.com/ZoneMinder/ZoneMinder/pull/792) ([knnniggett](https://github.com/knnniggett)) +- \#783 - related corrections [\#791](https://github.com/ZoneMinder/ZoneMinder/pull/791) ([onlyjob](https://github.com/onlyjob)) +- skins/classic: fix HTML export with USE\_DEEP\_STORAGE \(Closes: \#506\). [\#782](https://github.com/ZoneMinder/ZoneMinder/pull/782) ([onlyjob](https://github.com/onlyjob)) +- Check for libv4l1-videodev headers [\#781](https://github.com/ZoneMinder/ZoneMinder/pull/781) ([knnniggett](https://github.com/knnniggett)) +- build: add PATH\_MAX definitions \(needed on GNU Hurd\) [\#778](https://github.com/ZoneMinder/ZoneMinder/pull/778) ([onlyjob](https://github.com/onlyjob)) +- Freebsd fixes [\#775](https://github.com/ZoneMinder/ZoneMinder/pull/775) ([connortechnology](https://github.com/connortechnology)) +- Use tmpfiles.d to manage tmpdir and sockdir [\#774](https://github.com/ZoneMinder/ZoneMinder/pull/774) ([knnniggett](https://github.com/knnniggett)) +- Don't trigger linked cameras on new events [\#772](https://github.com/ZoneMinder/ZoneMinder/pull/772) ([balr0g](https://github.com/balr0g)) +- POD: zmupdate.pl converted to "pod2usage" [\#763](https://github.com/ZoneMinder/ZoneMinder/pull/763) ([onlyjob](https://github.com/onlyjob)) +- build: fix FTBFS with format-hardening \(please review\) [\#761](https://github.com/ZoneMinder/ZoneMinder/pull/761) ([onlyjob](https://github.com/onlyjob)) +- fixing POD errors [\#759](https://github.com/ZoneMinder/ZoneMinder/pull/759) ([onlyjob](https://github.com/onlyjob)) +- Ignore autogenerated files in git [\#746](https://github.com/ZoneMinder/ZoneMinder/pull/746) ([manupap1](https://github.com/manupap1)) +- when auth is needed, try command again before dying. [\#739](https://github.com/ZoneMinder/ZoneMinder/pull/739) ([connortechnology](https://github.com/connortechnology)) +- remove NETPBM dependency from autotools [\#737](https://github.com/ZoneMinder/ZoneMinder/pull/737) ([knnniggett](https://github.com/knnniggett)) +- fix extra slash when adding trackurl to controlurl [\#732](https://github.com/ZoneMinder/ZoneMinder/pull/732) ([connortechnology](https://github.com/connortechnology)) +- Fix image and css import paths for style/skin named "flat" [\#730](https://github.com/ZoneMinder/ZoneMinder/pull/730) ([ljack](https://github.com/ljack)) +- Update control.css [\#729](https://github.com/ZoneMinder/ZoneMinder/pull/729) ([ljack](https://github.com/ljack)) +- Fix event view [\#728](https://github.com/ZoneMinder/ZoneMinder/pull/728) ([connortechnology](https://github.com/connortechnology)) +- User selectable arp tool [\#723](https://github.com/ZoneMinder/ZoneMinder/pull/723) ([knnniggett](https://github.com/knnniggett)) +- remove unneeded files [\#722](https://github.com/ZoneMinder/ZoneMinder/pull/722) ([knnniggett](https://github.com/knnniggett)) +- add onvif ptz control into update script [\#721](https://github.com/ZoneMinder/ZoneMinder/pull/721) ([knnniggett](https://github.com/knnniggett)) +- Don't show ONVIf probe link when ONVIF support is not enabled [\#720](https://github.com/ZoneMinder/ZoneMinder/pull/720) ([knnniggett](https://github.com/knnniggett)) +- Allow zm to build w/o ffmpeg [\#719](https://github.com/ZoneMinder/ZoneMinder/pull/719) ([knnniggett](https://github.com/knnniggett)) +- Removed el6 from endif arguments [\#718](https://github.com/ZoneMinder/ZoneMinder/pull/718) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Update fr\_fr.php [\#714](https://github.com/ZoneMinder/ZoneMinder/pull/714) ([Jypy](https://github.com/Jypy)) +- Check to make sure that skin and css are valid. [\#713](https://github.com/ZoneMinder/ZoneMinder/pull/713) ([connortechnology](https://github.com/connortechnology)) +- Fixes \#710 Added libavformat version check around free context functions [\#711](https://github.com/ZoneMinder/ZoneMinder/pull/711) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- try harder to find arp. [\#709](https://github.com/ZoneMinder/ZoneMinder/pull/709) ([connortechnology](https://github.com/connortechnology)) +- Make el6 and el7 build process a little more automated [\#704](https://github.com/ZoneMinder/ZoneMinder/pull/704) ([clipo1979](https://github.com/clipo1979)) +- small improvements: [\#702](https://github.com/ZoneMinder/ZoneMinder/pull/702) ([connortechnology](https://github.com/connortechnology)) +- Centos 7 rpm packaging [\#700](https://github.com/ZoneMinder/ZoneMinder/pull/700) ([knnniggett](https://github.com/knnniggett)) +- tmpfile.conf for systemd [\#699](https://github.com/ZoneMinder/ZoneMinder/pull/699) ([clipo1979](https://github.com/clipo1979)) +- Improve delete event [\#696](https://github.com/ZoneMinder/ZoneMinder/pull/696) ([connortechnology](https://github.com/connortechnology)) +- process RTSP DESCRIBE response header [\#687](https://github.com/ZoneMinder/ZoneMinder/pull/687) ([knnniggett](https://github.com/knnniggett)) + +## [v1.28.1](https://github.com/ZoneMinder/ZoneMinder/tree/v1.28.1) (2015-02-05) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.28.0...v1.28.1) + +**Merged pull requests:** + +- fix content-type parsing when there are options on it [\#692](https://github.com/ZoneMinder/ZoneMinder/pull/692) ([connortechnology](https://github.com/connortechnology)) +- this fixes Digest Auth for the mjpeg stream on a TV-IP302PI [\#691](https://github.com/ZoneMinder/ZoneMinder/pull/691) ([connortechnology](https://github.com/connortechnology)) +- small performance improvement when streaming. [\#675](https://github.com/ZoneMinder/ZoneMinder/pull/675) ([connortechnology](https://github.com/connortechnology)) +- Kill zmcontrol [\#666](https://github.com/ZoneMinder/ZoneMinder/pull/666) ([connortechnology](https://github.com/connortechnology)) +- Don't fail if an unexpected rtp packet type is received [\#665](https://github.com/ZoneMinder/ZoneMinder/pull/665) ([knnniggett](https://github.com/knnniggett)) +- Versions command line args [\#664](https://github.com/ZoneMinder/ZoneMinder/pull/664) ([connortechnology](https://github.com/connortechnology)) +- Update et\_ee.php [\#662](https://github.com/ZoneMinder/ZoneMinder/pull/662) ([hanzese](https://github.com/hanzese)) +- \#658 Fix error message for finding arp path [\#660](https://github.com/ZoneMinder/ZoneMinder/pull/660) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Make the log export use ZM\_PATH\_SWAP and report the full path on error [\#657](https://github.com/ZoneMinder/ZoneMinder/pull/657) ([connortechnology](https://github.com/connortechnology)) +- replace getLoad\(\) / getDiskPercent\(\) with PHP native functions [\#654](https://github.com/ZoneMinder/ZoneMinder/pull/654) ([lifeofguenter](https://github.com/lifeofguenter)) +- Modified zmfilter.pl.in to fix \#652, crashing while processing backgroun... [\#653](https://github.com/ZoneMinder/ZoneMinder/pull/653) ([thebostik](https://github.com/thebostik)) +- Remove no longer needed patch because applied to master [\#651](https://github.com/ZoneMinder/ZoneMinder/pull/651) ([manupap1](https://github.com/manupap1)) +- Don't check for zmdc.pl when stopping via systemd [\#647](https://github.com/ZoneMinder/ZoneMinder/pull/647) ([ariscop](https://github.com/ariscop)) +- Split the debian package into several packages [\#646](https://github.com/ZoneMinder/ZoneMinder/pull/646) ([manupap1](https://github.com/manupap1)) +- Skin css default [\#645](https://github.com/ZoneMinder/ZoneMinder/pull/645) ([connortechnology](https://github.com/connortechnology)) +- Offer login prompt instead of throwing error [\#640](https://github.com/ZoneMinder/ZoneMinder/pull/640) ([jrd288](https://github.com/jrd288)) +- zmfilter: Send message for events that are still ongoing [\#638](https://github.com/ZoneMinder/ZoneMinder/pull/638) ([KristofRobot](https://github.com/KristofRobot)) +- Some fixes to the debian folder [\#636](https://github.com/ZoneMinder/ZoneMinder/pull/636) ([manupap1](https://github.com/manupap1)) +- Improve zmcontrol.pl [\#635](https://github.com/ZoneMinder/ZoneMinder/pull/635) ([connortechnology](https://github.com/connortechnology)) +- Add debconf / dbconfig support to debian8 folder [\#634](https://github.com/ZoneMinder/ZoneMinder/pull/634) ([manupap1](https://github.com/manupap1)) +- better fix for the view=console security flaw. [\#632](https://github.com/ZoneMinder/ZoneMinder/pull/632) ([connortechnology](https://github.com/connortechnology)) +- add check to see if user has rights to view this monitor [\#631](https://github.com/ZoneMinder/ZoneMinder/pull/631) ([connortechnology](https://github.com/connortechnology)) +- fix auth requirement on view=console by checking for user when AUTH is on [\#628](https://github.com/ZoneMinder/ZoneMinder/pull/628) ([connortechnology](https://github.com/connortechnology)) +- Output to stderror when zmu can't read zm.conf [\#627](https://github.com/ZoneMinder/ZoneMinder/pull/627) ([knnniggett](https://github.com/knnniggett)) +- Add missing dependency to policykit-1 [\#621](https://github.com/ZoneMinder/ZoneMinder/pull/621) ([manupap1](https://github.com/manupap1)) +- Replace PHP Short Open Tags - Fixes \#11 [\#620](https://github.com/ZoneMinder/ZoneMinder/pull/620) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Rtsp [\#615](https://github.com/ZoneMinder/ZoneMinder/pull/615) ([knnniggett](https://github.com/knnniggett)) +- Merge flat css to classic [\#614](https://github.com/ZoneMinder/ZoneMinder/pull/614) ([connortechnology](https://github.com/connortechnology)) +- echo the URL to the RTSP device during the OPTIONS directive [\#608](https://github.com/ZoneMinder/ZoneMinder/pull/608) ([knnniggett](https://github.com/knnniggett)) +- Fix some memory leaks in zma [\#607](https://github.com/ZoneMinder/ZoneMinder/pull/607) ([manupap1](https://github.com/manupap1)) +- Fix a mismatched free in zmc binary [\#606](https://github.com/ZoneMinder/ZoneMinder/pull/606) ([manupap1](https://github.com/manupap1)) +- New debian folder for jessie release [\#605](https://github.com/ZoneMinder/ZoneMinder/pull/605) ([manupap1](https://github.com/manupap1)) +- Css skins for classic [\#602](https://github.com/ZoneMinder/ZoneMinder/pull/602) ([connortechnology](https://github.com/connortechnology)) +- Fix package dependency on debian jessie [\#596](https://github.com/ZoneMinder/ZoneMinder/pull/596) ([manupap1](https://github.com/manupap1)) +- updated local\_zoneminder type extension file [\#594](https://github.com/ZoneMinder/ZoneMinder/pull/594) ([ndobbs](https://github.com/ndobbs)) +- Creating options documentation fixes \#568 [\#591](https://github.com/ZoneMinder/ZoneMinder/pull/591) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Convert french lang file to UTF-8 [\#589](https://github.com/ZoneMinder/ZoneMinder/pull/589) ([manupap1](https://github.com/manupap1)) +- Digest auth [\#588](https://github.com/ZoneMinder/ZoneMinder/pull/588) ([connortechnology](https://github.com/connortechnology)) +- Zmupdatefixes [\#584](https://github.com/ZoneMinder/ZoneMinder/pull/584) ([connortechnology](https://github.com/connortechnology)) +- Update et\_ee.php [\#582](https://github.com/ZoneMinder/ZoneMinder/pull/582) ([hanzese](https://github.com/hanzese)) +- Add zmeditconfigdata.sh script to source [\#577](https://github.com/ZoneMinder/ZoneMinder/pull/577) ([knnniggett](https://github.com/knnniggett)) +- Close logger and database on exit [\#575](https://github.com/ZoneMinder/ZoneMinder/pull/575) ([manupap1](https://github.com/manupap1)) +- Fix memory leaks with rtsp and a bug [\#574](https://github.com/ZoneMinder/ZoneMinder/pull/574) ([manupap1](https://github.com/manupap1)) +- Fix a bug when closing RTSP session over TCP [\#573](https://github.com/ZoneMinder/ZoneMinder/pull/573) ([manupap1](https://github.com/manupap1)) +- remove the case for level \>= 2. Since level is a bool, this code can ne... [\#572](https://github.com/ZoneMinder/ZoneMinder/pull/572) ([connortechnology](https://github.com/connortechnology)) +- Add Control 3S N5071 Dome Ptz Camera [\#570](https://github.com/ZoneMinder/ZoneMinder/pull/570) ([jmcastro2014](https://github.com/jmcastro2014)) +- Add the ability to specify the zm configdir at build time. [\#567](https://github.com/ZoneMinder/ZoneMinder/pull/567) ([knnniggett](https://github.com/knnniggett)) +- Debian package migration to CMake and some improves with lintian help [\#565](https://github.com/ZoneMinder/ZoneMinder/pull/565) ([cosmedd](https://github.com/cosmedd)) +- Use gnutls-openssl instead of gnutls to fix build with CMake. [\#564](https://github.com/ZoneMinder/ZoneMinder/pull/564) ([cosmedd](https://github.com/cosmedd)) +- Use our own SSRC when sending packets on the RTP control stream [\#561](https://github.com/ZoneMinder/ZoneMinder/pull/561) ([manupap1](https://github.com/manupap1)) +- Send keepalive messages if the rtsp server supports this feature [\#560](https://github.com/ZoneMinder/ZoneMinder/pull/560) ([manupap1](https://github.com/manupap1)) +- Fixed bug in rtsp streaming caused by a bad string concatenation [\#557](https://github.com/ZoneMinder/ZoneMinder/pull/557) ([manupap1](https://github.com/manupap1)) +- Add a stringVector join function for future use [\#556](https://github.com/ZoneMinder/ZoneMinder/pull/556) ([connortechnology](https://github.com/connortechnology)) +- Fixed bug in rtsp streaming caused by a signed - unsigned conversion. [\#555](https://github.com/ZoneMinder/ZoneMinder/pull/555) ([manupap1](https://github.com/manupap1)) +- Update Ubuntu install instructions [\#550](https://github.com/ZoneMinder/ZoneMinder/pull/550) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Ignore more files and initial travis framework [\#544](https://github.com/ZoneMinder/ZoneMinder/pull/544) ([kylejohnson](https://github.com/kylejohnson)) +- Update Travis to ffmpeg 2.4.2 [\#539](https://github.com/ZoneMinder/ZoneMinder/pull/539) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Add libvlc to Travis [\#535](https://github.com/ZoneMinder/ZoneMinder/pull/535) ([knnniggett](https://github.com/knnniggett)) +- 351-Rebase Attempt for ffmpeg stability fixes [\#531](https://github.com/ZoneMinder/ZoneMinder/pull/531) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- 478 Basic ONVIF Support [\#479](https://github.com/ZoneMinder/ZoneMinder/pull/479) ([altaroca](https://github.com/altaroca)) + +## [v1.28.0](https://github.com/ZoneMinder/ZoneMinder/tree/v1.28.0) (2014-10-18) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.27.0...v1.28.0) + +**Merged pull requests:** + +- fixes ftbs with no ffmpeg support [\#530](https://github.com/ZoneMinder/ZoneMinder/pull/530) ([knnniggett](https://github.com/knnniggett)) +- 498-Docker-Container-Broken [\#527](https://github.com/ZoneMinder/ZoneMinder/pull/527) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Changes to handling of tmpdir & related variables [\#524](https://github.com/ZoneMinder/ZoneMinder/pull/524) ([knnniggett](https://github.com/knnniggett)) +- Fixes 520 travis build ffmpeg failure [\#521](https://github.com/ZoneMinder/ZoneMinder/pull/521) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- full systemd support [\#502](https://github.com/ZoneMinder/ZoneMinder/pull/502) ([knnniggett](https://github.com/knnniggett)) +- Minor corrections to README.OpenSuse [\#501](https://github.com/ZoneMinder/ZoneMinder/pull/501) ([PX03AFK](https://github.com/PX03AFK)) +- Allow use other webservers than apache. [\#493](https://github.com/ZoneMinder/ZoneMinder/pull/493) ([cosmedd](https://github.com/cosmedd)) +- Initial attempt to migrate wiki to readthedocs \#434 [\#492](https://github.com/ZoneMinder/ZoneMinder/pull/492) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Update FI9821W\_Y2k.pm [\#485](https://github.com/ZoneMinder/ZoneMinder/pull/485) ([florian-asche](https://github.com/florian-asche)) +- V4l to monitor [\#480](https://github.com/ZoneMinder/ZoneMinder/pull/480) ([connortechnology](https://github.com/connortechnology)) +- Modified zmlinkcontent to chown and chmod content folder. Fixes \#463 [\#465](https://github.com/ZoneMinder/ZoneMinder/pull/465) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Fix for shared data size conflict [\#462](https://github.com/ZoneMinder/ZoneMinder/pull/462) ([knnniggett](https://github.com/knnniggett)) +- Update rules [\#459](https://github.com/ZoneMinder/ZoneMinder/pull/459) ([whopperg](https://github.com/whopperg)) +- Determine full path to arp [\#458](https://github.com/ZoneMinder/ZoneMinder/pull/458) ([knnniggett](https://github.com/knnniggett)) +- Fixes errors when opening Filters \(issue \#34\) [\#457](https://github.com/ZoneMinder/ZoneMinder/pull/457) ([knnniggett](https://github.com/knnniggett)) +- Fixed missing $ on ARRAY\(event\[id\]\). Fixes \#455 [\#456](https://github.com/ZoneMinder/ZoneMinder/pull/456) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- Wrap sort order. Fixes \#450 [\#451](https://github.com/ZoneMinder/ZoneMinder/pull/451) ([SteveGilvarry](https://github.com/SteveGilvarry)) +- scripts: BusyBox compatibility [\#445](https://github.com/ZoneMinder/ZoneMinder/pull/445) ([clandmeter](https://github.com/clandmeter)) +- Fixed issue DateTime handling in filter queries that broke timeline view... [\#442](https://github.com/ZoneMinder/ZoneMinder/pull/442) ([Tim-Craig](https://github.com/Tim-Craig)) +- Cleaning up the Contribution section of the README [\#440](https://github.com/ZoneMinder/ZoneMinder/pull/440) ([kylejohnson](https://github.com/kylejohnson)) +- Add Bountysource badge to README [\#438](https://github.com/ZoneMinder/ZoneMinder/pull/438) ([bountysource-support](https://github.com/bountysource-support)) +- Add new colums to zm\_create.sql.in [\#426](https://github.com/ZoneMinder/ZoneMinder/pull/426) ([m-bene](https://github.com/m-bene)) +- Ffmpegoptions [\#421](https://github.com/ZoneMinder/ZoneMinder/pull/421) ([m-bene](https://github.com/m-bene)) +- Update zm\_jpeg.cpp [\#418](https://github.com/ZoneMinder/ZoneMinder/pull/418) ([ghost](https://github.com/ghost)) +- Added an FAQ for AlarmCheckMethod [\#416](https://github.com/ZoneMinder/ZoneMinder/pull/416) ([kylejohnson](https://github.com/kylejohnson)) +- make skin selection persistent [\#415](https://github.com/ZoneMinder/ZoneMinder/pull/415) ([m-bene](https://github.com/m-bene)) +- Added a doc for contributing to the project [\#413](https://github.com/ZoneMinder/ZoneMinder/pull/413) ([kylejohnson](https://github.com/kylejohnson)) +- Update zmtrigger.pl.in [\#411](https://github.com/ZoneMinder/ZoneMinder/pull/411) ([martin67](https://github.com/martin67)) +- Add the web/api folder to cmake [\#409](https://github.com/ZoneMinder/ZoneMinder/pull/409) ([mastertheknife](https://github.com/mastertheknife)) +- Move API to under web dir [\#408](https://github.com/ZoneMinder/ZoneMinder/pull/408) ([kylejohnson](https://github.com/kylejohnson)) +- Rtsp digest [\#407](https://github.com/ZoneMinder/ZoneMinder/pull/407) ([m-bene](https://github.com/m-bene)) +- focus popup windows [\#406](https://github.com/ZoneMinder/ZoneMinder/pull/406) ([m-bene](https://github.com/m-bene)) +- remove call of undefined "fixDevices" function [\#405](https://github.com/ZoneMinder/ZoneMinder/pull/405) ([m-bene](https://github.com/m-bene)) +- Zms/videostream improvements [\#404](https://github.com/ZoneMinder/ZoneMinder/pull/404) ([Sune1337](https://github.com/Sune1337)) +- RESTful API in CakePHP, and docs [\#403](https://github.com/ZoneMinder/ZoneMinder/pull/403) ([kylejohnson](https://github.com/kylejohnson)) +- Remove SVN install from Travis CI [\#395](https://github.com/ZoneMinder/ZoneMinder/pull/395) ([hamiltont](https://github.com/hamiltont)) +- Remove Subversion from prerequesite lists [\#393](https://github.com/ZoneMinder/ZoneMinder/pull/393) ([hamiltont](https://github.com/hamiltont)) +- Dockerfile works properly, documentation updated [\#392](https://github.com/ZoneMinder/ZoneMinder/pull/392) ([hamiltont](https://github.com/hamiltont)) +- Remove apt-get upgrade [\#390](https://github.com/ZoneMinder/ZoneMinder/pull/390) ([hamiltont](https://github.com/hamiltont)) +- Update README.OpenSuse [\#389](https://github.com/ZoneMinder/ZoneMinder/pull/389) ([PX03AFK](https://github.com/PX03AFK)) +- Update CMakeLists.txt [\#388](https://github.com/ZoneMinder/ZoneMinder/pull/388) ([PX03AFK](https://github.com/PX03AFK)) +- Update zoneminder.cmake.OS13.spec - minor corrections [\#387](https://github.com/ZoneMinder/ZoneMinder/pull/387) ([PX03AFK](https://github.com/PX03AFK)) +- fix sql error which prevents remote login from working [\#385](https://github.com/ZoneMinder/ZoneMinder/pull/385) ([maciekczwa](https://github.com/maciekczwa)) +- Fix window sizes and input field sizes for flat skin [\#381](https://github.com/ZoneMinder/ZoneMinder/pull/381) ([m-bene](https://github.com/m-bene)) +- Fix reload loop on switching skins [\#380](https://github.com/ZoneMinder/ZoneMinder/pull/380) ([m-bene](https://github.com/m-bene)) +- Ability to skip frames in motion detection. [\#377](https://github.com/ZoneMinder/ZoneMinder/pull/377) ([Sune1337](https://github.com/Sune1337)) +- same dvr controls in event as in monitor [\#375](https://github.com/ZoneMinder/ZoneMinder/pull/375) ([m-bene](https://github.com/m-bene)) +- do not quote column names in parse filter [\#374](https://github.com/ZoneMinder/ZoneMinder/pull/374) ([m-bene](https://github.com/m-bene)) +- Fix 'Undefined index: filter' php warnings for filter view when [\#373](https://github.com/ZoneMinder/ZoneMinder/pull/373) ([m-bene](https://github.com/m-bene)) +- Reduce window size to exclude task bar area [\#371](https://github.com/ZoneMinder/ZoneMinder/pull/371) ([m-bene](https://github.com/m-bene)) +- Update zoneminder.tmpfiles [\#367](https://github.com/ZoneMinder/ZoneMinder/pull/367) ([PX03AFK](https://github.com/PX03AFK)) +- Update zoneminder.cmake.OS13.spec [\#362](https://github.com/ZoneMinder/ZoneMinder/pull/362) ([PX03AFK](https://github.com/PX03AFK)) +- Update README.OpenSuse [\#361](https://github.com/ZoneMinder/ZoneMinder/pull/361) ([PX03AFK](https://github.com/PX03AFK)) +- fix eyezm authentication issue [\#359](https://github.com/ZoneMinder/ZoneMinder/pull/359) ([knnniggett](https://github.com/knnniggett)) +- Fix prev button while in gapless mode. All buttons tested and working. [\#358](https://github.com/ZoneMinder/ZoneMinder/pull/358) ([knnniggett](https://github.com/knnniggett)) +- Update zmupdate.pl.in [\#353](https://github.com/ZoneMinder/ZoneMinder/pull/353) ([barjac](https://github.com/barjac)) +- make curl header check case insensitive [\#352](https://github.com/ZoneMinder/ZoneMinder/pull/352) ([m-bene](https://github.com/m-bene)) +- tie distro/opensuse folder into cmake build process [\#349](https://github.com/ZoneMinder/ZoneMinder/pull/349) ([knnniggett](https://github.com/knnniggett)) +- Initial upload for opensuse rpm [\#348](https://github.com/ZoneMinder/ZoneMinder/pull/348) ([PX03AFK](https://github.com/PX03AFK)) +- Patch for Debian bug 736516 - FTBFS on powerpc arch. [\#346](https://github.com/ZoneMinder/ZoneMinder/pull/346) ([knnniggett](https://github.com/knnniggett)) +- Nagyrobi [\#342](https://github.com/ZoneMinder/ZoneMinder/pull/342) ([knnniggett](https://github.com/knnniggett)) +- add feature to extend preclusive zone alarm state by x frames [\#338](https://github.com/ZoneMinder/ZoneMinder/pull/338) ([m-bene](https://github.com/m-bene)) +- Support building with new libavcodec versions. [\#325](https://github.com/ZoneMinder/ZoneMinder/pull/325) ([elenril](https://github.com/elenril)) +- Mysql2 pdo [\#231](https://github.com/ZoneMinder/ZoneMinder/pull/231) ([connortechnology](https://github.com/connortechnology)) + +## [v1.27.0](https://github.com/ZoneMinder/ZoneMinder/tree/v1.27.0) (2014-03-15) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.26.5...v1.27.0) + +**Merged pull requests:** + +- zmcamtool.pl - import and export ptz camera controls & camera presets [\#318](https://github.com/ZoneMinder/ZoneMinder/pull/318) ([kylejohnson](https://github.com/kylejohnson)) +- Example script to react to monitor alarms [\#317](https://github.com/ZoneMinder/ZoneMinder/pull/317) ([kylejohnson](https://github.com/kylejohnson)) +- Change comments for many Camera subclasses [\#316](https://github.com/ZoneMinder/ZoneMinder/pull/316) ([nereocystis](https://github.com/nereocystis)) +- Enable universe in the Docker container \[ci skip\] [\#310](https://github.com/ZoneMinder/ZoneMinder/pull/310) ([kylejohnson](https://github.com/kylejohnson)) +- Add wget to Dockerfile prereq [\#309](https://github.com/ZoneMinder/ZoneMinder/pull/309) ([kylejohnson](https://github.com/kylejohnson)) +- Add paths to flat skin in configure.ac and Makefile.am [\#308](https://github.com/ZoneMinder/ZoneMinder/pull/308) ([kylejohnson](https://github.com/kylejohnson)) +- Zoneminder - flat theme. [\#303](https://github.com/ZoneMinder/ZoneMinder/pull/303) ([justinlawrence](https://github.com/justinlawrence)) +- Additional compile guards to allow compilation on non-x86 archs [\#302](https://github.com/ZoneMinder/ZoneMinder/pull/302) ([pjhacnau](https://github.com/pjhacnau)) +- Adding PTZ module for Toshiba IK-WB11A [\#300](https://github.com/ZoneMinder/ZoneMinder/pull/300) ([Tim-Craig](https://github.com/Tim-Craig)) +- Add cURL source type [\#297](https://github.com/ZoneMinder/ZoneMinder/pull/297) ([mastertheknife](https://github.com/mastertheknife)) +- glob ptz scripts under control folder [\#294](https://github.com/ZoneMinder/ZoneMinder/pull/294) ([knnniggett](https://github.com/knnniggett)) +- Enclose value of the Notes attribute with quotes. [\#293](https://github.com/ZoneMinder/ZoneMinder/pull/293) ([hankintosh](https://github.com/hankintosh)) +- Fix for few problems in Debian packaging [\#290](https://github.com/ZoneMinder/ZoneMinder/pull/290) ([dmak](https://github.com/dmak)) +- Foscam IP cameras control script [\#289](https://github.com/ZoneMinder/ZoneMinder/pull/289) ([dmak](https://github.com/dmak)) +- Specfile changes related to zmfix [\#284](https://github.com/ZoneMinder/ZoneMinder/pull/284) ([knnniggett](https://github.com/knnniggett)) +- Remove references to zmfix in /distros [\#283](https://github.com/ZoneMinder/ZoneMinder/pull/283) ([nkwood](https://github.com/nkwood)) +- Add zmMontageScale so montage scale is also 'saved' [\#282](https://github.com/ZoneMinder/ZoneMinder/pull/282) ([scottgrobinson](https://github.com/scottgrobinson)) +- In the web/includes/functions.php there were some html formatting errors... [\#281](https://github.com/ZoneMinder/ZoneMinder/pull/281) ([martonmiklos](https://github.com/martonmiklos)) +- Remove zmfix [\#280](https://github.com/ZoneMinder/ZoneMinder/pull/280) ([mastertheknife](https://github.com/mastertheknife)) +- Add libVLC monitor type [\#277](https://github.com/ZoneMinder/ZoneMinder/pull/277) ([ebarnard](https://github.com/ebarnard)) +- Disables non-POSIX warning when using gnu wildcard [\#276](https://github.com/ZoneMinder/ZoneMinder/pull/276) ([knnniggett](https://github.com/knnniggett)) +- remove embedded jquery. \(fixes \#274\) [\#275](https://github.com/ZoneMinder/ZoneMinder/pull/275) ([kylejohnson](https://github.com/kylejohnson)) +- Fixes \#313, initial commit of 'working' dockerfile [\#314](https://github.com/ZoneMinder/ZoneMinder/pull/314) ([kylejohnson](https://github.com/kylejohnson)) + +## [v1.26.5](https://github.com/ZoneMinder/ZoneMinder/tree/v1.26.5) (2013-12-16) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.26.4...v1.26.5) + +**Merged pull requests:** + +- Add reference to zm\_update-1.26.5.sql in Makefile.am [\#269](https://github.com/ZoneMinder/ZoneMinder/pull/269) ([knnniggett](https://github.com/knnniggett)) +- Detection Support for WansView Cams [\#268](https://github.com/ZoneMinder/ZoneMinder/pull/268) ([Phhere](https://github.com/Phhere)) +- use proper DBI parameter passing to improve security [\#264](https://github.com/ZoneMinder/ZoneMinder/pull/264) ([connortechnology](https://github.com/connortechnology)) +- Fix RTSP decoding errors in 1.26.4 \(addresses \#221\) [\#259](https://github.com/ZoneMinder/ZoneMinder/pull/259) ([ebarnard](https://github.com/ebarnard)) +- Network Detection Support for Wansview [\#257](https://github.com/ZoneMinder/ZoneMinder/pull/257) ([Phhere](https://github.com/Phhere)) +- Fix checkJsonError messages [\#256](https://github.com/ZoneMinder/ZoneMinder/pull/256) ([Phhere](https://github.com/Phhere)) +- Update README.md [\#255](https://github.com/ZoneMinder/ZoneMinder/pull/255) ([zdanek](https://github.com/zdanek)) +- ipv6 support [\#252](https://github.com/ZoneMinder/ZoneMinder/pull/252) ([guotie](https://github.com/guotie)) +- Better inno d bupdate [\#251](https://github.com/ZoneMinder/ZoneMinder/pull/251) ([connortechnology](https://github.com/connortechnology)) +- Fix shared memory errors on centos 6.4 [\#250](https://github.com/ZoneMinder/ZoneMinder/pull/250) ([insidenothing](https://github.com/insidenothing)) +- Update zoneminder.service [\#246](https://github.com/ZoneMinder/ZoneMinder/pull/246) ([dtmf](https://github.com/dtmf)) +- remove extra stuff that I don't think we need because we are the source. Opinions? [\#240](https://github.com/ZoneMinder/ZoneMinder/pull/240) ([connortechnology](https://github.com/connortechnology)) +- Cast content\_length to signed int for error-check comparison [\#232](https://github.com/ZoneMinder/ZoneMinder/pull/232) ([josephevans](https://github.com/josephevans)) +- Apply INSERTs in Event::AddFrames in batches to fix issue \#222 [\#223](https://github.com/ZoneMinder/ZoneMinder/pull/223) ([fastolfe](https://github.com/fastolfe)) +- ffmpeg detection improvements [\#218](https://github.com/ZoneMinder/ZoneMinder/pull/218) ([mastertheknife](https://github.com/mastertheknife)) +- ZoneMinder Dutch Translation updates by Alco \(a.k. nightcrawler\) [\#211](https://github.com/ZoneMinder/ZoneMinder/pull/211) ([kylejohnson](https://github.com/kylejohnson)) +- Change Prev Button functionality [\#207](https://github.com/ZoneMinder/ZoneMinder/pull/207) ([knnniggett](https://github.com/knnniggett)) +- Delete PATH\_BUILD and TIME\_BUILD from zm.conf and fix ZM\_DB\_TYPE [\#243](https://github.com/ZoneMinder/ZoneMinder/pull/243) ([mastertheknife](https://github.com/mastertheknife)) +- Removeversionnumberfromzm.conf [\#242](https://github.com/ZoneMinder/ZoneMinder/pull/242) ([connortechnology](https://github.com/connortechnology)) +- Add alarm reference image blend percentage option and replace the text field if fast blends are enabled [\#241](https://github.com/ZoneMinder/ZoneMinder/pull/241) ([mastertheknife](https://github.com/mastertheknife)) +- Fix marker-out-of-bounds crash when defining zone points [\#233](https://github.com/ZoneMinder/ZoneMinder/pull/233) ([fastolfe](https://github.com/fastolfe)) + +## [v1.26.4](https://github.com/ZoneMinder/ZoneMinder/tree/v1.26.4) (2013-10-08) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.26.3...v1.26.4) + +**Merged pull requests:** + +- Change frameserver warnings to debug level 2 [\#205](https://github.com/ZoneMinder/ZoneMinder/pull/205) ([knnniggett](https://github.com/knnniggett)) +- Create pkgdatadir in make [\#203](https://github.com/ZoneMinder/ZoneMinder/pull/203) ([knnniggett](https://github.com/knnniggett)) +- Signal improvements and fixes [\#201](https://github.com/ZoneMinder/ZoneMinder/pull/201) ([mastertheknife](https://github.com/mastertheknife)) +- Create ZM\_PATH\_DATA and point zmupdate to ZM\_PATH\_DATA/db [\#200](https://github.com/ZoneMinder/ZoneMinder/pull/200) ([knnniggett](https://github.com/knnniggett)) +- remove ${CMAKE\_CURRENT\_SOURCE\_DIR} from add\_custom\_target [\#199](https://github.com/ZoneMinder/ZoneMinder/pull/199) ([knnniggett](https://github.com/knnniggett)) +- Added missing word in readme [\#194](https://github.com/ZoneMinder/ZoneMinder/pull/194) ([WDKevin](https://github.com/WDKevin)) +- Add cmake to ZoneMinder [\#178](https://github.com/ZoneMinder/ZoneMinder/pull/178) ([mastertheknife](https://github.com/mastertheknife)) +- Rtsp updates [\#174](https://github.com/ZoneMinder/ZoneMinder/pull/174) ([POKKAHOH](https://github.com/POKKAHOH)) +- Solution for Issue \#170 [\#172](https://github.com/ZoneMinder/ZoneMinder/pull/172) ([raulcaj](https://github.com/raulcaj)) +- Fixing debian build files including automated database setup [\#164](https://github.com/ZoneMinder/ZoneMinder/pull/164) ([jaydio](https://github.com/jaydio)) +- Add fedora rpm development files to zoneminder source tree [\#163](https://github.com/ZoneMinder/ZoneMinder/pull/163) ([knnniggett](https://github.com/knnniggett)) +- Improve Chrome browser support & log streaming events [\#162](https://github.com/ZoneMinder/ZoneMinder/pull/162) ([knnniggett](https://github.com/knnniggett)) +- Can't seem to catch a break tonight. Moving debian files into correct folder [\#149](https://github.com/ZoneMinder/ZoneMinder/pull/149) ([knnniggett](https://github.com/knnniggett)) +- Move debian folder under distros [\#148](https://github.com/ZoneMinder/ZoneMinder/pull/148) ([knnniggett](https://github.com/knnniggett)) +- Removing the redhat folder from the root for real this time [\#141](https://github.com/ZoneMinder/ZoneMinder/pull/141) ([knnniggett](https://github.com/knnniggett)) +- Redhat [\#136](https://github.com/ZoneMinder/ZoneMinder/pull/136) ([knnniggett](https://github.com/knnniggett)) +- Error correction in database creation script [\#122](https://github.com/ZoneMinder/ZoneMinder/pull/122) ([dukess](https://github.com/dukess)) +- Rewritten the query to allow mysql to use indexes [\#121](https://github.com/ZoneMinder/ZoneMinder/pull/121) ([rkojedzinszky](https://github.com/rkojedzinszky)) +- Update zmupdate.pl.in for 1.26.3 release [\#119](https://github.com/ZoneMinder/ZoneMinder/pull/119) ([knnniggett](https://github.com/knnniggett)) + +## [v1.26.3](https://github.com/ZoneMinder/ZoneMinder/tree/v1.26.3) (2013-09-10) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.26.2...v1.26.3) + +**Merged pull requests:** + +- Add 1.26.1 and 1.26.2 releases to zmupdate [\#116](https://github.com/ZoneMinder/ZoneMinder/pull/116) ([knnniggett](https://github.com/knnniggett)) + +## [v1.26.2](https://github.com/ZoneMinder/ZoneMinder/tree/v1.26.2) (2013-09-06) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.26.1...v1.26.2) + +**Merged pull requests:** + +- Use GitHub repo for version check [\#111](https://github.com/ZoneMinder/ZoneMinder/pull/111) ([chriswiggins](https://github.com/chriswiggins)) + +## [v1.26.1](https://github.com/ZoneMinder/ZoneMinder/tree/v1.26.1) (2013-09-06) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.26.0...v1.26.1) + +## [v1.26.0](https://github.com/ZoneMinder/ZoneMinder/tree/v1.26.0) (2013-09-05) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.26-beta.3...v1.26.0) + +## [v1.26-beta.3](https://github.com/ZoneMinder/ZoneMinder/tree/v1.26-beta.3) (2013-08-28) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.26-beta.2...v1.26-beta.3) + +## [v1.26-beta.2](https://github.com/ZoneMinder/ZoneMinder/tree/v1.26-beta.2) (2013-08-15) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.26-beta.1...v1.26-beta.2) + +## [v1.26-beta.1](https://github.com/ZoneMinder/ZoneMinder/tree/v1.26-beta.1) (2013-08-13) +[Full Changelog](https://github.com/ZoneMinder/ZoneMinder/compare/v1.25...v1.26-beta.1) + +## [v1.25](https://github.com/ZoneMinder/ZoneMinder/tree/v1.25) (2013-04-12) + + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f736e7b5..09f127996 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,31 +1,63 @@ # Main CMake file for the ZoneMinder project. # Created by mastertheknife (Kfir Itzhak) -# The goal is to ease up the installation of zoneminder. -# Our current installation method (using autotools) is outdated, slow and breaks now and then. -# The CMake installation method will require no parameters at all, default should sufficient and reliable. -# It will be still possible to install ZoneMinder using autotools, they don't conflict with each other. The cmake way is a complete re-write (different syntax) and aims to be identical to the autotools way, -# by having options using the same name and leaving ZM totally unmodified, while providing exactly the same things that ZM expects (config.h, configuration in *.in files, etc). -# # For more information and installation, see the INSTALL file # cmake_minimum_required (VERSION 2.6) project (zoneminder) -set(zoneminder_VERSION "1.28.0") +file (STRINGS "version" zoneminder_VERSION) +# make API version a minor of ZM version +set(zoneminder_API_VERSION "${zoneminder_VERSION}.1") -# CMake does not allow out-of-source build if CMakeCache.exists in the source folder. Abort and notify the user to save him from headache why it doesn't work. -if((NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt")) - message(FATAL_ERROR " You are attempting to do an out-of-source build, but a cmake cache file for an in-source build exists. Please delete the file CMakeCache.txt from the source folder to proceed.") -endif((NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt")) +# Make sure the submodules are there +if( NOT EXISTS "${CMAKE_SOURCE_DIR}/web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php" ) +message( SEND_ERROR "The git submodules are not available. Please run +git submodule update --init --recursive") +endif( NOT EXISTS "${CMAKE_SOURCE_DIR}/web/api/app/Plugin/Crud/Lib/CrudControllerTrait.php" ) -# Default build type. To change the build type, use the CMAKE_BUILD_TYPE configuration option. +# CMake does not allow out-of-source build if CMakeCache.exists +# in the source folder. Abort and notify the user +if( + (NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) + AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt")) + message(FATAL_ERROR " You are attempting to do an out-of-source build, + but a cmake cache file for an in-source build exists. Please delete + the file CMakeCache.txt from the source folder to proceed.") +endif( + (NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) + AND (EXISTS "${CMAKE_SOURCE_DIR}/CMakeCache.txt")) + +# Default build type. To change the build type, +# use the CMAKE_BUILD_TYPE configuration option. if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Release or Debug" FORCE) + set(CMAKE_BUILD_TYPE + Release CACHE STRING "Build type: Release or Debug" FORCE) endif(NOT CMAKE_BUILD_TYPE) # Can assist in troubleshooting #set(CMAKE_VERBOSE_MAKEFILE ON) #set(CMAKE_INSTALL_ALWAYS ON) +# Host OS Check +set(HOST_OS "") +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + set(HOST_OS "linux") +endif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +if(${CMAKE_SYSTEM_NAME} MATCHES ".*(SunOS|Solaris).*") + set(HOST_OS "solaris") + set(SOLARIS 1) +endif(${CMAKE_SYSTEM_NAME} MATCHES ".*(SunOS|Solaris).*") +if(${CMAKE_SYSTEM_NAME} MATCHES ".*BSD.*") + set(HOST_OS "BSD") + set(BSD 1) +endif(${CMAKE_SYSTEM_NAME} MATCHES ".*BSD.*") +if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + set(HOST_OS "darwin") +endif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") +if(NOT HOST_OS) + message(FATAL_ERROR + "ZoneMinder was unable to deterimine the host OS. Please report this. Value of CMAKE_SYSTEM_NAME: ${CMAKE_SYSTEM_NAME}") +endif(NOT HOST_OS) + # Default CLFAGS and CXXFLAGS: set(CMAKE_C_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") set(CMAKE_CXX_FLAGS_RELEASE "-Wall -D__STDC_CONSTANT_MACROS -O2") @@ -42,40 +74,105 @@ include (CheckFunctionExists) include (CheckPrototypeDefinition_fixed) include (CheckTypeSize) include (CheckStructHasMember) +include (CheckSendfile) # Configuration options -mark_as_advanced(FORCE ZM_EXTRA_LIBS ZM_MYSQL_ENGINE ZM_NO_MMAP CMAKE_INSTALL_FULL_BINDIR ZM_PERL_SUBPREFIX ZM_PERL_USE_PATH ZM_TARGET_DISTRO) -set(ZM_RUNDIR "/var/run/zm" CACHE PATH "Location of transient process files, default: /var/run/zm") -set(ZM_SOCKDIR "/var/run/zm" CACHE PATH "Location of Unix domain socket files, default /var/run/zm") -set(ZM_TMPDIR "/var/tmp/zm" CACHE PATH "Location of temporary files, default: /tmp/zm") -set(ZM_LOGDIR "/var/log/zm" CACHE PATH "Location of generated log files, default: /var/log/zm") -set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www" CACHE PATH "Location of the web files, default: /${CMAKE_INSTALL_DATADIR}/zoneminder/www") -set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH "Location of the cgi-bin files, default: /${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin") -set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH "Location of dynamic content (events and images), default: /var/lib/zoneminder") -set(ZM_DB_HOST "localhost" CACHE STRING "Hostname where ZoneMinder database located, default: localhost") -set(ZM_DB_NAME "zm" CACHE STRING "Name of ZoneMinder database, default: zm") -set(ZM_DB_USER "zmuser" CACHE STRING "Name of ZoneMinder database user, default: zmuser") -set(ZM_DB_PASS "zmpass" CACHE STRING "Password of ZoneMinder database user, default: zmpass") -set(ZM_WEB_USER "" CACHE STRING "The user apache or the local web server runs on. Leave empty for automatic detection. If that fails, you can use this variable to force") -set(ZM_WEB_GROUP "" CACHE STRING "The group apache or the local web server runs on, Leave empty to be the same as the web user") +mark_as_advanced( + FORCE ZM_EXTRA_LIBS + ZM_MYSQL_ENGINE + ZM_NO_MMAP + CMAKE_INSTALL_FULL_BINDIR + ZM_PERL_MM_PARMS + ZM_PERL_SEARCH_PATH + ZM_TARGET_DISTRO + ZM_CONFIG_DIR) + +set(ZM_RUNDIR "/var/run/zm" CACHE PATH + "Location of transient process files, default: /var/run/zm") +set(ZM_SOCKDIR "/var/run/zm" CACHE PATH + "Location of Unix domain socket files, default /var/run/zm") +set(ZM_TMPDIR "/var/tmp/zm" CACHE PATH + "Location of temporary files, default: /tmp/zm") +set(ZM_LOGDIR "/var/log/zm" CACHE PATH + "Location of generated log files, default: /var/log/zm") +set(ZM_WEBDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder/www" CACHE PATH + "Location of the web files, default: /${CMAKE_INSTALL_DATADIR}/zoneminder/www") +set(ZM_CGIDIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin" CACHE PATH + "Location of the cgi-bin files, default: /${CMAKE_INSTALL_LIBEXECDIR}/zoneminder/cgi-bin") +set(ZM_CONTENTDIR "/var/lib/zoneminder" CACHE PATH + "Location of dynamic content (events and images), default: /var/lib/zoneminder") +set(ZM_DB_HOST "localhost" CACHE STRING + "Hostname where ZoneMinder database located, default: localhost") +set(ZM_DB_NAME "zm" CACHE STRING + "Name of ZoneMinder database, default: zm") +set(ZM_DB_USER "zmuser" CACHE STRING + "Name of ZoneMinder database user, default: zmuser") +set(ZM_DB_PASS "zmpass" CACHE STRING + "Password of ZoneMinder database user, default: zmpass") +set(ZM_WEB_USER "" CACHE STRING + "The user apache or the local web server runs on. Leave empty for automatic detection. + If that fails, you can use this variable to force") +set(ZM_WEB_GROUP "" CACHE STRING + "The group apache or the local web server runs on, + Leave empty to be the same as the web user") # Advanced -set(ZM_EXTRA_LIBS "" CACHE STRING "A list of optional libraries, separated by semicolons, e.g. ssl;theora") -set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING "MySQL engine to use with database, default: InnoDB") -set(ZM_NO_MMAP "OFF" CACHE BOOL "Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF") -set(ZM_NO_FFMPEG "OFF" CACHE BOOL "Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF") -set(ZM_NO_LIBVLC "OFF" CACHE BOOL "Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF") -set(ZM_NO_CURL "OFF" CACHE BOOL "Set to ON to skip cURL checks and force building ZM without cURL. default: OFF") -set(ZM_NO_X10 "OFF" CACHE BOOL "Set to ON to build ZoneMinder without X10 support. default: OFF") -set(ZM_PERL_SUBPREFIX "${CMAKE_INSTALL_LIBDIR}/perl5" CACHE PATH "Use a different directory for the zm perl modules. NOTE: This is a subprefix, e.g. lib will be turned into /lib, default: /perl5") -set(ZM_PERL_USE_PATH "${CMAKE_INSTALL_PREFIX}/${ZM_PERL_SUBPREFIX}" CACHE PATH "Override the include path for zm perl modules. Useful if you are moving the perl modules without using the ZM_PERL_SUBPREFIX option. default: /") -set(ZM_TARGET_DISTRO "" CACHE STRING "Build ZoneMinder for a specific distribution. Currently, valid names are: f19, f20, el6, OS13") +set(ZM_CONFIG_DIR "/${CMAKE_INSTALL_SYSCONFDIR}" CACHE PATH + "Location of ZoneMinder configuration, default system config directory") +set(ZM_EXTRA_LIBS "" CACHE STRING + "A list of optional libraries, separated by semicolons, e.g. ssl;theora") +set(ZM_MYSQL_ENGINE "InnoDB" CACHE STRING + "MySQL engine to use with database, default: InnoDB") +set(ZM_NO_MMAP "OFF" CACHE BOOL + "Set to ON to not use mmap shared memory. Shouldn't be enabled unless you + experience problems with the shared memory. default: OFF") +set(ZM_NO_FFMPEG "OFF" CACHE BOOL + "Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF") +set(ZM_NO_LIBVLC "OFF" CACHE BOOL +"Set to ON to skip libvlc checks and force building ZM without libvlc. default: OFF") +set(ZM_NO_CURL "OFF" CACHE BOOL + "Set to ON to skip cURL checks and force building ZM without cURL. default: OFF") +set(ZM_NO_X10 "OFF" CACHE BOOL + "Set to ON to build ZoneMinder without X10 support. default: OFF") +set(ZM_ONVIF "ON" CACHE BOOL + "Set to ON to enable basic ONVIF support. This is EXPERIMENTAL and may not + work with all cameras claiming to be ONVIF compliant. default: ON") +set(ZM_PERL_MM_PARMS INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1 CACHE STRING + "By default, ZoneMinder's Perl modules are installed into the Vendor folders, + as defined by your installation of Perl. You can change that here. Consult Perl's + MakeMaker documentation for a definition of acceptable parameters. If you set this + to something that causes the modules to be installed outside Perl's normal search + path, then you will also need to set ZM_PERL_SEARCH_PATH accordingly.") +set(ZM_PERL_SEARCH_PATH "" CACHE PATH + "Use to add a folder to your Perl's search path. This will need to be set in cases + where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are + installed outside Perl's default search path.") +set(ZM_TARGET_DISTRO "" CACHE STRING + "Build ZoneMinder for a specific distribution. Currently, valid names are: f22, f23, el6, el7, OS13") # Reassign some variables if a target distro has been specified -if((ZM_TARGET_DISTRO STREQUAL "f19") OR (ZM_TARGET_DISTRO STREQUAL "f20") OR (ZM_TARGET_DISTRO STREQUAL "el6")) +if((ZM_TARGET_DISTRO STREQUAL "f22") OR (ZM_TARGET_DISTRO STREQUAL "f23")) set(ZM_RUNDIR "/var/run/zoneminder") set(ZM_SOCKDIR "/var/lib/zoneminder/sock") set(ZM_TMPDIR "/var/lib/zoneminder/temp") set(ZM_LOGDIR "/var/log/zoneminder") + set(ZM_CONFIG_DIR "/etc/zm") + set(ZM_WEBDIR "/usr/share/zoneminder/www") + set(ZM_CGIDIR "/usr/libexec/zoneminder/cgi-bin") +elseif(ZM_TARGET_DISTRO STREQUAL "el6") + set(ZM_RUNDIR "/var/run/zoneminder") + set(ZM_SOCKDIR "/var/lib/zoneminder/sock") + set(ZM_TMPDIR "/var/lib/zoneminder/temp") + set(ZM_LOGDIR "/var/log/zoneminder") + set(ZM_WEBDIR "/usr/share/zoneminder/www") + set(ZM_CGIDIR "/usr/libexec/zoneminder/cgi-bin") +elseif(ZM_TARGET_DISTRO STREQUAL "el7") + set(ZM_RUNDIR "/var/run/zoneminder") + set(ZM_SOCKDIR "/var/lib/zoneminder/sock") + set(ZM_TMPDIR "/var/lib/zoneminder/temp") + set(ZM_LOGDIR "/var/log/zoneminder") + set(ZM_CONFIG_DIR "/etc/zm") + set(ZM_WEBDIR "/usr/share/zoneminder/www") + set(ZM_CGIDIR "/usr/libexec/zoneminder/cgi-bin") elseif(ZM_TARGET_DISTRO STREQUAL "OS13") set(ZM_RUNDIR "/var/run/zoneminder") set(ZM_TMPDIR "/var/run/zoneminder") @@ -85,32 +182,66 @@ elseif(ZM_TARGET_DISTRO STREQUAL "OS13") set(ZM_WEB_GROUP "www") set(ZM_WEBDIR "/srv/www/htdocs/zoneminder") set(ZM_CGIDIR "/srv/www/cgi-bin") -endif((ZM_TARGET_DISTRO STREQUAL "f19") OR (ZM_TARGET_DISTRO STREQUAL "f20") OR (ZM_TARGET_DISTRO STREQUAL "el6")) +elseif(ZM_TARGET_DISTRO STREQUAL "FreeBSD") + set(ZM_RUNDIR "/var/run/zm") + set(ZM_SOCKDIR "/var/run/zm") + set(ZM_TMPDIR "/var/tmp/zm") + set(ZM_CONTENTDIR "/usr/local/var/lib/zoneminder") + set(ZM_WEB_USER "www") + set(ZM_WEB_GROUP "www") + set(ZM_CONFIG_DIR "/usr/local/etc/zm") + set(ZM_WEBDIR "/usr/local/share/zoneminder/www") + set(ZM_CGIDIR "/usr/local/libexec/zoneminder/cgi-bin") + set(ZM_PERL_MM_PARMS "INSTALLDIRS=site") +endif((ZM_TARGET_DISTRO STREQUAL "f22") OR (ZM_TARGET_DISTRO STREQUAL "f23")) # Required for certain checks to work -set(CMAKE_EXTRA_INCLUDE_FILES ${CMAKE_EXTRA_INCLUDE_FILES} stdio.h stdlib.h math.h signal.h) +set(CMAKE_EXTRA_INCLUDE_FILES + ${CMAKE_EXTRA_INCLUDE_FILES} stdio.h stdlib.h math.h signal.h + ) # Required for including headers from the this folder include_directories("${CMAKE_BINARY_DIR}") # This is required to enable searching in lib64 (if exists), do not change set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON) # System checks -check_include_file("linux/videodev.h" HAVE_LINUX_VIDEODEV_H) +check_include_file("libv4l1-videodev.h" HAVE_LIBV4L1_VIDEODEV_H) +if(NOT HAVE_LIBV4L1_VIDEODEV_H) + check_include_file("linux/videodev.h" HAVE_LINUX_VIDEODEV_H) +endif(NOT HAVE_LIBV4L1_VIDEODEV_H) check_include_file("linux/videodev2.h" HAVE_LINUX_VIDEODEV2_H) check_include_file("execinfo.h" HAVE_EXECINFO_H) +if (HAVE_EXECINFO_H) + check_function_exists("backtrace" HAVE_DECL_BACKTRACE) + if (NOT HAVE_DECL_BACKTRACE) + find_library (EXECINFO_LIBRARY NAMES execinfo) + if (EXECINFO_LIBRARY) + list(APPEND ZM_BIN_LIBS "-lexecinfo") + endif (EXECINFO_LIBRARY) + endif (NOT HAVE_DECL_BACKTRACE) + check_function_exists("backtrace_symbols" HAVE_DECL_BACKTRACE_SYMBOLS) +endif (HAVE_EXECINFO_H) check_include_file("ucontext.h" HAVE_UCONTEXT_H) check_include_file("sys/sendfile.h" HAVE_SYS_SENDFILE_H) check_include_file("sys/syscall.h" HAVE_SYS_SYSCALL_H) check_function_exists("syscall" HAVE_SYSCALL) check_function_exists("sendfile" HAVE_SENDFILE) -check_function_exists("backtrace" HAVE_DECL_BACKTRACE) -check_function_exists("backtrace_symbols" HAVE_DECL_BACKTRACE_SYMBOLS) check_function_exists("posix_memalign" HAVE_POSIX_MEMALIGN) check_type_size("siginfo_t" HAVE_SIGINFO_T) check_type_size("ucontext_t" HAVE_UCONTEXT_T) # *** LIBRARY CHECKS *** +if (UNIX) + include (CheckLibraryExists) + CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME) + if(NOT HAVE_CLOCK_GETTIME) + message(FATAL_ERROR "clock_gettime not found") + else(NOT HAVE_CLOCK_GETTIME) + list(APPEND ZM_BIN_LIBS "-lrt") + endif(NOT HAVE_CLOCK_GETTIME) +endif(UNIX) + # zlib find_package(ZLIB) if(ZLIB_FOUND) @@ -150,10 +281,12 @@ if(JPEG_FOUND) set(CMAKE_REQUIRED_INCLUDES "${JPEG_INCLUDE_DIR}") check_include_files("stdio.h;jpeglib.h" HAVE_JPEGLIB_H) if(NOT HAVE_JPEGLIB_H) - message(FATAL_ERROR " zm requires libjpeg headers - check that libjpeg development packages are installed") + message(FATAL_ERROR + "ZoneMinder requires libjpeg headers - check that libjpeg development packages are installed") endif(NOT HAVE_JPEGLIB_H) else(JPEG_FOUND) - message(FATAL_ERROR "zm requires jpeg but it was not found on your system") + message(FATAL_ERROR + "ZoneMinder requires jpeg but it was not found on your system") endif(JPEG_FOUND) # OpenSSL @@ -183,10 +316,12 @@ if(PTHREAD_LIBRARIES) mark_as_advanced(FORCE PTHREAD_LIBRARIES PTHREAD_INCLUDE_DIR) check_include_file("pthread.h" HAVE_PTHREAD_H) if(NOT HAVE_PTHREAD_H) - message(FATAL_ERROR " zm requires pthread headers - check that pthread development packages are installed") + message(FATAL_ERROR + "ZoneMinder requires pthread headers - check that pthread development packages are installed") endif(NOT HAVE_PTHREAD_H) else(PTHREAD_LIBRARIES) - message(FATAL_ERROR "zm requires pthread but it was not found on your system") + message(FATAL_ERROR + "ZoneMinder requires pthread but it was not found on your system") endif(PTHREAD_LIBRARIES) # pcre (using find_library and find_path) @@ -258,10 +393,12 @@ if(MYSQLCLIENT_LIBRARIES) mark_as_advanced(FORCE MYSQLCLIENT_LIBRARIES MYSQLCLIENT_INCLUDE_DIR) check_include_file("mysql/mysql.h" HAVE_MYSQL_H) if(NOT HAVE_MYSQL_H) - message(FATAL_ERROR "zm requires MySQL headers - check that MySQL development packages are installed") + message(FATAL_ERROR + "ZoneMinder requires MySQL headers - check that MySQL development packages are installed") endif(NOT HAVE_MYSQL_H) else(MYSQLCLIENT_LIBRARIES) - message(FATAL_ERROR "zm requires mysqlclient but it was not found on your system") + message(FATAL_ERROR + "ZoneMinder requires mysqlclient but it was not found on your system") endif(MYSQLCLIENT_LIBRARIES) set(PATH_FFMPEG "") @@ -273,7 +410,7 @@ if(NOT ZM_NO_FFMPEG) if(AVFORMAT_LIBRARIES) set(HAVE_LIBAVFORMAT 1) list(APPEND ZM_BIN_LIBS "${AVFORMAT_LIBRARIES}") - find_path(AVFORMAT_INCLUDE_DIR "libavformat/avformat.h") + find_path(AVFORMAT_INCLUDE_DIR "libavformat/avformat.h" /usr/include/ffmpeg) if(AVFORMAT_INCLUDE_DIR) include_directories("${AVFORMAT_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${AVFORMAT_INCLUDE_DIR}") @@ -290,7 +427,7 @@ if(NOT ZM_NO_FFMPEG) if(AVCODEC_LIBRARIES) set(HAVE_LIBAVCODEC 1) list(APPEND ZM_BIN_LIBS "${AVCODEC_LIBRARIES}") - find_path(AVCODEC_INCLUDE_DIR "libavcodec/avcodec.h") + find_path(AVCODEC_INCLUDE_DIR "libavcodec/avcodec.h" /usr/include/ffmpeg) if(AVCODEC_INCLUDE_DIR) include_directories("${AVCODEC_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${AVCODEC_INCLUDE_DIR}") @@ -307,7 +444,7 @@ if(NOT ZM_NO_FFMPEG) if(AVDEVICE_LIBRARIES) set(HAVE_LIBAVDEVICE 1) list(APPEND ZM_BIN_LIBS "${AVDEVICE_LIBRARIES}") - find_path(AVDEVICE_INCLUDE_DIR "libavdevice/avdevice.h") + find_path(AVDEVICE_INCLUDE_DIR "libavdevice/avdevice.h" /usr/include/ffmpeg) if(AVDEVICE_INCLUDE_DIR) include_directories("${AVDEVICE_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${AVDEVICE_INCLUDE_DIR}") @@ -324,7 +461,7 @@ if(NOT ZM_NO_FFMPEG) if(AVUTIL_LIBRARIES) set(HAVE_LIBAVUTIL 1) list(APPEND ZM_BIN_LIBS "${AVUTIL_LIBRARIES}") - find_path(AVUTIL_INCLUDE_DIR "libavutil/avutil.h") + find_path(AVUTIL_INCLUDE_DIR "libavutil/avutil.h" /usr/include/ffmpeg) if(AVUTIL_INCLUDE_DIR) include_directories("${AVUTIL_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${AVUTIL_INCLUDE_DIR}") @@ -342,7 +479,7 @@ if(NOT ZM_NO_FFMPEG) if(SWSCALE_LIBRARIES) set(HAVE_LIBSWSCALE 1) list(APPEND ZM_BIN_LIBS "${SWSCALE_LIBRARIES}") - find_path(SWSCALE_INCLUDE_DIR "libswscale/swscale.h") + find_path(SWSCALE_INCLUDE_DIR "libswscale/swscale.h" /usr/include/ffmpeg) if(SWSCALE_INCLUDE_DIR) include_directories("${SWSCALE_INCLUDE_DIR}") set(CMAKE_REQUIRED_INCLUDES "${SWSCALE_INCLUDE_DIR}") @@ -355,7 +492,9 @@ if(NOT ZM_NO_FFMPEG) endif(SWSCALE_LIBRARIES) # Find the path to the ffmpeg executable - find_program(FFMPEG_EXECUTABLE ffmpeg PATH_SUFFIXES ffmpeg) + find_program(FFMPEG_EXECUTABLE + NAMES ffmpeg avconv + PATH_SUFFIXES ffmpeg) if(FFMPEG_EXECUTABLE) set(PATH_FFMPEG "${FFMPEG_EXECUTABLE}") set(OPT_FFMPEG "yes") @@ -388,7 +527,8 @@ endif(NOT ZM_NO_LIBVLC) # Check for gnutls or crypto if((NOT HAVE_LIBCRYPTO) AND (NOT HAVE_LIBGNUTLS)) - message(FATAL_ERROR " zm requires crypto or gnutls but none were found on your system") + message(FATAL_ERROR + "ZoneMinder requires crypto or gnutls but none were found on your system") endif((NOT HAVE_LIBCRYPTO) AND (NOT HAVE_LIBGNUTLS)) # Check for V4L header files and enable ZM_HAS_V4L, ZM_HAS_V4L1, ZM_HAS_V4L2 accordingly @@ -396,17 +536,22 @@ endif((NOT HAVE_LIBCRYPTO) AND (NOT HAVE_LIBGNUTLS)) set(ZM_HAS_V4L 0) set(ZM_HAS_V4L1 0) set(ZM_HAS_V4L2 0) -if(HAVE_LINUX_VIDEODEV_H) +if(HAVE_LINUX_VIDEODEV_H OR HAVE_LIBV4L1_VIDEODEV_H) set(ZM_HAS_V4L 1) set(ZM_HAS_V4L1 1) -endif(HAVE_LINUX_VIDEODEV_H) +endif(HAVE_LINUX_VIDEODEV_H OR HAVE_LIBV4L1_VIDEODEV_H) if(HAVE_LINUX_VIDEODEV2_H) set(ZM_HAS_V4L 1) set(ZM_HAS_V4L2 1) endif(HAVE_LINUX_VIDEODEV2_H) -if((NOT HAVE_LINUX_VIDEODEV_H) AND (NOT HAVE_LINUX_VIDEODEV2_H)) - message(AUTHOR_WARNING " Video 4 Linux headers weren't found - Analog and USB camera support will not be available") -endif((NOT HAVE_LINUX_VIDEODEV_H) AND (NOT HAVE_LINUX_VIDEODEV2_H)) +if((NOT HAVE_LINUX_VIDEODEV_H) + AND (NOT HAVE_LIBV4L1_VIDEODEV_H) + AND (NOT HAVE_LINUX_VIDEODEV2_H)) + message(AUTHOR_WARNING + "Video 4 Linux headers weren't found - Analog and USB camera support will not be available") +endif((NOT HAVE_LINUX_VIDEODEV_H) + AND (NOT HAVE_LIBV4L1_VIDEODEV_H) + AND (NOT HAVE_LINUX_VIDEODEV2_H)) # Check for PCRE and enable ZM_PCRE accordingly set(ZM_PCRE 0) if(HAVE_LIBPCRE AND HAVE_PCRE_H) @@ -421,26 +566,43 @@ if(NOT ZM_NO_MMAP) set(ZM_MMAP_PERLPACKAGE "Sys::Mmap") endif(NOT ZM_NO_MMAP) -# Check for authenication functions +# Check for the ONVIF flag and enable ZM_HAS_ONVIF accordingly +set(ZM_HAS_ONVIF 0) +if(ZM_ONVIF) + set(ZM_HAS_ONVIF 1) +endif(ZM_ONVIF) + +# Check for authentication functions if(HAVE_OPENSSL_MD5_H) set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") - check_prototype_definition(MD5 "unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h" HAVE_MD5_OPENSSL) + check_prototype_definition( + MD5 + "unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md)" "NULL" "openssl/md5.h" + HAVE_MD5_OPENSSL) endif(HAVE_OPENSSL_MD5_H) if(HAVE_GNUTLS_OPENSSL_H) set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - check_prototype_definition(MD5 "unsigned char *MD5 (const unsigned char *buf, unsigned long len, unsigned char *md)" "NULL" "gnutls/openssl.h" HAVE_MD5_GNUTLS) + check_prototype_definition( + MD5 + "unsigned char *MD5 (const unsigned char *buf, unsigned long len, unsigned char *md)" "NULL" "gnutls/openssl.h" + HAVE_MD5_GNUTLS) endif(HAVE_GNUTLS_OPENSSL_H) if(HAVE_GNUTLS_GNUTLS_H) set(CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}") set(CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIR}") - check_prototype_definition(gnutls_fingerprint "int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h" HAVE_DECL_GNUTLS_FINGERPRINT) + check_prototype_definition( + gnutls_fingerprint + "int gnutls_fingerprint (gnutls_digest_algorithm_t algo, const gnutls_datum_t * data, void *result, size_t * result_size)" "0" "stdlib.h;gnutls/gnutls.h" + HAVE_DECL_GNUTLS_FINGERPRINT) endif(HAVE_GNUTLS_GNUTLS_H) if(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) set(HAVE_DECL_MD5 1) else(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) - message(AUTHOR_WARNING " ZM requires a working MD5 function for hashed authenication but none were found - hashed authenication will not be available") + message(AUTHOR_WARNING + "ZoneMinder requires a working MD5 function for hashed authenication but + none were found - hashed authenication will not be available") endif(HAVE_MD5_OPENSSL OR HAVE_MD5_GNUTLS) # Dirty fix for zm_user only using openssl's md5 if gnutls and gcrypt are not available. # This needs to be fixed in zm_user.[h,cpp] but such fix will also require changes to configure.ac @@ -452,26 +614,40 @@ endif(HAVE_LIBCRYPTO AND HAVE_OPENSSL_MD5_H AND HAVE_MD5_OPENSSL) # Check for Perl find_package(Perl) if(NOT PERL_FOUND) - message(FATAL_ERROR "zm requires Perl 5.6.0 or newer but it was not found on your system") + message(FATAL_ERROR + "ZoneMinder requires Perl 5.6.0 or newer but it was not found on your system") endif(NOT PERL_FOUND) # Checking for perl modules requires FindPerlModules.cmake # Check all required modules at once # TODO: Add checking for the optional modules -find_package(PerlModules COMPONENTS Sys::Syslog DBI DBD::mysql Getopt::Long Time::HiRes Date::Manip LWP::UserAgent ExtUtils::MakeMaker ${ZM_MMAP_PERLPACKAGE}) +find_package( + PerlModules COMPONENTS Sys::Syslog DBI DBD::mysql + Getopt::Long Time::HiRes Date::Manip LWP::UserAgent + ExtUtils::MakeMaker ${ZM_MMAP_PERLPACKAGE}) if(NOT PERLMODULES_FOUND) - message(FATAL_ERROR "Not all required perl modules were found on your system") + message(FATAL_ERROR + "Not all required perl modules were found on your system") endif(NOT PERLMODULES_FOUND) -# Attempt to check which user apache (or other web server) runs on by searching for a user beginning with apache or www and then cutting the user from the first matching user line +# Attempt to check which user apache (or other web server) runs on by +# searching for a user beginning with apache or www and then cutting the user +# from the first matching user line if(ZM_WEB_USER STREQUAL "") - # Check for a user matching ^apache and cut the username from the userline in the first match + # Check for a user matching ^apache and cut the username from the + # userline in the first match file(STRINGS "/etc/passwd" userline_apache REGEX "^apache") file(STRINGS "/etc/passwd" userline_www REGEX "^www") if(NOT (userline_apache STREQUAL "")) - execute_process(COMMAND echo ${userline_apache} COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process( + COMMAND echo ${userline_apache} + COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER + OUTPUT_STRIP_TRAILING_WHITESPACE) elseif(NOT (userline_www STREQUAL "")) - execute_process(COMMAND echo ${userline_www} COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process( + COMMAND echo ${userline_www} + COMMAND cut -d: -f1 OUTPUT_VARIABLE ZM_WEB_USER + OUTPUT_STRIP_TRAILING_WHITESPACE) endif(NOT (userline_apache STREQUAL "")) message(STATUS "Detected web server user: ${ZM_WEB_USER}") endif(ZM_WEB_USER STREQUAL "") @@ -485,13 +661,15 @@ message(STATUS "Using web group: ${ZM_WEB_GROUP}") # Check for polkit find_package(Polkit) if(NOT POLKIT_FOUND) - message(FATAL_ERROR "Running ZoneMinder requires polkit. Building ZoneMinder requires the polkit development package.") + message(FATAL_ERROR + "Running ZoneMinder requires polkit. Building ZoneMinder requires the polkit development package.") endif(NOT POLKIT_FOUND) # Some variables that zm expects set(ZM_PID "${ZM_RUNDIR}/zm.pid") -set(ZM_CONFIG "/${CMAKE_INSTALL_SYSCONFDIR}/zm.conf") +set(ZM_CONFIG "${ZM_CONFIG_DIR}/zm.conf") set(VERSION "${zoneminder_VERSION}") +set(API_VERSION "${zoneminder_API_VERSION}") set(PKGDATADIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/zoneminder") set(BINDIR "${CMAKE_INSTALL_FULL_BINDIR}") set(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}") @@ -501,7 +679,11 @@ set(CGI_PREFIX "${ZM_CGIDIR}") set(WEB_USER "${ZM_WEB_USER}") set(WEB_GROUP "${ZM_WEB_GROUP}") set(ZM_DB_TYPE "mysql") -set(EXTRA_PERL_LIB "use lib '${ZM_PERL_USE_PATH}';") +if(ZM_PERL_SEARCH_PATH) + set(EXTRA_PERL_LIB "use lib '${ZM_PERL_SEARCH_PATH}'; # Include custom perl install path") +else(ZM_PERL_SEARCH_PATH) + set(EXTRA_PERL_LIB "# Include from system perl paths only") +endif(ZM_PERL_SEARCH_PATH) # Generate files from the .in files configure_file(zm.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" @ONLY) @@ -509,6 +691,10 @@ configure_file(zoneminder-config.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h" @O configure_file(zmconfgen.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmconfgen.pl" @ONLY) configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh" @ONLY) +# Create a target for man pages +include(Pod2Man) +ADD_MANPAGE_TARGET() + # Process subdirectories add_subdirectory(src) add_subdirectory(scripts) @@ -517,14 +703,19 @@ add_subdirectory(web) add_subdirectory(misc) add_subdirectory(onvif) +# Enable ONVIF support +if(ZM_ONVIF) + add_subdirectory(onvif) +endif(ZM_ONVIF) + # Process distro subdirectories -if((ZM_TARGET_DISTRO STREQUAL "f19") OR (ZM_TARGET_DISTRO STREQUAL "f20")) +if((ZM_TARGET_DISTRO STREQUAL "f22") OR (ZM_TARGET_DISTRO STREQUAL "f23")) add_subdirectory(distros/fedora) -elseif(ZM_TARGET_DISTRO STREQUAL "el6") +elseif((ZM_TARGET_DISTRO STREQUAL "el6") OR (ZM_TARGET_DISTRO STREQUAL "el7")) add_subdirectory(distros/redhat) elseif(ZM_TARGET_DISTRO STREQUAL "OS13") add_subdirectory(distros/opensuse) -endif((ZM_TARGET_DISTRO STREQUAL "f19") OR (ZM_TARGET_DISTRO STREQUAL "f20")) +endif((ZM_TARGET_DISTRO STREQUAL "f22") OR (ZM_TARGET_DISTRO STREQUAL "f23")) # Print optional libraries detection status message(STATUS "Optional libraries found:${optlibsfound}") @@ -534,13 +725,15 @@ message(STATUS "Optional libraries not found:${optlibsnotfound}") message(STATUS "Running ZoneMinder configuration generator") execute_process(COMMAND perl ./zmconfgen.pl RESULT_VARIABLE zmconfgen_result) if(zmconfgen_result EQUAL 0) - message(STATUS "ZoneMinder configuration generator completed successfully") + message(STATUS + "ZoneMinder configuration generator completed successfully") else(zmconfgen_result EQUAL 0) - message(FATAL_ERROR "ZoneMinder configuration generator failed. Exit code: ${zmconfgen_result}") + message(FATAL_ERROR + "ZoneMinder configuration generator failed. Exit code: ${zmconfgen_result}") endif(zmconfgen_result EQUAL 0) # Install zm.conf -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" DESTINATION "/${CMAKE_INSTALL_SYSCONFDIR}") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zm.conf" DESTINATION "${ZM_CONFIG_DIR}") # Uninstall target configure_file( diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..5e211cb31 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,17 @@ +# Contributing + +The ZoneMinder project was originally written by Philip Coombes in 2001. It has since moved to Github and is maintained by just a few who volunteer their spare time. + +Over the years, ZoneMinder has accumulated a rather large user base. This presents a challenge to the development team when it comes to managing communications. Welcome to the world of software development, right? + +In order to keep the kinds of issues, which require changes to the source code, separate from all other questions and comments, our methods of communication are organized in the following manner: + +- The ZoneMinder Github forum is intended for bug reports and serious feature requests only +- The ZoneMinder user forum is intended for general questions and tech support +- The ZoneMinder IRC channel is intended for general questions and tech support + +More details can be found in our [Github Posting Rules](https://github.com/ZoneMinder/ZoneMinder/wiki/Github-Posting-Rules). Please read this before creating an issue in our Github forum. + +Knowledge of Github is a necessary first step to contribute to the project. To contribute, one must generate a pull request. For those just starting out, [this guide](https://github.com/ZoneMinder/ZoneMinder/wiki/Understanding-Github-and-Pull-Requests) will step you through the process. + +Note that pasting code into our Github forum, with the expectation we will do the work for you, is not acceptable. diff --git a/Dockerfile b/Dockerfile index 52b018efd..26df17d2b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # ZoneMinder -FROM ubuntu:precise +FROM ubuntu:trusty MAINTAINER Kyle Johnson # Let the container know that there is no tty @@ -10,10 +10,13 @@ ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y \ libpolkit-gobject-1-dev build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev \ libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 \ - libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg-turbo8-dev \ - libjpeg-turbo8 libtheora-dev libvorbis-dev libvpx-dev libx264-dev libmp4v2-dev ffmpeg mysql-client \ + libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm cmake libjpeg-turbo8-dev \ + libjpeg-turbo8 libtheora-dev libvorbis-dev libvpx-dev libx264-dev libmp4v2-dev libav-tools mysql-client \ apache2 php5 php5-mysql apache2-mpm-prefork libapache2-mod-php5 php5-cli openssh-server \ - mysql-server libvlc-dev libvlc5 libvlccore-dev libvlccore5 vlc-data vlc libcurl4-openssl-dev + mysql-server libvlc-dev libvlc5 libvlccore-dev libvlccore7 vlc-data libcurl4-openssl-dev \ + libavformat-dev libswscale-dev libavutil-dev libavcodec-dev libavfilter-dev \ + libavresample-dev libavdevice-dev libpostproc-dev libv4l-dev libtool libnetpbm10-dev \ + libmime-lite-perl dh-autoreconf dpatch # Copy local code into our container ADD . /ZoneMinder @@ -22,10 +25,11 @@ ADD . /ZoneMinder WORKDIR /ZoneMinder # Setup the ZoneMinder build environment -RUN aclocal && autoheader && automake --force-missing --add-missing && autoconf +#RUN aclocal && autoheader && automake --force-missing --add-missing && autoconf # Configure ZoneMinder -RUN ./configure --with-libarch=lib/$DEB_HOST_GNU_TYPE --disable-debug --host=$DEB_HOST_GNU_TYPE --build=$DEB_BUILD_GNU_TYPE --with-mysql=/usr --with-webdir=/var/www/zm --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-mmap=yes ZM_SSL_LIB=openssl ZM_DB_USER=zm ZM_DB_PASS=zm +#RUN ./configure --with-libarch=lib/$DEB_HOST_GNU_TYPE --disable-debug --host=$DEB_HOST_GNU_TYPE --build=$DEB_BUILD_GNU_TYPE --with-mysql=/usr --with-webdir=/var/www/zm --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-mmap=yes --enable-onvif ZM_SSL_LIB=openssl ZM_DB_USER=zm ZM_DB_PASS=zm +RUN cmake . # Build ZoneMinder RUN make @@ -33,6 +37,9 @@ RUN make # Install ZoneMinder RUN make install +# ensure writable folders +RUN ./zmlinkcontent.sh + # Adding the start script ADD utils/docker/start.sh /tmp/start.sh @@ -40,15 +47,23 @@ ADD utils/docker/start.sh /tmp/start.sh # TODO - Files ADD'ed have 755 already...why do we need this? RUN chmod 755 /tmp/start.sh -# Creating SSH privledge escalation dir +# give files in /usr/local/share/zoneminder/ +RUN chown -R www-data:www-data /usr/local/share/zoneminder/ + +# Creating SSH privilege escalation dir RUN mkdir /var/run/sshd # Adding apache virtual hosts file -ADD utils/docker/apache-vhost /etc/apache2/sites-enabled/000-default +ADD utils/docker/apache-vhost /etc/apache2/sites-available/000-default.conf +ADD utils/docker/phpdate.ini /etc/php5/apache2/conf.d/25-phpdate.ini # Set the root passwd RUN echo 'root:root' | chpasswd +# Add a user we can actually login with +RUN useradd -m -s /bin/bash -G sudo zoneminder +RUN echo 'zoneminder:zoneminder' | chpasswd + # Expose ssh and http ports EXPOSE 80 EXPOSE 22 diff --git a/INSTALL b/INSTALL index e8e9569d1..bcb7f78df 100644 --- a/INSTALL +++ b/INSTALL @@ -18,7 +18,7 @@ Important differences --------------------- * Unlike the autotools way, the cmake way does not require any options. It attempts to detect some things by its own (system directories, libarch, web user and group) and uses defaults for others (installation paths and such). * Unlike the autotools way, which links the binaries to a fixed list of libraries, the cmake way only links to libraries that it found on the system. If a library is not found, but required, a fatal error will be shown during the configuration step. -* Unlike the autotools way, the cmake way does not modify the system in any way it shouldnt. It only does what its supposed to do: Install files to your system. Nothing else and nothing leaks out of the DESTDIR environment variable (if used). This means that depending on your configuration, there might be an extra required step after installation: to link WEB_PATH/events and WEB_PATH/images folders to the correct places. +* Unlike the autotools way, the cmake way does not modify the system in any way it shouldn't. It only does what its supposed to do: Install files to your system. Nothing else and nothing leaks out of the DESTDIR environment variable (if used). This means that depending on your configuration, there might be an extra required step after installation: to link WEB_PATH/events and WEB_PATH/images folders to the correct places. Configuration ------------- @@ -27,11 +27,15 @@ cmake by default does not require any parameters, but its possible to override t Configuration can be done in 4 ways: 1) As a command line parameter, e.g. cmake -DCMAKE_VERBOSE_MAKEFILE=ON . 2) Using cmake-gui -4) Providing cmake with an initial cache file with the -C option +3) Providing cmake with an initial cache file with the -C option + IMPORTANT: Do not use the -C option if any major part of your system, excluding the version of zoneminder, has changed. + For example, do not use this option if: you have upgraded your distro to a new version, have gone from 32 to 64 bits, + or have migrated from one machine to another. 4) By editing the cache file CMakeCache.txt (after it has been generated) - Not recommended Possible configuration options: ZM_RUNDIR Location of transient process files, default: /var/run/zm + ZM_SOCKDIR Location of Unix domain socket files, default /var/run/zm ZM_TMPDIR Location of temporary files, default: /tmp/zm ZM_LOGDIR Location of generated log files, default: /var/log/zm ZM_WEBDIR Location of the web files, default: /share/zoneminder/www @@ -49,9 +53,8 @@ Advanced: ZM_NO_MMAP Set to ON to not use mmap shared memory. Shouldn't be enabled unless you experience problems with the shared memory. default: OFF ZM_NO_FFMPEG Set to ON to skip ffmpeg checks and force building ZM without ffmpeg. default: OFF ZM_NO_X10 Set to ON to build ZoneMinder without X10 support. default: OFF - ZM_PERL_SUBPREFIX Use a different directory for the zm perl modules. NOTE: This is a subprefix, e.g. lib will be turned into /lib, default: /perl5 - ZM_PERL_USE_PATH Override the include path for zm perl modules. Useful if you are moving the perl modules without using the ZM_PERL_SUBPREFIX option. default: / - + ZM_PERL_MM_PARMS By default, ZoneMinder's Perl modules are installed into the Vendor folders, as defined by your installation of Perl. You can change that here. Consult Perl's MakeMaker documentation for a definition of acceptable parameters. If you set this to something that causes the modules to be installed outside Perl's normal serach path, then you will also need to set ZM_PERL_SEARCH_PATH accordingly. default: "INSTALLDIRS=vendor NO_PACKLIST=1 NO_PERLLOCAL=1" + ZM_PERL_SEARCH_PATH Use to add a folder to your Perl's search path. This will need to be set in cases where ZM_PERL_MM_PARMS has been modified such that ZoneMinder's Perl modules are installed outside Perl's default search path. default: "" Useful configuration options provided by cmake: CMAKE_VERBOSE_MAKEFILE - Set this to ON (default OFF) to see what cmake is doing. Very useful for troubleshooting. @@ -79,6 +82,7 @@ Basic steps for installing ZoneMinder on a fresh system ------------------------------------------------------- 1) After installing all the required dependencies, in the project directory, run "cmake [extra options] ." This behaves like ./configure. It is also possible to supply configuration options, e.g. cmake -DZM_DB_PASS="mypass" . +IMPORTANT: Don't forget the dot "." at the end. 2) Run "make" to compile ZoneMinder 3) Run "make install" (as root, or use sudo) to install ZoneMinder to your system. 4) Create a directory for the content and the necessary symlinks by running zmlinkcontent.sh with the directory you want to use. e.g. ./zmlinkcontent.sh /nfs/zm @@ -88,8 +92,21 @@ NOTE: The database server, database name, user and password can be different and 7) Populate the zoneminder database using the script zm_create.sql. This should be found in /share/zoneminder/db or in the project/db directory. 8) Create an apache virtual host for ZoneMinder. Make sure to use the same paths as ZM_WEBDIR and ZM_CGIDIR in /etc/zm.conf -9) Create other config if desired (e.g. rsyslog, logrotate and such). Some of this can be found in /share/zoneminder/misc or project/misc directory -10) Setup an init script for your system. Its also possible to use "zmpkg.pl start" and "zmpkg.pl stop" if you can't find a one. +9) Verify date.timezone is set to your timezone. This parameter is often found inside the system php.ini file. Consult your distribution's documentation for the proper way to set this value. +10) Create other config if desired (e.g. rsyslog, logrotate and such). Some of this can be found in /share/zoneminder/misc or project/misc directory +11) Setup an appropriate startup script for your system. Two generic startup scripts have been provided, a legacy Sys V Init script and a Systemd service file. + +*Sys V Init Setup* +- Copy the sys v init script /scripts/zm from the build folder to /etc/init. +- Inspect the contents to make sure they apply to your distro. + +*SystemD Setup* +- Copy the zoneminder systemd service file /misc/zoneminder.service from the build folder to the systemd service file location. + For Redhat based distros, that folder is /usr/lib/systemd/system. +- Inspect the contents to make sure they apply to your distro. +- Tell systemd to load the new service file: "sudo systemctl daemon-reload". +- Copy /misc/zoneminder-tmpfiles.conf to /etc/tmpfiles.d +- Tell systemd to process this file: "sudo /usr/bin/systemd-tmpfiles --create /etc/tmpfiles.d/zoneminder.conf". Basic steps for upgrading ZoneMinder ------------------------------------ diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index 773af64a8..000000000 --- a/Makefile.am +++ /dev/null @@ -1,34 +0,0 @@ -AUTOMAKE_OPTIONS = foreign - -# And these to the user and group of your webserver -webuser = @WEB_USER@ -webgroup = @WEB_GROUP@ - -sysconf_DATA = \ - zm.conf - -SUBDIRS = \ - src \ - web \ - scripts \ - db \ - misc \ - onvif - -EXTRA_DIST = \ - zm.conf.in \ - zmconfgen.pl.in - -# Yes, you are correct. This is a HACK! -install-data-hook: - ( cd $(DESTDIR)$(sysconfdir); chown $(webuser):$(webgroup) $(sysconf_DATA); chmod 600 $(sysconf_DATA) ) - ( if ! test -e $(DESTDIR)$(ZM_RUNDIR); then mkdir -p $(DESTDIR)$(ZM_RUNDIR); fi; if test "$(DESTDIR)$(ZM_RUNDIR)" != "/var/run"; then chown $(webuser):$(webgroup) $(DESTDIR)$(ZM_RUNDIR); chmod u+w $(DESTDIR)$(ZM_RUNDIR); fi ) - ( if ! test -e $(DESTDIR)$(ZM_SOCKDIR); then mkdir -p $(DESTDIR)$(ZM_SOCKDIR); fi; if test "$(DESTDIR)$(ZM_SOCKDIR)" != "/var/run"; then chown $(webuser):$(webgroup) $(DESTDIR)$(ZM_SOCKDIR); chmod u+w $(DESTDIR)$(ZM_SOCKDIR); fi ) - ( if ! test -e $(DESTDIR)$(ZM_TMPDIR); then mkdir -m 700 -p $(DESTDIR)$(ZM_TMPDIR); fi; if test "$(DESTDIR)$(ZM_TMPDIR)" != "/tmp" && test "$(DESTDIR)$(ZM_TMPDIR)" != "/var/tmp"; then chown $(webuser):$(webgroup) $(DESTDIR)$(ZM_TMPDIR); chmod u+w $(DESTDIR)$(ZM_TMPDIR); fi ) - -uninstall-hook: - @-( cd $(DESTDIR)$(webdir); rm -rf events graphics images sounds temp ) - @-( if test "$(DESTDIR)$(ZM_RUNDIR)" != "/var/run"; then rm -rf $(DESTDIR)$(ZM_RUNDIR); fi ) - @-( if test "$(DESTDIR)$(ZM_SOCKDIR)" != "/var/run"; then rm -rf $(DESTDIR)$(ZM_SOCKDIR); fi ) - @-( if test "$(DESTDIR)$(ZM_TMPDIR)" != "/tmp" && test "$(DESTDIR)$(ZM_TMPDIR)" != "/var/tmp"; then rm -rf $(DESTDIR)$(ZM_TMPDIR); fi ) - @-( if test "$(DESTDIR)$(ZM_LOGDIR)" != "/var/log"; then rm -rf $(DESTDIR)$(ZM_LOGDIR); fi ) diff --git a/README.md b/README.md index aae10db51..78ae7a922 100644 --- a/README.md +++ b/README.md @@ -3,130 +3,56 @@ ZoneMinder [![Build Status](https://travis-ci.org/ZoneMinder/ZoneMinder.png)](https://travis-ci.org/ZoneMinder/ZoneMinder) [![Bountysource](https://api.bountysource.com/badge/team?team_id=204&style=bounties_received)](https://www.bountysource.com/teams/zoneminder/issues?utm_source=ZoneMinder&utm_medium=shield&utm_campaign=bounties_received) -All documentation for ZoneMinder is now online at http://www.zoneminder.com/wiki/index.php/Documentation +All documentation for ZoneMinder is now online at https://zoneminder.readthedocs.org ## Overview ZoneMinder is an integrated set of applications which provide a complete surveillance solution allowing capture, analysis, recording and monitoring of any CCTV or security cameras attached to a Linux based machine. It is designed to run on distributions which support the Video For Linux (V4L) interface and has been tested with video cameras attached to BTTV cards, various USB cameras and also supports most IP network cameras. -## Requirements +## Contacting the Development Team +Before creating an issue in our github forum, please read our posting rules: +https://github.com/ZoneMinder/ZoneMinder/wiki/Github-Posting-Rules -If you are installing ZoneMinder from a package, that package should provide all of the needed core components. +## Installation Methods -### Packages +### Building from Source is Discouraged -If you are compiling ZoneMinder from source, the below list contains the packages needed to get ZoneMinder built: +Historically, installing ZoneMinder onto your system required building from source code by issuing the traditional configure, make, make install commands. To get ZoneMinder to build, all of its dependencies had to be determined and installed beforehand. Init and logrotate scripts had to be manually copied into place following the build. Optional packages such as jscalendar and Cambozola had to be manually installed. Uninstalls could leave stale files around, which could cause problems during an upgrade. Speaking of upgrades, when it comes time to upgrade all these manual steps must be repeated again. -#### Ubuntu +Better methods exist today that do much of this for you. The current development team, along with other volunteers, have taken great strides in providing the resources necessary to avoid building from source. -A fresh build based on master branch running Ubuntu 1204 LTS. Will likely work for other versions as well. +### Install from a Package Repository +This is the recommended method to install ZoneMinder onto your system. ZoneMinder packages are maintained for the following distros: + +- Ubuntu via [Iconnor's PPA](https://launchpad.net/~iconnor/+archive/ubuntu/zoneminder) +- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder) +- RHEL/CentOS and clones via [zmrepo](http://zmrepo.zoneminder.com/) +- Fedora via [zmrepo](http://zmrepo.zoneminder.com/) +- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE) +- Maegia from their default repository + +If a repository that hosts ZoneMinder packages is not available for your distro, then you are encouraged to build your own package, rather than build from source. While each distro is different in ways that set it apart from all the others, they are often similar enough to allow you to adapt another distro's package building instructions to your own. + +### Building a ZoneMinder Package + +Building ZoneMinder into a package is not any harder than building from source. As a matter of fact, if you have successfully built ZoneMinder from source in the past, then you may find these steps to be easier. + +When building a package, it is best to do this work in a separate environment, dedicated to development purposes. This could be as simple as creating a virtual machine, using Docker, or using mock. All it takes is one “Oops” to regret doing this work on your production server. + +Lastly, if you desire to build a development snapshot from the master branch, it is recommended you first build your package using an official release of ZoneMinder. This will help identify whether any problems you may encounter are caused by the build process or is a new issue in the master branch. + +Please visit our [ReadtheDocs site](https://zoneminder.readthedocs.org/en/stable/installationguide/index.html) for distro specific instructions. + +### Package Maintainers +Many of the ZoneMinder configration variable default values are not configurable at build time through autotools or cmake. A new tool called *zmeditconfigdata.sh* has been added to allow package maintainers to manipulate any variable stored in ConfigData.pm without patching the source. + +For example, let's say I have created a new ZoneMinder package that contains the cambolzola javascript file. However, by default cambozola support is turned off. To fix that, add this to the pacakging script: ```bash -root@host:~# aptitude install -y apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg8-dev libjpeg8 apache2-mpm-prefork libapache2-mod-php5 php5-cli libphp-serialization-perl libgnutls-dev libjpeg8-dev libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libv4l-dev libtool ffmpeg libnetpbm10-dev libavdevice-dev libmime-lite-perl dh-autoreconf dpatch; - -root@host:~# git clone https://github.com/ZoneMinder/ZoneMinder.git zoneminder; -root@host:~# cd zoneminder; -root@host:~# ln -s distros/ubuntu1204 debian; -root@host:~# dpkg-checkbuilddeps; -root@host:~# dpkg-buildpackage; +./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes ``` -One level above you'll now find a deb package matching the architecture of the build host: - -```bash -root@host:~# ls -1 ~/zoneminder*; -/root/zoneminder_1.26.4-1_amd64.changes -/root/zoneminder_1.26.4-1_amd64.deb -/root/zoneminder_1.26.4-1.dsc -/root/zoneminder_1.26.4-1.tar.gz -``` - -The dpkg command itself does not resolve dependencies. That's what high-level interfaces like aptitude and apt-get are normally for. Unfortunately, unlike RPM, there's no easy way to install a separate deb package not contained with any repository. - -To overcome this "limitation" we'll use dpkg only to install the zoneminder package and apt-get to fetch all needed dependencies afterwards. Running dpkg-reconfigure in the end will ensure that the setup scripts e.g. for database provisioning were executed. - -```bash -root@host:~# dpkg -i /root/zoneminder_1.26.4-1_amd64.deb; apt-get install -f; -root@host:~# dpkg-reconfigure zoneminder; -``` -Alternatively you may also use gdebi to automatically resolve dependencies during installation: - -```bash -root@host:~# aptitude install -y gdebi; -root@host:~# gdebi /root/zoneminder_1.26.4-1_amd64.deb; -``` -```bash -sudo apt-get install apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev \ -libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 \ -libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg-turbo8-dev libjpeg-turbo8 \ -apache2-mpm-prefork libapache2-mod-php5 php5-cli -``` - -#### Debian - -A fresh build based on master branch running Debian 7 (wheezy): - -```bash -root@host:~# aptitude install -y apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg8-dev libjpeg8 apache2-mpm-prefork libapache2-mod-php5 php5-cli libphp-serialization-perl libgnutls-dev libjpeg8-dev libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libv4l-dev libtool ffmpeg libnetpbm10-dev libavdevice-dev libmime-lite-perl dh-autoreconf dpatch; - -root@host:~# git clone https://github.com/ZoneMinder/ZoneMinder.git zoneminder; -root@host:~# cd zoneminder; -root@host:~# ln -s distros/debian; -root@host:~# dpkg-checkbuilddeps; -root@host:~# dpkg-buildpackage; -``` - -One level above you'll now find a deb package matching the architecture of the build host: - -```bash -root@host:~# ls -1 ~/zoneminder*; -/root/zoneminder_1.26.4-1_amd64.changes -/root/zoneminder_1.26.4-1_amd64.deb -/root/zoneminder_1.26.4-1.dsc -/root/zoneminder_1.26.4-1.tar.gz -``` - -The dpkg command itself does not resolve dependencies. That's what high-level interfaces like aptitude and apt-get are normally for. Unfortunately, unlike RPM, there's no easy way to install a separate deb package not contained with any repository. - -To overcome this "limitation" we'll use dpkg only to install the zoneminder package and apt-get to fetch all needed dependencies afterwards. Running dpkg-reconfigure in the end will ensure that the setup scripts e.g. for database provisioning were executed. - -```bash -root@host:~# dpkg -i /root/zoneminder_1.26.4-1_amd64.deb; apt-get install -f; -root@host:~# dpkg-reconfigure zoneminder; -``` -Alternatively you may also use gdebi to automatically resolve dependencies during installation: - -```bash -root@host:~# aptitude install -y gdebi; -root@host:~# gdebi /root/zoneminder_1.26.4-1_amd64.deb; -``` - -#### CentOS / RHEL - -Additional repositories must be added before one can build zoneminder on CentOS or RHEL: - -1. RepoForge (formerly RPMForge) http://repoforge.org/use/ -2. EPEL https://fedoraproject.org/wiki/EPEL -3. Optional RPMFusion: http://rpmfusion.org/ [SEE NOTE] - -[NOTE]
-The RPMFusion repo contains significantly newer versions of ffmpeg and vlc. This leads to significantly better camera support. However, the RPMFusion repo contains packages that conflict with the other two repos. In order to resolve this, one must also install the yum priorities pacakge and use that to prioritize your repos in the following order: - -1. Base -2. RPMFusion -3. EPEL -4. RPMForge - -For instructions on yum priorities, visit this page: -http://wiki.centos.org/PackageManagement/Yum/Priorities - -Once your repos are in order, install the following: -```bash -sudo yum install automake bzip2-devel ffmpeg ffmpeg-devel gnutls-devel httpd libjpeg-turbo libjpeg-turbo-devel mysql-devel mysql-server pcre-devel \ -perl-Archive-Tar perl-Archive-Zip perl-Convert-BinHex perl-Date-Manip perl-DBD-MySQL perl-DBI perl-Device-SerialPort perl-Email-Date-Format perl-IO-stringy \ -perl-IO-Zlib perl-MailTools perl-MIME-Lite perl-MIME-tools perl-MIME-Types perl-Module-Load perl-Package-Constants perl-Sys-Mmap perl-Time-HiRes \ -perl-TimeDate perl-YAML-Syck php php-cli php-mysql x264 vlc-devel vlc-core libcurl libcurl-devel -``` +Note that zmeditconfigdata.sh is intended to be called, from the root build folder, prior to running cmake or configure. #### Docker @@ -134,11 +60,6 @@ Docker is a system to run applications inside isolated containers. ZoneMinder, a Dockerfile contained in this repository. However, there is still work needed to ensure that the main ZM features work properly and are documented. -### ffmpeg - -This release of ZoneMinder has been tested on and works with ffmpeg version N-55540-g93f4277. - - ## Contribution Model and Development * Source hosted at [GitHub](https://github.com/ZoneMinder/ZoneMinder/) diff --git a/acinclude.m4 b/acinclude.m4 deleted file mode 100644 index 605842149..000000000 --- a/acinclude.m4 +++ /dev/null @@ -1,74 +0,0 @@ -AC_DEFUN([AC_DEFINE_DIR], [ - prefix_NONE= - exec_prefix_NONE= - test "x$prefix" = xNONE && prefix_NONE=yes && prefix=$ac_default_prefix - test "x$exec_prefix" = xNONE && exec_prefix_NONE=yes && exec_prefix=$prefix -dnl In Autoconf 2.60, ${datadir} refers to ${datarootdir}, which in turn -dnl refers to ${prefix}. Thus we have to use `eval' twice. - eval ac_define_dir="\"[$]$2\"" - eval ac_define_dir="\"$ac_define_dir\"" - AC_SUBST($1, "$ac_define_dir") - AC_DEFINE_UNQUOTED($1, "$ac_define_dir", [$3]) - test "$prefix_NONE" && prefix=NONE - test "$exec_prefix_NONE" && exec_prefix=NONE -]) - -AC_DEFUN([AC_PROG_PERL_VERSION],[dnl -# Make sure we have perl -if test -z "$PERL"; then -AC_CHECK_PROG(PERL,perl,perl) -fi - -# Check if version of Perl is sufficient -ac_perl_version="$1" - -if test "x$PERL" != "x"; then - AC_MSG_CHECKING(for perl version greater than or equal to $ac_perl_version) - # NB: It would be nice to log the error if there is one, but we cannot rely - # on autoconf internals - $PERL -e "use $ac_perl_version;" > /dev/null 2>&1 - if test $? -ne 0; then - AC_MSG_RESULT(no); - $3 - else - AC_MSG_RESULT(ok); - $2 - fi -else - AC_MSG_WARN(could not find perl) -fi -])dnl - -AC_DEFUN([AC_PROG_PERL_MODULES],[dnl -ac_perl_modules="$1" -# Make sure we have perl -if test -z "$PERL"; then -AC_CHECK_PROG(PERL,perl,perl) -fi - -if test "x$PERL" != x; then - ac_perl_modules_failed=0 - for ac_perl_module in $ac_perl_modules; do - AC_MSG_CHECKING(for perl module $ac_perl_module) - - # Would be nice to log result here, but can't rely on autoconf internals - $PERL "-M$ac_perl_module" -e exit > /dev/null 2>&1 - if test $? -ne 0; then - AC_MSG_RESULT(no); - ac_perl_modules_failed=1 - else - AC_MSG_RESULT(ok); - fi - done - - # Run optional shell commands - if test "$ac_perl_modules_failed" = 0; then - : - $2 - else - : - $3 - fi -else - AC_MSG_WARN(could not find perl) -fi])dnl diff --git a/bootstrap.sh b/bootstrap.sh deleted file mode 100755 index 681d5b319..000000000 --- a/bootstrap.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -aclocal -autoheader -automake --add-missing -autoconf diff --git a/cmake/Modules/CheckSendfile.cmake b/cmake/Modules/CheckSendfile.cmake new file mode 100644 index 000000000..1796b92e3 --- /dev/null +++ b/cmake/Modules/CheckSendfile.cmake @@ -0,0 +1,58 @@ +# Check whether sendfile() is supported and what prototype it has +include(CheckCSourceCompiles) +if (UNIX OR MINGW) +SET(CMAKE_REQUIRED_DEFINITIONS -Werror-implicit-function-declaration) +endif() +check_c_source_compiles("#include +#include +int main() +{ +sendfile(1, 1, NULL, 0); +return 0; +}" HAVE_SENDFILE4_SUPPORT) +if(HAVE_SENDFILE4_SUPPORT) +add_definitions(-DHAVE_SENDFILE4_SUPPORT=1) +unset(CMAKE_REQUIRED_DEFINITIONS) +message(STATUS "Sendfile support: Linux/Solaris sendfile()") +return() +endif() +find_library(SENDFILE_LIBRARIES NAMES sendfile) +if(SENDFILE_LIBRARIES) +include(CheckLibraryExists) +check_library_exists(sendfile sendfile ${SENDFILE_LIBRARIES} HAVE_SENDFILE4_SUPPORT) +if(HAVE_SENDFILE4_SUPPORT) +add_definitions(-DHAVE_SENDFILE4_SUPPORT=1) +unset(CMAKE_REQUIRED_DEFINITIONS) +message(STATUS "Sendfile support: Solaris sendfile()") +return() +endif() +endif() +set(SENDFILE_LIBRARIES "") +check_c_source_compiles("#include +#include +int main() +{ +sendfile(1, 1, 0, 0, NULL, NULL, 0); +return 0; +}" HAVE_SENDFILE7_SUPPORT) +if(HAVE_SENDFILE7_SUPPORT) +add_definitions(-DHAVE_SENDFILE7_SUPPORT=1) +unset(CMAKE_REQUIRED_DEFINITIONS) +message(STATUS "Sendfile support: FreeBSD sendfile()") +return() +endif() +check_c_source_compiles("#include +#include +#include +int main() +{ +sendfile(1, 1, 0, NULL, NULL, 0); +return 0; +}" HAVE_SENDFILE6_SUPPORT) +if(HAVE_SENDFILE6_SUPPORT) +add_definitions(-DHAVE_SENDFILE6_SUPPORT=1) +unset(CMAKE_REQUIRED_DEFINITIONS) +message(STATUS "Sendfile support: MacOS sendfile()") +return() +endif() + diff --git a/cmake/Modules/FindPolkit.cmake b/cmake/Modules/FindPolkit.cmake index 62b386a90..cd7903176 100644 --- a/cmake/Modules/FindPolkit.cmake +++ b/cmake/Modules/FindPolkit.cmake @@ -19,7 +19,7 @@ # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls find_package(PkgConfig) - pkg_check_modules(PC_POLKIT polkit-gobject-1) + pkg_search_module(PC_POLKIT polkit-gobject-1 polkit) #pkg_check_modules(PC_POLKIT_AGENT polkit-agent-1) set(POLKIT_DEFINITIONS ${PC_POLKIT_CFLAGS_OTHER}) endif (NOT WIN32) @@ -31,8 +31,8 @@ HINTS ${PC_POLKIT_INCLUDE_DIRS} ) find_path( POLKIT_INCLUDE_DIR - NAMES polkit/polkit.h - PATH_SUFFIXES polkit-1 + NAMES polkit/polkit.h libpolkit/libpolkit.h + PATH_SUFFIXES polkit-1 polkit HINTS ${PC_POLKIT_INCLUDE_DIRS} ) #find_path( POLKIT_AGENT_INCLUDE_DIR @@ -43,7 +43,7 @@ #set(POLKIT_INCLUDE_DIRS ${GLIB2_INCLUDE_DIR} ${_POLKIT_INCLUDE_DIR}) #set(POLKIT_AGENT_INCLUDE_DIRS ${GLIB2_INCLUDE_DIR} ${_POLKIT_AGENT_INCLUDE_DIR}) find_library( POLKIT_LIBRARIES - NAMES polkit-gobject-1 + NAMES polkit-gobject-1 polkit HINTS ${PC_POLKIT_LIBDIR} ) #find_library( POLKIT_AGENT_LIBRARY diff --git a/cmake/Modules/Pod2Man.cmake b/cmake/Modules/Pod2Man.cmake new file mode 100644 index 000000000..6804fab53 --- /dev/null +++ b/cmake/Modules/Pod2Man.cmake @@ -0,0 +1,71 @@ +# +# Copyright (C) 2012 Emmanuel Roullit +# +# 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., +# 51 Franklin St, Fifth Floor, Boston, MA 02110, USA +# + +# Generate man pages of the project by using the +# POD header written in the tool source code. +# To use it, include this file in CMakeLists.txt and +# invoke POD2MAN(
) + +MACRO(POD2MAN PODFILE MANFILE SECTION) + FIND_PROGRAM(POD2MAN pod2man) + FIND_PROGRAM(GZIP gzip) + + IF(NOT POD2MAN) + MESSAGE(FATAL ERROR "Need pod2man installed to generate man page") + ENDIF(NOT POD2MAN) + + IF(NOT GZIP) + MESSAGE(FATAL ERROR "Need gzip installed to compress man page") + ENDIF(NOT GZIP) + + IF(NOT EXISTS ${PODFILE}) + MESSAGE(FATAL ERROR "Could not find pod file ${PODFILE} to generate man page") + ENDIF(NOT EXISTS ${PODFILE}) + + ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION} + DEPENDS ${PODFILE} + COMMAND ${POD2MAN} + ARGS --section ${SECTION} --center ${CMAKE_PROJECT_NAME} --release --stderr --name ${MANFILE} + ${PODFILE} > ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION} + ) + + ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz + COMMAND ${GZIP} --best -c ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION} > ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION} + ) + + SET(MANPAGE_TARGET "man-${MANFILE}") + + ADD_CUSTOM_TARGET(${MANPAGE_TARGET} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz) + ADD_DEPENDENCIES(man ${MANPAGE_TARGET}) + + INSTALL( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz + DESTINATION share/man/man${SECTION} + ) +ENDMACRO(POD2MAN PODFILE MANFILE SECTION) + +MACRO(ADD_MANPAGE_TARGET) + # It is not possible add a dependency to target 'install' + # Run hard-coded 'make man' when 'make install' is invoked + INSTALL(CODE "EXECUTE_PROCESS(COMMAND make man)") + ADD_CUSTOM_TARGET(man) +ENDMACRO(ADD_MANPAGE_TARGET) + diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 42c813fba..000000000 --- a/configure.ac +++ /dev/null @@ -1,457 +0,0 @@ -AC_PREREQ(2.59) -AC_INIT(zm,1.28.0,[http://www.zoneminder.com/forums/ - Please check FAQ first],zoneminder,http://www.zoneminder.com/downloads.html) -AM_INIT_AUTOMAKE -AC_CONFIG_SRCDIR(src/zm.h) -AC_CONFIG_HEADERS(config.h) - -AC_SUBST([AM_CXXFLAGS], [-D__STDC_CONSTANT_MACROS]) - -AC_SUBST(VERSION) -# -# Platform specific setup -# -############################# -AC_CANONICAL_HOST -# Check for which host we are on and setup a few things -# specifically based on the host -case $host_os in - darwin* ) - # Do something specific for mac - HOST_OS='darwin' - ;; - linux*) - # Do something specific for linux - HOST_OS='linux' - ;; - *BSD*) - # Do something specific for BSD - HOST_OS='BSD' - ;; - *) - #Default Case - AC_MSG_ERROR([Your platform is not currently supported]) - ;; -esac - -AC_SUBST(HOST_OS) - -AC_ARG_VAR(ZM_DB_TYPE,[Type of the ZoneMinder database, default mysql]) -AC_ARG_VAR(ZM_DB_HOST,[Hostname where ZoneMinder database located, default localhost]) -AC_ARG_VAR(ZM_DB_NAME,[Name of ZoneMinder database, default zm]) -AC_ARG_VAR(ZM_DB_USER,[Name of ZoneMinder database user, default zmuser]) -AC_ARG_VAR(ZM_DB_PASS,[Password of ZoneMinder database user, default zmpass]) -AC_ARG_VAR(ZM_SSL_LIB,[Library to use for ssl functions, default gnutls]) -AC_ARG_VAR(ZM_MYSQL_ENGINE,[MySQL engine to use with database, default InnoDB]) -AC_ARG_VAR(ZM_RUNDIR,[Location of transient process files, default /var/run/zm]) -AC_ARG_VAR(ZM_SOCKDIR,[Location of Unix domain socket files, default /var/run/zm]) -AC_ARG_VAR(ZM_TMPDIR,[Location of temporary files, default /var/tmp/zm]) -AC_ARG_VAR(ZM_LOGDIR,[Location of generated log files, default /var/log/zm]) - -if test "$ZM_DB_TYPE" == ""; then - AC_SUBST(ZM_DB_TYPE,[mysql]) -fi -if test "$ZM_DB_HOST" == ""; then - AC_SUBST(ZM_DB_HOST,[localhost]) -fi -if test "$ZM_DB_NAME" == ""; then - AC_SUBST(ZM_DB_NAME,[zm]) -fi -if test "$ZM_DB_USER" == ""; then - AC_SUBST(ZM_DB_USER,[zmuser]) -fi -if test "$ZM_DB_PASS" == ""; then - AC_SUBST(ZM_DB_PASS,[zmpass]) -fi -if test "$ZM_SSL_LIB" == ""; then - AC_SUBST(ZM_SSL_LIB,gnutls) -fi -if test "$ZM_MYSQL_ENGINE" == ""; then - AC_SUBST(ZM_MYSQL_ENGINE,InnoDB) -fi -if test "$ZM_RUNDIR" == ""; then - AC_SUBST(ZM_RUNDIR,[/var/run/zm]) -fi -if test "$ZM_SOCKDIR" == ""; then - AC_SUBST(ZM_SOCKDIR,[/var/run/zm]) -fi -if test "$ZM_TMPDIR" == ""; then - AC_SUBST(ZM_TMPDIR,[/tmp/zm]) -fi -if test "$ZM_LOGDIR" == ""; then - AC_SUBST(ZM_LOGDIR,[/var/log/zm]) -fi - -LIB_ARCH=lib -AC_ARG_WITH(libarch, - [ --with-libarch= architecture library path to use, default lib], - [LIB_ARCH=$with_libarch], - AC_MSG_WARN([You can call configure with the --with-libarch option. - This tells configure where to find architecture specific libraries. - The default of 'lib' is usually ok but 64 bit machines may require lib64. - e.g. --with-libarch=lib or --with-libarch=lib64]) -) -AC_SUBST(LIB_ARCH) - -LDFLAGS="-L/usr/lib/${build_alias} ${LDFLAGS}" - -MYSQL_PREFIX=/usr -AC_ARG_WITH(mysql, - [ --with-mysql= prefix of MySQL installation, default /usr], - [MYSQL_PREFIX=$with_mysql], - AC_MSG_WARN([You can call configure with the --with-mysql option. - This tells configure where to find the MySql C library and headers if configure cannot - locate them automatically. - e.g. --with-mysql=/usr/local or --with-mysql=/usr]) -) -AC_SUBST(MYSQL_PREFIX) -MYSQL_LIBS="-L${MYSQL_PREFIX}/${LIB_ARCH}/mysql" -MYSQL_CFLAGS="-I${MYSQL_PREFIX}/include" -AC_SUBST(MYSQL_LIBS) -AC_SUBST(MYSQL_CFLAGS) -LDFLAGS="$LDFLAGS ${MYSQL_LIBS}" - - -MARIADB_PREFIX=/usr -AC_ARG_WITH(mariadb, - [ --with-mariadb= prefix of MariaDB installation, default /usr], - [MYSQL_PREFIX=$with_mariadb], - AC_MSG_WARN([You can call configure with the --with-mariadb option. - This tells configure where to find the mariaDB C library and headers if configure cannot - locate them automatically. - e.g. --with-mariadb=/usr/local or --with-mariadb=/usr]) -) -AC_SUBST(MARIADB_PREFIX) -MARIADB_LIBS="-L${MARIADB_PREFIX}/${LIB_ARCH}/mariadb" -MARIADB_CFLAGS="-I${MARIADB_PREFIX}/include" -AC_SUBST(MARIADB_LIBS) -AC_SUBST(MARIADB_CFLAGS) -LDFLAGS="$LDFLAGS ${MARIADB_LIBS}" - -POLKIT_PREFIX=/usr -AC_ARG_WITH(polkit, -[ --with-polkit= prefix of polkit root directory, default /usr], -[POLKIT_PREFIX=$with_polkit], -AC_MSG_WARN([You can call configure with the --with-polkit option. -This tells configure where to place the polkit policy files.]) -) -AC_SUBST(POLKIT_PREFIX) -PKG_CHECK_MODULES(POLKIT, polkit-gobject-1) - -FFMPEG_PREFIX=/usr -AC_ARG_WITH(ffmpeg, - [ --with-ffmpeg= prefix of ffmpeg root directory for libavcodec etc, default /usr], - [FFMPEG_PREFIX=$with_ffmpeg], - AC_MSG_WARN([You can call configure with the --with-ffmpeg option. - This tells configure where to find the ffmpeg root directory within which are the libavcodec - and libavformat files that can be used to build true MPEG streaming into ZoneMinder. Ensure that - your copy of ffmpeg has installed libraries as well as binaries (use 'make installlib'). If you - are using a local install of ffmpeg you may have to remove or rename a previous real installation - as the headers and libraries from that will probably be picked up before your local copy. - e.g. --with-ffmpeg=/usr/local]) -) -AC_SUBST(FFMPEG_PREFIX) -FFMPEG_LIBS="-L${FFMPEG_PREFIX}/${LIB_ARCH}" -FFMPEG_CFLAGS="-I${FFMPEG_PREFIX}/include -D__STDC_CONSTANT_MACROS" -AC_SUBST(FFMPEG_LIBS) -AC_SUBST(FFMPEG_CFLAGS) - -LDFLAGS="${FFMPEG_LIBS} $LDFLAGS" -CFLAGS="${FFMPEG_CFLAGS} $CFLAGS" -CPPFLAGS="${FFMPEG_CFLAGS} $CPPFLAGS" - -EXTRA_LIBS= -AC_ARG_WITH(extralibs, - [ --with-extralibs="" string containing extra libraries to pass to link, default empty], - [EXTRA_LIBS=$with_extralibs], - AC_MSG_WARN([You can call configure with the --with-extralibs option. - Ordinarily you will need to use this option only when your copy of ffmpeg has been built - with support for additional formats and you would use this option to detail which additional - libraries ffmpeg was built with so that it is able to link successfully with ZoneMinder. - You will need to wrap this option in quotes if it contains any spaces. - e.g. --with-extralibs="-lmp3lame"]) -) -AC_SUBST(EXTRA_LIBS) - -LDFLAGS="$LDFLAGS ${EXTRA_LIBS}" - -AC_ARG_WITH(webdir, - [ --with-webdir= prefix of web directory], - [WEB_PREFIX=$with_webdir], - AC_MSG_ERROR([You must call configure with the --with-webdir option. - This tells configure where to install PHP and web files and scripts. - e.g. --with-webdir=/var/www/html or --with-webdir=/www/vhtdocs/]) -) -AC_SUBST(WEB_PREFIX) - -AC_ARG_WITH(cgidir, - [ --with-cgidir= prefix of cgi directory], - [CGI_PREFIX=$with_cgidir], - AC_MSG_ERROR([You must call configure with the --with-cgidir option. - This tells configure where to install cgi files and scripts. - e.g. --with-cgidir=/var/www/cgi-bin or --with-webdir=/www/vhtdocs//cgi-bin]) -) -AC_SUBST(CGI_PREFIX) - -WEB_USER=apache -AC_ARG_WITH(webuser, - [ --with-webuser= name of web user, default apache], - [WEB_USER=$with_webuser], - AC_MSG_WARN([You can call configure with the --with-webuser option. - This tells configure what the user name of the web user is if it is not the default of 'apache'. - e.g. --with-webuser=apache or --with-webuser=web]) -) -AC_SUBST(WEB_USER) - -WEB_GROUP=apache -AC_ARG_WITH(webgroup, - [ --with-webgroup= name of web group, default apache], - [WEB_GROUP=$with_webgroup], - AC_MSG_WARN([You can call configure with the --with-webgroup option. - This tells configure what the group name of the web group is if it is not the default of 'apache'. - e.g. --with-webgroup=apache or --with-webgroup=web]) -) -AC_SUBST(WEB_GROUP) - -WEB_HOST=zm.local -AC_ARG_WITH(webhost, - [ --with-webhost= name of web hostname, default zm.local], - [WEB_HOST=$with_webhost], - AC_MSG_WARN([You can call configure with the --with-webhost option. - This tells configure what the host name is for name based virtual hosting. This is only used to populate the sample web/zmHttpd.conf file. - e.g. --with-webhost=zm.localdomain]) -) -AC_SUBST(WEB_HOST) - -ENABLE_DEBUG=yes -AC_ARG_ENABLE(debug, - [ --enable-debug= enable or disable debug, default enabled], - [ENABLE_DEBUG=$enable_debug], - AC_MSG_WARN([You can call configure with the --enable-debug= or --disable-debug option. - This tells configure whether to compile ZoneMinder with debug included. Although debug is included - by default it is not output unless explicitly switched on elsewhere. These checks may induce a - small penalty on performance and if you are after squeezing the maximum possible performance out - of ZoneMinder you may use this switch to prevent debug from being compiled in. - e.g. --enable-debug=yes or --disable-debug]) -) -if test "$ENABLE_DEBUG" != "yes"; then - AC_DEFINE(ZM_DBG_OFF,1,"Whether debug is switched off and compiled out") -fi - -ENABLE_MMAP=yes -AC_ARG_ENABLE(mmap, - [ --enable-mmap= enable or disabled mapped memory versus shared memory, default mapped], - [ENABLE_MMAP=$enable_mmap], - AC_MSG_WARN([You can call configure with the --enable-mmap= or --disable-mmap option. - This tells configure whether to compile ZoneMinder with mmap support rather than IPC shared - memory. This is a feature that uses memory mapped into files which all processes can share. - Memory mapping requires less configuration and is more flexible than shared memory but may - slow down your system unless the mapped files are configured to reside on a fast or RAM based - filesystem which will normally be the case by default. - e.g. --enable-mmap=yes or --disable-mmap]) -) -if test "$ENABLE_MMAP" == "yes"; then - AC_DEFINE(ZM_MEM_MAPPED,1,"Whether to use mapped rather than shared memory") -else - AC_DEFINE(ZM_MEM_MAPPED,0,"Whether to use mapped rather than shared memory") -fi -AC_SUBST(ENABLE_MMAP) - -# Compiler -AC_LANG_CPLUSPLUS - -# Checks for programs. -AC_PROG_CXX -AC_PROG_CC -AC_PROG_INSTALL -AC_PROG_LN_S -AC_PROG_RANLIB -AC_PROG_MAKE_SET - -# Checks for typedefs, structures, and compiler characteristics. -AC_HEADER_STDBOOL -AC_C_CONST -AC_TYPE_UID_T -AC_C_INLINE -AC_TYPE_MODE_T -AC_TYPE_SIZE_T -AC_HEADER_TIME -AC_STRUCT_TM -AC_TYPE_SIGNAL - -AC_CHECK_TYPES(siginfo_t,,,[#include ]) -AC_CHECK_TYPES(ucontext_t,,,[#include ]) - -# Checks for library functions. -AC_PROG_GCC_TRADITIONAL -AC_FUNC_MALLOC -AC_FUNC_MMAP -AC_FUNC_SELECT_ARGTYPES -AC_FUNC_STAT -AC_FUNC_STRFTIME -AC_FUNC_STRTOD -AC_FUNC_VPRINTF -AC_CHECK_FUNCS([gethostbyname gethostname gettimeofday memmove memset mkdir munmap posix_memalign putenv select sendfile socket sqrt strcasecmp strchr strcspn strerror strncasecmp strrchr strspn strstr strtol strtoull]) -AC_CHECK_FUNCS([syscall sleep usleep ioctl ioctlsocket sigaction]) - -# Other programs -AC_CHECK_PROG(OPT_FFMPEG,ffmpeg,yes,no) -AC_PATH_PROG(PATH_FFMPEG,ffmpeg) -AC_CHECK_PROG(OPT_NETPBM,pnmscale,yes,no) -AC_PATH_PROG(PATH_NETPBM,pnmscale) -if test "$OPT_NETPBM" == "yes"; then -PATH_NETPBM=`dirname $PATH_NETPBM` -fi - -# Checks for libraries. -AC_SEARCH_LIBS(mysql_init,[mysqlclient mariadbclient],,AC_MSG_ERROR(zm requires libmysqlclient.a or libmariadbclient.a)) -AC_CHECK_LIB(jpeg,jpeg_start_compress,,AC_MSG_ERROR(zm requires libjpeg.a)) -AC_CHECK_LIB(pthread,pthread_create,,AC_MSG_ERROR(zm requires libpthread.a)) -AC_CHECK_LIB(dl,dlsym,,AC_MSG_ERROR(zm requires libdl.a)) -if test "$ZM_SSL_LIB" == "openssl"; then -AC_CHECK_HEADERS(openssl/md5.h,,AC_MSG_WARN(zm requires openssl/md5.h header to be installed for openssl),) -AC_CHECK_LIB(crypto,MD5,,AC_MSG_WARN([libcrypto.a is required for authenticated streaming - use ZM_SSL_LIB option to select gnutls instead])) -else -AC_CHECK_HEADERS(gnutls/openssl.h,AC_SUBST(ZM_HAS_GNUTLS_OPENSSL,1),AC_SUBST(ZM_HAS_GNUTLS_OPENSSL,0),) -AC_CHECK_HEADERS(gnutls/gnutls.h,AC_SUBST(ZM_HAS_GNUTLS,1),AC_SUBST(ZM_HAS_GNUTLS,0),) -if test "$ZM_HAS_GNUTLS_OPENSSL" == "0" && test "$ZM_HAS_GNUTLS" == "0"; then -AC_MSG_WARN(gnutls is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead) -fi -AC_CHECK_HEADERS(gcrypt.h,,AC_MSG_WARN(zm requires libgcrypt headers to be installed for gnutls),) -AC_CHECK_LIB(gcrypt,gcry_check_version,,AC_MSG_WARN([libgcrypt.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])) -AC_CHECK_LIB(gnutls,gnutls_fingerprint,,AC_MSG_WARN([libgnutls.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])) -if test "$ZM_HAS_GNUTLS_OPENSSL" == "1"; then -AC_CHECK_LIB(gnutls-openssl,MD5,,AC_MSG_WARN([libgnutls.a is required for authenticated streaming - use ZM_SSL_LIB option to select openssl instead])) -fi -fi -AC_CHECK_LIB(pcre,pcre_compile,,AC_MSG_WARN(libpcre.a may be required for remote/network camera support)) -AC_CHECK_LIB(z,zlibVersion) -AC_CHECK_LIB(x264,x264_predict_16x16_init) -AC_CHECK_LIB(avutil,av_malloc,,AC_MSG_WARN(libavutil.a may be required for MPEG streaming)) -# Don't bother to warn about this one -AC_CHECK_LIB(avcore,av_image_copy,,) -AC_CHECK_LIB(avcodec,avcodec_version,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) -AC_CHECK_LIB(avformat,avformat_version,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) -#AC_CHECK_LIB(avcodec,avcodec_open,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) -#AC_CHECK_LIB(avformat,av_new_stream,,AC_MSG_WARN(libavformat.a is required for MPEG streaming)) -AC_CHECK_LIB(avdevice,avdevice_register_all,,AC_MSG_WARN(libavdevice.a may be required for MPEG streaming)) -AC_CHECK_LIB(swscale,sws_scale,,,-lswscale) -AC_CHECK_LIB(vlc,libvlc_new,,AC_MSG_WARN(libvlc.a may be required for streaming)) -AC_CHECK_LIB(bz2,BZ2_bzCompress,,AC_MSG_WARN(zm requires libbz2.a for recent versions of ffmpeg)) -AC_CHECK_LIB(z,compress,,) -AC_CHECK_LIB(curl,curl_global_init,,) - -# Checks for header files. -AC_FUNC_ALLOCA -AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h limits.h memory.h stddef.h stdlib.h string.h strings.h sys/param.h sys/time.h syslog.h unistd.h values.h]) -AC_CHECK_HEADERS([netdb.h netinet/in.h arpa/inet.h sys/ioctl.h sys/socket.h sys/un.h glob.h sys/sendfile.h]) -AC_CHECK_HEADERS(execinfo.h,,,) -AC_CHECK_HEADERS(ucontext.h,,,) -AC_CHECK_HEADERS(sys/syscall.h,,,) -AC_CHECK_HEADERS(pthread.h,,,) -AC_CHECK_HEADERS(linux/videodev.h,AC_SUBST(ZM_HAS_V4L1,1),AC_SUBST(ZM_HAS_V4L1,0),) -AC_CHECK_HEADERS(linux/videodev2.h,AC_SUBST(ZM_HAS_V4L2,1),AC_SUBST(ZM_HAS_V4L2,0),) -if test "$ZM_HAS_V4L1" == "1" || test "$ZM_HAS_V4L2" == "1"; then -AC_SUBST(ZM_HAS_V4L,1) -else -AC_SUBST(ZM_HAS_V4L,0) -AC_MSG_WARN(zm requires Video4Linux or Video4Linux2 to be installed for analog or USB camera support) -fi -AC_CHECK_HEADERS(jpeglib.h,,AC_MSG_ERROR(zm requires libjpeg headers to be installed),) -AC_CHECK_HEADERS(mysql/mysql.h,,AC_MSG_ERROR(zm requires MySQL headers - check that MySQL development packages are installed),) -AC_LANG_PUSH([C]) -AC_CHECK_HEADERS(libavutil/avutil.h,,,) -AC_CHECK_HEADERS(libavcodec/avcodec.h,,,) -AC_CHECK_HEADERS(libavformat/avformat.h,,,) -AC_CHECK_HEADERS(libswscale/swscale.h,,,) -AC_LANG_POP([C]) -AC_CHECK_HEADERS(pcre/pcre.h,AC_SUBST(ZM_PCRE,"1"),,) -AC_CHECK_HEADERS(pcre.h,AC_SUBST(ZM_PCRE,"1"),,) -if test "$ENABLE_MMAP" == "yes"; then -AC_CHECK_HEADERS(sys/mman.h,,,) -AC_CHECK_HEADERS(fcntl.h,,,) -else -AC_CHECK_HEADERS(sys/ipc.h,,,) -AC_CHECK_HEADERS(sys/shm.h,,,) -fi -AC_CHECK_HEADERS(zlib.h,,,) -AC_CHECK_HEADERS(vlc/vlc.h,,,) -AC_CHECK_HEADERS(curl/curl.h,,,) - -if test "$ZM_SSL_LIB" == "openssl"; then -AC_CHECK_DECLS(MD5,,AC_MSG_ERROR([zm requires openssl/md5.h - use ZM_SSL_LIB option to select gnutls instead]),[#include -#include ]) -else -if test "$ZM_HAS_GNUTLS_OPENSSL" == "1"; then -AC_CHECK_DECLS(MD5,,AC_MSG_ERROR([zm requires gnutls/openssl.h - use ZM_SSL_LIB option to select openssl instead]),[#include -#include ]) -else -AC_CHECK_DECLS(gnutls_fingerprint,,AC_MSG_ERROR([zm requires gnutls/gnutls.h - use ZM_SSL_LIB option to select openssl instead]),[#include -#include ]) -fi -fi -AC_CHECK_DECLS(backtrace,,,[#include ]) -AC_CHECK_DECLS(backtrace_symbols,,,[#include ]) - -AC_SUBST(LDFLAGS) - -AC_PROG_PERL_VERSION(5.6.0) - -# Compulsory perl modules -AC_PROG_PERL_MODULES(Sys::Syslog,,AC_MSG_ERROR(zm requires SYS:Syslog)) -AC_PROG_PERL_MODULES(DBI,,AC_MSG_ERROR(zm requires DBI)) -AC_PROG_PERL_MODULES(DBD::mysql,,AC_MSG_ERROR(zm requires DBD::mysql)) -AC_PROG_PERL_MODULES(Getopt::Long,,AC_MSG_ERROR(zm requires Getopt::Long)) -AC_PROG_PERL_MODULES(Time::HiRes,,AC_MSG_ERROR(zm requires Time::HiRes)) -AC_PROG_PERL_MODULES(Date::Manip,,AC_MSG_ERROR(zm requires Date::Manip)) -AC_PROG_PERL_MODULES(LWP::UserAgent,,AC_MSG_ERROR(zm requires LWP::UserAgent)) -AC_PROG_PERL_MODULES(ExtUtils::MakeMaker,,AC_MSG_ERROR(zm requires ExtUtils::MakeMaker)) -if test "$ENABLE_MMAP" == "yes"; then -AC_PROG_PERL_MODULES(Sys::Mmap,,AC_MSG_ERROR(zm requires Sys::Mmap for mapped memory - set --enable-mmap=no to use IPC shared memory instead)) -fi - -# Optional perl modules -AC_PROG_PERL_MODULES(Module::Load,,AC_MSG_WARN(Module::Load is required for PTZ camera control)) -AC_PROG_PERL_MODULES(Device::SerialPort,,AC_MSG_WARN(Device::SerialPort is required for RS232/RS485 PTZ camera control)) -AC_PROG_PERL_MODULES(Net::FTP,,AC_MSG_WARN(Net::FTP is required for automatic event uploading using ftp)) -AC_PROG_PERL_MODULES(Net::SFTP::Foreign,,AC_MSG_WARN(Net::SFTP::Foreign is required for automatic event uploading using sftp)) -AC_PROG_PERL_MODULES(Expect,,AC_MSG_WARN(Expect is required for automatic event uploading using sftp)) -AC_PROG_PERL_MODULES(Archive::Tar,,AC_MSG_WARN(Archive::Tar may be required for automatic event uploading)) -AC_PROG_PERL_MODULES(Archive::Zip,,AC_MSG_WARN(Archive::Zip may be required for automatic event uploading)) -AC_PROG_PERL_MODULES(Net::SMTP,,AC_MSG_WARN(Net::SMTP may be required for automatic event email notification)) -AC_PROG_PERL_MODULES(MIME::Lite,,AC_MSG_WARN(MIME::Lite may be required for automatic event email notification)) -AC_PROG_PERL_MODULES(MIME::Entity,,AC_MSG_WARN(MIME::Entity may be required for automatic event email notification)) -AC_PROG_PERL_MODULES(X10::ActiveHome,,AC_MSG_WARN(X10::ActiveHome is required for X.10 support)) - -AC_DEFINE_DIR([BINDIR],[bindir],[Expanded binary directory]) -AC_DEFINE_DIR([LIBDIR],[libdir],[Expanded library directory]) -AC_DEFINE_DIR([DATADIR],[datadir],[Expanded data directory]) -AC_SUBST(PKGDATADIR,"$DATADIR/$PACKAGE") -AC_SUBST(ZM_PID,"$ZM_RUNDIR/zm.pid") -AC_DEFINE_DIR([SYSCONFDIR],[sysconfdir],[Expanded configuration directory]) -AC_SUBST(ZM_CONFIG,"$SYSCONFDIR/zm.conf") - -# Slight hack for non-standard perl install paths -if test "$prefix" != "NONE"; then - PERL_SITE_PREFIX=`perl -V:siteprefix | sed -e "s/.*='\(.*\)';/\1/"` - PERL_SITE_LIB=`perl -V:installsitelib | sed -e "s/.*='\(.*\)';/\1/"` - PERL_LIB_PATH=`echo $PERL_SITE_LIB | sed -e "s|^$PERL_SITE_PREFIX||"` - EXTRA_PERL_LIB="use lib '$prefix$PERL_LIB_PATH'; # Include custom perl install path" - PERL_MM_PARMS="PREFIX=$prefix" -else - EXTRA_PERL_LIB="# Include from system perl paths only" - PERL_MM_PARMS= -fi -AC_SUBST(PERL_MM_PARMS) -AC_SUBST(EXTRA_PERL_LIB) - -AC_CONFIG_FILES([Makefile zm.conf zmconfgen.pl db/Makefile db/zm_create.sql onvif/Makefile onvif/scripts/Makefile misc/Makefile misc/apache.conf misc/logrotate.conf misc/syslog.conf misc/com.zoneminder.systemctl.policy misc/com.zoneminder.systemctl.rules scripts/Makefile scripts/zm scripts/zmaudit.pl scripts/zmcontrol.pl scripts/zmdc.pl scripts/zmfilter.pl scripts/zmpkg.pl scripts/zmtrack.pl scripts/zmcamtool.pl scripts/zmtrigger.pl scripts/zmupdate.pl scripts/zmvideo.pl scripts/zmwatch.pl scripts/zmx10.pl scripts/zmdbbackup scripts/zmdbrestore scripts/zmeventdump scripts/zmlogrotate.conf scripts/ZoneMinder/lib/ZoneMinder/Base.pm scripts/ZoneMinder/lib/ZoneMinder/Config.pm scripts/ZoneMinder/lib/ZoneMinder/Memory.pm scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm src/Makefile src/zm_config.h web/Makefile web/ajax/Makefile web/css/Makefile web/graphics/Makefile web/includes/Makefile web/includes/config.php web/js/Makefile web/lang/Makefile web/skins/Makefile web/skins/classic/Makefile web/skins/classic/ajax/Makefile web/skins/classic/css/Makefile web/skins/classic/graphics/Makefile web/skins/classic/includes/Makefile web/skins/classic/js/Makefile web/skins/classic/lang/Makefile web/skins/classic/views/Makefile web/skins/classic/views/css/Makefile web/skins/classic/views/js/Makefile web/skins/mobile/Makefile web/skins/mobile/ajax/Makefile web/skins/mobile/css/Makefile web/skins/mobile/graphics/Makefile web/skins/mobile/includes/Makefile web/skins/mobile/lang/Makefile web/skins/mobile/views/Makefile web/skins/mobile/views/css/Makefile web/tools/Makefile web/tools/mootools/Makefile web/views/Makefile web/skins/xml/Makefile web/skins/xml/views/Makefile web/skins/xml/includes/Makefile web/skins/flat/Makefile web/skins/flat/ajax/Makefile web/skins/flat/css/Makefile web/skins/flat/graphics/Makefile web/skins/flat/includes/Makefile web/skins/flat/js/Makefile web/skins/flat/lang/Makefile web/skins/flat/views/Makefile web/skins/flat/views/css/Makefile web/skins/flat/views/js/Makefile]) - -# Create the definitions for compilation and defaults for the database -AC_CONFIG_COMMANDS([src/zm_config_defines.h],[perl ./zmconfgen.pl]) -# Manually generate the perl Makefile maker -AC_CONFIG_COMMANDS([scripts/ZoneMinder/Makefile],[(cd scripts/ZoneMinder; echo "perl Makefile.PL $PERL_MM_PARMS"; perl Makefile.PL $PERL_MM_PARMS)],[PERL_MM_PARMS=$PERL_MM_PARMS]) -AC_CONFIG_COMMANDS([onvif/modules/Makefile],[(cd onvif/modules; echo "perl Makefile.PL $PERL_MM_PARMS"; perl Makefile.PL $PERL_MM_PARMS)],[PERL_MM_PARMS=$PERL_MM_PARMS]) -AC_CONFIG_COMMANDS([onvif/proxy/Makefile],[(cd onvif/proxy; echo "perl Makefile.PL $PERL_MM_PARMS"; perl Makefile.PL $PERL_MM_PARMS)],[PERL_MM_PARMS=$PERL_MM_PARMS]) - -AC_OUTPUT diff --git a/db/Makefile.am b/db/Makefile.am deleted file mode 100644 index 9cb4e197c..000000000 --- a/db/Makefile.am +++ /dev/null @@ -1,14 +0,0 @@ -AUTOMAKE_OPTIONS = foreign - -zmdbdatadir = $(pkgdatadir)/db - -EXTRA_DIST = \ - zm_create.sql.in \ - $(dbupgrade_scripts) - -dist_zmdbdata_DATA = \ - zm_create.sql \ - $(dbupgrade_scripts) - -dbupgrade_scripts = $(wildcard zm_update-*.sql) - diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index d67c640f6..81280b393 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -236,13 +236,15 @@ CREATE TABLE `Filters` ( DROP TABLE IF EXISTS `Frames`; CREATE TABLE `Frames` ( + `Id` int(10) unsigned NOT NULL AUTO_INCREMENT, `EventId` int(10) unsigned NOT NULL default '0', `FrameId` int(10) unsigned NOT NULL default '0', `Type` enum('Normal','Bulk','Alarm') NOT NULL default 'Normal', `TimeStamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, `Delta` decimal(8,2) NOT NULL default '0.00', `Score` smallint(5) unsigned NOT NULL default '0', - PRIMARY KEY (`EventId`,`FrameId`), + PRIMARY KEY (`Id`), + INDEX `EventId_idx` (`EventId`), KEY `Type` (`Type`), KEY `TimeStamp` (`TimeStamp`) ) ENGINE=@ZM_MYSQL_ENGINE@; @@ -267,7 +269,8 @@ DROP TABLE IF EXISTS `Logs`; CREATE TABLE `Logs` ( `TimeKey` decimal(16,6) NOT NULL, `Component` varchar(32) NOT NULL, - `Pid` smallint(6) DEFAULT NULL, + `ServerId` int(10) unsigned, + `Pid` int(10) DEFAULT NULL, `Level` tinyint(3) NOT NULL, `Code` char(3) NOT NULL, `Message` text NOT NULL, @@ -315,12 +318,13 @@ DROP TABLE IF EXISTS `Monitors`; CREATE TABLE `Monitors` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', + `ServerId` int(10) unsigned, `Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local', `Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor', `Enabled` tinyint(3) unsigned NOT NULL default '1', `LinkedMonitors` varchar(255) NOT NULL default '', `Triggers` set('X10') NOT NULL default '', - `Device` varchar(64) NOT NULL default '', + `Device` tinytext NOT NULL default '', `Channel` tinyint(3) unsigned NOT NULL default '0', `Format` int(10) unsigned NOT NULL default '0', `V4LMultiBuffer` tinyint(1) unsigned NOT NULL default '0', @@ -340,6 +344,7 @@ CREATE TABLE `Monitors` ( `Palette` int(10) unsigned NOT NULL default '0', `Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0', `Deinterlacing` int(10) unsigned NOT NULL default '0', + `RTSPDescribe` tinyint(1) unsigned NOT NULL default '0', `Brightness` mediumint(7) NOT NULL default '-1', `Contrast` mediumint(7) NOT NULL default '-1', `Hue` mediumint(7) NOT NULL default '-1', @@ -348,6 +353,7 @@ CREATE TABLE `Monitors` ( `LabelFormat` varchar(64) NOT NULL default '%N - %y/%m/%d %H:%M:%S', `LabelX` smallint(5) unsigned NOT NULL default '0', `LabelY` smallint(5) unsigned NOT NULL default '0', + `LabelSize` smallint(5) unsigned NOT NULL DEFAULT '1', `ImageBufferCount` smallint(5) unsigned NOT NULL default '100', `WarmupCount` smallint(5) unsigned NOT NULL default '25', `PreEventCount` smallint(5) unsigned NOT NULL default '10', @@ -357,6 +363,8 @@ CREATE TABLE `Monitors` ( `SectionLength` int(10) unsigned NOT NULL default '600', `FrameSkip` smallint(5) unsigned NOT NULL default '0', `MotionFrameSkip` smallint(5) unsigned NOT NULL default '0', + `AnalysisFPS` decimal(5,2) default NULL, + `AnalysisUpdateDelay` smallint(5) unsigned NOT NULL default '0', `MaxFPS` decimal(5,2) default NULL, `AlarmMaxFPS` decimal(5,2) default NULL, `FPSReportInterval` smallint(5) unsigned NOT NULL default '250', @@ -376,21 +384,40 @@ CREATE TABLE `Monitors` ( `DefaultScale` smallint(5) unsigned NOT NULL default '100', `SignalCheckColour` varchar(32) NOT NULL default '#0000BE', `WebColour` varchar(32) NOT NULL default 'red', + `Exif` tinyint(1) unsigned NOT NULL default '0', `Sequence` smallint(5) unsigned default NULL, PRIMARY KEY (`Id`) ) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `States` --- +-- PP - Added IsActive to track custom run states +-- Also made sure Name is unique DROP TABLE IF EXISTS `States`; CREATE TABLE `States` ( + `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', `Definition` text NOT NULL, - PRIMARY KEY (`Name`) + `IsActive` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`Id`), + UNIQUE KEY (`Name`) ) ENGINE=@ZM_MYSQL_ENGINE@; +INSERT INTO States (Name,Definition,IsActive) VALUES ('default','','1'); + +-- +-- Table structure for table `Servers` +-- + +DROP TABLE IF EXISTS `Servers`; +CREATE TABLE `Servers` ( + `Id` int(10) unsigned NOT NULL auto_increment, + `Hostname` TEXT, + `Name` varchar(64) NOT NULL default '', + `State_Id` int(10) unsigned, + PRIMARY KEY (`Id`) +) ENGINE=@ZM_MYSQL_ENGINE@; -- -- Table structure for table `Stats` -- @@ -446,6 +473,7 @@ CREATE TABLE `Users` ( `Events` enum('None','View','Edit') NOT NULL default 'None', `Control` enum('None','View','Edit') NOT NULL default 'None', `Monitors` enum('None','View','Edit') NOT NULL default 'None', + `Groups` enum('None','View','Edit') NOT NULL default 'None', `Devices` enum('None','View','Edit') NOT NULL default 'None', `System` enum('None','View','Edit') NOT NULL default 'None', `MaxBandwidth` varchar(16) NOT NULL default '', @@ -462,7 +490,7 @@ DROP TABLE IF EXISTS `ZonePresets`; CREATE TABLE `ZonePresets` ( `Id` int(10) unsigned NOT NULL auto_increment, `Name` varchar(64) NOT NULL default '', - `Type` enum('Active','Inclusive','Exclusive','Preclusive','Inactive') NOT NULL default 'Active', + `Type` enum('Active','Inclusive','Exclusive','Preclusive','Inactive','Privacy') NOT NULL default 'Active', `Units` enum('Pixels','Percent') NOT NULL default 'Pixels', `CheckMethod` enum('AlarmedPixels','FilteredPixels','Blobs') NOT NULL default 'Blobs', `MinPixelThreshold` smallint(5) unsigned default NULL, @@ -491,7 +519,7 @@ CREATE TABLE `Zones` ( `Id` int(10) unsigned NOT NULL auto_increment, `MonitorId` int(10) unsigned NOT NULL default '0', `Name` varchar(64) NOT NULL default '', - `Type` enum('Active','Inclusive','Exclusive','Preclusive','Inactive') NOT NULL default 'Active', + `Type` enum('Active','Inclusive','Exclusive','Preclusive','Inactive','Privacy') NOT NULL default 'Active', `Units` enum('Pixels','Percent') NOT NULL default 'Pixels', `NumCoords` tinyint(3) unsigned NOT NULL default '0', `Coords` tinytext NOT NULL, @@ -531,7 +559,7 @@ CREATE TABLE `Zones` ( -- -- Create a default admin user. -- -insert into Users VALUES (NULL,'admin',password('admin'),'',1,'View','Edit','Edit','Edit','Edit','Edit','',''); +insert into Users VALUES (NULL,'admin',password('admin'),'',1,'View','Edit','Edit','Edit','Edit','Edit','Edit','',''); -- -- Add a sample filter to purge the oldest 100 events when the disk is 95% full @@ -559,6 +587,13 @@ INSERT INTO Controls VALUES (NULL,'Toshiba IK-WB11A','Remote','Toshiba_IK_WB11A' INSERT INTO Controls VALUES (NULL,'WanscamPT','Remote','Wanscam',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,1,0,1,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,16,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); INSERT INTO Controls VALUES (NULL,'3S Domo N5071', 'Remote', '3S', 0, 0, 1, 1, 0, 1, 1, 0, 0, 9999, 0, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 20, 9999, 0, 0, 0, 1, 1, 1, 1, 0, 0, 9999, 1, 9999, 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, 64, 1, 0, 1, 1, 0, 0, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 1, -180, 180, 40, 100, 1, 40, 100, 0, 0, 0, 0); INSERT INTO Controls VALUES (NULL,'ONVIF Camera','Ffmpeg','onvif',0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,255,16,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,6,1,1,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Foscam 9831W','Ffmpeg','FI9831W',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,0,0,0,0,0,0,0,1,16,1,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,0,0,0,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Foscam FI8918W','Ffmpeg','FI8918W',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,0,0,0,0,0,0,0,1,8,0,1,1,1,0,0,0,1,1,0,360,0,360,1,0,4,0,0,1,0,90,0,90,1,0,4,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','SPP1802SWPTZ',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,0,0,0,0,1,8,0,1,1,0,0,0,0,1,1,0,0,0,0,1,0,64,0,0,1,0,0,0,0,1,0,64,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Wanscam HW0025','Libvlc','WanscamHW0025', 1, 1, 1, 1, 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, 0, 0, 0, 1, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0); +INSERT INTO `Controls` VALUES (NULL,'IPCC 7210W','Libvlc','IPCC7210W', 1, 1, 1, 1, 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, 0, 0, 0, 1, 16, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 350, 0, 0, 1, 0, 10, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 0, 0, 0, 0); +INSERT INTO `Controls` VALUES (NULL,'Vivotek ePTZ','Remote','Vivotek_ePTZ',0,0,1,1,0,0,0,1,0,0,0,0,1,0,5,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,0,0,0,0,1,1,0,0,0,0,1,0,5,0,0,1,0,0,0,0,1,0,5,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Netcat ONVIF','Ffmpeg','Netcat',0,0,1,1,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,100,5,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,100,5,5,0,0,0,1,255,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0); -- -- Add some monitor preset values @@ -630,6 +665,7 @@ INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI8608W FFMPEG H.264','Ffmpeg',N INSERT INTO MonitorPresets VALUES (NULL,'Foscam FI9821W FFMPEG H.264','Ffmpeg',NULL,NULL,NULL,NULL,'','','','rtsp://:@:88/videoMain',NULL,1280,720,0,NULL,1,'12','','',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Loftek Sentinel PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,'','80','/videostream.cgi?user=&pwd=&resolution=32&rate=11',NULL,640,480,4,NULL,1,'13','',':@',100,100); INSERT INTO MonitorPresets VALUES (NULL,'Airlink 777W PTZ, 640x480, mjpeg','Remote','http',0,0,NULL,NULL,':@','80','/cgi/mjpg/mjpg.cgi',NULL,640,480,4,NULL,1,'7','',':@',100,100); +INSERT INTO MonitorPresets VALUES (NULL,'SunEyes SP-P1802SWPTZ','Libvlc','/dev/video','0',255,'','rtpMulti','','80','rtsp://:554/11','',1920,1080,0,0.00,1,'16','-speed=64',':',100,33); -- -- Add some zone preset values diff --git a/db/zm_update-1.28.1.sql b/db/zm_update-1.28.1.sql new file mode 100644 index 000000000..b7204905e --- /dev/null +++ b/db/zm_update-1.28.1.sql @@ -0,0 +1 @@ +ALTER TABLE Monitors MODIFY Device tinytext; diff --git a/db/zm_update-1.28.100.sql b/db/zm_update-1.28.100.sql new file mode 100644 index 000000000..1b3d3b745 --- /dev/null +++ b/db/zm_update-1.28.100.sql @@ -0,0 +1,22 @@ +-- +-- This updates a 1.28.99 database to 1.28.100 +-- + +-- +-- Add ServerId column to Monitors +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'ServerId' + ) > 0, +"SELECT 'Column ServerId exists in Monitors'", +"ALTER TABLE Monitors ADD `ServerId` int(10) unsigned AFTER `Name`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + diff --git a/db/zm_update-1.28.101.sql b/db/zm_update-1.28.101.sql new file mode 100644 index 000000000..1ba26bf98 --- /dev/null +++ b/db/zm_update-1.28.101.sql @@ -0,0 +1,22 @@ +-- +-- This updates a 1.28.100 database to 1.28.101 +-- + +-- +-- Add Groups column to Users +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Users' + AND table_schema = DATABASE() + AND column_name = 'Groups' + ) > 0, +"SELECT 'Column Groups exists in Users'", +"ALTER TABLE Users ADD COLUMN `Groups` ENUM('None','View','Edit') NOT NULL DEFAULT 'None' AFTER `Monitors`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + diff --git a/db/zm_update-1.28.102.sql b/db/zm_update-1.28.102.sql new file mode 100644 index 000000000..d0493c84f --- /dev/null +++ b/db/zm_update-1.28.102.sql @@ -0,0 +1,17 @@ +-- +-- Add Monitor Exif field +-- Used to enable or disable processing of the remote camera RTSP DESCRIBE response header +-- +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'Exif' + ) > 0, +"SELECT 'Column Exif already exists in Monitors'", +"ALTER TABLE `Monitors` ADD `Exif` tinyint(1) unsigned NOT NULL default '0' AFTER `WebColour`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.28.103.sql b/db/zm_update-1.28.103.sql new file mode 100644 index 000000000..dd6448eaa --- /dev/null +++ b/db/zm_update-1.28.103.sql @@ -0,0 +1,22 @@ +-- +-- This updates a 1.28.102 database to 1.28.103 +-- + +-- +-- Add LabelSize column to Monitors +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'LabelSize' + ) > 0, +"SELECT 'Column LabelSize exists in Monitors'", +"ALTER TABLE Monitors ADD `LabelSize` smallint(5) unsigned NOT NULL DEFAULT '1' AFTER `LabelY`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + diff --git a/db/zm_update-1.28.104.sql b/db/zm_update-1.28.104.sql new file mode 100644 index 000000000..b2a88c377 --- /dev/null +++ b/db/zm_update-1.28.104.sql @@ -0,0 +1,40 @@ +-- +-- This updates a 1.28.103 database to 1.28.104 +-- + +-- +-- Add AnalysisFPS column to Monitors +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'AnalysisFPS' + ) > 0, +"SELECT 'Column AnalysisFPS exists in Monitors'", +"ALTER TABLE Monitors ADD `AnalysisFPS` decimal(5,2) default NULL AFTER `MotionFrameSkip`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Add AnalysisUpdateDelay column to Monitors +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'AnalysisUpdateDelay' + ) > 0, +"SELECT 'Column AnalysisUpdateDelay exists in Monitors'", +"ALTER TABLE Monitors ADD `AnalysisUpdateDelay` smallint(5) unsigned not null default 0 AFTER `AnalysisFPS`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + diff --git a/db/zm_update-1.28.105.sql b/db/zm_update-1.28.105.sql new file mode 100644 index 000000000..4f6d34d11 --- /dev/null +++ b/db/zm_update-1.28.105.sql @@ -0,0 +1,10 @@ +-- +-- This updates a 1.28.104 database to 1.28.105 +-- + +-- +-- Add Privacy type to Zone Types +-- + +alter table Zones modify Type enum('Active','Inclusive','Exclusive','Preclusive','Inactive','Privacy') NOT NULL DEFAULT 'Active'; +alter table ZonePresets modify Type enum('Active','Inclusive','Exclusive','Preclusive','Inactive','Privacy') NOT NULL DEFAULT 'Active'; diff --git a/db/zm_update-1.28.106.sql b/db/zm_update-1.28.106.sql new file mode 100644 index 000000000..894022d22 --- /dev/null +++ b/db/zm_update-1.28.106.sql @@ -0,0 +1,21 @@ +-- +-- This updates a 1.28.105 database to 1.28.106 +-- + +-- +-- Add Monitor RTSPDescribe field +-- Used to enable or disable processing of the remote camera RTSP DESCRIBE response header +-- +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Monitors' + AND table_schema = DATABASE() + AND column_name = 'RTSPDescribe' + ) > 0, +"SELECT 'Column RTSPDescribe already exists in Monitors'", +"ALTER TABLE `Monitors` ADD `RTSPDescribe` tinyint(1) unsigned NOT NULL default '0' AFTER `Deinterlacing`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.28.107.sql b/db/zm_update-1.28.107.sql new file mode 100644 index 000000000..76fea0100 --- /dev/null +++ b/db/zm_update-1.28.107.sql @@ -0,0 +1,21 @@ +-- +-- This updates a 1.28.106 database to 1.28.107 +-- + +-- +-- Update Frame table to have a PrimaryKey of ID, insetad of a Composite Primary Key +-- Used primarially for compatibility with CakePHP +-- +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Frames' + AND table_schema = DATABASE() + AND column_name = 'Id' + ) > 0, +"SELECT 'Column ID already exists in Frames'", +"ALTER TABLE `Frames` ADD COLUMN `Id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT FIRST, DROP PRIMARY KEY, ADD PRIMARY KEY(`Id`)" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.28.108.sql b/db/zm_update-1.28.108.sql new file mode 100644 index 000000000..bbdcab32c --- /dev/null +++ b/db/zm_update-1.28.108.sql @@ -0,0 +1,22 @@ +-- +-- This updates a 1.28.107 database to 1.28.108 +-- + +-- +-- Update Frame table to have an Index on EventId, per the change made in 1.28.107 +-- +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.STATISTICS + WHERE table_name = 'Frames' + AND table_schema = DATABASE() + AND index_name = 'EventId_idx' + ) > 0, +"SELECT 'EventId Index already exists on Frames table'", +"CREATE INDEX `EventId_idx` ON `Frames` (`EventId`)" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + + diff --git a/db/zm_update-1.28.109.sql b/db/zm_update-1.28.109.sql new file mode 100644 index 000000000..dfbacdf5b --- /dev/null +++ b/db/zm_update-1.28.109.sql @@ -0,0 +1,21 @@ +-- +-- This updates a 1.28.108 database to 1.28.109 +-- + +-- +-- Update Frame table to have a PrimaryKey of ID, insetad of a Composite Primary Key +-- Used primarially for compatibility with CakePHP +-- +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Servers' + AND table_schema = DATABASE() + AND column_name = 'Hostname' + ) > 0, +"SELECT 'Column Hostname already exists in Servers'", +"ALTER TABLE `Servers` ADD COLUMN `Hostname` TEXT AFTER Name" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.28.110.sql b/db/zm_update-1.28.110.sql new file mode 100644 index 000000000..7a573bbf3 --- /dev/null +++ b/db/zm_update-1.28.110.sql @@ -0,0 +1,21 @@ +-- +-- This updates a 1.28.109 database to 1.28.110 +-- + +-- +-- Update Frame table to have a PrimaryKey of ID, insetad of a Composite Primary Key +-- Used primarially for compatibility with CakePHP +-- +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'Logs' + AND table_schema = DATABASE() + AND column_name = 'ServerId' + ) > 0, +"SELECT 'Column ServerId already exists in Logs'", +"ALTER TABLE `Logs` ADD COLUMN `ServerId` int(10) unsigned AFTER Component" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/db/zm_update-1.28.99.sql b/db/zm_update-1.28.99.sql new file mode 100644 index 000000000..5b342a811 --- /dev/null +++ b/db/zm_update-1.28.99.sql @@ -0,0 +1,395 @@ +-- +-- This updates a 1.28.10 database to 1.28.99 +-- + +-- +-- Add Controls definition for ONVIF +-- Add Controls definition for FI9831W +-- Add Controls definition for FI8918W +-- +INSERT INTO Controls +SELECT * FROM (SELECT NULL as Id, + 'ONVIF Camera' as Name, + 'Ffmpeg' as Type, + 'onvif' as Protocol, + 0 as CanWake, + 0 as CanSleep, + 1 as CanReset, + 1 as CanZoom, + 0 as CanAutoZoom, + 0 as CanZoomAbs, + 0 as CanZoomRel, + 1 as CanZoomCon, + 0 as MinZoomRange, + 0 as MaxZoomRange, + 0 as MinZoomStep, + 0 as MaxZoomStep, + 0 as HasZoomSpeed, + 0 as MinZoomSpeed, + 0 as MaxZoomSpeed, + 0 as CanFocus, + 0 as CanAutoFocus, + 0 as CanFocusAbs, + 0 as CanFocusRel, + 0 as CanFocusCon, + 0 as MinFocusRange, + 0 as MaxFocusRange, + 0 as MinFocusStep, + 0 as MaxFocusStep, + 0 as HasFocusSpeed, + 0 as MinFocusSpeed, + 0 as MaxFocusSpeed, + 1 as CanIris, + 0 as CanAutoIris, + 1 as CanIrisAbs, + 0 as CanIrisRel, + 0 as CanIrisCon, + 0 as MinIrisRange, + 255 as MaxIrisRange, + 16 as MinIrisStep, + 16 as MaxIrisStep, + 0 as HasIrisSpeed, + 0 as MinIrisSpeed, + 0 as MaxIrisSpeed, + 0 as CanGain, + 0 as CanAutoGain, + 0 as CanGainAbs, + 0 as CanGainRel, + 0 as CanGainCon, + 0 as MinGainRange, + 0 as MaxGainRange, + 0 as MinGainStep, + 0 as MaxGainStep, + 0 as HasGainSpeed, + 0 as MinGainSpeed, + 0 as MaxGainSpeed, + 1 as CanWhite, + 0 as CanAutoWhite, + 1 as CanWhiteAbs, + 0 as CanWhiteRel, + 0 as CanWhiteCon, + 0 as MinWhiteRange, + 6 as MaxWhiteRange, + 1 as MinWhiteStep, + 1 as MaxWhiteStep, + 0 as HasWhiteSpeed, + 0 as MinWhiteSpeed, + 0 as MaxWhiteSpeed, + 1 as HasPresets, + 10 as NumPresets, + 0 as HasHomePreset, + 1 as CanSetPresets, + 1 as CanMove, + 1 as CanMoveDiag, + 0 as CanMoveMap, + 0 as CanMoveAbs, + 0 as CanMoveRel, + 1 as CanMoveCon, + 1 as CanPan, + 0 as MinPanRange, + 0 as MaxPanRange, + 0 as MinPanStep, + 0 as MaxPanStep, + 0 as HasPanSpeed, + 0 as MinPanSpeed, + 0 as MaxPanSpeed, + 0 as HasTurboPan, + 0 as TurboPanSpeed, + 1 as CanTilt, + 0 as MinTiltRange, + 0 as MaxTiltRange, + 0 as MinTiltStep, + 0 as MaxTiltStep, + 0 as HasTiltSpeed, + 0 as MinTiltSpeed, + 0 as MaxTiltSpeed, + 0 as HasTurboTilt, + 0 as TurboTiltSpeed, + 0 as CanAutoScan, + 0 as NumScanPaths) AS tmp +WHERE NOT EXISTS ( + SELECT Name FROM Controls WHERE name = 'ONVIF Camera' +) LIMIT 1; + +INSERT INTO Controls +SELECT * FROM (SELECT NULL as Id, + 'Foscam 9831W' as Name, + 'Ffmpeg' as Type, + 'FI9831W' as Protocol, + 0 as CanWake, + 0 as CanSleep, + 1 as CanReset, + 0 as CanZoom, + 0 as CanAutoZoom, + 0 as CanZoomAbs, + 0 as CanZoomRel, + 0 as CanZoomCon, + 0 as MinZoomRange, + 0 as MaxZoomRange, + 0 as MinZoomStep, + 0 as MaxZoomStep, + 0 as HasZoomSpeed, + 0 as MinZoomSpeed, + 0 as MaxZoomSpeed, + 0 as CanFocus, + 0 as CanAutoFocus, + 0 as CanFocusAbs, + 0 as CanFocusRel, + 0 as CanFocusCon, + 0 as MinFocusRange, + 0 as MaxFocusRange, + 0 as MinFocusStep, + 0 as MaxFocusStep, + 0 as HasFocusSpeed, + 0 as MinFocusSpeed, + 0 as MaxFocusSpeed, + 0 as CanIris, + 0 as CanAutoIris, + 0 as CanIrisAbs, + 0 as CanIrisRel, + 0 as CanIrisCon, + 0 as MinIrisRange, + 0 as MaxIrisRange, + 0 as MinIrisStep, + 0 as MaxIrisStep, + 0 as HasIrisSpeed, + 0 as MinIrisSpeed, + 0 as MaxIrisSpeed, + 0 as CanGain, + 0 as CanAutoGain, + 0 as CanGainAbs, + 0 as CanGainRel, + 0 as CanGainCon, + 0 as MinGainRange, + 0 as MaxGainRange, + 0 as MinGainStep, + 0 as MaxGainStep, + 0 as HasGainSpeed, + 0 as MinGainSpeed, + 0 as MaxGainSpeed, + 0 as CanWhite, + 0 as CanAutoWhite, + 0 as CanWhiteAbs, + 0 as CanWhiteRel, + 0 as CanWhiteCon, + 0 as MinWhiteRange, + 0 as MaxWhiteRange, + 0 as MinWhiteStep, + 0 as MaxWhiteStep, + 0 as HasWhiteSpeed, + 0 as MinWhiteSpeed, + 0 as MaxWhiteSpeed, + 0 as HasPresets, + 16 as NumPresets, + 1 as HasHomePreset, + 1 as CanSetPresets, + 1 as CanMove, + 1 as CanMoveDiag, + 0 as CanMoveMap, + 0 as CanMoveAbs, + 0 as CanMoveRel, + 1 as CanMoveCon, + 1 as CanPan, + 0 as MinPanRange, + 360 as MaxPanRange, + 0 as MinPanStep, + 360 as MaxPanStep, + 1 as HasPanSpeed, + 0 as MinPanSpeed, + 4 as MaxPanSpeed, + 0 as HasTurboPan, + 0 as TurboPanSpeed, + 1 as CanTilt, + 0 as MinTiltRange, + 90 as MaxTiltRange, + 0 as MinTiltStep, + 90 as MaxTiltStep, + 0 as HasTiltSpeed, + 0 as MinTiltSpeed, + 0 as MaxTiltSpeed, + 0 as HasTurboTilt, + 0 as TurboTiltSpeed, + 0 as CanAutoScan, + 0 as NumScanPaths) AS tmp +WHERE NOT EXISTS ( + SELECT Name FROM Controls WHERE name = 'Foscam 9831W' +) LIMIT 1; + +INSERT INTO Controls +SELECT * FROM (SELECT NULL as Id, + 'Foscam FI8918W' as Name, + 'Ffmpeg' as Type, + 'FI8918W' as Protocol, + 0 as CanWake, + 0 as CanSleep, + 1 as CanReset, + 0 as CanZoom, + 0 as CanAutoZoom, + 0 as CanZoomAbs, + 0 as CanZoomRel, + 0 as CanZoomCon, + 0 as MinZoomRange, + 0 as MaxZoomRange, + 0 as MinZoomStep, + 0 as MaxZoomStep, + 0 as HasZoomSpeed, + 0 as MinZoomSpeed, + 0 as MaxZoomSpeed, + 0 as CanFocus, + 0 as CanAutoFocus, + 0 as CanFocusAbs, + 0 as CanFocusRel, + 0 as CanFocusCon, + 0 as MinFocusRange, + 0 as MaxFocusRange, + 0 as MinFocusStep, + 0 as MaxFocusStep, + 0 as HasFocusSpeed, + 0 as MinFocusSpeed, + 0 as MaxFocusSpeed, + 0 as CanIris, + 0 as CanAutoIris, + 0 as CanIrisAbs, + 0 as CanIrisRel, + 0 as CanIrisCon, + 0 as MinIrisRange, + 0 as MaxIrisRange, + 0 as MinIrisStep, + 0 as MaxIrisStep, + 0 as HasIrisSpeed, + 0 as MinIrisSpeed, + 0 as MaxIrisSpeed, + 0 as CanGain, + 0 as CanAutoGain, + 0 as CanGainAbs, + 0 as CanGainRel, + 0 as CanGainCon, + 0 as MinGainRange, + 0 as MaxGainRange, + 0 as MinGainStep, + 0 as MaxGainStep, + 0 as HasGainSpeed, + 0 as MinGainSpeed, + 0 as MaxGainSpeed, + 0 as CanWhite, + 0 as CanAutoWhite, + 0 as CanWhiteAbs, + 0 as CanWhiteRel, + 0 as CanWhiteCon, + 0 as MinWhiteRange, + 0 as MaxWhiteRange, + 0 as MinWhiteStep, + 0 as MaxWhiteStep, + 0 as HasWhiteSpeed, + 0 as MinWhiteSpeed, + 0 as MaxWhiteSpeed, + 0 as HasPresets, + 8 as NumPresets, + 0 as HasHomePreset, + 1 as CanSetPresets, + 1 as CanMove, + 1 as CanMoveDiag, + 0 as CanMoveMap, + 0 as CanMoveAbs, + 0 as CanMoveRel, + 1 as CanMoveCon, + 1 as CanPan, + 0 as MinPanRange, + 360 as MaxPanRange, + 0 as MinPanStep, + 360 as MaxPanStep, + 1 as HasPanSpeed, + 0 as MinPanSpeed, + 4 as MaxPanSpeed, + 0 as HasTurboPan, + 0 as TurboPanSpeed, + 1 as CanTilt, + 0 as MinTiltRange, + 90 as MaxTiltRange, + 0 as MinTiltStep, + 90 as MaxTiltStep, + 0 as HasTiltSpeed, + 0 as MinTiltSpeed, + 0 as MaxTiltSpeed, + 0 as HasTurboTilt, + 0 as TurboTiltSpeed, + 0 as CanAutoScan, + 0 as NumScanPaths) AS tmp +WHERE NOT EXISTS ( + SELECT Name FROM Controls WHERE name = 'Foscam FI8918W' +) LIMIT 1; + +-- +-- Hide USE_DEEP_STORAGE from user to prevent accidental event loss +-- +UPDATE `zm`.`Config` SET `Category`='hidden' WHERE `Name`='ZM_USE_DEEP_STORAGE'; + +-- +-- Add Id column to State +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'States' + AND table_schema = DATABASE() + AND column_name = 'Id' + ) > 0, +"SELECT 'Column Id exists in States'", +"ALTER TABLE States DROP PRIMARY KEY, ADD `Id` int(10) unsigned auto_increment NOT NULL PRIMARY KEY FIRST" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- PP:The States table will be updated to have a new column called IsActive +-- used to keep track of which custom state is active (if any) +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.COLUMNS + WHERE table_name = 'States' + AND table_schema = DATABASE() + AND column_name = 'IsActive' + ) > 0, +"SELECT 'Column IsActive exists in States'", +"ALTER TABLE `States` ADD `IsActive` tinyint(3) unsigned not null default 0 AFTER `Definition`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- PP:If default state does not exist, create it and set its IsActive to 1 +INSERT INTO States (Name,Definition,IsActive) + SELECT * FROM (SELECT 'default', '', '1') AS tmp + WHERE NOT EXISTS ( + SELECT Name FROM States WHERE Name = 'default' + ) LIMIT 1; + +-- PP:Start with a sane isActive state +UPDATE States SET IsActive = '0'; +UPDATE States SET IsActive = '1' WHERE Name = 'default'; + +-- PP:Finally convert States to make sure Names are unique +-- If duplicate states existed while upgrading, that is +-- very likely an error that ZM allowed earlier, so +-- we are picking up the first one and deleting the others +ALTER TABLE States ADD UNIQUE (Name); + +SET @s = (SELECT IF( + (SELECT COUNT(*) + FROM INFORMATION_SCHEMA.TABLES + WHERE table_name = 'Servers' + AND table_schema = DATABASE() + ) > 0, +"SELECT 'Servers table exists'", +"CREATE TABLE `Servers` ( + `Id` int(10) unsigned NOT NULL auto_increment, + `Name` varchar(64) NOT NULL default '', + `State_Id` int(10) unsigned, + PRIMARY KEY (`Id`) +)" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + diff --git a/db/zm_update-1.29.0.sql b/db/zm_update-1.29.0.sql new file mode 100644 index 000000000..5d17651a3 --- /dev/null +++ b/db/zm_update-1.29.0.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.28.110 database to 1.29.0 +-- +-- No changes required +-- diff --git a/db/zm_update-1.29.1.sql b/db/zm_update-1.29.1.sql new file mode 100644 index 000000000..8fd43c318 --- /dev/null +++ b/db/zm_update-1.29.1.sql @@ -0,0 +1,7 @@ +-- +-- This updates a 1.29.0 database to 1.29.1 +-- +-- + +-- Increase the size of the Pid field for FreeBSD +ALTER TABLE Logs MODIFY Pid int(10); diff --git a/db/zm_update-1.30.0.sql b/db/zm_update-1.30.0.sql new file mode 100644 index 000000000..959f899a3 --- /dev/null +++ b/db/zm_update-1.30.0.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.29.1 database to 1.30.0 +-- +-- No changes required +-- diff --git a/distros/debian/apache.conf b/distros/debian/apache.conf index 92a2b6414..6f2f30fd9 100644 --- a/distros/debian/apache.conf +++ b/distros/debian/apache.conf @@ -1,7 +1,6 @@ -Alias /zm /usr/share/zoneminder +Alias /zm /usr/share/zoneminder/www - - php_flag register_globals off + Options Indexes FollowSymLinks DirectoryIndex index.php diff --git a/distros/debian/changelog b/distros/debian/changelog index 708445ab9..72841f158 100644 --- a/distros/debian/changelog +++ b/distros/debian/changelog @@ -1,3 +1,32 @@ +zoneminder (1.28.108-nmu2015100101) wheezy; urgency=low + + * + + -- Isaac Connor Thu, 01 Oct 2015 18:20:29 +0000 + +zoneminder (1.28.107-nmu2015092401) wheezy; urgency=low + + * + + -- Isaac Connor Thu, 24 Sep 2015 14:15:46 +0000 + +zoneminder (1.28.1+106-nmu2015091001) wheezy; urgency=low + + * + + -- Isaac Connor Thu, 10 Sep 2015 18:03:43 +0000 + +zoneminder (1.28.0-0.1) wheezy; urgency=low + + * Use CMake instead of Autotools to simplify + debian/rules and have less build-depends. + * Some lintian love in debian/{control,copyright} + and perl ZoneMinder modules. + * Don't purge database if we use a remote + MySQL server. + + -- Cosme Domínguez Díaz Sun, 09 Nov 2014 02:20:20 +0100 + zoneminder (1.28.0-wheezy) wheezy; urgency=medium * Release diff --git a/distros/debian/control b/distros/debian/control index 26f919963..ebb59b3ab 100644 --- a/distros/debian/control +++ b/distros/debian/control @@ -2,14 +2,54 @@ Source: zoneminder Section: net Priority: optional Maintainer: Isaac Connor -Build-Depends: debhelper (>= 7.0.50), autoconf, automake, dpatch, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev | libmariadbclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev, libpcre3-dev, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libtool, libsys-mmap-perl, ffmpeg | libav-tools, libnetpbm10-dev, libavdevice-dev, libdevice-serialport-perl, libpcre3, libarchive-zip-perl, libmime-lite-perl, libjpeg8, dh-autoreconf, libvlccore-dev, libvlc-dev, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libgcrypt11-dev -Standards-Version: 3.9.2 +Build-Depends: debhelper (>= 9), cmake + , libphp-serialization-perl + , libgnutls28-dev | libgnutls-dev + , libmysqlclient-dev | libmariadbclient-dev + , libjpeg8-dev + , libpcre3-dev + , libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev + , libv4l-dev (>= 0.8.3) + , libbz2-dev + , libav-tools + , libnetpbm10-dev + , libavdevice-dev + , libvlccore-dev, libvlc-dev + , libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev + , libgcrypt11-dev, libpolkit-gobject-1-dev + , libphp-serialization-perl + , libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl + , libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl + , libmodule-load-perl, libsys-mmap-perl, libjson-any-perl + , libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl + , libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl + , libsys-cpu-perl, libsys-meminfo-perl + , libdata-uuid-perl +Standards-Version: 3.9.4 Package: zoneminder Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2 | httpd, libapache2-mod-php5 | libapache2-mod-fcgid | php5-fpm, php5, php5-mysql|php5-mysqlnd, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client|mysql-client, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg | libav-tools, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, netpbm, libavdevice53, libjpeg8, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0 -Recommends: mysql-server|mariadb-server -Description: A video camera security and surveillance solution +Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} + , apache2 | httpd, libapache2-mod-php5 | libapache2-mod-fcgid | php5-fpm + , php5-mysqlnd | php5-mysql + , mariadb-client | mysql-client + , libphp-serialization-perl + , libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, libdbd-mysql-perl + , libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl + , libmodule-load-perl, libsys-mmap-perl, libjson-any-perl + , libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl + , libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl + , libsys-cpu-perl, libsys-meminfo-perl + , libdata-uuid-perl + , libpcre3 + , libav-tools, libavdevice53 + , rsyslog | system-log-daemon + , netpbm , libjpeg8 + , zip + , libvlccore5 | libvlccore7, libvlc5 + , libpolkit-gobject-1-0, php5-gd +Recommends: mysql-server | mariadb-server +Description: Video camera security and surveillance solution ZoneMinder is intended for use in single or multi-camera video security applications, including commercial or home CCTV, theft prevention and child or family member or home monitoring and other care scenarios. It @@ -23,12 +63,12 @@ Description: A video camera security and surveillance solution Package: zoneminder-dbg Architecture: any -Depends: - zoneminder (= ${binary:Version}), - ${misc:Depends} -Description: debugging syumbols for zoneminder. +Section: debug +Priority: extra +Depends: zoneminder (= ${binary:Version}), ${misc:Depends} +Description: Debugging symbols for zoneminder. ZoneMinder is a video camera security and surveillance solution. - ZoneMinder is intended for use in single or multi-camera video security + ZoneMinder is intended for use in single or multi-camera video security applications, including commercial or home CCTV, theft prevention and child or family member or home monitoring and other care scenarios. It supports capture, analysis, recording, and monitoring of video data coming diff --git a/distros/debian/copyright b/distros/debian/copyright index a177502a0..1c10c59db 100644 --- a/distros/debian/copyright +++ b/distros/debian/copyright @@ -20,3 +20,38 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian GNU/Linux systems, the text of the GPL can be found in /usr/share/common-licenses/GPL. + +/usr/share/zoneminder/api/lib/Cake/*: +Copyright: + +Copyright (c) 2005-2013, Cake Software Foundation, Inc. + +License: + +CakePHP(tm) : The Rapid Development PHP Framework (http://cakephp.org) + +The MIT License + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +Cake Software Foundation, Inc. +1785 E. Sahara Avenue, +Suite 490-204 +Las Vegas, Nevada 89104, +United States of America. diff --git a/distros/debian/dirs b/distros/debian/dirs index 4178482c1..9e29e6113 100644 --- a/distros/debian/dirs +++ b/distros/debian/dirs @@ -3,4 +3,3 @@ var/lib/zm var/cache/zoneminder/events var/cache/zoneminder/images var/cache/zoneminder/temp -usr/share/zoneminder/db diff --git a/distros/debian/init.d b/distros/debian/init.d index d3354c1d8..036bb695d 100644 --- a/distros/debian/init.d +++ b/distros/debian/init.d @@ -23,12 +23,11 @@ command="$ZM_PATH_BIN/zmpkg.pl" start() { echo -n "Starting $prog: " - mkdir -p $RUNDIR && chown www-data:www-data $RUNDIR - mkdir -p $TMPDIR && chown www-data:www-data $TMPDIR + export TZ=:/etc/localtime + mkdir -p $RUNDIR $TMPDIR && chown www-data:www-data $RUNDIR $TMPDIR $command start RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure + [ $RETVAL = 0 ] && echo success || echo failure echo [ $RETVAL = 0 ] && touch /var/lock/zm return $RETVAL @@ -51,8 +50,7 @@ stop() { else $command stop RETVAL=$? - [ $RETVAL = 0 ] && echo success - [ $RETVAL != 0 ] && echo failure + [ $RETVAL = 0 ] && echo success || echo failure echo [ $RETVAL = 0 ] && rm -f /var/lock/zm fi diff --git a/distros/debian/install b/distros/debian/install index 9e8953409..97c5f7a03 100644 --- a/distros/debian/install +++ b/distros/debian/install @@ -1,7 +1,8 @@ usr/bin -usr/lib/cgi-bin +usr/lib/zoneminder/cgi-bin usr/share/man usr/share/perl5/ZoneMinder usr/share/perl5/ZoneMinder.pm -usr/share/zoneminder +usr/share/zoneminder/db +usr/share/zoneminder/www etc/zm diff --git a/distros/debian/links b/distros/debian/links index 9715ee428..5560a100a 100644 --- a/distros/debian/links +++ b/distros/debian/links @@ -1,4 +1,3 @@ var/cache/zoneminder/events usr/share/zoneminder/events var/cache/zoneminder/images usr/share/zoneminder/images var/cache/zoneminder/temp usr/share/zoneminder/temp -usr/lib/cgi-bin usr/share/zoneminder/cgi-bin diff --git a/distros/debian/postinst b/distros/debian/postinst index d06f9c641..39e3dfc32 100644 --- a/distros/debian/postinst +++ b/distros/debian/postinst @@ -3,51 +3,51 @@ set -e if [ "$1" = "configure" ]; then - if [ -e "/etc/init.d/mysql" ]; then - # - # Get mysql started if it isn't - # - if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then - invoke-rc.d mysql start - fi - if $(/etc/init.d/mysql status >/dev/null 2>&1); then - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload - # test if database if already present... - if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then - cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf - echo 'grant lock tables, alter,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql - fi - invoke-rc.d zoneminder stop || true - zmupdate.pl --nointeractive + . /etc/zm/zm.conf - else - echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' - fi - else - echo 'mysql not found, assuming remote server.' - fi - chown www-data:www-data /var/log/zm - chown www-data:www-data /var/lib/zm/ + # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group. + chown www-data:root /var/log/zm + chown www-data:www-data /var/lib/zm if [ -z "$2" ]; then - chown www-data:www-data -R /var/cache/zoneminder + chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* fi -fi -# Ensure zoneminder is stopped... -if [ -x "/etc/init.d/zoneminder" ]; then - if invoke-rc.d zoneminder status ; then - invoke-rc.d zoneminder stop || exit $? + + # Do this every time the package is installed or upgraded + + if [ "$ZM_DB_HOST" = "localhost" ]; then + if [ -e "/etc/init.d/mysql" ]; then + # + # Get mysql started if it isn't + # + if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then + invoke-rc.d mysql start + fi + if $(/etc/init.d/mysql status >/dev/null 2>&1); then + mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload + # test if database if already present... + if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then + cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + # This creates the user. + echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + else + echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + fi + + # Ensure zoneminder is stopped + invoke-rc.d zoneminder stop || true + zmupdate.pl --nointeractive + zmupdate.pl --nointeractive -f + invoke-rc.d zoneminder start || true + else + echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' + fi + else + echo 'mysql not found, assuming remote server.' fi -fi - -if [ "$1" = "configure" ]; then - if [ -z "$2" ]; then - chown www-data:www-data /var/log/zm - chown www-data:www-data /var/lib/zm/ - chown www-data:www-data -R /var/cache/zoneminder else - chown www-data:www-data /var/log/zm - zmupdate.pl - fi + echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" + fi + fi #DEBHELPER# diff --git a/distros/debian/postrm b/distros/debian/postrm index 28a00a7a0..fde590981 100644 --- a/distros/debian/postrm +++ b/distros/debian/postrm @@ -1,9 +1,11 @@ #! /bin/sh -# set -e # to be reinstated later +set -e if [ "$1" = "purge" ]; then - echo 'delete from user where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql - echo 'delete from db where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm + if [ -e "/etc/init.d/mysql" ]; then + echo 'delete from user where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql + echo 'delete from db where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql + mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm + fi fi #DEBHELPER# diff --git a/distros/debian/preinst b/distros/debian/preinst old mode 100755 new mode 100644 diff --git a/distros/debian/rules b/distros/debian/rules index df11f4988..4bdc7cb0c 100755 --- a/distros/debian/rules +++ b/distros/debian/rules @@ -1,71 +1,41 @@ #!/usr/bin/make -f # -*- makefile -*- -# Sample debian/rules that uses debhelper. -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. - # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 -# These are used for cross-compiling and for saving the configure script -# from having to guess our platform (since we know it already) -DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) -DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) - -CFLAGS = -Wall -g -CPPFLAGS = -D__STDC_CONSTANT_MACROS -CXXFLAGS = -DHAVE_LIBCRYPTO - -ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) - CFLAGS += -O0 -else - CFLAGS += -O2 -endif - -%: - dh $@ --with autoreconf +export CFLAGS = -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -pipe +export CXXFLAGS = -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -pipe +INSTDIR = debian/tmp override_dh_auto_configure: - CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --sysconfdir=/etc/zm --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info --with-mysql=/usr --with-mariadb=/usr --with-webdir=/usr/share/zoneminder --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-crashtrace=no --enable-mmap=yes + dh_auto_configure -- \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_SKIP_RPATH=ON \ + -DCMAKE_VERBOSE_MAKEFILE=OFF \ + -DCMAKE_COLOR_MAKEFILE=ON \ + -DZM_RUNDIR=/var/run/zm \ + -DZM_SOCKDIR=/var/run/zm \ + -DZM_TMPDIR=/var/tmp/zm \ + -DZM_LOGDIR=/var/log/zm \ + -DZM_WEBDIR=/usr/share/zoneminder/www \ + -DZM_CONTENTDIR=/var/cache/zoneminder \ + -DZM_CGIDIR=/usr/lib/zoneminder/cgi-bin \ + -DZM_WEB_USER=www-data \ + -DZM_WEB_GROUP=www-data \ + -DCMAKE_INSTALL_SYSCONFDIR=etc/zm -override_dh_clean: - # Add here commands to clean up after the build process. - [ ! -f Makefile ] || $(MAKE) distclean - dh_clean - -override_dh_install: - # Add here commands to install the package into debian/zm. - $(MAKE) install DESTDIR=$(CURDIR)/debian/zoneminder RUNDIR=$(CURDIR)/debian/zoneminder/var/run ZM_RUNDIR=$(CURDIR)/debian/zoneminder/var/run - install -D -m 0644 db/zm_create.sql $(CURDIR)/debian/zoneminder/usr/share/zoneminder/db - install -D -m 0644 db/zm_update-*.sql $(CURDIR)/debian/zoneminder/usr/share/zoneminder/db - install -D -m 0644 debian/apache.conf $(CURDIR)/debian/zoneminder/etc/zm - # - # NOTE: This is a short-term kludge; hopefully changes in the next - # upstream version will render this unnecessary. - rm -rf debian/zoneminder/usr/share/zoneminder/events - rm -rf debian/zoneminder/usr/share/zoneminder/images - rm -rf debian/zoneminder/usr/share/zoneminder/temp - ln -s /var/cache/zoneminder/events debian/zoneminder/usr/share/zoneminder/ - ln -s /var/cache/zoneminder/images debian/zoneminder/usr/share/zoneminder/ - ln -s /var/cache/zoneminder/temp debian/zoneminder/usr/share/zoneminder/ - - # - # This is a slightly lesser kludge; moving the cgi stuff to - # /usr/share/zoneminder/cgi-bin breaks one set of behavior, - # having it just in /usr/lib/cgi-bin breaks another bit of - # behavior. - # - ln -s /usr/lib/cgi-bin debian/zoneminder/usr/share/zoneminder/ - -override_dh_fixperms: - dh_fixperms - chown root:root debian/zoneminder/etc/zm/zm.conf +override_dh_auto_install: + dh_auto_install --buildsystem=cmake + install -D -m 0644 debian/apache.conf $(INSTDIR)/etc/zm/apache.conf + rm $(INSTDIR)/usr/share/zoneminder/api/lib/Cake/LICENSE.txt + rm $(INSTDIR)/usr/share/zoneminder/api/.gitignore + rm -r $(INSTDIR)/usr/share/zoneminder/api/lib/Cake/Test override_dh_auto_test: # do not run tests... -.PHONY: override_dh_strip override_dh_strip: - dh_strip --dbg-package=zoneminder-dbg + dh_strip --dbg-package=zoneminder-dbg + +%: + dh $@ --buildsystem=cmake --parallel diff --git a/distros/debian_cmake/changelog b/distros/debian_cmake/changelog deleted file mode 100644 index 0edaefce7..000000000 --- a/distros/debian_cmake/changelog +++ /dev/null @@ -1,29 +0,0 @@ -zoneminder (1.28.0-0.1) wheezy; urgency=low - - * Use CMake instead of Autotools to simplify - debian/rules and have less build-depends. - * Some lintian love in debian/{control,copyright} - and perl ZoneMinder modules. - * Don't purge database if we use a remote - MySQL server. - - -- Cosme Domínguez Díaz Sun, 09 Nov 2014 02:20:20 +0100 - -zoneminder (1.28.0-wheezy) wheezy; urgency=medium - - * Release - - -- Isaac Connor Fri, 17 Oct 2014 09:27:22 -0400 - -zoneminder (1.27.99+1-testing-SNAPSHOT2014072901) testing; urgency=medium - - * improve error messages - * Make zmupdate re-run the most recent patch so that people running the daily builds get their db updates - - -- Isaac Connor Tue, 29 Jul 2014 14:50:20 -0400 - -zoneminder (1.27.0+1-testing-v4ltomonitor-1) testing; urgency=high - - * Snapshot release - - - -- Isaac Connor Wed, 09 Jul 2014 21:35:29 -0400 diff --git a/distros/debian_cmake/control b/distros/debian_cmake/control deleted file mode 100644 index 01adf14df..000000000 --- a/distros/debian_cmake/control +++ /dev/null @@ -1,40 +0,0 @@ -Source: zoneminder -Section: net -Priority: optional -Maintainer: Isaac Connor -Build-Depends: debhelper (>= 9), cmake, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev | libmariadbclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev, libpcre3-dev, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libsys-mmap-perl, libav-tools, libnetpbm10-dev, libavdevice-dev, libdevice-serialport-perl, libarchive-zip-perl, libmime-lite-perl, libvlccore-dev, libvlc-dev, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libgcrypt11-dev, libpolkit-gobject-1-dev -Standards-Version: 3.9.4 - -Package: zoneminder -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2 | httpd, libapache2-mod-php5 | libapache2-mod-fcgid | php5-fpm, php5-mysqlnd | php5-mysql, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client | mysql-client, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, libav-tools, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, netpbm, libavdevice53, libjpeg8, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0 -Recommends: mysql-server | mariadb-server -Description: Video camera security and surveillance solution - ZoneMinder is intended for use in single or multi-camera video security - applications, including commercial or home CCTV, theft prevention and child - or family member or home monitoring and other care scenarios. It - supports capture, analysis, recording, and monitoring of video data coming - from one or more video or network cameras attached to a Linux system. - ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom - cameras using a variety of protocols. It is suitable for use as a home - video security system and for commercial or professional video security - and surveillance. It can also be integrated into a home automation system - via X.10 or other protocols. - -Package: zoneminder-dbg -Architecture: any -Priority: extra -Section: debug -Depends: zoneminder (= ${binary:Version}), ${misc:Depends} -Description: debugging syumbols for zoneminder. - ZoneMinder is a video camera security and surveillance solution. - ZoneMinder is intended for use in single or multi-camera video security - applications, including commercial or home CCTV, theft prevention and child - or family member or home monitoring and other care scenarios. It - supports capture, analysis, recording, and monitoring of video data coming - from one or more video or network cameras attached to a Linux system. - ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom - cameras using a variety of protocols. It is suitable for use as a home - video security system and for commercial or professional video security - and surveillance. It can also be integrated into a home automation system - via X.10 or other protocols. diff --git a/distros/debian_cmake/copyright b/distros/debian_cmake/copyright deleted file mode 100644 index 1c10c59db..000000000 --- a/distros/debian_cmake/copyright +++ /dev/null @@ -1,57 +0,0 @@ -Copyright: - -Copyright 2002 Philip Coombes - -License: - -This package 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 package 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 package; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -On Debian GNU/Linux systems, the text of the GPL can be found in -/usr/share/common-licenses/GPL. - -/usr/share/zoneminder/api/lib/Cake/*: -Copyright: - -Copyright (c) 2005-2013, Cake Software Foundation, Inc. - -License: - -CakePHP(tm) : The Rapid Development PHP Framework (http://cakephp.org) - -The MIT License - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - -Cake Software Foundation, Inc. -1785 E. Sahara Avenue, -Suite 490-204 -Las Vegas, Nevada 89104, -United States of America. diff --git a/distros/debian_cmake/install b/distros/debian_cmake/install deleted file mode 100644 index 9e8953409..000000000 --- a/distros/debian_cmake/install +++ /dev/null @@ -1,7 +0,0 @@ -usr/bin -usr/lib/cgi-bin -usr/share/man -usr/share/perl5/ZoneMinder -usr/share/perl5/ZoneMinder.pm -usr/share/zoneminder -etc/zm diff --git a/distros/debian_cmake/links b/distros/debian_cmake/links deleted file mode 100644 index 9715ee428..000000000 --- a/distros/debian_cmake/links +++ /dev/null @@ -1,4 +0,0 @@ -var/cache/zoneminder/events usr/share/zoneminder/events -var/cache/zoneminder/images usr/share/zoneminder/images -var/cache/zoneminder/temp usr/share/zoneminder/temp -usr/lib/cgi-bin usr/share/zoneminder/cgi-bin diff --git a/distros/debian_cmake/postinst b/distros/debian_cmake/postinst deleted file mode 100644 index d06f9c641..000000000 --- a/distros/debian_cmake/postinst +++ /dev/null @@ -1,53 +0,0 @@ -#! /bin/sh - -set -e - -if [ "$1" = "configure" ]; then - if [ -e "/etc/init.d/mysql" ]; then - # - # Get mysql started if it isn't - # - if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then - invoke-rc.d mysql start - fi - if $(/etc/init.d/mysql status >/dev/null 2>&1); then - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload - # test if database if already present... - if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then - cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf - echo 'grant lock tables, alter,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql - fi - - invoke-rc.d zoneminder stop || true - zmupdate.pl --nointeractive - - else - echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' - fi - else - echo 'mysql not found, assuming remote server.' - fi - chown www-data:www-data /var/log/zm - chown www-data:www-data /var/lib/zm/ - if [ -z "$2" ]; then - chown www-data:www-data -R /var/cache/zoneminder - fi -fi -# Ensure zoneminder is stopped... -if [ -x "/etc/init.d/zoneminder" ]; then - if invoke-rc.d zoneminder status ; then - invoke-rc.d zoneminder stop || exit $? - fi -fi - -if [ "$1" = "configure" ]; then - if [ -z "$2" ]; then - chown www-data:www-data /var/log/zm - chown www-data:www-data /var/lib/zm/ - chown www-data:www-data -R /var/cache/zoneminder - else - chown www-data:www-data /var/log/zm - zmupdate.pl - fi -fi -#DEBHELPER# diff --git a/distros/debian_cmake/postrm b/distros/debian_cmake/postrm deleted file mode 100644 index fde590981..000000000 --- a/distros/debian_cmake/postrm +++ /dev/null @@ -1,11 +0,0 @@ -#! /bin/sh -set -e - -if [ "$1" = "purge" ]; then - if [ -e "/etc/init.d/mysql" ]; then - echo 'delete from user where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql - echo 'delete from db where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm - fi -fi -#DEBHELPER# diff --git a/distros/debian_cmake/rules b/distros/debian_cmake/rules deleted file mode 100755 index 0bf0f3362..000000000 --- a/distros/debian_cmake/rules +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/make -f -# -*- makefile -*- -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -export CFLAGS = -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -pipe -export CXXFLAGS = -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -pipe -INSTDIR = debian/tmp - -override_dh_auto_configure: - dh_auto_configure -- \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_SKIP_RPATH=ON \ - -DCMAKE_VERBOSE_MAKEFILE=OFF \ - -DCMAKE_COLOR_MAKEFILE=ON \ - -DZM_RUNDIR=/var/run/zm \ - -DZM_SOCKDIR=/var/run/zm \ - -DZM_TMPDIR=/var/tmp/zm \ - -DZM_LOGDIR=/var/log/zm \ - -DZM_WEBDIR=/usr/share/zoneminder \ - -DZM_CONTENTDIR=/var/cache/zoneminder \ - -DZM_CGIDIR=/usr/lib/cgi-bin \ - -DZM_WEB_USER=www-data \ - -DZM_WEB_GROUP=www-data \ - -DZM_PERL_SUBPREFIX=/share/perl5 \ - -DCMAKE_INSTALL_SYSCONFDIR=etc/zm - -override_dh_auto_install: - dh_auto_install --buildsystem=cmake - install -D -m 0644 debian/apache.conf $(INSTDIR)/etc/zm/apache.conf - rm $(INSTDIR)/usr/share/zoneminder/api/lib/Cake/LICENSE.txt - rm $(INSTDIR)/usr/share/zoneminder/api/.gitignore - rm -r $(INSTDIR)/usr/share/zoneminder/api/lib/Cake/Test - -override_dh_auto_test: - # do not run tests... - -override_dh_strip: - dh_strip --dbg-package=zoneminder-dbg - -%: - dh $@ --buildsystem=cmake --parallel diff --git a/distros/debian_cmake/watch b/distros/debian_cmake/watch deleted file mode 100644 index 5a8a9c4d7..000000000 --- a/distros/debian_cmake/watch +++ /dev/null @@ -1,3 +0,0 @@ -version=3 -http://www.zoneminder.com/downloads.html \ - .*/ZoneMinder-(.*).tar.gz diff --git a/distros/fedora/CMakeLists.txt b/distros/fedora/CMakeLists.txt index 7409a2e39..e6fc72add 100644 --- a/distros/fedora/CMakeLists.txt +++ b/distros/fedora/CMakeLists.txt @@ -1,7 +1,16 @@ # CMakeLists.txt for the Fedora Target Distro. +# Display a message to show the Fedora build options are being processed. +message([STATUS] "Starting Fedora Build Options" ...) + +# Create the ZoneMinder Apache config file +configure_file(zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) +configure_file(zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY) +configure_file(zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY) +configure_file(zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY) + # Download jscalendar & move files into position -file(DOWNLOAD http://downloads.sourceforge.net/jscalendar/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip STATUS download_jsc) +file(DOWNLOAD http://iweb.dl.sourceforge.net/project/jscalendar/jscalendar/1.0/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip STATUS download_jsc) if(download_jsc EQUAL 0) message(STATUS "Jscalander successfully downloaded. Installing...") execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ERROR_VARIABLE unzip_jsc) @@ -25,14 +34,15 @@ install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_P install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/events \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/events\")") install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/images \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/images\")") install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/temp\")") +install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/api/app/tmp\")") # Fedora requires cambozola as a separate package so just link to it install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")") -# Install auxillary files required to run zoneminder on Fedora +# Install auxiliary files required to run zoneminder on Fedora install(FILES zoneminder.conf DESTINATION /etc/httpd/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) -install(FILES zoneminder.tmpfiles DESTINATION /etc/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(FILES ../../misc/zoneminder-tmpfiles.conf DESTINATION /etc/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) diff --git a/distros/fedora/README.Fedora b/distros/fedora/README.Fedora index 159e5345b..025699933 100644 --- a/distros/fedora/README.Fedora +++ b/distros/fedora/README.Fedora @@ -1,113 +1,150 @@ +What's New +========== + +1. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to + "/cgi-bin-zm/zms". This has been to done to avoid this bug: + https://bugzilla.redhat.com/show_bug.cgi?id=973067 + + IMPORTANT: You must manually inspect the value for PATH_ZMS under Options + and verify it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result + in a broken system. You have been warned. + +2. Due to the active state of the ZoneMinder project, we now recommend granting + ALL permission to the ZoneMinder mysql account. This change must be done + manually before ZoneMinder will run. See the installation steps below. + +3. This package uses the HTTPS protocol by default to access the web portal. + Requests using HTTP will auto-redirect to HTTPS. See README.https for + more information. + +4. This package ships with the new ZoneMinder API enabled. + New installs ============ -1. Unless you are already using the MySQL server or you are running it - remotely you will need to ensure that the server is installed and secured: +1. This package supports either community-mysql-server or mariadb-server with + mariadb being the preferred choice. Unless you are already using MariaDB or + Mysql server, you need to ensure that the server is configured to start + during boot and properly secured by running: - sudo yum install community-mysql-server - - sudo systemctl enable mysqld - - sudo systemctl start mysqld.service - + sudo yum install mariadb-server + sudo systemctl enable mariadb + sudo systemctl start mariadb.service mysql_secure_installation - NOTE: The Fedora team currently recommends mysql-community over mariadb - -2. Using the password for the root account set during the previous step, you - will need to create the ZoneMinder database, assuming your database server - is local: +2. Assuming the database is local and using the password for the root account + set during the previous step, you will need to create the ZoneMinder + database and configure a database account for ZoneMinder to use: mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql + mysql -uroot -p -e "grant all on zm.* to \ + 'zmuser'@localhost identified by 'zmpass';" mysqladmin -uroot -p reload -3. The database needs a user. One is not created by default because this would - introduce an obvious security issue. The following should set this up: + The database account credentials, zmuser/zmpass, are arbitrary. Set them to + anything that suits your environment. - mysql -u root -p - grant select,insert,update,delete,lock tables,alter on zm.* to - 'zmuser'@localhost identified by 'zmpass'; +3. If you have chosen to change the zoneminder database account credentials to + something other than zmuser/zmpass, you must now edit /etc/zm/zm.conf. + Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous + step. - Obviously, change at least zmpass to an actual, secure password or - passphrase. You can change zmuser as well if you like. + This version of zoneminder no longer requires you to make a similar change + to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php + This now happens dynamically. Do *not* make any changes to this file. -4. Edit /etc/zm.conf and, at the bottom, change ZM_DB_PASS and perhaps - ZM_DB_USER to match. - -5. Edit /etc/php.ini, uncomment the date.timezone line, and add your local - timezone. For whatever reason, PHP will complain loudly if this is not set, - or if it is set incorrectly, and these complaints will show up in the - zoneminder logging system as errors. +4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local + timezone. PHP will complain loudly if this is not set, or if it is set + incorrectly, and these complaints will show up in the zoneminder logging + system as errors. If you are not sure of the proper timezone specification to use, look at http://php.net/date.timezone -6. This package probably does not work with SELinux enabled at the moment. It - may be necessary to disable SELinux for httpd, or even completely for - ZoneMinder to function. This will be addressed in a later release. Run +5. Disable SELinux + + We currently do not have the resources to create and maintain an accurate + SELinux policy for ZoneMinder on Fedora. We will gladly accept pull + reqeusts from anyone who wishes to do the work. In the meantime, SELinux + will need to be disabled or put into permissive mode. + + To immediately disbale SELinux for the current seesion, issue the following + from the command line: sudo setenforce 0 - for testing, and edit /etc/sysconfig/selinux to disable it at boot time. + To permanently disable SELinux, edit /etc/selinux/config and change the + SELINUX line from "enforcing" to "disabled". This change will take + effect after a reboot. -7. IMPORTANT: Edit /etc/httpd/conf.d/zoneminder.conf and/or - /etc/httpd/conf/httpd.conf. +6. Install mod_ssl or configure /etc/httpd/conf.d/zoneminder.conf to meet your + needs. This package comes preconfigured for HTTPS using the default self + signed certificate on your system. The recommended way to complete this step + is to simply install mod_ssl: - The httpd.conf file included with this version of Fedora processes the conf.d - folder after the default ScriptAlias directive in the httpd.conf file. - Previously, the conf.d folder was processed before the default ScriptAlias - directive. This causes a ScriptAlias overlap and breaks Zoneminder's - streaming abilities. + sudo yum install mod_ssl - Reference: http://httpd.apache.org/docs/2.4/mod/mod_alias.html#order - Bug Report: https://bugzilla.redhat.com/show_bug.cgi?id=973067 + If this does not meet your needs, then read README.https to + learn about alternatives. When in doubt, install mod_ssl. - WORKAROUND #1 - If you are running zoneminder on a dedicated server then the simplest - solution may be to simply comment out the line in httpd.conf that reads: +7. Now start the web server: - ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" + sudo systemctl enable httpd + sudo systemctl start httpd - WORKAROUND #2 - If you need both the default cgi-bin folder & the zoneminder cgi-bin folder - then a solution might be to move the following line before the default - ScriptAlias directive in the httpd.conf file: +8. Now start zoneminder: - IncludeOptional conf.d/*.conf - -8. Now start the web server: - - sudo systemctl enable httpd.service - sudo systemctl start httpd.service - -9. Now start zoneminder: - - sudo systemctl enable zoneminder.service - sudo systemctl start zoneminder.service + sudo systemctl enable zoneminder + sudo systemctl start zoneminder Upgrades ======== -1. Update /etc/zm.conf. Check for any new settings and update the version - information. Comparing /etc/zm.conf and /etc/zm.conf.rpmnew should help to - do this. +1. Verify /etc/zm/zm.conf. -2. Add additional permissions to the zmuser account: + If zm.conf was manually edited before running the upgrade, the installation + may not overwrite it. In this case, it will create the file + /etc/zm/zm.conf.rpmnew. - mysql -u root -p - grant lock tables,alter on zm.* to - 'zmuser'@localhost identified by 'zmpass'; + For example, this will happen if you are using database account credentials + other than zmuser/zmpass. - Since this is an upgrade, the assumption is that the zmuser account already - has select, insert, update, and delete permission. + Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf + contains any new config settings that may be in zm.conf.rpmnew. + + This version of zoneminder no longer requires you to make a similar change + to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php + This now happens dynamically. Do *not* make any changes to this file. + +2. Verify permissions of the zmuser account. + + Over time, the database account permissions required for normal operation + have increased. Verify the zmuser database account has been granted all + permission to the ZoneMinder database: + + mysql -uroot -p -e "show grants for zmuser@localhost;" + + See step 2 of the Installation section to add missing permissions. -3. You will need to upgrade the ZoneMinder database as described in the - manual. Only if the previous step was succesful, may you run zmupdate like - so: +3. Verify the ZoneMinder Apache configuration file in the folder + /etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there + may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file + exists, inspect it and merge anything new in that file with zoneminder.conf. + Verify the SSL REquirements meet your needs. Read README.https if necessary. + +4. Upgrade the database before starting ZoneMinder. + + Most upgrades can be performed by executing the following command: - sudo zmupdate.pl --version= - - - If unsure then run it this way: + sudo zmupdate.pl + + Recent versions of ZoneMinder don't require any parameters added to the + zmupdate command. However, if ZoneMinder complains, you may need to call + zmupdate in the following manner: sudo zmupdate.pl --user=root --pass= --version= + +5. Now start zoneminder: + + sudo systemctl start zoneminder + diff --git a/distros/fedora/README.https b/distros/fedora/README.https new file mode 120000 index 000000000..18f7407c1 --- /dev/null +++ b/distros/fedora/README.https @@ -0,0 +1 @@ +../redhat/README.https \ No newline at end of file diff --git a/distros/fedora/archive/zoneminder.cmake.f19.spec b/distros/fedora/archive/zoneminder.cmake.f19.spec index a7aee3265..0af88f41f 100644 --- a/distros/fedora/archive/zoneminder.cmake.f19.spec +++ b/distros/fedora/archive/zoneminder.cmake.f19.spec @@ -72,7 +72,6 @@ too much degradation of performance. %build %cmake \ -DZM_TARGET_DISTRO="f19" \ - -DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` \ %{?_without_ffmpeg:-DZM_NO_FFMPEG=ON} \ %{?_without_x10:-DZM_NO_X10=ON} \ . @@ -141,7 +140,6 @@ fi # zmfix removed from zoneminder 1.26.6 #%attr(4755,root,root) %{_bindir}/zmfix %{_bindir}/zmpkg.pl -%{_bindir}/zmstreamer %{_bindir}/zmtrack.pl %{_bindir}/zmtrigger.pl %{_bindir}/zmu diff --git a/distros/fedora/zoneminder.conf b/distros/fedora/archive/zoneminder.conf similarity index 97% rename from distros/fedora/zoneminder.conf rename to distros/fedora/archive/zoneminder.conf index 597ea2bd2..66b3dc146 100644 --- a/distros/fedora/zoneminder.conf +++ b/distros/fedora/archive/zoneminder.conf @@ -32,7 +32,7 @@ Alias /zm "/usr/share/zoneminder/www" ScriptAlias /cgi-bin/zm "/usr/libexec/zoneminder/cgi-bin" AllowOverride All - Options ExecCGI + Options +ExecCGI +FollowSymLinks # Apache 2.4 Require all granted diff --git a/distros/fedora/archive/zoneminder.f19.spec b/distros/fedora/archive/zoneminder.f19.spec index 11a8ba249..d1be12aa2 100644 --- a/distros/fedora/archive/zoneminder.f19.spec +++ b/distros/fedora/archive/zoneminder.f19.spec @@ -232,7 +232,6 @@ fi # zmfix removed from zoneminder 1.26.6 #%attr(4755,root,root) %{_bindir}/zmfix %{_bindir}/zmpkg.pl -%{_bindir}/zmstreamer %{_bindir}/zmtrack.pl %{_bindir}/zmtrigger.pl %{_bindir}/zmu diff --git a/distros/fedora/zoneminder.cmake.f20.spec b/distros/fedora/archive/zoneminder.f20.spec similarity index 95% rename from distros/fedora/zoneminder.cmake.f20.spec rename to distros/fedora/archive/zoneminder.f20.spec index 5eea7150c..513a9cfee 100644 --- a/distros/fedora/zoneminder.cmake.f20.spec +++ b/distros/fedora/archive/zoneminder.f20.spec @@ -10,7 +10,7 @@ %define _without_x10 1 Name: zoneminder -Version: 1.28.0 +Version: 1.28.1 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons @@ -22,25 +22,23 @@ URL: http://www.zoneminder.com/ #Source: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz Source: ZoneMinder-%{version}.tar.gz -Patch1: zoneminder-1.28.0-defaults.patch - BuildRequires: cmake gnutls-devel systemd-units bzip2-devel BuildRequires: community-mysql-devel pcre-devel libjpeg-turbo-devel -BuildRequires: perl(Archive::Tar) perl(Archive::Zip) +BuildRequires: perl(Archive::Tar) perl(Archive::Zip) perl-podlators BuildRequires: perl(Date::Manip) perl(DBD::mysql) BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent) BuildRequires: perl(MIME::Entity) perl(MIME::Lite) BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap) BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign) BuildRequires: perl(Expect) perl(Sys::Syslog) -BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel +BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel libv4l-devel %{!?_without_ffmpeg:BuildRequires: ffmpeg-devel} %{!?_without_x10:BuildRequires: perl(X10::ActiveHome) perl(Astro::SunTime)} # cmake needs the following installed at build time due to the way it auto-detects certain parameters BuildRequires: httpd polkit-devel %{!?_without_ffmpeg:BuildRequires: ffmpeg} -Requires: httpd php php-mysql cambozola polkit +Requires: httpd php php-gd php-mysql cambozola polkit net-tools psmisc Requires: libjpeg-turbo vlc-core libcurl Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip) @@ -66,12 +64,18 @@ too much degradation of performance. %prep %setup -q -n ZoneMinder-%{version} -%patch1 -p0 -b .defaults +# Change the following default values +./utils/zmeditconfigdata.sh ZM_PATH_ZMS /cgi-bin/zm/nph-zms +./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes +./utils/zmeditconfigdata.sh ZM_PATH_SWAP /dev/shm +./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR /var/spool/zoneminder-upload +./utils/zmeditconfigdata.sh ZM_OPT_CONTROL yes +./utils/zmeditconfigdata.sh ZM_CHECK_FOR_UPDATES no +./utils/zmeditconfigdata.sh ZM_DYN_SHOW_DONATE_REMINDER no %build %cmake \ -DZM_TARGET_DISTRO="f20" \ - -DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` \ %{?_without_ffmpeg:-DZM_NO_FFMPEG=ON} \ %{?_without_x10:-DZM_NO_X10=ON} \ . @@ -138,7 +142,6 @@ fi %{_bindir}/zmf %{_bindir}/zmfilter.pl %{_bindir}/zmpkg.pl -%{_bindir}/zmstreamer %{_bindir}/zmtrack.pl %{_bindir}/zmtrigger.pl %{_bindir}/zmu diff --git a/distros/fedora/archive/zoneminder.f21.spec b/distros/fedora/archive/zoneminder.f21.spec new file mode 100644 index 000000000..35662bf6c --- /dev/null +++ b/distros/fedora/archive/zoneminder.f21.spec @@ -0,0 +1,396 @@ +%define zmuid $(id -un) +%define zmgid $(id -gn) +%define zmuid_final apache +%define zmgid_final apache + +%global _hardened_build 1 + +### Delete the lines below to build with ffmpeg and/or x10 +%define _without_ffmpeg 1 +%define _without_x10 1 + +Name: zoneminder +Version: 1.28.1 +Release: 1%{?dist} +Summary: A camera monitoring and analysis tool +Group: System Environment/Daemons +# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/ +# Mootools is inder the MIT license: http://mootools.net/ +License: GPLv2+ and LGPLv2+ and MIT +URL: http://www.zoneminder.com/ + +#Source: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz +Source: ZoneMinder-%{version}.tar.gz + +BuildRequires: cmake gnutls-devel systemd-units bzip2-devel +BuildRequires: community-mysql-devel pcre-devel libjpeg-turbo-devel +BuildRequires: perl(Archive::Tar) perl(Archive::Zip) perl-podlators +BuildRequires: perl(Date::Manip) perl(DBD::mysql) +BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent) +BuildRequires: perl(MIME::Entity) perl(MIME::Lite) +BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap) +BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign) +BuildRequires: perl(Expect) perl(Sys::Syslog) +BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel libv4l-devel +%{!?_without_ffmpeg:BuildRequires: ffmpeg-devel} +%{!?_without_x10:BuildRequires: perl(X10::ActiveHome) perl(Astro::SunTime)} +# cmake needs the following installed at build time due to the way it auto-detects certain parameters +BuildRequires: httpd polkit-devel +%{!?_without_ffmpeg:BuildRequires: ffmpeg} + +Requires: httpd php php-gd php-mysql cambozola polkit net-tools psmisc +Requires: libjpeg-turbo vlc-core libcurl +Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) +Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip) +Requires: perl(MIME::Entity) perl(MIME::Lite) perl(Net::SMTP) perl(Net::FTP) +Requires: perl(LWP::Protocol::https) +%{!?_without_ffmpeg:Requires: ffmpeg} + +Requires(post): systemd-units systemd-sysv +Requires(post): /usr/bin/gpasswd +Requires(post): /usr/bin/less +Requires(preun): systemd-units +Requires(postun): systemd-units + +%description +ZoneMinder is a set of applications which is intended to provide a complete +solution allowing you to capture, analyse, record and monitor any cameras you +have attached to a Linux based machine. It is designed to run on kernels which +support the Video For Linux (V4L) interface and has been tested with cameras +attached to BTTV cards, various USB cameras and IP network cameras. It is +designed to support as many cameras as you can attach to your computer without +too much degradation of performance. + +%prep +%setup -q -n ZoneMinder-%{version} + +# Change the following default values +./utils/zmeditconfigdata.sh ZM_PATH_ZMS /cgi-bin/zm/nph-zms +./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes +./utils/zmeditconfigdata.sh ZM_PATH_SWAP /dev/shm +./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR /var/spool/zoneminder-upload +./utils/zmeditconfigdata.sh ZM_OPT_CONTROL yes +./utils/zmeditconfigdata.sh ZM_CHECK_FOR_UPDATES no +./utils/zmeditconfigdata.sh ZM_DYN_SHOW_DONATE_REMINDER no + +%build +%cmake \ + -DZM_TARGET_DISTRO="f21" \ +%{?_without_ffmpeg:-DZM_NO_FFMPEG=ON} \ +%{?_without_x10:-DZM_NO_X10=ON} \ + . + +make %{?_smp_mflags} + +%install +export DESTDIR=%{buildroot} +make install + +%post +if [ $1 -eq 1 ] ; then + # Initial installation + /bin/systemctl daemon-reload >/dev/null 2>&1 || : +fi + +# Allow zoneminder access to local video sources, serial ports, and x10 +/usr/bin/gpasswd -a %{zmuid_final} video +/usr/bin/gpasswd -a %{zmuid_final} dialout + +# Display the README for post installation instructions +/usr/bin/less %{_docdir}/%{name}/README.Fedora + +%preun +if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + /bin/systemctl --no-reload disable zoneminder.service > /dev/null 2>&1 || : + /bin/systemctl stop zoneminder.service > /dev/null 2>&1 || : +fi + +%postun +/bin/systemctl daemon-reload >/dev/null 2>&1 || : +if [ $1 -ge 1 ] ; then + # Package upgrade, not uninstall + /bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : +fi + +%triggerun -- zoneminder < 1.25.0-4 +# Save the current service runlevel info +# User must manually run systemd-sysv-convert --apply zoneminder +# to migrate them to systemd targets +/usr/bin/systemd-sysv-convert --save zoneminder >/dev/null 2>&1 ||: + +# Run these because the SysV package being removed won't do them +/sbin/chkconfig --del zoneminder >/dev/null 2>&1 || : +/bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : + + +%files +%defattr(-,root,root,-) +%doc AUTHORS COPYING README.md distros/fedora/README.Fedora distros/fedora/jscalendar-doc +%config %attr(640,root,%{zmgid_final}) /etc/zm/zm.conf +%config(noreplace) %attr(644,root,root) /etc/httpd/conf.d/zoneminder.conf +%config(noreplace) /etc/tmpfiles.d/zoneminder.conf +%config(noreplace) /etc/logrotate.d/zoneminder + +%{_unitdir}/zoneminder.service + +%{_bindir}/zma +%{_bindir}/zmaudit.pl +%{_bindir}/zmc +%{_bindir}/zmcontrol.pl +%{_bindir}/zmdc.pl +%{_bindir}/zmf +%{_bindir}/zmfilter.pl +%{_bindir}/zmpkg.pl +%{_bindir}/zmtrack.pl +%{_bindir}/zmtrigger.pl +%{_bindir}/zmu +%{_bindir}/zmupdate.pl +%{_bindir}/zmvideo.pl +%{_bindir}/zmwatch.pl +%{_bindir}/zmcamtool.pl +%{_bindir}/zmsystemctl.pl +%{!?_without_x10:%{_bindir}/zmx10.pl} + +%{perl_vendorlib}/ZoneMinder* +%{_mandir}/man*/* +%dir %{_libexecdir}/zoneminder +%{_libexecdir}/zoneminder/cgi-bin +%dir %{_datadir}/zoneminder +%{_datadir}/zoneminder/db +%{_datadir}/zoneminder/www + +%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy +%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules + +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/events +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/images +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/sock +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/swap +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/temp +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/log/zoneminder +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/spool/zoneminder-upload +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /run/zoneminder + + +%changelog +* Sat Feb 14 2015 Andrew Bauer - 1.28.1 +- Bump version for 1.28.1 release on Fedora 21. + +* Sun Oct 5 2014 Andrew Bauer - 1.28.0 +- Bump version for 1.28.0 release. + +* Fri Mar 14 2014 Andrew Bauer - 1.27 +- Tweak build requirements for cmake + +* Sat Feb 01 2014 Andrew Bauer - 1.27 +- Add zmcamtool.pl. Bump version for 1.27 release. + +* Mon Dec 16 2013 Andrew Bauer - 1.26.5 +- This is a bug fixe release +- RTSP fixes, cmake enhancements, couple other misc fixes + +* Mon Oct 07 2013 Andrew Bauer - 1.26.4 +- Initial cmake build. + +* Sat Oct 05 2013 Andrew Bauer - 1.26.4 +- Fedora specific path changes have been moved to zoneminder-1.26.0-defaults.patch +- All files are now part of the zoneminder source tree. Update specfile accordingly. + +* Sat Sep 21 2013 Andrew Bauer - 1.26.3 +- Initial rebuild for ZoneMinder 1.26.3 release. + +* Fri Feb 15 2013 Fedora Release Engineering - 1.25.0-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Mon Jan 21 2013 Adam Tkac - 1.25.0-12 +- rebuild due to "jpeg8-ABI" feature drop + +* Mon Jan 7 2013 Remi Collet - 1.25.0-11 +- fix configuration file for httpd 2.4, #871502 + +* Fri Dec 21 2012 Adam Tkac - 1.25.0-10 +- rebuild against new libjpeg + +* Thu Aug 09 2012 Jason L Tibbitts III - 1.25.0-9 +- Add patch to work around v4l2 api breakage in 3.5 kernel. + +* Sun Jul 22 2012 Fedora Release Engineering - 1.25.0-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sat Jun 23 2012 Petr Pisar - 1.25.0-7 +- Perl 5.16 rebuild + +* Wed Mar 21 2012 Jason L Tibbitts III - 1.25.0-6 +- Fix stupid thinko in sql modifications. + +* Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-5 +- Clean up macro usage. + +* Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-4 +- Convert to systemd. +- Add tmpfiles.d configuration since the initscript isn't around to create + /run/zoneminder. +- Remove some pointless executable permissions. +- Add logrotate file. + +* Wed Feb 22 2012 Jason L Tibbitts III - 1.25.0-3 +- Update README.Fedora to reference systemctl and mention timezone info in + php.ini. +- Add proper default for EYEZM_LOG_TO_FILE. + + +* Thu Feb 09 2012 Jason L Tibbitts III - 1.25.0-2 +- Rebuild for new pcre. + +* Thu Jan 19 2012 Jason L Tibbitts III - 1.25.0-1 +- Update to 1.25.0 +- Fix gcc4.7 build problems. +- Drop gcc4.4 build fixes; for whatever reason they now break the build. +- Clean up old patches. +- Force setting of ZM_TMPDIR and ZM_RUNDIR. + +* Sat Jan 14 2012 Fedora Release Engineering - 1.24.4-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-3 +- Re-add the dist-tag that somehow got lost. + +* Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-2 +- Add patch for bug 711780 - fix syntax issue in Mapped.pm. +- Undo that patch, and undo another which was the cause of the whole mess. +- Fix up other patches so ZM_PATH_BUILD is both defined and useful. +- Make sure database creation mods actually take. +- Update Fedora-specific docs with some additional info. +- Use bundled mootools (javascript, so no guideline violation). +- Update download location. +- Update the gcrypt patch to actually work. +- Upstream changed the tarball without changing the version to patch a + vulnerability, so redownload. + +* Sun Aug 14 2011 Jason L Tibbitts III - 1.24.4-1 +- Initial attempt to upgrade to 1.24.4. +- Add patch from BZ 460310 to build against libgcrypt instead of requiring the + gnutls openssl libs. + +* Thu Jul 21 2011 Petr Sabata - 1.24.3-7.20110324svn3310 +- Perl mass rebuild + +* Wed Jul 20 2011 Petr Sabata - 1.24.3-6.20110324svn3310 +- Perl mass rebuild + +* Mon May 09 2011 Jason L Tibbitts III - 1.24.3-5.20110324svn3310 +- Bump for gnutls update. + +* Thu Mar 24 2011 Jason L Tibbitts III - 1.24.3-4.20110324svn3310 +- Update to latest 1.24.3 subversion. Turns out that what upstream was calling + 1.24.3 is really just an occasionally updated devel snapshot. +- Rebase various patches. + +* Wed Mar 23 2011 Dan Horák - 1.24.3-3 +- rebuilt for mysql 5.5.10 (soname bump in libmysqlclient) + +* Tue Feb 08 2011 Fedora Release Engineering - 1.24.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Jan 25 2011 Jason L Tibbitts III - 1.24.3-1 +- Update to latest upstream version. +- Rebase patches. +- Initial incomplete attempt to disable v4l1 support. + +* Fri Jan 21 2011 Jason L Tibbitts III - 1.24.2-6 +- Unbundle cambozola; instead link to the separately pacakged copy. +- Remove BuildRoot:, %%clean and buildroot cleaning in %%install. +- Git rid of mixed space/tab usage by removing all tabs. +- Remove unnecessary Conflicts: line. +- Attempt to force short_open_tag on for the code directories. +- Move default location of sockets, swaps, logfiles and some temporary files to + make more sense and allow things to work better with a future selinux policy. +- Fix errors in README.Fedora. + +* Wed Jun 02 2010 Marcela Maslanova - 1.24.2-5 +- Mass rebuild with perl-5.12.0 + +* Fri Dec 4 2009 Stepan Kasal - 1.24.2-4 +- rebuild against perl 5.10.1 +- use Perl vendorarch and archlib variables correctly + +* Mon Jul 27 2009 Fedora Release Engineering - 1.24.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-2 +- Bump release since 1.24.2-1 was mistakenly tagged a few months ago. + +* Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-1 +- Initial update to 1.24.2. +- Rebase patches. +- Update mootools download location. +- Update to mootools 1.2.3. +- Add additional dependencies for some optional features. + +* Sat Apr 11 2009 Martin Ebourne - 1.24.1-3 +- Remove unused Sys::Mmap perl dependency RPM is finding + +* Sat Apr 11 2009 Martin Ebourne - 1.24.1-2 +- Update gcc44 patch to disable -frepo, seems to be broken with gcc44 +- Added noffmpeg patch to make building outside mock easier + +* Sat Mar 21 2009 Martin Ebourne - 1.24.1-1 +- Patch for gcc 4.4 compilation errors +- Upgrade to 1.24.1 + +* Wed Feb 25 2009 Fedora Release Engineering - 1.23.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Sat Jan 24 2009 Caolán McNamara - 1.23.3-3 +- rebuild for dependencies + +* Mon Dec 15 2008 Martin Ebourne - 1.23.3-2 +- Fix permissions on zm.conf + +* Fri Jul 11 2008 Jason L Tibbitts III - 1.23.3-1 +- Initial attempt at packaging 1.23. + +* Tue Jul 1 2008 Martin Ebourne - 1.22.3-15 +- Add perl module compat dependency, bz #453590 + +* Tue May 6 2008 Martin Ebourne - 1.22.3-14 +- Remove default runlevel, bz #441315 + +* Mon Apr 28 2008 Jason L Tibbitts III - 1.22.3-13 +- Backport patch for CVE-2008-1381 from 1.23.3 to 1.22.3. + +* Tue Feb 19 2008 Fedora Release Engineering - 1.22.3-12 +- Autorebuild for GCC 4.3 + +* Thu Jan 3 2008 Martin Ebourne - 1.22.3-11 +- Fix compilation on gcc 4.3 + +* Thu Dec 6 2007 Martin Ebourne - 1.22.3-10 +- Rebuild for new openssl + +* Thu Aug 2 2007 Martin Ebourne - 1.22.3-8 +- Fix licence tag + +* Thu Jul 12 2007 Martin Ebourne - 1.22.3-7 +- Fixes from testing by Jitz including missing dependencies and database creation + +* Sat Jun 30 2007 Martin Ebourne - 1.22.3-6 +- Disable crashtrace on ppc + +* Sat Jun 30 2007 Martin Ebourne - 1.22.3-5 +- Fix uid for directories in /var/lib/zoneminder + +* Tue Jun 26 2007 Martin Ebourne - 1.22.3-4 +- Added perl Archive::Tar dependency +- Disabled web interface due to lack of access control on the event images + +* Sun Jun 10 2007 Martin Ebourne - 1.22.3-3 +- Changes recommended in review by Jason Tibbitts + +* Mon Apr 2 2007 Martin Ebourne - 1.22.3-2 +- Standardised on package name of zoneminder + +* Thu Dec 28 2006 Martin Ebourne - 1.22.3-1 +- First version. Uses some parts from zm-1.20.1 by Corey DeLasaux and Serg Oskin diff --git a/distros/fedora/zoneminder.logrotate b/distros/fedora/archive/zoneminder.logrotate similarity index 100% rename from distros/fedora/zoneminder.logrotate rename to distros/fedora/archive/zoneminder.logrotate diff --git a/distros/fedora/zoneminder.service b/distros/fedora/archive/zoneminder.service similarity index 100% rename from distros/fedora/zoneminder.service rename to distros/fedora/archive/zoneminder.service diff --git a/distros/fedora/zoneminder.tmpfiles b/distros/fedora/archive/zoneminder.tmpfiles similarity index 100% rename from distros/fedora/zoneminder.tmpfiles rename to distros/fedora/archive/zoneminder.tmpfiles diff --git a/distros/fedora/redalert.wav b/distros/fedora/redalert.wav deleted file mode 100644 index 8335ab23e..000000000 --- a/distros/fedora/redalert.wav +++ /dev/null @@ -1 +0,0 @@ -../redhat/redalert.wav diff --git a/distros/fedora/redalert.wav b/distros/fedora/redalert.wav new file mode 120000 index 000000000..eec3dce64 --- /dev/null +++ b/distros/fedora/redalert.wav @@ -0,0 +1 @@ +../redhat/redalert.wav \ No newline at end of file diff --git a/distros/fedora/zoneminder.conf.in b/distros/fedora/zoneminder.conf.in new file mode 120000 index 000000000..fd0098cf5 --- /dev/null +++ b/distros/fedora/zoneminder.conf.in @@ -0,0 +1 @@ +../redhat/zoneminder.el7.conf.in \ No newline at end of file diff --git a/distros/fedora/zoneminder.f22.spec b/distros/fedora/zoneminder.f22.spec new file mode 120000 index 000000000..808e7cb9f --- /dev/null +++ b/distros/fedora/zoneminder.f22.spec @@ -0,0 +1 @@ +zoneminder.f23.spec \ No newline at end of file diff --git a/distros/fedora/zoneminder.f23.spec b/distros/fedora/zoneminder.f23.spec new file mode 100644 index 000000000..5335b90cc --- /dev/null +++ b/distros/fedora/zoneminder.f23.spec @@ -0,0 +1,429 @@ +%define zmuid $(id -un) +%define zmgid $(id -gn) +%define zmuid_final apache +%define zmgid_final apache + +%global _hardened_build 1 + +### Delete the lines below to build with ffmpeg and/or x10 +%define _without_ffmpeg 1 +%define _without_x10 1 + +Name: zoneminder +Version: 1.30.0 +Release: 1%{?dist} +Summary: A camera monitoring and analysis tool +Group: System Environment/Daemons +# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/ +# Mootools is inder the MIT license: http://mootools.net/ +License: GPLv2+ and LGPLv2+ and MIT +URL: http://www.zoneminder.com/ + +#Source: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz +Source: ZoneMinder-%{version}.tar.gz + +BuildRequires: cmake gnutls-devel systemd-units bzip2-devel +BuildRequires: mariadb-devel pcre-devel libjpeg-turbo-devel +BuildRequires: perl(Archive::Tar) perl(Archive::Zip) perl-podlators +BuildRequires: perl(Date::Manip) perl(DBD::mysql) +BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent) +BuildRequires: perl(MIME::Entity) perl(MIME::Lite) +BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap) +BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign) +BuildRequires: perl(Expect) perl(Sys::Syslog) +BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel libv4l-devel +%{!?_without_ffmpeg:BuildRequires: ffmpeg-devel} +%{!?_without_x10:BuildRequires: perl(X10::ActiveHome) perl(Astro::SunTime)} +# cmake needs the following installed at build time due to the way it auto-detects certain parameters +BuildRequires: httpd polkit-devel +%{!?_without_ffmpeg:BuildRequires: ffmpeg} + +Requires: httpd php php-gd php-mysql cambozola polkit net-tools psmisc +Requires: libjpeg-turbo vlc-core libcurl +Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) +Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip) +Requires: perl(MIME::Entity) perl(MIME::Lite) perl(Net::SMTP) perl(Net::FTP) +Requires: perl(LWP::Protocol::https) +%{!?_without_ffmpeg:Requires: ffmpeg} + +Requires(post): systemd-units systemd-sysv +Requires(post): /usr/bin/gpasswd +Requires(post): /usr/bin/less +Requires(preun): systemd-units +Requires(postun): systemd-units + +%description +ZoneMinder is a set of applications which is intended to provide a complete +solution allowing you to capture, analyse, record and monitor any cameras you +have attached to a Linux based machine. It is designed to run on kernels which +support the Video For Linux (V4L) interface and has been tested with cameras +attached to BTTV cards, various USB cameras and IP network cameras. It is +designed to support as many cameras as you can attach to your computer without +too much degradation of performance. + +%prep +%setup -q -n ZoneMinder-%{version} + +# Change the following default values +./utils/zmeditconfigdata.sh ZM_PATH_ZMS /cgi-bin-zm/nph-zms +./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes +./utils/zmeditconfigdata.sh ZM_PATH_SWAP /dev/shm +./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR /var/spool/zoneminder-upload +./utils/zmeditconfigdata.sh ZM_OPT_CONTROL yes +./utils/zmeditconfigdata.sh ZM_CHECK_FOR_UPDATES no +./utils/zmeditconfigdata.sh ZM_DYN_SHOW_DONATE_REMINDER no +./utils/zmeditconfigdata.sh ZM_OPT_FAST_DELETE no + +%build +%cmake \ + -DZM_TARGET_DISTRO="f23" \ +%{?_without_ffmpeg:-DZM_NO_FFMPEG=ON} \ +%{?_without_x10:-DZM_NO_X10=ON} \ + . + +make %{?_smp_mflags} + +%install +export DESTDIR=%{buildroot} +make install + +%post + +# Add any new PTZ control configurations to the database (will not overwrite) +%{_bindir}/zmcamtool.pl --import >/dev/null 2>&1 || : + +if [ $1 -eq 1 ] ; then + # Initial installation + /bin/systemctl daemon-reload >/dev/null 2>&1 || : +fi + +# Allow zoneminder access to local video sources, serial ports, and x10 +/usr/bin/gpasswd -a %{zmuid_final} video +/usr/bin/gpasswd -a %{zmuid_final} dialout + +# Upgrade from a previous version of zoneminder +if [ $1 -eq 2 ] ; then + + # Add any new PTZ control configurations to the database (will not overwrite) + %{_bindir}/zmcamtool.pl --import >/dev/null 2>&1 || : + + # Freshen the database + %{_bindir}/zmupdate.pl -f >/dev/null 2>&1 || : + + # We can't run this automatically when new sql account permissions need to + # be manually added first + # Run zmupdate non-interactively + #/usr/bin/zmupdate.pl --nointeractive +fi + +# Warn the end user to read the README file +echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, read README.Fedora to finish the\ninstallation or upgrade!\n" +echo -e "\nThe README file is located here: %{_docdir}/%{name}\n" + +%preun +if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + /bin/systemctl --no-reload disable zoneminder.service > /dev/null 2>&1 || : + /bin/systemctl stop zoneminder.service > /dev/null 2>&1 || : +fi + +%postun +/bin/systemctl daemon-reload >/dev/null 2>&1 || : +if [ $1 -ge 1 ] ; then + # Package upgrade, not uninstall + /bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : +fi + +%triggerun -- zoneminder < 1.25.0-4 +# Save the current service runlevel info +# User must manually run systemd-sysv-convert --apply zoneminder +# to migrate them to systemd targets +/usr/bin/systemd-sysv-convert --save zoneminder >/dev/null 2>&1 ||: + +# Run these because the SysV package being removed won't do them +/sbin/chkconfig --del zoneminder >/dev/null 2>&1 || : +/bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : + + +%files +%defattr(-,root,root,-) +%doc AUTHORS COPYING README.md distros/fedora/README.Fedora distros/fedora/README.https distros/fedora/jscalendar-doc +%config %attr(640,root,%{zmgid_final}) /etc/zm/zm.conf +%config(noreplace) %attr(644,root,root) /etc/httpd/conf.d/zoneminder.conf +%config(noreplace) /etc/tmpfiles.d/zoneminder.conf +%config(noreplace) /etc/logrotate.d/zoneminder + +%{_unitdir}/zoneminder.service + +%{_bindir}/zma +%{_bindir}/zmaudit.pl +%{_bindir}/zmc +%{_bindir}/zmcontrol.pl +%{_bindir}/zmdc.pl +%{_bindir}/zmf +%{_bindir}/zmfilter.pl +%{_bindir}/zmpkg.pl +%{_bindir}/zmtrack.pl +%{_bindir}/zmtrigger.pl +%{_bindir}/zmu +%{_bindir}/zmupdate.pl +%{_bindir}/zmvideo.pl +%{_bindir}/zmwatch.pl +%{_bindir}/zmcamtool.pl +%{_bindir}/zmsystemctl.pl +%{_bindir}/zmtelemetry.pl +%{!?_without_x10:%{_bindir}/zmx10.pl} +%{_bindir}/zmonvif-probe.pl + +%{perl_vendorlib}/ZoneMinder* +%{perl_vendorlib}/ONVIF* +%{perl_vendorlib}/WSDiscovery* +%{perl_vendorlib}/WSSecurity* +%{perl_vendorlib}/WSNotification* +%{_mandir}/man*/* +%dir %{_libexecdir}/zoneminder +%{_libexecdir}/zoneminder/cgi-bin +%dir %{_datadir}/zoneminder +%{_datadir}/zoneminder/db +%{_datadir}/zoneminder/www + +%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy +%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules + +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/events +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/images +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/sock +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/swap +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/temp +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/log/zoneminder +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/spool/zoneminder-upload +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /run/zoneminder + + +%changelog +* Thu Mar 3 2016 Andrew Bauer - 1.30.0 +- Bump version fo 1.30.0 release. + +* Sat Nov 21 2015 Andrew Bauer - 1.29.0 +- Bump version for 1.29.0 release on Fedora 23. + +* Sat Feb 14 2015 Andrew Bauer - 1.28.1 +- Bump version for 1.28.1 release on Fedora 21. + +* Sun Oct 5 2014 Andrew Bauer - 1.28.0 +- Bump version for 1.28.0 release. + +* Fri Mar 14 2014 Andrew Bauer - 1.27 +- Tweak build requirements for cmake + +* Sat Feb 01 2014 Andrew Bauer - 1.27 +- Add zmcamtool.pl. Bump version for 1.27 release. + +* Mon Dec 16 2013 Andrew Bauer - 1.26.5 +- This is a bug fixe release +- RTSP fixes, cmake enhancements, couple other misc fixes + +* Mon Oct 07 2013 Andrew Bauer - 1.26.4 +- Initial cmake build. + +* Sat Oct 05 2013 Andrew Bauer - 1.26.4 +- Fedora specific path changes have been moved to zoneminder-1.26.0-defaults.patch +- All files are now part of the zoneminder source tree. Update specfile accordingly. + +* Sat Sep 21 2013 Andrew Bauer - 1.26.3 +- Initial rebuild for ZoneMinder 1.26.3 release. + +* Fri Feb 15 2013 Fedora Release Engineering - 1.25.0-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Mon Jan 21 2013 Adam Tkac - 1.25.0-12 +- rebuild due to "jpeg8-ABI" feature drop + +* Mon Jan 7 2013 Remi Collet - 1.25.0-11 +- fix configuration file for httpd 2.4, #871502 + +* Fri Dec 21 2012 Adam Tkac - 1.25.0-10 +- rebuild against new libjpeg + +* Thu Aug 09 2012 Jason L Tibbitts III - 1.25.0-9 +- Add patch to work around v4l2 api breakage in 3.5 kernel. + +* Sun Jul 22 2012 Fedora Release Engineering - 1.25.0-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sat Jun 23 2012 Petr Pisar - 1.25.0-7 +- Perl 5.16 rebuild + +* Wed Mar 21 2012 Jason L Tibbitts III - 1.25.0-6 +- Fix stupid thinko in sql modifications. + +* Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-5 +- Clean up macro usage. + +* Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-4 +- Convert to systemd. +- Add tmpfiles.d configuration since the initscript isn't around to create + /run/zoneminder. +- Remove some pointless executable permissions. +- Add logrotate file. + +* Wed Feb 22 2012 Jason L Tibbitts III - 1.25.0-3 +- Update README.Fedora to reference systemctl and mention timezone info in + php.ini. +- Add proper default for EYEZM_LOG_TO_FILE. + + +* Thu Feb 09 2012 Jason L Tibbitts III - 1.25.0-2 +- Rebuild for new pcre. + +* Thu Jan 19 2012 Jason L Tibbitts III - 1.25.0-1 +- Update to 1.25.0 +- Fix gcc4.7 build problems. +- Drop gcc4.4 build fixes; for whatever reason they now break the build. +- Clean up old patches. +- Force setting of ZM_TMPDIR and ZM_RUNDIR. + +* Sat Jan 14 2012 Fedora Release Engineering - 1.24.4-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-3 +- Re-add the dist-tag that somehow got lost. + +* Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-2 +- Add patch for bug 711780 - fix syntax issue in Mapped.pm. +- Undo that patch, and undo another which was the cause of the whole mess. +- Fix up other patches so ZM_PATH_BUILD is both defined and useful. +- Make sure database creation mods actually take. +- Update Fedora-specific docs with some additional info. +- Use bundled mootools (javascript, so no guideline violation). +- Update download location. +- Update the gcrypt patch to actually work. +- Upstream changed the tarball without changing the version to patch a + vulnerability, so redownload. + +* Sun Aug 14 2011 Jason L Tibbitts III - 1.24.4-1 +- Initial attempt to upgrade to 1.24.4. +- Add patch from BZ 460310 to build against libgcrypt instead of requiring the + gnutls openssl libs. + +* Thu Jul 21 2011 Petr Sabata - 1.24.3-7.20110324svn3310 +- Perl mass rebuild + +* Wed Jul 20 2011 Petr Sabata - 1.24.3-6.20110324svn3310 +- Perl mass rebuild + +* Mon May 09 2011 Jason L Tibbitts III - 1.24.3-5.20110324svn3310 +- Bump for gnutls update. + +* Thu Mar 24 2011 Jason L Tibbitts III - 1.24.3-4.20110324svn3310 +- Update to latest 1.24.3 subversion. Turns out that what upstream was calling + 1.24.3 is really just an occasionally updated devel snapshot. +- Rebase various patches. + +* Wed Mar 23 2011 Dan Horák - 1.24.3-3 +- rebuilt for mysql 5.5.10 (soname bump in libmysqlclient) + +* Tue Feb 08 2011 Fedora Release Engineering - 1.24.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Jan 25 2011 Jason L Tibbitts III - 1.24.3-1 +- Update to latest upstream version. +- Rebase patches. +- Initial incomplete attempt to disable v4l1 support. + +* Fri Jan 21 2011 Jason L Tibbitts III - 1.24.2-6 +- Unbundle cambozola; instead link to the separately pacakged copy. +- Remove BuildRoot:, %%clean and buildroot cleaning in %%install. +- Git rid of mixed space/tab usage by removing all tabs. +- Remove unnecessary Conflicts: line. +- Attempt to force short_open_tag on for the code directories. +- Move default location of sockets, swaps, logfiles and some temporary files to + make more sense and allow things to work better with a future selinux policy. +- Fix errors in README.Fedora. + +* Wed Jun 02 2010 Marcela Maslanova - 1.24.2-5 +- Mass rebuild with perl-5.12.0 + +* Fri Dec 4 2009 Stepan Kasal - 1.24.2-4 +- rebuild against perl 5.10.1 +- use Perl vendorarch and archlib variables correctly + +* Mon Jul 27 2009 Fedora Release Engineering - 1.24.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-2 +- Bump release since 1.24.2-1 was mistakenly tagged a few months ago. + +* Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-1 +- Initial update to 1.24.2. +- Rebase patches. +- Update mootools download location. +- Update to mootools 1.2.3. +- Add additional dependencies for some optional features. + +* Sat Apr 11 2009 Martin Ebourne - 1.24.1-3 +- Remove unused Sys::Mmap perl dependency RPM is finding + +* Sat Apr 11 2009 Martin Ebourne - 1.24.1-2 +- Update gcc44 patch to disable -frepo, seems to be broken with gcc44 +- Added noffmpeg patch to make building outside mock easier + +* Sat Mar 21 2009 Martin Ebourne - 1.24.1-1 +- Patch for gcc 4.4 compilation errors +- Upgrade to 1.24.1 + +* Wed Feb 25 2009 Fedora Release Engineering - 1.23.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Sat Jan 24 2009 Caolán McNamara - 1.23.3-3 +- rebuild for dependencies + +* Mon Dec 15 2008 Martin Ebourne - 1.23.3-2 +- Fix permissions on zm.conf + +* Fri Jul 11 2008 Jason L Tibbitts III - 1.23.3-1 +- Initial attempt at packaging 1.23. + +* Tue Jul 1 2008 Martin Ebourne - 1.22.3-15 +- Add perl module compat dependency, bz #453590 + +* Tue May 6 2008 Martin Ebourne - 1.22.3-14 +- Remove default runlevel, bz #441315 + +* Mon Apr 28 2008 Jason L Tibbitts III - 1.22.3-13 +- Backport patch for CVE-2008-1381 from 1.23.3 to 1.22.3. + +* Tue Feb 19 2008 Fedora Release Engineering - 1.22.3-12 +- Autorebuild for GCC 4.3 + +* Thu Jan 3 2008 Martin Ebourne - 1.22.3-11 +- Fix compilation on gcc 4.3 + +* Thu Dec 6 2007 Martin Ebourne - 1.22.3-10 +- Rebuild for new openssl + +* Thu Aug 2 2007 Martin Ebourne - 1.22.3-8 +- Fix licence tag + +* Thu Jul 12 2007 Martin Ebourne - 1.22.3-7 +- Fixes from testing by Jitz including missing dependencies and database creation + +* Sat Jun 30 2007 Martin Ebourne - 1.22.3-6 +- Disable crashtrace on ppc + +* Sat Jun 30 2007 Martin Ebourne - 1.22.3-5 +- Fix uid for directories in /var/lib/zoneminder + +* Tue Jun 26 2007 Martin Ebourne - 1.22.3-4 +- Added perl Archive::Tar dependency +- Disabled web interface due to lack of access control on the event images + +* Sun Jun 10 2007 Martin Ebourne - 1.22.3-3 +- Changes recommended in review by Jason Tibbitts + +* Mon Apr 2 2007 Martin Ebourne - 1.22.3-2 +- Standardised on package name of zoneminder + +* Thu Dec 28 2006 Martin Ebourne - 1.22.3-1 +- First version. Uses some parts from zm-1.20.1 by Corey DeLasaux and Serg Oskin diff --git a/distros/fedora/zoneminder.logrotate.in b/distros/fedora/zoneminder.logrotate.in new file mode 120000 index 000000000..c7e334ca4 --- /dev/null +++ b/distros/fedora/zoneminder.logrotate.in @@ -0,0 +1 @@ +../redhat/zoneminder.el7.logrotate.in \ No newline at end of file diff --git a/distros/fedora/zoneminder.service.in b/distros/fedora/zoneminder.service.in new file mode 100644 index 000000000..8c6214b42 --- /dev/null +++ b/distros/fedora/zoneminder.service.in @@ -0,0 +1,18 @@ +# ZoneMinder systemd unit file for Fedora +# Replace mariadb with community-mysql if using mysql service instead of mariadb + +[Unit] +Description=ZoneMinder CCTV recording and security system +After=network.target mariadb.service httpd.service +Requires=mariadb.service httpd.service + +[Service] +User=@WEB_USER@ +Type=forking +ExecStart=@BINDIR@/zmpkg.pl start +ExecReload=@BINDIR@/zmpkg.pl restart +ExecStop=@BINDIR@/zmpkg.pl stop +PIDFile="@ZM_RUNDIR@/zm.pid" + +[Install] +WantedBy=multi-user.target diff --git a/distros/fedora/zoneminder.tmpfiles.in b/distros/fedora/zoneminder.tmpfiles.in new file mode 120000 index 000000000..0e4f721f6 --- /dev/null +++ b/distros/fedora/zoneminder.tmpfiles.in @@ -0,0 +1 @@ +../redhat/zoneminder.tmpfiles.in \ No newline at end of file diff --git a/distros/opensuse/CMakeLists.txt b/distros/opensuse/CMakeLists.txt index d468c7287..10654f26d 100644 --- a/distros/opensuse/CMakeLists.txt +++ b/distros/opensuse/CMakeLists.txt @@ -38,7 +38,7 @@ install(CODE "execute_process(COMMAND ln -sf ../../../../${zm_webdir}/temp \"\$E # Opensuse cambazola? requires cambozola as a separate package so just link to it #install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")") -# Install auxillary files required to run zoneminder on OpenSuse +# Install auxiliary files required to run zoneminder on OpenSuse install(FILES zoneminder.conf DESTINATION /etc/apache2/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) install(FILES zoneminder.tmpfiles DESTINATION /etc/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/distros/opensuse/README.OpenSuse b/distros/opensuse/README.OpenSuse index 823ccd442..27103d960 100644 --- a/distros/opensuse/README.OpenSuse +++ b/distros/opensuse/README.OpenSuse @@ -138,7 +138,7 @@ Upgrades has select, insert, update, and delete permission. 3. You will need to upgrade the ZoneMinder database as described in the - manual. Only if the previous step was succesful, may you run zmupdate like + manual. Only if the previous step was successful, may you run zmupdate like so (as root): /opt/zoneminder/bin/zmupdate.pl diff --git a/distros/opensuse/redalert.wav b/distros/opensuse/redalert.wav deleted file mode 100644 index 8335ab23e..000000000 --- a/distros/opensuse/redalert.wav +++ /dev/null @@ -1 +0,0 @@ -../redhat/redalert.wav diff --git a/distros/opensuse/redalert.wav b/distros/opensuse/redalert.wav new file mode 120000 index 000000000..eec3dce64 --- /dev/null +++ b/distros/opensuse/redalert.wav @@ -0,0 +1 @@ +../redhat/redalert.wav \ No newline at end of file diff --git a/distros/redhat/CMakeLists.txt b/distros/redhat/CMakeLists.txt index 63e742d68..5de834109 100644 --- a/distros/redhat/CMakeLists.txt +++ b/distros/redhat/CMakeLists.txt @@ -1,10 +1,22 @@ # CMakeLists.txt for the Redhat/CentOS Target Distro. +# Display a message to show the RHEL build options are being processed. +message([STATUS] "Starting RHEL Build Options" ...) + # Create the zoneminder service file -configure_file(zoneminder.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) +if(ZM_TARGET_DISTRO STREQUAL "el7") + configure_file(zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) + configure_file(zoneminder.el7.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.el7.conf @ONLY) + configure_file(zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY) + configure_file(zoneminder.el7.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.el7.logrotate @ONLY) +else(ZM_TARGET_DISTRO STREQUAL "el7") + configure_file(zoneminder.sysvinit.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.sysvinit @ONLY) + configure_file(zoneminder.el6.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.el6.logrotate @ONLY) + configure_file(zoneminder.el6.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.el6.conf @ONLY) +endif(ZM_TARGET_DISTRO STREQUAL "el7") # Download jscalendar & move files into position -file(DOWNLOAD http://softlayer-dal.dl.sourceforge.net/project/jscalendar/jscalendar/1.0/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip LOG jsc_log STATUS download_jsc) +file(DOWNLOAD http://iweb.dl.sourceforge.net/project/jscalendar/jscalendar/1.0/jscalendar-1.0.zip ${CMAKE_CURRENT_SOURCE_DIR}/jscalendar-1.0.zip LOG jsc_log STATUS download_jsc) #message(STATUS "Log of jscalender script was: ${jsc_log}") if(download_jsc EQUAL 0) message(STATUS "Jscalander successfully downloaded. Installing...") @@ -14,15 +26,16 @@ else(download_jsc EQUAL 0) message(STATUS "Unable to download optional jscalander. Skipping...") endif(download_jsc EQUAL 0) +# Cambozola is now packaged in zmrepo # Download cambozola & move files into position -file(DOWNLOAD http://www.andywilcock.com/code/cambozola/cambozola-0.931.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/cambozola-0.931.tar.gz STATUS download_camb) -if(download_camb EQUAL 0) - message(STATUS "Cambozola successfully downloaded. Installing...") - execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cambozola.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ERROR_VARIABLE untar_camb) - message(STATUS "Status of cambozola script was: ${untar_camb}") -else(download_camb EQUAL 0) - message(STATUS "Unable to download optional Cambozola. Skipping...") -endif(download_camb EQUAL 0) +#file(DOWNLOAD http://www.andywilcock.com/code/cambozola/cambozola-0.931.tar.gz ${CMAKE_CURRENT_SOURCE_DIR}/cambozola-0.931.tar.gz STATUS download_camb) +#if(download_camb EQUAL 0) +# message(STATUS "Cambozola successfully downloaded. Installing...") +# execute_process(COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cambozola.sh WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ERROR_VARIABLE untar_camb) +# message(STATUS "Status of cambozola script was: ${untar_camb}") +#else(download_camb EQUAL 0) +# message(STATUS "Unable to download optional Cambozola. Skipping...") +#endif(download_camb EQUAL 0) # Create several empty folders file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp) @@ -39,12 +52,23 @@ install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_P install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/events \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/events\")") install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/images \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/images\")") install(CODE "execute_process(COMMAND ln -sf ../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/temp\")") +install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/api/app/tmp\")") -# Install auxillary files required to run zoneminder on CentOS -install(FILES zoneminder.conf DESTINATION /etc/httpd/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) -install(FILES zm-logrotate_d DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +# Link to Cambozola, which is now packaged in zmrepo +install(CODE "execute_process(COMMAND ln -sf ../../java/cambozola.jar \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/cambozola.jar\")") + +# Install auxiliary files required to run zoneminder on CentOS install(FILES redalert.wav DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/sounds PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) -install(FILES zoneminder.service DESTINATION /etc/rc.d/init.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +if(ZM_TARGET_DISTRO STREQUAL "el7") + install(FILES zoneminder.el7.conf DESTINATION /etc/httpd/conf.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + install(FILES zoneminder.el7.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + install(FILES ../../misc/zoneminder-tmpfiles.conf DESTINATION /etc/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +else(ZM_TARGET_DISTRO STREQUAL "el7") + install(FILES zoneminder.el6.conf DESTINATION /etc/httpd/conf.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + install(FILES zoneminder.el6.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + install(FILES zoneminder.sysvinit DESTINATION /etc/rc.d/init.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +endif(ZM_TARGET_DISTRO STREQUAL "el7") # Install jscalendar if(unzip_jsc STREQUAL "") diff --git a/distros/redhat/README.CentOS b/distros/redhat/README.CentOS index cc5e247f3..f9c7ea1fb 100644 --- a/distros/redhat/README.CentOS +++ b/distros/redhat/README.CentOS @@ -1,6 +1,20 @@ -================================================================================ - NEW INSTALLS -================================================================================ +What's New +========== + +1. Due to the active state of the ZoneMinder project, we now recommend granting + ALL permission to the ZoneMinder mysql account. This change must be done + manually before ZoneMinder will run. See the installation steps below. + +2. This package uses the HTTPS protocol by default to access the web portal. + Requests using HTTP will auto-redirect to HTTPS. See README.https for + more information. + +3. The php package that ships with CentOS 6 does not support the new ZoneMinder + API. If you require API functionality (such as using a mobile app) then you + should consider an upgrade to CentOS 7 or use Fedora. + +New installs +============ 1. Unless you are already using MySQL server, you need to ensure that the server is confired to start during boot and properly secured @@ -11,30 +25,38 @@ sudo chkconfig mysqld on 2. Using the password for the root account set during the previous step, you - will need to create the ZoneMinder database: + will need to create the ZoneMinder database and configure a database + account for ZoneMinder to use: - mysql -uroot -p - mysql> create database zm; - mysql> grant select,insert,update,delete,lock tables,alter on zm.* to - 'zmuser'@localhost identified by 'zmpass'; - mysql> exit; mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql + mysql -uroot -p -e "grant all on zm.* to \ + 'zmuser'@localhost identified by 'zmpass';" mysqladmin -uroot -p reload + The database account credentials, zmuser/zmpass, are arbitrary. Set them to + anything that suits your environment. + 3. If you have chosen to change the zoneminder mysql credentials to something other than zmuser/zmpass then you must now edit /etc/zm.conf. Change - ZM_DB_USER and ZM_DB_PASS to the values you created in step 2. + ZM_DB_USER and ZM_DB_PASS to the values you created in the previous step. -4. IMPORTANT: Edit /etc/php.ini and put in the appropriate timezone for - date.timezone! +4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local + timezone. PHP will complain loudly if this is not set, or if it is set + incorrectly, and these complaints will show up in the zoneminder logging + system as errors -5. The ZoneMinder web interface is disabled by default, you will need - to edit this file to enable it: + If you are not sure of the proper timezone specification to use, look at + http://php.net/date.timezone - /etc/httpd/conf.d/zoneminder.conf +5. Install mod_ssl or configure /etc/httpd/conf.d/zoneminder.conf to meet your + needs. This package comes preconfigured for HTTPS using the default self + signed certificate on your system. The recommended way to complete this step + is to simply install mod_ssl: - HINT: Most users will want to simply delete the line that says - "Deny from all". + sudo yum install mod_ssl + + If this does not meet your needs, then read README.https to + learn about alternatives. When in doubt, install mod_ssl. 6. Configure the web server to start automatically: @@ -45,8 +67,8 @@ called local_zoneminder. A copy of this policy is in the documentation folder. - Unfortunately, this has not resolved all the SELinux issues so - most will want to disable SELinux permanently by editing the following: + It is still possible to run into SELinux issues, however. If this is case, + you can disable SELinux permanently by editing the following: /etc/selinux/conf @@ -62,31 +84,50 @@ Then point your web browser to http:///zm -================================================================================ - UPGRADES -================================================================================ +Upgrades +======== -1. Add additional permissions to the zmuser account: +1. Verify /etc/zm.conf. - mysql -u root -p - grant lock tables,alter on zm.* to - 'zmuser'@localhost identified by 'zmpass'; + If zm.conf was manually edited before running the upgrade, the installation + may not overwrite it. In this case, it will create the file + /etc/zm.conf.rpmnew. - Since this is an upgrade, the assumption is that the zmuser account exists - and already has select, insert, update, and delete permission. + For example, this will happen if you are using database account credentials + other than zmuser/zmpass. + + Compare /etc/zm.conf to /etc/zm.conf.rpmnew. Verify that zm.conf + contains any new config settings that may be in zm.conf.rpmnew. + +2. Verify permissions of the zmuser account. + + Over time, the database account permissions required for normal operation + have increased. Verify the zmuser database account has been granted all + permission to the ZoneMinder database: + + mysql -uroot -p -e "show grants for zmuser@localhost;" + + See step 2 of the Installation section to add missing permissions. + +3. Verify the ZoneMinder Apache configuration file in the folder + /etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there + may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file + exists, inspect it and merge anything new in that file with zoneminder.conf. + Verify the SSL REquirements meet your needs. Read README.https if necessary. + +4. Upgrade the database before starting ZoneMinder. + + Most upgrades can be performed by executing the following command: -2. If you have previsouly changed the zoneminder mysql credentials to something - other than zmuser/zmpass then you must now edit /etc/zm.conf. Change - ZM_DB_USER and ZM_DB_PASS to their appropriate values. - -3. You will need to upgrade the ZoneMinder database as described in the manual. - Only if step 1 was succesfully applied, may you run zmupdate like so: - - sudo zmupdate.pl --version= - - - If unsure then run it this way: + sudo zmupdate.pl + + Recent versions of ZoneMinder don't require any parameters added to the + zmupdate command. However, if ZoneMinder complains, you may need to call + zmupdate in the following manner: sudo zmupdate.pl --user=root --pass= --version= +5. Now start zoneminder: + + sudo service zoneminder start diff --git a/distros/redhat/README.Centos7 b/distros/redhat/README.Centos7 new file mode 100644 index 000000000..1cd26a1af --- /dev/null +++ b/distros/redhat/README.Centos7 @@ -0,0 +1,148 @@ +What's New +========== + +1. The Apache ScriptAlias has been changed from "/cgi-bin/zm/zms" to + "/cgi-bin-zm/zms". This has been to done to avoid this bug: + https://bugzilla.redhat.com/show_bug.cgi?id=973067 + + IMPORTANT: You must manually verify the value of PATH_ZMS under Options. + Make sure it is set to "/cgi-bin-zm/nph-zms". Failure to do so will result + in a broken system. You have been warned. + +2. Due to the active state of the ZoneMinder project, we now recommend granting + ALL permission to the ZoneMinder mysql account. This change must be done + manually before ZoneMinder will run. See the installation steps below. + +3. This package uses the HTTPS protocol by default to access the web portal. + Requests using HTTP will auto-redirect to HTTPS. See README.https for + more information. + +4. This package ships with the new ZoneMinder API enabled. + +New installs +============ + +1. Unless you are already using MariaDB server, you need to ensure that + the server is configured to start during boot and properly secured + by running: + + sudo systemctl enable mariadb + sudo systemctl start mariadb + sudo mysql_secure_installation + +2. Using the password for the root account set during the previous step, you + will need to create the ZoneMinder database and configure a database + account for ZoneMinder to use: + + mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql + mysql -uroot -p -e "grant all on zm.* to \ + 'zmuser'@localhost identified by 'zmpass';" + mysqladmin -uroot -p reload + + The database account credentials, zmuser/zmpass, are arbitrary. Set them to + anything that suits your environment. + +3. If you have chosen to change the zoneminder database account credentials to + something other than zmuser/zmpass, you must now edit /etc/zm/zm.conf. + Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous + step. + + This version of zoneminder no longer requires you to make a similar change + to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php + This now happens dynamically. Do *not* make any changes to this file. + +4. Edit /etc/php.ini, uncomment the date.timezone line, and add your local + timezone. PHP will complain loudly if this is not set, or if it is set + incorrectly, and these complaints will show up in the zoneminder logging + system as errors. + + If you are not sure of the proper timezone specification to use, look at + http://php.net/date.timezone + +5. Disable SELinux + + We currently do not have the resources to create and maintain an accurate + SELinux policy for ZoneMinder on CentOS 7. We will gladly accept pull + reqeusts from anyone who wishes to do the work. In the meantime, SELinux + will need to be disabled or put into permissive mode. + + To immediately disbale SELinux for the current seesion, issue the following + from the command line: + + sudo setenforce 0 + + To permanently disable SELinux, edit /etc/selinux/config and change the + SELINUX line from "enforcing" to "disabled". This change will take + effect after a reboot. + +6. Install mod_ssl or configure /etc/httpd/conf.d/zoneminder.conf to meet your + needs. This package comes preconfigured for HTTPS using the default self + signed certificate on your system. The recommended way to complete this step + is to simply install mod_ssl: + + sudo yum install mod_ssl + + If this does not meet your needs, then read README.https to + learn about alternatives. When in doubt, install mod_ssl. + +7. Now start the web server: + + sudo systemctl enable httpd + sudo systemctl start httpd + +8. Now start zoneminder: + + sudo systemctl enable zoneminder + sudo systemctl start zoneminder + +Upgrades +======== + +1. Verify /etc/zm/zm.conf. + + If zm.conf was manually edited before running the upgrade, the installation + may not overwrite it. In this case, it will create the file + /etc/zm/zm.conf.rpmnew. + + For example, this will happen if you are using database account credentials + other than zmuser/zmpass. + + Compare /etc/zm/zm.conf to /etc/zm/zm.conf.rpmnew. Verify that zm.conf + contains any new config settings that may be in zm.conf.rpmnew. + + This version of zoneminder no longer requires you to make a similar change + to the credentials in /usr/share/zoneminder/www/api/app/Config/database.php + This now happens dynamically. Do *not* make any changes to this file. + +2. Verify permissions of the zmuser account. + + Over time, the database account permissions required for normal operation + have increased. Verify the zmuser database account has been granted all + permission to the ZoneMinder database: + + mysql -uroot -p -e "show grants for zmuser@localhost;" + + See step 2 of the Installation section to add missing permissions. + +3. Verify the ZoneMinder Apache configuration file in the folder + /etc/httpd/conf.d. You will have a file called "zoneminder.conf" and there + may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file + exists, inspect it and merge anything new in that file with zoneminder.conf. + Verify the SSL REquirements meet your needs. Read README.https if necessary. + +4. Upgrade the database before starting ZoneMinder. + + Most upgrades can be performed by executing the following command: + + sudo zmupdate.pl + + Recent versions of ZoneMinder don't require any parameters added to the + zmupdate command. However, if ZoneMinder complains, you may need to call + zmupdate in the following manner: + + sudo zmupdate.pl --user=root --pass= --version= + +5. Now start zoneminder: + + sudo systemctl start zoneminder + diff --git a/distros/redhat/README.https b/distros/redhat/README.https new file mode 100644 index 000000000..23affeb96 --- /dev/null +++ b/distros/redhat/README.https @@ -0,0 +1,25 @@ +HTTPS is now a requirement +========================== + +This package now depends on Apache's mod_ssl pacakge. This will automatically +be installed along with ZoneMinder. Upon installation, the mod_ssl package +will create a default, self-signed certificate. This is the certificate that +ZoneMinder will use out of the box. + +Since the certificate is self-signed, you will get a warning from your browser +the first time you access the web portal. This is normal. + +This is not intended to be an all encompasing solution for everyone. ZoneMinder +will work just fine over HTTPS the way it is currently configured. However, +here are a couple of considerations you may want to take. + +1. Create your own certificate. The CentOS wiki has a guide that describes how + to do this: https://wiki.centos.org/HowTos/Https . Additionally, Googling + "centos certificate" reveals many articles on the subject. Note that some + third party applications, such as zmNinja, will require you to create a + certificate different than the default certificate on your machine. + +2. You can turn off HTTPS entirely by simply commenting out the SSLRequireSSL + directives found in /etc/httpd/conf.d/zoneminder.conf. You should also + comment out the HTTP -> HTTPS Rewrite rule. + diff --git a/distros/redhat/zm-init b/distros/redhat/archive/zm-init similarity index 100% rename from distros/redhat/zm-init rename to distros/redhat/archive/zm-init diff --git a/distros/redhat/zoneminder-1.28.0-defaults.patch b/distros/redhat/archive/zoneminder-1.28.0-defaults.patch similarity index 100% rename from distros/redhat/zoneminder-1.28.0-defaults.patch rename to distros/redhat/archive/zoneminder-1.28.0-defaults.patch diff --git a/distros/redhat/archive/zoneminder.el6.spec b/distros/redhat/archive/zoneminder.el6.spec index 32bddfda6..46bce099a 100644 --- a/distros/redhat/archive/zoneminder.el6.spec +++ b/distros/redhat/archive/zoneminder.el6.spec @@ -237,7 +237,6 @@ fi # zmfix removed from zoneminder 1.26.6 #%attr(4755,root,root) %{_bindir}/zmfix %{_bindir}/zmpkg.pl -%{_bindir}/zmstreamer %{_bindir}/zmtrack.pl %{_bindir}/zmtrigger.pl %{_bindir}/zmu @@ -250,9 +249,11 @@ fi %{_bindir}/zmonvif-probe.pl %{perl_vendorlib}/ZoneMinder* -%{perl_vendorlib}/ONVIF* -%{perl_vendorlib}/WSDiscovery* -%{perl_vendorlib}/WSSecurity* +%{perl_vendorlib}/%{_arch}-linux-thread-multi/auto/ZoneMinder* +#%{perl_vendorlib}/ONVIF* +#%{perl_vendorlib}/WSDiscovery* +#%{perl_vendorlib}/WSSecurity* +#%{perl_vendorlib}/%{_arch}-linux-thread-multi/auto/ONVIF* %{_mandir}/man*/* %dir %{_libexecdir}/%{name} %{_libexecdir}/%{name}/cgi-bin @@ -277,6 +278,9 @@ fi * Sun Aug 03 2014 Andrew Bauer - 1.27 - Include ONVIF support files +* Fri Mar 14 2014 Andrew Bauer - 1.27 +- Tweak build requirements for cmake + * Sat Feb 01 2014 Andrew Bauer - 1.27 - Add zmcamtool.pl. Bump version for 1.27 release. diff --git a/distros/redhat/local_zoneminder.te b/distros/redhat/local_zoneminder.te index 4a060ffc3..c49505785 100644 --- a/distros/redhat/local_zoneminder.te +++ b/distros/redhat/local_zoneminder.te @@ -1,11 +1,12 @@ - -module local_zoneminder 1.1; +module local_zoneminder 1.2; require { type afs_ka_port_t; type netsupport_port_t; type port_t; type presence_port_t; + type postfix_master_t; + type postfix_qmgr_t; type postfix_pickup_t; type httpd_t; type var_lib_t; @@ -28,74 +29,75 @@ require { type git_port_t; type ipp_port_t; type aol_port_t; - type unconfined_t; - type kernel_t; - type init_t; - type auditd_t; - type mysqld_t; - type httpd_log_t; - type syslogd_t; - type httpd_t; - type initrc_state_t; - type initrc_t; - type var_lib_t; - type udev_t; - type mysqld_safe_t; - type sshd_t; - type crond_t; - type getty_t; - type httpd_var_lib_t; - type initrc_var_run_t; - type tmpfs_t; - type dhcpc_t; - type v4l_device_t; - type file_t; - class sock_file { write create unlink }; - class unix_stream_socket { read connectto }; - class lnk_file { write create getattr read lock unlink }; - class dir search; + type unconfined_t; + type kernel_t; + type init_t; + type auditd_t; + type mysqld_t; + type httpd_log_t; + type syslogd_t; + type httpd_t; + type initrc_state_t; + type initrc_t; + type var_lib_t; + type udev_t; + type mysqld_safe_t; + type sshd_t; + type crond_t; + type getty_t; + type httpd_var_lib_t; + type initrc_var_run_t; + type tmpfs_t; + type dhcpc_t; + type v4l_device_t; + type file_t; + class sock_file { write create unlink }; + class unix_stream_socket { read connectto }; + class lnk_file { write create getattr read lock unlink }; + class dir {search getattr }; class udp_socket name_bind; - class file { write getattr read lock unlink open }; - class shm { unix_read unix_write associate read write getattr }; - class chr_file getattr; + class file { write getattr read lock unlink open }; + class shm { unix_read unix_write associate read write getattr }; + class chr_file getattr; } #============= httpd_t ============== -allow httpd_t auditd_t:dir search; +allow httpd_t auditd_t:dir { search getattr }; allow httpd_t auditd_t:file { read getattr open }; -allow httpd_t crond_t:dir search; +allow httpd_t crond_t:dir { search getattr }; allow httpd_t crond_t:file { read getattr open }; -allow httpd_t dhcpc_t:dir search; +allow httpd_t dhcpc_t:dir { search getattr }; allow httpd_t dhcpc_t:file { read getattr open }; -allow httpd_t getty_t:dir search; +allow httpd_t getty_t:dir { search getattr }; allow httpd_t getty_t:file { read getattr open }; allow httpd_t httpd_log_t:file write; allow httpd_t httpd_var_lib_t:lnk_file { write getattr read lock unlink }; -allow httpd_t init_t:dir search; +allow httpd_t init_t:dir { search getattr }; allow httpd_t init_t:file { read getattr open }; #!!!! The source type 'httpd_t' can write to a 'file' of the following types: -# squirrelmail_spool_t, dirsrvadmin_config_t, httpd_lock_t, dirsrv_config_t, httpd_tmp_t, dirsrvadmin_tmp_t, httpd_cache_t, httpd_tmpfs_t, httpd_squirrelmail_t, dirsrv_var_log_t, zarafa_var_lib_t, dirsrv_var_run_t, httpd_var_lib_t, httpd_var_run_t, passenger_tmp_t, httpd_nutups_cgi_rw_content_t, httpd_apcupsd_cgi_rw_content_t, httpd_dspam_rw_content_t, httpd_mediawiki_rw_content_t, httpd_squid_rw_content_t, httpd_prewikka_rw_content_t, httpd_smokeping_cgi_rw_content_t, passenger_var_run_t, httpd_openshift_rw_content_t, httpd_dirsrvadmin_rw_content_t, httpd_w3c_validator_rw_content_t, httpd_user_rw_content_t, httpd_awstats_rw_content_t, httpdcontent, httpd_cobbler_rw_content_t, root_t, httpd_munin_rw_content_t, httpd_bugzilla_rw_content_t, httpd_cvs_rw_content_t, httpd_git_rw_content_t, httpd_sys_rw_content_t, httpd_sys_rw_content_t, httpd_nagios_rw_content_t +#squirrelmail_spool_t, mirrormanager_var_run_t, dirsrvadmin_config_t, httpd_lock_t, httpd_tmp_t, dirsrv_config_t, dirsrvadmin_tmp_t, httpd_cache_t, httpd_tmpfs_t, httpd_squirrelmail_t, dirsrv_var_run_t, dirsrv_var_log_t, httpd_var_lib_t, httpd_var_run_t, zarafa_var_lib_t, httpd_prewikka_rw_content_t, httpd_mediawiki_rw_content_t, httpd_squid_rw_content_t, passenger_var_run_t, httpd_smokeping_cgi_rw_content_t, httpd_openshift_rw_content_t, httpd_dirsrvadmin_rw_content_t, httpd_w3c_validator_rw_content_t, httpd_collectd_rw_content_t, cluster_var_lib_t, cluster_var_run_t, httpd_user_rw_content_t, httpd_awstats_rw_content_t, httpdcontent, root_t, httpd_cobbler_rw_content_t, httpd_munin_rw_content_t, cluster_conf_t, httpd_bugzilla_rw_content_t, passenger_tmp_t, httpd_cvs_rw_content_t, httpd_git_rw_content_t, httpd_sys_rw_content_t, httpd_sys_rw_content_t, httpd_nagios_rw_content_t, httpd_apcupsd_cgi_rw_content_t, httpd_nutups_cgi_rw_content_t, httpd_dspam_rw_content_t allow httpd_t initrc_state_t:file { read write getattr unlink open }; allow httpd_t initrc_t:unix_stream_socket connectto; allow httpd_t initrc_t:shm { unix_read unix_write associate read write getattr }; -allow httpd_t initrc_var_run_t:file { read lock open }; -allow httpd_t kernel_t:dir search; +allow httpd_t initrc_var_run_t:file { write read lock open }; +allow httpd_t kernel_t:dir { search getattr }; allow httpd_t kernel_t:file { read getattr open }; -allow httpd_t mysqld_safe_t:dir search; +allow httpd_t mysqld_safe_t:dir { search getattr }; allow httpd_t mysqld_safe_t:file { read getattr open }; -allow httpd_t mysqld_t:dir search; +allow httpd_t mysqld_t:dir { search getattr }; allow httpd_t mysqld_t:file { read getattr open }; -allow httpd_t sshd_t:dir search; +allow httpd_t sshd_t:dir { search getattr }; allow httpd_t sshd_t:file { read getattr open }; -allow httpd_t syslogd_t:dir search; +allow httpd_t syslogd_t:dir { search getattr }; allow httpd_t syslogd_t:file { read getattr open }; allow httpd_t tmpfs_t:sock_file write; -allow httpd_t udev_t:dir search; +allow httpd_t udev_t:dir { search getattr }; allow httpd_t udev_t:file { read getattr open }; -allow httpd_t unconfined_t:dir search; +allow httpd_t unconfined_t:dir { search getattr }; allow httpd_t unconfined_t:file { read getattr open }; allow httpd_t var_lib_t:lnk_file { write getattr read lock unlink }; +allow httpd_t var_lib_t:sock_file { write unlink }; allow httpd_t v4l_device_t:chr_file getattr; allow httpd_t afs_fs_port_t:udp_socket name_bind; allow httpd_t afs_ka_port_t:udp_socket name_bind; @@ -114,10 +116,10 @@ allow httpd_t mmcc_port_t:udp_socket name_bind; allow httpd_t netsupport_port_t:udp_socket name_bind; allow httpd_t nodejs_debug_port_t:udp_socket name_bind; allow httpd_t port_t:udp_socket name_bind; -allow httpd_t postfix_master_t:dir search; +allow httpd_t postfix_master_t:dir { search getattr }; allow httpd_t postfix_master_t:file { read getattr open }; -allow httpd_t postfix_pickup_t:dir search; +allow httpd_t postfix_pickup_t:dir { search getattr }; allow httpd_t postfix_pickup_t:file { read getattr open }; -allow httpd_t postfix_qmgr_t:dir search; +allow httpd_t postfix_qmgr_t:dir { search getattr }; allow httpd_t postfix_qmgr_t:file { read getattr open }; allow httpd_t presence_port_t:udp_socket name_bind; diff --git a/distros/redhat/redalert.wav b/distros/redhat/redalert.wav old mode 100755 new mode 100644 diff --git a/distros/redhat/zoneminder.conf b/distros/redhat/zoneminder.conf deleted file mode 100644 index 4a4d93261..000000000 --- a/distros/redhat/zoneminder.conf +++ /dev/null @@ -1,33 +0,0 @@ -# The Zoneminder web interface has been disabled by default due to a small -# security issue in the default install. -# -# When using Zoneminder's own authentication, recorded CCTV images are -# accessible from the web directly without passing the authentication. This -# means any attacker could see your CCTV images without a password. In order -# to avoid this you can disable Zoneminder's authentication and configure -# standard Apache authentication (see the Apache documentation for details on -# this). -# -# If you still wish to use Zoneminder's own authentication, or have an -# internal site which needs no authentication, you need to delete the line -# marked below and restart Apache. - -Alias /zm "/usr/share/zoneminder/www" - - Options -Indexes MultiViews FollowSymLinks - AllowOverride All - Order allow,deny - Allow from all - # The code unfortunately uses short tags in many places - php_value short_open_tag 1 - -Deny from all # DELETE THIS LINE - - -ScriptAlias /cgi-bin/zm "/usr/libexec/zoneminder/cgi-bin" - - AllowOverride All - Options ExecCGI - Order allow,deny - Allow from all - diff --git a/distros/redhat/zoneminder.el6.conf.in b/distros/redhat/zoneminder.el6.conf.in new file mode 100644 index 000000000..0fbee6a62 --- /dev/null +++ b/distros/redhat/zoneminder.el6.conf.in @@ -0,0 +1,28 @@ +# +# ZoneMinder Apache configuration file +# With SSLRequire and HTTPS auto redirect +# Modify this configuration to suit your requirements +# + +# Auto Redirect HTTP requests to HTTPS +RewriteEngine On +RewriteCond %{HTTPS} !=on +RewriteRule ^/?(zm)(.*) https://%{SERVER_NAME}/$1$2 [R,L] + +Alias /zm "@ZM_WEBDIR@" + + SSLRequireSSL + Options -Indexes MultiViews FollowSymLinks + AllowOverride All + Order allow,deny + Allow from all + + +ScriptAlias /cgi-bin/zm "@ZM_CGIDIR@" + + SSLRequireSSL + AllowOverride All + Options ExecCGI FollowSymLinks + Order allow,deny + Allow from all + diff --git a/distros/redhat/zm-logrotate_d b/distros/redhat/zoneminder.el6.logrotate.in similarity index 69% rename from distros/redhat/zm-logrotate_d rename to distros/redhat/zoneminder.el6.logrotate.in index 61cc9d0eb..5b852cba7 100644 --- a/distros/redhat/zm-logrotate_d +++ b/distros/redhat/zoneminder.el6.logrotate.in @@ -1,4 +1,4 @@ -/var/log/zoneminder/*log +@ZM_LOGDIR@/*log { weekly notifempty diff --git a/distros/redhat/zoneminder.cmake.el6.spec b/distros/redhat/zoneminder.el6.spec similarity index 86% rename from distros/redhat/zoneminder.cmake.el6.spec rename to distros/redhat/zoneminder.el6.spec index 586a27c57..0b7770b50 100644 --- a/distros/redhat/zoneminder.cmake.el6.spec +++ b/distros/redhat/zoneminder.el6.spec @@ -4,7 +4,7 @@ %define zmgid_final apache Name: zoneminder -Version: 1.28.0 +Version: 1.30.0 Release: 1%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons @@ -17,8 +17,6 @@ URL: http://www.zoneminder.com/ #Source0: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz Source0: ZoneMinder-%{version}.tar.gz -Patch1: zoneminder-1.28.0-defaults.patch - BuildRequires: cmake gnutls-devel bzip2-devel BuildRequires: mysql-devel pcre-devel libjpeg-turbo-devel BuildRequires: perl(Archive::Tar) perl(Archive::Zip) @@ -32,8 +30,8 @@ BuildRequires: libcurl-devel vlc-devel ffmpeg-devel polkit-devel # cmake needs the following installed at build time due to the way it auto-detects certain parameters BuildRequires: httpd ffmpeg -Requires: httpd php php-mysql mysql-server libjpeg-turbo polkit -Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) +Requires: httpd php php-gd php-mysql mysql-server libjpeg-turbo cambozola polkit net-tools +Requires: psmisc perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip) Requires: perl(MIME::Entity) perl(MIME::Lite) perl(Net::SMTP) perl(Net::FTP) Requires: libcurl vlc-core ffmpeg @@ -63,12 +61,20 @@ too much degradation of performance. %prep %setup -q -n ZoneMinder-%{version} -%patch1 -p0 -b .defaults +# Change the following default values +./utils/zmeditconfigdata.sh ZM_PATH_ZMS /cgi-bin/zm/nph-zms +./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes +./utils/zmeditconfigdata.sh ZM_PATH_SWAP /dev/shm +./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR /var/spool/zoneminder-upload +./utils/zmeditconfigdata.sh ZM_OPT_CONTROL yes +./utils/zmeditconfigdata.sh ZM_CHECK_FOR_UPDATES no +./utils/zmeditconfigdata.sh ZM_DYN_SHOW_DONATE_REMINDER no +./utils/zmeditconfigdata.sh ZM_OPT_FAST_DELETE no %build # Have to override CMAKE_INSTALL_LIBDIR for cmake < 2.8.7 due to this bug: # https://bugzilla.redhat.com/show_bug.cgi?id=795542 -%cmake -DZM_TARGET_DISTRO="el6" -DCMAKE_INSTALL_LIBDIR:PATH=%{_lib} -DZM_PERL_SUBPREFIX=`x="%{perl_vendorlib}" ; echo ${x#"%{_prefix}"}` . +%cmake -DZM_TARGET_DISTRO="el6" -DCMAKE_INSTALL_LIBDIR:PATH=%{_lib} . make %{?_smp_mflags} @@ -91,8 +97,24 @@ echo -e "\nCreating and installing a ZoneMinder SELinux policy module. Please wa /usr/bin/semodule_package -o %{_docdir}/%{name}-%{version}/local_zoneminder.pp -m %{_docdir}/%{name}-%{version}/local_zoneminder.mod > /dev/null /usr/sbin/semodule -i %{_docdir}/%{name}-%{version}/local_zoneminder.pp > /dev/null -# Display the README for post installation instructions -/usr/bin/less %{_docdir}/%{name}-%{version}/README.CentOS +# Upgrade from a previous version of zoneminder +if [ $1 -eq 2 ] ; then + + # Add any new PTZ control configurations to the database (will not overwrite) + %{_bindir}/zmcamtool.pl --import >/dev/null 2>&1 || : + + # Freshen the database + %{_bindir}/zmupdate.pl -f >/dev/null 2>&1 || : + + # We can't run this automatically when new sql account permissions need to + # be manually added first + # Run zmupdate non-interactively + #/usr/bin/zmupdate.pl --nointeractive +fi + +# Warn the end user to read the README file +echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, read README.Centos to finish the\ninstallation or upgrade!\n" +echo -e "\nThe README file is located here: %{_docdir}/%{name}-%{version}.\n" %preun if [ $1 -eq 0 ]; then @@ -113,8 +135,8 @@ rm -rf %{_docdir}/%{name}-%{version} %files %defattr(-,root,root,-) -%doc AUTHORS BUGS ChangeLog COPYING LICENSE NEWS README.md distros/redhat/README.CentOS distros/redhat/jscalendar-doc -%doc distros/redhat/cambozola-doc distros/redhat/local_zoneminder.te +%doc AUTHORS BUGS ChangeLog COPYING LICENSE NEWS README.md distros/redhat/README.CentOS distros/redhat/README.https distros/redhat/jscalendar-doc +%doc distros/redhat/local_zoneminder.te %config %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm.conf %config(noreplace) %attr(644,root,root) %{_sysconfdir}/httpd/conf.d/zoneminder.conf %config(noreplace) /etc/logrotate.d/%{name} @@ -128,7 +150,6 @@ rm -rf %{_docdir}/%{name}-%{version} %{_bindir}/zmf %{_bindir}/zmfilter.pl %{_bindir}/zmpkg.pl -%{_bindir}/zmstreamer %{_bindir}/zmtrack.pl %{_bindir}/zmtrigger.pl %{_bindir}/zmu @@ -137,15 +158,17 @@ rm -rf %{_docdir}/%{name}-%{version} %{_bindir}/zmwatch.pl %{_bindir}/zmcamtool.pl %{_bindir}/zmsystemctl.pl +%{_bindir}/zmtelemetry.pl %{_bindir}/zmx10.pl %{_bindir}/zmonvif-probe.pl %{perl_vendorlib}/ZoneMinder* -%{perl_vendorlib}/%{_arch}-linux-thread-multi/auto/ZoneMinder* +%{perl_vendorarch}/auto/ZoneMinder/.packlist +%{perl_vendorarch}/auto/ONVIF/.packlist %{perl_vendorlib}/ONVIF* %{perl_vendorlib}/WSDiscovery* %{perl_vendorlib}/WSSecurity* -%{perl_vendorlib}/%{_arch}-linux-thread-multi/auto/ONVIF* +%{perl_vendorlib}/WSNotification* %{_mandir}/man*/* %dir %{_libexecdir}/%{name} %{_libexecdir}/%{name}/cgi-bin @@ -166,12 +189,18 @@ rm -rf %{_docdir}/%{name}-%{version} %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload %changelog +* Thu Mar 3 2016 Andrew Bauer - 1.30.0 +- Bump version fo 1.30.0 release. + +* Tue Sep 8 2015 Andrew Bauer - 1.28.1 +- Require https, freshen dB on updates. + +* Wed Feb 18 2015 Andrew Bauer - 1.28.1 +- Include ONVIF support files + * Sun Oct 5 2014 Andrew Bauer - 1.28.0 - Bump version for 1.28.0 release. -* Sun Aug 03 2014 Andrew Bauer - 1.27 -- Include ONVIF support files - * Fri Mar 14 2014 Andrew Bauer - 1.27 - Tweak build requirements for cmake diff --git a/distros/redhat/zoneminder.el7.conf.in b/distros/redhat/zoneminder.el7.conf.in new file mode 100644 index 000000000..564e4ccbd --- /dev/null +++ b/distros/redhat/zoneminder.el7.conf.in @@ -0,0 +1,43 @@ +# +# ZoneMinder Apache configuration file +# With SSLRequire and HTTPS auto redirect +# Modify this configuration to suit your requirements +# + +# Auto Redirect HTTP requests to HTTPS +RewriteEngine On +RewriteCond %{HTTPS} !=on +RewriteRule ^/?(zm)(.*) https://%{SERVER_NAME}/$1$2 [R,L] + +Alias /zm "@ZM_WEBDIR@" + + SSLRequireSSL + Options -Indexes +MultiViews +FollowSymLinks + AllowOverride All + + # Apache 2.4 + Require all granted + + + # Apache 2.2 + Order deny,allow + Allow from all + + + +ScriptAlias /cgi-bin-zm "@ZM_CGIDIR@" + + SSLRequireSSL + AllowOverride All + Options +ExecCGI +FollowSymLinks + + # Apache 2.4 + Require all granted + + + # Apache 2.2 + Order deny,allow + Allow from all + + + diff --git a/distros/redhat/zoneminder.el7.logrotate.in b/distros/redhat/zoneminder.el7.logrotate.in new file mode 100644 index 000000000..b4919eb5e --- /dev/null +++ b/distros/redhat/zoneminder.el7.logrotate.in @@ -0,0 +1,8 @@ +@ZM_LOGDIR@/*.log { + missingok + notifempty + sharedscripts + postrotate + @BINDIR@/zmpkg.pl logrot 2> /dev/null > /dev/null || : + endscript +} diff --git a/distros/redhat/zoneminder.el7.spec b/distros/redhat/zoneminder.el7.spec new file mode 100644 index 000000000..b72e0bbe7 --- /dev/null +++ b/distros/redhat/zoneminder.el7.spec @@ -0,0 +1,427 @@ +%define zmuid $(id -un) +%define zmgid $(id -gn) +%define zmuid_final apache +%define zmgid_final apache + +%global _hardened_build 1 + +Name: zoneminder +Version: 1.30.0 +Release: 1%{?dist} +Summary: A camera monitoring and analysis tool +Group: System Environment/Daemons +# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/ +# Mootools is inder the MIT license: http://mootools.net/ +License: GPLv2+ and LGPLv2+ and MIT +URL: http://www.zoneminder.com/ + +#Source: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz +Source: ZoneMinder-%{version}.tar.gz + +BuildRequires: cmake gnutls-devel systemd-units bzip2-devel +BuildRequires: mariadb-devel pcre-devel libjpeg-turbo-devel +BuildRequires: perl(Archive::Tar) perl(Archive::Zip) perl-podlators +BuildRequires: perl(Date::Manip) perl(DBD::mysql) +BuildRequires: perl(ExtUtils::MakeMaker) perl(LWP::UserAgent) +BuildRequires: perl(MIME::Entity) perl(MIME::Lite) +BuildRequires: perl(PHP::Serialization) perl(Sys::Mmap) +BuildRequires: perl(Time::HiRes) perl(Net::SFTP::Foreign) +BuildRequires: perl(Expect) perl(Sys::Syslog) +BuildRequires: gcc gcc-c++ vlc-devel libcurl-devel libv4l-devel +BuildRequires: ffmpeg ffmpeg-devel perl(X10::ActiveHome) perl(Astro::SunTime) +# cmake needs the following installed at build time due to the way it auto-detects certain parameters +BuildRequires: httpd polkit-devel + +Requires: httpd php php-gd php-mysql mariadb-server cambozola polkit net-tools +Requires: psmisc libjpeg-turbo vlc-core libcurl +Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) +Requires: perl(DBD::mysql) perl(Archive::Tar) perl(Archive::Zip) +Requires: perl(MIME::Entity) perl(MIME::Lite) perl(Net::SMTP) perl(Net::FTP) +Requires: perl(LWP::Protocol::https) ffmpeg + +Requires(post): systemd-units systemd-sysv +Requires(post): /usr/bin/gpasswd +Requires(post): /usr/bin/less +Requires(preun): systemd-units +Requires(postun): systemd-units + +%description +ZoneMinder is a set of applications which is intended to provide a complete +solution allowing you to capture, analyse, record and monitor any cameras you +have attached to a Linux based machine. It is designed to run on kernels which +support the Video For Linux (V4L) interface and has been tested with cameras +attached to BTTV cards, various USB cameras and IP network cameras. It is +designed to support as many cameras as you can attach to your computer without +too much degradation of performance. + +%prep +%setup -q -n ZoneMinder-%{version} + +# Change the following default values +./utils/zmeditconfigdata.sh ZM_PATH_ZMS /cgi-bin-zm/nph-zms +./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes +./utils/zmeditconfigdata.sh ZM_PATH_SWAP /dev/shm +./utils/zmeditconfigdata.sh ZM_UPLOAD_FTP_LOC_DIR /var/spool/zoneminder-upload +./utils/zmeditconfigdata.sh ZM_OPT_CONTROL yes +./utils/zmeditconfigdata.sh ZM_CHECK_FOR_UPDATES no +./utils/zmeditconfigdata.sh ZM_DYN_SHOW_DONATE_REMINDER no +./utils/zmeditconfigdata.sh ZM_OPT_FAST_DELETE no + +%build +%cmake \ + -DZM_TARGET_DISTRO="el7" \ + . + +make %{?_smp_mflags} + +%install +export DESTDIR=%{buildroot} +make install + +%post +if [ $1 -eq 1 ] ; then + # Initial installation + /bin/systemctl daemon-reload >/dev/null 2>&1 || : +fi + +# Allow zoneminder access to local video sources, serial ports, and x10 +/usr/bin/gpasswd -a %{zmuid_final} video +/usr/bin/gpasswd -a %{zmuid_final} dialout + +# Disabled. SELinux policy does not work for RHEL 7. +# Create and load zoneminder selinux policy module +#echo -e "\nCreating and installing a ZoneMinder SELinux policy module. Please wait.\n" +#/usr/bin/checkmodule -M -m -o %{_docdir}/%{name}-%{version}/local_zoneminder.mod %{_docdir}/%{name}-%{version}/local_zoneminder.te > /dev/null +#/usr/bin/semodule_package -o %{_docdir}/%{name}-%{version}/local_zoneminder.pp -m %{_docdir}/%{name}-%{version}/local_zoneminder.mod > /dev/null +#/usr/sbin/semodule -i %{_docdir}/%{name}-%{version}/local_zoneminder.pp > /dev/null + +# Upgrade from a previous version of zoneminder +if [ $1 -eq 2 ] ; then + + # Add any new PTZ control configurations to the database (will not overwrite) + %{_bindir}/zmcamtool.pl --import >/dev/null 2>&1 || : + + # Freshen the database + %{_bindir}/zmupdate.pl -f >/dev/null 2>&1 || : + + # We can't run this automatically when new sql account permissions need to + # be manually added first + # Run zmupdate non-interactively + #/usr/bin/zmupdate.pl --nointeractive +fi + +# Warn the end user to read the README file +echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, read README.Centos7 to finish the\ninstallation or upgrade!\n" +echo -e "\nThe README file is located here: %{_docdir}/%{name}-%{version}.\n" + +%preun +if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + /bin/systemctl --no-reload disable zoneminder.service > /dev/null 2>&1 || : + /bin/systemctl stop zoneminder.service > /dev/null 2>&1 || : +# echo -e "\nRemoving ZoneMinder SELinux policy module. Please wait.\n" +# /usr/sbin/semodule -r local_zoneminder.pp +fi + +%postun +/bin/systemctl daemon-reload >/dev/null 2>&1 || : +if [ $1 -ge 1 ] ; then + # Package upgrade, not uninstall + /bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : +fi + +%triggerun -- zoneminder < 1.25.0-4 +# Save the current service runlevel info +# User must manually run systemd-sysv-convert --apply zoneminder +# to migrate them to systemd targets +/usr/bin/systemd-sysv-convert --save zoneminder >/dev/null 2>&1 ||: + +# Run these because the SysV package being removed won't do them +/sbin/chkconfig --del zoneminder >/dev/null 2>&1 || : +/bin/systemctl try-restart zoneminder.service >/dev/null 2>&1 || : + +%files +%defattr(-,root,root,-) +%doc AUTHORS BUGS ChangeLog COPYING LICENSE NEWS README.md distros/redhat/README.Centos7 distros/redhat/README.https distros/redhat/jscalendar-doc +%doc distros/redhat/local_zoneminder.te +%config %attr(640,root,%{zmgid_final}) /etc/zm/zm.conf +%config(noreplace) %attr(644,root,root) /etc/httpd/conf.d/zoneminder.conf +%config(noreplace) /etc/tmpfiles.d/zoneminder.conf +%config(noreplace) /etc/logrotate.d/zoneminder + +%{_unitdir}/zoneminder.service + +%{_bindir}/zma +%{_bindir}/zmaudit.pl +%{_bindir}/zmc +%{_bindir}/zmcontrol.pl +%{_bindir}/zmdc.pl +%{_bindir}/zmf +%{_bindir}/zmfilter.pl +%{_bindir}/zmpkg.pl +%{_bindir}/zmtrack.pl +%{_bindir}/zmtrigger.pl +%{_bindir}/zmu +%{_bindir}/zmupdate.pl +%{_bindir}/zmvideo.pl +%{_bindir}/zmwatch.pl +%{_bindir}/zmcamtool.pl +%{_bindir}/zmsystemctl.pl +%{_bindir}/zmtelemetry.pl +%{_bindir}/zmx10.pl +%{_bindir}/zmonvif-probe.pl + +%{perl_vendorlib}/ZoneMinder* +%{perl_vendorarch}/auto/ZoneMinder/.packlist +%{perl_vendorarch}/auto/ONVIF/.packlist +%{perl_vendorlib}/ONVIF* +%{perl_vendorlib}/WSDiscovery* +%{perl_vendorlib}/WSSecurity* +%{perl_vendorlib}/WSNotification* +%{_mandir}/man*/* +%dir %{_libexecdir}/zoneminder +%{_libexecdir}/zoneminder/cgi-bin +%dir %{_datadir}/zoneminder +%{_datadir}/zoneminder/db +%{_datadir}/zoneminder/www + +%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy +%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules + +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/events +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/images +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/sock +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/swap +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/lib/zoneminder/temp +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/log/zoneminder +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /var/spool/zoneminder-upload +%dir %attr(755,%{zmuid_final},%{zmgid_final}) /run/zoneminder + + +%changelog +* Thu Mar 3 2016 Andrew Bauer - 1.30.0 +- Bump version fo 1.30.0 release. + +* Mon Sep 7 2015 Andrew Bauer - 1.28.1 +- Require https, disable selinux module, freshen dB on updates. + +* Sun Feb 8 2015 Andrew Bauer - 1.28.1 +- Initial release for CentOS 7. + +* Sun Oct 5 2014 Andrew Bauer - 1.28.0 +- Bump version for 1.28.0 release. + +* Fri Mar 14 2014 Andrew Bauer - 1.27 +- Tweak build requirements for cmake + +* Sat Feb 01 2014 Andrew Bauer - 1.27 +- Add zmcamtool.pl. Bump version for 1.27 release. + +* Mon Dec 16 2013 Andrew Bauer - 1.26.5 +- This is a bug fixe release +- RTSP fixes, cmake enhancements, couple other misc fixes + +* Mon Oct 07 2013 Andrew Bauer - 1.26.4 +- Initial cmake build. + +* Sat Oct 05 2013 Andrew Bauer - 1.26.4 +- Fedora specific path changes have been moved to zoneminder-1.26.0-defaults.patch +- All files are now part of the zoneminder source tree. Update specfile accordingly. + +* Sat Sep 21 2013 Andrew Bauer - 1.26.3 +- Initial rebuild for ZoneMinder 1.26.3 release. + +* Fri Feb 15 2013 Fedora Release Engineering - 1.25.0-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Mon Jan 21 2013 Adam Tkac - 1.25.0-12 +- rebuild due to "jpeg8-ABI" feature drop + +* Mon Jan 7 2013 Remi Collet - 1.25.0-11 +- fix configuration file for httpd 2.4, #871502 + +* Fri Dec 21 2012 Adam Tkac - 1.25.0-10 +- rebuild against new libjpeg + +* Thu Aug 09 2012 Jason L Tibbitts III - 1.25.0-9 +- Add patch to work around v4l2 api breakage in 3.5 kernel. + +* Sun Jul 22 2012 Fedora Release Engineering - 1.25.0-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Sat Jun 23 2012 Petr Pisar - 1.25.0-7 +- Perl 5.16 rebuild + +* Wed Mar 21 2012 Jason L Tibbitts III - 1.25.0-6 +- Fix stupid thinko in sql modifications. + +* Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-5 +- Clean up macro usage. + +* Sat Feb 25 2012 Jason L Tibbitts III - 1.25.0-4 +- Convert to systemd. +- Add tmpfiles.d configuration since the initscript isn't around to create + /run/zoneminder. +- Remove some pointless executable permissions. +- Add logrotate file. + +* Wed Feb 22 2012 Jason L Tibbitts III - 1.25.0-3 +- Update README.Fedora to reference systemctl and mention timezone info in + php.ini. +- Add proper default for EYEZM_LOG_TO_FILE. + + +* Thu Feb 09 2012 Jason L Tibbitts III - 1.25.0-2 +- Rebuild for new pcre. + +* Thu Jan 19 2012 Jason L Tibbitts III - 1.25.0-1 +- Update to 1.25.0 +- Fix gcc4.7 build problems. +- Drop gcc4.4 build fixes; for whatever reason they now break the build. +- Clean up old patches. +- Force setting of ZM_TMPDIR and ZM_RUNDIR. + +* Sat Jan 14 2012 Fedora Release Engineering - 1.24.4-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-3 +- Re-add the dist-tag that somehow got lost. + +* Thu Sep 15 2011 Jason L Tibbitts III - 1.24.4-2 +- Add patch for bug 711780 - fix syntax issue in Mapped.pm. +- Undo that patch, and undo another which was the cause of the whole mess. +- Fix up other patches so ZM_PATH_BUILD is both defined and useful. +- Make sure database creation mods actually take. +- Update Fedora-specific docs with some additional info. +- Use bundled mootools (javascript, so no guideline violation). +- Update download location. +- Update the gcrypt patch to actually work. +- Upstream changed the tarball without changing the version to patch a + vulnerability, so redownload. + +* Sun Aug 14 2011 Jason L Tibbitts III - 1.24.4-1 +- Initial attempt to upgrade to 1.24.4. +- Add patch from BZ 460310 to build against libgcrypt instead of requiring the + gnutls openssl libs. + +* Thu Jul 21 2011 Petr Sabata - 1.24.3-7.20110324svn3310 +- Perl mass rebuild + +* Wed Jul 20 2011 Petr Sabata - 1.24.3-6.20110324svn3310 +- Perl mass rebuild + +* Mon May 09 2011 Jason L Tibbitts III - 1.24.3-5.20110324svn3310 +- Bump for gnutls update. + +* Thu Mar 24 2011 Jason L Tibbitts III - 1.24.3-4.20110324svn3310 +- Update to latest 1.24.3 subversion. Turns out that what upstream was calling + 1.24.3 is really just an occasionally updated devel snapshot. +- Rebase various patches. + +* Wed Mar 23 2011 Dan Horák - 1.24.3-3 +- rebuilt for mysql 5.5.10 (soname bump in libmysqlclient) + +* Tue Feb 08 2011 Fedora Release Engineering - 1.24.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Jan 25 2011 Jason L Tibbitts III - 1.24.3-1 +- Update to latest upstream version. +- Rebase patches. +- Initial incomplete attempt to disable v4l1 support. + +* Fri Jan 21 2011 Jason L Tibbitts III - 1.24.2-6 +- Unbundle cambozola; instead link to the separately pacakged copy. +- Remove BuildRoot:, %%clean and buildroot cleaning in %%install. +- Git rid of mixed space/tab usage by removing all tabs. +- Remove unnecessary Conflicts: line. +- Attempt to force short_open_tag on for the code directories. +- Move default location of sockets, swaps, logfiles and some temporary files to + make more sense and allow things to work better with a future selinux policy. +- Fix errors in README.Fedora. + +* Wed Jun 02 2010 Marcela Maslanova - 1.24.2-5 +- Mass rebuild with perl-5.12.0 + +* Fri Dec 4 2009 Stepan Kasal - 1.24.2-4 +- rebuild against perl 5.10.1 +- use Perl vendorarch and archlib variables correctly + +* Mon Jul 27 2009 Fedora Release Engineering - 1.24.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-2 +- Bump release since 1.24.2-1 was mistakenly tagged a few months ago. + +* Wed Jul 22 2009 Jason L Tibbitts III - 1.24.2-1 +- Initial update to 1.24.2. +- Rebase patches. +- Update mootools download location. +- Update to mootools 1.2.3. +- Add additional dependencies for some optional features. + +* Sat Apr 11 2009 Martin Ebourne - 1.24.1-3 +- Remove unused Sys::Mmap perl dependency RPM is finding + +* Sat Apr 11 2009 Martin Ebourne - 1.24.1-2 +- Update gcc44 patch to disable -frepo, seems to be broken with gcc44 +- Added noffmpeg patch to make building outside mock easier + +* Sat Mar 21 2009 Martin Ebourne - 1.24.1-1 +- Patch for gcc 4.4 compilation errors +- Upgrade to 1.24.1 + +* Wed Feb 25 2009 Fedora Release Engineering - 1.23.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Sat Jan 24 2009 Caolán McNamara - 1.23.3-3 +- rebuild for dependencies + +* Mon Dec 15 2008 Martin Ebourne - 1.23.3-2 +- Fix permissions on zm.conf + +* Fri Jul 11 2008 Jason L Tibbitts III - 1.23.3-1 +- Initial attempt at packaging 1.23. + +* Tue Jul 1 2008 Martin Ebourne - 1.22.3-15 +- Add perl module compat dependency, bz #453590 + +* Tue May 6 2008 Martin Ebourne - 1.22.3-14 +- Remove default runlevel, bz #441315 + +* Mon Apr 28 2008 Jason L Tibbitts III - 1.22.3-13 +- Backport patch for CVE-2008-1381 from 1.23.3 to 1.22.3. + +* Tue Feb 19 2008 Fedora Release Engineering - 1.22.3-12 +- Autorebuild for GCC 4.3 + +* Thu Jan 3 2008 Martin Ebourne - 1.22.3-11 +- Fix compilation on gcc 4.3 + +* Thu Dec 6 2007 Martin Ebourne - 1.22.3-10 +- Rebuild for new openssl + +* Thu Aug 2 2007 Martin Ebourne - 1.22.3-8 +- Fix licence tag + +* Thu Jul 12 2007 Martin Ebourne - 1.22.3-7 +- Fixes from testing by Jitz including missing dependencies and database creation + +* Sat Jun 30 2007 Martin Ebourne - 1.22.3-6 +- Disable crashtrace on ppc + +* Sat Jun 30 2007 Martin Ebourne - 1.22.3-5 +- Fix uid for directories in /var/lib/zoneminder + +* Tue Jun 26 2007 Martin Ebourne - 1.22.3-4 +- Added perl Archive::Tar dependency +- Disabled web interface due to lack of access control on the event images + +* Sun Jun 10 2007 Martin Ebourne - 1.22.3-3 +- Changes recommended in review by Jason Tibbitts + +* Mon Apr 2 2007 Martin Ebourne - 1.22.3-2 +- Standardised on package name of zoneminder + +* Thu Dec 28 2006 Martin Ebourne - 1.22.3-1 +- First version. Uses some parts from zm-1.20.1 by Corey DeLasaux and Serg Oskin diff --git a/distros/redhat/zoneminder.service.in b/distros/redhat/zoneminder.service.in new file mode 100644 index 000000000..7afe8473a --- /dev/null +++ b/distros/redhat/zoneminder.service.in @@ -0,0 +1,18 @@ +# ZoneMinder systemd unit file for CentOS 7 + +[Unit] +Description=ZoneMinder CCTV recording and security system +After=network.target mariadb.service httpd.service +Requires=mariadb.service httpd.service + +[Service] +User=@WEB_USER@ +Type=forking +ExecStart=@BINDIR@/zmpkg.pl start +ExecReload=@BINDIR@/zmpkg.pl restart +ExecStop=@BINDIR@/zmpkg.pl stop +PIDFile="@ZM_RUNDIR@/zm.pid" + +[Install] +WantedBy=multi-user.target + diff --git a/distros/redhat/zoneminder.in b/distros/redhat/zoneminder.sysvinit.in old mode 100755 new mode 100644 similarity index 100% rename from distros/redhat/zoneminder.in rename to distros/redhat/zoneminder.sysvinit.in diff --git a/distros/redhat/zoneminder.tmpfiles.in b/distros/redhat/zoneminder.tmpfiles.in new file mode 100644 index 000000000..f655a9c9f --- /dev/null +++ b/distros/redhat/zoneminder.tmpfiles.in @@ -0,0 +1,3 @@ +D @ZM_RUNDIR@ 0755 @WEB_USER@ @WEB_GROUP@ +D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@ +D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@ diff --git a/distros/ubuntu1204/NEWS b/distros/ubuntu1204/NEWS new file mode 100644 index 000000000..6200726cf --- /dev/null +++ b/distros/ubuntu1204/NEWS @@ -0,0 +1,10 @@ +zoneminder (1.28.1-1) unstable; urgency=low + + This version is no longer automatically initialize or upgrade database. + See README.Debian for details. + + Changed installation paths (please correct your web server configuration): + /usr/share/zoneminder --> /usr/share/zoneminder/www + /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin + + -- Dmitry Smirnov Tue, 31 Mar 2015 15:12:17 +1100 diff --git a/distros/ubuntu1204/README.Debian b/distros/ubuntu1204/README.Debian index b8ea413e3..2ba809fe4 100644 --- a/distros/ubuntu1204/README.Debian +++ b/distros/ubuntu1204/README.Debian @@ -1,9 +1,120 @@ -zoneminder for Debian +Zoneminder for Debian --------------------- -There is one manual step to get the web interface working. -You need to link /etc/zm/apache.conf to /etc/apache2/conf.d/zoneminder.conf, -then reload the apache config (i.e. /etc/init.d/apache2 reload) +Initializing database +--------------------- + + pv /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf +OR + cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf + + echo 'grant lock tables,alter,create,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +Hint: generate secure password with `pwgen` and update "/etc/zm/zm.conf" +accordingly. + +The following command can help to ensure that zoneminder can read its +configuration file: + + chgrp -c www-data /etc/zm/zm.conf + + +Upgrading database +------------------ + +Prior to 1.28.1 database upgrade was performed automatically. +"zoneminder" service will refuse to start with outdated database. + +Assuming that database is on "localhost" then the following command can be +used to upgrade "zm" database: + + zmupdate.pl + +Additional permissions may be required to perform upgrade: + + echo 'grant lock tables, create, alter on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +The following command prints the current version of zoneminder database: + + echo 'select Value from Config where Name = "ZM_DYN_CURR_VERSION";' \ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf --skip-column-names zm + + +Enabling service +---------------- + +By default Zoneminder service is not starting automatically and need to be +manually activated once database is configured: + +On systemd: + + sudo systemctl enable zoneminder.service + +On SysV: + + sudo update-rc.d zoneminder enable + + +Web server set-up +----------------- + +There are few manual steps to get the web interface working: + +## Apache2 + +Apache can be configured as folder "/zm" using sample .conf: + + sudo a2enconf zoneminder + +Alternatively Apache web site configuration template can be used to setup +zoneminder as "http://zoneminder": + + sudo cp -v /usr/share/doc/zoneminder/examples/apache.conf /etc/apache2/sites-available/ + sudo a2ensite zoneminder.conf + +Common configuration steps for Apache2: + + sudo a2enmod cgi + sudo service apache2 reload + + +## nginx / fcgiwrap + +Nginx needs "php5-fpm" package to support PHP and "fcgiwrap" package +for binary "cgi-bin" applications: + + sudo apt-get install php5-fpm fcgiwrap + +To enable a URL alias that makes Zoneminder available from + + http://yourserver/zm + +the following line is to be added to "server" section of a web site +configuration: + + include /usr/share/doc/zoneminder/examples/nginx.conf; + +For "default" web site it would be sufficient to include the above +statement to the file + + /etc/nginx/sites-enabled/default + +To avoid problems with feeds from multiple cameras "fcgiwrap" should be +configured to start at least as many processes as there are cameras. +It can be done by adjusting DAEMON_OPTS in "/etc/default/fcgiwrap". +Systemd users may be affected by the following bug: + + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=792705 + + +## Note: + +When Zoneminder web site is running it may be necessary to set +Options/Paths/PATH_ZMS to "/zm/cgi-bin/nph-zms" or according to chosen web +site configuration. + Changing the location for images and events ------------------------------------------- @@ -33,19 +144,17 @@ These lines in fstab could allow you to bind-mount an alternate location /dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2 /dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2 - - -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 Access to /dev/video* --------------------- -For cameras which require access to /dev/video*, zoneminder may need the +For cameras which require access to /dev/video*, zoneminder may need the www-data user added to the video group in order to see those cameras: adduser www-data video -Note that all web applications running on the zoneminder server will then have +Note that all web applications running on the zoneminder server will then have access to all video devices on the system. -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 diff --git a/distros/ubuntu1204/TODO.Debian b/distros/ubuntu1204/TODO.Debian new file mode 100644 index 000000000..9dc59613b --- /dev/null +++ b/distros/ubuntu1204/TODO.Debian @@ -0,0 +1,12 @@ + +## Separate substantial /usr/share into its own arch-all package. + +## Decide how to handle database updates. + + * Consider possibility that database may be on another machine (#469239). + * Consider dbconfig-common? Probably not (what if database is not on localhost?). + +### Run `zmupdate.pl` from service control scripts (init.d, service) on start? + + Automatic upgrade will break "one DB, many zoneminders" setup (unimportant?). + diff --git a/distros/ubuntu1204/apache.conf b/distros/ubuntu1204/apache.conf deleted file mode 100644 index 92a2b6414..000000000 --- a/distros/ubuntu1204/apache.conf +++ /dev/null @@ -1,9 +0,0 @@ -Alias /zm /usr/share/zoneminder - - - php_flag register_globals off - Options Indexes FollowSymLinks - - DirectoryIndex index.php - - diff --git a/distros/ubuntu1204/changelog b/distros/ubuntu1204/changelog index 2884e449c..74cf1d0b8 100644 --- a/distros/ubuntu1204/changelog +++ b/distros/ubuntu1204/changelog @@ -1,55 +1,573 @@ -zoneminder (1.28.0-trusty) trusty; urgency=medium +zoneminder (1.28.1+1-vivid-SNAPSHOT2015081701) vivid; urgency=medium - * Release + * include api, switch to cmake build - -- Isaac Connor Fri, 17 Oct 2014 09:25:55 -0400 + -- Isaac Connor Mon, 17 Aug 2015 10:29:23 -0400 -zoneminder (1.27.99+1-trusty-SNAPSHOT2014101401) trusty; urgency=medium - * Several PR merges in big push for 1.28.0 +zoneminder (1.28.1-8) unstable; urgency=medium - -- Isaac Connor Tue, 14 Oct 2014 09:28:29 -0400 + * Patchworks: + + New upstream "980-fix-image-size.patch". + + New "default_cgi-path.patch" to correct default ZM_PATH_ZMS. + * postinst: set "root" as group owner for "/var/log/zm" to silence + logrotate warnings. + * Minor correction to README.Debian. -zoneminder (1.27.99+1-trusty-SNAPSHOT2014092601) trusty; urgency=medium + -- Dmitry Smirnov Sun, 16 Aug 2015 19:19:50 +1000 - * style updates and db fixes for database logging filters +zoneminder (1.28.1-7) unstable; urgency=medium - -- Isaac Connor Fri, 26 Sep 2014 14:44:45 -0400 + * Build-Depends += "cakephp (<< 3.0.0~)"; + Zoneminder is not compatible with latest CakePHP. + * Handle conffile removal from maintscript. + * rules: build man pages reproducibly. + * gbp.conf: renamed old style config section [git-dch] to [dch]. + * README + + added instructions to update owner of the "/etc/zm/zm.conf" + (Closes: #789327). + + zmupdate.pl needs CREATE rights. + + added note about required number of "fcgiwrap" workers. + * New upstream patch: "zmtrigger-plus.patch". -zoneminder (1.27.99+1-trusty-SNAPSHOT2014090801) trusty; urgency=medium + -- Dmitry Smirnov Mon, 20 Jul 2015 16:30:15 +1000 - * several segfault fixes for local cameras +zoneminder (1.28.1-6) unstable; urgency=low - -- Isaac Connor Mon, 08 Sep 2014 16:56:57 -0400 + * New "zoneminder-doc" and "zoneminder-dbg" packages. -zoneminder (1.27.99+1-trusty-SNAPSHOT2014090701) trusty; urgency=medium + -- Dmitry Smirnov Sun, 19 Apr 2015 14:50:41 +1000 - * Fix segfaults for local cameras, also now includes the systemd support patch +zoneminder (1.28.1-5) unstable; urgency=low - -- Isaac Connor Sun, 07 Sep 2014 17:19:01 -0400 + * Move handling of "/var/run/zm" and "/tmp/zm" from .service into .tmpfile. + Let dh_installinit do the job. Thanks, Andrew Bauer. + * Use dh_apache2 to install Apache conf file; remove old conf and symlink. + * Promote "libapache2-mod-php5 | php5-fpm" to Recommends. + * Build-Depends: + + dh-linktree + + cakephp (>= 2.6.3) + + libjs-jquery + + libjs-mootools + * Depends: + - libjs-jquery + - libjs-mootools + * Build-time replace bundled CakePHP with system one using "dh-linktree". + * Use "dh-linktree" to handle mootools and jquery symlinks. -zoneminder (1.27.99+1-trusty-SNAPSHOT2014082102) trusty; urgency=medium + -- Dmitry Smirnov Sun, 19 Apr 2015 11:45:01 +1000 - * Fix UI inputs for v4l multibuffer and captures per frame +zoneminder (1.28.1-4) unstable; urgency=low - -- Isaac Connor Thu, 21 Aug 2014 12:03:31 -0400 + * New patch to fix HTML export with USE_DEEP_STORAGE (closes: #723706). + * New "783.patch" to describe potential data loss in ZM_USE_DEEP_STORAGE. + * New patch to change default date format to region-neutral ISO notation + with time zone. + * Build sphinx documentation: + + Install "zoneminder.1" man page. + + Build-Depends += "python-sphinx | python3-sphinx" + + Added commented "zoneminder-doc" package. + + Added "docs.patch" to unlink distro-specific installation docs. + * rules: + + set ZM_CONTENTDIR, ZM_SOCKDIR and ZM_TMPDIR. + + remove mistakengly installed Perl module templates. + * Updated startup scripts to create ZM_TMPDIR. + * Hurd improvements: + + New patch to add PATH_MAX definitions. + + Build without MMAP support on Hurd. + + libsys-mmap-perl [!hurd-any]. -zoneminder (1.27.99+1-trusty-SNAPSHOT2014082101) trusty; urgency=medium + -- Dmitry Smirnov Mon, 06 Apr 2015 18:18:55 +1000 - * fall back to Config table values for V4l MultiBUffer and Captures Per Frame - * add mention of monitor page settings for thse in the config table +zoneminder (1.28.1-3) unstable; urgency=low - -- Isaac Connor Thu, 21 Aug 2014 10:04:46 -0400 + * Updated Apache2 and nginx configuration templates to support CGI. + * Updated README.Debian to document cgi-bin setup. + * Removed "/usr/share/zoneminder/www/cgi-bin" symlink. + * Added "apache2.patch" to correct Apache2 site configuration example. + * control: Suggests += "fcgiwrap". + * rules: added dh_systemd overrides to prevent automatic service + activation and start. + * Added note about manual service activation to README.Debian + (Closes: #781733). -zoneminder (1.27.99+1-precise-SNAPSHOT2014080601) precise; urgency=medium + -- Dmitry Smirnov Thu, 02 Apr 2015 23:20:20 +1100 - * improve error messages - * Make zmupdate re-run the most recent patch so that people running the daily builds get their db updates +zoneminder (1.28.1-2) unstable; urgency=low - -- Isaac Connor Wed, 06 Aug 2014 20:20:20 -0400 + * Removed word "Linux" from short package description. + * Build-Depends: do not require "libv4l-dev" on Hurd i.e. [!hurd-any]. + * Added run-time Perl Depends: + + libdbd-mysql-perl + + libimage-info-perl + + libmodule-load-conditional-perl + + libnet-sftp-foreign-perl + + liburi-encode-perl + * Prepare for package split: added commented "libzoneminder-perl" + and "zoneminder-dbg" packages to "debian/control". + * rules: do not install worthless ".packlist" file. + * Updated "libv4l1-videodev.h.patch" to fix v4lv1 detection in CMake. -zoneminder (1.27.0+1-trusty-v4ltomonitor-1) trusty; urgency=high + -- Dmitry Smirnov Thu, 02 Apr 2015 13:25:19 +1100 - * Snapshot release - +zoneminder (1.28.1-1) unstable; urgency=low - -- Isaac Connor Wed, 09 Jul 2014 21:35:29 -0400 + [ Dmitry Smirnov ] + * New upstream release [February 2015]. + * Upload to unstable. + * Disabled automatic database upgrades: post(inst|rm) scripts no longer + touch database or do unexpected stuff (Closes: #779254). + See README.Debian for details. + * Updated installation paths: + + /usr/share/zoneminder --> /usr/share/zoneminder/www + + /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin + * Added logrotate config (Closes: #544826). + Thanks, Alberto Reyes. + * Native systemd service; "--with systemd" added to dh. + * Build with CMake instead of autoconf; rules clean-up. + * Build with all hardening. + * Build and install "zmupdate.pl.1" man page. + * Added nginx/php5-fpm configuration example. + * Install upstream "apache.conf" example. + * Described setup of Zoneminer web site and database in README.Debian. + * Install "/etc/zm/zm.conf" with tighter permissions. + * Added TODO.Debian. + * Added "debian/clean"; "debian/gbp.conf"; bug-presubj. + * Remove bundled Cake tests to take ~5 MB off big-usr-share. + * Standards-Version: 3.9.6; compat/debhelper to version 9. + * Vcs links to new git repository at collab-maint. + * Build-Depends: + + dh-systemd + + libgcrypt11-dev --> libgcrypt-dev + + libcurl4-gnutls-dev + + libvlc-dev + + policykit-1 (required by "zmsystemctl.pl") + - dh-autoreconf, autoconf, automake + * Depends: + - apache2 + - libapache2-mod-php5 (moved to Suggests) + - libpcre3 (invalid) + - libmodule-load-perl (obsolete; replaced with perl-modules) + - libarchive-tar-perl (obsolete; replaced with perl-modules) + - mysql-server (moved to Recommends, Closes: #759504). + - php5 + + libav-tools + + libjs-jquery (replaces bundled component) + + libjs-mootool (replaces bundled component) + + libjson-any-perl (Closes: #690803). + + perl-modules (Closes: #745819). + * Recommends: + + apache2 | httpd + + mysql-server | virtual-mysql-server (Closes: #732874). + * Suggests: + + libapache2-mod-php5 | php5-fpm + + logrotate + * Refreshed, renamed and re-ordered patches; added DEP-3 headers. + * Removed "vendor_perl" patch (applied-upstream). + * New patches: + + cmake-fix-confpath.patch + + cmake-gnutls.patch + + cmake-nossl.patch + + cmake.patch + + format-hardening.patch + + pod_man_fixes.patch + + pod_name_fixes.patch + + pod_zmupdate-to-pod2usage.patch + * Lintianisation (incomplete): + - extra-license-file + - init.d-script-missing-lsb-description + - init.d-script-does-not-source-init-functions + - privacy-breach-generic + - package-contains-empty-directory + - manpage-has-errors-from-pod2man + - manpage-has-bad-whatis-entry + - quilt-patch-missing-description + - no-dep5-copyright + * Lintian-overrides: + + unusual-interpreter usr/bin/zmsystemctl.pl #!/usr/bin/pkexec + + script-not-executable usr/share/zoneminder/www/api/* + + script-with-language-extension usr/bin/*.pl + + source-is-missing web/tools/mootools/mootools-*-yc.js + + source-is-missing web/skins/*/js/jquery-1.4.2.min.js + + source-contains-prebuilt-javascript-object + * Renamed files in "debian". + * watch: dfsg repacksuffix and dversionmangle. + * "debian/copyright" to Copyright-Format-1.0. + * Set myself as new Maintainer (Closes: #760314). + + [ Vagrant Cascadian ] + * Removed obsolete DM-Upload-Allowed flag. + * Update debian/watch to use tarballs from github. + * Add Build-Depends on libgcrypt11-dev (Closes: #745819). + * Use canonical alioth Vcs-Hg URL. + * debian/control: Add Build-Depends: libpolkit-gobject-1-dev. + * Removed configure flag "--enable-crashtrace=no", which is no longer + present upstream. + + -- Dmitry Smirnov Tue, 31 Mar 2015 15:11:13 +1100 + +zoneminder (1.26.5-3.1) experimental; urgency=low + + * Non-maintainer upload. + * Add libav10.patch and compile against libav10 (Closes: #739461) + + -- Reinhard Tartler Wed, 19 Mar 2014 00:31:22 +0000 + +zoneminder (1.26.5-3) unstable; urgency=low + + + * Previous release still didn't build on PPC - this has been corrected. + (Closes: #736516) + + -- Peter Howard Tue, 4 Feb 2014 02:02:10 +1000 + +zoneminder (1.26.5-2) unstable; urgency=low + + * Remove dependency on ffmpeg + (Closes: #721161) + + * Builds again on non-x86 target architectures. + + -- Peter Howard Thu, 23 Jan 2014 01:02:10 +1000 + +zoneminder (1.26.5-1) unstable; urgency=low + + * New upstream version + (Closes: #694131) + * Change Build-Depends on libgnutls-dev to libgnutls-openssl-dev + (Closes: #731560) + -- Peter Howard Tue, 17 Dec 2013 01:02:10 +1000 + +zoneminder (1.25.0-4) unstable; urgency=high + + * Add CVE-2013-0232 patch + [SECURITY] CVE-2013-0232: Shell escape commands with untrusted content. + Thanks to James McCoy (Closes: #698910) + Thanks also to Salvatore Bonaccorso + + -- Peter Howard Tue, 12 Jun 2013 12:02:10 +1000 + +zoneminder (1.25.0-3) unstable; urgency=low + + * debian/rules: Export CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS, to ensure + hardening build flags are enabled. + + -- Vagrant Cascadian Tue, 28 Aug 2012 12:10:03 -0700 + +zoneminder (1.25.0-2) unstable; urgency=low + + [ Vagrant Cascadian ] + * Add a patch to disable checking for updated versions by default, as + upgrades should happen through package management. + * Use dpkg-buildflags in debian/rules to set default compiler flags. + * Ensure zoneminder is stopped before starting (Closes: #657407). + + [ Peter Howard ] + * Fix postinst to add permission for table creation during upgrade + (Closes: #657407). + + -- Vagrant Cascadian Thu, 23 Aug 2012 12:40:34 -0700 + +zoneminder (1.25.0-1.1) unstable; urgency=low + + * Non-maintainer upload. + * Fix "ftbfs with GCC-4.7": add patch Fix-FTBFS-with-gcc-4.7 from Cyril + Brulebois: fix missing includes. + (Closes: #667428) + + -- gregor herrmann Sun, 13 May 2012 17:02:21 +0200 + +zoneminder (1.25.0-1) unstable; urgency=low + + * Fix typo in libv4l1-videodev.h patch that caused v4l1 support to be + dropped. + * Fail to build if version in postinst doesn't match upstream version. + * Add Build-Depends: libavdevice-dev to fix MPEG streaming (Closes: #515558). + * debian/rules: Convert to using debhelper overrides. + * Set debian/compat to 7. + * Simplify debian/watch file. + * Refresh debian/patches/use_libjs-mootools. + * Refresh debian/patches/libv4l1-videodev.h. + * Remove dependencies on php4 and related packages. + * Remove build-dependencies on libmysqlclient14-dev and + libmysqlclient15-dev. + * Update Build-Depends to use libjpeg-dev instead of libjpeg62-dev + (Closes: #647114). + * Add patch to fix build by testing for C headers rather than C++ headers. + Thanks to Ryan Niebur. (Closes: #654230) + * Add a patch to fix build problems caused by API changes in libav 0.8. + Thanks again to Ryan Niebur. (Closes: #654230) + + -- Vagrant Cascadian Mon, 16 Jan 2012 11:58:05 -0800 + +zoneminder (1.24.4-1) unstable; urgency=low + + [ Peter Howard ] + * Initial release of 1.24.4 (Closes: #634985). + - Fix 32/64-bit type declarations (Closes: #614404). + * Update patches. + + [ Vagrant Cascadian ] + * Add patch to fix FTBFS by using libv4l1-videodev.h from libv4l-dev. + Thanks to Andreas Metzler for reporting the issue. + (Closes: #619813). + * Document adding the www-data user to the video group in README.Debian. + (Closes: #611324) + * Depend on libsys-mmap-perl to enable mapped memory support. + (Closes: #607331) + * Update libjs-mootools patch to use -nc variants (Closes: #635075). + * Depend on javascript-common, to ensure that /javascript is available in + the web server. + * Set the upstream version in postinst at build time. + * Use dh-autoreconf to properly clean up autogenerated files during build. + * Add Vcs-HG to debian/control. + * Add Build-Depends: libv4l-dev, libbz2-dev, dh-autoreconf, libsys-mmap-perl. + + -- Vagrant Cascadian Sun, 24 Jul 2011 16:44:30 +0200 + +zoneminder (1.24.2-9) unstable; urgency=low + + * Apply patch from Ubuntu to fix FTBFS with ffmpeg 0.6: + - Add -D__STDC_CONSTANT_MACROS to CPPFLAGS (closes: 614080). + * Update Standards-Version to 3.9.1, no changes necessary. + + -- Vagrant Cascadian Sun, 20 Feb 2011 23:43:02 -0800 + +zoneminder (1.24.2-8) unstable; urgency=medium + + [ Vagrant Cascadian ] + * Apply patch to fix V4L2 cameras without crop support (closes: #608790). + Thanks to piratebab. + * Add preinst script which aborts if dangerous symlinks exist. + (closes: #608793) + + [ Peter Howard ] + * Added to README.Debian with info about images and events directories. + (closes: #608793) + + -- Vagrant Cascadian Sat, 15 Jan 2011 19:39:26 -0800 + +zoneminder (1.24.2-7) unstable; urgency=medium + + * Do not set ownership of /var/cache/zoneminder when upgrading, which fixes a + regression causing upgrades to take inordinately long with large + installations (closes: #597040). + + -- Vagrant Cascadian Fri, 17 Sep 2010 11:24:41 -0700 + +zoneminder (1.24.2-6) unstable; urgency=low + + * Only remove database on purge. This requires only creating the database if + it doesn't already exist, and upgrading the database only if the database + is an older version (closes: #497107). + + * Do not prompt the user on database upgrades by using the --nointeractive + flag when calling zmupdate.pl from postinst (closes: #595902). + + -- Vagrant Cascadian Fri, 10 Sep 2010 10:06:06 -0700 + +zoneminder (1.24.2-5) unstable; urgency=low + + [ Peter Howard ] + * Add zip dependency + (closes: #494261) + * Add debian/watch file + (closes: #545552) + * Use packaged libjs-mootools + (closes: #585590) + * Miscellaneous cleanups + + [ Vagrant Cascadian ] + * Add vagrant@debian.org as uploader + * Update Standards-Version to 3.9.0, no changes necessary. + + -- Vagrant Cascadian Fri, 23 Jul 2010 18:12:50 -0500 + +zoneminder (1.24.2-4.1) unstable; urgency=low + + * Non-maintainer upload. + * Fix "package removed, processes still running": apply patch to + debian/postinst by Vagrant Cascadian: use invoke-rc.d and run + mysql-related actions only when mysql is running (closes: #583648). + + -- gregor herrmann Thu, 01 Jul 2010 19:47:10 +0200 + +zoneminder (1.24.2-4) unstable; urgency=high + * Update init.d to list mysql dependency + (closes: #583505) + * Change depenency from libmime-perl to libmime-tools-perl + (closes: #585589) + * Problems in changelog format fixed + (closes: #585592) + * Fix debian-rules-ignores-make-clean-error + (closes: #585593) + -- Peter Howard Mon, 14 jun 2010 15:02:10 +1000 + +zoneminder (1.24.2-3) unstable; urgency=high + * Changes symbols to build with libjpeg8 + (closes: #565326, #568327) + * Note: location of all perl files should have been fixed in previous release + (closes: #553096) + -- Peter Howard Mon, 26 apr 2010 15:02:10 +1000 + +zoneminder (1.24.2-2) unstable; urgency=high + + * Remove custom perl parth from zmpkg.pl, fix location of manpages. + (closes: #551746, #553092) + * Fix GCC4.4 bug + (closes: #531717) + * Fix potential bug in postinst script + + -- Peter Howard Sat, 14 Nov 2009 15:02:10 +1000 + +zoneminder (1.24.2-1) unstable; urgency=high + + * Initial release of zoneminder 1.24.2 + -- Peter Howard Fri, 11 Sep 2009 07:02:50 +1000 + +zoneminder (1.24.1-1) unstable; urgency=high + + * Initial release of zoneminder 1.24.1, closing CVE-2008-3882, + CVE-2008-3881, CVE-2008-3880 + (closes: #497640) + * Change syslog dependency to rsyslog. + (closes: #526918) + * Add missing perl depenency. + * Restore patch to disable "check for updates" by default. + * Removed spurious '$' in init script. + (closes: #486064) + * Change permission of zm.conf from 0600 to 0400 for CVE-2008-6755 + (closes: #528252) + -- Peter Howard Sat, 16 May 2009 07:02:50 +1000 + +zoneminder (1.23.3-4) unstable; urgency=high + + * update to get it building with latest unstable. Thanks to waldi@debian.org + (closes: #517569) + -- Peter Howard Thu, 16 Apr 2009 01:02:50 +1000 + +zoneminder (1.23.3-3) unstable; urgency=high + + * ffmpeg confirmed working + (closes: #475145) + * Fix upgrade problem intrudouced in 1.23.3-1 + (closes: #481637) + * Include libmime-lite-perl in dependencies + (closes: #486312) + -- Peter Howard Thu, 18 Sep 2008 01:02:50 +1000 + +zoneminder (1.23.3-2) unstable; urgency=high + + * ffmpeg finally working? + + -- Peter Howard Wed, 13 Aug 2008 01:02:50 +1000 + +zoneminder (1.23.3-1) unstable; urgency=high + + * Initial version for 1.23.3 - security fix. + (closes: #479034) + + -- Peter Howard Wed, 19 Mar 2008 01:02:50 +1000 + +zoneminder (1.23.2-2) unstable; urgency=low + + * Update to init.d + (closes: #468856) + * Add dependency on logging daemon + (closes: #471277) + + -- Peter Howard Wed, 19 Mar 2008 01:02:50 +1000 + +zoneminder (1.23.2-1) unstable; urgency=low + + * Initial version for 1.23.2 + (closes: #464152) + * Zoneminder 1.23.2 upstream includes fix for GCC 4.3 + (closes: #454980) + * Includes ffmpeg patch by Alexander Kushnirenko + + -- Peter Howard Sat, 01 Mar 2008 16:02:50 +1000 + +zoneminder (1.22.3-10) unstable; urgency=low + + * Fix bug introduced in -9 where perl is put under /usr/local + (closes: #457507) + + -- Peter Howard Mon, 24 Dec 2007 16:02:50 +1000 + +zoneminder (1.22.3-9) unstable; urgency=low + + * Starting zoneminder via init script now invokes "zmfix -a" + (closes: #481637) + * Change apache2-mpm-prefork dependency to apache2 + * Temp dir for export under /var/cache/zoneminder (but linked back to + /usr/share/zoneminder for now) + * Redo use of gnutls rather than openssl for md5 hashes + + -- Peter Howard Mon, 10 Dec 2007 16:02:50 +1000 + +zoneminder (1.22.3-8) unstable; urgency=low + + * Build now includes libpcre3 + (closes: #437533) + * "Monitor Presets" patch now applied to package during build. + + -- Peter Howard Sat, 18 Aug 2007 14:35:23 +1000 + +zoneminder (1.22.3-7) unstable; urgency=low + + * Turn off debug trace and crash dump on build + (closes:#414857,#414891) + * Additional perl libraries added in dependencies + (closes:#416291) + * Change preferred PHP version from 4 to 5 + -- Peter Howard Sun, 29 Jul 2007 15:11:13 +1000 + +zoneminder (1.22.3-6) unstable; urgency=low + + * Removed a similar bash only statement from zmpkg.pl + (closes:414882) + + -- Peter Howard Sat, 14 Apr 2007 11:46:56 +1000 + +zoneminder (1.22.3-5) unstable; urgency=low + + * Installs with "phone home" feature turned off by default, and permissions + on /etc/zm/zm.conf fixed (now the 0600 it s hould be) + (closes:415349) + * Removed "stupid bash-ism" on mysqld check in postinst file. + + -- Peter Howard Fri, 6 Apr 2007 15:50:00 +1000 + +zoneminder (1.22.3-4) unstable; urgency=low + + * Put libmysqlclient-15-dev in front of -14-dev so sbuild works + (closes: #414410) + + -- Peter Howard Mon, 12 Mar 2007 11:38:56 +1100 + +zoneminder (1.22.3-3) unstable; urgency=low + + * Clean up of postinstall, postrm ; user "zm" definitely was a mistake + * Also in postinstall: check and start MySQL if it's not running. + * init.d script now checks if zoneminder isn't running and still returns 0 + (which helps uninstalling) + * Addition of php5 dependency options as well as php4. + + -- Peter Howard Mon, 26 Feb 2007 10:40:52 +1100 + +zoneminder (1.22.3-2) unstable; urgency=low + + * Added zmuser in the mysql creation; this should fix the install problem + for people, but needs to be cleaned up (in -3) + + -- Peter Howard Fri, 16 Feb 2007 14:16:03 +1100 + +zoneminder (1.22.3-1) unstable; urgency=low + + * Initial Version. (closes: #248393) + * Patched out use of openssl; uses gnutls instead for MD5 hashes. + * Removed MakeMaker-inserted Perl licensing (with authors permission) in + various scripts; replaced with GPL. + + -- Peter Howard Wed, 7 Feb 2007 14:09:01 +1100 diff --git a/distros/ubuntu1204/clean b/distros/ubuntu1204/clean new file mode 100644 index 000000000..941ef2a3a --- /dev/null +++ b/distros/ubuntu1204/clean @@ -0,0 +1,3 @@ +.gitattributes +web/api/.gitattributes +web/api/.gitignore diff --git a/distros/ubuntu1204/conf/apache2/zoneminder.conf b/distros/ubuntu1204/conf/apache2/zoneminder.conf new file mode 100644 index 000000000..fa596d5ab --- /dev/null +++ b/distros/ubuntu1204/conf/apache2/zoneminder.conf @@ -0,0 +1,20 @@ +# Remember to enable cgi mod (i.e. "a2enmod cgi"). +ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" + + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + AllowOverride All + Require all granted + + +Alias /zm /usr/share/zoneminder/www + + php_flag register_globals off + Options Indexes FollowSymLinks + + DirectoryIndex index.php + + + + + AllowOverride All + diff --git a/distros/ubuntu1204/control b/distros/ubuntu1204/control index 9c4dc5995..d7c2232cf 100644 --- a/distros/ubuntu1204/control +++ b/distros/ubuntu1204/control @@ -1,16 +1,72 @@ Source: zoneminder Section: net Priority: optional -Maintainer: Isaac Connor -Build-Depends: debhelper (>= 7.0.50), autoconf, automake, dpatch, libphp-serialization-perl, libgnutls-dev, libmysqlclient-dev | libmariadbclient-dev, libdbd-mysql-perl, libdate-manip-perl, libwww-perl, libjpeg8-dev, libpcre3-dev, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libv4l-dev (>= 0.8.3), libbz2-dev, libtool, libsys-mmap-perl, ffmpeg | libav-tools, libnetpbm10-dev, libavdevice-dev, libdevice-serialport-perl, libpcre3, libarchive-zip-perl, libmime-lite-perl, libjpeg8, dh-autoreconf, libvlccore-dev, libvlc-dev, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libgcrypt11-dev, libpolkit-gobject-1-dev -Standards-Version: 3.9.2 +Maintainer: Dmitry Smirnov +Uploaders: Vagrant Cascadian +Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree + ,cmake + ,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev + ,libbz2-dev + ,libgcrypt-dev + ,libcurl4-gnutls-dev + ,libgnutls-openssl-dev + ,libjpeg8-dev|libjpeg9-dev|libjpeg62-turbo-dev, + ,libmysqlclient-dev + ,libpcre3-dev + ,libpolkit-gobject-1-dev + ,libv4l-dev (>= 0.8.3) [!hurd-any] + ,libvlc-dev + ,libdate-manip-perl + ,libdbd-mysql-perl + ,libphp-serialization-perl + ,libsys-mmap-perl [!hurd-any] + ,libwww-perl + ,libdata-uuid-perl +# Unbundled (dh_linktree): + ,libjs-jquery + ,libjs-mootools +Standards-Version: 3.9.4 +Homepage: http://www.zoneminder.com/ +Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git +Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git Package: zoneminder Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, php5, php5-mysql|php5-mysqlnd, libphp-serialization-perl, libdate-manip-perl, libmime-lite-perl, libmime-tools-perl, mariadb-client|mysql-client, libwww-perl, libarchive-tar-perl, libarchive-zip-perl, libdevice-serialport-perl, libpcre3, ffmpeg | libav-tools, rsyslog | system-log-daemon, libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, netpbm, libavdevice53 | libavdevice55, libjpeg8, zip, libnet-sftp-foreign-perl, libio-pty-perl, libexpect-perl, libvlccore5 | libvlccore7 | libvlccore8, libvlc5, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev, libpolkit-gobject-1-0, liburi-encode-perl -Recommends: mysql-server|mariadb-server -Description: A video camera security and surveillance solution - ZoneMinder is intended for use in single or multi-camera video security +Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} + ,javascript-common + ,libav-tools|ffmpeg + ,libdate-manip-perl + ,libdbd-mysql-perl + ,libmime-lite-perl + ,libmime-tools-perl + ,libphp-serialization-perl + ,libmodule-load-conditional-perl + ,libnet-sftp-foreign-perl +# ,libzoneminder-perl (= ${source:Version}) + ,libarchive-zip-perl + ,libdbd-mysql-perl + ,libdevice-serialport-perl + ,libimage-info-perl + ,libjson-any-perl + ,libsys-mmap-perl [!hurd-any] + ,liburi-encode-perl + ,libwww-perl + ,libdata-uuid-perl + ,mysql-client | virtual-mysql-client + ,perl-modules + ,php5-mysql, php5-gd + ,policykit-1 + ,rsyslog | system-log-daemon + ,zip + ,libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl + , libsys-cpu-perl, libsys-meminfo-perl +Recommends: ${misc:Recommends} + ,libapache2-mod-php5 | php5-fpm + ,mysql-server | virtual-mysql-server + ,zoneminder-doc (>= ${source:Version}) +Suggests: fcgiwrap, logrotate +Description: video camera security and surveillance solution + ZoneMinder is intended for use in single or multi-camera video security applications, including commercial or home CCTV, theft prevention and child or family member or home monitoring and other care scenarios. It supports capture, analysis, recording, and monitoring of video data coming @@ -21,14 +77,42 @@ Description: A video camera security and surveillance solution and surveillance. It can also be integrated into a home automation system via X.10 or other protocols. -Package: zoneminder-dbg -Architecture: any -Depends: - zoneminder (= ${binary:Version}), - ${misc:Depends} -Description: debugging syumbols for zoneminder. - ZoneMinder is a video camera security and surveillance solution. - ZoneMinder is intended for use in single or multi-camera video security +#Package: libzoneminder-perl +#Section: perl +#Architecture: all +#Multi-Arch: foreign +#Depends: ${misc:Depends}, ${perl:Depends} +# ,libarchive-zip-perl +# ,libdbd-mysql-perl +# ,libdevice-serialport-perl +# ,libimage-info-perl +# ,libjson-any-perl +# ,libsys-mmap-perl [!hurd-any] +# ,liburi-encode-perl +# ,libwww-perl +#Description: ZoneMinder Perl libraries +# ZoneMinder is intended for use in single or multi-camera video security +# applications, including commercial or home CCTV, theft prevention and child +# or family member or home monitoring and other care scenarios. It +# supports capture, analysis, recording, and monitoring of video data coming +# from one or more video or network cameras attached to a Linux system. +# ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom +# cameras using a variety of protocols. It is suitable for use as a home +# video security system and for commercial or professional video security +# and surveillance. It can also be integrated into a home automation system +# via X.10 or other protocols. +# . +# This package provides ZoneMinder Perl libraries; it can be used to +# write custom interfaces as well. + +Package: zoneminder-doc +Section: doc +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends}, ${sphinxdoc:Depends} +Suggests: www-browser +Description: ZoneMinder documentation + ZoneMinder is intended for use in single or multi-camera video security applications, including commercial or home CCTV, theft prevention and child or family member or home monitoring and other care scenarios. It supports capture, analysis, recording, and monitoring of video data coming @@ -38,3 +122,24 @@ Description: debugging syumbols for zoneminder. video security system and for commercial or professional video security and surveillance. It can also be integrated into a home automation system via X.10 or other protocols. + . + This package provides ZoneMinder documentation in HTML format. + +Package: zoneminder-dbg +Section: debug +Priority: extra +Architecture: any +Depends: zoneminder (= ${binary:Version}), ${misc:Depends} +Description: Zoneminder -- debugging symbols + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + . + This package provides debugging symbols diff --git a/distros/ubuntu1204/copyright b/distros/ubuntu1204/copyright index a177502a0..c48025a25 100644 --- a/distros/ubuntu1204/copyright +++ b/distros/ubuntu1204/copyright @@ -1,22 +1,174 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ZoneMinder +Upstream-Contact: Philip Coombes +Source: https://github.com/ZoneMinder/ZoneMinder +Comment: + This package was originally debianized by matrix + on Mon, 7 Mar 2005 02:07:57 -0500. + It was re-done for submission to the Debian project by Peter Howard + on Fri, 8 Dec 2006 10:19:43 +1100 +Files-Excluded: + web/skins/*/js/jquery-* + web/tools/mootools/*-yc.js + +Files: * +Copyright: 2001-2014 Philip Coombes + 2008 Brian Rudy + 2014 Vincent Giovannone + 2013 Tim Craig + 2003-2008 Corey DeLasaux + 2001-2010 Chris Kistner +License: GPL-2+ + +Files: distros/* +Copyright: 2001-2008 Philip Coombes + 2014 Isaac Connor + 2005 Serg Oskin +License: GPL-2+ + +Files: web/skins/*/js/jquery-* +Copyright: 2010 John Resig + 2010 The Dojo Foundation +License: GPL-2 or Expat +Comment: + Dual licensed under the MIT or GPL Version 2 licenses. + http://jquery.org/license + . + Includes Sizzle.js http://sizzlejs.com/ + Released under the MIT, BSD, and GPL Licenses. + +Files: web/tools/mootools/*.js +Copyright: 2009 Marcelo Jorge Vieira (metal) + 2006-2010 Valerio Proietti (http://mad4milk.net/) +License: Expat + +Files: web/api/* +Copyright: 2005-2013 Cake Software Foundation, Inc. (http://cakefoundation.org) +License: Expat + +Files: + cmake/Modules/CheckPrototypeDefinition*.cmake + cmake/Modules/FindGLIB2.cmake + cmake/Modules/FindPolkit.cmake + cmake/Modules/GNUInstallDirs.cmake Copyright: + 2005-2011 Kitware, Inc. + 2010-2011 Andreas Schneider + 2009 Dario Freddi + 2008 Laurent Montel, + 2011 Nikita Krupen'ko +License: BSD-3-clause + 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. + . + * The names of Kitware, Inc., the Insight Consortium, or the names of + any consortium members, or of any contributors, may not be used to + endorse or promote products derived from this software without + specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER 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 AUTHORS 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. -Copyright 2002 Philip Coombes +Files: cmake/Modules/FindPerlModules.cmake +Copyright: 2012 Iowa State University +License: Boost-1.0 + Boost Software License - Version 1.0 - August 17th, 2003 + . + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to + do so, all subject to the following: + . + The copyright notices in the Software and this entire statement, including + the above license grant, this restriction and the following disclaimer, + must be included in all copies of the Software, in whole or in part, and + all derivative works of the Software, unless such copies or derivative + works are solely in the form of machine-executable object code generated by + a source language processor. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. -License: +Files: debian/* +Copyright: 2015 Dmitry Smirnov + 2007-2014 Peter Howard + 2010-2012 Vagrant Cascadian + 2001-2008 Philip Coombes +License: GPL-2+ -This package 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. +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. -This package 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. +License: GPL-2+ + This package 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 package 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 package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". -You should have received a copy of the GNU General Public -License along with this package; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - -On Debian GNU/Linux systems, the text of the GPL can be found in -/usr/share/common-licenses/GPL. +License: GPL-2 + This package 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; version 2 of the License. + . + This package 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 package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". diff --git a/distros/ubuntu1204/examples/nginx.conf b/distros/ubuntu1204/examples/nginx.conf new file mode 100644 index 000000000..5636ca3e1 --- /dev/null +++ b/distros/ubuntu1204/examples/nginx.conf @@ -0,0 +1,32 @@ +location /zm/cgi-bin { + gzip off; + alias /usr/lib/zoneminder/cgi-bin; + + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_pass unix:/var/run/fcgiwrap.socket; +} + +location /zm { +# if ($scheme ~ ^http:){ +# rewrite ^(.*)$ https://$host$1 permanent; +# } + + gzip off; + alias /usr/share/zoneminder/www; + index index.php; + + location ~ \.php$ { + if (!-f $request_filename) { return 404; } + expires epoch; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_index index.php; + fastcgi_pass unix:/var/run/php5-fpm.sock; + } + + location ~ \.(jpg|jpeg|gif|png|ico)$ { + access_log off; + expires 33d; + } +} diff --git a/distros/ubuntu1204/gbp.conf b/distros/ubuntu1204/gbp.conf new file mode 100644 index 000000000..4608913d9 --- /dev/null +++ b/distros/ubuntu1204/gbp.conf @@ -0,0 +1,7 @@ + +[dch] +id-length = 0 + +[import-orig] +pristine-tar = False +merge = False diff --git a/distros/ubuntu1204/libzoneminder-perl.install b/distros/ubuntu1204/libzoneminder-perl.install new file mode 100644 index 000000000..67191d9cf --- /dev/null +++ b/distros/ubuntu1204/libzoneminder-perl.install @@ -0,0 +1,2 @@ +usr/share/man/man3 +usr/share/perl5 diff --git a/distros/ubuntu1204/patches/default_cgi-path.patch b/distros/ubuntu1204/patches/default_cgi-path.patch new file mode 100644 index 000000000..8bfc2ba06 --- /dev/null +++ b/distros/ubuntu1204/patches/default_cgi-path.patch @@ -0,0 +1,16 @@ +Last-Update: 2015-08-16 +Forwarded: no +Author: Dmitry Smirnov +Description: correct path to CGI app according to default web server configuration. + +--- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in ++++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +@@ -428,7 +428,7 @@ our @options = + }, + { + name => "ZM_PATH_ZMS", +- default => "/cgi-bin/nph-zms", ++ default => "/zm/cgi-bin/nph-zms", + description => "Web path to zms streaming server", + help => qqq(" + The ZoneMinder streaming server is required to send streamed diff --git a/distros/ubuntu1204/patches/series b/distros/ubuntu1204/patches/series new file mode 100644 index 000000000..fc70f4006 --- /dev/null +++ b/distros/ubuntu1204/patches/series @@ -0,0 +1,2 @@ +default_cgi-path.patch +use_libjs-mootools.patch diff --git a/distros/ubuntu1204/patches/use_libjs-mootools.patch b/distros/ubuntu1204/patches/use_libjs-mootools.patch new file mode 100644 index 000000000..b3925f6d0 --- /dev/null +++ b/distros/ubuntu1204/patches/use_libjs-mootools.patch @@ -0,0 +1,18 @@ +Last-Update: 2015-03-29 +Forwarded: no +Bug-Debian: http://bugs.debian.org/585590 +Reviewed-By: Dmitry Smirnov +Description: use mootools shipped by debian, rather than the zoneminder included mootools. + +--- a/web/skins/classic/includes/functions.php ++++ b/web/skins/classic/includes/functions.php +@@ -63,9 +63,8 @@ + } + ?> + + +- + + + /dev/null 2>&1); then - invoke-rc.d mysql start - fi - if $(/etc/init.d/mysql status >/dev/null 2>&1); then - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload - # test if database if already present... - if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then - cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf - echo 'grant lock tables, alter,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql - fi - - invoke-rc.d zoneminder stop || true - zmupdate.pl --nointeractive - - else - echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' - fi - else - echo 'mysql not found, assuming remote server.' - fi - chown www-data:www-data /var/log/zm - chown www-data:www-data /var/lib/zm/ - if [ -z "$2" ]; then - chown www-data:www-data -R /var/cache/zoneminder - fi -fi -# Ensure zoneminder is stopped... -if [ -x "/etc/init.d/zoneminder" ]; then - if invoke-rc.d zoneminder status ; then - invoke-rc.d zoneminder stop || exit $? - fi -fi - -if [ "$1" = "configure" ]; then - if [ -z "$2" ]; then - chown www-data:www-data /var/log/zm - chown www-data:www-data /var/lib/zm/ - chown www-data:www-data -R /var/cache/zoneminder - else - chown www-data:www-data /var/log/zm - zmupdate.pl - fi -fi -#DEBHELPER# diff --git a/distros/ubuntu1204/postrm b/distros/ubuntu1204/postrm deleted file mode 100644 index 28a00a7a0..000000000 --- a/distros/ubuntu1204/postrm +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/sh -# set -e # to be reinstated later - -if [ "$1" = "purge" ]; then - echo 'delete from user where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql - echo 'delete from db where User="zmuser";' | mysql --defaults-file=/etc/mysql/debian.cnf mysql - mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm -fi -#DEBHELPER# diff --git a/distros/ubuntu1204/rules b/distros/ubuntu1204/rules index df11f4988..b467c870f 100755 --- a/distros/ubuntu1204/rules +++ b/distros/ubuntu1204/rules @@ -1,71 +1,87 @@ #!/usr/bin/make -f # -*- makefile -*- -# Sample debian/rules that uses debhelper. -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 -# These are used for cross-compiling and for saving the configure script -# from having to guess our platform (since we know it already) -DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) -DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) +export DEB_BUILD_MAINT_OPTIONS = hardening=+all +export DEB_LDFLAGS_MAINT_APPEND += -Wl,--as-needed -CFLAGS = -Wall -g -CPPFLAGS = -D__STDC_CONSTANT_MACROS -CXXFLAGS = -DHAVE_LIBCRYPTO - -ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) - CFLAGS += -O0 -else - CFLAGS += -O2 +ifeq ($(DEB_BUILD_ARCH_OS),hurd) +ARGS:= -DZM_NO_MMAP=ON endif %: - dh $@ --with autoreconf + dh $@ --parallel --buildsystem=cmake --builddirectory=dbuild \ + --with sphinxdoc,apache2,linktree override_dh_auto_configure: - CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" ./configure --host=$(DEB_HOST_GNU_TYPE) --build=$(DEB_BUILD_GNU_TYPE) --sysconfdir=/etc/zm --prefix=/usr --mandir=\$${prefix}/share/man --infodir=\$${prefix}/share/info --with-mysql=/usr --with-mariadb=/usr --with-webdir=/usr/share/zoneminder --with-ffmpeg=/usr --with-cgidir=/usr/lib/cgi-bin --with-webuser=www-data --with-webgroup=www-data --enable-crashtrace=no --enable-mmap=yes + dh_auto_configure -- $(ARGS) \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_BUILD_TYPE=Release \ + -DZM_CONFIG_DIR="/etc/zm" \ + -DZM_RUNDIR="/var/run/zm" \ + -DZM_SOCKDIR="/var/run/zm" \ + -DZM_TMPDIR="/tmp/zm" \ + -DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \ + -DZM_CONTENTDIR="/var/cache/zoneminder" override_dh_clean: - # Add here commands to clean up after the build process. - [ ! -f Makefile ] || $(MAKE) distclean - dh_clean + dh_clean $(MANPAGES1) + $(RM) -r docs/_build docs/installationguide -override_dh_install: - # Add here commands to install the package into debian/zm. - $(MAKE) install DESTDIR=$(CURDIR)/debian/zoneminder RUNDIR=$(CURDIR)/debian/zoneminder/var/run ZM_RUNDIR=$(CURDIR)/debian/zoneminder/var/run - install -D -m 0644 db/zm_create.sql $(CURDIR)/debian/zoneminder/usr/share/zoneminder/db - install -D -m 0644 db/zm_update-*.sql $(CURDIR)/debian/zoneminder/usr/share/zoneminder/db - install -D -m 0644 debian/apache.conf $(CURDIR)/debian/zoneminder/etc/zm - # - # NOTE: This is a short-term kludge; hopefully changes in the next - # upstream version will render this unnecessary. - rm -rf debian/zoneminder/usr/share/zoneminder/events - rm -rf debian/zoneminder/usr/share/zoneminder/images - rm -rf debian/zoneminder/usr/share/zoneminder/temp - ln -s /var/cache/zoneminder/events debian/zoneminder/usr/share/zoneminder/ - ln -s /var/cache/zoneminder/images debian/zoneminder/usr/share/zoneminder/ - ln -s /var/cache/zoneminder/temp debian/zoneminder/usr/share/zoneminder/ +build-indep: + #$(MAKE) -C docs text + $(MAKE) -C docs html - # - # This is a slightly lesser kludge; moving the cgi stuff to - # /usr/share/zoneminder/cgi-bin breaks one set of behavior, - # having it just in /usr/lib/cgi-bin breaks another bit of - # behavior. - # - ln -s /usr/lib/cgi-bin debian/zoneminder/usr/share/zoneminder/ +MANPAGES1 = dbuild/scripts/zmupdate.pl.1 +$(MANPAGES1): + # generate man page(s): + pod2man -s1 --stderr --utf8 $(patsubst %.1, %, $@) $@ + +## reproducible build: +LAST_CHANGE=$(shell dpkg-parsechangelog -S Date) +BUILD_DATE=$(shell LC_ALL=C date -u "+%B %d, %Y" -d "$(LAST_CHANGE)") +override_dh_installman: $(MANPAGES1) + $(MAKE) -C docs man SPHINXOPTS="-D today=\"$(BUILD_DATE)\"" + dh_installman --language=C $(MANPAGES1) + +override_dh_auto_install: + dh_auto_install --destdir=$(CURDIR)/debian/tmp + # remove worthless files: + $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*/*/.packlist + $(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*.in + # remove empty directories: + find $(CURDIR)/debian/tmp/usr -type d -empty -delete -printf 'removed %p\n' + # remove extra-license-file: + $(RM) -v $(CURDIR)/debian/tmp/usr/share/zoneminder/www/api/lib/Cake/LICENSE.txt override_dh_fixperms: dh_fixperms - chown root:root debian/zoneminder/etc/zm/zm.conf + ## 637685 + chmod -c o-r $(CURDIR)/debian/zoneminder/etc/zm/zm.conf -override_dh_auto_test: - # do not run tests... +override_dh_installinit: + dh_installinit --no-start + +override_dh_apache2: + dh_apache2 --noenable -.PHONY: override_dh_strip override_dh_strip: - dh_strip --dbg-package=zoneminder-dbg + [ -d "$(CURDIR)/debian/zoneminder-dbg" ] \ + && dh_strip --dbg-package=zoneminder-dbg \ + || dh_strip + +#%: +# dh $@ --parallel --buildsystem=autoconf --with autoreconf +# +#override_dh_auto_configure: +# dh_auto_configure -- \ +# --sysconfdir=/etc/zm \ +# --with-mysql=/usr \ +# --with-webdir=/usr/share/zoneminder \ +# --with-ffmpeg=/usr \ +# --with-cgidir=/usr/lib/cgi-bin \ +# --with-webuser=www-data \ +# --with-webgroup=www-data \ +# --enable-mmap=yes diff --git a/distros/ubuntu1204/source/format b/distros/ubuntu1204/source/format new file mode 100644 index 000000000..89ae9db8f --- /dev/null +++ b/distros/ubuntu1204/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/distros/ubuntu1204/source/lintian-overrides b/distros/ubuntu1204/source/lintian-overrides new file mode 100644 index 000000000..3669e5de8 --- /dev/null +++ b/distros/ubuntu1204/source/lintian-overrides @@ -0,0 +1,9 @@ +## Actually sources are there: "*-nc.js". +source-is-missing web/tools/mootools/mootools-*-yc.js + +## We're using "libjs-jquery" instead. +source-is-missing web/skins/*/js/jquery-1.4.2.min.js + +## Acknowledged, will repack eventually. +source-contains-prebuilt-javascript-object web/tools/mootools/mootools-*-yc.js +source-contains-prebuilt-javascript-object web/skins/*/js/jquery-1.4.2.min.js diff --git a/distros/ubuntu1204/watch b/distros/ubuntu1204/watch index 5a8a9c4d7..7ee690edb 100644 --- a/distros/ubuntu1204/watch +++ b/distros/ubuntu1204/watch @@ -1,3 +1,7 @@ version=3 -http://www.zoneminder.com/downloads.html \ - .*/ZoneMinder-(.*).tar.gz + +opts=\ +repacksuffix=+dfsg,\ +dversionmangle=s{\+dfsg\d*}{},\ + https://github.com/ZoneMinder/ZoneMinder/releases \ + .*/ZoneMinder/archive/v(.*).tar.gz diff --git a/distros/ubuntu1204/zoneminder-doc.doc-base b/distros/ubuntu1204/zoneminder-doc.doc-base new file mode 100644 index 000000000..c43dc4336 --- /dev/null +++ b/distros/ubuntu1204/zoneminder-doc.doc-base @@ -0,0 +1,8 @@ +Document: zoneminder-doc +Title: Zoneminder documentation +Abstract: This document describes how to use Zoneminder. +Section: System/Administration + +Format: HTML +Index: /usr/share/doc/zoneminder-doc/html/index.html +Files: /usr/share/doc/zoneminder-doc/html/* diff --git a/distros/ubuntu1204/zoneminder-doc.install b/distros/ubuntu1204/zoneminder-doc.install new file mode 100644 index 000000000..c19bc6f3a --- /dev/null +++ b/distros/ubuntu1204/zoneminder-doc.install @@ -0,0 +1 @@ +docs/_build/html usr/share/doc/zoneminder-doc/ diff --git a/distros/ubuntu1204/zoneminder-doc.links b/distros/ubuntu1204/zoneminder-doc.links new file mode 100644 index 000000000..cc09f6462 --- /dev/null +++ b/distros/ubuntu1204/zoneminder-doc.links @@ -0,0 +1,2 @@ +## Convenience symlink: +/usr/share/doc/zoneminder-doc/html /usr/share/doc/zoneminder/html diff --git a/distros/ubuntu1204/zoneminder.apache2 b/distros/ubuntu1204/zoneminder.apache2 new file mode 100644 index 000000000..466144fa7 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.apache2 @@ -0,0 +1 @@ +conf debian/conf/apache2/zoneminder.conf nginx diff --git a/distros/ubuntu1204/zoneminder.bug-presubj b/distros/ubuntu1204/zoneminder.bug-presubj new file mode 100644 index 000000000..990fc1d94 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.bug-presubj @@ -0,0 +1,5 @@ +Unless bug is specific to Debian please consider reporting it directly to +upstream developer(s): + + https://github.com/ZoneMinder/ZoneMinder/issues + diff --git a/distros/ubuntu1204/dirs b/distros/ubuntu1204/zoneminder.dirs similarity index 100% rename from distros/ubuntu1204/dirs rename to distros/ubuntu1204/zoneminder.dirs diff --git a/distros/debian_cmake/docs b/distros/ubuntu1204/zoneminder.docs similarity index 100% rename from distros/debian_cmake/docs rename to distros/ubuntu1204/zoneminder.docs diff --git a/distros/ubuntu1204/zoneminder.examples b/distros/ubuntu1204/zoneminder.examples new file mode 100644 index 000000000..3b8befe7b --- /dev/null +++ b/distros/ubuntu1204/zoneminder.examples @@ -0,0 +1,2 @@ +debian/examples/* +dbuild/misc/apache.conf diff --git a/distros/debian_cmake/init.d b/distros/ubuntu1204/zoneminder.init similarity index 70% rename from distros/debian_cmake/init.d rename to distros/ubuntu1204/zoneminder.init index cbc8fb10e..5cdf62165 100644 --- a/distros/debian_cmake/init.d +++ b/distros/ubuntu1204/zoneminder.init @@ -8,25 +8,28 @@ # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Control ZoneMinder as a Service +# Description: ZoneMinder CCTV recording and surveillance system ### END INIT INFO -# description: Control ZoneMinder as a Service # chkconfig: 2345 20 20 # Source function library. -#. /etc/rc.d/init.d/functions +. /lib/lsb/init-functions prog=ZoneMinder ZM_PATH_BIN="/usr/bin" -RUNDIR=/var/run/zm -TMPDIR=/tmp/zm +RUNDIR="/var/run/zm" +TMPDIR="/tmp/zm" command="$ZM_PATH_BIN/zmpkg.pl" start() { echo -n "Starting $prog: " - mkdir -p $RUNDIR $TMPDIR && chown www-data:www-data $RUNDIR $TMPDIR + export TZ=:/etc/localtime + mkdir -p "$RUNDIR" && chown www-data:www-data "$RUNDIR" + mkdir -p "$TMPDIR" && chown www-data:www-data "$TMPDIR" $command start RETVAL=$? - [ $RETVAL = 0 ] && echo success || echo failure + [ $RETVAL = 0 ] && echo success + [ $RETVAL != 0 ] && echo failure echo [ $RETVAL = 0 ] && touch /var/lock/zm return $RETVAL @@ -35,11 +38,11 @@ stop() { echo -n "Stopping $prog: " # # Why is this status check being done? - # as $command stop returns 1 if zoneminder - # is stopped, which will result in - # this returning 1, which will stuff + # as $command stop returns 1 if zoneminder + # is stopped, which will result in + # this returning 1, which will stuff # dpkg when it tries to stop zoneminder before - # uninstalling . . . + # uninstalling . . . # result=`$command status` if [ ! "$result" = "running" ]; then @@ -49,7 +52,8 @@ stop() { else $command stop RETVAL=$? - [ $RETVAL = 0 ] && echo success || echo failure + [ $RETVAL = 0 ] && echo success + [ $RETVAL != 0 ] && echo failure echo [ $RETVAL = 0 ] && rm -f /var/lock/zm fi diff --git a/distros/ubuntu1204/zoneminder.install b/distros/ubuntu1204/zoneminder.install new file mode 100644 index 000000000..8a26777c0 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.install @@ -0,0 +1,10 @@ +etc/zm/zm.conf +usr/bin +usr/lib/zoneminder +usr/share/polkit-1 +usr/share/zoneminder/db +usr/share/zoneminder/www + +# libzoneminder-perl files: +usr/share/man/man3 +usr/share/perl5 diff --git a/distros/ubuntu1204/zoneminder.links b/distros/ubuntu1204/zoneminder.links new file mode 100644 index 000000000..4c44d3238 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.links @@ -0,0 +1,4 @@ +/var/cache/zoneminder/events /usr/share/zoneminder/www/events +/var/cache/zoneminder/images /usr/share/zoneminder/www/images +/var/cache/zoneminder/temp /usr/share/zoneminder/www/temp +/tmp/zm /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/ubuntu1204/zoneminder.linktrees b/distros/ubuntu1204/zoneminder.linktrees new file mode 100644 index 000000000..2e843bbf1 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.linktrees @@ -0,0 +1,14 @@ +## cakephp +#replace /usr/share/php/Cake /usr/share/zoneminder/www/api/lib/Cake + +## libjs-mootools +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core.js +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-nc.js +replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-yc.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-nc.js +replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-yc.js + +## libjs-jquery +replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/classic/js/jquery-1.4.2.min.js +replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/flat/js/jquery-1.4.2.min.js diff --git a/distros/ubuntu1204/zoneminder.lintian-overrides b/distros/ubuntu1204/zoneminder.lintian-overrides new file mode 100644 index 000000000..90be05a9f --- /dev/null +++ b/distros/ubuntu1204/zoneminder.lintian-overrides @@ -0,0 +1,14 @@ +# Depends: policykit-1 +unusual-interpreter usr/bin/zmsystemctl.pl #!/usr/bin/pkexec + +# Intentionally not others-readable, #637685. +non-standard-file-perm etc/zm/zm.conf 0640 != 0644 + +# Bundled Cake PHP framework, not intended for direct execution: +script-not-executable usr/share/zoneminder/www/api/* + +# Annoying but seems to be too much troubles to fix; should be fixed upstream: +script-with-language-extension usr/bin/*.pl + +# dh-linktree: +package-contains-broken-symlink usr/share/zoneminder/www/api/lib/Cake/* diff --git a/distros/ubuntu1204/zoneminder.logrotate b/distros/ubuntu1204/zoneminder.logrotate new file mode 100644 index 000000000..ac7ce0795 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.logrotate @@ -0,0 +1,10 @@ +/var/log/zm/*log { + missingok + notifempty + sharedscripts + postrotate + /usr/bin/zmpkg.pl logrot >>/dev/null 2>&1 || : + endscript + weekly + rotate 3 +} diff --git a/distros/ubuntu1204/zoneminder.maintscript b/distros/ubuntu1204/zoneminder.maintscript new file mode 100644 index 000000000..3aa20b3a0 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.maintscript @@ -0,0 +1 @@ +rm_conffile /etc/zm/apache.conf 1.28.1-5~ diff --git a/distros/ubuntu1204/zoneminder.manpages b/distros/ubuntu1204/zoneminder.manpages new file mode 100644 index 000000000..d2053d688 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.manpages @@ -0,0 +1 @@ +docs/_build/man/*.1 diff --git a/distros/ubuntu1204/zoneminder.postinst b/distros/ubuntu1204/zoneminder.postinst new file mode 100644 index 000000000..7c01cdde4 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.postinst @@ -0,0 +1,53 @@ +#! /bin/sh + +set -e + +if [ "$1" = "configure" ]; then + + . /etc/zm/zm.conf + + # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group + chown www-data:root /var/log/zm + chown www-data:www-data /var/lib/zm + if [ -z "$2" ]; then + chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* + fi + + # Do this every time the package is installed or upgraded + + if [ "$ZM_DB_HOST" = "localhost" ]; then + if [ -e "/etc/init.d/mysql" ]; then + # + # Get mysql started if it isn't + # + if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then + invoke-rc.d mysql start + fi + if $(/etc/init.d/mysql status >/dev/null 2>&1); then + mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload + # test if database if already present... + if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then + cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + # This creates the user. + echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + else + echo "grant lock tables, alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + fi + + # Ensure zoneminder is stopped + invoke-rc.d zoneminder stop || true + zmupdate.pl --nointeractive + zmupdate.pl --nointeractive -f + invoke-rc.d zoneminder start || true + else + echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' + fi + else + echo 'mysql not found, assuming remote server.' + fi + else + echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" + fi +fi + +#DEBHELPER# diff --git a/distros/ubuntu1204/zoneminder.postrm b/distros/ubuntu1204/zoneminder.postrm new file mode 100644 index 000000000..ba2066c8d --- /dev/null +++ b/distros/ubuntu1204/zoneminder.postrm @@ -0,0 +1,14 @@ +#! /bin/sh + +set -e + +if [ "$1" = "purge" ]; then + echo " +Reminder: to completely remove \"zoneminder\" it may be necessary + * to delete database using the following sample command: + sudo mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm + * to delete remaining data files in "/var/cache/zoneminder". +" +fi + +#DEBHELPER# diff --git a/distros/debian_cmake/preinst b/distros/ubuntu1204/zoneminder.preinst similarity index 56% rename from distros/debian_cmake/preinst rename to distros/ubuntu1204/zoneminder.preinst index 6cd01ba55..3f75a1b3e 100644 --- a/distros/debian_cmake/preinst +++ b/distros/ubuntu1204/zoneminder.preinst @@ -2,22 +2,28 @@ set -e +## Remove obsolete symlink which is in the way of dh_apache2: +ol="/etc/apache2/conf-available/zoneminder.conf" +if [ -h "${ol}" ]; then + [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" +fi + abort=false -if [ -L /usr/share/zoneminder/events ]; then - l=$(readlink /usr/share/zoneminder/events) +if [ -h /usr/share/zoneminder/www/events ]; then + l=$(readlink /usr/share/zoneminder/www/events) if [ "$l" != "/var/cache/zoneminder/events" ]; then abort=true fi fi -if [ -L /usr/share/zoneminder/images ]; then - l=$(readlink /usr/share/zoneminder/images ) +if [ -h /usr/share/zoneminder/www/images ]; then + l=$(readlink /usr/share/zoneminder/www/images ) if [ "$l" != "/var/cache/zoneminder/images" ]; then abort=true fi fi if [ "$abort" = "true" ]; then - cat >&2 << EOF + cat >&2 << EOF Aborting installation of zoneminder due to non-default symlinks in /usr/share/zoneminder for the images and/or events directory, which could result in loss of data. Please move your data in each of these directories to @@ -28,5 +34,3 @@ EOF fi #DEBHELPER# - -exit 0 diff --git a/distros/ubuntu1204/zoneminder.service b/distros/ubuntu1204/zoneminder.service new file mode 100644 index 000000000..d5b326d59 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.service @@ -0,0 +1,19 @@ +# ZoneMinder systemd unit file +# This file is intended to work with Debian distributions + +[Unit] +Description=ZoneMinder CCTV recording and surveillance system +After=network.target mysql.service +Requires=mysql.service + +[Service] +#User=www-data +Type=forking +ExecStart=/usr/bin/zmpkg.pl start +ExecReload=/usr/bin/zmpkg.pl restart +ExecStop=/usr/bin/zmpkg.pl stop +PIDFile=/var/run/zm/zm.pid +Restart=on-abnormal + +[Install] +WantedBy=multi-user.target diff --git a/distros/ubuntu1204/zoneminder.tmpfile b/distros/ubuntu1204/zoneminder.tmpfile new file mode 100644 index 000000000..d307c6640 --- /dev/null +++ b/distros/ubuntu1204/zoneminder.tmpfile @@ -0,0 +1,2 @@ +d /var/run/zm 0755 www-data www-data +d /tmp/zm 0755 www-data www-data diff --git a/distros/debian_cmake/README.Debian b/distros/ubuntu1504_cmake_split_packages/README.Debian similarity index 100% rename from distros/debian_cmake/README.Debian rename to distros/ubuntu1504_cmake_split_packages/README.Debian diff --git a/distros/debian_cmake/apache.conf b/distros/ubuntu1504_cmake_split_packages/apache.conf similarity index 53% rename from distros/debian_cmake/apache.conf rename to distros/ubuntu1504_cmake_split_packages/apache.conf index 92a2b6414..6f2f30fd9 100644 --- a/distros/debian_cmake/apache.conf +++ b/distros/ubuntu1504_cmake_split_packages/apache.conf @@ -1,7 +1,6 @@ -Alias /zm /usr/share/zoneminder +Alias /zm /usr/share/zoneminder/www - - php_flag register_globals off + Options Indexes FollowSymLinks DirectoryIndex index.php diff --git a/distros/ubuntu1504_cmake_split_packages/changelog b/distros/ubuntu1504_cmake_split_packages/changelog new file mode 100644 index 000000000..37a592b94 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/changelog @@ -0,0 +1,95 @@ +zoneminder (1.28.1+1-trusty-SNAPSHOT2015061201) vivid; urgency=medium + + * translation fixes + * Merge angular api + * now build using cmake + * use split up packaging + + -- Isaac Connor Fri, 12 Jun 2015 11:22:36 -0400 + +zoneminder (1.28.1+1-trusty-SNAPSHOT2015030201) trusty; urgency=medium + + * maybe fix for RTSP Basic Auth + * Also remove dependency on netpbm + + -- Isaac Connor Mon, 02 Mar 2015 11:25:59 -0500 + +zoneminder (1.28.1+1-utopic-SNAPSHOT2015022301) utopic; urgency=medium + + * Big merge of onvif support and some fixes. + + -- Isaac Connor Mon, 23 Feb 2015 19:45:45 -0500 + +zoneminder (1.28.0+1-trusty-SNAPSHOT2015021201) trusty; urgency=medium + + * add mysql-client-5.6 as a dependency instaed of mysql-client. + + -- Isaac Connor Fri, 13 Feb 2015 09:35:13 -0500 + +zoneminder (1.28.0+1-trusty-SNAPSHOT2015011101) trusty; urgency=medium + + * small changes + + -- Isaac Connor Fri, 12 Dec 2014 16:38:36 -0500 + +zoneminder (1.28.0+1-utopic-SNAPSHOT2014112001) utopic; urgency=medium + + * Various fixes and developments since 1.28.0. Includes Digest-Auth for HTTP and better for RTSP + + -- Isaac Connor Thu, 20 Nov 2014 10:57:57 -0500 + +zoneminder (1.28.0-trusty) trusty; urgency=medium + + * Release + + -- Isaac Connor Fri, 17 Oct 2014 09:25:55 -0400 + +zoneminder (1.27.99+1-trusty-SNAPSHOT2014101401) trusty; urgency=medium + + * Several PR merges in big push for 1.28.0 + + -- Isaac Connor Tue, 14 Oct 2014 09:28:29 -0400 + +zoneminder (1.27.99+1-trusty-SNAPSHOT2014092601) trusty; urgency=medium + + * style updates and db fixes for database logging filters + + -- Isaac Connor Fri, 26 Sep 2014 14:44:45 -0400 + +zoneminder (1.27.99+1-trusty-SNAPSHOT2014090801) trusty; urgency=medium + + * several segfault fixes for local cameras + + -- Isaac Connor Mon, 08 Sep 2014 16:56:57 -0400 + +zoneminder (1.27.99+1-trusty-SNAPSHOT2014090701) trusty; urgency=medium + + * Fix segfaults for local cameras, also now includes the systemd support patch + + -- Isaac Connor Sun, 07 Sep 2014 17:19:01 -0400 + +zoneminder (1.27.99+1-trusty-SNAPSHOT2014082102) trusty; urgency=medium + + * Fix UI inputs for v4l multibuffer and captures per frame + + -- Isaac Connor Thu, 21 Aug 2014 12:03:31 -0400 + +zoneminder (1.27.99+1-trusty-SNAPSHOT2014082101) trusty; urgency=medium + + * fall back to Config table values for V4l MultiBUffer and Captures Per Frame + * add mention of monitor page settings for thse in the config table + + -- Isaac Connor Thu, 21 Aug 2014 10:04:46 -0400 + +zoneminder (1.27.99+1-precise-SNAPSHOT2014080601) precise; urgency=medium + + * improve error messages + * Make zmupdate re-run the most recent patch so that people running the daily builds get their db updates + + -- Isaac Connor Wed, 06 Aug 2014 20:20:20 -0400 + +zoneminder (1.27.0+1-trusty-v4ltomonitor-1) trusty; urgency=high + + * Snapshot release - + + -- Isaac Connor Wed, 09 Jul 2014 21:35:29 -0400 diff --git a/distros/debian_cmake/compat b/distros/ubuntu1504_cmake_split_packages/compat similarity index 100% rename from distros/debian_cmake/compat rename to distros/ubuntu1504_cmake_split_packages/compat diff --git a/distros/ubuntu1504_cmake_split_packages/control b/distros/ubuntu1504_cmake_split_packages/control new file mode 100644 index 000000000..5f313897f --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/control @@ -0,0 +1,129 @@ +Source: zoneminder +Section: net +Priority: optional +Maintainer: Isaac Connor +Build-Depends: debhelper (>= 9), po-debconf (>= 1.0), autoconf, automake, libtool, cmake, dh-autoreconf +, libphp-serialization-perl, libgnutls-dev +, libmysqlclient-dev | libmariadbclient-dev, libdbd-mysql-perl +, libdate-manip-perl, libwww-perl +, libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev +, libpcre3-dev +, libavcodec-ffmpeg-dev, libavformat-ffmpeg-dev, libswscale-ffmpeg-dev, libavutil-ffmpeg-dev, libavdevice-ffmpeg-dev +, libv4l-dev (>= 0.8.3) +, libbz2-dev +, libsys-mmap-perl +, libdevice-serialport-perl, libarchive-zip-perl, libmime-lite-perl +, libvlccore-dev, libvlc-dev +, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev +, libgcrypt11-dev | libgcrypt20-dev, libpolkit-gobject-1-dev +, libdbi-perl, libnet-sftp-foreign-perl, libexpect-perl, libmime-tools-perl, libx264-dev, libmp4v2-dev +Standards-Version: 3.9.6 +Homepage: http://www.zoneminder.com/ + +Package: zoneminder +Section: metapackages +Architecture: all +Depends: ${misc:Depends}, + libzoneminder-perl (>= ${source:Version}), + zoneminder-database (>= ${source:Version}), + zoneminder-core (>= ${binary:Version}), + zoneminder-ui-base (>= ${source:Version}), + zoneminder-ui-classic (>= ${source:Version}) +Description: Video camera security and surveillance solution (metapackage) + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + +Package: libzoneminder-perl +Section: perl +Architecture: all +Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl, + libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, + libsys-mmap-perl, liburi-encode-perl, libwww-perl +Description: Perl libraries for ZoneMinder + ZoneMinder is a video camera security and surveillance solution. + . + This package provides the libraries for the perl scripts, it can be used to + write custom interfaces as well. + +Package: zoneminder-database +Section: database +Architecture: all +Depends: ${misc:Depends}, debconf, dbconfig-common, + mysql-client | mariadb-client +Recommends: mysql-server | mariadb-server +Description: Database management package for ZoneMinder + ZoneMinder is a video camera security and surveillance solution. + . + This package provides the sql files and maintenance scripts to perform all the + database operations (installation, upgrade or removal) on a local or a remote + server. + +Package: zoneminder-core +Section: video +Architecture: any +Depends: libzoneminder-perl (= ${source:Version}), + zoneminder-database (= ${source:Version}), ${shlibs:Depends}, ${misc:Depends}, + ${perl:Depends}, libarchive-tar-perl, libarchive-zip-perl, libdate-manip-perl, + libdbi-perl, libmodule-load-conditional-perl, libmime-lite-perl, + libmime-tools-perl, libnet-sftp-foreign-perl, libphp-serialization-perl, + debconf, ffmpeg | libav-tools, rsyslog | system-log-daemon, zip, + policykit-1, apache2, libmp4v2-2, libpcre++0 +, libsys-cpu-perl, libsys-meminfo-perl +, libdata-dump-perl, libclass-std-fast-perl, libsoap-wsdl-perl, libio-socket-multicast-perl, libdigest-sha-perl +Description: Core binaries and perl scripts for ZoneMinder + ZoneMinder is a video camera security and surveillance solution. + . + This package provides the executable compiled binaries which do the main video + processing work and the perl scripts which perform helper and/or external + interface tasks. + +Package: zoneminder-core-dbg +Priority: extra +Section: debug +Architecture: any +Depends: zoneminder-core (= ${binary:Version}), ${misc:Depends} +Description: Debugging symbols for ZoneMinder + ZoneMinder is a video camera security and surveillance solution. + . + This package provides the debugging symbols for the executable compiled + binaries. + +Package: zoneminder-ui-base +Section: web +Architecture: any +Depends: zoneminder-core (= ${binary:Version}), ${shlibs:Depends}, + ${misc:Depends}, debconf, apache2, libapache2-mod-php5 | libapache2-mod-fcgid, + php5, php5-mysql | php5-mysqlnd, php5-gd +Description: Essential files for ZoneMinder's web user interface + ZoneMinder is a video camera security and surveillance solution. + . + This package provides the essential web files and maintenance scripts to set up + a basic web environment. + +Package: zoneminder-ui-classic +Section: web +Architecture: all +Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} +Description: Classic web user interface for ZoneMinder + ZoneMinder is a video camera security and surveillance solution. + . + This package provides the classic web user interface. + +Package: zoneminder-ui-api +Section: web +Architecture: all +Depends: zoneminder-ui-base (>= ${source:Version}), ${misc:Depends} +Description: API interface for ZoneMinder + ZoneMinder is a video camera security and surveillance solution. + . + This package provides an API interface mainly intended for use with angular-ui + or mobile applications, but can be used with any other custom programs as well. + diff --git a/distros/ubuntu1504_cmake_split_packages/copyright b/distros/ubuntu1504_cmake_split_packages/copyright new file mode 100644 index 000000000..a177502a0 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/copyright @@ -0,0 +1,22 @@ +Copyright: + +Copyright 2002 Philip Coombes + +License: + +This package 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 package 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 package; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +On Debian GNU/Linux systems, the text of the GPL can be found in +/usr/share/common-licenses/GPL. diff --git a/distros/ubuntu1204/docs b/distros/ubuntu1504_cmake_split_packages/docs similarity index 100% rename from distros/ubuntu1204/docs rename to distros/ubuntu1504_cmake_split_packages/docs diff --git a/distros/ubuntu1504_cmake_split_packages/libzoneminder-perl.install b/distros/ubuntu1504_cmake_split_packages/libzoneminder-perl.install new file mode 100644 index 000000000..792ffc15e --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/libzoneminder-perl.install @@ -0,0 +1,4 @@ +usr/share/perl5/ZoneMinder +usr/share/perl5/ZoneMinder.pm +debian/tmp/usr/share/man/man3/ZoneMinder.3pm +debian/tmp/usr/share/man/man3/ZoneMinder::* diff --git a/web/skins/flat/includes/init.php b/distros/ubuntu1504_cmake_split_packages/patches/series similarity index 100% rename from web/skins/flat/includes/init.php rename to distros/ubuntu1504_cmake_split_packages/patches/series diff --git a/distros/ubuntu1504_cmake_split_packages/po/POTFILES.in b/distros/ubuntu1504_cmake_split_packages/po/POTFILES.in new file mode 100644 index 000000000..5b155907e --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/po/POTFILES.in @@ -0,0 +1,3 @@ +[type: gettext/rfc822deb] zoneminder-core.templates +[type: gettext/rfc822deb] zoneminder-database.templates +[type: gettext/rfc822deb] zoneminder-ui-base.templates diff --git a/distros/ubuntu1504_cmake_split_packages/po/fr.po b/distros/ubuntu1504_cmake_split_packages/po/fr.po new file mode 100644 index 000000000..85ced7fd2 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/po/fr.po @@ -0,0 +1,252 @@ +# debconf french translation file for ZoneMinder. +# Copyright (C) 2001-2008 Philip Coombes +# This file is distributed under the same license as the zoneminder package. +# First author: Emmanuel Papin , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: zoneminder\n" +"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n" +"POT-Creation-Date: 2014-12-16 12:34+0100\n" +"PO-Revision-Date: 2014-12-07 00:40+0100\n" +"Last-Translator: Emmanuel Papin \n" +"Language-Team: French \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:1001 +msgid "Delete this non empty directory?" +msgstr "Supprimer ce répertoire non vide ?" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:1001 +msgid "" +"A purge of the ZoneMinder package is performed but the directory '/var/cache/" +"zoneminder' is not empty so it will not be deleted." +msgstr "" +"Une purge du paquet ZoneMinder est en cours mais le répertoire '/var/cache/" +"zoneminder' n'est pas vide et sera donc conservé." + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:1001 +msgid "" +"Please consider that this directory is designed to contain data resulting " +"from event detection. Therefore, \"proof of evidence\" could be lost!\"" +msgstr "" +"Veuillez considérer que ce répertoire est conçu pour contenir des données " +"résultants de la détection d'événements. Par conséquent, des preuves " +"pourraient être perdues !" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:1001 +msgid "" +"If you are not sure of your decision, please do not delete this directory " +"but perform a manual checkup." +msgstr "" +"Si vous n'êtes pas sûr de votre décision, veuillez conserver ce répertoire " +"et effectuer une vérification manuelle." + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:2001 +msgid "Deletion confirmed?" +msgstr "Supression confirmée ?" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:2001 +msgid "" +"You have allowed the deletion of directory '/var/cache/zoneminder' although " +"it may contain critical data." +msgstr "" +"Vous avez autorisé la suppression du répertoire '/var/cache/zoneminder' bien " +"qu'il puisse contenir des données critiques." + +#. Type: select +#. Choices +#: ../zoneminder-database.templates:1001 +msgid "local" +msgstr "local" + +#. Type: select +#. Choices +#: ../zoneminder-database.templates:1001 +msgid "remote" +msgstr "distant" + +#. Type: select +#. Description +#: ../zoneminder-database.templates:1002 +msgid "Database location:" +msgstr "Emplacement de la base de donnée :" + +#. Type: select +#. Description +#: ../zoneminder-database.templates:1002 +msgid "" +"A database server is required to run ZoneMinder. The database can be " +"installed either locally or remotely on a machine of your network." +msgstr "" +"Un serveur de base de données est requis pour ZoneMinder. La base de donnée " +"peut être installée localement ou à distance sur une machine de votre réseau." + +#. Type: select +#. Description +#: ../zoneminder-database.templates:1002 +msgid "" +"If you choose a remote location, you will have to select the 'tcp/ip' " +"connection method and enter the hostname or ip address of the remote machine " +"in the next configuration screens." +msgstr "" +"Si vous choisissez un emplacement distant, vous devrez sélectionner la " +"méthode de connexion 'tcp/ip' et entrer le nom réseau ou l'adresse ip de la " +"machine distante dans les écrans de configuration suivants." + +#. Type: error +#. Description +#: ../zoneminder-database.templates:2001 +msgid "No local database server is available:" +msgstr "Aucun serveur local de base de données n'est disponible :" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:2001 +msgid "" +"Currently ZoneMinder supports mysql or mariadb database server but none of " +"them appears to be installed on this machine." +msgstr "" +"Actuellement ZoneMinder supporte les serveurs de base de données mysql et " +"mariadb mais aucun d'entre eux n'est installé sur cette machine." + +#. Type: error +#. Description +#: ../zoneminder-database.templates:2001 +msgid "" +"In order to complete ZoneMinder's installation, after ending of this " +"assistant, please install a compatible database server and then restart the " +"assistant by invoking:" +msgstr "" +"Afin de compléter l'installation de ZoneMinder, après la fermeture de cet " +"assitant, veuillez installer un serveur de base de données compatible et " +"ensuite redémarrez l'assistant en invoquant :" + +#. Type: error +#. Description +#. Type: error +#. Description +#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001 +msgid "$ sudo dpkg-reconfigure zoneminder" +msgstr "$ sudo dpkg-reconfigure zoneminder" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "Remote database servers are not allowed:" +msgstr "Les serveurs de base de données distants ne sont pas autorisés :" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "" +"The current configuration of dbconfig-common does not allow installation of " +"a database on remote servers." +msgstr "" +"La configuration actuelle de dbconfig-common ne permet pas l'installation de " +"bases de données sur des serveurs distants." + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "" +"In order to reconfigure dbconfig-common, please invoke the following command " +"after ending of this assistant:" +msgstr "" +"Afin de reconfigurer dbconfig-common, veuillez invoquer la commande suivante " +"après la fermeture de cet assitant :" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "$ sudo dpkg-reconfigure dbconfig-common" +msgstr "$ sudo dpkg-reconfigure dbconfig-common" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "" +"Then, to complete ZoneMinder's installation, please restart this assistant " +"by invoking:" +msgstr "" +"Ensuite, pour compléter l'installation de ZoneMinder, veuillez redémarrer " +"cet assistant en invoquant :" + +#. Type: password +#. Description +#: ../zoneminder-database.templates:4001 +msgid "New password for the ZoneMinder 'admin' user:" +msgstr "Nouveau mot de passe pour le compte 'admin' de ZoneMinder :" + +#. Type: password +#. Description +#: ../zoneminder-database.templates:4001 +msgid "Please enter the password of the default administrative user." +msgstr "Veuillez entrer le mot de passe du compte administrateur par défaut." + +#. Type: password +#. Description +#: ../zoneminder-database.templates:4001 +msgid "" +"While not mandatory, it is highly recommended that you set a custom password " +"for the administrative 'admin' user." +msgstr "" +"Bien que cela ne soit pas obligatoire, il est fortement recommandé de " +"fournir un mot de passe personnalisé pour le compte administrateur 'admin'." + +#. Type: password +#. Description +#: ../zoneminder-database.templates:4001 +msgid "If this field is left blank, the password will not be changed." +msgstr "Si le champ est laissé vide, le mot de passe ne sera pas changé." + +#. Type: password +#. Description +#: ../zoneminder-database.templates:5001 +msgid "Repeat password for the ZoneMinder 'admin' user:" +msgstr "Répéter le mot de passe pour le compte 'admin' de ZoneMinder :" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:6001 +msgid "Password input error" +msgstr "Erreur de mot de passe" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:6001 +msgid "The two passwords you entered were not the same. Please try again." +msgstr "" +"Les deux mots de passe saisis ne sont pas les mêmes. Veuillez essayer à " +"nouveau." + +#. Type: multiselect +#. Description +#: ../zoneminder-ui-base.templates:1001 +msgid "Web server to reconfigure automatically:" +msgstr "Serveur web à reconfigurer automatiquement :" + +#. Type: multiselect +#. Description +#: ../zoneminder-ui-base.templates:1001 +msgid "" +"Please choose the web server that should be automatically configured for " +"ZoneMinder's web portal access." +msgstr "" +"Veuillez choisir le serveur web à reconfigurer automatiquement pour l'accès " +"au portail web de ZoneMinder." diff --git a/distros/ubuntu1504_cmake_split_packages/po/templates.pot b/distros/ubuntu1504_cmake_split_packages/po/templates.pot new file mode 100644 index 000000000..941a4094e --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/po/templates.pot @@ -0,0 +1,222 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: zoneminder\n" +"Report-Msgid-Bugs-To: zoneminder@packages.debian.org\n" +"POT-Creation-Date: 2014-12-16 12:34+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:1001 +msgid "Delete this non empty directory?" +msgstr "" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:1001 +msgid "" +"A purge of the ZoneMinder package is performed but the directory '/var/cache/" +"zoneminder' is not empty so it will not be deleted." +msgstr "" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:1001 +msgid "" +"Please consider that this directory is designed to contain data resulting " +"from event detection. Therefore, \"proof of evidence\" could be lost!\"" +msgstr "" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:1001 +msgid "" +"If you are not sure of your decision, please do not delete this directory " +"but perform a manual checkup." +msgstr "" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:2001 +msgid "Deletion confirmed?" +msgstr "" + +#. Type: boolean +#. Description +#: ../zoneminder-core.templates:2001 +msgid "" +"You have allowed the deletion of directory '/var/cache/zoneminder' although " +"it may contain critical data." +msgstr "" + +#. Type: select +#. Choices +#: ../zoneminder-database.templates:1001 +msgid "local" +msgstr "" + +#. Type: select +#. Choices +#: ../zoneminder-database.templates:1001 +msgid "remote" +msgstr "" + +#. Type: select +#. Description +#: ../zoneminder-database.templates:1002 +msgid "Database location:" +msgstr "" + +#. Type: select +#. Description +#: ../zoneminder-database.templates:1002 +msgid "" +"A database server is required to run ZoneMinder. The database can be " +"installed either locally or remotely on a machine of your network." +msgstr "" + +#. Type: select +#. Description +#: ../zoneminder-database.templates:1002 +msgid "" +"If you choose a remote location, you will have to select the 'tcp/ip' " +"connection method and enter the hostname or ip address of the remote machine " +"in the next configuration screens." +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:2001 +msgid "No local database server is available:" +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:2001 +msgid "" +"Currently ZoneMinder supports mysql or mariadb database server but none of " +"them appears to be installed on this machine." +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:2001 +msgid "" +"In order to complete ZoneMinder's installation, after ending of this " +"assistant, please install a compatible database server and then restart the " +"assistant by invoking:" +msgstr "" + +#. Type: error +#. Description +#. Type: error +#. Description +#: ../zoneminder-database.templates:2001 ../zoneminder-database.templates:3001 +msgid "$ sudo dpkg-reconfigure zoneminder" +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "Remote database servers are not allowed:" +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "" +"The current configuration of dbconfig-common does not allow installation of " +"a database on remote servers." +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "" +"In order to reconfigure dbconfig-common, please invoke the following command " +"after ending of this assistant:" +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "$ sudo dpkg-reconfigure dbconfig-common" +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:3001 +msgid "" +"Then, to complete ZoneMinder's installation, please restart this assistant " +"by invoking:" +msgstr "" + +#. Type: password +#. Description +#: ../zoneminder-database.templates:4001 +msgid "New password for the ZoneMinder 'admin' user:" +msgstr "" + +#. Type: password +#. Description +#: ../zoneminder-database.templates:4001 +msgid "Please enter the password of the default administrative user." +msgstr "" + +#. Type: password +#. Description +#: ../zoneminder-database.templates:4001 +msgid "" +"While not mandatory, it is highly recommended that you set a custom password " +"for the administrative 'admin' user." +msgstr "" + +#. Type: password +#. Description +#: ../zoneminder-database.templates:4001 +msgid "If this field is left blank, the password will not be changed." +msgstr "" + +#. Type: password +#. Description +#: ../zoneminder-database.templates:5001 +msgid "Repeat password for the ZoneMinder 'admin' user:" +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:6001 +msgid "Password input error" +msgstr "" + +#. Type: error +#. Description +#: ../zoneminder-database.templates:6001 +msgid "The two passwords you entered were not the same. Please try again." +msgstr "" + +#. Type: multiselect +#. Description +#: ../zoneminder-ui-base.templates:1001 +msgid "Web server to reconfigure automatically:" +msgstr "" + +#. Type: multiselect +#. Description +#: ../zoneminder-ui-base.templates:1001 +msgid "" +"Please choose the web server that should be automatically configured for " +"ZoneMinder's web portal access." +msgstr "" diff --git a/distros/ubuntu1504_cmake_split_packages/rules b/distros/ubuntu1504_cmake_split_packages/rules new file mode 100755 index 000000000..a534e8089 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/rules @@ -0,0 +1,164 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + +CFLAGS = -Wall +CPPFLAGS = -D__STDC_CONSTANT_MACROS +CXXFLAGS = -DHAVE_LIBCRYPTO + +ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS))) +DEBOPT = --enable-debug +CFLAGS += -g +CXXFLAGS += -g +else +DEBOPT = +endif + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) +CFLAGS += -O0 +else +CFLAGS += -O2 +endif + +INSTDIR = debian/tmp + +# These are used to get the most recent version of the original sources from github +UURL = $(shell git config --get remote.origin.url) +BRANCH = $(shell git rev-parse --abbrev-ref HEAD) +HEAD = $(shell git rev-parse HEAD) +PKD = $(abspath $(dir $(MAKEFILE_LIST))) +PKG = $(word 2,$(shell dpkg-parsechangelog -l$(PKD)/changelog | grep ^Source)) +VER ?= $(shell dpkg-parsechangelog -l$(PKD)/changelog | perl -ne 'print $$1 if m{^Version:\s+(?:\d+:)?(\d.*)(?:\-|\+nmu\d+.*)};') +DTYPE = +TARBALL = ../$(PKG)_$(VER)$(DTYPE).orig.tar.xz + +%: + dh $@ --buildsystem=cmake --parallel + +override_dh_auto_configure: + CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" dh_auto_configure -- \ + -DCMAKE_INSTALL_PREFIX=/usr \ + -DCMAKE_SKIP_RPATH=ON \ + -DCMAKE_VERBOSE_MAKEFILE=OFF \ + -DCMAKE_COLOR_MAKEFILE=ON \ + -DZM_RUNDIR=/var/run/zm \ + -DZM_SOCKDIR=/var/run/zm \ + -DZM_TMPDIR=/var/tmp/zm \ + -DZM_LOGDIR=/var/log/zm \ + -DZM_WEBDIR=/usr/share/zoneminder \ + -DZM_CONTENTDIR=/var/cache/zoneminder \ + -DZM_CGIDIR=/usr/lib/cgi-bin \ + -DZM_WEB_USER=www-data \ + -DZM_WEB_GROUP=www-data \ + -DCMAKE_INSTALL_SYSCONFDIR=etc/zm + +override_dh_auto_test: + # do not run tests... + + +override_dh_clean: + # Add here commands to clean up after the build process. + [ ! -f Makefile ] || $(MAKE) distclean + dh_clean src/zm_config_defines.h + # + # Delete remaining auto-generated Makefile if Makefile.in exists + find $(CURDIR)/ -type f -name "Makefile" | while read file; do \ + [ -f $$file.in ] && rm -f $$file; \ + done || true + # + # Delete remaining auto-generated Makefile.in if Makefile.am exists + find $(CURDIR)/ -type f -name "Makefile.in" | while read filein; do \ + fileam=`echo $$filein | sed 's/\(.*\)\.in/\1\.am/'`; \ + [ -f $$fileam ] && rm -f $$filein; \ + done || true + +override_dh_install: + dh_install --fail-missing + # + # NOTE: This is a short-term kludge; hopefully changes in the next + # upstream version will render this unnecessary. + rm -rf debian/zoneminder/usr/share/zoneminder/events + rm -rf debian/zoneminder/usr/share/zoneminder/images + rm -rf debian/zoneminder/usr/share/zoneminder/temp + # The link stuff for these folders has been moved to + # zoneminder-core.links file + # + # This is a slightly lesser kludge; moving the cgi stuff to + # /usr/share/zoneminder/cgi-bin breaks one set of behavior, + # having it just in /usr/lib/cgi-bin breaks another bit of + # behavior. + # The link stuff for /usr/share/zoneminder/cgi-bin has been moved to + # zoneminder-ui-base.links file + +override_dh_installinit: + dh_installinit --package=zoneminder-core --name=zoneminder + +override_dh_systemd_start: + dh_systemd_start --package=zoneminder-core --name=zoneminder \ + --restart-after-upgrade + +override_dh_systemd_enable: + dh_systemd_enable --package=zoneminder-core --name=zoneminder + +override_dh_fixperms: + dh_fixperms + # + # As requested by the Debian Webapps Policy Manual §3.2.1 + chown root:www-data debian/zoneminder-core/etc/zm/zm.conf + chmod 640 debian/zoneminder-core/etc/zm/zm.conf + +.PHONY: override_dh_strip +override_dh_strip: + dh_strip --dbg-package=zoneminder-core-dbg + +# Inspired by https://wiki.debian.org/onlyjob/get-orig-source +.PHONY: get-orig-source +get-orig-source: $(TARBALL) $(info I: $(PKG)_$(VER)$(DTYPE)) + @ + +$(TARBALL): + $(if $(wildcard $(PKG)-$(VER)),$(error folder '$(PKG)-$(VER)' exists, aborting...)) + @echo "# Cloning origin repository..."; \ + if ! git clone $(UURL) $(PKG)-$(VER); then \ + $(RM) -r $(PKG)-$(VER); \ + echo "failed to clone repository, aborting..."; \ + false; \ + fi + @if [ $(BRANCH) != "master" ]; then \ + cd $(PKG)-$(VER); \ + echo "# Not on master branch, fetching origin branch '$(BRANCH)'..."; \ + git fetch origin $(BRANCH):$(BRANCH) || false; \ + echo "# Switching to branch '$(BRANCH)'..."; \ + git checkout $(BRANCH) || false; \ + fi + @echo "# Checking local source..." + @if [ $$(cd $(PKG)-$(VER) && git rev-parse HEAD) = $(HEAD) ]; then \ + echo "even with origin, ok"; \ + true; \ + else \ + echo "not even with origin, aborting..."; \ + false; \ + fi + @echo "# Setting times..." + @cd $(PKG)-$(VER) \ + && for F in $$(git ls-tree -r --name-only HEAD | sed -e "s/\s/\*/g"); do \ + touch --no-dereference -d "$$(git log -1 --format="%ai" -- $$F)" "$$F"; \ + done + @echo "# Cleaning-up..." + cd $(PKG)-$(VER) && $(RM) -r .git + @echo "# Packing file '$(TARBALL)'..." + @find -L "$(PKG)-$(VER)" -xdev -type f -print | sort \ + | XZ_OPT="-6v" tar -caf "$(TARBALL)" -T- --owner=root --group=root --mode=a+rX \ + && $(RM) -r "$(PKG)-$(VER)" diff --git a/distros/ubuntu1504_cmake_split_packages/source/format b/distros/ubuntu1504_cmake_split_packages/source/format new file mode 100644 index 000000000..89ae9db8f --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/web/skins/flat/views/js/plugin.js.php b/distros/ubuntu1504_cmake_split_packages/source/local-options similarity index 100% rename from web/skins/flat/views/js/plugin.js.php rename to distros/ubuntu1504_cmake_split_packages/source/local-options diff --git a/distros/ubuntu1504_cmake_split_packages/source/options b/distros/ubuntu1504_cmake_split_packages/source/options new file mode 100644 index 000000000..8bd61fce6 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/source/options @@ -0,0 +1 @@ +extend-diff-ignore = "(^|/)(config\.sub|config\.guess|Makefile|aclocal.m4|compile|config.h.in|configure|depcomp|install-sh|missing)$" diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.config b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.config new file mode 100644 index 000000000..2a15a599e --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.config @@ -0,0 +1,11 @@ +#!/bin/sh +# config maintainer script for zoneminder-core package + +set -e + +# Source the debconf stuff +. /usr/share/debconf/confmodule + +#DEBHELPER# + +exit 0 diff --git a/distros/debian_cmake/dirs b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.dirs similarity index 89% rename from distros/debian_cmake/dirs rename to distros/ubuntu1504_cmake_split_packages/zoneminder-core.dirs index 9e29e6113..350c32aff 100644 --- a/distros/debian_cmake/dirs +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.dirs @@ -1,5 +1,4 @@ var/log/zm -var/lib/zm var/cache/zoneminder/events var/cache/zoneminder/images var/cache/zoneminder/temp diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install new file mode 100644 index 000000000..bd0a03bc2 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.install @@ -0,0 +1,5 @@ +etc/zm +usr/bin +usr/share/polkit-1/actions +usr/share/polkit-1/rules.d +usr/share/man/man8 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.links b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.links new file mode 100644 index 000000000..e7d90d176 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.links @@ -0,0 +1,3 @@ +var/cache/zoneminder/events usr/share/zoneminder/www/events +var/cache/zoneminder/images usr/share/zoneminder/www/images +var/cache/zoneminder/temp usr/share/zoneminder/www/temp diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postinst b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postinst new file mode 100644 index 000000000..da2b444fe --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postinst @@ -0,0 +1,80 @@ +#! /bin/sh +# postinst maintainer script for zoneminder-core package + +set -e + +# Source the debconf stuff +. /usr/share/debconf/confmodule + +# Source the config file +CONFIGFILE=/etc/zm/zm.conf +. $CONFIGFILE + +# Do this when the package is installed, upgraded or reconfigured +if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then + + # Retrieve data from dbconfig (inputs from user) + . /etc/dbconfig-common/zoneminder.conf + + # ZoneMinder config file handling + # Inspired by: http://manpages.debian.org/cgi-bin/man.cgi?query=debconf-devel&sektion=7 + + # Backup the config file + cp -a -f $CONFIGFILE ${CONFIGFILE}.postinst.bak + + # Redeclare variables if missing in config file + test -z $dbc_dbserver || grep -Eq "^ *ZM_DB_HOST=" $CONFIGFILE \ + || echo "ZM_DB_HOST=" >> ${CONFIGFILE}.postinst.bak + test -z $dbc_dbname || grep -Eq "^ *ZM_DB_NAME=" $CONFIGFILE \ + || echo "ZM_DB_NAME=" >> ${CONFIGFILE}.postinst.bak + test -z $dbc_dbuser || grep -Eq "^ *ZM_DB_USER=" $CONFIGFILE \ + || echo "ZM_DB_USER=" >> ${CONFIGFILE}.postinst.bak + test -z $dbc_dbpass || grep -Eq "^ *ZM_DB_PASS=" $CONFIGFILE \ + || echo "ZM_DB_PASS=" >> ${CONFIGFILE}.postinst.bak + + # Prevent ZM_DB_HOST to be empty if user selected the 'unix socket' method + if test -z $dbc_dbserver; then + dbc_dbserver_override="localhost" + else + dbc_dbserver_override=$dbc_dbserver + fi + + # Update variables in config file + sed -i "s/^ *ZM_DB_HOST=.*/ZM_DB_HOST=$dbc_dbserver_override/" \ + ${CONFIGFILE}.postinst.bak + test -z $dbc_dbname || sed -i "s/^ *ZM_DB_NAME=.*/ZM_DB_NAME=$dbc_dbname/" \ + ${CONFIGFILE}.postinst.bak + test -z $dbc_dbuser || sed -i "s/^ *ZM_DB_USER=.*/ZM_DB_USER=$dbc_dbuser/" \ + ${CONFIGFILE}.postinst.bak + test -z $dbc_dbpass || sed -i "s/^ *ZM_DB_PASS=.*/ZM_DB_PASS=$dbc_dbpass/" \ + ${CONFIGFILE}.postinst.bak + + # Clean-up backup file + mv -f ${CONFIGFILE}.postinst.bak $CONFIGFILE + + + # Set some file permissions + chown $ZM_WEB_USER:$ZM_WEB_GROUP /var/log/zm + if [ -z "$2" ]; then + chown $ZM_WEB_USER:$ZM_WEB_GROUP -R /var/cache/zoneminder + fi + # As requested by the Debian Webapps Policy Manual §3.2.1 + chown root:${ZM_WEB_GROUP} $CONFIGFILE + chmod 640 $CONFIGFILE +fi + +# Do this every time the package is installed or upgraded +# Test for database presence to avoid failure of zmupdate.pl +if [ "$dbc_install" = "true" ] && [ "$1" = "configure" ]; then + + # Ensure zoneminder is stopped + deb-systemd-invoke stop zoneminder.service || exit $? + + # Run the ZoneMinder update tool + zmupdate.pl --nointeractive + +fi + +#DEBHELPER# + +exit 0 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postrm b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postrm new file mode 100644 index 000000000..d75e75e8b --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.postrm @@ -0,0 +1,37 @@ +#! /bin/sh +# postrm maintainer script for zoneminder-core package + +set -e + +# Source the debconf stuff +if [ -f /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule +fi + +if [ "$1" = "purge" ]; then + + # Ask the user if we have to remove the cache directory even if not empty + if [ -d /var/cache/zoneminder ] \ + && [ ! $(find /var/cache/zoneminder -maxdepth 0 -type d -empty 2>/dev/null) ]; then + RET="" + db_input high zoneminder/ask_delete || true + db_go || true + db_get zoneminder/ask_delete + if [ "$RET" = "true" ]; then + RET="" + db_input high zoneminder/ask_delete_again || true + db_go || true + db_get zoneminder/ask_delete_again + if [ "$RET" = "true" ]; then + rm -rf /var/cache/zoneminder + fi + fi + fi +fi + +#DEBHELPER# + +# postrm rm may freeze without that +db_stop + +exit 0 diff --git a/distros/ubuntu1204/preinst b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.preinst old mode 100755 new mode 100644 similarity index 90% rename from distros/ubuntu1204/preinst rename to distros/ubuntu1504_cmake_split_packages/zoneminder-core.preinst index 6cd01ba55..3ed1ef661 --- a/distros/ubuntu1204/preinst +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.preinst @@ -1,4 +1,5 @@ #!/bin/sh +# preinst maintainer script for zoneminder-core package set -e @@ -17,7 +18,7 @@ if [ -L /usr/share/zoneminder/images ]; then fi if [ "$abort" = "true" ]; then - cat >&2 << EOF + cat >&2 << EOF Aborting installation of zoneminder due to non-default symlinks in /usr/share/zoneminder for the images and/or events directory, which could result in loss of data. Please move your data in each of these directories to diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.templates b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.templates new file mode 100644 index 000000000..35fdefd7a --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.templates @@ -0,0 +1,19 @@ +Template: zoneminder/ask_delete +Type: boolean +Default: false +_Description: Delete this non empty directory? + A purge of the ZoneMinder package is performed but the directory + '/var/cache/zoneminder' is not empty so it will not be deleted. + . + Please consider that this directory is designed to contain data resulting from + event detection. Therefore, "proof of evidence" could be lost!" + . + If you are not sure of your decision, please do not delete this directory but + perform a manual checkup. + +Template: zoneminder/ask_delete_again +Type: boolean +Default: false +_Description: Deletion confirmed? + You have allowed the deletion of directory '/var/cache/zoneminder' although + it may contain critical data. diff --git a/distros/ubuntu1204/init.d b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.init similarity index 98% rename from distros/ubuntu1204/init.d rename to distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.init index d3354c1d8..f22143c38 100644 --- a/distros/ubuntu1204/init.d +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.init @@ -23,6 +23,7 @@ command="$ZM_PATH_BIN/zmpkg.pl" start() { echo -n "Starting $prog: " + export TZ=:/etc/localtime mkdir -p $RUNDIR && chown www-data:www-data $RUNDIR mkdir -p $TMPDIR && chown www-data:www-data $TMPDIR $command start diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.service b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.service new file mode 100644 index 000000000..d82270024 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.service @@ -0,0 +1,19 @@ +# ZoneMinder systemd unit file +# This file is intended to work with debian distributions + +[Unit] +Description=ZoneMinder CCTV recording and security system +After=network.target mysql.service apache2.service +Requires=apache2.service +Wants=mysql.service + +[Service] +User=www-data +Type=forking +ExecStart=/usr/bin/zmpkg.pl start +ExecReload=/usr/bin/zmpkg.pl restart +ExecStop=/usr/bin/zmpkg.pl stop +PIDFile=/var/run/zm/zm.pid + +[Install] +WantedBy=multi-user.target diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.tmpfile b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.tmpfile new file mode 100644 index 000000000..6ea70bf35 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-core.zoneminder.tmpfile @@ -0,0 +1 @@ +d /var/run/zm 0755 www-data www-data diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.config b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.config new file mode 100644 index 000000000..f6a84d36d --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.config @@ -0,0 +1,142 @@ +#!/bin/sh +# config maintainer script for zoneminder + +set -e + +# Source the debconf stuff +. /usr/share/debconf/confmodule + +# Set the first version in which dbconfig-common was introduced in the package +dbc_first_version="1.28.0" + +CONFIGFILE=/etc/zm/zm.conf +if [ -e $CONFIGFILE ]; then + # Source the config file if exists + . $CONFIGFILE +elif [ -e ${CONFIGFILE}.dpkg-new ]; then + # If no config file, source the config file which is going to be installed + # by the core package + . ${CONFIGFILE}.dpkg-new +else + # If no config file is going to be installed, set some default values + ZM_DB_HOST= + ZM_DB_NAME="zm" + ZM_DB_USER="zmuser" +fi + +# Set some variables for the dbconfig-common stuff +dbc_dbserver="$ZM_DB_HOST" +dbc_dbname="$ZM_DB_NAME" +dbc_dbuser="$ZM_DB_USER" + +if [ -f /usr/share/dbconfig-common/dpkg/config ]; then + + # Default use dbconfig-common + dbc_install="true" + + # Currently we only support mysql database + dbc_dbtypes="mysql" + + # Set authentication method to password + dbc_authmethod_user="password" + + # Source the dbconfig-common stuff + . /usr/share/dbconfig-common/dpkg/config +fi + +# Do this when the package is installed, upgraded or reconfigured +# Most of answers are cached so the questions will not be asked again +if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then + + # Ask the user if the database shall be installed locally or remotely + db_input high zoneminder/debconf_dblocation || true + db_go || true + db_get zoneminder/debconf_dblocation + + if [ "$RET" = "local" ]; then + if [ ! -e "/usr/sbin/mysqld" ]; then + # Display a message and exit if the user want a local database but + # no database server is available + db_input high zoneminder/debconf_dblocalmissingerror || true + db_go || true + exit 0 + else + # Set the database server to localhost + dbc_dbserver="localhost" + fi + else + # Source the dbconfig main configuration file + if [ -f /etc/dbconfig-common/config ]; then + . /etc/dbconfig-common/config + fi + if [ "$dbc_remote_questions_default" = "false" ]; then + # Display a message and exit if the dbconfig configuration does not + # allow installation of remote databases + # Note: To overcome this issue, we could think to override the + # default setting by using dbc_remote_questions_default='true' in + # maintainer scripts but unfortunately this does not work due to + # current dbconfig design + # More information here: + # https://bugs.launchpad.net/ubuntu/+source/dbconfig-common/+bug/1065331 + db_input high zoneminder/debconf_dbconfigerror || true + db_go || true + exit 0 + fi + fi + + # Ask the user for all database settings + dbc_go zoneminder $@ + + # Ask the user for the password of the database administrator if the user + # has not yet answered to this question. + # This situation may occur if the user skipped the database creation step + # when reconfiguring the package. + RET="" + db_get zoneminder/mysql/admin-pass + if [ -z "$RET" ]; then + db_input high zoneminder/mysql/admin-pass || true + db_go || true + fi + + # Do this only when not upgrading the package (no old version in argument) + if [ -z "$2" ]; then + # Ask for the password of 'admin' user + while :; do + RET="" + db_input high zoneminder/admin_password || true + db_go || true + db_get zoneminder/admin_password + # If password isn't empty we ask for password verification + if [ -z "$RET" ]; then + db_fset zoneminder/admin_password seen false + db_fset zoneminder/admin_password_again seen false + break + fi + ROOT_PW="$RET" + db_input high zoneminder/admin_password_again || true + db_go || true + db_get zoneminder/admin_password_again + if [ "$RET" = "$ROOT_PW" ]; then + ROOT_PW="" + break + fi + db_fset zoneminder/password_mismatch seen false + db_input critical zoneminder/password_mismatch || true + db_set zoneminder/admin_password "" + db_set zoneminder/admin_password_again "" + db_go || true + done + else + # If we are upgrading the package, set an empty password to disable + # password update in ZoneMinder database + db_set zoneminder/admin_password "" + fi + # Set the seen flag to not ask this question again if no password is + # provided + db_fset zoneminder/admin_password seen true + +fi + +#DEBHELPER# + +exit 0 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.dirs b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.dirs new file mode 100644 index 000000000..b37463a9e --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.dirs @@ -0,0 +1,3 @@ +usr/share/zoneminder/db +usr/share/dbconfig-common/data/zoneminder/install +usr/share/dbconfig-common/data/zoneminder/upgrade/mysql diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.install new file mode 100644 index 000000000..756c5bbfa --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.install @@ -0,0 +1 @@ +usr/share/zoneminder/db diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postinst b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postinst new file mode 100644 index 000000000..41d4e5b5b --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postinst @@ -0,0 +1,79 @@ +#! /bin/sh +# postinst maintainer script for zoneminder-db package + +set -e + +# Source the debconf stuff +. /usr/share/debconf/confmodule + +mysql_update() { + + # Source the dbconfig stuff + . /usr/share/dbconfig-common/internal/mysql + + # Update the password of the hard-coded default 'admin' account + test -z $ADMIN_PASSWORD || dbc_mysql_exec_command "UPDATE Users SET Password = password('$ADMIN_PASSWORD') WHERE Username = 'admin';" || true + + # Update the database version + dbc_mysql_exec_command "UPDATE Config SET Value = '$DB_VERSION' WHERE Name = 'ZM_DYN_DB_VERSION';" || true +} + +if [ -f /usr/share/dbconfig-common/dpkg/postinst ]; then + + # Set the first version in which dbconfig-common was introduced in the package + dbc_first_version="1.28.0" + + # Set the database type + dbc_dbtypes="mysql" + + # Source the dbconfig-common stuff + . /usr/share/dbconfig-common/dpkg/postinst +fi + +# Do this when the package is installed, upgraded or reconfigured +if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then + + # Install sql database create file for dbconfig + # (needed at first package installation) + if [ ! -f /usr/share/dbconfig-common/data/zoneminder/install/mysql ]; then + install -m 644 /usr/share/zoneminder/db/zm_create.sql \ + /usr/share/dbconfig-common/data/zoneminder/install/mysql + # Remove unneeded sql requests + # dbconfig will create the underlying database + sed -i "/^ *CREATE DATABASE /d" \ + /usr/share/dbconfig-common/data/zoneminder/install/mysql + sed -i "/^ *USE /d" \ + /usr/share/dbconfig-common/data/zoneminder/install/mysql + fi + + # Symlink sql update files for dbconfig (needed when upgrading the package) + for sqlfile in /usr/share/zoneminder/db/zm_update-*.sql; do + lnk=`echo $sqlfile | sed "s/^\/usr\/share\/zoneminder\/db\/zm_update-\(.*\)\.sql/\1/"` + if [ ! -L /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk ]; then + ln -sf $sqlfile \ + /usr/share/dbconfig-common/data/zoneminder/upgrade/mysql/$lnk + fi + done || true + + # Create the underlying database and populate it + # dbconfig will take care of applying any updates which are newer than the + # previously installed version + dbc_go zoneminder $@ + + # Get the password of ZoneMinder user 'admin' from debconf + db_get zoneminder/admin_password + ADMIN_PASSWORD=$RET + + # Remove the password from debconf database + test -z $ADMIN_PASSWORD || db_reset zoneminder/admin_password || true + + # Get the lastest database version from dbconfig upgrade folder + DB_VERSION=$(ls -rv /usr/share/dbconfig-common/data/zoneminder/upgrade/$dbc_dbtypes | head -1) + + # Update the default admin account and database version + mysql_update +fi + +#DEBHELPER# + +exit 0 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postrm b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postrm new file mode 100644 index 000000000..231f01ad7 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.postrm @@ -0,0 +1,34 @@ +#! /bin/sh +# postrm maintainer script for zoneminder-db package + +set -e + +# Source the debconf stuff +if [ -f /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule +fi + +# Source the dbconfig stuff +if [ -f /usr/share/dbconfig-common/dpkg/postrm ]; then + . /usr/share/dbconfig-common/dpkg/postrm + # Ask the user what do to with dbconfig when removing the package + dbc_go zoneminder $@ +fi + +if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then + # Remove dbconfig stuff added in postinst script + rm -rf /usr/share/dbconfig-common/data/zoneminder + # No need to manually remove the zm database, dbconfig take care of this +fi + +if [ "$1" = "purge" ]; then + # Delete a potential remaining file used in postinst script + rm -f /etc/zm/zm.conf.postinst.bak +fi + +#DEBHELPER# + +# postrm rm may freeze without that +db_stop + +exit 0 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.prerm b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.prerm new file mode 100644 index 000000000..31786116a --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.prerm @@ -0,0 +1,22 @@ +#!/bin/sh +# prerm script for zoneminder-db package + +set -e + +# Source the debconf stuff if file exists +if [ -f /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule +fi + +# If dbconfig-common is installed and has been used by zoneminder +if [ -f /usr/share/dbconfig-common/dpkg/prerm ] \ + && [ -f /etc/dbconfig-common/zoneminder.conf ]; then + # Source the dbconfig stuff + . /usr/share/dbconfig-common/dpkg/prerm + # Ask the user what do to with dbconfig before removing the package + dbc_go zoneminder $@ +fi + +# #DEBHELPER# + +exit 0 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-database.templates b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.templates new file mode 100644 index 000000000..4de4342f6 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-database.templates @@ -0,0 +1,58 @@ +Template: zoneminder/debconf_dblocation +Type: select +__Choices: local, remote +Default: local +_Description: Database location: + A database server is required to run ZoneMinder. The database can be installed + either locally or remotely on a machine of your network. + . + If you choose a remote location, you will have to select the 'tcp/ip' + connection method and enter the hostname or ip address of the remote machine + in the next configuration screens. + +Template: zoneminder/debconf_dblocalmissingerror +Type: error +_Description: No local database server is available: + Currently ZoneMinder supports mysql or mariadb database server but none of them + appears to be installed on this machine. + . + In order to complete ZoneMinder's installation, after ending of this assistant, + please install a compatible database server and then restart the assistant by + invoking: + . + $ sudo dpkg-reconfigure zoneminder + +Template: zoneminder/debconf_dbconfigerror +Type: error +_Description: Remote database servers are not allowed: + The current configuration of dbconfig-common does not allow installation of + a database on remote servers. + . + In order to reconfigure dbconfig-common, please invoke the following command + after ending of this assistant: + . + $ sudo dpkg-reconfigure dbconfig-common + . + Then, to complete ZoneMinder's installation, please restart this assistant by + invoking: + . + $ sudo dpkg-reconfigure zoneminder + +Template: zoneminder/admin_password +Type: password +_Description: New password for the ZoneMinder 'admin' user: + Please enter the password of the default administrative user. + . + While not mandatory, it is highly recommended that you set a custom password + for the administrative 'admin' user. + . + If this field is left blank, the password will not be changed. + +Template: zoneminder/admin_password_again +Type: password +_Description: Repeat password for the ZoneMinder 'admin' user: + +Template: zoneminder/password_mismatch +Type: error +_Description: Password input error + The two passwords you entered were not the same. Please try again. diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-api.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-api.install new file mode 100644 index 000000000..5759ebc85 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-api.install @@ -0,0 +1 @@ +usr/share/zoneminder/api diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-api.links b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-api.links new file mode 100644 index 000000000..888c23f60 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-api.links @@ -0,0 +1 @@ +/var/tmp/zm /usr/share/zoneminder/www/api/app/tmp diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.config b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.config new file mode 100644 index 000000000..2660208a8 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.config @@ -0,0 +1,20 @@ +#!/bin/sh +# config maintainer script for zoneminder-ui-base package + +set -e + +# Source the debconf stuff +. /usr/share/debconf/confmodule + +# Do this when the package is installed, upgraded or reconfigured +# Most of answers are cached so the questions will not be asked again +if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then + + # Ask the user for the web server(s) to configure + db_input high zoneminder/webserver || true + db_go || true +fi + +#DEBHELPER# + +exit 0 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.install new file mode 100644 index 000000000..f72b569be --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.install @@ -0,0 +1,11 @@ +debian/apache.conf etc/zm +usr/lib/cgi-bin +usr/share/zoneminder/ajax +usr/share/zoneminder/css +usr/share/zoneminder/graphics +usr/share/zoneminder/includes +usr/share/zoneminder/index.php +usr/share/zoneminder/js +usr/share/zoneminder/lang +usr/share/zoneminder/tools +usr/share/zoneminder/views diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.links b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.links new file mode 100644 index 000000000..b00a147d6 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.links @@ -0,0 +1 @@ +usr/lib/cgi-bin usr/share/zoneminder/cgi-bin diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postinst b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postinst new file mode 100644 index 000000000..a5bce3c98 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postinst @@ -0,0 +1,48 @@ +#! /bin/sh +# postinst maintainer script for zoneminder-ui-base package + +set -e + +# Source the debconf stuff +. /usr/share/debconf/confmodule + +apache_install() { + + mkdir -p /etc/apache2/conf-available + ln -sf ../../zm/apache.conf /etc/apache2/conf-available/zoneminder.conf + + COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true) + + if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then + . /usr/share/apache2/apache2-maintscript-helper + apache2_invoke enconf zoneminder + elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then + [ -d /etc/apache2/conf.d/ ] && [ ! -L /etc/apache2/conf.d/zoneminder.conf ] && ln -s ../conf-available/zoneminder.conf /etc/apache2/conf.d/zoneminder.conf + fi + + # Enable CGI script module in apache (not enabled by default on jessie) + a2enmod cgi >/dev/null 2>&1 + + # Reload the web server + deb-systemd-invoke reload apache2.service || true +} + +# Do this when the package is installed, upgraded or reconfigured +if [ "$1" = "configure" ] || [ "$1" = "reconfigure" ]; then + + # Configure the web server + db_get zoneminder/webserver + webservers="$RET" + + for webserver in $webservers; do + webserver=${webserver%,} + # Currently we only support apache2 + if [ "$webserver" = "apache2" ] ; then + apache_install $1 + fi + done +fi + +#DEBHELPER# + +exit 0 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postrm b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postrm new file mode 100644 index 000000000..fd4d0d6d8 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.postrm @@ -0,0 +1,41 @@ +#! /bin/sh +# postrm maintainer script for zoneminder-ui-base package + +set -e + +# Source the debconf stuff +if [ -f /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule +fi + +apache_remove() { + COMMON_STATE=$(dpkg-query -f '${Status}' -W 'apache2.2-common' 2>/dev/null | awk '{print $3}' || true) + if [ -e /usr/share/apache2/apache2-maintscript-helper ] ; then + . /usr/share/apache2/apache2-maintscript-helper + apache2_invoke disconf zoneminder + elif [ "$COMMON_STATE" = "installed" ] || [ "$COMMON_STATE" = "unpacked" ] ; then + rm -f /etc/apache2/conf.d/zoneminder.conf + fi + rm -f /etc/apache2/conf-available/zoneminder.conf + # Reload the web server + deb-systemd-invoke reload apache2.service || true +} + +if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then + # Deconfigure the web server + db_get zoneminder/webserver || true + for webserver in $RET; do + webserver=${webserver%,} + # Currently we only support apache2 + if [ "$webserver" = "apache2" ] ; then + apache_remove $1 + fi + done +fi + +#DEBHELPER# + +# postrm rm may freeze without that +db_stop + +exit 0 diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.templates b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.templates new file mode 100644 index 000000000..31e70277f --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-base.templates @@ -0,0 +1,7 @@ +Template: zoneminder/webserver +Type: multiselect +Choices: apache2 +Default: apache2 +_Description: Web server to reconfigure automatically: + Please choose the web server that should be automatically configured for + ZoneMinder's web portal access. diff --git a/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-classic.install b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-classic.install new file mode 100644 index 000000000..9532d9dc9 --- /dev/null +++ b/distros/ubuntu1504_cmake_split_packages/zoneminder-ui-classic.install @@ -0,0 +1 @@ +usr/share/zoneminder/skins/classic diff --git a/distros/ubuntu1604/NEWS b/distros/ubuntu1604/NEWS new file mode 100644 index 000000000..6200726cf --- /dev/null +++ b/distros/ubuntu1604/NEWS @@ -0,0 +1,10 @@ +zoneminder (1.28.1-1) unstable; urgency=low + + This version is no longer automatically initialize or upgrade database. + See README.Debian for details. + + Changed installation paths (please correct your web server configuration): + /usr/share/zoneminder --> /usr/share/zoneminder/www + /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin + + -- Dmitry Smirnov Tue, 31 Mar 2015 15:12:17 +1100 diff --git a/distros/ubuntu1604/README.Debian b/distros/ubuntu1604/README.Debian new file mode 100644 index 000000000..8182e0678 --- /dev/null +++ b/distros/ubuntu1604/README.Debian @@ -0,0 +1,160 @@ +Zoneminder for Debian +--------------------- + +Initializing database +--------------------- + + pv /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf +OR + cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf + + echo 'grant lock tables,alter,create,index,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +Hint: generate secure password with `pwgen` and update "/etc/zm/zm.conf" +accordingly. + +The following command can help to ensure that zoneminder can read its +configuration file: + + chgrp -c www-data /etc/zm/zm.conf + + +Upgrading database +------------------ + +Prior to 1.28.1 database upgrade was performed automatically. +"zoneminder" service will refuse to start with outdated database. + +Assuming that database is on "localhost" then the following command can be +used to upgrade "zm" database: + + zmupdate.pl + +Additional permissions may be required to perform upgrade: + + echo 'grant lock tables, create, alter on zm.* to 'zmuser'@localhost identified by "zmpass";'\ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql + +The following command prints the current version of zoneminder database: + + echo 'select Value from Config where Name = "ZM_DYN_CURR_VERSION";' \ + | sudo mysql --defaults-file=/etc/mysql/debian.cnf --skip-column-names zm + + +Enabling service +---------------- + +By default Zoneminder service is not starting automatically and need to be +manually activated once database is configured: + +On systemd: + + sudo systemctl enable zoneminder.service + +On SysV: + + sudo update-rc.d zoneminder enable + + +Web server set-up +----------------- + +There are few manual steps to get the web interface working: + +## Apache2 + +Apache can be configured as folder "/zm" using sample .conf: + + sudo a2enconf zoneminder + +Alternatively Apache web site configuration template can be used to setup +zoneminder as "http://zoneminder": + + sudo cp -v /usr/share/doc/zoneminder/examples/apache.conf /etc/apache2/sites-available/ + sudo a2ensite zoneminder.conf + +Common configuration steps for Apache2: + + sudo a2enmod cgi + sudo service apache2 reload + + +## nginx / fcgiwrap + +Nginx needs "php5-fpm" package to support PHP and "fcgiwrap" package +for binary "cgi-bin" applications: + + sudo apt-get install php5-fpm fcgiwrap + +To enable a URL alias that makes Zoneminder available from + + http://yourserver/zm + +the following line is to be added to "server" section of a web site +configuration: + + include /usr/share/doc/zoneminder/examples/nginx.conf; + +For "default" web site it would be sufficient to include the above +statement to the file + + /etc/nginx/sites-enabled/default + +To avoid problems with feeds from multiple cameras "fcgiwrap" should be +configured to start at least as many processes as there are cameras. +It can be done by adjusting DAEMON_OPTS in "/etc/default/fcgiwrap". +Systemd users may be affected by the following bug: + + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=792705 + + +## Note: + +When Zoneminder web site is running it may be necessary to set +Options/Paths/PATH_ZMS to "/zm/cgi-bin/nph-zms" or according to chosen web +site configuration. + + +Changing the location for images and events +------------------------------------------- + +Zoneminder, in its upstream form, stores data in /usr/share/zoneminder/. This +package modifies that by changing /usr/share/zoneminder/images and +/usr/share/zoneminder/events to symlinks to directories under +/var/cache/zoneminder. + +There are numerous places these could be put and ways to do it. But, at the +moment, if you change this, an upgrade will fail with a warning about these +locations having changed (the reason for this was that previously, an upgrade +would silently revert the changes and cause event loss - refer +bug #608793). + +If you do want to change the location, here are a couple of suggestions. +(thanks to vagrant@freegeek.org): + +These lines in fstab could allow you to bind-mount an alternate location + + /dev/sdX1 /otherdrive ext3 defaults 0 2 + /otherdrive/zoneminder/images /var/cache/zoneminder/images bind defaults 0 2 + /otherdrive/zoneminder/events /var/cache/zoneminder/events bind defaults 0 2 + + or if you have a separate partition for each: + + /dev/sdX1 /var/cache/zoneminder/images ext3 defaults 0 2 + /dev/sdX2 /var/cache/zoneminder/events ext3 defaults 0 2 + + -- Peter Howard , Sun, 16 Jan 2010 01:35:51 +1100 + +Access to /dev/video* +--------------------- + +For cameras which require access to /dev/video*, zoneminder may need the +www-data user added to the video group in order to see those cameras: + + adduser www-data video + +Note that all web applications running on the zoneminder server will then have +access to all video devices on the system. + + -- Vagrant Cascadian Sun, 27 Mar 2011 13:06:56 -0700 diff --git a/distros/ubuntu1604/TODO.Debian b/distros/ubuntu1604/TODO.Debian new file mode 100644 index 000000000..9dc59613b --- /dev/null +++ b/distros/ubuntu1604/TODO.Debian @@ -0,0 +1,12 @@ + +## Separate substantial /usr/share into its own arch-all package. + +## Decide how to handle database updates. + + * Consider possibility that database may be on another machine (#469239). + * Consider dbconfig-common? Probably not (what if database is not on localhost?). + +### Run `zmupdate.pl` from service control scripts (init.d, service) on start? + + Automatic upgrade will break "one DB, many zoneminders" setup (unimportant?). + diff --git a/distros/ubuntu1604/changelog b/distros/ubuntu1604/changelog new file mode 100644 index 000000000..beef67111 --- /dev/null +++ b/distros/ubuntu1604/changelog @@ -0,0 +1,607 @@ +zoneminder (1.29.0+dfsg-1) unstable; urgency=low + + * New upstream release [February 2016] (Closes: #788317, #770851). + + [ Dmitry Smirnov ] + * copyright/Files-Excluded += "onvif/*" due to licensing uncertainty. + * Fixed FTBFS when built with dpkg-buildpackage -A (Closes: #806126). + * FFmpeg 2.9 support. Thanks, Andreas Cadhalpun. (Closes: #803850). + * Use "ffmpeg" instead of "avconv": + + "libav_path.patch" replaced with "default_ffmpeg_path.patch". + * zoneminder/Depends: + - perl-modules (package-relation-with-perl-modules) + - libav-tools + * zoneminder/Recommends: + + ffmpeg | libav-tools + * Updated Vcs URLs. + * Build/install new man pages. + * Removed obsolete lintian-overrides. + * README: grant "index" right to DB user. + * systemd: start after MySQL but do not require the latter. + * Added new patch with spelling corrections. + * Removed obsolete patches: + - 783.patch + - 980-fix-image-size.patch + - cmake-fix-confpath.patch + - cmake.patch + - cmake-gnutls.patch + - fix-html-export.patch + - format-hardening.patch + - libv4l1-videodev.h.patch + - pod_man_fixes.patch + - pod_name_fixes.patch + - pod_zmupdate-to-pod2usage.patch + - respect-privacy.patch + - zmtrigger-plus.patch + + [ Vagrant Cascadian ] + * Remove myself from Uploaders. + + -- Dmitry Smirnov Tue, 09 Feb 2016 15:40:32 +1100 + +zoneminder (1.28.1-8) unstable; urgency=medium + + * Patchworks: + + New upstream "980-fix-image-size.patch". + + New "default_cgi-path.patch" to correct default ZM_PATH_ZMS. + * postinst: set "root" as group owner for "/var/log/zm" to silence + logrotate warnings. + * Minor correction to README.Debian. + + -- Dmitry Smirnov Sun, 16 Aug 2015 19:19:50 +1000 + +zoneminder (1.28.1-7) unstable; urgency=medium + + * Build-Depends += "cakephp (<< 3.0.0~)"; + Zoneminder is not compatible with latest CakePHP. + * Handle conffile removal from maintscript. + * rules: build man pages reproducibly. + * gbp.conf: renamed old style config section [git-dch] to [dch]. + * README + + added instructions to update owner of the "/etc/zm/zm.conf" + (Closes: #789327). + + zmupdate.pl needs CREATE rights. + + added note about required number of "fcgiwrap" workers. + * New upstream patch: "zmtrigger-plus.patch". + + -- Dmitry Smirnov Mon, 20 Jul 2015 16:30:15 +1000 + +zoneminder (1.28.1-6) unstable; urgency=low + + * New "zoneminder-doc" and "zoneminder-dbg" packages. + + -- Dmitry Smirnov Sun, 19 Apr 2015 14:50:41 +1000 + +zoneminder (1.28.1-5) unstable; urgency=low + + * Move handling of "/var/run/zm" and "/tmp/zm" from .service into .tmpfile. + Let dh_installinit do the job. Thanks, Andrew Bauer. + * Use dh_apache2 to install Apache conf file; remove old conf and symlink. + * Promote "libapache2-mod-php5 | php5-fpm" to Recommends. + * Build-Depends: + + dh-linktree + + cakephp (>= 2.6.3) + + libjs-jquery + + libjs-mootools + * Depends: + - libjs-jquery + - libjs-mootools + * Build-time replace bundled CakePHP with system one using "dh-linktree". + * Use "dh-linktree" to handle mootools and jquery symlinks. + + -- Dmitry Smirnov Sun, 19 Apr 2015 11:45:01 +1000 + +zoneminder (1.28.1-4) unstable; urgency=low + + * New patch to fix HTML export with USE_DEEP_STORAGE (closes: #723706). + * New "783.patch" to describe potential data loss in ZM_USE_DEEP_STORAGE. + * New patch to change default date format to region-neutral ISO notation + with time zone. + * Build sphinx documentation: + + Install "zoneminder.1" man page. + + Build-Depends += "python-sphinx | python3-sphinx" + + Added commented "zoneminder-doc" package. + + Added "docs.patch" to unlink distro-specific installation docs. + * rules: + + set ZM_CONTENTDIR, ZM_SOCKDIR and ZM_TMPDIR. + + remove mistakengly installed Perl module templates. + * Updated startup scripts to create ZM_TMPDIR. + * Hurd improvements: + + New patch to add PATH_MAX definitions. + + Build without MMAP support on Hurd. + + libsys-mmap-perl [!hurd-any]. + + -- Dmitry Smirnov Mon, 06 Apr 2015 18:18:55 +1000 + +zoneminder (1.28.1-3) unstable; urgency=low + + * Updated Apache2 and nginx configuration templates to support CGI. + * Updated README.Debian to document cgi-bin setup. + * Removed "/usr/share/zoneminder/www/cgi-bin" symlink. + * Added "apache2.patch" to correct Apache2 site configuration example. + * control: Suggests += "fcgiwrap". + * rules: added dh_systemd overrides to prevent automatic service + activation and start. + * Added note about manual service activation to README.Debian + (Closes: #781733). + + -- Dmitry Smirnov Thu, 02 Apr 2015 23:20:20 +1100 + +zoneminder (1.28.1-2) unstable; urgency=low + + * Removed word "Linux" from short package description. + * Build-Depends: do not require "libv4l-dev" on Hurd i.e. [!hurd-any]. + * Added run-time Perl Depends: + + libdbd-mysql-perl + + libimage-info-perl + + libmodule-load-conditional-perl + + libnet-sftp-foreign-perl + + liburi-encode-perl + * Prepare for package split: added commented "libzoneminder-perl" + and "zoneminder-dbg" packages to "debian/control". + * rules: do not install worthless ".packlist" file. + * Updated "libv4l1-videodev.h.patch" to fix v4lv1 detection in CMake. + + -- Dmitry Smirnov Thu, 02 Apr 2015 13:25:19 +1100 + +zoneminder (1.28.1-1) unstable; urgency=low + + [ Dmitry Smirnov ] + * New upstream release [February 2015]. + * Upload to unstable. + * Disabled automatic database upgrades: post(inst|rm) scripts no longer + touch database or do unexpected stuff (Closes: #779254). + See README.Debian for details. + * Updated installation paths: + + /usr/share/zoneminder --> /usr/share/zoneminder/www + + /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin + * Added logrotate config (Closes: #544826). + Thanks, Alberto Reyes. + * Native systemd service; "--with systemd" added to dh. + * Build with CMake instead of autoconf; rules clean-up. + * Build with all hardening. + * Build and install "zmupdate.pl.1" man page. + * Added nginx/php5-fpm configuration example. + * Install upstream "apache.conf" example. + * Described setup of Zoneminer web site and database in README.Debian. + * Install "/etc/zm/zm.conf" with tighter permissions. + * Added TODO.Debian. + * Added "debian/clean"; "debian/gbp.conf"; bug-presubj. + * Remove bundled Cake tests to take ~5 MB off big-usr-share. + * Standards-Version: 3.9.6; compat/debhelper to version 9. + * Vcs links to new git repository at collab-maint. + * Build-Depends: + + dh-systemd + + libgcrypt11-dev --> libgcrypt-dev + + libcurl4-gnutls-dev + + libvlc-dev + + policykit-1 (required by "zmsystemctl.pl") + - dh-autoreconf, autoconf, automake + * Depends: + - apache2 + - libapache2-mod-php5 (moved to Suggests) + - libpcre3 (invalid) + - libmodule-load-perl (obsolete; replaced with perl-modules) + - libarchive-tar-perl (obsolete; replaced with perl-modules) + - mysql-server (moved to Recommends, Closes: #759504). + - php5 + + libav-tools + + libjs-jquery (replaces bundled component) + + libjs-mootool (replaces bundled component) + + libjson-any-perl (Closes: #690803). + + perl-modules (Closes: #745819). + * Recommends: + + apache2 | httpd + + mysql-server | virtual-mysql-server (Closes: #732874). + * Suggests: + + libapache2-mod-php5 | php5-fpm + + logrotate + * Refreshed, renamed and re-ordered patches; added DEP-3 headers. + * Removed "vendor_perl" patch (applied-upstream). + * New patches: + + cmake-fix-confpath.patch + + cmake-gnutls.patch + + cmake-nossl.patch + + cmake.patch + + format-hardening.patch + + pod_man_fixes.patch + + pod_name_fixes.patch + + pod_zmupdate-to-pod2usage.patch + * Lintianisation (incomplete): + - extra-license-file + - init.d-script-missing-lsb-description + - init.d-script-does-not-source-init-functions + - privacy-breach-generic + - package-contains-empty-directory + - manpage-has-errors-from-pod2man + - manpage-has-bad-whatis-entry + - quilt-patch-missing-description + - no-dep5-copyright + * Lintian-overrides: + + unusual-interpreter usr/bin/zmsystemctl.pl #!/usr/bin/pkexec + + script-not-executable usr/share/zoneminder/www/api/* + + script-with-language-extension usr/bin/*.pl + + source-is-missing web/tools/mootools/mootools-*-yc.js + + source-is-missing web/skins/*/js/jquery-1.4.2.min.js + + source-contains-prebuilt-javascript-object + * Renamed files in "debian". + * watch: dfsg repacksuffix and dversionmangle. + * "debian/copyright" to Copyright-Format-1.0. + * Set myself as new Maintainer (Closes: #760314). + + [ Vagrant Cascadian ] + * Removed obsolete DM-Upload-Allowed flag. + * Update debian/watch to use tarballs from github. + * Add Build-Depends on libgcrypt11-dev (Closes: #745819). + * Use canonical alioth Vcs-Hg URL. + * debian/control: Add Build-Depends: libpolkit-gobject-1-dev. + * Removed configure flag "--enable-crashtrace=no", which is no longer + present upstream. + + -- Dmitry Smirnov Tue, 31 Mar 2015 15:11:13 +1100 + +zoneminder (1.26.5-3.1) experimental; urgency=low + + * Non-maintainer upload. + * Add libav10.patch and compile against libav10 (Closes: #739461) + + -- Reinhard Tartler Wed, 19 Mar 2014 00:31:22 +0000 + +zoneminder (1.26.5-3) unstable; urgency=low + + + * Previous release still didn't build on PPC - this has been corrected. + (Closes: #736516) + + -- Peter Howard Tue, 4 Feb 2014 02:02:10 +1000 + +zoneminder (1.26.5-2) unstable; urgency=low + + * Remove dependency on ffmpeg + (Closes: #721161) + + * Builds again on non-x86 target architectures. + + -- Peter Howard Thu, 23 Jan 2014 01:02:10 +1000 + +zoneminder (1.26.5-1) unstable; urgency=low + + * New upstream version + (Closes: #694131) + * Change Build-Depends on libgnutls-dev to libgnutls-openssl-dev + (Closes: #731560) + -- Peter Howard Tue, 17 Dec 2013 01:02:10 +1000 + +zoneminder (1.25.0-4) unstable; urgency=high + + * Add CVE-2013-0232 patch + [SECURITY] CVE-2013-0232: Shell escape commands with untrusted content. + Thanks to James McCoy (Closes: #698910) + Thanks also to Salvatore Bonaccorso + + -- Peter Howard Tue, 12 Jun 2013 12:02:10 +1000 + +zoneminder (1.25.0-3) unstable; urgency=low + + * debian/rules: Export CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS, to ensure + hardening build flags are enabled. + + -- Vagrant Cascadian Tue, 28 Aug 2012 12:10:03 -0700 + +zoneminder (1.25.0-2) unstable; urgency=low + + [ Vagrant Cascadian ] + * Add a patch to disable checking for updated versions by default, as + upgrades should happen through package management. + * Use dpkg-buildflags in debian/rules to set default compiler flags. + * Ensure zoneminder is stopped before starting (Closes: #657407). + + [ Peter Howard ] + * Fix postinst to add permission for table creation during upgrade + (Closes: #657407). + + -- Vagrant Cascadian Thu, 23 Aug 2012 12:40:34 -0700 + +zoneminder (1.25.0-1.1) unstable; urgency=low + + * Non-maintainer upload. + * Fix "ftbfs with GCC-4.7": add patch Fix-FTBFS-with-gcc-4.7 from Cyril + Brulebois: fix missing includes. + (Closes: #667428) + + -- gregor herrmann Sun, 13 May 2012 17:02:21 +0200 + +zoneminder (1.25.0-1) unstable; urgency=low + + * Fix typo in libv4l1-videodev.h patch that caused v4l1 support to be + dropped. + * Fail to build if version in postinst doesn't match upstream version. + * Add Build-Depends: libavdevice-dev to fix MPEG streaming (Closes: #515558). + * debian/rules: Convert to using debhelper overrides. + * Set debian/compat to 7. + * Simplify debian/watch file. + * Refresh debian/patches/use_libjs-mootools. + * Refresh debian/patches/libv4l1-videodev.h. + * Remove dependencies on php4 and related packages. + * Remove build-dependencies on libmysqlclient14-dev and + libmysqlclient15-dev. + * Update Build-Depends to use libjpeg-dev instead of libjpeg62-dev + (Closes: #647114). + * Add patch to fix build by testing for C headers rather than C++ headers. + Thanks to Ryan Niebur. (Closes: #654230) + * Add a patch to fix build problems caused by API changes in libav 0.8. + Thanks again to Ryan Niebur. (Closes: #654230) + + -- Vagrant Cascadian Mon, 16 Jan 2012 11:58:05 -0800 + +zoneminder (1.24.4-1) unstable; urgency=low + + [ Peter Howard ] + * Initial release of 1.24.4 (Closes: #634985). + - Fix 32/64-bit type declarations (Closes: #614404). + * Update patches. + + [ Vagrant Cascadian ] + * Add patch to fix FTBFS by using libv4l1-videodev.h from libv4l-dev. + Thanks to Andreas Metzler for reporting the issue. + (Closes: #619813). + * Document adding the www-data user to the video group in README.Debian. + (Closes: #611324) + * Depend on libsys-mmap-perl to enable mapped memory support. + (Closes: #607331) + * Update libjs-mootools patch to use -nc variants (Closes: #635075). + * Depend on javascript-common, to ensure that /javascript is available in + the web server. + * Set the upstream version in postinst at build time. + * Use dh-autoreconf to properly clean up autogenerated files during build. + * Add Vcs-HG to debian/control. + * Add Build-Depends: libv4l-dev, libbz2-dev, dh-autoreconf, libsys-mmap-perl. + + -- Vagrant Cascadian Sun, 24 Jul 2011 16:44:30 +0200 + +zoneminder (1.24.2-9) unstable; urgency=low + + * Apply patch from Ubuntu to fix FTBFS with ffmpeg 0.6: + - Add -D__STDC_CONSTANT_MACROS to CPPFLAGS (closes: 614080). + * Update Standards-Version to 3.9.1, no changes necessary. + + -- Vagrant Cascadian Sun, 20 Feb 2011 23:43:02 -0800 + +zoneminder (1.24.2-8) unstable; urgency=medium + + [ Vagrant Cascadian ] + * Apply patch to fix V4L2 cameras without crop support (closes: #608790). + Thanks to piratebab. + * Add preinst script which aborts if dangerous symlinks exist. + (closes: #608793) + + [ Peter Howard ] + * Added to README.Debian with info about images and events directories. + (closes: #608793) + + -- Vagrant Cascadian Sat, 15 Jan 2011 19:39:26 -0800 + +zoneminder (1.24.2-7) unstable; urgency=medium + + * Do not set ownership of /var/cache/zoneminder when upgrading, which fixes a + regression causing upgrades to take inordinately long with large + installations (closes: #597040). + + -- Vagrant Cascadian Fri, 17 Sep 2010 11:24:41 -0700 + +zoneminder (1.24.2-6) unstable; urgency=low + + * Only remove database on purge. This requires only creating the database if + it doesn't already exist, and upgrading the database only if the database + is an older version (closes: #497107). + + * Do not prompt the user on database upgrades by using the --nointeractive + flag when calling zmupdate.pl from postinst (closes: #595902). + + -- Vagrant Cascadian Fri, 10 Sep 2010 10:06:06 -0700 + +zoneminder (1.24.2-5) unstable; urgency=low + + [ Peter Howard ] + * Add zip dependency + (closes: #494261) + * Add debian/watch file + (closes: #545552) + * Use packaged libjs-mootools + (closes: #585590) + * Miscellaneous cleanups + + [ Vagrant Cascadian ] + * Add vagrant@debian.org as uploader + * Update Standards-Version to 3.9.0, no changes necessary. + + -- Vagrant Cascadian Fri, 23 Jul 2010 18:12:50 -0500 + +zoneminder (1.24.2-4.1) unstable; urgency=low + + * Non-maintainer upload. + * Fix "package removed, processes still running": apply patch to + debian/postinst by Vagrant Cascadian: use invoke-rc.d and run + mysql-related actions only when mysql is running (closes: #583648). + + -- gregor herrmann Thu, 01 Jul 2010 19:47:10 +0200 + +zoneminder (1.24.2-4) unstable; urgency=high + * Update init.d to list mysql dependency + (closes: #583505) + * Change depenency from libmime-perl to libmime-tools-perl + (closes: #585589) + * Problems in changelog format fixed + (closes: #585592) + * Fix debian-rules-ignores-make-clean-error + (closes: #585593) + -- Peter Howard Mon, 14 jun 2010 15:02:10 +1000 + +zoneminder (1.24.2-3) unstable; urgency=high + * Changes symbols to build with libjpeg8 + (closes: #565326, #568327) + * Note: location of all perl files should have been fixed in previous release + (closes: #553096) + -- Peter Howard Mon, 26 apr 2010 15:02:10 +1000 + +zoneminder (1.24.2-2) unstable; urgency=high + + * Remove custom perl parth from zmpkg.pl, fix location of manpages. + (closes: #551746, #553092) + * Fix GCC4.4 bug + (closes: #531717) + * Fix potential bug in postinst script + + -- Peter Howard Sat, 14 Nov 2009 15:02:10 +1000 + +zoneminder (1.24.2-1) unstable; urgency=high + + * Initial release of zoneminder 1.24.2 + -- Peter Howard Fri, 11 Sep 2009 07:02:50 +1000 + +zoneminder (1.24.1-1) unstable; urgency=high + + * Initial release of zoneminder 1.24.1, closing CVE-2008-3882, + CVE-2008-3881, CVE-2008-3880 + (closes: #497640) + * Change syslog dependency to rsyslog. + (closes: #526918) + * Add missing perl depenency. + * Restore patch to disable "check for updates" by default. + * Removed spurious '$' in init script. + (closes: #486064) + * Change permission of zm.conf from 0600 to 0400 for CVE-2008-6755 + (closes: #528252) + -- Peter Howard Sat, 16 May 2009 07:02:50 +1000 + +zoneminder (1.23.3-4) unstable; urgency=high + + * update to get it building with latest unstable. Thanks to waldi@debian.org + (closes: #517569) + -- Peter Howard Thu, 16 Apr 2009 01:02:50 +1000 + +zoneminder (1.23.3-3) unstable; urgency=high + + * ffmpeg confirmed working + (closes: #475145) + * Fix upgrade problem intrudouced in 1.23.3-1 + (closes: #481637) + * Include libmime-lite-perl in dependencies + (closes: #486312) + -- Peter Howard Thu, 18 Sep 2008 01:02:50 +1000 + +zoneminder (1.23.3-2) unstable; urgency=high + + * ffmpeg finally working? + + -- Peter Howard Wed, 13 Aug 2008 01:02:50 +1000 + +zoneminder (1.23.3-1) unstable; urgency=high + + * Initial version for 1.23.3 - security fix. + (closes: #479034) + + -- Peter Howard Wed, 19 Mar 2008 01:02:50 +1000 + +zoneminder (1.23.2-2) unstable; urgency=low + + * Update to init.d + (closes: #468856) + * Add dependency on logging daemon + (closes: #471277) + + -- Peter Howard Wed, 19 Mar 2008 01:02:50 +1000 + +zoneminder (1.23.2-1) unstable; urgency=low + + * Initial version for 1.23.2 + (closes: #464152) + * Zoneminder 1.23.2 upstream includes fix for GCC 4.3 + (closes: #454980) + * Includes ffmpeg patch by Alexander Kushnirenko + + -- Peter Howard Sat, 01 Mar 2008 16:02:50 +1000 + +zoneminder (1.22.3-10) unstable; urgency=low + + * Fix bug introduced in -9 where perl is put under /usr/local + (closes: #457507) + + -- Peter Howard Mon, 24 Dec 2007 16:02:50 +1000 + +zoneminder (1.22.3-9) unstable; urgency=low + + * Starting zoneminder via init script now invokes "zmfix -a" + (closes: #481637) + * Change apache2-mpm-prefork dependency to apache2 + * Temp dir for export under /var/cache/zoneminder (but linked back to + /usr/share/zoneminder for now) + * Redo use of gnutls rather than openssl for md5 hashes + + -- Peter Howard Mon, 10 Dec 2007 16:02:50 +1000 + +zoneminder (1.22.3-8) unstable; urgency=low + + * Build now includes libpcre3 + (closes: #437533) + * "Monitor Presets" patch now applied to package during build. + + -- Peter Howard Sat, 18 Aug 2007 14:35:23 +1000 + +zoneminder (1.22.3-7) unstable; urgency=low + + * Turn off debug trace and crash dump on build + (closes:#414857,#414891) + * Additional perl libraries added in dependencies + (closes:#416291) + * Change preferred PHP version from 4 to 5 + -- Peter Howard Sun, 29 Jul 2007 15:11:13 +1000 + +zoneminder (1.22.3-6) unstable; urgency=low + + * Removed a similar bash only statement from zmpkg.pl + (closes:414882) + + -- Peter Howard Sat, 14 Apr 2007 11:46:56 +1000 + +zoneminder (1.22.3-5) unstable; urgency=low + + * Installs with "phone home" feature turned off by default, and permissions + on /etc/zm/zm.conf fixed (now the 0600 it s hould be) + (closes:415349) + * Removed "stupid bash-ism" on mysqld check in postinst file. + + -- Peter Howard Fri, 6 Apr 2007 15:50:00 +1000 + +zoneminder (1.22.3-4) unstable; urgency=low + + * Put libmysqlclient-15-dev in front of -14-dev so sbuild works + (closes: #414410) + + -- Peter Howard Mon, 12 Mar 2007 11:38:56 +1100 + +zoneminder (1.22.3-3) unstable; urgency=low + + * Clean up of postinstall, postrm ; user "zm" definitely was a mistake + * Also in postinstall: check and start MySQL if it's not running. + * init.d script now checks if zoneminder isn't running and still returns 0 + (which helps uninstalling) + * Addition of php5 dependency options as well as php4. + + -- Peter Howard Mon, 26 Feb 2007 10:40:52 +1100 + +zoneminder (1.22.3-2) unstable; urgency=low + + * Added zmuser in the mysql creation; this should fix the install problem + for people, but needs to be cleaned up (in -3) + + -- Peter Howard Fri, 16 Feb 2007 14:16:03 +1100 + +zoneminder (1.22.3-1) unstable; urgency=low + + * Initial Version. (closes: #248393) + * Patched out use of openssl; uses gnutls instead for MD5 hashes. + * Removed MakeMaker-inserted Perl licensing (with authors permission) in + various scripts; replaced with GPL. + + -- Peter Howard Wed, 7 Feb 2007 14:09:01 +1100 diff --git a/distros/ubuntu1604/clean b/distros/ubuntu1604/clean new file mode 100644 index 000000000..941ef2a3a --- /dev/null +++ b/distros/ubuntu1604/clean @@ -0,0 +1,3 @@ +.gitattributes +web/api/.gitattributes +web/api/.gitignore diff --git a/distros/ubuntu1604/compat b/distros/ubuntu1604/compat new file mode 100644 index 000000000..ec635144f --- /dev/null +++ b/distros/ubuntu1604/compat @@ -0,0 +1 @@ +9 diff --git a/distros/ubuntu1604/conf/apache2/zoneminder.conf b/distros/ubuntu1604/conf/apache2/zoneminder.conf new file mode 100644 index 000000000..40fbf4d7c --- /dev/null +++ b/distros/ubuntu1604/conf/apache2/zoneminder.conf @@ -0,0 +1,19 @@ +# Remember to enable cgi mod (i.e. "a2enmod cgi"). +ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin" + + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + AllowOverride All + Require all granted + + +Alias /zm /usr/share/zoneminder/www + + Options Indexes FollowSymLinks + + DirectoryIndex index.php + + + + + AllowOverride All + diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control new file mode 100644 index 000000000..c1ee3cc0d --- /dev/null +++ b/distros/ubuntu1604/control @@ -0,0 +1,153 @@ +Source: zoneminder +Section: net +Priority: optional +Maintainer: Dmitry Smirnov +Uploaders: Vagrant Cascadian +Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree + ,cmake + ,libavdevice-dev (>= 6:10~) + ,libavcodec-dev (>= 6:10~) + ,libavformat-dev (>= 6:10~) + ,libavutil-dev (>= 6:10~) + ,libswscale-dev (>= 6:10~) + ,libbz2-dev + ,libgcrypt-dev + ,libcurl4-gnutls-dev + ,libgnutls-openssl-dev + ,libjpeg-dev + ,libmysqlclient-dev + ,libpcre3-dev + ,libpolkit-gobject-1-dev + ,libv4l-dev (>= 0.8.3) [!hurd-any] + ,libvlc-dev + ,libdate-manip-perl + ,libdbd-mysql-perl + ,libphp-serialization-perl + ,libsys-mmap-perl [!hurd-any] + ,libwww-perl + ,libdata-uuid-perl +# Unbundled (dh_linktree): + ,libjs-jquery + ,libjs-mootools +Standards-Version: 3.9.6 +Homepage: http://www.zoneminder.com/ +Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git +Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git + +Package: zoneminder +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} + ,javascript-common + ,libav-tools + ,libdate-manip-perl + ,libdbd-mysql-perl + ,libmime-lite-perl + ,libmime-tools-perl + ,libphp-serialization-perl + ,libmodule-load-conditional-perl + ,libnet-sftp-foreign-perl + ,libarchive-zip-perl + ,libdbd-mysql-perl + ,libdevice-serialport-perl + ,libimage-info-perl + ,libjson-any-perl + ,libsys-mmap-perl [!hurd-any] + ,liburi-encode-perl + ,libwww-perl + ,libdata-dump-perl + ,libclass-std-fast-perl + ,libsoap-wsdl-perl + ,libio-socket-multicast-perl + ,libdigest-sha-perl + ,libsys-cpu-perl, libsys-meminfo-perl + ,libdata-uuid-perl + ,mysql-client | virtual-mysql-client + ,perl-modules + ,php5-mysql | php-mysql, php5-gd | php-gd + ,policykit-1 + ,rsyslog | system-log-daemon + ,zip +Recommends: ${misc:Recommends} + ,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm + ,mysql-server | virtual-mysql-server + ,zoneminder-doc (>= ${source:Version}) + ,ffmpeg +Suggests: fcgiwrap, logrotate +Description: video camera security and surveillance solution + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + +#Package: libzoneminder-perl +#Section: perl +#Architecture: all +#Multi-Arch: foreign +#Depends: ${misc:Depends}, ${perl:Depends} +# ,libarchive-zip-perl +# ,libdbd-mysql-perl +# ,libdevice-serialport-perl +# ,libimage-info-perl +# ,libjson-any-perl +# ,libsys-mmap-perl [!hurd-any] +# ,liburi-encode-perl +# ,libwww-perl +#Description: ZoneMinder Perl libraries +# ZoneMinder is intended for use in single or multi-camera video security +# applications, including commercial or home CCTV, theft prevention and child +# or family member or home monitoring and other care scenarios. It +# supports capture, analysis, recording, and monitoring of video data coming +# from one or more video or network cameras attached to a Linux system. +# ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom +# cameras using a variety of protocols. It is suitable for use as a home +# video security system and for commercial or professional video security +# and surveillance. It can also be integrated into a home automation system +# via X.10 or other protocols. +# . +# This package provides ZoneMinder Perl libraries; it can be used to +# write custom interfaces as well. + +Package: zoneminder-doc +Section: doc +Architecture: all +Multi-Arch: foreign +Depends: ${misc:Depends}, ${sphinxdoc:Depends}, python-sphinx-rtd-theme | python3-sphinx-rtd-theme +Suggests: www-browser +Description: ZoneMinder documentation + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + . + This package provides ZoneMinder documentation in HTML format. + +Package: zoneminder-dbg +Section: debug +Priority: extra +Architecture: any +Depends: zoneminder (= ${binary:Version}), ${misc:Depends} +Description: Zoneminder -- debugging symbols + ZoneMinder is intended for use in single or multi-camera video security + applications, including commercial or home CCTV, theft prevention and child + or family member or home monitoring and other care scenarios. It + supports capture, analysis, recording, and monitoring of video data coming + from one or more video or network cameras attached to a Linux system. + ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom + cameras using a variety of protocols. It is suitable for use as a home + video security system and for commercial or professional video security + and surveillance. It can also be integrated into a home automation system + via X.10 or other protocols. + . + This package provides debugging symbols diff --git a/distros/ubuntu1604/copyright b/distros/ubuntu1604/copyright new file mode 100644 index 000000000..c48025a25 --- /dev/null +++ b/distros/ubuntu1604/copyright @@ -0,0 +1,174 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ZoneMinder +Upstream-Contact: Philip Coombes +Source: https://github.com/ZoneMinder/ZoneMinder +Comment: + This package was originally debianized by matrix + on Mon, 7 Mar 2005 02:07:57 -0500. + It was re-done for submission to the Debian project by Peter Howard + on Fri, 8 Dec 2006 10:19:43 +1100 +Files-Excluded: + web/skins/*/js/jquery-* + web/tools/mootools/*-yc.js + +Files: * +Copyright: 2001-2014 Philip Coombes + 2008 Brian Rudy + 2014 Vincent Giovannone + 2013 Tim Craig + 2003-2008 Corey DeLasaux + 2001-2010 Chris Kistner +License: GPL-2+ + +Files: distros/* +Copyright: 2001-2008 Philip Coombes + 2014 Isaac Connor + 2005 Serg Oskin +License: GPL-2+ + +Files: web/skins/*/js/jquery-* +Copyright: 2010 John Resig + 2010 The Dojo Foundation +License: GPL-2 or Expat +Comment: + Dual licensed under the MIT or GPL Version 2 licenses. + http://jquery.org/license + . + Includes Sizzle.js http://sizzlejs.com/ + Released under the MIT, BSD, and GPL Licenses. + +Files: web/tools/mootools/*.js +Copyright: 2009 Marcelo Jorge Vieira (metal) + 2006-2010 Valerio Proietti (http://mad4milk.net/) +License: Expat + +Files: web/api/* +Copyright: 2005-2013 Cake Software Foundation, Inc. (http://cakefoundation.org) +License: Expat + +Files: + cmake/Modules/CheckPrototypeDefinition*.cmake + cmake/Modules/FindGLIB2.cmake + cmake/Modules/FindPolkit.cmake + cmake/Modules/GNUInstallDirs.cmake +Copyright: + 2005-2011 Kitware, Inc. + 2010-2011 Andreas Schneider + 2009 Dario Freddi + 2008 Laurent Montel, + 2011 Nikita Krupen'ko +License: BSD-3-clause + 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. + . + * The names of Kitware, Inc., the Insight Consortium, or the names of + any consortium members, or of any contributors, may not be used to + endorse or promote products derived from this software without + specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER 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 AUTHORS 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. + +Files: cmake/Modules/FindPerlModules.cmake +Copyright: 2012 Iowa State University +License: Boost-1.0 + Boost Software License - Version 1.0 - August 17th, 2003 + . + Permission is hereby granted, free of charge, to any person or organization + obtaining a copy of the software and accompanying documentation covered by + this license (the "Software") to use, reproduce, display, distribute, + execute, and transmit the Software, and to prepare derivative works of the + Software, and to permit third-parties to whom the Software is furnished to + do so, all subject to the following: + . + The copyright notices in the Software and this entire statement, including + the above license grant, this restriction and the following disclaimer, + must be included in all copies of the Software, in whole or in part, and + all derivative works of the Software, unless such copies or derivative + works are solely in the form of machine-executable object code generated by + a source language processor. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +Files: debian/* +Copyright: 2015 Dmitry Smirnov + 2007-2014 Peter Howard + 2010-2012 Vagrant Cascadian + 2001-2008 Philip Coombes +License: GPL-2+ + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +License: GPL-2+ + This package 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 package 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 package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". + +License: GPL-2 + This package 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; version 2 of the License. + . + This package 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 package; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + . + The complete text of the GNU General Public License version 2 + can be found in "/usr/share/common-licenses/GPL-2". diff --git a/distros/ubuntu1604/examples/nginx.conf b/distros/ubuntu1604/examples/nginx.conf new file mode 100644 index 000000000..5636ca3e1 --- /dev/null +++ b/distros/ubuntu1604/examples/nginx.conf @@ -0,0 +1,32 @@ +location /zm/cgi-bin { + gzip off; + alias /usr/lib/zoneminder/cgi-bin; + + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_pass unix:/var/run/fcgiwrap.socket; +} + +location /zm { +# if ($scheme ~ ^http:){ +# rewrite ^(.*)$ https://$host$1 permanent; +# } + + gzip off; + alias /usr/share/zoneminder/www; + index index.php; + + location ~ \.php$ { + if (!-f $request_filename) { return 404; } + expires epoch; + include /etc/nginx/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $request_filename; + fastcgi_index index.php; + fastcgi_pass unix:/var/run/php5-fpm.sock; + } + + location ~ \.(jpg|jpeg|gif|png|ico)$ { + access_log off; + expires 33d; + } +} diff --git a/distros/ubuntu1604/gbp.conf b/distros/ubuntu1604/gbp.conf new file mode 100644 index 000000000..4608913d9 --- /dev/null +++ b/distros/ubuntu1604/gbp.conf @@ -0,0 +1,7 @@ + +[dch] +id-length = 0 + +[import-orig] +pristine-tar = False +merge = False diff --git a/distros/ubuntu1604/libzoneminder-perl.install b/distros/ubuntu1604/libzoneminder-perl.install new file mode 100644 index 000000000..67191d9cf --- /dev/null +++ b/distros/ubuntu1604/libzoneminder-perl.install @@ -0,0 +1,2 @@ +usr/share/man/man3 +usr/share/perl5 diff --git a/distros/ubuntu1604/patches/default_cgi-path.patch b/distros/ubuntu1604/patches/default_cgi-path.patch new file mode 100644 index 000000000..8bfc2ba06 --- /dev/null +++ b/distros/ubuntu1604/patches/default_cgi-path.patch @@ -0,0 +1,16 @@ +Last-Update: 2015-08-16 +Forwarded: no +Author: Dmitry Smirnov +Description: correct path to CGI app according to default web server configuration. + +--- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in ++++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +@@ -428,7 +428,7 @@ our @options = + }, + { + name => "ZM_PATH_ZMS", +- default => "/cgi-bin/nph-zms", ++ default => "/zm/cgi-bin/nph-zms", + description => "Web path to zms streaming server", + help => qqq(" + The ZoneMinder streaming server is required to send streamed diff --git a/distros/ubuntu1604/patches/series b/distros/ubuntu1604/patches/series new file mode 100644 index 000000000..fc70f4006 --- /dev/null +++ b/distros/ubuntu1604/patches/series @@ -0,0 +1,2 @@ +default_cgi-path.patch +use_libjs-mootools.patch diff --git a/distros/ubuntu1604/patches/use_libjs-mootools.patch b/distros/ubuntu1604/patches/use_libjs-mootools.patch new file mode 100644 index 000000000..b3925f6d0 --- /dev/null +++ b/distros/ubuntu1604/patches/use_libjs-mootools.patch @@ -0,0 +1,18 @@ +Last-Update: 2015-03-29 +Forwarded: no +Bug-Debian: http://bugs.debian.org/585590 +Reviewed-By: Dmitry Smirnov +Description: use mootools shipped by debian, rather than the zoneminder included mootools. + +--- a/web/skins/classic/includes/functions.php ++++ b/web/skins/classic/includes/functions.php +@@ -63,9 +63,8 @@ + } + ?> + + +- + + + >/dev/null 2>&1 || : + endscript + daily + rotate 7 +} diff --git a/distros/ubuntu1604/zoneminder.maintscript b/distros/ubuntu1604/zoneminder.maintscript new file mode 100644 index 000000000..3aa20b3a0 --- /dev/null +++ b/distros/ubuntu1604/zoneminder.maintscript @@ -0,0 +1 @@ +rm_conffile /etc/zm/apache.conf 1.28.1-5~ diff --git a/distros/ubuntu1604/zoneminder.manpages b/distros/ubuntu1604/zoneminder.manpages new file mode 100644 index 000000000..d2053d688 --- /dev/null +++ b/distros/ubuntu1604/zoneminder.manpages @@ -0,0 +1 @@ +docs/_build/man/*.1 diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst new file mode 100644 index 000000000..64699d1ca --- /dev/null +++ b/distros/ubuntu1604/zoneminder.postinst @@ -0,0 +1,59 @@ +#! /bin/sh + +set -e + +if [ "$1" = "configure" ]; then + + . /etc/zm/zm.conf + + # The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group + chown www-data:root /var/log/zm + chown www-data:www-data /var/lib/zm + if [ -z "$2" ]; then + chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/* + fi + + # Do this every time the package is installed or upgraded + + if [ "$ZM_DB_HOST" = "localhost" ]; then + + if [ -e "/etc/init.d/mysql" ]; then + + # + # Get mysql started if it isn't + # + if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then + deb-systemd-invoke start mysql.service || exit $? + fi + + if $(/etc/init.d/mysql status >/dev/null 2>&1); then + mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload + # test if database if already present... + if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then + cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf + # This creates the user. + echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + else + echo "grant lock tables,alter,select,insert,update,delete,create,index on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql + fi + + # Ensure zoneminder is stopped + deb-systemd-invoke stop zoneminder.service || exit $? + zmupdate.pl --nointeractive + zmupdate.pl --nointeractive -f + deb-systemd-invoke start zoneminder.service || exit $? + + else + echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.' + fi + else + echo 'mysql not found, assuming remote server.' + fi + + else + echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)" + fi + +fi + +#DEBHELPER# diff --git a/distros/ubuntu1604/zoneminder.postrm b/distros/ubuntu1604/zoneminder.postrm new file mode 100644 index 000000000..ba2066c8d --- /dev/null +++ b/distros/ubuntu1604/zoneminder.postrm @@ -0,0 +1,14 @@ +#! /bin/sh + +set -e + +if [ "$1" = "purge" ]; then + echo " +Reminder: to completely remove \"zoneminder\" it may be necessary + * to delete database using the following sample command: + sudo mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm + * to delete remaining data files in "/var/cache/zoneminder". +" +fi + +#DEBHELPER# diff --git a/distros/ubuntu1604/zoneminder.preinst b/distros/ubuntu1604/zoneminder.preinst new file mode 100644 index 000000000..9459b48d0 --- /dev/null +++ b/distros/ubuntu1604/zoneminder.preinst @@ -0,0 +1,36 @@ +#!/bin/sh + +set -e + +## Remove obsolete symlink which is in the way of dh_apache2: +ol="/etc/apache2/conf-available/zoneminder.conf" +if [ -h "${ol}" ]; then + [ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}" +fi + +abort=false +if [ -h /usr/share/zoneminder/www/events ]; then + l=$(readlink /usr/share/zoneminder/www/events) + if [ "$l" != "/var/cache/zoneminder/events" -a "$l" != "/var/cache/zoneminder/events/" ]; then + abort=true + fi +fi +if [ -h /usr/share/zoneminder/www/images ]; then + l=$(readlink /usr/share/zoneminder/www/images ) + if [ "$l" != "/var/cache/zoneminder/images" -a "$l" != "/var/cache/zoneminder/images/" ]; then + abort=true + fi +fi + +if [ "$abort" = "true" ]; then + cat >&2 << EOF +Aborting installation of zoneminder due to non-default symlinks in +/usr/share/zoneminder for the images and/or events directory, which could +result in loss of data. Please move your data in each of these directories to +/var/cache/zoneminder before installing zoneminder from the package. +EOF + exit 1 + +fi + +#DEBHELPER# diff --git a/distros/ubuntu1604/zoneminder.service b/distros/ubuntu1604/zoneminder.service new file mode 100644 index 000000000..e3575c039 --- /dev/null +++ b/distros/ubuntu1604/zoneminder.service @@ -0,0 +1,20 @@ +# ZoneMinder systemd unit file +# This file is intended to work with Debian distributions + +[Unit] +Description=ZoneMinder CCTV recording and surveillance system +After=network.target mysql.service +# Remarked out so that it will start ZM on machines that don't have mysql installed +#Requires=mysql.service + +[Service] +#User=www-data +Type=forking +ExecStart=/usr/bin/zmpkg.pl start +ExecReload=/usr/bin/zmpkg.pl restart +ExecStop=/usr/bin/zmpkg.pl stop +PIDFile=/var/run/zm/zm.pid +Restart=on-abnormal + +[Install] +WantedBy=multi-user.target diff --git a/distros/ubuntu1604/zoneminder.tmpfile b/distros/ubuntu1604/zoneminder.tmpfile new file mode 100644 index 000000000..d307c6640 --- /dev/null +++ b/distros/ubuntu1604/zoneminder.tmpfile @@ -0,0 +1,2 @@ +d /var/run/zm 0755 www-data www-data +d /tmp/zm 0755 www-data www-data diff --git a/docs/_static/zmstyle.css b/docs/_static/zmstyle.css new file mode 100644 index 000000000..b8f0235f3 --- /dev/null +++ b/docs/_static/zmstyle.css @@ -0,0 +1,3 @@ +img { + border: 1px solid black !important; + } diff --git a/docs/api.rst b/docs/api.rst index 26b6be251..9546a279a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,10 +1,10 @@ API -=== +==== -This document will provide an overview of ZoneMinder's API. +This document will provide an overview of ZoneMinder's API. This is work in progress. Overview --------- +^^^^^^^^ In an effort to further 'open up' ZoneMinder, an API was needed. This will allow quick integration with and development of ZoneMinder. @@ -13,106 +13,332 @@ The API is built in CakePHP and lives under the ``/api`` directory. It provides a RESTful service and supports CRUD (create, retrieve, update, delete) functions for Monitors, Events, Frames, Zones and Config. -Examples --------- +Security +^^^^^^^^^ +The APIs tie into ZoneMinder's existing security model. This means if you have +OPT_AUTH enabled, you need to log into ZoneMinder using the same browser you plan to +use the APIs from. If you are developing an app that relies on the API, you need +to do a POST login from the app into ZoneMinder before you can access the API. -Here be a list of examples. Some results may be truncated. +Then, you need to re-use the authentication information of the login (returned as cookie states) +with subsequent APIs for the authentication information to flow through to the APIs. + +This means if you plan to use cuRL to experiment with these APIs, you first need to do + +:: + + curl -d "username=XXXX&password=YYYY&action=login&view=console" -c cookies.txt http://yourzmip/zm/index.php + +replacing *XXXX* and *YYYY* with your username and password, respectively. + +Please make sure you do this in a directory where you have write permissions, otherwise cookies.txt will not be created +and the command will silently fail. + + +What the "-c cookies.txt" does is store a cookie state reflecting that you have logged into ZM. You now need +to apply that cookie state to all subsequent APIs. You do that by using a '-b cookies.txt' to subsequent APIs if you are +using CuRL like so: + +:: + + curl -b cookies.txt http://yourzmip/zm/api/monitors.json + +This would return a list of monitors and pass on the authentication information to the ZM API layer. + +So remember, if you are using authentication, please add a ``-b cookies.txt`` to each of the commands below if you are using +CuRL. If you are not using CuRL and writing your own app, you need to make sure you pass on cookies to subsequent requests +in your app. + +Examples (please read security notice above) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You will see each URL ending in either ``.xml`` or ``.json``. This is the format of the request, and it determines the format that any data returned to you will be in. I like json, however you can use xml if you'd like. +(In all examples, replace 'server' with IP or hostname & port where ZoneMinder is running) + +API Version +^^^^^^^^^^^ +To retrieve the API version: + +:: + + curl http://server/zm/api/host/getVersion.json + + Return a list of all monitors ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``curl -XGET http://zmdevapi/monitors.json`` +:: + + curl http://server/zm/api/monitors.json Retrieve monitor 1 -^^^^^^^^^^^^^^^^^^ -``curl -XGET http://zmdevapi/monitors/1.json`` +^^^^^^^^^^^^^^^^^^^ + +:: + + curl http://server/zm/api/monitors/1.json + + +Change State of Monitor 1 +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This API changes monitor 1 to Modect and Enabled + +:: + + curl -XPOST http://server/zm/api/monitors/1.json -d "Monitor[Function]=Modect&Monitor[Enabled]:true" Add a monitor -^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ This command will add a new http monitor. -``curl -XPOST http://zmdevapi/monitors.js -d "Monitor[Name]=Cliff-Burton \ -&Monitor[Function]=Modect \ -&Monitor[Protocol]=http \ -&Monitor[Method]=simple \ -&Monitor[Host]=ussr:pass@192.168.11.20 \ -&Monitor[Port]=80 \ -&Monitor[Path]=/mjpg/video.mjpg \ -&Monitor[Width]=704 \ -&Monitor[Height]=480 \ -&Monitor[Colours]=4"`` +:: + + curl -XPOST http://server/zm/api/monitors.json -d "Monitor[Name]=Cliff-Burton \ + &Monitor[Function]=Modect \ + &Monitor[Protocol]=http \ + &Monitor[Method]=simple \ + &Monitor[Host]=usr:pass@192.168.11.20 \ + &Monitor[Port]=80 \ + &Monitor[Path]=/mjpg/video.mjpg \ + &Monitor[Width]=704 \ + &Monitor[Height]=480 \ + &Monitor[Colours]=4" Edit monitor 1 -^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^ This command will change the 'Name' field of Monitor 1 to 'test1' -``curl -XPUT http://zmdevapi/monitors/1.json -d "Monitor[Name]=test1"`` +:: + + curl -XPUT http://server/zm/api/monitors/1.json -d "Monitor[Name]=test1" + Delete monitor 1 -^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^ This command will delete Monitor 1, but will _not_ delete any Events which depend on it. +:: + + curl -XDELETE http://server/zm/api/monitors/1.json + + +Arm/Disarm monitors +^^^^^^^^^^^^^^^^^^^^ + +This command will force an alarm on Monitor 1: + +:: + + curl http://server/zm/api/monitors/alarm/id:1/command:on.json + +This command will disable the alarm on Monitor 1: + +:: + + curl http://server/zm/api/monitors/alarm/id:1/command:off.json + +This command will report the status of the alarm Monitor 1: + +:: + + curl http://server/zm/api/monitors/alarm/id:1/command:status.json -``curl -XDELETE http://zmdevapi/monitors/1.json`` Return a list of all events -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -``curl -XGET http://zmdevapi/events.json`` +:: + + http://server/zm/api/events.json + + +Note that events list can be quite large and this API (as with all other APIs in ZM) +uses pagination. Each page returns a specific set of entries. By default this is 25 +and ties into WEB_EVENTS_PER_PAGE in the ZM options menu. + +So the logic to iterate through all events should be something like this (pseudocode): +(unfortunately there is no way to get pageCount without getting the first page) + +:: + + data = http://server/zm/api/events.json?page=1 # this returns the first page + # The json object returned now has a property called data.pagination.pageCount + count = data.pagination.pageCount; + for (i=1, i=:2015-05-15 18:43:56/EndTime <=:2015-05-16 18:43:56.json + + +To try this in CuRL, you need to URL escape the spaces like so: + +:: + + curl -XGET "http://server/zm/api/events/index/MonitorId:5/StartTime%20>=:2015-05-15%2018:43:56/EndTime%20<=:2015-05-16%2018:43:56.json" + + +Return a list of events for all monitors within a specified date/time range +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + curl -XGET "http://server/zm/api/events/index/StartTime%20>=:2015-05-15%2018:43:56/EndTime%20<=:208:43:56.json" + + +Return event count based on times and conditions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The API also supports a handy mechanism to return a count of events for a period of time. + +This returns number of events per monitor that were recorded in the last one hour + +:: + + curl "http://server/zm/api/events/consoleEvents/1%20hour.json" + +This returns number of events per monitor that were recorded in the last day where there were atleast 10 frames that were alarms" + +:: + + curl "http://server/zm/api/events/consoleEvents/1%20day.json/AlarmFrames >=: 10.json" + + + + + +Configuration Apis +^^^^^^^^^^^^^^^^^^^ + +The APIs allow you to access all the configuration parameters of ZM that you typically set inside the web console. +This returns the full list of configuration parameters: + +:: + + curl -XGET http://server/zm/api/configs.json + + +Each configuration parameter has an Id, Name, Value and other fields. Chances are you are likely only going to focus on these 3. + +(Example of changing config TBD) + +Run State Apis ^^^^^^^^^^^^^^^ -This command will change the 'Value' field of Config 121 to 901. +ZM API can be used to start/stop/restart/list states of ZM as well +Examples: + +:: + + curl -XGET http://server/zm/api/states.json # returns list of run states + curl -XPOST http://server/zm/api/states/change/restart.json #restarts ZM + curl -XPOST http://server/zm/api/states/change/stop.json #Stops ZM + curl -XPOST http://server/zm/api/states/change/start.json #Starts ZM + -``curl -XPUT http://zmdevapi/configs/121.json -d "Config[Value]=901"`` Create a Zone -^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ + +:: + + curl -XPOST http://server/zm/api/zones.json -d "Zone[Name]=Jason-Newsted \ + &Zone[MonitorId]=3 \ + &Zone[Type]=Active \ + &Zone[Units]=Percent \ + &Zone[NumCoords]=4 \ + &Zone[Coords]=0,0 639,0 639,479 0,479 \ + &Zone[AlarmRGB]=16711680 \ + &Zone[CheckMethod]=Blobs \ + &Zone[MinPixelThreshold]=25 \ + &Zone[MaxPixelThreshold]= \ + &Zone[MinAlarmPixels]=9216 \ + &Zone[MaxAlarmPixels]= \ + &Zone[FilterX]=3 \ + &Zone[FilterY]=3 \ + &Zone[MinFilterPixels]=9216 \ + &Zone[MaxFilterPixels]=230400 \ + &Zone[MinBlobPixels]=6144 \ + &Zone[MaxBlobPixels]= \ + &Zone[MinBlobs]=1 \ + &Zone[MaxBlobs]= \ + &Zone[OverloadFrames]=0" + +PTZ Control APIs +^^^^^^^^^^^^^^^^ +PTZ controls associated with a monitor are stored in the Controls table and not the Monitors table inside ZM. What that means is when you get the details of a Monitor, you will only know if it is controllable (isControllable:true) and the control ID. +To be able to retrieve PTZ information related to that Control ID, you need to use the controls API + +This returns all the control definitions: +:: + + curl http://server/zm/api/controls.json + +This returns control definitions for a specific control ID=5 +:: + + curl http://server/zm/api/controls/5.json + +Host APIs +^^^^^^^^^^ + +ZM APIs have various APIs that help you in determining host (aka ZM) daemon status, load etc. Some examples: + +:: + + curl -XGET http://server/zm/api/host/daemonCheck.json # 1 = ZM running 0=not running + curl -XGET http://server/zm/api/host/getLoad.json # returns current load of ZM + curl -XGET http://server/zm/api/host/getDiskPercent.json # returns in GB (not percentage), disk usage per monitor (that is, space taken to store various event related information,images etc. per monitor) `` -``curl -XPOST http://zmdevapi/zones.json -d "Zone[Name]=Jason-Newsted \ -&Zone[MonitorId]=3 \ -&Zone[Type]=Active \ -&Zone[Units]=Percent \ -&Zone[NumCoords]=4 \ -&Zone[Coords]=0,0 639,0 639,479 0,479 \ -&Zone[AlarmRGB]=16711680 \ -&Zone[CheckMethod]=Blobs \ -&Zone[MinPixelThreshold]=25 \ -&Zone[MaxPixelThreshold]= \ -&Zone[MinAlarmPixels]=9216 \ -&Zone[MaxAlarmPixels]= \ -&Zone[FilterX]=3 \ -&Zone[FilterY]=3 \ -&Zone[MinFilterPixels]=9216 \ -&Zone[MaxFilterPixels]=230400 \ -&Zone[MinBlobPixels]=6144 \ -&Zone[MaxBlobPixels]= \ -&Zone[MinBlobs]=1 \ -&Zone[MaxBlobs]= \ -&Zone[OverloadFrames]=0"`` diff --git a/docs/conf.py b/docs/conf.py index a934a71c5..9bda5df7b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,6 +15,9 @@ import sys import os +def setup(app): + app.add_stylesheet('zmstyle.css') + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -51,9 +54,9 @@ copyright = u'2014, https://github.com/ZoneMinder/ZoneMinder/graphs/contributors # built documents. # # The short X.Y version. -version = '1.27.0' +#version = '1.28.1' # The full version, including alpha/beta/rc tags. -release = '1.27.0' +#release = '1.28.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -99,13 +102,14 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' +#html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -html_theme_options = { - "stickysidebar": "true" -} +#html_theme_options = { +# "stickysidebar": "true" +#} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] @@ -130,6 +134,7 @@ html_theme_options = { # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] +#html_style='zmstyles.css' # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied diff --git a/docs/contributing.rst b/docs/contributing.rst index eddf3ff4e..4b164442d 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -1,7 +1,14 @@ Contributing ============ +Source hosted at `GitHub `__ +Report issues/questions/feature requests on `GitHub Issues `__ -1. Check for `open issues `_ or open a fresh issue to start a discussion around a feature idea or a bug. -2. Fork the ZoneMinder repository on Github to start making your changes. -3. Send a pull request and bug the maintainer until it gets merged and published. Stop by the #zoneminder channel on irc.freenode.net. +Pull requests are very welcome! If you would like to contribute, please follow the following steps. +* Fork the repo +* Open an issue at our `GitHub Issues Tracker `__. Describe the bug that you've found, or the feature which you're asking for. Jot down the issue number (e.g. 456) +* Create your feature branch (``git checkout -b 456-my-new-feature``) +* Commit your changes (``git commit -m 'Added some feature'``) It is preferred that you 'commit early and often' instead of bunching all changes into a single commit. +* Push your branch to your fork on github (``git push origin 456-my-new-feature``) +* Create new Pull Request +* The team will then review, discuss and hopefully merge your changes. diff --git a/docs/faq.rst b/docs/faq.rst index b8b957fb6..1c5c36d09 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -1,60 +1,745 @@ FAQ -=== +===== -This is the FAQ page. Feel free to contribute any FAQs that you think are missing. +This is the FAQ page. Feel free to contribute any FAQs that you think are missing. -Why can't I view all of my monitors in Montage view? ----------------------------------------------------- -1. You will most likely need to increase `mysql_max_connections` in my.cnf. -2. If using Firefox, you may need to increase `network.http.max-persistent-connections-per-server` in `about:config`. +How can I stop ZoneMinder filling up my disk? +--------------------------------------------- +Recent versions of ZoneMinder come with a filter you can use for this purpose already included. +The filter is called **PurgeWhenFull** and to find it, choose one of the event counts from the console page, for instance events in the last hour, for one of your monitors. **Note** that this filter is automatically enabled if you do a frresh install of ZoneMinder including creating a new Database. If you already have an existing Database and are upgrading Zoneminder, it will retain the settings of the filter (which in earlier releases was disabled by default). So you may want to check if PurgeWhenFull is enabled and if not, enable it. + +To enable it, go to Web Console, click on any of your Events of any of your monitors. +This will bring up an event listing and a filter window. + +In the filter window there is a drop down select box labeled 'Use Filter', that lets your select a saved filter. Select 'PurgeWhenFull' and it will load that filter. + +Make any modifications you might want, such as the percentage full you want it to kick in, or how many events to delete at a time (it will repeat the filter as many times as needed to clear the space, but will only delete this many events each time to get there). + +Then click on 'Save' which will bring up a new window. Make sure the 'Automatically delete' box is checked and press save to save your filter. This will then run in the background to keep your disk within those limits. + +After you've done that, you changes will automatically be loaded into zmfilter within a few minutes. + +Check the ``zmfilter.log`` file to make sure it is running as sometimes missing perl modules mean that it never runs but people don't always realize. + +**Purge By Age** +To delete events that are older than 7 days, create a new filter with "Date" set to "less than" and a value of "-7 days", sort by "date/time" in "asc"ending order, then enable the checkbox "delete all matches". You can also use a value of week or week and days: "-2 week" or "-2 week 4 day" + +Save with 'Run Filter In Background' enabled to have it run automatically. +Optional skip archived events: click on the plus sign next to -7 days to add another condition. "and" "archive status" equal to "unarchived only". + +Optional slow delete: limit the number of results to 3. If you have a large backlog of events that would be deleted, this can hard spike the CPU usage for a long time. Limiting the number of results to only the first three each time the filter is run spreads out the delete processes over time, dramatically lessening the CPU load. + +There are two methods for ZM to remove files when they are deleted that can be found in Options under the System tab ZM_OPT_FAST_DELETE and ZM_RUN_AUDIT. + + +ZM_OPT_FAST_DELETE: + +Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if your are trying to do a lot of events at once. It is recommended that you set this option which means that the browser client only deletes the key entries in the events table, which means the events will no longer appear in the listing, and leaves the zmaudit daemon to clear up the rest later. + + + +ZM_RUN_AUDIT: + +The zmaudit daemon exists to check that the saved information in the database and on the file system match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronize the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. This is recommended for most systems however if you have a very large number of events the process of scanning the database and file system may take a long time and impact performance. In this case you may prefer to not have zmaudit running unconditionally and schedule occasional checks at other, more convenient, times. + + + +ZM_AUDIT_CHECK_INTERVAL: + +The zmaudit daemon exists to check that the saved information in the database and on the files system match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronize the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and file system may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed. + + +Math for Memory: Making sure you have enough memory to handle your cameras +--------------------------------------------------------------------------- +One of the most common issues for erratic ZoneMinder behavior is you don't have enough memory to handle all your cameras. Many users often configure multiple HD cameras at full resolution and 15FPS or more and then face various issues about processes failing, blank screens and other completely erratic behavior. The core reason for all of this is you either don't have enough memory or horsepower to handle all your cameras. The solution often is to reduce FPS, reduce cameras or bump up your server capabilities. + +Here are some guidelines with examples on how you can figure out how much memory you need. With respect to CPU, you should benchmark your server using standard unix tools like top, iotop and others to make sure your CPU load is manageable. ZoneMinder also shows average load on the top right corner of the Web Console for easy access. + +In *general* a good estimate of memory required would be: + +:: + + Min Memory = 1.2 * ((image-width*image-height*image buffer size*target color space*number of cameras/8/1024/1024 ) + +Where: +* image-width and image-height are the width and height of images that your camera is configured for (in my case, 1280x960). This value is in the Source tab for each monitor +* image buffer size is the # of images ZM will keep in memory (this is used by ZM to make sure it has pre and post images before detecting an alarm - very useful because by the time an alarm is detected, the reason for the alarm may move out of view and a buffer is really useful for this, including for analyzing stats/scores). This value is in the buffers tab for each monitor +* target color space is the color depth - 8bit, 24bit or 32bit. It's again in the source tab of each monitor +The 1.2 at the start is basically adding 20% on top of the calculation to account for image/stream overheads (this is an estimate) + +So let's do the math. If we have 4 cameras running at 1280x960 with 32bit color space and one camera running at 640x480 with 8bit greyscale color space, the system would require: + +``1.2 * ((1280*960*50*32*4/8/1024/1024 ) + (640 *480 *50*8/8 /1024/1024))`` + +Or, around 900MB of memory. + +So if you have 2GB of memory, you should be all set. Right? **Not, really**: + + * This is just the base memory required to capture the streams. Remember ZM is always capturing streams irrespective of whether you are actually recording or not - to make sure its image ring buffer is there with pre images when an alarm kicks in. + * You also need to account for other processes not related to ZM running in your box + * You also need to account for other ZM processes - for example, I noticed the audit daemon takes up a good amount of memory when it runs, DB updates also take up memory + +So a good rule of thumb is to make sure you have twice the memory as the calculation above (and if you are using the ZM server for other purposes, please factor in those memory requirements as well) + +**Also remember by default ZM only uses 50% of your available memory unless you change it** + +As it turns out, ZM uses mapped memory and by default, 50% of your physical memory is what this will grow to. When you reach that limit , ZM breaks down with various errors. + + +(**Note**: Mapped memory is applicable when you install ZoneMinder with mapped memory support, which is the default mode. If you have specifically disabled mapped memory then please see the next FAQ enty on how to increase shared memory) + +A good way to know how much memory is allocated to ZM for its operation is to do a ``df -h`` + +A sample output on Ubuntu: + +:: + + pp@camerapc:~$ df -h + Filesystem Size Used Avail Use% Mounted on + /dev/sda1 226G 96G 119G 45% / + none 4.0K 0 4.0K 0% /sys/fs/cgroup + udev 1.8G 4.0K 1.8G 1% /dev + tmpfs 371M 816K 370M 1% /run + none 5.0M 0 5.0M 0% /run/lock + tmpfs 2.6G 923M 1.7G 36% /run/shm + none 100M 0 100M 0% /run/user + + +The key item here is tmpfs --> the example above shows we have allocated 1.7G of mapped memory space of which 36% is used which is a healthy number. If you are seeing this to go beyond 70% you should probaby increase mapped memory + + +If you want to increase this limit to 70% of your memory, add the following to ``/etc/fstab`` +``tmpfs /run/shm tmpfs defaults,noexec,nosuid,size=70% 0 0`` + + +What does a 'Can't shmget: Invalid argument' error in my logs mean? (and my camera does not display at higher resolutions) +-------------------------------------------------------------------------------------------------------------------------------------- + +(*Note*: This is applicable for systems that have mapped memory disabled in ZoneMinder. By default, Mapped memory is enabled and unless you have disabled it manually, please refer to the "Math for Memory" question above and how to increase mapped memory limits) + +This error is discussed in the README in the following excerpt:- +''...this is caused by an attempt to allocate an amount of shared memory greater than your system can handle. The size it requests is based on the following formula, ``ring buffer size x image width x image height x 3 (for 24 bit images) + a bit of overhead``. + +So, for example: + +:: + + 384x288 capture resolution, that makes: 110 592 pixels + in 24 bit color that's x24 = 2 654 208 bits per frame + by 80 frames ring buffer x80 = 212 336 640 bits per camera + by 4 cameras x4 = 849 346 560 bits. + Plus 10% overhead = 934 281 216 bits + That's 116 785 152 bytes, and + = 114 048 kB, respectively 111.38 MB. + If my shared memory is set to 134 217 728, which is exactly 128MB, + that means I shouldn't have any problem. + (Note that 1 byte = 8 bits and 1kbyte = 1024bytes, 1MB = 1024 kB) + +If for instance you were using 24bit 640x480 then this would come to about 92Mb if you are using the default buffer size of 100. If this is too large then you can either reduce the image or buffer sizes or increase the maximum amount of shared memory available. If you are using RedHat then you can get details on how to change these settings `here `__ + +You should be able to use a similar procedure with other distributions to modify the shared memory pool without kernel recompilations though in some cases this may be necessary. Note, this error also sometimes occurs if you have an old shared memory segment lying around from a previous run that is too small. Use the ipcs and ipcrm system commands to check and remove it if necessary.'" + +You can often find out how many 4KB shared memory pages are available by typing the following :- + +:: + + # cat /proc/sys/kernel/shmall + 2097152 + +In recent kernels the shmall is set to 2097152 memory pages multiplied by 4096 bytes per page for a total of 8 GB of shared memory available. You only need to increase the shmall value if you have a computer with more than 8GB of memory and wish to use more of it for shared memory usage, such as large databases. + +The most shared memory bytes you can allocate in one go :- + +:: + + # cat /proc/sys/kernel/shmmax + 33554432 + +In recent kernels the shmmax is set to 33554432 bytes for only 32 MB of maximum shared memory allocatable at a time, hardly enough for ZoneMinder to go above 320 x 240 x 24-bit resolution at 40 frames in the buffer if it is using the /dev/shm shared memory device, so this value needs to be increased. If you are using ZoneMinder with the memory mapped (mmap) compile time option then this doesn't affect you. + +To change the value to 128 MB temporarily during this kernel execution type (for example) :- +``echo 536870912 >/proc/sys/kernel/shmmax`` + +*Be sure to restart ZoneMinder after this.* + +However be aware that sometimes you will only need to change the shmmax value as shmall is often large enough. Also changing these values in this way is only effective until your machine is rebooted. + +To change them permanently you will need to edit ``/etc/sysctl.conf`` and add the following lines (for example) :- +``kernel.shmmax = 536870912`` + +Or if your distribution has the ``/etc/sysctl.d/`` folder you can create a file in this folder without modifying the ``/etc/sysctl.d`` so you won't lose the changes during distro upgrades :- +```echo kernel.shmmax = 536870912 >/etc/sysctl.d/60-kernel-shm.conf``` + +To load these settings in the sysctl.conf file type: +``sysctl -p`` + +To check your shared memory settings type: +``ipcs -l`` + +Note that with Megapixel cameras like the Axis 207mw becoming cheaper and more attractive, the above memory settings are not adequate. To get Zoneminder working with a full 1280x1024 resolution camera in full color, increase ``134217728`` (128 MB) to, for example, ``268435456`` (256 MB) and multiple this value by each camera. + +These changes will now also be set the next time your machine is restarted. + +Versions 1.24.x of ZoneMinder also allows you to use an alternate method of shared memory allocation, `Mmap mapped memory `__ . This requires less configuration and can be simpler to use. Mapped memory allows you to use a special type of file as the placeholder for your memory and this file is 'mapped' into memory space for easy and fast access. + +To enable mapped memory in ZoneMinder you need add add the --enable--mmap=yes switch to your configure line. By default mapped memory files are created in /dev/shm which on most distributions is a dedicated pseudo-partition containing memory formatted as a filesystem. If your system uses a different path then this can be changed in ZoneMinder in Options->paths->PATH_MAP. It uses a filesystem type called `tmpfs `__. If you type ``df -h`` you should see this area and the size of memory it currently allows. To increase size for tmpfs you need to edit /etc/default/tmpfs. Search for: +``SHM_SIZE=128M`` +and change to something like +``SHM_SIZE=1G`` +then reboot the system. You could possibly need to change RUN_SIZE, too. + +It is important that you do not use a disk based filesystem for your memory mapped files as this will cause memory access to be extremely slow. ZoneMinder creates files called .zm.mmap. in the mapped memory filesystem. + +Mapped memory is subject to the same limitations in terms of total memory as using more traditional shared memory but does not require any configuration per allocation or chunk. In future versions of ZoneMinder this will be the default shared memory storage method. + +Another good article about shared memory settings can be found `here `__ . + +The essential difference was that the kernel.shmall setting is NOT in a direct memory setting in KB but in pages of memory. it is Max Pages of memory + +*For example:* If you want to allocate a maximum memory setting to 8GB you have to convert it to the number of pages (or segments). +with a page size of 4096. +kernel.shmall = 8000x1024x1024/4096 +``kernel.shmall = 2097152`` +NOT 8388608000 as would be suggested in the RedHat article linked above. + +shmmax is the max amount to allocate in one request - +this is is an actual memory size (as opposed to pages) set to 4GB +``kernel.shmmax = 4294967296`` + +The ``/etc/sysctl.conf`` would have these lines + +:: + + kernel.shmall = 2097152 + kernel.shmmax = 4294967296 + +As above, reload your sysctl.conf with ``sysctl -p`` and check that the settings are correct with ``ipcs -l``. + +I have enabled motion detection but it is not always being triggered when things happen in the camera view +--------------------------------------------------------------------------------------------------------------- + +ZoneMinder uses zones to examine images for motion detection. When you create the initial zones you can choose from a number of preset values for sensitivity etc. Whilst these are usually a good starting point they are not always suitable for all situations and you will probably need to tweak the values for your specific circumstances. The meanings of the various settings are described in the documentation (`here `__) however if you believe you have sensible settings configured then there are two diagnostic approaches you can use. + +Another user contributed illustrated Zone definition guide can be found here: `An illustrated guide to Zones `__ + +Event Statistics +^^^^^^^^^^^^^^^^^ +The first technique is to use event statistics. Firstly you should ensure they are switched on in Options->Logging->RECORD_EVENT_STATS. This will then cause the raw motion detection statistics for any subsequently generated events to be written to the DB. These can then be accessed by first clicking on the Frames or Alarm Frames values of the event from any event list view in the web gui. Then click on the score value to see the actual values that caused the event. Alternatively the stats can be accessed by clicking on the 'Stats' link when viewing any individual frame. The values displayed there correspond with the values that are used in the zone configuration and give you an idea of what 'real world' values are being generated. + +Note that if you are investigating why events 'do not' happen then these will not be saved and so won't be accessible. The best thing to do in that circumstance is to make your zone more sensitive so that it captures all events (perhap even ones you don't want) so you can get an idea of what values are being generated and then start to adjust back to less sensitive settings if necessary. You should make sure you test your settings under a variety of lighting conditions (e.g. day and night, sunny or dull) to get the best feel for that works and what doesn't. + +Using statistics will slow your system down to a small degree and use a little extra disk space in the DB so once you are happy you can switch them off again. However it is perfectly feasible to keep them permanently on if your system is able to cope which will allow you to review your setting periodically. + +Diagnostic Images +^^^^^^^^^^^^^^^^^^^^ +The second approach is to use diagnostic images which are saved copies of the intermediate images that ZM uses when determining motion detection. These are switched on and off using Options->Logging->RECORD_DIAG_IMAGES. + +There are two kinds of diagnostic images which are and are written (and continuously overwritten) to the top level monitor event directory. If an event occurs then the files are additionally copied to the event directory and renamed with the appropriate frame number as a prefix. + +The first set are produced by the monitor on the image as a whole. The diag-r.jpg image is the current reference image against which all individual frames are compared and the diag-d.jpg image is the delta image highlighting the difference between the reference image and the last analysed image. In this images identical pixels will be black and the more different a pixel is the whiter it will be. Viewing this image and determining the colour of the pixels is a good way of getting a feel for the pixel differences you might expect (often more than you think). + +The second set of diag images are labelled as diag--.jpg where zoneid is the id of the zone in question (Smile) and the stage is where in the alarm check process the image is generated from. So if you have several zones you can expect to see multiple files. Also these files are only interested in what is happening in their zone only and will ignore anything else outside of the zone. The stages that each number represents are as follows, + +# Alarmed Pixels - This image shows all pixels in the zone that are considered to be alarmed as white pixels and all other pixels as black. +# Filtered Pixels - This is as stage one except that all pixels removed by the filters are now black. The white pixels represent the pixels that are candidates to generate an event. +# Raw Blobs - This image contains all alarmed pixels from stage 2 but aggrageted into blobs. Each blob will have a different greyscale value (between 1 and 254) so they can be difficult to spot with the naked eye but using a colour picker or photoshop will make it easier to see what blob is what. +# Filtered Blobs - This image is as stage 3 but under (or over) sized blobs have been removed. This is the final step before determining if an event has occurred, just prior to the number of blobs being counted. Thus this image forms the basis for determining whether an event is generated and outlining on alarmed images is done from the blobs in this image. + +Using the above images you should be able to tell at all stages what ZM is doing to determine if an event should happen or not. They are useful diagnostic tools but as is mentioned elsewhere they will massively slow your system down and take up a great deal more space. You should never leave ZM running for any length of time with diagnostic images on. + +Why can't ZoneMinder capture images (either at all or just particularly fast) when I can see my camera just fine in xawtv or similar? +---------------------------------------------------------------------------------------------------------------------------------------------- + +With capture cards ZoneMinder will pull images as fast as it possibly can unless limited by configuration. ZoneMinder (and any similar application) uses the frame grabber interface to copy frames from video memory into user memory. This takes some time, plus if you have several inputs sharing one capture chip it has to switch between inputs between captures which further slows things down. + +On average a card that can capture at 25fps per chip PAL for one input will do maybe 6-10fps for two, 1-4fps for three and 1-2 for four. For a 30fps NTSC chip the figures will be correspondingly higher. However sometimes it is necessary to slow down capture even further as after an input switch it may take a short while for the new image to settle before it can be captured without corruption. + +When using xawtv etc to view the stream you are not looking at an image captured using the frame grabber but the card's video memory mapped onto your screen. This requires no capture or processing unless you do an explicit capture via the J or ctrl-J keys for instance. Some cards or drivers do not support the frame grabber interface at all so may not work with ZoneMinder even though you can view the stream in xawtv. If you can grab a still using the grab functionality of xawtv then in general your card will work with ZoneMinder. + +Why can't I see streamed images when I can see stills in the Zone window etc? +------------------------------------------------------------------------------------- + +This issue is normally down to one of two causes + +1) You are using Internet Explorer and are trying to view multi-part jpeg streams. IE does not support these streams directly, unlike most other browsers. You will need to install Cambozola or another multi-part jpeg aware pluging to view them. To do this you will need to obtain the applet from the Downloads page and install the cambozola.jar file in the same directly as the ZoneMinder php files. Then find the ZoneMinder Options->Images page and enable ZM_OPT_CAMBOZOLA and enter the web path to the .jar file in ZM_PATH_CAMBOZOLA. This will ordinarily just be cambozola.jar. Provided (Options / B/W tabs) WEB_H_CAN_STREAM is set to auto and WEB_H_STREAM_METHOD is set to jpeg then Cambozola should be loaded next time you try and view a stream. + +'''NOTE''': If you find that the Cambozola applet loads in IE but the applet just displays the version # of Cambozola and the author's name (as opposed to seeing the streaming images), you may need to chmod (''-rwxrwxr-x'') your (''usr/share/zoneminder/'') cambozola.jar: + +:: + + sudo chmod 775 cambozola.jar + +Once I did this, images started to stream for me. + +2) The other common cause for being unable to view streams is that you have installed the ZoneMinder cgi binaries (zms and nph-zms) in a different directory than your web server is expecting. Make sure that the --with-cgidir option you use to the ZoneMinder configure script is the same as the CGI directory configure for your web server. If you are using Apache, which is the most common one, then in your httpd.conf file there should be a line like ``ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"`` where the last directory in the quotes is the one you have specified. If not then change one or the other to match. Be warned that configuring apache can be complex so changing the one passed to the ZoneMinder configure (and then rebuilding and reinstalling) is recommended in the first instance. If you change the apache config you will need to restart apache for the changes to take effect. If you still cannot see stream reliably then try changing Options->Paths->ZM_PATH_ZMS to just use zms if nph-zms is specified, or vice versa. Also check in your apache error logs. + +I have several monitors configured but when I load the Montage view in FireFox why can I only see two? or, Why don't all my cameras display when I use the Montage view in FireFox? +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +By default FireFox only supports a small number of simultaneous connections. Using the montage view usually requires one persistent connection for each camera plus intermittent connections for other information such as statuses. + +You will need to increase the number of allowed connections to use the montage view with more than a small number of cameras. Certain FireFox extensions such as FasterFox may also help to achieve the same result. + +To resolve this situation, follow the instructions below: + +Enter ``about:config`` in the address bar + +scroll down to +``browser.cache.check_doc_frequency 3`` +change the 3 to a 1 + +:: + + browser.cache.disk.enable True -> False + network.http.max-connections-per-server -> put a value of 100 + network.http.max-persistent-connections-per-proxy -> 100 again + network.http.max-persistent-connections-per-server -> 100 again + +Why is ZoneMinder using so much CPU? +--------------------------------------- + +The various elements of ZoneMinder can be involved in some pretty intensive activity, especially while analysing images for motion. However generally this should not overwhelm your machine unless it is very old or underpowered. + +There are a number of specific reasons why processor loads can be high either by design or by accident. To figure out exactly what is causing it in your circumstances requires a bit of experimentation. + +The main causes are. + + * Using a video palette other than greyscale or RGB24. This can cause a relatively minor performace hit, though still significant. Although some cameras and cards require using planar palettes ZM currently doesn't support this format internally and each frame is converted to an RGB representation prior to processing. Unless you have compelling reasons for using YUV or reduced RGB type palettes such as hitting USB transfer limits I would experiment to see if RGB24 or greyscale is quicker. Put your monitors into 'Monitor' mode so that only the capture daemons are running and monitor the process load of these (the 'zmc' processes) using top. Try it with various palettes to see if it makes a difference. + * Big image sizes. A image of 640x480 requires at least four times the processing of a 320x240 image. Experiment with different sizes to see what effect it may have. Sometimes a large image is just two interlaced smaller frames so has no real benefit anyway. This is especially true for analog cameras/cards as image height over 320 (NTSC) or 352 PAL) are invariably interlaced. + * Capture frame rates. Unless there's a compelling reason in your case there is often little benefit in running cameras at 25fps when 5-10fps would often get you results just as good. Try changing your monitor settings to limit your cameras to lower frame rates. You can still configure ZM to ignore these limits and capture as fast as possible when motion is detected. + * Run function. Obviously running in Record or Mocord modes or in Modect with lots of events generates a lot of DB and file activity and so CPU and load will increase. + * Basic default detection zones. By default when a camera is added one detection zone is added which covers the whole image with a default set of parameters. If your camera covers a view in which various regions are unlikely to generate a valid alarm (ie the sky) then I would experiment with reducing the zone sizes or adding inactive zones to blank out areas you don't want to monitor. Additionally the actual settings of the zone themselves may not be optimal. When doing motion detection the number of changed pixels above a threshold is examined, then this is filter, then contiguous regions are calculated to see if an alarm is generated. If any maximum or minimum threshold is exceeded according to your zone settings at any time the calculation stops. If your settings always result in the calculations going through to the last stage before being failed then additional CPU time is used unnecessarily. Make sure your maximum and minimumzone thresholds are set to sensible values and experiment by switching RECORD_EVENT_STATS on and seeing what the actual values of alarmed pixels etc are during sample events. + * Optimise your settings. After you've got some settings you're happy with then switching off RECORD_EVENT_STATS will prevent the statistics being written to the database which saves some time. Other settings which might make a difference are ZM_FAST_RGB_DIFFS, ZM_OPT_FRAME_SERVER and the JPEG_xxx_QUALITY ones. + +I'm sure there are other things which might make a difference such as what else you have running on the box and memory sizes (make sure there's no swapping going on). Also speed of disk etc will make some difference during event capture and also if you are watching the whole time then you may have a bunch of zms processes running also. + +I think the biggest factors are image size, colour depth and capture rate. Having said that I also don't always know why you get certains results from 'top'. For instance if I have a 'zma' daemon running for a monitor that is capturing an image. I've commented out the actual analysis so all it's doing is blending the image with the previous one. In colour mode this takes ~11 milliseconds per frame on my system and the camera is capturing at ~10fps. Using 'top' this reports the process as using ~5% of CPU and permanently in R(un) state. Changing to greyscale mode the blending takes ~4msec (as you would expect as this is roughly a third of 11) but top reports the process as now with 0% CPU and permanently in S(leep) state. So an actual CPU resource usage change of a factor of 3 causes huge differences in reported CPU usage. I have yet to get to the bottom of this but I suspect it's to do with scheduling somewhere along the line and that maybe the greyscale processing will fit into one scheduling time slice whereas the colour one won't but I have no evidence of this yet! + +Why is the timeline view all messed up? +----------------------------------------- + +The timeline view is a new view allowing you to see a graph of alarm activity over time and to quickly scan and home in on events of interest. However this feature is highly complex and still in beta. It is based extensively on HTML div tags, sometimes lots of them. Whilst FireFox is able to render this view successfully other browsers, particular Internet Explorer do not seem able to cope and so present a messed up view, either always or when there are a lot of events. +Using the timeline view is only recommended when using FireFox, however even then there may be issues. + +This function has from time to time been corrupted in the SVN release or in the stable releases, try and reinstall from a fresh download. + +How much Hard Disk Space / Bandwidth do I need for ZM? +--------------------------------------------------------------- +Please see `this excel sheet `__ or `this online excel sheet `__ (both are user contributed excel sheets) + +Or go to `this link `__ for the Axis bandwidth calculator. Although this is aimed at Axis cameras it still produces valid results for any kind of IP camera. + +As a quick guide I have 4 cameras at 320x240 storing 1 fps except during alarm events. After 1 week 60GB of space in the volume where the events are stored (/var/www/html/zm) has been used. + +When I try and run ZoneMinder I get lots of audit permission errors in the logs and it won't start +------------------------------------------------------------------------------------------------------- +Many Linux distributions nowadays are built with security in mind. One of the latest methods of achieving this is via SELinux (Secure Linux) which controls who is able to run what in a more precise way then traditional accounting and file based permissions (`link `__). +If you are seeing entries in your system log like: + + Jun 11 20:44:02 kernel: audit(1150033442.443:226): avc: denied { read } for pid=5068 + comm="uptime" name="utmp" dev=dm-0 ino=16908345 scontext=user_u:system_r:httpd_sys_script_t + tcontext=user_u:object_r:initrc_var_run_t tclass=file + +then it is likely that your system has SELinux enabled and it is preventing ZoneMinder from performaing certain activities. You then have two choices. You can either tune SELinux to permit the required operations or you can disable SELinux entirely which will permit ZoneMinder to run unhindered. Disabling SELinux is usually performed by editing its configuration file (e.g., ``/etc/selinux/config``) and then rebooting. However if you run a public server you should read up on the risks associated with disabled Secure Linux before disabling it. + +Note that SELinux may cause errors other than those listed above. If you are in any doubt then it can be worth disabling SELinux experimentally to see if it fixes your problem before trying other solutions. How do I enable ZoneMinder's security? --------------------------------------- +------------------------------------------- +In the console, click on Options. Check the box next to "ZM_OPT_USE_AUTH". You will immediately be asked to login. The default username is 'admin' and the password is 'admin'. -You may also consider to use the web server security, for example, htaccess files under Apache, or mod_auth. +To Manage Users: +In main console, go to Options->Users. -1. In the console, click on Options. -2. Check the box next to "ZM_OPT_USE_AUTH". -3. Click Save -4. You will immediately be asked to login. The username is 'admin' and the password is 'admin'. +You may also consider to use the web server security, for example, htaccess files under Apache scope; You may even use this as an additional/redundant security on top of Zoneminders built-in security features; -To Manage Users ---------------- +Why does ZM stop recording once I have 32000 events for my monitor? +------------------------------------------------------------------------ +Storing more than 32k files in a single folder is a limitation of some filesystems. To avoid this, enable USE_DEEP_STORAGE under Options. -1. In main console, go to Options->Users. +USE_DEEP_STORAGE is now the default for new ZoneMinder systems so this limitation should only apply to users upgrading from a previous version of ZoneMinder. -The "Zones" view for a Monitor is blank (I can't see / setup a Zone) --------------------------------------------------------------------- +Versions of ZM from 1.23.0 onwards allow you to have a deeper filesystem with fewer files per individual directory. As well as not being susceptible to the 32k limit, this is also somewhat faster. -Snapshots and Zones images are stored in the `images` directory in your webroot. -Ensure that the `images` directory is writable by the user which ZoneMinder is -running as. If the `images` directory is a symlink, ensure that your web server -has access to that directory as well. +If you have upgraded from a previous version of ZoneMinder and this option is not already enabled, it is very important to follow the steps below to enable it on an existing system. Failure to properly follow these steps **WILL RESULT IN LOSS OF YOUR DATA!** -How do the 3 AlarmCheckMethods interact? ----------------------------------------- +:: -In example, if I set the alarm % to 5-10% and the filtered and blob to 1-100%, what happens? + # Stop ZoneMinder + # Backup your event data and the dB if you have the available storage + # Enable USE_DEEP_STORAGE under Options. + # From the command line, run "sudo zmupdate.pl --migrate-events" + # Monitor the output for any events that fail to convert. + # After the conversion completes, you can restart ZoneMinder -1. If any of the min/max values is 0, the check that the value is applied to is skipped. -2. If you have a min-alarmed area and you're below that, then it quits. -3. If you have a max-alarmed area and you're above that, then it quits. -4. If you're on filtered or blobs +Note that you can re-run the migrate-events command if any error messages scroll off the screen. - 1. and have a min filtered area that you're below then it quits - 2. and have a max filtered area that you're above then it quits +You can read about the lack of a limit in the number of sub-directories in the ext4 filesystem at: `this link `__ +and see what tools may assist in your use of this filesystem `here `__ +If you search for ext3 or reiserfs on the forums you will find various threads on this issue with guidance on +how to convert. -5. If you're on blobs +Managing system load (with IP Cameras in mind) +---------------------------------------------------- - 1. any blob smaller than the min blob area (if set) is discarded - 2. any blob larger than the max blob area (if set) is discarded - 3. If there are less remaining blobs than the minimum-blobs, then it quits. - 4. If there are more remaining blobs than the maximum-blobs, then it quits. +Introduction +^^^^^^^^^^^^^^^ +Zoneminder is a superb application in every way, but it does a job that needs a lot of horsepower especially when using multiple IP cameras. IP Cams require an extra level of processing to analogue cards as the jpg or mjpeg images need to be decoded before analysing. This needs grunt. If you have lots of cameras, you need lots of grunt. + +Why do ZM need so much grunt? +Think what Zoneminder is actually doing. In modect mode ZM is: +1. Fetching a jpeg from the camera. (Either in single part or multipart stream) +2. Decoding the jpeg image. +3. Comparing the zoned selections to the previous image or images and applying rules. +4. If in alarm state, writing that image to the disk and updating the mysql database. + +If you're capturing at five frames per second, the above is repeated five times every second, multiplied by the number of cameras. Decoding the images is what takes the real power from the processor and this is the main reason why analogue cameras which present an image ready-decoded in memory take less work. + +How do I know if my computer is overloaded? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If your CPU is running at 100% all the time, it's probably overloaded (or running at exact optimisation). If the load is consistently high (over 10.0 for a single processor) then Bad Things happen - like lost frames, unrecorded events etc. Occasional peaks are fine, normal and nothing to worry about. + +Zoneminder runs on Linux, Linux measures system load using "load", which is complicated but gives a rough guide on what the computer is doing at any given time. Zoneminder shows Load on the main page (top right) as well as disk space. Typing "uptime" on the command line will give a similar guide, but with three figures to give a fuller measure of what's happening over a period of time but for the best guide to see what's happening, install "htop" - which gives easy to read graphs for load, memory and cpu usage. + +A load of 1.0 means the processor has "just enough to do right now". Also worth noting that a load of 4.0 means exactly the same for a quad processor machine - each number equals a single processor's workload. A very high load can be fine on a computer that has a stacked workload - such as a machine sending out bulk emails, or working its way through a knotty problem; it'll just keep churning away until it's done. However - Zoneminder needs to process information in real time so it can't afford to stack its jobs, it needs to deal with them right away. + +For a better and full explanation of Load: `Please read this `__ + +My load is too high, how can I reduce it? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +(The previous documentation explained how to use turbo jpeg libraries as an optimization technique. These libraries have long been part of standard linux distros since that article was authored and hence that section has been removed) + +Zoneminder is *very* tweakable and it's possible to tune it to compromise. The following are good things to try, in no particular order; + + * If your camera allows you to change image size, think whether you can get away with smaller images. Smaller pics = less load. 320x240 is usually ok for close-up corridor shots. + + * Go Black and White. Colour pictures use twice to three times the CPU, memory and diskspace but give little benefit to identification. + + * Reduce frames per second. Halve the fps, halve the workload. If your camera supports fps throttling (Axis do), try that - saves ZM having to drop frames from a stream. 2-5 fps seems to be widely used. + + * Experiment with using jpeg instead of mjpeg. Some users have reported it gives better performance, but YMMV. + + * Tweak the zones. Keep them as small and as few as possible. Stick to one zone unless you really need more. Read `this `__ for an easy to understand explanation along with the official Zone guide. + + * Schedule. If you are running a linux system at near capacity, you'll need to think carefully about things like backups and scheduled tasks. updatedb - the process which maintains a file database so that 'locate' works quickly, is normally scheduled to run once a day and if on a busy system can create a heavy increase on the load. The same is true for scheduled backups, especially those which compress the files. Re-schedule these tasks to a time when the cpu is less likely to be busy, if possible - and also use the "nice" command to reduce their priority. (crontab and /etc/cron.daily/ are good places to start) + + * Reduce clutter on your PC. Don't run X unless you really need it, the GUI is a huge overhead in both memory and cpu. + +More expensive options: + + * Increase RAM. If your system is having to use disk swap it will HUGELY impact performance in all areas. Again, htop is a good monitor - but first you need to understand that because Linux is using all the memory, it doesn't mean it needs it all - linux handles ram very differently to Windows/DOS and caches stuff. htop will show cached ram as a different colour in the memory graph. Also check that you're actually using a high memory capable kernel - many kernels don't enable high memory by default. + + * Faster CPU. Simple but effective. Zoneminder also works very well with multiple processor systems out of the box (if SMP is enabled in your kernel). The load of different cameras is spread across the processors. + + + * Try building Zoneminder with processor specific instructions that are optimised to the system it will be running on, also increasing the optimisation level of GCC beyond -O2 will help. + +:: + + ./configure CFLAGS="-g -O3 -march=athlon-xp -mtune=athlon-xp" CXXFLAGS="-g -O3 -march=athlon-xp -mtune=athlon-xp" + +The above command is optimised for an Athlon XP cpu so you will need to use the specific processor tag for your cpu, also the compiler optimisation has been increased to -O3. + +You also need to put in your normal ./configure commands as if you were compiling with out this optimisation. + +A further note is that the compile must be performed on the system that Zoneminder will be running on as this optimisation will make it hardware specific code. + +Processor specific commands can be found in the GCC manual along with some more options that may increase performanc. +``__ + +The below command has been used to compile Zoneminder on a Athlon XP system running CentOS 5.5 and along with the libjpeg-turbo modification to reduce the CPU load in half, libjpeg-turbo reduced the load by 1/3 before the processor optimisation. +:: + + ./configure --with-webdir=/var/www/html/zm --with-cgidir=/var/www/cgi-bin CFLAGS="-g -O3 -march=athlon-xp -mtune=athlon-xp" CXXFLAGS="-D__STDC_CONSTANT_MACROS -g -O3 -march=athlon-xp -mtune=athlon-xp" --enable-mmap --sysconfdir=/etc/zm + +The following command has been used to compile Zoneminder 1.25 on a CentOS 6.0 system, the native command should choose the processor automatically during compile time, this needs to be performed on the actual system!!. + +:: + + CFLAGS="-g -O3 -march=native -mtune=native" CXXFLAGS="-D__STDC_CONSTANT_MACROS -g -O3 -march=native -mtune=native" ./configure --with-webdir=/var/www/html/zm --with-cgidir=/var/www/cgi-bin --with-webuser=apache --with-webgroup=apache ZM_DB_HOST=localhost ZM_DB_NAME=zm ZM_DB_USER=your_zm_user ZM_DB_PASS=your_zm_password ZM_SSL_LIB=openssl + + +What about disks and bandwidth? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +A typical 100mbit LAN will cope with most setups easily. If you're feeding from cameras over smaller or internet links, obviously fps will be much lower. + +Disk and Bandwidth calculators are referenced on the Zoneminder wiki here: http://www.zoneminder.com/wiki/index.php/FAQ#How_much_Hard_Disk_Space_.2F_Bandwidth_do_I_need_for_ZM.3F + + +Building ZoneMinder +-------------------- + +When running configure I am getting a lot of messages about not being able to compile the ffmpeg libraries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you see output from configure that looks like this + +:: + + checking libavcodec/avcodec.h usability... no + checking libavcodec/avcodec.h presence... yes + configure: WARNING: libavcodec/avcodec.h: present but cannot be compiled + configure: WARNING: libavcodec/avcodec.h: check for missing + prerequisite headers? + configure: WARNING: libavcodec/avcodec.h: see the Autoconf documentation + configure: WARNING: libavcodec/avcodec.h: section "Present But + Cannot Be Compiled" + configure: WARNING: libavcodec/avcodec.h: proceeding with the compiler's + result + configure: WARNING: ## ------------------------------------- ## + configure: WARNING: ## Report this to support@zoneminder.com ## + configure: WARNING: ## ------------------------------------- ## + +then it is caused not by the ZoneMinder build system but ffmpeg itself. However there is a workaround you can use which is to add ``CPPFLAGS=-D__STDC_CONSTANT_MACROS`` + +to the ZoneMinder ``./configure`` command which should solve the issue. However this is not a proper 'fix' as such, which can only come from the ffmpeg project itself. + +I cannot build ZoneMinder and am getting lots of undefined C++ template errors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + +This is almost certainly due to the 'ccache' package which attempts to speed up compilation by caching compiled objects. Unfortunately one of the side effects is that it breaks the GNU g++ template resolution method that ZoneMinder uses in building by prevent files getting recompiled. The simplest way around this is to remove the ccache package using your distros package manager. + +How do I build for X10 support? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You do not need to rebuild ZM for X10 support. You will need to install the perl module and switch on X10 in the options, then restart. Installing the perl module is covered in the README amongst other places but in summary, do: + + perl -MCPAN -eshell + install X10::ActiveHome + quit + +Extending Zoneminder +------------------------ +.. _runstate_cron_example: + +How can I get ZM to do different things at different times of day or week? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you want to configure ZoneMinder to do motion detection during the day and just record at night, for example, you will need to use ZoneMinder 'run states'. A run state is a particular configuration of monitor functions that you want to use at any time. + +To save a run state you should first configure your monitors for Modect, Record, Monitor etc as you would want them during one of the times of day. Then click on the running state link at the top of the Console view. This will usually say 'Running' or 'Stopped'. You will then be able to save the current state and give it a name, 'Daytime' for example. Now configure your monitors how you would want them during other times of day and save that, for instance as 'Nighttime'. + +Now you can switch between these two states by selecting them from the same dialog you saved them, or from the command line from issue the command ''zmpkg.pl '', for example ''zmpkg.pl Daytime''. + +The final step you need to take, is scheduling the time the changes take effect. For this you can use `cron `__. A simple entry to change to the Daylight state at at 8am and to the nighttime state at 8pm would be as follows, + +:: + + 0 8 * * * root /usr/local/bin/zmpkg.pl Daytime + 0 20 * * * root /usr/local/bin/zmpkg.pl Nighttime + +On Ubuntu 7.04 and possibly others, look in /usr/bin not just /usr/local/bin for the zmpkg.pl file. + +Although the example above describes changing states at different times of day, the same principle can equally be applied to days of the week or other more arbitrary periods. + + +How can I use ZoneMinder to trigger something else when there is an alarm? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +ZoneMinder includes a perl API which means you can create a script to interact with the ZM shared memory data and use it in your own scripts to react to ZM alarms or to trigger ZM to generate new alarms. Full details are in the README or by doing ``perldoc ZoneMinder``, ``perldoc ZoneMinder::SharedMem`` etc. +Below is an example script that checks all monitors for alarms and when one occurs, prints a message to the screen. You can add in your own code to make this reaction a little more useful. + +:: + + #!/usr/bin/perl -w + + use strict; + + use ZoneMinder; + + $| = 1; + + zmDbgInit( "myscript", level=>0, to_log=>0, to_syslog=>0, to_term=>1 ); + + my $dbh = DBI->connect( "DBI:mysql:database=".ZM_DB_NAME.";host=".ZM_DB_HOST, ZM_DB_USER, ZM_DB_PASS ); + + my $sql = "select M.*, max(E.Id) as LastEventId from Monitors as M left join Events as E on M.Id = E.MonitorId where M.Function != 'None' group by (M.Id)"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + + my $res = $sth->execute() or die( "Can't execute '$sql': ".$sth->errstr() ); + my @monitors; + while ( my $monitor = $sth->fetchrow_hashref() ) + { + push( @monitors, $monitor ); + } + + while( 1 ) + { + foreach my $monitor ( @monitors ) + { + next if ( !zmMemVerify( $monitor ) ); + + if ( my $last_event_id = zmHasAlarmed( $monitor, $monitor->{LastEventId} ) ) + { + $monitor->{LastEventId} = $last_event_id; + print( "Monitor ".$monitor->{Name}." has alarmed\n" ); + # + # Do your stuff here + # + } + } + sleep( 1 ); + } + +Trouble Shooting +------------------- +Here are some things that will help you track down whats wrong. +This is also how to obtain the info that we need to help you on the forums. + +What logs should I check for errors? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +ZoneMinder creates its own logs and are usually located in the ``/tmp`` directory. + +The ZoneMinder logs for the RPM packages are located in ``/var/log/zm``. + +Depending on your problem errors can show up in any of these logs but, usually the logs of interest are ``zmdc.log`` and ``zmpkg.log`` if ZM is not able to start. + +Now since ZM is dependent on other components to work, you might not find errors in ZM but in the other components. + +:: + + */var/log/messages and/or /var/log/syslog + */var/log/dmesg + */var/log/httpd/error_log`` (RedHat/Fedora) or ``/var/log/apache2/error_log + */var/log/mysqld.log`` (Errors here don't happen very often but just in case) + +If ZM is not functioning, you should always be able to find an error in at least one of these logs. Use the [[tail]] command to get info from the logs. This can be done like so: + + tail -f /var/log/messages /var/log/httpd/error_log /var/log/zm/zm*.log + +This will append any data entered to any of these logs to your console screen (``-f``). To exit, hit [ctrl -c]. + + +More verbose logging for the ZoneMinder binaries is available by enabling the debug option from the control panel and will be placed in the path you have configured for the debug logs. Output can be limited to a specific binary as described in the Debug options page under the "?" marks. + +How can I trouble shoot the hardware and/or software? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Here are some commands to get information about your hardware. Some commands are distribution dependent. +* ``[[lspci]] -vv`` -- Returns lots of detailed info. Check for conflicting interrupts or port assignments. You can sometimes alter interrupts/ ports in bios. Try a different pci slot to get a clue if it is HW conflict (command provided by the pciutils package). +* ``[[scanpci]] -v`` -- Gives you information from your hardware EPROM +* ``[[lsusb]] -vv`` -- Returns lots of detail about USB devices (camand provided by usbutils package). +* ``[[dmesg]]`` -- Shows you how your hardware initialized (or didn't) on boot-up. You will get the most use of this. +* ``[[v4l-info]]`` -- to see how driver is talking to card. look for unusual values. +* ``[[modinfo bttv]]`` -- some bttv driver stats. +* ``[[zmu]] -m 0 -q -v`` -- Returns various information regarding a monitor configuration. +* ``[[ipcs]] `` -- Provides information on the ipc facilities for which the calling process has read access. +* ``[[ipcrm]] `` -- The ipcrm command can be used to remove an IPC object from the kernel. +* ``cat /proc/interrupts`` -- This will dispaly what interrupts your hardware is using. + +Why am I getting a 403 access error with my web browser when trying to access http //localhost/zm? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The apache web server needs to have the right permissions and configuration to be able to read the Zoneminder files. Check the forums for solution, and edit the apache configuration and change directory permissions to give apache the right to read the Zoneminder files. Depending on your Zoneminder configuration, you would use the zm user and group that Zoneminder was built with, such as wwwuser and www. + +Why am I getting broken images when trying to view events? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Zoneminder and the Apache web server need to have the right permissions. Check this forum topic and similar ones: +http://www.zoneminder.com/forums/viewtopic.php?p=48754#48754 + +Why is the image from my color camera appearing in black and white? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If you recently upgraded to zoneminder 1.26, there is a per camera option that defaults to black and white and can be mis-set if your upgrade didn't happen right. See this thread: http://www.zoneminder.com/forums/viewtopic.php?f=30&t=21344 + +This may occur if you have a NTSC analog camera but have configured the source in ZoneMinder as PAL for the Device Format under the source tab. You may also be mislead because zmu can report the video port as being PAL when the camera is actually NTSC. Confirm the format of your analog camera by checking it's technical specifications, possibly found with the packaging it came in, on the manufacturers website, or even on the retail website where you purchased the camera. Change the Device Format setting to NTSC and set it to the lowest resolution of 320 x 240. If you have confirmed that the camera itself is NTSC format, but don't get a picture using the NTSC setting, consider increasing the shared memory '''kernel.shmall''' and '''kernel.shmmax''' settings in /etc/sysctl.conf to a larger value such as 268435456. This is also the reason you should start with the 320x240 resolution, so as to minimize the potential of memory problems which would interfere with your attempts to troubleshoot the device format issue. Once you have obtained a picture in the monitor using the NTSC format, then you can experiment with raising the resolution. + +Why do I only see blue screens with a timestamp when monitoring my camera? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If this camera is attached to a capture card, then you may have selected the wrong Device Source or Channel when configuring the monitor in the ZoneMinder console. If you have a capture card with 2 D-sub style inputs(looks like a VGA port) to which you attach a provided splitter that splits off multiple cables, then the splitter may be attached to the wrong port. For example, PV-149 capture cards have two D-sub style ports labeled as DB1 and DB2, and come packaged with a connector for one of these ports that splits into 4 BNC connecters. The initial four video ports are available with the splitter attached to DB1. + +Why do I only see black screens with a timestamp when monitoring my camera? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In the monitor windows where you see the black screen with a timestamp, select settings and enter the Brightness, Contrast, Hue, and Color settings reported for the device by '''zmu -d -q -v'''. 32768 may be appropriate values to try for these settings. After saving the settings, select Settings again to confirm they saved successfully. + +I am getting messages about a backtrace in my logs, what do I do? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If you are seeing entries in your log like the following + +:: + + Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /lib64/libc.so.6 [0x3347230210]] + Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /lib64/libc.so.6(memset+0xce) [0x334727684e]] + Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma [0x40ee9a]] + Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma [0x419946]] + Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma [0x4213cf]] + Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma(cos+0x35c) [0x404674]] + Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /lib64/libc.so.6(__libc_start_main+0xf4) [0x334721da44]] + Jan 11 20:25:22 localhost zma_m2[19051]: ERR [Backtrace: /usr/local/bin/zma(cos+0xd1) [0x4043e9]] + Jan 11 20:25:22 localhost zma_m2[19051]: INF [Backtrace complete] + +then you can help diagnose the problem by running a special command to translate the hex addresses into helpful information. This command is called addr2line and you can type 'man addr2line' for more information. +Basically addr2line takes two sets of parameters, the first is the name of the binary file, and the second is a list of addresses. Both of these pieces of information are displayed in the logs. The filename is the first part after the 'Backtrace:' tag, in this case /usr/local/bin/zma, though it may well be different in your case. Some of the lines refer to libraries rather than the zma executable but those can be ignored for now, the important part is noting which ZM binary is involved. The binary file is passed in following the -e flag. The addresses to pass to addr2line are those contained in the '[]' pairs. Again you can ignore those that are on a line that refers to a library but it will not hurt if you include them. +So in the example above, the command would be ``addr2line -e /usr/local/bin/zma 0x40ee9a 0x419946 0x4213cf 0x404674 0x4043e9`` +This should then dump out a more symbolic list containing source file names and line numbers, and it is this information which will be helpful if posted to the forums. Sometimes addr2line fails to produce useful output. This is usually because either the problem is so severe that it has corrupted the stack and prevented useful information from being displayed, or that you have either compiled ZM without the -g flag for debug, or you have stripped the binaries of symbol information after installation. This this case you would need to rebuild temporarily with debug enabled for the information to be useful. + + +This error some times happens when a linked camera looses its link or it is corrupted by the user or some other system event, try deleting the affected cameras and recreating them in the Zoneminder console. + +How do I repair the MySQL Database? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +There is two ways to go about this. In most cases you can run from the command prompt -> +* mysqlcheck --all-databases --auto-repair -p'''your_database_password''' -u '''your_databse_user''' + +If that does not work then you will have to make sure that ZoneMinder is stopped then run the following (nothing should be using the database while running this and you will have to adjust for your correct path if it is different). -> +* myisamchk --silent --force --fast --update-state -O key_buffer=64M -O sort_buffer=64M -O read_buffer=1M -O write_buffer=1M /var/lib/mysql/*/*.MYI + + +How do I repair the MySQL Database when the cli fails? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In Ubuntu, the commands listed above do not seem to work. However, actually doing it by hand from within MySQL does. (But that is beyond the scope of this document) But that got me thinking... And phpmyadmin does work. Bring up a terminal. +``sudo apt-get install phpmyadmin`` + +Now go to http://zoneminder_IP/ and stop the ZM service. Continue to http://zoneminder_IP/phpmyadmin and select the zoneminder database. Select and tables marked 'in use' and pick the action 'repare' to fix. Restart the zoneminder service from the web browser. Remove or disable the phpmyadmin tool, as it is not always the most secure thing around, and opens your database wide to any skilled hacker. +``sudo apt-get remove phpmyadmin`` + +I upgraded by distribution and ZM stopped working +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Some possibilties (Incomplete list and subject to correction) +``[[/usr/local/bin/zmfix: /usr/lib/libmysqlclient.so.15: version `MYSQL_5.0' not found (required by /usr/local/bin/zmfix)]]`` :: Solution: Recompile and reinstall Zoneminder. +Any time you update a major version that ZoneMinder depends on, you need to recompile ZoneMinder. + +Zoneminder doesn't start automatically on boot +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Check the list for log entries like "zmfix[766]: ERR [Can't connect to server: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)] ". +What can happen is that zoneminder is started too quickly after Mysql and tries to contact the database server before it's ready. Zoneminder gets no answer and aborts. +August 2010 - Ubuntu upgrades seem to be leaving several systems in this state. One way around this is to add a delay to the zoneminder startup script allowing Mysql to finish starting. +"Simply adding 'sleep 15' in the line above 'zmfix -a' in the /etc/init.d/zoneminder file fixed my ZoneMinder startup problems!" - credit to Pada. + +Remote Path setup for Panasonic and other Camera +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On adding or editing the source you can select the preset link for the parameters for the specified camera . In version 1.23.3 presets for BTTV,Axis,Panasonic,GadSpot,VEO, and BlueNet are available . Selecting the presets ZM fills up the required value for the remote path variable + +Why do I get repeated/ mixed/unstable/ blank monitors on bt878-like cards (a.k.a. PICO 2000) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Please have a check at [[Pico2000]]; + +What causes "Invalid JPEG file structure: two SOI markers" from zmc (1.24.x) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Some settings that used to be global only are now per camera. On the Monitor Source tab, if you are using Remote Protocol "HTTP" and Remote Method "Simple", try changing Remote Method to "Regexp". + +Miscellaneous +------------------- +I see ZoneMinder is licensed under the GPL. What does that allow or restrict me in doing with ZoneMinder? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ZoneMinder license is described at the end of the documentation and consists of the following section + + 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. + +This means that ZoneMinder is licensed under the terms described `here `__. There is a comprehensive FAQ covering the GPL at http://www.gnu.org/licenses/gpl-faq.html but in essence you are allowed to redistribute or modify GPL licensed software provided that you release your distribution or modifications freely under the same terms. You are allowed to sell systems based on GPL software. You are not allowed to restrict or reduce the rights of GPL software in your distribution however. Of course if you are just making modifications for your system locally you are not releasing changes so you have no obligations in this case. I recommend reading the GPL FAQ for more in-depth coverage of this issue. + +Can I use ZoneMinder as part of my commercial product? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The GPL license allows you produce systems based on GPL software provided your systems also adhere to that license and any modifications you make are also released under the same terms. The GPL does not permit you to include ZoneMinder in proprietary systems (see http://www.gnu.org/licenses/gpl-faq.html#GPLInProprietarySystem for details). If you wish to include ZoneMinder in this kind of system then you will need to license ZoneMinder under different terms. This is sometimes possible and you will need to contact me for further details in these circumstances. -If AlarmedPixels is selected, you can only enter min/max pixel threshold and -min/max alarmed area. If FilteredPixels is selected, the Blob options are -disabled. The Blob check method allows you to specify all options. Filtered -adds more checks than alarmed, and blobs adds more checks than filtered. The -final 'score' is calculated using final check method. diff --git a/docs/index.rst b/docs/index.rst index 46713b754..46da979cf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,7 +13,7 @@ ZoneMinder Documentation Welcome to ZoneMinder's documentation, the following resources are available :doc:`installationguide/index` - Many distrubution repos only hold older versions of ZoneMinder, current versions contain many bug fixes and updated functionality. Instructions here for installing updated packages or compiling from source. + Many distribution repos only hold older versions of ZoneMinder, current versions contain many bug fixes and updated functionality. Instructions here for installing updated packages or compiling from source. :doc:`userguide/index` Guide to setting up ZoneMinder for the first time and detailed guides for using the ZoneMinder front end. diff --git a/docs/installationguide/centos.rst b/docs/installationguide/centos.rst deleted file mode 100644 index 606aaffd5..000000000 --- a/docs/installationguide/centos.rst +++ /dev/null @@ -1,5 +0,0 @@ -Centos -====== - - - diff --git a/docs/installationguide/fedora.rst b/docs/installationguide/fedora.rst deleted file mode 100644 index 09ffe4297..000000000 --- a/docs/installationguide/fedora.rst +++ /dev/null @@ -1,4 +0,0 @@ -Fedora -====== - - diff --git a/docs/installationguide/images/zm-multiserver.png b/docs/installationguide/images/zm-multiserver.png new file mode 100644 index 000000000..360b612a8 Binary files /dev/null and b/docs/installationguide/images/zm-multiserver.png differ diff --git a/docs/installationguide/images/zm-multiserver.xml b/docs/installationguide/images/zm-multiserver.xml new file mode 100644 index 000000000..c829e1a94 --- /dev/null +++ b/docs/installationguide/images/zm-multiserver.xml @@ -0,0 +1 @@ +7Vvfb+I4F/1rkHYfFpEEQnlsmXZ2pXZUqaPd/fZlZIib+JsQs44pZf76uU6uEydOgELoUgkeELn+fc/JuddO6HnTxetnQZbRAw9o3HMHwWvP+9Rz3SvfhW9l2OSGsTfKDaFgQW5ySsMT+0HROEDrigU0rVSUnMeSLavGOU8SOpcVGxGCr6vVnnlcHXVJQj1iaXiak9i2/sUCGeGyXL+0/05ZGOmRHX+Sl8zI/Hso+CrB8Xqu95x98uIF0X1lC/VuwYeCc+hG/Vq8Tmms/Kh9lHvjrqW0mKSgCU5kRwMHUXgh8QpX+g9P6ANLAirA/rACF//2RMVLdvmFyjUX3+HXV77kMQ83PdePYaibGRT7ofqVd5jKjXZdtniqRnSgeB0xSZ+WZK5K18AVsEVyEWNxKgX/TqfQtwBLAnMB4zOL45op4oL94IkkuiFMUDLA6zpmYQI2yVXPz1AFyeRcFZMzHYQ+U80pkjUzocM+U76gUsA6B1jqISORyB52sC5ZMR5jlchgxFgzmSATw6LnEiD4gRi14OWOLbweSALkW6gF7cYC1qjsBzicoF/nMBBwwXb4ggWBGqYRYQ61n2O+BksE9Sg0MLFR0AguiWRcdeWCA2Fm0AVLQnVdXgHvwKCKVXM9R7ipBgPfn067gdgZDCsYu+MrC+QR3jkmxg7e88dBjJ0YEN+zBO65C7jdgKtRKsAd9xHLHfCimB4Fr2aSARsNINLgJRcy4iFPSHxbWsF7WkGVbwx8aRJcq+BWIgmWO8AXq/6fSrlBGMhKcgVWMcI9z9yt+ml1a8pXIoM6cxzGcElESLHeCAVJLWKr9wWNgQEv1XDa5MmsKayKqFa6wpKzRKZGz4/KYIhyTZUdDMwlKnmPza2dQa31EMO6Hj53A7aqgV2scS/8J+jDjvB/ZfJvZe57Pl7+L7scT/DykQoG01M3ddb4XRnjoYyeIWFGflXia4Sx6g8neOPuQ7ADeKHdYqj+n5DxcjDdX3+xOGNwwJRzkMpp9rG0H0r8qyvnxoOSUJCAAW61IKDNn5iADDoX60SBX3SmM99MsiOyVJNZvEK7ZdRP8tQw7c9WAMEhY9Q0H7Nk72ZpUFhl0zPoyqD1jbYha0GlMfyguxqy4jcHDb8qEOBMO+vTCV7XIWOEzDOo8SS5UBuWCznOkBzDyTuSowjKtnDk+WLAXnTCOI2Vd6HuL183S5Xd/dqUVOYWGNdoaVGsLT9r3oVp6z2Z0fiRpwzBnXEp+QIqxLWCIg+1EtMz5vZyfgJuOTVyTeztZpG9VLabnXALifzfJSswY7OJujbbqOt6o5NnMNrWXQazPyD23tBGyMrwUpi9rNnM+6h2RpJVN1LCaoLY6rad7jB3Vw2M1ba35X12Lj9sUeMdufzOjootoe4o50QXmwJfy8IWWNUZYnGihWac4D4CU5yMkpnusTxwbBKeSTU/9jT1TOFpCmqF8aiUp4nneWBS689Wo+Xd/3eljkuBmN7dnQcf05SHrzJd0ieZW8JePsAl7u0d99Lcp7VTHKXXO4JhQfvd0dBg3aSJdDoEHkM6HWm33YUGDPuBe1Mc/zdlr820afB9K7StZIhIkOm90uyApFERkes4Mp6O+wzUIe2rpyIZkkdhpbPi6oZ5bCfFQ/SviaQ+xznuCHUPPT3bMInuc8cYborwYztwWxjdP2LWRxpO8CzirRGzdcrdR0zd8xsDxGAwnSqpqAeITwTiIkntCGHFA2tTdcywb4hDl13ZUdHpW4opQPc7NHgKXM2U9La+skVrelqkjcdpnb3//3BaN3QgaWtKtTvXuvpIB+8OWqd8gt3B9kf0WrAuWrB/ptq1Bng+hsxCA2wCnyxvdVyk4keWgNGwnjucSgLqIx0sAa1T7l4C7IdDFwk4MwkYTqqvFbyzBOBpzUeWAP/ddjz1kQ7e8bROuXsJsB8CXiTgzCTAr78a+J4SoJ9KNOyIZ3rD+McjVJiSBRXE3nru91Cw6OtCtb2oNl8JJjff5rnTO3mBbTSqbj2KR8AGzTQTO3/crDu+vIB6yncUXa/+jqL99OVkL6DqN5ovL6CeCNyR9Xbx6V5Ahcvy7wR52lH+P8O7/Qk= \ No newline at end of file diff --git a/docs/installationguide/images/zm_first_screen_post_install.png b/docs/installationguide/images/zm_first_screen_post_install.png new file mode 100644 index 000000000..568d7fcf5 Binary files /dev/null and b/docs/installationguide/images/zm_first_screen_post_install.png differ diff --git a/docs/installationguide/index.rst b/docs/installationguide/index.rst index 58a8f4b21..e030ed7cf 100644 --- a/docs/installationguide/index.rst +++ b/docs/installationguide/index.rst @@ -8,5 +8,5 @@ Contents: ubuntu debian - fedora - centos + redhat + multiserver diff --git a/docs/installationguide/multiserver.rst b/docs/installationguide/multiserver.rst new file mode 100644 index 000000000..e9ebefac2 --- /dev/null +++ b/docs/installationguide/multiserver.rst @@ -0,0 +1,55 @@ +Multi-Server Install +==================== + +It is possible to run multiple ZoneMinder servers and manage them from a single interface. To achieve this each zoneminder server is connected to a single shared database server and shares file storage for event data. + +.. image:: images/zm-multiserver.png + +Topology Design Notes +--------------------- + +1. Device symbols represent separate logical functions, not necessarily separate hardware. For example, the Database Server and a ZoneMinder Server, can reside on the same physical hardware. + +2. Configure each ZoneMinder Server to use the same, remote Database Server (Green). + +3. The Storage Server (Red) represents shared storage, accessible by all ZoneMinder Servers, mounted under each server’s events folder. + +4. Create at least two networks for best performance. Dedicate a Storage LAN for communication with the Storage and Database Servers. Make use of multipath and jumbo frames if possible. Keep all other traffic off the Storage LAN! Dedicate the second LAN, called the Video LAN in the diagram, for all other traffic. + +New installs +------------ + +1. Follow the normal instructions for your distro for installing ZoneMinder onto all the ZoneMinder servers in the normal fashion. Only a single database will be needed either as standalone, or on one of the ZoneMinder Servers. + +2. On each ZoneMinder server, edit zm.conf. Find the ZM_DB_HOST variable and set it to the name or ip address of your Database Server. Find the ZM_SERVER_HOST and enter a name for this ZoneMinder server. Use a name easily recognizable by you. This name is not used by ZoneMinder for dns or any other form of network conectivity. + +3. Copy the file /usr/share/zoneminder/db/zm_create.sql from one of the ZoneMinder Servers to the machine targeted as the Database Server. + +4. Install mysql/mariadb server onto the Database Server. + +5. It is advised to run "mysql_secure_installation" to help secure the server. + +6. Using the password for the root account set during the previous step, create the ZoneMinder database and configure a database account for ZoneMinder to use: + +:: + + mysql -u root -p < zm_create.sql + mysql -uroot -p -e "grant all on zm.* to 'zmuser'@localhost identified by 'zmpass';" + mysqladmin -u root -p reload + +The database account credentials, zmuser/zmpass, are arbitrary. Set them to anything that suits your environment. +Note that these commands are just an example and might not be secure enough for your environment. + +7. If you have chosen to change the ZoneMinder database account credentials to something other than zmuser/zmpass, you must now update zm.conf on each ZoneMinder Server. Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous step. + +Additionally, you must also edit /usr/share/zoneminder/www/api/app/Config/database.php in a similar manner on each ZoneMinder Server. Scroll down and change login and password to the values you created in the previous step. + +8. All ZoneMinders Servers must share a common events folder. This can be done in any manner supported by the underlying operating system. From the Storage Server, share/export a folder to be used for ZoneMinder events. + +9. From each ZoneMinder Server, mount the shared events folder on the Storage Server to the events folder on the local ZoneMinder Server. + +NOTE: The location of this folder varies by distro. This folder is often found under "/var/lib/zoneminder/events" for RedHat based distros and "/var/cache/zoneminder/events" for Debain based distros. This folder is NOT a Symbolic Link! + +10. Open your browser and point it to the web console on any of the ZoneMinder Servers (they will all be the same). Open Options, click the Servers tab,and populate this screen with all of your ZoneMinder Servers. Each server has a field for its name and its hostname. The name is what you used for ZM_SERVER_HOST in step 2. The hostname is the network name or ip address ZoneMinder should use. + +11. When creating a new Monitor, remember to select the server the camera will be assigned to from the Server drop down box. diff --git a/docs/installationguide/redhat.rst b/docs/installationguide/redhat.rst new file mode 100644 index 000000000..e15d58aa7 --- /dev/null +++ b/docs/installationguide/redhat.rst @@ -0,0 +1,203 @@ +Redhat +====== + +.. contents:: + +These instructions apply to all Redhat distros and their clones, including but not limited to: Fedora, RHEL, CentOS, Scientific Linux, and others. While the installation instructions are the same for each distro, the reason why one might use one distro over the other is different. A short description follows, which is intended to help you chose what distro best fits your needs. + +Background: RHEL, CentOS, and Clones +------------------------------------ + +These distributions are classified as enterprise operating systems and have a long operating lifetime of many years. By design, they will not have the latest and greatest versions of any package. Instead, stable packages are the emphasis. + +Replacing any core package in these distributions with a newer package from a third party is expressly verboten. The ZoneMinder development team will not do this, and neither should you. If you have the perception that you've got to have a newer version of mysql, gnome, apache, etc. then, rather than upgrade these packages, you should instead consider using a different distribution such as Fedora. + +The ZoneMinder team will not provide support for systems which have had any core package replaced with a package from a third party. + +Background: Fedora +------------------------------------ + +One can think of Fedora as RHEL or CentOS Beta. This is, in fact, what it is. Fedora is primarily geared towards development and testing of newer, sometimes bleeding edge, packages. The ZoneMinder team uses this distro to determine the interoperability of ZoneMinder with the latest and greatest versions of packages like mysql, apache, systemd, and others. If a problem is detected, it will be addressed long before it makes it way into RHEL. + +Fedora has a short life-cycle of just 6 months. However, Fedora, and consequently ZoneMinder, is available on armv7 architecture. Rejoice, Raspberry Pi users! + +If you desire newer packages than what is available in RHEL or CentOS, you should consider using Fedora. + +Zmrepo – A ZoneMinder RPM Repository +------------------------------------ + +Zmrepo is a turn key solution. It will install all of ZoneMinder's dependencies for you. This is the easiest and the recommended way to install ZoneMinder on any system running a Redhat based distribution. + +Zmrepo supports the two most recent, major releases of each Redhat based distro. + +The following notes are based on real problems which have occurred: + +- Zmrepo assumes you have installed the underlying distrubution **using the official installation media for that distro**. Third party "Spins" are not supported and may not work correctly. + +- ZoneMinder is intended to be installed in an environment dedicated to ZoneMinder. While ZoneMinder will play well with many applications, some invariably will not. Asterisk is one such example. + +- Be advised that you need to start with a clean system before using zmrepo. + +- If you have previously installed ZoneMinder from-source, then your system is **NOT** clean. You must manually search for and delete all ZoneMinder related files before using zmrepo (look under /usr/local). Make uninstall helps, but it will not do this for you correctly. You **WILL** have problems if you ignore this step. + +- It is not necessary, and not recommended, to install a LAMP stack ahead of time. + +- Disable other third party repos and uninstall any of ZoneMinder's third party dependencies, which might already be on the system, especially ffmpeg and vlc. Attempting to install dependencies yourself often causes problems. + +- Each ZoneMinder rpm includes a README file under /usr/share/doc. You must follow the all steps in this README file, precisely, each and every time ZoneMinder is installed or upgraded. **Failure to do so is guaranteed to result in a non-functional system.** + +To begin the installation of ZoneMinder on your Redhat based distro, please navigate to: http://zmrepo.zoneminder.com + +How to Build a (Custom) ZoneMinder Package +------------------------------------------ + +If you are looking to do development or the packages in zmrepo just don't suit you, then you should follow these steps to learn how to build your own ZoneMinder RPM. + +Background +********** +The following method documents how to build ZoneMinder into an RPM package, compatible with Fedora, Redhat, CentOS, and other compatible clones. This is exactly how the RPMS in zmrepo are built. + +The method documented below was chosen because: + +- All of ZoneMinder's dependencies are downloaded and installed automatically + +- Cross platform capable. The build host does not have to be the same distro or release version as the target. + +- Once your build environment is set up, few steps are required to run the build again in the future. + +- Troubleshooting becomes easier if we are all building ZoneMinder the same way. + +The build instructions below make use of a custom script called "buildzm.sh". Advanced users are encouraged to view the contents of this script. Notice that the script doesn't really do a whole lot. The goal of the script is to simply make the process a little easier for the first time user. Once you become familar with the build process, you can issue the mock commands found in the buildzm.sh script yourself if you so desire. + +***IMPORTANT*** +Certain commands in these instructions require root privileges while other commands do not. Pay close attention to this. If the instructions below state to issue a command without a “sudo” prefix, then you should *not* be root while issuing the command. Getting this incorrect will result in a failed build. + +Set Up Your Environment +*********************** +Before you begin, set up an rpmbuild environment by following `this guide `_ by the CentOS developers. + +Next, navigate to `Zmrepo `_, and follow the instructions to enable zmrepo on your system. + +With zmrepo enabled, issue the following command: + +:: + + sudo yum install zmrepo-mock-configs mock + + +Add your user account to the group mock: + +:: + + sudo gpasswd -a {your account name} mock + + +Your build environment is now set up. + +Build from SRPM +*************** +To continue, you need a ZoneMinder SRPM. For starters, let's use one of the SRPMS from zmrepo. Go browse the `Zmrepo `_ site and choose an appropriate SRPM and place it into the ~/rpmbuild/SRPMS folder. + +For CentOS 7, I have chosen the following SRPM: + +:: + + wget -P ~/rpmbuild/SRPMS http://zmrepo.zoneminder.com/el/7/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm + + +Now comes the fun part. To build ZoneMinder, issue the following command: + +:: + + buildzm.sh zmrepo-el7-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm + + +Want to build ZoneMinder for Fedora, instead of CentOS, from the same host? Once you download the Fedora SRPM, issue the following: + +:: + + buildzm.sh zmrepo-f21-x86_64 ~/rpmbuild/SRPMS/zoneminder-1.28.1-1.fc21.src.rpm + +Notice that the buildzm.sh tool requires the following parameters: + +:: + + buildzm.sh MOCKCONFIG ZONEMINDER_SRPM + +The list of available Mock config files are available here: + +:: + + ls /etc/mock/zmrepo*.cfg + + +You choose the config file based on the desired distro (e.g. el6, el7, f20, f21) and basearch (e.g. x86, x86_64, arhmhfp). Notice that, when specifying the Mock config as a commandline parameter, you should leave off the ".cfg" filename extension. + +Installation +************ +Once the build completes, you will be presented with a folder containing the RPM's that were built. Copy the newly built ZoneMinder RPM to the desired system, enable zmrepo per the instruction on the `Zmrepo `_ +website, and then install the rpm by issuing the appropriate yum install command. Finish the installation by following the zoneminder setup instructions in the distro specific readme file, named README.{distroname}, which will be installed into the /usr/share/doc/zoneminder* folder. + +Finally, you may want to consider editing the zmrepo repo file under /etc/yum.repos.d and placing an “exclude=zoneminder*” line into the config file. This will prevent your system from overwriting your manually built RPM with the ZoneMinder RPM found in the repo. + +How to Modify the Source Prior to Build +*************************************** +Before attempting this part of the instructions, make sure and follow the previous instructions for building one of the unmodified SRPMS from zmrepo. Knowing this part works will assist in troubleshooting should something go wrong. + +These instructions may vary depending on what exactly you want to do. The following example assumes you want to build a development snapshot from the master branch. + +From the previous instructions, we downloaded a CentOS 7 ZoneMinder SRPM and placed it into ~/rpmbuild/SRPMS. For this example, install it onto your system: + +:: + + rpm -ivh ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm + + +IMPORTANT: This operation must be done with your normal user account. Do *not* perform this command as root. + +Make sure you have git installed: + +:: + + sudo yum install git + + +Now clone the ZoneMinder git repository: + +:: + + cd + git clone https://github.com/ZoneMinder/ZoneMinder + cd ZoneMinder + git submodule init + git submodule update + +This will create a sub-folder called ZoneMinder, which will contain the latest development. + +We want to turn this into a tarball, but first we need to figure out what to name it. Look here: + +:: + + ls ~/rpmbuild/SOURCES + +The tarball from the previsouly installed SRPM should be there. This is the name we will use. For this example, the name is ZoneMinder-1.28.1.tar.gz. From one folder above the local ZoneMinder git repository, execute the following: + +:: + + mv ZoneMinder ZoneMinder-1.28.1 + tar -cvzf ~/rpmbuild/SOURCES/ZoneMinder-1.28.1.tar.gz ZoneMinder-1.28.1/* + +The trailing "/\*" leaves off the hidden dot "." file and folders from the git repo, which is what we want. +Note that we are overwriting the original tarball. If you wish to keep the original tarball then create a copy prior to creating the new tarball. + +Now build a new src.rpm: + +:: + + rpmbuild -bs --nodeps ~/rpmbuild/SPECS/zoneminder.el7.spec + +This step will overwrite the SRPM you originally downloaded, so you may want to back it up prior to completing this step. Note that the name of the specfile will vary slightly depending on the target distro. + +You should now have a new SRPM under ~/rpmbuild/SRPMS. In our example, the SRPM is called zoneminder-1.28.1-2.el7.centos.src.rpm. Now follow the previous instructions that describe how to use the buildzm script, using ~/rpmbuild/SRPMS/zoneminder-1.28.1-2.el7.centos.src.rpm as the path to your SRPM. + + diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst index 4f6d8289d..80cb27552 100644 --- a/docs/installationguide/ubuntu.rst +++ b/docs/installationguide/ubuntu.rst @@ -1,52 +1,391 @@ -Ubuntu -====== +Ubuntu Instruction +=================== -PPA Install ------------ -Follow these instructions to install current release version on Ubuntu.: +.. contents:: - sudo apt-add-repository ppa:iconnor/zoneminder +Easy Way: Install ZoneMinder from a package (Ubuntu 15.x+) +----------------------------------------------------------- +These instructions are for a brand new ubuntu 15.04 system which does not have ZM installed. -Once you have updated the repository then update and install the package.: - - sudo apt-get update - sudo apt-get install zoneminder +**Step 1**: Make sure we add the correct packages + +:: + + sudo add-apt-repository ppa:iconnor/zoneminder + sudo apt-get update + +if you don't have mysql already installed: + +:: + + sudo apt-get install mysql-server + +This will ask you to set up a master password for the DB (you are asked for the mysql root password when installing mysql server). + +**Step 2**: Install ZoneMinder + +:: + + sudo apt-get install zoneminder + +**Step 3**: Configure the Database + +:: + + sudo mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql + mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';" + +You don't really need this, but no harm (needed if you are upgrading) + +:: + + sudo /usr/bin/zmupdate.pl + +**Step 4**: Configure systemd to recognize ZoneMinder and configure Apache correctly: + +:: + + sudo systemctl enable zoneminder + sudo a2enconf zoneminder + sudo a2enmod cgi + sudo chown -R www-data:www-data /usr/share/zoneminder/ + + +We need this for API routing to work: + +:: + + sudo a2enmod rewrite + +This is probably a bug with iconnor's PPA as of Oct 3, 2015 with package 1.28.107. After installing, ``zm.conf`` does not have the right read permissions, so we need to fix that. This may go away in future PPA releases: + +:: + + sudo chown www-data:www-data /etc/zm/zm.conf + +We also need to install php5-gd (as of 1.28.107, this is not installed) + +:: + + sudo apt-get install php5-gd + +**Step 5**: Edit Timezone in PHP + +:: + + vi /etc/php5/apache2/php.ini + +Look for [Date] and inside it you will see a date.timezone +that is commented. remove the comment and specific your timezone. +Please make sure the timezone is valid (see this: http://php.net/manual/en/timezones.php) + +In my case: + +:: + + date.timezone = America/New_York + +**Step 6**: Restart services + +:: + + sudo service apache2 reload + sudo systemctl restart zoneminder + + +**Step 7: make sure live streaming works**: Make sure you can view Monitor streams: + +startup ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM (you should not need to do this for packages, as this should automatically work) + + +**Step 8**: If you have changed your DB login/password from zmuser/zmpass, the API won't know about it + +If you changed the DB password **after** installing ZM, the APIs will not be able to connect to the DB. + +If you have, go to ``zoneminder/www/api/app/Config`` & Edit ``database.php`` + +There is a class there called ``DATABASE_CONFIG`` - change the ``$default`` array to reflect your new details. Example: + +:: + + public $default = array( + 'datasource' => 'Database/Mysql', + 'persistent' => false, + 'host' => 'localhost', + 'login' => 'mynewDBusername', + 'password' => 'mynewDBpassword' + 'database' => 'zm', + 'prefix' => '', + //'encoding' => 'utf8', + ); + + +You are done. Lets proceed to make sure everything works: + +Making sure ZM and APIs work: + +1. open up a browser and go to ``http://localhost/zm`` - should bring up ZM +2. (OPTIONAL - just for peace of mind) open up a tab and go to ``http://localhost/zm/api`` - should bring up a screen showing CakePHP version with some green color boxes. Green is good. If you see red, or you don't see green, there may be a problem (should not happen). Ignore any warnings in yellow saying "DebugKit" not installed. You don't need it +3. open up a tab in the same browser and go to ``http://localhost/zm/api/host/getVersion.json`` + +If it responds with something like: + +:: + + { + "version": "1.28.107", + "apiversion": "1.28.107.1" + } + + +**Then your APIs are working** + +Make sure ZM and APIs work with security: +1. Enable OPT_AUTH in ZM +2. Log out of ZM in browser +3. Open a NEW tab in the SAME BROWSER (important) and go to ``http://localhost/zm/api/host/getVersion.json`` - should give you "Unauthorized" along with a lot more of text +4. Go to another tab in the SAME BROWSER (important) and log into ZM +5. Repeat step 3 and it should give you the ZM and API version + +**Congrats** your installation is complete + + +Easy Way: Install ZoneMinder from a package (Ubuntu 14.x) +----------------------------------------------------------- +**These instructions are for a brand new ubuntu 14.x system which does not have ZM installed.** + +**Step 1:** Install ZoneMinder + +:: + + sudo add-apt-repository ppa:iconnor/zoneminder + sudo apt-get update + sudo apt-get install zoneminder + +(just press OK for the prompts you get) + +**Step 2:** Set up DB + +:: + + sudo mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql + mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';" + +**Step 3:** Set up Apache + +:: + + sudo a2enconf zoneminder + sudo a2enmod rewrite + sudo a2enmod cgi + +**Step 4:**:Some tweaks that will be needed: + +Edit ``/etc/init.d/zoneminder``: + +add a ``sleep 10`` right after line 25 that reads ``echo -n "Starting $prog:"`` +(The reason we need this sleep is to make sure ZM starts after mysqld starts) + +As of Oct 3 2015, zm.conf is not readable by ZM. This is likely a bug and will go away in the next package + +:: + + sudo chown www-data:www-data /etc/zm/zm.conf -Build Package From Source -------------------------- +**Step 5**: If you have changed your DB login/password -A fresh build based on master branch running Ubuntu 1204 LTS. Will likely work for other versions as well.:: +If you changed the DB password **after** installing ZM, the APIs will not be able to connect to the DB. - root@host:~# aptitude install -y apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg8-dev libjpeg8 apache2-mpm-prefork libapache2-mod-php5 php5-cli libphp-serialization-perl libgnutls-dev libjpeg8-dev libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libv4l-dev libtool ffmpeg libnetpbm10-dev libavdevice-dev libmime-lite-perl dh-autoreconf dpatch; +If you have, go to ``/usr/share/zoneminder/www/api/app/Config`` & Edit ``database.php`` - root@host:~# git clone https://github.com/ZoneMinder/ZoneMinder.git zoneminder; - root@host:~# cd zoneminder; - root@host:~# ln -s distros/ubuntu1204 debian; - root@host:~# dpkg-checkbuilddeps; - root@host:~# dpkg-buildpackage; +There is a class there called ``DATABASE_CONFIG`` - change the ``$default`` array to reflect your new details. Example: + +:: + + public $default = array( + 'datasource' => 'Database/Mysql', + 'persistent' => false, + 'host' => 'localhost', + 'login' => 'mynewDBusername', + 'password' => 'mynewDBpassword' + 'database' => 'zm', + 'prefix' => '', + //'encoding' => 'utf8',` + ); + +We also need to install php5-gd (as of 1.28.107, this is not installed) + +:: + + sudo apt-get install php5-gd -One level above you'll now find a deb package matching the architecture of the build host\::: +**Step 6**: Edit Timezone in PHP - root@host:~# ls -1 ~/zoneminder\*; - /root/zoneminder_1.26.4-1_amd64.changes - /root/zoneminder_1.26.4-1_amd64.deb - /root/zoneminder_1.26.4-1.dsc - /root/zoneminder_1.26.4-1.tar.gz +``sudo vi /etc/php5/apache2/php.ini`` +Look for [Date] and inside it you will see a date.timezone +that is commented. remove the comment and specific your timezone. +Please make sure the timezone is valid (see [this](http://php.net/manual/en/timezones.php)) + +In my case: + +:: + + date.timezone = America/New_York -The dpkg command itself does not resolve dependencies. That's what high-level interfaces like aptitude and apt-get are normally for. Unfortunately, unlike RPM, there's no easy way to install a separate deb package not contained with any repository. +**Step 7: make sure live streaming works**: Make sure you can view Monitor streams: -To overcome this "limitation" we'll use dpkg only to install the zoneminder package and apt-get to fetch all needed dependencies afterwards. Running dpkg-reconfigure in the end will ensure that the setup scripts e.g. for database provisioning were executed.:: +startup ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM (you should not need to do this for packages, as this should automatically work) - root@host:~# dpkg -i /root/zoneminder_1.26.4-1_amd64.deb; apt-get install -f; - root@host:~# dpkg-reconfigure zoneminder; -Alternatively you may also use gdebi to automatically resolve dependencies during installation\::: - root@host:~# aptitude install -y gdebi; - root@host:~# gdebi /root/zoneminder_1.26.4-1_amd64.deb; +restart: + +:: + + sudo service apache2 restart + sudo service zoneminder restart + +**Step 8**: Making sure ZM and APIs work: (optional - only if you need APIs) + +1. open up a browser and go to ``http://localhost/zm`` - should bring up ZM +2. (OPTIONAL - just for peace of mind) open up a tab and go to ``http://localhost/zm/api`` - should bring up a screen showing CakePHP version with some green color boxes. Green is good. If you see red, or you don't see green, there may be a problem (should not happen). Ignore any warnings in yellow saying "DebugKit" not installed. You don't need it +3. open up a tab in the same browser and go to ``http://localhost/zm/api/host/getVersion.json`` + +If it responds with something like: + +:: + + { + "version": "1.28.107", + "apiversion": "1.28.107.1" + } + +Then your APIs are working + +Make sure you can view Monitor View: +1. Open up ZM, configure your monitors and verify you can view Monitor feeds. +2. If not, open up ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM (you should not need to do this for packages, as this should automatically work) + +Make sure ZM and APIs work with security: +1. Enable OPT_AUTH in ZM +2. Log out of ZM in browser +3. Open a NEW tab in the SAME BROWSER (important) and go to ``http://localhost/zm/api/host/getVersion.json`` - should give you "Unauthorized" along with a lot more of text +4. Go to another tab in the SAME BROWSER (important) and log into ZM +5. Repeat step 3 and it should give you the ZM and API version + +**Congrats** Your installation is complete + + + + +Harder Way: Build Package From Source +------------------------------------------- +(These instructions assume installation from source on a ubuntu 15.x+ system) + +**Step 1:** Grab the package installer script + +:: + + wget https://raw.githubusercontent.com/ZoneMinder/ZoneMinder/master/utils/do_debian_package.sh + chmod a+x do_debian_package.sh + + +**Step 2:** Update the system + +:: + + sudo apt-get update + + +**Step 3** Create the package + +To build the latest master snapshot: + +:: + + ./do_debian_package.sh `lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'` `date +%Y%m%d`01 local master + + +To build the latest stable release: + +:: + + ./do_debian_package.sh `lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'` `date +%Y%m%d`01 local stable + + +Note that the ``lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`` part simply extracts your distribution name - like "vivid", "trusty" etc. You can always replace it by your distro name if you know it. As far as the script goes, it checks if your distro is "trusty" in which case it pulls in pre-systemd release configurations and if its not "trusty" it assumes its based on systemd and pulls in systemd related config files. + +(At the end the script will ask if you want to retain the checked out version of zoneminder. If you are a developer and are making local changes, make sure you select "y" so that the next time you do the build process mentioned here, it keeps your changes. Selecting any other value than "y" or "Y" will delete the checked out code and only retain the package) + +This should now create a bunch of .deb files + +**Step 4:** Install the package + +:: + + sudo gdebi zoneminder__.deb + (example sudo gdebi zoneminder_1.29.0-vivid-2016012001_amd64.deb) + + +**This will report DB errors - ignore - you need to configure the DB and some other stuff** + +**Step 5:** Post install configuration + +:: + + sudo mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql + mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';" + + sudo a2enmod cgi rewrite + sudo a2enconf zoneminder + + + +**Step 6:** Fix PHP TimeZone + +``sudo vi /etc/php5/apache2/php.ini`` + +Look for [Date] and inside it you will see a date.timezone that is commented. remove the comment and specific your timezone. Please make sure the timezone is valid (see http://php.net/manual/en/timezones.php) + +Example: + +``date.timezone = America/New_York`` + +**Step 7:** Fix some key permission issues and make sure API works + +:: + + sudo chown www-data /etc/zm/zm.conf + sudo chown -R www-data /usr/share/zoneminder/www/api/ + + +**Step 8:** Restart all services + +:: + + sudo service apache2 restart + sudo service zoneminder restart + +Check if ZM is running properly + +:: + + sudo service zoneminder status + + +**Step 9:** Make sure streaming works - set PATH_ZMS + +open up ZM console in your browser, go to Options->Path and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM + + +**Step 10:** Make sure everything works + +* point your browser to http://yourzmip/zm - you should see ZM console running +* point your browser to http://yourzmip/zm/api/host/getVersion.json - you should see an API version +* Configure your monitors and make sure its all a-ok + - sudo apt-get install apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg-turbo8-dev libjpeg-turbo8 apache2-mpm-prefork libapache2-mod-php5 php5-cli diff --git a/docs/userguide/cameracontrol.rst b/docs/userguide/cameracontrol.rst index 93619d15c..551a6cf46 100644 --- a/docs/userguide/cameracontrol.rst +++ b/docs/userguide/cameracontrol.rst @@ -11,11 +11,39 @@ Control Scripts It should be emphasised that the control and capability elements of ZoneMinder are not intended to be able to support every camera out of the box. Some degree of development is likely to be required for many cameras. +Controlling Monitors +==================== + +If you have defined your system as having controllable monitors and you are looking at a monitor that is configured for control, then clicking on the ‘Control’ link along the top of the window will change the short event listing area to a control area. The capabilities you have defined earlier determine exactly what is displayed in this window. Generally you will have a Pan/Tilt control area along with one or subsidiary areas such as zoom or focus control to the side. If you have preset support then these will be near the bottom of the window. The normal method of controlling the monitor is by clicking on the appropriate graphics which then send a command via the control script to the camera itself. This may sometimes take a noticeable delay before the camera responds. + +It is usually the case that the control arrows are sensitive to where you click on them. If you have a camera that allows different speeds to be used for panning or zooming etc then clicking near the point of the arrow will invoke the faster speed whilst clicking near the base of the arrow will be slower. If you have defined continuous motion then ongoing activities can be stopped by clicking on the area between the arrows, which will either be a graphic in the case of pan/tilt controls or a word in the case of zoom and focus controls etc. + +Certain control capabilities such as mapped motion allow direct control by clicking on the image itself when used in browsers which support streamed images directly. Used in this way you can just click on the area of the image that interests you and the camera will centre on that spot. You can also use direct image control for relative motion when the area of the image you click on defines the direction and the distance away from the centre of the image determines the speed. As it is not always very easy to estimate direction near the centre of the image, the active area does not start until a short distance away from the centre, resulting in a ‘dead’ zone in the middle of the image. + +Control Flow +^^^^^^^^^^^^ +Having a basic understanding of how camera control works in ZoneMinder will go a long way in debugging issues in the future. It is important to note that many of the 'camera control' scripts are user contributed and it is entirely possible that they break in a future version upgrade. + +* ZoneMinder relies on 'control protocols' for specific camera models. These 'control' protocols are nothing but perl packages located in ``/usr/share/perl5/ZoneMinder/Control/`` (in Ubuntu distributions) that are invoked by ZoneMinder when you invoke a PTZ operation + +* When you associate a 'protocol' for PTZ for a camera, you are effectively letting ZoneMinder know where to locate the perl file that will eventually control the camera movement + +* Let's for example, assume that you are configuring a Foscam 9831W camera and have associated the '9831w' protocol to that camara. This basically means when you move the camera via ZoneMinder, it will pass on the movements to FI9831w.pm in ``/usr/share/perl5/ZoneMinder/Control/`` + +* ZoneMinder also maintains protocol configuration parameters in a table called ``Controls`` in the DB. This table is used to store parameters like whether the camera supports continuous move, zoom etc. + +* The ``Controls`` table is used by ZoneMinder to build its PTZ web interface. For example, an FI9831W camera does not support Zoom --> so when you open the PTZ interface of ZoneMinder via the Web Console and navigate to the FI9831W camera, the Zoom option will not be shown. It knows not to show this because the ``Control`` table entry for FI9831W specifies it does not support Zoom. Note that you edit these parameters via Source->Control->Control Type->Edit in the web console + +* If you ever look at any of the control protocol files, you will notice it has functions like ``moveRelUp`` or ``moveConLeft`` etc. -> these are the functions that eventually get invoked to move the camera around and it is expected that contributors who implement missing camera profiles fill in these functions with the appropriate camera specific commands. This way, the core ZoneMinder code does not need to worry about camera specific commands. All it needs to know is the features of a camera and accordinfly invoke abstract commands in the protocol perl file and it is the responsibility of the perl file for that camera to implement the specifics. So, if you are facing problems with PTZ not working, these protocol files are what you should be debugging. + + Control Capabilities ^^^^^^^^^^^^^^^^^^^^ If you have a camera that supports PTZ controls and wish to use it with ZoneMinder then the first thing you need to do is ensure that it has an accurate entry in the capabilities table. To do this you need to go to the Control tab of the Monitor configuration dialog and select ‘Edit’ where it is listed by the Control Type selection box. This will bring up a new window which lists, with a brief summary, the existing capabilities. To edit an existing capability to modify select the Id or Name of the capability in question, or click on the Add button to add a new control capability. Either of these approaches will create a new window, in familiar style, with tabs along the top and forms fields below. In the case of the capabilities table there are a large number of settings and tabs, the mean and use of these are briefly explained below. + + Main Tab -------- Name diff --git a/docs/userguide/components.rst b/docs/userguide/components.rst index 693f437d8..48aff0be4 100644 --- a/docs/userguide/components.rst +++ b/docs/userguide/components.rst @@ -3,6 +3,12 @@ Components ZoneMinder is not a single monolithic application but is formed from several components. These components primarily include executable compiled binaries which do the main video processing work, perl scripts which usually perform helper and/or external interface tasks and php web scripts which are used for the web interface. +System Overview +---------------- +Depicted below is a high level diagram of the ZoneMinder system with key components + +.. image:: images/zm-system-overview.jpg + A brief description of each of the principle components follows. Binaries @@ -20,16 +26,13 @@ Binaries PHP --- -As well as this there are the web PHP files in the web directory. Currently these consist of 4 possible skins. +As well as this there are the web PHP files in the web directory. Currently these consist of a single skin with Classic and Flat styles. **Classic** Original ZoneMinder skin **Flat** - An updated version of classic skin, retaining the same layout with a more modern style -**XMl** - Used by eyeZM as an interfacing skin -**Mobile** - [Check status and purpose] + An updated version of Classic skin, retaining the same layout with a more modern style. Originally a skin this is now just a CSS style. + Perl ---- diff --git a/docs/userguide/controlmonitor.rst b/docs/userguide/controlmonitor.rst deleted file mode 100644 index d3acb5bdb..000000000 --- a/docs/userguide/controlmonitor.rst +++ /dev/null @@ -1,8 +0,0 @@ -Controlling Monitors -==================== - -If you have defined your system as having controllable monitors and you are looking at a monitor that is configured for control, then clicking on the ‘Control’ link along the top of the window will change the short event listing area to a control area. The capabilities you have defined earlier determine exactly what is displayed in this window. Generally you will have a Pan/Tilt control area along with one or subsidiary areas such as zoom or focus control to the side. If you have preset support then these will be near the bottom of the window. The normal method of controlling the monitor is by clicking on the appropriate graphics which then send a command via the control script to the camera itself. This may sometimes take a noticeable delay before the camera responds. - -It is usually the case that the control arrows are sensitive to where you click on them. If you have a camera that allows different speeds to be used for panning or zooming etc then clicking near the point of the arrow will invoke the faster speed whilst clicking near the base of the arrow will be slower. If you have defined continuous motion then ongoing activities can be stopped by clicking on the area between the arrows, which will either be a graphic in the case of pan/tilt controls or a word in the case of zoom and focus controls etc. - -Certain control capabilities such as mapped motion allow direct control by clicking on the image itself when used in browsers which support streamed images directly. Used in this way you can just click on the area of the image that interests you and the camera will centre on that spot. You can also use direct image control for relative motion when the area of the image you click on defines the direction and the distance away from the centre of the image determines the speed. As it is not always very easy to estimate direction near the centre of the image, the active area does not start until a short distance away from the centre, resulting in a ‘dead’ zone in the middle of the image. diff --git a/docs/userguide/definemonitor.rst b/docs/userguide/definemonitor.rst index 07180dc36..d69b44425 100644 --- a/docs/userguide/definemonitor.rst +++ b/docs/userguide/definemonitor.rst @@ -3,7 +3,15 @@ Defining Monitors To use ZoneMinder properly you need to define at least one Monitor. Essentially, a monitor is associated with a camera and can continually check it for motion detection and such like. -There are a small number of camera setups that ZoneMinder knows about and which can be accessed by clicking on the ‘Presets’ link. Selecting one of the presets will fill in the monitor configuration with appropriate values but you will still need to enter others and confirm the preset settings. +You can access the monitor window by clicking on the "Add New Monitor" button, or by clicking on the "Source" column of a predefined monitor. + +.. image:: images/definemonitor-monitor.png + :width: 600px + +There are a small number of camera setups that ZoneMinder knows about and which can be accessed by clicking on the ‘Presets’ link. Selecting one of the presets will fill in the monitor configuration with appropriate values but you will still need to enter others and confirm the preset settings. Here is an example of the presets window: + +.. image:: images/definemonitor-preset.png + :width: 600px The options are divided into a set of tabs to make it easier to edit. You do not have to ‘save’ to change to different tab so you can make all the changes you require and then click ‘Save’ at the end. The individual options are explained in a little more detail below, @@ -13,18 +21,21 @@ Monitor Tab Name The name for your monitor. This should be made up of alphanumeric characters (a-z,A-Z,0-9) and hyphen (-) and underscore(_) only. Whitespace is not allowed. +Server + Multi-Server implementation allows the ability to define multiple ZoneMinder servers sharing a single database. When servers are configured this setting allows you nominate the server for each monitor. + Source Type This determines whether the camera is a local one attached to a physical video or USB port on your machine, a remote network camera or an image source that is represented by a file (for instance periodically downloaded from a alternate location). Choosing one or the other affects which set of options are shown in the Source tab. Function This essentially defines what the monitor is doing. This can be one of the following; - None – The monitor is currently disabled. No streams can be viewed or events generated. Nothing is recorded. - Monitor – The monitor is only available for live streaming. No image analysis is done so no alarms or events will be generated, and nothing will be recorded. - Modect – or MOtion DEteCTtion. All captured images will be analysed and events generated with recorded video where motion is detected. - Record – The monitor will be continuously recorded. Events of a fixed-length will be generated regardless of motion, analogous to a conventional time-lapse video recorder. No motion detection takes place in this mode. - Mocord – The monitor will be continuously recorded, with any motion being highlighted within those events. - Nodect – or No DEteCTtion. This is a special mode designed to be used with external triggers. In Nodect no motion detection takes place but events are recorded if external triggers require it. + * None – The monitor is currently disabled. No streams can be viewed or events generated. Nothing is recorded. + * Monitor – The monitor is only available for live streaming. No image analysis is done so no alarms or events will be generated, and nothing will be recorded. + * Modect – or MOtion DEteCTtion. All captured images will be analysed and events generated with recorded video where motion is detected. + * Record – The monitor will be continuously recorded. Events of a fixed-length will be generated regardless of motion, analogous to a conventional time-lapse video recorder. No motion detection takes place in this mode. + * Mocord – The monitor will be continuously recorded, with any motion being highlighted within those events. + * Nodect – or No DEteCTtion. This is a special mode designed to be used with external triggers. In Nodect no motion detection takes place but events are recorded if external triggers require it. Generally speaking it is best to choose ‘Monitor’ as an initial setting here. @@ -169,12 +180,12 @@ Note: This tab and its options will only appear if you have indicated that your X10 Activation String The contents of this field determine when a monitor starts and/or stops being active when running in ‘Triggered; mode and with X10 triggers. The format of this string is as follows, - n : If you simply enter a number then the monitor will be activated when an X10 ON signal for that unit code is detected and will be deactivated when an OFF signal is detected. - !n : This inverts the previous mode, e.g. !5 means that the monitor is activated when an OFF signal for unit code 5 is detected and deactivated by an ON. - n+ : Entering a unit code followed by + means that the monitor is activated on receipt of a ON signal for that unit code but will ignore the OFF signal and as such will not be deactivated by this instruction. If you prepend a '!' as per the previous definition it similarly inverts the mode, i.e. the ON signal deactivates the monitor. - n+ : As per the previous mode except that the monitor will deactivate itself after the given number of seconds. - n- : Entering a unit code followed by - means that the monitor is deactivated on receipt of a OFF signal for that unit code but will ignore the ON signal and as such will not be activated by this instruction. If you prepend a '!' as per the previous definition it similarly inverts the mode, i.e. the OFF signal activates the monitor. - n- : As per the previous mode except that the monitor will activate itself after the given number of seconds. + * n : If you simply enter a number then the monitor will be activated when an X10 ON signal for that unit code is detected and will be deactivated when an OFF signal is detected. + * !n : This inverts the previous mode, e.g. !5 means that the monitor is activated when an OFF signal for unit code 5 is detected and deactivated by an ON. + * n+ : Entering a unit code followed by + means that the monitor is activated on receipt of a ON signal for that unit code but will ignore the OFF signal and as such will not be deactivated by this instruction. If you prepend a '!' as per the previous definition it similarly inverts the mode, i.e. the ON signal deactivates the monitor. + * n+ : As per the previous mode except that the monitor will deactivate itself after the given number of seconds. + * n- : Entering a unit code followed by - means that the monitor is deactivated on receipt of a OFF signal for that unit code but will ignore the ON signal and as such will not be activated by this instruction. If you prepend a '!' as per the previous definition it similarly inverts the mode, i.e. the OFF signal activates the monitor. + * n- : As per the previous mode except that the monitor will activate itself after the given number of seconds. You can also combine several of these expressions to by separating them with a comma to create multiple circumstances of activation. However for now leave this blank. diff --git a/docs/userguide/definezone.rst b/docs/userguide/definezone.rst index dd754ab40..0cacd3b91 100644 --- a/docs/userguide/definezone.rst +++ b/docs/userguide/definezone.rst @@ -1,7 +1,9 @@ Defining Zones ============== -The next important thing to do with a new monitor is set up Zones for it to use. By default you'll already have one generated for you when you created your monitor but you might want to modify it or add others. Click on the Zones column for your monitor and you should see a small popup window appear which contains an image from your camera overlain with a stippled pattern representing your zone. In the default case this will cover the whole image. The colour of the zones appearing here is determined by what type they are. The default zone is Active and so will be red, Inclusive zones are orange, exclusive zones are purple, preclusive zones are blue and inactive zones are white. +The next important thing to do with a new monitor is set up Zones for it to use. By default you'll already have one generated for you when you created your monitor (the default zone is the full area captured by the monitor) but you might want to modify it or add others. + +Click on the Zones column for your monitor and you should see a small popup window appear which contains an image from your camera overlain with a stippled pattern representing your zone. In the default case this will cover the whole image. The colour of the zones appearing here is determined by what type they are. The default zone is Active and so will be red, Inclusive zones are orange, exclusive zones are purple, preclusive zones are blue and inactive zones are white. Beneath the zones image will be a table containing a listing of your zones. Clicking on either the relevant bit of the image or on the Id or Name in the table will bring up another window where you can edit the particulars for your Zones. For more information on defining or editing a zone, see Defining Zones. @@ -15,7 +17,7 @@ Name Each Zone can be named for reference purposes. It is used for logging and debugging. Choose a name that helps you identify your zones. Type - This is one of the more important concepts in ZoneMinder and there are five to choose from. + This is one of the more important concepts in ZoneMinder and there are six to choose from. * Active Triggers an alarm when motion is detected within it. This is the zone type you'll use most often, and which will be set for your default zone. Only Active and Exclusive zones can trigger an alarm. @@ -30,7 +32,10 @@ Type This zone type is relatively recent. It is called a Preclusive zone because if it is triggered it actually precludes an alarm being generated for that image frame. So motion or other changes that occur in a Preclusive zone will have the effect of ensuring that no alarm occurs at all. The application for this zone type is primarily as a shortcut for detecting general large-scale lighting or other changes. Generally this may be achieved by limiting the maximum number of alarm pixels or other measure in an Active zone. However in some cases that zone may cover an area where the area of variable illumination occurs in different places as the sun and/or shadows move and it thus may be difficult to come up with general values. Additionally, if the sun comes out rapidly then although the initial change may be ignored in this way as the reference image catches up an alarm may ultimately be triggered as the image becomes less different. Using one or more Preclusive zones offers a different approach. Preclusive zones are designed to be fairly small, even just a few pixels across, with quite low alarm thresholds. They should be situated in areas of the image that are less likely to have motion occur such as high on a wall or in a corner. Should a general illumination change occur they would be triggered at least as early as any Active zones and prevent any other zones from generating an alarm. Obviously careful placement is required to ensure that they do not cancel any genuine alarms or that they are not so close together that any motion just hops from one Preclusive zone to another. Preclusive zones may also be used to reduce processing time by situating one over an Active zone. The Preclusive zone is processed first; if it is small, and is triggered, the rest of the zone/image will not be processed. * Inactive - Suppresses the detection of motion within it. This can be layered on top of any other zone type, preventing motion within the Inactive zone from being effective for any other zone type. Use inactive zones to cover areas in which nothing notable will ever happen or where you get false alarms that don't relate to what you are trying to monitor. Inactive zones may be overlaid on other zones to blank out areas, and are processed first. As a general practice, you should try and make zones abut each other instead of overlapping to avoid repeated duplicate processing of the same area. + Suppresses the detection of motion within it. This can be layered on top of any other zone type, preventing motion within the Inactive zone from being effective for any other zone type. Use inactive zones to cover areas in which nothing notable will ever happen or where you get false alarms that don't relate to what you are trying to monitor. Inactive zones may be overlaid on other zones to blank out areas, and are processed first (with the exception of Privacy zones, see below). As a general practice, you should try and make zones abut each other instead of overlapping to avoid repeated duplicate processing of the same area. + + * Privacy + Blackens the pixels within it. This can be used if you want to hide some regions in the image if the situation does not allow another solution. This zone type is different to all the others in that it gets processed as soon as possible during capture (even before the timestamp gets into the image) and not in the analyzing process. So if you add, change or delete a Privacy zone, you don't see the changes in the image until the capture process gets restarted. This will be done automatically, but needs a few seconds. Preset The preset chooser sets sensible default values based on computational needs (fast v. best) and sensitivity (low, medium, high.) It is not required that you select a preset, and you can alter any of the parameters after choosing a preset. For a small number of monitors with ZoneMinder running on modern equipment, Best, high sensitivity can be chosen as a good starting point. @@ -40,7 +45,11 @@ Units * Percentage - Selecting this option will allow may of the following values to be entered (or viewed) as a percentage. The sense of the percentage values refers to the area of the zone and not the image as a whole. This makes trying to work out necessary sizes rather easier. Region points - [[File:Zone - Region sample.jpg|frame|right|The sample region shown to the right shows a region defined by 6 control points. The shape of the region causes the check methods to ignore the sidewalk and areas of the porch wall that receive changing sunlight; two conditions that are not of interest in this zone.]] + +.. image:: images/define-zone-region-sample.jpg + +The sample region shown to the right shows a region defined by 6 control points. The shape of the region causes the check methods to ignore the sidewalk and areas of the porch wall that receive changing sunlight; two conditions that are not of interest in this zone. + A region is a part of the captured image that is of interest for this zone. By default, a region is configured to cover the whole captured image. Depending on the selected type of this zone, the shape of the region can be adjusted to accommodate multiple effects. This can be done by dragging the control points in the reference image around, or by altering the coordinates found in the controls below the reference image. Clicking on a control point in the reference image highlights the coordinates in the table below. Clicking the + button in a point row adds a control point between this point and the next; clicking the - button removes this control point. It is possible to accidentally place a control point outside of the valid coordinates of the image. This will prevent the monitor from working properly. You can make zones almost any shape you like; except that zones may not self-intersect (i.e. edges crossing over each other). Alarm Colour @@ -71,8 +80,13 @@ Min/Max Filtered Area Applying the filtering analysis results in an area that is less than or equal to the alarmed area. Thus the minimum and maximum filtered area parameters for alarm should be equal to or less than the corresponding alarm area parameters, or the FilteredPixels analysis will never trigger an alarm. In particular, it is useful to raise the minimum alarmed area parameter until false events from image artifacts disappear, and setting a minimum filtered area parameter less the minimum alarmed area parameter by enough to capture small events of interest. Blobs - File:Zone - 1 blob example.jpg|frame|right|This captured frame shows an image with 1 identified blob. The blob is outlined in the Alarm Colour specified above. - When two or more Filtered areas touch or share a boundary, it is sensible to evaluate the regions as one contiguous area instead of separate entities. A Blob is a contiguous area made up of multiple filtered areas. Whereas FilteredPixes is useful for excluding parts of the image that are not part of the actual scene, Blob filtering is better suited to disregarding areas of the actual scene that are not of interest. + +.. image:: images/define-zone-blob.jpg + +This image shows an image with 1 identified blob. The blob is outlined in the Alarm Colour specified above. + +When two or more Filtered areas touch or share a boundary, it is sensible to evaluate the regions as one contiguous area instead of separate entities. A Blob is a contiguous area made up of multiple filtered areas. Whereas FilteredPixes is useful for excluding parts of the image that are not part of the actual scene, Blob filtering is better suited to disregarding areas of the actual scene that are not of interest. + Selecting the Blobs Alarm Check Method opens up all of the available parameters. Enabling Blobs adds one more layer of analysis to the AlarmedPixel and FilteredPixel checks in the determination of a valid alarm along along with 2 additional parameter categories for tuning: the size of the blobs, and the number of blobs. A Blob is not necessarily the whole object that may be of interest. In the example image, the subject is moving, but only a portion of him is marked as a blob. This is because as the subject moves, many pixels of the image do not change in value beyond the set threshold. A pixel that is representing the subject's shoulder in one frame may be representing his back in the next, however, the value of the pixel remains nearly the same. Min/Max Blob Area @@ -88,3 +102,7 @@ Overload Frame Ignore Count * Number of Blobs > Max Blobs The idea is that after a change like a light going on that is considered too big to count as an alarm, it could take a couple of frames for things to settle down again. +Other information +----------------- +Refer to `this `__ user contributed Zone guide for additional information will illustrations if you are new to zones and need more help. + diff --git a/docs/userguide/filterevents.rst b/docs/userguide/filterevents.rst index 05f05c5e7..bc0dcffe2 100644 --- a/docs/userguide/filterevents.rst +++ b/docs/userguide/filterevents.rst @@ -1,54 +1,112 @@ Filtering Events ================ -The other columns on the main console window contain various event totals for your monitors over the last hour, day, week and month as well as a grand total and a total for events that you may have archived for safekeeping. Clicking on one of these totals or on the 'All' or 'Archive' links from the monitor window described above will present you with a new display. This is the full event window and contains a list of events selected according to a filter which will also pop up in its own window. Thus if you clicked on a 'day' total the filter will indicate that this is the period for which events are being filtered. The event listing window contains a similar listing to the recent events in the monitor window. The primary differences are that the frames and alarm frames and the score and maximum score are now broken out into their own columns, all of which can be sorted by clicking on the heading. Also this window will not refresh automatically, rather only on request. Other than that, you can choose to view events here or delete them as before. +Filters allow you to define complex conditions with associated actions in ZoneMinder. Examples could include: -The other window that appeared is a filter window. You can use this window to create your own filters or to modify existing ones. You can even save your favourite filters to re-use at a future date. Filtering itself is fairly simple; you first choose how many expressions you'd like your filter to contain. Changing this value will cause the window to redraw with a corresponding row for each expression. You then select what you want to filter on and how the expressions relate by choosing whether they are 'and' or 'or' relationships. For filters comprised of many expressions you will also get the option to bracket parts of the filter to ensure you can express it as desired. Then if you like choose how you want your results sorted and whether you want to limit the amount of events displayed. +* Send an email each time a new event occurs for a specific monitor +* Delete events that are more than 10 days old -There are several different elements to an event that you can filter on, some of which require further explanation. These are as follows, 'Date/Time' which must evaluate to a date and a time together, 'Date' and 'Time' which are variants which may only contain the relevant subsets of this, 'Weekday' which as expected is a day of the week. +And many more. -All of the preceding elements take a very flexible free format of dates and time based on the PHP strtotime function (http://www.php.net/manual/en/function.strtotime.php). This allows values such as 'last Wednesday' etc to be entered. I recommend acquainting yourself with this function to see what the allowed formats are. However automated filters are run in perl and so are parsed by the Date::Manip package. Not all date formats are available in both so if you are saved your filter to do automatic deletions or other tasks you should make sure that the date and time format you use is compatible with both methods. The safest type of format to use is ‘-3 day’ or similar with easily parseable numbers and units are in English. +The filter window can be accessed from various views, one of which is to simply tap on the filter button in the main web view: -The other things you can filter on are all fairly self explanatory, except perhaps for 'Archived' which you can use to include or exclude Archived events. In general you'll probably do most filtering on un-archived events. There are also two elements, Disk Blocks and Disk Percent which don’t directly relate to the events themselves but to the disk partition on which the events are stored. These allow you to specify an amount of disk usage either in blocks or in percentage as returned by the ‘df’ command. They relate to the amount of disk space used and not the amount left free. Once your filter is specified, clicking 'submit' will filter the events according to your specification. As the disk based elements are not event related directly if you create a filter and include the term ‘DiskPercent > 95’ then if your current disk usage is over that amount when you submit the filter then all events will be listed whereas if it is less then none at all will. As such the disk related terms will tend to be used mostly for automatic filters (see below). If you have created a filter you want to keep, you can name it and save it by clicking 'Save'. +.. image:: images/filter-button.png + :width: 600px -If you do this then the subsequent dialog will also allow you specify whether you want this filter automatically applied in order to delete events or upload events via ftp to another server and mail notifications of events to one or more email accounts. Emails and messages (essentially small emails intended for mobile phones or pagers) have a format defined in the Options screen, and may include a variety of tokens that can be substituted for various details of the event that caused them. This includes links to the event view or the filter as well as the option of attaching images or videos to the email itself. Be aware that tokens that represent links may require you to log in to access the actual page, and sometimes may function differently when viewed outside of the general ZoneMinder context. The tokens you can use are as follows. +You can use the filter window to create your own filters or to modify existing ones. You can even save your favourite filters to re-use at a future date. Filtering itself is fairly simple; you first choose how many expressions you'd like your filter to contain. Changing this value will cause the window to redraw with a corresponding row for each expression. You then select what you want to filter on and how the expressions relate by choosing whether they are 'and' or 'or' relationships. For filters comprised of many expressions you will also get the option to bracket parts of the filter to ensure you can express it as desired. Then if you like choose how you want your results sorted and whether you want to limit the amount of events displayed. -: %EI% Id of the event -: %EN% Name of the event -: %EC% Cause of the event -: %ED% Event description -: %ET% Time of the event -: %EL% Length of the event -: %EF% Number of frames in the event -: %EFA% Number of alarm frames in the event -: %EST% Total score of the event -: %ESA% Average score of the event -: %ESM% Maximum score of the event -: %EP% Path to the event -: %EPS% Path to the event stream -: %EPI% Path to the event images -: %EPI1% Path to the first alarmed event image -: %EPIM% Path to the (first) event image with the highest score -: %EI1% Attach first alarmed event image -: %EIM% Attach (first) event image with the highest score -: %EV% Attach event mpeg video -: %MN% Name of the monitor -: %MET% Total number of events for the monitor -: %MEH% Number of events for the monitor in the last hour -: %MED% Number of events for the monitor in the last day -: %MEW% Number of events for the monitor in the last week -: %MEM% Number of events for the monitor in the last month -: %MEA% Number of archived events for the monitor -: %MP% Path to the monitor window -: %MPS% Path to the monitor stream -: %MPI% Path to the monitor recent image -: %FN% Name of the current filter that matched -: %FP% Path to the current filter that matched -: %ZP% Path to your ZoneMinder console -Finally you can also specify a script which is run on each matched event. This script should be readable and executable by your web server user. It will get run once per event and the relative path to the directory containing the event in question. Normally this will be of the form / so from this path you can derive both the monitor name and event id and perform any action you wish. Note that arbitrary commands are not allowed to be specified in the filter, for security the only thing it may contain is the full path to an executable. What that contains is entirely up to you however. +Here is what the filter window looks like -Filtering is a powerful mechanism you can use to eliminate events that fit a certain pattern however in many cases modifying the zone settings will better address this. Where it really comes into its own is generally in applying time filters, so for instance events that happen during weekdays or at certain times of the day are highlighted, uploaded or deleted. Additionally using disk related terms in your filters means you can automatically create filters that delete the oldest events when your disk gets full. Be warned however that if you use this strategy then you should limit the returned results to the amount of events you want deleted in each pass until the disk usage is at an acceptable level. If you do not do this then the first pass when the disk usage is high will match, and then delete, all events unless you have used other criteria inside of limits. ZoneMinder ships with a sample filter already installed, though disabled. The PurgeWhenFull filter can be used to delete the oldest events when your disk starts filling up. To use it you should select and load it in the filter interface, modify it to your requirements, and then save it making you sure you check the ‘Delete all matches’ option. This will then run in the background and ensure that your disk does not fill up with events. +.. image:: images/filter-filterview.png + :width: 800px + +* *A*: This is a dropdown list where you can select pre-defined filters. You will notice that ZoneMinder comes with a PurgeWhenFull filter that is configured to delete events if you reach 95% of disk space. +* *B* and *C*: This is where you specify conditions that need to match before the filter is executed. You use the "+" and "-" buttons to add/delete conditions +* *D*: This is where you specify what needs to happen when the conditions match: + * Archive all matches: sets the archive field to 1 in the Database for the matched events. + Think of 'archiving' as grouping them under a special category - you can view archived + events later and also make sure archived events don't get deleted, for example + * Email details of all matches: Sends an email to the configured address with details about the event. + The email can be customized as per TBD + * Execute command on all matches: Allows you to execute any arbitrary command on the matched events + * Delete all matches: Deletes all the matched events +* *E*: Use 'Submit' to 'test' your matching conditions. This will just match and show you what filters match. Use 'Execute' to actually execute the action after matching your conditions. Use 'Save' to save the filter for future use and 'Reset' to clear your settings + +.. NOTE:: More details on filter conditions: + + There are several different elements to an event that you can filter on, some of which require further explanation. These are as follows, + * 'Date/Time' which must evaluate to a date and a time together, + * 'Date' and 'Time' which are variants which may only contain the relevant subsets of this, + * 'Weekday' which as expected is a day of the week. + + All of the preceding elements take a very flexible free format of dates and time based on the PHP strtotime function (http://www.php.net/manual/en/function.strtotime.php). This allows values such as 'last Wednesday' etc to be entered. We recommend acquainting yourself with this function to see what the allowed formats are. However automated filters are run in perl and so are parsed by the Date::Manip package. Not all date formats are available in both so if you are saved your filter to do automatic deletions or other tasks you should make sure that the date and time format you use is compatible with both methods. The safest type of format to use is ‘-3 day’ or similar with easily parseable numbers and units are in English. + + The other things you can filter on are all fairly self explanatory, except perhaps for 'Archived' which you can use to include or exclude Archived events. In general you'll probably do most filtering on un-archived events. There are also two elements, Disk Blocks and Disk Percent which don’t directly relate to the events themselves but to the disk partition on which the events are stored. These allow you to specify an amount of disk usage either in blocks or in percentage as returned by the ‘df’ command. They relate to the amount of disk space used and not the amount left free. Once your filter is specified, clicking 'submit' will filter the events according to your specification. As the disk based elements are not event related directly if you create a filter and include the term ‘DiskPercent > 95’ then if your current disk usage is over that amount when you submit the filter then all events will be listed whereas if it is less then none at all will. As such the disk related terms will tend to be used mostly for automatic filters (see below). If you have created a filter you want to keep, you can name it and save it by clicking 'Save'. + + If you do this then the subsequent dialog will also allow you specify whether you want this filter automatically applied in order to delete events or upload events via ftp to another server and mail notifications of events to one or more email accounts. Emails and messages (essentially small emails intended for mobile phones or pagers) have a format defined in the Options screen, and may include a variety of tokens that can be substituted for various details of the event that caused them. This includes links to the event view or the filter as well as the option of attaching images or videos to the email itself. Be aware that tokens that represent links may require you to log in to access the actual page, and sometimes may function differently when viewed outside of the general ZoneMinder context. The tokens you can use are as follows. + + * %EI% Id of the event + * %EN% Name of the event + * %EC% Cause of the event + * %ED% Event description + * %ET% Time of the event + * %EL% Length of the event + * %EF% Number of frames in the event + * %EFA% Number of alarm frames in the event + * %EST% Total score of the event + * %ESA% Average score of the event + * %ESM% Maximum score of the event + * %EP% Path to the event + * %EPS% Path to the event stream + * %EPI% Path to the event images + * %EPI1% Path to the first alarmed event image + * %EPIM% Path to the (first) event image with the highest score + * %EI1% Attach first alarmed event image + * %EIM% Attach (first) event image with the highest score + * %EV% Attach event mpeg video + * %MN% Name of the monitor + * %MET% Total number of events for the monitor + * %MEH% Number of events for the monitor in the last hour + * %MED% Number of events for the monitor in the last day + * %MEW% Number of events for the monitor in the last week + * %MEM% Number of events for the monitor in the last month + * %MEA% Number of archived events for the monitor + * %MP% Path to the monitor window + * %MPS% Path to the monitor stream + * %MPI% Path to the monitor recent image + * %FN% Name of the current filter that matched + * %FP% Path to the current filter that matched + * %ZP% Path to your ZoneMinder console + + Finally you can also specify a script which is run on each matched event. This script should be readable and executable by your web server user. It will get run once per event and the relative path to the directory containing the event in question. Normally this will be of the form / so from this path you can derive both the monitor name and event id and perform any action you wish. Note that arbitrary commands are not allowed to be specified in the filter, for security the only thing it may contain is the full path to an executable. What that contains is entirely up to you however. + + Filtering is a powerful mechanism you can use to eliminate events that fit a certain pattern however in many cases modifying the zone settings will better address this. Where it really comes into its own is generally in applying time filters, so for instance events that happen during weekdays or at certain times of the day are highlighted, uploaded or deleted. Additionally using disk related terms in your filters means you can automatically create filters that delete the oldest events when your disk gets full. Be warned however that if you use this strategy then you should limit the returned results to the amount of events you want deleted in each pass until the disk usage is at an acceptable level. If you do not do this then the first pass when the disk usage is high will match, and then delete, all events unless you have used other criteria inside of limits. ZoneMinder ships with a sample filter already installed, though disabled. The PurgeWhenFull filter can be used to delete the oldest events when your disk starts filling up. To use it you should select and load it in the filter interface, modify it to your requirements, and then save it making you sure you check the ‘Delete all matches’ option. This will then run in the background and ensure that your disk does not fill up with events. + + +Saving filters +----------------- + +.. image:: images/filter-save.png + :width: 400px + +When saving filters, if you want the filter to run in the background make sure you select the "Run filter in background" option. When checked, ZoneMinder will make sure the filter is checked regularly. For example, if you want to be notified of new events by email, you should make sure this is checked. Filters that are configured to run in the background have a "*" next to it. + +For example: + +.. image:: images/filter-choosefilter.png + :width: 400px + +How filters actually work +-------------------------- +It is useful to know how filters actually work behind the scenes in ZoneMinder, in the event you find your filter not functioning as intended: + +* the primary filter processing process in ZoneMinder is a perl file called ``zmfilter.pl`` +* zmfilter.pl runs every FILTER_EXECUTE_INTERVAL seconds (default is 20s, can be changed in Options->System) +* in each run, it goes through all the filters which are marked as "Run in Background" and if the conditions match performs the specified action +* zmfilter.pl also reloads all the filters every FILTER_RELOAD_DELAY seconds (default is 300s/5mins, can be changed in Options->System) + * So if you have just created a new filter, zmfilter will not see it till the next FILTER_RELOAD_DELAY cycle + * This is also important if you are using "relative times" like 'now' - see :ref:`relative_caveat` Relative items in date strings @@ -56,11 +114,12 @@ Relative items in date strings Relative items adjust a date (or the current date if none) forward or backward. The effects of relative items accumulate. Here are some examples: +:: -1 year -1 year ago -3 years -2 days +* 1 year +* 1 year ago +* 3 years +* 2 days The unit of time displacement may be selected by the string ‘year’ or ‘month’ for moving by whole years or months. These are fuzzy units, as years and months are not all of equal duration. More precise units are ‘fortnight’ which is worth 14 days, ‘week’ worth 7 days, ‘day’ worth 24 hours, ‘hour’ worth 60 minutes, ‘minute’ or ‘min’ worth 60 seconds, and ‘second’ or ‘sec’ worth one second. An ‘s’ suffix on these units is accepted and ignored. @@ -75,11 +134,18 @@ When a relative item causes the resulting date to cross a boundary where the clo The fuzz in units can cause problems with relative items. For example, ‘2003-07-31 -1 month’ might evaluate to 2003-07-01, because 2003-06-31 is an invalid date. To determine the previous month more reliably, you can ask for the month before the 15th of the current month. For example: +:: + $ date -R + Thu, 31 Jul 2003 13:02:39 -0700 + $ date --date='-1 month' +'Last month was %B?' + Last month was July? + $ date --date="$(date +%Y-%m-15) -1 month" +'Last month was %B!' + Last month was June! @@ -98,3 +164,24 @@ You use "less than" to indicate that you want to match events before the specifi You should always test your filters before enabling any actions based on them to make sure they consistently return the results you want. You can use the submit button to see what events are returned by your query. + +.. _relative_caveat: + +Caveat with Relative items +-------------------------- + +One thing to remember if you specify relative dates like "now" or "1 minute ago", etc, they are converted to a specific date and time by Zoneminder's filtering process (zmfilter.pl) when the filters are loaded. They are _NOT_ recomputed each time the filter runs. Filters are re-loaded depending on the value specified by FILTER_RELOAD_DELAY variable in the Zoneminder Web Console->Options->System + +This may cause confusion in the following cases, for example: +Let's say a user specifies that he wants to be notified of events via email the moment the event "DateTime" is "less than" "now" as a filter criteria. When the filter first gets loaded by zmfilter.pl, this will translate to "Match events where Start Time < " + localtime() where local time is the time that is resolved when this filter gets loaded. Now till the time the filter gets reloaded after FILTER_RELOAD_DELAY seconds (which is usually set to 300 seconds, or 5 minutes), that time does not get recomputed, so the filter will not process any new events that occur after that computed date till another 5 minutes, which is probably not what you want. + +Troubleshooting tips +-------------------- + +If your filter is not working, here are some useful tips: + +* Look at Info and Debug logs in Zoneminder +* Run ``sudo zmfilter.pl -f `` from command line and see the log output +* Check how long your action is taking - zmfilter.pl will wait for the action to complete before it checks again +* If you are using relative times like 'now' or '1 year ago' etc. remember that zmfilter converts that relative time to an absolute date only when it reloads filters, which is dictated by the FILTER_RELOAD_DELAY duration. So, for example, if you are wondering why your events are not being detected before intervals of 5 minutes and you have used such a relative condition, this is why +* In the event that you see your new filter is working great when you try it out from the Web Console (using the Submit or Execute button) but does not seem to work when its running in background mode, you might have just chanced upon a compatibility issue between how Perl and PHP translate free form text to dates/times. When you test it via the "Submit" or "Execute" button, you are invoking a PHP function for time conversion. When the filter runs in background mode, zmfilter.pl calls a perl equivalent function. In some cases, depending on the version of Perl and PHP you have, the results may vary. If you face this situation, the best thing to do is to run ``sudo zmfilter.pl -f `` from a terminal to make sure the filter actually works in Perl as well. diff --git a/docs/userguide/gettingstarted.rst b/docs/userguide/gettingstarted.rst index 8ae629059..b2e92f63f 100644 --- a/docs/userguide/gettingstarted.rst +++ b/docs/userguide/gettingstarted.rst @@ -3,6 +3,148 @@ Getting Started Having followed the :doc:`/installationguide/index` for your distribution you should now be able to load the ZoneMinder web frontend. By default this will be with the Classic skin, below is an example of the page you should now see. -To add cameras to the system you need to create a Monitor for each camera. Click 'Add New Monitor' to bring up the dialog. +.. image:: ../installationguide/images/zm_first_screen_post_install.png -[To be completed, removing tutorial style documentation from wiki documentation and putting first time user guide here] + +Enabling Authentication +^^^^^^^^^^^^^^^^^^^^^^^ +We strongly recommend enabling authentication right away. There are some situations where certain users don't enable authentication, such as instances where the server is in a LAN not directly exposed to the Internet, and is only accessible via VPN etc., but in most cases, authentication should be enabled. So let's do that right away. + +* Click on the Options link on the top right corner of the web interface +* You will now be presented with a screen full of options. Click on the "System" tab + +.. image:: images/getting-started-enable-auth.png + +* The relevant portions to change are marked in red above +* Enable OPT_USE_ATH - this automatically switches to authentication mode with a default user (more on that later) +* Select a random string for AUTH_HASH_SECRET - this is used to make the authentication logic more secure, so + please generate your own string and please don't use the same value in the example. +* The other options highlighed above should already be set, but if not, please make sure they are + +* Click on Save at the bottom and that's it! The next time you refresh that page, you will now be presented with a login screen. Job well done! + +.. image:: images/getting-started-login.png + +.. NOTE:: The default login/password is "admin/admin" + + +Switching to flat theme +^^^^^^^^^^^^^^^^^^^^^^^ +What you see is what is called a "classic" skin. Zoneminder has a host of configuration options that you can customize over time. This guide is meant to get you started the easiest possible way, so we will not go into all the details. However, it is worthwhile to note that Zoneminder also has a 'flat' theme that depending on your preferences may look more modern. So let's use that as an example of introducing you to the Options menu + +* Click on the Options link on the top right of the web interface in the image above +* This will bring you to the options window as shown below. Click on the "System" tab and then select the + "flat" option for CSS_DEFAULT as shown below + +.. image:: images/getting-started-flat-css.png + +* Click Save at the bottom + +Now, switch to the "Display" tab and also select "Flat" there like so: + +.. image:: images/getting-started-flat-css-2.png + +Your screen will now look like this: + + +Congratulations! You now have a modern looking interface. + +.. image:: images/getting-started-modern-look.png + +Understanding the Web Console +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Before we proceed, lets spend a few minutes understanding the key functions of the web console. +For the sake of illustration, we are going to use a populated zoneminder configuration with several monitors and events. +Obviously, this does not reflect your current web console - which is essentially void of any useful information till now, +as we are yet to add things. Let's take a small break and understand what the various functions are before we configure our +own empty screen. + +.. image:: images/getting-started-understand-console.png + +* **A**: This is the username that is logged in. You are logged in as 'admin' here +* **B**: Click here to explore the various options of ZoneMinder and how to configure them. You already used this to enable authentication and change style above. Over time, you will find this to have many other things you will want to customize. +* **C**: This link, when clicked, opens up a color coded log window of what is going on in Zoneminder and often gives you good insight into what is going wrong or right. Note that the color here is red - that is an indication that some error occurred in ZoneMinder. You should click it and investigate. +* **D**: This is the core of ZoneMinder - recording events. It gives you a count of how many events were recorded over the hour, day, week, month. +* **E**: These are the "Zones". Zones are areas within the camera that you mark as 'hotspots' for motion detection. Simply put, when you first configure your monitors (cameras), by default Zoneminder uses the entire field of view of the camera to detect motion. You may not want this. You may want to create "zones" specifically for detecting motion and ignore others. For example, lets consider a room with a fan that spins. You surely don't want to consider the fan moving continuously a reason for triggering a record? Probably not - in that case, you'd leave the fan out while making your zones. +* **F**: This is the "source" column that tells you the type of the camera - if its an IP camera, a USB camera or more. In this example, they are all IP cameras. Note the color red on item F ? Well that means there is something wrong with that camera. No wonder the log also shows red. Good indication for you to tap on logs and investigate +* **G**: This defines how Zoneminder will record events. There are various modes. In brief Modect == record if a motion is detected,Record = always record 24x7, Mocord = always record PLUS detect motion, Monitor = just provide a live view but don't record anytime, Nodect = Don't record till an external entity via zmtrigger tells Zoneminder to (this is advanced usage). +* **H**: If you click on these links you can view a "Montage" of all your configured monitors or cycle through each one +* **I**: One of the most often missed features is the ability of ZoneMinder to maintain "run states". If you click on the "Running" text, ZoneMinder brings up a popup that allows you to define additional "states" (referred to as runstates). A runstate is essentially a snapshot that records the state of each monitor and you can switch between states easily. For example, you might have a run state defined that switches all monitors to "monitor" mode in which they are not recording anything while another state that sets some of the monitors to "modect". Why would you want this? A great example is to disable recording when you are at home and enable when you are away, based on time of day or other triggers. You can switch states by selecting an appropriate state manually, or do it automatically via cron jobs, for example. An example of using cron to automatically switch is provided in the :ref:`FAQ `. More esoteric examples of switching run states based on phone location can be found `here `__. + +Here is an example of multiple run states that I've defined. Each one of these runstates changes the mode of specific monitors depending on time of day and other conditions. Use your imagination to decide which conditions require state changes. + +.. image:: images/runstates.png + + + +Adding Monitors +^^^^^^^^^^^^^^^ +Now that we have a basic understanding of the web console, lets go about adding a new camera (monitor). For this example, lets assume we have an IP camera that streams RTSP at LAN IP address 192.168.1.33. + +The first thing we will need to know is how to access that camera's video feed. You will need to consult your camera's manual or check their forum. Zoneminder community users also have a frequently updated list right `here `__ that lists information about many cameras. If you don't find your list there and can't seem to find it elsewhere, feel free to register and ask in the `user foums `__. + +The camera we are using as an example here is a Foscam 9831W which is a 1280x960 RTSP camera, and the URL to access it's feed is *username:password@IPADDRESS:PORT/videoMain* + +Let's get started: + +Click on the "Add new monitor" button below: + +.. image:: images/getting-started-modern-look.png + +This brings up the new monitor window: + +.. image:: images/getting-started-add-monitor-general.png + :width: 800px + +* We've given it a name of 'Garage', because, well, its better than Monitor-1 and this is my Garage camera. + +* There are various source types. As a brief introduction you'd want to use 'Local' if your camera is physically attached to your ZM server (like a USB camera, for example), and one of 'Remote', 'FFMpeg', 'Libvlc' or 'cURL' for a remote camera (not necessarily, but usually). For this example, let's go with 'Remote'. + +.. NOTE:: + As a thumb rule, if you have a camera accessible via IP and it does HTTP or RTSP, + start with Remote, then try FFMpeg and libvlc if it doesn't work (:doc:`/userguide/definemonitor` + covers other modes in more details). If you are wondering what 'File' does, well, ZoneMinder was + built with compatibility in mind. Take a look at `this post + `__ to see how file can be used for leisure reading. + +* Let's leave the Function as 'Monitor' just so we can use this as an example to change it later another way. Practically, feel free to select your mode right now - Modect, Record etc depending on what you want ZoneMinder to do with this camera + +* We've put in MaxFPS and AlarmFPS as 20 here. **You can leave this empty too**. Whatever you do here, *it's important to make sure these values are higher than the FPS of the camera*. The reason we've added a value here is that as of Aug 2015, if a camera goes offline, ZoneMinder eats up a lot of CPU trying to reach it and putting a larger value here than the actual FPS helps in that specific situation. + +.. NOTE:: + We strongly recommend not putting in a lower FPS here that the one configured inside your camera. + Zoneminder should not be used to manage camera frame rate. That always causes many problems. It's + much better you set the value directly in-camera and either leave this blank or specify a higher FPS + here. In this case, our actual camera FPS is 3 and we've set this value here to 10. + +* We are done for the General tab. Let's move to the next tab + +.. image:: images/getting-started-add-monitor-source.png + :width: 800px + +* Let's select a protocol of RTSP and a remote method of RTP/RTSP (this is an RTSP camera) +* The other boxes are mostly self-explanatory + +That's pretty much it. Click on Save. We are not going to explore the other tabs in this simple guide. + +You now have a configured monitor: + +.. image:: images/getting-started-add-monitor-orange.png + +If you want to change its mode from Monitor to say, Modect (Motion Detect), later all you need to do is click on the Function column that says 'Monitor' and change it to 'Modect' like so: + + +.. image:: images/getting-started-add-monitor-modect.png + +and we now have: + +.. image:: images/getting-started-add-monitor-modect-ready.png + +And then, finally, to see if everything works, lets click on the monitor name ('Garage' in this example) and that should bring up a live feed just like this: + +.. image:: images/getting-started-add-monitor-live.png + + +Conclusion +^^^^^^^^^^ +This was a quick 'Getting Started' guide where you were introduced to the very basics of how to add a monitor (camera). We've skipped many details to keep this concise. Please refer to :doc:`/userguide/definemonitor` for many other customization details. diff --git a/docs/userguide/images/Console.png b/docs/userguide/images/Console.png new file mode 100644 index 000000000..f8e5e166e Binary files /dev/null and b/docs/userguide/images/Console.png differ diff --git a/docs/userguide/images/define-zone-blob.jpg b/docs/userguide/images/define-zone-blob.jpg new file mode 100644 index 000000000..d779de173 Binary files /dev/null and b/docs/userguide/images/define-zone-blob.jpg differ diff --git a/docs/userguide/images/define-zone-region-sample.jpg b/docs/userguide/images/define-zone-region-sample.jpg new file mode 100644 index 000000000..24dab9224 Binary files /dev/null and b/docs/userguide/images/define-zone-region-sample.jpg differ diff --git a/docs/userguide/images/definemonitor-monitor.png b/docs/userguide/images/definemonitor-monitor.png new file mode 100644 index 000000000..72ab60359 Binary files /dev/null and b/docs/userguide/images/definemonitor-monitor.png differ diff --git a/docs/userguide/images/definemonitor-preset.png b/docs/userguide/images/definemonitor-preset.png new file mode 100644 index 000000000..2d2708f81 Binary files /dev/null and b/docs/userguide/images/definemonitor-preset.png differ diff --git a/docs/userguide/images/filter-button.png b/docs/userguide/images/filter-button.png new file mode 100644 index 000000000..a480b1900 Binary files /dev/null and b/docs/userguide/images/filter-button.png differ diff --git a/docs/userguide/images/filter-choosefilter.png b/docs/userguide/images/filter-choosefilter.png new file mode 100644 index 000000000..a28ebb71a Binary files /dev/null and b/docs/userguide/images/filter-choosefilter.png differ diff --git a/docs/userguide/images/filter-filterview.png b/docs/userguide/images/filter-filterview.png new file mode 100644 index 000000000..561996668 Binary files /dev/null and b/docs/userguide/images/filter-filterview.png differ diff --git a/docs/userguide/images/filter-save.png b/docs/userguide/images/filter-save.png new file mode 100644 index 000000000..96db574c6 Binary files /dev/null and b/docs/userguide/images/filter-save.png differ diff --git a/docs/userguide/images/getting-started-add-monitor-general.png b/docs/userguide/images/getting-started-add-monitor-general.png new file mode 100644 index 000000000..9b722907d Binary files /dev/null and b/docs/userguide/images/getting-started-add-monitor-general.png differ diff --git a/docs/userguide/images/getting-started-add-monitor-live.png b/docs/userguide/images/getting-started-add-monitor-live.png new file mode 100644 index 000000000..a1d7f4100 Binary files /dev/null and b/docs/userguide/images/getting-started-add-monitor-live.png differ diff --git a/docs/userguide/images/getting-started-add-monitor-modect-ready.png b/docs/userguide/images/getting-started-add-monitor-modect-ready.png new file mode 100644 index 000000000..338a0e097 Binary files /dev/null and b/docs/userguide/images/getting-started-add-monitor-modect-ready.png differ diff --git a/docs/userguide/images/getting-started-add-monitor-modect.png b/docs/userguide/images/getting-started-add-monitor-modect.png new file mode 100644 index 000000000..bc6795b37 Binary files /dev/null and b/docs/userguide/images/getting-started-add-monitor-modect.png differ diff --git a/docs/userguide/images/getting-started-add-monitor-orange.png b/docs/userguide/images/getting-started-add-monitor-orange.png new file mode 100644 index 000000000..9d5e227aa Binary files /dev/null and b/docs/userguide/images/getting-started-add-monitor-orange.png differ diff --git a/docs/userguide/images/getting-started-add-monitor-source.png b/docs/userguide/images/getting-started-add-monitor-source.png new file mode 100644 index 000000000..e8b19925e Binary files /dev/null and b/docs/userguide/images/getting-started-add-monitor-source.png differ diff --git a/docs/userguide/images/getting-started-enable-auth.png b/docs/userguide/images/getting-started-enable-auth.png new file mode 100644 index 000000000..9160ffecc Binary files /dev/null and b/docs/userguide/images/getting-started-enable-auth.png differ diff --git a/docs/userguide/images/getting-started-flat-css-2.png b/docs/userguide/images/getting-started-flat-css-2.png new file mode 100644 index 000000000..5c4a4aa9d Binary files /dev/null and b/docs/userguide/images/getting-started-flat-css-2.png differ diff --git a/docs/userguide/images/getting-started-flat-css.png b/docs/userguide/images/getting-started-flat-css.png new file mode 100644 index 000000000..35fd21085 Binary files /dev/null and b/docs/userguide/images/getting-started-flat-css.png differ diff --git a/docs/userguide/images/getting-started-login.png b/docs/userguide/images/getting-started-login.png new file mode 100644 index 000000000..d981048c0 Binary files /dev/null and b/docs/userguide/images/getting-started-login.png differ diff --git a/docs/userguide/images/getting-started-modern-look.png b/docs/userguide/images/getting-started-modern-look.png new file mode 100644 index 000000000..34dbcdfc8 Binary files /dev/null and b/docs/userguide/images/getting-started-modern-look.png differ diff --git a/docs/userguide/images/getting-started-understand-console.png b/docs/userguide/images/getting-started-understand-console.png new file mode 100644 index 000000000..f332b4977 Binary files /dev/null and b/docs/userguide/images/getting-started-understand-console.png differ diff --git a/docs/userguide/images/runstates.png b/docs/userguide/images/runstates.png new file mode 100644 index 000000000..ced74cb55 Binary files /dev/null and b/docs/userguide/images/runstates.png differ diff --git a/docs/userguide/images/viewevents-main.png b/docs/userguide/images/viewevents-main.png new file mode 100644 index 000000000..70093724f Binary files /dev/null and b/docs/userguide/images/viewevents-main.png differ diff --git a/docs/userguide/images/viewevents-stream.png b/docs/userguide/images/viewevents-stream.png new file mode 100644 index 000000000..672a74e4d Binary files /dev/null and b/docs/userguide/images/viewevents-stream.png differ diff --git a/docs/userguide/images/viewmonitor-main.png b/docs/userguide/images/viewmonitor-main.png new file mode 100644 index 000000000..74354f0b0 Binary files /dev/null and b/docs/userguide/images/viewmonitor-main.png differ diff --git a/docs/userguide/images/viewmonitor-stream.png b/docs/userguide/images/viewmonitor-stream.png new file mode 100644 index 000000000..126a67bb2 Binary files /dev/null and b/docs/userguide/images/viewmonitor-stream.png differ diff --git a/docs/userguide/images/zm-system-overview.jpg b/docs/userguide/images/zm-system-overview.jpg new file mode 100644 index 000000000..1624e44c6 Binary files /dev/null and b/docs/userguide/images/zm-system-overview.jpg differ diff --git a/docs/userguide/images/zm-system-overview.xml b/docs/userguide/images/zm-system-overview.xml new file mode 100644 index 000000000..d0d55d5d0 --- /dev/null +++ b/docs/userguide/images/zm-system-overview.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/docs/userguide/index.rst b/docs/userguide/index.rst index 1175fdbd0..35bfcf39d 100644 --- a/docs/userguide/index.rst +++ b/docs/userguide/index.rst @@ -9,7 +9,6 @@ User Guide definemonitor definezone viewmonitors - controlmonitor filterevents viewevents options diff --git a/docs/userguide/logging.rst b/docs/userguide/logging.rst index be90de9c2..4256079ae 100644 --- a/docs/userguide/logging.rst +++ b/docs/userguide/logging.rst @@ -19,24 +19,33 @@ The syslog service uses the concept of priorities and facilities where the forme So armed with the knowledge of the priority and facility of a message, the syslog.conf file can be amended to handle messages however you like. -So to ensure that all ZoneMinder messages go to a specific log file you can add the following line near the top of your syslog.conf file:: +So to ensure that all ZoneMinder messages go to a specific log file you can add the following line near the top of your syslog.conf file: -
# Save ZoneMinder messages to zm.log
-  local1.*                        /var/log/zm/zm.log
+:: -which will ensure that all messages produced with the local1 facility are routed to fhe /var/log/zm/zm.log file. However this does not necessarily prevent them also going into the standard system log. To do this you will need to modify the line that determines which messages are logged to this file. This may look something like:: + # Save ZoneMinder messages to zm.log + local1.* /var/log/zm/zm.log -
# Log anything (except mail) of level info or higher.
+which will ensure that all messages produced with the local1 facility are routed to fhe /var/log/zm/zm.log file. However this does not necessarily prevent them also going into the standard system log. To do this you will need to modify the line that determines which messages are logged to this file. This may look something like:
+
+::
+
+  # Log anything (except mail) of level info or higher.
   # Don't log private authentication messages!
-  *.info;mail.none;news.none;authpriv.none;cron.none      /var/log/messages
+ *.info;mail.none;news.none;authpriv.none;cron.none /var/log/messages -by default. To remove ZoneMinder messages altogether from this file you can modify this line to look like:: +by default. To remove ZoneMinder messages altogether from this file you can modify this line to look like: -
*.info;local1.!*;mail.none;news.none;authpriv.none;cron.none     /var/log/messages
+:: -which instructs syslog to ignore any messages from the local1 facility. If however you still want warnings and errors to occur in the system log file, you could change it to:: + *.info;local1.!*;mail.none;news.none;authpriv.none;cron.none /var/log/messages -
*.info;local1.!*;local1.warning;mail.none;news.none;authpriv.none;cron.none     /var/log/messages
+which instructs syslog to ignore any messages from the local1 facility. If however you still want warnings and errors to occur in the system log file, you could change it to: + +:: + + + *.info;local1.!*;local1.warning;mail.none;news.none;authpriv.none;cron.none /var/log/messages which follows the ignore instruction with a further one to indicate that any messages with a facility of local1 and a priority of warning or above should still go into the file. @@ -50,15 +59,17 @@ Once you have debug being logged you can modify the level by sending USR1 and US If you wish to run a binary directly from the command line to test specific functionality or scenarios, you can set the ZM_DBG_LEVEL and ZM_DBG_LOG environment variables to set the level and log file of the debug you wish to see, and the ZM_DBG_PRINT environment variable to 1 to output the debug directly to your terminal. -All ZoneMinder logs can now be rotated by logrotate. A sample logrotate config file is shown below.:: +All ZoneMinder logs can now be rotated by logrotate. A sample logrotate config file is shown below: -
/var/log/zm/*.log {
+::
+
+  /var/log/zm/*.log {
       missingok
       notifempty
       sharedscripts
       postrotate
           /usr/local/bin/zmpkg.pl logrot 2> /dev/null > /dev/null || true
       endscript
-  }
+ } diff --git a/docs/userguide/mobile.rst b/docs/userguide/mobile.rst index d71845663..b6feb805d 100644 --- a/docs/userguide/mobile.rst +++ b/docs/userguide/mobile.rst @@ -1,20 +1,22 @@ Mobile Devices ============== -* After 1.24.x, access to the "light" interface changed to http://xxx.xxx.xxx.xxx/zm/index.php?skin=mobile ; You may find that you have a directory named "skins" under ZoneMinder base dir, so you may use one of the existent skins as a base to adapt to your needs; +Here are some options for using ZoneMinder on Mobile devices: -Type this in your mobile browser: http://xxx.xxx.xxx.xxx/zm/index.php?format=xhtml (deprecated) +Third party mobile clients +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* zmNinja (`source code `__, needs APIs to be installed to work) + * Available in App Store and Play Store - `website `__ +* zmView (limited, free) and zmView Pro (more features, paid) + * Available in App Store and Play Store, relies on ZM skins `website `__ -ZoneMinder has always had a minimal WML (Wireless Markup Language) capability to allow it to function on mobile phones and similar devices. However as of 1.20.0 this is now deprecated and has been replaced with a new XHTML – Mobile Profile mode as well as the default HTML4. XHTML-MP is a small, and limited, version of XHTML intended for mobile devices and is based on XHTML Basic. It does not contain scripting or other dynamic elements and essentially is a subset of HTML as most people know it. +Using the existing web console +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +* You can directly use the ZoneMinder interface by launching a browser and going to the ZoneMinder server just like you do on the Desktop +* ZoneMinder also has a "mobile skin" that offers limited functionality (not all views are present in this skin). You can point your mobile browser to ``http://yourzoneminderip/zm/index.php?skin=mobile`` and bookmark it. **Note however that 1.29 is the last release that will support the mobile skin. It's use is deprecated** -The ZoneMinder XHTML-MP interface allows you to log into your installation via your phone or mobile devices and perform a limited number of tasks. These include viewing recent events, and monitoring live streams. However unlike the full interfaces these elements are presented as still images requiring manual refreshing. For now the XHTML-MP interface is presented as a prototype interface; rather than one offering full capabilities. As such, please feel free to make comments or offer suggestions via the forums on http://www.zoneminder.com. +Discontinued clients +^^^^^^^^^^^^^^^^^^^^ +The following are a list of clients that do not work and have not been updated: -As well as XHTML-MP, ideally I’d like to be able to offer a WML2.0 interface. WML2.0 is a blending of WML1.3, which is traditional WAP, and XHTML. As such it offers the scripting that WML has traditionally included plus the better control of mark-up that is the realm of XHTML. Unfortunately so far I’m unaware of any devices that support WML2.0 even if they say they are WAP2 compliant; certainly I’ve never had a phone that does. If you find out that a particular phone does support this then please let me know (or better still send me the phone!). - -If you wish to use the XHTML-MP interface to ZoneMinder there is no extra configuration required to enable it per se. However ZoneMinder needs to be able to figure out what kind of content to deliver to particular browsers, so you have two choices. You can edit zm.php and include a definition that corresponds to your phone, describing a small number of basic capabilities, you will see a couple of examples already there, or you can use the comprehensive open source WURFL package available from http://wurfl.sourceforge.net/. You will need to download both the WURFL php files and the wurfl.xml file itself. WURFL is a resource containing information on the capabilities of a huge number of mobile phones, devices and browsers. Thus once it has matched your phone it can determine various capabilities it may possess. This means that ZoneMinder itself only has to deal with these capabilities and not the individual phone types. If you prefer you can also add the format=xHTML url parameter when you load ZoneMinder to force the xHTML format and skip the automatic determination altoghether. - -To use WURFL you should install the php files in the same directory as ZoneMinder and then create a ‘wurfl’ sub-directory and ensure it is readable and writeable (or preferably owned by) your web server user. You should put the wurfl.xml file in there. One other thing you may need to change, as the xml file is quite large, is the ‘memory_limit’ setting in php.ini as the default setting of 8Mb may be too small. Once you’ve done this you should find that your phone or device is recognised and if it can support XHTML-MP it will receive that interface. If your phone is very new, or you are using an old version of the XML file you might find that it is not present however. The WURFL library uses a caching strategy to avoid reloading the whole XML file each time so check if a sensible looking cache file has been created in the ‘wurfl’ sub-directory also check the wurfl.log in the same place. - -The WURFL is a third party application and as such I am unable to offer support directly for it. If you feel your device is missing or incorrectly represented please contact the authors via their own channels. If on the other hand you have any comments on ZoneMinder on your device specifically please let me know and I would be pleased to hear about it. - -As support for cookies in mobile devices is patchy at best, the groups feature is not fully implemented in the XHTML-MP views. Instead if there is a group called ‘Mobile’ already defined then that group will always be effective, if not then all monitors available to the logged in user will be visible, +* eyeZM diff --git a/docs/userguide/monitor/images/Monitor_Misc.png b/docs/userguide/monitor/images/Monitor_Misc.png new file mode 100644 index 000000000..0d7ab50be Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_Misc.png differ diff --git a/docs/userguide/monitor/images/Monitor_Source_ffmpeg.png b/docs/userguide/monitor/images/Monitor_Source_ffmpeg.png new file mode 100644 index 000000000..c3de6b2d9 Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_Source_ffmpeg.png differ diff --git a/docs/userguide/monitor/images/Monitor_Source_local.png b/docs/userguide/monitor/images/Monitor_Source_local.png new file mode 100644 index 000000000..3dd4f2855 Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_Source_local.png differ diff --git a/docs/userguide/monitor/images/Monitor_buffers.png b/docs/userguide/monitor/images/Monitor_buffers.png new file mode 100644 index 000000000..402084392 Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_buffers.png differ diff --git a/docs/userguide/monitor/images/Monitor_general.png b/docs/userguide/monitor/images/Monitor_general.png new file mode 100644 index 000000000..f4c39cd5f Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_general.png differ diff --git a/docs/userguide/monitor/images/Monitor_preset.png b/docs/userguide/monitor/images/Monitor_preset.png new file mode 100644 index 000000000..3faf1954e Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_preset.png differ diff --git a/docs/userguide/monitor/images/Monitor_source_curl.png b/docs/userguide/monitor/images/Monitor_source_curl.png new file mode 100644 index 000000000..b276ca64d Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_source_curl.png differ diff --git a/docs/userguide/monitor/images/Monitor_source_file.png b/docs/userguide/monitor/images/Monitor_source_file.png new file mode 100644 index 000000000..d68751cff Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_source_file.png differ diff --git a/docs/userguide/monitor/images/Monitor_source_libvlc.png b/docs/userguide/monitor/images/Monitor_source_libvlc.png new file mode 100644 index 000000000..3cc38ddc0 Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_source_libvlc.png differ diff --git a/docs/userguide/monitor/images/Monitor_source_remote.png b/docs/userguide/monitor/images/Monitor_source_remote.png new file mode 100644 index 000000000..e77fb2a41 Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_source_remote.png differ diff --git a/docs/userguide/monitor/images/Monitor_timestamp.png b/docs/userguide/monitor/images/Monitor_timestamp.png new file mode 100644 index 000000000..e7d473d0d Binary files /dev/null and b/docs/userguide/monitor/images/Monitor_timestamp.png differ diff --git a/docs/userguide/options.rst b/docs/userguide/options.rst index 085b87c90..8186d8b6a 100644 --- a/docs/userguide/options.rst +++ b/docs/userguide/options.rst @@ -1,17 +1,22 @@ Options ======= -The final area covered by the tutorial is the options and user section. If you are running in authenticated mode and don’t have system privileges then you will not see this section at all and if you are running in un-authenticated mode then no user section will be displayed. The various options you can specify are displayed in a tabbed dialog with each group of options displayed under a different heading. Each option is displayed with its name, a short description and the current value. You can also click on the ‘?’ link following each description to get a fuller explanation about each option. This is the same as you would get from zmconfig.pl. A number of option groups have a master option near the top which enables or disables the whole group so you should be aware of the state of this before modifying options and expecting them to make any difference. If you have changed the value of an option you should then ‘save’ it. A number of the option groups will then prompt you to let you know that the option(s) you have changed will require a system restart. This is not done automatically in case you will be changing many values in the same session, however once you have made all of your changes you should restart ZoneMinder as soon as possible. The reason for this is that web and some scripts will pick up the new changes immediately but some of the daemons will still be using the old values and this can lead to data inconsistency or loss. -One of the options you may notice in the ‘System’ tab allows you to specify the default language for your installation of ZoneMinder. Versions 1.17.0 and later support multiple languages but rely on users to assist in creating language files for specific languages. To specify a language you will have to give the applicable code, thus for UK English this is en_gb, and for US English it would be en_us, if no language is given then UK English is assumed. Most languages will be specified in this nn_mm format and to check which languages are available look for files named zm_lang_*.php in the ZoneMinder build directory where the parts represented by the ‘*’ would be what you would enter as a language. This is slightly unwieldy and will probably be improved in future to make it easier to determine language availability. On checking which languages are available it may be that your preferred language is not currently included and if this is the case please consider doing a translation and sending it back to it may be included in future releases. All the language elements are given in the zm_lang_en_gb.php file along with a few notes to help you understand the format. +.. toctree:: -As mentioned above, you may also see a ‘users’ tab in the Options area. In this section you will see a list of the current users defined on the system. You can also add or delete users from here. It is recommended you do not delete the admin user unless you have created another fully privileged user to take over the same role. Each user is defined with a name and password (which is hidden) as well as an enabled setting which you can use to temporarily enable or disable users, for example a guest user for limited time access. As well as that there is a language setting that allows you to define user specific languages. Setting a language here that is different than the system language will mean that when that user logs in they will have the web interface presented in their own language rather than the system default, if it is available. Specifying a language here is done in the same way as for the system default language described above. - -There are also five values that define the user permissions, these are ‘Stream’, ‘Events’, ‘Control’, ‘Monitors’ and ‘System’ Each can have values of ‘None’, ‘View’ or ‘Edit’ apart from ‘Stream’ which has no ‘Edit’ setting. These values cover access to the following areas; ‘Stream’ defines whether a user is allowed to view the ‘live’ video feeds coming from the cameras. You may wish to allow a user to view historical events only in which case this setting should be ‘none’. The ‘Events’ setting determines whether a user can view and modify or delete any retained historical events. The ‘Control’ setting allows you to indicate whether the user is able to control any Pan/Tilt/Zoom type cameras you may have on your system. The ‘Monitors’ setting specifies whether a user can see the current monitor settings and change them. Finally the ‘System’ setting determines whether a user can view or modify the system settings as a whole, such as options and users or controlling the running of the system as a whole. - -As well as these settings there is also a ‘Bandwidth’ setting which can be used to limit the maximum bandwidth that a user can view at and a ‘Monitor Ids’ setting that can be used for non-’System’ users to restrict them to only being able to access streams, events or monitors for the given monitors ids as a comma separated list with no spaces. If a user with ‘Monitors’ edit privileges is limited to specific monitors here they will not be able to add or delete monitors but only change the details of those they have access to. If a user has ‘System’ privileges then the ‘Monitors Ids’ setting is ignored and has no effect.’ - -That’s pretty much is it for the tour, though there is a lot more to ZoneMinder as you will discover. You should experiment with the various settings to get the results you think are right for your requirements. + options/options_display + options/options_system + options/options_config + options/options_paths + options/options_web + options/options_images + options/options_logging + options/options_network + options/options_email + options/options_upload + options/options_x10 + options/options_bw + options/options_users diff --git a/docs/userguide/options/images/Options_BW.png b/docs/userguide/options/images/Options_BW.png new file mode 100644 index 000000000..614944b41 Binary files /dev/null and b/docs/userguide/options/images/Options_BW.png differ diff --git a/docs/userguide/options/images/Options_BW_Phone.png b/docs/userguide/options/images/Options_BW_Phone.png new file mode 100644 index 000000000..dd3e27a50 Binary files /dev/null and b/docs/userguide/options/images/Options_BW_Phone.png differ diff --git a/docs/userguide/options/images/Options_Config.png b/docs/userguide/options/images/Options_Config.png new file mode 100644 index 000000000..d61ecf32e Binary files /dev/null and b/docs/userguide/options/images/Options_Config.png differ diff --git a/docs/userguide/options/images/Options_Display.png b/docs/userguide/options/images/Options_Display.png new file mode 100644 index 000000000..05ad5b506 Binary files /dev/null and b/docs/userguide/options/images/Options_Display.png differ diff --git a/docs/userguide/options/images/Options_Logging.png b/docs/userguide/options/images/Options_Logging.png new file mode 100644 index 000000000..a0b00d767 Binary files /dev/null and b/docs/userguide/options/images/Options_Logging.png differ diff --git a/docs/userguide/options/images/Options_Network.png b/docs/userguide/options/images/Options_Network.png new file mode 100644 index 000000000..d94ab9992 Binary files /dev/null and b/docs/userguide/options/images/Options_Network.png differ diff --git a/docs/userguide/options/images/Options_Paths.png b/docs/userguide/options/images/Options_Paths.png new file mode 100644 index 000000000..6da6da4b9 Binary files /dev/null and b/docs/userguide/options/images/Options_Paths.png differ diff --git a/docs/userguide/options/images/Options_Servers.png b/docs/userguide/options/images/Options_Servers.png new file mode 100644 index 000000000..2ca47edba Binary files /dev/null and b/docs/userguide/options/images/Options_Servers.png differ diff --git a/docs/userguide/options/images/Options_System.png b/docs/userguide/options/images/Options_System.png new file mode 100644 index 000000000..f100dd7f0 Binary files /dev/null and b/docs/userguide/options/images/Options_System.png differ diff --git a/docs/userguide/options/images/Options_Users.png b/docs/userguide/options/images/Options_Users.png new file mode 100644 index 000000000..31a697cc1 Binary files /dev/null and b/docs/userguide/options/images/Options_Users.png differ diff --git a/docs/userguide/options/images/Options_X10.png b/docs/userguide/options/images/Options_X10.png new file mode 100644 index 000000000..34e9e87ad Binary files /dev/null and b/docs/userguide/options/images/Options_X10.png differ diff --git a/docs/userguide/options/images/Options_email.png b/docs/userguide/options/images/Options_email.png new file mode 100644 index 000000000..c1c17f569 Binary files /dev/null and b/docs/userguide/options/images/Options_email.png differ diff --git a/docs/userguide/options/images/Options_eyezm.png b/docs/userguide/options/images/Options_eyezm.png new file mode 100644 index 000000000..397a92d3b Binary files /dev/null and b/docs/userguide/options/images/Options_eyezm.png differ diff --git a/docs/userguide/options/images/Options_images.png b/docs/userguide/options/images/Options_images.png new file mode 100644 index 000000000..2f73ee7a0 Binary files /dev/null and b/docs/userguide/options/images/Options_images.png differ diff --git a/docs/userguide/options/images/Options_upload.png b/docs/userguide/options/images/Options_upload.png new file mode 100644 index 000000000..46cd6dd89 Binary files /dev/null and b/docs/userguide/options/images/Options_upload.png differ diff --git a/docs/userguide/options/images/Options_web.png b/docs/userguide/options/images/Options_web.png new file mode 100644 index 000000000..2376030f0 Binary files /dev/null and b/docs/userguide/options/images/Options_web.png differ diff --git a/docs/userguide/options/options_bw.rst b/docs/userguide/options/options_bw.rst new file mode 100644 index 000000000..8dddf9f85 --- /dev/null +++ b/docs/userguide/options/options_bw.rst @@ -0,0 +1,42 @@ +Options - High, Medium and Low B/W +---------------------------------- + +.. image:: images/Options_BW.png + +There are now a number of options that are grouped into bandwidth categories, this allows you to configure the ZoneMinder client to work optimally over the various access methods you might to access the client. The following options are available in H, M and L options. These 3 groups control what happens when the client is running in 'high', 'medium' and 'low' bandwidth mode respectively. In most cases the default values will be suitable as a starting point. + +High - You should set these options for when accessing the ZoneMinder client over a local network or high speed link. + +Medium - You should set these options for when accessing the ZoneMinder client over a slower cable or DSL link. + +Slow - You should set these options for when accessing Zoneminder client over a slow network link. + +WEB_H_REFRESH_MAIN, WEB_M_REFRESH_MAIN, WEB_L_REFRESH_MAIN - How often (in seconds) the main console window should refresh itself. The main console window lists a general status and the event totals for all monitors. This is not a trivial task and should not be repeated too frequently or it may affect the performance of the rest of the system. + +WEB_H_REFRESH_CYCLE, WEB_M_REFRESH_CYCLE, WEB_L_REFRESH_CYCLE - How often (in seconds) the cycle watch window swaps to the next monitor. The cycle watch window is a method of continuously cycling between images from all of your monitors. This option determines how often to refresh with a new image. + +WEB_H_REFRESH_IMAGE, WEB_M_REFRESH_IMAGE, WEB_L_REFRESH_IMAGE - How often (in seconds) the watched image is refreshed (if not streaming). The live images from a monitor can be viewed in either streamed or stills mode. This option determines how often a stills image is refreshed, it has no effect if streaming is selected. + +WEB_H_REFRESH_STATUS, WEB_M_REFRESH_STATUS, WEB_L_REFRESH_STATUS - How often (in seconds) the status refreshes itself in the watch window. The monitor window is actually made from several frames. The one in the middle merely contains a monitor status which needs to refresh fairly frequently to give a true indication. This option determines that frequency. + +WEB_H_REFRESH_EVENTS, WEB_M_REFRESH_EVENTS, WEB_L_REFRESH_EVENTS - How often (in seconds) the event listing is refreshed in the watch window. The monitor window is actually made from several frames. The lower framme contains a listing of the last few events for easy access. This option determines how often this is refreshed. + +WEB_H_CAN_STREAM, WEB_M_CAN_STREAM, WEB_L_CAN_STREAM - If you know that your browser can handle image streams of the type 'multipart/x-mixed-replace' but ZoneMinder does not detect this correctly you can set this option to ensure that the stream is delivered with or without the use of the Cambozola plugin. Selecting 'yes' will tell ZoneMinder that your browser can handle the streams nativ + +WEB_H_STREAM_METHOD, WEB_M_STREAM_METHOD, WEB_H_STREAM_METHOD - ZoneMinder can be configured to use either mpeg encoded video or a series or still jpeg images when sending video streams. This option defines which is used. If you choose mpeg you should ensure that you have the appropriate plugins available on your browser whereas choosing jpeg will work natively on Mozilla and related browsers and with a Java applet on Internet Explorer + +WEB_H_DEFAULT_SCALE, WEB_M_DEFAULT_SCALE, WEB_L_DEFAULT_SCALE - Normally ZoneMinder will display 'live' or 'event' streams in their native size. However if you have monitors with large dimensions or a slow link you may prefer to reduce this size, alternatively for small monitors you can enlarge it. This options lets you specify what the default scaling factor will be. It is expressed as a percentage so 100 is normal size, 200 is double size etc. + +WEB_H_DEFAULT_RATE, WEB_M_DEFAULT_RATE, WEB_L_DEFAULT_RATE - Normally ZoneMinder will display 'event' streams at their native rate, i.e. as close to real-time as possible. However if you have long events it is often convenient to replay them at a faster rate for review. This option lets you specify what the default replay rate will be. It is expressed as a percentage so 100 is normal rate, 200 is double speed etc. + +WEB_H_VIDEO_BITRATE, WEB_M_VIDEO_BITRATE, WEB_L_VIDEO_BITRATE - When encoding real video via the ffmpeg library a bit rate can be specified which roughly corresponds to the available bandwidth used for the stream. This setting effectively corresponds to a 'quality' setting for the video. A low value will result in a blocky image whereas a high value will produce a clearer view. Note that this setting does not control the frame rate of the video however the quality of the video produced is affected both by this setting and the frame rate that the video is produced at. A higher frame rate at a particular bit rate result in individual frames being at a lower quality. + +WEB_H_VIDEO_MAXFPS, WEB_M_VIDEO_MAXFPS, WEB_L_VIDEO_MAXFPS - When using streamed video the main control is the bitrate which determines how much data can be transmitted. However a lower bitrate at high frame rates results in a lower quality image. This option allows you to limit the maximum frame rate to ensure that video quality is maintained. An additional advantage is that encoding video at high frame rates is a processor intensive task when for the most part a very high frame rate offers little perceptible improvement over one that has a more manageable resource requirement. Note, this option is implemented as a cap beyond which binary reduction takes place. So if you have a device capturing at 15fps and set this option to 10fps then the video is not produced at 10fps, but rather at 7.5fps (15 divided by 2) as the final frame rate must be the original divided by a power of 2. + +WEB_H_SCALE_THUMBS, WEB_M_SCALE_THUMBS, WEB_L_SCALE_THUMBS - If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the browser to conserve bandwidth at the cost of cpu on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package. + +WEB_H_EVENTS_VIEW, WEB_M_EVENTS_VIEW, WEB_L_EVENTS_VIEW - Stored events can be viewed in either an events list format or in a timeline based one. This option sets the default view that will be used. Choosing one view here does not prevent the other view being used as it will always be selectable from whichever view is currently being used. + +WEB_H_SHOW_PROGRESS, WEB_M_SHOW_PROGRESS, WEB_L_SHOW_PROGRESS - When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to specific points in the event, but can can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on limited bandwidth connections may be out of step with the replay. This option allows you to turn off the progress display, whilst still keeping the navigation aspect, where bandwidth prevents it functioning effectively. + +WEB_H_AJAX_TIMEOUT, WEB_M_AJAX_TIMEOUT, WEB_L_AJAX_TIMEOUT - The newer versions of the live feed and event views use Ajax to request information from the server and populate the views dynamically. This option allows you to specify a timeout if required after which requests are abandoned. A timeout may be necessary if requests would overwise hang such as on a slow connection. This would tend to consume a lot of browser memory and make the interface unresponsive. Ordinarily no requests should timeout so this setting should be set to a value greater than the slowest expected response. This value is in milliseconds but if set to zero then no timeout will be used. \ No newline at end of file diff --git a/docs/userguide/options/options_config.rst b/docs/userguide/options/options_config.rst new file mode 100644 index 000000000..0f419e2ab --- /dev/null +++ b/docs/userguide/options/options_config.rst @@ -0,0 +1,38 @@ +Options - Config +---------------- + +.. image:: images/Options_Config.png + +TIMESTAMP_ON_CAPTURE - ZoneMinder can add a timestamp to images in two ways. The default method, when this option is set, is that each image is timestamped immediately when captured and so the image held in memory is marked right away. The second method does not timestamp the images until they are either saved as part of an event or accessed over the web. The timestamp used in both methods will contain the same time as this is preserved along with the image. The first method ensures that an image is timestamped regardless of any other circumstances but will result in all images being timestamped even those never saved or viewed. The second method necessitates that saved images are copied before being saved otherwise two timestamps perhaps at different scales may be applied. This has the (perhaps) desirable side effect that the timestamp is always applied at the same resolution so an image that has scaling applied will still have a legible and correctly scaled timestamp. + +CPU_EXTENSIONS - When advanced processor extensions such as SSE2 or SSSE3 are available, ZoneMinder can use them, which should increase performance and reduce system load. Enabling this option on processors that do not support the advanced processors extensions used by ZoneMinder is harmless and will have no effect. + +FAST_IMAGE_BLENDS - To detect alarms ZoneMinder needs to blend the captured image with the stored reference image to update it for comparison with the next image. The reference blend percentage specified for the monitor controls how much the new image affects the reference image. There are two methods that are available for this. If this option is set then fast calculation which does not use any multiplication or division is used. This calculation is extremely fast, however it limits the possible blend percentages to 50%, 25%, 12.5%, 6.25%, 3.25% and 1.5%. Any other blend percentage will be rounded to the nearest possible one. The alternative is to switch this option off and use standard blending instead, which is slower. + +OPT_ADAPTIVE_SKIP - In previous versions of ZoneMinder the analysis daemon would attempt to keep up with the capture daemon by processing the last captured frame on each pass. This would sometimes have the undesirable side-effect of missing a chunk of the initial activity that caused the alarm because the pre-alarm frames would all have to be written to disk and the database before processing the next frame, leading to some delay between the first and second event frames. Setting this option enables a newer adaptive algorithm where the analysis daemon attempts to process as many captured frames as possible, only skipping frames when in danger of the capture daemon overwriting yet to be processed frames. This skip is variable depending on the size of the ring buffer and the amount of space left in it. Enabling this option will give you much better coverage of the beginning of alarms whilst biasing out any skipped frames towards the middle or end of the event. However you should be aware that this will have the effect of making the analysis daemon run somewhat behind the capture daemon during events and for particularly fast rates of capture it is possible for the adaptive algorithm to be overwhelmed and not have time to react to a rapid build up of pending frames and thus for a buffer overrun condition to occur. + +MAX_SUSPEND_TIME - ZoneMinder allows monitors to have motion detection to be suspended, for instance while panning a camera. Ordinarily this relies on the operator resuming motion detection afterwards as failure to do so can leave a monitor in a permanently suspended state. This setting allows you to set a maximum time which a camera may be suspended for before it automatically resumes motion detection. This time can be extended by subsequent suspend indications after the first so continuous camera movement will also occur while the monitor is suspended. + +STRICT_VIDEO_CONFIG - With some video devices errors can be reported in setting the various video attributes when in fact the operation was successful. Switching this option off will still allow these errors to be reported but will not cause them to kill the video capture daemon. Note however that doing this will cause all errors to be ignored including those which are genuine and which may cause the video capture to not function correctly. Use this option with caution. + +SIGNAL_CHECK_POINTS - For locally attached video cameras ZoneMinder can check for signal loss by looking at a number of random points on each captured image. If all of these points are set to the same fixed colour then the camera is assumed to have lost signal. When this happens any open events are closed and a short one frame signal loss event is generated, as is another when the signal returns. This option defines how many points on each image to check. Note that this is a maximum, any points found to not have the check colour will abort any further checks so in most cases on a couple of points will actually be checked. Network and file based cameras are never checked. + +V4L_MULTI_BUFFER - Performance when using Video 4 Linux devices is usually best if multiple buffers are used allowing the next image to be captured while the previous one is being processed. If you have multiple devices on a card sharing one input that requires switching then this approach can sometimes cause frames from one source to be mixed up with frames from another. Switching this option off prevents multi buffering resulting in slower but more stable image capture. This option is ignored for non-local cameras or if only one input is present on a capture chip. This option addresses a similar problem to the ZM_CAPTURES_PER_FRAME option and you should normally change the value of only one of the options at a time. If you have different capture cards that need different values you can ovveride them in each individual monitor on the source page. + +CAPTURES_PER_FRAME - If you are using cameras attached to a video capture card which forces multiple inputs to share one capture chip, it can sometimes produce images with interlaced frames reversed resulting in poor image quality and a distinctive comb edge appearance. Increasing this setting allows you to force additional image captures before one is selected as the captured frame. This allows the capture hardware to 'settle down' and produce better quality images at the price of lesser capture rates. This option has no effect on (a) network cameras, or (b) where multiple inputs do not share a capture chip. This option addresses a similar problem to the ZM_V4L_MULTI_BUFFER option and you should normally change the value of only one of the options at a time. If you have different capture cards that need different values you can ovveride them in each individual monitor on the source page. + +FORCED_ALARM_SCORE - The 'zmu' utility can be used to force an alarm on a monitor rather than rely on the motion detection algorithms. This option determines what score to give these alarms to distinguish them from regular ones. It must be 255 or less. + +BULK_FRAME_INTERVAL - Traditionally ZoneMinder writes an entry into the Frames database table for each frame that is captured and saved. This works well in motion detection scenarios but when in a DVR situation ('Record' or 'Mocord' mode) this results in a huge number of frame writes and a lot of database and disk bandwidth for very little additional information. Setting this to a non-zero value will enabled ZoneMinder to group these non-alarm frames into one 'bulk' frame entry which saves a lot of bandwidth and space. The only disadvantage of this is that timing information for individual frames is lost but in constant frame rate situations this is usually not significant. This setting is ignored in Modect mode and individual frames are still written if an alarm occurs in Mocord mode also. + +EVENT_CLOSE_MODE - When a monitor is running in a continuous recording mode (Record or Mocord) events are usually closed after a fixed period of time (the section length). However in Mocord mode it is possible that motion detection may occur near the end of a section. This option controls what happens when an alarm occurs in Mocord mode. The 'time' setting means that the event will be closed at the end of the section regardless of alarm activity. The 'idle' setting means that the event will be closed at the end of the section if there is no alarm activity occurring at the time otherwise it will be closed once the alarm is over meaning the event may end up being longer than the normal section length. The 'alarm' setting means that if an alarm occurs during the event, the event will be closed once the alarm is over regardless of when this occurs. This has the effect of limiting the number of alarms to one per event and the events will be shorter than the section length if an alarm has occurred. + +CREATE_ANALYSIS_IMAGES - By default during an alarm ZoneMinder records both the raw captured image and one that has been analysed and had areas where motion was detected outlined. This can be very useful during zone configuration or in analysing why events occurred. However it also incurs some overhead and in a stable system may no longer be necessary. This parameter allows you to switch the generation of these images off. + +WEIGHTED_ALARM_CENTRES - ZoneMinder will always calculate the centre point of an alarm in a zone to give some indication of where on the screen it is. This can be used by the experimental motion tracking feature or your own custom extensions. In the alarmed or filtered pixels mode this is a simple midpoint between the extents of the detected pxiesl. However in the blob method this can instead be calculated using weighted pixel locations to give more accurate positioning for irregularly shaped blobs. This method, while more precise is also slower and so is turned off by default. + +EVENT_IMAGE_DIGITS - As event images are captured they are stored to the filesystem with a numerical index. By default this index has three digits so the numbers start 001, 002 etc. This works works for most scenarios as events with more than 999 frames are rarely captured. However if you have extremely long events and use external applications then you may wish to increase this to ensure correct sorting of images in listings etc. Warning, increasing this value on a live system may render existing events unviewable as the event will have been saved with the previous scheme. Decreasing this value should have no ill effects. + +DEFAULT_ASPECT_RATIO - When specifying the dimensions of monitors you can click a checkbox to ensure that the width stays in the correct ratio to the height, or vice versa. This setting allows you to indicate what the ratio of these settings should be. This should be specified in the format : and the default of 4:3 normally be acceptable but 11:9 is another common setting. If the checkbox is not clicked when specifying monitor dimensions this setting has no effect. + +USER_SELF_EDIT - Ordinarily only users with system edit privilege are able to change users details. Switching this option on allows ordinary users to change their passwords and their language settings \ No newline at end of file diff --git a/docs/userguide/options/options_display.rst b/docs/userguide/options/options_display.rst new file mode 100644 index 000000000..db76b7505 --- /dev/null +++ b/docs/userguide/options/options_display.rst @@ -0,0 +1,13 @@ +Options - Display +----------------- + +.. image:: images/Options_Display.png + +This option screen allows user to select the skin for ZoneMinder. Currently available skins are: + +* Classic +* Flat +* XML (Deprecated in favour of web/API) +* Mobile (Deprecated) + + diff --git a/docs/userguide/options/options_email.rst b/docs/userguide/options/options_email.rst new file mode 100644 index 000000000..741ae0b6d --- /dev/null +++ b/docs/userguide/options/options_email.rst @@ -0,0 +1,96 @@ +Options - Email +--------------- + +.. image:: images/Options_email.png + +OPT_EMAIL - In ZoneMinder you can create event filters that specify whether events that match certain criteria should have their details emailed to you at a designated email address. This will allow you to be notified of events as soon as they occur and also to quickly view the events directly. This option specifies whether this functionality should be available. The email created with this option can be any size and is intended to be sent to a regular email reader rather than a mobile device. + +EMAIL_ADDRESS - This option is used to define the email address that any events that match the appropriate filters will be sent to. + +EMAIL_SUBJECT - This option is used to define the subject of the email that is sent for any events that match the appropriate filters. + +EMAIL_BODY - This option is used to define the content of the email that is sent for any events that match the appropriate filters. + ++--------+--------------------------------------------------------+ +| Token | Description | ++========+========================================================+ +| %EI% | Id of the event | ++--------+--------------------------------------------------------+ +| %EN% | Name of the event | ++--------+--------------------------------------------------------+ +| %EC% | Cause of the event | ++--------+--------------------------------------------------------+ +| %ED% | Event description | ++--------+--------------------------------------------------------+ +| %ET% | Time of the event | ++--------+--------------------------------------------------------+ +| %EL% | Length of the event | ++--------+--------------------------------------------------------+ +| %EF% | Number of frames in the event | ++--------+--------------------------------------------------------+ +| %EFA% | Number of alarm frames in the event | ++--------+--------------------------------------------------------+ +| %EST% | Total score of the event | ++--------+--------------------------------------------------------+ +| %ESA% | Average score of the event | ++--------+--------------------------------------------------------+ +| %ESM% | Maximum score of the event | ++--------+--------------------------------------------------------+ +| %EP% | Path to the event | ++--------+--------------------------------------------------------+ +| %EPS% | Path to the event stream | ++--------+--------------------------------------------------------+ +| %EPI% | Path to the event images | ++--------+--------------------------------------------------------+ +| %EPI1% | Path to the first alarmed event image | ++--------+--------------------------------------------------------+ +| %EPIM% | Path to the (first) event image with the highest score | ++--------+--------------------------------------------------------+ +| %EI1% | Attach first alarmed event image | ++--------+--------------------------------------------------------+ +| %EIM% | Attach (first) event image with the highest score | ++--------+--------------------------------------------------------+ +| %EV% | Attach event mpeg video | ++--------+--------------------------------------------------------+ +| %MN% | Name of the monitor | ++--------+--------------------------------------------------------+ +| %MET% | Total number of events for the monitor | ++--------+--------------------------------------------------------+ +| %MEH% | Number of events for the monitor in the last hour | ++--------+--------------------------------------------------------+ +| %MED% | Number of events for the monitor in the last day | ++--------+--------------------------------------------------------+ +| %MEW% | Number of events for the monitor in the last week | ++--------+--------------------------------------------------------+ +| %MEM% | Number of events for the monitor in the last month | ++--------+--------------------------------------------------------+ +| %MEA% | Number of archived events for the monitor | ++--------+--------------------------------------------------------+ +| %MP% | Path to the monitor window | ++--------+--------------------------------------------------------+ +| %MPS% | Path to the monitor stream | ++--------+--------------------------------------------------------+ +| %MPI% | Path to the monitor recent image | ++--------+--------------------------------------------------------+ +| %FN% | Name of the current filter that matched | ++--------+--------------------------------------------------------+ +| %FP% | Path to the current filter that matched | ++--------+--------------------------------------------------------+ +| %ZP% | Path to your ZoneMinder console | ++--------+--------------------------------------------------------+ + +OPT_MESSAGE - In ZoneMinder you can create event filters that specify whether events that match certain criteria should have their details sent to you at a designated short message email address. This will allow you to be notified of events as soon as they occur. This option specifies whether this functionality should be available. The email created by this option will be brief and is intended to be sent to an SMS gateway or a minimal mail reader such as a mobile device or phone rather than a regular email reader. + +MESSAGE_ADDRESS - This option is used to define the short message email address that any events that match the appropriate filters will be sent to. + +MESSAGE_SUBJECT - This option is used to define the subject of the message that is sent for any events that match the appropriate filters. + +MESSAGE_BODY - This option is used to define the content of the message that is sent for any events that match the appropriate filters. + +NEW_MAIL_MODULES - Traditionally ZoneMinder has used the MIME::Entity perl module to construct and send notification emails and messages. Some people have reported problems with this module not being present at all or flexible enough for their needs. If you are one of those people this option allows you to select a new mailing method using MIME::Lite and Net::SMTP instead. This method was contributed by Ross Melin and should work for everyone but has not been extensively tested so currently is not selected by default. + +EMAIL_HOST - If you have chosen SMTP as the method by which to send notification emails or messages then this option allows you to choose which SMTP server to use to send them. The default of localhost may work if you have the sendmail, exim or a similar daemon running however you may wish to enter your ISP's SMTP mail server here. + +FROM_EMAIL - The emails or messages that will be sent to you informing you of events can appear to come from a designated email address to help you with mail filtering etc. An address of something like ZoneMinder\@your.domain is recommended. + +URL - The emails or messages that will be sent to you informing you of events can include a link to the events themselves for easy viewing. If you intend to use this feature then set this option to the url of your installation as it would appear from where you read your email, e.g. http://host.your.domain/zm.php. \ No newline at end of file diff --git a/docs/userguide/options/options_images.rst b/docs/userguide/options/options_images.rst new file mode 100644 index 000000000..3e8598425 --- /dev/null +++ b/docs/userguide/options/options_images.rst @@ -0,0 +1,44 @@ +Options - Images +---------------- + +.. image:: images/Options_images.png + +OPT_FFMPEG - ZoneMinder can optionally encode a series of video images into an MPEG encoded movie file for viewing, downloading or storage. This option allows you to specify whether you have the ffmpeg tools installed. Note that creating MPEG files can be fairly CPU and disk intensive and is not a required option as events can still be reviewed as video streams without it. + +PATH_FFMPEG - This path should point to where ffmpeg has been installed. + +FFMPEG_INPUT_OPTIONS - Ffmpeg can take many options on the command line to control the quality of video produced. This option allows you to specify your own set that apply to the input to ffmpeg (options that are given before the -i option). Check the ffmpeg documentation for a full list of options which may be used here. + +FFMPEG_OUTPUT_OPTIONS - Ffmpeg can take many options on the command line to control the quality of video produced. This option allows you to specify your own set that apply to the output from ffmpeg (options that are given after the -i option). Check the ffmpeg documentation for a full list of options which may be used here. The most common one will often be to force an output frame rate supported by the video encoder. + +FFMPEG_FORMATS - Ffmpeg can generate video in many different formats. This option allows you to list the ones you want to be able to select. As new formats are supported by ffmpeg you can add them here and be able to use them immediately. Adding a '*' after a format indicates that this will be the default format used for web video, adding '**' defines the default format for phone video. + +FFMPEG_OPEN_TIMEOUT - When Ffmpeg is opening a stream, it can take a long time before failing; certain circumstances even seem to be able to lock indefinitely. This option allows you to set a maximum time in seconds to pass before closing the stream and trying to reopen it again. + +JPEG_STREAM_QUALITY - When viewing a 'live' stream for a monitor ZoneMinder will grab an image from the buffer and encode it into JPEG format before sending it. This option specifies what image quality should be used to encode these images. A higher number means better quality but less compression so will take longer to view over a slow connection. By contrast a low number means quicker to view images but at the price of lower quality images. This option does not apply when viewing events or still images as these are usually just read from disk and so will be encoded at the quality specified by the previous options. + +MPEG_TIMED_FRAMES - When using streamed MPEG based video, either for live monitor streams or events, ZoneMinder can send the streams in two ways. If this option is selected then the timestamp for each frame, taken from it's capture time, is included in the stream. This means that where the frame rate varies, for instance around an alarm, the stream will still maintain it's 'real' timing. If this option is not selected then an approximate frame rate is calculated and that is used to schedule frames instead. This option should be selected unless you encounter problems with your preferred streaming method. + +MPEG_LIVE_FORMAT - When using MPEG mode ZoneMinder can output live video. However what formats are handled by the browser varies greatly between machines. This option allows you to specify a video format using a file extension format, so you would just enter the extension of the file type you would like and the rest is determined from that. The default of 'asf' works well under Windows with Windows Media Player but I'm currently not sure what, if anything, works on a Linux platform. If you find out please let me know! If this option is left blank then live streams will revert to being in motion jpeg format + +MPEG_REPLAY_FORMAT - When using MPEG mode ZoneMinder can replay events in encoded video format. However what formats are handled by the browser varies greatly between machines. This option allows you to specify a video format using a file extension format, so you would just enter the extension of the file type you would like and the rest is determined from that. The default of 'asf' works well under Windows with Windows Media Player and 'mpg', or 'avi' etc should work under Linux. If you know any more then please let me know! If this option is left blank then live streams will revert to being in motion jpeg format + +RAND_STREAM - Some browsers can cache the streams used by ZoneMinder. In order to prevent his a harmless random string can be appended to the url to make each invocation of the stream appear unique. + +OPT_CAMBOZOLA - Cambozola is a handy low fat cheese flavoured Java applet that ZoneMinder uses to view image streams on browsers such as Internet Explorer that don't natively support this format. If you use this browser it is highly recommended to install this from http://www.charliemouse.com/code/cambozola/ however if it is not installed still images at a lower refresh rate can still be viewed. + +PATH_CAMBOZOLA - Cambozola is a handy low fat cheese flavoured Java applet that ZoneMinder uses to view image streams on browsers such as Internet Explorer that don't natively support this format. If you use this browser it is highly recommended to install this from http://www.charliemouse.com/code/cambozola/ however if it is not installed still images at a lower refresh rate can still be viewed. Leave this as 'cambozola.jar' if cambozola is installed in the same directory as the ZoneMinder web client files. + +RELOAD_CAMBOZOLA - Cambozola allows for the viewing of streaming MJPEG however it caches the entire stream into cache space on the computer, setting this to a number > 0 will cause it to automatically reload after that many seconds to avoid filling up a hard drive. + +OPT_FFMPEG - ZoneMinder can optionally encode a series of video images into an MPEG encoded movie file for viewing, downloading or storage. This option allows you to specify whether you have the ffmpeg tools installed. Note that creating MPEG files can be fairly CPU and disk intensive and is not a required option as events can still be reviewed as video streams without it. + +PATH_FFMPEG - This path should point to where ffmpeg has been installed. + +FFMPEG_INPUT_OPTIONS - Ffmpeg can take many options on the command line to control the quality of video produced. This option allows you to specify your own set that apply to the input to ffmpeg (options that are given before the -i option). Check the ffmpeg documentation for a full list of options which may be used here. + +FFMPEG_OUTPUT_OPTIONS - Ffmpeg can take many options on the command line to control the quality of video produced. This option allows you to specify your own set that apply to the output from ffmpeg (options that are given after the -i option). Check the ffmpeg documentation for a full list of options which may be used here. The most common one will often be to force an output frame rate supported by the video encoder. + +FFMPEG_FORMATS - Ffmpeg can generate video in many different formats. This option allows you to list the ones you want to be able to select. As new formats are supported by ffmpeg you can add them here and be able to use them immediately. Adding a '*' after a format indicates that this will be the default format used for web video, adding '**' defines the default format for phone video. + +FFMPEG_OPEN_TIMEOUT - When Ffmpeg is opening a stream, it can take a long time before failing; certain circumstances even seem to be able to lock indefinitely. This option allows you to set a maximum time in seconds to pass before closing the stream and trying to reopen it again. diff --git a/docs/userguide/options/options_logging.rst b/docs/userguide/options/options_logging.rst new file mode 100644 index 000000000..75cfe4098 --- /dev/null +++ b/docs/userguide/options/options_logging.rst @@ -0,0 +1,42 @@ +Options - Logging +----------------- + +.. image:: images/Options_Logging.png + +LOG_LEVEL_SYSLOG - ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that goes to the system log. ZoneMinder binaries have always logged to the system log but now scripts and web logging is also included. To preserve the previous behaviour you should ensure this value is set to Info or Warning. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance. If you want debug you will also need to set a level and component below + +LOG_LEVEL_FILE - ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that goes to individual log files written by specific components. This is how logging worked previously and although useful for tracking down issues in specific components it also resulted in many disparate log files. To preserve this behaviour you should ensure this value is set to Info or Warning. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance though file output has less impact than the other options. If you want debug you will also need to set a level and component below + +LOG_LEVEL_WEBLOG - ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output from the web interface that goes to the httpd error log. Note that only web logging from PHP and JavaScript files is included and so this option is really only useful for investigating specific issues with those components. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance. If you want debug you will also need to set a level and component below + +LOG_LEVEL_DATABASE - ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that is written to the database. This is a new option which can make viewing logging output easier and more intuitive and also makes it easier to get an overall impression of how the system is performing. If you have a large or very busy system then it is possible that use of this option may slow your system down if the table becomes very large. Ensure you use the LOG_DATABASE_LIMIT option to keep the table to a manageable size. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance. If you want debug you will also need to set a level and component below + +LOG_DATABASE_LIMIT - If you are using database logging then it is possible to quickly build up a large number of entries in the Logs table. This option allows you to specify how many of these entries are kept. If you set this option to a number greater than zero then that number is used to determine the maximum number of rows, less than or equal to zero indicates no limit and is not recommended. You can also set this value to time values such as ' day' which will limit the log entries to those newer than that time. You can specify 'hour', 'day', 'week', 'month' and 'year', note that the values should be singular (no 's' at the end). The Logs table is pruned periodically so it is possible for more than the expected number of rows to be present briefly in the meantime. + +LOG_DEBUG" - ZoneMinder components usually support debug logging available to help with diagnosing problems. Binary components have several levels of debug whereas more other components have only one. Normally this is disabled to minimise performance penalties and avoid filling logs too quickly. This option lets you switch on other options that allow you to configure additional debug information to be output. Components will pick up this instruction when they are restarted. + +LOG_DEBUG_TARGET - There are three scopes of debug available. Leaving this option blank means that all components will use extra debug (not recommended). Setting this option to '_', e.g. _zmc, will limit extra debug to that component only. Setting this option to '__', e.g. '_zmc_m1' will limit extra debug to that instance of the component only. This is ordinarily what you probably want to do. To debug scripts use their names without the .pl extension, e.g. '_zmvideo' and to debug issues with the web interface use '_web'. You can specify multiple targets by separating them with '|' characters. + +LOG_DEBUG_LEVEL - There are 9 levels of debug available, with higher numbers being more debug and level 0 being no debug. However not all levels are used by all components. Also if there is debug at a high level it is usually likely to be output at such a volume that it may obstruct normal operation. For this reason you should set the level carefully and cautiously until the degree of debug you wish to see is present. Scripts and the web interface only have one level so this is an on/off type option for them. + +LOG_DEBUG_FILE - This option allows you to specify a different target for debug output. All components have a default log file which will norally be in /tmp or /var/log and this is where debug will be written to if this value is empty. Adding a path here will temporarily redirect debug, and other logging output, to this file. This option is a simple filename and you are debugging several components then they will all try and write to the same file with undesirable consequences. Appending a '+' to the filename will cause the file to be created with a '.' suffix containing your process id. In this way debug from each run of a component is kept separate. This is the recommended setting as it will also prevent subsequent runs from overwriting the same log. You should ensure that permissions are set up to allow writing to the file and directory specified here. + +LOG_CHECK_PERIOD - When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to indicate what period of historical events are used in this calculation. This value is expressed in seconds and is ignored if LOG_LEVEL_DATABASE is set to None. + +LOG_ALERT_WAR_COUNT - When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many warnings must have occurred within the defined time period to generate an overall system alert state. A value of zero means warnings are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None. + +LOG_ALERT_ERR_COUNT - When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many errors must have occurred within the defined time period to generate an overall system alert state. A value of zero means errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None. + +LOG_ALERT_FAT_COUNT - When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many fatal errors (including panics) must have occurred within the defined time period to generate an overall system alert state. A value of zero means fatal errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None. + +LOG_ALARM_WAR_COUNT - When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many warnings must have occurred within the defined time period to generate an overall system alarm state. A value of zero means warnings are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None. + +LOG_ALARM_ERR_COUNT - When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many errors must have occurred within the defined time period to generate an overall system alarm state. A value of zero means errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None. + +LOG_ALARM_FAT_COUNT - When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many fatal errors (including panics) must have occurred within the defined time period to generate an overall system alarm state. A value of zero means fatal errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None. + +RECORD_EVENT_STATS - This version of ZoneMinder records detailed information about events in the Stats table. This can help in profiling what the optimum settings are for Zones though this is tricky at present. However in future releases this will be done more easily and intuitively, especially with a large sample of events. The default option of 'yes' allows this information to be collected now in readiness for this but if you are concerned about performance you can switch this off in which case no Stats information will be saved. + +RECORD_DIAG_IMAGES - In addition to recording event statistics you can also record the intermediate diagnostic images that display the results of the various checks and processing that occur when trying to determine if an alarm event has taken place. There are several of these images generated for each frame and zone for each alarm or alert frame so this can have a massive impact on performance. Only switch this setting on for debug or analysis purposes and remember to switch it off again once no longer required. + +DUMP_CORES - When an unrecoverable error occurs in a ZoneMinder binary process is has traditionally been trapped and the details written to logs to aid in remote analysis. However in some cases it is easier to diagnose the error if a core file, which is a memory dump of the process at the time of the error, is created. This can be interactively analysed in the debugger and may reveal more or better information than that available from the logs. This option is recommended for advanced users only otherwise leave at the default. Note using this option to trigger core files will mean that there will be no indication in the binary logs that a process has died, they will just stop, however the zmdc log will still contain an entry. Also note that you may have to explicitly enable core file creation on your system via the 'ulimit -c' command or other means otherwise no file will be created regardless of the value of this option. \ No newline at end of file diff --git a/docs/userguide/options/options_network.rst b/docs/userguide/options/options_network.rst new file mode 100644 index 000000000..0ff0704f7 --- /dev/null +++ b/docs/userguide/options/options_network.rst @@ -0,0 +1,14 @@ +Options - Network +----------------- + +.. image:: images/Options_Network.png + +HTTP_VERSION - ZoneMinder can communicate with network cameras using either of the HTTP/1.1 or HTTP/1.0 standard. A server will normally fall back to the version it supports with no problem so this should usually by left at the default. However it can be changed to HTTP/1.0 if necessary to resolve particular issues. + +HTTP_UA - When ZoneMinder communicates with remote cameras it will identify itself using this string and it's version number. This is normally sufficient, however if a particular cameras expects only to communicate with certain browsers then this can be changed to a different string identifying ZoneMinder as Internet Explorer or Netscape etc. + +HTTP_TIMEOUT - When retrieving remote images ZoneMinder will wait for this length of time before deciding that an image is not going to arrive and taking steps to retry. This timeout is in milliseconds (1000 per second) and will apply to each part of an image if it is not sent in one whole chunk. + +MIN_RTP_PORT - When ZoneMinder communicates with MPEG4 capable cameras using RTP with the unicast method it must open ports for the camera to connect back to for control and streaming purposes. This setting specifies the minimum port number that ZoneMinder will use. Ordinarily two adjacent ports are used for each camera, one for control packets and one for data packets. This port should be set to an even number, you may also need to open up a hole in your firewall to allow cameras to connect back if you wish to use unicasting. + +MAX_RTP_PORT - When ZoneMinder communicates with MPEG4 capable cameras using RTP with the unicast method it must open ports for the camera to connect back to for control and streaming purposes. This setting specifies the maximum port number that ZoneMinder will use. Ordinarily two adjacent ports are used for each camera, one for control packets and one for data packets. This port should be set to an even number, you may also need to open up a hole in your firewall to allow cameras to connect back if you wish to use unicasting. You should also ensure that you have opened up at least two ports for each monitor that will be connecting to unicasting network cameras. \ No newline at end of file diff --git a/docs/userguide/options/options_paths.rst b/docs/userguide/options/options_paths.rst new file mode 100644 index 000000000..024998194 --- /dev/null +++ b/docs/userguide/options/options_paths.rst @@ -0,0 +1,22 @@ +Options - Paths +--------------- + +.. image:: images/Options_Paths.png + +ZM_DIR_EVENTS - This is the path to the events directory where all the event images and other miscellaneous files are stored. CAUTION: The directory you specify here cannot be outside the web root. This is a common mistake. Most users should never change this value. If you intend to record events to a second disk or network share, then you should mount the drive or share directly to the ZoneMinder events folder or follow the instructions in the ZoneMinder Wiki titled Using a dedicated Hard Drive. + +USE_DEEP_STORAGE - Traditionally ZoneMinder stores all events for a monitor in one directory for that monitor. This is simple and efficient except when you have very large amounts of events. Some filesystems are unable to store more than 32k files in one directory and even without this limitation, large numbers of files in a directory can slow creation and deletion of files. This option allows you to select an alternate method of storing events by year/month/day/hour/min/second which has the effect of separating events out into more directories, resulting in less per directory, and also making it easier to manually navigate to any events that may have happened at a particular time or date. + +DIR_IMAGES - ZoneMinder generates a myriad of images, mostly of which are associated with events. For those that aren't this is where they go. CAUTION: The directory you specify here cannot be outside the web root. This is a common mistake. Most users should never change this value. If you intend to save images to a second disk or network share, then you should mount the drive or share directly to the ZoneMinder images folder or follow the instructions in the ZoneMinder Wiki titled Using a dedicated Hard Drive. + +DIR_SOUNDS - ZoneMinder can optionally play a sound file when an alarm is detected. This indicates where to look for this file. CAUTION: The directory you specify here cannot be outside the web root. Most users should never change this value. + +PATH_ZMS - The ZoneMinder streaming server is required to send streamed images to your browser. It will be installed into the cgi-bin path given at configuration time. This option determines what the web path to the server is rather than the local path on your machine. Ordinarily the streaming server runs in parser-header mode however if you experience problems with streaming you can change this to non-parsed-header (nph) mode by changing 'zms' to 'nph-zms'. + +PATH_MAP - ZoneMinder has historically used IPC shared memory for shared data between processes. This has it's advantages and limitations. This version of ZoneMinder can use an alternate method, mapped memory, instead with can be enabled with the --enable--mmap directive to configure. This requires less system configuration and is generally more flexible. However it requires each shared data segment to map onto a filesystem file. This option indicates where those mapped files go. You should ensure that this location has sufficient space for these files and for the best performance it should be a tmpfs file system or ramdisk otherwise disk access may render this method slower than the regular shared memory one. + +PATH_SOCKS - ZoneMinder generally uses Unix domain sockets where possible. This reduces the need for port assignments and prevents external applications from possibly compromising the daemons. However each Unix socket requires a .sock file to be created. This option indicates where those socket files go. + +PATH_LOGS - There are various daemons that are used by ZoneMinder to perform various tasks. Most generate helpful log files and this is where they go. They can be deleted if not required for debugging. + +PATH_SWAP - Buffered playback requires temporary swap images to be stored for each instance of the streaming daemons. This option determines where these images will be stored. The images will actually be stored in sub directories beneath this location and will be automatically cleaned up after a period of time. diff --git a/docs/userguide/options/options_servers.rst b/docs/userguide/options/options_servers.rst new file mode 100644 index 000000000..83e146871 --- /dev/null +++ b/docs/userguide/options/options_servers.rst @@ -0,0 +1,12 @@ +Options - Servers +----------------- + +.. image:: images/Options_Servers.png + +Servers tab is used for setting up multiple ZoneMinder servers sharing the same database and using a shared file share for all event data. To add a new server use the Add Server button. All that is required is a Name for the Server and Hostname. + +To delete a server mark that server and click the Delete button. + +Please note that all servers must have a functional web UI as the live view must come from the monitor's host server. + +On each server, you will have to edit /etc/zm/zm.conf and set either ZM_SERVER_NAME= diff --git a/docs/userguide/options/options_system.rst b/docs/userguide/options/options_system.rst new file mode 100644 index 000000000..7df3d259a --- /dev/null +++ b/docs/userguide/options/options_system.rst @@ -0,0 +1,48 @@ +Options - System +---------------- + +.. image:: images/Options_System.png + +LANG_DEFAULT - ZoneMinder allows the web interface to use languages other than English if the appropriate language file has been created and is present. This option allows you to change the default language that is used from the shipped language, British English, to another language. + +OPT_USE_AUTH - ZoneMinder can run in two modes. The simplest is an entirely unauthenticated mode where anyone can access ZoneMinder and perform all tasks. This is most suitable for installations where the web server access is limited in other ways. The other mode enables user accounts with varying sets of permissions. Users must login or authenticate to access ZoneMinder and are limited by their defined permissions. Authenticated mode alone should not be relied up for securing Internet connected ZoneMinder. + +AUTH_TYPE - ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authentication 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured ion ZoneMinder. + +AUTH_RELAY - When ZoneMinder is running in authenticated mode it can pass user details between the web pages and the back end processes. There are two methods for doing this. This first is to use a time limited hashed string which contains no direct username or password details, the second method is to pass the username and passwords around in plaintext. This method is not recommend except where you do not have the md5 libraries available on your system or you have a completely isolated system with no external access. You can also switch off authentication relaying if your system is isolated in other ways. + +AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and password. Although these string are reasonably secure the addition of a random secret increases security substantially. + +AUTH_HASH_IPS - When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help. + +AUTH_HASH_LOGINS - The normal process for logging into ZoneMinder is via the login screen with username and password. In some circumstances it may be desirable to allow access directly to one or more pages, for instance from a third party application. If this option is enabled then adding an 'auth' parameter to any request will include a shortcut login bypassing the login screen, if not already logged in. As authentication hashes are time and, optionally, IP limited this can allow short-term access to ZoneMinder screens from other web pages etc. In order to use this the calling application will hae to generate the authentication hash itself and ensure it is valid. If you use this option you should ensure that you have modified the ZM_AUTH_HASH_SECRET to somethign unique to your system. + +OPT_FAST_DELETE - Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if your are trying to do a lot of events at once. It is recommended that you set this option which means that the browser client only deletes the key entries in the events table, which means the events will no longer appear in the listing, and leaves the zmaudit daemon to clear up the rest later. + +FILTER_RELOAD_DELAY - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often in seconds the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value. + +FILTER_EXECUTE_INTERVAL - ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often the filters are executed on the saved event in the database. If you want a rapid response to new events this should be a smaller value, however this may increase the overall load on the system and affect performance of other elements. + +MAX_RESTART_DELAY - The zmdc (zm daemon control) process controls when processeses are started or stopped and will attempt to restart any that fail. If a daemon fails frequently then a delay is introduced between each restart attempt. If the daemon stills fails then this delay is increased to prevent extra load being placed on the system by continual restarts. This option controls what this maximum delay is. + +WATCH_CHECK_INTERVAL - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines how often the daemons are checked. + +WATCH_MAX_DELAY - The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinitely). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above. + +RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. This is recommended for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to not have zmaudit running unconditionally and schedule occasional checks at other, more convenient, times. + +AUDIT_CHECK_INTERVAL - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed. + +OPT_FRAME_SERVER - In some circumstances it is possible for a slow disk to take so long writing images to disk that it causes the analysis daemon to fall behind especially during high frame rate events. Setting this option to yes enables a frame server daemon (zmf) which will be sent the images from the analysis daemon and will do the actual writing of images itself freeing up the analysis daemon to get on with other things. Should this transmission fail or other permanent or transient error occur, this function will fall back to the analysis daemon. + +FRAME_SOCKET_SIZE - For large captured images it is possible for the writes from the analysis daemon to the frame server to fail as the amount to be written exceeds the default buffer size. While the images are then written by the analysis daemon so no data is lost, it defeats the object of the frame server daemon in the first place. You can use this option to indicate that a larger buffer size should be used. Note that you may have to change the existing maximum socket buffer size on your system via sysctl (or in /proc/sys/net/core/wmem_max) to allow this new size to be set. Alternatively you can change the default buffer size on your system in the same way in which case that will be used with no change necessary in this option + +OPT_CONTROL - ZoneMinder includes limited support for controllable cameras. A number of sample protocols are included and others can easily be added. If you wish to control your cameras via ZoneMinder then select this option otherwise if you only have static cameras or use other control methods then leave this option off. + +OPT_TRIGGERS - ZoneMinder can interact with external systems which prompt or cancel alarms. This is done via the zmtrigger.pl script. This option indicates whether you want to use these external triggers. Most people will say no here. + +CHECK_FOR_UPDATES - From ZoneMinder version 1.17.0 onwards new versions are expected to be more frequent. To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable +UPDATE_CHECK_PROXY - If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of http://:/ + +SHM_KEY - ZoneMinder uses shared memory to speed up communication between modules. To identify the right area to use shared memory keys are used. This option controls what the base key is, each monitor will have it's Id or'ed with this to get the actual key used. You will not normally need to change this value unless it clashes with another instance of ZoneMinder on the same machine. Only the first four hex digits are used, the lower four will be masked out and ignored. + diff --git a/docs/userguide/options/options_upload.rst b/docs/userguide/options/options_upload.rst new file mode 100644 index 000000000..da4baa076 --- /dev/null +++ b/docs/userguide/options/options_upload.rst @@ -0,0 +1,32 @@ +Options - Upload +---------------- + +.. image:: images/Options_upload.png + +OPT_UPLOAD - In ZoneMinder you can create event filters that specify whether events that match certain criteria should be uploaded to a remote server for archiving. This option specifies whether this functionality should be available + +UPLOAD_ARCH_FORMAT - Uploaded events may be stored in either .tar or .zip format, this option specifies which. Note that to use this you will need to have the Archive::Tar and/or Archive::Zip perl modules installed. + +UPLOAD_ARCH_COMPRESS - When the archive files are created they can be compressed. However in general since the images are compressed already this saves only a minimal amount of space versus utilising more CPU in their creation. Only enable if you have CPU to waste and are limited in disk space on your remote server or bandwidth. + +UPLOAD_ARCH_ANALYSE - When the archive files are created they can contain either just the captured frames or both the captured frames and, for frames that caused an alarm, the analysed image with the changed area highlighted. This option controls files are included. Only include analysed frames if you have a high bandwidth connection to the remote server or if you need help in figuring out what caused an alarm in the first place as archives with these files in can be considerably larger. + +UPLOAD_PROTOCOL - ZoneMinder can upload events to a remote server using either FTP or SFTP. Regular FTP is widely supported but not necessarily very secure whereas SFTP (Secure FTP) runs over an ssh connection and so is encrypted and uses regular ssh ports. Note that to use this you will need to have the appropriate perl module, either Net::FTP or Net::SFTP installed depending on your choice. + +UPLOAD_HOST - You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the name, or ip address, of the server to use. + +UPLOAD_PORT - You can use filters to instruct ZoneMinder to upload events to a remote server. If you are using the SFTP protocol then this option allows you to specify a particular port to use for connection. If this option is left blank then the default, port 22, is used. This option is ignored for FTP uploads. + +UPLOAD_USER - You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the username that ZoneMinder should use to log in for transfer. + +UPLOAD_PASS - You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the password that ZoneMinder should use to log in for transfer. If you are using certificate based logins for SFTP servers you can leave this option blank. + +UPLOAD_LOC_DIR - You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the local directory that ZoneMinder should use for temporary upload files. These are files that are created from events, uploaded and then deleted. + +UPLOAD_REM_DIR - You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the remote directory that ZoneMinder should use to upload event files to. + +UPLOAD_TIMEOUT - You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the maximum inactivity timeout (in seconds) that should be tolerated before ZoneMinder determines that the transfer has failed and closes down the connection. + +UPLOAD_FTP_PASSIVE - You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates that ftp transfers should be done in passive mode. This uses a single connection for all ftp activity and, whilst slower than active transfers, is more robust and likely to work from behind filewalls. This option is ignored for SFTP transfers. + +UPLOAD_DEBUG - You can use filters to instruct ZoneMinder to upload events to a remote server. If you are having (or expecting) troubles with uploading events then setting this to 'yes' permits additional information to be generated by the underlying transfer modules and included in the logs. \ No newline at end of file diff --git a/docs/userguide/options/options_users.rst b/docs/userguide/options/options_users.rst new file mode 100644 index 000000000..4599cea9b --- /dev/null +++ b/docs/userguide/options/options_users.rst @@ -0,0 +1,10 @@ +Options - Users +--------------- + +.. image:: images/Options_Users.png + +In this section you will see a list of the current users defined on the system. You can also add or delete users from here. It is recommended you do not delete the admin user unless you have created another fully privileged user to take over the same role. Each user is defined with a name and password (which is hidden) as well as an enabled setting which you can use to temporarily enable or disable users, for example a guest user for limited time access. As well as that there is a language setting that allows you to define user specific languages. Setting a language here that is different than the system language will mean that when that user logs in they will have the web interface presented in their own language rather than the system default, if it is available. + +There are also five values that define the user permissions, these are ‘Stream’, ‘Events’, ‘Control’, ‘Monitors’ and ‘System’ Each can have values of ‘None’, ‘View’ or ‘Edit’ apart from ‘Stream’ which has no ‘Edit’ setting. These values cover access to the following areas; ‘Stream’ defines whether a user is allowed to view the ‘live’ video feeds coming from the cameras. You may wish to allow a user to view historical events only in which case this setting should be ‘none’. The ‘Events’ setting determines whether a user can view and modify or delete any retained historical events. The ‘Control’ setting allows you to indicate whether the user is able to control any Pan/Tilt/Zoom type cameras you may have on your system. The ‘Monitors’ setting specifies whether a user can see the current monitor settings and change them. Finally the ‘System’ setting determines whether a user can view or modify the system settings as a whole, such as options and users or controlling the running of the system as a whole. + +As well as these settings there is also a ‘Bandwidth’ setting which can be used to limit the maximum bandwidth that a user can view at and a ‘Monitor Ids’ setting that can be used for non-’System’ users to restrict them to only being able to access streams, events or monitors for the given monitors ids as a comma separated list with no spaces. If a user with ‘Monitors’ edit privileges is limited to specific monitors here they will not be able to add or delete monitors but only change the details of those they have access to. If a user has ‘System’ privileges then the ‘Monitors Ids’ setting is ignored and has no effect.’ \ No newline at end of file diff --git a/docs/userguide/options/options_web.rst b/docs/userguide/options/options_web.rst new file mode 100644 index 000000000..9425e3fe8 --- /dev/null +++ b/docs/userguide/options/options_web.rst @@ -0,0 +1,30 @@ +Options - Web +------------- + +.. image:: images/Options_web.png + +WEB_TITLE_PREFIX - If you have more than one installation of ZoneMinder it can be helpful to display different titles for each one. Changing this option allows you to customise the window titles to include further information to aid identification. + +WEB_RESIZE_CONSOLE - Traditionally the main ZoneMinder web console window has resized itself to shrink to a size small enough to list only the monitors that are actually present. This is intended to make the window more unobtrusize but may not be to everyones tastes, especially if opened in a tab in browsers which support this kind if layout. Switch this option off to have the console window size left to the users preference + +WEB_POPUP_ON_ALARM - When viewing a live monitor stream you can specify whether you want the window to pop to the front if an alarm occurs when the window is minimised or behind another window. This is most useful if your monitors are over doors for example when they can pop up if someone comes to the doorway. + +WEB_SOUND_ON_ALARM - When viewing a live monitor stream you can specify whether you want the window to play a sound to alert you if an alarm occurs. + +WEB_ALARM_SOUND - You can specify a sound file to play if an alarm occurs whilst you are watching a live monitor stream. So long as your browser understands the format it does not need to be any particular type. This file should be placed in the sounds directory defined earlier. + +WEB_COMPACT_MONTAGE - The montage view shows the output of all of your active monitors in one window. This include a small menu and status information for each one. This can increase the web traffic and make the window larger than may be desired. Setting this option on removes all this extraneous information and just displays the images. + +WEB_EVENT_SORT_FIELD - Events in lists can be initially ordered in any way you want. This option controls what field is used to sort them. You can modify this ordering from filters or by clicking on headings in the lists themselves. Bear in mind however that the 'Prev' and 'Next' links, when scrolling through events, relate to the ordering in the lists and so not always to time based ordering. + +WEB_EVENT_SORT_ORDER - Events in lists can be initially ordered in any way you want. This option controls what order (ascending or descending) is used to sort them. You can modify this ordering from filters or by clicking on headings in the lists themselves. Bear in mind however that the 'Prev' and 'Next' links, when scrolling through events, relate to the ordering in the lists and so not always to time based ordering. + +WEB_EVENTS_PER_PAGE - In the event list view you can either list all events or just a page at a time. This option controls how many events are listed per page in paged mode and how often to repeat the column headers in non-paged mode. + +WEB_LIST_THUMBS - Ordinarily the event lists just display text details of the events to save space and time. By switching this option on you can also display small thumbnails to help you identify events of interest. The size of these thumbnails is controlled by the following two options. + +WEB_LIST_THUMB_WIDTH - This options controls the width of the thumbnail images that appear in the event lists. It should be fairly small to fit in with the rest of the table. If you prefer you can specify a height instead in the next option but you should only use one of the width or height and the other option should be set to zero. If both width and height are specified then width will be used and height ignored. + +WEB_LIST_THUMB_HEIGHT - This options controls the height of the thumbnail images that appear in the event lists. It should be fairly small to fit in with the rest of the table. If you prefer you can specify a width instead in the previous option but you should only use one of the width or height and the other option should be set to zero. If both width and height are specified then width will be used and height ignored. + +WEB_USE_OBJECT_TAGS - There are two methods of including media content in web pages. The most common way is use the EMBED tag which is able to give some indication of the type of content. However this is not a standard part of HTML. The official method is to use OBJECT tags which are able to give more information allowing the correct media viewers etc to be loaded. However these are less widely supported and content may be specifically tailored to a particular platform or player. This option controls whether media content is enclosed in EMBED tags only or whether, where appropriate, it is additionally wrapped in OBJECT tags. Currently OBJECT tags are only used in a limited number of circumstances but they may become more widespread in the future. It is suggested that you leave this option on unless you encounter problems playing some content. \ No newline at end of file diff --git a/docs/userguide/options/options_x10.rst b/docs/userguide/options/options_x10.rst new file mode 100644 index 000000000..0662ebe7e --- /dev/null +++ b/docs/userguide/options/options_x10.rst @@ -0,0 +1,12 @@ +Options - X10 +------------- + +.. image:: images/Options_X10.png + +OPT_X10 - If you have an X10 Home Automation setup in your home you can use ZoneMinder to initiate or react to X10 signals if your computer has the appropriate interface controller. This option indicates whether X10 options will be available in the browser client. + +X10_DEVICE - If you have an X10 controller device (e.g. XM10U) connected to your computer this option details which port it is connected on, the default of /dev/ttyS0 maps to serial or com port 1. + +X10_HOUSE_CODE - X10 devices are grouped together by identifying them as all belonging to one House Code. This option details what that is. It should be a single letter between A and P. + +X10_DB_RELOAD_INTERVAL - The zmx10 daemon periodically checks the database to find out what X10 events trigger, or result from, alarms. This option determines how frequently this check occurs, unless you change this area frequently this can be a fairly large value. \ No newline at end of file diff --git a/docs/userguide/viewevents.rst b/docs/userguide/viewevents.rst index 8540da907..70eea0032 100644 --- a/docs/userguide/viewevents.rst +++ b/docs/userguide/viewevents.rst @@ -1,13 +1,36 @@ Viewing Events ============== -From the monitor or filtered events listing you can now click on an event to view it in more detail. If you have streaming capability you will see a series of images that make up the event. Under that you should also see a progress bar. Depending on your configuration this will either be static or will be filled in to indicate how far through the event you are. By default this functionality is turned off for low bandwidth settings as the image delivery tends to not be able to keep up with real-time and the progress bar cannot take this into account. Regardless of whether the progress bar updates, you can click on it to navigate to particular points in the events. + +From the monitor or filtered events listing you can now click on an event to view it in more detail. + +This is an example view that shows events for a specific monitor: + +.. image:: images/viewevents-main.png + + +If you have streaming capability you will see a series of images that make up the event. Under that you should also see a progress bar. Depending on your configuration this will either be static or will be filled in to indicate how far through the event you are. By default this functionality is turned off for low bandwidth settings as the image delivery tends to not be able to keep up with real-time and the progress bar cannot take this into account. Regardless of whether the progress bar updates, you can click on it to navigate to particular points in the events. You will also see a link to allow you to view the still images themselves. If you don't have streaming then you will be taken directly to this page. The images themselves are thumbnail size and depending on the configuration and bandwidth you have chosen will either be the full images scaled in your browser of actual scaled images. If it is the latter, if you have low bandwidth for example, it may take a few seconds to generate the images. If thumbnail images are required to be generated, they will be kept and not re-generated in future. Once the images appear you can mouse over them to get the image sequence number and the image score. -You will notice for the first time that alarm images now contain an overlay outlining the blobs that represent the alarmed area. This outline is in the colour defined for that zone and lets you see what it was that caused the alarm. Clicking on one of the thumbnails will take you to a full size window where you can see the image in all its detail and scroll through the various images that make up the event. If you have the ZM_RECORD_EVENT_STATS option on, you will be able to click the 'Stats' link here and get some analysis of the cause of the event. Should you determine that you don't wish to keep the event, clicking on Delete will erase it from the database and file system. Returning to the event window, other options here are renaming the event to something more meaningful, refreshing the window to replay the event stream, deleting the event, switching between streamed and still versions of the event (if supported) and generating an MPEG video of the event (if supported). +Here is an example of viewing an event stream: + +.. image:: images/viewevents-stream.png + :width: 650px + +* **A**: Administrative Event options on the event including viewing individual frames +* **B**: The actual image stream +* **C**: Navigation control +* **D**: You can switch between watching a single event or Continuous mode (where it advances to the next event after playback is complete) +* **E**: Event progress bar - how much of the current event has been played back + +You will notice for the first time that alarm images now contain an overlay outlining the blobs that represent the alarmed area. This outline is in the colour defined for that zone and lets you see what it was that caused the alarm. Clicking on one of the thumbnails will take you to a full size window where you can see the image in all its detail and scroll through the various images that make up the event. If you have the ZM_RECORD_EVENT_STATS option on, you will be able to click the 'Stats' link here and get some analysis of the cause of the event. + +More details on the Administrative Event options (A) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Should you determine that you don't wish to keep the event, clicking on Delete will erase it from the database and file system. Returning to the event window, other options here are renaming the event to something more meaningful, refreshing the window to replay the event stream, deleting the event, switching between streamed and still versions of the event (if supported) and generating an MPEG video of the event (if supported). These last two options require further explanation. Archiving an event means that it is kept to one side and not displayed in the normal event listings unless you specifically ask to view the archived events. This is useful for keeping events that you think may be important or just wish to protect. Once an event is archived it can be deleted or unarchived but you cannot accidentally delete it when viewing normal unarchived events. -The final option of generating an MPEG video is still somewhat experimental and its usefulness may vary. It uses the open source ffmpeg encoder to generate short videos, which will be downloaded to your browsing machine or viewed in place. When using the ffmpeg encoder, ZoneMinder will attempt to match the duration of the video with the duration of the event. Ffmpeg has a particularly rich set of options and you can specify during configuration which additional options you may wish to include to suit your preferences. In particular you may need to specify additional, or different, options if you are creating videos of events with particularly slow frame rates as some codecs only support certain ranges of frame rates. A common value for FFMPEG_OUTPUT_OPTIONS under Options > Images might be '-r 25 -b 800k' for 25 fps and 800 kbps. Details of these options can be found in the [http://ffmpeg.org/ffmpeg-doc.html documentation] for the encoders and is outside the scope of this document. +The final option of generating an MPEG video is still somewhat experimental and its usefulness may vary. It uses the open source ffmpeg encoder to generate short videos, which will be downloaded to your browsing machine or viewed in place. When using the ffmpeg encoder, ZoneMinder will attempt to match the duration of the video with the duration of the event. Ffmpeg has a particularly rich set of options and you can specify during configuration which additional options you may wish to include to suit your preferences. In particular you may need to specify additional, or different, options if you are creating videos of events with particularly slow frame rates as some codecs only support certain ranges of frame rates. A common value for FFMPEG_OUTPUT_OPTIONS under Options > Images might be ``'-r 25 -b 800k'`` for 25 fps and 800 kbps. Details of these options can be found in the `documentation `__ for the encoders and is outside the scope of this document. Building an MPEG video, especially for a large event, can take some time and should not be undertaken lightly as the effect on your host box of many CPU intensive encoders will not be good. However once a video has been created for an event it will be kept so subsequent viewing will not incur the generation overhead. Videos can also be included in notification emails, however care should be taken when using this option as for many frequent events the penalty in CPU and disk space can quickly mount up. diff --git a/docs/userguide/viewmonitors.rst b/docs/userguide/viewmonitors.rst index 86c512a8e..1c67c4a25 100644 --- a/docs/userguide/viewmonitors.rst +++ b/docs/userguide/viewmonitors.rst @@ -1,9 +1,16 @@ Viewing Monitors ================ -As this point you should have one or more Monitors running with one or more Zones each. Returning to the main Console window you will see your monitors listed once more. The columns not explored so far are the Monitor name, and various event totals for certain periods of time. Clicking on any of the event totals will bring up a variation on the same window but click on the Monitor name for now. If it is not a link then this means that that monitor is not running so ensure that you have started ZoneMinder and that your Monitor function is not set to ‘None’. If the link works, clicking on it will pop another window up which should be scaled to contain a heading, an image from your monitor, a status and a list of recent events if any have been generated. +ZoneMinder allows you to view a live feed of your configured monitors. Once can access this view by clicking on the "Name" column of any of the monitors -Depending on whether you are able to view a streamed image or not the image frame will either be this stream or a series of stills. You have the option to change from one to the other (if available) at the centre of the top heading. Also along the top are a handful of other links. These let you change the scale of the image stream, modify image settings (for local devices) or close the window. If you have cameras that can be controlled, a ‘Control’ link should also be present which is described below. +.. image:: images/viewmonitor-main.png + :width: 500px + + +Clicking on the name produces a view similar to this: + +.. image:: images/viewmonitor-stream.png + :width: 500px The image should be self-explanatory but if it looks like garbage it is possible that the video configuration is wrong so look in your system error log and check for or report anything unusual. The centre of the window will have a tiny frame that just contains a status; this will be 'Idle', 'Alarm' or 'Alert' depending on the function of the Monitor and what's going on in the field of view. Idle means nothing is happening, Alarm means there is an alarm in progress and Alert means that an alarm has happened and the monitor is ‘cooling down’, if another alarm is generated in this time it will just become part of the same event. These indicators are colour coded in green, red and amber. diff --git a/misc/CMakeLists.txt b/misc/CMakeLists.txt index c09f630e0..1b3bd7c5a 100644 --- a/misc/CMakeLists.txt +++ b/misc/CMakeLists.txt @@ -7,6 +7,7 @@ configure_file(syslog.conf.in "${CMAKE_CURRENT_BINARY_DIR}/syslog.conf" @ONLY) configure_file(com.zoneminder.systemctl.policy.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.policy" @ONLY) configure_file(com.zoneminder.systemctl.rules.in "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.rules" @ONLY) configure_file(zoneminder.service.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder.service" @ONLY) +configure_file(zoneminder-tmpfiles.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zoneminder-tmpfiles.conf" @ONLY) # Do not install the misc files by default #install(FILES "${CMAKE_CURRENT_BINARY_DIR}/apache.conf" "${CMAKE_CURRENT_BINARY_DIR}/logrotate.conf" "${CMAKE_CURRENT_BINARY_DIR}/syslog.conf" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/misc") diff --git a/misc/Makefile.am b/misc/Makefile.am deleted file mode 100644 index b854d66f6..000000000 --- a/misc/Makefile.am +++ /dev/null @@ -1,16 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -EXTRA_DIST = \ - apache.conf.in \ - logrotate.conf.in \ - syslog.conf.in \ - zoneminder.service.in \ - com.zoneminder.systemctl.policy.in \ - com.zoneminder.systemctl.rules.in - -polkit_actiondir = @POLKIT_PREFIX@/share/polkit-1/actions -dist_polkit_action_DATA = com.zoneminder.systemctl.policy - -polkit_rulesdir = @POLKIT_PREFIX@/share/polkit-1/rules.d -dist_polkit_rules_DATA = com.zoneminder.systemctl.rules - diff --git a/misc/apache.conf.in b/misc/apache.conf.in index 06c9beb15..1ea6ed7c1 100644 --- a/misc/apache.conf.in +++ b/misc/apache.conf.in @@ -14,17 +14,26 @@ AllowOverride All
- ScriptAlias /cgi-bin/ "@CGI_PREFIX@" + ScriptAlias /cgi-bin "@CGI_PREFIX@" Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch AllowOverride All + + # Apache 2.4 + Require all granted + + + # Apache 2.2 + Order deny,allow + Allow from all + # Use the first option to have Apache logs written to the general log # directory, or the second to have them written to the regular Apache # directory (you may have to change the path to that used on your system) ErrorLog @ZM_LOGDIR@/apache-error.log - ErrorLog /var/log/httpd/zm-error.log + #ErrorLog /var/log/httpd/zm-error.log # Possible values include: debug, info, notice, warn, error, crit, # alert, emerg. @@ -34,6 +43,6 @@ # directory, or the second to have them written to the regular Apache # directory (you may have to change the path to that used on your system) CustomLog @ZM_LOGDIR@/apache-access.log combined - CustomLog /var/log/httpd/zm-access.log combined + #CustomLog /var/log/httpd/zm-access.log combined diff --git a/misc/logcheck b/misc/logcheck new file mode 100644 index 000000000..28940a6cc --- /dev/null +++ b/misc/logcheck @@ -0,0 +1,29 @@ +zmc_m[[:digit:]]+\[[[:digit:]]+\]: INF \[[[:alnum:]]+: [[:digit:]]+ - Capturing at [.[:digit:]]+ fps\]$ +zma_m[[:digit:]]+\[[[:digit:]]+\]: INF \[[[:alnum:]]+: [[:digit:]]+ - Processing at [.[:digit:]]+ fps\]$ +zmc_m[[:digit:]]+\[[[:digit:]]+\]: INF \[Got signal 15 \(Terminated\), exiting\]$ +zmc_m[[:digit:]]+\[[[:digit:]]+\]: INF \[Starting Capture\]$ +zmc_m[[:digit:]]+\[[[:digit:]]+\]: ERR \[(Connection dropped by remote end|Unable to read (sub)?header|Unable to get response(, disconnecting)?|Select error: Interrupted system call|Can't connect to remote camera: Interrupted system call|Unable to connect to (the remote )?camera(, aborting)?)\]$ +zmc_m[[:digit:]]+\[[[:digit:]]+\]: WAR \[(Unable to capture image, retrying|Select timed out)\]$ +zmaudit\[[[:digit:]]+\]: INF \[Deleted [[:digit:]]+ log table entries by time\]$ +zmdc\[[[:digit:]]+\]: INF \[Starting pending process, [. [:alnum:]-]+\]$ +zmdc\[[[:digit:]]+\]: INF \['[. [:alnum:]-]+' start(ed|ing) at [/: [:digit:]]+(, pid = [[:digit:]]+)?\]$ +zmwatch\[[[:digit:]]+\]: INF \[Watchdog starting\]$ +zmwatch\[[[:digit:]]+\]: INF \[Watchdog pausing for 30 seconds\]$ +zmc_m3\[[[:digit:]]+\]: ERR \[Can't connect to remote camera: (No route to host|Connection refused)\]$ +zmdc\[[[:digit:]]+\]: ERR \['zmc -m 3' exited abnormally, exit status 255\]$ +zmc_m1\[[[:digit:]]+\]: ERR-zm_remote_camera_rtsp.cpp/302 \[Error while decoding frame 0\]$ +zmc_m1\[[[:digit:]]+\]: ERR-zm_remote_camera_rtsp.cpp/303 \[256: [ [:xdigit:]]+\]$ +zma_m[[:digit:]]+\[[[:digit:]]+\]: INF \[[[:alnum:]]+: [[:digit:]]+ - Gone into alarm state\]$ +zma_m[[:digit:]]+\[[[:digit:]]+\]: INF \[[[:alnum:]]+: [[:digit:]]+ - Opening new event [[:digit:]]+, alarm start\]$ +zma_m[[:digit:]]+\[[[:digit:]]+\]: INF \[[[:alnum:]]+: [[:digit:]]+ - Gone into alert state\]$ +zma_m[[:digit:]]+\[[[:digit:]]+\]: WAR \[Waiting for capture daemon\]$ +zma_m[[:digit:]]+\[[[:digit:]]+\]: INF \[[[:alnum:]]+: [[:digit:]]+ - Gone back into alarm state\]$ +zma_m[[:digit:]]+\[[[:digit:]]+\]: INF \[[[:alnum:]]+: [[:digit:]]+ - Left alarm state \([[:digit:]]+\) - [[:digit:]]+\([[:digit:]]+\) images\]$ +zma_m[[:digit:]]+\[[[:digit:]]+\]: INF \[[[:alnum:]]+: [[:digit:]]+ - Closing event [[:digit:]]+, alarm end\]$ +zmaudit\[[[:digit:]]+\]: INF \[Filesystem event '[[:digit:]]+/[[:digit:]]+' does not exist in database\]$ +zmaudit\[[[:digit:]]+\]: INF \[deleting\]$ +zmaudit\[[[:digit:]]+\]: INF \[Found orphaned frame records for event '[[:digit:]]+'\]$ +zmfilter\[[[:digit:]]+\]: INF \[Deleting event [[:digit:]]+\]$ +zmtelemetry\[[[:digit:]]+\]: INF \[Telemetry data uploaded successfully.\]$ +zmtelemetry\[[[:digit:]]+\]: INF \[Sending data to ZoneMinder Telemetry server.\]$ +zmtelemetry\[[[:digit:]]+\]: INF \[Collec?ting data to send to ZoneMinder Telemetry server.\]$ diff --git a/misc/zoneminder-tmpfiles.conf.in b/misc/zoneminder-tmpfiles.conf.in new file mode 100644 index 000000000..79b27f909 --- /dev/null +++ b/misc/zoneminder-tmpfiles.conf.in @@ -0,0 +1,4 @@ +D @ZM_RUNDIR@ 0755 @WEB_USER@ @WEB_GROUP@ +D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@ +D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@ + diff --git a/onvif/README b/onvif/README new file mode 100644 index 000000000..25065e18a --- /dev/null +++ b/onvif/README @@ -0,0 +1,102 @@ +ZoneMinder ONVIF support + +Copyright (C) 2016 Jan M. Hochstein + +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. +-------------------------------------------------------------------------- + +1. PURPOSE +---------- + +The files in this folder and its subfolders add ONVIF support to zoneminder. +The ONVIF consortium (www.onvif.org) publishes the ONVIF specifications. These +specifications describe ways of connecting to and communicating with network +video devices. Hardware and software that adhere to these specifications are +meant to be interoperable. + + +2. SUBFOLDERS +------------- + +doc/ - Documentation +modules/ - perl modules necessary for SOAP communication +proxy/ - perl SOAP proxy modules generated according to the ONVIF + specifications +scripts/ - application logic +wsdl/ - (empty) this is only used during the proxy generation process + + +3. LICENSE +---------- +Any files in this folder and its subfolders are published under the same +license as this file if they do not carry additional license information. + + +4. PROXY GENERATION +------------------- + +SOAP messages use XML format and need to comply to the schema expected by the +receiver. While it is possible to build messages through string concatenation +it is impractical for all but testing purposes. +Here we use SOAP proxy modules generated by the script wsdl2perl.pl from the +SOAP::WSDL module (http://search.cpan.org/~mkutter/SOAP-WSDL-2.00.10/) +version 2.00.10. + +A - Get the input files. + + Download these files and any web service (.wsdl) or schema (.xsd) + files referenced therein into the wsdl/ subfolder. + + http://www.onvif.org/onvif/ver10/device/wsdl/devicemgmt.wsdl + http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl + http://www.onvif.org/onvif/ver20/ptz/wsdl/ptz.wsdl + http://www.onvif.org/onvif/ver20/analytics/wsdl/analytics.wsdl + + The complete list of files we have during build is: + + analytics.wsdl + b-2.xsd + bf-2.xsd + br-2.xsd + brw-2.wsdl + bw-2.wsdl + devicemgmt.wsdl + media.wsdl + onvif.xsd + ptz.wsdl + r-2.xsd + rw-2.wsdl + soap-envelop.xsd + t-1.xsd + wsdd-discovery-1.1-schema-os.xsd + wsdd-discovery-1.1-wsdl-os.wsdl + wsdiscovery-udp.wsdl + wsdl11soap12.xsd + wsdl.xsd + wsnotification.wsdl + xmlmime.xsd + xop-include.xsd + +B - Generate the proxy modules using these commands. + + wsdl2perl.pl -p ONVIF::Device:: -b proxy/lib file:wsdl/devicemgmt.wsdl + wsdl2perl.pl -p ONVIF::Media:: -b proxy/lib file:wsdl/media.wsdl + wsdl2perl.pl -p ONVIF::PTZ:: -b proxy/lib file:wsdl/ptz.wsdl + wsdl2perl.pl -p ONVIF::Event:: -b proxy/lib file:wsdl/event.wsdl + wsdl2perl.pl -p ONVIF::Analytics:: -b proxy/lib file:wsdl/analytics.wsdl + wsdl2perl.pl -p WSDiscovery:: -b proxy/lib file:wsdl/wsdiscovery-udp.wsdl + wsdl2perl.pl -p WSNotification:: -b proxy/lib file:wsdl/wsnotification.wsdl + + I had to tweak SOAP::WSDL a little bit to get correct proxies. diff --git a/onvif/modules/CMakeLists.txt b/onvif/modules/CMakeLists.txt index 5e3a878bb..d7ddbf466 100644 --- a/onvif/modules/CMakeLists.txt +++ b/onvif/modules/CMakeLists.txt @@ -1,10 +1,18 @@ +# CMakeLists.txt for the ZoneMinder ONVIF modules. +# If this is an out-of-source build, copy the files we need to the binary directory +if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.PL" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/lib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE) +endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) + +# MAKEMAKER_NOECHO_COMMAND previously defined in /scripts/zoneminder/CMakeLists.txt # Add build target for the perl modules -add_custom_target(zmonvifmodules ALL perl Makefile.PL FIRST_MAKEFILE=MakefilePerl PREFIX="${CMAKE_CURRENT_BINARY_DIR}/output" LIB="${CMAKE_CURRENT_BINARY_DIR}/output/${ZM_PERL_SUBPREFIX}" INSTALLSITEMAN3DIR="${CMAKE_CURRENT_BINARY_DIR}/output/${CMAKE_INSTALL_MANDIR}/man3" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl modules") +add_custom_target(zmonvifmodules ALL perl Makefile.PL ${ZM_PERL_MM_PARMS} FIRST_MAKEFILE=MakefilePerl DESTDIR="${CMAKE_CURRENT_BINARY_DIR}/output" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make -f MakefilePerl pure_install COMMENT "Building ZoneMinder perl ONVIF proxy module") # Add install target for the perl modules -install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "${CMAKE_INSTALL_PREFIX}") +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "/") # Add additional files and directories to make clean set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "output;blib;pm_to_blib;MakefilePerl") diff --git a/onvif/modules/lib/ONVIF/Client.pm b/onvif/modules/lib/ONVIF/Client.pm index 55075b5a4..3541736d0 100644 --- a/onvif/modules/lib/ONVIF/Client.pm +++ b/onvif/modules/lib/ONVIF/Client.pm @@ -1,7 +1,7 @@ # ========================================================================== # # ZoneMinder ONVIF Client module -# Copyright (C) Jan M. Hochstein +# Copyright (C) 2014 Jan M. Hochstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/onvif/modules/lib/ONVIF/Deserializer/MessageParser.pm b/onvif/modules/lib/ONVIF/Deserializer/MessageParser.pm index c9234ae76..b0ce6f0fd 100644 --- a/onvif/modules/lib/ONVIF/Deserializer/MessageParser.pm +++ b/onvif/modules/lib/ONVIF/Deserializer/MessageParser.pm @@ -1,4 +1,27 @@ -#!/usr/bin/perl +# ========================================================================== +# +# ZoneMinder ONVIF Client module +# Copyright (C) 2014 Jan M. Hochstein +# +# 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 module contains the implementation of a SOAP message parser +# + package ONVIF::Deserializer::MessageParser; use strict; use warnings; diff --git a/onvif/modules/lib/ONVIF/Deserializer/XSD.pm b/onvif/modules/lib/ONVIF/Deserializer/XSD.pm index 31ffcac4f..a6f03aebe 100644 --- a/onvif/modules/lib/ONVIF/Deserializer/XSD.pm +++ b/onvif/modules/lib/ONVIF/Deserializer/XSD.pm @@ -1,9 +1,32 @@ +# ========================================================================== +# +# ZoneMinder ONVIF Client module +# Copyright (C) 2014 Jan M. Hochstein +# +# 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 module contains the implementation of a SOAP deserializer +# + package ONVIF::Deserializer::XSD; use strict; use warnings; use base qw(SOAP::WSDL::Deserializer::XSD); -use Data::Dump qw(dump); use SOAP::WSDL::SOAP::Typelib::Fault11; use ONVIF::Deserializer::MessageParser; @@ -31,6 +54,10 @@ SOAP::WSDL::Factory::Deserializer->register('1.2', __PACKAGE__ ); ## we get the soap version from the message parser my %soap_version_of :ATTR( :default<()>); +<<<<<<< HEAD +======= + +>>>>>>> master sub soap_version { my ($self) = @_; if($SOAP::WSDL::Deserializer::XSD::parser_of{ident $self}) { @@ -50,8 +77,6 @@ sub deserialize { $SOAP::WSDL::Deserializer::XSD::parser_of{ ${ $self } } = $parser; } -#print "Deserializing:\n"; dump($content); - $parser->class_resolver( $self->SOAP::WSDL::Deserializer::XSD::get_class_resolver() ); eval { $parser->parse_string( $content ) }; diff --git a/onvif/modules/lib/ONVIF/Serializer/Base.pm b/onvif/modules/lib/ONVIF/Serializer/Base.pm index b6edf360f..adbb173c6 100644 --- a/onvif/modules/lib/ONVIF/Serializer/Base.pm +++ b/onvif/modules/lib/ONVIF/Serializer/Base.pm @@ -1,4 +1,27 @@ -#!/usr/bin/perl -w +# ========================================================================== +# +# ZoneMinder ONVIF Client module +# Copyright (C) 2014 Jan M. Hochstein +# +# 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 module contains the base class for the SOAP serializers +# + package ONVIF::Serializer::Base; use strict; use warnings; @@ -74,8 +97,11 @@ sub serialize { my $soap_prefix = $opt->{ namespace }->{ $SOAP_NS }; - # envelope start with namespaces - my $xml = "<$soap_prefix\:Envelope "; + # XML starts with header + my $xml = "\n"; + + # envelope starts with namespaces + $xml .= "<$soap_prefix\:Envelope "; while (my ($uri, $prefix) = each %{ $opt->{ namespace } }) { diff --git a/onvif/modules/lib/ONVIF/Serializer/SOAP11.pm b/onvif/modules/lib/ONVIF/Serializer/SOAP11.pm index 2813a0688..442a11d7a 100644 --- a/onvif/modules/lib/ONVIF/Serializer/SOAP11.pm +++ b/onvif/modules/lib/ONVIF/Serializer/SOAP11.pm @@ -1,4 +1,27 @@ -#!/usr/bin/perl -w +# ========================================================================== +# +# ZoneMinder ONVIF Client module +# Copyright (C) 2014 Jan M. Hochstein +# +# 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 module contains the SOAP 1.1 serializer +# + package ONVIF::Serializer::SOAP11; use strict; use warnings; diff --git a/onvif/modules/lib/ONVIF/Serializer/SOAP12.pm b/onvif/modules/lib/ONVIF/Serializer/SOAP12.pm index d378a3614..2805ea40f 100644 --- a/onvif/modules/lib/ONVIF/Serializer/SOAP12.pm +++ b/onvif/modules/lib/ONVIF/Serializer/SOAP12.pm @@ -1,4 +1,27 @@ -#!/usr/bin/perl -w +# ========================================================================== +# +# ZoneMinder ONVIF Client module +# Copyright (C) 2014 Jan M. Hochstein +# +# 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 module contains the SOAP 1.2 serializer +# + package ONVIF::Serializer::SOAP12; use strict; use warnings; diff --git a/onvif/modules/lib/WSDiscovery/TransportUDP.pm b/onvif/modules/lib/WSDiscovery/TransportUDP.pm index 9bc6c74c9..edc837da6 100644 --- a/onvif/modules/lib/WSDiscovery/TransportUDP.pm +++ b/onvif/modules/lib/WSDiscovery/TransportUDP.pm @@ -1,7 +1,7 @@ # ========================================================================== # # Perl WS-Discovery implementation -# Copyright (C) Jan M. Hochstein +# Copyright (C) 2014 Jan M. Hochstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -61,16 +61,19 @@ sub _notify_response } sub send_multi() { - my ($self, $address, $port, $data) = @_; + my ($self, $address, $port, $utf8_string) = @_; my $destination = $address . ':' . $port; my $socket = IO::Socket::Multicast->new(PROTO => 'udp', LocalPort=>$port, PeerAddr=>$destination, ReuseAddr=>1) or die 'Cannot open multicast socket to ' . ${address} . ':' . ${port}; + + my $bytes = $utf8_string; + utf8::encode($bytes); $socket->mcast_ttl(1); - $socket->send($data); + $socket->send($bytes); } sub receive_multi() { diff --git a/onvif/modules/lib/WSSecurity/SecuritySerializer.pm b/onvif/modules/lib/WSSecurity/SecuritySerializer.pm index a2daac028..11a918af7 100644 --- a/onvif/modules/lib/WSSecurity/SecuritySerializer.pm +++ b/onvif/modules/lib/WSSecurity/SecuritySerializer.pm @@ -1,7 +1,7 @@ # ========================================================================== # # Perl WS-Security header for SOAP::WSDL -# Copyright (C) Jan M. Hochstein +# Copyright (C) 2014 Jan M. Hochstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -26,7 +26,7 @@ use strict; use warnings; use SOAP::WSDL::Factory::Serializer; use Time::Local; -use Digest::SHA1; +use Digest::SHA; use MIME::Base64; @@ -94,7 +94,7 @@ sub ws_authen { my $nonce = $nonce_generator->(); my $timestamp = timestamp(); - my $pwDigest = Digest::SHA1::sha1( $nonce . $timestamp . $password ); + my $pwDigest = Digest::SHA::sha1( $nonce . $timestamp . $password ); my $passwordHash = MIME::Base64::encode_base64($pwDigest,""); my $nonceHash = MIME::Base64::encode_base64($nonce,""); diff --git a/onvif/proxy/CMakeLists.txt b/onvif/proxy/CMakeLists.txt index fece81d11..1a40c0ffb 100644 --- a/onvif/proxy/CMakeLists.txt +++ b/onvif/proxy/CMakeLists.txt @@ -1,10 +1,18 @@ +# CMakeLists.txt for the ZoneMinder ONVIF proxy module. +# If this is an out-of-source build, copy the files we need to the binary directory +if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.PL" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/lib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE) +endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) + +# MAKEMAKER_NOECHO_COMMAND previously defined in /scripts/zoneminder/CMakeLists.txt # Add build target for the perl modules -add_custom_target(zmonvifproxy ALL perl Makefile.PL FIRST_MAKEFILE=MakefilePerl PREFIX="${CMAKE_CURRENT_BINARY_DIR}/output" LIB="${CMAKE_CURRENT_BINARY_DIR}/output/${ZM_PERL_SUBPREFIX}" INSTALLSITEMAN3DIR="${CMAKE_CURRENT_BINARY_DIR}/output/${CMAKE_INSTALL_MANDIR}/man3" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl modules") +add_custom_target(zmonvifproxy ALL perl Makefile.PL ${ZM_PERL_MM_PARMS} FIRST_MAKEFILE=MakefilePerl DESTDIR="${CMAKE_CURRENT_BINARY_DIR}/output" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make -f MakefilePerl pure_install COMMENT "Building ZoneMinder perl ONVIF proxy module") # Add install target for the perl modules -install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "${CMAKE_INSTALL_PREFIX}") +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "/") # Add additional files and directories to make clean set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "output;blib;pm_to_blib;MakefilePerl") diff --git a/onvif/proxy/lib/ONVIF/Media/Types/TopicNamespaceType.pm b/onvif/proxy/lib/ONVIF/Media/Types/TopicNamespaceType.pm index 437ef094c..a9545c41b 100644 --- a/onvif/proxy/lib/ONVIF/Media/Types/TopicNamespaceType.pm +++ b/onvif/proxy/lib/ONVIF/Media/Types/TopicNamespaceType.pm @@ -63,7 +63,7 @@ sub __get_attr_class { } -use base qw(ONVIF::Media::Types::TopicType); +use base qw(WSNotification::Types::TopicType); # Variety: sequence use Class::Std::Fast::Storable constructor => 'none'; use base qw(SOAP::WSDL::XSD::Typelib::ComplexType); @@ -90,7 +90,7 @@ __PACKAGE__->_factory( { 'documentation' => 'ONVIF::Media::Types::Documentation', 'MessagePattern' => 'ONVIF::Media::Types::QueryExpressionType', - 'Topic' => 'ONVIF::Media::Types::TopicType', + 'Topic' => 'WSNotification::Types::TopicType', }, { diff --git a/onvif/proxy/lib/ONVIF/PTZ/Types/TopicNamespaceType.pm b/onvif/proxy/lib/ONVIF/PTZ/Types/TopicNamespaceType.pm index d5ae7f8d5..ea69830bd 100644 --- a/onvif/proxy/lib/ONVIF/PTZ/Types/TopicNamespaceType.pm +++ b/onvif/proxy/lib/ONVIF/PTZ/Types/TopicNamespaceType.pm @@ -63,7 +63,7 @@ sub __get_attr_class { } -use base qw(ONVIF::PTZ::Types::TopicType); +use base qw(WSNotification::Types::TopicType); # Variety: sequence use Class::Std::Fast::Storable constructor => 'none'; use base qw(SOAP::WSDL::XSD::Typelib::ComplexType); @@ -90,7 +90,7 @@ __PACKAGE__->_factory( { 'documentation' => 'ONVIF::PTZ::Types::Documentation', 'MessagePattern' => 'ONVIF::PTZ::Types::QueryExpressionType', - 'Topic' => 'ONVIF::PTZ::Types::TopicType', + 'Topic' => 'WSNotification::Types::TopicType', }, { diff --git a/onvif/proxy/lib/WSDiscovery10/Elements/Header.pm b/onvif/proxy/lib/WSDiscovery10/Elements/Header.pm new file mode 100644 index 000000000..df2d7f389 --- /dev/null +++ b/onvif/proxy/lib/WSDiscovery10/Elements/Header.pm @@ -0,0 +1,55 @@ + +package WSDiscovery10::Elements::Header; +use strict; +use warnings; + + +__PACKAGE__->_set_element_form_qualified(0); + +sub get_xmlns { 'http://schemas.xmlsoap.org/soap/envelope/' }; + +our $XML_ATTRIBUTE_CLASS; +undef $XML_ATTRIBUTE_CLASS; + +sub __get_attr_class { + return $XML_ATTRIBUTE_CLASS; +} + +use Class::Std::Fast::Storable constructor => 'none'; +use base qw(SOAP::WSDL::XSD::Typelib::ComplexType); + +Class::Std::initialize(); + +{ # BLOCK to scope variables + +my %Action_of :ATTR(:get); +my %MessageID_of :ATTR(:get); +my %ReplyTo_of :ATTR(:get); +my %To_of :ATTR(:get); + +__PACKAGE__->_factory( + [ qw( Action MessageID ReplyTo To ) ], + { + 'Action' => \%Action_of, + 'MessageID' => \%MessageID_of, + 'ReplyTo' => \%ReplyTo_of, + 'To' => \%To_of, + }, + { + 'Action' => 'WSDiscovery10::Elements::Action', + 'MessageID' => 'WSDiscovery10::Elements::MessageID', + 'ReplyTo' => 'WSDiscovery10::Elements::ReplyTo', + 'To' => 'WSDiscovery10::Elements::To', + }, + { + 'Action' => '', + 'MessageID' => '', + 'ReplyTo' => '', + 'To' => '', + } +); + +} # end BLOCK + + +1; diff --git a/onvif/proxy/lib/WSDiscovery10/Interfaces/WSDiscovery/WSDiscoveryPort.pm b/onvif/proxy/lib/WSDiscovery10/Interfaces/WSDiscovery/WSDiscoveryPort.pm index d194bbc87..f1b6446a5 100644 --- a/onvif/proxy/lib/WSDiscovery10/Interfaces/WSDiscovery/WSDiscoveryPort.pm +++ b/onvif/proxy/lib/WSDiscovery10/Interfaces/WSDiscovery/WSDiscoveryPort.pm @@ -34,7 +34,11 @@ sub ProbeOp { }, header => { - + 'use' => 'literal', + namespace => 'http://schemas.xmlsoap.org/ws/2004/08/addressing', + encodingStyle => '', + parts => [qw( WSDiscovery10::Elements::Header )], + }, headerfault => { diff --git a/onvif/proxy/lib/WSDiscovery10/Typemaps/WSDiscovery.pm b/onvif/proxy/lib/WSDiscovery10/Typemaps/WSDiscovery.pm index 635a19393..c19a7c647 100644 --- a/onvif/proxy/lib/WSDiscovery10/Typemaps/WSDiscovery.pm +++ b/onvif/proxy/lib/WSDiscovery10/Typemaps/WSDiscovery.pm @@ -23,7 +23,12 @@ our $typemap_1 = { 'ProbeMatches/ProbeMatch/Types' => 'WSDiscovery10::Types::QNameListType', 'ProbeMatches/ProbeMatch/EndpointReference' => 'WSDiscovery10::Types::EndpointReferenceType', 'ProbeMatches/ProbeMatch/EndpointReference/ReferenceProperties' => 'WSDiscovery10::Types::ReferencePropertiesType', - 'ProbeMatches/ProbeMatch/EndpointReference/PortType' => 'WSDiscovery10::Types::AttributedQName' + 'ProbeMatches/ProbeMatch/EndpointReference/PortType' => 'WSDiscovery10::Types::AttributedQName', + 'MessageID' => '__SKIP__', + 'RelatesTo' => '__SKIP__', + 'To' => '__SKIP__', + 'Action' => '__SKIP__', + 'AppSequence' => '__SKIP__', }; ; diff --git a/onvif/proxy/lib/WSDiscovery10/Types/ProbeType.pm b/onvif/proxy/lib/WSDiscovery10/Types/ProbeType.pm index 10c290e8e..8d7e9e7ca 100644 --- a/onvif/proxy/lib/WSDiscovery10/Types/ProbeType.pm +++ b/onvif/proxy/lib/WSDiscovery10/Types/ProbeType.pm @@ -7,8 +7,10 @@ __PACKAGE__->_set_element_form_qualified(0); sub get_xmlns { 'http://schemas.xmlsoap.org/ws/2005/04/discovery' }; -our $XML_ATTRIBUTE_CLASS; -undef $XML_ATTRIBUTE_CLASS; +our $XML_ATTRIBUTE_CLASS = 'WSDiscovery10::Types::ProbeType::_ProbeType::XmlAttr'; + +#our $XML_ATTRIBUTE_CLASS; +#undef $XML_ATTRIBUTE_CLASS; sub __get_attr_class { return $XML_ATTRIBUTE_CLASS; @@ -49,11 +51,55 @@ __PACKAGE__->_factory( } # end BLOCK +package WSDiscovery10::Types::ProbeType::_ProbeType::XmlAttr; +#use base qw(SOAP::WSDL::XSD::Typelib::ComplexType); +use Class::Std::Fast::Storable constructor => 'none', cache => 1; +use base qw(SOAP::WSDL::XSD::Typelib::Builtin::anySimpleType); +{ # BLOCK to scope variables +my %Attribs_of :ATTR(:get); +sub new +{ + my $self = pop @{ Class::Std::Fast::OBJECT_CACHE_REF()->{ $_[0] } }; + $self = bless \(my $o = Class::Std::Fast::ID()), $_[0] + if not defined $self; + + $self->BUILD(${$self}, $_[1]); + return $self; +} + +sub BUILD +{ + my ($self, $ident, $arg_ref) = @_; + + $Attribs_of{$ident} = $arg_ref; +} + +# without this no attributes are serialized +# SOAP::WSDL::XSD::Typelib::CompexType sub serialize_attr() + +sub as_bool :BOOLIFY { 1 } + +sub serialize() +{ + my $ident = ${ $_[0] }; + my $option_ref = $_[1]; + my $attr_str = ""; + + foreach my $attr (keys %{$Attribs_of{$ident}}) + { + my $value = %{$Attribs_of{$ident}}{$attr}; + $attr_str .= " $attr=\"$value\""; + } + + return $attr_str; +} + +} # end BLOCK 1; diff --git a/onvif/scripts/CMakeLists.txt b/onvif/scripts/CMakeLists.txt index 09775b3f2..3405fa756 100644 --- a/onvif/scripts/CMakeLists.txt +++ b/onvif/scripts/CMakeLists.txt @@ -1,4 +1,9 @@ # CMakeLists.txt for the ZoneMinder perl scripts. +# If this is an out-of-source build, copy the files we need to the binary directory +if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/zmonvif-probe.pl" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") +endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) + # Install the perl scripts install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmonvif-probe.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/onvif/scripts/zmonvif-probe.pl b/onvif/scripts/zmonvif-probe.pl index f38657eec..10aa26a08 100755 --- a/onvif/scripts/zmonvif-probe.pl +++ b/onvif/scripts/zmonvif-probe.pl @@ -3,7 +3,7 @@ # ========================================================================== # # ZoneMinder ONVIF Control Protocol Module -# Copyright (C) Jan M. Hochstein +# Copyright (C) 2014 Jan M. Hochstein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -25,10 +25,12 @@ # use Getopt::Std; +use Data::UUID; require ONVIF::Client; require WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort; +require WSDiscovery10::Elements::Header; require WSDiscovery10::Elements::Types; require WSDiscovery10::Elements::Scopes; @@ -95,9 +97,7 @@ sub deserialize_message sub interpret_messages { - my ($svc_discover, $responses_ref, $services_ref) = @_; - my @responses = @$responses_ref; - my %services = %$services_ref; + my ($svc_discover, $services, @responses ) = @_; foreach my $response ( @responses ) { @@ -108,7 +108,7 @@ sub interpret_messages my $result = deserialize_message($svc_discover, $response); if(not $result) { if($verbose) { - print "Error deserializing message:\n" . $result . "\n"; + print "Error deserializing message. No message returned from deserializer.\n"; } next; } @@ -116,15 +116,23 @@ sub interpret_messages my $xaddr; foreach my $l_xaddr (split ' ', $result->get_ProbeMatch()->get_XAddrs()) { # find IPv4 address - if($l_xaddr =~ m|//[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/|) { + if($verbose) { + print "l_xaddr = $l_xaddr\n"; + } + if($l_xaddr =~ m|//[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[:/]|) { $xaddr = $l_xaddr; last; + } else { + print STDERR "Unable to find IPv4 address from xaddr $l_xaddr\n"; } } + # No usable address found + next if not $xaddr; + # ignore multiple responses from one service - next if defined $services{$xaddr}; - $services{$xaddr} = 1; + next if defined $services->{$xaddr}; + $services->{$xaddr} = 1; print "$xaddr, " . $svc_discover->get_soap_version() . ", "; @@ -162,42 +170,62 @@ sub discover ## try both soap versions my %services; + my $uuid_gen = Data::UUID->new(); + if($verbose) { print "Probing for SOAP 1.1\n" } - my $svc_discover = WSDiscovery::Interfaces::WSDiscovery::WSDiscoveryPort->new({ + my $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({ # no_dispatch => '1', }); $svc_discover->set_soap_version('1.1'); + my $uuid = $uuid_gen->create_str(); + my $result = $svc_discover->ProbeOp( { # WSDiscovery::Types::ProbeType - Types => { 'dn:NetworkVideoTransmitter', 'tds:Device' }, # QNameListType + Types => 'http://www.onvif.org/ver10/network/wsdl:NetworkVideoTransmitter http://www.onvif.org/ver10/device/wsdl:Device', # QNameListType Scopes => { value => '' }, - },, + }, + WSDiscovery10::Elements::Header->new({ + Action => { value => 'http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe' }, + MessageID => { value => "urn:uuid:$uuid" }, + To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' }, + }) ); # print $result . "\n"; - interpret_messages($svc_discover, \@responses, \%services); + interpret_messages($svc_discover, \%services, @responses); @responses = (); if($verbose) { print "Probing for SOAP 1.2\n" } - $svc_discover = WSDiscovery::Interfaces::WSDiscovery::WSDiscoveryPort->new({ + $svc_discover = WSDiscovery10::Interfaces::WSDiscovery::WSDiscoveryPort->new({ # no_dispatch => '1', }); $svc_discover->set_soap_version('1.2'); + # copies of the same Probe message must have the same MessageID. + # This is not a copy. So we generate a new uuid. + $uuid = $uuid_gen->create_str(); + $result = $svc_discover->ProbeOp( { # WSDiscovery::Types::ProbeType - Types => { 'dn:NetworkVideoTransmitter', 'tds:Device' }, # QNameListType + xmlattr => { 'xmlns:dn' => 'http://www.onvif.org/ver10/network/wsdl', + 'xmlns:tds' => 'http://www.onvif.org/ver10/device/wsdl', }, + Types => 'dn:NetworkVideoTransmitter tds:Device', # QNameListType Scopes => { value => '' }, - },, + }, + WSDiscovery10::Elements::Header->new({ + Action => { value => 'http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe' }, + MessageID => { value => "urn:uuid:$uuid" }, + To => { value => 'urn:schemas-xmlsoap-org:ws:2005:04:discovery' }, + }) ); # print $result . "\n"; - interpret_messages($svc_discover, @responses, \%services); + interpret_messages($svc_discover, \%services, @responses); } @@ -226,16 +254,21 @@ sub profiles $profile->get_VideoEncoderConfiguration()->get_RateControl()->get_FrameRateLimit() . ", "; - $result = $client->get_endpoint('media')->GetStreamUri( { - StreamSetup => { # ONVIF::Media::Types::StreamSetup - Stream => 'RTP_unicast', # StreamType - Transport => { # ONVIF::Media::Types::Transport - Protocol => 'RTSP', # TransportProtocol - }, - }, - ProfileToken => $token, # ReferenceToken - } ,, ); - die $result if not $result; + # Specification gives conflicting values for unicast stream types, try both. + # http://www.onvif.org/onvif/ver10/media/wsdl/media.wsdl#op.GetStreamUri + foreach my $streamtype ( 'RTP_unicast', 'RTP-Unicast' ) { + $result = $client->get_endpoint('media')->GetStreamUri( { + StreamSetup => { # ONVIF::Media::Types::StreamSetup + Stream => $streamtype, # StreamType + Transport => { # ONVIF::Media::Types::Transport + Protocol => 'RTSP', # TransportProtocol + }, + }, + ProfileToken => $token, # ReferenceToken + } ,, ); + last if $result; + } + die $result if not $result; # print $result . "\n"; print $result->get_MediaUri()->get_Uri() . diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index 58e916335..d7abaa336 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -16,6 +16,7 @@ configure_file(zmvideo.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" @ONLY) configure_file(zmwatch.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" @ONLY) configure_file(zmcamtool.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" @ONLY) configure_file(zmsystemctl.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmsystemctl.pl" @ONLY) +configure_file(zmtelemetry.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" @ONLY) if(NOT ZM_NO_X10) configure_file(zmx10.pl.in "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" @ONLY) endif(NOT ZM_NO_X10) @@ -24,8 +25,14 @@ endif(NOT ZM_NO_X10) configure_file(zm.in "${CMAKE_CURRENT_BINARY_DIR}/zm" @ONLY) #configure_file(zmeventdump.in zmeventdump @ONLY) +# Generate man files for the perl scripts destined for the bin folder +file(GLOB perlscripts RELATIVE "${CMAKE_CURRENT_BINARY_DIR}" "*.pl") +FOREACH(PERLSCRIPT ${perlscripts}) + POD2MAN(${CMAKE_CURRENT_SOURCE_DIR}/${PERLSCRIPT} zoneminder-${PERLSCRIPT} 8) +ENDFOREACH(PERLSCRIPT ${perlscripts}) + # Install the perl scripts -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmsystemctl.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmaudit.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcontrol.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmdc.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmfilter.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmpkg.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrack.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtrigger.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmupdate.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmvideo.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmwatch.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmcamtool.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmsystemctl.pl" "${CMAKE_CURRENT_BINARY_DIR}/zmtelemetry.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) if(NOT ZM_NO_X10) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/zmx10.pl" DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) endif(NOT ZM_NO_X10) diff --git a/scripts/Makefile.am b/scripts/Makefile.am deleted file mode 100644 index 92fddf98d..000000000 --- a/scripts/Makefile.am +++ /dev/null @@ -1,75 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -# Ack! Nasty hack to get modules Makefile regenerated if wiped with make clean -all-local: ZoneMinder/Makefile - -ZoneMinder/Makefile: ZoneMinder/Makefile.PL - ( cd ZoneMinder; perl Makefile.PL ) - -bin_SCRIPTS = \ - zmdc.pl \ - zmaudit.pl \ - zmfilter.pl \ - zmtrigger.pl \ - zmx10.pl \ - zmwatch.pl \ - zmpkg.pl \ - zmupdate.pl \ - zmvideo.pl \ - zmcontrol.pl \ - zmtrack.pl \ - zmcamtool.pl \ - zmsystemctl.pl - -SUBDIRS = \ - . \ - ZoneMinder - -EXTRA_DIST = \ - zmdc.pl.in \ - zmaudit.pl.in \ - zmfilter.pl.in \ - zmtrigger.pl.in \ - zmx10.pl.in \ - zmwatch.pl.in \ - zmpkg.pl.in \ - zmupdate.pl.in \ - zmvideo.pl.in \ - zmcontrol.pl.in \ - zmtrack.pl.in \ - zmcamtool.pl.in \ - zmsystemctl.pl.in \ - ZoneMinder/Makefile.PL \ - ZoneMinder/README \ - ZoneMinder/Changes \ - ZoneMinder/META.yml \ - ZoneMinder/MANIFEST \ - ZoneMinder/t/ZoneMinder.t \ - ZoneMinder/lib/ZoneMinder.pm \ - ZoneMinder/lib/ZoneMinder/Base.pm.in \ - ZoneMinder/lib/ZoneMinder/Config.pm.in \ - ZoneMinder/lib/ZoneMinder/Logger.pm \ - ZoneMinder/lib/ZoneMinder/General.pm \ - ZoneMinder/lib/ZoneMinder/Database.pm \ - ZoneMinder/lib/ZoneMinder/Memory.pm.in \ - ZoneMinder/lib/ZoneMinder/Memory/Shared.pm \ - ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm \ - ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm \ - ZoneMinder/lib/ZoneMinder/ConfigData.pm.in \ - ZoneMinder/lib/ZoneMinder/Control.pm \ - ZoneMinder/lib/ZoneMinder/Control \ # Grab all ptz control skips under the Control folder - ZoneMinder/lib/ZoneMinder/Trigger/Channel.pm \ - ZoneMinder/lib/ZoneMinder/Trigger/Channel/Handle.pm \ - ZoneMinder/lib/ZoneMinder/Trigger/Channel/Spawning.pm \ - ZoneMinder/lib/ZoneMinder/Trigger/Channel/Inet.pm \ - ZoneMinder/lib/ZoneMinder/Trigger/Channel/Unix.pm \ - ZoneMinder/lib/ZoneMinder/Trigger/Channel/File.pm \ - ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm \ - ZoneMinder/lib/ZoneMinder/Trigger/Connection.pm \ - ZoneMinder/lib/ZoneMinder/Trigger/Connection/Example.pm \ - zm.in \ - zmdbbackup.in \ - zmdbrestore.in \ - zmeventdump.in \ - zmlogrotate.conf.in - diff --git a/scripts/ZoneMinder/CMakeLists.txt b/scripts/ZoneMinder/CMakeLists.txt index 61bee83af..3ed9bf7c6 100644 --- a/scripts/ZoneMinder/CMakeLists.txt +++ b/scripts/ZoneMinder/CMakeLists.txt @@ -2,13 +2,13 @@ # If this is an out-of-source build, copy the files we need to the binary directory if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Changes" "${CMAKE_CURRENT_BINARY_DIR}/Changes") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.PL" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.PL") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/MANIFEST" "${CMAKE_CURRENT_BINARY_DIR}/MANIFEST") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/META.yml" "${CMAKE_CURRENT_BINARY_DIR}/META.yml") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/README" "${CMAKE_CURRENT_BINARY_DIR}/README") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/t" "${CMAKE_CURRENT_BINARY_DIR}/t") - execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/lib" "${CMAKE_CURRENT_BINARY_DIR}/lib") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Changes" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/Makefile.PL" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/MANIFEST" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/META.yml" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/README" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/t" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/lib" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}" PATTERN "*.in" EXCLUDE) endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) # Create files from the .in files @@ -24,10 +24,10 @@ else(CMAKE_VERBOSE_MAKEFILE) endif(CMAKE_VERBOSE_MAKEFILE) # Add build target for the perl modules -add_custom_target(zmperlmodules ALL perl Makefile.PL FIRST_MAKEFILE=MakefilePerl PREFIX="${CMAKE_CURRENT_BINARY_DIR}/output" LIB="${CMAKE_CURRENT_BINARY_DIR}/output/${ZM_PERL_SUBPREFIX}" INSTALLSITEMAN3DIR="${CMAKE_CURRENT_BINARY_DIR}/output/${CMAKE_INSTALL_MANDIR}/man3" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make --makefile=MakefilePerl COMMAND make --makefile=MakefilePerl pure_install COMMENT "Building ZoneMinder perl modules") +add_custom_target(zmperlmodules ALL perl Makefile.PL ${ZM_PERL_MM_PARMS} FIRST_MAKEFILE=MakefilePerl DESTDIR="${CMAKE_CURRENT_BINARY_DIR}/output" ${MAKEMAKER_NOECHO_COMMAND} COMMAND make -f MakefilePerl pure_install COMMENT "Building ZoneMinder perl modules") # Add install target for the perl modules -install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "${CMAKE_INSTALL_PREFIX}") +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/output/" DESTINATION "/") # Add additional files and directories to make clean set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "output;blib;pm_to_blib;MakefilePerl") diff --git a/scripts/ZoneMinder/lib/ZoneMinder.pm b/scripts/ZoneMinder/lib/ZoneMinder.pm index 41c972650..87a6b14b7 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder.pm @@ -36,34 +36,42 @@ use ZoneMinder::General qw(:all); use ZoneMinder::Database qw(:all); use ZoneMinder::Memory qw(:all); -our @ISA = qw(Exporter ZoneMinder::Base ZoneMinder::Config ZoneMinder::Logger ZoneMinder::General ZoneMinder::Database ZoneMinder::Memory); +our @ISA = qw( + Exporter + ZoneMinder::Base + ZoneMinder::Config + ZoneMinder::Logger + ZoneMinder::General + ZoneMinder::Database + ZoneMinder::Memory +); # Items to export into callers namespace by default. Note: do not export # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. -# This allows declaration use ZoneMinder ':all'; +# This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( - 'base' => [ - @ZoneMinder::Base::EXPORT_OK - ], - 'config' => [ - @ZoneMinder::Config::EXPORT_OK - ], - 'debug' => [ - @ZoneMinder::Logger::EXPORT_OK - ], - 'general' => [ - @ZoneMinder::General::EXPORT_OK - ], - 'database' => [ - @ZoneMinder::Database::EXPORT_OK - ], - 'memory' => [ - @ZoneMinder::Memory::EXPORT_OK - ], + 'base' => [ + @ZoneMinder::Base::EXPORT_OK + ], + 'config' => [ + @ZoneMinder::Config::EXPORT_OK + ], + 'debug' => [ + @ZoneMinder::Logger::EXPORT_OK + ], + 'general' => [ + @ZoneMinder::General::EXPORT_OK + ], + 'database' => [ + @ZoneMinder::Database::EXPORT_OK + ], + 'memory' => [ + @ZoneMinder::Memory::EXPORT_OK + ], ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Base.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Base.pm.in index 3f10e9137..70140ea66 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Base.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Base.pm.in @@ -19,7 +19,7 @@ # # ========================================================================== # -# This module contains the common definitions and functions used by the rest +# This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Base; @@ -38,7 +38,7 @@ use constant ZM_VERSION => "@VERSION@"; # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. -# This allows declaration use ZoneMinder ':all'; +# This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'all' => [ qw(ZM_VERSION) ] ); @@ -62,7 +62,11 @@ ZoneMinder::Base - Base perl module for ZoneMinder =head1 DESCRIPTION -This module is the base module for the rest of the ZoneMinder modules. It is included by each of the other modules but serves no purpose other than to propagate the perl module version amongst the other modules. You will never need to use this module directly but if you write new ZoneMinder modules they should include it. +This module is the base module for the rest of the ZoneMinder modules. It +is included by each of the other modules but serves no purpose other than +to propagate the perl module version amongst the other modules. You will +never need to use this module directly but if you write new ZoneMinder +modules they should include it. =head2 EXPORT diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in index 21f7a2d50..1f21b7407 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in @@ -19,7 +19,7 @@ # # ========================================================================== # -# This module contains the common definitions and functions used by the rest +# This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Config; @@ -30,6 +30,7 @@ use warnings; require Exporter; require ZoneMinder::Base; +use ZoneMinder::ConfigData qw(:all); our @ISA = qw(Exporter ZoneMinder::Base); @@ -38,15 +39,20 @@ use vars qw( %Config ); # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. -# This allows declaration use ZoneMinder ':all'; +# This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our @EXPORT_CONFIG = qw( %Config ); # Get populated by BEGIN our %EXPORT_TAGS = ( - 'constants' => [ qw( - ZM_PID - ) ] + functions => [ qw( + zmConfigLoad + loadConfigFromDB + saveConfigToDB + ) ], + constants => [ qw( + ZM_PID + ) ] ); push( @{$EXPORT_TAGS{config}}, @EXPORT_CONFIG ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; @@ -65,38 +71,165 @@ use Carp; # Load the config from the database into the symbol table BEGIN { - my $config_file = ZM_CONFIG; - ( my $local_config_file = $config_file ) =~ s|^.*/|./|; - if ( -s $local_config_file and -r $local_config_file ) - { - print( STDERR "Warning, overriding installed $local_config_file file with local copy\n" ); - $config_file = $local_config_file; - } - open( CONFIG, "<".$config_file ) or croak( "Can't open config file '$config_file': $!" ); - foreach my $str ( ) - { - next if ( $str =~ /^\s*$/ ); - next if ( $str =~ /^\s*#/ ); + my $config_file = ZM_CONFIG; + open( my $CONFIG, "<", $config_file ) + or croak( "Can't open config file '$config_file': $!" ); + foreach my $str ( <$CONFIG> ) + { + next if ( $str =~ /^\s*$/ ); + next if ( $str =~ /^\s*#/ ); my ( $name, $value ) = $str =~ /^\s*([^=\s]+)\s*=\s*(.*?)\s*$/; - if ( ! $name ) { - print( STDERR "Warning, bad line in $config_file: $str\n" ); - next; - } # end if - $name =~ tr/a-z/A-Z/; - $Config{$name} = $value; - } - close( CONFIG ); + if ( ! $name ) { + print( STDERR "Warning, bad line in $config_file: $str\n" ); + next; + } # end if + $name =~ tr/a-z/A-Z/; + $Config{$name} = $value; + } + close( $CONFIG ); - use DBI; - my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ) or croak( "Can't connect to db" ); - my $sql = 'select * from Config'; - my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() ); - while( my $config = $sth->fetchrow_hashref() ) { - $Config{$config->{Name}} = $config->{Value}; - } - $sth->finish(); - #$dbh->disconnect(); + use DBI; + my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} + .";host=".$Config{ZM_DB_HOST} + , $Config{ZM_DB_USER} + , $Config{ZM_DB_PASS} + ) or croak( "Can't connect to db" ); + my $sql = 'select * from Config'; + my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() ); + while( my $config = $sth->fetchrow_hashref() ) { + $Config{$config->{Name}} = $config->{Value}; + } + $sth->finish(); + #$dbh->disconnect(); + if ( ! exists $Config{ZM_SERVER_ID} ) { + $Config{ZM_SERVER_ID} = undef; + $sth = $dbh->prepare_cached( 'SELECT * FROM Servers WHERE Name=?' ); + if ( $Config{ZM_SERVER_NAME} ) { + $res = $sth->execute( $Config{ZM_SERVER_NAME} ); + my $result = $sth->fetchrow_hashref(); + $Config{ZM_SERVER_ID} = $$result{Id}; + } elsif ( $Config{ZM_SERVER_HOST} ) { + $res = $sth->execute( $Config{ZM_SERVER_HOST} ); + my $result = $sth->fetchrow_hashref(); + $Config{ZM_SERVER_ID} = $$result{Id}; + } + $sth->finish(); + } +} + +sub loadConfigFromDB { + print( "Loading config from DB\n" ); + my $dbh = ZoneMinder::Database::zmDbConnect(); + if ( !$dbh ) { + print( "Error: unable to load options from database: $DBI::errstr\n" ); + return( 0 ); + } + my $sql = "select * from Config"; + my $sth = $dbh->prepare_cached( $sql ) + or croak( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() + or croak( "Can't execute: ".$sth->errstr() ); + my $option_count = 0; + while( my $config = $sth->fetchrow_hashref() ) { + my ( $name, $value ) = ( $config->{Name}, $config->{Value} ); +#print( "Name = '$name'\n" ); + my $option = $options_hash{$name}; + if ( !$option ) { + warn( "No option '$name' found, removing" ); + next; + } +#next if ( $option->{category} eq 'hidden' ); + if ( defined($value) ) { + if ( $option->{type} == $types{boolean} ) { + $option->{value} = $value?"yes":"no"; + } else { + $option->{value} = $value; + } + } + $option_count++;; + } + $sth->finish(); + return( $option_count ); +} + +sub saveConfigToDB { + print( "Saving config to DB\n" ); + my $dbh = ZoneMinder::Database::zmDbConnect(); + if ( !$dbh ) + { + print( "Error: unable to save options to database: $DBI::errstr\n" ); + return( 0 ); + } + + my $ac = $dbh->{AutoCommit}; + $dbh->{AutoCommit} = 0; + + $dbh->do('LOCK TABLE Config WRITE') + or croak( "Can't lock Config table: " . $dbh->errstr() ); + + my $sql = "delete from Config"; + my $res = $dbh->do( $sql ) + or croak( "Can't do '$sql': ".$dbh->errstr() ); + + $sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Requires = ?"; + my $sth = $dbh->prepare_cached( $sql ) + or croak( "Can't prepare '$sql': ".$dbh->errstr() ); + foreach my $option ( @options ) + { +#next if ( $option->{category} eq 'hidden' ); +#print( $option->{name}."\n" ) if ( !$option->{category} ); + $option->{db_type} = $option->{type}->{db_type}; + $option->{db_hint} = $option->{type}->{hint}; + $option->{db_pattern} = $option->{type}->{pattern}; + $option->{db_format} = $option->{type}->{format}; + if ( $option->{db_type} eq "boolean" ) + { + $option->{db_value} = ($option->{value} eq "yes") + ? "1" + : "0" + ; + } + else + { + $option->{db_value} = $option->{value}; + } + if ( my $requires = $option->{requires} ) + { + $option->{db_requires} = join( ";", + map { + my $value = $_->{value}; + $value = ($value eq "yes") + ? 1 + : 0 + if ( $options_hash{$_->{name}}->{db_type} eq "boolean" ) + ; ( "$_->{name}=$value" ) + } @$requires + ); + } + else + { + } + my $res = $sth->execute( + $option->{id}, + $option->{name}, + $option->{db_value}, + $option->{db_type}, + $option->{default}, + $option->{db_hint}, + $option->{db_pattern}, + $option->{db_format}, + $option->{description}, + $option->{help}, + $option->{category}, + $option->{readonly} ? 1 : 0, + $option->{db_requires} + ) or croak( "Can't execute: ".$sth->errstr() ); + } + $sth->finish(); + + $dbh->do('UNLOCK TABLES'); + $dbh->{AutoCommit} = $ac; } 1; @@ -108,15 +241,40 @@ ZoneMinder::Config - ZoneMinder configuration module. =head1 SYNOPSIS - use ZoneMinder::Config qw(:all); +use ZoneMinder::Config qw(:all); =head1 DESCRIPTION -The ZoneMinder::Config module is used to import the ZoneMinder configuration from the database. It will do this at compile time in a BEGIN block and require access to the zm.conf file either in the current directory or in its defined location in order to determine database access details, configuration from this file will also be included. If the :all or :config tags are used then this configuration is exported into the namespace of the calling program or module. +The ZoneMinder::Config module is used to import the ZoneMinder +configuration from the database. It will do this at compile time in a BEGIN +block and require access to the zm.conf file either in the current +directory or in its defined location in order to determine database access +details, configuration from this file will also be included. If the :all or +:config tags are used then this configuration is exported into the +namespace of the calling program or module. -Once the configuration has been imported then configuration variables are defined as constants and can be accessed directory by name, e.g. +Once the configuration has been imported then configuration variables are +defined as constants and can be accessed directory by name, e.g. - $lang = $Config{ZM_LANG_DEFAULT}; +$lang = $Config{ZM_LANG_DEFAULT}; + +=head1 METHODS + +=over 4 + +=item loadConfigFromDB (); + +Loads existing configuration from the database (if any) and merges it with +the definitions held in this module. This results in the merging of any new +configuration and the removal of any deprecated configuration while +preserving the existing values of every else. + +=item saveConfigToDB (); + +Saves configuration held in memory to the database. The act of loading and +saving configuration is a convenient way to ensure that the configuration +held in the database corresponds with the most recent definitions and that +all components are using the same set of configuration. =head2 EXPORT @@ -139,7 +297,7 @@ Copyright (C) 2001-2008 Philip Coombes This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.3 or, -at your option, any later version of Perl 5 you may have available. + at your option, any later version of Perl 5 you may have available. -=cut + =cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm b/scripts/ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm deleted file mode 100644 index 673e13c98..000000000 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigAdmin.pm +++ /dev/null @@ -1,223 +0,0 @@ -# ========================================================================== -# -# ZoneMinder Config Admin Module, $Date$, $Revision$ -# Copyright (C) 2001-2008 Philip Coombes -# -# 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 module contains the debug definitions and functions used by the rest -# of the ZoneMinder scripts -# -package ZoneMinder::ConfigAdmin; - -use 5.006; -use strict; -use warnings; - -require Exporter; -require ZoneMinder::Base; - -our @ISA = qw(Exporter ZoneMinder::Base); - -# Items to export into callers namespace by default. Note: do not export -# names by default without a very good reason. Use EXPORT_OK instead. -# Do not simply export all your public functions/methods/constants. - -# This allows declaration use ZoneMinder ':all'; -# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK -# will save memory. -our %EXPORT_TAGS = ( - 'functions' => [ qw( - loadConfigFromDB - saveConfigToDB - ) ] -); -push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; - -our @EXPORT_OK = ( @{ $EXPORT_TAGS{'functions'} } ); - -our @EXPORT = qw(); - -our $VERSION = $ZoneMinder::Base::VERSION; - -# ========================================================================== -# -# Configuration Administration -# -# ========================================================================== - -use ZoneMinder::Config qw(:all); -use ZoneMinder::ConfigData qw(:all); - -use Carp; - -sub loadConfigFromDB -{ - print( "Loading config from DB\n" ); - my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); - - if ( !$dbh ) - { - print( "Error: unable to load options from database: $DBI::errstr\n" ); - return( 0 ); - } - my $sql = "select * from Config"; - my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or croak( "Can't execute: ".$sth->errstr() ); - my $option_count = 0; - while( my $config = $sth->fetchrow_hashref() ) - { - my ( $name, $value ) = ( $config->{Name}, $config->{Value} ); - #print( "Name = '$name'\n" ); - my $option = $options_hash{$name}; - if ( !$option ) - { - warn( "No option '$name' found, removing" ); - next; - } - #next if ( $option->{category} eq 'hidden' ); - if ( defined($value) ) - { - if ( $option->{type} == $types{boolean} ) - { - $option->{value} = $value?"yes":"no"; - } - else - { - $option->{value} = $value; - } - } - $option_count++;; - } - $sth->finish(); - $dbh->disconnect(); - return( $option_count ); -} - -sub saveConfigToDB -{ - print( "Saving config to DB\n" ); - my $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); - - if ( !$dbh ) - { - print( "Error: unable to save options to database: $DBI::errstr\n" ); - return( 0 ); - } - - my $ac = $dbh->{AutoCommit}; - $dbh->{AutoCommit} = 0; - - $dbh->do('LOCK TABLE Config WRITE') or croak( "Can't lock Config table: " . $dbh->errstr() ); - - my $sql = "delete from Config"; - my $res = $dbh->do( $sql ) or croak( "Can't do '$sql': ".$dbh->errstr() ); - - $sql = "replace into Config set Id = ?, Name = ?, Value = ?, Type = ?, DefaultValue = ?, Hint = ?, Pattern = ?, Format = ?, Prompt = ?, Help = ?, Category = ?, Readonly = ?, Requires = ?"; - my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); - foreach my $option ( @options ) - { - #next if ( $option->{category} eq 'hidden' ); - #print( $option->{name}."\n" ) if ( !$option->{category} ); - $option->{db_type} = $option->{type}->{db_type}; - $option->{db_hint} = $option->{type}->{hint}; - $option->{db_pattern} = $option->{type}->{pattern}; - $option->{db_format} = $option->{type}->{format}; - if ( $option->{db_type} eq "boolean" ) - { - $option->{db_value} = ($option->{value} eq "yes")?"1":"0"; - } - else - { - $option->{db_value} = $option->{value}; - } - if ( my $requires = $option->{requires} ) - { - $option->{db_requires} = join( ";", map { my $value = $_->{value}; $value = ($value eq "yes")?1:0 if ( $options_hash{$_->{name}}->{db_type} eq "boolean" ); ( "$_->{name}=$value" ) } @$requires ); - } - else - { - } - my $res = $sth->execute( $option->{id}, $option->{name}, $option->{db_value}, $option->{db_type}, $option->{default}, $option->{db_hint}, $option->{db_pattern}, $option->{db_format}, $option->{description}, $option->{help}, $option->{category}, $option->{readonly}?1:0, $option->{db_requires} ) or croak( "Can't execute: ".$sth->errstr() ); - } - $sth->finish(); - - $dbh->do('UNLOCK TABLES'); - $dbh->{AutoCommit} = $ac; - - $dbh->disconnect(); -} - -1; -__END__ - -=head1 NAME - -ZoneMinder::ConfigAdmin - ZoneMinder Configuration Administration module - -=head1 SYNOPSIS - - use ZoneMinder::ConfigAdmin; - use ZoneMinder::ConfigAdmin qw(:all); - - loadConfigFromDB(); - saveConfigToDB(); - -=head1 DESCRIPTION - -The ZoneMinder:ConfigAdmin module contains the master definition of the ZoneMinder configuration options as well as helper methods. This module is intended for specialist confguration management and would not normally be used by end users. - -The configuration held in this module, which was previously in zmconfig.pl, includes the name, default value, description, help text, type and category for each option, as well as a number of additional fields in a small number of cases. - -=head1 METHODS - -=over 4 - -=item loadConfigFromDB (); - -Loads existing configuration from the database (if any) and merges it with the definitions held in this module. This results in the merging of any new configuration and the removal of any deprecated configuration while preserving the existing values of every else. - -=item saveConfigToDB (); - -Saves configuration held in memory to the database. The act of loading and saving configuration is a convenient way to ensure that the configuration held in the database corresponds with the most recent definitions and that all components are using the same set of configuration. - -=head2 EXPORT - -None by default. -The :data tag will export the various configuration data structures -The :functions tag will export the helper functions. -The :all tag will export all above symbols. - - -=head1 SEE ALSO - -http://www.zoneminder.com - -=head1 AUTHOR - -Philip Coombes, Ephilip.coombes@zoneminder.comE - -=head1 COPYRIGHT AND LICENSE - -Copyright (C) 2001-2008 Philip Coombes - -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself, either Perl version 5.8.3 or, -at your option, any later version of Perl 5 you may have available. - - -=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 54bd14e26..610d2432d 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -19,7 +19,7 @@ # # ========================================================================== # -# This module contains the debug definitions and functions used by the rest +# This module contains the debug definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::ConfigData; @@ -37,15 +37,15 @@ our @ISA = qw(Exporter ZoneMinder::Base); # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. -# This allows declaration use ZoneMinder ':all'; +# This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( - 'data' => [ qw( - %types - @options - %options_hash - ) ] + 'data' => [ qw( + %types + @options + %options_hash + ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; @@ -73,920 +73,2119 @@ sub INIT # Types our %types = ( - string => { db_type=>"string", hint=>"string", pattern=>qr|^(.+)$|, format=>q( $1 ) }, - alphanum => { db_type=>"string", hint=>"alphanumeric", pattern=>qr|^([a-zA-Z0-9-_]+)$|, format=>q( $1 ) }, - text => { db_type=>"text", hint=>"free text", pattern=>qr|^(.+)$|, format=>q( $1 ) }, - boolean => { db_type=>"boolean", hint=>"yes|no", pattern=>qr|^([yn])|i, check=>q( $1 ), format=>q( ($1 =~ /^y/) ? "yes" : "no" ) }, - integer => { db_type=>"integer", hint=>"integer", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - decimal => { db_type=>"decimal", hint=>"decimal", pattern=>qr|^(\d+(?:\.\d+)?)$|, format=>q( $1 ) }, - hexadecimal => { db_type=>"hexadecimal", hint=>"hexadecimal", pattern=>qr|^(?:0x)?([0-9a-f]{1,8})$|, format=>q( "0x".$1 ) }, - tristate => { db_type=>"string", hint=>"auto|yes|no", pattern=>qr|^([ayn])|i, check=>q( $1 ), format=>q( ($1 =~ /^y/) ? "yes" : ($1 =~ /^n/ ? "no" : "auto" ) ) }, - abs_path => { db_type=>"string", hint=>"/absolute/path/to/somewhere", pattern=>qr|^((?:/[^/]*)+?)/?$|, format=>q( $1 ) }, - rel_path => { db_type=>"string", hint=>"relative/path/to/somewhere", pattern=>qr|^((?:[^/].*)?)/?$|, format=>q( $1 ) }, - directory => { db_type=>"string", hint=>"directory", pattern=>qr|^([a-zA-Z0-9-_.]+)$|, format=>q( $1 ) }, - file => { db_type=>"string", hint=>"filename", pattern=>qr|^([a-zA-Z0-9-_.]+)$|, format=>q( $1 ) }, - hostname => { db_type=>"string", hint=>"host.your.domain", pattern=>qr|^([a-zA-Z0-9_.-]+)$|, format=>q( $1 ) }, - url => { db_type=>"string", hint=>"http://host.your.domain/", pattern=>qr|^(?:http://)?(.+)$|, format=>q( "http://".$1 ) }, - email => { db_type=>"string", hint=>"your.name\@your.domain", pattern=>qr|^([a-zA-Z0-9_.-]+)\@([a-zA-Z0-9_.-]+)$|, format=>q( $1\@$2 ) }, + string => { + db_type =>"string", + hint =>"string", + pattern =>qr|^(.+)$|, + format =>q( $1 ) + }, + alphanum => { + db_type =>"string", + hint =>"alphanumeric", + pattern =>qr|^([a-zA-Z0-9-_]+)$|, + format =>q( $1 ) + }, + text => { + db_type =>"text", + hint =>"free text", + pattern =>qr|^(.+)$|, + format =>q( $1 ) + }, + boolean => { + db_type =>"boolean", + hint =>"yes|no", + pattern =>qr|^([yn])|i, + check =>q( $1 ), + format =>q( ($1 =~ /^y/) + ? "yes" + : "no" + ) + }, + integer => { + db_type =>"integer", + hint =>"integer", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + decimal => { + db_type =>"decimal", + hint =>"decimal", + pattern =>qr|^(\d+(?:\.\d+)?)$|, + format =>q( $1 ) + }, + hexadecimal => { + db_type =>"hexadecimal", + hint =>"hexadecimal", + pattern =>qr|^(?:0x)?([0-9a-f]{1,8})$|, + format =>q( "0x".$1 ) + }, + tristate => { + db_type =>"string", + hint =>"auto|yes|no", + pattern =>qr|^([ayn])|i, check=>q( $1 ), + format =>q( ($1 =~ /^y/) + ? "yes" + : ($1 =~ /^n/ ? "no" : "auto" ) + ) + }, + abs_path => { + db_type =>"string", + hint =>"/absolute/path/to/somewhere", + pattern =>qr|^((?:/[^/]*)+?)/?$|, + format =>q( $1 ) + }, + rel_path => { + db_type =>"string", + hint =>"relative/path/to/somewhere", + pattern =>qr|^((?:[^/].*)?)/?$|, + format =>q( $1 ) + }, + directory => { + db_type =>"string", + hint =>"directory", + pattern =>qr|^([a-zA-Z0-9-_.]+)$|, + format =>q( $1 ) + }, + file => { + db_type =>"string", + hint =>"filename", + pattern =>qr|^([a-zA-Z0-9-_.]+)$|, + format =>q( $1 ) + }, + hostname => { + db_type =>"string", + hint =>"host.your.domain", + pattern =>qr|^([a-zA-Z0-9_.-]+)$|, + format =>q( $1 ) + }, + url => { + db_type =>"string", + hint =>"http://host.your.domain/", + pattern =>qr|^(?:http://)?(.+)$|, + format =>q( "http://".$1 ) + }, + email => { + db_type =>"string", + hint =>"your.name\@your.domain", + pattern =>qr|^([a-zA-Z0-9_.-]+)\@([a-zA-Z0-9_.-]+)$|, + format =>q( $1\@$2 ) + }, ); +sub qqq { ## Un-pad paragraph of text. + local $_ = shift; + s{\n?^\s*}{ }mg; + return $_; +} + our @options = ( - { - name => "ZM_LANG_DEFAULT", - default => "en_gb", - description => "Default language used by web interface", - help => "ZoneMinder allows the web interface to use languages other than English if the appropriate language file has been created and is present. This option allows you to change the default language that is used from the shipped language, British English, to another language", - type => $types{string}, - category => "system", - }, - { - name => "ZM_OPT_USE_AUTH", - default => "no", - description => "Authenticate user logins to ZoneMinder", - help => "ZoneMinder can run in two modes. The simplest is an entirely unauthenticated mode where anyone can access ZoneMinder and perform all tasks. This is most suitable for installations where the web server access is limited in other ways. The other mode enables user accounts with varying sets of permissions. Users must login or authenticate to access ZoneMinder and are limited by their defined permissions.", - type => $types{boolean}, - category => "system", - }, - { - name => "ZM_AUTH_TYPE", - default => "builtin", - description => "What is used to authenticate ZoneMinder users", - help => "ZoneMinder can use two methods to authenticate users when running in authenticated mode. The first is a builtin method where ZoneMinder provides facilities for users to log in and maintains track of their identity. The second method allows interworking with other methods such as http basic authentication which passes an independently authentication 'remote' user via http. In this case ZoneMinder would use the supplied user without additional authentication provided such a user is configured ion ZoneMinder.", - requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" } ], - type => { db_type=>"string", hint=>"builtin|remote", pattern=>qr|^([br])|i, format=>q( $1 =~ /^b/ ? "builtin" : "remote" ) }, - category => "system", - }, - { - name => "ZM_AUTH_RELAY", - default => "hashed", - description => "Method used to relay authentication information", - help => "When ZoneMinder is running in authenticated mode it can pass user details between the web pages and the back end processes. There are two methods for doing this. This first is to use a time limited hashed string which contains no direct username or password details, the second method is to pass the username and passwords around in plaintext. This method is not recommend except where you do not have the md5 libraries available on your system or you have a completely isolated system with no external access. You can also switch off authentication relaying if your system is isolated in other ways.", - requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" } ], - type => { db_type=>"string", hint=>"hashed|plain|none", pattern=>qr|^([hpn])|i, format=>q( ($1 =~ /^h/) ? "hashed" : ($1 =~ /^p/ ? "plain" : "none" ) ) }, - category => "system", - }, - { - name => "ZM_AUTH_HASH_SECRET", - default => "...Change me to something unique...", - description => "Secret for encoding hashed authentication information", - help => "When ZoneMinder is running in hashed authenticated mode it is necessary to generate hashed strings containing encrypted sensitive information such as usernames and password. Although these string are reasonably secure the addition of a random secret increases security substantially.", - requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" }, { name=>"ZM_AUTH_RELAY", value=>"hashed" } ], - type => $types{string}, - category => "system", - }, - { - name => "ZM_AUTH_HASH_IPS", - default => "yes", - description => "Include IP addresses in the authentication hash", - help => "When ZoneMinder is running in hashed authenticated mode it can optionally include the requesting IP address in the resultant hash. This adds an extra level of security as only requests from that address may use that authentication key. However in some circumstances, such as access over mobile networks, the requesting address can change for each request which will cause most requests to fail. This option allows you to control whether IP addresses are included in the authentication hash on your system. If you experience intermitent problems with authentication, switching this option off may help.", - requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" }, { name=>"ZM_AUTH_RELAY", value=>"hashed" } ], - type => $types{boolean}, - category => "system", - }, - { - name => "ZM_AUTH_HASH_LOGINS", - default => "no", - description => "Allow login by authentication hash", - help => "The normal process for logging into ZoneMinder is via the login screen with username and password. In some circumstances it may be desirable to allow access directly to one or more pages, for instance from a third party application. If this option is enabled then adding an 'auth' parameter to any request will include a shortcut login bypassing the login screen, if not already logged in. As authentication hashes are time and, optionally, IP limited this can allow short-term access to ZoneMinder screens from other web pages etc. In order to use this the calling application will hae to generate the authentication hash itself and ensure it is valid. If you use this option you should ensure that you have modified the ZM_AUTH_HASH_SECRET to somethign unique to your system.", - requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" }, { name=>"ZM_AUTH_RELAY", value=>"hashed" } ], - type => $types{boolean}, - category => "system", - }, - { - name => "ZM_DIR_EVENTS", - default => "events", - description => "Directory where events are stored", - help => "This is the path to the events directory where all the event images and other miscellaneous files are stored. CAUTION: The directory you specify here cannot be outside the web root. This is a common mistake. Most users should never change this value. If you intend to record events to a second disk or network share, then you should mount the drive or share directly to the ZoneMinder events folder or follow the instructions in the ZoneMinder Wiki titled Using a dedicated Hard Drive.", - type => $types{directory}, - category => "paths", - }, - { - name => "ZM_USE_DEEP_STORAGE", - default => "yes", - description => "Use a deep filesystem hierarchy for events", - help => "Traditionally ZoneMinder stores all events for a monitor in one directory for that monitor. This is simple and efficient except when you have very large amounts of events. Some filesystems are unable to store more than 32k files in one directory and even without this limitation, large numbers of files in a directory can slow creation and deletion of files. This option allows you to select an alternate method of storing events by year/month/day/hour/min/second which has the effect of separating events out into more directories, resulting in less per directory, and also making it easier to manually navigate to any events that may have happened at a particular time or date.", - type => $types{boolean}, - category => "paths", - }, - { - name => "ZM_DIR_IMAGES", - default => "images", - description => "Directory where the images that the ZoneMinder client generates are stored", - help => "ZoneMinder generates a myriad of images, mostly of which are associated with events. For those that aren't this is where they go. CAUTION: The directory you specify here cannot be outside the web root. This is a common mistake. Most users should never change this value. If you intend to save images to a second disk or network share, then you should mount the drive or share directly to the ZoneMinder images folder or follow the instructions in the ZoneMinder Wiki titled Using a dedicated Hard Drive.", - type => $types{directory}, - category => "paths", - }, - { - name => "ZM_DIR_SOUNDS", - default => "sounds", - description => "Directory to the sounds that the ZoneMinder client can use", - help => "ZoneMinder can optionally play a sound file when an alarm is detected. This indicates where to look for this file. CAUTION: The directory you specify here cannot be outside the web root. Most users should never change this value.", - type => $types{directory}, - category => "paths", - }, - { - name => "ZM_PATH_ZMS", - default => "/cgi-bin/nph-zms", - description => "Web path to zms streaming server", - help => "The ZoneMinder streaming server is required to send streamed images to your browser. It will be installed into the cgi-bin path given at configuration time. This option determines what the web path to the server is rather than the local path on your machine. Ordinarily the streaming server runs in parser-header mode however if you experience problems with streaming you can change this to non-parsed-header (nph) mode by changing 'zms' to 'nph-zms'.", - type => $types{rel_path}, - category => "paths", - }, - { - name => "ZM_COLOUR_JPEG_FILES", - default => "no", - description => "Colourise greyscale JPEG files", - help => "Cameras that capture in greyscale can write their captured images to jpeg files with a corresponding greyscale colour space. This saves a small amount of disk space over colour ones. However some tools such as ffmpeg either fail to work with this colour space or have to convert it beforehand. Setting this option to yes uses up a little more space but makes creation of MPEG files much faster.", - type => $types{boolean}, - category => "images", - }, - { - name => "ZM_ADD_JPEG_COMMENTS", - default => "no", - description => "Add jpeg timestamp annotations as file header comments", - help => "JPEG files may have a number of extra fields added to the file header. The comment field may have any kind of text added. This options allows you to have the same text that is used to annotate the image additionally included as a file header comment. If you archive event images to other locations this may help you locate images for particular events or times if you use software that can read comment headers.", - type => $types{boolean}, - category => "images", - }, - { - name => "ZM_JPEG_FILE_QUALITY", - default => "70", - description => "Set the JPEG quality setting for the saved event files (1-100)", - help => "When ZoneMinder detects an event it will save the images associated with that event to files. These files are in the JPEG format and can be viewed or streamed later. This option specifies what image quality should be used to save these files. A higher number means better quality but less compression so will take up more disk space and take longer to view over a slow connection. By contrast a low number means smaller, quicker to view, files but at the price of lower quality images. This setting applies to all images written except if the capture image has caused an alarm and the alarm file quality option is set at a higher value when that is used instead.", - type => $types{integer}, - category => "images", - }, - { - name => "ZM_JPEG_ALARM_FILE_QUALITY", - default => "0", - description => "Set the JPEG quality setting for the saved event files during an alarm (1-100)", - help => "This value is equivalent to the regular jpeg file quality setting above except that it only applies to images saved while in an alarm state and then only if this value is set to a higher quality setting than the ordinary file setting. If set to a lower value then it is ignored. Thus leaving it at the default of 0 effectively means to use the regular file quality setting for all saved images. This is to prevent acccidentally saving important images at a worse quality setting.", - type => $types{integer}, - category => "images", - }, - # Deprecated, now stream quality - { - name => "ZM_JPEG_IMAGE_QUALITY", - default => "70", - description => "Set the JPEG quality setting for the streamed 'live' images (1-100)", - help => "When viewing a 'live' stream for a monitor ZoneMinder will grab an image from the buffer and encode it into JPEG format before sending it. This option specifies what image quality should be used to encode these images. A higher number means better quality but less compression so will take longer to view over a slow connection. By contrast a low number means quicker to view images but at the price of lower quality images. This option does not apply when viewing events or still images as these are usually just read from disk and so will be encoded at the quality specified by the previous options.", - type => $types{integer}, - category => "hidden", - }, - { - name => "ZM_JPEG_STREAM_QUALITY", - default => "70", - description => "Set the JPEG quality setting for the streamed 'live' images (1-100)", - help => "When viewing a 'live' stream for a monitor ZoneMinder will grab an image from the buffer and encode it into JPEG format before sending it. This option specifies what image quality should be used to encode these images. A higher number means better quality but less compression so will take longer to view over a slow connection. By contrast a low number means quicker to view images but at the price of lower quality images. This option does not apply when viewing events or still images as these are usually just read from disk and so will be encoded at the quality specified by the previous options.", - type => $types{integer}, - category => "images", - }, - { - name => "ZM_MPEG_TIMED_FRAMES", - default => "yes", - description => "Tag video frames with a timestamp for more realistic streaming", - help => "When using streamed MPEG based video, either for live monitor streams or events, ZoneMinder can send the streams in two ways. If this option is selected then the timestamp for each frame, taken from it's capture time, is included in the stream. This means that where the frame rate varies, for instance around an alarm, the stream will still maintain it's 'real' timing. If this option is not selected then an approximate frame rate is calculated and that is used to schedule frames instead. This option should be selected unless you encounter problems with your preferred streaming method.", - type => $types{boolean}, - category => "images", - }, - { - name => "ZM_MPEG_LIVE_FORMAT", - default => "swf", - description => "What format 'live' video streams are played in", - help => "When using MPEG mode ZoneMinder can output live video. However what formats are handled by the browser varies greatly between machines. This option allows you to specify a video format using a file extension format, so you would just enter the extension of the file type you would like and the rest is determined from that. The default of 'asf' works well under Windows with Windows Media Player but I'm currently not sure what, if anything, works on a Linux platform. If you find out please let me know! If this option is left blank then live streams will revert to being in motion jpeg format", - type => $types{string}, - category => "images", - }, - { - name => "ZM_MPEG_REPLAY_FORMAT", - default => "swf", - description => "What format 'replay' video streams are played in", - help => "When using MPEG mode ZoneMinder can replay events in encoded video format. However what formats are handled by the browser varies greatly between machines. This option allows you to specify a video format using a file extension format, so you would just enter the extension of the file type you would like and the rest is determined from that. The default of 'asf' works well under Windows with Windows Media Player and 'mpg', or 'avi' etc should work under Linux. If you know any more then please let me know! If this option is left blank then live streams will revert to being in motion jpeg format", - type => $types{string}, - category => "images", - }, - { - name => "ZM_RAND_STREAM", - default => "yes", - description => "Add a random string to prevent caching of streams", - help => "Some browsers can cache the streams used by ZoneMinder. In order to prevent his a harmless random string can be appended to the url to make each invocation of the stream appear unique.", - type => $types{boolean}, - category => "images", - }, - { - name => "ZM_OPT_CAMBOZOLA", - default => "no", - description => "Is the (optional) cambozola java streaming client installed", - help => "Cambozola is a handy low fat cheese flavoured Java applet that ZoneMinder uses to view image streams on browsers such as Internet Explorer that don't natively support this format. If you use this browser it is highly recommended to install this from http://www.charliemouse.com/code/cambozola/ however if it is not installed still images at a lower refresh rate can still be viewed.", - type => $types{boolean}, - category => "images", - }, - { - name => "ZM_PATH_CAMBOZOLA", - default => "cambozola.jar", - description => "Web path to (optional) cambozola java streaming client", - help => "Cambozola is a handy low fat cheese flavoured Java applet that ZoneMinder uses to view image streams on browsers such as Internet Explorer that don't natively support this format. If you use this browser it is highly recommended to install this from http://www.charliemouse.com/code/cambozola/ however if it is not installed still images at a lower refresh rate can still be viewed. Leave this as 'cambozola.jar' if cambozola is installed in the same directory as the ZoneMinder web client files.", - requires => [ { name=>"ZM_OPT_CAMBOZOLA", value=>"yes" } ], - type => $types{rel_path}, - category => "images", - }, - { - name => "ZM_RELOAD_CAMBOZOLA", - default => "0", - description => "After how many seconds should Cambozola be reloaded in live view", - help => "Cambozola allows for the viewing of streaming MJPEG however it caches the entire stream into cache space on the computer, setting this to a number > 0 will cause it to automatically reload after that many seconds to avoid filling up a hard drive.", - type => $types{integer}, - category => "images", - }, - { - name => "ZM_TIMESTAMP_ON_CAPTURE", - default => "yes", - description => "Timestamp images as soon as they are captured", - help => "ZoneMinder can add a timestamp to images in two ways. The default method, when this option is set, is that each image is timestamped immediately when captured and so the image held in memory is marked right away. The second method does not timestamp the images until they are either saved as part of an event or accessed over the web. The timestamp used in both methods will contain the same time as this is preserved along with the image. The first method ensures that an image is timestamped regardless of any other circumstances but will result in all images being timestamped even those never saved or viewed. The second method necessitates that saved images are copied before being saved otherwise two timestamps perhaps at different scales may be applied. This has the (perhaps) desirable side effect that the timestamp is always applied at the same resolution so an image that has scaling applied will still have a legible and correctly scaled timestamp.", - type => $types{boolean}, - category => "config", - }, - { - name => "ZM_CPU_EXTENSIONS", - default => "yes", - description => "Use advanced CPU extensions to increase performance", - help => "When advanced processor extensions such as SSE2 or SSSE3 are available, ZoneMinder can use them, which should increase performance and reduce system load. Enabling this option on processors that do not support the advanced processors extensions used by ZoneMinder is harmless and will have no effect.", - type => $types{boolean}, - category => "config", - }, - { - name => "ZM_FAST_IMAGE_BLENDS", - default => "yes", - description => "Use a fast algorithm to blend the reference image", - help => "To detect alarms ZoneMinder needs to blend the captured image with the stored reference image to update it for comparison with the next image. The reference blend percentage specified for the monitor controls how much the new image affects the reference image. There are two methods that are available for this. If this option is set then fast calculation which does not use any multiplication or division is used. This calculation is extremely fast, however it limits the possible blend percentages to 50%, 25%, 12.5%, 6.25%, 3.25% and 1.5%. Any other blend percentage will be rounded to the nearest possible one. The alternative is to switch this option off and use standard blending instead, which is slower.", - type => $types{boolean}, - category => "config", - }, - { - name => "ZM_OPT_ADAPTIVE_SKIP", - default => "yes", - description => "Should frame analysis try and be efficient in skipping frames", - help => "In previous versions of ZoneMinder the analysis daemon would attempt to keep up with the capture daemon by processing the last captured frame on each pass. This would sometimes have the undesirable side-effect of missing a chunk of the initial activity that caused the alarm because the pre-alarm frames would all have to be written to disk and the database before processing the next frame, leading to some delay between the first and second event frames. Setting this option enables a newer adaptive algorithm where the analysis daemon attempts to process as many captured frames as possible, only skipping frames when in danger of the capture daemon overwriting yet to be processed frames. This skip is variable depending on the size of the ring buffer and the amount of space left in it. Enabling this option will give you much better coverage of the beginning of alarms whilst biasing out any skipped frames towards the middle or end of the event. However you should be aware that this will have the effect of making the analysis daemon run somewhat behind the capture daemon during events and for particularly fast rates of capture it is possible for the adaptive algorithm to be overwhelmed and not have time to react to a rapid build up of pending frames and thus for a buffer overrun condition to occur.", - type => $types{boolean}, - category => "config", - }, - { - name => "ZM_MAX_SUSPEND_TIME", - default => "30", - description => "Maximum time that a monitor may have motion detection suspended", - help => "ZoneMinder allows monitors to have motion detection to be suspended, for instance while panning a camera. Ordinarily this relies on the operator resuming motion detection afterwards as failure to do so can leave a monitor in a permanently suspended state. This setting allows you to set a maximum time which a camera may be suspended for before it automatically resumes motion detection. This time can be extended by subsequent suspend indications after the first so continuous camera movement will also occur while the monitor is suspended.", - type => $types{integer}, - category => "config", - }, - # Deprecated, really no longer necessary - { - name => "ZM_OPT_REMOTE_CAMERAS", - default => "no", - description => "Are you going to use remote/networked cameras", - help => "ZoneMinder can work with both local cameras, ie. those attached physically to your computer and remote or network cameras. If you will be using networked cameras select this option.", - type => $types{boolean}, - category => "hidden", - }, - # Deprecated, now set on a per monitor basis using the Method field - { - name => "ZM_NETCAM_REGEXPS", - default => "yes", - description => "Use regular expression matching with network cameras", - help => "Traditionally ZoneMinder has used complex regular regular expressions to handle the multitude of formats that network cameras produce. In versions from 1.21.1 the default is to use a simpler and faster built in pattern matching methodology. This works well with most networks cameras but if you have problems you can try the older, but more flexible, regular expression based method by selecting this option. Note, to use this method you must have libpcre installed on your system.", - requires => [ { name => "ZM_OPT_REMOTE_CAMERAS", value => "yes" } ], - type => $types{boolean}, - category => "hidden", - }, - { - name => "ZM_HTTP_VERSION", - default => "1.0", - description => "The version of HTTP that ZoneMinder will use to connect", - help => "ZoneMinder can communicate with network cameras using either of the HTTP/1.1 or HTTP/1.0 standard. A server will normally fall back to the version it supports with no problem so this should usually by left at the default. However it can be changed to HTTP/1.0 if necessary to resolve particular issues.", - type => { db_type=>"string", hint=>"1.1|1.0", pattern=>qr|^(1\.[01])$|, format=>q( $1?$1:"" ) }, - category => "network", - }, - { - name => "ZM_HTTP_UA", - default => "ZoneMinder", - description => "The user agent that ZoneMinder uses to identify itself", - help => "When ZoneMinder communicates with remote cameras it will identify itself using this string and it's version number. This is normally sufficient, however if a particular cameras expects only to communicate with certain browsers then this can be changed to a different string identifying ZoneMinder as Internet Explorer or Netscape etc.", - type => $types{string}, - category => "network", - }, - { - name => "ZM_HTTP_TIMEOUT", - default => "2500", - description => "How long ZoneMinder waits before giving up on images (milliseconds)", - help => "When retrieving remote images ZoneMinder will wait for this length of time before deciding that an image is not going to arrive and taking steps to retry. This timeout is in milliseconds (1000 per second) and will apply to each part of an image if it is not sent in one whole chunk.", - type => $types{integer}, - category => "network", - }, { - name => "ZM_MIN_RTP_PORT", - default => "40200", - description => "Minimum port that ZoneMinder will listen for RTP traffic on", - help => "When ZoneMinder communicates with MPEG4 capable cameras using RTP with the unicast method it must open ports for the camera to connect back to for control and streaming purposes. This setting specifies the minimum port number that ZoneMinder will use. Ordinarily two adjacent ports are used for each camera, one for control packets and one for data packets. This port should be set to an even number, you may also need to open up a hole in your firewall to allow cameras to connect back if you wish to use unicasting.", - type => $types{integer}, - category => "network", - }, - { - name => "ZM_MAX_RTP_PORT", - default => "40499", - description => "Maximum port that ZoneMinder will listen for RTP traffic on", - help => "When ZoneMinder communicates with MPEG4 capable cameras using RTP with the unicast method it must open ports for the camera to connect back to for control and streaming purposes. This setting specifies the maximum port number that ZoneMinder will use. Ordinarily two adjacent ports are used for each camera, one for control packets and one for data packets. This port should be set to an even number, you may also need to open up a hole in your firewall to allow cameras to connect back if you wish to use unicasting. You should also ensure that you have opened up at least two ports for each monitor that will be connecting to unicasting network cameras.", - type => $types{integer}, - category => "network", - }, - { - name => "ZM_OPT_FFMPEG", - default => "@OPT_FFMPEG@", - description => "Is the ffmpeg video encoder/decoder installed", - help => "ZoneMinder can optionally encode a series of video images into an MPEG encoded movie file for viewing, downloading or storage. This option allows you to specify whether you have the ffmpeg tools installed. Note that creating MPEG files can be fairly CPU and disk intensive and is not a required option as events can still be reviewed as video streams without it.", - type => $types{boolean}, - category => "images", - }, - { - name => "ZM_PATH_FFMPEG", - default => "@PATH_FFMPEG@", - description => "Path to (optional) ffmpeg mpeg encoder", - help => "This path should point to where ffmpeg has been installed.", - requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], - type => $types{abs_path}, - category => "images", - }, - { - name => "ZM_FFMPEG_INPUT_OPTIONS", - default => "", - description => "Additional input options to ffmpeg", - help => "Ffmpeg can take many options on the command line to control the quality of video produced. This option allows you to specify your own set that apply to the input to ffmpeg (options that are given before the -i option). Check the ffmpeg documentation for a full list of options which may be used here.", - requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], - type => $types{string}, - category => "images", - }, - { - name => "ZM_FFMPEG_OUTPUT_OPTIONS", - default => "-r 25", - description => "Additional output options to ffmpeg", - help => "Ffmpeg can take many options on the command line to control the quality of video produced. This option allows you to specify your own set that apply to the output from ffmpeg (options that are given after the -i option). Check the ffmpeg documentation for a full list of options which may be used here. The most common one will often be to force an output frame rate supported by the video encoder.", - requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], - type => $types{string}, - category => "images", - }, - { - name => "ZM_FFMPEG_FORMATS", - default => "mpg mpeg wmv asf avi* mov swf 3gp**", - description => "Formats to allow for ffmpeg video generation", - help => "Ffmpeg can generate video in many different formats. This option allows you to list the ones you want to be able to select. As new formats are supported by ffmpeg you can add them here and be able to use them immediately. Adding a '*' after a format indicates that this will be the default format used for web video, adding '**' defines the default format for phone video.", - requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], - type => $types{string}, - category => "images", - }, - { - name => "ZM_FFMPEG_OPEN_TIMEOUT", - default => "10", - description => "Timeout in seconds when opening a stream.", - help => "When Ffmpeg is opening a stream, it can take a long time before failing; certain circumstances even seem to be able to lock indefinitely. This option allows you to set a maximum time in seconds to pass before closing the stream and trying to reopen it again.", - requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], - type => $types{integer}, - category => "images", - }, - { - name => "ZM_LOG_LEVEL_SYSLOG", - default => "0", - description => "Save logging output to the system log", - help => "ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that goes to the system log. ZoneMinder binaries have always logged to the system log but now scripts and web logging is also included. To preserve the previous behaviour you should ensure this value is set to Info or Warning. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance. If you want debug you will also need to set a level and component below", - type => { db_type=>"integer", hint=>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "logging", - }, - { - name => "ZM_LOG_LEVEL_FILE", - default => "-5", - description => "Save logging output to component files", - help => "ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that goes to individual log files written by specific components. This is how logging worked previously and although useful for tracking down issues in specific components it also resulted in many disparate log files. To preserve this behaviour you should ensure this value is set to Info or Warning. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance though file output has less impact than the other options. If you want debug you will also need to set a level and component below", - type => { db_type=>"integer", hint=>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "logging", - }, - { - name => "ZM_LOG_LEVEL_WEBLOG", - default => "-5", - description => "Save logging output to the weblog", - help => "ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output from the web interface that goes to the httpd error log. Note that only web logging from PHP and JavaScript files is included and so this option is really only useful for investigating specific issues with those components. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance. If you want debug you will also need to set a level and component below", - type => { db_type=>"integer", hint=>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "logging", - }, - { - name => "ZM_LOG_LEVEL_DATABASE", - default => "0", - description => "Save logging output to the database", - help => "ZoneMinder logging is now more more integrated between components and allows you to specify the destination for logging output and the individual levels for each. This option lets you control the level of logging output that is written to the database. This is a new option which can make viewing logging output easier and more intuitive and also makes it easier to get an overall impression of how the system is performing. If you have a large or very busy system then it is possible that use of this option may slow your system down if the table becomes very large. Ensure you use the LOG_DATABASE_LIMIT option to keep the table to a manageable size. This option controls the maximum level of logging that will be written, so Info includes Warnings and Errors etc. To disable entirely, set this option to None. You should use caution when setting this option to Debug as it can affect severely affect system performance. If you want debug you will also need to set a level and component below", - type => { db_type=>"integer", hint=>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "logging", - }, - { - name => "ZM_LOG_DATABASE_LIMIT", - default => "7 day", - description => "Maximum number of log entries to retain", - help => "If you are using database logging then it is possible to quickly build up a large number of entries in the Logs table. This option allows you to specify how many of these entries are kept. If you set this option to a number greater than zero then that number is used to determine the maximum number of rows, less than or equal to zero indicates no limit and is not recommended. You can also set this value to time values such as ' day' which will limit the log entries to those newer than that time. You can specify 'hour', 'day', 'week', 'month' and 'year', note that the values should be singular (no 's' at the end). The Logs table is pruned periodically so it is possible for more than the expected number of rows to be present briefly in the meantime.", - type => $types{string}, - category => "logging", - }, - { - name => "ZM_LOG_DEBUG", - default => "no", - description => "Switch debugging on", - help => "ZoneMinder components usually support debug logging available to help with diagnosing problems. Binary components have several levels of debug whereas more other components have only one. Normally this is disabled to minimise performance penalties and avoid filling logs too quickly. This option lets you switch on other options that allow you to configure additional debug information to be output. Components will pick up this instruction when they are restarted.", - type => $types{boolean}, - category => "logging", - }, - { - name => "ZM_LOG_DEBUG_TARGET", - default => "", - description => "What components should have extra debug enabled", - help => "There are three scopes of debug available. Leaving this option blank means that all components will use extra debug (not recommended). Setting this option to '_', e.g. _zmc, will limit extra debug to that component only. Setting this option to '__', e.g. '_zmc_m1' will limit extra debug to that instance of the component only. This is ordinarily what you probably want to do. To debug scripts use their names without the .pl extension, e.g. '_zmvideo' and to debug issues with the web interface use '_web'. You can specify multiple targets by separating them with '|' characters.", - requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], - type => $types{string}, - category => "logging", - }, - { - name => "ZM_LOG_DEBUG_LEVEL", - default => 1, - description => "What level of extra debug should be enabled", - help => "There are 9 levels of debug available, with higher numbers being more debug and level 0 being no debug. However not all levels are used by all components. Also if there is debug at a high level it is usually likely to be output at such a volume that it may obstruct normal operation. For this reason you should set the level carefully and cautiously until the degree of debug you wish to see is present. Scripts and the web interface only have one level so this is an on/off type option for them.", - requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], - type => { db_type=>"integer", hint=>"1|2|3|4|5|6|7|8|9", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "logging", - }, - { - name => "ZM_LOG_DEBUG_FILE", - default => "@ZM_LOGDIR@/zm_debug.log+", - description => "Where extra debug is output to", - help => "This option allows you to specify a different target for debug output. All components have a default log file which will norally be in /tmp or /var/log and this is where debug will be written to if this value is empty. Adding a path here will temporarily redirect debug, and other logging output, to this file. This option is a simple filename and you are debugging several components then they will all try and write to the same file with undesirable consequences. Appending a '+' to the filename will cause the file to be created with a '.' suffix containing your process id. In this way debug from each run of a component is kept separate. This is the recommended setting as it will also prevent subsequent runs from overwriting the same log. You should ensure that permissions are set up to allow writing to the file and directory specified here.", - requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], - type => $types{string}, - category => "logging", - }, - { - name => "ZM_LOG_CHECK_PERIOD", - default => "900", - description => "Time period used when calculating overall system health", - help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to indicate what period of historical events are used in this calculation. This value is expressed in seconds and is ignored if LOG_LEVEL_DATABASE is set to None.", - type => $types{integer}, - category => "logging", - }, - { - name => "ZM_LOG_ALERT_WAR_COUNT", - default => "1", - description => "Number of warnings indicating system alert state", - help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many warnings must have occurred within the defined time period to generate an overall system alert state. A value of zero means warnings are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", - type => $types{integer}, - category => "logging", - }, - { - name => "ZM_LOG_ALERT_ERR_COUNT", - default => "1", - description => "Number of errors indicating system alert state", - help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many errors must have occurred within the defined time period to generate an overall system alert state. A value of zero means errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", - type => $types{integer}, - category => "logging", - }, - { - name => "ZM_LOG_ALERT_FAT_COUNT", - default => "0", - description => "Number of fatal error indicating system alert state", - help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many fatal errors (including panics) must have occurred within the defined time period to generate an overall system alert state. A value of zero means fatal errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", - type => $types{integer}, - category => "logging", - }, - { - name => "ZM_LOG_ALARM_WAR_COUNT", - default => "100", - description => "Number of warnings indicating system alarm state", - help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many warnings must have occurred within the defined time period to generate an overall system alarm state. A value of zero means warnings are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", - type => $types{integer}, - category => "logging", - }, - { - name => "ZM_LOG_ALARM_ERR_COUNT", - default => "10", - description => "Number of errors indicating system alarm state", - help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many errors must have occurred within the defined time period to generate an overall system alarm state. A value of zero means errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", - type => $types{integer}, - category => "logging", - }, - { - name => "ZM_LOG_ALARM_FAT_COUNT", - default => "1", - description => "Number of fatal error indicating system alarm state", - help => "When ZoneMinder is logging events to the database it can retrospectively examine the number of warnings and errors that have occurred to calculate an overall state of system health. This option allows you to specify how many fatal errors (including panics) must have occurred within the defined time period to generate an overall system alarm state. A value of zero means fatal errors are not considered. This value is ignored if LOG_LEVEL_DATABASE is set to None.", - type => $types{integer}, - category => "logging", - }, - { - name => "ZM_RECORD_EVENT_STATS", - default => "yes", - description => "Record event statistical information, switch off if too slow", - help => "This version of ZoneMinder records detailed information about events in the Stats table. This can help in profiling what the optimum settings are for Zones though this is tricky at present. However in future releases this will be done more easily and intuitively, especially with a large sample of events. The default option of 'yes' allows this information to be collected now in readiness for this but if you are concerned about performance you can switch this off in which case no Stats information will be saved.", - type => $types{boolean}, - category => "logging", - }, - { - name => "ZM_RECORD_DIAG_IMAGES", - default => "no", - description => "Record intermediate alarm diagnostic images, can be very slow", - help => "In addition to recording event statistics you can also record the intermediate diagnostic images that display the results of the various checks and processing that occur when trying to determine if an alarm event has taken place. There are several of these images generated for each frame and zone for each alarm or alert frame so this can have a massive impact on performance. Only switch this setting on for debug or analysis purposes and remember to switch it off again once no longer required.", - type => $types{boolean}, - category => "logging", - }, - { - name => "ZM_DUMP_CORES", - default => "no", - description => "Create core files on unexpected process failure.", - help => "When an unrecoverable error occurs in a ZoneMinder binary process is has traditionally been trapped and the details written to logs to aid in remote analysis. However in some cases it is easier to diagnose the error if a core file, which is a memory dump of the process at the time of the error, is created. This can be interactively analysed in the debugger and may reveal more or better information than that available from the logs. This option is recommended for advanced users only otherwise leave at the default. Note using this option to trigger core files will mean that there will be no indication in the binary logs that a process has died, they will just stop, however the zmdc log will still contain an entry. Also note that you may have to explicitly enable core file creation on your system via the 'ulimit -c' command or other means otherwise no file will be created regardless of the value of this option.", - type => $types{boolean}, - category => "logging", - }, - { - name => "ZM_PATH_MAP", - default => "/dev/shm", - description => "Path to the mapped memory files that that ZoneMinder can use", - help => "ZoneMinder has historically used IPC shared memory for shared data between processes. This has it's advantages and limitations. This version of ZoneMinder can use an alternate method, mapped memory, instead with can be enabled with the --enable--mmap directive to configure. This requires less system configuration and is generally more flexible. However it requires each shared data segment to map onto a filesystem file. This option indicates where those mapped files go. You should ensure that this location has sufficient space for these files and for the best performance it should be a tmpfs file system or ramdisk otherwise disk access may render this method slower than the regular shared memory one.", - type => $types{abs_path}, - category => "paths", - }, - { - name => "ZM_PATH_SOCKS", - default => "@ZM_SOCKDIR@", - description => "Path to the various Unix domain socket files that ZoneMinder uses", - help => "ZoneMinder generally uses Unix domain sockets where possible. This reduces the need for port assignments and prevents external applications from possibly compromising the daemons. However each Unix socket requires a .sock file to be created. This option indicates where those socket files go.", - type => $types{abs_path}, - category => "paths", - }, - { - name => "ZM_PATH_LOGS", - default => "@ZM_LOGDIR@", - description => "Path to the various logs that the ZoneMinder daemons generate", - help => "There are various daemons that are used by ZoneMinder to perform various tasks. Most generate helpful log files and this is where they go. They can be deleted if not required for debugging.", - type => $types{abs_path}, - category => "paths", - }, - { - name => "ZM_PATH_SWAP", - default => "@ZM_TMPDIR@", - description => "Path to location for temporary swap images used in streaming", - help => "Buffered playback requires temporary swap images to be stored for each instance of the streaming daemons. This option determines where these images will be stored. The images will actually be stored in sub directories beneath this location and will be automatically cleaned up after a period of time.", - type => $types{abs_path}, - category => "paths", - }, - { - name => "ZM_WEB_TITLE_PREFIX", - default => "ZM", - description => "The title prefix displayed on each window", - help => "If you have more than one installation of ZoneMinder it can be helpful to display different titles for each one. Changing this option allows you to customise the window titles to include further information to aid identification.", - type => $types{string}, - category => "web", - }, - { - name => "ZM_WEB_RESIZE_CONSOLE", - default => "yes", - description => "Should the console window resize itself to fit", - help => "Traditionally the main ZoneMinder web console window has resized itself to shrink to a size small enough to list only the monitors that are actually present. This is intended to make the window more unobtrusize but may not be to everyones tastes, especially if opened in a tab in browsers which support this kind if layout. Switch this option off to have the console window size left to the users preference", - type => $types{boolean}, - category => "web", - }, - { - name => "ZM_WEB_POPUP_ON_ALARM", - default => "yes", - description => "Should the monitor window jump to the top if an alarm occurs", - help => "When viewing a live monitor stream you can specify whether you want the window to pop to the front if an alarm occurs when the window is minimised or behind another window. This is most useful if your monitors are over doors for example when they can pop up if someone comes to the doorway.", - type => $types{boolean}, - category => "web", - }, - { - name => "ZM_OPT_X10", - default => "no", - description => "Support interfacing with X10 devices", - help => "If you have an X10 Home Automation setup in your home you can use ZoneMinder to initiate or react to X10 signals if your computer has the appropriate interface controller. This option indicates whether X10 options will be available in the browser client.", - type => $types{boolean}, - category => "x10", - }, - { - name => "ZM_X10_DEVICE", - default => "/dev/ttyS0", - description => "What device is your X10 controller connected on", - requires => [ { name => "ZM_OPT_X10", value => "yes" } ], - help => "If you have an X10 controller device (e.g. XM10U) connected to your computer this option details which port it is conected on, the default of /dev/ttyS0 maps to serial or com port 1.", - type => $types{abs_path}, - category => "x10", - }, - { - name => "ZM_X10_HOUSE_CODE", - default => "A", - description => "What X10 house code should be used", - requires => [ { name => "ZM_OPT_X10", value => "yes" } ], - help => "X10 devices are grouped together by identifying them as all belonging to one House Code. This option details what that is. It should be a single letter between A and P.", - type => { db_type=>"string", hint=>"A-P", pattern=>qr|^([A-P])|i, format=>q( uc($1) ) }, - category => "x10", - }, - { - name => "ZM_X10_DB_RELOAD_INTERVAL", - default => "60", - description => "How often (in seconds) the X10 daemon reloads the monitors from the database", - requires => [ { name => "ZM_OPT_X10", value => "yes" } ], - help => "The zmx10 daemon periodically checks the database to find out what X10 events trigger, or result from, alarms. This option determines how frequently this check occurs, unless you change this area frequently this can be a fairly large value.", - type => $types{integer}, - category => "x10", - }, - { - name => "ZM_WEB_SOUND_ON_ALARM", - default => "no", - description => "Should the monitor window play a sound if an alarm occurs", - help => "When viewing a live monitor stream you can specify whether you want the window to play a sound to alert you if an alarm occurs.", - type => $types{boolean}, - category => "web", - }, - { - name => "ZM_WEB_ALARM_SOUND", - default => "", - description => "The sound to play on alarm, put this in the sounds directory", - help => "You can specify a sound file to play if an alarm occurs whilst you are watching a live monitor stream. So long as your browser understands the format it does not need to be any particular type. This file should be placed in the sounds directory defined earlier.", - type => $types{file}, - requires => [ { name => "ZM_WEB_SOUND_ON_ALARM", value => "yes" } ], - category => "web", - }, - { - name => "ZM_WEB_COMPACT_MONTAGE", - default => "no", - description => "Compact the montage view by removing extra detail", - help => "The montage view shows the output of all of your active monitors in one window. This include a small menu and status information for each one. This can increase the web traffic and make the window larger than may be desired. Setting this option on removes all this extraneous information and just displays the images.", - type => $types{boolean}, - category => "web", - }, - { - name => "ZM_OPT_FAST_DELETE", - default => "yes", - description => "Delete only event database records for speed", - help => "Normally an event created as the result of an alarm consists of entries in one or more database tables plus the various files associated with it. When deleting events in the browser it can take a long time to remove all of this if your are trying to do a lot of events at once. It is recommended that you set this option which means that the browser client only deletes the key entries in the events table, which means the events will no longer appear in the listing, and leaves the zmaudit daemon to clear up the rest later.", - type => $types{boolean}, - category => "system", - }, - { - name => "ZM_STRICT_VIDEO_CONFIG", - default => "yes", - description => "Allow errors in setting video config to be fatal", - help => "With some video devices errors can be reported in setting the various video attributes when in fact the operation was successful. Switching this option off will still allow these errors to be reported but will not cause them to kill the video capture daemon. Note however that doing this will cause all errors to be ignored including those which are genuine and which may cause the video capture to not function correctly. Use this option with caution.", - type => $types{boolean}, - category => "config", - }, - { - name => "ZM_SIGNAL_CHECK_POINTS", - default => "10", - description => "How many points in a captured image to check for signal loss", - help => "For locally attached video cameras ZoneMinder can check for signal loss by looking at a number of random points on each captured image. If all of these points are set to the same fixed colour then the camera is assumed to have lost signal. When this happens any open events are closed and a short one frame signal loss event is generated, as is another when the signal returns. This option defines how many points on each image to check. Note that this is a maximum, any points found to not have the check colour will abort any further checks so in most cases on a couple of points will actually be checked. Network and file based cameras are never checked.", - type => $types{integer}, - category => "config", - }, - { - name => "ZM_V4L_MULTI_BUFFER", - default => "yes", - description => "Use more than one buffer for Video 4 Linux devices", - help => "Performance when using Video 4 Linux devices is usually best if multiple buffers are used allowing the next image to be captured while the previous one is being processed. If you have multiple devices on a card sharing one input that requires switching then this approach can sometimes cause frames from one source to be mixed up with frames from another. Switching this option off prevents multi buffering resulting in slower but more stable image capture. This option is ignored for non-local cameras or if only one input is present on a capture chip. This option addresses a similar problem to the ZM_CAPTURES_PER_FRAME option and you should normally change the value of only one of the options at a time. If you have different capture cards that need different values you can ovveride them in each individual monitor on the source page.", - type => $types{boolean}, - category => "config", - }, - { - name => "ZM_CAPTURES_PER_FRAME", - default => "1", - description => "How many images are captured per returned frame, for shared local cameras", - help => "If you are using cameras attached to a video capture card which forces multiple inputs to share one capture chip, it can sometimes produce images with interlaced frames reversed resulting in poor image quality and a distinctive comb edge appearance. Increasing this setting allows you to force additional image captures before one is selected as the captured frame. This allows the capture hardware to 'settle down' and produce better quality images at the price of lesser capture rates. This option has no effect on (a) network cameras, or (b) where multiple inputs do not share a capture chip. This option addresses a similar problem to the ZM_V4L_MULTI_BUFFER option and you should normally change the value of only one of the options at a time. If you have different capture cards that need different values you can ovveride them in each individual monitor on the source page.", - type => $types{integer}, - category => "config", - }, - { - name => "ZM_FILTER_RELOAD_DELAY", - default => "300", - description => "How often (in seconds) filters are reloaded in zmfilter", - help => "ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often the filters are reloaded from the database to get the latest versions or new filters. If you don't change filters very often this value can be set to a large value.", - type => $types{integer}, - category => "system", - }, - { - name => "ZM_FILTER_EXECUTE_INTERVAL", - default => "60", - description => "How often (in seconds) to run automatic saved filters", - help => "ZoneMinder allows you to save filters to the database which allow events that match certain criteria to be emailed, deleted or uploaded to a remote machine etc. The zmfilter daemon loads these and does the actual operation. This option determines how often the filters are executed on the saved event in the database. If you want a rapid response to new events this should be a smaller value, however this may increase the overall load on the system and affect performance of other elements.", - type => $types{integer}, - category => "system", - }, - { - name => "ZM_OPT_UPLOAD", - default => "no", - description => "Should ZoneMinder support uploading events from filters", - help => "In ZoneMinder you can create event filters that specify whether events that match certain criteria should be uploaded to a remote server for archiving. This option specifies whether this functionality should be available", - type => $types{boolean}, - category => "upload", - }, - { - name => "ZM_UPLOAD_ARCH_FORMAT", - default => "tar", - description => "What format the uploaded events should be created in.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - help => "Uploaded events may be stored in either .tar or .zip format, this option specifies which. Note that to use this you will need to have the Archive::Tar and/or Archive::Zip perl modules installed.", - type => { db_type=>"string", hint=>"tar|zip", pattern=>qr|^([tz])|i, format=>q( $1 =~ /^t/ ? "tar" : "zip" ) }, - category => "upload", - }, - { - name => "ZM_UPLOAD_ARCH_COMPRESS", - default => "no", - description => "Should archive files be compressed", - help => "When the archive files are created they can be compressed. However in general since the images are compressed already this saves only a minimal amount of space versus utilising more CPU in their creation. Only enable if you have CPU to waste and are limited in disk space on your remote server or bandwidth.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{boolean}, - category => "upload", - }, - { - name => "ZM_UPLOAD_ARCH_ANALYSE", - default => "no", - description => "Include the analysis files in the archive", - help => "When the archive files are created they can contain either just the captured frames or both the captured frames and, for frames that caused an alarm, the analysed image with the changed area highlighted. This option controls files are included. Only include analysed frames if you have a high bandwidth connection to the remote server or if you need help in figuring out what caused an alarm in the first place as archives with these files in can be considerably larger.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{boolean}, - category => "upload", - }, - { - name => "ZM_UPLOAD_PROTOCOL", - default => "ftp", - description => "What protocol to use to upload events", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - help => "ZoneMinder can upload events to a remote server using either FTP or SFTP. Regular FTP is widely supported but not necessarily very secure whereas SFTP (Secure FTP) runs over an ssh connection and so is encrypted and uses regular ssh ports. Note that to use this you will need to have the appropriate perl module, either Net::FTP or Net::SFTP installed depending on your choice.", - type => { db_type=>"string", hint=>"ftp|sftp", pattern=>qr|^([tz])|i, format=>q( $1 =~ /^f/ ? "ftp" : "sftp" ) }, - category => "upload", - }, - { - name => "ZM_UPLOAD_FTP_HOST", - default => "", - description => "The remote server to upload to", - help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the name, or ip address, of the server to use.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{hostname}, - category => "hidden", - }, - { - name => "ZM_UPLOAD_HOST", - default => "", - description => "The remote server to upload events to", - help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the name, or ip address, of the server to use.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{hostname}, - category => "upload", - }, - { - name => "ZM_UPLOAD_PORT", - default => "", - description => "The port on the remote upload server, if not the default (SFTP only)", - help => "You can use filters to instruct ZoneMinder to upload events to a remote server. If you are using the SFTP protocol then this option allows you to specify a particular port to use for connection. If this option is left blank then the default, port 22, is used. This option is ignored for FTP uploads.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{integer}, - category => "upload", - }, - { - name => "ZM_UPLOAD_FTP_USER", - default => "", - description => "Your ftp username", - help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the username that ZoneMinder should use to log in for ftp transfer.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{alphanum}, - category => "hidden", - }, - { - name => "ZM_UPLOAD_USER", - default => "", - description => "Remote server username", - help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the username that ZoneMinder should use to log in for transfer.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{alphanum}, - category => "upload", - }, - { - name => "ZM_UPLOAD_FTP_PASS", - default => "", - description => "Your ftp password", - help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the password that ZoneMinder should use to log in for ftp transfer.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{string}, - category => "hidden", - }, - { - name => "ZM_UPLOAD_PASS", - default => "", - description => "Remote server password", - help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the password that ZoneMinder should use to log in for transfer. If you are using certicate based logins for SFTP servers you can leave this option blank.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{string}, - category => "upload", - }, - { - name => "ZM_UPLOAD_FTP_LOC_DIR", - default => "@ZM_TMPDIR@", - description => "The local directory in which to create upload files", - help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the local directory that ZoneMinder should use for temporary upload files. These are files that are created from events, uploaded and then deleted.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{abs_path}, - category => "hidden", - }, - { - name => "ZM_UPLOAD_LOC_DIR", - default => "@ZM_TMPDIR@", - description => "The local directory in which to create upload files", - help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the local directory that ZoneMinder should use for temporary upload files. These are files that are created from events, uploaded and then deleted.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{abs_path}, - category => "upload", - }, - { - name => "ZM_UPLOAD_FTP_REM_DIR", - default => "", - description => "The remote directory to upload to", - help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the remote directory that ZoneMinder should use to upload event files to.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{rel_path}, - category => "hidden", - }, - { - name => "ZM_UPLOAD_REM_DIR", - default => "", - description => "The remote directory to upload to", - help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the remote directory that ZoneMinder should use to upload event files to.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{rel_path}, - category => "upload", - }, - { - name => "ZM_UPLOAD_FTP_TIMEOUT", - default => "120", - description => "How long to allow the transfer to take for each file", - help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates the maximum ftp inactivity timeout (in seconds) that should be tolerated before ZoneMinder determines that the transfer has failed and closes down the connection.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{integer}, - category => "hidden", - }, - { - name => "ZM_UPLOAD_TIMEOUT", - default => "120", - description => "How long to allow the transfer to take for each file", - help => "You can use filters to instruct ZoneMinder to upload events to a remote server. This option indicates the maximum inactivity timeout (in seconds) that should be tolerated before ZoneMinder determines that the transfer has failed and closes down the connection.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{integer}, - category => "upload", - }, - { - name => "ZM_UPLOAD_FTP_PASSIVE", - default => "yes", - description => "Use passive ftp when uploading", - help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. This option indicates that ftp transfers should be done in passive mode. This uses a single connection for all ftp activity and, whilst slower than active transfers, is more robust and likely to work from behind filewalls. This option is ignored for SFTP transfers.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - help => "If your computer is behind a firewall or proxy you may need to set FTP to passive mode. In fact for simple transfers it makes little sense to do otherwise anyway but you can set this to 'No' if you wish.", - type => $types{boolean}, - category => "upload", - }, - { - name => "ZM_UPLOAD_FTP_DEBUG", - default => "no", - description => "Switch ftp debugging on", - help => "You can use filters to instruct ZoneMinder to upload events to a remote ftp server. If you are having (or expecting) troubles with uploading events then setting this to 'yes' permits additional information to be included in the zmfilter log file.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{boolean}, - category => "hidden", - }, - { - name => "ZM_UPLOAD_DEBUG", - default => "no", - description => "Switch upload debugging on", - help => "You can use filters to instruct ZoneMinder to upload events to a remote server. If you are having (or expecting) troubles with uploading events then setting this to 'yes' permits additional information to be generated by the underlying transfer modules and included in the logs.", - requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], - type => $types{boolean}, - category => "upload", - }, - { - name => "ZM_OPT_EMAIL", - default => "no", - description => "Should ZoneMinder email you details of events that match corresponding filters", - help => "In ZoneMinder you can create event filters that specify whether events that match certain criteria should have their details emailed to you at a designated email address. This will allow you to be notified of events as soon as they occur and also to quickly view the events directly. This option specifies whether this functionality should be available. The email created with this option can be any size and is intended to be sent to a regular email reader rather than a mobile device.", - type => $types{boolean}, - category => "mail", - }, - { - name => "ZM_EMAIL_ADDRESS", - default => "", - description => "The email address to send matching event details to", - requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], - help => "This option is used to define the email address that any events that match the appropriate filters will be sent to.", - type => $types{email}, - category => "mail", - }, - { - name => "ZM_EMAIL_TEXT", - default => 'subject = "ZoneMinder: Alarm - %MN%-%EI% (%ESM% - %ESA% %EFA%)" + name => "ZM_SKIN_DEFAULT", + default => "classic", + description => "Default skin used by web interface", + + help => qqq(" + ZoneMinder allows the use of many different web interfaces. + This option allows you to set the default skin used by the + website. Users can change their skin later, this merely sets + the default. + "), + type => $types{string}, + category => "system", + }, + { + name => "ZM_CSS_DEFAULT", + default => "classic", + description => "Default set of css files used by web interface", + help => qqq(" + ZoneMinder allows the use of many different web interfaces, and + some skins allow the use of different set of CSS files to + control the appearance. This option allows you to set the + default set of css files used by the website. Users can change + their css later, this merely sets the default. + "), + type => $types{string}, + category => "system", + }, + { + name => "ZM_LANG_DEFAULT", + default => "en_gb", + description => "Default language used by web interface", + help => qqq(" + ZoneMinder allows the web interface to use languages other than + English if the appropriate language file has been created and + is present. This option allows you to change the default + language that is used from the shipped language, British + English, to another language + "), + type => $types{string}, + category => "system", + }, + { + name => "ZM_OPT_USE_AUTH", + default => "no", + description => "Authenticate user logins to ZoneMinder", + help => qqq(" + ZoneMinder can run in two modes. The simplest is an entirely + unauthenticated mode where anyone can access ZoneMinder and + perform all tasks. This is most suitable for installations + where the web server access is limited in other ways. The other + mode enables user accounts with varying sets of permissions. + Users must login or authenticate to access ZoneMinder and are + limited by their defined permissions. + "), + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_AUTH_TYPE", + default => "builtin", + description => "What is used to authenticate ZoneMinder users", + help => qqq(" + ZoneMinder can use two methods to authenticate users when + running in authenticated mode. The first is a builtin method + where ZoneMinder provides facilities for users to log in and + maintains track of their identity. The second method allows + interworking with other methods such as http basic + authentication which passes an independently authentication + 'remote' user via http. In this case ZoneMinder would use the + supplied user without additional authentication provided such a + user is configured ion ZoneMinder. + "), + requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" } ], + type => { + db_type =>"string", + hint =>"builtin|remote", + pattern =>qr|^([br])|i, + format =>q( $1 =~ /^b/ ? "builtin" : "remote" ) + }, + category => "system", + }, + { + name => "ZM_AUTH_RELAY", + default => "hashed", + description => "Method used to relay authentication information", + help => qqq(" + When ZoneMinder is running in authenticated mode it can pass + user details between the web pages and the back end processes. + There are two methods for doing this. This first is to use a + time limited hashed string which contains no direct username or + password details, the second method is to pass the username and + passwords around in plaintext. This method is not recommend + except where you do not have the md5 libraries available on + your system or you have a completely isolated system with no + external access. You can also switch off authentication + relaying if your system is isolated in other ways. + "), + requires => [ { name=>"ZM_OPT_USE_AUTH", value=>"yes" } ], + type => { + db_type =>"string", + hint =>"hashed|plain|none", + pattern =>qr|^([hpn])|i, + format =>q( ($1 =~ /^h/) + ? "hashed" + : ($1 =~ /^p/ ? "plain" : "none" ) + ) + }, + category => "system", + }, + { + name => "ZM_AUTH_HASH_SECRET", + default => "...Change me to something unique...", + description => "Secret for encoding hashed authentication information", + help => qqq(" + When ZoneMinder is running in hashed authenticated mode it is + necessary to generate hashed strings containing encrypted + sensitive information such as usernames and password. Although + these string are reasonably secure the addition of a random + secret increases security substantially. + "), + requires => [ + { name=>"ZM_OPT_USE_AUTH", value=>"yes" }, + { name=>"ZM_AUTH_RELAY", value=>"hashed" } + ], + type => $types{string}, + category => "system", + }, + { + name => "ZM_AUTH_HASH_IPS", + default => "yes", + description => "Include IP addresses in the authentication hash", + help => qqq(" + When ZoneMinder is running in hashed authenticated mode it can + optionally include the requesting IP address in the resultant + hash. This adds an extra level of security as only requests + from that address may use that authentication key. However in + some circumstances, such as access over mobile networks, the + requesting address can change for each request which will cause + most requests to fail. This option allows you to control + whether IP addresses are included in the authentication hash on + your system. If you experience intermitent problems with + authentication, switching this option off may help. + "), + requires => [ + { name=>"ZM_OPT_USE_AUTH", value=>"yes" }, + { name=>"ZM_AUTH_RELAY", value=>"hashed" } + ], + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_AUTH_HASH_LOGINS", + default => "no", + description => "Allow login by authentication hash", + help => qqq(" + The normal process for logging into ZoneMinder is via the login + screen with username and password. In some circumstances it may + be desirable to allow access directly to one or more pages, for + instance from a third party application. If this option is + enabled then adding an 'auth' parameter to any request will + include a shortcut login bypassing the login screen, if not + already logged in. As authentication hashes are time and, + optionally, IP limited this can allow short-term access to + ZoneMinder screens from other web pages etc. In order to use + this the calling application will have to generate the + authentication hash itself and ensure it is valid. If you use + this option you should ensure that you have modified the + ZM_AUTH_HASH_SECRET to something unique to your system. + "), + requires => [ + { name=>"ZM_OPT_USE_AUTH", value=>"yes" }, + { name=>"ZM_AUTH_RELAY", value=>"hashed" } + ], + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_OPT_USE_API", + default => "yes", + description => "Enable ZoneMinder APIs", + help => qqq(" + ZoneMinder now features a new API using which 3rd party + applications can interact with ZoneMinder data. It is + STRONGLY recommended that you enable authentication along + with APIs. Note that the APIs return sensitive data like + Monitor access details which are configured as JSON objects. + Which is why we recommend you enabling authentication, especially + if you are exposing your ZM instance on the Internet. + "), + type => $types{boolean}, + category => "system", + }, +# PP - Google reCaptcha settings + { + name => "ZM_OPT_USE_GOOG_RECAPTCHA", + default => "no", + description => "Add Google reCaptcha to login page", + help => qqq(" + This option allows you to include a google + reCaptcha validation at login. This means in addition to providing + a valid usernane and password, you will also have to + pass the reCaptcha test. Please note that enabling this + option results in the zoneminder login page reach out + to google servers for captcha validation. Also please note + that enabling this option will break 3rd party clients + like zmNinja and zmView as they also need to login to ZoneMinder + and they will fail the reCaptcha test. + "), + requires => [ + {name=>"ZM_OPT_USE_AUTH", value=>"yes"} + ], + type => $types {boolean}, + category => "system", + }, + + { + name => "ZM_OPT_GOOG_RECAPTCHA_SITEKEY", + default => "...Insert your recaptcha site-key here...", + description => "Your recaptcha site-key", + help => qqq("You need to generate your keys from + the Google reCaptcha website. + Please refer to https://www.google.com/recaptcha/ + for more details. + "), + requires => [ + {name=>"ZM_OPT_USE_GOOG_RECAPTCHA", value=>"yes"} + ], + type => $types {string}, + category => "system", + }, + { + name => "ZM_OPT_GOOG_RECAPTCHA_SECRETKEY", + default => "...Insert your recaptcha secret-key here...", + description => "Your recaptcha secret-key", + help => qqq("You need to generate your keys from + the Google reCaptcha website. + Please refer to https://www.google.com/recaptcha/ + for more details. + "), + requires => [ + {name=>"ZM_OPT_USE_GOOG_RECAPTCHA", value=>"yes"} + ], + type => $types {string}, + category => "system", + }, + + + { + name => "ZM_DIR_EVENTS", + default => "events", + description => "Directory where events are stored", + help => qqq(" + This is the path to the events directory where all the event + images and other miscellaneous files are stored. CAUTION: The + directory you specify here cannot be outside the web root. This + is a common mistake. Most users should never change this value. + If you intend to record events to a second disk or network + share, then you should mount the drive or share directly to the + ZoneMinder events folder or follow the instructions in the + ZoneMinder Wiki titled Using a dedicated Hard Drive. + "), + type => $types{directory}, + category => "paths", + }, + { + name => "ZM_USE_DEEP_STORAGE", + default => "yes", + description => "Use a deep filesystem hierarchy for events", + help => qqq(" + This option is now the default for new ZoneMinder systems and + should not be changed. Previous versions of ZoneMinder stored + all events for a monitor under one folder. Enabling + USE_DEEP_STORAGE causes ZoneMinder to store events under a + folder structure that follows year/month/day/hour/min/second. + Storing events this way avoids the limitation of storing more + than 32k files in a single folder inherent in some filesystems. + It is important to note that you cannot simply change this + option. You must stop zoneminder, enable USE_DEEP_STORAGE, and + then run \"sudo zmupdate.pl --migrate-events\". FAILURE TO DO + SO WILL RESULT IN LOSS OF YOUR DATA! Consult the ZoneMinder + WiKi for further details. + "), + type => $types{boolean}, + category => "hidden", + }, + { + name => "ZM_DIR_IMAGES", + default => "images", + description => "Directory where the images that the ZoneMinder client generates are stored", + help => qqq(" + ZoneMinder generates a myriad of images, mostly of which are + associated with events. For those that aren't this is where + they go. CAUTION: The directory you specify here cannot be + outside the web root. This is a common mistake. Most users + should never change this value. If you intend to save images to + a second disk or network share, then you should mount the drive + or share directly to the ZoneMinder images folder or follow the + instructions in the ZoneMinder Wiki titled Using a dedicated + Hard Drive. + "), + type => $types{directory}, + category => "paths", + }, + { + name => "ZM_DIR_SOUNDS", + default => "sounds", + description => "Directory to the sounds that the ZoneMinder client can use", + help => qqq(" + ZoneMinder can optionally play a sound file when an alarm is + detected. This indicates where to look for this file. CAUTION: + The directory you specify here cannot be outside the web root. + Most users should never change this value. + "), + type => $types{directory}, + category => "paths", + }, + { + name => "ZM_PATH_ZMS", + default => "/cgi-bin/nph-zms", + description => "Web path to zms streaming server", + help => qqq(" + The ZoneMinder streaming server is required to send streamed + images to your browser. It will be installed into the cgi-bin + path given at configuration time. This option determines what + the web path to the server is rather than the local path on + your machine. Ordinarily the streaming server runs in + parser-header mode however if you experience problems with + streaming you can change this to non-parsed-header (nph) mode + by changing 'zms' to 'nph-zms'. + "), + type => $types{rel_path}, + category => "paths", + }, + { + name => "ZM_COLOUR_JPEG_FILES", + default => "no", + description => "Colourise greyscale JPEG files", + help => qqq(" + Cameras that capture in greyscale can write their captured + images to jpeg files with a corresponding greyscale colour + space. This saves a small amount of disk space over colour + ones. However some tools such as ffmpeg either fail to work + with this colour space or have to convert it beforehand. + Setting this option to yes uses up a little more space but + makes creation of MPEG files much faster. + "), + type => $types{boolean}, + category => "images", + }, + { + name => "ZM_ADD_JPEG_COMMENTS", + default => "no", + description => "Add jpeg timestamp annotations as file header comments", + help => qqq(" + JPEG files may have a number of extra fields added to the file + header. The comment field may have any kind of text added. This + options allows you to have the same text that is used to + annotate the image additionally included as a file header + comment. If you archive event images to other locations this + may help you locate images for particular events or times if + you use software that can read comment headers. + "), + type => $types{boolean}, + category => "images", + }, + { + name => "ZM_JPEG_FILE_QUALITY", + default => "70", + description => "Set the JPEG quality setting for the saved event files (1-100)", + help => qqq(" + When ZoneMinder detects an event it will save the images + associated with that event to files. These files are in the + JPEG format and can be viewed or streamed later. This option + specifies what image quality should be used to save these + files. A higher number means better quality but less + compression so will take up more disk space and take longer to + view over a slow connection. By contrast a low number means + smaller, quicker to view, files but at the price of lower + quality images. This setting applies to all images written + except if the capture image has caused an alarm and the alarm + file quality option is set at a higher value when that is used + instead. + "), + type => $types{integer}, + category => "images", + }, + { + name => "ZM_JPEG_ALARM_FILE_QUALITY", + default => "0", + description => "Set the JPEG quality setting for the saved event files during an alarm (1-100)", + help => qqq(" + This value is equivalent to the regular jpeg file quality + setting above except that it only applies to images saved while + in an alarm state and then only if this value is set to a + higher quality setting than the ordinary file setting. If set + to a lower value then it is ignored. Thus leaving it at the + default of 0 effectively means to use the regular file quality + setting for all saved images. This is to prevent acccidentally + saving important images at a worse quality setting. + "), + type => $types{integer}, + category => "images", + }, + # Deprecated, now stream quality + { + name => "ZM_JPEG_IMAGE_QUALITY", + default => "70", + description => "Set the JPEG quality setting for the streamed 'live' images (1-100)", + help => qqq(" + When viewing a 'live' stream for a monitor ZoneMinder will grab + an image from the buffer and encode it into JPEG format before + sending it. This option specifies what image quality should be + used to encode these images. A higher number means better + quality but less compression so will take longer to view over a + slow connection. By contrast a low number means quicker to view + images but at the price of lower quality images. This option + does not apply when viewing events or still images as these are + usually just read from disk and so will be encoded at the + quality specified by the previous options. + "), + type => $types{integer}, + category => "hidden", + }, + { + name => "ZM_JPEG_STREAM_QUALITY", + default => "70", + description => "Set the JPEG quality setting for the streamed 'live' images (1-100)", + help => qqq(" + When viewing a 'live' stream for a monitor ZoneMinder will grab + an image from the buffer and encode it into JPEG format before + sending it. This option specifies what image quality should be + used to encode these images. A higher number means better + quality but less compression so will take longer to view over a + slow connection. By contrast a low number means quicker to view + images but at the price of lower quality images. This option + does not apply when viewing events or still images as these are + usually just read from disk and so will be encoded at the + quality specified by the previous options. + "), + type => $types{integer}, + category => "images", + }, + { + name => "ZM_MPEG_TIMED_FRAMES", + default => "yes", + description => "Tag video frames with a timestamp for more realistic streaming", + help => qqq(" + When using streamed MPEG based video, either for live monitor + streams or events, ZoneMinder can send the streams in two ways. + If this option is selected then the timestamp for each frame, + taken from it's capture time, is included in the stream. This + means that where the frame rate varies, for instance around an + alarm, the stream will still maintain it's 'real' timing. If + this option is not selected then an approximate frame rate is + calculated and that is used to schedule frames instead. This + option should be selected unless you encounter problems with + your preferred streaming method. + "), + type => $types{boolean}, + category => "images", + }, + { + name => "ZM_MPEG_LIVE_FORMAT", + default => "swf", + description => "What format 'live' video streams are played in", + help => qqq(" + When using MPEG mode ZoneMinder can output live video. However + what formats are handled by the browser varies greatly between + machines. This option allows you to specify a video format + using a file extension format, so you would just enter the + extension of the file type you would like and the rest is + determined from that. The default of 'asf' works well under + Windows with Windows Media Player but I'm currently not sure + what, if anything, works on a Linux platform. If you find out + please let me know! If this option is left blank then live + streams will revert to being in motion jpeg format + "), + type => $types{string}, + category => "images", + }, + { + name => "ZM_MPEG_REPLAY_FORMAT", + default => "swf", + description => "What format 'replay' video streams are played in", + help => qqq(" + When using MPEG mode ZoneMinder can replay events in encoded + video format. However what formats are handled by the browser + varies greatly between machines. This option allows you to + specify a video format using a file extension format, so you + would just enter the extension of the file type you would like + and the rest is determined from that. The default of 'asf' + works well under Windows with Windows Media Player and 'mpg', + or 'avi' etc should work under Linux. If you know any more then + please let me know! If this option is left blank then live + streams will revert to being in motion jpeg format + "), + type => $types{string}, + category => "images", + }, + { + name => "ZM_RAND_STREAM", + default => "yes", + description => "Add a random string to prevent caching of streams", + help => qqq(" + Some browsers can cache the streams used by ZoneMinder. In + order to prevent his a harmless random string can be appended + to the url to make each invocation of the stream appear unique. + "), + type => $types{boolean}, + category => "images", + }, + { + name => "ZM_OPT_CAMBOZOLA", + default => "no", + description => "Is the (optional) cambozola java streaming client installed", + help => qqq(" + Cambozola is a handy low fat cheese flavoured Java applet that + ZoneMinder uses to view image streams on browsers such as + Internet Explorer that don't natively support this format. If + you use this browser it is highly recommended to install this + from http://www.charliemouse.com/code/cambozola/ however if it + is not installed still images at a lower refresh rate can still + be viewed. + "), + type => $types{boolean}, + category => "images", + }, + { + name => "ZM_PATH_CAMBOZOLA", + default => "cambozola.jar", + description => "Web path to (optional) cambozola java streaming client", + help => qqq(" + Cambozola is a handy low fat cheese flavoured Java applet that + ZoneMinder uses to view image streams on browsers such as + Internet Explorer that don't natively support this format. If + you use this browser it is highly recommended to install this + from http://www.charliemouse.com/code/cambozola/ however if it + is not installed still images at a lower refresh rate can still + be viewed. Leave this as 'cambozola.jar' if cambozola is + installed in the same directory as the ZoneMinder web client + files. + "), + requires => [ { name=>"ZM_OPT_CAMBOZOLA", value=>"yes" } ], + type => $types{rel_path}, + category => "images", + }, + { + name => "ZM_RELOAD_CAMBOZOLA", + default => "0", + description => "After how many seconds should Cambozola be reloaded in live view", + help => qqq(" + Cambozola allows for the viewing of streaming MJPEG however it + caches the entire stream into cache space on the computer, + setting this to a number > 0 will cause it to automatically + reload after that many seconds to avoid filling up a hard + drive. + "), + type => $types{integer}, + category => "images", + }, + { + name => "ZM_TIMESTAMP_ON_CAPTURE", + default => "yes", + description => "Timestamp images as soon as they are captured", + help => qqq(" + ZoneMinder can add a timestamp to images in two ways. The + default method, when this option is set, is that each image is + timestamped immediately when captured and so the image held in + memory is marked right away. The second method does not + timestamp the images until they are either saved as part of an + event or accessed over the web. The timestamp used in both + methods will contain the same time as this is preserved along + with the image. The first method ensures that an image is + timestamped regardless of any other circumstances but will + result in all images being timestamped even those never saved + or viewed. The second method necessitates that saved images are + copied before being saved otherwise two timestamps perhaps at + different scales may be applied. This has the (perhaps) + desirable side effect that the timestamp is always applied at + the same resolution so an image that has scaling applied will + still have a legible and correctly scaled timestamp. + "), + type => $types{boolean}, + category => "config", + }, + { + name => "ZM_CPU_EXTENSIONS", + default => "yes", + description => "Use advanced CPU extensions to increase performance", + help => qqq(" + When advanced processor extensions such as SSE2 or SSSE3 are + available, ZoneMinder can use them, which should increase + performance and reduce system load. Enabling this option on + processors that do not support the advanced processors + extensions used by ZoneMinder is harmless and will have no + effect. + "), + type => $types{boolean}, + category => "config", + }, + { + name => "ZM_FAST_IMAGE_BLENDS", + default => "yes", + description => "Use a fast algorithm to blend the reference image", + help => qqq(" + To detect alarms ZoneMinder needs to blend the captured image + with the stored reference image to update it for comparison + with the next image. The reference blend percentage specified + for the monitor controls how much the new image affects the + reference image. There are two methods that are available for + this. If this option is set then fast calculation which does + not use any multiplication or division is used. This + calculation is extremely fast, however it limits the possible + blend percentages to 50%, 25%, 12.5%, 6.25%, 3.25% and 1.5%. + Any other blend percentage will be rounded to the nearest + possible one. The alternative is to switch this option off + and use standard blending instead, which is slower. + "), + type => $types{boolean}, + category => "config", + }, + { + name => "ZM_OPT_ADAPTIVE_SKIP", + default => "yes", + description => "Should frame analysis try and be efficient in skipping frames", + help => qqq(" + In previous versions of ZoneMinder the analysis daemon would + attempt to keep up with the capture daemon by processing the + last captured frame on each pass. This would sometimes have the + undesirable side-effect of missing a chunk of the initial + activity that caused the alarm because the pre-alarm frames + would all have to be written to disk and the database before + processing the next frame, leading to some delay between the + first and second event frames. Setting this option enables a + newer adaptive algorithm where the analysis daemon attempts to + process as many captured frames as possible, only skipping + frames when in danger of the capture daemon overwriting yet to + be processed frames. This skip is variable depending on the + size of the ring buffer and the amount of space left in it. + Enabling this option will give you much better coverage of the + beginning of alarms whilst biasing out any skipped frames + towards the middle or end of the event. However you should be + aware that this will have the effect of making the analysis + daemon run somewhat behind the capture daemon during events and + for particularly fast rates of capture it is possible for the + adaptive algorithm to be overwhelmed and not have time to react + to a rapid build up of pending frames and thus for a buffer + overrun condition to occur. + "), + type => $types{boolean}, + category => "config", + }, + { + name => "ZM_MAX_SUSPEND_TIME", + default => "30", + description => "Maximum time that a monitor may have motion detection suspended", + help => qqq(" + ZoneMinder allows monitors to have motion detection to be + suspended, for instance while panning a camera. Ordinarily this + relies on the operator resuming motion detection afterwards as + failure to do so can leave a monitor in a permanently suspended + state. This setting allows you to set a maximum time which a + camera may be suspended for before it automatically resumes + motion detection. This time can be extended by subsequent + suspend indications after the first so continuous camera + movement will also occur while the monitor is suspended. + "), + type => $types{integer}, + category => "config", + }, + # Deprecated, really no longer necessary + { + name => "ZM_OPT_REMOTE_CAMERAS", + default => "no", + description => "Are you going to use remote/networked cameras", + help => qqq(" + ZoneMinder can work with both local cameras, ie. those attached + physically to your computer and remote or network cameras. If + you will be using networked cameras select this option. + "), + type => $types{boolean}, + category => "hidden", + }, + # Deprecated, now set on a per monitor basis using the Method field + { + name => "ZM_NETCAM_REGEXPS", + default => "yes", + description => "Use regular expression matching with network cameras", + help => qqq(" + Traditionally ZoneMinder has used complex regular regular + expressions to handle the multitude of formats that network + cameras produce. In versions from 1.21.1 the default is to use + a simpler and faster built in pattern matching methodology. + This works well with most networks cameras but if you have + problems you can try the older, but more flexible, regular + expression based method by selecting this option. Note, to use + this method you must have libpcre installed on your system. + "), + requires => [ { name => "ZM_OPT_REMOTE_CAMERAS", value => "yes" } ], + type => $types{boolean}, + category => "hidden", + }, + { + name => "ZM_HTTP_VERSION", + default => "1.0", + description => "The version of HTTP that ZoneMinder will use to connect", + help => qqq(" + ZoneMinder can communicate with network cameras using either of + the HTTP/1.1 or HTTP/1.0 standard. A server will normally fall + back to the version it supports with no problem so this should + usually by left at the default. However it can be changed to + HTTP/1.0 if necessary to resolve particular issues. + "), + type => { + db_type =>"string", + hint =>"1.1|1.0", + pattern =>qr|^(1\.[01])$|, + format =>q( $1?$1:"" ) + }, + category => "network", + }, + { + name => "ZM_HTTP_UA", + default => "ZoneMinder", + description => "The user agent that ZoneMinder uses to identify itself", + help => qqq(" + When ZoneMinder communicates with remote cameras it will + identify itself using this string and it's version number. This + is normally sufficient, however if a particular cameras expects + only to communicate with certain browsers then this can be + changed to a different string identifying ZoneMinder as + Internet Explorer or Netscape etc. + "), + type => $types{string}, + category => "network", + }, + { + name => "ZM_HTTP_TIMEOUT", + default => "2500", + description => "How long ZoneMinder waits before giving up on images (milliseconds)", + help => qqq(" + When retrieving remote images ZoneMinder will wait for this + length of time before deciding that an image is not going to + arrive and taking steps to retry. This timeout is in + milliseconds (1000 per second) and will apply to each part of + an image if it is not sent in one whole chunk. + "), + type => $types{integer}, + category => "network", + }, + { + name => "ZM_MIN_RTP_PORT", + default => "40200", + description => "Minimum port that ZoneMinder will listen for RTP traffic on", + help => qqq(" + When ZoneMinder communicates with MPEG4 capable cameras using + RTP with the unicast method it must open ports for the camera + to connect back to for control and streaming purposes. This + setting specifies the minimum port number that ZoneMinder will + use. Ordinarily two adjacent ports are used for each camera, + one for control packets and one for data packets. This port + should be set to an even number, you may also need to open up a + hole in your firewall to allow cameras to connect back if you + wish to use unicasting. + "), + type => $types{integer}, + category => "network", + }, + { + name => "ZM_MAX_RTP_PORT", + default => "40499", + description => "Maximum port that ZoneMinder will listen for RTP traffic on", + help => qqq(" + When ZoneMinder communicates with MPEG4 capable cameras using + RTP with the unicast method it must open ports for the camera + to connect back to for control and streaming purposes. This + setting specifies the maximum port number that ZoneMinder will + use. Ordinarily two adjacent ports are used for each camera, + one for control packets and one for data packets. This port + should be set to an even number, you may also need to open up a + hole in your firewall to allow cameras to connect back if you + wish to use unicasting. You should also ensure that you have + opened up at least two ports for each monitor that will be + connecting to unicasting network cameras. + "), + type => $types{integer}, + category => "network", + }, + { + name => "ZM_OPT_FFMPEG", + default => "@OPT_FFMPEG@", + description => "Is the ffmpeg video encoder/decoder installed", + help => qqq(" + ZoneMinder can optionally encode a series of video images into + an MPEG encoded movie file for viewing, downloading or storage. + This option allows you to specify whether you have the ffmpeg + tools installed. Note that creating MPEG files can be fairly + CPU and disk intensive and is not a required option as events + can still be reviewed as video streams without it. + "), + type => $types{boolean}, + category => "images", + }, + { + name => "ZM_PATH_FFMPEG", + default => "@PATH_FFMPEG@", + description => "Path to (optional) ffmpeg mpeg encoder", + help => "This path should point to where ffmpeg has been installed.", + requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], + type => $types{abs_path}, + category => "images", + }, + { + name => "ZM_FFMPEG_INPUT_OPTIONS", + default => "", + description => "Additional input options to ffmpeg", + help => qqq(" + Ffmpeg can take many options on the command line to control the + quality of video produced. This option allows you to specify + your own set that apply to the input to ffmpeg (options that + are given before the -i option). Check the ffmpeg documentation + for a full list of options which may be used here. + "), + requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], + type => $types{string}, + category => "images", + }, + { + name => "ZM_FFMPEG_OUTPUT_OPTIONS", + default => "-r 25", + description => "Additional output options to ffmpeg", + help => qqq(" + Ffmpeg can take many options on the command line to control the + quality of video produced. This option allows you to specify + your own set that apply to the output from ffmpeg (options that + are given after the -i option). Check the ffmpeg documentation + for a full list of options which may be used here. The most + common one will often be to force an output frame rate + supported by the video encoder. + "), + requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], + type => $types{string}, + category => "images", + }, + { + name => "ZM_FFMPEG_FORMATS", + default => "mpg mpeg wmv asf avi* mov swf 3gp**", + description => "Formats to allow for ffmpeg video generation", + help => qqq(" + Ffmpeg can generate video in many different formats. This + option allows you to list the ones you want to be able to + select. As new formats are supported by ffmpeg you can add them + here and be able to use them immediately. Adding a '*' after a + format indicates that this will be the default format used for + web video, adding '**' defines the default format for phone + video. + "), + requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], + type => $types{string}, + category => "images", + }, + { + name => "ZM_FFMPEG_OPEN_TIMEOUT", + default => "10", + description => "Timeout in seconds when opening a stream.", + help => qqq(" + When Ffmpeg is opening a stream, it can take a long time before + failing; certain circumstances even seem to be able to lock + indefinitely. This option allows you to set a maximum time in + seconds to pass before closing the stream and trying to reopen + it again. + "), + requires => [ { name=>"ZM_OPT_FFMPEG", value=>"yes" } ], + type => $types{integer}, + category => "images", + }, + { + name => "ZM_LOG_LEVEL_SYSLOG", + default => "0", + description => "Save logging output to the system log", + help => qqq(" + ZoneMinder logging is now more more integrated between + components and allows you to specify the destination for + logging output and the individual levels for each. This option + lets you control the level of logging output that goes to the + system log. ZoneMinder binaries have always logged to the + system log but now scripts and web logging is also included. To + preserve the previous behaviour you should ensure this value is + set to Info or Warning. This option controls the maximum level + of logging that will be written, so Info includes Warnings and + Errors etc. To disable entirely, set this option to None. You + should use caution when setting this option to Debug as it can + affect severely affect system performance. If you want debug + you will also need to set a level and component below + "), + type => { + db_type =>"integer", + hint =>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "logging", + }, + { + name => "ZM_LOG_LEVEL_FILE", + default => "-5", + description => "Save logging output to component files", + help => qqq(" + ZoneMinder logging is now more more integrated between + components and allows you to specify the destination for + logging output and the individual levels for each. This option + lets you control the level of logging output that goes to + individual log files written by specific components. This is + how logging worked previously and although useful for tracking + down issues in specific components it also resulted in many + disparate log files. To preserve this behaviour you should + ensure this value is set to Info or Warning. This option + controls the maximum level of logging that will be written, so + Info includes Warnings and Errors etc. To disable entirely, set + this option to None. You should use caution when setting this + option to Debug as it can affect severely affect system + performance though file output has less impact than the other + options. If you want debug you will also need to set a level + and component below + "), + type => { + db_type =>"integer", + hint =>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "logging", + }, + { + name => "ZM_LOG_LEVEL_WEBLOG", + default => "-5", + description => "Save logging output to the weblog", + help => qqq(" + ZoneMinder logging is now more more integrated between + components and allows you to specify the destination for + logging output and the individual levels for each. This option + lets you control the level of logging output from the web + interface that goes to the httpd error log. Note that only web + logging from PHP and JavaScript files is included and so this + option is really only useful for investigating specific issues + with those components. This option controls the maximum level + of logging that will be written, so Info includes Warnings and + Errors etc. To disable entirely, set this option to None. You + should use caution when setting this option to Debug as it can + affect severely affect system performance. If you want debug + you will also need to set a level and component below + "), + type => { + db_type =>"integer", + hint =>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "logging", + }, + { + name => "ZM_LOG_LEVEL_DATABASE", + default => "0", + description => "Save logging output to the database", + help => qqq(" + ZoneMinder logging is now more more integrated between + components and allows you to specify the destination for + logging output and the individual levels for each. This option + lets you control the level of logging output that is written to + the database. This is a new option which can make viewing + logging output easier and more intuitive and also makes it + easier to get an overall impression of how the system is + performing. If you have a large or very busy system then it is + possible that use of this option may slow your system down if + the table becomes very large. Ensure you use the + LOG_DATABASE_LIMIT option to keep the table to a manageable + size. This option controls the maximum level of logging that + will be written, so Info includes Warnings and Errors etc. To + disable entirely, set this option to None. You should use + caution when setting this option to Debug as it can affect + severely affect system performance. If you want debug you will + also need to set a level and component below + "), + type => { + db_type =>"integer", + hint =>"None=-5|Panic=-4|Fatal=-3|Error=-2|Warning=-1|Info=0|Debug=1", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "logging", + }, + { + name => "ZM_LOG_DATABASE_LIMIT", + default => "7 day", + description => "Maximum number of log entries to retain", + help => qqq(" + If you are using database logging then it is possible to + quickly build up a large number of entries in the Logs table. + This option allows you to specify how many of these entries are + kept. If you set this option to a number greater than zero then + that number is used to determine the maximum number of rows, + less than or equal to zero indicates no limit and is not + recommended. You can also set this value to time values such as + ' day' which will limit the log entries to those newer than + that time. You can specify 'hour', 'day', 'week', 'month' and + 'year', note that the values should be singular (no 's' at the + end). The Logs table is pruned periodically so it is possible + for more than the expected number of rows to be present briefly + in the meantime. + "), + type => $types{string}, + category => "logging", + }, + { + name => "ZM_LOG_DEBUG", + default => "no", + description => "Switch debugging on", + help => qqq(" + ZoneMinder components usually support debug logging available + to help with diagnosing problems. Binary components have + several levels of debug whereas more other components have only + one. Normally this is disabled to minimise performance + penalties and avoid filling logs too quickly. This option lets + you switch on other options that allow you to configure + additional debug information to be output. Components will pick + up this instruction when they are restarted. + "), + type => $types{boolean}, + category => "logging", + }, + { + name => "ZM_LOG_DEBUG_TARGET", + default => "", + description => "What components should have extra debug enabled", + help => qqq(" + There are three scopes of debug available. Leaving this option + blank means that all components will use extra debug (not + recommended). Setting this option to '_', e.g. _zmc, + will limit extra debug to that component only. Setting this + option to '__', e.g. '_zmc_m1' will limit + extra debug to that instance of the component only. This is + ordinarily what you probably want to do. To debug scripts use + their names without the .pl extension, e.g. '_zmvideo' and to + debug issues with the web interface use '_web'. You can specify + multiple targets by separating them with '|' characters. + "), + requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], + type => $types{string}, + category => "logging", + }, + { + name => "ZM_LOG_DEBUG_LEVEL", + default => 1, + description => "What level of extra debug should be enabled", + help => qqq(" + There are 9 levels of debug available, with higher numbers + being more debug and level 0 being no debug. However not all + levels are used by all components. Also if there is debug at a + high level it is usually likely to be output at such a volume + that it may obstruct normal operation. For this reason you + should set the level carefully and cautiously until the degree + of debug you wish to see is present. Scripts and the web + interface only have one level so this is an on/off type option + for them. + "), + requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], + type => { + db_type =>"integer", + hint =>"1|2|3|4|5|6|7|8|9", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "logging", + }, + { + name => "ZM_LOG_DEBUG_FILE", + default => "@ZM_LOGDIR@/zm_debug.log+", + description => "Where extra debug is output to", + help => qqq(" + This option allows you to specify a different target for debug + output. All components have a default log file which will + norally be in /tmp or /var/log and this is where debug will be + written to if this value is empty. Adding a path here will + temporarily redirect debug, and other logging output, to this + file. This option is a simple filename and you are debugging + several components then they will all try and write to the same + file with undesirable consequences. Appending a '+' to the + filename will cause the file to be created with a '.' + suffix containing your process id. In this way debug from each + run of a component is kept separate. This is the recommended + setting as it will also prevent subsequent runs from + overwriting the same log. You should ensure that permissions + are set up to allow writing to the file and directory specified + here. + "), + requires => [ { name => "ZM_LOG_DEBUG", value => "yes" } ], + type => $types{string}, + category => "logging", + }, + { + name => "ZM_LOG_CHECK_PERIOD", + default => "900", + description => "Time period used when calculating overall system health", + help => qqq(" + When ZoneMinder is logging events to the database it can + retrospectively examine the number of warnings and errors that + have occurred to calculate an overall state of system health. + This option allows you to indicate what period of historical + events are used in this calculation. This value is expressed in + seconds and is ignored if LOG_LEVEL_DATABASE is set to None. + "), + type => $types{integer}, + category => "logging", + }, + { + name => "ZM_LOG_ALERT_WAR_COUNT", + default => "1", + description => "Number of warnings indicating system alert state", + help => qqq(" + When ZoneMinder is logging events to the database it can + retrospectively examine the number of warnings and errors that + have occurred to calculate an overall state of system health. + This option allows you to specify how many warnings must have + occurred within the defined time period to generate an overall + system alert state. A value of zero means warnings are not + considered. This value is ignored if LOG_LEVEL_DATABASE is set + to None. + "), + type => $types{integer}, + category => "logging", + }, + { + name => "ZM_LOG_ALERT_ERR_COUNT", + default => "1", + description => "Number of errors indicating system alert state", + help => qqq(" + When ZoneMinder is logging events to the database it can + retrospectively examine the number of warnings and errors that + have occurred to calculate an overall state of system health. + This option allows you to specify how many errors must have + occurred within the defined time period to generate an overall + system alert state. A value of zero means errors are not + considered. This value is ignored if LOG_LEVEL_DATABASE is set + to None. + "), + type => $types{integer}, + category => "logging", + }, + { + name => "ZM_LOG_ALERT_FAT_COUNT", + default => "0", + description => "Number of fatal error indicating system alert state", + help => qqq(" + When ZoneMinder is logging events to the database it can + retrospectively examine the number of warnings and errors that + have occurred to calculate an overall state of system health. + This option allows you to specify how many fatal errors + (including panics) must have occurred within the defined time + period to generate an overall system alert state. A value of + zero means fatal errors are not considered. This value is + ignored if LOG_LEVEL_DATABASE is set to None. + "), + type => $types{integer}, + category => "logging", + }, + { + name => "ZM_LOG_ALARM_WAR_COUNT", + default => "100", + description => "Number of warnings indicating system alarm state", + help => qqq(" + When ZoneMinder is logging events to the database it can + retrospectively examine the number of warnings and errors that + have occurred to calculate an overall state of system health. + This option allows you to specify how many warnings must have + occurred within the defined time period to generate an overall + system alarm state. A value of zero means warnings are not + considered. This value is ignored if LOG_LEVEL_DATABASE is set + to None. + "), + type => $types{integer}, + category => "logging", + }, + { + name => "ZM_LOG_ALARM_ERR_COUNT", + default => "10", + description => "Number of errors indicating system alarm state", + help => qqq(" + When ZoneMinder is logging events to the database it can + retrospectively examine the number of warnings and errors that + have occurred to calculate an overall state of system health. + This option allows you to specify how many errors must have + occurred within the defined time period to generate an overall + system alarm state. A value of zero means errors are not + considered. This value is ignored if LOG_LEVEL_DATABASE is set + to None. + "), + type => $types{integer}, + category => "logging", + }, + { + name => "ZM_LOG_ALARM_FAT_COUNT", + default => "1", + description => "Number of fatal error indicating system alarm state", + help => qqq(" + When ZoneMinder is logging events to the database it can + retrospectively examine the number of warnings and errors that + have occurred to calculate an overall state of system health. + This option allows you to specify how many fatal errors + (including panics) must have occurred within the defined time + period to generate an overall system alarm state. A value of + zero means fatal errors are not considered. This value is + ignored if LOG_LEVEL_DATABASE is set to None. + "), + type => $types{integer}, + category => "logging", + }, + { + name => "ZM_RECORD_EVENT_STATS", + default => "yes", + description => "Record event statistical information, switch off if too slow", + help => qqq(" + This version of ZoneMinder records detailed information about + events in the Stats table. This can help in profiling what the + optimum settings are for Zones though this is tricky at + present. However in future releases this will be done more + easily and intuitively, especially with a large sample of + events. The default option of 'yes' allows this information to + be collected now in readiness for this but if you are concerned + about performance you can switch this off in which case no + Stats information will be saved. + "), + type => $types{boolean}, + category => "logging", + }, + { + name => "ZM_RECORD_DIAG_IMAGES", + default => "no", + description => "Record intermediate alarm diagnostic images, can be very slow", + help => qqq(" + In addition to recording event statistics you can also record + the intermediate diagnostic images that display the results of + the various checks and processing that occur when trying to + determine if an alarm event has taken place. There are several + of these images generated for each frame and zone for each + alarm or alert frame so this can have a massive impact on + performance. Only switch this setting on for debug or analysis + purposes and remember to switch it off again once no longer + required. + "), + type => $types{boolean}, + category => "logging", + }, + { + name => "ZM_DUMP_CORES", + default => "no", + description => "Create core files on unexpected process failure.", + help => qqq(" + When an unrecoverable error occurs in a ZoneMinder binary + process is has traditionally been trapped and the details + written to logs to aid in remote analysis. However in some + cases it is easier to diagnose the error if a core file, which + is a memory dump of the process at the time of the error, is + created. This can be interactively analysed in the debugger and + may reveal more or better information than that available from + the logs. This option is recommended for advanced users only + otherwise leave at the default. Note using this option to + trigger core files will mean that there will be no indication + in the binary logs that a process has died, they will just + stop, however the zmdc log will still contain an entry. Also + note that you may have to explicitly enable core file creation + on your system via the 'ulimit -c' command or other means + otherwise no file will be created regardless of the value of + this option. + "), + type => $types{boolean}, + category => "logging", + }, + { + name => "ZM_PATH_MAP", + default => "/dev/shm", + description => "Path to the mapped memory files that that ZoneMinder can use", + help => qqq(" + ZoneMinder has historically used IPC shared memory for shared + data between processes. This has it's advantages and + limitations. This version of ZoneMinder can use an alternate + method, mapped memory, instead with can be enabled with the + --enable--mmap directive to configure. This requires less + system configuration and is generally more flexible. However it + requires each shared data segment to map onto a filesystem + file. This option indicates where those mapped files go. You + should ensure that this location has sufficient space for these + files and for the best performance it should be a tmpfs file + system or ramdisk otherwise disk access may render this method + slower than the regular shared memory one. + "), + type => $types{abs_path}, + category => "paths", + }, + { + name => "ZM_PATH_SOCKS", + default => "@ZM_SOCKDIR@", + description => "Path to the various Unix domain socket files that ZoneMinder uses", + help => qqq(" + ZoneMinder generally uses Unix domain sockets where possible. + This reduces the need for port assignments and prevents + external applications from possibly compromising the daemons. + However each Unix socket requires a .sock file to be created. + This option indicates where those socket files go. + "), + type => $types{abs_path}, + category => "paths", + }, + { + name => "ZM_PATH_LOGS", + default => "@ZM_LOGDIR@", + description => "Path to the various logs that the ZoneMinder daemons generate", + help => qqq(" + There are various daemons that are used by ZoneMinder to + perform various tasks. Most generate helpful log files and this + is where they go. They can be deleted if not required for + debugging. + "), + type => $types{abs_path}, + category => "paths", + }, + { + name => "ZM_PATH_SWAP", + default => "@ZM_TMPDIR@", + description => "Path to location for temporary swap images used in streaming", + help => qqq(" + Buffered playback requires temporary swap images to be stored + for each instance of the streaming daemons. This option + determines where these images will be stored. The images will + actually be stored in sub directories beneath this location and + will be automatically cleaned up after a period of time. + "), + type => $types{abs_path}, + category => "paths", + }, + { + name => "ZM_PATH_ARP", + default => "", + description => "Path to a supported ARP tool", + help => qqq(" + The camera probe function uses Address Resolution Protocol in + order to find known devices on the network. Optionally supply + the full path to \"ip neigh\", \"arp -a\", or any other tool on + your system that returns ip/mac address pairs. If this field is + left empty, ZoneMinder will search for the command \"arp\" and + attempt to use that. + "), + type => $types{abs_path}, + category => "paths", + }, + { + name => "ZM_WEB_TITLE_PREFIX", + default => "ZM", + description => "The title prefix displayed on each window", + help => qqq(" + If you have more than one installation of ZoneMinder it can be + helpful to display different titles for each one. Changing this + option allows you to customise the window titles to include + further information to aid identification. + "), + type => $types{string}, + category => "web", + }, + { + name => "ZM_WEB_RESIZE_CONSOLE", + default => "yes", + description => "Should the console window resize itself to fit", + help => qqq(" + Traditionally the main ZoneMinder web console window has + resized itself to shrink to a size small enough to list only + the monitors that are actually present. This is intended to + make the window more unobtrusize but may not be to everyones + tastes, especially if opened in a tab in browsers which support + this kind if layout. Switch this option off to have the console + window size left to the users preference + "), + type => $types{boolean}, + category => "web", + }, + { + name => "ZM_WEB_POPUP_ON_ALARM", + default => "yes", + description => "Should the monitor window jump to the top if an alarm occurs", + help => qqq(" + When viewing a live monitor stream you can specify whether you + want the window to pop to the front if an alarm occurs when the + window is minimised or behind another window. This is most + useful if your monitors are over doors for example when they + can pop up if someone comes to the doorway. + "), + type => $types{boolean}, + category => "web", + }, + { + name => "ZM_OPT_X10", + default => "no", + description => "Support interfacing with X10 devices", + help => qqq(" + If you have an X10 Home Automation setup in your home you can + use ZoneMinder to initiate or react to X10 signals if your + computer has the appropriate interface controller. This option + indicates whether X10 options will be available in the browser + client. + "), + type => $types{boolean}, + category => "x10", + }, + { + name => "ZM_X10_DEVICE", + default => "/dev/ttyS0", + description => "What device is your X10 controller connected on", + requires => [ { name => "ZM_OPT_X10", value => "yes" } ], + help => qqq(" + If you have an X10 controller device (e.g. XM10U) connected to + your computer this option details which port it is connected on, + the default of /dev/ttyS0 maps to serial or com port 1. + "), + type => $types{abs_path}, + category => "x10", + }, + { + name => "ZM_X10_HOUSE_CODE", + default => "A", + description => "What X10 house code should be used", + requires => [ { name => "ZM_OPT_X10", value => "yes" } ], + help => qqq(" + X10 devices are grouped together by identifying them as all + belonging to one House Code. This option details what that is. + It should be a single letter between A and P. + "), + type => { db_type=>"string", hint=>"A-P", pattern=>qr|^([A-P])|i, format=>q( uc($1) ) }, + category => "x10", + }, + { + name => "ZM_X10_DB_RELOAD_INTERVAL", + default => "60", + description => "How often (in seconds) the X10 daemon reloads the monitors from the database", + requires => [ { name => "ZM_OPT_X10", value => "yes" } ], + help => qqq(" + The zmx10 daemon periodically checks the database to find out + what X10 events trigger, or result from, alarms. This option + determines how frequently this check occurs, unless you change + this area frequently this can be a fairly large value. + "), + type => $types{integer}, + category => "x10", + }, + { + name => "ZM_WEB_SOUND_ON_ALARM", + default => "no", + description => "Should the monitor window play a sound if an alarm occurs", + help => qqq(" + When viewing a live monitor stream you can specify whether you + want the window to play a sound to alert you if an alarm + occurs. + "), + type => $types{boolean}, + category => "web", + }, + { + name => "ZM_WEB_ALARM_SOUND", + default => "", + description => "The sound to play on alarm, put this in the sounds directory", + help => qqq(" + You can specify a sound file to play if an alarm occurs whilst + you are watching a live monitor stream. So long as your browser + understands the format it does not need to be any particular + type. This file should be placed in the sounds directory + defined earlier. + "), + type => $types{file}, + requires => [ { name => "ZM_WEB_SOUND_ON_ALARM", value => "yes" } ], + category => "web", + }, + { + name => "ZM_WEB_COMPACT_MONTAGE", + default => "no", + description => "Compact the montage view by removing extra detail", + help => qqq(" + The montage view shows the output of all of your active + monitors in one window. This include a small menu and status + information for each one. This can increase the web traffic and + make the window larger than may be desired. Setting this option + on removes all this extraneous information and just displays + the images. + "), + type => $types{boolean}, + category => "web", + }, + { + name => "ZM_OPT_FAST_DELETE", + default => "yes", + description => "Delete only event database records for speed", + help => qqq(" + Normally an event created as the result of an alarm consists of + entries in one or more database tables plus the various files + associated with it. When deleting events in the browser it can + take a long time to remove all of this if your are trying to do + a lot of events at once. It is recommended that you set this + option which means that the browser client only deletes the key + entries in the events table, which means the events will no + longer appear in the listing, and leaves the zmaudit daemon to + clear up the rest later. + "), + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_STRICT_VIDEO_CONFIG", + default => "yes", + description => "Allow errors in setting video config to be fatal", + help => qqq(" + With some video devices errors can be reported in setting the + various video attributes when in fact the operation was + successful. Switching this option off will still allow these + errors to be reported but will not cause them to kill the video + capture daemon. Note however that doing this will cause all + errors to be ignored including those which are genuine and + which may cause the video capture to not function correctly. + Use this option with caution. + "), + type => $types{boolean}, + category => "config", + }, + { + name => 'ZM_LD_PRELOAD', + default => '', + description => "Path to library to preload before launching daemons", + help => qqq("Some older cameras require the use of the v4l1 compat + library. This setting allows the setting of the path + to the library, so that it can be loaded by zmdc.pl + before launching zmc."), + type => $types{abs_path}, + category => 'config', + }, + { + name => "ZM_SIGNAL_CHECK_POINTS", + default => "10", + description => "How many points in a captured image to check for signal loss", + help => qqq(" + For locally attached video cameras ZoneMinder can check for + signal loss by looking at a number of random points on each + captured image. If all of these points are set to the same + fixed colour then the camera is assumed to have lost signal. + When this happens any open events are closed and a short one + frame signal loss event is generated, as is another when the + signal returns. This option defines how many points on each + image to check. Note that this is a maximum, any points found + to not have the check colour will abort any further checks so + in most cases on a couple of points will actually be checked. + Network and file based cameras are never checked. + "), + type => $types{integer}, + category => "config", + }, + { + name => "ZM_V4L_MULTI_BUFFER", + default => "yes", + description => "Use more than one buffer for Video 4 Linux devices", + help => qqq(" + Performance when using Video 4 Linux devices is usually best if + multiple buffers are used allowing the next image to be + captured while the previous one is being processed. If you have + multiple devices on a card sharing one input that requires + switching then this approach can sometimes cause frames from + one source to be mixed up with frames from another. Switching + this option off prevents multi buffering resulting in slower + but more stable image capture. This option is ignored for + non-local cameras or if only one input is present on a capture + chip. This option addresses a similar problem to the + ZM_CAPTURES_PER_FRAME option and you should normally change the + value of only one of the options at a time. If you have + different capture cards that need different values you can + ovveride them in each individual monitor on the source page. + "), + type => $types{boolean}, + category => "config", + }, + { + name => "ZM_CAPTURES_PER_FRAME", + default => "1", + description => "How many images are captured per returned frame, for shared local cameras", + help => qqq(" + If you are using cameras attached to a video capture card which + forces multiple inputs to share one capture chip, it can + sometimes produce images with interlaced frames reversed + resulting in poor image quality and a distinctive comb edge + appearance. Increasing this setting allows you to force + additional image captures before one is selected as the + captured frame. This allows the capture hardware to 'settle + down' and produce better quality images at the price of lesser + capture rates. This option has no effect on (a) network + cameras, or (b) where multiple inputs do not share a capture + chip. This option addresses a similar problem to the + ZM_V4L_MULTI_BUFFER option and you should normally change the + value of only one of the options at a time. If you have + different capture cards that need different values you can + ovveride them in each individual monitor on the source page. + "), + type => $types{integer}, + category => "config", + }, + { + name => "ZM_FILTER_RELOAD_DELAY", + default => "300", + description => "How often (in seconds) filters are reloaded in zmfilter", + help => qqq(" + ZoneMinder allows you to save filters to the database which + allow events that match certain criteria to be emailed, deleted + or uploaded to a remote machine etc. The zmfilter daemon loads + these and does the actual operation. This option determines how + often the filters are reloaded from the database to get the + latest versions or new filters. If you don't change filters + very often this value can be set to a large value. + "), + type => $types{integer}, + category => "system", + }, + { + name => "ZM_FILTER_EXECUTE_INTERVAL", + default => "60", + description => "How often (in seconds) to run automatic saved filters", + help => qqq(" + ZoneMinder allows you to save filters to the database which + allow events that match certain criteria to be emailed, deleted + or uploaded to a remote machine etc. The zmfilter daemon loads + these and does the actual operation. This option determines how + often the filters are executed on the saved event in the + database. If you want a rapid response to new events this + should be a smaller value, however this may increase the + overall load on the system and affect performance of other + elements. + "), + type => $types{integer}, + category => "system", + }, + { + name => "ZM_OPT_UPLOAD", + default => "no", + description => "Should ZoneMinder support uploading events from filters", + help => qqq(" + In ZoneMinder you can create event filters that specify whether + events that match certain criteria should be uploaded to a + remote server for archiving. This option specifies whether this + functionality should be available + "), + type => $types{boolean}, + category => "upload", + }, + { + name => "ZM_UPLOAD_ARCH_FORMAT", + default => "tar", + description => "What format the uploaded events should be created in.", + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + help => qqq(" + Uploaded events may be stored in either .tar or .zip format, + this option specifies which. Note that to use this you will + need to have the Archive::Tar and/or Archive::Zip perl modules + installed. + "), + type => { + db_type =>"string", + hint =>"tar|zip", + pattern =>qr|^([tz])|i, + format =>q( $1 =~ /^t/ ? "tar" : "zip" ) + }, + category => "upload", + }, + { + name => "ZM_UPLOAD_ARCH_COMPRESS", + default => "no", + description => "Should archive files be compressed", + help => qqq(" + When the archive files are created they can be compressed. + However in general since the images are compressed already this + saves only a minimal amount of space versus utilising more CPU + in their creation. Only enable if you have CPU to waste and are + limited in disk space on your remote server or bandwidth. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{boolean}, + category => "upload", + }, + { + name => "ZM_UPLOAD_ARCH_ANALYSE", + default => "no", + description => "Include the analysis files in the archive", + help => qqq(" + When the archive files are created they can contain either just + the captured frames or both the captured frames and, for frames + that caused an alarm, the analysed image with the changed area + highlighted. This option controls files are included. Only + include analysed frames if you have a high bandwidth connection + to the remote server or if you need help in figuring out what + caused an alarm in the first place as archives with these files + in can be considerably larger. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{boolean}, + category => "upload", + }, + { + name => "ZM_UPLOAD_PROTOCOL", + default => "ftp", + description => "What protocol to use to upload events", + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + help => qqq(" + ZoneMinder can upload events to a remote server using either + FTP or SFTP. Regular FTP is widely supported but not + necessarily very secure whereas SFTP (Secure FTP) runs over an + ssh connection and so is encrypted and uses regular ssh ports. + Note that to use this you will need to have the appropriate + perl module, either Net::FTP or Net::SFTP installed depending + on your choice. + "), + type => { + db_type =>"string", + hint =>"ftp|sftp", + pattern =>qr|^([tz])|i, + format =>q( $1 =~ /^f/ ? "ftp" : "sftp" ) + }, + category => "upload", + }, + { + name => "ZM_UPLOAD_FTP_HOST", + default => "", + description => "The remote server to upload to", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote ftp server. This option indicates the name, or ip + address, of the server to use. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{hostname}, + category => "hidden", + }, + { + name => "ZM_UPLOAD_HOST", + default => "", + description => "The remote server to upload events to", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote server. This option indicates the name, or ip address, + of the server to use. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{hostname}, + category => "upload", + }, + { + name => "ZM_UPLOAD_PORT", + default => "", + description => "The port on the remote upload server, if not the default (SFTP only)", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote server. If you are using the SFTP protocol then this + option allows you to specify a particular port to use for + connection. If this option is left blank then the default, port + 22, is used. This option is ignored for FTP uploads. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{integer}, + category => "upload", + }, + { + name => "ZM_UPLOAD_FTP_USER", + default => "", + description => "Your ftp username", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote ftp server. This option indicates the username that + ZoneMinder should use to log in for ftp transfer. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{alphanum}, + category => "hidden", + }, + { + name => "ZM_UPLOAD_USER", + default => "", + description => "Remote server username", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote server. This option indicates the username that + ZoneMinder should use to log in for transfer. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{alphanum}, + category => "upload", + }, + { + name => "ZM_UPLOAD_FTP_PASS", + default => "", + description => "Your ftp password", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote ftp server. This option indicates the password that + ZoneMinder should use to log in for ftp transfer. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{string}, + category => "hidden", + }, + { + name => "ZM_UPLOAD_PASS", + default => "", + description => "Remote server password", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote server. This option indicates the password that + ZoneMinder should use to log in for transfer. If you are using + certificate based logins for SFTP servers you can leave this + option blank. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{string}, + category => "upload", + }, + { + name => "ZM_UPLOAD_FTP_LOC_DIR", + default => "@ZM_TMPDIR@", + description => "The local directory in which to create upload files", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote ftp server. This option indicates the local directory + that ZoneMinder should use for temporary upload files. These + are files that are created from events, uploaded and then + deleted. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{abs_path}, + category => "hidden", + }, + { + name => "ZM_UPLOAD_LOC_DIR", + default => "@ZM_TMPDIR@", + description => "The local directory in which to create upload files", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote server. This option indicates the local directory that + ZoneMinder should use for temporary upload files. These are + files that are created from events, uploaded and then deleted. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{abs_path}, + category => "upload", + }, + { + name => "ZM_UPLOAD_FTP_REM_DIR", + default => "", + description => "The remote directory to upload to", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote ftp server. This option indicates the remote directory + that ZoneMinder should use to upload event files to. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{rel_path}, + category => "hidden", + }, + { + name => "ZM_UPLOAD_REM_DIR", + default => "", + description => "The remote directory to upload to", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote server. This option indicates the remote directory + that ZoneMinder should use to upload event files to. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{rel_path}, + category => "upload", + }, + { + name => "ZM_UPLOAD_FTP_TIMEOUT", + default => "120", + description => "How long to allow the transfer to take for each file", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote ftp server. This option indicates the maximum ftp + inactivity timeout (in seconds) that should be tolerated before + ZoneMinder determines that the transfer has failed and closes + down the connection. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{integer}, + category => "hidden", + }, + { + name => "ZM_UPLOAD_TIMEOUT", + default => "120", + description => "How long to allow the transfer to take for each file", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote server. This option indicates the maximum inactivity + timeout (in seconds) that should be tolerated before ZoneMinder + determines that the transfer has failed and closes down the + connection. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{integer}, + category => "upload", + }, + { + name => "ZM_UPLOAD_STRICT", + default => "no", + description => "Require strict host key checking for SFTP uploads", + help => qqq(" + You can require SFTP uploads to verify the host key of the remote server + for protection against man-in-the-middle attacks. You will need to add the + server's key to the known_hosts file. On most systems, this will be + ~/.ssh/known_hosts, where ~ is the home directory of the web server running + ZoneMinder. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{boolean}, + category => "upload", + }, + { + name => "ZM_UPLOAD_FTP_PASSIVE", + default => "yes", + description => "Use passive ftp when uploading", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote ftp server. This option indicates that ftp transfers + should be done in passive mode. This uses a single connection + for all ftp activity and, whilst slower than active transfers, + is more robust and likely to work from behind filewalls. This + option is ignored for SFTP transfers. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + help => qqq(" + If your computer is behind a firewall or proxy you may need to + set FTP to passive mode. In fact for simple transfers it makes + little sense to do otherwise anyway but you can set this to + 'No' if you wish. + "), + type => $types{boolean}, + category => "upload", + }, + { + name => "ZM_UPLOAD_FTP_DEBUG", + default => "no", + description => "Switch ftp debugging on", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote ftp server. If you are having (or expecting) troubles + with uploading events then setting this to 'yes' permits + additional information to be included in the zmfilter log file. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{boolean}, + category => "hidden", + }, + { + name => "ZM_UPLOAD_DEBUG", + default => "no", + description => "Switch upload debugging on", + help => qqq(" + You can use filters to instruct ZoneMinder to upload events to + a remote server. If you are having (or expecting) troubles with + uploading events then setting this to 'yes' permits additional + information to be generated by the underlying transfer modules + and included in the logs. + "), + requires => [ { name => "ZM_OPT_UPLOAD", value => "yes" } ], + type => $types{boolean}, + category => "upload", + }, + { + name => "ZM_OPT_EMAIL", + default => "no", + description => "Should ZoneMinder email you details of events that match corresponding filters", + help => qqq(" + In ZoneMinder you can create event filters that specify whether + events that match certain criteria should have their details + emailed to you at a designated email address. This will allow + you to be notified of events as soon as they occur and also to + quickly view the events directly. This option specifies whether + this functionality should be available. The email created with + this option can be any size and is intended to be sent to a + regular email reader rather than a mobile device. + "), + type => $types{boolean}, + category => "mail", + }, + { + name => "ZM_EMAIL_ADDRESS", + default => "", + description => "The email address to send matching event details to", + requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], + help => qqq(" + This option is used to define the email address that any events + that match the appropriate filters will be sent to. + "), + type => $types{email}, + category => "mail", + }, + { + name => "ZM_EMAIL_TEXT", + default => 'subject = "ZoneMinder: Alarm - %MN%-%EI% (%ESM% - %ESA% %EFA%)" body = " Hello, @@ -1003,24 +2202,30 @@ The details are as follows :- This alarm was matched by the %FN% filter and can be viewed at %EPS% ZoneMinder"', - description => "The text of the email used to send matching event details", - requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], - help => "This option is used to define the content of the email that is sent for any events that match the appropriate filters.", - type => $types{text}, - category => "hidden", - }, - { - name => "ZM_EMAIL_SUBJECT", - default => "ZoneMinder: Alarm - %MN%-%EI% (%ESM% - %ESA% %EFA%)", - description => "The subject of the email used to send matching event details", - requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], - help => "This option is used to define the subject of the email that is sent for any events that match the appropriate filters.", - type => $types{string}, - category => "mail", - }, - { - name => "ZM_EMAIL_BODY", - default => " + description => "The text of the email used to send matching event details", + requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], + help => qqq(" + This option is used to define the content of the email that is + sent for any events that match the appropriate filters. + "), + type => $types{text}, + category => "hidden", + }, + { + name => "ZM_EMAIL_SUBJECT", + default => "ZoneMinder: Alarm - %MN%-%EI% (%ESM% - %ESA% %EFA%)", + description => "The subject of the email used to send matching event details", + requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], + help => qqq(" + This option is used to define the subject of the email that is + sent for any events that match the appropriate filters. + "), + type => $types{string}, + category => "mail", + }, + { + name => "ZM_EMAIL_BODY", + default => " Hello, An alarm has been detected on your installation of the ZoneMinder. @@ -1036,896 +2241,1636 @@ The details are as follows :- This alarm was matched by the %FN% filter and can be viewed at %EPS% ZoneMinder", - description => "The body of the email used to send matching event details", - requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], - help => "This option is used to define the content of the email that is sent for any events that match the appropriate filters.", - type => $types{text}, - category => "mail", - }, - { - name => "ZM_OPT_MESSAGE", - default => "no", - description => "Should ZoneMinder message you with details of events that match corresponding filters", - help => "In ZoneMinder you can create event filters that specify whether events that match certain criteria should have their details sent to you at a designated short message email address. This will allow you to be notified of events as soon as they occur. This option specifies whether this functionality should be available. The email created by this option will be brief and is intended to be sent to an SMS gateway or a minimal mail reader such as a mobile device or phone rather than a regular email reader.", - type => $types{boolean}, - category => "mail", - }, - { - name => "ZM_MESSAGE_ADDRESS", - default => "", - description => "The email address to send matching event details to", - requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], - help => "This option is used to define the short message email address that any events that match the appropriate filters will be sent to.", - type => $types{email}, - category => "mail", - }, - { - name => "ZM_MESSAGE_TEXT", - default => 'subject = "ZoneMinder: Alarm - %MN%-%EI%" + description => "The body of the email used to send matching event details", + requires => [ { name => "ZM_OPT_EMAIL", value => "yes" } ], + help => qqq(" + This option is used to define the content of the email that is + sent for any events that match the appropriate filters. + "), + type => $types{text}, + category => "mail", + }, + { + name => "ZM_OPT_MESSAGE", + default => "no", + description => "Should ZoneMinder message you with details of events that match corresponding filters", + help => qqq(" + In ZoneMinder you can create event filters that specify whether + events that match certain criteria should have their details + sent to you at a designated short message email address. This + will allow you to be notified of events as soon as they occur. + This option specifies whether this functionality should be + available. The email created by this option will be brief and + is intended to be sent to an SMS gateway or a minimal mail + reader such as a mobile device or phone rather than a regular + email reader. + "), + type => $types{boolean}, + category => "mail", + }, + { + name => "ZM_MESSAGE_ADDRESS", + default => "", + description => "The email address to send matching event details to", + requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], + help => qqq(" + This option is used to define the short message email address + that any events that match the appropriate filters will be sent + to. + "), + type => $types{email}, + category => "mail", + }, + { + name => "ZM_MESSAGE_TEXT", + default => 'subject = "ZoneMinder: Alarm - %MN%-%EI%" body = "ZM alarm detected - %EL% secs, %EF%/%EFA% frames, t%EST%/m%ESM%/a%ESA% score."', - description => "The text of the message used to send matching event details", - requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], - help => "This option is used to define the content of the message that is sent for any events that match the appropriate filters.", - type => $types{text}, - category => "hidden", - }, - { - name => "ZM_MESSAGE_SUBJECT", - default => "ZoneMinder: Alarm - %MN%-%EI%", - description => "The subject of the message used to send matching event details", - requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], - help => "This option is used to define the subject of the message that is sent for any events that match the appropriate filters.", - type => $types{string}, - category => "mail", - }, - { - name => "ZM_MESSAGE_BODY", - default => "ZM alarm detected - %EL% secs, %EF%/%EFA% frames, t%EST%/m%ESM%/a%ESA% score.", - description => "The body of the message used to send matching event details", - requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], - help => "This option is used to define the content of the message that is sent for any events that match the appropriate filters.", - type => $types{text}, - category => "mail", - }, - { - name => "ZM_NEW_MAIL_MODULES", - default => "no", - description => "Use a newer perl method to send emails", - requires => [ { name => "ZM_OPT_EMAIL", value => "yes" }, { name => "ZM_OPT_MESSAGE", value => "yes" } ], - help => "Traditionally ZoneMinder has used the MIME::Entity perl module to construct and send notification emails and messages. Some people have reported problems with this module not being present at all or flexible enough for their needs. If you are one of those people this option allows you to select a new mailing method using MIME::Lite and Net::SMTP instead. This method was contributed by Ross Melin and should work for everyone but has not been extensively tested so currently is not selected by default.", - type => $types{boolean}, - category => "mail", - }, - { - name => "ZM_EMAIL_HOST", - default => "localhost", - description => "The host address of your SMTP mail server", - requires => [ { name => "ZM_OPT_EMAIL", value => "yes" }, { name => "ZM_OPT_MESSAGE", value => "yes" } ], - help => "If you have chosen SMTP as the method by which to send notification emails or messages then this option allows you to choose which SMTP server to use to send them. The default of localhost may work if you have the sendmail, exim or a similar daemon running however you may wish to enter your ISP's SMTP mail server here.", - type => $types{hostname}, - category => "mail", - }, - { - name => "ZM_FROM_EMAIL", - default => "", - description => "The email address you wish your event notifications to originate from", - requires => [ { name => "ZM_OPT_EMAIL", value => "yes" }, { name => "ZM_OPT_MESSAGE", value => "yes" } ], - help => "The emails or messages that will be sent to you informing you of events can appear to come from a designated email address to help you with mail filtering etc. An address of something like ZoneMinder\@your.domain is recommended.", - type => $types{email}, - category => "mail", - }, - { - name => "ZM_URL", - default => "", - description => "The URL of your ZoneMinder installation", - requires => [ { name => "ZM_OPT_EMAIL", value => "yes" }, { name => "ZM_OPT_MESSAGE", value => "yes" } ], - help => "The emails or messages that will be sent to you informing you of events can include a link to the events themselves for easy viewing. If you intend to use this feature then set this option to the url of your installation as it would appear from where you read your email, e.g. http://host.your.domain/zm.php.", - type => $types{url}, - category => "mail", - }, - { - name => "ZM_MAX_RESTART_DELAY", - default => "600", - description => "Maximum delay (in seconds) for daemon restart attempts.", - help => "The zmdc (zm daemon control) process controls when processeses are started or stopped and will attempt to restart any that fail. If a daemon fails frequently then a delay is introduced between each restart attempt. If the daemon stills fails then this delay is increased to prevent extra load being placed on the system by continual restarts. This option controls what this maximum delay is.", - type => $types{integer}, - category => "system", - }, - { - name => "ZM_WATCH_CHECK_INTERVAL", - default => "10", - description => "How often to check the capture daemons have not locked up", - help => "The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinately). This option determines how often the daemons are checked.", - type => $types{integer}, - category => "system", - }, - { - name => "ZM_WATCH_MAX_DELAY", - default => "5", - description => "The maximum delay allowed since the last captured image", - help => "The zmwatch daemon checks the image capture performance of the capture daemons to ensure that they have not locked up (rarely a sync error may occur which blocks indefinately). This option determines the maximum delay to allow since the last captured frame. The daemon will be restarted if it has not captured any images after this period though the actual restart may take slightly longer in conjunction with the check interval value above.", - type => $types{decimal}, - category => "system", - }, - { + description => "The text of the message used to send matching event details", + requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], + help => qqq(" + This option is used to define the content of the message that + is sent for any events that match the appropriate filters. + "), + type => $types{text}, + category => "hidden", + }, + { + name => "ZM_MESSAGE_SUBJECT", + default => "ZoneMinder: Alarm - %MN%-%EI%", + description => "The subject of the message used to send matching event details", + requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], + help => qqq(" + This option is used to define the subject of the message that + is sent for any events that match the appropriate filters. + "), + type => $types{string}, + category => "mail", + }, + { + name => "ZM_MESSAGE_BODY", + default => "ZM alarm detected - %EL% secs, %EF%/%EFA% frames, t%EST%/m%ESM%/a%ESA% score.", + description => "The body of the message used to send matching event details", + requires => [ { name => "ZM_OPT_MESSAGE", value => "yes" } ], + help => qqq(" + This option is used to define the content of the message that + is sent for any events that match the appropriate filters. + "), + type => $types{text}, + category => "mail", + }, + { + name => "ZM_NEW_MAIL_MODULES", + default => "no", + description => "Use a newer perl method to send emails", + requires => [ + { name => "ZM_OPT_EMAIL", value => "yes" }, + { name => "ZM_OPT_MESSAGE", value => "yes" } + ], + help => qqq(" + Traditionally ZoneMinder has used the MIME::Entity perl module + to construct and send notification emails and messages. Some + people have reported problems with this module not being + present at all or flexible enough for their needs. If you are + one of those people this option allows you to select a new + mailing method using MIME::Lite and Net::SMTP instead. This + method was contributed by Ross Melin and should work for + everyone but has not been extensively tested so currently is + not selected by default. + "), + type => $types{boolean}, + category => "mail", + }, + { + name => "ZM_EMAIL_HOST", + default => "localhost", + description => "The host address of your SMTP mail server", + requires => [ + { name => "ZM_OPT_EMAIL", value => "yes" }, + { name => "ZM_OPT_MESSAGE", value => "yes" } + ], + help => qqq(" + If you have chosen SMTP as the method by which to send + notification emails or messages then this option allows you to + choose which SMTP server to use to send them. The default of + localhost may work if you have the sendmail, exim or a similar + daemon running however you may wish to enter your ISP's SMTP + mail server here. + "), + type => $types{hostname}, + category => "mail", + }, + { + name => "ZM_FROM_EMAIL", + default => "", + description => "The email address you wish your event notifications to originate from", + requires => [ + { name => "ZM_OPT_EMAIL", value => "yes" }, + { name => "ZM_OPT_MESSAGE", value => "yes" } + ], + help => qqq(" + The emails or messages that will be sent to you informing you + of events can appear to come from a designated email address to + help you with mail filtering etc. An address of something like + ZoneMinder\@your.domain is recommended. + "), + type => $types{email}, + category => "mail", + }, + { + name => "ZM_URL", + default => "", + description => "The URL of your ZoneMinder installation", + requires => [ + { name => "ZM_OPT_EMAIL", value => "yes" }, + { name => "ZM_OPT_MESSAGE", value => "yes" } + ], + help => qqq(" + The emails or messages that will be sent to you informing you + of events can include a link to the events themselves for easy + viewing. If you intend to use this feature then set this option + to the url of your installation as it would appear from where + you read your email, e.g. http://host.your.domain/zm.php. + "), + type => $types{url}, + category => "mail", + }, + { + name => "ZM_MAX_RESTART_DELAY", + default => "600", + description => "Maximum delay (in seconds) for daemon restart attempts.", + help => qqq(" + The zmdc (zm daemon control) process controls when processeses + are started or stopped and will attempt to restart any that + fail. If a daemon fails frequently then a delay is introduced + between each restart attempt. If the daemon stills fails then + this delay is increased to prevent extra load being placed on + the system by continual restarts. This option controls what + this maximum delay is. + "), + type => $types{integer}, + category => "system", + }, + { + name => "ZM_WATCH_CHECK_INTERVAL", + default => "10", + description => "How often to check the capture daemons have not locked up", + help => qqq(" + The zmwatch daemon checks the image capture performance of the + capture daemons to ensure that they have not locked up (rarely + a sync error may occur which blocks indefinitely). This option + determines how often the daemons are checked. + "), + type => $types{integer}, + category => "system", + }, + { + name => "ZM_WATCH_MAX_DELAY", + default => "5", + description => "The maximum delay allowed since the last captured image", + help => qqq(" + The zmwatch daemon checks the image capture performance of the + capture daemons to ensure that they have not locked up (rarely + a sync error may occur which blocks indefinitely). This option + determines the maximum delay to allow since the last captured + frame. The daemon will be restarted if it has not captured any + images after this period though the actual restart may take + slightly longer in conjunction with the check interval value + above. + "), + type => $types{decimal}, + category => "system", + }, + { - name => "ZM_RUN_AUDIT", - default => "yes", - description => "Run zmaudit to check data consistency", - help => "The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. This option controls whether zmaudit is run in the background and performs these checks and fixes continuously. This is recommended for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to not have zmaudit running unconditionally and schedule occasional checks at other, more convenient, times.", - type => $types{boolean}, - category => "system", - }, - { - - name => "ZM_AUDIT_CHECK_INTERVAL", - default => "900", - description => "How often to check database and filesystem consistency", - help => "The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed.", - type => $types{integer}, - category => "system", - }, - { - name => "ZM_FORCED_ALARM_SCORE", - default => "255", - description => "Score to give forced alarms", - help => "The 'zmu' utility can be used to force an alarm on a monitor rather than rely on the motion detection algorithms. This option determines what score to give these alarms to distinguish them from regular ones. It must be 255 or less.", - type => $types{integer}, - category => "config", - }, - { - name => "ZM_BULK_FRAME_INTERVAL", - default => "100", - description => "How often a bulk frame should be written to the database", - help => "Traditionally ZoneMinder writes an entry into the Frames database table for each frame that is captured and saved. This works well in motion detection scenarios but when in a DVR situation ('Record' or 'Mocord' mode) this results in a huge number of frame writes and a lot of database and disk bandwidth for very little additional information. Setting this to a non-zero value will enabled ZoneMinder to group these non-alarm frames into one 'bulk' frame entry which saves a lot of bandwidth and space. The only disadvantage of this is that timing information for individual frames is lost but in constant frame rate situations this is usually not significant. This setting is ignored in Modect mode and individual frames are still written if an alarm occurs in Mocord mode also.", - type => $types{integer}, - category => "config", - }, - { - name => "ZM_EVENT_CLOSE_MODE", - default => "idle", - description => "When continuous events are closed.", - help => "When a monitor is running in a continuous recording mode (Record or Mocord) events are usually closed after a fixed period of time (the section length). However in Mocord mode it is possible that motion detection may occur near the end of a section. This option controls what happens when an alarm occurs in Mocord mode. The 'time' setting means that the event will be closed at the end of the section regardless of alarm activity. The 'idle' setting means that the event will be closed at the end of the section if there is no alarm activity occuring at the time otherwise it will be closed once the alarm is over meaning the event may end up being longer than the normal section length. The 'alarm' setting means that if an alarm occurs during the event, the event will be closed once the alarm is over regardless of when this occurs. This has the effect of limiting the number of alarms to one per event and the events will be shorter than the section length if an alarm has occurred.", - type => $types{boolean}, - type => { db_type=>"string", hint=>"time|idle|alarm", pattern=>qr|^([tia])|i, format=>q( ($1 =~ /^t/) ? "time" : ($1 =~ /^i/ ? "idle" : "time" ) ) }, - category => "config", - }, + name => "ZM_RUN_AUDIT", + default => "yes", + description => "Run zmaudit to check data consistency", + help => qqq(" + The zmaudit daemon exists to check that the saved information + in the database and on the filesystem match and are consistent + with each other. If an error occurs or if you are using 'fast + deletes' it may be that database records are deleted but files + remain. In this case, and similar, zmaudit will remove + redundant information to synchronise the two data stores. This + option controls whether zmaudit is run in the background and + performs these checks and fixes continuously. This is + recommended for most systems however if you have a very large + number of events the process of scanning the database and + filesystem may take a long time and impact performance. In this + case you may prefer to not have zmaudit running unconditionally + and schedule occasional checks at other, more convenient, + times. + "), + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_AUDIT_CHECK_INTERVAL", + default => "900", + description => "How often to check database and filesystem consistency", + help => qqq(" + The zmaudit daemon exists to check that the saved information + in the database and on the filesystem match and are consistent + with each other. If an error occurs or if you are using 'fast + deletes' it may be that database records are deleted but files + remain. In this case, and similar, zmaudit will remove + redundant information to synchronise the two data stores. The + default check interval of 900 seconds (15 minutes) is fine for + most systems however if you have a very large number of events + the process of scanning the database and filesystem may take a + long time and impact performance. In this case you may prefer + to make this interval much larger to reduce the impact on your + system. This option determines how often these checks are + performed. + "), + type => $types{integer}, + category => "system", + }, + { + name => "ZM_AUDIT_MIN_AGE", + default => "86400", + description => "The minimum age in seconds event data must be in order to be deleted.", + help => qqq(" + The zmaudit daemon exists to check that the saved information + in the database and on the filesystem match and are consistent + with each other. Event files or db records that are younger than + this setting will not be deleted and a warning will be given. + "), + type => $types{integer}, + category => "system", + }, + { + name => "ZM_FORCED_ALARM_SCORE", + default => "255", + description => "Score to give forced alarms", + help => qqq(" + The 'zmu' utility can be used to force an alarm on a monitor + rather than rely on the motion detection algorithms. This + option determines what score to give these alarms to + distinguish them from regular ones. It must be 255 or less. + "), + type => $types{integer}, + category => "config", + }, + { + name => "ZM_BULK_FRAME_INTERVAL", + default => "100", + description => "How often a bulk frame should be written to the database", + help => qqq(" + Traditionally ZoneMinder writes an entry into the Frames + database table for each frame that is captured and saved. This + works well in motion detection scenarios but when in a DVR + situation ('Record' or 'Mocord' mode) this results in a huge + number of frame writes and a lot of database and disk bandwidth + for very little additional information. Setting this to a + non-zero value will enabled ZoneMinder to group these non-alarm + frames into one 'bulk' frame entry which saves a lot of + bandwidth and space. The only disadvantage of this is that + timing information for individual frames is lost but in + constant frame rate situations this is usually not significant. + This setting is ignored in Modect mode and individual frames + are still written if an alarm occurs in Mocord mode also. + "), + type => $types{integer}, + category => "config", + }, + { + name => "ZM_EVENT_CLOSE_MODE", + default => "idle", + description => "When continuous events are closed.", + help => qqq(" + When a monitor is running in a continuous recording mode + (Record or Mocord) events are usually closed after a fixed + period of time (the section length). However in Mocord mode it + is possible that motion detection may occur near the end of a + section. This option controls what happens when an alarm occurs + in Mocord mode. The 'time' setting means that the event will be + closed at the end of the section regardless of alarm activity. + The 'idle' setting means that the event will be closed at the + end of the section if there is no alarm activity occurring at + the time otherwise it will be closed once the alarm is over + meaning the event may end up being longer than the normal + section length. The 'alarm' setting means that if an alarm + occurs during the event, the event will be closed once the + alarm is over regardless of when this occurs. This has the + effect of limiting the number of alarms to one per event and + the events will be shorter than the section length if an alarm + has occurred. + "), + type => $types{boolean}, + type => { + db_type =>"string", + hint =>"time|idle|alarm", + pattern =>qr|^([tia])|i, + format =>q( ($1 =~ /^t/) + ? "time" + : ($1 =~ /^i/ ? "idle" : "time" ) + ) + }, + category => "config", + }, # Deprecated, superseded by event close mode - { - name => "ZM_FORCE_CLOSE_EVENTS", - default => "no", - description => "Close events at section ends.", - help => "When a monitor is running in a continuous recording mode (Record or Mocord) events are usually closed after a fixed period of time (the section length). However in Mocord mode it is possible that motion detection may occur near the end of a section and ordinarily this will prevent the event being closed until the motion has ceased. Switching this option on will force the event closed at the specified time regardless of any motion activity.", - type => $types{boolean}, - category => "hidden", - }, - { - name => "ZM_CREATE_ANALYSIS_IMAGES", - default => "yes", - description => "Create analysed alarm images with motion outlined", - help => "By default during an alarm ZoneMinder records both the raw captured image and one that has been analysed and had areas where motion was detected outlined. This can be very useful during zone configuration or in analysing why events occured. However it also incurs some overhead and in a stable system may no longer be necessary. This parameter allows you to switch the generation of these images off.", - type => $types{boolean}, - category => "config", - }, - { - name => "ZM_WEIGHTED_ALARM_CENTRES", - default => "no", - description => "Use a weighted algorithm to calculate the centre of an alarm", - help => "ZoneMinder will always calculate the centre point of an alarm in a zone to give some indication of where on the screen it is. This can be used by the experimental motion tracking feature or your own custom extensions. In the alarmed or filtered pixels mode this is a simple midpoint between the extents of the detected pxiesl. However in the blob method this can instead be calculated using weighted pixel locations to give more accurate positioning for irregularly shaped blobs. This method, while more precise is also slower and so is turned off by default.", - type => $types{boolean}, - category => "config", - }, - { - name => "ZM_EVENT_IMAGE_DIGITS", - default => "5", - description => "How many significant digits are used in event image numbering", - help => "As event images are captured they are stored to the filesystem with a numerical index. By default this index has three digits so the numbers start 001, 002 etc. This works works for most scenarios as events with more than 999 frames are rarely captured. However if you have extremely long events and use external applications then you may wish to increase this to ensure correct sorting of images in listings etc. Warning, increasing this value on a live system may render existing events unviewable as the event will have been saved with the previous scheme. Decreasing this value should have no ill effects.", - type => $types{integer}, - category => "config", - }, - { - name => "ZM_DEFAULT_ASPECT_RATIO", - default => "4:3", - description => "The default width:height aspect ratio used in monitors", - help => "When specifying the dimensions of monitors you can click a checkbox to ensure that the width stays in the correct ratio to the height, or vice versa. This setting allows you to indicate what the ratio of these settings should be. This should be specified in the format : and the default of 4:3 normally be acceptable but 11:9 is another common setting. If the checkbox is not clicked when specifying monitor dimensions this setting has no effect.", - type => $types{string}, - category => "config", - }, - { - name => "ZM_USER_SELF_EDIT", - default => "no", - description => "Allow unprivileged users to change their details", - help => "Ordinarily only users with system edit privilege are able to change users details. Switching this option on allows ordinary users to change their passwords and their language settings", - type => $types{boolean}, - category => "config", - }, - { - name => "ZM_OPT_FRAME_SERVER", - default => "no", - description => "Should analysis farm out the writing of images to disk", - #requires => [ { name => "ZM_OPT_ADAPTIVE_SKIP", value => "yes" } ], - help => "In some circumstances it is possible for a slow disk to take so long writing images to disk that it causes the analysis daemon to fall behind especially during high frame rate events. Setting this option to yes enables a frame server daemon (zmf) which will be sent the images from the analysis daemon and will do the actual writing of images itself freeing up the analysis daemon to get on with other things. Should this transmission fail or other permanent or transient error occur, this function will fall back to the analysis daemon.", - type => $types{boolean}, - category => "system", - }, - { - name => "ZM_FRAME_SOCKET_SIZE", - default => "0", - description => "Specify the frame server socket buffer size if non-standard", - requires => [ { name => "ZM_OPT_FRAME_SERVER", value => "yes" } ], - help => "For large captured images it is possible for the writes from the analysis daemon to the frame server to fail as the amount to be written exceeds the default buffer size. While the images are then written by the analysis daemon so no data is lost, it defeats the object of the frame server daemon in the first place. You can use this option to indicate that a larger buffer size should be used. Note that you may have to change the existing maximum socket buffer size on your system via sysctl (or in /proc/sys/net/core/wmem_max) to allow this new size to be set. Alternatively you can change the default buffer size on your system in the same way in which case that will be used with no change necessary in this option", - type => $types{integer}, - category => "system", - }, - { - name => "ZM_OPT_CONTROL", - default => "no", - description => "Support controllable (e.g. PTZ) cameras", - help => "ZoneMinder includes limited support for controllable cameras. A number of sample protocols are included and others can easily be added. If you wish to control your cameras via ZoneMinder then select this option otherwise if you only have static cameras or use other control methods then leave this option off.", - type => $types{boolean}, - category => "system", - }, - { - name => "ZM_OPT_TRIGGERS", - default => "no", - description => "Interface external event triggers via socket or device files", - help => "ZoneMinder can interact with external systems which prompt or cancel alarms. This is done via the zmtrigger.pl script. This option indicates whether you want to use these external triggers. Most people will say no here.", - type => $types{boolean}, - category => "system", - }, - { - name => "ZM_CHECK_FOR_UPDATES", - default => "yes", - description => "Check with zoneminder.com for updated versions", - help => "From ZoneMinder version 1.17.0 onwards new versions are expected to be more frequent. To save checking manually for each new version ZoneMinder can check with the zoneminder.com website to determine the most recent release. These checks are infrequent, about once per week, and no personal or system information is transmitted other than your current version number. If you do not wish these checks to take place or your ZoneMinder system has no internet access you can switch these check off with this configuration variable", - type => $types{boolean}, - category => "system", - }, - { - name => "ZM_UPDATE_CHECK_PROXY", - default => "", - description => "Proxy url if required to access zoneminder.com", - help => "If you use a proxy to access the internet then ZoneMinder needs to know so it can access zoneminder.com to check for updates. If you do use a proxy enter the full proxy url here in the form of http://:/", - type => $types{string}, - category => "system", - }, - { - name => "ZM_SHM_KEY", - default => "0x7a6d0000", - description => "Shared memory root key to use", - help => "ZoneMinder uses shared memory to speed up communication between modules. To identify the right area to use shared memory keys are used. This option controls what the base key is, each monitor will have it's Id or'ed with this to get the actual key used. You will not normally need to change this value unless it clashes with another instance of ZoneMinder on the same machine. Only the first four hex digits are used, the lower four will be masked out and ignored.", - type => $types{hexadecimal}, - category => "system", - }, + { + name => "ZM_FORCE_CLOSE_EVENTS", + default => "no", + description => "Close events at section ends.", + help => qqq(" + When a monitor is running in a continuous recording mode + (Record or Mocord) events are usually closed after a fixed + period of time (the section length). However in Mocord mode it + is possible that motion detection may occur near the end of a + section and ordinarily this will prevent the event being closed + until the motion has ceased. Switching this option on will + force the event closed at the specified time regardless of any + motion activity. + "), + type => $types{boolean}, + category => "hidden", + }, + { + name => "ZM_CREATE_ANALYSIS_IMAGES", + default => "yes", + description => "Create analysed alarm images with motion outlined", + help => qqq(" + By default during an alarm ZoneMinder records both the raw + captured image and one that has been analysed and had areas + where motion was detected outlined. This can be very useful + during zone configuration or in analysing why events occurred. + However it also incurs some overhead and in a stable system may + no longer be necessary. This parameter allows you to switch the + generation of these images off. + "), + type => $types{boolean}, + category => "config", + }, + { + name => "ZM_WEIGHTED_ALARM_CENTRES", + default => "no", + description => "Use a weighted algorithm to calculate the centre of an alarm", + help => qqq(" + ZoneMinder will always calculate the centre point of an alarm + in a zone to give some indication of where on the screen it is. + This can be used by the experimental motion tracking feature or + your own custom extensions. In the alarmed or filtered pixels + mode this is a simple midpoint between the extents of the + detected pxiesl. However in the blob method this can instead be + calculated using weighted pixel locations to give more accurate + positioning for irregularly shaped blobs. This method, while + more precise is also slower and so is turned off by default. + "), + type => $types{boolean}, + category => "config", + }, + { + name => "ZM_EVENT_IMAGE_DIGITS", + default => "5", + description => "How many significant digits are used in event image numbering", + help => qqq(" + As event images are captured they are stored to the filesystem + with a numerical index. By default this index has three digits + so the numbers start 001, 002 etc. This works works for most + scenarios as events with more than 999 frames are rarely + captured. However if you have extremely long events and use + external applications then you may wish to increase this to + ensure correct sorting of images in listings etc. Warning, + increasing this value on a live system may render existing + events unviewable as the event will have been saved with the + previous scheme. Decreasing this value should have no ill + effects. + "), + type => $types{integer}, + category => "config", + }, + { + name => "ZM_DEFAULT_ASPECT_RATIO", + default => "4:3", + description => "The default width:height aspect ratio used in monitors", + help => qqq(" + When specifying the dimensions of monitors you can click a + checkbox to ensure that the width stays in the correct ratio to + the height, or vice versa. This setting allows you to indicate + what the ratio of these settings should be. This should be + specified in the format : and the + default of 4:3 normally be acceptable but 11:9 is another + common setting. If the checkbox is not clicked when specifying + monitor dimensions this setting has no effect. + "), + type => $types{string}, + category => "config", + }, + { + name => "ZM_USER_SELF_EDIT", + default => "no", + description => "Allow unprivileged users to change their details", + help => qqq(" + Ordinarily only users with system edit privilege are able to + change users details. Switching this option on allows ordinary + users to change their passwords and their language settings + "), + type => $types{boolean}, + category => "config", + }, + { + name => "ZM_OPT_FRAME_SERVER", + default => "no", + description => "Should analysis farm out the writing of images to disk", + #requires => [ { name => "ZM_OPT_ADAPTIVE_SKIP", value => "yes" } ], + help => qqq(" + In some circumstances it is possible for a slow disk to take so + long writing images to disk that it causes the analysis daemon + to fall behind especially during high frame rate events. + Setting this option to yes enables a frame server daemon (zmf) + which will be sent the images from the analysis daemon and will + do the actual writing of images itself freeing up the analysis + daemon to get on with other things. Should this transmission + fail or other permanent or transient error occur, this function + will fall back to the analysis daemon. + "), + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_FRAME_SOCKET_SIZE", + default => "0", + description => "Specify the frame server socket buffer size if non-standard", + requires => [ { name => "ZM_OPT_FRAME_SERVER", value => "yes" } ], + help => qqq(" + For large captured images it is possible for the writes from + the analysis daemon to the frame server to fail as the amount + to be written exceeds the default buffer size. While the images + are then written by the analysis daemon so no data is lost, it + defeats the object of the frame server daemon in the first + place. You can use this option to indicate that a larger buffer + size should be used. Note that you may have to change the + existing maximum socket buffer size on your system via sysctl + (or in /proc/sys/net/core/wmem_max) to allow this new size to + be set. Alternatively you can change the default buffer size on + your system in the same way in which case that will be used + with no change necessary in this option + "), + type => $types{integer}, + category => "system", + }, + { + name => "ZM_OPT_CONTROL", + default => "no", + description => "Support controllable (e.g. PTZ) cameras", + help => qqq(" + ZoneMinder includes limited support for controllable cameras. A + number of sample protocols are included and others can easily + be added. If you wish to control your cameras via ZoneMinder + then select this option otherwise if you only have static + cameras or use other control methods then leave this option + off. + "), + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_OPT_TRIGGERS", + default => "no", + description => "Interface external event triggers via socket or device files", + help => qqq(" + ZoneMinder can interact with external systems which prompt or + cancel alarms. This is done via the zmtrigger.pl script. This + option indicates whether you want to use these external + triggers. Most people will say no here. + "), + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_CHECK_FOR_UPDATES", + default => "yes", + description => "Check with zoneminder.com for updated versions", + help => qqq(" + From ZoneMinder version 1.17.0 onwards new versions are + expected to be more frequent. To save checking manually for + each new version ZoneMinder can check with the zoneminder.com + website to determine the most recent release. These checks are + infrequent, about once per week, and no personal or system + information is transmitted other than your current version + number. If you do not wish these checks to take place or your + ZoneMinder system has no internet access you can switch these + check off with this configuration variable + "), + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_TELEMETRY_DATA", + default => "yes", + description => "Send usage information to ZoneMinder", + help => qqq(" + Enable collection of usage information of the local system and send + it to the ZoneMinder development team. This data will be used to + determine things like who and where our customers are, how big their + systems are, the underlying hardware and operating system, etc. + This is being done for the sole purpoase of creating a better + product for our target audience. This script is intended to be + completely transparent to the end user, and can be disabled from + the web console under Options. + "), + type => $types{boolean}, + category => "system", + }, + { + name => "ZM_TELEMETRY_UUID", + default => "", + description => "Unique identifier for ZoneMinder telemetry", + help => qqq(" + This variable is auto-generated once by the system and is used to + uniquely identify it among all other ZoneMinder systems in + existence. + "), + type => $types{string}, + category => "dynamic", + }, + { + name => "ZM_TELEMETRY_LAST_UPLOAD", + default => "", + description => "When the last ZoneMinder telemetry upload ocurred", + help => "", + type => $types{integer}, + readonly => 1, + category => "dynamic", + }, + { + name => "ZM_UPDATE_CHECK_PROXY", + default => "", + description => "Proxy url if required to access zoneminder.com", + help => qqq(" + If you use a proxy to access the internet then ZoneMinder needs + to know so it can access zoneminder.com to check for updates. + If you do use a proxy enter the full proxy url here in the form + of http://:/ + "), + type => $types{string}, + category => "system", + }, + { + name => "ZM_SHM_KEY", + default => "0x7a6d0000", + description => "Shared memory root key to use", + help => qqq(" + ZoneMinder uses shared memory to speed up communication between + modules. To identify the right area to use shared memory keys + are used. This option controls what the base key is, each + monitor will have it's Id or'ed with this to get the actual key + used. You will not normally need to change this value unless it + clashes with another instance of ZoneMinder on the same + machine. Only the first four hex digits are used, the lower + four will be masked out and ignored. + "), + type => $types{hexadecimal}, + category => "system", + }, # Deprecated, really no longer necessary - { - name => "ZM_WEB_REFRESH_METHOD", - default => "javascript", - description => "What method windows should use to refresh themselves", - help => "Many windows in Javascript need to refresh themselves to keep their information current. This option determines what method they should use to do this. Choosing 'javascript' means that each window will have a short JavaScript statement in with a timer to prompt the refresh. This is the most compatible method. Choosing 'http' means the refresh instruction is put in the HTTP header. This is a cleaner method but refreshes are interrupted or cancelled when a link in the window is clicked meaning that the window will no longer refresh and this would have to be done manually.", - type => { db_type=>"string", hint=>"javascript|http", pattern=>qr|^([jh])|i, format=>q( $1 =~ /^j/ ? "javascript" : "http" ) }, - category => "hidden", - }, - { - name => "ZM_WEB_EVENT_SORT_FIELD", - default => "DateTime", - description => "Default field the event lists are sorted by", - help => "Events in lists can be initially ordered in any way you want. This option controls what field is used to sort them. You can modify this ordering from filters or by clicking on headings in the lists themselves. Bear in mind however that the 'Prev' and 'Next' links, when scrolling through events, relate to the ordering in the lists and so not always to time based ordering.", - type => { db_type=>"string", hint=>"Id|Name|Cause|MonitorName|DateTime|Length|Frames|AlarmFrames|TotScore|AvgScore|MaxScore", pattern=>qr|.|, format=>q( $1 ) }, - category => "web", - }, - { - name => "ZM_WEB_EVENT_SORT_ORDER", - default => "asc", - description => "Default order the event lists are sorted by", - help => "Events in lists can be initially ordered in any way you want. This option controls what order (ascending or descending) is used to sort them. You can modify this ordering from filters or by clicking on headings in the lists themselves. Bear in mind however that the 'Prev' and 'Next' links, when scrolling through events, relate to the ordering in the lists and so not always to time based ordering.", - type => { db_type=>"string", hint=>"asc|desc", pattern=>qr|^([ad])|i, format=>q( $1 =~ /^a/i ? "asc" : "desc" ) }, - category => "web", - }, - { - name => "ZM_WEB_EVENTS_PER_PAGE", - default => "25", - description => "How many events to list per page in paged mode", - help => "In the event list view you can either list all events or just a page at a time. This option controls how many events are listed per page in paged mode and how often to repeat the column headers in non-paged mode.", - type => $types{integer}, - category => "web", - }, - { - name => "ZM_WEB_LIST_THUMBS", - default => "no", - description => "Display mini-thumbnails of event images in event lists", - help => "Ordinarily the event lists just display text details of the events to save space and time. By switching this option on you can also display small thumbnails to help you identify events of interest. The size of these thumbnails is controlled by the following two options.", - type => $types{boolean}, - category => "web", - }, - { - name => "ZM_WEB_LIST_THUMB_WIDTH", - default => "48", - description => "The width of the thumbnails that appear in the event lists", - help => "This options controls the width of the thumbnail images that appear in the event lists. It should be fairly small to fit in with the rest of the table. If you prefer you can specify a height instead in the next option but you should only use one of the width or height and the other option should be set to zero. If both width and height are specified then width will be used and height ignored.", - type => $types{integer}, - requires => [ { name => "ZM_WEB_LIST_THUMBS", value => "yes" } ], - category => "web", - }, - { - name => "ZM_WEB_LIST_THUMB_HEIGHT", - default => "0", - description => "The height of the thumbnails that appear in the event lists", - help => "This options controls the height of the thumbnail images that appear in the event lists. It should be fairly small to fit in with the rest of the table. If you prefer you can specify a width instead in the previous option but you should only use one of the width or height and the other option should be set to zero. If both width and height are specified then width will be used and height ignored.", - type => $types{integer}, - requires => [ { name => "ZM_WEB_LIST_THUMBS", value => "yes" } ], - category => "web", - }, - { - name => "ZM_WEB_USE_OBJECT_TAGS", - default => "yes", - description => "Wrap embed in object tags for media content", - help => "There are two methods of including media content in web pages. The most common way is use the EMBED tag which is able to give some indication of the type of content. However this is not a standard part of HTML. The official method is to use OBJECT tags which are able to give more information allowing the correct media viewers etc to be loaded. However these are less widely supported and content may be specifically tailored to a particular platform or player. This option controls whether media content is enclosed in EMBED tags only or whether, where appropriate, it is additionally wrapped in OBJECT tags. Currently OBJECT tags are only used in a limited number of circumstances but they may become more widespread in the future. It is suggested that you leave this option on unless you encounter problems playing some content.", - type => $types{boolean}, - category => "web", - }, - { - name => "ZM_WEB_H_REFRESH_MAIN", - default => "60", - introduction => "There are now a number of options that are grouped into bandwidth categories, this allows you to configure the ZoneMinder client to work optimally over the various access methods you might to access the client.\n\nThe next few options control what happens when the client is running in 'high' bandwidth mode. You should set these options for when accessing the ZoneMinder client over a local network or high speed link. In most cases the default values will be suitable as a starting point.", - description => "How often (in seconds) the main console window should refresh itself", - help => "The main console window lists a general status and the event totals for all monitors. This is not a trivial task and should not be repeated too frequently or it may affect the performance of the rest of the system.", - type => $types{integer}, - category => "highband", - }, - { - name => "ZM_WEB_H_REFRESH_CYCLE", - default => "10", - description => "How often (in seconds) the cycle watch window swaps to the next monitor", - help => "The cycle watch window is a method of continuously cycling between images from all of your monitors. This option determines how often to refresh with a new image.", - type => $types{integer}, - category => "highband", - }, - { - name => "ZM_WEB_H_REFRESH_IMAGE", - default => "3", - description => "How often (in seconds) the watched image is refreshed (if not streaming)", - help => "The live images from a monitor can be viewed in either streamed or stills mode. This option determines how often a stills image is refreshed, it has no effect if streaming is selected.", - type => $types{integer}, - category => "highband", - }, - { - name => "ZM_WEB_H_REFRESH_STATUS", - default => "1", - description => "How often (in seconds) the status refreshes itself in the watch window", - help => "The monitor window is actually made from several frames. The one in the middle merely contains a monitor status which needs to refresh fairly frequently to give a true indication. This option determines that frequency.", - type => $types{integer}, - category => "highband", - }, - { - name => "ZM_WEB_H_REFRESH_EVENTS", - default => "5", - description => "How often (in seconds) the event listing is refreshed in the watch window", - help => "The monitor window is actually made from several frames. The lower framme contains a listing of the last few events for easy access. This option determines how often this is refreshed.", - type => $types{integer}, - category => "highband", - }, - { - name => "ZM_WEB_H_CAN_STREAM", - default => "auto", - description => "Override the automatic detection of browser streaming capability", - help => "If you know that your browser can handle image streams of the type 'multipart/x-mixed-replace' but ZoneMinder does not detect this correctly you can set this option to ensure that the stream is delivered with or without the use of the Cambozola plugin. Selecting 'yes' will tell ZoneMinder that your browser can handle the streams natively, 'no' means that it can't and so the plugin will be used while 'auto' lets ZoneMinder decide.", - type => $types{tristate}, - category => "highband", - }, - { - name => "ZM_WEB_H_STREAM_METHOD", - default => "jpeg", - description => "Which method should be used to send video streams to your browser.", - help => "ZoneMinder can be configured to use either mpeg encoded video or a series or still jpeg images when sending video streams. This option defines which is used. If you choose mpeg you should ensure that you have the appropriate plugins available on your browser whereas choosing jpeg will work natively on Mozilla and related browsers and with a Java applet on Internet Explorer", - type => { db_type=>"string", hint=>"mpeg|jpeg", pattern=>qr|^([mj])|i, format=>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) }, - category => "highband", - }, - { - name => "ZM_WEB_H_DEFAULT_SCALE", - default => "100", - description => "What the default scaling factor applied to 'live' or 'event' views is (%)", - help => "Normally ZoneMinder will display 'live' or 'event' streams in their native size. However if you have monitors with large dimensions or a slow link you may prefer to reduce this size, alternatively for small monitors you can enlarge it. This options lets you specify what the default scaling factor will be. It is expressed as a percentage so 100 is normal size, 200 is double size etc.", - type => { db_type=>"integer", hint=>"25|33|50|75|100|150|200|300|400", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "highband", - }, - { - name => "ZM_WEB_H_DEFAULT_RATE", - default => "100", - description => "What the default replay rate factor applied to 'event' views is (%)", - help => "Normally ZoneMinder will display 'event' streams at their native rate, i.e. as close to real-time as possible. However if you have long events it is often convenient to replay them at a faster rate for review. This option lets you specify what the default replay rate will be. It is expressed as a percentage so 100 is normal rate, 200 is double speed etc.", - type => { db_type=>"integer", hint=>"25|50|100|150|200|400|1000|2500|5000|10000", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "highband", - }, - { - name => "ZM_WEB_H_VIDEO_BITRATE", - default => "150000", - description => "What the bitrate of the video encoded stream should be set to", - help => "When encoding real video via the ffmpeg library a bit rate can be specified which roughly corresponds to the available bandwidth used for the stream. This setting effectively corresponds to a 'quality' setting for the video. A low value will result in a blocky image whereas a high value will produce a clearer view. Note that this setting does not control the frame rate of the video however the quality of the video produced is affected both by this setting and the frame rate that the video is produced at. A higher frame rate at a particular bit rate result in individual frames being at a lower quality.", - type => $types{integer}, - category => "highband", - }, - { - name => "ZM_WEB_H_VIDEO_MAXFPS", - default => "30", - description => "What the maximum frame rate for streamed video should be", - help => "When using streamed video the main control is the bitrate which determines how much data can be transmitted. However a lower bitrate at high frame rates results in a lower quality image. This option allows you to limit the maximum frame rate to ensure that video quality is maintained. An additional advantage is that encoding video at high frame rates is a processor intensive task when for the most part a very high frame rate offers little perceptible improvement over one that has a more manageable resource requirement. Note, this option is implemented as a cap beyond which binary reduction takes place. So if you have a device capturing at 15fps and set this option to 10fps then the video is not produced at 10fps, but rather at 7.5fps (15 divided by 2) as the final frame rate must be the original divided by a power of 2.", - type => $types{integer}, - category => "highband", - }, - { - name => "ZM_WEB_H_SCALE_THUMBS", - default => "no", - description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", - help => "If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the browser to conserve bandwidth at the cost of cpu on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package.", - type => $types{boolean}, - category => "highband", - }, - { - name => "ZM_WEB_H_EVENTS_VIEW", - default => "events", - description => "What the default view of multiple events should be.", - help => "Stored events can be viewed in either an events list format or in a timeline based one. This option sets the default view that will be used. Choosing one view here does not prevent the other view being used as it will always be selectable from whichever view is currently being used.", - type => { db_type=>"string", hint=>"events|timeline", pattern=>qr|^([lt])|i, format=>q( $1 =~ /^e/ ? "events" : "timeline" ) }, - category => "highband", - }, - { - name => "ZM_WEB_H_SHOW_PROGRESS", - default => "yes", - description => "Show the progress of replay in event view.", - help => "When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to specific points in the event, but can can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on limited bandwidth connections may be out of step with the replay. This option allows you to turn off the progress display, whilst still keeping the navigation aspect, where bandwidth prevents it functioning effectively.", - type => $types{boolean}, - category => "highband", - }, - { - name => "ZM_WEB_H_AJAX_TIMEOUT", - default => "3000", - description => "How long to wait for Ajax request responses (ms)", - help => "The newer versions of the live feed and event views use Ajax to request information from the server and populate the views dynamically. This option allows you to specify a timeout if required after which requests are abandoned. A timeout may be necessary if requests would overwise hang such as on a slow connection. This would tend to consume a lot of browser memory and make the interface unresponsive. Ordinarily no requests should timeout so this setting should be set to a value greater than the slowest expected response. This value is in milliseconds but if set to zero then no timeout will be used.", - type => $types{integer}, - category => "highband", - }, - { - name => "ZM_WEB_M_REFRESH_MAIN", - default => "300", - description => "How often (in seconds) the main console window should refresh itself", - help => "The main console window lists a general status and the event totals for all monitors. This is not a trivial task and should not be repeated too frequently or it may affect the performance of the rest of the system.", - type => $types{integer}, - introduction => "The next few options control what happens when the client is running in 'medium' bandwidth mode. You should set these options for when accessing the ZoneMinder client over a slower cable or DSL link. In most cases the default values will be suitable as a starting point.", - category => "medband", - }, - { - name => "ZM_WEB_M_REFRESH_CYCLE", - default => "20", - description => "How often (in seconds) the cycle watch window swaps to the next monitor", - help => "The cycle watch window is a method of continuously cycling between images from all of your monitors. This option determines how often to refresh with a new image.", - type => $types{integer}, - category => "medband", - }, - { - name => "ZM_WEB_M_REFRESH_IMAGE", - default => "10", - description => "How often (in seconds) the watched image is refreshed (if not streaming)", - help => "The live images from a monitor can be viewed in either streamed or stills mode. This option determines how often a stills image is refreshed, it has no effect if streaming is selected.", - type => $types{integer}, - category => "medband", - }, - { - name => "ZM_WEB_M_REFRESH_STATUS", - default => "5", - description => "How often (in seconds) the status refreshes itself in the watch window", - help => "The monitor window is actually made from several frames. The one in the middle merely contains a monitor status which needs to refresh fairly frequently to give a true indication. This option determines that frequency.", - type => $types{integer}, - category => "medband", - }, - { - name => "ZM_WEB_M_REFRESH_EVENTS", - default => "60", - description => "How often (in seconds) the event listing is refreshed in the watch window", - help => "The monitor window is actually made from several frames. The lower framme contains a listing of the last few events for easy access. This option determines how often this is refreshed.", - type => $types{integer}, - category => "medband", - }, - { - name => "ZM_WEB_M_CAN_STREAM", - default => "auto", - description => "Override the automatic detection of browser streaming capability", - help => "If you know that your browser can handle image streams of the type 'multipart/x-mixed-replace' but ZoneMinder does not detect this correctly you can set this option to ensure that the stream is delivered with or without the use of the Cambozola plugin. Selecting 'yes' will tell ZoneMinder that your browser can handle the streams natively, 'no' means that it can't and so the plugin will be used while 'auto' lets ZoneMinder decide.", - type => $types{tristate}, - category => "medband", - }, - { - name => "ZM_WEB_M_STREAM_METHOD", - default => "jpeg", - description => "Which method should be used to send video streams to your browser.", - help => "ZoneMinder can be configured to use either mpeg encoded video or a series or still jpeg images when sending video streams. This option defines which is used. If you choose mpeg you should ensure that you have the appropriate plugins available on your browser whereas choosing jpeg will work natively on Mozilla and related browsers and with a Java applet on Internet Explorer", - type => { db_type=>"string", hint=>"mpeg|jpeg", pattern=>qr|^([mj])|i, format=>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) }, - category => "medband", - }, - { - name => "ZM_WEB_M_DEFAULT_SCALE", - default => "100", - description => "What the default scaling factor applied to 'live' or 'event' views is (%)", - help => "Normally ZoneMinder will display 'live' or 'event' streams in their native size. However if you have monitors with large dimensions or a slow link you may prefer to reduce this size, alternatively for small monitors you can enlarge it. This options lets you specify what the default scaling factor will be. It is expressed as a percentage so 100 is normal size, 200 is double size etc.", - type => { db_type=>"integer", hint=>"25|33|50|75|100|150|200|300|400", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "medband", - }, - { - name => "ZM_WEB_M_DEFAULT_RATE", - default => "100", - description => "What the default replay rate factor applied to 'event' views is (%)", - help => "Normally ZoneMinder will display 'event' streams at their native rate, i.e. as close to real-time as possible. However if you have long events it is often convenient to replay them at a faster rate for review. This option lets you specify what the default replay rate will be. It is expressed as a percentage so 100 is normal rate, 200 is double speed etc.", - type => { db_type=>"integer", hint=>"25|50|100|150|200|400|1000|2500|5000|10000", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "medband", - }, - { - name => "ZM_WEB_M_VIDEO_BITRATE", - default => "75000", - description => "What the bitrate of the video encoded stream should be set to", - help => "When encoding real video via the ffmpeg library a bit rate can be specified which roughly corresponds to the available bandwidth used for the stream. This setting effectively corresponds to a 'quality' setting for the video. A low value will result in a blocky image whereas a high value will produce a clearer view. Note that this setting does not control the frame rate of the video however the quality of the video produced is affected both by this setting and the frame rate that the video is produced at. A higher frame rate at a particular bit rate result in individual frames being at a lower quality.", - type => $types{integer}, - category => "medband", - }, - { - name => "ZM_WEB_M_VIDEO_MAXFPS", - default => "10", - description => "What the maximum frame rate for streamed video should be", - help => "When using streamed video the main control is the bitrate which determines how much data can be transmitted. However a lower bitrate at high frame rates results in a lower quality image. This option allows you to limit the maximum frame rate to ensure that video quality is maintained. An additional advantage is that encoding video at high frame rates is a processor intensive task when for the most part a very high frame rate offers little perceptible improvement over one that has a more manageable resource requirement. Note, this option is implemented as a cap beyond which binary reduction takes place. So if you have a device capturing at 15fps and set this option to 10fps then the video is not produced at 10fps, but rather at 7.5fps (15 divided by 2) as the final frame rate must be the original divided by a power of 2.", - type => $types{integer}, - category => "medband", - }, - { - name => "ZM_WEB_M_SCALE_THUMBS", - default => "yes", - description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", - help => "If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the browser to conserve bandwidth at the cost of cpu on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package.", - type => $types{boolean}, - category => "medband", - }, - { - name => "ZM_WEB_M_EVENTS_VIEW", - default => "events", - description => "What the default view of multiple events should be.", - help => "Stored events can be viewed in either an events list format or in a timeline based one. This option sets the default view that will be used. Choosing one view here does not prevent the other view being used as it will always be selectable from whichever view is currently being used.", - type => { db_type=>"string", hint=>"events|timeline", pattern=>qr|^([lt])|i, format=>q( $1 =~ /^e/ ? "events" : "timeline" ) }, - category => "medband", - }, - { - name => "ZM_WEB_M_SHOW_PROGRESS", - default => "yes", - description => "Show the progress of replay in event view.", - help => "When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to specific points in the event, but can can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on limited bandwidth connections may be out of step with the replay. This option allows you to turn off the progress display, whilst still keeping the navigation aspect, where bandwidth prevents it functioning effectively.", - type => $types{boolean}, - category => "medband", - }, - { - name => "ZM_WEB_M_AJAX_TIMEOUT", - default => "5000", - description => "How long to wait for Ajax request responses (ms)", - help => "The newer versions of the live feed and event views use Ajax to request information from the server and populate the views dynamically. This option allows you to specify a timeout if required after which requests are abandoned. A timeout may be necessary if requests would overwise hang such as on a slow connection. This would tend to consume a lot of browser memory and make the interface unresponsive. Ordinarily no requests should timeout so this setting should be set to a value greater than the slowest expected response. This value is in milliseconds but if set to zero then no timeout will be used.", - type => $types{integer}, - category => "medband", - }, - { - name => "ZM_WEB_L_REFRESH_MAIN", - default => "300", - description => "How often (in seconds) the main console window should refresh itself", - introduction => "The next few options control what happens when the client is running in 'low' bandwidth mode. You should set these options for when accessing the ZoneMinder client over a modem or slow link. In most cases the default values will be suitable as a starting point.", - help => "The main console window lists a general status and the event totals for all monitors. This is not a trivial task and should not be repeated too frequently or it may affect the performance of the rest of the system.", - type => $types{integer}, - category => "lowband", - }, - { - name => "ZM_WEB_L_REFRESH_CYCLE", - default => "30", - description => "How often (in seconds) the cycle watch window swaps to the next monitor", - help => "The cycle watch window is a method of continuously cycling between images from all of your monitors. This option determines how often to refresh with a new image.", - type => $types{integer}, - category => "lowband", - }, - { - name => "ZM_WEB_L_REFRESH_IMAGE", - default => "15", - description => "How often (in seconds) the watched image is refreshed (if not streaming)", - help => "The live images from a monitor can be viewed in either streamed or stills mode. This option determines how often a stills image is refreshed, it has no effect if streaming is selected.", - type => $types{integer}, - category => "lowband", - }, - { - name => "ZM_WEB_L_REFRESH_STATUS", - default => "10", - description => "How often (in seconds) the status refreshes itself in the watch window", - help => "The monitor window is actually made from several frames. The one in the middle merely contains a monitor status which needs to refresh fairly frequently to give a true indication. This option determines that frequency.", - type => $types{integer}, - category => "lowband", - }, - { - name => "ZM_WEB_L_REFRESH_EVENTS", - default => "180", - description => "How often (in seconds) the event listing is refreshed in the watch window", - help => "The monitor window is actually made from several frames. The lower framme contains a listing of the last few events for easy access. This option determines how often this is refreshed.", - type => $types{integer}, - category => "lowband", - }, - { - name => "ZM_WEB_L_CAN_STREAM", - default => "auto", - description => "Override the automatic detection of browser streaming capability", - help => "If you know that your browser can handle image streams of the type 'multipart/x-mixed-replace' but ZoneMinder does not detect this correctly you can set this option to ensure that the stream is delivered with or without the use of the Cambozola plugin. Selecting 'yes' will tell ZoneMinder that your browser can handle the streams natively, 'no' means that it can't and so the plugin will be used while 'auto' lets ZoneMinder decide.", - type => $types{tristate}, - category => "lowband", - }, - { - name => "ZM_WEB_L_STREAM_METHOD", - default => "jpeg", - description => "Which method should be used to send video streams to your browser.", - help => "ZoneMinder can be configured to use either mpeg encoded video or a series or still jpeg images when sending video streams. This option defines which is used. If you choose mpeg you should ensure that you have the appropriate plugins available on your browser whereas choosing jpeg will work natively on Mozilla and related browsers and with a Java applet on Internet Explorer", - type => { db_type=>"string", hint=>"mpeg|jpeg", pattern=>qr|^([mj])|i, format=>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) }, - category => "lowband", - }, - { - name => "ZM_WEB_L_DEFAULT_SCALE", - default => "100", - description => "What the default scaling factor applied to 'live' or 'event' views is (%)", - help => "Normally ZoneMinder will display 'live' or 'event' streams in their native size. However if you have monitors with large dimensions or a slow link you may prefer to reduce this size, alternatively for small monitors you can enlarge it. This options lets you specify what the default scaling factor will be. It is expressed as a percentage so 100 is normal size, 200 is double size etc.", - type => { db_type=>"integer", hint=>"25|33|50|75|100|150|200|300|400", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "lowband", - }, - { - name => "ZM_WEB_L_DEFAULT_RATE", - default => "100", - description => "What the default replay rate factor applied to 'event' views is (%)", - help => "Normally ZoneMinder will display 'event' streams at their native rate, i.e. as close to real-time as possible. However if you have long events it is often convenient to replay them at a faster rate for review. This option lets you specify what the default replay rate will be. It is expressed as a percentage so 100 is normal rate, 200 is double speed etc.", - type => { db_type=>"integer", hint=>"25|50|100|150|200|400|1000|2500|5000|10000", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "lowband", - }, - { - name => "ZM_WEB_L_VIDEO_BITRATE", - default => "25000", - description => "What the bitrate of the video encoded stream should be set to", - help => "When encoding real video via the ffmpeg library a bit rate can be specified which roughly corresponds to the available bandwidth used for the stream. This setting effectively corresponds to a 'quality' setting for the video. A low value will result in a blocky image whereas a high value will produce a clearer view. Note that this setting does not control the frame rate of the video however the quality of the video produced is affected both by this setting and the frame rate that the video is produced at. A higher frame rate at a particular bit rate result in individual frames being at a lower quality.", - type => $types{integer}, - category => "lowband", - }, - { - name => "ZM_WEB_L_VIDEO_MAXFPS", - default => "5", - description => "What the maximum frame rate for streamed video should be", - help => "When using streamed video the main control is the bitrate which determines how much data can be transmitted. However a lower bitrate at high frame rates results in a lower quality image. This option allows you to limit the maximum frame rate to ensure that video quality is maintained. An additional advantage is that encoding video at high frame rates is a processor intensive task when for the most part a very high frame rate offers little perceptible improvement over one that has a more manageable resource requirement. Note, this option is implemented as a cap beyond which binary reduction takes place. So if you have a device capturing at 15fps and set this option to 10fps then the video is not produced at 10fps, but rather at 7.5fps (15 divided by 2) as the final frame rate must be the original divided by a power of 2.", - type => $types{integer}, - category => "lowband", - }, - { - name => "ZM_WEB_L_SCALE_THUMBS", - default => "yes", - description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", - help => "If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the browser to conserve bandwidth at the cost of cpu on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package.", - type => $types{boolean}, - category => "lowband", - }, - { - name => "ZM_WEB_L_EVENTS_VIEW", - default => "events", - description => "What the default view of multiple events should be.", - help => "Stored events can be viewed in either an events list format or in a timeline based one. This option sets the default view that will be used. Choosing one view here does not prevent the other view being used as it will always be selectable from whichever view is currently being used.", - type => { db_type=>"string", hint=>"events|timeline", pattern=>qr|^([lt])|i, format=>q( $1 =~ /^e/ ? "events" : "timeline" ) }, - category => "lowband", - }, - { - name => "ZM_WEB_L_SHOW_PROGRESS", - default => "no", - description => "Show the progress of replay in event view.", - help => "When viewing events an event navigation panel and progress bar is shown below the event itself. This allows you to jump to specific points in the event, but can can also dynamically update to display the current progress of the event replay itself. This progress is calculated from the actual event duration and is not directly linked to the replay itself, so on limited bandwidth connections may be out of step with the replay. This option allows you to turn off the progress display, whilst still keeping the navigation aspect, where bandwidth prevents it functioning effectively.", - type => $types{boolean}, - category => "lowband", - }, - { - name => "ZM_WEB_L_AJAX_TIMEOUT", - default => "10000", - description => "How long to wait for Ajax request responses (ms)", - help => "The newer versions of the live feed and event views use Ajax to request information from the server and populate the views dynamically. This option allows you to specify a timeout if required after which requests are abandoned. A timeout may be necessary if requests would overwise hang such as on a slow connection. This would tend to consume a lot of browser memory and make the interface unresponsive. Ordinarily no requests should timeout so this setting should be set to a value greater than the slowest expected response. This value is in milliseconds but if set to zero then no timeout will be used.", - type => $types{integer}, - category => "lowband", - }, - { - name => "ZM_WEB_P_CAN_STREAM", - default => "auto", - description => "Override the automatic detection of browser streaming capability", - help => "If you know that your browser can handle image streams of the type 'multipart/x-mixed-replace' but ZoneMinder does not detect this correctly you can set this option to ensure that the stream is delivered with or without the use of the Cambozola plugin. Selecting 'yes' will tell ZoneMinder that your browser can handle the streams natively, 'no' means that it can't and so the plugin will be used while 'auto' lets ZoneMinder decide.", - type => $types{tristate}, - category => "phoneband", - }, - { - name => "ZM_WEB_P_STREAM_METHOD", - default => "jpeg", - description => "Which method should be used to send video streams to your browser.", - help => "ZoneMinder can be configured to use either mpeg encoded video or a series or still jpeg images when sending video streams. This option defines which is used. If you choose mpeg you should ensure that you have the appropriate plugins available on your browser whereas choosing jpeg will work natively on Mozilla and related browsers and with a Java applet on Internet Explorer", - type => { db_type=>"string", hint=>"mpeg|jpeg", pattern=>qr|^([mj])|i, format=>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) }, - category => "phoneband", - }, - { - name => "ZM_WEB_P_DEFAULT_SCALE", - default => "100", - description => "What the default scaling factor applied to 'live' or 'event' views is (%)", - help => "Normally ZoneMinder will display 'live' or 'event' streams in their native size. However if you have monitors with large dimensions or a slow link you may prefer to reduce this size, alternatively for small monitors you can enlarge it. This options lets you specify what the default scaling factor will be. It is expressed as a percentage so 100 is normal size, 200 is double size etc.", - type => { db_type=>"integer", hint=>"25|33|50|75|100|150|200|300|400", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "phoneband", - }, - { - name => "ZM_WEB_P_DEFAULT_RATE", - default => "100", - description => "What the default replay rate factor applied to 'event' views is (%)", - help => "Normally ZoneMinder will display 'event' streams at their native rate, i.e. as close to real-time as possible. However if you have long events it is often convenient to replay them at a faster rate for review. This option lets you specify what the default replay rate will be. It is expressed as a percentage so 100 is normal rate, 200 is double speed etc.", - type => { db_type=>"integer", hint=>"25|50|100|150|200|400|1000|2500|5000|10000", pattern=>qr|^(\d+)$|, format=>q( $1 ) }, - category => "phoneband", - }, - { - name => "ZM_WEB_P_VIDEO_BITRATE", - default => "8000", - description => "What the bitrate of the video encoded stream should be set to", - help => "When encoding real video via the ffmpeg library a bit rate can be specified which roughly corresponds to the available bandwidth used for the stream. This setting effectively corresponds to a 'quality' setting for the video. A low value will result in a blocky image whereas a high value will produce a clearer view. Note that this setting does not control the frame rate of the video however the quality of the video produced is affected both by this setting and the frame rate that the video is produced at. A higher frame rate at a particular bit rate result in individual frames being at a lower quality.", - type => $types{integer}, - category => "phoneband", - }, - { - name => "ZM_WEB_P_VIDEO_MAXFPS", - default => "5", - description => "What the maximum frame rate for streamed video should be", - help => "When using streamed video the main control is the bitrate which determines how much data can be transmitted. However a lower bitrate at high frame rates results in a lower quality image. This option allows you to limit the maximum frame rate to ensure that video quality is maintained. An additional advantage is that encoding video at high frame rates is a processor intensive task when for the most part a very high frame rate offers little perceptible improvement over one that has a more manageable resource requirement. Note, this option is implemented as a cap beyond which binary reduction takes place. So if you have a device capturing at 15fps and set this option to 10fps then the video is not produced at 10fps, but rather at 7.5fps (15 divided by 2) as the final frame rate must be the original divided by a power of 2.", - type => $types{integer}, - category => "phoneband", - }, - { - name => "ZM_WEB_P_SCALE_THUMBS", - default => "yes", - description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", - help => "If unset, this option sends the whole image to the browser which resizes it in the window. If set the image is scaled down on the server before sending a reduced size image to the browser to conserve bandwidth at the cost of cpu on the server. Note that ZM can only perform the resizing if the appropriate PHP graphics functionality is installed. This is usually available in the php-gd package.", - type => $types{boolean}, - category => "phoneband", - }, - { - name => "ZM_WEB_P_AJAX_TIMEOUT", - default => "10000", - description => "How long to wait for Ajax request responses (ms)", - help => "The newer versions of the live feed and event views use Ajax to request information from the server and populate the views dynamically. This option allows you to specify a timeout if required after which requests are abandoned. A timeout may be necessary if requests would overwise hang such as on a slow connection. This would tend to consume a lot of browser memory and make the interface unresponsive. Ordinarily no requests should timeout so this setting should be set to a value greater than the slowest expected response. This value is in milliseconds but if set to zero then no timeout will be used.", - type => $types{integer}, - category => "phoneband", - }, - { - name => "ZM_DYN_LAST_VERSION", - default => "", - description => "What the last version of ZoneMinder recorded from zoneminder.com is", - help => "", - type => $types{string}, - readonly => 1, - category => "dynamic", - }, - { - name => "ZM_DYN_CURR_VERSION", - default => "@VERSION@", - description => "What the effective current version of ZoneMinder is, might be different from actual if versions ignored", - help => "", - type => $types{string}, - readonly => 1, - category => "dynamic", - }, - { - name => "ZM_DYN_DB_VERSION", - default => "@VERSION@", - description => "What the version of the database is, from zmupdate", - help => "", - type => $types{string}, - readonly => 1, - category => "dynamic", - }, - { - name => "ZM_DYN_LAST_CHECK", - default => "", - description => "When the last check for version from zoneminder.com was", - help => "", - type => $types{integer}, - readonly => 1, - category => "dynamic", - }, - { - name => "ZM_DYN_NEXT_REMINDER", - default => "", - description => "When the earliest time to remind about versions will be", - help => "", - type => $types{string}, - readonly => 1, - category => "dynamic", - }, - { - name => "ZM_DYN_DONATE_REMINDER_TIME", - default => 0, - description => "When the earliest time to remind about donations will be", - help => "", - type => $types{integer}, - readonly => 1, - category => "dynamic", - }, - { - name => "ZM_DYN_SHOW_DONATE_REMINDER", - default => "yes", - description => "Remind about donations or not", - help => "", - type => $types{boolean}, - readonly => 1, - category => "dynamic", - }, - { - name => "ZM_EYEZM_DEBUG", - default => "no", - description => "Switch additional debugging on for eyeZm Plugin", - help => "Enable or Disable extra debugging from the eyeZm Plugin. Extra debugging information will be displayed in it's own file (EYEZM_LOG_TO_FILE is set), or your Apache error log", - type => $types{boolean}, - category => "eyeZm", - }, - { - name => "ZM_EYEZM_LOG_TO_FILE", - default => "yes", - description => "When eyeZm Debugging is enabled, enabling this logs output to it's own file", - help => "When EYEZM_DEBUG is on and EYEZM_LOG_TO_FILE is on, output generated from the eyeZm Plugin will go to it's own file. Otherwise it will go to the apache error log.", - type => $types{boolean}, - category => "eyeZm", - }, - { - name => "ZM_EYEZM_LOG_FILE", - default => "@ZM_LOGDIR@/zm_xml.log", - description => "Default filename to use when logging eyeZm Output and EYEZM_LOG_TO_FILE is enabled", - help => "This file will contain it's own output from the eyeZm Plugin when EYEZM_LOG_TO_FILE and EYEZM_DEBUG are both enabled", - type => $types{string}, - category => "eyeZm", - }, - { - name => "ZM_EYEZM_EVENT_VCODEC", - default => "mpeg4", - description => "Default video-codec to use for encoding events", - help => "The eyeZm Plugin calls FFMPEG externally to encode the captured images. If your FFMPEG is not built with support for H264, change this to MPEG-4. If using H264, please check http://www.eyezm.com for H264 requirements and that your eyeZm version supports H264 (v1.2+).", - type => { db_type=>"string", hint=>"mpeg4|h264", pattern=>qr|^([mh])|i, format=>q( $1 =~ /^m/ ? "mpeg4" : "h264" ) }, - category => "eyeZm", - }, - { - name => "ZM_EYEZM_FEED_VCODEC", - default => "mjpeg", - description => "Default video-codec to use for streaming the live feed", - help => "Determines whether the live stream is generated using native MJPEG streaming with ZoneMinder, or H264 using FFMPEG and HTML-5 streaming. If using H264, please check http://www.eyezm.com for H264 requirements and that your eyeZm version supports H264 (v1.2+). This is just a default parameter, and can be overridden with eyeZm.", - type => { db_type=>"string", hint=>"mjpeg|h264", pattern=>qr|^([mh])|i, format=>q( $1 =~ /^m/ ? "mjpeg" : "h264" ) }, - category => "eyeZm", - }, - { - name => "ZM_EYEZM_H264_DEFAULT_BR", - default => "96k", - description => "Default bit-rate to use with FFMPEG for H264 streaming", - help => "When using the eyeZm Plugin to stream H264 data, FFMPEG requires a bitrate to control the quality and bandwidth of the video. This should be specified in a format acceptable to FFMPEG. The default value is sufficient for most installations. This is just a default parameter, and can be overridden with eyeZm.", - type => $types{string}, - category => "eyeZm", - }, - { - name => "ZM_EYEZM_H264_DEFAULT_EVBR", - default => "128k", - description => "Default bit-rate to use with FFMPEG for H264 event viewing", - help => "When using the eyeZm Plugin to view events in H264, FFMPEG requires a bitrate to control the quality and bandwidth of the video. This should be specified in a format acceptable to FFMPEG. The default value is sufficient for most installations. This is just a default parameter, and can be overridden with eyeZm.", - type => $types{string}, - category => "eyeZm", - }, - { - name => "ZM_EYEZM_H264_TIMEOUT", - default => "20", - description => "Timeout (sec) to wait for H264 stream to start before terminating", - help => "The eyeZm Plugin will attempt to spawn an H264 stream when requested, and require that it complete within the timeout specified. If you have a slow system or find through the logs that the H264 stream is not starting because the timeout is expiring, even though FFMPEG is running, try increasing this value. If you have a fast system, decreasing this value can improve the responsiveness when there are issues starting H264 streams", - type => $types{string}, - category => "eyeZm", - }, - { - name => "ZM_EYEZM_SEG_DURATION", - default => "3", - description => "Segment duration used for streaming using HTTP-5 Streaming protocol", - help => "The HTTP-5 Live Streaming Protocol segments the input video stream into small chunks of a duration specified by this parameter. Increasing the segment duration will help with choppy connections on the other end, but will increase the latency in starting a stream.", - type => $types{string}, - category => "eyeZm", - }, + { + name => "ZM_WEB_REFRESH_METHOD", + default => "javascript", + description => "What method windows should use to refresh themselves", + help => qqq(" + Many windows in Javascript need to refresh themselves to keep + their information current. This option determines what method + they should use to do this. Choosing 'javascript' means that + each window will have a short JavaScript statement in with a + timer to prompt the refresh. This is the most compatible + method. Choosing 'http' means the refresh instruction is put in + the HTTP header. This is a cleaner method but refreshes are + interrupted or cancelled when a link in the window is clicked + meaning that the window will no longer refresh and this would + have to be done manually. + "), + type => { + db_type =>"string", + hint =>"javascript|http", + pattern =>qr|^([jh])|i, + format =>q( $1 =~ /^j/ + ? "javascript" + : "http" + ) + }, + category => "hidden", + }, + { + name => "ZM_WEB_EVENT_SORT_FIELD", + default => "DateTime", + description => "Default field the event lists are sorted by", + help => qqq(" + Events in lists can be initially ordered in any way you want. + This option controls what field is used to sort them. You can + modify this ordering from filters or by clicking on headings in + the lists themselves. Bear in mind however that the 'Prev' and + 'Next' links, when scrolling through events, relate to the + ordering in the lists and so not always to time based ordering. + "), + type => { + db_type =>"string", + hint =>"Id|Name|Cause|MonitorName|DateTime|Length|Frames|AlarmFrames|TotScore|AvgScore|MaxScore", + pattern =>qr|.|, + format =>q( $1 ) + }, + category => "web", + }, + { + name => "ZM_WEB_EVENT_SORT_ORDER", + default => "asc", + description => "Default order the event lists are sorted by", + help => qqq(" + Events in lists can be initially ordered in any way you want. + This option controls what order (ascending or descending) is + used to sort them. You can modify this ordering from filters or + by clicking on headings in the lists themselves. Bear in mind + however that the 'Prev' and 'Next' links, when scrolling + through events, relate to the ordering in the lists and so not + always to time based ordering. + "), + type => { + db_type =>"string", + hint =>"asc|desc", + pattern =>qr|^([ad])|i, + format =>q( $1 =~ /^a/i ? "asc" : "desc" ) + }, + category => "web", + }, + { + name => "ZM_WEB_EVENTS_PER_PAGE", + default => "25", + description => "How many events to list per page in paged mode", + help => qqq(" + In the event list view you can either list all events or just a + page at a time. This option controls how many events are listed + per page in paged mode and how often to repeat the column + headers in non-paged mode. + "), + type => $types{integer}, + category => "web", + }, + { + name => "ZM_WEB_LIST_THUMBS", + default => "no", + description => "Display mini-thumbnails of event images in event lists", + help => qqq(" + Ordinarily the event lists just display text details of the + events to save space and time. By switching this option on you + can also display small thumbnails to help you identify events + of interest. The size of these thumbnails is controlled by the + following two options. + "), + type => $types{boolean}, + category => "web", + }, + { + name => "ZM_WEB_LIST_THUMB_WIDTH", + default => "48", + description => "The width of the thumbnails that appear in the event lists", + help => qqq(" + This options controls the width of the thumbnail images that + appear in the event lists. It should be fairly small to fit in + with the rest of the table. If you prefer you can specify a + height instead in the next option but you should only use one + of the width or height and the other option should be set to + zero. If both width and height are specified then width will be + used and height ignored. + "), + type => $types{integer}, + requires => [ { name => "ZM_WEB_LIST_THUMBS", value => "yes" } ], + category => "web", + }, + { + name => "ZM_WEB_LIST_THUMB_HEIGHT", + default => "0", + description => "The height of the thumbnails that appear in the event lists", + help => qqq(" + This options controls the height of the thumbnail images that + appear in the event lists. It should be fairly small to fit in + with the rest of the table. If you prefer you can specify a + width instead in the previous option but you should only use + one of the width or height and the other option should be set + to zero. If both width and height are specified then width will + be used and height ignored. + "), + type => $types{integer}, + requires => [ { name => "ZM_WEB_LIST_THUMBS", value => "yes" } ], + category => "web", + }, + { + name => "ZM_WEB_USE_OBJECT_TAGS", + default => "yes", + description => "Wrap embed in object tags for media content", + help => qqq(" + There are two methods of including media content in web pages. + The most common way is use the EMBED tag which is able to give + some indication of the type of content. However this is not a + standard part of HTML. The official method is to use OBJECT + tags which are able to give more information allowing the + correct media viewers etc to be loaded. However these are less + widely supported and content may be specifically tailored to a + particular platform or player. This option controls whether + media content is enclosed in EMBED tags only or whether, where + appropriate, it is additionally wrapped in OBJECT tags. + Currently OBJECT tags are only used in a limited number of + circumstances but they may become more widespread in the + future. It is suggested that you leave this option on unless + you encounter problems playing some content. + "), + type => $types{boolean}, + category => "web", + }, + { + name => "ZM_WEB_H_REFRESH_MAIN", + default => "60", + introduction => qqq(" + There are now a number of options that are grouped into + bandwidth categories, this allows you to configure the + ZoneMinder client to work optimally over the various access + methods you might to access the client.\n\nThe next few options + control what happens when the client is running in 'high' + bandwidth mode. You should set these options for when accessing + the ZoneMinder client over a local network or high speed link. + In most cases the default values will be suitable as a starting + point. + "), + description => "How often (in seconds) the main console window should refresh itself", + help => qqq(" + The main console window lists a general status and the event + totals for all monitors. This is not a trivial task and should + not be repeated too frequently or it may affect the performance + of the rest of the system. + "), + type => $types{integer}, + category => "highband", + }, + { + name => "ZM_WEB_H_REFRESH_CYCLE", + default => "10", + description => "How often (in seconds) the cycle watch window swaps to the next monitor", + help => qqq(" + The cycle watch window is a method of continuously cycling + between images from all of your monitors. This option + determines how often to refresh with a new image. + "), + type => $types{integer}, + category => "highband", + }, + { + name => "ZM_WEB_H_REFRESH_IMAGE", + default => "3", + description => "How often (in seconds) the watched image is refreshed (if not streaming)", + help => qqq(" + The live images from a monitor can be viewed in either streamed + or stills mode. This option determines how often a stills image + is refreshed, it has no effect if streaming is selected. + "), + type => $types{integer}, + category => "highband", + }, + { + name => "ZM_WEB_H_REFRESH_STATUS", + default => "1", + description => "How often (in seconds) the status refreshes itself in the watch window", + help => qqq(" + The monitor window is actually made from several frames. The + one in the middle merely contains a monitor status which needs + to refresh fairly frequently to give a true indication. This + option determines that frequency. + "), + type => $types{integer}, + category => "highband", + }, + { + name => "ZM_WEB_H_REFRESH_EVENTS", + default => "5", + description => "How often (in seconds) the event listing is refreshed in the watch window", + help => qqq(" + The monitor window is actually made from several frames. The + lower framme contains a listing of the last few events for easy + access. This option determines how often this is refreshed. + "), + type => $types{integer}, + category => "highband", + }, + { + name => "ZM_WEB_H_CAN_STREAM", + default => "auto", + description => "Override the automatic detection of browser streaming capability", + help => qqq(" + If you know that your browser can handle image streams of the + type 'multipart/x-mixed-replace' but ZoneMinder does not detect + this correctly you can set this option to ensure that the + stream is delivered with or without the use of the Cambozola + plugin. Selecting 'yes' will tell ZoneMinder that your browser + can handle the streams natively, 'no' means that it can't and + so the plugin will be used while 'auto' lets ZoneMinder decide. + "), + type => $types{tristate}, + category => "highband", + }, + { + name => "ZM_WEB_H_STREAM_METHOD", + default => "jpeg", + description => "Which method should be used to send video streams to your browser.", + help => qqq(" + ZoneMinder can be configured to use either mpeg encoded video + or a series or still jpeg images when sending video streams. + This option defines which is used. If you choose mpeg you + should ensure that you have the appropriate plugins available + on your browser whereas choosing jpeg will work natively on + Mozilla and related browsers and with a Java applet on Internet + Explorer + "), + type => { + db_type =>"string", + hint =>"mpeg|jpeg", + pattern =>qr|^([mj])|i, + format =>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) + }, + category => "highband", + }, + { + name => "ZM_WEB_H_DEFAULT_SCALE", + default => "100", + description => "What the default scaling factor applied to 'live' or 'event' views is (%)", + help => qqq(" + Normally ZoneMinder will display 'live' or 'event' streams in + their native size. However if you have monitors with large + dimensions or a slow link you may prefer to reduce this size, + alternatively for small monitors you can enlarge it. This + options lets you specify what the default scaling factor will + be. It is expressed as a percentage so 100 is normal size, 200 + is double size etc. + "), + type => { + db_type =>"integer", + hint =>"25|33|50|75|100|150|200|300|400", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "highband", + }, + { + name => "ZM_WEB_H_DEFAULT_RATE", + default => "100", + description => "What the default replay rate factor applied to 'event' views is (%)", + help => qqq(" + Normally ZoneMinder will display 'event' streams at their + native rate, i.e. as close to real-time as possible. However if + you have long events it is often convenient to replay them at a + faster rate for review. This option lets you specify what the + default replay rate will be. It is expressed as a percentage so + 100 is normal rate, 200 is double speed etc. + "), + type => { + db_type =>"integer", + hint =>"25|50|100|150|200|400|1000|2500|5000|10000", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "highband", + }, + { + name => "ZM_WEB_H_VIDEO_BITRATE", + default => "150000", + description => "What the bitrate of the video encoded stream should be set to", + help => qqq(" + When encoding real video via the ffmpeg library a bit rate can + be specified which roughly corresponds to the available + bandwidth used for the stream. This setting effectively + corresponds to a 'quality' setting for the video. A low value + will result in a blocky image whereas a high value will produce + a clearer view. Note that this setting does not control the + frame rate of the video however the quality of the video + produced is affected both by this setting and the frame rate + that the video is produced at. A higher frame rate at a + particular bit rate result in individual frames being at a + lower quality. + "), + type => $types{integer}, + category => "highband", + }, + { + name => "ZM_WEB_H_VIDEO_MAXFPS", + default => "30", + description => "What the maximum frame rate for streamed video should be", + help => qqq(" + When using streamed video the main control is the bitrate which + determines how much data can be transmitted. However a lower + bitrate at high frame rates results in a lower quality image. + This option allows you to limit the maximum frame rate to + ensure that video quality is maintained. An additional + advantage is that encoding video at high frame rates is a + processor intensive task when for the most part a very high + frame rate offers little perceptible improvement over one that + has a more manageable resource requirement. Note, this option + is implemented as a cap beyond which binary reduction takes + place. So if you have a device capturing at 15fps and set this + option to 10fps then the video is not produced at 10fps, but + rather at 7.5fps (15 divided by 2) as the final frame rate must + be the original divided by a power of 2. + "), + type => $types{integer}, + category => "highband", + }, + { + name => "ZM_WEB_H_SCALE_THUMBS", + default => "no", + description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", + help => qqq(" + If unset, this option sends the whole image to the browser + which resizes it in the window. If set the image is scaled down + on the server before sending a reduced size image to the + browser to conserve bandwidth at the cost of cpu on the server. + Note that ZM can only perform the resizing if the appropriate + PHP graphics functionality is installed. This is usually + available in the php-gd package. + "), + type => $types{boolean}, + category => "highband", + }, + { + name => "ZM_WEB_H_EVENTS_VIEW", + default => "events", + description => "What the default view of multiple events should be.", + help => qqq(" + Stored events can be viewed in either an events list format or + in a timeline based one. This option sets the default view that + will be used. Choosing one view here does not prevent the other + view being used as it will always be selectable from whichever + view is currently being used. + "), + type => { + db_type =>"string", + hint =>"events|timeline", + pattern =>qr|^([lt])|i, + format =>q( $1 =~ /^e/ ? "events" : "timeline" ) + }, + category => "highband", + }, + { + name => "ZM_WEB_H_SHOW_PROGRESS", + default => "yes", + description => "Show the progress of replay in event view.", + help => qqq(" + When viewing events an event navigation panel and progress bar + is shown below the event itself. This allows you to jump to + specific points in the event, but can can also dynamically + update to display the current progress of the event replay + itself. This progress is calculated from the actual event + duration and is not directly linked to the replay itself, so on + limited bandwidth connections may be out of step with the + replay. This option allows you to turn off the progress + display, whilst still keeping the navigation aspect, where + bandwidth prevents it functioning effectively. + "), + type => $types{boolean}, + category => "highband", + }, + { + name => "ZM_WEB_H_AJAX_TIMEOUT", + default => "3000", + description => "How long to wait for Ajax request responses (ms)", + help => qqq(" + The newer versions of the live feed and event views use Ajax to + request information from the server and populate the views + dynamically. This option allows you to specify a timeout if + required after which requests are abandoned. A timeout may be + necessary if requests would overwise hang such as on a slow + connection. This would tend to consume a lot of browser memory + and make the interface unresponsive. Ordinarily no requests + should timeout so this setting should be set to a value greater + than the slowest expected response. This value is in + milliseconds but if set to zero then no timeout will be used. + "), + type => $types{integer}, + category => "highband", + }, + { + name => "ZM_WEB_M_REFRESH_MAIN", + default => "300", + description => "How often (in seconds) the main console window should refresh itself", + help => qqq(" + The main console window lists a general status and the event + totals for all monitors. This is not a trivial task and should + not be repeated too frequently or it may affect the performance + of the rest of the system. + "), + type => $types{integer}, + introduction => qqq(" + The next few options control what happens when the client is + running in 'medium' bandwidth mode. You should set these + options for when accessing the ZoneMinder client over a slower + cable or DSL link. In most cases the default values will be + suitable as a starting point. + "), + category => "medband", + }, + { + name => "ZM_WEB_M_REFRESH_CYCLE", + default => "20", + description => "How often (in seconds) the cycle watch window swaps to the next monitor", + help => qqq(" + The cycle watch window is a method of continuously cycling + between images from all of your monitors. This option + determines how often to refresh with a new image. + "), + type => $types{integer}, + category => "medband", + }, + { + name => "ZM_WEB_M_REFRESH_IMAGE", + default => "10", + description => "How often (in seconds) the watched image is refreshed (if not streaming)", + help => qqq(" + The live images from a monitor can be viewed in either streamed + or stills mode. This option determines how often a stills image + is refreshed, it has no effect if streaming is selected. + "), + type => $types{integer}, + category => "medband", + }, + { + name => "ZM_WEB_M_REFRESH_STATUS", + default => "5", + description => "How often (in seconds) the status refreshes itself in the watch window", + help => qqq(" + The monitor window is actually made from several frames. The + one in the middle merely contains a monitor status which needs + to refresh fairly frequently to give a true indication. This + option determines that frequency. + "), + type => $types{integer}, + category => "medband", + }, + { + name => "ZM_WEB_M_REFRESH_EVENTS", + default => "60", + description => "How often (in seconds) the event listing is refreshed in the watch window", + help => qqq(" + The monitor window is actually made from several frames. The + lower framme contains a listing of the last few events for easy + access. This option determines how often this is refreshed. + "), + type => $types{integer}, + category => "medband", + }, + { + name => "ZM_WEB_M_CAN_STREAM", + default => "auto", + description => "Override the automatic detection of browser streaming capability", + help => qqq(" + If you know that your browser can handle image streams of the + type 'multipart/x-mixed-replace' but ZoneMinder does not detect + this correctly you can set this option to ensure that the + stream is delivered with or without the use of the Cambozola + plugin. Selecting 'yes' will tell ZoneMinder that your browser + can handle the streams natively, 'no' means that it can't and + so the plugin will be used while 'auto' lets ZoneMinder decide. + "), + type => $types{tristate}, + category => "medband", + }, + { + name => "ZM_WEB_M_STREAM_METHOD", + default => "jpeg", + description => "Which method should be used to send video streams to your browser.", + help => qqq(" + ZoneMinder can be configured to use either mpeg encoded video + or a series or still jpeg images when sending video streams. + This option defines which is used. If you choose mpeg you + should ensure that you have the appropriate plugins available + on your browser whereas choosing jpeg will work natively on + Mozilla and related browsers and with a Java applet on Internet + Explorer + "), + type => { + db_type =>"string", + hint =>"mpeg|jpeg", + pattern =>qr|^([mj])|i, + format =>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) + }, + category => "medband", + }, + { + name => "ZM_WEB_M_DEFAULT_SCALE", + default => "100", + description => "What the default scaling factor applied to 'live' or 'event' views is (%)", + help => qqq(" + Normally ZoneMinder will display 'live' or 'event' streams in + their native size. However if you have monitors with large + dimensions or a slow link you may prefer to reduce this size, + alternatively for small monitors you can enlarge it. This + options lets you specify what the default scaling factor will + be. It is expressed as a percentage so 100 is normal size, 200 + is double size etc. + "), + type => { + db_type =>"integer", + hint =>"25|33|50|75|100|150|200|300|400", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "medband", + }, + { + name => "ZM_WEB_M_DEFAULT_RATE", + default => "100", + description => "What the default replay rate factor applied to 'event' views is (%)", + help => qqq(" + Normally ZoneMinder will display 'event' streams at their + native rate, i.e. as close to real-time as possible. However if + you have long events it is often convenient to replay them at a + faster rate for review. This option lets you specify what the + default replay rate will be. It is expressed as a percentage so + 100 is normal rate, 200 is double speed etc. + "), + type => { + db_type =>"integer", + hint =>"25|50|100|150|200|400|1000|2500|5000|10000", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "medband", + }, + { + name => "ZM_WEB_M_VIDEO_BITRATE", + default => "75000", + description => "What the bitrate of the video encoded stream should be set to", + help => qqq(" + When encoding real video via the ffmpeg library a bit rate can + be specified which roughly corresponds to the available + bandwidth used for the stream. This setting effectively + corresponds to a 'quality' setting for the video. A low value + will result in a blocky image whereas a high value will produce + a clearer view. Note that this setting does not control the + frame rate of the video however the quality of the video + produced is affected both by this setting and the frame rate + that the video is produced at. A higher frame rate at a + particular bit rate result in individual frames being at a + lower quality. + "), + type => $types{integer}, + category => "medband", + }, + { + name => "ZM_WEB_M_VIDEO_MAXFPS", + default => "10", + description => "What the maximum frame rate for streamed video should be", + help => qqq(" + When using streamed video the main control is the bitrate which + determines how much data can be transmitted. However a lower + bitrate at high frame rates results in a lower quality image. + This option allows you to limit the maximum frame rate to + ensure that video quality is maintained. An additional + advantage is that encoding video at high frame rates is a + processor intensive task when for the most part a very high + frame rate offers little perceptible improvement over one that + has a more manageable resource requirement. Note, this option + is implemented as a cap beyond which binary reduction takes + place. So if you have a device capturing at 15fps and set this + option to 10fps then the video is not produced at 10fps, but + rather at 7.5fps (15 divided by 2) as the final frame rate must + be the original divided by a power of 2. + "), + type => $types{integer}, + category => "medband", + }, + { + name => "ZM_WEB_M_SCALE_THUMBS", + default => "yes", + description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", + help => qqq(" + If unset, this option sends the whole image to the browser + which resizes it in the window. If set the image is scaled down + on the server before sending a reduced size image to the + browser to conserve bandwidth at the cost of cpu on the server. + Note that ZM can only perform the resizing if the appropriate + PHP graphics functionality is installed. This is usually + available in the php-gd package. + "), + type => $types{boolean}, + category => "medband", + }, + { + name => "ZM_WEB_M_EVENTS_VIEW", + default => "events", + description => "What the default view of multiple events should be.", + help => qqq(" + Stored events can be viewed in either an events list format or + in a timeline based one. This option sets the default view that + will be used. Choosing one view here does not prevent the other + view being used as it will always be selectable from whichever + view is currently being used. + "), + type => { + db_type =>"string", + hint =>"events|timeline", + pattern =>qr|^([lt])|i, + format =>q( $1 =~ /^e/ ? "events" : "timeline" ) + }, + category => "medband", + }, + { + name => "ZM_WEB_M_SHOW_PROGRESS", + default => "yes", + description => "Show the progress of replay in event view.", + help => qqq(" + When viewing events an event navigation panel and progress bar + is shown below the event itself. This allows you to jump to + specific points in the event, but can can also dynamically + update to display the current progress of the event replay + itself. This progress is calculated from the actual event + duration and is not directly linked to the replay itself, so on + limited bandwidth connections may be out of step with the + replay. This option allows you to turn off the progress + display, whilst still keeping the navigation aspect, where + bandwidth prevents it functioning effectively. + "), + type => $types{boolean}, + category => "medband", + }, + { + name => "ZM_WEB_M_AJAX_TIMEOUT", + default => "5000", + description => "How long to wait for Ajax request responses (ms)", + help => qqq(" + The newer versions of the live feed and event views use Ajax to + request information from the server and populate the views + dynamically. This option allows you to specify a timeout if + required after which requests are abandoned. A timeout may be + necessary if requests would overwise hang such as on a slow + connection. This would tend to consume a lot of browser memory + and make the interface unresponsive. Ordinarily no requests + should timeout so this setting should be set to a value greater + than the slowest expected response. This value is in + milliseconds but if set to zero then no timeout will be used. + "), + type => $types{integer}, + category => "medband", + }, + { + name => "ZM_WEB_L_REFRESH_MAIN", + default => "300", + description => "How often (in seconds) the main console window should refresh itself", + introduction => qqq(" + The next few options control what happens when the client is + running in 'low' bandwidth mode. You should set these options + for when accessing the ZoneMinder client over a modem or slow + link. In most cases the default values will be suitable as a + starting point. + "), + help => qqq(" + The main console window lists a general status and the event + totals for all monitors. This is not a trivial task and should + not be repeated too frequently or it may affect the performance + of the rest of the system. + "), + type => $types{integer}, + category => "lowband", + }, + { + name => "ZM_WEB_L_REFRESH_CYCLE", + default => "30", + description => "How often (in seconds) the cycle watch window swaps to the next monitor", + help => qqq(" + The cycle watch window is a method of continuously cycling + between images from all of your monitors. This option + determines how often to refresh with a new image. + "), + type => $types{integer}, + category => "lowband", + }, + { + name => "ZM_WEB_L_REFRESH_IMAGE", + default => "15", + description => "How often (in seconds) the watched image is refreshed (if not streaming)", + help => qqq(" + The live images from a monitor can be viewed in either streamed + or stills mode. This option determines how often a stills image + is refreshed, it has no effect if streaming is selected. + "), + type => $types{integer}, + category => "lowband", + }, + { + name => "ZM_WEB_L_REFRESH_STATUS", + default => "10", + description => "How often (in seconds) the status refreshes itself in the watch window", + help => qqq(" + The monitor window is actually made from several frames. The + one in the middle merely contains a monitor status which needs + to refresh fairly frequently to give a true indication. This + option determines that frequency. + "), + type => $types{integer}, + category => "lowband", + }, + { + name => "ZM_WEB_L_REFRESH_EVENTS", + default => "180", + description => "How often (in seconds) the event listing is refreshed in the watch window", + help => qqq(" + The monitor window is actually made from several frames. The + lower framme contains a listing of the last few events for easy + access. This option determines how often this is refreshed. + "), + type => $types{integer}, + category => "lowband", + }, + { + name => "ZM_WEB_L_CAN_STREAM", + default => "auto", + description => "Override the automatic detection of browser streaming capability", + help => qqq(" + If you know that your browser can handle image streams of the + type 'multipart/x-mixed-replace' but ZoneMinder does not detect + this correctly you can set this option to ensure that the + stream is delivered with or without the use of the Cambozola + plugin. Selecting 'yes' will tell ZoneMinder that your browser + can handle the streams natively, 'no' means that it can't and + so the plugin will be used while 'auto' lets ZoneMinder decide. + "), + type => $types{tristate}, + category => "lowband", + }, + { + name => "ZM_WEB_L_STREAM_METHOD", + default => "jpeg", + description => "Which method should be used to send video streams to your browser.", + help => qqq(" + ZoneMinder can be configured to use either mpeg encoded video + or a series or still jpeg images when sending video streams. + This option defines which is used. If you choose mpeg you + should ensure that you have the appropriate plugins available + on your browser whereas choosing jpeg will work natively on + Mozilla and related browsers and with a Java applet on Internet + Explorer + "), + type => { + db_type =>"string", + hint =>"mpeg|jpeg", + pattern =>qr|^([mj])|i, + format =>q( $1 =~ /^m/ ? "mpeg" : "jpeg" ) + }, + category => "lowband", + }, + { + name => "ZM_WEB_L_DEFAULT_SCALE", + default => "100", + description => "What the default scaling factor applied to 'live' or 'event' views is (%)", + help => qqq(" + Normally ZoneMinder will display 'live' or 'event' streams in + their native size. However if you have monitors with large + dimensions or a slow link you may prefer to reduce this size, + alternatively for small monitors you can enlarge it. This + options lets you specify what the default scaling factor will + be. It is expressed as a percentage so 100 is normal size, 200 + is double size etc. + "), + type => { + db_type =>"integer", + hint =>"25|33|50|75|100|150|200|300|400", + pattern =>qr|^(\d+)$|, + format =>q( $1 ) + }, + category => "lowband", + }, + { + name => "ZM_WEB_L_DEFAULT_RATE", + default => "100", + description => "What the default replay rate factor applied to 'event' views is (%)", + help => qqq(" + Normally ZoneMinder will display 'event' streams at their + native rate, i.e. as close to real-time as possible. However if + you have long events it is often convenient to replay them at a + faster rate for review. This option lets you specify what the + default replay rate will be. It is expressed as a percentage so + 100 is normal rate, 200 is double speed etc. + "), + type => { + db_type =>"integer", + hint =>"25|50|100|150|200|400|1000|2500|5000|10000", + pattern =>qr|^(\d+)$|, format=>q( $1 ) + }, + category => "lowband", + }, + { + name => "ZM_WEB_L_VIDEO_BITRATE", + default => "25000", + description => "What the bitrate of the video encoded stream should be set to", + help => qqq(" + When encoding real video via the ffmpeg library a bit rate can + be specified which roughly corresponds to the available + bandwidth used for the stream. This setting effectively + corresponds to a 'quality' setting for the video. A low value + will result in a blocky image whereas a high value will produce + a clearer view. Note that this setting does not control the + frame rate of the video however the quality of the video + produced is affected both by this setting and the frame rate + that the video is produced at. A higher frame rate at a + particular bit rate result in individual frames being at a + lower quality. + "), + type => $types{integer}, + category => "lowband", + }, + { + name => "ZM_WEB_L_VIDEO_MAXFPS", + default => "5", + description => "What the maximum frame rate for streamed video should be", + help => qqq(" + When using streamed video the main control is the bitrate which + determines how much data can be transmitted. However a lower + bitrate at high frame rates results in a lower quality image. + This option allows you to limit the maximum frame rate to + ensure that video quality is maintained. An additional + advantage is that encoding video at high frame rates is a + processor intensive task when for the most part a very high + frame rate offers little perceptible improvement over one that + has a more manageable resource requirement. Note, this option + is implemented as a cap beyond which binary reduction takes + place. So if you have a device capturing at 15fps and set this + option to 10fps then the video is not produced at 10fps, but + rather at 7.5fps (15 divided by 2) as the final frame rate must + be the original divided by a power of 2. + "), + type => $types{integer}, + category => "lowband", + }, + { + name => "ZM_WEB_L_SCALE_THUMBS", + default => "yes", + description => "Scale thumbnails in events, bandwidth versus cpu in rescaling", + help => qqq(" + If unset, this option sends the whole image to the browser + which resizes it in the window. If set the image is scaled down + on the server before sending a reduced size image to the + browser to conserve bandwidth at the cost of cpu on the server. + Note that ZM can only perform the resizing if the appropriate + PHP graphics functionality is installed. This is usually + available in the php-gd package. + "), + type => $types{boolean}, + category => "lowband", + }, + { + name => "ZM_WEB_L_EVENTS_VIEW", + default => "events", + description => "What the default view of multiple events should be.", + help => qqq(" + Stored events can be viewed in either an events list format or + in a timeline based one. This option sets the default view that + will be used. Choosing one view here does not prevent the other + view being used as it will always be selectable from whichever + view is currently being used. + "), + type => { + db_type =>"string", + hint =>"events|timeline", + pattern =>qr|^([lt])|i, + format =>q( $1 =~ /^e/ ? "events" : "timeline" ) + }, + category => "lowband", + }, + { + name => "ZM_WEB_L_SHOW_PROGRESS", + default => "no", + description => "Show the progress of replay in event view.", + help => qqq(" + When viewing events an event navigation panel and progress bar + is shown below the event itself. This allows you to jump to + specific points in the event, but can can also dynamically + update to display the current progress of the event replay + itself. This progress is calculated from the actual event + duration and is not directly linked to the replay itself, so on + limited bandwidth connections may be out of step with the + replay. This option allows you to turn off the progress + display, whilst still keeping the navigation aspect, where + bandwidth prevents it functioning effectively. + "), + type => $types{boolean}, + category => "lowband", + }, + { + name => "ZM_WEB_L_AJAX_TIMEOUT", + default => "10000", + description => "How long to wait for Ajax request responses (ms)", + help => qqq(" + The newer versions of the live feed and event views use Ajax to + request information from the server and populate the views + dynamically. This option allows you to specify a timeout if + required after which requests are abandoned. A timeout may be + necessary if requests would overwise hang such as on a slow + connection. This would tend to consume a lot of browser memory + and make the interface unresponsive. Ordinarily no requests + should timeout so this setting should be set to a value greater + than the slowest expected response. This value is in + milliseconds but if set to zero then no timeout will be used. + "), + type => $types{integer}, + category => "lowband", + }, + { + name => "ZM_DYN_LAST_VERSION", + default => "", + description => "What the last version of ZoneMinder recorded from zoneminder.com is", + help => "", + type => $types{string}, + readonly => 1, + category => "dynamic", + }, + { + name => "ZM_DYN_CURR_VERSION", + default => "@VERSION@", + description => qqq(" + What the effective current version of ZoneMinder is, might be + different from actual if versions ignored + "), + help => "", + type => $types{string}, + readonly => 1, + category => "dynamic", + }, + { + name => "ZM_DYN_DB_VERSION", + default => "@VERSION@", + description => "What the version of the database is, from zmupdate", + help => "", + type => $types{string}, + readonly => 1, + category => "dynamic", + }, + { + name => "ZM_DYN_LAST_CHECK", + default => "", + description => "When the last check for version from zoneminder.com was", + help => "", + type => $types{integer}, + readonly => 1, + category => "dynamic", + }, + { + name => "ZM_DYN_NEXT_REMINDER", + default => "", + description => "When the earliest time to remind about versions will be", + help => "", + type => $types{string}, + readonly => 1, + category => "dynamic", + }, + { + name => "ZM_DYN_DONATE_REMINDER_TIME", + default => 0, + description => "When the earliest time to remind about donations will be", + help => "", + type => $types{integer}, + readonly => 1, + category => "dynamic", + }, + { + name => "ZM_DYN_SHOW_DONATE_REMINDER", + default => "yes", + description => "Remind about donations or not", + help => "", + type => $types{boolean}, + readonly => 1, + category => "dynamic", + }, + { + name => "ZM_SSMTP_MAIL", + default => "no", + description => qqq(" + Use a SSMTP mail server if available. + NEW_MAIL_MODULES must be enabled + "), + requires => [ + { name => "ZM_OPT_EMAIL", value => "yes" }, + { name => "ZM_OPT_MESSAGE", value => "yes" }, + { name => "ZM_NEW_MAIL_MODULES", value => "yes" } + ], + help => qqq(" + SSMTP is a lightweight and efficient method to send email. + The SSMTP application is not installed by default. + NEW_MAIL_MODULES must also be enabled. + Please visit: http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder + for setup and configuration help. + "), + type => $types{boolean}, + category => "mail", + }, + { + name => "ZM_SSMTP_PATH", + default => "", + description => "SSMTP executable path", + requires => [{ name => "ZM_SSMTP_MAIL", value => "yes" }], + help => qqq(" + Recommend setting the path to the SSMTP application. + If path is not defined. Zoneminder will try to determine + the path via shell command. Example path: /usr/sbin/ssmtp. + "), + type => $types{string}, + category => "mail", + }, ); our %options_hash = map { ( $_->{name}, $_ ) } @options; @@ -1936,22 +3881,22 @@ sub initialiseConfig { return if ( $configInitialised ); - # Do some initial data munging to finish the data structures - # Create option ids - my $option_id = 0; - foreach my $option ( @options ) - { - if ( defined($option->{default}) ) - { - $option->{value} = $option->{default} - } - else - { - $option->{value} = ''; - } - #next if ( $option->{category} eq 'hidden' ); - $option->{id} = $option_id++; - } + # Do some initial data munging to finish the data structures + # Create option ids + my $option_id = 0; + foreach my $option ( @options ) + { + if ( defined($option->{default}) ) + { + $option->{value} = $option->{default} + } + else + { + $option->{value} = ''; + } + #next if ( $option->{category} eq 'hidden' ); + $option->{id} = $option_id++; + } $configInitialised = 1; } @@ -1972,9 +3917,15 @@ ZoneMinder::ConfigData - ZoneMinder Configuration Data module =head1 DESCRIPTION -The ZoneMinder:ConfigData module contains the master definition of the ZoneMinder configuration options as well as helper methods. This module is intended for specialist confguration management and would not normally be used by end users. +The ZoneMinder:ConfigData module contains the master definition of the +ZoneMinder configuration options as well as helper methods. This module is +intended for specialist confguration management and would not normally be +used by end users. -The configuration held in this module, which was previously in zmconfig.pl, includes the name, default value, description, help text, type and category for each option, as well as a number of additional fields in a small number of cases. +The configuration held in this module, which was previously in zmconfig.pl, +includes the name, default value, description, help text, type and category +for each option, as well as a number of additional fields in a small number +of cases. =head1 METHODS @@ -1982,11 +3933,19 @@ The configuration held in this module, which was previously in zmconfig.pl, incl =item loadConfigFromDB (); -Loads existing configuration from the database (if any) and merges it with the definitions held in this module. This results in the merging of any new configuration and the removal of any deprecated configuration while preserving the existing values of every else. +Loads existing configuration from the database (if any) and merges it with +the definitions held in this module. This results in the merging of any new +configuration and the removal of any deprecated configuration while +preserving the existing values of every else. =item saveConfigToDB (); -Saves configuration held in memory to the database. The act of loading and saving configuration is a convenient way to ensure that the configuration held in the database corresponds with the most recent definitions and that all components are using the same set of configuration. +Saves configuration held in memory to the database. The act of loading and +saving configuration is a convenient way to ensure that the configuration +held in the database corresponds with the most recent definitions and that +all components are using the same set of configuration. + +=back =head2 EXPORT diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control.pm index 2a646772d..0822dc3d3 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control.pm @@ -19,7 +19,7 @@ # # ========================================================================== # -# This module contains the base class definitions for the camera control +# This module contains the base class definitions for the camera control # protocol implementations # package ZoneMinder::Control; @@ -45,17 +45,17 @@ our $AUTOLOAD; sub new { - my $class = shift; - my $id = shift; - my $self = {}; - $self->{name} = "PelcoD"; + my $class = shift; + my $id = shift; + my $self = {}; + $self->{name} = "PelcoD"; if ( !defined($id) ) { Fatal( "No monitor defined when invoking protocol ".$self->{name} ); } - $self->{id} = $id; - bless( $self, $class ); - return $self; + $self->{id} = $id; + bless( $self, $class ); + return $self; } sub DESTROY @@ -64,32 +64,32 @@ sub DESTROY sub AUTOLOAD { - my $self = shift; - my $class = ref($self) || croak( "$self not object" ); - my $name = $AUTOLOAD; - $name =~ s/.*://; - if ( exists($self->{$name}) ) - { - return( $self->{$name} ); - } - croak( "Can't access $name member of object of class $class" ); + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + croak( "Can't access $name member of object of class $class" ); } -sub getKey() +sub getKey { - my $self = shift; + my $self = shift; return( $self->{id} ); } sub open { - my $self = shift; + my $self = shift; Fatal( "No open method defined for protocol ".$self->{name} ); } sub close { - my $self = shift; + my $self = shift; Fatal( "No close method defined for protocol ".$self->{name} ); } @@ -145,9 +145,9 @@ sub executeCommand &{$self->{$command}}( $self, $params ); } -sub printMsg() +sub printMsg { - my $self = shift; + my $self = shift; Fatal( "No printMsg method defined for protocol ".$self->{name} ); } diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/3S.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/3S.pm index f825af109..47d783bff 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/3S.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/3S.pm @@ -21,10 +21,10 @@ # # This module contains the implementation of the 3S camera control # protocol -#Model: N5071 -#Hardware Version: 00 -#Firmware Version: V1.03_STD-1 -#Firmware Build Time: Jun 19 2012 15:28:17 +#Model: N5071 +#Hardware Version: 00 +#Firmware Version: V1.03_STD-1 +#Firmware Build Time: Jun 19 2012 15:28:17 package ZoneMinder::Control::3S; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm index e5a5882b9..2372a2d81 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/AxisV2.pm @@ -33,8 +33,6 @@ require ZoneMinder::Control; our @ISA = qw(ZoneMinder::Control); -our $VERSION = $ZoneMinder::Base::VERSION; - # ========================================================================== # # Axis V2 Control Protocol @@ -79,7 +77,7 @@ sub open use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; - $self->{ua}->agent( "ZoneMinder Control Agent/".ZM_VERSION ); + $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); $self->{state} = 'open'; } diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8608W_Y2k.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8608W_Y2k.pm index 7ffbf248a..2e5b21f76 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8608W_Y2k.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8608W_Y2k.pm @@ -53,7 +53,7 @@ our @ISA = qw(ZoneMinder::Control); # of the position of your mouse on the arrow. # Extremity of arrow equal to fastest speed of movement # Close the base of arrow to lowest speed of movement -# for diagonaly you can click before the begining of the arrow for low speed +# for diagonaly you can click before the beginning of the arrow for low speed # In round center equal to stop to move and switch of latest OSD # -You can clic directly on the image that equal to click on arrow (for the left there is a bug in zoneminder speed is inverted) # -Zoom Tele switch ON InfraRed LED and stay to manual IR MODE @@ -63,7 +63,7 @@ our @ISA = qw(ZoneMinder::Control); # -8 Preset PTZ are implemented and functionnal # -This Script use for login "admin" this hardcoded and your password must setup in "Control Device" section # -This script is compatible with the basic authentification method used by mostly new camera based with hi3510 chipset -# -AutoStop function is active and you must set up value (in sec exemple 0.7) under AutoStop section +# -AutoStop function is active and you must set up value (in sec example 0.7) under AutoStop section # or you can set up to 0 for disable it (in this case you need to click to the circle center for stop) # -"White In" to control Brightness, "auto" for restore the original value of Brightness # -"White Out" to control Contrast, "man" for restore the original value of Contrast @@ -129,7 +129,7 @@ sub printMsg } sub sendCmd -{ +{ my $self = shift; my $cmd = shift; my $result = undef; @@ -211,7 +211,7 @@ sub moveConUp if ( $tiltspeed < 10 ) { $tiltspeed = 1; } - Debug( "Move Up" ); + Debug( "Move Up" ); if ( $osd eq "on" ) { my $cmd = "param.cgi?cmd=setoverlayattr&-region=1&-show=1&-name=Move Up $tiltspeed"; @@ -658,9 +658,9 @@ sub presetGoto __END__ # Below is stub documentation for your module. You'd better edit it! -=head1 FI-8608W +=head1 NAME -ZoneMinder::Database - Perl extension for FOSCAM FI-8608W by Christophe_Y2k +ZoneMinder::Control::FI-8608W - Perl extension for FOSCAM FI-8608W by Christophe_Y2k =head1 SYNOPSIS diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8620_Y2k.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8620_Y2k.pm index 7d9cd261d..3e2addf85 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8620_Y2k.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8620_Y2k.pm @@ -57,7 +57,7 @@ our @ISA = qw(ZoneMinder::Control); # of the position of your mouse on the arrow. # Extremity of arrow equal to fastest speed of movement # Close the base of arrow to lowest speed of movement -# for diagonaly you can click before the begining of the arrow for low speed +# for diagonaly you can click before the beginning of the arrow for low speed # In round center equal to stop to move # -You can clic directly on the image that equal to click on arrow (for the left there is a bug in zoneminder speed is inverted) # -Zoom Tele/Wide with time control to simulate speed because speed value do not work (buggy firmware or not implemented on this cam) @@ -67,7 +67,7 @@ our @ISA = qw(ZoneMinder::Control); # You Need to configure ZoneMinder PANSPEED & TILTSEPPED & ZOOMSPEED 1 to 63 by 1 step # -This Script use for login "admin" this hardcoded and your password must setup in "Control Device" section # -This script is compatible with the basic authentification method used by mostly new camera -# -AutoStop function is active and you must set up value (in sec exemple 0.5) under AutoStop section +# -AutoStop function is active and you must set up value (in sec example 0.5) under AutoStop section # or you can set up to 0 for disable it but the camera never stop to move and trust me, she can move all the night... # (you need to click to the center arrow for stop) # -"White In" to control Brightness, "auto" for restore the original value of Brightness @@ -734,9 +734,9 @@ sub presetGoto __END__ # Below is stub documentation for your module. You'd better edit it! -=head1 FI8620 +=head1 NAME -ZoneMinder::Database - Perl extension for FOSCAM FI8620 +ZoneMinder::Control::FI8620 - Perl extension for FOSCAM FI8620 =head1 SYNOPSIS diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8908W.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8908W.pm index ab877c609..dd30484c3 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8908W.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8908W.pm @@ -43,176 +43,176 @@ use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); sub new -{ - my $class = shift; - my $id = shift; - my $self = ZoneMinder::Control->new( $id ); - bless( $self, $class ); - srand( time() ); - return $self; +{ + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new( $id ); + bless( $self, $class ); + srand( time() ); + return $self; } our $AUTOLOAD; sub AUTOLOAD { - my $self = shift; - my $class = ref($self) || croak( "$self not object" ); - my $name = $AUTOLOAD; - $name =~ s/.*://; - if ( exists($self->{$name}) ) - { - return( $self->{$name} ); - } - Fatal( "Can't access $name member of object of class $class" ); + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + Fatal( "Can't access $name member of object of class $class" ); } sub open { - my $self = shift; + my $self = shift; - $self->loadMonitor(); + $self->loadMonitor(); - use LWP::UserAgent; - $self->{ua} = LWP::UserAgent->new; - $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); - $self->{state} = 'open'; + $self->{state} = 'open'; } sub close { - my $self = shift; - $self->{state} = 'closed'; + my $self = shift; + $self->{state} = 'closed'; } sub printMsg { - my $self = shift; - my $msg = shift; - my $msg_len = length($msg); + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); - Debug( $msg."[".$msg_len."]" ); + Debug( $msg."[".$msg_len."]" ); } sub sendCmd { - my $self = shift; - my $cmd = shift; - my $result = undef; + my $self = shift; + my $cmd = shift; + my $result = undef; - my ($user, $password) = split /:/, $self->{Monitor}->{ControlDevice}; + my ($user, $password) = split /:/, $self->{Monitor}->{ControlDevice}; - if ( !defined $password ) { - # If value of "Control device" does not consist of two parts, then only password is given and we fallback to default user: - $password = $user; - $user = 'admin'; - } + if ( !defined $password ) { + # If value of "Control device" does not consist of two parts, then only password is given and we fallback to default user: + $password = $user; + $user = 'admin'; + } - $cmd .= "user=$user&pwd=$password"; + $cmd .= "user=$user&pwd=$password"; - printMsg( $cmd, "Tx" ); + printMsg( $cmd, "Tx" ); - my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" ); - my $res = $self->{ua}->request($req); + my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" ); + my $res = $self->{ua}->request($req); - if ( $res->is_success ) - { - $result = !undef; - } - else - { - Error( "Error check failed: '".$res->status_line()."' for URL ".$req->uri() ); - } + if ( $res->is_success ) + { + $result = !undef; + } + else + { + Error( "Error check failed: '".$res->status_line()."' for URL ".$req->uri() ); + } - return( $result ); + return( $result ); } sub reset { - my $self = shift; - Debug( "Camera Reset" ); - $self->sendCmd( 'reboot.cgi?' ); + my $self = shift; + Debug( "Camera Reset" ); + $self->sendCmd( 'reboot.cgi?' ); } #Up Arrow sub moveConUp { - my $self = shift; - Debug( "Move Up" ); - $self->sendCmd( 'decoder_control.cgi?command=0&' ); + my $self = shift; + Debug( "Move Up" ); + $self->sendCmd( 'decoder_control.cgi?command=0&' ); } #Down Arrow sub moveConDown { - my $self = shift; - Debug( "Move Down" ); - $self->sendCmd( 'decoder_control.cgi?command=2&' ); + my $self = shift; + Debug( "Move Down" ); + $self->sendCmd( 'decoder_control.cgi?command=2&' ); } #Left Arrow sub moveConLeft { - my $self = shift; - Debug( "Move Left" ); - $self->sendCmd( 'decoder_control.cgi?command=6&' ); + my $self = shift; + Debug( "Move Left" ); + $self->sendCmd( 'decoder_control.cgi?command=6&' ); } #Right Arrow sub moveConRight { - my $self = shift; - Debug( "Move Right" ); - $self->sendCmd( 'decoder_control.cgi?command=4&' ); + my $self = shift; + Debug( "Move Right" ); + $self->sendCmd( 'decoder_control.cgi?command=4&' ); } #Diagonally Up Right Arrow sub moveConUpRight { - my $self = shift; - Debug( "Move Diagonally Up Right" ); - $self->sendCmd( 'decoder_control.cgi?command=90&' ); + my $self = shift; + Debug( "Move Diagonally Up Right" ); + $self->sendCmd( 'decoder_control.cgi?command=90&' ); } #Diagonally Down Right Arrow sub moveConDownRight { - my $self = shift; - Debug( "Move Diagonally Down Right" ); - $self->sendCmd( 'decoder_control.cgi?command=92&' ); + my $self = shift; + Debug( "Move Diagonally Down Right" ); + $self->sendCmd( 'decoder_control.cgi?command=92&' ); } #Diagonally Up Left Arrow sub moveConUpLeft { - my $self = shift; - Debug( "Move Diagonally Up Left" ); - $self->sendCmd( 'decoder_control.cgi?command=91&' ); + my $self = shift; + Debug( "Move Diagonally Up Left" ); + $self->sendCmd( 'decoder_control.cgi?command=91&' ); } #Diagonally Down Left Arrow sub moveConDownLeft { - my $self = shift; - Debug( "Move Diagonally Down Left" ); - $self->sendCmd( 'decoder_control.cgi?command=93&' ); + my $self = shift; + Debug( "Move Diagonally Down Left" ); + $self->sendCmd( 'decoder_control.cgi?command=93&' ); } #Stop sub moveStop { - my $self = shift; - Debug( "Move Stop" ); - $self->sendCmd( 'decoder_control.cgi?command=1&' ); + my $self = shift; + Debug( "Move Stop" ); + $self->sendCmd( 'decoder_control.cgi?command=1&' ); } #Move Camera to Home Position sub presetHome { - my $self = shift; - Debug( "Home Preset" ); - $self->sendCmd( 'decoder_control.cgi?command=25&' ); + my $self = shift; + Debug( "Home Preset" ); + $self->sendCmd( 'decoder_control.cgi?command=25&' ); } 1; @@ -220,12 +220,17 @@ sub presetHome __END__ =pod +=head1 NAME + +ZoneMinder::Control::FI8908W - Foscam FI8908W camera control + =head1 DESCRIPTION -This module contains the implementation of the Foscam FI8908W / FI8918W IP camera control -protocol. +This module contains the implementation of the Foscam FI8908W / FI8918W IP +camera control protocol. + +The module uses "Control Device" value to retrieve user and password. User +and password should be separated by colon, e.g. user:password. If colon is +not provided, then "admin" is used as a fallback value for the user. -The module uses "Control Device" value to retrieve user and password. User and password should -be separated by colon, e.g. user:password. If colon is not provided, then "admin" is used -as a fallback value for the user. =cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8918W.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8918W.pm new file mode 100644 index 000000000..c9997526d --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI8918W.pm @@ -0,0 +1,356 @@ +# Modified on Jun 19 2016 by PP +# Changes made +# - modified command to work properly and pick up credentials from Control Device +# - the old script did not stop moving- added autostop +# (note that mjpeg cameras have onestep but that is too granular) +# - You need to set "user=xxx&pwd=yyy" in the ControlDevice field (NOT usr like in Foscam HD) + +# ========================================================================== +# +# ZoneMinder Foscam FI8918W IP Control Protocol Module, $Date: 2009-11-25 09:20:00 +0000 (Wed, 04 Nov 2009) $, $Revision: 0001 $ +# Copyright (C) 2001-2008 Philip Coombes +# Modified for use with Foscam FI8918W IP Camera by Dave Harris +# Modified Feb 2011 by Howard Durdle (http://durdl.es/x) to: +# fix horizontal panning, add presets and IR on/off +# use Control Device field to pass username and password +# Modified May 2014 by Arun Horne (http://arunhorne.co.uk) to: +# use HTTP basic auth as required by firmware 11.37.x.x upward +# +# 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 module contains the implementation of the Foscam FI8918W IP camera control +# protocol +# + +package MyAgent; + +use base 'LWP::UserAgent'; + + +package ZoneMinder::Control::FI8918W; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +our $VERSION = $ZoneMinder::Base::VERSION; + +# ========================================================================== +# +# Foscam FI8918W IP Control Protocol +# +# ========================================================================== + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); + + use Time::HiRes qw( usleep ); + +sub new +{ + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new( $id ); + my $logindetails = ""; + bless( $self, $class ); + srand( time() ); + return $self; +} + +our $AUTOLOAD; + +sub AUTOLOAD +{ + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + Fatal( "Can't access $name member of object of class $class" ); +} +our $stop_command; + +sub open +{ + my $self = shift; + + $self->loadMonitor(); + + $self->{ua} = MyAgent->new; + $self->{ua}->agent( "ZoneMinder Control Agent/" ); + + $self->{state} = 'open'; +} + +sub close +{ + my $self = shift; + $self->{state} = 'closed'; +} + +sub printMsg +{ + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); + + Debug( $msg."[".$msg_len."]" ); +} + +sub sendCmd +{ + my $self = shift; + my $cmd = shift; + my $result = undef; + printMsg( $cmd, "Tx" ); + + # PP Old cameras also support onstep=1 but it is too granular. Instead using moveCon and stop after interval + # PP - cleaned up URL to take it properly from Control device + # Control device needs to be of format user=xxx&pwd=yyy + my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd"."&".$self->{Monitor}->{ControlDevice}); + print ("Sending $req\n"); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) + { + $result = !undef; + } + else + { + Error( "Error REALLY check failed:'".$res->status_line()."'" ); + Error ("Cmd:".$req); + } + + return( $result ); +} + +sub reset +{ + my $self = shift; + Debug( "Camera Reset" ); + my $cmd = "reboot.cgi?"; + $self->sendCmd( $cmd ); +} + +# PP - in all move operations, added auto stop after timeout + +#Up Arrow +sub moveConUp +{ + my $self = shift; + Debug( "Move Up" ); + my $cmd = "decoder_control.cgi?command=0"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Down Arrow +sub moveConDown +{ + my $self = shift; + Debug( "Move Down" ); + my $cmd = "decoder_control.cgi?command=2"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Left Arrow +sub moveConLeft +{ + my $self = shift; + Debug( "Move Left" ); + my $cmd = "decoder_control.cgi?command=6"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Right Arrow +sub moveConRight +{ + my $self = shift; + Debug( "Move Right" ); + my $cmd = "decoder_control.cgi?command=4"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Up Right Arrow +sub moveConUpRight +{ + my $self = shift; + Debug( "Move Diagonally Up Right" ); + my $cmd = "decoder_control.cgi?command=90"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); + +} + +#Diagonally Down Right Arrow +sub moveConDownRight +{ + my $self = shift; + Debug( "Move Diagonally Down Right" ); + my $cmd = "decoder_control.cgi?command=92"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Up Left Arrow +sub moveConUpLeft +{ + my $self = shift; + Debug( "Move Diagonally Up Left" ); + my $cmd = "decoder_control.cgi?command=91"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Down Left Arrow +sub moveConDownLeft +{ + my $self = shift; + Debug( "Move Diagonally Down Left" ); + my $cmd = "decoder_control.cgi?command=93"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Stop +sub moveStop +{ + my $self = shift; + Debug( "Move Stop" ); + my $cmd = "decoder_control.cgi?command=1"; + $self->sendCmd( $cmd ); +} + +# PP - imported from 9831 - autostop after usleep +sub autoStop +{ + my $self = shift; + my $autostop = shift; + if( $autostop ) + { + Debug( "Auto Stop" ); + usleep( $autostop ); + my $cmd = "decoder_control.cgi?command=1"; + $self->sendCmd( $cmd ); + } +} + +#Move Camera to Home Position +sub presetHome +{ + my $self = shift; + Debug( "Home Preset" ); + my $cmd = "decoder_control.cgi?command=25"; + $self->sendCmd( $cmd ); +} + +#Set preset +sub presetSet +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + my $presetCmd = 30 + ($preset*2); + Debug( "Set Preset $preset with cmd $presetCmd" ); + my $cmd = "decoder_control.cgi?command=$presetCmd"; + $self->sendCmd( $cmd ); +} + +#Goto preset +sub presetGoto +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + my $presetCmd = 31 + ($preset*2); + Debug( "Goto Preset $preset with cmd $presetCmd" ); + my $cmd = "decoder_control.cgi?command=$presetCmd"; + $self->sendCmd( $cmd ); +} + +#Turn IR on +sub wake +{ + my $self = shift; + Debug( "Wake - IR on" ); + my $cmd = "decoder_control.cgi?command=95"; + $self->sendCmd( $cmd ); +} + +#Turn IR off +sub sleep +{ + my $self = shift; + Debug( "Sleep - IR off" ); + my $cmd = "decoder_control.cgi?command=94"; + $self->sendCmd( $cmd ); +} + +1; +__END__ + +=head1 FI8918W + +ZoneMinder::Database - Perl extension for FOSCAM FI8918W + +=head1 SYNOPSIS + +Control script for Foscam MJPEG 8918W cameras. + +=head1 DESCRIPTION + +You need to set "user=xxx&pwd=yyy" in the ControlDevice field +of the control tab for that monitor. +Auto TimeOut should be 1. Don't set it to less - processes +start crashing :) +NOTE: unlike HD foscam cameras, this one uses "user" not "usr" +in the control device + +=head2 EXPORT + +None by default. + + + +=head1 SEE ALSO + +=head1 AUTHOR + +Philip Coombes, Ephilip.coombes@zoneminder.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2008 Philip Coombes + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + + +=cut + diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9821W_Y2k.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9821W_Y2k.pm index 4177f5857..dd73a04e6 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9821W_Y2k.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9821W_Y2k.pm @@ -102,7 +102,6 @@ sub close sub printMsg { - my $self = shift; my $msg = shift; my $msg_len = length($msg); Debug( $msg."[".$msg_len."]" ); @@ -113,9 +112,28 @@ sub sendCmd my $self = shift; my $cmd = shift; my $result = undef; + + my ($user, $password) = split /:/, $self->{Monitor}->{ControlDevice}; + if ( ! $password ) { + $password = $user; + $user = 'admin'; + } + $user = 'admin' if ! $user; + $password = 'pwd' if ! $password; + + $cmd .= "&usr=$user&pwd=$password"; + printMsg( $cmd, "Tx" ); - my $temps = time(); - my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/CGIProxy.fcgi?usr%3Dadmin%26pwd%3D".$self->{Monitor}->{ControlDevice}."%26cmd%3D".$cmd."%26".$temps ); + my $url; + if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) { + $url = $self->{Monitor}->{ControlAddress}; + } else { + $url = "http://".$self->{Monitor}->{ControlAddress}; + } + $url .= "/cgi-bin/CGIProxy.fcgi?cmd=$cmd%26".time; + printMsg( $url, "Tx" ); + + my $req = HTTP::Request->new( GET=>$url ); my $res = $self->{ua}->request($req); if ( $res->is_success ) { @@ -134,7 +152,7 @@ sub reset # Setup OSD my $cmd = "setOSDSetting%26isEnableTimeStamp%3D0%26isEnableDevName%3D1%26dispPos%3D0%26isEnabledOSDMask%3D0"; $self->sendCmd( $cmd ); - # Setup For Stream=0 Resolution=720p Bandwith=4M FPS=30 KeyFrameInterval/GOP=100 VBR=ON + # Setup For Stream=0 Resolution=720p Bandwidth=4M FPS=30 KeyFrameInterval/GOP=100 VBR=ON $cmd = "setVideoStreamParam%26streamType%3D0%26resolution%3D0%26bitRate%3D4194304%26frameRate%3D30%26GOP%3D100%26isVBR%3D1"; $self->sendCmd( $cmd ); # Setup For Infrared AUTO @@ -696,9 +714,9 @@ sub presetGoto __END__ # Below is stub documentation for your module. You'd better edit it! -=head1 FI9821W +=head1 NAME -ZoneMinder::Database - Perl extension for FOSCAM FI9821W +ZoneMinder::Control::FI9821W - Perl extension for FOSCAM FI9821W =head1 SYNOPSIS diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9831W.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9831W.pm new file mode 100644 index 000000000..739ff1175 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/FI9831W.pm @@ -0,0 +1,763 @@ +# Modified by PP to clean up user/auth dependencies inside the script +# Also, you can specify your auth credentials in the Control tab of the monitor +# In "ControlDevice" put in +# usr=xxxx&pwd=xxx +# where xxx is the auth credentials to your foscam camera +# The Foscam CGI manual referred to was v1.0.10 +# All other notices below may be stale +# +# ========================================================================== +# +# ZoneMinder FOSCAM version 1.0 API Control Protocol Module, $Date$, $Revision$ +# Copyright (C) 2001-2008 Philip Coombes +# +# 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 module FI8620_Y2k.pm contains the implementation of API camera control +# For FOSCAM FI8620 Dome PTZ Camera (This cam support only H264 streaming) +# V1.0 Le 09 AOUT 2013 - production usable for the script but not for the camera "reboot itself" +# If you wan't to contact me i understand French and English, precise ZoneMinder in subject +# My name is Christophe DAPREMONT my email is christophe_y2k@yahoo.fr +# +# ========================================================================================= +# +package ZoneMinder::Control::FI9831W; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); +# =================================================================================================================================== +# +# FI9821 FOSCAM PT H264 Control Protocol +# with Firmware version V1.2.1.1 (latest at 09/08/2013) +# based with the latest buggy CGI doc from FOSCAM ( http://foscam.us/forum/cgi-sdk-for-hd-camera-t6045.html ) +# This IPCAM work under ZoneMinder V1.25 from alternative source of code +# from this svn at https://svn.unixmedia.net/public/zum/trunk/zum/ +# Many Thanks to "MASTERTHEKNIFE" for the excellent speed optimisation ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=17652 ) +# And to "NEXTIME" for the recent source update and incredible plugins ( http://www.zoneminder.com/forums/viewtopic.php?f=9&t=20587 ) +# And all people helping ZoneMinder dev. +# +# -FUNCTION: display on OSD +# speed is progressive in function of where you click on arrow ========> +# speed low=/ \=speed high +# =================================================================================================================================== +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); +use Time::HiRes qw( usleep ); + +# Set $osd to "off" if you wan't disabled OSD i need to place this variable in another script because +# this script is reload at every command ,if i want the button on/off (Focus MAN) for OSD works... +# PP - changed this to off - it achieves OSD by renaming the Device and what happens is at times +# it does not reset the name if a command fails. Net result: Your camera gets a name like "Move Left" which +# I bet you won't like +my $osd = "off"; +my $cmd; + +sub new +{ + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new( $id ); + bless( $self, $class ); + srand( time() ); + return $self; +} + +our $AUTOLOAD; + +sub AUTOLOAD +{ + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + Fatal( "Can't access $name member of object of class $class" ); +} + +sub open +{ + my $self = shift; + $self->loadMonitor(); + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + #PP + #$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + $self->{ua}->agent( "ZoneMinder Control Agent/" ); + $self->{state} = 'open'; +} + +sub close +{ + my $self = shift; + $self->{state} = 'closed'; +} + +sub printMsg +{ + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); + Debug( $msg."[".$msg_len."]" ); +} + +sub sendCmd +{ + my $self = shift; + my $cmd = shift; + my $result = undef; + printMsg( $cmd, "Tx" ); + my $temps = time(); + #PP - cleaned this up so it picks up the full auth from Control Devices + my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/CGIProxy.fcgi?cmd=".$cmd."&".$self->{Monitor}->{ControlDevice} ); + #my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi-bin/CGIProxy.fcgi?usr%3Dadmin%26pwd%3D".$self->{Monitor}->{ControlDevice}."%26cmd%3D".$cmd."%26".$temps ); + my $res = $self->{ua}->request($req); + if ( $res->is_success ) + { + $result = !undef; + } + else + { + Error( "Error check failed: '".$res->status_line()."'" ); + } + return( $result ); +} + +# PP - changed this to a system reboot. Its harmful to reset here. Settings may change +# with different firmware versions. Better to make this a reboot and use the camera +# interface to reset streams +sub reset +{ my $self = shift; + Debug ( "Reboot= setup camera FoscamHD" ); + $cmd = "rebootSystem"; + #my $cmd = "setOSDSetting%26isEnableTimeStamp%3D0%26isEnableDevName%3D1%26dispPos%3D0%26isEnabledOSDMask%3D0"; + Info ("Sending reboot $cmd"); + $self->sendCmd( $cmd ); + # Setup For Stream=0 Resolution=720p Bandwidth=4M FPS=30 KeyFrameInterval/GOP=100 VBR=ON + #$cmd = "setVideoStreamParam%26streamType%3D0%26resolution%3D0%26bitRate%3D4194304%26frameRate%3D30%26GOP%3D100%26isVBR%3D1"; + #$self->sendCmd( $cmd ); + # Setup For Infrared AUTO + #$cmd = "setInfraLedConfig%26Mode%3D1"; + #$self->sendCmd( $cmd ); + # Reset image settings + #$cmd = "resetImageSetting"; + #$self->sendCmd( $cmd ); +} + +sub moveStop +{ + my $self = shift; + Debug( "Move Stop" ); + my $cmd = "ptzStopRun"; + $self->sendCmd( $cmd ); + if ($osd eq "on") + { + $cmd = "setDevName%26devName%3D."; + $self->sendCmd( $cmd ); + $cmd = "setOSDSetting%26isEnableDevName%3D1"; + $self->sendCmd( $cmd ); + } +} + +sub autoStop +{ + my $self = shift; + my $autostop = shift; + if( $autostop ) + { + Debug( "Auto Stop" ); + usleep( $autostop ); + my $cmd = "ptzStopRun"; + $self->sendCmd( $cmd ); + } +} + +sub moveConUp +{ + my $self = shift; + my $params = shift; + my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); + # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 + $tiltspeed = abs($tiltspeed - 4); + # Normalisation en cas de valeur erronée dans la base de données + if ( $tiltspeed > 4 ) { + $tiltspeed = 4; + } + if ( $tiltspeed < 0 ) { + $tiltspeed = 0; + } + Debug( "Move Up" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DMove Up $tiltspeed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; + $self->sendCmd( $cmd ); + $cmd = "ptzMoveUp"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub moveConDown +{ + my $self = shift; + my $params = shift; + my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); + # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 + $tiltspeed = abs($tiltspeed - 4); + # Normalization + if ( $tiltspeed > 4 ) { + $tiltspeed = 4; + } + if ( $tiltspeed < 0 ) { + $tiltspeed = 0; + } + Debug( "Move Down" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DMove Down $tiltspeed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; + $self->sendCmd( $cmd ); + $cmd = "ptzMoveDown"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub moveConLeft +{ + my $self = shift; + my $params = shift; + my $panspeed = $self->getParam( $params, 'panspeed' ); + # Normalisation en cas de valeur erronée dans la base de données + if ( $panspeed > 4 ) { + $panspeed = 4; + } + if ( $panspeed < 0 ) { + $panspeed = 0; + } + Debug( "Move Left" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DMove Left $panspeed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setPTZSpeed%26speed%3D$panspeed"; + $self->sendCmd( $cmd ); + $cmd = "ptzMoveLeft"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + + +sub moveConRight +{ + my $self = shift; + my $params = shift; + my $panspeed = $self->getParam( $params, 'panspeed' ); + # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 + $panspeed = abs($panspeed - 4); + # Normalisation en cas de valeur erronée dans la base de données + if ( $panspeed > 4 ) { + $panspeed = 4; + } + if ( $panspeed < 0 ) { + $panspeed = 0; + } + Debug( "Move Right" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DMove Right $panspeed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setPTZSpeed%26speed%3D$panspeed"; + $self->sendCmd( $cmd ); + $cmd = "ptzMoveRight"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub moveConUpLeft +{ + my $self = shift; + my $params = shift; + my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); + # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 + $tiltspeed = abs($tiltspeed - 4); + # Normalisation en cas de valeur erronée dans la base de données + if ( $tiltspeed > 4 ) { + $tiltspeed = 4; + } + if ( $tiltspeed < 0 ) { + $tiltspeed = 0; + } + Debug( "Move Con Up Left" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DMove Up Left $tiltspeed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; + $self->sendCmd( $cmd ); + $cmd = "ptzMoveTopLeft"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub moveConUpRight +{ + my $self = shift; + my $params = shift; + my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); + # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 + $tiltspeed = abs($tiltspeed - 4); + # Normalisation en cas de valeur erronée dans la base de données + if ( $tiltspeed > 4 ) { + $tiltspeed = 4; + } + if ( $tiltspeed < 0 ) { + $tiltspeed = 0; + } + Debug( "Move Con Up Right" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DMove Up Right $tiltspeed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; + $self->sendCmd( $cmd ); + $cmd = "ptzMoveTopRight"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub moveConDownLeft +{ + my $self = shift; + my $params = shift; + my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); + # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 + $tiltspeed = abs($tiltspeed - 4); + # Normalisation en cas de valeur erronée dans la base de données + if ( $tiltspeed > 4 ) { + $tiltspeed = 4; + } + if ( $tiltspeed < 0 ) { + $tiltspeed = 0; + } + Debug( "Move Con Down Left" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DMove Down Left $tiltspeed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; + $self->sendCmd( $cmd ); + $cmd = "ptzMoveBottomLeft"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub moveConDownRight +{ + my $self = shift; + my $params = shift; + my $tiltspeed = $self->getParam( $params, 'tiltspeed' ); + # speed inverter 4-->0 , 3-->1 , 2-->2 , 1-->3 , 0-->4 + $tiltspeed = abs($tiltspeed - 4); + # Normalisation en cas de valeur erronée dans la base de données + if ( $tiltspeed > 4 ) { + $tiltspeed = 4; + } + if ( $tiltspeed < 0 ) { + $tiltspeed = 0; + } + Debug( "Move Con Down Right" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DMove Down Right $tiltspeed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setPTZSpeed%26speed%3D$tiltspeed"; + $self->sendCmd( $cmd ); + $cmd = "ptzMoveBottomRight"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub zoomConTele +{ + my $self = shift; + Debug( "Zoom-Tele=MANU IR LED ON" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DManual IR LED Switch ON"; + $self->sendCmd( $cmd ); + } + my $cmd = "setInfraLedConfig%26mode%3D1"; + $self->sendCmd( $cmd ); + $cmd = "openInfraLed"; + $self->sendCmd( $cmd ); +} + +sub zoomConWide +{ + my $self = shift; + Debug( "Zoom-Wide=MANU IR LED OFF" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DManual IR LED Switch OFF"; + $self->sendCmd( $cmd ); + } + my $cmd = "setInfraLedConfig%26mode%3D1"; + $self->sendCmd( $cmd ); + $cmd = "closeInfraLed"; + $self->sendCmd( $cmd ); +} + +sub wake +{ + my $self = shift; + Debug( "Wake=AUTO IR LED" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DAuto IR LED Mode"; + $self->sendCmd( $cmd ); + } + my $cmd = "setInfraLedConfig%26mode%3D0"; + $self->sendCmd( $cmd ); +} + +sub focusConNear +{ + my $self = shift; + my $params = shift; + my $speed = $self->getParam( $params, 'speed' ); + # Normalisation en cas de valeur erronée dans la base de données + if ( $speed > 100 ) { + $speed = 100; + } + if ( $speed < 0 ) { + $speed = 0; + } + Debug( "Focus Near=Sharpness" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DSharpness $speed"; + $self->sendCmd( $cmd ); + $cmd = "setOSDSetting%26isEnableDevName%3D1"; + $self->sendCmd( $cmd ); + } + my $cmd = "setSharpness%26sharpness%3D$speed"; + $self->sendCmd( $cmd ); + # La variable speed ne fonctionne pas en paramètre du focus, alors je l'utilise pour définir la durée de la commande + # le résulat est identique + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub focusConFar +{ + my $self = shift; + my $params = shift; + my $speed = $self->getParam( $params, 'speed' ); + # Normalisation en cas de valeur erronée dans la base de données + if ( $speed > 100 ) { + $speed = 100; + } + if ( $speed < 0 ) { + $speed = 0; + } + Debug( "Focus Far" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DSharpness $speed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setSharpness%26sharpness%3D$speed"; + $self->sendCmd( $cmd ); + # La variable speed ne fonctionne pas en paramètre du focus alors je l'utilise pour définir la durée de la commande + # le résulat est identique + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub focusAuto +{ + my $self = shift; + Debug( "Focus Auto=Reset Sharpness" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DReset Sharpness"; + $self->sendCmd( $cmd ); + } + my $cmd = "setSharpness%26sharpness%3D10"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub focusMan +{ + my $self = shift; + Debug( "Focus Manu=Reset Sharpness" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DFOSCAM FI9821W Script V1.0 By Christophe_y2k"; + $self->sendCmd( $cmd ); + } +} + +sub whiteConIn +{ + my $self = shift; + my $params = shift; + my $speed = $self->getParam( $params, 'speed' ); + # Normalisation en cas de valeur erronée dans la base de données + if ( $speed > 100 ) { + $speed = 100; + } + if ( $speed < 0 ) { + $speed = 0; + } + Debug( "White ConIn=brightness" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DBrightness $speed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setBrightness%26brightness%3D$speed"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub whiteConOut +{ + my $self = shift; + my $params = shift; + my $speed = $self->getParam( $params, 'speed' ); + # Normalisation en cas de valeur erronée dans la base de données + if ( $speed > 100 ) { + $speed = 100; + } + if ( $speed < 0 ) { + $speed = 0; + } + Debug( "White ConOut=Contrast" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DContrast $speed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setContrast%26constrast%3D$speed"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub whiteAuto +{ + my $self = shift; + Debug( "White Auto=Brightness Reset" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DBrightness Reset"; + $self->sendCmd( $cmd ); + } + my $cmd = "setBrightness%26brightness%3D50"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub whiteMan +{ + my $self = shift; + Debug( "White Manuel=Contrast Reset" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DContrast Reset"; + $self->sendCmd( $cmd ); + } + my $cmd = "setContrast%26constrast%3D44"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub irisConOpen +{ + my $self = shift; + my $params = shift; + my $speed = $self->getParam( $params, 'speed' ); + # Normalisation en cas de valeur erronée dans la base de données + if ( $speed > 100 ) { + $speed = 100; + } + if ( $speed < 0 ) { + $speed = 0; + } + Debug( "Iris ConOpen=Saturation" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DSaturation $speed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setSaturation%26saturation%3D$speed"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub irisConClose +{ + my $self = shift; + my $params = shift; + my $speed = $self->getParam( $params, 'speed' ); + # Normalisation en cas de valeur erronée dans la base de données + if ( $speed > 100 ) { + $speed = 100; + } + if ( $speed < 0 ) { + $speed = 0; + } + Debug( "Iris ConClose=Hue" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DHue $speed"; + $self->sendCmd( $cmd ); + } + my $cmd = "setHue%26hue%3D$speed"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub irisAuto +{ + my $self = shift; + Debug( "Iris Auto=Saturation Reset" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DSaturation Reset"; + $self->sendCmd( $cmd ); + } + my $cmd = "setSaturation%26saturation%3D30"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub irisMan +{ + my $self = shift; + Debug( "Iris Manuel=Hue Reset" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DHue Reset"; + $self->sendCmd( $cmd ); + } + my $cmd = "setHue%26hue%3D6"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +sub presetSet +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + if ( ( $preset >= 1 ) && ( $preset <= 16 ) ) { + Debug( "Clear Preset $preset" ); + my $cmd = "ptzDeletePresetPoint%26name%3D$preset"; + $self->sendCmd( $cmd ); + Debug( "Set Preset $preset" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DSet Preset $preset"; + $self->sendCmd( $cmd ); + } + $cmd = "ptzAddPresetPoint%26name%3D$preset"; + $self->sendCmd( $cmd ); + } +} + +sub presetGoto +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + if ( ( $preset >= 1 ) && ( $preset <= 16 ) ) { + Debug( "Goto Preset $preset" ); + if ( $osd eq "on" ) + { + my $cmd = "setDevName%26devName%3DGoto Preset $preset"; + $self->sendCmd( $cmd ); + } + my $cmd = "setPTZSpeed%26speed%3D0"; + $self->sendCmd( $cmd ); + $cmd = "ptzGotoPresetPoint%26name%3D$preset"; + $self->sendCmd( $cmd ); + } +} + +1; +__END__ +# Below is stub documentation for your module. You'd better edit it! + +=head1 FI9831W + +ZoneMinder::Database - Perl extension for FOSCAM FI9831W + +=head1 SYNOPSIS + +Control script for Foscam HD cameras. Tested on 9831W but +should work on others too. + +=head1 DESCRIPTION + +Control script for Foscam HD cameras. Tested on 9831W but +should work on others too. +You need to set "usr=xxx&pwd=yyy" in the ControlDevice field +of the control tab for that monitor. +Auto TimeOut should be 1. Don't set it to less - processes +start crashing :) + +=head2 EXPORT + +None by default. + + + +=head1 SEE ALSO + +=head1 AUTHOR + +Philip Coombes, Ephilip.coombes@zoneminder.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2008 Philip Coombes + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + + +=cut + diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCC7210W.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCC7210W.pm new file mode 100755 index 000000000..cd21316d3 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCC7210W.pm @@ -0,0 +1,377 @@ +# ========================================================================== +# +# ZoneMinder IPCC-7210W IP Control Protocol Module, $Date: 2015-11-18$, $Revision: 0001$ +# Modified for use with IPCC-7210W on Nov 2015 +# +# 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 module contains the implementation of the +# IPCC-7210W IP camera control protocol +# +package ZoneMinder::Control::IPCC-7210W; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +# ========================================================================== +# +# IPCC-7210W IP Control Protocol +# +# ========================================================================== + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); + +use Time::HiRes qw( usleep ); + +sub new +{ + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new( $id ); + bless( $self, $class ); + srand( time() ); + return $self; +} + +our $AUTOLOAD; + +sub AUTOLOAD +{ + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + Fatal( "Can't access $name member of object of class $class" ); +} + +sub open +{ + my $self = shift; + + $self->loadMonitor(); + + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + + $self->{state} = 'open'; +} + +sub close +{ + my $self = shift; + $self->{state} = 'closed'; +} + +sub printMsg +{ + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); + + Debug( $msg."[".$msg_len."]" ); +} + +sub sendCmd +{ + my $self = shift; + my $cmd = shift; + + my $result = undef; + + printMsg( $cmd, "Tx" ); + + #print( "http://$address/$cmd\n" ); + my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd".$self->{Monitor}->{ControlDevice} ); + Info( "http://".$self->{Monitor}->{ControlAddress}."/$cmd".$self->{Monitor}->{ControlDevice} ); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) + { + $result = !undef; + } + else + { + Error( "Error check failed: '".$res->status_line()."'" ); + } + + return( $result ); +} + + +sub cameraReset +{ + my $self = shift; + Debug( "Camera Reset" ); + my $cmd = "reboot.cgi?"; + $self->sendCmd( $cmd ); +} + + +#Up Arrow +sub moveConUp +{ + my $self = shift; + my $params = shift; + Debug( "Move Up" ); + my $cmd = "decoder_control.cgi?command=0&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Down Arrow +sub moveConDown +{ + my $self = shift; + my $params = shift; + Debug( "Move Down" ); + my $cmd = "decoder_control.cgi?command=2&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Left Arrow +sub moveConLeft +{ + my $self = shift; + my $params = shift; + Debug( "Move Left" ); + my $cmd = "decoder_control.cgi?command=4&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Right Arrow +sub moveConRight +{ + my $self = shift; + my $params = shift; + Debug( "Move Right" ); + my $cmd = "decoder_control.cgi?command=6&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Diagonally Up Right Arrow +sub moveConUpRight +{ + my $self = shift; + my $params = shift; + Debug( "Move Diagonally Up Right" ); + my $cmd = "decoder_control.cgi?command=91&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Diagonally Down Right Arrow +sub moveConDownRight +{ + my $self = shift; + my $params = shift; + Debug( "Move Diagonally Down Right" ); + my $cmd = "decoder_control.cgi?command=93&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Diagonally Up Left Arrow +sub moveConUpLeft +{ + my $self = shift; + my $params = shift; + Debug( "Move Diagonally Up Left" ); + my $cmd = "decoder_control.cgi?command=90&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Diagonally Down Left Arrow +sub moveConDownLeft +{ + my $self = shift; + my $params = shift; + Debug( "Move Diagonally Down Left" ); + my $cmd = "decoder_control.cgi?command=92&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Stop +sub moveStop +{ + my $self = shift; + Debug( "Move Stop" ); + my $cmd = "decoder_control.cgi?command=1&onestep=1&"; + $self->sendCmd( $cmd ); +} + +#Move Camera to Home Position +sub presetHome +{ + my $self = shift; + Debug( "Home Preset" ); + my $cmd = "decoder_control.cgi?command=25&onestep=0&"; + $self->sendCmd( $cmd ); +} + +# zoom out +sub zoomRelTele +{ + my $self = shift; + Debug( "Zoom Tele" ); + my $cmd = "camera_control.cgi?param=17&value=1&"; + $self->sendCmd( $cmd ); +} + +#zoom in +sub zoomRelWide +{ + my $self = shift; + Debug( "Zoom Wide" ); + my $cmd = "camera_control.cgi?param=18&value=1&"; + $self->sendCmd( $cmd ); +} + + +#Set preset +sub presetSet +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + my $presetCmd = 30 + (($preset-1)*2); + Debug( "Set Preset $preset with cmd $presetCmd" ); + my $cmd = "decoder_control.cgi?command=$presetCmd&onestep=0&sit=$presetCmd&"; + $self->sendCmd( $cmd ); +} + +#Goto preset +sub presetGoto +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + my $presetCmd = 31 + (($preset-1)*2); + Debug( "Goto Preset $preset with cmd $presetCmd" ); + my $cmd = "decoder_control.cgi?command=$presetCmd&onestep=0&sit=$presetCmd&"; + $self->sendCmd( $cmd ); +} + +#Turn IR on +sub wake +{ + my $self = shift; + Debug( "Wake - IR on" ); + my $cmd = "camera_control.cgi?param=14&value=1&"; + $self->sendCmd( $cmd ); +} + +#Turn IR off +sub sleep +{ + my $self = shift; + Debug( "Sleep - IR off" ); + my $cmd = "camera_control.cgi?param=14&value=0&"; + $self->sendCmd( $cmd ); +} +1; +__END__ + + + +# Below is stub documentation for your module. You'd better edit it! + +=head1 NAME + +ZoneMinder::IPCC-7210W - Perl extension for IPCC-7210W PTZ control + +=head1 SYNOPSIS + + use ZoneMinder::Control::IPCC + +=head1 DESCRIPTION + +This script provides Pan/Tilt/Zoom control for IPCC-7210W camera. + +=head1 SEE ALSO + +ZoneMinder::Control::Wanscam + +=head1 AUTHOR + + + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2008 + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/M8640.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/M8640.pm index 584e04dd1..10bbea5de 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/M8640.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/M8640.pm @@ -107,14 +107,18 @@ sub sendCmd { printMsg( $cmd, "Tx" ); #print( "http://$address/$cmd\n" ); #my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd" ); - - my $url; - if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) { - $url = $self->{Monitor}->{ControlAddress}.'/cgi-bin/setGPIO.cgi?preventCache='.time; - } else { - $url = 'http://'.$self->{Monitor}->{ControlAddress}.'/cgi-bin/setGPIO.cgi?preventCache='.time; - } # en dif - Error("Url: $url $cmd"); + + my $url; + if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) { + $url = $self->{Monitor}->{ControlAddress} + .'/cgi-bin/setGPIO.cgi?preventCache='.time + ; + } else { + $url = 'http://'.$self->{Monitor}->{ControlAddress} + .'/cgi-bin/setGPIO.cgi?preventCache='.time + ; + } # en dif + Error("Url: $url $cmd"); my $uri = URI::Encode->new( { encode_reserved => 0 } ); my $encoded = $uri->encode( $cmd ); my $res = $self->{ua}->post( $url, Content=>"data=$encoded" ); @@ -203,7 +207,11 @@ sub moveMap my $xcoord = $self->getParam( $params, 'xcoord' ); my $ycoord = $self->getParam( $params, 'ycoord' ); Debug( "Move Map to $xcoord,$ycoord" ); - my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth=".$self->{Monitor}->{Width}."&imageheight=".$self->{Monitor}->{Height}; + my $cmd = "/axis-cgi/com/ptz.cgi?center=$xcoord,$ycoord&imagewidth=" + .$self->{Monitor}->{Width} + ."&imageheight=" + .$self->{Monitor}->{Height} + ; $self->sendCmd( $cmd ); } diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm new file mode 100644 index 000000000..0e273b5b7 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Netcat.pm @@ -0,0 +1,483 @@ +# ========================================================================== +# +# ZoneMinder Netcat IP Control Protocol Module, $Date: 2009-11-25 09:20:00 +0000 (Wed, 04 Nov 2009) $, $Revision: 0001 $ +# Copyright (C) 2001-2008 Philip Coombes +# Converted for use with Netcat IP Camera by Andrew Bauer (knnniggett@users.sourceforge.net) +# +# 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 module contains the first implementation of the Netcat IP camera control +# protocol +# +package ZoneMinder::Control::Netcat; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +our %CamParams = (); + +# ========================================================================== +# +# Netcat IP Control Protocol +# This script sends ONVIF compliant commands and may work with other cameras +# +# The Netcat camera gladly accepts any command with or without authentication, +# which prevented me from developing Onvif authentication in this control script. +# +# Basic preset functions are supported, but more advanced features, which make +# use of abnormally high preset numbers (ir lamp control, tours, pan speed, etc) +# may or may not work. +# +# +# Possible future improvements (for anyone to improve upon): +# - Onvif authentication +# - Build the SOAP commands at runtime rather than use templates +# - Implement previously mentioned advanced features +# +# Implementing the first two will require additional Perl modules, and adding +# more dependencies to ZoneMinder is always a concern. +# +# On ControlAddress use the format : +# ADDRESS:PORT +# eg : 10.1.2.1:8899 +# 10.0.100.1:8899 +# +# Use port 8899 for the Netcat camera +# +# Make sure and place a value in the Auto Stop Timeout field. +# Recommend starting with a value of 1 second, and adjust accordingly. +# +# ========================================================================== + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); + +use Time::HiRes qw( usleep ); + +sub new +{ + + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new( $id ); + my $logindetails = ""; + bless( $self, $class ); + srand( time() ); + return $self; +} + +our $AUTOLOAD; + +sub AUTOLOAD +{ + my $self = shift; + my $class = ref( ) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + Fatal( "Can't access $name member of object of class $class" ); + } + +sub open +{ + my $self = shift; + + $self->loadMonitor(); + + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + + $self->{state} = 'open'; +} + +sub close +{ + my $self = shift; + $self->{state} = 'closed'; +} + +sub printMsg +{ + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); + + Debug( $msg."[".$msg_len."]" ); +} + +sub sendCmd +{ + my $self = shift; + my $cmd = shift; + my $msg = shift; + my $content_type = shift; + my $result = undef; + + printMsg( $cmd, "Tx" ); + + my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/$cmd"; + my $req = HTTP::Request->new( POST => $server_endpoint ); + $req->header('content-type' => $content_type); + $req->header('Host' => $self->{Monitor}->{ControlAddress}); + $req->header('content-length' => length($msg)); + $req->header('accept-encoding' => 'gzip, deflate'); + $req->header('connection' => 'Close'); + $req->content($msg); + + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + $result = !undef; + } else { + Error( "After sending PTZ command, camera returned the following error:'".$res->status_line()."'" ); + } + return( $result ); +} + +sub getCamParams +{ + my $self = shift; + my $msg = '000'; + my $server_endpoint = "http://".$self->{Monitor}->{ControlAddress}."/onvif/imaging"; + my $req = HTTP::Request->new( POST => $server_endpoint ); + $req->header('content-type' => 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/GetImagingSettings"'); + $req->header('Host' => $self->{Monitor}->{ControlAddress}); + $req->header('content-length' => length($msg)); + $req->header('accept-encoding' => 'gzip, deflate'); + $req->header('connection' => 'Close'); + $req->content($msg); + + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) { + # We should really use an xml or soap library to parse the xml tags + my $content = $res->decoded_content; + + if ($content =~ /.*(.+)<\/tt:Brightness>.*/) { + $CamParams{$1} = $2; + } + if ($content =~ /.*(.+)<\/tt:Contrast>.*/) { + $CamParams{$1} = $2; + } + } + else + { + Error( "Unable to retrieve camera image settings:'".$res->status_line()."'" ); + } +} + +#autoStop +#This makes use of the ZoneMinder Auto Stop Timeout on the Control Tab +sub autoStop +{ + my $self = shift; + my $autostop = shift; + + if( $autostop ) { + Debug( "Auto Stop" ); + my $cmd = 'onvif/PTZ'; + my $msg = '000truefalse'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + usleep( $autostop ); + $self->sendCmd( $cmd, $msg, $content_type ); + } +} + +# Reset the Camera +sub reset +{ + Debug( "Camera Reset" ); + my $self = shift; + my $cmd = ""; + my $msg = ''; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver10/device/wsdl/SystemReboot"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +#Up Arrow +sub moveConUp +{ + Debug( "Move Up" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Down Arrow +sub moveConDown +{ + Debug( "Move Down" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Left Arrow +sub moveConLeft +{ + Debug( "Move Left" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Right Arrow +sub moveConRight +{ + Debug( "Move Right" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Zoom In +sub zoomConTele +{ + Debug( "Zoom Tele" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Zoom Out +sub zoomConWide +{ + Debug( "Zoom Wide" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Up Right Arrow +#This camera does not have builtin diagonal commands so we emulate them +sub moveConUpRight +{ + Debug( "Move Diagonally Up Right" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Down Right Arrow +#This camera does not have builtin diagonal commands so we emulate them +sub moveConDownRight +{ + Debug( "Move Diagonally Down Right" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Up Left Arrow +#This camera does not have builtin diagonal commands so we emulate them +sub moveConUpLeft +{ + Debug( "Move Diagonally Up Left" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Down Left Arrow +#This camera does not have builtin diagonal commands so we emulate them +sub moveConDownLeft +{ + Debug( "Move Diagonally Down Left" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Stop +sub moveStop +{ + Debug( "Move Stop" ); + my $self = shift; + my $cmd = 'onvif/PTZ'; + my $msg ='000truefalse'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +#Set Camera Preset +sub presetSet +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Set Preset $preset" ); + my $cmd = 'onvif/PTZ'; + my $msg ='000'.$preset.''; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/SetPreset"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +#Recall Camera Preset +sub presetGoto +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + Debug( "Goto Preset $preset" ); + my $cmd = 'onvif/PTZ'; + my $msg ='000'.$preset.''; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/GotoPreset"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +#Horizontal Patrol +#To be determined if this camera supports this feature +sub horizontalPatrol +{ + Debug( "Horizontal Patrol" ); + my $self = shift; + my $cmd = ''; + my $msg =''; + my $content_type = ''; +# $self->sendCmd( $cmd, $msg, $content_type ); + Error( "PTZ Command not implemented in control script." ); +} + +#Horizontal Patrol Stop +#To be determined if this camera supports this feature +sub horizontalPatrolStop +{ + Debug( "Horizontal Patrol Stop" ); + my $self = shift; + my $cmd = ''; + my $msg =''; + my $content_type = ''; +# $self->sendCmd( $cmd, $msg, $content_type ); + Error( "PTZ Command not implemented in control script." ); +} + +# Increase Brightness +sub irisAbsOpen +{ + Debug( "Iris $CamParams{'Brightness'}" ); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{'Brightness'}); + my $step = $self->getParam( $params, 'step' ); + my $max = 100; + + $CamParams{'Brightness'} += $step; + $CamParams{'Brightness'} = $max if ($CamParams{'Brightness'} > $max); + + my $cmd = 'onvif/imaging'; + my $msg ='000'.$CamParams{'Brightness'}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +# Decrease Brightness +sub irisAbsClose +{ + Debug( "Iris $CamParams{'Brightness'}" ); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{'brightness'}); + my $step = $self->getParam( $params, 'step' ); + my $min = 0; + + $CamParams{'Brightness'} -= $step; + $CamParams{'Brightness'} = $min if ($CamParams{'Brightness'} < $min); + + my $cmd = 'onvif/imaging'; + my $msg ='000'.$CamParams{'Brightness'}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; + $self->sendCmd( $cmd, $msg, $content_type ); +} + +# Increase Contrast +sub whiteAbsIn +{ + Debug( "Iris $CamParams{'Contrast'}" ); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{'Contrast'}); + my $step = $self->getParam( $params, 'step' ); + my $max = 100; + + $CamParams{'Contrast'} += $step; + $CamParams{'Contrast'} = $max if ($CamParams{'Contrast'} > $max); + + my $cmd = 'onvif/imaging'; + my $msg ='000'.$CamParams{'Contrast'}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; +} + +# Decrease Contrast +sub whiteAbsOut +{ + Debug( "Iris $CamParams{'Contrast'}" ); + my $self = shift; + my $params = shift; + $self->getCamParams() unless($CamParams{'Contrast'}); + my $step = $self->getParam( $params, 'step' ); + my $min = 0; + + $CamParams{'Contrast'} -= $step; + $CamParams{'Contrast'} = $min if ($CamParams{'Contrast'} < $min); + + my $cmd = 'onvif/imaging'; + my $msg ='000'.$CamParams{'Contrast'}.'true'; + my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/imaging/wsdl/SetImagingSettings"'; +} + +1; + diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/PelcoP.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/PelcoP.pm index c9d9d907d..794393644 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/PelcoP.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/PelcoP.pm @@ -22,7 +22,7 @@ # This module contains the implementation of the Pelco-P camera control # protocol # -package ZoneMinder::Control::PelcoD; +package ZoneMinder::Control::PelcoP; use 5.006; use strict; @@ -133,8 +133,10 @@ sub sendCmd my $result = undef; + # Pelco P protocol checksum is created by XOR'ing the first seven bytes in the message + # including the first byte, the STX sync packet my $checksum = 0x00; - for ( my $i = 1; $i < int(@$cmd); $i++ ) + for ( my $i = 0; $i < int(@$cmd); $i++ ) { $checksum ^= $cmd->[$i]; } diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/SPP1802SWPTZ.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/SPP1802SWPTZ.pm new file mode 100644 index 000000000..77d2da751 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/SPP1802SWPTZ.pm @@ -0,0 +1,353 @@ +# Modified on Sep 28 2015 by Bobby Billingsley +# Changes made +# - Copied FI8918W.pm to SPP1802SWPTZ.pm +# - modified to control a SunEyes SP-P1802SWPTZ + +# ========================================================================== +# ZoneMinder SunEyes SP-P1802SWPTZ IP Control Protocol Module +# +# 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 module contains the implementation of the SunEyes SP-P1802SWPTZ IP +# camera control protocol +# + +package MyAgent; + +use base 'LWP::UserAgent'; + + +package ZoneMinder::Control::SPP1802SWPTZ; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +our $VERSION = $ZoneMinder::Base::VERSION; + +# ========================================================================== +# +# SunEyes SP-P1802SWPTZ IP Control Protocol +# +# ========================================================================== + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); + + use Time::HiRes qw( usleep ); + +sub new +{ + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new( $id ); + my $logindetails = ""; + bless( $self, $class ); + srand( time() ); + return $self; +} + +our $AUTOLOAD; + +sub AUTOLOAD +{ + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + Fatal( "Can't access $name member of object of class $class" ); +} +our $stop_command; + +sub open +{ + my $self = shift; + + $self->loadMonitor(); + + $self->{ua} = MyAgent->new; + $self->{ua}->agent( "ZoneMinder Control Agent/" ); + + $self->{state} = 'open'; +} + +sub close +{ + my $self = shift; + $self->{state} = 'closed'; +} + +sub printMsg +{ + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); + + Debug( $msg."[".$msg_len."]" ); +} + +sub sendCmd +{ + my $self = shift; + my $cmd = shift; + my $result = undef; + printMsg( $cmd, "Tx" ); + + # PP Old cameras also support onstep=1 but it is too granular. Instead using moveCon and stop after interval + # PP - cleaned up URL to take it properly from Control device + # Control device needs to be of format user=xxx&pwd=yyy + my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd"."&".$self->{Monitor}->{ControlDevice}); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) + { + $result = !undef; + } + else + { + Error( "Error really, REALLY check failed:'".$res->status_line()."'" ); + Error ("Cmd:".$cmd); + } + + return( $result ); +} + +sub reset +{ + my $self = shift; + Debug( "Camera Reset" ); + my $cmd = "reboot.cgi?"; + $self->sendCmd( $cmd ); +} + +# PP - in all move operations, added auto stop after timeout + +#Up Arrow +sub moveConUp +{ + my $self = shift; + Debug( "Move Up" ); + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=up"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Down Arrow +sub moveConDown +{ + my $self = shift; + Debug( "Move Down" ); + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=down"; + $self->sendCmd( $cmd ); +} + +#Left Arrow +sub moveConLeft +{ + my $self = shift; + Debug( "Move Left" ); + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=left"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Right Arrow +sub moveConRight +{ + my $self = shift; + Debug( "Move Right" ); + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=right"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); +} + +#Diagonally Up Right Arrow +sub moveConUpRight +{ + my $self = shift; + Debug( "Move Diagonally Up Right" ); + foreach my $dir ("up","right") { + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=$dir"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); + } +} + +#Diagonally Down Right Arrow +sub moveConDownRight +{ + my $self = shift; + Debug( "Move Diagonally Down Right" ); + foreach my $dir ("down","right") { + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=$dir"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); + } +} + +#Diagonally Up Left Arrow +sub moveConUpLeft +{ + my $self = shift; + Debug( "Move Diagonally Up Left" ); + foreach my $dir ("up","left") { + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=$dir"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); + } +} + +#Diagonally Down Left Arrow +sub moveConDownLeft +{ + my $self = shift; + Debug( "Move Diagonally Down Left" ); + foreach my $dir ("down","left") { + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=$dir"; + $self->sendCmd( $cmd ); + $self->autoStop( $self->{Monitor}->{AutoStopTimeout} ); + } +} + +#Stop +sub moveStop +{ + my $self = shift; + Debug( "Move Stop" ); + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=stop"; + $self->sendCmd( $cmd ); +} + +# PP - imported from 9831 - autostop after usleep +sub autoStop +{ + my $self = shift; + my $autostop = shift; + if( $autostop ) { + Debug( "Auto Stop" ); + usleep( $autostop ); + my $cmd = "cgi-bin/hi3510/ptzctrl.cgi?&-chn=0&-act=stop"; + $self->sendCmd( $cmd ); + } +} + +#Move Camera to Home Position +sub presetHome +{ + my $self = shift; + Debug( "Home Preset" ); + my $cmd = "ptzgotopoint.cgi?&-chn=0&-point=1"; + $self->sendCmd( $cmd ); +} + +#Set preset +sub presetSet +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + my $presetCmd = "cgi-bin/hi3510/ptzsetpoint.cgi?&-chn=0&-point=$preset"; + Debug( "Set Preset $preset with cmd $presetCmd" ); + my $cmd = $presetCmd; + $self->sendCmd( $cmd ); +} + +#Goto preset +sub presetGoto +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + my $presetCmd = "cgi-bin/hi3510/ptzgotopoint.cgi?&-chn=0&-point=$preset"; + + Debug( "Set Preset $preset with cmd $presetCmd" ); + my $cmd = $presetCmd; + $self->sendCmd( $cmd ); +} + +#Turn IR on +sub wake +{ + my $self = shift; + Debug( "Wake - IR on" ); + my $cmd = "decoder_control.cgi?command=95"; + $self->sendCmd( $cmd ); +} + +#Turn IR off +sub sleep +{ + my $self = shift; + Debug( "Sleep - IR off" ); + my $cmd = "decoder_control.cgi?command=94"; + $self->sendCmd( $cmd ); +} + +1; +__END__ + +=head1 SPP1802SWPTZ + +ZoneMinder::Database - Perl extension for SunEyes SP-P1802SWPTZ + +=head1 SYNOPSIS + +Control script for SunEyes SP-P1802SWPTZ cameras. + +=head1 DESCRIPTION + +You can set "-speed=x" in the ControlDevice field of the control tab for +that monitor. x should be an integer between 0 and 64 +Auto TimeOut should be 1. Don't set it to less - processes +start crashing :) + +=head2 EXPORT + +None by default. + + + +=head1 SEE ALSO + +=head1 AUTHOR + +Bobby Billingsley, Ebobby(at)bofh(dot)dkE +based on the work of: +Pliable Pixels, https://github.com/pliablepixels + +git checkout -b SunEyes_sp-p1802swptz + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2015- Bobby Billingsley + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + + +=cut + diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm index fd97c5a49..f3fc94754 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/SkyIPCam7xx.pm @@ -107,7 +107,14 @@ sub sendCmd printMsg( $cmd, "Tx" ); - my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."$cmd" ); + my $url; + if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) { + $url = $self->{Monitor}->{ControlAddress}.$cmd; + } else { + $url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd; + } # en dif + my $req = HTTP::Request->new( GET=>$url ); + my $res = $self->{ua}->request($req); if ( $res->is_success ) diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/TVIP862.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/TVIP862.pm index 204652c17..d7c669583 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/TVIP862.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/TVIP862.pm @@ -20,8 +20,6 @@ # This module contains the implementation of the Trendnet TV-IP672PI IP camera control # protocol. Also works or TV-IP862IC # -# -# # For Zoneminder 1.26+ # # Under control capability: @@ -62,16 +60,17 @@ our @ISA = qw(ZoneMinder::Control); # # ******** YOU MUST CHANGE THE FOLLOWING LINES TO MATCH YOUR CAMERA! ********** -# +# # I assume that "TV-IP672WI" would work for the TV-IP672WI, but can't test since I don't own one. -# +# # TV-IP672PI works for the PI version, of course. # # Finally, the username is the username you'd like to authenticate as. # -our $REALM = "TV-IP862IC"; -our $USERNAME = "admin"; - +our $REALM = 'TV-IP862IC'; +our $USERNAME = 'admin'; +our $PASSWORD = ''; +our $ADDRESS = ''; # ========================================================================== # @@ -82,8 +81,6 @@ our $USERNAME = "admin"; use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); -use Time::HiRes qw( usleep ); - sub new { my $class = shift; @@ -112,13 +109,59 @@ sub AUTOLOAD sub open { my $self = shift; - $self->loadMonitor(); + my ( $protocol, $username, $password, $address ) + = $self->{Monitor}->{ControlAddress} =~ /^(https?:\/\/)?([^:]+):([^\/@]+)@(.*)$/; + if ( $username ) { + $USERNAME = $username; + $PASSWORD = $password; + $ADDRESS = $address; + } else { + Error( "Failed to parse auth from address"); + $ADDRESS = $self->{Monitor}->{ControlAddress}; + } + if ( ! $ADDRESS =~ /:/ ) { + Error( "You generally need to also specify the port. I will append :80" ); + $ADDRESS .= ':80'; + } + use LWP::UserAgent; $self->{ua} = LWP::UserAgent->new; $self->{ua}->agent( "ZoneMinder Control Agent/".$ZoneMinder::Base::ZM_VERSION ); $self->{state} = 'open'; +# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string) + Debug ( "sendCmd credentials control address:'".$ADDRESS + ."' realm:'" . $REALM + . "' username:'" . $USERNAME + . "' password:'".$PASSWORD + ."'" + ); + $self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD); + + # Detect REALM + my $req = HTTP::Request->new( GET=>"http://".$ADDRESS."/cgi/ptdc.cgi" ); + my $res = $self->{ua}->request($req); + + if ( ! $res->is_success ) { + Debug("Need newer REALM"); + if ( $res->status_line() eq '401 Unauthorized' ) { + my $headers = $res->headers(); + foreach my $k ( keys %$headers ) { + Debug("Initial Header $k => $$headers{$k}"); + } # end foreach + if ( $$headers{'www-authenticate'} ) { + my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/; + if ( $tokens =~ /\w+="([^"]+)"/i ) { + $REALM = $1; + Debug( "Changing REALM to $REALM" ); + $self->{ua}->credentials($ADDRESS,$REALM,$USERNAME,$PASSWORD); + } # end if + } else { + Debug("No headers line"); + } # end if headers + } # end if $res->status_line() eq '401 Unauthorized' + } # end if ! $res->is_success } sub close @@ -146,34 +189,29 @@ sub sendCmd my $result = undef; - Debug ( $cmd, "Tx" ); + my $url = "http://".$ADDRESS."/cgi/ptdc.cgi?command=".$cmd; + my $req = HTTP::Request->new( GET=>$url ); - my $ua = LWP::UserAgent->new(); + Debug ("sendCmd command: " . $url ); - my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/cgi/ptdc.cgi?command=".$cmd ); - -# credentials: ("ip:port" (no prefix!), realm (string), username (string), password (string) - $self->{ua}->credentials($self->{Monitor}->{ControlAddress},$REALM,$USERNAME,$self->{Monitor}->{ControlDevice}); - - Debug ( "sendCmd credentials control address:'".$self->{Monitor}->{ControlAddress}."' realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$self->{Monitor}->{ControlDevice}."'"); - - Debug ("sendCmd command: " . $cmd); - my $res = $self->{ua}->request($req); if ( $res->is_success ) { $result = !undef; } else { - if ( $res->status_line() eq '401 Unauthorized' ) { - Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $self->{Monitor}->{ControlDevice} ); - my $res = $self->{ua}->request($req); - if ( $res->is_success ) { - $result = !undef; - } - } - if ( ! $result ) { - Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" ); - } + if ( $res->status_line() eq '401 Unauthorized' ) { + Error( "Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD ); + Error("Content was " . $res->content() ); + my $res = $self->{ua}->request($req); + if ( $res->is_success ) { + $result = !undef; + } else { + Error("Content was " . $res->content() ); + } + } + if ( ! $result ) { + Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" ); + } } return( $result ); @@ -194,26 +232,20 @@ sub sendCmdPost my $result = undef; - if ($url eq undef) - { - Error ("url passed to sendCmdPost is undefined."); - return(-1); - } + { + Error ("url passed to sendCmdPost is undefined."); + return(-1); + } Debug ("sendCmdPost url: " . $url . " cmd: " . $cmd); - my $ua = LWP::UserAgent->new(); - - my $req = HTTP::Request->new(POST => "http://".$self->{Monitor}->{ControlAddress}.$url); + my $req = HTTP::Request->new(POST => "http://".$ADDRESS.$url); $req->content_type('application/x-www-form-urlencoded'); $req->content($cmd); - $self->{ua}->credentials($self->{Monitor}->{ControlAddress},$REALM,$USERNAME,$self->{Monitor}->{ControlDevice}); + Debug ( "sendCmdPost credentials control address:'".$ADDRESS."' realm:'" . $REALM . "' username:'" . $USERNAME . "' password:'".$PASSWORD."'"); - Debug ( "sendCmdPost credentials control address:'".$self->{Monitor}->{ControlAddress}."' realm:'" . $REALM . "' username:'" . $USERNAME . "' -password:'".$self->{Monitor}->{ControlDevice}."'"); - my $res = $self->{ua}->request($req); if ( $res->is_success ) @@ -222,13 +254,12 @@ password:'".$self->{Monitor}->{ControlDevice}."'"); } else { - Error( "Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $self->{Monitor}->{ControlDevice} ); - Error( "Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" ); - if ( $res->status_line() eq '401 Unauthorized' ) { - Error( "Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $self->{Monitor}->{ControlDevice} ); - } else { - Error( "Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $self->{Monitor}->{ControlDevice} ); - } # endif + Error( "sendCmdPost Error check failed: '".$res->status_line()."' cmd:'".$cmd."'" ); + if ( $res->status_line() eq '401 Unauthorized' ) { + Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD ); + } else { + Error( "sendCmdPost Error check failed: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD ); + } # endif } return( $result ); diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm new file mode 100644 index 000000000..3649b35bc --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Vivotek_ePTZ.pm @@ -0,0 +1,227 @@ +# ========================================================================== +# +# ZoneMinder Vivotek ePTZ Control Protocol Module +# Copyright (C) 2015 Robin Daermann +# +# 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 module contains the implementation of the Vivotek ePTZ camera control +# protocol +# +package ZoneMinder::Control::Vivotek_ePTZ; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +# ========================================================================== +# +# Vivotek ePTZ Control Protocol +# +# ========================================================================== + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); + +use Time::HiRes qw( usleep ); + +sub new +{ + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new( $id ); + Debug( "Camera New" ); + bless( $self, $class ); + srand( time() ); + return $self; +} + +our $AUTOLOAD; + +sub AUTOLOAD +{ + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + Debug( "Camera AUTOLOAD" ); + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + Fatal( "Can't access $name member of object of class $class" ); +} + +sub open +{ + my $self = shift; + + $self->loadMonitor(); + Debug( "Camera open" ); + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + + $self->{state} = 'open'; +} + +sub close +{ + my $self = shift; + $self->{state} = 'closed'; +} + +sub printMsg +{ + my $msg = shift; + my $msg_len = length($msg); + + Debug( $msg."[".$msg_len."]" ); +} + +sub sendCmd +{ + my ($self, $cmd, $speedcmd) = @_; + + my $result = undef; + + printMsg( $speedcmd, "Tx" ); + printMsg( $cmd, "Tx" ); + + my $req = HTTP::Request->new( GET => "http://" . $self->{Monitor}->{ControlAddress} . "/cgi-bin/camctrl/eCamCtrl.cgi?stream=0&$speedcmd&$cmd" ); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) + { + $result = !undef; + } + else + { + Error( "Request failed: '" . $res->status_line() . "' (URI: '" . $req->as_string() . "')" ); + } + + return( $result ); +} + +sub moveConUp +{ + my ($self, $params) = @_; + my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6); + Debug( "Move Up" ); + $self->sendCmd( 'move=up', $speed ); +} + +sub moveConDown +{ + my ($self, $params) = @_; + my $speed = 'speedtilt=' . ($params->{tiltspeed} - 6); + Debug( "Move Down" ); + $self->sendCmd( 'move=down', $speed ); +} + +sub moveConLeft +{ + my ($self, $params) = @_; + my $speed = 'speedpan=-' . $params->{panspeed}; + Debug( "Move Left" ); + $self->sendCmd( 'move=left', $speed ); +} + +sub moveConRight +{ + my ($self, $params) = @_; + my $speed = 'speedpan=' . ($params->{panspeed} - 6); + Debug( "Move Right" ); + $self->sendCmd( 'move=right', $speed ); +} + +sub moveStop +{ + my $self = shift; + Debug( "Move Stop" ); + # not implemented +} + +sub zoomConTele +{ + my ($self, $params) = @_; + my $speed = 'speedzoom=' . ($params->{speed} - 6); + Debug( "Zoom In" ); + $self->sendCmd( 'zoom=tele', $speed ); +} + +sub zoomConWide +{ + my ($self, $params) = @_; + my $speed = 'speedzoom=' . ($params->{speed} - 6); + Debug( "Zoom Out" ); + $self->sendCmd( 'zoom=wide', $speed ); +} + +sub reset +{ + my $self = shift; + Debug( "Camera Reset" ); + $self->sendCmd( 'move=home' ); +} + +1; +__END__ + +=head1 NAME + +ZoneMinder::Control::Vivotek_ePTZ - ZoneMinder Perl extension for Vivotek ePTZ +camera control protocol + +=head1 SYNOPSIS + + use ZoneMinder::Control::Vivotek_ePTZ; + +=head1 DESCRIPTION + +This module implements the ePTZ protocol used in various Vivotek IP cameras, +developed with a Vivotek IB8369 model. + +Currently, only simple pan, tilt and zoom function is implemented. Presets will +follow later. + +=head2 EXPORT + +None. + +=head1 SEE ALSO + +I would say, see ZoneMinder::Control documentation. But it is a stub. + +=head1 AUTHOR + +Robin Daermann Er.daermann@ids-services.deE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2015 by Robin Daermann + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Wanscam.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Wanscam.pm index b2ed3e2b5..f419aacfc 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Wanscam.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Wanscam.pm @@ -16,8 +16,8 @@ # Rename to Wanscam # Pan Left/Right switched # IR On/Off switched -# Brightness Increase/Decrease in 16 steps -# +# Brightness Increase/Decrease in 16 steps +# # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/WanscamHW0025.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/WanscamHW0025.pm new file mode 100755 index 000000000..3dd9c2cc5 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/WanscamHW0025.pm @@ -0,0 +1,399 @@ +# ========================================================================== +# +# ZoneMinder Wanscam HW0025 IP Control Protocol Module, $Date: 2009-11-25 09:20:00 +0000 (Wed, 04 Nov 2009) $, $Revision: 0001 $ +# Copyright (C) 2001-2015 Florian Neumair +# Modified for use with Foscam FI8918W IP Camera by Dave Harris +# Modified Feb 2011 by Howard Durdle (http://durdl.es/x) to: +# fix horizontal panning, add presets and IR on/off +# use Control Device field to pass username and password +# +# 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 module contains the implementation of the Wanscam HW0025 IP camera control +# protocol +# +# Known working Devices: +# * Wanscam HW0025 +# * IPCC-7210W +# +package ZoneMinder::Control::WanscamHW0025; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +# ========================================================================== +# +# Wanscam HW0025 IP Control Protocol +# +# ========================================================================== + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); + +use Time::HiRes qw( usleep ); + +sub new +{ + my $class = shift; + my $id = shift; + my $self = ZoneMinder::Control->new( $id ); + bless( $self, $class ); + srand( time() ); + return $self; +} + +our $AUTOLOAD; + +sub AUTOLOAD +{ + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + Fatal( "Can't access $name member of object of class $class" ); +} + +sub open +{ + my $self = shift; + + $self->loadMonitor(); + + use LWP::UserAgent; + $self->{ua} = LWP::UserAgent->new; + $self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION ); + + $self->{state} = 'open'; +} + +sub close +{ + my $self = shift; + $self->{state} = 'closed'; +} + +sub printMsg +{ + my $self = shift; + my $msg = shift; + my $msg_len = length($msg); + + Debug( $msg."[".$msg_len."]" ); +} + +sub sendCmd +{ + my $self = shift; + my $cmd = shift; + + my $result = undef; + + printMsg( $cmd, "Tx" ); + + #print( "http://$address/$cmd\n" ); + my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/$cmd".$self->{Monitor}->{ControlDevice} ); + Info( "http://".$self->{Monitor}->{ControlAddress}."/$cmd".$self->{Monitor}->{ControlDevice} ); + my $res = $self->{ua}->request($req); + + if ( $res->is_success ) + { + $result = !undef; + } + else + { + Error( "Error check failed: '".$res->status_line()."'" ); + } + + return( $result ); +} + + +sub cameraReset +{ + my $self = shift; + Debug( "Camera Reset" ); + my $cmd = "reboot.cgi?"; + $self->sendCmd( $cmd ); +} + +#Up Arrow +sub moveConUp +{ + my $self = shift; + my $params = shift; + Debug( "Move Up" ); + my $cmd = "decoder_control.cgi?command=0&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Down Arrow +sub moveConDown +{ + my $self = shift; + my $params = shift; + Debug( "Move Down" ); + my $cmd = "decoder_control.cgi?command=2&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Left Arrow +sub moveConLeft +{ + my $self = shift; + my $params = shift; + Debug( "Move Left" ); + my $cmd = "decoder_control.cgi?command=4&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Right Arrow +sub moveConRight +{ + my $self = shift; + my $params = shift; + Debug( "Move Right" ); + my $cmd = "decoder_control.cgi?command=6&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Diagonally Up Right Arrow +sub moveConUpRight +{ + my $self = shift; + my $params = shift; + Debug( "Move Diagonally Up Right" ); + my $cmd = "decoder_control.cgi?command=91&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Diagonally Down Right Arrow +sub moveConDownRight +{ + my $self = shift; + my $params = shift; + Debug( "Move Diagonally Down Right" ); + my $cmd = "decoder_control.cgi?command=93&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Diagonally Up Left Arrow +sub moveConUpLeft +{ + my $self = shift; + my $params = shift; + Debug( "Move Diagonally Up Left" ); + my $cmd = "decoder_control.cgi?command=90&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Diagonally Down Left Arrow +sub moveConDownLeft +{ + my $self = shift; + my $params = shift; + Debug( "Move Diagonally Down Left" ); + my $cmd = "decoder_control.cgi?command=92&onestep=1&"; + $self->sendCmd( $cmd ); + my $autostop = $self->getParam( $params, 'autostop', 0 ); + if ( $autostop && $self->{Monitor}->{AutoStopTimeout} ) + { + usleep( $self->{Monitor}->{AutoStopTimeout} ); + $self->moveStop( $params ); + } +} + +#Stop +sub moveStop +{ + my $self = shift; + Debug( "Move Stop" ); + my $cmd = "decoder_control.cgi?command=1&onestep=1&"; + $self->sendCmd( $cmd ); +} + +#Move Camera to Home Position +sub presetHome +{ + my $self = shift; + Debug( "Home Preset" ); + my $cmd = "decoder_control.cgi?command=25&onestep=0&"; + $self->sendCmd( $cmd ); +} + +# zoom out +sub zoomRelTele +{ + my $self = shift; + Debug( "Zoom Tele" ); + my $cmd = "camera_control.cgi?param=17&value=1&"; + $self->sendCmd( $cmd ); +} + +#zoom in +sub zoomRelWide +{ + my $self = shift; + Debug( "Zoom Wide" ); + my $cmd = "camera_control.cgi?param=18&value=1&"; + $self->sendCmd( $cmd ); +} + + +#Set preset +sub presetSet +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + my $presetCmd = 30 + (($preset-1)*2); + Debug( "Set Preset $preset with cmd $presetCmd" ); + my $cmd = "decoder_control.cgi?command=$presetCmd&onestep=0&sit=$presetCmd&"; + $self->sendCmd( $cmd ); +} + +#Goto preset +sub presetGoto +{ + my $self = shift; + my $params = shift; + my $preset = $self->getParam( $params, 'preset' ); + my $presetCmd = 31 + (($preset-1)*2); + Debug( "Goto Preset $preset with cmd $presetCmd" ); + my $cmd = "decoder_control.cgi?command=$presetCmd&onestep=0&sit=$presetCmd&"; + $self->sendCmd( $cmd ); +} + +#Turn IR on +sub wake +{ + my $self = shift; + Debug( "Wake - IR on" ); + my $cmd = "camera_control.cgi?param=14&value=1&"; + $self->sendCmd( $cmd ); +} + +#Turn IR off +sub sleep +{ + my $self = shift; + Debug( "Sleep - IR off" ); + my $cmd = "camera_control.cgi?param=14&value=0&"; + $self->sendCmd( $cmd ); +} +1; +__END__ +# Below is stub documentation for your module. You'd better edit it! + +=head1 NAME + +ZoneMinder::Database - Perl extension for blah blah blah + +=head1 SYNOPSIS + + use ZoneMinder::Control::Wanscam + blah blah blah + +=head1 DESCRIPTION + +Stub documentation for ZoneMinder, created by h2xs. It looks like the +author of the extension was negligent enough to leave the stub +unedited. + +Blah blah blah. +=head2 EXPORT + +None by default. + + + +=head1 SEE ALSO + +Mention other useful documentation such as the documentation of +related modules or operating system documentation (such as man pages +in UNIX), or any relevant external documentation such as RFCs or +standards. + +If you have a mailing list set up for your module, mention it here. + +If you have a web site set up for your module, mention it here. + +=head1 AUTHOR + +Philip Coombes, + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2008 Philip Coombes + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/mjpgStreamer.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/mjpgStreamer.pm index 255dac056..18a73cae8 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/mjpgStreamer.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/mjpgStreamer.pm @@ -126,32 +126,32 @@ sub sendCmd sub Up { my $self = shift; - $self->moveConUp(); + $self->moveConUp(); } sub Down { my $self = shift; - $self->moveConDown(); + $self->moveConDown(); } sub Left { my $self = shift; - $self->moveConLeft(); + $self->moveConLeft(); } sub Right { my $self = shift; - $self->moveConRight(); + $self->moveConRight(); } sub reset { my $self = shift; - $self->cameraReset(); + $self->cameraReset(); } diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/onvif.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/onvif.pm index 09ef59c8e..76b161582 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/onvif.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/onvif.pm @@ -36,7 +36,7 @@ our %CamParams = (); # ========================================================================== # # ONVIF Control Protocol -# +# # On ControlAddress use the format : # USERNAME:PASSWORD@ADDRESS:PORT # eg : admin:@10.1.2.1:80 @@ -50,7 +50,7 @@ use ZoneMinder::Config qw(:all); use Time::HiRes qw( usleep ); sub new -{ +{ my $class = shift; my $id = shift; @@ -90,7 +90,7 @@ sub open } sub close -{ +{ my $self = shift; $self->{state} = 'closed'; } @@ -133,7 +133,7 @@ sub getCamParams my $req = HTTP::Request->new( GET=>"http://".$self->{Monitor}->{ControlAddress}."/get_camera_params.cgi" ); my $res = $self->{ua}->request($req); - if ( $res->is_success ) + if ( $res->is_success ) { # Parse results setting values in %FCParams my $content = $res->decoded_content; @@ -141,7 +141,7 @@ sub getCamParams while ($content =~ s/var\s+([^=]+)=([^;]+);//ms) { $CamParams{$1} = $2; } - } + } else { Error( "Error check failed:'".$res->status_line()."'" ); diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm index 29b3c8b2a..7dfe0654b 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm @@ -19,7 +19,7 @@ # # ========================================================================== # -# This module contains the common definitions and functions used by the rest +# This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Database; @@ -37,17 +37,17 @@ our @ISA = qw(Exporter ZoneMinder::Base); # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. -# This allows declaration use ZoneMinder ':all'; +# This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( 'functions' => [ qw( - zmDbConnect - zmDbDisconnect - zmDbGetMonitors - zmDbGetMonitor - zmDbGetMonitorAndControl - ) ] + zmDbConnect + zmDbDisconnect + zmDbGetMonitors + zmDbGetMonitor + zmDbGetMonitorAndControl + ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; @@ -70,37 +70,46 @@ use Carp; our $dbh = undef; -sub zmDbConnect( ;$ ) +sub zmDbConnect { - my $force = shift; - if ( $force ) - { - zmDbDisconnect(); - } - if ( !defined( $dbh ) ) - { + my $force = shift; + if ( $force ) + { + zmDbDisconnect(); + } + if ( !defined( $dbh ) ) + { my ( $host, $port ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); if ( defined($port) ) { - $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$host.";port=".$port, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); + $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} + .";host=".$host + .";port=".$port + , $Config{ZM_DB_USER} + , $Config{ZM_DB_PASS} + ); } else { - $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); + $dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} + .";host=".$Config{ZM_DB_HOST} + , $Config{ZM_DB_USER} + , $Config{ZM_DB_PASS} + ); } $dbh->trace( 0 ); - } - return( $dbh ); + } + return( $dbh ); } -sub zmDbDisconnect() +sub zmDbDisconnect { - if ( defined( $dbh ) ) - { - $dbh->disconnect(); - $dbh = undef; - } + if ( defined( $dbh ) ) + { + $dbh->disconnect(); + $dbh = undef; + } } use constant DB_MON_ALL => 0; # All monitors @@ -110,78 +119,88 @@ use constant DB_MON_MOTION => 3; # All monitors that are doing motion detection use constant DB_MON_RECORD => 4; # All monitors that are doing unconditional recording use constant DB_MON_PASSIVE => 5; # All monitors that are in nodect state -sub zmDbGetMonitors( ;$ ) +sub zmDbGetMonitors { - zmDbConnect(); + zmDbConnect(); - my $function = shift || DB_MON_ALL; - my $sql = "select * from Monitors"; + my $function = shift || DB_MON_ALL; + my $sql = "select * from Monitors"; - if ( $function ) - { - if ( $function == DB_MON_CAPT ) - { - $sql .= " where Function >= 'Monitor'"; - } - elsif ( $function == DB_MON_ACTIVE ) - { - $sql .= " where Function > 'Monitor'"; - } - elsif ( $function == DB_MON_MOTION ) - { - $sql .= " where Function = 'Modect' or Function = 'Mocord'"; - } - elsif ( $function == DB_MON_RECORD ) - { - $sql .= " where Function = 'Record' or Function = 'Mocord'"; - } - elsif ( $function == DB_MON_PASSIVE ) - { - $sql .= " where Function = 'Nodect'"; - } - } - my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or croak( "Can't execute '$sql': ".$sth->errstr() ); + if ( $function ) + { + if ( $function == DB_MON_CAPT ) + { + $sql .= " where Function >= 'Monitor'"; + } + elsif ( $function == DB_MON_ACTIVE ) + { + $sql .= " where Function > 'Monitor'"; + } + elsif ( $function == DB_MON_MOTION ) + { + $sql .= " where Function = 'Modect' or Function = 'Mocord'"; + } + elsif ( $function == DB_MON_RECORD ) + { + $sql .= " where Function = 'Record' or Function = 'Mocord'"; + } + elsif ( $function == DB_MON_PASSIVE ) + { + $sql .= " where Function = 'Nodect'"; + } + } + my $sth = $dbh->prepare_cached( $sql ) + or croak( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() + or croak( "Can't execute '$sql': ".$sth->errstr() ); - my @monitors; + my @monitors; while( my $monitor = $sth->fetchrow_hashref() ) { - push( @monitors, $monitor ); - } - $sth->finish(); - return( \@monitors ); + push( @monitors, $monitor ); + } + $sth->finish(); + return( \@monitors ); } -sub zmDbGetMonitor( $ ) +sub zmDbGetMonitor { - zmDbConnect(); + zmDbConnect(); - my $id = shift; + my $id = shift; - return( undef ) if ( !defined($id) ); + return( undef ) if ( !defined($id) ); - my $sql = "select * from Monitors where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or croak( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $id ) or croak( "Can't execute '$sql': ".$sth->errstr() ); + my $sql = "select * from Monitors where Id = ?"; + my $sth = $dbh->prepare_cached( $sql ) + or croak( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $id ) + or croak( "Can't execute '$sql': ".$sth->errstr() ); my $monitor = $sth->fetchrow_hashref(); - return( $monitor ); + return( $monitor ); } -sub zmDbGetMonitorAndControl( $ ) +sub zmDbGetMonitorAndControl { - zmDbConnect(); + zmDbConnect(); - my $id = shift; + my $id = shift; - return( undef ) if ( !defined($id) ); + return( undef ) if ( !defined($id) ); - my $sql = "select C.*,M.*,C.Protocol from Monitors as M inner join Controls as C on (M.ControlId = C.Id) where M.Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $id ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sql = "SELECT C.*,M.*,C.Protocol + FROM Monitors as M + INNER JOIN Controls as C on (M.ControlId = C.Id) + WHERE M.Id = ?" + ; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $id ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); my $monitor = $sth->fetchrow_hashref(); - return( $monitor ); + return( $monitor ); } 1; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm new file mode 100644 index 000000000..67c117460 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -0,0 +1,330 @@ +# ========================================================================== +# +# ZoneMinder Event Module, $Date$, $Revision$ +# Copyright (C) 2001-2008 Philip Coombes +# +# 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 module contains the common definitions and functions used by the rest +# of the ZoneMinder scripts +# +package ZoneMinder::Event; + +use 5.006; +use strict; +use warnings; + +require Exporter; +require ZoneMinder::Base; +require Date::Manip; + +our @ISA = qw(Exporter ZoneMinder::Base); + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. + +# This allows declaration use ZoneMinder ':all'; +# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK +# will save memory. +our %EXPORT_TAGS = ( + 'functions' => [ qw( + ) ] +); +push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; + +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); + +our @EXPORT = qw(); + +our $VERSION = $ZoneMinder::Base::VERSION; + +# ========================================================================== +# +# General Utility Functions +# +# ========================================================================== + +use ZoneMinder::Config qw(:all); +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Database qw(:all); + +use POSIX; + +sub new { + my ( $parent, $id, $data ) = @_; + + my $self = {}; + bless $self, $parent; + $$self{dbh} = $ZoneMinder::Database::dbh; +#zmDbConnect(); + if ( ( $$self{Id} = $id ) or $data ) { +#$log->debug("loading $parent $id") if $debug or DEBUG_ALL; + $self->load( $data ); + } + return $self; +} # end sub new + +sub load { + my ( $self, $data ) = @_; + my $type = ref $self; + if ( ! $data ) { +#$log->debug("Object::load Loading from db $type"); + $data = $$self{dbh}->selectrow_hashref( 'SELECT * FROM Events WHERE Id=?', {}, $$self{Id} ); + if ( ! $data ) { + Error( "Failure to load Event record for $$self{Id}: Reason: " . $$self{dbh}->errstr ); + } else { + Debug( 3, "Loaded Event $$self{Id}" ); + } # end if + } # end if ! $data + if ( $data and %$data ) { + @$self{keys %$data} = values %$data; + } # end if +} # end sub load + +sub Name { + if ( @_ > 1 ) { + $_[0]{Name} = $_[1]; + } + return $_[0]{Name}; +} # end sub Path + +sub find { + shift if $_[0] eq 'ZoneMinder::Event'; + my %sql_filters = @_; + + my $sql = 'SELECT * FROM Events'; + my @sql_filters; + my @sql_values; + + if ( exists $sql_filters{Name} ) { + push @sql_filters , ' Name = ? '; + push @sql_values, $sql_filters{Name}; + } + + $sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters; + $sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit}; + + my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() ); + my $res = $sth->execute( @sql_values ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + + my @results; + + while( my $db_filter = $sth->fetchrow_hashref() ) { + my $filter = new ZoneMinder::Event( $$db_filter{Id}, $db_filter ); + push @results, $filter; + } # end while + return @results; +} + +sub find_one { + my @results = find(@_); + return $results[0] if @results; +} + +sub getEventPath +{ + my $event = shift; + + my $event_path = ""; + if ( $Config{ZM_USE_DEEP_STORAGE} ) + { + $event_path = $Config{ZM_DIR_EVENTS} + .'/'.$event->{MonitorId} + .'/'.strftime( "%y/%m/%d/%H/%M/%S", + localtime($event->{Time}) + ) + ; + } + else + { + $event_path = $Config{ZM_DIR_EVENTS} + .'/'.$event->{MonitorId} + .'/'.$event->{Id} + ; + } + + if ( index($Config{ZM_DIR_EVENTS},'/') != 0 ){ + $event_path = $Config{ZM_PATH_WEB} + .'/'.$event_path + ; + } + return( $event_path ); +} + +sub GenerateVideo { + my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_; + + my $event_path = getEventPath( $self ); + chdir( $event_path ); + ( my $video_name = $self->{Name} ) =~ s/\s/_/g; + + my @file_parts; + if ( $rate ) + { + my $file_rate = $rate; + $file_rate =~ s/\./_/; + $file_rate =~ s/_00//; + $file_rate =~ s/(_\d+)0+$/$1/; + $file_rate = 'r'.$file_rate; + push( @file_parts, $file_rate ); + } + elsif ( $fps ) + { + my $file_fps = $fps; + $file_fps =~ s/\./_/; + $file_fps =~ s/_00//; + $file_fps =~ s/(_\d+)0+$/$1/; + $file_fps = 'R'.$file_fps; + push( @file_parts, $file_fps ); + } + + if ( $scale ) + { + my $file_scale = $scale; + $file_scale =~ s/\./_/; + $file_scale =~ s/_00//; + $file_scale =~ s/(_\d+)0+$/$1/; + $file_scale = 's'.$file_scale; + push( @file_parts, $file_scale ); + } + elsif ( $size ) + { + my $file_size = 'S'.$size; + push( @file_parts, $file_size ); + } + my $video_file = "$video_name-".$file_parts[0]."-".$file_parts[1].".$format"; + if ( $overwrite || !-s $video_file ) + { + Info( "Creating video file $video_file for event $self->{Id}\n" ); + + my $frame_rate = sprintf( "%.2f", $self->{Frames}/$self->{FullLength} ); + if ( $rate ) + { + if ( $rate != 1.0 ) + { + $frame_rate *= $rate; + } + } + elsif ( $fps ) + { + $frame_rate = $fps; + } + + my $width = $self->{MonitorWidth}; + my $height = $self->{MonitorHeight}; + my $video_size = " ${width}x${height}"; + + if ( $scale ) + { + if ( $scale != 1.0 ) + { + $width = int($width*$scale); + $height = int($height*$scale); + $video_size = " ${width}x${height}"; + } + } + elsif ( $size ) + { + $video_size = $size; + } + my $command = $Config{ZM_PATH_FFMPEG} + ." -y -r $frame_rate " + .$Config{ZM_FFMPEG_INPUT_OPTIONS} + ." -i %0" + .$Config{ZM_EVENT_IMAGE_DIGITS} + ."d-capture.jpg -s $video_size " +#. " -f concat -i /tmp/event_files.txt" + ." -s $video_size " + .$Config{ZM_FFMPEG_OUTPUT_OPTIONS} + ." '$video_file' > ffmpeg.log 2>&1" + ; + Debug( $command."\n" ); + my $output = qx($command); + + my $status = $? >> 8; + if ( $status ) + { + Error( "Unable to generate video, check " + .$event_path."/ffmpeg.log for details" + ); + return; + } + + Info( "Finished $video_file\n" ); + return $event_path.'/'.$video_file; + } else { + Info( "Video file $video_file already exists for event $self->{Id}\n" ); + return $event_path.'/'.$video_file; + } + return; +} # end sub GenerateVideo + +1; +__END__ +# Below is stub documentation for your module. You'd better edit it! + +=head1 NAME + +ZoneMinder::Database - Perl extension for blah blah blah + +=head1 SYNOPSIS + + use ZoneMinder::Event; + blah blah blah + +=head1 DESCRIPTION + +Stub documentation for ZoneMinder, created by h2xs. It looks like the +author of the extension was negligent enough to leave the stub +unedited. + +Blah blah blah. + +=head2 EXPORT + +None by default. + + + +=head1 SEE ALSO + +Mention other useful documentation such as the documentation of +related modules or operating system documentation (such as man pages +in UNIX), or any relevant external documentation such as RFCs or +standards. + +If you have a mailing list set up for your module, mention it here. + +If you have a web site set up for your module, mention it here. + +=head1 AUTHOR + +Philip Coombes, Ephilip.coombes@zoneminder.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2008 Philip Coombes + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm new file mode 100644 index 000000000..b7806ec4a --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Filter.pm @@ -0,0 +1,526 @@ +# ========================================================================== +# +# ZoneMinder Filter Module, $Date$, $Revision$ +# Copyright (C) 2001-2008 Philip Coombes +# +# 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 module contains the common definitions and functions used by the rest +# of the ZoneMinder scripts +# +package ZoneMinder::Filter; + +use 5.006; +use strict; +use warnings; + +require Exporter; +require ZoneMinder::Base; +require Date::Manip; + +our @ISA = qw(Exporter ZoneMinder::Base); + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. + +# This allows declaration use ZoneMinder ':all'; +# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK +# will save memory. +our %EXPORT_TAGS = ( + 'functions' => [ qw( + ) ] +); +push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; + +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); + +our @EXPORT = qw(); + +our $VERSION = $ZoneMinder::Base::VERSION; + +# ========================================================================== +# +# General Utility Functions +# +# ========================================================================== + +use ZoneMinder::Config qw(:all); +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Database qw(:all); + +use POSIX; + +sub new { + my ( $parent, $id, $data ) = @_; + + my $self = {}; + bless $self, $parent; + $$self{dbh} = $ZoneMinder::Database::dbh; +#zmDbConnect(); + if ( ( $$self{Id} = $id ) or $data ) { +#$log->debug("loading $parent $id") if $debug or DEBUG_ALL; + $self->load( $data ); + } + return $self; +} # end sub new + +sub load { + my ( $self, $data ) = @_; + my $type = ref $self; + if ( ! $data ) { +#$log->debug("Object::load Loading from db $type"); + $data = $$self{dbh}->selectrow_hashref( 'SELECT * FROM Filter WHERE Id=?', {}, $$self{Id} ); + if ( ! $data ) { + Error( "Failure to load Filter record for $$self{Id}: Reason: " . $$self{dbh}->errstr ); + } else { + Debug( 3, "Loaded Filter $$self{Id}" ); + } # end if + } # end if ! $data + if ( $data and %$data ) { + @$self{keys %$data} = values %$data; + } # end if +} # end sub load + +sub Name { + if ( @_ > 1 ) { + $_[0]{Name} = $_[1]; + } + return $_[0]{Name}; +} # end sub Path + +sub find { + shift if $_[0] eq 'ZoneMinder::Filter'; + my %sql_filters = @_; + + my $sql = 'SELECT * FROM Filters'; + my @sql_filters; + my @sql_values; + + if ( exists $sql_filters{Name} ) { + push @sql_filters , ' Name = ? '; + push @sql_values, $sql_filters{Name}; + } + + $sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters; + $sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit}; + + my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() ); + my $res = $sth->execute( @sql_values ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + + my @results; + + while( my $db_filter = $sth->fetchrow_hashref() ) { + my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter ); + push @results, $filter; + } # end while + return @results; +} + +sub find_one { + my @results = find(@_); + return $results[0] if @results; +} + +sub Execute { + my $self = $_[0]; + + my $sql = $self->Sql(); + + if ( $self->{HasDiskPercent} ) + { + my $disk_percent = getDiskPercent(); + $sql =~ s/zmDiskPercent/$disk_percent/g; + } + if ( $self->{HasDiskBlocks} ) + { + my $disk_blocks = getDiskBlocks(); + $sql =~ s/zmDiskBlocks/$disk_blocks/g; + } + if ( $self->{HasSystemLoad} ) + { + my $load = getLoad(); + $sql =~ s/zmSystemLoad/$load/g; + } + + my $sth = $$self{dbh}->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$$self{dbh}->errstr() ); + my $res = $sth->execute(); + if ( !$res ) + { + Error( "Can't execute filter '$sql', ignoring: ".$sth->errstr() ); + return; + } + my @results; + + while( my $event = $sth->fetchrow_hashref() ) { + push @results, $event; + } + $sth->finish(); + return @results; +} + +sub Sql { + my $self = $_[0]; + if ( ! $$self{Sql} ) { + my $filter_expr = ZoneMinder::General::jsonDecode( $self->{Query} ); + my $sql = "SELECT E.Id, + E.MonitorId, + M.Name as MonitorName, + M.DefaultRate, + M.DefaultScale, + E.Name, + E.Cause, + E.Notes, + E.StartTime, + unix_timestamp(E.StartTime) as Time, + E.Length, + E.Frames, + E.AlarmFrames, + E.TotScore, + E.AvgScore, + E.MaxScore, + E.Archived, + E.Videoed, + E.Uploaded, + E.Emailed, + E.Messaged, + E.Executed + FROM Events as E + INNER JOIN Monitors as M on M.Id = E.MonitorId + "; + $self->{Sql} = ''; + + if ( $filter_expr->{terms} ) { + for ( my $i = 0; $i < @{$filter_expr->{terms}}; $i++ ) { + if ( exists($filter_expr->{terms}[$i]->{cnj}) ) { + $self->{Sql} .= " ".$filter_expr->{terms}[$i]->{cnj}." "; + } + if ( exists($filter_expr->{terms}[$i]->{obr}) ) { + $self->{Sql} .= " ".str_repeat( "(", $filter_expr->{terms}[$i]->{obr} )." "; + } + my $value = $filter_expr->{terms}[$i]->{val}; + my @value_list; + if ( $filter_expr->{terms}[$i]->{attr} ) { + if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) { + my ( $temp_attr_name ) = $filter_expr->{terms}[$i]->{attr} =~ /^Monitor(.+)$/; + $self->{Sql} .= "M.".$temp_attr_name; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) { + $self->{Sql} .= "E.StartTime"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) { + $self->{Sql} .= "to_days( E.StartTime )"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) { + $self->{Sql} .= "extract( hour_second from E.StartTime )"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Weekday' ) { + $self->{Sql} .= "weekday( E.StartTime )"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskPercent' ) { + $self->{Sql} .= "zmDiskPercent"; + $self->{HasDiskPercent} = !undef; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskBlocks' ) { + $self->{Sql} .= "zmDiskBlocks"; + $self->{HasDiskBlocks} = !undef; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'SystemLoad' ) { + $self->{Sql} .= "zmSystemLoad"; + $self->{HasSystemLoad} = !undef; + } else { + $self->{Sql} .= "E.".$filter_expr->{terms}[$i]->{attr}; + } + + ( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/; + foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) { + if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) { + $value = "'$temp_value'"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Name' + || $filter_expr->{terms}[$i]->{attr} eq 'Cause' + || $filter_expr->{terms}[$i]->{attr} eq 'Notes' + ) { + $value = "'$temp_value'"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) { + $value = DateTimeToSQL( $temp_value ); + if ( !$value ) { + Error( "Error parsing date/time '$temp_value', " + ."skipping filter '$self->{Name}'\n" ); + return; + } + $value = "'$value'"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) { + $value = DateTimeToSQL( $temp_value ); + if ( !$value ) { + Error( "Error parsing date/time '$temp_value', " + ."skipping filter '$self->{Name}'\n" ); + return; + } + $value = "to_days( '$value' )"; + } elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) { + $value = DateTimeToSQL( $temp_value ); + if ( !$value ) { + Error( "Error parsing date/time '$temp_value', " + ."skipping filter '$self->{Name}'\n" ); + return; + } + $value = "extract( hour_second from '$value' )"; + } else { + $value = $temp_value; + } + push( @value_list, $value ); + } # end foreach temp_value + } # end if has an attr + if ( $filter_expr->{terms}[$i]->{op} ) { + if ( $filter_expr->{terms}[$i]->{op} eq '=~' ) { + $self->{Sql} .= " regexp $value"; + } elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) { + $self->{Sql} .= " not regexp $value"; + } elsif ( $filter_expr->{terms}[$i]->{op} eq '=[]' ) { + $self->{Sql} .= " in (".join( ",", @value_list ).")"; + } elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) { + $self->{Sql} .= " not in (".join( ",", @value_list ).")"; + } else { + $self->{Sql} .= " ".$filter_expr->{terms}[$i]->{op}." $value"; + } + } # end if has an operator + if ( exists($filter_expr->{terms}[$i]->{cbr}) ) { + $self->{Sql} .= " ".str_repeat( ")", $filter_expr->{terms}[$i]->{cbr} )." "; + } + } # end foreach term + } # end if terms + + if ( $self->{Sql} ) + { + if ( $self->{AutoMessage} ) + { + # Include all events, including events that are still ongoing + # and have no EndTime yet + $sql .= " and ( ".$self->{Sql}." )"; + } + else + { + # Only include closed events (events with valid EndTime) + $sql .= " where not isnull(E.EndTime) and ( ".$self->{Sql}." )"; + } + } + my @auto_terms; + if ( $self->{AutoArchive} ) + { + push( @auto_terms, "E.Archived = 0" ) + } + if ( $self->{AutoVideo} ) + { + push( @auto_terms, "E.Videoed = 0" ) + } + if ( $self->{AutoUpload} ) + { + push( @auto_terms, "E.Uploaded = 0" ) + } + if ( $self->{AutoEmail} ) + { + push( @auto_terms, "E.Emailed = 0" ) + } + if ( $self->{AutoMessage} ) + { + push( @auto_terms, "E.Messaged = 0" ) + } + if ( $self->{AutoExecute} ) + { + push( @auto_terms, "E.Executed = 0" ) + } + if ( @auto_terms ) + { + $sql .= " and ( ".join( " or ", @auto_terms )." )"; + } + if ( !$filter_expr->{sort_field} ) + { + $filter_expr->{sort_field} = 'StartTime'; + $filter_expr->{sort_asc} = 0; + } + my $sort_column = ''; + if ( $filter_expr->{sort_field} eq 'Id' ) + { + $sort_column = "E.Id"; + } + elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) + { + $sort_column = "M.Name"; + } + elsif ( $filter_expr->{sort_field} eq 'Name' ) + { + $sort_column = "E.Name"; + } + elsif ( $filter_expr->{sort_field} eq 'StartTime' ) + { + $sort_column = "E.StartTime"; + } + elsif ( $filter_expr->{sort_field} eq 'Secs' ) + { + $sort_column = "E.Length"; + } + elsif ( $filter_expr->{sort_field} eq 'Frames' ) + { + $sort_column = "E.Frames"; + } + elsif ( $filter_expr->{sort_field} eq 'AlarmFrames' ) + { + $sort_column = "E.AlarmFrames"; + } + elsif ( $filter_expr->{sort_field} eq 'TotScore' ) + { + $sort_column = "E.TotScore"; + } + elsif ( $filter_expr->{sort_field} eq 'AvgScore' ) + { + $sort_column = "E.AvgScore"; + } + elsif ( $filter_expr->{sort_field} eq 'MaxScore' ) + { + $sort_column = "E.MaxScore"; + } + else + { + $sort_column = "E.StartTime"; + } + my $sort_order = $filter_expr->{sort_asc}?"asc":"desc"; + $sql .= " order by ".$sort_column." ".$sort_order; + if ( $filter_expr->{limit} ) + { + $sql .= " limit 0,".$filter_expr->{limit}; + } + Debug( "SQL:$sql\n" ); + $self->{Sql} = $sql; + } # end if has Sql + return $self->{Sql}; +} # end sub Sql + +sub getDiskPercent +{ + my $command = "df ."; + my $df = qx( $command ); + my $space = -1; + if ( $df =~ /\s(\d+)%/ms ) + { + $space = $1; + } + return( $space ); +} +sub getDiskBlocks +{ + my $command = "df ."; + my $df = qx( $command ); + my $space = -1; + if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) + { + $space = $1; + } + return( $space ); +} +sub getLoad +{ + my $command = "uptime ."; + my $uptime = qx( $command ); + my $load = -1; + if ( $uptime =~ /load average:\s+([\d.]+)/ms ) + { + $load = $1; + Info( "Load: $load" ); + } + return( $load ); +} + +# +# More or less replicates the equivalent PHP function +# +sub strtotime +{ + my $dt_str = shift; + return( Date::Manip::UnixDate( $dt_str, '%s' ) ); +} + +# +# More or less replicates the equivalent PHP function +# +sub str_repeat +{ + my $string = shift; + my $count = shift; + return( ${string}x${count} ); +} + +# Formats a date into MySQL format +sub DateTimeToSQL +{ + my $dt_str = shift; + my $dt_val = strtotime( $dt_str ); + if ( !$dt_val ) + { + Error( "Unable to parse date string '$dt_str'\n" ); + return( undef ); + } + return( strftime( "%Y-%m-%d %H:%M:%S", localtime( $dt_val ) ) ); +} + +1; +__END__ +# Below is stub documentation for your module. You'd better edit it! + +=head1 NAME + +ZoneMinder::Database - Perl extension for blah blah blah + +=head1 SYNOPSIS + + use ZoneMinder::Filter; + blah blah blah + +=head1 DESCRIPTION + +Stub documentation for ZoneMinder, created by h2xs. It looks like the +author of the extension was negligent enough to leave the stub +unedited. + +Blah blah blah. + +=head2 EXPORT + +None by default. + + + +=head1 SEE ALSO + +Mention other useful documentation such as the documentation of +related modules or operating system documentation (such as man pages +in UNIX), or any relevant external documentation such as RFCs or +standards. + +If you have a mailing list set up for your module, mention it here. + +If you have a web site set up for your module, mention it here. + +=head1 AUTHOR + +Philip Coombes, Ephilip.coombes@zoneminder.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2008 Philip Coombes + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/General.pm b/scripts/ZoneMinder/lib/ZoneMinder/General.pm index 4d60852a7..944781f6b 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/General.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/General.pm @@ -19,7 +19,7 @@ # # ========================================================================== # -# This module contains the common definitions and functions used by the rest +# This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::General; @@ -76,7 +76,7 @@ use ZoneMinder::Database qw(:all); use POSIX; # For running general shell commands -sub executeShellCommand( $ ) +sub executeShellCommand { my $command = shift; my $output = qx( $command ); @@ -90,7 +90,7 @@ sub executeShellCommand( $ ) return( $status ); } -sub getCmdFormat() +sub getCmdFormat { Debug( "Testing valid shell syntax\n" ); @@ -107,7 +107,6 @@ sub getCmdFormat() my $suffix = ""; my $command = $prefix.$null_command.$suffix; Debug( "Testing \"$command\"\n" ); - $command .= " > /dev/null 2>&1"; my $output = qx($command); my $status = $? >> 8; if ( !$status ) @@ -162,7 +161,7 @@ our $testedShellSyntax = 0; our ( $cmdPrefix, $cmdSuffix ); # For running ZM daemons etc -sub runCommand( $ ) +sub runCommand { if ( !$testedShellSyntax ) { @@ -196,30 +195,45 @@ sub runCommand( $ ) return( $output ); } -sub getEventPath( $ ) +sub getEventPath { my $event = shift; my $event_path = ""; if ( $Config{ZM_USE_DEEP_STORAGE} ) { - $event_path = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.strftime( "%y/%m/%d/%H/%M/%S", localtime($event->{Time}) ); + $event_path = $Config{ZM_DIR_EVENTS} + .'/'.$event->{MonitorId} + .'/'.strftime( "%y/%m/%d/%H/%M/%S", + localtime($event->{Time}) + ) + ; } else { - $event_path = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.$event->{Id}; + $event_path = $Config{ZM_DIR_EVENTS} + .'/'.$event->{MonitorId} + .'/'.$event->{Id} + ; + } + + if ( index($Config{ZM_DIR_EVENTS},'/') != 0 ){ + $event_path = $Config{ZM_PATH_WEB} + .'/'.$event_path + ; } - $event_path = $Config{ZM_PATH_WEB}.'/'.$event_path if ( index($Config{ZM_DIR_EVENTS},'/') != 0 ); return( $event_path ); } -sub createEventPath( $ ) +sub createEventPath { # # WARNING assumes running from events directory # my $event = shift; - my $eventRootPath = ($Config{ZM_DIR_EVENTS}=~m|/|)?$Config{ZM_DIR_EVENTS}:($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}); + my $eventRootPath = ($Config{ZM_DIR_EVENTS}=~m|/|) + ? $Config{ZM_DIR_EVENTS} + : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}); my $eventPath = $eventRootPath.'/'.$event->{MonitorId}; if ( $Config{ZM_USE_DEEP_STORAGE} ) @@ -242,7 +256,8 @@ sub createEventPath( $ ) # Create event id symlink my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); - symlink( $timePath, $idFile ) or Fatal( "Can't symlink $idFile -> $eventPath: $!" ); + symlink( $timePath, $idFile ) + or Fatal( "Can't symlink $idFile -> $eventPath: $!" ); makePath( $timePath, $eventPath ); $eventPath .= '/'.$timePath; @@ -250,8 +265,9 @@ sub createEventPath( $ ) # Create empty id tag file $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); - open( ID_FP, ">$idFile" ) or Fatal( "Can't open $idFile: $!" ); - close( ID_FP ); + open( my $ID_FP, ">", $idFile ) + or Fatal( "Can't open $idFile: $!" ); + close( $ID_FP ); setFileOwner( $idFile ); } else @@ -260,8 +276,9 @@ sub createEventPath( $ ) $eventPath .= '/'.$event->{Id}; my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); - open( ID_FP, ">$idFile" ) or Fatal( "Can't open $idFile: $!" ); - close( ID_FP ); + open( my $ID_FP, ">", $idFile ) + or Fatal( "Can't open $idFile: $!" ); + close( $ID_FP ); setFileOwner( $idFile ); } return( $eventPath ); @@ -272,15 +289,20 @@ use Data::Dumper; our $_setFileOwner = undef; our ( $_ownerUid, $_ownerGid ); -sub _checkProcessOwner() +sub _checkProcessOwner { if ( !defined($_setFileOwner) ) { my ( $processOwner ) = getpwuid( $> ); if ( $processOwner ne $Config{ZM_WEB_USER} ) { - # Not running as web user, so should be root in whch case chown the temporary directory - ( my $ownerName, my $ownerPass, $_ownerUid, $_ownerGid ) = getpwnam( $Config{ZM_WEB_USER} ) or Fatal( "Can't get user details for web user '".$Config{ZM_WEB_USER}."': $!" ); + # Not running as web user, so should be root in which case chown + # the temporary directory + ( my $ownerName, my $ownerPass, $_ownerUid, $_ownerGid ) + = getpwnam( $Config{ZM_WEB_USER} ) + or Fatal( "Can't get user details for web user '" + .$Config{ZM_WEB_USER}."': $!" + ); $_setFileOwner = 1; } else @@ -291,19 +313,22 @@ sub _checkProcessOwner() return( $_setFileOwner ); } -sub setFileOwner( $ ) +sub setFileOwner { my $file = shift; if ( _checkProcessOwner() ) { - chown( $_ownerUid, $_ownerGid, $file ) or Fatal( "Can't change ownership of file '$file' to '".$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!" ); + chown( $_ownerUid, $_ownerGid, $file ) + or Fatal( "Can't change ownership of file '$file' to '" + .$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!" + ); } } our $_hasImageInfo = undef; -sub _checkForImageInfo() +sub _checkForImageInfo { if ( !defined($_hasImageInfo) ) { @@ -317,7 +342,7 @@ sub _checkForImageInfo() return( $_hasImageInfo ); } -sub createEvent( $;$ ) +sub createEvent { my $event = shift; @@ -335,9 +360,14 @@ sub createEvent( $;$ ) elsif ( $event->{MonitorId} ) { my $sql = "select * from Monitors where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{MonitorId} ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); - $event->{monitor} = $sth->fetchrow_hashref() or Fatal( "Unable to create event, can't load monitor with id '".$event->{MonitorId}."'" ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{MonitorId} ) + or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); + $event->{monitor} = $sth->fetchrow_hashref() + or Fatal( "Unable to create event, can't load monitor with id '" + .$event->{MonitorId}."'" + ); $sth->finish(); } else @@ -358,7 +388,9 @@ sub createEvent( $;$ ) my $imageInfo = Image::Info::image_info( $frame->{imagePath} ); if ( $imageInfo->{error} ) { - Error( "Unable to extract image info from '".$frame->{imagePath}."': ".$imageInfo->{error} ); + Error( "Unable to extract image info from '" + .$frame->{imagePath}."': ".$imageInfo->{error} + ); } else { @@ -394,18 +426,25 @@ sub createEvent( $;$ ) push( @values, $event->{$field} ); } - my $sql = "insert into Events (".join(',',@fields).") values (".join(',',@formats).")"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( @values ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); + my $sql = "INSERT INTO Events (".join(',',@fields) + .") VALUES (".join(',',@formats).")" + ; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( @values ) + or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); $event->{Id} = $dbh->{mysql_insertid}; Info( "Created event ".$event->{Id} ); if ( $event->{EndTime} ) { - $event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id} if ( $event->{Name} eq 'New Event' ); + $event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id} + if ( $event->{Name} eq 'New Event' ); my $sql = "update Events set Name = ? where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Name}, $event->{Id} ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Name}, $event->{Id} ) + or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); } my $eventPath = createEventPath( $event ); @@ -428,26 +467,46 @@ sub createEvent( $;$ ) push( @values, $frame->{$field} ); } - my $sql = "insert into Frames (".join(',',@fields).") values (".join(',',@formats).")"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( @values ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); + my $sql = "insert into Frames (".join(',',@fields) + .") values (".join(',',@formats).")" + ; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( @values ) + or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); #$frame->{FrameId} = $dbh->{mysql_insertid}; if ( $frame->{imagePath} ) { - $frame->{capturePath} = sprintf( "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", $eventPath, $frame->{FrameId} ); - rename( $frame->{imagePath}, $frame->{capturePath} ) or Fatal( "Can't copy ".$frame->{imagePath}." to ".$frame->{capturePath}.": $!" ); + $frame->{capturePath} = sprintf( + "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS} + ."d-capture.jpg" + , $eventPath + , $frame->{FrameId} + ); + rename( $frame->{imagePath}, $frame->{capturePath} ) + or Fatal( "Can't copy ".$frame->{imagePath} + ." to ".$frame->{capturePath}.": $!" + ); setFileOwner( $frame->{capturePath} ); if ( 0 && $Config{ZM_CREATE_ANALYSIS_IMAGES} ) { - $frame->{analysePath} = sprintf( "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-analyse.jpg", $eventPath, $frame->{FrameId} ); - link( $frame->{capturePath}, $frame->{analysePath} ) or Fatal( "Can't link ".$frame->{capturePath}." to ".$frame->{analysePath}.": $!" ); + $frame->{analysePath} = sprintf( + "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS} + ."d-analyse.jpg" + , $eventPath + , $frame->{FrameId} + ); + link( $frame->{capturePath}, $frame->{analysePath} ) + or Fatal( "Can't link ".$frame->{capturePath} + ." to ".$frame->{analysePath}.": $!" + ); setFileOwner( $frame->{analysePath} ); } } } } -sub addEventImage( $$ ) +sub addEventImage { my $event = shift; my $frame = shift; @@ -455,7 +514,7 @@ sub addEventImage( $$ ) # TBD } -sub updateEvent( $ ) +sub updateEvent { my $event = shift; @@ -467,7 +526,8 @@ sub updateEvent( $ ) my $dbh = zmDbConnect(); - $event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id} if ( $event->{Name} eq 'New Event' ); + $event->{Name} = $event->{monitor}->{EventPrefix}.$event->{Id} + if ( $event->{Name} eq 'New Event' ); my %formats = ( StartTime => 'from_unixtime(?)', @@ -484,11 +544,13 @@ sub updateEvent( $ ) my $sql = "update Events set ".join(',',@sets)." where Id = ?"; push( @values, $event->{Id} ); - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( @values ) or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare sql '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( @values ) + or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); } -sub deleteEventFiles( $;$ ) +sub deleteEventFiles { # # WARNING assumes running from events directory @@ -541,7 +603,7 @@ sub deleteEventFiles( $;$ ) } } -sub makePath( $;$ ) +sub makePath { my $path = shift; my $root = shift; @@ -585,7 +647,7 @@ sub _testJSON $hasJSONAny = 1 if ( $result ); } -sub _getJSONType( $ ) +sub _getJSONType { my $value = shift; return( 'null' ) unless( defined($value) ); @@ -596,9 +658,9 @@ sub _getJSONType( $ ) return( 'string' ); } -sub jsonEncode( $ ); +sub jsonEncode; -sub jsonEncode( $ ) +sub jsonEncode { my $value = shift; @@ -649,7 +711,7 @@ sub jsonEncode( $ ) } } -sub jsonDecode( $ ) +sub jsonDecode { my $value = shift; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index 5b4438c72..a6a82db87 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -19,7 +19,7 @@ # # ========================================================================== # -# This module contains the debug definitions and functions used by the rest +# This module contains the debug definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Logger; @@ -126,6 +126,7 @@ our %priorities = ( ); our $logger; +our $LOGFILE; sub new { @@ -257,7 +258,12 @@ sub initialise( @ ) { foreach my $target ( split( /\|/, $Config{ZM_LOG_DEBUG_TARGET} ) ) { - if ( $target eq $this->{id} || $target eq "_".$this->{id} || $target eq $this->{idRoot} || $target eq "_".$this->{idRoot} || $target eq "" ) + if ( $target eq $this->{id} + || $target eq "_".$this->{id} + || $target eq $this->{idRoot} + || $target eq "_".$this->{idRoot} + || $target eq "" + ) { if ( $Config{ZM_LOG_DEBUG_LEVEL} > NOLOG ) { @@ -286,11 +292,18 @@ sub initialise( @ ) $this->{autoFlush} = $ENV{'LOG_FLUSH'}?1:0 if ( defined($ENV{'LOG_FLUSH'}) ); $this->{initialised} = !undef; - - Debug( "LogOpts: level=".$codes{$this->{level}}."/".$codes{$this->{effectiveLevel}}.", screen=".$codes{$this->{termLevel}}.", database=".$codes{$this->{databaseLevel}}.", logfile=".$codes{$this->{fileLevel}}."->".$this->{logFile}.", syslog=".$codes{$this->{syslogLevel}} ); + + Debug( "LogOpts: level=".$codes{$this->{level}} + ."/".$codes{$this->{effectiveLevel}} + .", screen=".$codes{$this->{termLevel}} + .", database=".$codes{$this->{databaseLevel}} + .", logfile=".$codes{$this->{fileLevel}} + ."->".$this->{logFile} + .", syslog=".$codes{$this->{syslogLevel}} + ); } -sub terminate() +sub terminate { my $this = shift; return unless ( $this->{initialised} ); @@ -300,7 +313,7 @@ sub terminate() $this->termLevel( NOLOG ); } -sub reinitialise() +sub reinitialise { my $this = shift; @@ -322,7 +335,7 @@ sub reinitialise() $this->databaseLevel( $databaseLevel ) if ( $databaseLevel > NOLOG ); } -sub limit( $ ) +sub limit { my $this = shift; my $level = shift; @@ -331,7 +344,7 @@ sub limit( $ ) return( $level ); } -sub getTargettedEnv( $ ) +sub getTargettedEnv { my $this = shift; my $name = shift; @@ -354,7 +367,7 @@ sub getTargettedEnv( $ ) return( $value ); } -sub fetch() +sub fetch { if ( !$logger ) { @@ -364,7 +377,7 @@ sub fetch() return( $logger ); } -sub id( ;$ ) +sub id { my $this = shift; my $id = shift; @@ -388,7 +401,7 @@ sub id( ;$ ) return( $this->{id} ); } -sub level( ;$ ) +sub level { my $this = shift; my $level = shift; @@ -405,20 +418,20 @@ sub level( ;$ ) return( $this->{level} ); } -sub debugOn() +sub debugOn { my $this = shift; return( $this->{effectiveLevel} >= DEBUG ); } -sub trace( ;$ ) +sub trace { my $this = shift; $this->{trace} = $_[0] if ( @_ ); return( $this->{trace} ); } -sub termLevel( ;$ ) +sub termLevel { my $this = shift; my $termLevel = shift; @@ -434,7 +447,7 @@ sub termLevel( ;$ ) return( $this->{termLevel} ); } -sub databaseLevel( ;$ ) +sub databaseLevel { my $this = shift; my $databaseLevel = shift; @@ -451,23 +464,39 @@ sub databaseLevel( ;$ ) if ( defined($port) ) { - $this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$host.";port=".$port, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); + $this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} + .";host=".$host + .";port=".$port + , $Config{ZM_DB_USER} + , $Config{ZM_DB_PASS} + ); } else { - $this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}.";host=".$Config{ZM_DB_HOST}, $Config{ZM_DB_USER}, $Config{ZM_DB_PASS} ); + $this->{dbh} = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME} + .";host=".$Config{ZM_DB_HOST} + , $Config{ZM_DB_USER} + , $Config{ZM_DB_PASS} + ); } if ( !$this->{dbh} ) { $databaseLevel = NOLOG; - Error( "Unable to write log entries to DB, can't connect to database '".$Config{ZM_DB_NAME}."' on host '".$Config{ZM_DB_HOST}."'" ); + Error( "Unable to write log entries to DB, can't connect to database '" + .$Config{ZM_DB_NAME} + ."' on host '" + .$Config{ZM_DB_HOST} + ."'" + ); } else { $this->{dbh}->{AutoCommit} = 1; - Fatal( "Can't set AutoCommit on in database connection" ) unless( $this->{dbh}->{AutoCommit} ); + Fatal( "Can't set AutoCommit on in database connection" ) + unless( $this->{dbh}->{AutoCommit} ); $this->{dbh}->{mysql_auto_reconnect} = 1; - Fatal( "Can't set mysql_auto_reconnect on in database connection" ) unless( $this->{dbh}->{mysql_auto_reconnect} ); + Fatal( "Can't set mysql_auto_reconnect on in database connection" ) + unless( $this->{dbh}->{mysql_auto_reconnect} ); $this->{dbh}->trace( 0 ); } } @@ -486,7 +515,7 @@ sub databaseLevel( ;$ ) return( $this->{databaseLevel} ); } -sub fileLevel( ;$ ) +sub fileLevel { my $this = shift; my $fileLevel = shift; @@ -503,7 +532,7 @@ sub fileLevel( ;$ ) return( $this->{fileLevel} ); } -sub syslogLevel( ;$ ) +sub syslogLevel { my $this = shift; my $syslogLevel = shift; @@ -520,19 +549,19 @@ sub syslogLevel( ;$ ) return( $this->{syslogLevel} ); } -sub openSyslog() +sub openSyslog { my $this = shift; openlog( $this->{id}, "pid", "local1" ); } -sub closeSyslog() +sub closeSyslog { my $this = shift; #closelog(); } -sub logFile( $ ) +sub logFile { my $this = shift; my $logFile = shift; @@ -546,18 +575,21 @@ sub logFile( $ ) } } -sub openFile() +sub openFile { my $this = shift; - if ( open( LOGFILE, ">>".$this->{logFile} ) ) + if ( open( $LOGFILE, ">>", $this->{logFile} ) ) { - LOGFILE->autoflush() if ( $this->{autoFlush} ); + $LOGFILE->autoflush() if ( $this->{autoFlush} ); my $webUid = (getpwnam( $Config{ZM_WEB_USER} ))[2]; my $webGid = (getgrnam( $Config{ZM_WEB_GROUP} ))[2]; if ( $> == 0 ) { - chown( $webUid, $webGid, $this->{logFile} ) or Fatal( "Can't change permissions on log file '".$this->{logFile}."': $!" ) + chown( $webUid, $webGid, $this->{logFile} ) + or Fatal( "Can't change permissions on log file '" + .$this->{logFile}."': $!" + ) } } else @@ -567,13 +599,13 @@ sub openFile() } } -sub closeFile() +sub closeFile { my $this = shift; - close( LOGFILE ) if ( fileno(LOGFILE) ); + close( $LOGFILE ) if ( fileno($LOGFILE) ); } -sub logPrint( $;$ ) +sub logPrint { my $this = shift; my $level = shift; @@ -586,7 +618,17 @@ sub logPrint( $;$ ) my $code = $codes{$level}; my ($seconds, $microseconds) = gettimeofday(); - my $message = sprintf( "%s.%06d %s[%d].%s [%s]", strftime( "%x %H:%M:%S", localtime( $seconds ) ), $microseconds, $this->{id}, $$, $code, $string ); + my $message = sprintf( + "%s.%06d %s[%d].%s [%s]" + , strftime( "%x %H:%M:%S" + ,localtime( $seconds ) + ) + , $microseconds + , $this->{id} + , $$ + , $code + , $string + ); if ( $this->{trace} ) { $message = Carp::shortmess( $message ); @@ -595,8 +637,9 @@ sub logPrint( $;$ ) { $message = $message."\n"; } - syslog( $priorities{$level}, $code." [%s]", $string ) if ( $level <= $this->{syslogLevel} ); - print( LOGFILE $message ) if ( $level <= $this->{fileLevel} ); + syslog( $priorities{$level}, $code." [%s]", $string ) + if ( $level <= $this->{syslogLevel} ); + print( $LOGFILE $message ) if ( $level <= $this->{fileLevel} ); if ( $level <= $this->{databaseLevel} ) { my $sql = "insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( ?, ?, ?, ?, ?, ?, ?, NULL )"; @@ -606,7 +649,14 @@ sub logPrint( $;$ ) $this->{databaseLevel} = NOLOG; Fatal( "Can't prepare log entry '$sql': ".$this->{dbh}->errstr() ); } - my $res = $this->{sth}->execute( $seconds+($microseconds/1000000.0), $this->{id}, $$, $level, $code, $string, $this->{fileName} ); + my $res = $this->{sth}->execute( $seconds+($microseconds/1000000.0) + , $this->{id} + , $$ + , $level + , $code + , $string + , $this->{fileName} + ); if ( !$res ) { $this->{databaseLevel} = NOLOG; @@ -624,7 +674,7 @@ sub logInit( ;@ ) $logger->initialise( %options ); } -sub logReinit() +sub logReinit { fetch()->reinitialise(); } @@ -636,7 +686,7 @@ sub logTerm $logger = undef; } -sub logHupHandler() +sub logHupHandler { my $savedErrno = $!; return unless( $logger ); @@ -645,47 +695,47 @@ sub logHupHandler() $! = $savedErrno; } -sub logSetSignal() +sub logSetSignal { $SIG{HUP} = \&logHupHandler; } -sub logClearSignal() +sub logClearSignal { $SIG{HUP} = 'DEFAULT'; } -sub logLevel( ;$ ) +sub logLevel { return( fetch()->level( @_ ) ); } -sub logDebugging() +sub logDebugging { return( fetch()->debugOn() ); } -sub logTermLevel( ;$ ) +sub logTermLevel { return( fetch()->termLevel( @_ ) ); } -sub logDatabaseLevel( ;$ ) +sub logDatabaseLevel { return( fetch()->databaseLevel( @_ ) ); } -sub logFileLevel( ;$ ) +sub logFileLevel { return( fetch()->fileLevel( @_ ) ); } -sub logSyslogLevel( ;$ ) +sub logSyslogLevel { return( fetch()->syslogLevel( @_ ) ); } -sub Mark( ;$$ ) +sub Mark { my $level = shift; $level = DEBUG unless( defined($level) ); @@ -693,7 +743,7 @@ sub Mark( ;$$ ) fetch()->logPrint( $level, $tag ); } -sub Dump( \$;$ ) +sub Dump { my $var = shift; my $label = shift; @@ -756,11 +806,19 @@ ZoneMinder::Logger - ZoneMinder Logger module =head1 DESCRIPTION -The ZoneMinder:Logger module contains the common debug and error reporting routines used by the ZoneMinder scripts. +The ZoneMinder:Logger module contains the common debug and error reporting +routines used by the ZoneMinder scripts. -To use debug in your scripts you need to include this module, and call logInit. Thereafter you can sprinkle Debug or Error calls etc throughout the code safe in the knowledge that they will be reported to your error log, and possibly the syslogger, in a meaningful and consistent format. +To use debug in your scripts you need to include this module, and call +logInit. Thereafter you can sprinkle Debug or Error calls etc throughout +the code safe in the knowledge that they will be reported to your error +log, and possibly the syslogger, in a meaningful and consistent format. -Debug is discussed in terms of levels where 1 and above (currently only 1 for scripts) is considered debug, 0 is considered as informational, -1 is a warning, -2 is an error and -3 is a fatal error or panic. Where levels are mentioned below as thresholds the value given and anything with a lower level (ie. more serious) will be included. +Debug is discussed in terms of levels where 1 and above (currently only 1 +for scripts) is considered debug, 0 is considered as informational, -1 is a +warning, -2 is an error and -3 is a fatal error or panic. Where levels are +mentioned below as thresholds the value given and anything with a lower +level (ie. more serious) will be included. =head1 METHODS @@ -768,7 +826,12 @@ Debug is discussed in terms of levels where 1 and above (currently only 1 for sc =item logInit ( $id, %options ); -Initialises the debug and prepares the logging for forthcoming operations. If not called explicitly it will be called by the first debug call in your script, but with default (and probably meaningless) options. The only compulsory arguments are $id which must be a string that will identify debug coming from this script in mixed logs. Other options may be provided as below, +Initialises the debug and prepares the logging for forthcoming operations. +If not called explicitly it will be called by the first debug call in your +script, but with default (and probably meaningless) options. The only +compulsory arguments are $id which must be a string that will identify +debug coming from this script in mixed logs. Other options may be provided +as below, Option Default Description --------- --------- ----------- @@ -805,27 +868,41 @@ These methods can be used to get and set the current settings as defined in logI =item Debug( $string ); -This method will output a debug message if the current debug level permits it, otherwise does nothing. This message will be tagged with the DBG string in the logs. +This method will output a debug message if the current debug level permits +it, otherwise does nothing. This message will be tagged with the DBG string +in the logs. =item Info( $string ); -This method will output an informational message if the current debug level permits it, otherwise does nothing. This message will be tagged with the INF string in the logs. +This method will output an informational message if the current debug level +permits it, otherwise does nothing. This message will be tagged with the +INF string in the logs. =item Warning( $string ); -This method will output a warning message if the current debug level permits it, otherwise does nothing. This message will be tagged with the WAR string in the logs. +This method will output a warning message if the current debug level +permits it, otherwise does nothing. This message will be tagged with the +WAR string in the logs. =item Error( $string ); -This method will output an error message if the current debug level permits it, otherwise does nothing. This message will be tagged with the ERR string in the logs. +This method will output an error message if the current debug level permits +it, otherwise does nothing. This message will be tagged with the ERR string +in the logs. =item Fatal( $string ); -This method will output a fatal error message and then die if the current debug level permits it, otherwise does nothing. This message will be tagged with the FAT string in the logs. +This method will output a fatal error message and then die if the current +debug level permits it, otherwise does nothing. This message will be tagged +with the FAT string in the logs. =item Panic( $string ); -This method will output a panic error message and then die with a stack trace if the current debug level permits it, otherwise does nothing. This message will be tagged with the PNC string in the logs. +This method will output a panic error message and then die with a stack +trace if the current debug level permits it, otherwise does nothing. This +message will be tagged with the PNC string in the logs. + +=back =head2 EXPORT @@ -841,7 +918,8 @@ The :all tag will export all above symbols. Carp Sys::Syslog -The ZoneMinder README file Troubleshooting section for an extended discussion on the use and configuration of syslog with ZoneMinder. +The ZoneMinder README file Troubleshooting section for an extended +discussion on the use and configuration of syslog with ZoneMinder. http://www.zoneminder.com diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in index df32e7608..8e7b9228e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in @@ -19,7 +19,7 @@ # # ========================================================================== # -# This module contains the common definitions and functions used by the rest +# This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Memory; @@ -37,48 +37,48 @@ our @ISA = qw(Exporter ZoneMinder::Base); # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. -# This allows declaration use ZoneMinder ':all'; +# This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( - 'constants' => [ qw( - STATE_IDLE - STATE_PREALARM - STATE_ALARM - STATE_ALERT - STATE_TAPE - ACTION_GET - ACTION_SET - ACTION_RELOAD - ACTION_SUSPEND - ACTION_RESUME - TRIGGER_CANCEL - TRIGGER_ON - TRIGGER_OFF - ) ], - 'functions' => [ qw( - zmMemVerify - zmMemInvalidate - zmMemRead - zmMemWrite - zmMemTidy - zmGetMonitorState - zmGetAlarmLocation - zmIsAlarmed - zmInAlarm - zmHasAlarmed - zmGetLastEvent - zmGetLastWriteTime - zmGetLastReadTime - zmMonitorEnable - zmMonitorDisable - zmMonitorSuspend - zmMonitorResume - zmTriggerEventOn - zmTriggerEventOff - zmTriggerEventCancel - zmTriggerShowtext - ) ], + 'constants' => [ qw( + STATE_IDLE + STATE_PREALARM + STATE_ALARM + STATE_ALERT + STATE_TAPE + ACTION_GET + ACTION_SET + ACTION_RELOAD + ACTION_SUSPEND + ACTION_RESUME + TRIGGER_CANCEL + TRIGGER_ON + TRIGGER_OFF + ) ], + 'functions' => [ qw( + zmMemVerify + zmMemInvalidate + zmMemRead + zmMemWrite + zmMemTidy + zmGetMonitorState + zmGetAlarmLocation + zmIsAlarmed + zmInAlarm + zmHasAlarmed + zmGetLastEvent + zmGetLastWriteTime + zmGetLastReadTime + zmMonitorEnable + zmMonitorDisable + zmMonitorSuspend + zmMonitorResume + zmTriggerEventOn + zmTriggerEventOff + zmTriggerEventCancel + zmTriggerShowtext + ) ], ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; @@ -144,42 +144,42 @@ our $mem_seq = 0; our $mem_data = { - "shared_data" => { "type"=>"SharedData", "seq"=>$mem_seq++, "contents"=> { - "size" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "last_write_index" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "last_read_index" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "state" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "last_event" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "action" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "brightness" => { "type"=>"int32", "seq"=>$mem_seq++ }, - "hue" => { "type"=>"int32", "seq"=>$mem_seq++ }, - "colour" => { "type"=>"int32", "seq"=>$mem_seq++ }, - "contrast" => { "type"=>"int32", "seq"=>$mem_seq++ }, - "alarm_x" => { "type"=>"int32", "seq"=>$mem_seq++ }, - "alarm_y" => { "type"=>"int32", "seq"=>$mem_seq++ }, - "valid" => { "type"=>"uint8", "seq"=>$mem_seq++ }, - "active" => { "type"=>"uint8", "seq"=>$mem_seq++ }, - "signal" => { "type"=>"uint8", "seq"=>$mem_seq++ }, - "format" => { "type"=>"uint8", "seq"=>$mem_seq++ }, - "imagesize" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "epadding1" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "epadding2" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "last_write_time" => { "type"=>"time_t64", "seq"=>$mem_seq++ }, - "last_read_time" => { "type"=>"time_t64", "seq"=>$mem_seq++ }, - "control_state" => { "type"=>"uint8[256]", "seq"=>$mem_seq++ }, - } - }, - "trigger_data" => { "type"=>"TriggerData", "seq"=>$mem_seq++, "contents"=> { - "size" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "trigger_state" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "trigger_score" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "padding" => { "type"=>"uint32", "seq"=>$mem_seq++ }, - "trigger_cause" => { "type"=>"int8[32]", "seq"=>$mem_seq++ }, - "trigger_text" => { "type"=>"int8[256]", "seq"=>$mem_seq++ }, - "trigger_showtext" => { "type"=>"int8[256]", "seq"=>$mem_seq++ }, - } - }, - "end" => { "seq"=>$mem_seq++, "size"=> 0 } + "shared_data" => { "type"=>"SharedData", "seq"=>$mem_seq++, "contents"=> { + "size" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "last_write_index" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "last_read_index" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "state" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "last_event" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "action" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "brightness" => { "type"=>"int32", "seq"=>$mem_seq++ }, + "hue" => { "type"=>"int32", "seq"=>$mem_seq++ }, + "colour" => { "type"=>"int32", "seq"=>$mem_seq++ }, + "contrast" => { "type"=>"int32", "seq"=>$mem_seq++ }, + "alarm_x" => { "type"=>"int32", "seq"=>$mem_seq++ }, + "alarm_y" => { "type"=>"int32", "seq"=>$mem_seq++ }, + "valid" => { "type"=>"uint8", "seq"=>$mem_seq++ }, + "active" => { "type"=>"uint8", "seq"=>$mem_seq++ }, + "signal" => { "type"=>"uint8", "seq"=>$mem_seq++ }, + "format" => { "type"=>"uint8", "seq"=>$mem_seq++ }, + "imagesize" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "epadding1" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "epadding2" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "last_write_time" => { "type"=>"time_t64", "seq"=>$mem_seq++ }, + "last_read_time" => { "type"=>"time_t64", "seq"=>$mem_seq++ }, + "control_state" => { "type"=>"uint8[256]", "seq"=>$mem_seq++ }, + } + }, + "trigger_data" => { "type"=>"TriggerData", "seq"=>$mem_seq++, "contents"=> { + "size" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "trigger_state" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "trigger_score" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "padding" => { "type"=>"uint32", "seq"=>$mem_seq++ }, + "trigger_cause" => { "type"=>"int8[32]", "seq"=>$mem_seq++ }, + "trigger_text" => { "type"=>"int8[256]", "seq"=>$mem_seq++ }, + "trigger_showtext" => { "type"=>"int8[256]", "seq"=>$mem_seq++ }, + } + }, + "end" => { "seq"=>$mem_seq++, "size"=> 0 } }; our $mem_size = 0; @@ -187,209 +187,248 @@ our $mem_verified = {}; sub zmMemInit { - my $offset = 0; + my $offset = 0; - foreach my $section_data ( sort { $a->{seq} <=> $b->{seq} } values( %$mem_data ) ) - { - $section_data->{offset} = $offset; - $section_data->{align} = 0; + foreach my $section_data ( sort { $a->{seq} <=> $b->{seq} } values( %$mem_data ) ) + { + $section_data->{offset} = $offset; + $section_data->{align} = 0; - if ( $section_data->{align} > 1 ) - { - my $rem = $offset % $section_data->{align}; - if ( $rem > 0 ) - { - $offset += ($section_data->{align} - $rem); - } - } - foreach my $member_data ( sort { $a->{seq} <=> $b->{seq} } values( %{$section_data->{contents}} ) ) - { - if ( $member_data->{type} eq "long" || $member_data->{type} eq "ulong" || $member_data->{type} eq "size_t") - { - $member_data->{size} = $member_data->{align} = $native; - } - elsif( $member_data->{type} eq "int64" || $member_data->{type} eq "uint64" || $member_data->{type} eq "time_t64") - { - $member_data->{size} = $member_data->{align} = 8; - } - elsif ( $member_data->{type} eq "int32" || $member_data->{type} eq "uint32" || $member_data->{type} eq "bool4" ) - { - $member_data->{size} = $member_data->{align} = 4; - } - elsif ($member_data->{type} eq "int16" || $member_data->{type} eq "uint16") - { - $member_data->{size} = $member_data->{align} = 2; - } - elsif ( $member_data->{type} eq "int8" || $member_data->{type} eq "uint8" || $member_data->{type} eq "bool1" ) - { - $member_data->{size} = $member_data->{align} = 1; - } - elsif ( $member_data->{type} =~ /^u?int8\[(\d+)\]$/ ) - { - $member_data->{size} = $1; - $member_data->{align} = 1; - } - else - { - Fatal( "Unexpected type '".$member_data->{type}."' found in shared data definition." ); - } + if ( $section_data->{align} > 1 ) + { + my $rem = $offset % $section_data->{align}; + if ( $rem > 0 ) + { + $offset += ($section_data->{align} - $rem); + } + } + foreach my $member_data ( sort { $a->{seq} <=> $b->{seq} } values( %{$section_data->{contents}} ) ) + { + if ( $member_data->{type} eq "long" + || $member_data->{type} eq "ulong" + || $member_data->{type} eq "size_t" + ) + { + $member_data->{size} = $member_data->{align} = $native; + } + elsif( $member_data->{type} eq "int64" + || $member_data->{type} eq "uint64" + || $member_data->{type} eq "time_t64" + ) + { + $member_data->{size} = $member_data->{align} = 8; + } + elsif ( $member_data->{type} eq "int32" + || $member_data->{type} eq "uint32" + || $member_data->{type} eq "bool4" + ) + { + $member_data->{size} = $member_data->{align} = 4; + } + elsif ($member_data->{type} eq "int16" + || $member_data->{type} eq "uint16" + ) + { + $member_data->{size} = $member_data->{align} = 2; + } + elsif ( $member_data->{type} eq "int8" + || $member_data->{type} eq "uint8" + || $member_data->{type} eq "bool1" + ) + { + $member_data->{size} = $member_data->{align} = 1; + } + elsif ( $member_data->{type} =~ /^u?int8\[(\d+)\]$/ ) + { + $member_data->{size} = $1; + $member_data->{align} = 1; + } + else + { + Fatal( "Unexpected type '".$member_data->{type} + ."' found in shared data definition." + ); + } - if ( $member_data->{align} > 1 && ($offset%$member_data->{align}) > 0 ) - { - $offset += ($member_data->{align} - ($offset%$member_data->{align})); - } - $member_data->{offset} = $offset; - $offset += $member_data->{size} - } - $section_data->{size} = $offset - $section_data->{offset}; - } + if ( $member_data->{align} > 1 && ($offset%$member_data->{align}) > 0 ) + { + $offset += ($member_data->{align} - ($offset%$member_data->{align})); + } + $member_data->{offset} = $offset; + $offset += $member_data->{size} + } + $section_data->{size} = $offset - $section_data->{offset}; + } - $mem_size = $offset; + $mem_size = $offset; } &zmMemInit(); -sub zmMemVerify( $ ) +sub zmMemVerify { - my $monitor = shift; - if ( !zmMemAttach( $monitor, $mem_size ) ) - { - return( undef ); - } + my $monitor = shift; + if ( !zmMemAttach( $monitor, $mem_size ) ) + { + return( undef ); + } my $mem_key = zmMemKey( $monitor ); - if ( !defined($mem_verified->{$mem_key}) ) - { - my $sd_size = zmMemRead( $monitor, "shared_data:size", 1 ); - if ( $sd_size != $mem_data->{shared_data}->{size} ) - { - if ( $sd_size ) - { - Error( "Shared data size conflict in shared_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{shared_data}->{size}.", got ".$sd_size ); - } - else - { - Debug( "Shared data size conflict in shared_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{shared_data}->{size}.", got ".$sd_size ); - } - return( undef ); - } - my $td_size = zmMemRead( $monitor, "trigger_data:size", 1 ); - if ( $td_size != $mem_data->{trigger_data}->{size} ) - { - if ( $td_size ) - { - Error( "Shared data size conflict in trigger_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{triggger_data}->{size}.", got ".$td_size ); - } - else - { - Debug( "Shared data size conflict in trigger_data for monitor ".$monitor->{Name}.", expected ".$mem_data->{triggger_data}->{size}.", got ".$td_size ); - } - return( undef ); - } - $mem_verified->{$mem_key} = !undef; - } - return( !undef ); + if ( !defined($mem_verified->{$mem_key}) ) + { + my $sd_size = zmMemRead( $monitor, "shared_data:size", 1 ); + if ( $sd_size != $mem_data->{shared_data}->{size} ) + { + if ( $sd_size ) + { + Error( "Shared data size conflict in shared_data for monitor " + .$monitor->{Name} + .", expected " + .$mem_data->{shared_data}->{size} + .", got " + .$sd_size + ); + } + else + { + Debug( "Shared data size conflict in shared_data for monitor " + .$monitor->{Name} + .", expected " + .$mem_data->{shared_data}->{size} + .", got ".$sd_size + ); + } + return( undef ); + } + my $td_size = zmMemRead( $monitor, "trigger_data:size", 1 ); + if ( $td_size != $mem_data->{trigger_data}->{size} ) + { + if ( $td_size ) + { + Error( "Shared data size conflict in trigger_data for monitor " + .$monitor->{Name} + .", expected " + .$mem_data->{triggger_data}->{size} + .", got " + .$td_size + ); + } + else + { + Debug( "Shared data size conflict in trigger_data for monitor " + .$monitor->{Name} + .", expected " + .$mem_data->{triggger_data}->{size} + .", got " + .$td_size + ); + } + return( undef ); + } + $mem_verified->{$mem_key} = !undef; + } + return( !undef ); } -sub zmMemRead( $$;$ ) +sub zmMemRead { - my $monitor = shift; - my $fields = shift; - my $nocheck = shift; + my $monitor = shift; + my $fields = shift; + my $nocheck = shift; - if ( !($nocheck || zmMemVerify( $monitor )) ) - { - return( undef ); - } + if ( !($nocheck || zmMemVerify( $monitor )) ) + { + return( undef ); + } - if ( !ref($fields) ) - { - $fields = [ $fields ]; - } - my @values; - foreach my $field ( @$fields ) - { - my ( $section, $element ) = split( /[\/:.]/, $field ); - Fatal( "Invalid shared data selector '$field'" ) if ( !$section || !$element ); + if ( !ref($fields) ) + { + $fields = [ $fields ]; + } + my @values; + foreach my $field ( @$fields ) + { + my ( $section, $element ) = split( /[\/:.]/, $field ); + Fatal( "Invalid shared data selector '$field'" ) if ( !$section || !$element ); - my $offset = $mem_data->{$section}->{contents}->{$element}->{offset}; - my $type = $mem_data->{$section}->{contents}->{$element}->{type}; - my $size = $mem_data->{$section}->{contents}->{$element}->{size}; + my $offset = $mem_data->{$section}->{contents}->{$element}->{offset}; + my $type = $mem_data->{$section}->{contents}->{$element}->{type}; + my $size = $mem_data->{$section}->{contents}->{$element}->{size}; - my $data = zmMemGet( $monitor, $offset, $size ); + my $data = zmMemGet( $monitor, $offset, $size ); if ( !defined($data) ) { Error( "Unable to read '$field' from memory for monitor ".$monitor->{Id} ); zmMemInvalidate( $monitor ); return( undef ); } - my $value; - if ( $type eq "long" ) - { - ( $value ) = unpack( "l!", $data ); - } - elsif ( $type eq "ulong" || $type eq "size_t" ) - { - ( $value ) = unpack( "L!", $data ); - } - elsif ( $type eq "int64" || $type eq "time_t64" ) - { - # The "q" type is only available on 64bit platforms, so use native. - ( $value ) = unpack( "l!", $data ); - } - elsif ( $type eq "uint64" ) - { - # The "q" type is only available on 64bit platforms, so use native. - ( $value ) = unpack( "L!", $data ); - } - elsif ( $type eq "int32" ) - { - ( $value ) = unpack( "l", $data ); - } - elsif ( $type eq "uint32" || $type eq "bool4" ) - { - ( $value ) = unpack( "L", $data ); - } - elsif ( $type eq "int16" ) - { - ( $value ) = unpack( "s", $data ); - } - elsif ( $type eq "uint16" ) - { - ( $value ) = unpack( "S", $data ); - } - elsif ( $type eq "int8" ) - { - ( $value ) = unpack( "c", $data ); - } - elsif ( $type eq "uint8" || $type eq "bool1" ) - { - ( $value ) = unpack( "C", $data ); - } - elsif ( $type =~ /^int8\[\d+\]$/ ) - { - ( $value ) = unpack( "Z".$size, $data ); - } - elsif ( $type =~ /^uint8\[\d+\]$/ ) - { - ( $value ) = unpack( "C".$size, $data ); - } - else - { - Fatal( "Unexpected type '".$type."' found for '".$field."'" ); - } - push( @values, $value ); - } - if ( wantarray() ) - { - return( @values ) - } - return( $values[0] ); + my $value; + if ( $type eq "long" ) + { + ( $value ) = unpack( "l!", $data ); + } + elsif ( $type eq "ulong" || $type eq "size_t" ) + { + ( $value ) = unpack( "L!", $data ); + } + elsif ( $type eq "int64" || $type eq "time_t64" ) + { + # The "q" type is only available on 64bit platforms, so use native. + ( $value ) = unpack( "l!", $data ); + } + elsif ( $type eq "uint64" ) + { + # The "q" type is only available on 64bit platforms, so use native. + ( $value ) = unpack( "L!", $data ); + } + elsif ( $type eq "int32" ) + { + ( $value ) = unpack( "l", $data ); + } + elsif ( $type eq "uint32" || $type eq "bool4" ) + { + ( $value ) = unpack( "L", $data ); + } + elsif ( $type eq "int16" ) + { + ( $value ) = unpack( "s", $data ); + } + elsif ( $type eq "uint16" ) + { + ( $value ) = unpack( "S", $data ); + } + elsif ( $type eq "int8" ) + { + ( $value ) = unpack( "c", $data ); + } + elsif ( $type eq "uint8" || $type eq "bool1" ) + { + ( $value ) = unpack( "C", $data ); + } + elsif ( $type =~ /^int8\[\d+\]$/ ) + { + ( $value ) = unpack( "Z".$size, $data ); + } + elsif ( $type =~ /^uint8\[\d+\]$/ ) + { + ( $value ) = unpack( "C".$size, $data ); + } + else + { + Fatal( "Unexpected type '".$type."' found for '".$field."'" ); + } + push( @values, $value ); + } + if ( wantarray() ) + { + return( @values ) + } + return( $values[0] ); } -sub zmMemInvalidate( $ ) +sub zmMemInvalidate { - my $monitor = shift; + my $monitor = shift; my $mem_key = zmMemKey($monitor); if ( $mem_key ) { @@ -398,307 +437,313 @@ sub zmMemInvalidate( $ ) } } -sub zmMemTidy() +sub zmMemTidy { zmMemClean(); } -sub zmMemWrite( $$;$ ) +sub zmMemWrite { - my $monitor = shift; - my $field_values = shift; - my $nocheck = shift; + my $monitor = shift; + my $field_values = shift; + my $nocheck = shift; - if ( !($nocheck || zmMemVerify( $monitor )) ) - { - return( undef ); - } + if ( !($nocheck || zmMemVerify( $monitor )) ) + { + return( undef ); + } - while ( my ( $field, $value ) = each( %$field_values ) ) - { - my ( $section, $element ) = split( /[\/:.]/, $field ); - Fatal( "Invalid shared data selector '$field'" ) if ( !$section || !$element ); + while ( my ( $field, $value ) = each( %$field_values ) ) + { + my ( $section, $element ) = split( /[\/:.]/, $field ); + Fatal( "Invalid shared data selector '$field'" ) + if ( !$section || !$element ); - my $offset = $mem_data->{$section}->{contents}->{$element}->{offset}; - my $type = $mem_data->{$section}->{contents}->{$element}->{type}; - my $size = $mem_data->{$section}->{contents}->{$element}->{size}; + my $offset = $mem_data->{$section}->{contents}->{$element}->{offset}; + my $type = $mem_data->{$section}->{contents}->{$element}->{type}; + my $size = $mem_data->{$section}->{contents}->{$element}->{size}; - my $data; - if ( $type eq "long" ) - { - $data = pack( "l!", $value ); - } - elsif ( $type eq "ulong" || $type eq "size_t" ) - { - $data = pack( "L!", $value ); - } - elsif ( $type eq "int64" || $type eq "time_t64" ) - { - # The "q" type is only available on 64bit platforms, so use native. - $data = pack( "l!", $value ); - } - elsif ( $type eq "uint64" ) - { - # The "q" type is only available on 64bit platforms, so use native. - $data = pack( "L!", $value ); - } - elsif ( $type eq "int32" ) - { - $data = pack( "l", $value ); - } - elsif ( $type eq "uint32" || $type eq "bool4" ) - { - $data = pack( "L", $value ); - } - elsif ( $type eq "int16" ) - { - $data = pack( "s", $value ); - } - elsif ( $type eq "uint16" ) - { - $data = pack( "S", $value ); - } - elsif ( $type eq "int8" ) - { - $data = pack( "c", $value ); - } - elsif ( $type eq "uint8" || $type eq "bool1" ) - { - $data = pack( "C", $value ); - } - elsif ( $type =~ /^int8\[\d+\]$/ ) - { - $data = pack( "Z".$size, $value ); - } - elsif ( $type =~ /^uint8\[\d+\]$/ ) - { - $data = pack( "C".$size, $value ); - } - else - { - Fatal( "Unexpected type '".$type."' found for '".$field."'" ); - } + my $data; + if ( $type eq "long" ) + { + $data = pack( "l!", $value ); + } + elsif ( $type eq "ulong" || $type eq "size_t" ) + { + $data = pack( "L!", $value ); + } + elsif ( $type eq "int64" || $type eq "time_t64" ) + { + # The "q" type is only available on 64bit platforms, so use native. + $data = pack( "l!", $value ); + } + elsif ( $type eq "uint64" ) + { + # The "q" type is only available on 64bit platforms, so use native. + $data = pack( "L!", $value ); + } + elsif ( $type eq "int32" ) + { + $data = pack( "l", $value ); + } + elsif ( $type eq "uint32" || $type eq "bool4" ) + { + $data = pack( "L", $value ); + } + elsif ( $type eq "int16" ) + { + $data = pack( "s", $value ); + } + elsif ( $type eq "uint16" ) + { + $data = pack( "S", $value ); + } + elsif ( $type eq "int8" ) + { + $data = pack( "c", $value ); + } + elsif ( $type eq "uint8" || $type eq "bool1" ) + { + $data = pack( "C", $value ); + } + elsif ( $type =~ /^int8\[\d+\]$/ ) + { + $data = pack( "Z".$size, $value ); + } + elsif ( $type =~ /^uint8\[\d+\]$/ ) + { + $data = pack( "C".$size, $value ); + } + else + { + Fatal( "Unexpected type '".$type."' found for '".$field."'" ); + } if ( !zmMemPut( $monitor, $offset, $size, $data ) ) { - Error( "Unable to write '$value' to '$field' in memory for monitor ".$monitor->{Id} ); + Error( "Unable to write '$value' to '$field' in memory for monitor " + .$monitor->{Id} + ); zmMemInvalidate( $monitor ); return( undef ); } - } - return( !undef ); + } + return( !undef ); } -sub zmGetMonitorState( $ ) +sub zmGetMonitorState { - my $monitor = shift; + my $monitor = shift; - return( zmMemRead( $monitor, "shared_data:state" ) ); + return( zmMemRead( $monitor, "shared_data:state" ) ); } -sub zmGetAlarmLocation( $ ) +sub zmGetAlarmLocation { - my $monitor = shift; + my $monitor = shift; - return( zmMemRead( $monitor, [ "shared_data:alarm_x", "shared_data:alarm_y" ] ) ); + return( zmMemRead( $monitor, [ "shared_data:alarm_x", "shared_data:alarm_y" ] ) ); } -sub zmSetControlState( $$ ) +sub zmSetControlState { - my $monitor = shift; - my $control_state = shift; + my $monitor = shift; + my $control_state = shift; - zmMemWrite( $monitor, { "shared_data:control_state" => $control_state } ); + zmMemWrite( $monitor, { "shared_data:control_state" => $control_state } ); } -sub zmGetControlState( $ ) +sub zmGetControlState { - my $monitor = shift; + my $monitor = shift; - return( zmMemRead( $monitor, "shared_data:control_state" ) ); + return( zmMemRead( $monitor, "shared_data:control_state" ) ); } -sub zmSaveControlState( $$ ) +sub zmSaveControlState { - my $monitor = shift; - my $control_state = shift; + my $monitor = shift; + my $control_state = shift; - zmSetControlState( $monitor, freeze( $control_state ) ); + zmSetControlState( $monitor, freeze( $control_state ) ); } -sub zmRestoreControlState( $ ) +sub zmRestoreControlState { - my $monitor = shift; + my $monitor = shift; - return( thaw( zmGetControlState( $monitor ) ) ); + return( thaw( zmGetControlState( $monitor ) ) ); } -sub zmIsAlarmed( $ ) +sub zmIsAlarmed { - my $monitor = shift; + my $monitor = shift; - my $state = zmGetMonitorState( $monitor ); + my $state = zmGetMonitorState( $monitor ); - return( $state == STATE_ALARM ); + return( $state == STATE_ALARM ); } -sub zmInAlarm( $ ) +sub zmInAlarm { - my $monitor = shift; + my $monitor = shift; - my $state = zmGetMonitorState( $monitor ); + my $state = zmGetMonitorState( $monitor ); - return( $state == STATE_ALARM || $state == STATE_ALERT ); + return( $state == STATE_ALARM || $state == STATE_ALERT ); } -sub zmHasAlarmed( $$ ) +sub zmHasAlarmed { - my $monitor = shift; - my $last_event_id = shift; + my $monitor = shift; + my $last_event_id = shift; - my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state", "shared_data:last_event" ] ); + my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state" + ,"shared_data:last_event" + ] + ); - if ( $state == STATE_ALARM || $state == STATE_ALERT ) - { - return( $last_event ); - } - elsif( $last_event != $last_event_id ) - { - return( $last_event ); - } - return( undef ); + if ( $state == STATE_ALARM || $state == STATE_ALERT ) + { + return( $last_event ); + } + elsif( $last_event != $last_event_id ) + { + return( $last_event ); + } + return( undef ); } -sub zmGetLastEvent( $ ) +sub zmGetLastEvent { - my $monitor = shift; + my $monitor = shift; - return( zmMemRead( $monitor, "shared_data:last_event" ) ); + return( zmMemRead( $monitor, "shared_data:last_event" ) ); } -sub zmGetLastWriteTime( $ ) +sub zmGetLastWriteTime { - my $monitor = shift; + my $monitor = shift; - return( zmMemRead( $monitor, "shared_data:last_write_time" ) ); + return( zmMemRead( $monitor, "shared_data:last_write_time" ) ); } -sub zmGetLastReadTime( $ ) +sub zmGetLastReadTime { - my $monitor = shift; + my $monitor = shift; - return( zmMemRead( $monitor, "shared_data:last_read_time" ) ); + return( zmMemRead( $monitor, "shared_data:last_read_time" ) ); } -sub zmGetMonitorActions( $ ) +sub zmGetMonitorActions { - my $monitor = shift; + my $monitor = shift; - return( zmMemRead( $monitor, "shared_data:action" ) ); + return( zmMemRead( $monitor, "shared_data:action" ) ); } -sub zmMonitorEnable( $ ) +sub zmMonitorEnable { - my $monitor = shift; + my $monitor = shift; - my $action = zmMemRead( $monitor, "shared_data:action" ); - $action |= ACTION_SUSPEND; - zmMemWrite( $monitor, { "shared_data:action" => $action } ); + my $action = zmMemRead( $monitor, "shared_data:action" ); + $action |= ACTION_SUSPEND; + zmMemWrite( $monitor, { "shared_data:action" => $action } ); } -sub zmMonitorDisable( $ ) +sub zmMonitorDisable { - my $monitor = shift; + my $monitor = shift; - my $action = zmMemRead( $monitor, "shared_data:action" ); - $action |= ACTION_RESUME; - zmMemWrite( $monitor, { "shared_data:action" => $action } ); + my $action = zmMemRead( $monitor, "shared_data:action" ); + $action |= ACTION_RESUME; + zmMemWrite( $monitor, { "shared_data:action" => $action } ); } -sub zmMonitorSuspend( $ ) +sub zmMonitorSuspend { - my $monitor = shift; + my $monitor = shift; - my $action = zmMemRead( $monitor, "shared_data:action" ); - $action |= ACTION_SUSPEND; - zmMemWrite( $monitor, { "shared_data:action" => $action } ); + my $action = zmMemRead( $monitor, "shared_data:action" ); + $action |= ACTION_SUSPEND; + zmMemWrite( $monitor, { "shared_data:action" => $action } ); } -sub zmMonitorResume( $ ) +sub zmMonitorResume { - my $monitor = shift; + my $monitor = shift; - my $action = zmMemRead( $monitor, "shared_data:action" ); - $action |= ACTION_RESUME; - zmMemWrite( $monitor, { "shared_data:action" => $action } ); + my $action = zmMemRead( $monitor, "shared_data:action" ); + $action |= ACTION_RESUME; + zmMemWrite( $monitor, { "shared_data:action" => $action } ); } -sub zmGetTriggerState( $ ) +sub zmGetTriggerState { - my $monitor = shift; + my $monitor = shift; - return( zmMemRead( $monitor, "trigger_data:trigger_state" ) ); + return( zmMemRead( $monitor, "trigger_data:trigger_state" ) ); } -sub zmTriggerEventOn( $$$;$$ ) +sub zmTriggerEventOn { - my $monitor = shift; - my $score = shift; - my $cause = shift; - my $text = shift; - my $showtext = shift; + my $monitor = shift; + my $score = shift; + my $cause = shift; + my $text = shift; + my $showtext = shift; - my $values = { - "trigger_data:trigger_score" => $score, - "trigger_data:trigger_cause" => $cause, - }; - $values->{"trigger_data:trigger_text"} = $text if ( defined($text) ); - $values->{"trigger_data:trigger_showtext"} = $showtext if ( defined($showtext) ); - $values->{"trigger_data:trigger_state"} = TRIGGER_ON; # Write state last so event not read incomplete + my $values = { + "trigger_data:trigger_score" => $score, + "trigger_data:trigger_cause" => $cause, + }; + $values->{"trigger_data:trigger_text"} = $text if ( defined($text) ); + $values->{"trigger_data:trigger_showtext"} = $showtext if ( defined($showtext) ); + $values->{"trigger_data:trigger_state"} = TRIGGER_ON; # Write state last so event not read incomplete - zmMemWrite( $monitor, $values ); + zmMemWrite( $monitor, $values ); } -sub zmTriggerEventOff( $ ) +sub zmTriggerEventOff { - my $monitor = shift; + my $monitor = shift; - my $values = { - "trigger_data:trigger_state" => TRIGGER_OFF, - "trigger_data:trigger_score" => 0, - "trigger_data:trigger_cause" => "", - "trigger_data:trigger_text" => "", - "trigger_data:trigger_showtext" => "", - }; + my $values = { + "trigger_data:trigger_state" => TRIGGER_OFF, + "trigger_data:trigger_score" => 0, + "trigger_data:trigger_cause" => "", + "trigger_data:trigger_text" => "", + "trigger_data:trigger_showtext" => "", + }; - zmMemWrite( $monitor, $values ); + zmMemWrite( $monitor, $values ); } -sub zmTriggerEventCancel( $ ) +sub zmTriggerEventCancel { - my $monitor = shift; + my $monitor = shift; - my $values = { - "trigger_data:trigger_state" => TRIGGER_CANCEL, - "trigger_data:trigger_score" => 0, - "trigger_data:trigger_cause" => "", - "trigger_data:trigger_text" => "", - "trigger_data:trigger_showtext" => "", - }; + my $values = { + "trigger_data:trigger_state" => TRIGGER_CANCEL, + "trigger_data:trigger_score" => 0, + "trigger_data:trigger_cause" => "", + "trigger_data:trigger_text" => "", + "trigger_data:trigger_showtext" => "", + }; - zmMemWrite( $monitor, $values ); + zmMemWrite( $monitor, $values ); } -sub zmTriggerShowtext( $$ ) +sub zmTriggerShowtext { - my $monitor = shift; - my $showtext = shift; + my $monitor = shift; + my $showtext = shift; - my $values = { - "trigger_data:trigger_showtext" => $showtext, - }; + my $values = { + "trigger_data:trigger_showtext" => $showtext, + }; - zmMemWrite( $monitor, $values ); + zmMemWrite( $monitor, $values ); } 1; @@ -722,44 +767,76 @@ ZoneMinder::MappedMem - ZoneMinder Mapped Memory access module } } - ( $lri, $lwi ) = zmMemRead( $monitor, [ "shared_data:last_read_index", "shared_data:last_write_index" ] ); + ( $lri, $lwi ) = zmMemRead( $monitor, [ "shared_data:last_read_index", + "shared_data:last_write_index" + ] + ); zmMemWrite( $monitor, { "trigger_data:trigger_showtext" => "Some Text" } ); =head1 DESCRIPTION -The ZoneMinder:MappedMem module contains methods for accessing and writing to mapped memory as well as helper methods for common operations. +The ZoneMinder:MappedMem module contains methods for accessing and writing +to mapped memory as well as helper methods for common operations. -The core elements of ZoneMinder used mapped memory to allow multiple access to resources. Although ZoneMinder scripts have used this information before, up until now it was difficult to access and prone to errors. This module introduces a common API for mapped memory access (both reading and writing) making it a lot easier to customise scripts or even create your own. +The core elements of ZoneMinder used mapped memory to allow multiple access +to resources. Although ZoneMinder scripts have used this information +before, up until now it was difficult to access and prone to errors. This +module introduces a common API for mapped memory access (both reading and +writing) making it a lot easier to customise scripts or even create your +own. -All the methods listed below require a 'monitor' parameter. This must be a reference to a hash with at least the 'Id' field set to the monitor id of the mapped memory you wish to access. Using database methods to select the monitor details will also return this kind of data. Some of the mapped memory methods will add and amend new fields to this hash. - -=over 4 +All the methods listed below require a 'monitor' parameter. This must be a +reference to a hash with at least the 'Id' field set to the monitor id of +the mapped memory you wish to access. Using database methods to select the +monitor details will also return this kind of data. Some of the mapped +memory methods will add and amend new fields to this hash. =head1 METHODS +=over 4 + =item zmMemVerify ( $monitor ); -Verify that the mapped memory of the monitor given exists and is valid. It will return an undefined value if it is not valid. You should generally call this method first before using any of the other methods, but most of the remaining methods will also do so if the memory has not already been verified. +Verify that the mapped memory of the monitor given exists and is valid. It +will return an undefined value if it is not valid. You should generally +call this method first before using any of the other methods, but most of +the remaining methods will also do so if the memory has not already been +verified. =item zmMemInvalidate ( $monitor ); -Following an error, reset the mapped memory ids and attempt to reverify on the next operation. This is mostly used when a mapped memory segment has gone away and been recreated with a different id. +Following an error, reset the mapped memory ids and attempt to reverify on +the next operation. This is mostly used when a mapped memory segment has +gone away and been recreated with a different id. =item zmMemRead ( $monitor, $readspec ); -This method is used to read data from mapped memory attached to the given monitor. The mapped memory will be verified if it has not already been. The 'readspec' must either be a string of the form "
:" or a reference to an array of strings of the same format. In the first case a single value is returned, in the latter case a list of values is return. Errors will cause undefined to be returned. The allowable sections and field names are described below. +This method is used to read data from mapped memory attached to the given +monitor. The mapped memory will be verified if it has not already been. The +'readspec' must either be a string of the form "
:" or a +reference to an array of strings of the same format. In the first case a +single value is returned, in the latter case a list of values is return. +Errors will cause undefined to be returned. The allowable sections and +field names are described below. =item zmMemWrite ( $monitor, $writespec ); -This method is used to write data to mapped memory attached to the given monitor. The mapped memory will be verified if it has not already been. The 'writespec' must be a reference to a hash with keys of the form "
:" and values as the data to be written. Errors will cause undefined to be returned, otherwise a non-undefined value will be returned. The allowable sections and field names are described below. +This method is used to write data to mapped memory attached to the given +monitor. The mapped memory will be verified if it has not already been. The +'writespec' must be a reference to a hash with keys of the form +"
:" and values as the data to be written. Errors will cause +undefined to be returned, otherwise a non-undefined value will be returned. +The allowable sections and field names are described below. =item $state = zmGetMonitorState ( $monitor ); -Return the current state of the given monitor. This is an integer value and can be compared with the STATE constants given below. +Return the current state of the given monitor. This is an integer value and +can be compared with the STATE constants given below. =item $event_id = zmGetLastEvent ( $monitor ); -Return the event id of the last event that the monitor generated, or 0 if no event has been generated by the current monitor process. +Return the event id of the last event that the monitor generated, or 0 if +no event has been generated by the current monitor process. =item zmIsAlarmed ( $monitor ); @@ -767,27 +844,38 @@ Return 1 if the monitor given is currently in an alarm state, 0 otherwise. =item zmInAlarm ( $monitor ); -Return 1 if the monitor given is currently in an alarm or alerted state, 0 otherwise. +Return 1 if the monitor given is currently in an alarm or alerted state, 0 +otherwise. =item zmHasAlarmed ( $monitor ); -Return 1 if the given monitor is in an alarm state, or has been in an alarm state since the last call to this method. +Return 1 if the given monitor is in an alarm state, or has been in an alarm +state since the last call to this method. =item ( $x, $y ) = zmGetAlarmLocation ( $monitor ); -Return an x,y pair indicating the image co-ordinates of the centre of the last motion event generated by the given monitor. If no event has been generated by the current monitor process, or the alarm was not motion related, returns -1,-1. +Return an x,y pair indicating the image co-ordinates of the centre of the +last motion event generated by the given monitor. If no event has been +generated by the current monitor process, or the alarm was not motion +related, returns -1,-1. =item zmGetLastWriteTime ( $monitor ); -Returns the time (in utc seconds) since the last image was captured by the given monitor and written to shared memory, or 0 otherwise. +Returns the time (in utc seconds) since the last image was captured by the +given monitor and written to shared memory, or 0 otherwise. =item zmGetLastReadTime ( $monitor ); -Returns the time (in utc seconds) since the last image was read from shared memory by the analysis daemon of the given monitor, or 0 otherwise or if the monitor is in monitor only mode. +Returns the time (in utc seconds) since the last image was read from shared +memory by the analysis daemon of the given monitor, or 0 otherwise or if +the monitor is in monitor only mode. =item zmMonitorSuspend ( $monitor ); -Suspend the given monitor from generating events caused by motion. This method can be used to prevent camera actions such as panning or zooming from causing events. If configured to do so, the monitor may automatically resume after a defined period. +Suspend the given monitor from generating events caused by motion. This +method can be used to prevent camera actions such as panning or zooming +from causing events. If configured to do so, the monitor may automatically +resume after a defined period. =item zmMonitorResume ( $monitor ); @@ -795,23 +883,43 @@ Allow the given monitor to resume generating events caused by motion. =item zmTriggerEventOn ( $monitor, $score, $cause [, $text, $showtext ] ); -Trigger the given monitor to generate an event. You must supply an event score and a cause string indicating the reason for the event. You may also supply a text string containing further details about the event and a showtext string which may be included in the timestamp annotation on any images captured during the event, if configured to do so. +Trigger the given monitor to generate an event. You must supply an event +score and a cause string indicating the reason for the event. You may also +supply a text string containing further details about the event and a +showtext string which may be included in the timestamp annotation on any +images captured during the event, if configured to do so. =item zmTriggerEventOff ( $monitor ); -Trigger the given monitor to not generate any events. This method does not cancel zmTriggerEventOn, but is exclusive to it. This method is intended to allow external triggers to prevent normal events being generated by monitors in the same way as zmMonitorSuspend but applies to all events and not just motion, and is intended for longer timescales than are appropriate for suspension. +Trigger the given monitor to not generate any events. This method does not +cancel zmTriggerEventOn, but is exclusive to it. This method is intended to +allow external triggers to prevent normal events being generated by +monitors in the same way as zmMonitorSuspend but applies to all events and +not just motion, and is intended for longer timescales than are appropriate +for suspension. =item zmTriggerEventCancel ( $monitor ); -Cancel any previous trigger on or off requests. This stops a triggered alarm if it exists from a previous 'on' and allows events to be generated once more following a previous 'off'. +Cancel any previous trigger on or off requests. This stops a triggered +alarm if it exists from a previous 'on' and allows events to be generated +once more following a previous 'off'. =item zmTriggerShowtext ( $monitor, $showtest ); -Indicate that the given text should be displayed in the timestamp annotation on any images captured, if the format of the annotation string defined for the monitor permits. +Indicate that the given text should be displayed in the timestamp +annotation on any images captured, if the format of the annotation string +defined for the monitor permits. + +=back =head1 DATA -The data fields in mapped memory that may be accessed are as follows. There are two main sections, shared_data which is general data and trigger_data which is used for event triggering. Whilst reading from these fields is harmless, extreme care must be taken when writing to mapped memory, especially in the shared_data section as this is normally written to only by monitor capture and analysis processes. +The data fields in mapped memory that may be accessed are as follows. There +are two main sections, shared_data which is general data and trigger_data +which is used for event triggering. Whilst reading from these fields is +harmless, extreme care must be taken when writing to mapped memory, +especially in the shared_data section as this is normally written to only +by monitor capture and analysis processes. shared_data The general mapped memory section size The size, in bytes, of this section @@ -842,19 +950,40 @@ The data fields in mapped memory that may be accessed are as follows. There are =head1 CONSTANTS -The following constants are used by the methods above, but can also be used by user scripts if required. +The following constants are used by the methods above, but can also be used +by user scripts if required. + +=over 4 =item STATE_IDLE STATE_PREALARM STATE_ALARM STATE_ALERT STATE_TAPE -These constants define the state of the monitor with respect to alarms and events. They are used in the shared_data:state field. +These constants define the state of the monitor with respect to alarms and +events. They are used in the shared_data:state field. =item ACTION_GET ACTION_SET ACTION_RELOAD ACTION_SUSPEND ACTION_RESUME -These constants defines the various values that can exist in the shared_data:action field. This is a bitmask which when non-zero defines an action that an executing monitor process should take. ACTION_GET requires that the current values of brightness, contrast, colour and hue are taken from the camera and written to the equivalent mapped memory fields. ACTION_SET implies the reverse, that the values in mapped memory should be written to the camera. ACTION_RELOAD signal that the monitor process should reload itself from the database in case any settings have changed there. ACTION_SUSPEND signals that a monitor should stop exaiming images for motion, though other alarms may still occur. ACTION_RESUME sigansl that a monitor should resume motion detectiom. +These constants defines the various values that can exist in the +shared_data:action field. This is a bitmask which when non-zero defines an +action that an executing monitor process should take. ACTION_GET requires +that the current values of brightness, contrast, colour and hue are taken +from the camera and written to the equivalent mapped memory fields. +ACTION_SET implies the reverse, that the values in mapped memory should be +written to the camera. ACTION_RELOAD signal that the monitor process should +reload itself from the database in case any settings have changed there. +ACTION_SUSPEND signals that a monitor should stop exaiming images for +motion, though other alarms may still occur. ACTION_RESUME sigansl that a +monitor should resume motion detectiom. -=item TRIGGER_CANCEL TRIGGER_ON TRIGGER_OFF +=item TRIGGER_CANCEL TRIGGER_ON TRIGGER_OFF -These constants are used in the definition of external triggers. TRIGGER_CANCEL is used to indicated that any previous trigger settings should be cancelled, TRIGGER_ON signals that an alarm should be created (or continued)) as a result of the current trigger and TRIGGER_OFF signals that the trigger should prevent any alarms from being generated. See the trigger methods above for further details. +These constants are used in the definition of external triggers. +TRIGGER_CANCEL is used to indicated that any previous trigger settings +should be cancelled, TRIGGER_ON signals that an alarm should be created (or +continued)) as a result of the current trigger and TRIGGER_OFF signals that +the trigger should prevent any alarms from being generated. See the trigger +methods above for further details. + +=back =head1 EXPORT diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm b/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm index 173c48493..017b5aa8c 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory/Mapped.pm @@ -36,18 +36,18 @@ our @ISA = qw(Exporter ZoneMinder::Base); # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. -# This allows declaration use ZoneMinder ':all'; +# This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( - 'functions' => [ qw( - zmMemKey - zmMemAttach - zmMemDetach - zmMemGet - zmMemPut - zmMemClean - ) ], + 'functions' => [ qw( + zmMemKey + zmMemAttach + zmMemDetach + zmMemGet + zmMemPut + zmMemClean + ) ], ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; @@ -68,117 +68,131 @@ use ZoneMinder::Logger qw(:all); use Sys::Mmap; -sub zmMemKey( $ ) +sub zmMemKey { - my $monitor = shift; + my $monitor = shift; return( defined($monitor->{MMapAddr})?$monitor->{MMapAddr}:undef ); } -sub zmMemAttach( $$ ) +sub zmMemAttach { - my ( $monitor, $size ) = @_; - if ( ! $size ) { - Error( "No size passed to zmMemAttach for monitor $$monitor{Id}\n" ); - return( undef ); - } - if ( !defined($monitor->{MMapAddr}) ) - { + my ( $monitor, $size ) = @_; + if ( ! $size ) { + Error( "No size passed to zmMemAttach for monitor $$monitor{Id}\n" ); + return( undef ); + } + if ( !defined($monitor->{MMapAddr}) ) + { my $mmap_file = $Config{ZM_PATH_MAP}."/zm.mmap.".$monitor->{Id}; - if ( ! -e $mmap_file ) { - Error( sprintf( "Memory map file '%s' does not exist. zmc might not be running.", $mmap_file ) ); - return ( undef ); - } + if ( ! -e $mmap_file ) { + Error( sprintf( "Memory map file '%s' does not exist. zmc might not be running." + , $mmap_file + ) + ); + return ( undef ); + } - my $mmap_file_size = -s $mmap_file; + my $mmap_file_size = -s $mmap_file; - if ( $mmap_file_size < $size ) { - Error( sprintf( "Memory map file '%s' should have been %d but was instead %d", $mmap_file, $size, $mmap_file_size ) ); - return ( undef ); - } - if ( !open( MMAP, "+<".$mmap_file ) ) + if ( $mmap_file_size < $size ) { + Error( sprintf( "Memory map file '%s' should have been %d but was instead %d" + , $mmap_file + , $size + , $mmap_file_size + ) + ); + return ( undef ); + } + if ( !open( MMAP, "+<", $mmap_file ) ) { - Error( sprintf( "Can't open memory map file '%s': $!\n", $mmap_file ) ); - return( undef ); + Error( sprintf( "Can't open memory map file '%s': $!\n", $mmap_file ) ); + return( undef ); } my $mmap = undef; my $mmap_addr = mmap( $mmap, $size, PROT_READ|PROT_WRITE, MAP_SHARED, \*MMAP ); if ( !$mmap_addr || !$mmap ) { - Error( sprintf( "Can't mmap to file '%s': $!\n", $mmap_file ) ); - close( MMAP ); - return( undef ); + Error( sprintf( "Can't mmap to file '%s': $!\n", $mmap_file ) ); + close( MMAP ); + return( undef ); } - $monitor->{MMapHandle} = \*MMAP; - $monitor->{MMapAddr} = $mmap_addr; - $monitor->{MMap} = \$mmap; - } - return( !undef ); + $monitor->{MMapHandle} = \*MMAP; + $monitor->{MMapAddr} = $mmap_addr; + $monitor->{MMap} = \$mmap; + } + return( !undef ); } -sub zmMemDetach( $ ) +sub zmMemDetach { - my $monitor = shift; + my $monitor = shift; if ( $monitor->{MMap} ) { if ( ! munmap( ${$monitor->{MMap}} ) ) { - Warn( "Unable to munmap for monitor $$monitor{Id}\n"); - } - delete $monitor->{MMap}; + Warn( "Unable to munmap for monitor $$monitor{Id}\n"); + } + delete $monitor->{MMap}; } if ( $monitor->{MMapAddr} ) { - delete $monitor->{MMapAddr}; + delete $monitor->{MMapAddr}; } if ( $monitor->{MMapHandle} ) { close( $monitor->{MMapHandle} ); - delete $monitor->{MMapHandle}; + delete $monitor->{MMapHandle}; } } -sub zmMemGet( $$$ ) +sub zmMemGet { - my $monitor = shift; - my $offset = shift; - my $size = shift; + my $monitor = shift; + my $offset = shift; + my $size = shift; - my $mmap = $monitor->{MMap}; + my $mmap = $monitor->{MMap}; if ( !$mmap || !$$mmap ) { - Error( sprintf( "Can't read from mapped memory for monitor '%d', gone away?", $monitor->{Id} ) ); + Error( sprintf( "Can't read from mapped memory for monitor '%d', gone away?" + , $monitor->{Id} + ) + ); return( undef ); } - my $data = substr( $$mmap, $offset, $size ); + my $data = substr( $$mmap, $offset, $size ); return( $data ); } -sub zmMemPut( $$$$ ) +sub zmMemPut { - my $monitor = shift; - my $offset = shift; - my $size = shift; - my $data = shift; + my $monitor = shift; + my $offset = shift; + my $size = shift; + my $data = shift; - my $mmap = $monitor->{MMap}; + my $mmap = $monitor->{MMap}; if ( !$mmap || !$$mmap ) { - Error( sprintf( "Can't write mapped memory for monitor '%d', gone away?", $monitor->{Id} ) ); + Error( sprintf( "Can't write mapped memory for monitor '%d', gone away?" + , $monitor->{Id} + ) + ); return( undef ); } - substr( $$mmap, $offset, $size ) = $data; - return( !undef ); + substr( $$mmap, $offset, $size ) = $data; + return( !undef ); } sub zmMemClean { - Debug( "Removing memory map files\n" ); + Debug( "Removing memory map files\n" ); my $mapPath = $Config{ZM_PATH_MAP}."/zm.mmap.*"; foreach my $mapFile( glob( $mapPath ) ) { ( $mapFile ) = $mapFile =~ /^(.+)$/; - Debug( "Removing memory map file '$mapFile'\n" ); + Debug( "Removing memory map file '$mapFile'\n" ); unlink( $mapFile ); } } diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory/Shared.pm b/scripts/ZoneMinder/lib/ZoneMinder/Memory/Shared.pm index 59dc399f1..301327095 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory/Shared.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory/Shared.pm @@ -19,7 +19,7 @@ # # ========================================================================== # -# This module contains the common definitions and functions used by the rest +# This module contains the common definitions and functions used by the rest # of the ZoneMinder scripts # package ZoneMinder::Memory::Shared; @@ -39,18 +39,18 @@ eval 'sub IPC_CREAT {0001000}' unless defined &IPC_CREAT; # names by default without a very good reason. Use EXPORT_OK instead. # Do not simply export all your public functions/methods/constants. -# This allows declaration use ZoneMinder ':all'; +# This allows declaration use ZoneMinder ':all'; # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( - 'functions' => [ qw( + 'functions' => [ qw( zmMemKey zmMemAttach zmMemDetach zmMemGet zmMemPut zmMemClean - ) ], + ) ], ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; @@ -69,82 +69,98 @@ our $VERSION = $ZoneMinder::Base::VERSION; use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); -sub zmMemKey( $ ) +sub zmMemKey { - my $monitor = shift; - return( defined($monitor->{ShmKey})?$monitor->{ShmKey}:undef ); + my $monitor = shift; + return( defined($monitor->{ShmKey})?$monitor->{ShmKey}:undef ); } -sub zmMemAttach( $$ ) +sub zmMemAttach { - my $monitor = shift; - my $size = shift; - if ( !defined($monitor->{ShmId}) ) - { - my $shm_key = (hex($Config{ZM_SHM_KEY})&0xffff0000)|$monitor->{Id}; - my $shm_id = shmget( $shm_key, $size, &IPC_CREAT | 0777 ); - if ( !defined($shm_id) ) - { - Error( sprintf( "Can't get shared memory id '%x', %d: $!\n", $shm_key, $monitor->{Id} ) ); - return( undef ); - } - $monitor->{ShmKey} = $shm_key; - $monitor->{ShmId} = $shm_id; - } - return( !undef ); + my $monitor = shift; + my $size = shift; + if ( !defined($monitor->{ShmId}) ) + { + my $shm_key = (hex($Config{ZM_SHM_KEY})&0xffff0000)|$monitor->{Id}; + my $shm_id = shmget( $shm_key, $size, &IPC_CREAT | 0777 ); + if ( !defined($shm_id) ) + { + Error( sprintf( "Can't get shared memory id '%x', %d: $!\n" + , $shm_key + , $monitor->{Id} + ) + ); + return( undef ); + } + $monitor->{ShmKey} = $shm_key; + $monitor->{ShmId} = $shm_id; + } + return( !undef ); } -sub zmMemDetach( $ ) +sub zmMemDetach { - my $monitor = shift; + my $monitor = shift; - delete $monitor->{ShmId}; + delete $monitor->{ShmId}; } -sub zmMemGet( $$$ ) +sub zmMemGet { - my $monitor = shift; - my $offset = shift; - my $size = shift; + my $monitor = shift; + my $offset = shift; + my $size = shift; + + my $shm_key = $monitor->{ShmKey}; + my $shm_id = $monitor->{ShmId}; - my $shm_key = $monitor->{ShmKey}; - my $shm_id = $monitor->{ShmId}; - my $data; if ( !shmread( $shm_id, $data, $offset, $size ) ) { - Error( sprintf( "Can't read from shared memory '%x/%d': $!", $shm_key, $shm_id ) ); + Error( sprintf( "Can't read from shared memory '%x/%d': $!" + , $shm_key + , $shm_id + ) + ); return( undef ); } - return( $data ); + return( $data ); } -sub zmMemPut( $$$$ ) +sub zmMemPut { - my $monitor = shift; - my $offset = shift; - my $size = shift; - my $data = shift; + my $monitor = shift; + my $offset = shift; + my $size = shift; + my $data = shift; + + my $shm_key = $monitor->{ShmKey}; + my $shm_id = $monitor->{ShmId}; - my $shm_key = $monitor->{ShmKey}; - my $shm_id = $monitor->{ShmId}; - if ( !shmwrite( $shm_id, $data, $offset, $size ) ) { - Error( sprintf( "Can't write to shared memory '%x/%d': $!", $shm_key, $shm_id ) ); + Error( sprintf( "Can't write to shared memory '%x/%d': $!" + , $shm_key + , $shm_id + ) + ); return( undef ); } - return( !undef ); + return( !undef ); } sub zmMemClean { Debug( "Removing shared memory\n" ); # Find ZoneMinder shared memory - my $command = "ipcs -m | grep '^".substr( sprintf( "0x%x", hex($Config{ZM_SHM_KEY}) ), 0, -2 )."'"; + my $command = "ipcs -m | grep '^" + .substr( sprintf( "0x%x", hex($Config{ZM_SHM_KEY}) ), 0, -2 ) + ."'" + ; Debug( "Checking for shared memory with '$command'\n" ); - open( CMD, "$command |" ) or Fatal( "Can't execute '$command': $!" ); - while( ) + open( my $CMD, '<', "$command |" ) + or Fatal( "Can't execute '$command': $!" ); + while( <$CMD> ) { chomp; my ( $key, $id ) = split( /\s+/ ); @@ -156,7 +172,7 @@ sub zmMemClean qx( $command ); } } - close( CMD ); + close( $CMD ); } 1; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Server.pm b/scripts/ZoneMinder/lib/ZoneMinder/Server.pm new file mode 100644 index 000000000..df8f2fb10 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Server.pm @@ -0,0 +1,161 @@ +# ========================================================================== +# +# ZoneMinder Server Module, $Date$, $Revision$ +# Copyright (C) 2001-2008 Philip Coombes +# +# 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 module contains the common definitions and functions used by the rest +# of the ZoneMinder scripts +# +package ZoneMinder::Server; + +use 5.006; +use strict; +use warnings; + +require Exporter; +require ZoneMinder::Base; + +our @ISA = qw(Exporter ZoneMinder::Base); + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. + +# This allows declaration use ZoneMinder ':all'; +# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK +# will save memory. +our %EXPORT_TAGS = ( + 'functions' => [ qw( + ) ] +); +push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; + +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); + +our @EXPORT = qw(); + +our $VERSION = $ZoneMinder::Base::VERSION; + +# ========================================================================== +# +# General Utility Functions +# +# ========================================================================== + +use ZoneMinder::Config qw(:all); +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Database qw(:all); + +use POSIX; + +sub new { + my ( $parent, $id, $data ) = @_; + + my $self = {}; + bless $self, $parent; + if ( ( $$self{Id} = $id ) or $data ) { +#$log->debug("loading $parent $id") if $debug or DEBUG_ALL; + $self->load( $data ); + } + return $self; +} # end sub new + +sub load { + my ( $self, $data ) = @_; + my $type = ref $self; + if ( ! $data ) { +#$log->debug("Object::load Loading from db $type"); + $data = $ZoneMinder::Database::dbh->selectrow_hashref( 'SELECT * FROM Servers WHERE Id=?', {}, $$self{Id} ); + if ( ! $data ) { + if ( $ZoneMinder::Database::dbh->errstr ) { + Error( "Failure to load Server record for $$self{id}: Reason: " . $ZoneMinder::Database::dbh->errstr ); + } # end if + } # end if + } # end if ! $data + if ( $data and %$data ) { + @$self{keys %$data} = values %$data; + } # end if +} # end sub load + +sub Name { + if ( @_ > 1 ) { + $_[0]{Name} = $_[1]; + } + return $_[0]{Name}; +} # end sub Name + +sub Hostname { + if ( @_ > 1 ) { + $_[0]{Hostname} = $_[1]; + } + return $_[0]{Hostname}; +} # end sub Hostname + +1; +__END__ +# Below is stub documentation for your module. You'd better edit it! + +=head1 NAME + +ZoneMinder::Database - Perl extension for blah blah blah + +=head1 SYNOPSIS + + use ZoneMinder::Server; + blah blah blah + +=head1 DESCRIPTION + +Stub documentation for ZoneMinder, created by h2xs. It looks like the +author of the extension was negligent enough to leave the stub +unedited. + +Blah blah blah. + +=head2 EXPORT + +None by default. + + + +=head1 SEE ALSO + +Mention other useful documentation such as the documentation of +related modules or operating system documentation (such as man pages +in UNIX), or any relevant external documentation such as RFCs or +standards. + +If you have a mailing list set up for your module, mention it here. + +If you have a web site set up for your module, mention it here. + +=head1 AUTHOR + +Philip Coombes, Ephilip.coombes@zoneminder.comE + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2001-2008 Philip Coombes + +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.3 or, +at your option, any later version of Perl 5 you may have available. + + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel.pm index 0344e6d94..8b746ac3d 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel.pm @@ -46,53 +46,53 @@ our $AUTOLOAD; sub new { - my $class = shift; - my $self = {}; - $self->{readable} = !undef; - $self->{writeable} = !undef; - $self->{selectable} = undef; - $self->{state} = 'closed'; - bless( $self, $class ); - return $self; + my $class = shift; + my $self = {}; + $self->{readable} = !undef; + $self->{writeable} = !undef; + $self->{selectable} = undef; + $self->{state} = 'closed'; + bless( $self, $class ); + return $self; } sub clone { - my $self = shift; - my $clone = { %$self }; - bless $clone, ref $self; + my $self = shift; + my $clone = { %$self }; + bless $clone, ref $self; } -sub open() +sub open { - my $self = shift; - my $class = ref($self) or croak( "Can't get class for non object $self" ); - croak( "Abstract base class method called for object of class $class" ); + my $self = shift; + my $class = ref($self) or croak( "Can't get class for non object $self" ); + croak( "Abstract base class method called for object of class $class" ); } -sub close() +sub close { - my $self = shift; - my $class = ref($self) or croak( "Can't get class for non object $self" ); - croak( "Abstract base class method called for object of class $class" ); + my $self = shift; + my $class = ref($self) or croak( "Can't get class for non object $self" ); + croak( "Abstract base class method called for object of class $class" ); } -sub getState() +sub getState { - my $self = shift; - return( $self->{state} ); + my $self = shift; + return( $self->{state} ); } -sub isOpen() +sub isOpen { - my $self = shift; - return( $self->{state} eq "open" ); + my $self = shift; + return( $self->{state} eq "open" ); } -sub isConnected() +sub isConnected { - my $self = shift; - return( $self->{state} eq "connected" ); + my $self = shift; + return( $self->{state} eq "connected" ); } sub DESTROY @@ -101,15 +101,15 @@ sub DESTROY sub AUTOLOAD { - my $self = shift; - my $class = ref($self) || croak( "$self not object" ); - my $name = $AUTOLOAD; - $name =~ s/.*://; - if ( !exists($self->{$name}) ) - { - croak( "Can't access $name member of object of class $class" ); - } - return( $self->{$name} ); + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( !exists($self->{$name}) ) + { + croak( "Can't access $name member of object of class $class" ); + } + return( $self->{$name} ); } 1; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/File.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/File.pm index 234dae3d7..0a4bdea8a 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/File.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/File.pm @@ -48,23 +48,26 @@ use Fcntl; sub new { - my $class = shift; - my %params = @_; - my $self = ZoneMinder::Trigger::Channel::Handle->new; - $self->{path} = $params{path}; - bless( $self, $class ); - return $self; + my $class = shift; + my %params = @_; + my $self = ZoneMinder::Trigger::Channel::Handle->new; + $self->{path} = $params{path}; + bless( $self, $class ); + return $self; } -sub open() +sub open { - my $self = shift; - local *sfh; - #sysopen( *sfh, $conn->{path}, O_NONBLOCK|O_RDONLY ) or croak( "Can't sysopen: $!" ); - #open( *sfh, "<".$conn->{path} ) or croak( "Can't open: $!" ); - open( *sfh, "+<".$self->{path} ) or croak( "Can't open: $!" ); - $self->{state} = 'open'; - $self->{handle} = *sfh; + my $self = shift; + local *sfh; + #sysopen( *sfh, $conn->{path}, O_NONBLOCK|O_RDONLY ) or croak( "Can't sysopen: $!" ); + #open( *sfh, "<".$conn->{path} ) or croak( "Can't open: $!" ); + if ( ! open( *sfh, "+<", $self->{path} ) ) { + Error( "Can't open file at $$self{path}: $!" ); + croak( "Can't open file at $$self{path}: $!" ); + } + $self->{state} = 'open'; + $self->{handle} = *sfh; } 1; @@ -73,7 +76,7 @@ __END__ =head1 NAME -ZoneMinder::Database - Perl extension for blah blah blah +ZoneMinder::Trigger::Channel::File - ZOneMinder object for a file based trigger channel =head1 SYNOPSIS diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Handle.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Handle.pm index 353dc4a32..d26924476 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Handle.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Handle.pm @@ -46,12 +46,12 @@ use POSIX; sub new { - my $class = shift; - my $port = shift; - my $self = ZoneMinder::Trigger::Channel->new(); - $self->{handle} = undef; - bless( $self, $class ); - return $self; + my $class = shift; + my $port = shift; + my $self = ZoneMinder::Trigger::Channel->new(); + $self->{handle} = undef; + bless( $self, $class ); + return $self; } sub spawns @@ -59,45 +59,51 @@ sub spawns return( undef ); } -sub close() +sub close { - my $self = shift; - close( $self->{handle} ); - $self->{state} = 'closed'; - $self->{handle} = undef; + my $self = shift; + close( $self->{handle} ); + $self->{state} = 'closed'; + $self->{handle} = undef; } -sub read() +sub read { - my $self = shift; - my $buffer; - my $nbytes = sysread( $self->{handle}, $buffer, POSIX::BUFSIZ ); - if ( !$nbytes ) - { - return( undef ); - } - Debug( "Read '$buffer' ($nbytes bytes)\n" ); - return( $buffer ); + my $self = shift; + my $buffer; + my $nbytes = sysread( $self->{handle}, $buffer, POSIX::BUFSIZ ); + if ( !$nbytes ) + { + return( undef ); + } + Debug( "Read '$buffer' ($nbytes bytes)\n" ); + return( $buffer ); } -sub write() +sub write { - my $self = shift; - my $buffer = shift; - my $nbytes = syswrite( $self->{handle}, $buffer ); - if ( !defined( $nbytes) || $nbytes < length($buffer) ) - { - Error( "Unable to write buffer '".$buffer.", expected ".length($buffer)." bytes, sent ".($nbytes?$nbytes:'undefined').": $!\n" ); - return( undef ); - } - Debug( "Wrote '$buffer' ($nbytes bytes)\n" ); - return( !undef ); + my $self = shift; + my $buffer = shift; + my $nbytes = syswrite( $self->{handle}, $buffer ); + if ( !defined( $nbytes) || $nbytes < length($buffer) ) + { + Error( "Unable to write buffer '".$buffer + .", expected " + .length($buffer) + ." bytes, sent " + .($nbytes?$nbytes:'undefined') + .": $!\n" + ); + return( undef ); + } + Debug( "Wrote '$buffer' ($nbytes bytes)\n" ); + return( !undef ); } -sub fileno() +sub fileno { - my $self = shift; - return( defined($self->{handle})?fileno($self->{handle}):-1 ); + my $self = shift; + return( defined($self->{handle})?fileno($self->{handle}):-1 ); } 1; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Inet.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Inet.pm index 68df0980f..0f977f3da 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Inet.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Inet.pm @@ -48,44 +48,54 @@ use Socket; sub new { - my $class = shift; - my %params = @_; - my $self = ZoneMinder::Trigger::Channel::Spawning->new(); - $self->{selectable} = !undef; - $self->{port} = $params{port}; - bless( $self, $class ); - return $self; + my $class = shift; + my %params = @_; + my $self = ZoneMinder::Trigger::Channel::Spawning->new(); + $self->{selectable} = !undef; + $self->{port} = $params{port}; + bless( $self, $class ); + return $self; } -sub open() +sub open { - my $self = shift; - local *sfh; - my $saddr = sockaddr_in( $self->{port}, INADDR_ANY ); - socket( *sfh, PF_INET, SOCK_STREAM, getprotobyname('tcp') ) or croak( "Can't open socket: $!" ); - setsockopt( *sfh, SOL_SOCKET, SO_REUSEADDR, 1 ); - bind( *sfh, $saddr ) or croak( "Can't bind: $!" ); - listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" ); - $self->{state} = 'open'; - $self->{handle} = *sfh; + my $self = shift; + local *sfh; + if ( ! socket( *sfh, PF_INET, SOCK_STREAM, getprotobyname('tcp') ) ) { + Error( "Can't open socket: $!" ); + croak( "Can't open socket: $!" ); + } + setsockopt( *sfh, SOL_SOCKET, SO_REUSEADDR, 1 ); + + my $saddr = sockaddr_in( $self->{port}, INADDR_ANY ); + if ( ! bind( *sfh, $saddr ) ) { + Error( "Can't bind to port $$self{port}: $!" ); + croak( "Can't bind to port $$self{port}: $!" ); + } + if ( ! listen( *sfh, SOMAXCONN ) ) { + Error( "Can't listen: $!" ); + croak( "Can't listen: $!" ); + } + $self->{state} = 'open'; + $self->{handle} = *sfh; } -sub _spawn( $ ) +sub _spawn { - my $self = shift; - my $new_handle = shift; - my $clone = $self->clone(); - $clone->{handle} = $new_handle; - $clone->{state} = 'connected'; - return( $clone ); + my $self = shift; + my $new_handle = shift; + my $clone = $self->clone(); + $clone->{handle} = $new_handle; + $clone->{state} = 'connected'; + return( $clone ); } -sub accept() +sub accept { - my $self = shift; - local *cfh; - my $paddr = accept( *cfh, $self->{handle} ); - return( $self->_spawn( *cfh ) ); + my $self = shift; + local *cfh; + my $paddr = accept( *cfh, $self->{handle} ); + return( $self->_spawn( *cfh ) ); } 1; @@ -94,7 +104,7 @@ __END__ =head1 NAME -ZoneMinder::Database - Perl extension for blah blah blah +ZoneMinder::Trigger::Channel::Inet =head1 SYNOPSIS diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm index 7e4ad4730..ddfc53436 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Serial.pm @@ -46,64 +46,76 @@ use Device::SerialPort; sub new { - my $class = shift; - my %params = @_; - my $self = ZoneMinder::Trigger::Channel->new; - $self->{path} = $params{path}; - bless( $self, $class ); - return $self; + my $class = shift; + my %params = @_; + my $self = ZoneMinder::Trigger::Channel->new; + $self->{path} = $params{path}; + bless( $self, $class ); + return $self; } -sub open() +sub open { - my $self = shift; - my $device = new Device::SerialPort( $self->{path} ); - $device->baudrate(9600); - $device->databits(8); - $device->parity('none'); - $device->stopbits(1); - $device->handshake('none'); + my $self = shift; + my $device = new Device::SerialPort( $self->{path} ); + if ( ! $device ) + { + Error( "Unable to open $$self{path}: $!" ); + $self->{state} = 'closed'; + return; + } + $device->baudrate(9600); + $device->databits(8); + $device->parity('none'); + $device->stopbits(1); + $device->handshake('none'); - $device->read_const_time(50); - $device->read_char_time(10); + $device->read_const_time(50); + $device->read_char_time(10); - $self->{device} = $device; - $self->{state} = 'open'; - $self->{state} = 'connected'; + $self->{device} = $device; + $self->{state} = 'open'; + $self->{state} = 'connected'; } -sub close() +sub close { - my $self = shift; - $self->{device}->close(); - $self->{state} = 'closed'; + my $self = shift; + $self->{device}->close(); + $self->{state} = 'closed'; } -sub read() +sub read { - my $self = shift; - my $buffer = $self->{device}->lookfor(); - if ( !$buffer || !length($buffer) ) - { - return( undef ); - } - Debug( "Read '$buffer' (".length($buffer)." bytes)\n" ); - return( $buffer ); + my $self = shift; + my $buffer = $self->{device}->lookfor(); + if ( !$buffer || !length($buffer) ) + { + return( undef ); + } + Debug( "Read '$buffer' (".length($buffer)." bytes)\n" ); + return( $buffer ); } -sub write() +sub write { - my $self = shift; - my $buffer = shift; - my $nbytes = $self->{device}->write( $buffer ); - $self->{device}->write_drain(); - if ( !defined( $nbytes) || $nbytes < length($buffer) ) - { - Error( "Unable to write buffer '".$buffer.", expected ".length($buffer)." bytes, sent ".$nbytes.": $!\n" ); - return( undef ); - } - Debug( "Wrote '$buffer' ($nbytes bytes)\n" ); - return( !undef ); + my $self = shift; + my $buffer = shift; + my $nbytes = $self->{device}->write( $buffer ); + $self->{device}->write_drain(); + if ( !defined( $nbytes) || $nbytes < length($buffer) ) + { + Error( "Unable to write buffer '".$buffer + .", expected " + .length($buffer) + ." bytes, sent " + .$nbytes + .": $!\n" + ); + return( undef ); + } + Debug( "Wrote '$buffer' ($nbytes bytes)\n" ); + return( !undef ); } 1; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Spawning.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Spawning.pm index ff726ac38..aeaca182e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Spawning.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Spawning.pm @@ -45,12 +45,12 @@ use ZoneMinder::Logger qw(:all); sub new { - my $class = shift; - my $port = shift; - my $self = ZoneMinder::Trigger::Channel::Handle->new(); - $self->{spawns} = !undef; - bless( $self, $class ); - return $self; + my $class = shift; + my $port = shift; + my $self = ZoneMinder::Trigger::Channel::Handle->new(); + $self->{spawns} = !undef; + bless( $self, $class ); + return $self; } sub spawns diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Unix.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Unix.pm index a671ccd66..ea74c957e 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Unix.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Channel/Unix.pm @@ -48,28 +48,37 @@ use Socket; sub new { - my $class = shift; - my %params = @_; - my $self = ZoneMinder::Trigger::Channel->new; - $self->{selectable} = !undef; - $self->{path} = $params{path}; - bless( $self, $class ); - return $self; + my $class = shift; + my %params = @_; + my $self = ZoneMinder::Trigger::Channel->new; + $self->{selectable} = !undef; + $self->{path} = $params{path}; + bless( $self, $class ); + return $self; } -sub open() +sub open { - my $self = shift; - local *sfh; - unlink( $self->{path} ); - my $saddr = sockaddr_un( $self->{path} ); - socket( *sfh, PF_UNIX, SOCK_STREAM, 0 ) or croak( "Can't open socket: $!" ); - bind( *sfh, $saddr ) or croak( "Can't bind: $!" ); - listen( *sfh, SOMAXCONN ) or croak( "Can't listen: $!" ); - $self->{handle} = *sfh; + my $self = shift; + local *sfh; + unlink( $self->{path} ); + my $saddr = sockaddr_un( $self->{path} ); + if ( ! socket( *sfh, PF_UNIX, SOCK_STREAM, 0 ) ) { + Error( "Can't open unix socket at $$self{path}: $!" ); + croak( "Can't open unix socket at $$self{path}: $!" ); + } + if ( ! bind( *sfh, $saddr ) ) { + Error( "Can't bind unix socket at $$self{path}: $!" ); + croak( "Can't bind unix socket at $$self{path}: $!" ); + } + if ( ! listen( *sfh, SOMAXCONN ) ) { + Error( "Can't listen: $!" ); + croak( "Can't listen: $!" ); + } + $self->{handle} = *sfh; } -sub _spawn( $ ) +sub _spawn { my $self = shift; my $new_handle = shift; @@ -79,7 +88,7 @@ sub _spawn( $ ) return( $clone ); } -sub accept() +sub accept { my $self = shift; local *cfh; @@ -93,12 +102,11 @@ __END__ =head1 NAME -ZoneMinder::Database - Perl extension for blah blah blah +ZoneMinder::Trigger::Channel::Unix - Object for Unix socket channel =head1 SYNOPSIS - use ZoneMinder::Database; - blah blah blah +See zmtrigger.pl =head1 DESCRIPTION diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection.pm index 41b332d46..12cf3136d 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection.pm @@ -46,115 +46,121 @@ our $AUTOLOAD; sub new { - my $class = shift; - my %params = @_; - my $self = {}; - $self->{name} = $params{name}; - $self->{channel} = $params{channel}; - $self->{input} = $params{mode} =~ /r/i; - $self->{output} = $params{mode} =~ /w/i; - bless( $self, $class ); - return $self; + my $class = shift; + my %params = @_; + my $self = {}; + $self->{name} = $params{name}; + $self->{channel} = $params{channel}; + $self->{input} = $params{mode} =~ /r/i; + $self->{output} = $params{mode} =~ /w/i; + bless( $self, $class ); + return $self; } sub clone { - my $self = shift; - my $clone = { %$self }; - bless $clone, ref $self; - return( $clone ); + my $self = shift; + my $clone = { %$self }; + bless $clone, ref $self; + return( $clone ); } sub spawns { - my $self = shift; + my $self = shift; return( $self->{channel}->spawns() ); } -sub _spawn( $ ) +sub _spawn { - my $self = shift; - my $new_channel = shift; - my $clone = $self->clone(); - $clone->{channel} = $new_channel; - return( $clone ); + my $self = shift; + my $new_channel = shift; + my $clone = $self->clone(); + $clone->{channel} = $new_channel; + return( $clone ); } -sub accept() +sub accept { - my $self = shift; - my $new_channel = $self->{channel}->accept(); - return( $self->_spawn( $new_channel ) ); + my $self = shift; + my $new_channel = $self->{channel}->accept(); + return( $self->_spawn( $new_channel ) ); } -sub open() +sub open { - my $self = shift; - return( $self->{channel}->open() ); + my $self = shift; + return( $self->{channel}->open() ); } -sub close() +sub close { - my $self = shift; - return( $self->{channel}->close() ); + my $self = shift; + return( $self->{channel}->close() ); } -sub fileno() +sub fileno { - my $self = shift; - return( $self->{channel}->fileno() ); + my $self = shift; + return( $self->{channel}->fileno() ); } -sub isOpen() +sub isOpen { - my $self = shift; - return( $self->{channel}->isOpen() ); + my $self = shift; + return( $self->{channel}->isOpen() ); } -sub isConnected() +sub isConnected { - my $self = shift; - return( $self->{channel}->isConnected() ); + my $self = shift; + return( $self->{channel}->isConnected() ); } -sub canRead() +sub canRead { - my $self = shift; - return( $self->{input} && $self->isConnected() ); + my $self = shift; + return( $self->{input} && $self->isConnected() ); } -sub canWrite() +sub canWrite { - my $self = shift; - return( $self->{output} && $self->isConnected() ); + my $self = shift; + return( $self->{output} && $self->isConnected() ); } sub getMessages { - my $self = shift; - my $buffer = $self->{channel}->read(); + my $self = shift; + my $buffer = $self->{channel}->read(); - return( undef ) if ( !defined($buffer) ); + return( undef ) if ( !defined($buffer) ); - my @messages = split( /\r?\n/, $buffer ); - return( \@messages ); + my @messages = split( /\r?\n/, $buffer ); + return( \@messages ); } sub putMessages { - my $self = shift; - my $messages = shift; + my $self = shift; + my $messages = shift; - if ( @$messages ) - { - my $buffer = join( "\n", @$messages ); - $buffer .= "\n"; - if ( !$self->{channel}->write( $buffer ) ) - { - Error( "Unable to write buffer '".$buffer." to connection ".$self->{name}." (".$self->fileno().")\n" ); - } - } - return( undef ); + if ( @$messages ) + { + my $buffer = join( "\n", @$messages ); + $buffer .= "\n"; + if ( !$self->{channel}->write( $buffer ) ) + { + Error( "Unable to write buffer '".$buffer + ." to connection " + .$self->{name} + ." (" + .$self->fileno() + .")\n" + ); + } + } + return( undef ); } sub timedActions @@ -167,22 +173,22 @@ sub DESTROY sub AUTOLOAD { - my $self = shift; - my $class = ref($self) || croak( "$self not object" ); - my $name = $AUTOLOAD; - $name =~ s/.*://; - if ( exists($self->{$name}) ) - { - return( $self->{$name} ); - } - elsif ( defined($self->{channel}) ) - { - if ( exists($self->{channel}->{$name}) ) - { - return( $self->{channel}->{$name} ); - } - } - croak( "Can't access $name member of object of class $class" ); + my $self = shift; + my $class = ref($self) || croak( "$self not object" ); + my $name = $AUTOLOAD; + $name =~ s/.*://; + if ( exists($self->{$name}) ) + { + return( $self->{$name} ); + } + elsif ( defined($self->{channel}) ) + { + if ( exists($self->{channel}->{$name}) ) + { + return( $self->{channel}->{$name} ); + } + } + croak( "Can't access $name member of object of class $class" ); } 1; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection/Example.pm b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection/Example.pm index 3bed2b351..6f3a86575 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection/Example.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Trigger/Connection/Example.pm @@ -44,40 +44,40 @@ use ZoneMinder::Logger qw(:all); sub new { - my $class = shift; - my $path = shift; - my $self = ZoneMinder::Trigger::Connection->new( @_ ); - bless( $self, $class ); - return $self; + my $class = shift; + my $path = shift; + my $self = ZoneMinder::Trigger::Connection->new( @_ ); + bless( $self, $class ); + return $self; } sub getMessages { - my $self = shift; - my $buffer = $self->{channel}->read(); + my $self = shift; + my $buffer = $self->{channel}->read(); - return( undef ) if ( !defined($buffer) ); + return( undef ) if ( !defined($buffer) ); - Debug( "Handling buffer '$buffer'\n" ); - my @messages = grep { s/-/|/g; 1; } split( /\r?\n/, $buffer ); - return( \@messages ); + Debug( "Handling buffer '$buffer'\n" ); + my @messages = grep { s/-/|/g; 1; } split( /\r?\n/, $buffer ); + return( \@messages ); } sub putMessages { - my $self = shift; - my $messages = shift; + my $self = shift; + my $messages = shift; - if ( @$messages ) - { - my $buffer = join( "\n", grep{ s/\|/-/; 1; } @$messages ); - $buffer .= "\n"; - if ( !$self->{channel}->write( $buffer ) ) - { - Error( "Unable to write buffer '".$buffer." to connection ".$self->{name}." (".$self->fileno().")\n" ); - } - } - return( undef ); + if ( @$messages ) + { + my $buffer = join( "\n", grep{ s/\|/-/; 1; } @$messages ); + $buffer .= "\n"; + if ( !$self->{channel}->write( $buffer ) ) + { + Error( "Unable to write buffer '".$buffer." to connection ".$self->{name}." (".$self->fileno().")\n" ); + } + } + return( undef ); } 1; diff --git a/scripts/zm.in b/scripts/zm.in index a46a89280..b600cd983 100755 --- a/scripts/zm.in +++ b/scripts/zm.in @@ -3,6 +3,9 @@ # chkconfig: 2345 99 00 # processname: zmpkg.pl +# This script is intended for use with legacy SysV init environments ONLY +# For systemd environments, use the ZoneMinder systemd unit file instead + # Source function library. . /etc/rc.d/init.d/functions @@ -13,12 +16,12 @@ LOCKFILE=/var/lock/subsys/zm loadconf() { - if [ -f $ZM_CONFIG ]; then - . $ZM_CONFIG - else - echo "ERROR: $ZM_CONFIG not found." - return 1 - fi + if [ -f $ZM_CONFIG ]; then + . $ZM_CONFIG + else + echo "ERROR: $ZM_CONFIG not found." + return 1 + fi } loadconf @@ -27,95 +30,95 @@ command="$ZM_PATH_BIN/zmpkg.pl" start() { # Commenting out as it is not needed. Leaving as a placeholder for future use. -# zmupdate || return $? - loadconf || return $? - #Make sure the directory for our PID folder exists or create one. - [ ! -d $pidfile ] \ - && mkdir -m 774 $pidfile \ - && chown $ZM_WEB_USER:$ZM_WEB_GROUP $pidfile - #Make sure the folder for the socks file exists or create one - GetPath="select Value from Config where Name='ZM_PATH_SOCKS'" +# zmupdate || return $? + loadconf || return $? + #Make sure the directory for our PID folder exists or create one. + [ ! -d $pidfile ] \ + && mkdir -m 774 $pidfile \ + && chown $ZM_WEB_USER:$ZM_WEB_GROUP $pidfile + #Make sure the folder for the socks file exists or create one + GetPath="select Value from Config where Name='ZM_PATH_SOCKS'" dbHost=`echo $ZM_DB_HOST | cut -d: -f1` dbPort=`echo $ZM_DB_HOST | cut -d: -s -f2` if [ "$dbPort" = "" ] then - ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$ZM_DB_HOST -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'` + ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$ZM_DB_HOST -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'` else - ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$dbHost -P$dbPort -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'` + ZM_PATH_SOCK=`echo $GetPath | mysql -B -h$dbHost -P$dbPort -u$ZM_DB_USER -p$ZM_DB_PASS $ZM_DB_NAME | grep -v '^Value'` fi - [ ! -d $ZM_PATH_SOCK ] \ - && mkdir -m 774 $ZM_PATH_SOCK \ - && chown $ZM_WEB_USER:$ZM_WEB_GROUP $ZM_PATH_SOCK - echo -n $"Starting $prog: " - $command start - RETVAL=$? - [ $RETVAL = 0 ] && success || failure - echo - [ $RETVAL = 0 ] && touch $LOCKFILE - return $RETVAL + [ ! -d $ZM_PATH_SOCK ] \ + && mkdir -m 774 $ZM_PATH_SOCK \ + && chown $ZM_WEB_USER:$ZM_WEB_GROUP $ZM_PATH_SOCK + echo -n $"Starting $prog: " + $command start + RETVAL=$? + [ $RETVAL = 0 ] && success || failure + echo + [ $RETVAL = 0 ] && touch $LOCKFILE + return $RETVAL } stop() { - loadconf - echo -n $"Stopping $prog: " - $command stop - RETVAL=$? - [ $RETVAL = 0 ] && success || failure - echo - [ $RETVAL = 0 ] && rm -f $LOCKFILE + loadconf + echo -n $"Stopping $prog: " + $command stop + RETVAL=$? + [ $RETVAL = 0 ] && success || failure + echo + [ $RETVAL = 0 ] && rm -f $LOCKFILE } zmstatus() { - loadconf - result=`$command status` - if [ "$result" = "running" ]; then - echo "ZoneMinder is running" - $ZM_PATH_BIN/zmu -l - RETVAL=0 - else - echo "ZoneMinder is stopped" - RETVAL=1 - fi + loadconf + result=`$command status` + if [ "$result" = "running" ]; then + echo "ZoneMinder is running" + $ZM_PATH_BIN/zmu -l + RETVAL=0 + else + echo "ZoneMinder is stopped" + RETVAL=1 + fi } zmupdate() { - if [ -x $ZM_PATH_BIN/zmupdate.pl ]; then - $ZM_PATH_BIN/zmupdate.pl -f - fi + if [ -x $ZM_PATH_BIN/zmupdate.pl ]; then + $ZM_PATH_BIN/zmupdate.pl -f + fi } case "$1" in - 'start') - start - ;; - 'stop') - stop - ;; - 'restart') - stop - start - ;; - 'condrestart') - loadconf - result=`$ZM_PATH_BIN/zmdc.pl check` - if [ "$result" = "running" ]; then - $ZM_PATH_BIN/zmdc.pl shutdown > /dev/null - rm -f $LOCKFILE - start - fi - ;; - 'status') - status httpd - status mysqld - zmstatus - ;; - *) - echo "Usage: $0 { start | stop | restart | condrestart | status }" - RETVAL=1 - ;; + 'start') + start + ;; + 'stop') + stop + ;; + 'restart') + stop + start + ;; + 'condrestart') + loadconf + result=`$ZM_PATH_BIN/zmdc.pl check` + if [ "$result" = "running" ]; then + $ZM_PATH_BIN/zmdc.pl shutdown > /dev/null + rm -f $LOCKFILE + start + fi + ;; + 'status') + status httpd + status mysqld + zmstatus + ;; + *) + echo "Usage: $0 { start | stop | restart | condrestart | status }" + RETVAL=1 + ;; esac exit $RETVAL diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index b3dab1004..7f2df08b9 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -20,15 +20,33 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script checks for consistency between the event filesystem and -# the database. If events are found in one and not the other they are -# deleted (optionally). Additionally any monitor event directories that -# do not correspond to a database monitor are similarly disposed of. -# However monitors in the database that don't have a directory are left -# alone as this is valid if they are newly created and have no events -# yet. -# + +=head1 NAME + +zmaudit.pl - ZoneMinder event file system and database consistency checker + +=head1 SYNOPSIS + + zmaudit.pl [-r,-report|-i,-interactive] + +=head1 DESCRIPTION + +This script checks for consistency between the event filesystem and +the database. If events are found in one and not the other they are +deleted (optionally). Additionally any monitor event directories that +do not correspond to a database monitor are similarly disposed of. +However monitors in the database that don't have a directory are left +alone as this is valid if they are newly created and have no events +yet. + +=head1 OPTIONS + + -r, --report - Just report don't actually do anything + -i, --interactive - Ask before applying any changes + -c, --continuous - Run continuously + -v, --version - Print the installed version of ZoneMinder + +=cut use strict; use bytes; @@ -38,7 +56,6 @@ use bytes; # # ========================================================================== -use constant MIN_AGE => 300; # Minimum age when we will delete anything use constant MAX_AGED_DIRS => 10; # Number of event dirs to check age on use constant RECOVER_TAG => "(r)"; # Tag to append to event name when recovered use constant RECOVER_TEXT => "Recovered."; # Text to append to event notes when recovered @@ -56,48 +73,43 @@ use POSIX; use File::Find; use Time::HiRes qw/gettimeofday/; use Getopt::Long; +use autouse 'Pod::Usage'=>qw(pod2usage); use constant IMAGE_PATH => $Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_IMAGES}; -use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)?$Config{ZM_DIR_EVENTS}:($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}); +use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|) + ? $Config{ZM_DIR_EVENTS} + : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}) +; $| = 1; -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $report = 0; my $interactive = 0; my $continuous = 0; - -sub usage -{ - print( " -Usage: zmaudit.pl [-r,-report|-i,-interactive] -Parameters are :- --r, --report - Just report don't actually do anything --i, --interactive - Ask before applying any changes --c, --continuous - Run continuously -"); - exit( -1 ); -} - -sub aud_print( $ ); -sub confirm( ;$$ ); -sub deleteSwapImage(); +my $version; logInit(); logSetSignal(); -if ( !GetOptions( 'report'=>\$report, 'interactive'=>\$interactive, 'continuous'=>\$continuous ) ) -{ - usage(); -} +GetOptions( + 'report' =>\$report, + 'interactive' =>\$interactive, + 'continuous' =>\$continuous, + 'version' =>\$version +) or pod2usage(-exitstatus => -1); +if ( $version ) { + print( ZoneMinder::Base::ZM_VERSION . "\n"); + exit(0); +} if ( ($report + $interactive + $continuous) > 1 ) { - print( STDERR "Error, only option may be specified\n" ); - usage(); + print( STDERR "Error, only one option may be specified\n" ); + pod2usage(-exitstatus => -1); } my $dbh = zmDbConnect(); @@ -107,60 +119,80 @@ chdir( EVENT_PATH ); my $max_image_age = 6/24; # 6 hours my $max_swap_age = 24/24; # 24 hours my $image_path = IMAGE_PATH; -my $swap_image_path = $Config{ZM_PATH_SWAP}; my $loop = 1; my $cleaned = 0; MAIN: while( $loop ) { - while ( ! ( $dbh and $dbh->ping() ) ) { - $dbh = zmDbConnect(); + while ( ! ( $dbh and $dbh->ping() ) ) { + $dbh = zmDbConnect(); - last if $dbh; if ( $continuous ) { - # if we are running continuously, then just skip to the next interval, otherwise we are a one off run, so wait a second and retry until someone kills us. + Error("Unable to connect to database"); + # if we are running continuously, then just skip to the next + # interval, otherwise we are a one off run, so wait a second and + # retry until someone kills us. sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} ); } else { - sleep 1; + Fatal("Unable to connect to database"); } # end if } # end while can't connect to the db - my $db_monitors; + if ( $continuous ) { + # if we are running continuously, then just skip to the next + # interval, otherwise we are a one off run, so wait a second and + # retry until someone kills us. + sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} ); + } else { + sleep 1; + } # end if + + if ( ! exists $Config{ZM_AUDIT_MIN_AGE} ) { + Fatal("ZM_AUDIT_MIN_AGE is not set in config."); + } + + my $db_monitors; my $monitorSelectSql = "select Id from Monitors order by Id"; - my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql ) or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() ); - my $eventSelectSql = "select Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age from Events where MonitorId = ? order by Id"; - my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql ) or Fatal( "Can't prepare '$eventSelectSql': ".$dbh->errstr() ); + my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql ) + or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() ); + my $eventSelectSql = "SELECT Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age + FROM Events WHERE MonitorId = ? ORDER BY Id"; + my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql ) + or Fatal( "Can't prepare '$eventSelectSql': ".$dbh->errstr() ); $cleaned = 0; - my $res = $monitorSelectSth->execute() or Fatal( "Can't execute: ".$monitorSelectSth->errstr() ); + my $res = $monitorSelectSth->execute() + or Fatal( "Can't execute: ".$monitorSelectSth->errstr() ); while( my $monitor = $monitorSelectSth->fetchrow_hashref() ) { Debug( "Found database monitor '$monitor->{Id}'" ); my $db_events = $db_monitors->{$monitor->{Id}} = {}; - my $res = $eventSelectSth->execute( $monitor->{Id} ) or Fatal( "Can't execute: ".$eventSelectSth->errstr() ); + my $res = $eventSelectSth->execute( $monitor->{Id} ) + or Fatal( "Can't execute: ".$eventSelectSth->errstr() ); while ( my $event = $eventSelectSth->fetchrow_hashref() ) { $db_events->{$event->{Id}} = $event->{Age}; } Debug( "Got ".int(keys(%$db_events))." events\n" ); - $eventSelectSth->finish(); } - $monitorSelectSth->finish(); my $fs_monitors; - foreach my $monitor ( <[0-9]*> ) + foreach my $monitor ( glob("[0-9]*") ) { + # Thie glob above gives all files starting with a digit. So a monitor with a name starting with a digit will be in this list. + next if $monitor =~ /\D/; Debug( "Found filesystem monitor '$monitor'" ); my $fs_events = $fs_monitors->{$monitor} = {}; ( my $monitor_dir ) = ( $monitor =~ /^(.*)$/ ); # De-taint if ( $Config{ZM_USE_DEEP_STORAGE} ) { - foreach my $day_dir ( <$monitor_dir/*/*/*> ) + foreach my $day_dir ( glob("$monitor_dir/*/*/*") ) { Debug( "Checking $day_dir" ); ( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint chdir( $day_dir ); - opendir( DIR, "." ) or Fatal( "Can't open directory '$day_dir': $!" ); + opendir( DIR, "." ) + or Fatal( "Can't open directory '$day_dir': $!" ); my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR ); closedir( DIR ); my $count = 0; @@ -227,7 +259,7 @@ MAIN: while( $loop ) { { while ( my ( $fs_event, $age ) = each(%$fs_events ) ) { - if ( !defined($db_events->{$fs_event}) && ($age < 0 || ($age > MIN_AGE)) ) + if ( !defined($db_events->{$fs_event}) && ($age < 0 || ($age > $Config{ZM_AUDIT_MIN_AGE})) ) { aud_print( "Filesystem event '$fs_monitor/$fs_event' does not exist in database" ); if ( confirm() ) @@ -252,7 +284,7 @@ MAIN: while( $loop ) { } my $monitor_links; - foreach my $link ( <*> ) + foreach my $link ( glob("*") ) { next if ( !-l $link ); next if ( -e $link ); @@ -270,13 +302,17 @@ MAIN: while( $loop ) { $cleaned = 0; my $deleteMonitorSql = "delete low_priority from Monitors where Id = ?"; - my $deleteMonitorSth = $dbh->prepare_cached( $deleteMonitorSql ) or Fatal( "Can't prepare '$deleteMonitorSql': ".$dbh->errstr() ); + my $deleteMonitorSth = $dbh->prepare_cached( $deleteMonitorSql ) + or Fatal( "Can't prepare '$deleteMonitorSql': ".$dbh->errstr() ); my $deleteEventSql = "delete low_priority from Events where Id = ?"; - my $deleteEventSth = $dbh->prepare_cached( $deleteEventSql ) or Fatal( "Can't prepare '$deleteEventSql': ".$dbh->errstr() ); + my $deleteEventSth = $dbh->prepare_cached( $deleteEventSql ) + or Fatal( "Can't prepare '$deleteEventSql': ".$dbh->errstr() ); my $deleteFramesSql = "delete low_priority from Frames where EventId = ?"; - my $deleteFramesSth = $dbh->prepare_cached( $deleteFramesSql ) or Fatal( "Can't prepare '$deleteFramesSql': ".$dbh->errstr() ); + my $deleteFramesSth = $dbh->prepare_cached( $deleteFramesSql ) + or Fatal( "Can't prepare '$deleteFramesSql': ".$dbh->errstr() ); my $deleteStatsSql = "delete low_priority from Stats where EventId = ?"; - my $deleteStatsSth = $dbh->prepare_cached( $deleteStatsSql ) or Fatal( "Can't prepare '$deleteStatsSql': ".$dbh->errstr() ); + my $deleteStatsSth = $dbh->prepare_cached( $deleteStatsSql ) + or Fatal( "Can't prepare '$deleteStatsSql': ".$dbh->errstr() ); while ( my ( $db_monitor, $db_events ) = each(%$db_monitors) ) { if ( my $fs_events = $fs_monitors->{$db_monitor} ) @@ -285,27 +321,33 @@ MAIN: while( $loop ) { { while ( my ( $db_event, $age ) = each(%$db_events ) ) { - if ( !defined($fs_events->{$db_event}) && ($age > MIN_AGE) ) - { - aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem" ); - if ( confirm() ) - { - my $res = $deleteEventSth->execute( $db_event ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); - $res = $deleteFramesSth->execute( $db_event ) or Fatal( "Can't execute: ".$deleteFramesSth->errstr() ); - $res = $deleteStatsSth->execute( $db_event ) or Fatal( "Can't execute: ".$deleteStatsSth->errstr() ); - $cleaned = 1; - } + if ( !defined($fs_events->{$db_event}) ) { + if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { + aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem" ); + if ( confirm() ) { + my $res = $deleteEventSth->execute( $db_event ) + or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); + $res = $deleteFramesSth->execute( $db_event ) + or Fatal( "Can't execute: ".$deleteFramesSth->errstr() ); + $res = $deleteStatsSth->execute( $db_event ) + or Fatal( "Can't execute: ".$deleteStatsSth->errstr() ); + $cleaned = 1; + } + } else { + aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem but too young to delete." ); + } } } } } else { - #aud_print( "Database monitor '$db_monitor' does not exist in filesystem" ); + aud_print( "Database monitor '$db_monitor' does not exist in filesystem" ); #if ( confirm() ) #{ # We don't actually do this in case it's new - #my $res = $deleteMonitorSth->execute( $db_monitor ) or Fatal( "Can't execute: ".$deleteMonitorSth->errstr() ); + #my $res = $deleteMonitorSth->execute( $db_monitor ) + # or Fatal( "Can't execute: ".$deleteMonitorSth->errstr() ); #$cleaned = 1; #} } @@ -314,87 +356,147 @@ MAIN: while( $loop ) { # Remove orphaned events (with no monitor) $cleaned = 0; - my $selectOrphanedEventsSql = "select Events.Id, Events.Name from Events left join Monitors on (Events.MonitorId = Monitors.Id) where isnull(Monitors.Id)"; - my $selectOrphanedEventsSth = $dbh->prepare_cached( $selectOrphanedEventsSql ) or Fatal( "Can't prepare '$selectOrphanedEventsSql': ".$dbh->errstr() ); - $res = $selectOrphanedEventsSth->execute() or Fatal( "Can't execute: ".$selectOrphanedEventsSth->errstr() ); + my $selectOrphanedEventsSql = "SELECT Events.Id, Events.Name + FROM Events LEFT JOIN Monitors ON (Events.MonitorId = Monitors.Id) + WHERE isnull(Monitors.Id)"; + my $selectOrphanedEventsSth = $dbh->prepare_cached( $selectOrphanedEventsSql ) + or Fatal( "Can't prepare '$selectOrphanedEventsSql': ".$dbh->errstr() ); + $res = $selectOrphanedEventsSth->execute() + or Fatal( "Can't execute: ".$selectOrphanedEventsSth->errstr() ); while( my $event = $selectOrphanedEventsSth->fetchrow_hashref() ) { aud_print( "Found orphaned event with no monitor '$event->{Id}'" ); if ( confirm() ) { - $res = $deleteEventSth->execute( $event->{Id} ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); + $res = $deleteEventSth->execute( $event->{Id} ) + or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); $cleaned = 1; } } - $selectOrphanedEventsSth->finish(); redo MAIN if ( $cleaned ); # Remove empty events (with no frames) $cleaned = 0; - my $selectEmptyEventsSql = "select * from Events as E left join Frames as F on (E.Id = F.EventId) where isnull(F.EventId) and now() - interval ".MIN_AGE." second > E.StartTime"; - my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql ) or Fatal( "Can't prepare '$selectEmptyEventsSql': ".$dbh->errstr() ); - $res = $selectEmptyEventsSth->execute() or Fatal( "Can't execute: ".$selectEmptyEventsSth->errstr() ); + my $selectEmptyEventsSql = "SELECT E.Id AS Id, E.StartTime, F.EventId FROM Events as E LEFT JOIN Frames as F ON (E.Id = F.EventId) + WHERE isnull(F.EventId) AND now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second > E.StartTime"; + my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql ) + or Fatal( "Can't prepare '$selectEmptyEventsSql': ".$dbh->errstr() ); + $res = $selectEmptyEventsSth->execute() + or Fatal( "Can't execute: ".$selectEmptyEventsSth->errstr() ); while( my $event = $selectEmptyEventsSth->fetchrow_hashref() ) { aud_print( "Found empty event with no frame records '$event->{Id}'" ); if ( confirm() ) { - $res = $deleteEventSth->execute( $event->{Id} ) or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); + $res = $deleteEventSth->execute( $event->{Id} ) + or Fatal( "Can't execute: ".$deleteEventSth->errstr() ); $cleaned = 1; } } - $selectEmptyEventsSth->finish(); redo MAIN if ( $cleaned ); # Remove orphaned frame records $cleaned = 0; - my $selectOrphanedFramesSql = "select distinct EventId from Frames where EventId not in (select Id from Events)"; - my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql ) or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() ); - $res = $selectOrphanedFramesSth->execute() or Fatal( "Can't execute: ".$selectOrphanedFramesSth->errstr() ); + my $selectOrphanedFramesSql = "SELECT DISTINCT EventId FROM Frames + WHERE EventId NOT IN (SELECT Id FROM Events)"; + my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql ) + or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() ); + $res = $selectOrphanedFramesSth->execute() + or Fatal( "Can't execute: ".$selectOrphanedFramesSth->errstr() ); while( my $frame = $selectOrphanedFramesSth->fetchrow_hashref() ) { aud_print( "Found orphaned frame records for event '$frame->{EventId}'" ); if ( confirm() ) { - $res = $deleteFramesSth->execute( $frame->{EventId} ) or Fatal( "Can't execute: ".$deleteFramesSth->errstr() ); + $res = $deleteFramesSth->execute( $frame->{EventId} ) + or Fatal( "Can't execute: ".$deleteFramesSth->errstr() ); $cleaned = 1; } } - $selectOrphanedFramesSth->finish(); redo MAIN if ( $cleaned ); # Remove orphaned stats records $cleaned = 0; - my $selectOrphanedStatsSql = "select distinct EventId from Stats where EventId not in (select Id from Events)"; - my $selectOrphanedStatsSth = $dbh->prepare_cached( $selectOrphanedStatsSql ) or Fatal( "Can't prepare '$selectOrphanedStatsSql': ".$dbh->errstr() ); - $res = $selectOrphanedStatsSth->execute() or Fatal( "Can't execute: ".$selectOrphanedStatsSth->errstr() ); + my $selectOrphanedStatsSql = "SELECT DISTINCT EventId FROM Stats + WHERE EventId NOT IN (SELECT Id FROM Events)"; + my $selectOrphanedStatsSth = $dbh->prepare_cached( $selectOrphanedStatsSql ) + or Fatal( "Can't prepare '$selectOrphanedStatsSql': ".$dbh->errstr() ); + $res = $selectOrphanedStatsSth->execute() + or Fatal( "Can't execute: ".$selectOrphanedStatsSth->errstr() ); while( my $stat = $selectOrphanedStatsSth->fetchrow_hashref() ) { aud_print( "Found orphaned statistic records for event '$stat->{EventId}'" ); if ( confirm() ) { - $res = $deleteStatsSth->execute( $stat->{EventId} ) or Fatal( "Can't execute: ".$deleteStatsSth->errstr() ); + $res = $deleteStatsSth->execute( $stat->{EventId} ) + or Fatal( "Can't execute: ".$deleteStatsSth->errstr() ); $cleaned = 1; } } - $selectOrphanedStatsSth->finish(); redo MAIN if ( $cleaned ); # New audit to close any events that were left open for longer than MIN_AGE seconds - my $selectUnclosedEventsSql = "select E.Id, max(F.TimeStamp) as EndTime, unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length, max(F.FrameId) as Frames, count(if(F.Score>0,1,NULL)) as AlarmFrames, sum(F.Score) as TotScore, max(F.Score) as MaxScore, M.EventPrefix as Prefix from Events as E left join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where isnull(E.Frames) or isnull(E.EndTime) group by E.Id having EndTime < (now() - interval ".MIN_AGE." second)"; - my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql ) or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() ); - my $updateUnclosedEventsSql = "update low_priority Events set Name = ?, EndTime = ?, Length = ?, Frames = ?, AlarmFrames = ?, TotScore = ?, AvgScore = ?, MaxScore = ?, Notes = concat_ws( ' ', Notes, ? ) where Id = ?"; - my $updateUnclosedEventsSth = $dbh->prepare_cached( $updateUnclosedEventsSql ) or Fatal( "Can't prepare '$updateUnclosedEventsSql': ".$dbh->errstr() ); - $res = $selectUnclosedEventsSth->execute() or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() ); + my $selectUnclosedEventsSql = + "SELECT E.Id, + max(F.TimeStamp) as EndTime, + unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length, + max(F.FrameId) as Frames, + count(if(F.Score>0,1,NULL)) as AlarmFrames, + sum(F.Score) as TotScore, + max(F.Score) as MaxScore, + M.EventPrefix as Prefix + FROM Events as E + LEFT JOIN Monitors as M on E.MonitorId = M.Id + INNER JOIN Frames as F on E.Id = F.EventId + WHERE isnull(E.Frames) or isnull(E.EndTime) + GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)" + ; + my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql ) + or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() ); + my $updateUnclosedEventsSql = + "UPDATE low_priority Events + SET Name = ?, + EndTime = ?, + Length = ?, + Frames = ?, + AlarmFrames = ?, + TotScore = ?, + AvgScore = ?, + MaxScore = ?, + Notes = concat_ws( ' ', Notes, ? ) + WHERE Id = ?" + ; + my $updateUnclosedEventsSth = $dbh->prepare_cached( $updateUnclosedEventsSql ) + or Fatal( "Can't prepare '$updateUnclosedEventsSql': ".$dbh->errstr() ); + $res = $selectUnclosedEventsSth->execute() + or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() ); while( my $event = $selectUnclosedEventsSth->fetchrow_hashref() ) { aud_print( "Found open event '$event->{Id}'" ); if ( confirm( 'close', 'closing' ) ) { - $res = $updateUnclosedEventsSth->execute( sprintf( "%s%d%s", $event->{Prefix}, $event->{Id}, RECOVER_TAG ), $event->{EndTime}, $event->{Length}, $event->{Frames}, $event->{AlarmFrames}, $event->{TotScore}, $event->{AlarmFrames}?int($event->{TotScore}/$event->{AlarmFrames}):0, $event->{MaxScore}, RECOVER_TEXT, $event->{Id} ) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() ); + $res = $updateUnclosedEventsSth->execute + ( + sprintf("%s%d%s", + $event->{Prefix}, + $event->{Id}, + RECOVER_TAG + ), + $event->{EndTime}, + $event->{Length}, + $event->{Frames}, + $event->{AlarmFrames}, + $event->{TotScore}, + $event->{AlarmFrames} + ? int($event->{TotScore} / $event->{AlarmFrames}) + : 0 + , + $event->{MaxScore}, + RECOVER_TEXT, + $event->{Id} + ) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() ); } } - $selectUnclosedEventsSth->finish(); # Now delete any old image files if ( my @old_files = grep { -M > $max_image_age } <$image_path/*.{jpg,gif,wbmp}> ) @@ -406,7 +508,7 @@ MAIN: while( $loop ) { } # Now delete any old swap files - ( my $swap_image_root ) = ( $swap_image_path =~ /^(.*)$/ ); # De-taint + ( my $swap_image_root ) = ( $Config{ZM_PATH_SWAP} =~ /^(.*)$/ ); # De-taint File::Find::find( { wanted=>\&deleteSwapImage, untaint=>1 }, $swap_image_root ); # Prune the Logs table if required @@ -415,27 +517,43 @@ MAIN: while( $loop ) { if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^\d+$/ ) { # Number of rows - my $selectLogRowCountSql = "select count(*) as Rows from Logs"; - my $selectLogRowCountSth = $dbh->prepare_cached( $selectLogRowCountSql ) or Fatal( "Can't prepare '$selectLogRowCountSql': ".$dbh->errstr() ); - $res = $selectLogRowCountSth->execute() or Fatal( "Can't execute: ".$selectLogRowCountSth->errstr() ); + my $selectLogRowCountSql = "SELECT count(*) as Rows from Logs"; + my $selectLogRowCountSth = $dbh->prepare_cached( $selectLogRowCountSql ) + or Fatal( "Can't prepare '$selectLogRowCountSql': ".$dbh->errstr() ); + $res = $selectLogRowCountSth->execute() + or Fatal( "Can't execute: ".$selectLogRowCountSth->errstr() ); my $row = $selectLogRowCountSth->fetchrow_hashref(); my $logRows = $row->{Rows}; - $selectLogRowCountSth->finish(); if ( $logRows > $Config{ZM_LOG_DATABASE_LIMIT} ) { - my $deleteLogByRowsSql = "delete low_priority from Logs order by TimeKey asc limit ?"; - my $deleteLogByRowsSth = $dbh->prepare_cached( $deleteLogByRowsSql ) or Fatal( "Can't prepare '$deleteLogByRowsSql': ".$dbh->errstr() ); - $res = $deleteLogByRowsSth->execute( $logRows - $Config{ZM_LOG_DATABASE_LIMIT} ) or Fatal( "Can't execute: ".$deleteLogByRowsSth->errstr() ); - aud_print( "Deleted ".$deleteLogByRowsSth->rows()." log table entries by count\n" ) if ( $deleteLogByRowsSth->rows() ); + my $deleteLogByRowsSql = "DELETE low_priority FROM Logs ORDER BY TimeKey ASC LIMIT ?"; + my $deleteLogByRowsSth = $dbh->prepare_cached( $deleteLogByRowsSql ) + or Fatal( "Can't prepare '$deleteLogByRowsSql': ".$dbh->errstr() ); + $res = $deleteLogByRowsSth->execute( $logRows - $Config{ZM_LOG_DATABASE_LIMIT} ) + or Fatal( "Can't execute: ".$deleteLogByRowsSth->errstr() ); + if ( $deleteLogByRowsSth->rows() ) + { + aud_print( "Deleted ".$deleteLogByRowsSth->rows() + ." log table entries by count\n" ) + ; + } } } else { # Time of record - my $deleteLogByTimeSql = "delete low_priority from Logs where TimeKey < unix_timestamp(now() - interval ".$Config{ZM_LOG_DATABASE_LIMIT}.")"; - my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql ) or Fatal( "Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr() ); - $res = $deleteLogByTimeSth->execute() or Fatal( "Can't execute: ".$deleteLogByTimeSth->errstr() ); - aud_print( "Deleted ".$deleteLogByTimeSth->rows()." log table entries by time\n" ) if ( $deleteLogByTimeSth->rows() ); + my $deleteLogByTimeSql = + "DELETE low_priority FROM Logs + WHERE TimeKey < unix_timestamp(now() - interval ".$Config{ZM_LOG_DATABASE_LIMIT}.")"; + my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql ) + or Fatal( "Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr() ); + $res = $deleteLogByTimeSth->execute() + or Fatal( "Can't execute: ".$deleteLogByTimeSth->errstr() ); + if ( $deleteLogByTimeSth->rows() ){ + aud_print( "Deleted ".$deleteLogByTimeSth->rows() + ." log table entries by time\n" ) + ; + } } } $loop = $continuous; @@ -445,7 +563,7 @@ MAIN: while( $loop ) { exit( 0 ); -sub aud_print( $ ) +sub aud_print { my $string = shift; if ( !$continuous ) @@ -458,7 +576,7 @@ sub aud_print( $ ) } } -sub confirm( ;$$ ) +sub confirm { my $prompt = shift || "delete"; my $action = shift || "deleting"; @@ -498,7 +616,7 @@ sub confirm( ;$$ ) return( $yesno ); } -sub deleteSwapImage() +sub deleteSwapImage { my $file = $_; diff --git a/scripts/zmcamtool.pl.in b/scripts/zmcamtool.pl.in index 6efd5deb0..8d6a6321c 100644 --- a/scripts/zmcamtool.pl.in +++ b/scripts/zmcamtool.pl.in @@ -20,12 +20,43 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script provides a way to import new ptz camera controls & camera presets -# into existing zoneminder systems. This script also provides a way to export -# ptz camera controls & camera presets from an existing zoneminder system into -# a sql file, which can then be easily imported to another zoneminder system. -# + +=head1 NAME + +zmcamtool.pl - ZoneMinder tool to import camera controls and presets + +=head1 SYNOPSIS + + zmcamtool.pl [--user= --pass=] + [--import [file.sql] [--overwrite]] + [--export [name]] + [--topreset id [--noregex]] + +=head1 DESCRIPTION + +This script provides a way to import new ptz camera controls & camera presets +into existing zoneminder systems. This script also provides a way to export +ptz camera controls & camera presets from an existing zoneminder system into +a sql file, which can then be easily imported to another zoneminder system. + +=head1 OPTIONS + + --export - Export all camera controls and presets to STDOUT. + Optionally specify a control or preset name. + --import [file.sql] - Import new camera controls and presets found in + zm_create.sql into the ZoneMinder dB. + Optionally specify an alternate sql file to read from. + --overwrite - Overwrite any existing controls or presets. + with the same name as the new controls or presets. + --topreset id - Copy a monitor to a Camera Preset given the monitor id. + --noregex - Do not try to find and replace fields such as usernames, + passwords, IP addresses, etc with generic placeholders + when converting a monitor to a preset. + --help - Print usage information. + --user= - Alternate dB user with privileges to alter dB. + --pass= - Password of alternate dB user with privileges to alter dB. + +=cut use strict; use bytes; @@ -35,6 +66,7 @@ use ZoneMinder::Logger qw(:all); use ZoneMinder::Database qw(:all); use DBI; use Getopt::Long; +use autouse 'Pod::Usage'=>qw(pod2usage); $ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; @@ -55,253 +87,260 @@ my $noregex = 0; my $sqlfile = ''; my $dbUser = $Config{ZM_DB_USER}; my $dbPass = $Config{ZM_DB_PASS}; +my $version = 0; -# Process commandline parameters with getopt long -if ( !GetOptions( 'export'=>\$export, 'import'=>\$import, 'overwrite'=>\$overwrite, 'help'=>\$help, 'topreset'=>\$topreset, 'noregex'=>\$noregex, 'user:s'=>\$dbUser, 'pass:s'=>\$dbPass ) ) { - Usage(); -} +GetOptions( + 'export' =>\$export, + 'import' =>\$import, + 'overwrite' =>\$overwrite, + 'help' =>\$help, + 'topreset' =>\$topreset, + 'noregex' =>\$noregex, + 'user:s' =>\$dbUser, + 'pass:s' =>\$dbPass, + 'version' =>\$version +) or pod2usage(-exitstatus => -1); $Config{ZM_DB_USER} = $dbUser; $Config{ZM_DB_PASS} = $dbPass; +if ( $version ) { + print( ZoneMinder::Base::ZM_VERSION . "\n"); + exit(0); +} # Check to make sure commandline params make sense if ( ((!$help) && ($import + $export + $topreset) != 1 )) { - print( STDERR qq/Please give only one of the following: "import", "export", or "topreset".\n/ ); - Usage(); + print( STDERR qq/Please give only one of the following: "import", "export", or "topreset".\n/ ); + pod2usage(-exitstatus => -1); } if ( ($export)&&($overwrite) ) { - print( "Warning: Overwrite parameter ignored during an export.\n"); + print( "Warning: Overwrite parameter ignored during an export.\n"); } if ( ($noregex)&&(!$topreset) ) { - print( qq/Warning: Noregex parameter only applies when "topreset" parameter is also set. Ignoring.\n/); + print( qq/Warning: Noregex parameter only applies when "topreset" parameter is also set. Ignoring.\n/); } if ( ($topreset)&&($ARGV[0] !~ /\d\d*/) ) { - print( STDERR qq/Parameter "topreset" requires a valid monitor ID.\n/ ); - Usage(); + print( STDERR qq/Parameter "topreset" requires a valid monitor ID.\n/ ); + pod2usage(-exitstatus => -1); } # Call the appropriate subroutine based on the params given on the commandline if ($help) { - Usage(); + pod2usage(-exitstatus => -1); } if ($export) { - exportsql(); + exportsql(); } if ($import) { - importsql(); + importsql(); } if ($topreset) { - toPreset(); + toPreset(); } ############### # SUBROUTINES # ############### -# Usage subroutine help text -sub Usage -{ -die(" -USAGE: -zmcamtool.pl [--user= --pass=] - [--import [file.sql] [--overwrite]] - [--export [name]] - [--topreset id [--noregex]] - -PARAMETERS: ---export - Export all camera controls and presets to STDOUT. - Optionally specify a control or preset name. ---import [file.sql] - Import new camera controls and presets found in - zm_create.sql into the ZoneMinder dB. - Optionally specify an alternate sql file to read from. ---overwrite - Overwrite any existing controls or presets. - with the same name as the new controls or presets. ---topreset id - Copy a monitor to a Camera Preset given the monitor id. ---noregex - Do not try to find and replace fields such as usernames, - passwords, ip addresses, etc with generic placeholders - when converting a monitor to a preset. ---help - Print usage information. ---user= - Alternate dB user with privileges to alter dB. ---pass= - Password of alternate dB user with privileges to alter dB. -\n"); -} - # Execute a pre-built sql select query sub selectQuery { - my $dbh = shift; - my $sql = shift; - my $monitorid = shift; + my $dbh = shift; + my $sql = shift; + my $monitorid = shift; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute($monitorid) or die( "Can't execute: ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute($monitorid) + or die( "Can't execute: ".$sth->errstr() ); - my @data = $sth->fetchrow_array(); - $sth->finish(); + my @data = $sth->fetchrow_array(); + $sth->finish(); - return @data; + return @data; } # Exectute a pre-built sql query sub runQuery { - my $dbh = shift; - my $sql = shift; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); + my $dbh = shift; + my $sql = shift; + my $sth = $dbh->prepare_cached( $sql ) + or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() + or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); - return $res; + return $res; } # Build and execute a sql insert query sub insertQuery { - my $dbh = shift; - my $tablename = shift; - my @data = @_; + my $dbh = shift; + my $tablename = shift; + my @data = @_; - my $sql = "insert into $tablename values (NULL,".(join ", ", ("?") x @data).")"; # Add "?" for each array element + my $sql = "INSERT INTO $tablename VALUES (NULL," + .(join ", ", ("?") x @data).")"; # Add "?" for each array element - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute(@data) or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); + my $sth = $dbh->prepare_cached( $sql ) + or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute(@data) + or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); - return $res; + return $res; } # Build and execute a sql delete query sub deleteQuery { - my $dbh = shift; - my $sqltable = shift; - my $sqlname = shift; + my $dbh = shift; + my $sqltable = shift; + my $sqlname = shift; - my $sql = "delete from $sqltable where Name = ?"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute($sqlname) or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); + my $sql = "DELETE FROM $sqltable WHERE Name = ?"; + my $sth = $dbh->prepare_cached( $sql ) + or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute($sqlname) + or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); - return $res; + return $res; } # Build and execute a sql select count query sub checkExists { - my $dbh = shift; - my $sqltable = shift; - my $sqlname = shift; - my $result = 0; + my $dbh = shift; + my $sqltable = shift; + my $sqlname = shift; + my $result = 0; - my $sql = "select count(*) from $sqltable where Name = ?"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute($sqlname) or die( "Can't execute: ".$sth->errstr() ); + my $sql = "SELECT count(*) FROM $sqltable WHERE Name = ?"; + my $sth = $dbh->prepare_cached( $sql ) + or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute($sqlname) + or die( "Can't execute: ".$sth->errstr() ); - my $rows = $sth->fetchrow_arrayref(); - $sth->finish(); + my $rows = $sth->fetchrow_arrayref(); + $sth->finish(); - if ($rows->[0] > 0) { - $result = 1; - } + if ($rows->[0] > 0) { + $result = 1; + } - return $result; + return $result; } # Import camera control & presets into the zoneminder dB sub importsql { - my @newcontrols; - my @overwritecontrols; - my @skippedcontrols; - my @newpresets; - my @overwritepresets; - my @skippedpresets; - my %controls; - my %monitorpresets; + my @newcontrols; + my @overwritecontrols; + my @skippedcontrols; + my @newpresets; + my @overwritepresets; + my @skippedpresets; + my %controls; + my %monitorpresets; - if ($ARGV[0]) { - $sqlfile = $ARGV[0]; - } else { - $sqlfile = $Config{ZM_PATH_DATA}.'/db/zm_create.sql'; - } + if ($ARGV[0]) { + $sqlfile = $ARGV[0]; + } else { + $sqlfile = $Config{ZM_PATH_DATA}.'/db/zm_create.sql'; + } - open(my $SQLFILE,"<",$sqlfile) or die( "Can't Open file: $!\n" ); + open(my $SQLFILE,"<",$sqlfile) + or die( "Can't Open file: $!\n" ); - # Find and extract ptz control and monitor preset records - while (<$SQLFILE>) { - # Our regex replaces the primary key with NULL - if (s/^(INSERT INTO .*?Controls.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) { - $controls{$3} = $_; - } elsif (s/^(INSERT INTO .*?MonitorPresets.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) { - $monitorpresets{$3} = $_; - } - } - close $SQLFILE; + # Find and extract ptz control and monitor preset records + while (<$SQLFILE>) { + # Our regex replaces the primary key with NULL + if (s/^(INSERT INTO .*?Controls.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) { + $controls{$3} = $_; + } elsif (s/^(INSERT INTO .*?MonitorPresets.*? VALUES \().*?(,')(.*?)(',.*)/$1NULL$2$3$4/i) { + $monitorpresets{$3} = $_; + } + } + close $SQLFILE; - if ( ! (%controls || %monitorpresets) ) { - die( "Error: No relevant data found in $sqlfile.\n" ); - } + if ( ! (%controls || %monitorpresets) ) { + die( "Error: No relevant data found in $sqlfile.\n" ); + } - # Now that we've got what we were looking for, compare to what is already in the dB + # Now that we've got what we were looking for, + # compare to what is already in the dB - my $dbh = zmDbConnect(); - foreach (keys %controls) { - if (!checkExists($dbh,"Controls",$_)) { - # No existing Control was found. Add new control to dB. - runQuery($dbh,$controls{$_}); - push @newcontrols, $_; - } elsif ($overwrite) { - # An existing Control was found and the overwrite flag is set. Overwrite the control. - deleteQuery($dbh,"Controls",$_); - runQuery($dbh,$controls{$_}); - push @overwritecontrols, $_; - } else { - # An existing Control was found and the overwrite flag was not set. Do nothing. - push @skippedcontrols, $_; - } - } + my $dbh = zmDbConnect(); + foreach (keys %controls) { + if (!checkExists($dbh,"Controls",$_)) { + # No existing Control was found. Add new control to dB. + runQuery($dbh,$controls{$_}); + push @newcontrols, $_; + } elsif ($overwrite) { + # An existing Control was found and the overwrite flag is set. + # Overwrite the control. + deleteQuery($dbh,"Controls",$_); + runQuery($dbh,$controls{$_}); + push @overwritecontrols, $_; + } else { + # An existing Control was found and the overwrite flag was not set. + # Do nothing. + push @skippedcontrols, $_; + } + } - foreach (keys %monitorpresets) { - if (!checkExists($dbh,"MonitorPresets",$_)) { - # No existing MonitorPreset was found. Add new MonitorPreset to dB. - runQuery($dbh,$monitorpresets{$_}); - push @newpresets, $_; - } elsif ($overwrite) { - # An existing MonitorPreset was found and the overwrite flag is set. Overwrite the MonitorPreset. - deleteQuery($dbh,"MonitorPresets",$_); - runQuery($dbh,$monitorpresets{$_}); - push @overwritepresets, $_; - } else { - # An existing MonitorPreset was found and the overwrite flag was not set. Do nothing. - push @skippedpresets, $_; - } - } + foreach (keys %monitorpresets) { + if (!checkExists($dbh,"MonitorPresets",$_)) { + # No existing MonitorPreset was found. Add new MonitorPreset to dB. + runQuery($dbh,$monitorpresets{$_}); + push @newpresets, $_; + } elsif ($overwrite) { + # An existing MonitorPreset was found and the overwrite flag is set. + # Overwrite the MonitorPreset. + deleteQuery($dbh,"MonitorPresets",$_); + runQuery($dbh,$monitorpresets{$_}); + push @overwritepresets, $_; + } else { + # An existing MonitorPreset was found and the overwrite flag was + # not set. Do nothing. + push @skippedpresets, $_; + } + } - if (@newcontrols) { - print "Number of ptz camera controls added: ".scalar(@newcontrols)."\n"; - } - if (@overwritecontrols) { - print "Number of existing ptz camera controls overwritten: ".scalar(@overwritecontrols)."\n"; - } - if (@skippedcontrols) { - print "Number of existing ptz camera controls skipped: ".scalar(@skippedcontrols)."\n"; - } + if (@newcontrols) { + print "Number of ptz camera controls added: " + .scalar(@newcontrols)."\n"; + } + if (@overwritecontrols) { + print "Number of existing ptz camera controls overwritten: " + .scalar(@overwritecontrols)."\n"; + } + if (@skippedcontrols) { + print "Number of existing ptz camera controls skipped: " + .scalar(@skippedcontrols)."\n"; + } - if (@newpresets) { - print "Number of monitor presets added: ".scalar(@newpresets)."\n"; - } - if (@overwritepresets) { - print "Number of existing monitor presets overwritten: ".scalar(@overwritepresets)."\n"; - } - if (@skippedpresets) { - print "Number of existing presets skipped: ".scalar(@skippedpresets)."\n"; - } + if (@newpresets) { + print "Number of monitor presets added: " + .scalar(@newpresets)."\n"; + } + if (@overwritepresets) { + print "Number of existing monitor presets overwritten: " + .scalar(@overwritepresets)."\n"; + } + if (@skippedpresets) { + print "Number of existing presets skipped: " + .scalar(@skippedpresets)."\n"; + } } # Export camera controls & presets from the zoneminder dB to STDOUT @@ -312,14 +351,14 @@ my ( $host, $port ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); my $command = "mysqldump -t --skip-opt --compact -h".$host; $command .= " -P".$port if defined($port); if ( $dbUser ) { - $command .= " -u".$dbUser; - if ( $dbPass ) { - $command .= " -p".$dbPass; - } - } + $command .= " -u".$dbUser; + if ( $dbPass ) { + $command .= " -p".$dbPass; + } + } if ($ARGV[0]) { - $command .= qq( --where="Name = '$ARGV[0]'"); + $command .= qq( --where="Name = '$ARGV[0]'"); } $command .= " zm Controls MonitorPresets"; @@ -327,78 +366,81 @@ $command .= " zm Controls MonitorPresets"; my $output = qx($command); my $status = $? >> 8; if ( $status || logDebugging() ) { - chomp( $output ); - print( "Output: $output\n" ); + chomp( $output ); + print( "Output: $output\n" ); } if ( $status ) { - die( "Command '$command' exited with status: $status\n" ); + die( "Command '$command' exited with status: $status\n" ); } else { - # NULLify the primary keys before printing the output to STDOUT - $output =~ s/VALUES \((.*?),'/VALUES \(NULL,'/ig; - print $output; - } + # NULLify the primary keys before printing the output to STDOUT + $output =~ s/VALUES \((.*?),'/VALUES \(NULL,'/ig; + print $output; + } } sub toPreset { - my $dbh = zmDbConnect(); - my $monitorid = $ARGV[0]; + my $dbh = zmDbConnect(); + my $monitorid = $ARGV[0]; - # Grap the following fields from the Monitors table - my $sql = "select - Name, - Type, - Device, - Channel, - Format, - Protocol, - Method, - Host, - Port, - Path, - SubPath, - Width, - Height, - Palette, - MaxFPS, - Controllable, - ControlId, - ControlDevice, - ControlAddress, - DefaultRate, - DefaultScale - from Monitors where Id = ?"; - my @data = selectQuery($dbh,$sql,$monitorid); + # Grap the following fields from the Monitors table + my $sql = "SELECT + Name, + Type, + Device, + Channel, + Format, + Protocol, + Method, + Host, + Port, + Path, + SubPath, + Width, + Height, + Palette, + MaxFPS, + Controllable, + ControlId, + ControlDevice, + ControlAddress, + DefaultRate, + DefaultScale + FROM Monitors WHERE Id = ?"; + my @data = selectQuery($dbh,$sql,$monitorid); - if (!@data) { - die( "Error: Monitor Id $monitorid does not appear to exist in the database.\n" ); - } + if (!@data) { + die( "Error: Monitor Id $monitorid does not appear to exist in the database.\n" ); + } - # Attempt to search for and replace system specific values such as ip addresses, ports, usernames, etc. with generic placeholders - if (!$noregex) { - foreach (@data) { - s/\b(?:\d{1,3}\.){3}\d{1,3}\b//; # ip address - s/:(6553[0-5]|655[0-2]\d|65[0-4]\d\d|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)$/:/; # tcpip port - s/\/\/.*:.*@/\/\/:@/; # user & pwd preceeding an ip address - s/(&|\?)(user|username)=\w\w*(&|\?)/$1$2=$3/i; # username embeded in url - s/(&|\?)(pwd|password)=\w\w*(&|\?)/$1$2=$3/i; # password embeded in url - s/\w\w*:\w\w*/:/; # user & pwd in their own field - s/\/dev\/video\d\d*/\/dev\/video/; # local video devices - } - } + # Attempt to search for and replace system specific values such as + # ip addresses, ports, usernames, etc. with generic placeholders + if (!$noregex) { + foreach (@data) { + s/\b(?:\d{1,3}\.){3}\d{1,3}\b//; # ip address + s/:(6553[0-5]|655[0-2]\d|65[0-4]\d\d|6[0-4]\d{3}|[1-5]\d{4}|[1-9]\d{0,3}|0)$/:/; # tcpip port + s/\/\/.*:.*@/\/\/:@/; # user & pwd preceding an ip address + s/(&|\?)(user|username)=\w\w*(&|\?)/$1$2=$3/i; # username embedded in url + s/(&|\?)(pwd|password)=\w\w*(&|\?)/$1$2=$3/i; # password embedded in url + s/\w\w*:\w\w*/:/; # user & pwd in their own field + s/\/dev\/video\d\d*/\/dev\/video/; # local video devices + } + } - if (!checkExists($dbh,"MonitorPresets",$data[0])) { - # No existing Preset was found. Add new Preset to dB. - print "Adding new preset: $data[0]\n"; - insertQuery($dbh,"MonitorPresets",@data); - } elsif ($overwrite) { - # An existing Control was found and the overwrite flag is set. Overwrite the control. - print "Existing preset $data[0] detected.\nOverwriting...\n"; - deleteQuery($dbh,"MonitorPresets",$data[0]); - insertQuery($dbh,"MonitorPresets",@data); - } else { - # An existing Control was found and the overwrite flag was not set. Do nothing. - print "Existing preset $data[0] detected and overwrite flag not set.\nSkipping...\n"; - } + if (!checkExists($dbh,"MonitorPresets",$data[0])) { + # No existing Preset was found. Add new Preset to dB. + print "Adding new preset: $data[0]\n"; + insertQuery($dbh,"MonitorPresets",@data); + } elsif ($overwrite) { + # An existing Control was found and the overwrite flag is set. + # Overwrite the control. + print "Existing preset $data[0] detected.\nOverwriting...\n"; + deleteQuery($dbh,"MonitorPresets",$data[0]); + insertQuery($dbh,"MonitorPresets",@data); + } else { + # An existing Control was found and the overwrite flag was not set. + # Do nothing. + print "Existing preset $data[0] detected and overwrite flag not set.\nSkipping...\n"; + } } diff --git a/scripts/zmcontrol.pl.in b/scripts/zmcontrol.pl.in index dcfef5c87..9b34533f7 100644 --- a/scripts/zmcontrol.pl.in +++ b/scripts/zmcontrol.pl.in @@ -20,38 +20,53 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script continuously monitors the recorded events for the given -# monitor and applies any filters which would delete and/or upload -# matching events -# + +=head1 NAME + +zmcontrol.pl - ZoneMinder control script + +=head1 SYNOPSIS + + zmcontrol.pl --id {monitor_id} --command={command} [various options] + +=head1 DESCRIPTION + +FIXME FIXME + +=head1 OPTIONS + + --autostop - + --xcoord [ arg ] - X-coord + --ycoord [ arg ] - Y-coord + --speed [ arg ] - Speed + --step [ arg ] - + --panspeed [ arg ] - + --panstep [ arg ] - + --tiltspeed [ arg ] - + --tiltstep [ arg ] - + --preset [ arg ] - + +=cut use strict; @EXTRA_PERL_LIB@ use ZoneMinder; use Getopt::Long; +use autouse 'Pod::Usage'=>qw(pod2usage); use POSIX qw/strftime EPIPE/; use Socket; #use Data::Dumper; -use Module::Load; +use Module::Load::Conditional qw{can_load};; use constant MAX_CONNECT_DELAY => 10; use constant MAX_COMMAND_WAIT => 1800; $| = 1; -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; -sub Usage -{ - print( " -Usage: zmcontrol.pl --id --command= -"); - exit(); -} - logInit(); my $arg_string = join( " ", @ARGV ); @@ -59,29 +74,25 @@ my $arg_string = join( " ", @ARGV ); my $id; my %options; -if ( !GetOptions( - 'id=i'=>\$id, - 'command=s'=>\$options{command}, - 'xcoord=i'=>\$options{xcoord}, - 'ycoord=i'=>\$options{ycoord}, - 'speed=i'=>\$options{speed}, - 'step=i'=>\$options{step}, - 'panspeed=i'=>\$options{panspeed}, - 'tiltspeed=i'=>\$options{tiltspeed}, - 'panstep=i'=>\$options{panstep}, - 'tiltstep=i'=>\$options{tiltstep}, - 'preset=i'=>\$options{preset}, - 'autostop'=>\$options{autostop}, - ) -) -{ - Usage(); -} +GetOptions( + 'id=i' =>\$id, + 'command=s' =>\$options{command}, + 'xcoord=i' =>\$options{xcoord}, + 'ycoord=i' =>\$options{ycoord}, + 'speed=i' =>\$options{speed}, + 'step=i' =>\$options{step}, + 'panspeed=i' =>\$options{panspeed}, + 'tiltspeed=i' =>\$options{tiltspeed}, + 'panstep=i' =>\$options{panstep}, + 'tiltstep=i' =>\$options{tiltstep}, + 'preset=i' =>\$options{preset}, + 'autostop' =>\$options{autostop}, +) or pod2usage(-exitstatus => -1); if ( !$id || !$options{command} ) { print( STDERR "Please give a valid monitor id and command\n" ); - Usage(); + pod2usage(-exitstatus => -1); } ( $id ) = $id =~ /^(\w+)$/; @@ -90,13 +101,14 @@ Debug( $arg_string ); my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock'; -socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); +socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) + or Fatal( "Can't open socket: $!" ); my $saddr = sockaddr_un( $sock_file ); my $server_up = connect( CLIENT, $saddr ); if ( !$server_up ) { - # The server isn't there + # The server isn't there my $monitor = zmDbGetMonitorAndControl( $id ); if ( !$monitor ) { @@ -129,17 +141,22 @@ if ( !$server_up ) Info( "Starting control server $id/$protocol" ); close( CLIENT ); + if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) { + Fatal("Can't load ZoneMinder::Control::$protocol"); + } + if ( my $cpid = fork() ) { logReinit(); # Parent process just sleep and fall through - socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or die( "Can't open socket: $!" ); + socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) + or die( "Can't open socket: $!" ); my $attempts = 0; while (!connect( CLIENT, $saddr )) { $attempts++; - Fatal( "Can't connect: $!" ) if ($attempts > MAX_CONNECT_DELAY); + Fatal( "Can't connect: $! after $attempts attempts to $sock_file" ) if ($attempts > MAX_CONNECT_DELAY); sleep(1); } } @@ -152,19 +169,20 @@ if ( !$server_up ) logReinit(); - Info( "Control server $id/$protocol starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() ) ); + Info( "Control server $id/$protocol starting at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + ); $0 = $0." --id $id"; - load "ZoneMinder::Control::$protocol"; - my $control = "ZoneMinder::Control::$protocol"->new( $id ); my $control_key = $control->getKey(); $control->loadMonitor(); $control->open(); - socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); + socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) + or Fatal( "Can't open socket: $!" ); unlink( $sock_file ); bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" ); listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); @@ -190,8 +208,11 @@ if ( !$server_up ) #Debug( Dumper( $params ) ); my $command = $params->{command}; - $control->$command( $params ); close( CLIENT ); + if ( $command eq 'quit' ) { + last; + } + $control->$command( $params ); } else { @@ -215,7 +236,9 @@ if ( !$server_up ) last; } } - Info( "Control server $id/$protocol exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() ) ); + Info( "Control server $id/$protocol exiting at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + ); unlink( $sock_file ); $control->close(); exit( 0 ); diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 5df490947..bafb0ed9e 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -20,13 +20,30 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script is the gateway for controlling the various ZoneMinder -# daemons. All starting, stopping and restarting goes through here. -# On the first invocation it starts up a server which subsequently -# records what's running and what's not. Other invocations just -# connect to the server and pass instructions to it. -# + +=head1 NAME + +zmdc.pl - ZoneMinder Daemon Control script + +=head1 SYNOPSIS + + zmdc.pl {command} [daemon [options]] + +=head1 DESCRIPTION + +This script is the gateway for controlling the various ZoneMinder +daemons. All starting, stopping and restarting goes through here. +On the first invocation it starts up a server which subsequently +records what's running and what's not. Other invocations just +connect to the server and pass instructions to it. + +=head1 OPTIONS + + {command} - One of 'startup|shutdown|status|check|logrot' or + 'start|stop|restart|reload|version'. + [daemon [options]] - Daemon name and options, required for second group of commands + +=cut use strict; use bytes; @@ -49,14 +66,19 @@ use ZoneMinder; use POSIX; use Socket; use IO::Handle; -use Data::Dumper; +use autouse 'Pod::Usage'=>qw(pod2usage); +#use Data::Dumper; use constant SOCK_FILE => $Config{ZM_PATH_SOCKS}.'/zmdc.sock'; $| = 1; -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; +if ( $Config{ZM_LD_PRELOAD} ) { + Debug("Adding ENV{LD_PRELOAD} = $Config{ZM_LD_PRELOAD}"); + $ENV{LD_PRELOAD} = $Config{ZM_LD_PRELOAD}; +} delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my @daemons = ( @@ -69,33 +91,26 @@ my @daemons = ( 'zmx10.pl', 'zmwatch.pl', 'zmupdate.pl', - 'zmtrack.pl' + 'zmtrack.pl', + 'zmtelemetry.pl' ); -sub Usage -{ - print( " -Usage: zmdc.pl [daemon [options]] -Parameters are :- - - One of 'startup|shutdown|status|check|logrot' or - 'start|stop|restart|reload'. -[daemon [options]] - Daemon name and options, required for second group of commands -"); - exit( -1 ); -} - my $command = shift @ARGV; if( !$command ) { print( STDERR "No command given\n" ); - Usage(); + pod2usage(-exitstatus => -1); } -my $needs_daemon = $command !~ /(?:startup|shutdown|status|check|logrot)/; +if ( $command eq 'version' ) { + print ZoneMinder::Base::ZM_VERSION."\n"; + exit( 0 ); +} +my $needs_daemon = $command !~ /(?:startup|shutdown|status|check|logrot|version)/; my $daemon = shift( @ARGV ); if( $needs_daemon && !$daemon ) { print( STDERR "No daemon given\n" ); - Usage(); + pod2usage(-exitstatus => -1); } my @args; @@ -109,7 +124,7 @@ if ( $needs_daemon ) else { print( STDERR "Invalid daemon '$daemon' specified" ); - Usage(); + pod2usage(-exitstatus => -1); } } @@ -148,7 +163,7 @@ if ( !$server_up ) print( "Unable to connect to server\n" ); exit( -1 ); } - # The server isn't there + # The server isn't there print( "Starting server\n" ); close( CLIENT ); @@ -214,7 +229,7 @@ use ZoneMinder; use POSIX; use Socket; use IO::Handle; -use Data::Dumper; +#use Data::Dumper; our %cmd_hash; our %pid_hash; @@ -231,19 +246,24 @@ sub run logInit(); - dPrint( ZoneMinder::Logger::INFO, "Server starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); + dPrint( ZoneMinder::Logger::INFO, "Server starting at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + ."\n" + ); - if ( open( PID, ">".ZM_PID ) ) + if ( open( my $PID, '>', ZM_PID ) ) { - print( PID $$ ); - close( PID ); + print( $PID $$ ); + close( $PID ); + } else { + Error( "Can't open pid file at " . ZM_PID ); } killAll( 1 ); socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); - unlink( main::SOCK_FILE ); - bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" ); + unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE ); + bind( SERVER, $saddr ) or Fatal( "Can't bind to " . main::SOCK_FILE . ": $!" ); listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); $SIG{CHLD} = \&reaper; @@ -350,9 +370,12 @@ sub run restartPending(); } } - dPrint( ZoneMinder::Logger::INFO, "Server exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); - unlink( main::SOCK_FILE ); - unlink( ZM_PID ); + dPrint( ZoneMinder::Logger::INFO, "Server exiting at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + ."\n" + ); + unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE ); + unlink( ZM_PID ) or Error( "Unable to unlink " . ZM_PID .". Error message was: $!" ) if ( -e ZM_PID ); exit(); } @@ -398,8 +421,7 @@ sub start my $daemon = shift; my @args = @_; - my $command = $daemon; - $command .= ' '.join( ' ', ( @args ) ) if ( @args ); + my $command = join(' ', $daemon, @args ); my $process = $cmd_hash{$command}; if ( !$process ) @@ -409,7 +431,10 @@ sub start } elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) { - dPrint( ZoneMinder::Logger::INFO, "'$process->{command}' already running at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}\n" ); + dPrint( ZoneMinder::Logger::INFO, "'$process->{command}' already running at " + .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ) + .", pid = $process->{pid}\n" + ); return(); } @@ -424,7 +449,10 @@ sub start $process->{started} = time(); delete( $process->{pending} ); - dPrint( ZoneMinder::Logger::INFO, "'$command' starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}\n" ); + dPrint( ZoneMinder::Logger::INFO, "'$command' starting at " + .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ) + .", pid = $process->{pid}\n" + ); $cmd_hash{$process->{command}} = $pid_hash{$cpid} = $process; sigprocmask( SIG_SETMASK, $sigset ) or Fatal( "Can't restore SIGCHLD: $!" ); @@ -433,7 +461,11 @@ sub start { logReinit(); - dPrint( ZoneMinder::Logger::INFO, "'".join( ' ', ( $daemon, @args ) )."' started at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); + dPrint( ZoneMinder::Logger::INFO, "'".join( ' ', ( $daemon, @args ) ) + ."' started at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + ."\n" + ); if ( $daemon =~ /^${daemon_patt}$/ ) { @@ -480,54 +512,78 @@ sub start } } -sub _stop -{ - my $final = shift; - my $daemon = shift; - my @args = @_; +# Sends the stop signal, without waiting around to see if the process died. +sub send_stop { + my ( $final, $process ) = @_; - my $command = $daemon; - $command .= ' '.join( ' ', ( @args ) ) if ( @args ); + my $command = $process->{command}; + if ( $process->{pending} ) { + + delete( $cmd_hash{$command} ); + dPrint( ZoneMinder::Logger::INFO, "Command '$command' removed from pending list at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + ."\n" + ); + return(); + } + + my $pid = $process->{pid}; + if ( !$pid_hash{$pid} ) + { + dPrint( ZoneMinder::Logger::ERROR, "No process with command of '$command' pid $pid is running\n" ); + return(); + } + + dPrint( ZoneMinder::Logger::INFO, "'$command' sending stop to pid $pid at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + ."\n" + ); + $process->{keepalive} = !$final; + kill( 'TERM', $pid ); + return $pid; +} # end sub send_stop + +sub kill_until_dead { + my ( $process ) = @_; + # Now check it has actually gone away, if not kill -9 it + my $count = 0; + while( $process and $$process{pid} and kill( 0, $$process{pid} ) ) + { + if ( $count++ > 5 ) + { + dPrint( ZoneMinder::Logger::WARNING, "'$$process{command}' has not stopped at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + .". Sending KILL to pid $$process{pid}\n" + ); + kill( 'KILL', $$process{pid} ); + } + + sleep( 1 ); + } +} + +sub _stop { + my ($final, $process ) = @_; + + my $pid = send_stop( $final, $process ); + return if ! $pid; + delete( $cmd_hash{$$process{command}} ); + + kill_until_dead( $process ); +} + +sub stop +{ + my ( $daemon, @args ) = @_; + my $command = join(' ', $daemon, @args ); my $process = $cmd_hash{$command}; if ( !$process ) { dPrint( ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'\n" ); return(); } - elsif ( $process->{pending} ) - { - delete( $cmd_hash{$command} ); - dPrint( ZoneMinder::Logger::INFO, "Command '$command' removed from pending list at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); - return(); - } - my $cpid = $process->{pid}; - if ( !$pid_hash{$cpid} ) - { - dPrint( ZoneMinder::Logger::ERROR, "No process with command of '$command' is running\n" ); - return(); - } - - dPrint( ZoneMinder::Logger::INFO, "'$daemon ".join( ' ', @args )."' stopping at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); - $process->{keepalive} = !$final; - kill( 'TERM', $cpid ); - delete( $cmd_hash{$command} ); - - # Now check it has actually gone away, if not kill -9 it - my $count = 0; - while( $cpid && kill( 0, $cpid ) ) - { - if ( $count++ > 5 ) - { - kill( 'KILL', $cpid ); - } - sleep( 1 ); - } -} - -sub stop -{ - _stop( 1, @_ ); + _stop( 1, $process ); } sub restart @@ -545,7 +601,7 @@ sub restart my $cpid = $process->{pid}; if ( defined($pid_hash{$cpid}) ) { - _stop( 0, $daemon, @args ); + _stop( 0, $process ); return; } } @@ -606,7 +662,7 @@ sub reaper my $exit_signal = $status&0xfe; my $core_dumped = $status&0x01; - my $out_str = "'$process->{daemon} ".join( ' ', @{$process->{args}} )."' "; + my $out_str = "'$process->{command}' "; if ( $exit_signal ) { if ( $exit_signal == 15 || $exit_signal == 14 ) # TERM or ALRM @@ -645,17 +701,16 @@ sub reaper if ( $process->{keepalive} ) { + # Schedule for immediate restart + $cmd_hash{$process->{command}} = $process; if ( !$process->{delay} || ($process->{runtime} > $Config{ZM_MAX_RESTART_DELAY} ) ) { #start( $process->{daemon}, @{$process->{args}} ); - # Schedule for immediate restart - $cmd_hash{$process->{command}} = $process; $process->{pending} = $process->{stopped}; $process->{delay} = 5; } else { - $cmd_hash{$process->{command}} = $process; $process->{pending} = $process->{stopped}+$process->{delay}; $process->{delay} *= 2; # Limit the start delay to 15 minutes max @@ -685,14 +740,28 @@ sub restartPending sub shutdownAll { - foreach my $process ( values( %pid_hash ) ) - { - stop( $process->{daemon}, @{$process->{args}} ); + foreach my $pid ( keys %pid_hash ) { + # This is a quick fix because a SIGCHLD can happen and alter pid_hash while we are in here. + next if ! $pid_hash{$pid}; + send_stop( 1, $pid_hash{$pid} ); + } + foreach my $pid ( keys %pid_hash ) { + # This is a quick fix because a SIGCHLD can happen and alter pid_hash while we are in here. + next if ! $pid_hash{$pid}; + + my $process = $pid_hash{$pid}; + + kill_until_dead( $process ); + delete( $cmd_hash{$$process{command}} ); + delete( $pid_hash{$pid} ); } killAll( 5 ); - dPrint( ZoneMinder::Logger::INFO, "Server shutdown at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); - unlink( main::SOCK_FILE ); - unlink( ZM_PID ); + dPrint( ZoneMinder::Logger::INFO, "Server shutdown at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + ."\n" + ); + unlink( main::SOCK_FILE ) or Error( "Unable to unlink " . main::SOCK_FILE .". Error message was: $!" ) if ( -e main::SOCK_FILE ); + unlink( ZM_PID ) or Error( "Unable to unlink " . ZM_PID .". Error message was: $!" ) if ( -e ZM_PID ); close( CLIENT ); close( SERVER ); exit(); @@ -746,7 +815,10 @@ sub status if ( $process->{pending} ) { - dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) )."\n" ); + dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " + .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) ) + ."\n" + ); } else { @@ -757,13 +829,19 @@ sub status return(); } } - dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' running since ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}" ); + dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' running since " + .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ) + .", pid = $process->{pid}" + ); } else { foreach my $process ( values(%pid_hash) ) { - my $out_str = "'$process->{command}' running since ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ).", pid = $process->{pid}"; + my $out_str = "'$process->{command}' running since " + .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{started}) ) + .", pid = $process->{pid}" + ; $out_str .= ", valid" if ( kill( 0, $process->{pid} ) ); $out_str .= "\n"; dPrint( ZoneMinder::Logger::DEBUG, $out_str ); @@ -772,7 +850,10 @@ sub status { if ( $process->{pending} ) { - dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at ".strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) )."\n" ); + dPrint( ZoneMinder::Logger::DEBUG, "'$process->{command}' pending at " + .strftime( '%y/%m/%d %H:%M:%S', localtime( $process->{pending}) ) + ."\n" + ); } } } @@ -782,19 +863,22 @@ sub killAll { my $delay = shift; sleep( $delay ); - my $killall; - if ( '@HOST_OS@' eq 'BSD' ) { - $killall = 'killall -'; - } else { - $killall = 'killall -q -s '; - } - foreach my $daemon ( @daemons ) { - - my $cmd = $killall ."TERM $daemon"; - Debug( $cmd ); - qx( $cmd ); - } - sleep( $delay ); + my $killall; + if ( '@HOST_OS@' eq 'BSD' ) + { + $killall = 'killall -q -'; + } elsif ( '@HOST_OS@' eq 'solaris' ) { + $killall = 'pkill -'; + } else { + $killall = 'killall -q -s '; + } + foreach my $daemon ( @daemons ) + { + my $cmd = $killall ."TERM $daemon"; + Debug( $cmd ); + qx( $cmd ); + } + sleep( $delay ); foreach my $daemon ( @daemons ) { my $cmd = $killall."KILL $daemon"; diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in old mode 100644 new mode 100755 index 1e9f1bbce..a101edf3a --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -20,11 +20,27 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script continuously monitors the recorded events for the given -# monitor and applies any filters which would delete and/or upload -# matching events -# + +=head1 NAME + +zmfilter.pl - ZoneMinder tool to filter events + +=head1 SYNOPSIS + + zmfilter.pl [-f ,--filter=] | -v, --version + +=head1 DESCRIPTION + +This script continuously monitors the recorded events for the given +monitor and applies any filters which would delete and/or upload +matching events. + +=head1 OPTIONS + + -f{filter name}, --filter={filter name} - The name of a specific filter to run + -v, --version - Print ZoneMinder version + +=cut use strict; use bytes; @@ -44,14 +60,19 @@ use constant START_DELAY => 5; # How long to wait before starting @EXTRA_PERL_LIB@ use ZoneMinder; +require ZoneMinder::Filter; use DBI; use POSIX; use Time::HiRes qw/gettimeofday/; -use Date::Manip; +#use Date::Manip; use Getopt::Long; -use Data::Dumper; +use autouse 'Pod::Usage'=>qw(pod2usage); +use autouse 'Data::Dumper'=>qw(Dumper); -use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)?$Config{ZM_DIR_EVENTS}:($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}); +use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|) + ? $Config{ZM_DIR_EVENTS} + : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}) +; logInit(); logSetSignal(); @@ -105,62 +126,27 @@ if ( $Config{ZM_OPT_MESSAGE} ) } } - $| = 1; -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL}; my $event_id = 0; my $filter_parm = ""; - -sub Usage -{ - print( " -Usage: zmfilter.pl [-f ,--filter=] -Parameters are :- --f, --filter= - The name of a specific filter to run -"); - exit( -1 ); -} +my $version = 0; # -# More or less replicates the equivalent PHP function -# -sub strtotime -{ - my $dt_str = shift; - return( UnixDate( $dt_str, '%s' ) ); -} -# -# More or less replicates the equivalent PHP function -# -sub str_repeat -{ - my $string = shift; - my $count = shift; - return( ${string}x${count} ); -} +GetOptions( + 'filter=s' =>\$filter_parm, + 'version' =>\$version +) or pod2usage(-exitstatus => -1); -# Formats a date into MySQL format -sub DateTimeToSQL -{ - my $dt_str = shift; - my $dt_val = strtotime( $dt_str ); - if ( !$dt_val ) - { - Error( "Unable to parse date string '$dt_str'\n" ); - return( undef ); - } - return( strftime( "%Y-%m-%d %H:%M:%S", localtime( $dt_val ) ) ); -} - -if ( !GetOptions( 'filter=s'=>\$filter_parm ) ) -{ - Usage(); +if ( $version ) { + print ZoneMinder::Base::ZM_VERSION . "\n"; + exit(0); } if ( ! EVENT_PATH ) { @@ -191,10 +177,11 @@ my $last_action = 0; while( 1 ) { - if ( (time() - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) + my $now = time; + if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) { Debug( "Reloading filters\n" ); - $last_action = time(); + $last_action = $now; $filters = getFilters( $filter_parm ); } @@ -209,49 +196,12 @@ while( 1 ) sleep( $delay ); } -sub getDiskPercent -{ - my $command = "df ."; - my $df = qx( $command ); - my $space = -1; - if ( $df =~ /\s(\d+)%/ms ) - { - $space = $1; - } - return( $space ); -} - -sub getDiskBlocks -{ - my $command = "df ."; - my $df = qx( $command ); - my $space = -1; - if ( $df =~ /\s(\d+)\s+\d+\s+\d+%/ms ) - { - $space = $1; - } - return( $space ); -} - -sub getLoad -{ - my $command = "uptime ."; - my $uptime = qx( $command ); - my $load = -1; - if ( $uptime =~ /load average:\s+([\d.]+)/ms ) - { - $load = $1; - Info( "Load: $load" ); - } - return( $load ); -} - sub getFilters { my $filter_name = shift; my @filters; - my $sql = "select * from Filters where"; + my $sql = "SELECT * FROM Filters WHERE"; if ( $filter_name ) { $sql .= " Name = ? and"; @@ -260,269 +210,41 @@ sub getFilters { $sql .= " Background = 1 and"; } - $sql .= " (AutoArchive = 1 or AutoVideo = 1 or AutoUpload = 1 or AutoEmail = 1 or AutoMessage = 1 or AutoExecute = 1 or AutoDelete = 1) order by Name"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $sql .= "( AutoArchive = 1 + or AutoVideo = 1 + or AutoUpload = 1 + or AutoEmail = 1 + or AutoMessage = 1 + or AutoExecute = 1 + or AutoDelete = 1 + ) ORDER BY Name"; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); my $res; if ( $filter_name ) { - $res = $sth->execute( $filter_name ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + $res = $sth->execute( $filter_name ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); } else { - $res = $sth->execute() or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + $res = $sth->execute() + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); } FILTER: while( my $db_filter = $sth->fetchrow_hashref() ) { + my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter ); Debug( "Found filter '$db_filter->{Name}'\n" ); - my $filter_expr = jsonDecode( $db_filter->{Query} ); - my $sql = "select E.Id,E.MonitorId,M.Name as MonitorName,M.DefaultRate,M.DefaultScale,E.Name,E.Cause,E.Notes,E.StartTime,unix_timestamp(E.StartTime) as Time,E.Length,E.Frames,E.AlarmFrames,E.TotScore,E.AvgScore,E.MaxScore,E.Archived,E.Videoed,E.Uploaded,E.Emailed,E.Messaged,E.Executed from Events as E inner join Monitors as M on M.Id = E.MonitorId where not isnull(E.EndTime)"; - $db_filter->{Sql} = ''; + my $sql = $filter->Sql(); - if ( @{$filter_expr->{terms}} ) - { - for ( my $i = 0; $i < @{$filter_expr->{terms}}; $i++ ) - { - if ( exists($filter_expr->{terms}[$i]->{cnj}) ) - { - $db_filter->{Sql} .= " ".$filter_expr->{terms}[$i]->{cnj}." "; - } - if ( exists($filter_expr->{terms}[$i]->{obr}) ) - { - $db_filter->{Sql} .= " ".str_repeat( "(", $filter_expr->{terms}[$i]->{obr} )." "; - } - my $value = $filter_expr->{terms}[$i]->{val}; - my @value_list; - if ( $filter_expr->{terms}[$i]->{attr} ) - { - if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) - { - my ( $temp_attr_name ) = $filter_expr->{terms}[$i]->{attr} =~ /^Monitor(.+)$/; - $db_filter->{Sql} .= "M.".$temp_attr_name; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) - { - $db_filter->{Sql} .= "E.StartTime"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) - { - $db_filter->{Sql} .= "to_days( E.StartTime )"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) - { - $db_filter->{Sql} .= "extract( hour_second from E.StartTime )"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Weekday' ) - { - $db_filter->{Sql} .= "weekday( E.StartTime )"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskPercent' ) - { - $db_filter->{Sql} .= "zmDiskPercent"; - $db_filter->{HasDiskPercent} = !undef; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskBlocks' ) - { - $db_filter->{Sql} .= "zmDiskBlocks"; - $db_filter->{HasDiskBlocks} = !undef; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'SystemLoad' ) - { - $db_filter->{Sql} .= "zmSystemLoad"; - $db_filter->{HasSystemLoad} = !undef; - } - else - { - $db_filter->{Sql} .= "E.".$filter_expr->{terms}[$i]->{attr}; - } - - ( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/; - foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) - { - if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) - { - $value = "'$temp_value'"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Name' || $filter_expr->{terms}[$i]->{attr} eq 'Cause' || $filter_expr->{terms}[$i]->{attr} eq 'Notes' ) - { - $value = "'$temp_value'"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) - { - $value = DateTimeToSQL( $temp_value ); - if ( !$value ) - { - Error( "Error parsing date/time '$temp_value', skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - } - $value = "'$value'"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) - { - $value = DateTimeToSQL( $temp_value ); - if ( !$value ) - { - Error( "Error parsing date/time '$temp_value', skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - } - $value = "to_days( '$value' )"; - } - elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) - { - $value = DateTimeToSQL( $temp_value ); - if ( !$value ) - { - Error( "Error parsing date/time '$temp_value', skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - } - $value = "extract( hour_second from '$value' )"; - } - else - { - $value = $temp_value; - } - push( @value_list, $value ); - } - } - if ( $filter_expr->{terms}[$i]->{op} ) - { - if ( $filter_expr->{terms}[$i]->{op} eq '=~' ) - { - $db_filter->{Sql} .= " regexp $value"; - } - elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) - { - $db_filter->{Sql} .= " not regexp $value"; - } - elsif ( $filter_expr->{terms}[$i]->{op} eq '=[]' ) - { - $db_filter->{Sql} .= " in (".join( ",", @value_list ).")"; - } - elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) - { - $db_filter->{Sql} .= " not in (".join( ",", @value_list ).")"; - } - else - { - $db_filter->{Sql} .= " ".$filter_expr->{terms}[$i]->{op}." $value"; - } - } - if ( exists($filter_expr->{terms}[$i]->{cbr}) ) - { - $db_filter->{Sql} .= " ".str_repeat( ")", $filter_expr->{terms}[$i]->{cbr} )." "; - } - } - } - if ( $db_filter->{Sql} ) - { - $sql .= " and ( ".$db_filter->{Sql}." )"; - } - my @auto_terms; - if ( $db_filter->{AutoArchive} ) - { - push( @auto_terms, "E.Archived = 0" ) - } - if ( $db_filter->{AutoVideo} ) - { - push( @auto_terms, "E.Videoed = 0" ) - } - if ( $db_filter->{AutoUpload} ) - { - push( @auto_terms, "E.Uploaded = 0" ) - } - if ( $db_filter->{AutoEmail} ) - { - push( @auto_terms, "E.Emailed = 0" ) - } - if ( $db_filter->{AutoMessage} ) - { - push( @auto_terms, "E.Messaged = 0" ) - } - if ( $db_filter->{AutoExecute} ) - { - push( @auto_terms, "E.Executed = 0" ) - } - if ( @auto_terms ) - { - $sql .= " and ( ".join( " or ", @auto_terms )." )"; - } - if ( !$filter_expr->{sort_field} ) - { - $filter_expr->{sort_field} = 'StartTime'; - $filter_expr->{sort_asc} = 0; - } - my $sort_column = ''; - if ( $filter_expr->{sort_field} eq 'Id' ) - { - $sort_column = "E.Id"; - } - elsif ( $filter_expr->{sort_field} eq 'MonitorName' ) - { - $sort_column = "M.Name"; - } - elsif ( $filter_expr->{sort_field} eq 'Name' ) - { - $sort_column = "E.Name"; - } - elsif ( $filter_expr->{sort_field} eq 'StartTime' ) - { - $sort_column = "E.StartTime"; - } - elsif ( $filter_expr->{sort_field} eq 'Secs' ) - { - $sort_column = "E.Length"; - } - elsif ( $filter_expr->{sort_field} eq 'Frames' ) - { - $sort_column = "E.Frames"; - } - elsif ( $filter_expr->{sort_field} eq 'AlarmFrames' ) - { - $sort_column = "E.AlarmFrames"; - } - elsif ( $filter_expr->{sort_field} eq 'TotScore' ) - { - $sort_column = "E.TotScore"; - } - elsif ( $filter_expr->{sort_field} eq 'AvgScore' ) - { - $sort_column = "E.AvgScore"; - } - elsif ( $filter_expr->{sort_field} eq 'MaxScore' ) - { - $sort_column = "E.MaxScore"; - } - else - { - $sort_column = "E.StartTime"; - } - my $sort_order = $filter_expr->{sort_asc}?"asc":"desc"; - $sql .= " order by ".$sort_column." ".$sort_order; - if ( $filter_expr->{limit} ) - { - $sql .= " limit 0,".$filter_expr->{limit}; - } - Debug( "SQL:$sql\n" ); - $db_filter->{Sql} = $sql; - if ( $db_filter->{AutoExecute} ) - { - my $script = $db_filter->{AutoExecuteCmd}; - $script =~ s/\s.*$//; - if ( !-e $script ) - { - Error( "Auto execute script '$script' not found, skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - - } - elsif ( !-x $script ) - { - Error( "Auto execute script '$script' not executable, skipping filter '$db_filter->{Name}'\n" ); - next FILTER; - } - } - push( @filters, $db_filter ); + if ( ! $sql ) { + Error( "Error parsing Sql. skipping filter '$db_filter->{Name}'\n" ); + next FILTER; + } + push( @filters, $filter ); } $sth->finish(); - + Debug( "Got " . @filters . " filters" ); return( \@filters ); } @@ -540,43 +262,20 @@ sub checkFilter ($filter->{AutoExecute}?", execute":""). "\n" ); - my $sql = $filter->{Sql}; - - if ( $filter->{HasDiskPercent} ) - { - my $disk_percent = getDiskPercent(); - $sql =~ s/zmDiskPercent/$disk_percent/g; - } - if ( $filter->{HasDiskBlocks} ) - { - my $disk_blocks = getDiskBlocks(); - $sql =~ s/zmDiskBlocks/$disk_blocks/g; - } - if ( $filter->{HasSystemLoad} ) - { - my $load = getLoad(); - $sql =~ s/zmSystemLoad/$load/g; - } - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute(); - if ( !$res ) - { - Error( "Can't execute filter '$sql', ignoring: ".$sth->errstr() ); - return; - } - - while( my $event = $sth->fetchrow_hashref() ) - { + foreach my $event ( $filter->Execute() ) { Debug( "Checking event $event->{Id}\n" ); my $delete_ok = !undef; +$dbh->ping(); if ( $filter->{AutoArchive} ) { Info( "Archiving event $event->{Id}\n" ); # Do it individually to avoid locking up the table for new events my $sql = "update Events set Archived = 1 where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Error( "Can't execute '$sql': ".$sth->errstr() ); } if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) { @@ -617,21 +316,27 @@ sub checkFilter { if ( $delete_ok ) { - Info( "Deleting event $event->{Id}\n" ); + Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId}\n" ); # Do it individually to avoid locking up the table for new events my $sql = "delete from Events where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); if ( ! $Config{ZM_OPT_FAST_DELETE} ) { my $sql = "delete from Frames where EventId = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); $sql = "delete from Stats where EventId = ?"; - $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); deleteEventFiles( $event->{Id}, $event->{MonitorId} ); } @@ -642,7 +347,6 @@ sub checkFilter } } } - $sth->finish(); } sub generateVideo @@ -683,7 +387,8 @@ sub generateVideo $format = $ffmpeg_formats[0]; } - my $command = $Config{ZM_PATH_BIN}."/zmvideo.pl -e ".$event->{Id}." -r ".$rate." -s ".$scale." -f ".$format; + my $command = $Config{ZM_PATH_BIN}."/zmvideo.pl -e " + .$event->{Id}." -r ".$rate." -s ".$scale." -f ".$format; my $output = qx($command); chomp( $output ); my $status = $? >> 8; @@ -703,11 +408,13 @@ sub generateVideo else { my $sql = "update Events set Videoed = 1 where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); if ( wantarray() ) { - return( $format, sprintf( "%s/%s", getEventPath( $event ), $output ) ); + return( $format, sprintf( "%s/%s", getEventPath( $event ), $output ) ); } } return( 1 ); @@ -725,7 +432,15 @@ sub uploadArchFile } my $archFile = $event->{MonitorName}.'-'.$event->{Id}; - my $archImagePath = getEventPath( $event )."/".(( $Config{ZM_UPLOAD_ARCH_ANALYSE} )?'{*analyse,*capture}':'*capture').".jpg"; + my $archImagePath = getEventPath( $event ) + ."/" + .( + ( $Config{ZM_UPLOAD_ARCH_ANALYSE} ) + ? '{*analyse,*capture}' + : '*capture' + ) + .".jpg" + ; my @archImageFiles = glob($archImagePath); my $archLocPath; @@ -748,7 +463,10 @@ sub uploadArchFile $archError = 1; last; } - $member->desiredCompressionMethod( $Config{ZM_UPLOAD_ARCH_COMPRESS} ? &COMPRESSION_DEFLATED : &COMPRESSION_STORED ); + $member->desiredCompressionMethod( $Config{ZM_UPLOAD_ARCH_COMPRESS} + ? &COMPRESSION_DEFLATED + : &COMPRESSION_STORED + ); } if ( !$archError ) { @@ -777,7 +495,12 @@ sub uploadArchFile $archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile; Info( "Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n" ); - if ( $archError = !Archive::Tar->create_archive( $archLocPath, $Config{ZM_UPLOAD_ARCH_COMPRESS}, @archImageFiles ) ) + if ( $archError = !Archive::Tar->create_archive( + $archLocPath, + $Config{ZM_UPLOAD_ARCH_COMPRESS}, + @archImageFiles + ) + ) { Error( "Tar error: ".Archive::Tar->error()."\n " ); } @@ -792,42 +515,66 @@ sub uploadArchFile if ( $Config{ZM_UPLOAD_PROTOCOL} eq "ftp" ) { Info( "Uploading to ".$Config{ZM_UPLOAD_HOST}." using FTP\n" ); - my $ftp = Net::FTP->new( $Config{ZM_UPLOAD_HOST}, Timeout=>$Config{ZM_UPLOAD_TIMEOUT}, Passive=>$Config{ZM_UPLOAD_FTP_PASSIVE}, Debug=>$Config{ZM_UPLOAD_DEBUG} ); + my $ftp = Net::FTP->new( + $Config{ZM_UPLOAD_HOST}, + Timeout=>$Config{ZM_UPLOAD_TIMEOUT}, + Passive=>$Config{ZM_UPLOAD_FTP_PASSIVE}, + Debug=>$Config{ZM_UPLOAD_DEBUG} + ); if ( !$ftp ) { Error( "Can't create FTP connection: $@" ); return( 0 ); } - $ftp->login( $Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS} ) or Error( "FTP - Can't login" ); - $ftp->binary() or Error( "FTP - Can't go binary" ); - $ftp->cwd( $Config{ZM_UPLOAD_REM_DIR} ) or Error( "FTP - Can't cwd" ) if ( $Config{ZM_UPLOAD_REM_DIR} ); - $ftp->put( $archLocPath ) or Error( "FTP - Can't upload '$archLocPath'" ); - $ftp->quit() or Error( "FTP - Can't quit" ); + $ftp->login( $Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS} ) + or Error( "FTP - Can't login" ); + $ftp->binary() + or Error( "FTP - Can't go binary" ); + $ftp->cwd( $Config{ZM_UPLOAD_REM_DIR} ) + or Error( "FTP - Can't cwd" ) + if ( $Config{ZM_UPLOAD_REM_DIR} ); + $ftp->put( $archLocPath ) + or Error( "FTP - Can't upload '$archLocPath'" ); + $ftp->quit() + or Error( "FTP - Can't quit" ); } else { my $host = $Config{ZM_UPLOAD_HOST}; - $host .= ":".$Config{ZM_UPLOAD_PORT} if $Config{ZM_UPLOAD_PORT}; + $host .= ":".$Config{ZM_UPLOAD_PORT} + if $Config{ZM_UPLOAD_PORT}; Info( "Uploading to ".$host." using SFTP\n" ); my %sftpOptions = ( host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER} ); - $sftpOptions{password} = $Config{ZM_UPLOAD_PASS} if $Config{ZM_UPLOAD_PASS}; - $sftpOptions{port} = $Config{ZM_UPLOAD_PORT} if $Config{ZM_UPLOAD_PORT}; - $sftpOptions{timeout} = $Config{ZM_UPLOAD_TIMEOUT} if $Config{ZM_UPLOAD_TIMEOUT}; - $sftpOptions{more} = [ '-o'=>'StrictHostKeyChecking=no' ]; - $Net::SFTP::Foreign::debug = -1 if $Config{ZM_UPLOAD_DEBUG}; + $sftpOptions{password} = $Config{ZM_UPLOAD_PASS} + if $Config{ZM_UPLOAD_PASS}; + $sftpOptions{port} = $Config{ZM_UPLOAD_PORT} + if $Config{ZM_UPLOAD_PORT}; + $sftpOptions{timeout} = $Config{ZM_UPLOAD_TIMEOUT} + if $Config{ZM_UPLOAD_TIMEOUT}; + my @more_ssh_args; + push @more_ssh_args, ['-o'=>'StrictHostKeyChecking=no'] + if ! $Config{ZM_UPLOAD_STRICT}; + push @more_ssh_args, ['-v'] + if $Config{ZM_UPLOAD_DEBUG}; + $sftpOptions{more} = [@more_ssh_args]; my $sftp = Net::SFTP::Foreign->new( $Config{ZM_UPLOAD_HOST}, %sftpOptions ); if ( $sftp->error ) { Error( "Can't create SFTP connection: ".$sftp->error ); return( 0 ); } - $sftp->setcwd( $Config{ZM_UPLOAD_REM_DIR} ) or Error( "SFTP - Can't setcwd: ".$sftp->error ) if $Config{ZM_UPLOAD_REM_DIR}; - $sftp->put( $archLocPath, $archFile ) or Error( "SFTP - Can't upload '$archLocPath': ".$sftp->error ); + $sftp->setcwd( $Config{ZM_UPLOAD_REM_DIR} ) + or Error( "SFTP - Can't setcwd: ".$sftp->error ) + if $Config{ZM_UPLOAD_REM_DIR}; + $sftp->put( $archLocPath, $archFile ) + or Error( "SFTP - Can't upload '$archLocPath': ".$sftp->error ); } unlink( $archLocPath ); my $sql = "update Events set Uploaded = 1 where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); } return( 1 ); } @@ -848,9 +595,28 @@ sub substituteTags if ( $need_monitor ) { my $db_now = strftime( "%Y-%m-%d %H:%M:%S", localtime() ); - my $sql = "select M.Id, count(E.Id) as EventCount, count(if(E.Archived,1,NULL)) as ArchEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 1 HOUR && E.Archived = 0,1,NULL)) as HourEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 1 DAY && E.Archived = 0,1,NULL)) as DayEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 7 DAY && E.Archived = 0,1,NULL)) as WeekEventCount, count(if(E.StartTime>'$db_now' - INTERVAL 1 MONTH && E.Archived = 0,1,NULL)) as MonthEventCount from Monitors as M left join Events as E on E.MonitorId = M.Id where MonitorId = ? group by E.MonitorId order by Id"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{MonitorId} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sql = "SELECT + M.Id, + count(E.Id) as EventCount, + count(if(E.Archived,1,NULL)) + as ArchEventCount, + count(if(E.StartTime>'$db_now' - INTERVAL 1 HOUR && E.Archived = 0,1,NULL)) + as HourEventCount, + count(if(E.StartTime>'$db_now' - INTERVAL 1 DAY && E.Archived = 0,1,NULL)) + as DayEventCount, + count(if(E.StartTime>'$db_now' - INTERVAL 7 DAY && E.Archived = 0,1,NULL)) + as WeekEventCount, + count(if(E.StartTime>'$db_now' - INTERVAL 1 MONTH && E.Archived = 0,1,NULL)) + as MonthEventCount + FROM Monitors as M LEFT JOIN Events as E on E.MonitorId = M.Id + WHERE MonitorId = ? + GROUP BY E.MonitorId + ORDER BY Id" + ; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{MonitorId} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); $monitor = $sth->fetchrow_hashref(); $sth->finish(); return() if ( !$monitor ); @@ -863,9 +629,14 @@ sub substituteTags my $max_alarm_score = 0; if ( $need_images ) { - my $sql = "select * from Frames where EventId = ? and Type = 'Alarm' order by FrameId"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sql = "SELECT * FROM Frames + WHERE EventId = ? AND Type = 'Alarm' + ORDER BY FrameId" + ; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); while( my $frame = $sth->fetchrow_hashref() ) { if ( !$first_alarm_frame ) @@ -913,14 +684,34 @@ sub substituteTags $text =~ s/%EPIM%/$url?view=frame&mid=$event->{MonitorId}&eid=$event->{Id}&fid=$max_alarm_frame->{FrameId}/g; if ( $attachments_ref && $text =~ s/%EI1%//g ) { - push( @$attachments_ref, { type=>"image/jpeg", path=>sprintf( "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", getEventPath( $event ), $first_alarm_frame->{FrameId} ) } ); + push( @$attachments_ref, + { + type=>"image/jpeg", + path=>sprintf( + "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", + getEventPath( $event ), + $first_alarm_frame->{FrameId} + ) + } + ); } if ( $attachments_ref && $text =~ s/%EIM%//g ) { # Don't attach the same image twice - if ( !@$attachments_ref || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) ) + if ( !@$attachments_ref + || ($first_alarm_frame->{FrameId} != $max_alarm_frame->{FrameId} ) + ) { - push( @$attachments_ref, { type=>"image/jpeg", path=>sprintf( "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", getEventPath( $event ), $max_alarm_frame->{FrameId} ) } ); + push( @$attachments_ref, + { + type=>"image/jpeg", + path=>sprintf( + "%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg", + getEventPath( $event ), + $max_alarm_frame->{FrameId} + ) + } + ); } } } @@ -948,7 +739,7 @@ sub substituteTags $text =~ s/%FN%/$filter->{Name}/g; ( my $filter_name = $filter->{Name} ) =~ s/ /+/g; $text =~ s/%FP%/$url?view=filter&mid=$event->{MonitorId}&filter_name=$filter_name/g; - + return( $text ); } @@ -1004,10 +795,32 @@ sub sendEmail Disposition => "attachment" ); } + if ( $Config{ZM_SSMTP_MAIL} ){ + + my $ssmtp_location = $Config{ZM_SSMTP_PATH}; + + if( ! $ssmtp_location ){ + + $ssmtp_location = qx('which ssmtp'); + + if ( logDebugging() ) + { + Debug( "which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message\n" ); + } + + } + + $mail->send( 'sendmail', $ssmtp_location, $Config{ZM_EMAIL_ADDRESS} ); + + }else{ + + MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + $mail->send(); + } ### Send the Message - MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 ); - $mail->send(); - } + #MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 ); + #$mail->send(); + } else { my $mail = MIME::Entity->build( @@ -1040,8 +853,10 @@ sub sendEmail Info( "Notification email sent\n" ); } my $sql = "update Events set Emailed = 1 where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); return( 1 ); } @@ -1101,7 +916,7 @@ sub sendMessage ### Send the Message MIME::Lite->send( "smtp", $Config{ZM_EMAIL_HOST}, Timeout=>60 ); $mail->send(); - } + } else { my $mail = MIME::Entity->build( @@ -1121,7 +936,9 @@ sub sendMessage Encoding => "base64" ); } - $mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, MailFrom => $Config{ZM_FROM_EMAIL} ); + $mail->smtpsend( Host => $Config{ZM_EMAIL_HOST}, + MailFrom => $Config{ZM_FROM_EMAIL} + ); } }; if ( $@ ) @@ -1134,8 +951,10 @@ sub sendMessage Info( "Notification message sent\n" ); } my $sql = "update Events set Messaged = 1 where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); return( 1 ); } @@ -1166,8 +985,10 @@ sub executeCommand else { my $sql = "update Events set Executed = 1 where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $event->{Id} ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $event->{Id} ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); } return( 1 ); } diff --git a/scripts/zmpkg.pl.in b/scripts/zmpkg.pl.in index 022d1aaef..42de011fe 100644 --- a/scripts/zmpkg.pl.in +++ b/scripts/zmpkg.pl.in @@ -20,10 +20,21 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script is used to start and stop the ZoneMinder package primarily to -# allow command line control for automatic restart on reboot (see zm script) -# + +=head1 NAME + +zmpkg.pl - ZoneMinder Package Control Script + +=head1 SYNOPSIS + + zmpkg.pl {start|stop|restart|status|logrot|'state'|version} + +=head1 DESCRIPTION + +This script is used to start and stop the ZoneMinder package primarily to +allow command line control for automatic restart on reboot (see zm script) + +=cut use strict; use bytes; @@ -38,53 +49,68 @@ use ZoneMinder; use DBI; use POSIX; use Time::HiRes qw/gettimeofday/; +use autouse 'Pod::Usage'=>qw(pod2usage); # Detaint our environment -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; +my $store_state=""; # PP - will remember state name passed logInit(); -my $command = $ARGV[0]; +my $command = $ARGV[0]||''; +if ( $command eq 'version' ) { + print ZoneMinder::Base::ZM_VERSION . "\n"; + exit(0); +} my $state; -my $dbh = zmDbConnect(); +my $dbh; -if ( !$command || $command !~ /^(?:start|stop|restart|status|logrot)$/ ) +if ( !$command || $command !~ /^(?:start|stop|restart|status|logrot|version)$/ ) { - if ( $command ) - { - # Check to see if it's a valid run state - my $sql = 'select * from States where Name = ?'; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $command ) or Fatal( "Can't execute: ".$sth->errstr() ); - if ( $state = $sth->fetchrow_hashref() ) - { - $state->{Name} = $command; - $state->{Definitions} = []; - foreach( split( /,/, $state->{Definition} ) ) - { - my ( $id, $function, $enabled ) = split( /:/, $_ ); - push( @{$state->{Definitions}}, { Id=>$id, Function=>$function, Enabled=>$enabled } ); - } - $command = 'state'; - } - else - { - $command = undef; - } - } - if ( !$command ) - { - print( "Usage: zmpkg.pl \n" ); - exit( -1 ); - } + if ( $command ) + { + $dbh = zmDbConnect(); + # Check to see if it's a valid run state + my $sql = 'select * from States where Name = ?'; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $command ) + or Fatal( "Can't execute: ".$sth->errstr() ); + if ( $state = $sth->fetchrow_hashref() ) + { + $state->{Name} = $command; + $state->{Definitions} = []; + foreach( split( /,/, $state->{Definition} ) ) + { + my ( $id, $function, $enabled ) = split( /:/, $_ ); + push( @{$state->{Definitions}}, + { Id=>$id, Function=>$function, Enabled=>$enabled } + ); + } + $store_state=$command; # PP - Remember the name that was passed to search in DB + $command = 'state'; + } + else + { + $command = undef; + } + } + if ( !$command ) + { + pod2usage(-exitstatus => -1); + } } +$dbh = zmDbConnect() if ! $dbh; +# PP - Sane state check +isActiveSanityCheck(); # Move to the right place -chdir( $Config{ZM_PATH_WEB} ) or Fatal( "Can't chdir to '".$Config{ZM_PATH_WEB}."': $!" ); +chdir( $Config{ZM_PATH_WEB} ) + or Fatal( "Can't chdir to '".$Config{ZM_PATH_WEB}."': $!" ); my $dbg_id = ""; @@ -94,212 +120,317 @@ my $retval = 0; if ( $command eq "state" ) { - Info( "Updating DB: $state->{Name}\n" ); - my $sql = "select * from Monitors order by Id asc"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); - while( my $monitor = $sth->fetchrow_hashref() ) - { - foreach my $definition ( @{$state->{Definitions}} ) - { - if ( $monitor->{Id} =~ /^$definition->{Id}$/ ) - { - $monitor->{NewFunction} = $definition->{Function}; - $monitor->{NewEnabled} = $definition->{Enabled}; - } - } - #next if ( !$monitor->{NewFunction} ); - $monitor->{NewFunction} = 'None' if ( !$monitor->{NewFunction} ); - $monitor->{NewEnabled} = 0 if ( !$monitor->{NewEnabled} ); - if ( $monitor->{Function} ne $monitor->{NewFunction} || $monitor->{Enabled} ne $monitor->{NewEnabled} ) - { - my $sql = "update Monitors set Function = ?, Enabled = ? where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute( $monitor->{NewFunction}, $monitor->{NewEnabled}, $monitor->{Id} ) or Fatal( "Can't execute: ".$sth->errstr() ); - } - } - $sth->finish(); + Info( "Updating DB: $state->{Name}\n" ); + my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=? ORDER BY Id ASC' : 'SELECT * FROM Monitors ORDER BY Id ASC'; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID}: () ) + or Fatal( "Can't execute: ".$sth->errstr() ); + while( my $monitor = $sth->fetchrow_hashref() ) + { + foreach my $definition ( @{$state->{Definitions}} ) + { + if ( $monitor->{Id} =~ /^$definition->{Id}$/ ) + { + $monitor->{NewFunction} = $definition->{Function}; + $monitor->{NewEnabled} = $definition->{Enabled}; + } + } + #next if ( !$monitor->{NewFunction} ); + $monitor->{NewFunction} = 'None' + if ( !$monitor->{NewFunction} ); + $monitor->{NewEnabled} = 0 + if ( !$monitor->{NewEnabled} ); + if ( $monitor->{Function} ne $monitor->{NewFunction} + || $monitor->{Enabled} ne $monitor->{NewEnabled} + ) + { + my $sql = "update Monitors set Function = ?, Enabled = ? where Id = ?"; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $monitor->{NewFunction}, $monitor->{NewEnabled}, $monitor->{Id} ) + or Fatal( "Can't execute: ".$sth->errstr() ); + } + } + $sth->finish(); + + # PP - Now mark a specific state as active + resetStates(); + Info ("Marking $store_state as Enabled"); + $sql = "update States set IsActive = '1' where Name = ?"; + $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute( $store_state ) + or Fatal( "Can't execute: ".$sth->errstr() ); - $command = "restart"; + # PP - zero out other states isActive + $command = "restart"; } # Check if we are running systemd and if we have been called by the system if ( $command =~ /^(start|stop|restart)$/ ) { - # We have to detaint to keep perl from complaining - $command = $1; + # We have to detaint to keep perl from complaining + $command = $1; - if ( systemdRunning() && !calledBysystem() ) { - qx(@BINDIR@/zmsystemctl.pl $command); - $command = ""; - } + + if ( systemdRunning() && !calledBysystem() ) { + qx(@BINDIR@/zmsystemctl.pl $command); + $command = ""; + } } if ( $command =~ /^(?:stop|restart)$/ ) { - my $status = runCommand( "zmdc.pl check" ); + my $status = runCommand( "zmdc.pl check" ); - if ( $status eq "running" ) - { - runCommand( "zmdc.pl shutdown" ); - zmMemTidy(); - } - else - { - $retval = 1; - } + if ( $status eq "running" ) + { + runCommand( "zmdc.pl shutdown" ); + zmMemTidy(); + } + else + { + $retval = 1; + } } #runCommand( "zmupdate.pl -f" ); if ( $command =~ /^(?:start|restart)$/ ) { - my $status = runCommand( "zmdc.pl check" ); + my $status = runCommand( "zmdc.pl check" ); - if ( $status eq "stopped" ) - { - if ( $Config{ZM_DYN_DB_VERSION} and ( $Config{ZM_DYN_DB_VERSION} ne ZM_VERSION ) ) + if ( $status eq "stopped" ) + { + if ( $Config{ZM_DYN_DB_VERSION} + and ( $Config{ZM_DYN_DB_VERSION} ne ZM_VERSION ) + ) { - Fatal( "Version mismatch, system is version ".ZM_VERSION.", database is ".$Config{ZM_DYN_DB_VERSION}.", please run zmupdate.pl to update." ); + Fatal( "Version mismatch, system is version ".ZM_VERSION + .", database is ".$Config{ZM_DYN_DB_VERSION} + .", please run zmupdate.pl to update." + ); exit( -1 ); } # Recreate the temporary directory if it's been wiped - verifyFolder("@ZM_TMPDIR@"); + verifyFolder("@ZM_TMPDIR@"); # Recreate the run directory if it's been wiped - verifyFolder("@ZM_RUNDIR@"); + verifyFolder("@ZM_RUNDIR@"); # Recreate the sock directory if it's been wiped - verifyFolder("@ZM_SOCKDIR@"); + verifyFolder("@ZM_SOCKDIR@"); - zmMemTidy(); - runCommand( "zmdc.pl startup" ); + zmMemTidy(); + runCommand( "zmdc.pl startup" ); - my $sql = "select * from Monitors"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); - while( my $monitor = $sth->fetchrow_hashref() ) - { - if ( $monitor->{Function} ne 'None' ) - { - if ( $monitor->{Type} ne 'Extdect' ) - { - if ( $monitor->{Type} eq 'Local' ) - { - runCommand( "zmdc.pl start zmc -d $monitor->{Device}" ); - } - else - { - runCommand( "zmdc.pl start zmc -m $monitor->{Id}" ); - } + if ( $Config{ZM_SERVER_ID} ) { + Info( "Multi-server configuration detected. Starting up services for server $Config{ZM_SERVER_ID}\n"); + } else { + Info( "Single server configuration detected. Starting up services." ); } - if ( $monitor->{Function} ne 'Monitor' ) - { - if ( $Config{ZM_OPT_FRAME_SERVER} ) - { - runCommand( "zmdc.pl start zmf -m $monitor->{Id}" ); - } - runCommand( "zmdc.pl start zma -m $monitor->{Id}" ); - } - if ( $Config{ZM_OPT_CONTROL} ) - { - if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) - { - if ( $monitor->{Controllable} && $monitor->{TrackMotion} ) - { - runCommand( "zmdc.pl start zmtrack.pl -m $monitor->{Id}" ); - } - } - } - } - } - $sth->finish(); - # This is now started unconditionally - runCommand( "zmdc.pl start zmfilter.pl" ); - if ( $Config{ZM_RUN_AUDIT} ) - { - runCommand( "zmdc.pl start zmaudit.pl -c" ); - } - if ( $Config{ZM_OPT_TRIGGERS} ) - { - runCommand( "zmdc.pl start zmtrigger.pl" ); - } - if ( $Config{ZM_OPT_X10} ) - { - runCommand( "zmdc.pl start zmx10.pl -c start" ); - } - runCommand( "zmdc.pl start zmwatch.pl" ); - if ( $Config{ZM_CHECK_FOR_UPDATES} ) - { - runCommand( "zmdc.pl start zmupdate.pl -c" ); - } - } - else - { - $retval = 1; - } + my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors'; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () ) + or Fatal( "Can't execute: ".$sth->errstr() ); + while( my $monitor = $sth->fetchrow_hashref() ) + { + if ( $monitor->{Function} ne 'None' ) + { + if ( $monitor->{Type} eq 'Local' ) + { + runCommand( "zmdc.pl start zmc -d $monitor->{Device}" ); + } + else + { + runCommand( "zmdc.pl start zmc -m $monitor->{Id}" ); + } + if ( $monitor->{Function} ne 'Monitor' ) + { + if ( $Config{ZM_OPT_FRAME_SERVER} ) + { + runCommand( "zmdc.pl start zmf -m $monitor->{Id}" ); + } + runCommand( "zmdc.pl start zma -m $monitor->{Id}" ); + } + if ( $Config{ZM_OPT_CONTROL} ) + { + if ( $monitor->{Function} eq 'Modect' || $monitor->{Function} eq 'Mocord' ) + { + if ( $monitor->{Controllable} && $monitor->{TrackMotion} ) + { + runCommand( "zmdc.pl start zmtrack.pl -m $monitor->{Id}" ); + } + } + } + } + } + $sth->finish(); + + # This is now started unconditionally + runCommand( "zmdc.pl start zmfilter.pl" ); + if ( $Config{ZM_RUN_AUDIT} ) + { + runCommand( "zmdc.pl start zmaudit.pl -c" ); + } + if ( $Config{ZM_OPT_TRIGGERS} ) + { + runCommand( "zmdc.pl start zmtrigger.pl" ); + } + if ( $Config{ZM_OPT_X10} ) + { + runCommand( "zmdc.pl start zmx10.pl -c start" ); + } + runCommand( "zmdc.pl start zmwatch.pl" ); + if ( $Config{ZM_CHECK_FOR_UPDATES} ) + { + runCommand( "zmdc.pl start zmupdate.pl -c" ); + } + if ( $Config{ZM_TELEMETRY_DATA} ) + { + runCommand( "zmdc.pl start zmtelemetry.pl" ); + } + } + else + { + $retval = 1; + } } if ( $command eq "status" ) { - my $status = runCommand( "zmdc.pl check" ); + my $status = runCommand( "zmdc.pl check" ); - print( STDOUT $status."\n" ); + print( STDOUT $status."\n" ); } if ( $command eq "logrot" ) { - runCommand( "zmdc.pl logrot" ); + runCommand( "zmdc.pl logrot" ); } exit( $retval ); +# PP - Make sure isActive is on and only one +sub isActiveSanityCheck +{ + + Info ("Sanity checking States table..."); + $dbh = zmDbConnect() if ! $dbh; + + # PP - First, make sure default exists and there is only one + my $sql = "select Name from States where Name = 'default'"; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() + or Fatal( "Can't execute: ".$sth->errstr() ); + + if ($sth->rows != 1) # PP - no row, or too many rows. Either case is an error + { + Info( "Fixing States table - either no default state or duplicate default states" ); + $sql = "delete from States where Name = 'default'"; + $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute() + or Fatal( "Can't execute: ".$sth->errstr() ); + $sql = "insert into States (Name,Definition,IsActive) VALUES ('default','','1');"; + $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute() + or Fatal( "Can't execute: ".$sth->errstr() ); + } + + + # PP - Now make sure no two states have IsActive=1 + $sql = "select Name from States where IsActive = '1'"; + $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute() + or Fatal( "Can't execute: ".$sth->errstr() ); + + if ( $sth->rows != 1 ) + { + Info( "Fixing States table so only one run state is active" ); + resetStates(); + $sql = "update States set IsActive='1' WHERE Name='default'"; + $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute() + or Fatal( "Can't execute: ".$sth->errstr() ); + + + } +} + + +# PP - zeroes out isActive for all states +sub resetStates +{ + $dbh = zmDbConnect() if ! $dbh; + my $sql = "update States set IsActive = '0'"; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() + or Fatal( "Can't execute: ".$sth->errstr() ); + +} + sub systemdRunning { - my $result = 0; + my $result = 0; - my $output = qx(ps -o comm="" 1); - chomp( $output ); + my $output = qx(ps -o comm="" -p 1); + chomp( $output ); - if ($output =~ /systemd/) { - $result = 1; - } + if ($output =~ /systemd/) { + $result = 1; + } - return $result; + return $result; } sub calledBysystem { - my $result = 0; - my $ppid = getppid(); + my $result = 0; + my $ppid = getppid(); - my $output = qx(ps -o comm="" $ppid); - chomp( $output ); + my $output = qx(ps -o comm="" -p $ppid); + chomp( $output ); - if ($output =~ /^(?:systemd|init)$/) { - $result = 1; - } + if ($output =~ /^(?:systemd|init)$/) { + $result = 1; + } - return $result; + return $result; } sub verifyFolder { - my $folder = shift; + my $folder = shift; # Recreate the temporary directory if it's been wiped if ( !-e $folder ) { Debug( "Recreating directory '$folder'" ); - mkdir( "$folder", 0774 ) or Fatal( "Can't create missing temporary directory '$folder': $!" ); + mkdir( "$folder", 0774 ) + or Fatal( "Can't create missing temporary directory '$folder': $!" ); my ( $runName ) = getpwuid( $> ); if ( $runName ne $Config{ZM_WEB_USER} ) { - # Not running as web user, so should be root in which case chown the directory - my ( $webName, $webPass, $webUid, $webGid ) = getpwnam( $Config{ZM_WEB_USER} ) or Fatal( "Can't get user details for web user '".$Config{ZM_WEB_USER}."': $!" ); - chown( $webUid, $webGid, "$folder" ) or Fatal( "Can't change ownership of directory '$folder' to '".$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!" ); + # Not running as web user, so should be root in which case + # chown the directory + my ( $webName, $webPass, $webUid, $webGid ) = getpwnam( $Config{ZM_WEB_USER} ) + or Fatal( "Can't get user details for web user '" + .$Config{ZM_WEB_USER}."': $!" + ); + chown( $webUid, $webGid, "$folder" ) + or Fatal( "Can't change ownership of directory '$folder' to '" + .$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!" + ); } } } diff --git a/scripts/zmsystemctl.pl.in b/scripts/zmsystemctl.pl.in index cb3eb74ee..75acae242 100644 --- a/scripts/zmsystemctl.pl.in +++ b/scripts/zmsystemctl.pl.in @@ -2,7 +2,7 @@ # # ========================================================================== # -# ZoneMinder Update Script, $Date$, $Revision$ +# ZoneMinder systemctl wrapper, $Date$, $Revision$ # Copyright (C) 2001-2008 Philip Coombes # # This program is free software; you can redistribute it and/or @@ -20,25 +20,44 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This is a wrapper script that allows zoneminder to start and stop itself -# in a manner that keeps it in-sync with systemd. This script is intended -# to be called internally by zoneminder and may not give the desired results -# if run from the command line. -# + +=head1 NAME + +zmsystemctl.pl - ZoneMinder systemctl wrapper + +=head1 SYNOPSIS + + zmsystemctl.pl {start|stop|restart|version} + +=head1 DESCRIPTION + +This script allows an unpriveledged user to start, stop, or restart the +zoneminder service on a system running systemd. It does this by redirecting +commands through pkexec, which checks the available polkit policy files. The +default policy file grants the system web account user permission. This can be +changed or expanded by modifying the policy file. See man polkit for details. + +=head1 SEE ALSO + +polkit(8), pkexec(1) + +=cut use warnings; use strict; use bytes; +use autouse 'Pod::Usage'=>qw(pod2usage); @EXTRA_PERL_LIB@ use ZoneMinder::Logger qw(:all); my $command = $ARGV[0]; -if ( (scalar(@ARGV) == 1) && ($command =~ /^(start|stop|restart)$/ )) { - $command = $1; +if ( (scalar(@ARGV) == 1) + && ($command =~ /^(start|stop|restart|version)$/ ) +){ + $command = $1; } else { - die(" USAGE: zmsystemctl.pl \n"); + pod2usage(-exitstatus => -1); } my $path = qx(which systemctl); @@ -46,7 +65,7 @@ chomp($path); my $status = $? >> 8; if ( !$path || $status ) { - Fatal( "Unable to determine systemctl executable. Is systemd in use?" ); + Fatal( "Unable to determine systemctl executable. Is systemd in use?" ); } Info( "Redirecting command through systemctl\n" ); diff --git a/scripts/zmtelemetry.pl.in b/scripts/zmtelemetry.pl.in new file mode 100644 index 000000000..5a5328829 --- /dev/null +++ b/scripts/zmtelemetry.pl.in @@ -0,0 +1,343 @@ +#!/usr/bin/perl -w +# +# ========================================================================== +# +# ZoneMinder Update Script, $Date$, $Revision$ +# Copyright (C) 2001-2008 Philip Coombes +# +# 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. +# +# ========================================================================== + +=head1 NAME + +zmtelemetry.pl - Send usage information to the ZoneMinder development team + +=head1 SYNOPSIS + + zmtelemetry.pl + +=head1 DESCRIPTION + +This script collects usage information of the local system and sends it to the +ZoneMinder development team. This data will be used to determine things like +who and where our customers are, how big their systems are, the underlying +hardware and operating system, etc. This is being done for the sole purpoase of +creating a better product for our target audience. This script is intended to +be completely transparent to the end user, and can be disabled from the web +console under Options. + +=head1 OPTIONS + +none currently + +=cut +use strict; +use bytes; + +@EXTRA_PERL_LIB@ +use ZoneMinder; +use DBI; +use Getopt::Long; +use autouse 'Pod::Usage'=>qw(pod2usage); +use LWP::UserAgent; +use Sys::MemInfo qw(totalmem); +use Sys::CPU qw(cpu_count); +use POSIX qw(strftime uname); + +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; +$ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; +delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; + +use constant CHECK_INTERVAL => (14*24*60*60); # Interval between version checks + +# Setting these as contants for now. +# Alternatively, we can put these in the dB and then retrieve using the Config hash. +use constant ZM_TELEMETRY_SERVER_ENDPOINT => 'https://zmanon:2b2d0b4skps@telemetry.zoneminder.com/zmtelemetry/testing5'; + +if ( $Config{ZM_TELEMETRY_DATA} ) +{ + print( "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); + + my $lastCheck = $Config{ZM_TELEMETRY_LAST_UPLOAD}; + + while( 1 ) { + my $now = time(); + if ( ($now-$lastCheck) > CHECK_INTERVAL ) { + Info( "Collecting data to send to ZoneMinder Telemetry server." ); + my $dbh = zmDbConnect(); + # Build the telemetry hash + # We should keep *BSD systems in mind when calling system commands + my %telemetry; + $telemetry{uuid} = getUUID($dbh); + $telemetry{ip} = getIP(); + $telemetry{timestamp} = strftime( "%Y-%m-%dT%H:%M:%S%z", localtime() ); + $telemetry{monitor_count} = countQuery($dbh,"Monitors"); + $telemetry{event_count} = countQuery($dbh,"Events"); + $telemetry{architecture} = runSysCmd("uname -p"); + ($telemetry{kernel}, $telemetry{distro}, $telemetry{version}) = getDistro(); + $telemetry{zm_version} = ZoneMinder::Base::ZM_VERSION; + $telemetry{system_memory} = totalmem(); + $telemetry{processor_count} = cpu_count(); + $telemetry{monitors} = getMonitorRef($dbh); + + Info( "Sending data to ZoneMinder Telemetry server." ); + + my $result = jsonEncode( \%telemetry ); + + if ( sendData($result) ) { + $lastCheck = $now; + + my $sql = "update Config set Value = ? where Name = 'ZM_TELEMETRY_LAST_UPLOAD'"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( "$lastCheck" ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + } + } + sleep( 3600 ); + } + print( "Update agent exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); +} + +############### +# SUBROUTINES # +############### + +# Find, verify, then run the supplied system command +sub runSysCmd { + my $msg = shift; + my @arguments = split(/ /,$msg); + chomp($arguments[0]); + my $path = qx( which $arguments[0] ); + + my $status = $? >> 8; + my $result = ""; + if ( !$path || $status ) { + Warning( "Cannot find the $arguments[0] executable." ); + } else { + chomp($path); + $arguments[0] = $path; + my $cmd = join(" ",@arguments); + $result = qx( $cmd ); + chomp($result); + } + +return $result; +} + +# Upload message data to ZoneMinder telemetry server +sub sendData { + my $msg = shift; + + my $ua = LWP::UserAgent->new; + my $server_endpoint = ZM_TELEMETRY_SERVER_ENDPOINT; + + if ( $Config{ZM_UPDATE_CHECK_PROXY} ) { + $ua->proxy( "https", $Config{ZM_UPDATE_CHECK_PROXY} ); + } + + Debug("Posting telemetry data to: $server_endpoint"); + + # set custom HTTP request header fields + my $req = HTTP::Request->new(POST => $server_endpoint); + $req->header('content-type' => 'application/x-www-form-urlencoded'); + $req->header('content-length' => length($msg)); + $req->header('connection' => 'Close'); + + $req->content($msg); + + my $resp = $ua->request($req); + my $resp_msg = $resp->decoded_content; + my $resp_code = $resp->code; + if ($resp->is_success) { + Info("Telemetry data uploaded successfully."); + Debug("Telemetry server upload success response message: $resp_msg"); + } else { + Warning("Telemetry server returned HTTP POST error code: $resp_code"); + Debug("Telemetry server upload failure response message: $resp_msg"); + } +return $resp->is_success; +} + +# Retrieves the UUID from the database. Creates a new UUID if one does not exist. +sub getUUID { + my $dbh = shift; + my $uuid= ""; + + # Verify the current UUID is valid and not nil + if (( $Config{ZM_TELEMETRY_UUID} =~ /([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i ) && ( $Config{ZM_TELEMETRY_UUID} ne "00000000-0000-0000-0000-000000000000" )) { + $uuid = $Config{ZM_TELEMETRY_UUID}; + } else { + my $sql = "SELECT uuid()"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + $uuid = $Config{ZM_TELEMETRY_UUID} = $sth->fetchrow_array(); + $sth->finish(); + + $sql = "UPDATE Config set Value = ? WHERE Name = 'ZM_TELEMETRY_UUID'"; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + $res = $sth->execute( "$uuid" ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + } + Debug("Using UUID of: $uuid"); + +return $uuid; +} + +# Retrieves the local server's external IP address +sub getIP { + my $ipaddr = "0.0.0.0"; + my $ua = LWP::UserAgent->new; + my $server_endpoint = "https://wiki.zoneminder.com/ip.php"; + + my $req = HTTP::Request->new(GET => $server_endpoint); + my $resp = $ua->request($req); + + if ($resp->is_success) { + $ipaddr = $resp->decoded_content; + } + + Debug("Found external ip address of: $ipaddr"); + +return $ipaddr; +} + +# As the name implies, just your average mysql count query +sub countQuery { + my $dbh = shift; + my $table = shift; + + my $sql = "SELECT count(*) FROM $table"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + my $count = $sth->fetchrow_array(); + $sth->finish(); + +return $count +} + +# Returns a reference to an array of hashes containing data from all monitors +sub getMonitorRef { + my $dbh = shift; + + my $sql = "SELECT Id,Name,Type,Function,Width,Height,Colours,MaxFPS,AlarmMaxFPS FROM Monitors"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + my $arrayref = $sth->fetchall_arrayref({}); + +return $arrayref; +} + +sub getDistro { + my $kernel = ""; + my $distro = ""; + my $version = ""; + my @uname = uname(); + + if ( $uname[0] =~ /Linux/ ) { + Debug("Linux distro detected."); + ($kernel, $distro, $version) = linuxDistro(); + } elsif ( $uname[0] =~ /.*BSD/ ) { + Debug("BSD distro detected."); + $kernel = $uname[3]; + $distro = $uname[0]; + $version = $uname[2]; + } elsif ( $uname[0] =~ /Darwin/ ) { + Debug("Mac OS distro detected."); + $kernel = $uname[3]; + $distro = runSysCmd("sw_vers -productName"); + $version = runSysCmd("sw_vers -productVersion"); + } elsif ( $uname[0] =~ /SunOS|Solaris/ ) { + Debug("Sun Solaris detected."); + $kernel = $uname[3]; + $distro = $uname[1]; + $version = $uname[2]; + } else { + Warning("ZoneMinder was unable to determine the host system. Please report."); + $kernel = "Unknown"; + $distro = "Unknown"; + $version = "Unknown"; + } + +return ($kernel, $distro, $version); +} + +sub linuxDistro { + my @uname = uname(); + my $kernel = $uname[2]; + my $distro = "Unknown Linux Distro"; + my $version = "Unknown Linux Version"; + my $found = 0; + + # os-release is the standard for many new distros based on systemd + if ( -f "/etc/os-release" ) { + open(my $RELFILE,"<","/etc/os-release") or die( "Can't Open file: $!\n" ); + while (<$RELFILE>) { + if ( /^NAME=(")?(.*)(?(1)\1|).*$/ ) { + $distro = $2; + $found = 1; + } + if ( /^VERSION_ID=(")?(.*)(?(1)\1|).*$/ ) { + $version = $2; + $found = 1; + } + } + close $RELFILE; + # exists on many distros but does not always contain useful information, such as redhat + } elsif ( -f "/etc/lsb-release" ) { + open(my $RELFILE,"<","/etc/lsb-release") or die( "Can't Open file: $!\n" ); + while (<$RELFILE>) { + if ( /^DISTRIB_DESCRIPTION=(")?(.*)(?(1)\1|).*$/ ) { + $distro = $2; + $found = 1; + } + if ( /^DISTRIB_RELEASE=(")?(.*)(?(1)\1|).*$/ ) { + $version = $2; + $found = 1; + } + } + close $RELFILE; + } + + # If all else fails, search through a list of known release files until we find one + if ( !$found ) { + my @releasefile = ("/etc/SuSE-release", "/etc/redhat-release", "/etc/redhat_version", + "/etc/fedora-release", "/etc/slackware-release", "/etc/slackware-version", + "/etc/debian_release", "/etc/debian_version", "/etc/mandrake-release", + "/etc/yellowdog-release", "/etc/gentoo-release"); + foreach (@releasefile) { + if ( -f $_ ) { + open(my $RELFILE,"<",$_) or die( "Can't Open file: $!\n" ); + while (<$RELFILE>) { + if ( /(.*).* (\d+\.?\d*) .*/ ) { + $distro = $1; + $version = $2; + $found = 1; + } + } + close $RELFILE; + last; + } + } + } + + if ( !$found ) { + Warning("ZoneMinder was unable to determine Linux distro. Please report."); + } + +return ($kernel, $distro, $version); +} + + diff --git a/scripts/zmtrack.pl.in b/scripts/zmtrack.pl.in index 6822f6677..cd00e809d 100644 --- a/scripts/zmtrack.pl.in +++ b/scripts/zmtrack.pl.in @@ -20,10 +20,26 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script is used to trigger and cancel alarms from external sources -# using an arbitrary text based format -# + +=head1 NAME + +zmtrack.pl - ZoneMinder Experimental PTZ Tracking Script + +=head1 SYNOPSIS + + zmtrack.pl -m + zmtrack.pl --monitor= + +=head1 OPTIONS + + -m, --monitor= - Id of the monitor to track + +=head1 DESCRIPTION + +This script is used to trigger and cancel alarms from external sources +using an arbitrary text based format. + +=cut use strict; use bytes; @@ -45,32 +61,21 @@ use constant SLEEP_TIME => 10000; # In microseconds use ZoneMinder; use DBI; use POSIX; -use Data::Dumper; +use autouse 'Data::Dumper'=>qw(Dumper); use Getopt::Long; +use autouse 'Pod::Usage'=>qw(pod2usage); use Time::HiRes qw( usleep ); $| = 1; -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $mid = 0; -sub Usage -{ - print( " - Usage: zmtrack.pl -m ,--monitor=] - Parameters are :- - -m, --monitor= - Id of the monitor to track - "); - exit( -1 ); -} - -if ( !GetOptions( 'monitor=s'=>\$mid ) ) -{ - Usage(); -} +GetOptions( 'monitor=s'=>\$mid ) + or pod2usage(-exitstatus => -1); logInit(); logSetSignal(); @@ -78,44 +83,52 @@ logSetSignal(); my ( $detaint_mid ) = $mid =~ /^(\d+)$/; $mid = $detaint_mid; -print( "Tracker daemon $mid (experimental) starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" ); +print( "Tracker daemon $mid (experimental) starting at " + .strftime( '%y/%m/%d %H:%M:%S', localtime() ) + ."\n" +); my $dbh = zmDbConnect(); -my $sql = "select C.*,M.* from Monitors as M left join Controls as C on M.ControlId = C.Id where M.Id = ?"; -my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); +my $sql = "SELECT C.*,M.* FROM Monitors as M + LEFT JOIN Controls as C on M.ControlId = C.Id + WHERE M.Id = ?" +; +my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); -my $res = $sth->execute( $mid ) or Fatal( "Can't execute '$sql': ".$sth->errstr() ); +my $res = $sth->execute( $mid ) + or Fatal( "Can't execute '$sql': ".$sth->errstr() ); my $monitor = $sth->fetchrow_hashref(); if ( !$monitor ) { - print( "Can't find monitor '$mid'\n" ); - exit( -1 ); + print( "Can't find monitor '$mid'\n" ); + exit( -1 ); } if ( !$monitor->{Controllable} ) { - print( "Monitor '$mid' is not controllable\n" ); - exit( -1 ); + print( "Monitor '$mid' is not controllable\n" ); + exit( -1 ); } if ( !$monitor->{TrackMotion} ) { - print( "Monitor '$mid' is not configured to track motion\n" ); - exit( -1 ); + print( "Monitor '$mid' is not configured to track motion\n" ); + exit( -1 ); } if ( !$monitor->{CanMoveMap} ) { - print( "Monitor '$mid' cannot move in map mode" ); - if ( $monitor->{CanMoveRel} ) - { - print( ", falling back to pseudo map mode\n" ); - } - else - { - print( "\n" ); - exit( -1 ); - } + print( "Monitor '$mid' cannot move in map mode" ); + if ( $monitor->{CanMoveRel} ) + { + print( ", falling back to pseudo map mode\n" ); + } + else + { + print( "\n" ); + exit( -1 ); + } } Debug( "Found monitor for id '$monitor'\n" ); @@ -123,84 +136,100 @@ exit( -1 ) if ( !zmMemVerify( $monitor ) ); sub Suspend { - my $monitor = shift; - zmMonitorSuspend( $monitor ); + my $monitor = shift; + zmMonitorSuspend( $monitor ); } sub Resume { - my $monitor = shift; - sleep( $monitor->{TrackDelay} ); - zmMonitorResume( $monitor ); + my $monitor = shift; + sleep( $monitor->{TrackDelay} ); + zmMonitorResume( $monitor ); } sub Track { - my $monitor = shift; - my ( $x, $y ) = @_; - my ( $detaint_x ) = $x =~ /^(\d+)$/; $x = $detaint_x; - my ( $detaint_y ) = $y =~ /^(\d+)$/; $y = $detaint_y; + my $monitor = shift; + my ( $x, $y ) = @_; + my ( $detaint_x ) = $x =~ /^(\d+)$/; $x = $detaint_x; + my ( $detaint_y ) = $y =~ /^(\d+)$/; $y = $detaint_y; - my $ctrlCommand = ZM_PATH_BIN."/zmcontrol.pl -i ".$monitor->{Id}; - $ctrlCommand .= " --command=".($monitor->{CanMoveMap}?"moveMap":"movePseudoMap")." --xcoord=$x --ycoord=$y"; - executeShellCommand( $ctrlCommand ); + my $ctrlCommand = $Config{ZM_PATH_BIN} + ."/zmcontrol.pl -i " + .$monitor->{Id} + ; + $ctrlCommand .= " --command=" + .( $monitor->{CanMoveMap} ? "moveMap" + : "movePseudoMap" + ) + ." --xcoord=$x --ycoord=$y" + ; + executeShellCommand( $ctrlCommand ); } sub Return { - my $monitor = shift; + my $monitor = shift; - my $ctrlCommand = ZM_PATH_BIN."/zmcontrol.pl -i ".$monitor->{Id}; - if ( $monitor->{ReturnLocation} > 0 ) - { - $ctrlCommand .= " --command=presetGoto --preset=".$monitor->{ReturnLocation}; - } - else - { - $ctrlCommand .= " --command=presetHome"; - } - executeShellCommand( $ctrlCommand ); + my $ctrlCommand = $Config{ZM_PATH_BIN} + ."/zmcontrol.pl -i " + .$monitor->{Id} + ; + if ( $monitor->{ReturnLocation} > 0 ) + { + $ctrlCommand .= " --command=presetGoto --preset=" + .$monitor->{ReturnLocation} + ; + } + else + { + $ctrlCommand .= " --command=presetHome"; + } + executeShellCommand( $ctrlCommand ); } my $last_alarm = 0; if ( ($monitor->{ReturnLocation} >= 0) ) { - Suspend( $monitor ); - Return( $monitor ); - Resume( $monitor ); + Suspend( $monitor ); + Return( $monitor ); + Resume( $monitor ); } my $alarmed = undef; while( 1 ) { - if ( zmIsAlarmed( $monitor ) ) - { - my ( $alarm_x, $alarm_y ) = zmGetAlarmLocation( $monitor ); - if ( $alarm_x >= 0 && $alarm_y >= 0 ) - { - Debug( "Got alarm at $alarm_x, $alarm_y\n" ); - Suspend( $monitor ); - Track( $monitor, $alarm_x, $alarm_y ); - Resume( $monitor ); - $last_alarm = time(); - $alarmed = !undef; - } - } - else - { - if ( logDebugging() && $alarmed ) - { - print( "Left alarm state\n" ); - $alarmed = undef; - } - if ( ($monitor->{ReturnLocation} >= 0) && ($last_alarm > 0) && ((time()-$last_alarm) > $monitor->{ReturnDelay}) ) - { - Debug( "Returning to location ".$monitor->{ReturnLocation}."\n" ); - Suspend( $monitor ); - Return( $monitor ); - Resume( $monitor ); - $last_alarm = 0; - } - } - usleep( SLEEP_TIME ); + if ( zmIsAlarmed( $monitor ) ) + { + my ( $alarm_x, $alarm_y ) = zmGetAlarmLocation( $monitor ); + if ( $alarm_x >= 0 && $alarm_y >= 0 ) + { + Debug( "Got alarm at $alarm_x, $alarm_y\n" ); + Suspend( $monitor ); + Track( $monitor, $alarm_x, $alarm_y ); + Resume( $monitor ); + $last_alarm = time(); + $alarmed = !undef; + } + } + else + { + if ( logDebugging() && $alarmed ) + { + print( "Left alarm state\n" ); + $alarmed = undef; + } + if ( ($monitor->{ReturnLocation} >= 0) + && ($last_alarm > 0) + && ((time()-$last_alarm) > $monitor->{ReturnDelay}) + ) + { + Debug( "Returning to location ".$monitor->{ReturnLocation}."\n" ); + Suspend( $monitor ); + Return( $monitor ); + Resume( $monitor ); + $last_alarm = 0; + } + } + usleep( SLEEP_TIME ); } diff --git a/scripts/zmtrigger.pl.in b/scripts/zmtrigger.pl.in index 7dbca9898..2f180e090 100644 --- a/scripts/zmtrigger.pl.in +++ b/scripts/zmtrigger.pl.in @@ -20,11 +20,93 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script is used to trigger and cancel alarms from external connections -# using an arbitrary text based format -# -# ========================================================================== + +=head1 NAME + +zmtrigger.pl - ZoneMinder External Trigger Script + +=head1 DESCRIPTION + +This script is used to trigger and cancel alarms from external connections +using an arbitrary text based format. + +This script offers generic solution to external triggering of alarms. It +can handle external connections via either internet socket, unix socket or +file/device interfaces. You can either use it 'as is' if you can interface +with the existing format, or override connections and channels to customise +it to your needs. + +If enabled by the OPT_TRIGGERS option, Zoneminder service start +zmtrigger.pl which listens for control messages on TCP port 6802. + +=head1 TRIGGER MESSAGE FORMAT + +B|B|B|B|B|B + +=over 4 + +=item B + + is the id number or name of the ZM monitor. + +=item B + + Valid actions are 'on', 'off', 'cancel' or 'show' where + 'on' forces an alarm condition on; + 'off' forces an alarm condition off; + 'cancel' negates the previous 'on' or 'off'; + 'show' updates the auxiliary text represented by the %Q + placeholder, which can optionally be added to the affected monitor's + timestamp label format. + + Ordinarily you would use 'on' and 'cancel', 'off' would tend to be + used to suppress motion based events. Additionally 'on' and 'off' can + take an additional time offset, e.g. on+20 which automatically + cancel's the previous action after that number of seconds. + +=item B + + is the score given to the alarm, usually to indicate it's + importance. For 'on' triggers it should be non-zero, otherwise it should + be zero. + +=item B + + is a 32 char max string indicating the reason for, or source of + the alarm e.g. 'Relay 1 open'. This is saved in the 'Cause' field of the + event. Ignored for 'off' or 'cancel' messages. + +=item B + + is a 256 char max additional info field, which is saved in the + 'Description' field of an event. Ignored for 'off' or 'cancel' messages. + +=item B + + is up to 32 characters of text that can be displayed in the + timestamp that is added to images. The 'show' action is designed to + update this text without affecting alarms but the text is updated, if + present, for any of the actions. This is designed to allow external input + to appear on the images captured, for instance temperature or personnel + identity etc. + +=back + +Note that multiple messages can be sent at once and should be LF or CRLF +delimited. This script is not necessarily intended to be a solution in +itself, but is intended to be used as 'glue' to help ZoneMinder interface +with other systems. It will almost certainly require some customisation +before you can make any use of it. If all you want to do is generate alarms +from external sources then using the ZoneMinder::SharedMem perl module is +likely to be easier. + +=head1 EXAMPLES + + 3|on+10|1|motion|text|showtext + +Triggers "alarm" on camera #3 for 10 seconds with score=1, cause="motion". + +=cut use strict; use bytes; @@ -52,8 +134,22 @@ use ZoneMinder::Trigger::Channel::Serial; use ZoneMinder::Trigger::Connection; my @connections; -push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan1", channel=>ZoneMinder::Trigger::Channel::Inet->new( port=>6802 ), mode=>"rw" ) ); -push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan2", channel=>ZoneMinder::Trigger::Channel::Unix->new( path=>$Config{ZM_PATH_SOCKS}.'/zmtrigger.sock' ), mode=>"rw" ) ); +push( @connections, + ZoneMinder::Trigger::Connection->new( + name=>"Chan1 TCP on port 6802", + channel=>ZoneMinder::Trigger::Channel::Inet->new( port=>6802 ), + mode=>"rw" + ) +); +push( @connections, + ZoneMinder::Trigger::Connection->new( + name=>"Chan2 Unix Socket at " . $Config{ZM_PATH_SOCKS}.'/zmtrigger.sock', + channel=>ZoneMinder::Trigger::Channel::Unix->new( + path=>$Config{ZM_PATH_SOCKS}.'/zmtrigger.sock' + ), + mode=>"rw" + ) +); #push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan3", channel=>ZoneMinder::Trigger::Channel::File->new( path=>'/tmp/zmtrigger.out' ), mode=>"w" ) ); #push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan4", channel=>ZoneMinder::Trigger::Channel::Serial->new( path=>'/dev/ttyS0' ), mode=>"rw" ) ); @@ -65,13 +161,13 @@ push( @connections, ZoneMinder::Trigger::Connection->new( name=>"Chan2", channel use DBI; #use Socket; -use Data::Dumper; +use autouse 'Data::Dumper'=>qw(Dumper); use POSIX qw( EINTR ); use Time::HiRes qw( usleep ); $| = 1; -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; @@ -85,8 +181,8 @@ my $dbh = zmDbConnect(); my $base_rin = ''; foreach my $connection ( @connections ) { - Info( "Opening connection '$connection->{name}'\n" ); - $connection->open(); + Info( "Opening connection '$connection->{name}'\n" ); + $connection->open(); } my @in_select_connections = grep { $_->input() && $_->selectable() } @connections; @@ -95,7 +191,7 @@ my @out_connections = grep { $_->output() } @connections; foreach my $connection ( @in_select_connections ) { - vec( $base_rin, $connection->fileno(), 1 ) = 1; + vec( $base_rin, $connection->fileno(), 1 ) = 1; } my %spawned_connections; @@ -111,333 +207,386 @@ my $timeout = SELECT_TIMEOUT; my %actions; while( 1 ) { - $rin = $base_rin; - # Add the file descriptors of any spawned connections - foreach my $fileno ( keys(%spawned_connections) ) - { - vec( $rin, $fileno, 1 ) = 1; - } + $rin = $base_rin; + # Add the file descriptors of any spawned connections + foreach my $fileno ( keys(%spawned_connections) ) + { + vec( $rin, $fileno, 1 ) = 1; + } - my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout ); - if ( $nfound > 0 ) - { - Debug( "Got input from $nfound connections\n" ); - foreach my $connection ( @in_select_connections ) - { - if ( vec( $rout, $connection->fileno(), 1 ) ) - { - Debug( "Got input from connection ".$connection->name()." (".$connection->fileno().")\n" ); - if ( $connection->spawns() ) - { - my $new_connection = $connection->accept(); - $spawned_connections{$new_connection->fileno()} = $new_connection; - Debug( "Added new spawned connection (".$new_connection->fileno()."), ".int(keys(%spawned_connections))." spawned connections\n" ); - } - else - { - my $messages = $connection->getMessages(); - if ( defined($messages) ) - { - foreach my $message ( @$messages ) - { - handleMessage( $connection, $message ); - } - } - } - } - } - foreach my $connection ( values(%spawned_connections) ) - { - if ( vec( $rout, $connection->fileno(), 1 ) ) - { - Debug( "Got input from spawned connection ".$connection->name()." (".$connection->fileno().")\n" ); - my $messages = $connection->getMessages(); - if ( defined($messages) ) - { - foreach my $message ( @$messages ) - { - handleMessage( $connection, $message ); - } - } - else - { - delete( $spawned_connections{$connection->fileno()} ); - Debug( "Removed spawned connection (".$connection->fileno()."), ".int(keys(%spawned_connections))." spawned connections\n" ); - $connection->close(); - } - } - } - } - elsif ( $nfound < 0 ) - { - if ( $! == EINTR ) - { - # Do nothing - } - else - { - Fatal( "Can't select: $!" ); - } - } + my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout ); + if ( $nfound > 0 ) + { + Debug( "Got input from $nfound connections\n" ); + foreach my $connection ( @in_select_connections ) + { + if ( vec( $rout, $connection->fileno(), 1 ) ) + { + Debug( "Got input from connection " + .$connection->name() + ." (" + .$connection->fileno() + .")\n" + ); + if ( $connection->spawns() ) + { + my $new_connection = $connection->accept(); + $spawned_connections{$new_connection->fileno()} = $new_connection; + Debug( "Added new spawned connection (" + .$new_connection->fileno() + ."), " + .int(keys(%spawned_connections)) + ." spawned connections\n" + ); + } + else + { + my $messages = $connection->getMessages(); + if ( defined($messages) ) + { + foreach my $message ( @$messages ) + { + handleMessage( $connection, $message ); + } + } + } + } + } + foreach my $connection ( values(%spawned_connections) ) + { + if ( vec( $rout, $connection->fileno(), 1 ) ) + { + Debug( "Got input from spawned connection " + .$connection->name() + ." (" + .$connection->fileno() + .")\n" + ); + my $messages = $connection->getMessages(); + if ( defined($messages) ) + { + foreach my $message ( @$messages ) + { + handleMessage( $connection, $message ); + } + } + else + { + delete( $spawned_connections{$connection->fileno()} ); + Debug( "Removed spawned connection (" + .$connection->fileno() + ."), " + .int(keys(%spawned_connections)) + ." spawned connections\n" + ); + $connection->close(); + } + } + } + } + elsif ( $nfound < 0 ) + { + if ( $! == EINTR ) + { + # Do nothing + } + else + { + Fatal( "Can't select: $!" ); + } + } - # Check polled connections - foreach my $connection ( @in_poll_connections ) - { - my $messages = $connection->getMessages(); - if ( defined($messages) ) - { - foreach my $message ( @$messages ) - { - handleMessage( $connection, $message ); - } - } - } + # Check polled connections + foreach my $connection ( @in_poll_connections ) + { + my $messages = $connection->getMessages(); + if ( defined($messages) ) + { + foreach my $message ( @$messages ) + { + handleMessage( $connection, $message ); + } + } + } - # Check for alarms that might have happened - my @out_messages; - foreach my $monitor ( values(%monitors) ) - { - my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state", "shared_data:last_event" ] ); + # Check for alarms that might have happened + my @out_messages; + foreach my $monitor ( values(%monitors) ) + { + my ( $state, $last_event ) + = zmMemRead( $monitor, + [ "shared_data:state", + "shared_data:last_event" + ] + ); - #print( "$monitor->{Id}: S:$state, LE:$last_event\n" ); - #print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" ); - if ( $state == STATE_ALARM || $state == STATE_ALERT ) # In alarm state - { - if ( !defined($monitor->{LastEvent}) || ($last_event != $monitor->{LastEvent}) ) # A new event - { - push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event ); - } - else # The same one as last time, so ignore it - { - # Do nothing - } - } - elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE) || ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE) ) # Out of alarm state - { - push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); - } - elsif ( defined($monitor->{LastEvent}) && ($last_event != $monitor->{LastEvent}) ) # We've missed a whole event - { - push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event ); - push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); - } - $monitor->{LastState} = $state; - $monitor->{LastEvent} = $last_event; - } - foreach my $connection ( @out_connections ) - { - if ( $connection->canWrite() ) - { - $connection->putMessages( \@out_messages ); - } - } - foreach my $connection ( values(%spawned_connections) ) - { - if ( $connection->canWrite() ) - { - $connection->putMessages( \@out_messages ); - } - } + #print( "$monitor->{Id}: S:$state, LE:$last_event\n" ); + #print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" ); + if ( $state == STATE_ALARM + || $state == STATE_ALERT + ) # In alarm state + { + if ( !defined($monitor->{LastEvent}) + || ($last_event != $monitor->{LastEvent}) + ) # A new event + { + push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event ); + } + else # The same one as last time, so ignore it + { + # Do nothing + } + } + elsif ( ($state == STATE_IDLE + && $monitor->{LastState} != STATE_IDLE + ) + || ($state == STATE_TAPE + && $monitor->{LastState} != STATE_TAPE + ) + ) # Out of alarm state + { + push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); + } + elsif ( defined($monitor->{LastEvent}) + && ($last_event != $monitor->{LastEvent}) + ) # We've missed a whole event + { + push( @out_messages, $monitor->{Id}."|on|".time()."|".$last_event ); + push( @out_messages, $monitor->{Id}."|off|".time()."|".$last_event ); + } + $monitor->{LastState} = $state; + $monitor->{LastEvent} = $last_event; + } + foreach my $connection ( @out_connections ) + { + if ( $connection->canWrite() ) + { + $connection->putMessages( \@out_messages ); + } + } + foreach my $connection ( values(%spawned_connections) ) + { + if ( $connection->canWrite() ) + { + $connection->putMessages( \@out_messages ); + } + } - Debug( "Checking for timed actions\n" ) if ( int(keys(%actions)) ); - my $now = time(); - foreach my $action_time ( sort( grep { $_ < $now } keys( %actions ) ) ) - { - Info( "Found actions expiring at $action_time\n" ); - foreach my $action ( @{$actions{$action_time}} ) - { - my $connection = $action->{connection}; - my $message = $action->{message}; - Info( "Found action '$message'\n" ); - handleMessage( $connection, $message ); - } - delete( $actions{$action_time} ); - } + Debug( "Checking for timed actions\n" ) + if ( int(keys(%actions)) ); + my $now = time(); + foreach my $action_time ( sort( grep { $_ < $now } keys( %actions ) ) ) + { + Info( "Found actions expiring at $action_time\n" ); + foreach my $action ( @{$actions{$action_time}} ) + { + my $connection = $action->{connection}; + my $message = $action->{message}; + Info( "Found action '$message'\n" ); + handleMessage( $connection, $message ); + } + delete( $actions{$action_time} ); + } # Allow connections to do their own timed actions - foreach my $connection ( @connections ) - { - my $messages = $connection->timedActions(); - if ( defined($messages) ) - { - foreach my $message ( @$messages ) - { - handleMessage( $connection, $message ); - } - } - } - foreach my $connection ( values(%spawned_connections) ) - { - my $messages = $connection->timedActions(); - if ( defined($messages) ) - { - foreach my $message ( @$messages ) - { - handleMessage( $connection, $message ); - } - } - } + foreach my $connection ( @connections ) + { + my $messages = $connection->timedActions(); + if ( defined($messages) ) + { + foreach my $message ( @$messages ) + { + handleMessage( $connection, $message ); + } + } + } + foreach my $connection ( values(%spawned_connections) ) + { + my $messages = $connection->timedActions(); + if ( defined($messages) ) + { + foreach my $message ( @$messages ) + { + handleMessage( $connection, $message ); + } + } + } - # If necessary reload monitors - if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) - { - foreach my $monitor ( values(%monitors) ) + # If necessary reload monitors + if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) + { + foreach my $monitor ( values(%monitors) ) { # Free up any used memory handle zmMemInvalidate( $monitor ); } - loadMonitors(); - } + loadMonitors(); + } } Info( "Trigger daemon exiting\n" ); exit; sub loadMonitors { - Debug( "Loading monitors\n" ); - $monitor_reload_time = time(); + Debug( "Loading monitors\n" ); + $monitor_reload_time = time(); - my %new_monitors = (); + my %new_monitors = (); - my $sql = "select * from Monitors where find_in_set( Function, 'Modect,Mocord,Nodect' )"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); - while( my $monitor = $sth->fetchrow_hashref() ) - { - next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok + my $sql = "SELECT * FROM Monitors + WHERE find_in_set( Function, 'Modect,Mocord,Nodect' )". + ( $Config{ZM_SERVER_ID} ? 'AND ServerId=?' : '' ) + ; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () ) + or Fatal( "Can't execute: ".$sth->errstr() ); + while( my $monitor = $sth->fetchrow_hashref() ) + { + next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok - if ( defined($monitors{$monitor->{Id}}->{LastState}) ) - { - $monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState}; - } - else - { - $monitor->{LastState} = zmGetMonitorState( $monitor ); - } - if ( defined($monitors{$monitor->{Id}}->{LastEvent}) ) - { - $monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent}; - } - else - { - $monitor->{LastEvent} = zmGetLastEvent( $monitor ); - } - $new_monitors{$monitor->{Id}} = $monitor; - } - %monitors = %new_monitors; + if ( defined($monitors{$monitor->{Id}}->{LastState}) ) + { + $monitor->{LastState} = $monitors{$monitor->{Id}}->{LastState}; + } + else + { + $monitor->{LastState} = zmGetMonitorState( $monitor ); + } + if ( defined($monitors{$monitor->{Id}}->{LastEvent}) ) + { + $monitor->{LastEvent} = $monitors{$monitor->{Id}}->{LastEvent}; + } + else + { + $monitor->{LastEvent} = zmGetLastEvent( $monitor ); + } + $new_monitors{$monitor->{Id}} = $monitor; + } + %monitors = %new_monitors; } sub handleMessage { - my $connection = shift; - my $message = shift; + my $connection = shift; + my $message = shift; - my ( $id, $action, $score, $cause, $text, $showtext ) = split( /\|/, $message ); - $score = 0 if ( !defined($score) ); - $cause = "" if ( !defined($cause) ); - $text = "" if ( !defined($text) ); + my ( $id, $action, $score, $cause, $text, $showtext ) + = split( /\|/, $message ); + $score = 0 if ( !defined($score) ); + $cause = "" if ( !defined($cause) ); + $text = "" if ( !defined($text) ); - my $monitor = $monitors{$id}; - if ( !$monitor ) - { - Warning( "Can't find monitor '$id' for message '$message'\n" ); - return; - } - Debug( "Found monitor for id '$id'\n" ); + my $monitor = $monitors{$id}; + if ( !$monitor ) + { + Warning( "Can't find monitor '$id' for message '$message'\n" ); + return; + } + Debug( "Found monitor for id '$id'\n" ); - next if ( !zmMemVerify( $monitor ) ); + next if ( !zmMemVerify( $monitor ) ); - Debug( "Handling action '$action'\n" ); - if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) - { - my $state = $1; - my $delay = $2; - if ( $state eq "enable" ) - { - zmMonitorEnable( $monitor ); - } - else - { - zmMonitorDisable( $monitor ); - } - # Force a reload - $monitor_reload_time = 0; - Info( "Set monitor to $state\n" ); - if ( $delay ) - { - my $action_time = time()+$delay; - my $action_text = $id."|".(($state eq "enable")?"disable":"enable"); - my $action_array = $actions{$action_time}; - if ( !$action_array ) - { - $action_array = $actions{$action_time} = []; - } - push( @$action_array, { connection=>$connection, message=>$action_text } ); - Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" ); - } - } - elsif ( $action =~ /^(on|off)(?:\+(\d+))?$/ ) - { - next if ( !$monitor->{Enabled} ); + Debug( "Handling action '$action'\n" ); + if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) + { + my $state = $1; + my $delay = $2; + if ( $state eq "enable" ) + { + zmMonitorEnable( $monitor ); + } + else + { + zmMonitorDisable( $monitor ); + } + # Force a reload + $monitor_reload_time = 0; + Info( "Set monitor to $state\n" ); + if ( $delay ) + { + my $action_text = $id."|".( ($state eq "enable") + ? "disable" + : "enable" + ); + handleDelay($delay, $connection, $action_text); + } + } + elsif ( $action =~ /^(on|off)(?:[ \+](\d+))?$/ ) + { + next if ( !$monitor->{Enabled} ); - my $trigger = $1; - my $delay = $2; - my $trigger_data; - if ( $trigger eq "on" ) - { - zmTriggerEventOn( $monitor, $score, $cause, $text ); - zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); - Info( "Trigger '$trigger' '$cause'\n" ); - } - elsif ( $trigger eq "off" ) - { - my $last_event = zmGetLastEvent( $monitor ); - zmTriggerEventOff( $monitor ); - zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); - Info( "Trigger '$trigger'\n" ); - # Wait til it's finished - while( zmInAlarm( $monitor ) && ($last_event == zmGetLastEvent( $monitor )) ) + my $trigger = $1; + my $delay = $2; + my $trigger_data; + if ( $trigger eq "on" ) + { + zmTriggerEventOn( $monitor, $score, $cause, $text ); + zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); + Info( "Trigger '$trigger' '$cause'\n" ); + if ( $delay ) { - # Tenth of a second - usleep( 100000 ); + my $action_text = $id."|cancel"; + handleDelay($delay, $connection, $action_text); } - zmTriggerEventCancel( $monitor ); - } - else - { - Info( "Trigger '$trigger'\n" ); - zmTriggerEventCancel( $monitor ); - } - if ( $delay ) - { - my $action_time = time()+$delay; - #my $action_text = $id."|cancel|0|".$cause."|".$text; - my $action_text = $id."|cancel"; - my $action_array = $actions{$action_time}; - if ( !$action_array ) - { - $action_array = $actions{$action_time} = []; - } - push( @$action_array, { connection=>$connection, message=>$action_text } ); - Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" ); - } - } - elsif( $action eq "cancel" ) - { - zmTriggerEventCancel( $monitor ); - zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); - Info( "Cancelled event\n" ); - } - elsif( $action eq "show" ) - { - zmTriggerShowtext( $monitor, $showtext ); - Info( "Updated show text to '$showtext'\n" ); - } - else - { - Error( "Unrecognised action '$action' in message '$message'\n" ); - } + } + elsif ( $trigger eq "off" ) + { + if ( $delay ) + { + my $action_text = $id."|off|0|".$cause."|".$text; + handleDelay($delay, $connection, $action_text); + } else { + my $last_event = zmGetLastEvent( $monitor ); + zmTriggerEventOff( $monitor ); + zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); + Info( "Trigger '$trigger'\n" ); + # Wait til it's finished + while( zmInAlarm( $monitor ) + && ($last_event == zmGetLastEvent( $monitor )) + ) + { + # Tenth of a second + usleep( 100000 ); + } + zmTriggerEventCancel( $monitor ); + } + } + } + elsif( $action eq "cancel" ) + { + zmTriggerEventCancel( $monitor ); + zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); + Info( "Cancelled event\n" ); + } + elsif( $action eq "show" ) + { + zmTriggerShowtext( $monitor, $showtext ); + Info( "Updated show text to '$showtext'\n" ); + } + else + { + Error( "Unrecognised action '$action' in message '$message'\n" ); + } } # end sub handleMessage +sub handleDelay +{ + my $delay = shift; + my $connection = shift; + my $action_text = shift; + + my $action_time = time()+$delay; + my $action_array = $actions{$action_time}; + if ( !$action_array ) + { + $action_array = $actions{$action_time} = []; + } + push( @$action_array, { connection=>$connection, + message=>$action_text + } + ); + Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)\n" ); +} 1; __END__ diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index 83502154c..c0dcd3868 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -20,13 +20,37 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script just checks what the most recent release of ZoneMinder is -# at the the moment. It will eventually be responsible for applying and -# configuring upgrades etc, including on the fly upgrades. -# + +=head1 NAME + +zmupdate.pl - check and upgrade ZoneMinder database + +=head1 SYNOPSIS + + zmupdate.pl -c,--check | -f,--freshen | -v,--version= [-u -p] + +=head1 DESCRIPTION + +This script just checks what the most recent release of ZoneMinder is +at the the moment. It will eventually be responsible for applying and +configuring upgrades etc, including on the fly upgrades. + +=head1 OPTIONS + + -c, --check - Check for updated versions of ZoneMinder + -f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi + --migrate-events - Update database structures as per USE_DEEP_STORAGE setting. + -v, --version= - Force upgrade to the current version from + -u, --user= - Alternate DB user with privileges to alter DB + -p, --pass= - Password of alternate DB user with privileges to alter DB + -d,--dir= - Directory containing update files if not in default build location + -interactive - interact with the user + -nointeractive - do not interact with the user + +=cut use strict; use bytes; +use version; # ========================================================================== # @@ -48,11 +72,11 @@ use ZoneMinder::Config qw(:all); use ZoneMinder::Logger qw(:all); use ZoneMinder::General qw(:all); use ZoneMinder::Database qw(:all); -use ZoneMinder::ConfigAdmin qw( :functions ); use POSIX; use DBI; use Getopt::Long; -use Data::Dumper; +use autouse 'Pod::Usage'=>qw(pod2usage); +use autouse 'Data::Dumper'=>qw(Dumper); use constant EVENT_PATH => ($Config{ZM_DIR_EVENTS}=~m|/|)?$Config{ZM_DIR_EVENTS}:($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}); @@ -78,25 +102,19 @@ my $version = ''; my $dbUser = $Config{ZM_DB_USER}; my $dbPass = $Config{ZM_DB_PASS}; my $updateDir = ''; -sub Usage -{ - print( " -Usage: zmupdate.pl <-c,--check|-f,--freshen|-v,--version=> [-u -p]> -Parameters are :- --c, --check - Check for updated versions of ZoneMinder --f, --freshen - Freshen the configuration in the database. Equivalent of old zmconfig.pl -noi --v, --version= - Force upgrade to the current version from --u, --user= - Alternate DB user with privileges to alter DB --p, --pass= - Password of alternate DB user with privileges to alter DB --d,--dir= - Directory containing update files if not in default build location -"); - exit( -1 ); -} -if ( !GetOptions( 'check'=>\$check, 'freshen'=>\$freshen, 'rename'=>\$rename, 'zone-fix'=>\$zoneFix, 'migrate-events'=>\$migrateEvents, 'version=s'=>\$version, 'interactive!'=>\$interactive, 'user:s'=>\$dbUser, 'pass:s'=>\$dbPass, 'dir:s'=>\$updateDir ) ) -{ - Usage(); -} +GetOptions( + 'check' =>\$check, + 'freshen' =>\$freshen, + 'rename' =>\$rename, + 'zone-fix' =>\$zoneFix, + 'migrate-events' =>\$migrateEvents, + 'version=s' =>\$version, + 'interactive!' =>\$interactive, + 'user:s' =>\$dbUser, + 'pass:s' =>\$dbPass, + 'dir:s' =>\$updateDir +) or pod2usage(-exitstatus => -1); my $dbh = zmDbConnect(); $Config{ZM_DB_USER} = $dbUser; @@ -111,14 +129,14 @@ if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version) else { print( STDERR "Please give a valid option\n" ); - Usage(); + pod2usage(-exitstatus => -1); } } if ( ($check + $freshen + $rename + $zoneFix + $migrateEvents + ($version?1:0)) > 1 ) { print( STDERR "Please give only one option\n" ); - Usage(); + pod2usage(-exitstatus => -1); } if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) @@ -136,6 +154,7 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'"; my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( "$currVersion" ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); } while( 1 ) @@ -151,7 +170,7 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) if ( $Config{ZM_UPDATE_CHECK_PROXY} ) { $ua->proxy( "http", $Config{ZM_UPDATE_CHECK_PROXY} ); } - my $req = HTTP::Request->new( GET=>'http://zoneminder.github.io/ZoneMinder/version.txt' ); + my $req = HTTP::Request->new( GET=>'https://update.zoneminder.com/version.txt' ); my $res = $ua->request($req); if ( $res->is_success ) @@ -165,10 +184,12 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) my $lv_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_VERSION'"; my $lv_sth = $dbh->prepare_cached( $lv_sql ) or die( "Can't prepare '$lv_sql': ".$dbh->errstr() ); my $lv_res = $lv_sth->execute( $lastVersion ) or die( "Can't execute: ".$lv_sth->errstr() ); + $lv_sth->finish(); my $lc_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_CHECK'"; my $lc_sth = $dbh->prepare_cached( $lc_sql ) or die( "Can't prepare '$lc_sql': ".$dbh->errstr() ); my $lc_res = $lc_sth->execute( $lastCheck ) or die( "Can't execute: ".$lc_sth->errstr() ); + $lc_sth->finish(); } else { @@ -220,14 +241,14 @@ if ( $zoneFix ) } $sth->finish(); + $sql = "update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?"; + $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); foreach my $zone ( @zones ) { my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100; my $zone_height = (($zone->{HiY}*$zone->{MonitorHeight})-($zone->{LoY}*$zone->{MonitorHeight}))/100; my $zone_area = $zone_width * $zone_height; my $monitor_area = $zone->{MonitorWidth} * $zone->{MonitorHeight}; - my $sql = "update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( ($zone->{MinAlarmPixels}*$monitor_area)/$zone_area, ($zone->{MaxAlarmPixels}*$monitor_area)/$zone_area, @@ -238,6 +259,7 @@ if ( $zoneFix ) $zone->{Id} ) or die( "Can't execute: ".$sth->errstr() ); } + $sth->finish(); } if ( $migrateEvents ) { @@ -299,11 +321,13 @@ if ( $migrateEvents ) symlink( $newTimePath, $idLink ) or die( "Can't symlink $newTimePath -> $idLink: $!" ); rename( $oldEventPath, $newEventPath ) or die( "Can't move $oldEventPath -> $newEventPath: $!" ); } + $sth->finish(); print( "Updating configuration.\n" ); $sql = "update Config set Value = ? where Name = 'ZM_USE_DEEP_STORAGE'"; $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); $res = $sth->execute( 1 ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); print( "All events converted.\n\n" ); } @@ -315,38 +339,41 @@ if ( $migrateEvents ) if ( $freshen ) { print( "\nFreshening configuration in database\n" ); - loadConfigFromDB(); - saveConfigToDB(); + ZoneMinder::Config::loadConfigFromDB(); + ZoneMinder::Config::saveConfigToDB(); } -# Now check for MyISAM Tables -my @MyISAM_Tables; -my $sql = "SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='zm' AND engine = 'MyISAM'"; -my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); -my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); +# Don't do innoDB upgrade if not interactive +if ( $interactive ) { + # Now check for MyISAM Tables + my @MyISAM_Tables; + my $sql = "SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema='zm' AND engine = 'MyISAM'"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); -while( my $dbTable = $sth->fetchrow() ) { - push @MyISAM_Tables, $dbTable; -} -$sth->finish(); + while( my $dbTable = $sth->fetchrow() ) { + push @MyISAM_Tables, $dbTable; + } + $sth->finish(); -if ( @MyISAM_Tables ) { - print( "\nPrevious versions of ZoneMinder used the MyISAM database engine.\nHowever, the recommended database engine is InnoDB.\n"); - print( "\nHint: InnoDB tables are much less likely to be corrupted during an unclean shutdown.\n\nPress 'y' to convert your tables to InnoDB or 'n' to skip : "); - my $response = ; - chomp( $response ); - if ( $response =~ /^[yY]$/ ) { - print "\nConverting MyISAM tables to InnoDB. Please wait.\n"; - foreach (@MyISAM_Tables) { + if ( @MyISAM_Tables ) { + print( "\nPrevious versions of ZoneMinder used the MyISAM database engine.\nHowever, the recommended database engine is InnoDB.\n"); + print( "\nHint: InnoDB tables are much less likely to be corrupted during an unclean shutdown.\n\nPress 'y' to convert your tables to InnoDB or 'n' to skip : "); + my $response = ; + chomp( $response ); + if ( $response =~ /^[yY]$/ ) { $dbh->do(q|SET sql_mode='traditional'|); # Elevate warnings to errors - my $sql = "ALTER TABLE $_ ENGINE = InnoDB"; - my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); - $sth->finish(); + print "\nConverting MyISAM tables to InnoDB. Please wait.\n"; + foreach (@MyISAM_Tables) { + my $sql = "ALTER TABLE $_ ENGINE = InnoDB"; + my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); + } $dbh->do(q|SET sql_mode=''|); # Set mode back to default } } -} +} # end if interactive if ( $version ) { @@ -384,7 +411,8 @@ if ( $version ) if ( $response =~ /^[yY]$/ ) { my ( $host, $port ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ); - my $command = "mysqldump -h".$host; + my $command = "mysqldump"; + $command .= " -h".$host if defined($host); $command .= " -P".$port if defined($port); if ( $dbUser ) { @@ -471,8 +499,8 @@ if ( $version ) print( "\nUpgrading database to version ".ZM_VERSION."\n" ); # Update config first of all - loadConfigFromDB(); - saveConfigToDB(); + ZoneMinder::Config::loadConfigFromDB(); + ZoneMinder::Config::saveConfigToDB(); my $cascade = undef; if ( $cascade || $version eq "1.19.0" ) @@ -1005,16 +1033,23 @@ if ( $version ) my @files; $updateDir = $Config{ZM_PATH_DATA}."/db" if ! $updateDir; opendir( my $dh, $updateDir ) || die "Can't open updateDir $!"; - @files = sort grep { (!/^\./) && /^zm_update\-[\d\.]+\.sql$/ && -f "$updateDir/$_" } readdir($dh); + #@files = sort grep { (!/^\./) && /^zm_update\-[\d\.]+\.sql$/ && -f "$updateDir/$_" } readdir($dh); + #PP - use perl version sort + @files = sort { + my ($x) = ($a =~ m/^zm_update\-(.*)\.sql$/); + my ($y) = ($b =~ m/^zm_update\-(.*)\.sql$/); + version->parse($x) <=> version->parse($y) + } grep { (!/^\./) && /^zm_update\-[\d\.]+\.sql$/ && -f "$updateDir/$_" } readdir($dh); closedir $dh; - if ( ! @files ) { - die "Should have found upgrade scripts at $updateDir\n"; - } # end if + if ( ! @files ) { + die "Should have found upgrade scripts at $updateDir\n"; + } # end if $dbh->{'AutoCommit'} = 0; foreach my $patch ( @files ) { my ( $v ) = $patch =~ /^zm_update\-([\d\.]+)\.sql$/; - if ( $v ge $version ) { + #PP make sure we use version compare + if ( version->parse('v' . $v) > version->parse('v' . $version) ) { print( "Upgrading DB to $v from $version\n" ); patchDB( $dbh, $v ); if ( $dbh->errstr() ) { @@ -1034,6 +1069,7 @@ if ( $version ) my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() ); my $res = $sth->execute( "$installed_version", "ZM_DYN_DB_VERSION" ) or die( "Can't execute: ".$sth->errstr() ); $res = $sth->execute( "$installed_version", "ZM_DYN_CURR_VERSION" ) or die( "Can't execute: ".$sth->errstr() ); + $sth->finish(); } else { diff --git a/scripts/zmvideo.pl.in b/scripts/zmvideo.pl.in index 46cf83998..754213af6 100644 --- a/scripts/zmvideo.pl.in +++ b/scripts/zmvideo.pl.in @@ -20,10 +20,40 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script is used to create MPEG videos of events for the web pages -# or as email attachments. -# + +=head1 NAME + +zmvideo.pl - ZoneMinder Video Creation Script + +=head1 SYNOPSIS + + zmvideo.pl [ -e ,--event= | --filter= ] [--format ] + [--rate=] + [--scale=] + [--fps=] + [--size=] + [--overwrite] + +=head1 DESCRIPTION + +This script is used to create MPEG videos of events for the web pages +or as email attachments. + +=head1 OPTIONS + -c[=filename], --concat[=filename] - When creating videos for multiple events, create a concatenated video as well. + - If not specified, filename is taken from filter name. + -e, --event= - What event to create the video for + --filter_name= - The name of a saved filter to generate a video for all events returned by it. + --filter_id= - The id of a saved filter to generate a video for all events returned by it. + -f, --format= - What format to create the video in, default is mpg. For ffmpeg only. + -r, --rate= - Relative rate, 1 = realtime, 2 = double speed, 0.5 = half speed etc. + -s, --scale= - Scale, 1 = normal, 2 = double size, 0.5 = half size etc. + -F, --fps= - Absolute frame rate, in frames per second + -S, --size= - Absolute video size, WxH or other specification supported by ffmpeg + -o, --overwrite - Whether to overwrite an existing file, off by default. + -v, --version - Outputs the currently installed version of ZoneMinder + +=cut use strict; use bytes; @@ -35,95 +65,101 @@ use bytes; @EXTRA_PERL_LIB@ use ZoneMinder; +require ZoneMinder::Filter; +require ZoneMinder::Event; use DBI; -use Data::Dumper; +use autouse 'Data::Dumper'=>qw(Dumper); use POSIX qw(strftime); use Getopt::Long qw(:config no_ignore_case ); +use Cwd; +use autouse 'Pod::Usage'=>qw(pod2usage); $| = 1; -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; logInit(); my $event_id; +my $concat_name; +my $filter_name; +my $filter_id; my $format = 'mpg'; my $rate = ''; my $scale = ''; my $fps = ''; my $size = ''; my $overwrite = 0; +my $version = 0; my @formats = split( /\s+/, $Config{ZM_FFMPEG_FORMATS} ); for ( my $i = 0; $i < @formats; $i++ ) { - if ( $i =~ /^(.+)\*$/ ) - { - $format = $formats[$i] = $1; - } + if ( $i =~ /^(.+)\*$/ ) + { + $format = $formats[$i] = $1; + } } -sub Usage -{ - print( " -Usage: zmvideo.pl -e ,--event= [--format ] [--rate=] [--scale=] [--fps=] [--size=] [--overwrite] -Parameters are :- --e, --event= - What event to create the video for --f, --format= - What format to create the video in, default is mpg. For ffmpeg only. --r, --rate= - Relative rate , 1 = realtime, 2 = double speed , 0.5 = half speed etc --s, --scale= - Scale, 1 = normal, 2 = double size, 0.5 = half size etc --F, --fps= - Absolute frame rate, in frames per second --S, --size= - Absolute video size, WxH or other specification supported by ffmpeg --o, --overwrite - Whether to overwrite an existing file, off by default. -"); - exit( -1 ); +GetOptions( + 'concat|c:s' =>\$concat_name, + 'event|e=i' =>\$event_id, + 'filter_name=s' =>\$filter_name, + 'filter_id=i' =>\$filter_id, + 'format|f=s' =>\$format, + 'rate|r=f' =>\$rate, + 'scale|s=f' =>\$scale, + 'fps|F=f' =>\$fps, + 'size|S=s' =>\$size, + 'overwrite' =>\$overwrite, + 'version' =>\$version +) or pod2usage(-exitstatus => -1); + +if ( $version ) { + print ZoneMinder::Base::ZM_VERSION . "\n"; + exit(0); } -if ( !GetOptions( 'event=i'=>\$event_id, 'format|f=s'=>\$format, 'rate|r=f'=>\$rate, 'scale|s=f'=>\$scale, 'fps|F=f'=>\$fps, 'size|S=s'=>\$size, 'overwrite'=>\$overwrite ) ) +if ( !( $filter_id or $filter_name or $event_id ) || $event_id < 0 ) { - Usage(); -} - -if ( !$event_id || $event_id < 0 ) -{ - print( STDERR "Please give a valid event id\n" ); - Usage(); + print( STDERR "Please give a valid event id or filter name\n" ); + pod2usage(-exitstatus => -1); } if ( ! $Config{ZM_OPT_FFMPEG} ) { - print( STDERR "Mpeg encoding is not currently enabled\n" ); - exit(-1); + print( STDERR "Mpeg encoding is not currently enabled\n" ); + exit(-1); } if ( !$rate && !$fps ) { - $rate = 1; + $rate = 1; } if ( !$scale && !$size ) { - $scale = 1; + $scale = 1; } if ( $rate && ($rate < 0.25 || $rate > 100) ) { - print( STDERR "Rate is out of range, 0.25 >= rate <= 100\n" ); - Usage(); + print( STDERR "Rate is out of range, 0.25 >= rate <= 100\n" ); + pod2usage(-exitstatus => -1); } if ( $scale && ($scale < 0.25 || $scale > 4) ) { - print( STDERR "Scale is out of range, 0.25 >= scale <= 4\n" ); - Usage(); + print( STDERR "Scale is out of range, 0.25 >= scale <= 4\n" ); + pod2usage(-exitstatus => -1); } if ( $fps && ($fps > 30) ) { - print( STDERR "FPS is out of range, <= 30\n" ); - Usage(); + print( STDERR "FPS is out of range, <= 30\n" ); + pod2usage(-exitstatus => -1); } my ( $detaint_format ) = $format =~ /^(\w+)$/; @@ -140,104 +176,86 @@ $size = $detaint_size; my $dbh = zmDbConnect(); -my @filters; -my $sql = "select max(F.Delta)-min(F.Delta) as FullLength, E.*, unix_timestamp(E.StartTime) as Time, M.Name as MonitorName, M.Width as MonitorWidth, M.Height as MonitorHeight, M.Palette from Frames as F inner join Events as E on F.EventId = E.Id inner join Monitors as M on E.MonitorId = M.Id where EventId = '$event_id' group by F.EventId"; -my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); -my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); -my $event = $sth->fetchrow_hashref(); -$sth->finish(); -my $event_path = getEventPath( $event ); -chdir( $event_path ); -( my $video_name = $event->{Name} ) =~ s/\s/_/g; +my $cwd = getcwd; -my @file_parts; -if ( $rate ) -{ - my $file_rate = $rate; - $file_rate =~ s/\./_/; - $file_rate =~ s/_00//; - $file_rate =~ s/(_\d+)0+$/$1/; - $file_rate = 'r'.$file_rate; - push( @file_parts, $file_rate ); -} -elsif ( $fps ) -{ - my $file_fps = $fps; - $file_fps =~ s/\./_/; - $file_fps =~ s/_00//; - $file_fps =~ s/(_\d+)0+$/$1/; - $file_fps = 'R'.$file_fps; - push( @file_parts, $file_fps ); -} - -if ( $scale ) -{ - my $file_scale = $scale; - $file_scale =~ s/\./_/; - $file_scale =~ s/_00//; - $file_scale =~ s/(_\d+)0+$/$1/; - $file_scale = 's'.$file_scale; - push( @file_parts, $file_scale ); -} -elsif ( $size ) -{ - my $file_size = 'S'.$size; - push( @file_parts, $file_size ); -} -my $video_file = "$video_name-".$file_parts[0]."-".$file_parts[1].".$format"; - -if ( $overwrite || !-s $video_file ) -{ - Info( "Creating video file $video_file for event $event->{Id}\n" ); - - my $frame_rate = sprintf( "%.2f", $event->{Frames}/$event->{FullLength} ); - if ( $rate ) - { - if ( $rate != 1.0 ) - { - $frame_rate *= $rate; - } - } - elsif ( $fps ) - { - $frame_rate = $fps; - } - - my $width = $event->{MonitorWidth}; - my $height = $event->{MonitorHeight}; - my $video_size = " ${width}x${height}"; - - if ( $scale ) - { - if ( $scale != 1.0 ) - { - $width = int($width*$scale); - $height = int($height*$scale); - $video_size = " ${width}x${height}"; - } - } - elsif ( $size ) - { - $video_size = $size; - } - - my $command = $Config{ZM_PATH_FFMPEG}." -y -r $frame_rate ".$Config{ZM_FFMPEG_INPUT_OPTIONS}." -i %0".$Config{ZM_EVENT_IMAGE_DIGITS}."d-capture.jpg -s $video_size ".$Config{ZM_FFMPEG_OUTPUT_OPTIONS}." '$video_file' > ffmpeg.log 2>&1"; - Debug( $command."\n" ); - my $output = qx($command); - - my $status = $? >> 8; - if ( $status ) - { - Error( "Unable to generate video, check ".$event_path."/ffmpeg.log for details" ); - exit( -1 ); - } +my $video_name; +my @event_ids; +if ( $event_id ) { + @event_ids = ( $event_id ); - Info( "Finished $video_file\n" ); +} elsif ( $filter_name or $filter_id ) { + my $Filter = ZoneMinder::Filter->find_one( + ($filter_name ? ( Name => $filter_name ) : () ), + ($filter_id ? ( Id => $filter_name ) : () ), + ); + if ( ! $Filter ) { + Fatal("Filter $filter_name $filter_id not found."); + } + @event_ids = map { $_->{Id} }$Filter->Execute(); + Fatal( "No events found for $filter_name") if ! @event_ids; + $concat_name = $filter_name if $concat_name eq ''; } -else -{ - Info( "Video file $video_file already exists for event $event->{Id}\n" ); + +my $sql = " SELECT max(F.Delta)-min(F.Delta) as FullLength, + E.*, + unix_timestamp(E.StartTime) as Time, + M.Name as MonitorName, + M.Width as MonitorWidth, + M.Height as MonitorHeight, + M.Palette + FROM Frames as F + INNER JOIN Events as E on F.EventId = E.Id + INNER JOIN Monitors as M on E.MonitorId = M.Id + WHERE EventId = ? + GROUP BY F.EventId" + ; +my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + +my @video_files; +foreach my $event_id ( @event_ids ) { + + my $res = $sth->execute( $event_id ) + or Fatal( "Can't execute: ".$sth->errstr() ); + my $event = $sth->fetchrow_hashref(); + + my $Event = new ZoneMinder::Event( $$event{Id}, $event ); + my $video_file = $Event->GenerateVideo( $rate, $fps, $scale, $size, $overwrite, $format ); + if ( $video_file ) { + push @video_files, $video_file; + } +} # end foreach event_id + +if ( $concat_name ) { + ($cwd) = $cwd =~ /(.*)/; # detaint + chdir( $cwd ); + ($concat_name ) = $concat_name =~ /([\-A-Za-z0-9_\.]*)/; + my $concat_list_file = "/tmp/$concat_name.concat.lst"; + + my $video_file = $concat_name . '.'. $detaint_format; + + open( my $fd, '>', $concat_list_file ) or die "Can't open $concat_list_file: $!"; + foreach ( @video_files ) { + print $fd "file '$_'\n"; + } + close $fd; + my $command = $Config{ZM_PATH_FFMPEG} + . " -f concat -i $concat_list_file -c copy " + ." '$video_file' > ffmpeg.log 2>&1" + ; + Debug( $command."\n" ); + my $output = qx($command); + + my $status = $? >> 8; + + unlink $concat_list_file; + if ( $status ) + { + Error( "Unable to generate video, check /ffmpeg.log for details"); + exit(-1); + } + } +#unlink $input_file_list; #print( STDOUT $event->{MonitorId}.'/'.$event->{Id}.'/'.$video_file."\n" ); -print( STDOUT $video_file."\n" ); +#print( STDOUT $video_file."\n" ); exit( 0 ); diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index bfa8b54bd..4e2ff927c 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -20,11 +20,22 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This does some basic setup for ZoneMinder to run and then periodically -# checks the fps output of the active daemons to check they haven't -# locked up. If they have then they are killed and restarted -# + +=head1 NAME + +zmwatch.pl - ZoneMinder WatchDog Script + +=head1 SYNOPSIS + +zmwatch.pl + +=head1 DESCRIPTION + +This does some basic setup for ZoneMinder to run and then periodically +checks the fps output of the active daemons to check they haven't +locked up. If they have then they are killed and restarted + +=cut use strict; use bytes; @@ -46,22 +57,14 @@ use constant START_DELAY => 30; # To give everything else time to start use ZoneMinder; use POSIX; use DBI; -use Data::Dumper; +use autouse 'Data::Dumper'=>qw(Dumper); $| = 1; -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; -sub Usage -{ - print( " -Usage: zmwatch.pl -"); - exit( -1 ); -} - logInit(); logSetSignal(); @@ -71,78 +74,115 @@ sleep( START_DELAY ); my $dbh = zmDbConnect(); -my $sql = "select * from Monitors"; -my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); +my $sql = $Config{ZM_SERVER_ID} ? 'SELECT * FROM Monitors WHERE ServerId=?' : 'SELECT * FROM Monitors'; +my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); while( 1 ) { - my $now = time(); - my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); - while( my $monitor = $sth->fetchrow_hashref() ) - { - if ( $monitor->{Function} ne 'None' ) - { - my $restart = 0; - if ( zmMemVerify( $monitor ) && zmMemRead( $monitor, "shared_data:valid" ) ) - { - # Check we have got an image recently - my $image_time = zmGetLastWriteTime( $monitor ); - next if ( !defined($image_time) ); # Can't read from shared data - next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died. + my $now = time(); + my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () ) + or Fatal( "Can't execute: ".$sth->errstr() ); + while( my $monitor = $sth->fetchrow_hashref() ) + { + next if $monitor->{Function} eq 'None'; + my $restart = 0; + if ( zmMemVerify( $monitor ) + && zmMemRead( $monitor, "shared_data:valid" ) + ) + { + # Check we have got an image recently + my $image_time = zmGetLastWriteTime( $monitor ); + if ( !defined($image_time) ) { + # Can't read from shared data + Debug( "LastWriteTime is not defined." ); + next; + } + if ( !$image_time ) { + # We can't get the last capture time so can't be sure it's died. + Debug( "LastWriteTime is = $image_time." ); + next; + } - my $max_image_delay = ($monitor->{MaxFPS}&&($monitor->{MaxFPS}>0)&&($monitor->{MaxFPS}<1))?(3/$monitor->{MaxFPS}):$Config{ZM_WATCH_MAX_DELAY}; - my $image_delay = $now-$image_time; - Debug( "Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay\n" ); - if ( $image_delay > $max_image_delay ) - { - Info( "Restarting capture daemon for ".$monitor->{Name}.", time since last capture $image_delay seconds ($now-$image_time)\n" ); + my $max_image_delay = ( $monitor->{MaxFPS} + &&($monitor->{MaxFPS}>0) + &&($monitor->{MaxFPS}<1) + ) ? (3/$monitor->{MaxFPS}) + : $Config{ZM_WATCH_MAX_DELAY} + ; + my $image_delay = $now-$image_time; + Debug( "Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay\n" ); + if ( $image_delay > $max_image_delay ) + { + Info( "Restarting capture daemon for " + .$monitor->{Name}.", time since last capture $image_delay seconds ($now-$image_time)\n" + ); + $restart = 1; + } + } + else + { + Info( "Restarting capture daemon for ".$monitor->{Name}.", shared data not valid\n" ); + $restart = 1; + } + + if ( $restart ) + { + my $command; + if ( $monitor->{Type} eq 'Local' ) + { + $command = "zmdc.pl restart zmc -d $monitor->{Device}"; + } + else + { + $command = "zmdc.pl restart zmc -m $monitor->{Id}"; + } + runCommand( $command ); + } + elsif ( $monitor->{Function} ne 'Monitor' ) + { + # Now check analysis daemon + $restart = 0; + # Check we have got an image recently + my $image_time = zmGetLastReadTime( $monitor ); + if ( !defined($image_time) ) { + # Can't read from shared data + $restart = 1; + Error( "Error reading shared data for $$monitor{id} $$monitor{Name}\n"); + } elsif ( !$image_time ) { + # We can't get the last capture time so can't be sure it's died. + $restart = 1; + Error( "Error getting last capture time for $$monitor{id} $$monitor{Name}\n"); + } else { + + my $max_image_delay = ( $monitor->{MaxFPS} + &&($monitor->{MaxFPS}>0) + &&($monitor->{MaxFPS}<1) + ) ? (3/$monitor->{MaxFPS}) + : $Config{ZM_WATCH_MAX_DELAY} + ; + my $image_delay = $now-$image_time; + Debug( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" ); + if ( $image_delay > $max_image_delay ) + { + Info( "Analysis daemon for $$monitor{id} $$monitor{Name} needs restarting," + ." time since last analysis $image_delay seconds ($now-$image_time)\n" + ); $restart = 1; } - } - else - { - #Info( "Restarting capture daemon for ".$monitor->{Name}.", shared data not valid\n" ); - #$restart = 1; - } + } if ( $restart ) { - my $command; - if ( $monitor->{Type} eq 'Local' ) - { - $command = "zmdc.pl restart zmc -d $monitor->{Device}"; - } - else - { - $command = "zmdc.pl restart zmc -m $monitor->{Id}"; - } + Info( "Restarting analysis daemon for $$monitor{id} $$monitor{Name}\n"); + my $command = "zmdc.pl restart zma -m ".$monitor->{Id}; runCommand( $command ); - } - elsif ( $monitor->{Function} ne 'Monitor' ) - { - if ( zmMemVerify( $monitor ) && zmMemRead( $monitor, "shared_data:valid" ) ) - { - # Check we have got an image recently - my $image_time = zmGetLastReadTime( $monitor ); - next if ( !defined($image_time) ); # Can't read from shared data - next if ( !$image_time ); # We can't get the last capture time so can't be sure it's died. - - my $max_image_delay = ($monitor->{MaxFPS}&&($monitor->{MaxFPS}>0)&&($monitor->{MaxFPS}<1))?(3/$monitor->{MaxFPS}):$Config{ZM_WATCH_MAX_DELAY}; - my $image_delay = $now-$image_time; - Debug( "Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay\n" ); - if ( $image_delay > $max_image_delay ) - { - Info( "Restarting analysis daemon for ".$monitor->{Name}.", time since last analysis $image_delay seconds ($now-$image_time)\n" ); - my $command = "zmdc.pl restart zma -m ".$monitor->{Id}; - runCommand( $command ); - } - } - } - } + } # end if restart + } # end if check analysis daemon # Prevent open handles building up if we have connect to shared memory zmMemInvalidate( $monitor ); - } - sleep( $Config{ZM_WATCH_CHECK_INTERVAL} ); -} + } # end foreach monitor + sleep( $Config{ZM_WATCH_CHECK_INTERVAL} ); +} # end while (1) Info( "Watchdog exiting\n" ); exit(); diff --git a/scripts/zmx10.pl.in b/scripts/zmx10.pl.in index f661e656f..8daa63c87 100644 --- a/scripts/zmx10.pl.in +++ b/scripts/zmx10.pl.in @@ -20,10 +20,28 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ========================================================================== -# -# This script controls the monitoring of the X10 interface and the consequent -# management of the ZM daemons based on the receipt of X10 signals. -# + +=head1 NAME + +zmx10.pl - ZoneMinder X10 Control Script + +=head1 SYNOPSIS + + zmx10.pl -c ,--command= [-u ,--unit-code=] + +=head1 DESCRIPTION + +This script controls the monitoring of the X10 interface and the consequent +management of the ZM daemons based on the receipt of X10 signals. + +=head1 OPTIONS + + -c , --command= - Command to issue, one of 'on','off','dim','bright','status','shutdown' + -u , --unit-code= - Unit code to act on required for all commands + except 'status' (optional) and 'shutdown' + -v, --verison - Pirnts the currently installed version of ZoneMinder + +=cut use strict; use bytes; @@ -46,76 +64,77 @@ use ZoneMinder; use POSIX; use Socket; use Getopt::Long; -use Data::Dumper; +use autouse 'Pod::Usage'=>qw(pod2usage); +use autouse 'Data::Dumper'=>qw(Dumper); use constant SOCK_FILE => $Config{ZM_PATH_SOCKS}.'/zmx10.sock'; $| = 1; -$ENV{PATH} = '/bin:/usr/bin'; +$ENV{PATH} = '/bin:/usr/bin:/usr/local/bin'; $ENV{SHELL} = '/bin/sh' if exists $ENV{SHELL}; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; -sub Usage -{ - print( " -Usage: zmx10.pl -c ,--command= [-u ,--unit-code=] -Parameters are :- --c , --command= - Command to issue, one of 'on','off','dim','bright','status','shutdown' --u , --unit-code= - Unit code to act on required for all commands except 'status' (optional) and 'shutdown' -"); - exit( -1 ); -} - logInit(); logSetSignal(); my $command; my $unit_code; +my $version; -if ( !GetOptions( 'command=s'=>\$command, 'unit-code=i'=>\$unit_code ) ) -{ - Usage(); +GetOptions( + 'command=s' =>\$command, + 'unit-code=i' =>\$unit_code, + 'version' =>\$version +) or pod2usage(-exitstatus => -1); + +if ( $version ) { + print ZoneMinder::Base::ZM_VERSION; + exit(0); } die( "No command given" ) unless( $command ); -die( "No unit code given" ) unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) ); +die( "No unit code given" ) + unless( $unit_code || ($command =~ /(?:start|status|shutdown)/) ); if ( $command eq "start" ) { - X10Server::runServer(); - exit(); + X10Server::runServer(); + exit(); } -socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); +socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) + or Fatal( "Can't open socket: $!" ); my $saddr = sockaddr_un( SOCK_FILE ); if ( !connect( CLIENT, $saddr ) ) { - # The server isn't there - print( "Unable to connect, starting server\n" ); - close( CLIENT ); + # The server isn't there + print( "Unable to connect, starting server\n" ); + close( CLIENT ); - if ( my $cpid = fork() ) - { - # Parent process just sleep and fall through - sleep( 2 ); + if ( my $cpid = fork() ) + { + # Parent process just sleep and fall through + sleep( 2 ); logReinit(); - socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); - connect( CLIENT, $saddr ) or Fatal( "Can't connect: $!" ); - } - elsif ( defined($cpid) ) - { - setpgrp(); + socket( CLIENT, PF_UNIX, SOCK_STREAM, 0 ) + or Fatal( "Can't open socket: $!" ); + connect( CLIENT, $saddr ) + or Fatal( "Can't connect: $!" ); + } + elsif ( defined($cpid) ) + { + setpgrp(); logReinit(); - X10Server::runServer(); - } - else - { - Fatal( "Can't fork: $!" ); - } + X10Server::runServer(); + } + else + { + Fatal( "Can't fork: $!" ); + } } # The server is there, connect to it #print( "Writing commands\n" ); @@ -126,8 +145,8 @@ print( CLIENT $message ); shutdown( CLIENT, 1 ); while ( my $line = ) { - chomp( $line ); - print( "$line\n" ); + chomp( $line ); + print( "$line\n" ); } close( CLIENT ); #print( "Finished writing, bye\n" ); @@ -150,7 +169,7 @@ use POSIX; use DBI; use Socket; use X10::ActiveHome; -use Data::Dumper; +use autouse 'Data::Dumper'=>qw(Dumper); our $dbh; our $x10; @@ -161,515 +180,599 @@ our %pending_tasks; sub runServer { - Info( "X10 server starting\n" ); + Info( "X10 server starting\n" ); - socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) or Fatal( "Can't open socket: $!" ); - unlink( main::SOCK_FILE ); - my $saddr = sockaddr_un( main::SOCK_FILE ); - bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" ); - listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); + socket( SERVER, PF_UNIX, SOCK_STREAM, 0 ) + or Fatal( "Can't open socket: $!" ); + unlink( main::SOCK_FILE ); + my $saddr = sockaddr_un( main::SOCK_FILE ); + bind( SERVER, $saddr ) or Fatal( "Can't bind: $!" ); + listen( SERVER, SOMAXCONN ) or Fatal( "Can't listen: $!" ); - $dbh = zmDbConnect(); + $dbh = zmDbConnect(); - $x10 = new X10::ActiveHome( port=>ZM_X10_DEVICE, house_code=>ZM_X10_HOUSE_CODE, debug=>0 ); + $x10 = new X10::ActiveHome( port=>$Config{ZM_X10_DEVICE}, house_code=>$Config{ZM_X10_HOUSE_CODE}, debug=>0 ); - loadTasks(); + loadTasks(); - $x10->register_listener( \&x10listen ); + $x10->register_listener( \&x10listen ); - my $rin = ''; - vec( $rin, fileno(SERVER),1) = 1; - vec( $rin, $x10->select_fds(),1) = 1; - my $timeout = 0.2; - #print( "F:".fileno(SERVER)."\n" ); - my $reload = undef; - my $reload_count = 0; - my $reload_limit = &ZM_X10_DB_RELOAD_INTERVAL / $timeout; - while( 1 ) - { - my $nfound = select( my $rout = $rin, undef, undef, $timeout ); - #print( "Off select, NF:$nfound, ER:$!\n" ); - #print( vec( $rout, fileno(SERVER),1)."\n" ); - #print( vec( $rout, $x10->select_fds(),1)."\n" ); - if ( $nfound > 0 ) - { - if ( vec( $rout, fileno(SERVER),1) ) - { - my $paddr = accept( CLIENT, SERVER ); - my $message = ; + my $rin = ''; + vec( $rin, fileno(SERVER),1) = 1; + vec( $rin, $x10->select_fds(),1) = 1; + my $timeout = 0.2; + #print( "F:".fileno(SERVER)."\n" ); + my $reload = undef; + my $reload_count = 0; + my $reload_limit = $Config{ZM_X10_DB_RELOAD_INTERVAL} / $timeout; + while( 1 ) + { + my $nfound = select( my $rout = $rin, undef, undef, $timeout ); + #print( "Off select, NF:$nfound, ER:$!\n" ); + #print( vec( $rout, fileno(SERVER),1)."\n" ); + #print( vec( $rout, $x10->select_fds(),1)."\n" ); + if ( $nfound > 0 ) + { + if ( vec( $rout, fileno(SERVER),1) ) + { + my $paddr = accept( CLIENT, SERVER ); + my $message = ; - my ( $command, $unit_code ) = split( /;/, $message ); + my ( $command, $unit_code ) = split( /;/, $message ); - my $device; - if ( defined($unit_code) ) - { - if ( $unit_code < 1 || $unit_code > 16 ) - { - dPrint( ZoneMinder::Logger::ERROR, "Invalid unit code '$unit_code'\n" ); - next; - } + my $device; + if ( defined($unit_code) ) + { + if ( $unit_code < 1 || $unit_code > 16 ) + { + dPrint( ZoneMinder::Logger::ERROR, "Invalid unit code '$unit_code'\n" ); + next; + } - $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; - } - } + $device = $device_hash{$unit_code}; + if ( !$device ) + { + $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), + status=>'unknown' + }; + } + } - my $result; - if ( $command eq 'on' ) - { - $result = $device->{appliance}->on(); - } - elsif ( $command eq 'off' ) - { - $result = $device->{appliance}->off(); - } - #elsif ( $command eq 'dim' ) - #{ - #$result = $device->{appliance}->dim(); - #} - #elsif ( $command eq 'bright' ) - #{ - #$result = $device->{appliance}->bright(); - #} - elsif ( $command eq 'status' ) - { - if ( $device ) - { - dPrint( ZoneMinder::Logger::DEBUG, $unit_code." ".$device->{status}."\n" ); - } - else - { - foreach my $unit_code ( sort( keys(%device_hash) ) ) - { - my $device = $device_hash{$unit_code}; - dPrint( ZoneMinder::Logger::DEBUG, $unit_code." ".$device->{status}."\n" ); - } - } - } - elsif ( $command eq 'shutdown' ) - { - last; - } - else - { - dPrint( ZoneMinder::Logger::ERROR, "Invalid command '$command'\n" ); - } - if ( defined($result) ) - { - if ( 1 || $result ) - { - $device->{status} = uc($command); - dPrint( ZoneMinder::Logger::DEBUG, $device->{appliance}->address()." $command, ok\n" ); - #x10listen( new X10::Event( sprintf("%s %s", $device->{appliance}->address, uc($command) ) ) ); - } - else - { - dPrint( ZoneMinder::Logger::ERROR, $device->{appliance}->address()." $command, failed\n" ); - } - } - close( CLIENT ); - } - elsif ( vec( $rout, $x10->select_fds(),1) ) - { - $x10->handle_input(); - } - else - { - Fatal( "Bogus descriptor" ); - } - } - elsif ( $nfound < 0 ) - { - if ( $! != EINTR ) - { - Fatal( "Can't select: $!" ); - } - } - else - { - #print( "Select timed out\n" ); - # Check for state changes - foreach my $monitor_id ( sort(keys(%monitor_hash) ) ) - { - my $monitor = $monitor_hash{$monitor_id}; - my $state = zmGetMonitorState( $monitor ); - if ( !defined($state) ) - { - $reload = !undef; - next; - } - if ( defined( $monitor->{LastState} ) ) - { - my $task_list; - if ( ($state == STATE_ALARM || $state == STATE_ALERT) && ($monitor->{LastState} == STATE_IDLE || $monitor->{LastState} == STATE_TAPE) ) # Gone into alarm state - { - Debug( "Applying ON_list for $monitor_id\n" ); - $task_list = $monitor->{"ON_list"}; - } - elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE) || ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE) ) # Come out of alarm state - { - Debug( "Applying OFF_list for $monitor_id\n" ); - $task_list = $monitor->{"OFF_list"}; - } - if ( $task_list ) - { - foreach my $task ( @$task_list ) - { - processTask( $task ); - } - } - } - $monitor->{LastState} = $state; - } + my $result; + if ( $command eq 'on' ) + { + $result = $device->{appliance}->on(); + } + elsif ( $command eq 'off' ) + { + $result = $device->{appliance}->off(); + } + #elsif ( $command eq 'dim' ) + #{ + #$result = $device->{appliance}->dim(); + #} + #elsif ( $command eq 'bright' ) + #{ + #$result = $device->{appliance}->bright(); + #} + elsif ( $command eq 'status' ) + { + if ( $device ) + { + dPrint( ZoneMinder::Logger::DEBUG, $unit_code." ".$device->{status}."\n" ); + } + else + { + foreach my $unit_code ( sort( keys(%device_hash) ) ) + { + my $device = $device_hash{$unit_code}; + dPrint( ZoneMinder::Logger::DEBUG, $unit_code." ".$device->{status}."\n" ); + } + } + } + elsif ( $command eq 'shutdown' ) + { + last; + } + else + { + dPrint( ZoneMinder::Logger::ERROR, "Invalid command '$command'\n" ); + } + if ( defined($result) ) + { + if ( 1 || $result ) + { + $device->{status} = uc($command); + dPrint( ZoneMinder::Logger::DEBUG, $device->{appliance}->address()." $command, ok\n" ); + #x10listen( new X10::Event( sprintf("%s %s", $device->{appliance}->address, uc($command) ) ) ); + } + else + { + dPrint( ZoneMinder::Logger::ERROR, $device->{appliance}->address()." $command, failed\n" ); + } + } + close( CLIENT ); + } + elsif ( vec( $rout, $x10->select_fds(),1) ) + { + $x10->handle_input(); + } + else + { + Fatal( "Bogus descriptor" ); + } + } + elsif ( $nfound < 0 ) + { + if ( $! != EINTR ) + { + Fatal( "Can't select: $!" ); + } + } + else + { + #print( "Select timed out\n" ); + # Check for state changes + foreach my $monitor_id ( sort(keys(%monitor_hash) ) ) + { + my $monitor = $monitor_hash{$monitor_id}; + my $state = zmGetMonitorState( $monitor ); + if ( !defined($state) ) + { + $reload = !undef; + next; + } + if ( defined( $monitor->{LastState} ) ) + { + my $task_list; + if ( ($state == STATE_ALARM || $state == STATE_ALERT) + && ($monitor->{LastState} == STATE_IDLE || $monitor->{LastState} == STATE_TAPE) + ) # Gone into alarm state + { + Debug( "Applying ON_list for $monitor_id\n" ); + $task_list = $monitor->{"ON_list"}; + } + elsif ( ($state == STATE_IDLE && $monitor->{LastState} != STATE_IDLE) + || ($state == STATE_TAPE && $monitor->{LastState} != STATE_TAPE) + ) # Come out of alarm state + { + Debug( "Applying OFF_list for $monitor_id\n" ); + $task_list = $monitor->{"OFF_list"}; + } + if ( $task_list ) + { + foreach my $task ( @$task_list ) + { + processTask( $task ); + } + } + } + $monitor->{LastState} = $state; + } - # Check for pending tasks - my $now = time(); - foreach my $activation_time ( sort(keys(%pending_tasks) ) ) - { - last if ( $activation_time > $now ); - my $pending_list = $pending_tasks{$activation_time}; - foreach my $task ( @$pending_list ) - { - processTask( $task ); - } - delete( $pending_tasks{$activation_time} ); - } - if ( $reload || ++$reload_count >= $reload_limit ) - { - loadTasks(); - $reload = undef; - $reload_count = 0; - } - } - } - Info( "X10 server exiting\n" ); - close( SERVER ); - exit(); + # Check for pending tasks + my $now = time(); + foreach my $activation_time ( sort(keys(%pending_tasks) ) ) + { + last if ( $activation_time > $now ); + my $pending_list = $pending_tasks{$activation_time}; + foreach my $task ( @$pending_list ) + { + processTask( $task ); + } + delete( $pending_tasks{$activation_time} ); + } + if ( $reload || ++$reload_count >= $reload_limit ) + { + loadTasks(); + $reload = undef; + $reload_count = 0; + } + } + } + Info( "X10 server exiting\n" ); + close( SERVER ); + exit(); } sub addToDeviceList { - my $unit_code = shift; - my $event = shift; - my $monitor = shift; - my $function = shift; - my $limit = shift; + my $unit_code = shift; + my $event = shift; + my $monitor = shift; + my $function = shift; + my $limit = shift; - Debug( "Adding to device list, uc:$unit_code, ev:$event, mo:".$monitor->{Id}.", fu:$function, li:$limit\n" ); - my $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; - } + Debug( "Adding to device list, uc:$unit_code, ev:$event, mo:" + .$monitor->{Id}.", fu:$function, li:$limit\n" + ); + my $device = $device_hash{$unit_code}; + if ( !$device ) + { + $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), + status=>'unknown' + }; + } - my $task = { type=>"device", monitor=>$monitor, address=>$device->{appliance}->address(), function=>$function }; - if ( $limit ) - { - $task->{limit} = $limit - } + my $task = { type=>"device", + monitor=>$monitor, + address=>$device->{appliance}->address(), + function=>$function + }; + if ( $limit ) + { + $task->{limit} = $limit + } - my $task_list = $device->{$event."_list"}; - if ( !$task_list ) - { - $task_list = $device->{$event."_list"} = []; - } - push( @$task_list, $task ); + my $task_list = $device->{$event."_list"}; + if ( !$task_list ) + { + $task_list = $device->{$event."_list"} = []; + } + push( @$task_list, $task ); } sub addToMonitorList { - my $monitor = shift; - my $event = shift; - my $unit_code = shift; - my $function = shift; - my $limit = shift; + my $monitor = shift; + my $event = shift; + my $unit_code = shift; + my $function = shift; + my $limit = shift; - Debug( "Adding to monitor list, uc:$unit_code, ev:$event, mo:".$monitor->{Id}.", fu:$function, li:$limit\n" ); - my $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; - } + Debug( "Adding to monitor list, uc:$unit_code, ev:$event, mo:".$monitor->{Id} + .", fu:$function, li:$limit\n" + ); + my $device = $device_hash{$unit_code}; + if ( !$device ) + { + $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), + status=>'unknown' + }; + } - my $task = { type=>"monitor", device=>$device, id=>$monitor->{Id}, function=>$function }; - if ( $limit ) - { - $task->{limit} = $limit; - } + my $task = { type=>"monitor", + device=>$device, + id=>$monitor->{Id}, + function=>$function + }; + if ( $limit ) + { + $task->{limit} = $limit; + } - my $task_list = $monitor->{$event."_list"}; - if ( !$task_list ) - { - $task_list = $monitor->{$event."_list"} = []; - } - push( @$task_list, $task ); + my $task_list = $monitor->{$event."_list"}; + if ( !$task_list ) + { + $task_list = $monitor->{$event."_list"} = []; + } + push( @$task_list, $task ); } sub loadTasks { - %monitor_hash = (); + %monitor_hash = (); - Debug( "Loading tasks\n" ); - # Clear out all old device task lists - foreach my $unit_code ( sort( keys(%device_hash) ) ) - { - my $device = $device_hash{$unit_code}; - $device->{ON_list} = []; - $device->{OFF_list} = []; - } + Debug( "Loading tasks\n" ); + # Clear out all old device task lists + foreach my $unit_code ( sort( keys(%device_hash) ) ) + { + my $device = $device_hash{$unit_code}; + $device->{ON_list} = []; + $device->{OFF_list} = []; + } - my $sql = "select M.*,T.* from Monitors as M inner join TriggersX10 as T on (M.Id = T.MonitorId) where find_in_set( M.Function, 'Modect,Record,Mocord,Nodect' ) and M.Enabled = 1 and find_in_set( 'X10', M.Triggers )"; - my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); - my $res = $sth->execute() or Fatal( "Can't execute: ".$sth->errstr() ); - while( my $monitor = $sth->fetchrow_hashref() ) - { - next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok + my $sql = "SELECT M.*,T.* from Monitors as M + INNER JOIN TriggersX10 as T on (M.Id = T.MonitorId) + WHERE find_in_set( M.Function, 'Modect,Record,Mocord,Nodect' ) + AND M.Enabled = 1 + AND find_IN_set( 'X10', M.Triggers )" + ; + my $sth = $dbh->prepare_cached( $sql ) + or Fatal( "Can't prepare '$sql': ".$dbh->errstr() ); + my $res = $sth->execute() + or Fatal( "Can't execute: ".$sth->errstr() ); + while( my $monitor = $sth->fetchrow_hashref() ) + { + next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok - $monitor_hash{$monitor->{Id}} = $monitor; + $monitor_hash{$monitor->{Id}} = $monitor; - if ( $monitor->{Activation} ) - { - Debug( "$monitor->{Name} has active string '$monitor->{Activation}'\n" ); - foreach my $code_string ( split( /,/, $monitor->{Activation} ) ) - { - #Debug( "Code string: $code_string\n" ); - my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); - $limit = 0 if ( !$limit ); - if ( $unit_code ) - { - if ( !$modifier || $modifier eq '+' ) - { - addToDeviceList( $unit_code, "ON", $monitor, !$invert?"start_active":"stop_active", $limit ); - } - if ( !$modifier || $modifier eq '-' ) - { - addToDeviceList( $unit_code, "OFF", $monitor, !$invert?"stop_active":"start_active", $limit ); - } - } - } - } - if ( $monitor->{AlarmInput} ) - { - Debug( "$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'\n" ); - foreach my $code_string ( split( /,/, $monitor->{AlarmInput} ) ) - { - #Debug( "Code string: $code_string\n" ); - my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); - $limit = 0 if ( !$limit ); - if ( $unit_code ) - { - if ( !$modifier || $modifier eq '+' ) - { - addToDeviceList( $unit_code, "ON", $monitor, !$invert?"start_alarm":"stop_alarm", $limit ); - } - if ( !$modifier || $modifier eq '-' ) - { - addToDeviceList( $unit_code, "OFF", $monitor, !$invert?"stop_alarm":"start_alarm", $limit ); - } - } - } - } - if ( $monitor->{AlarmOutput} ) - { - Debug( "$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'\n" ); - foreach my $code_string ( split( /,/, $monitor->{AlarmOutput} ) ) - { - #Debug( "Code string: $code_string\n" ); - my ( $invert, $unit_code, $modifier, $limit ) = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); - $limit = 0 if ( !$limit ); - if ( $unit_code ) - { - if ( !$modifier || $modifier eq '+' ) - { - addToMonitorList( $monitor, "ON", $unit_code, !$invert?"on":"off", $limit ); - } - if ( !$modifier || $modifier eq '-' ) - { - addToMonitorList( $monitor, "OFF", $unit_code, !$invert?"off":"on", $limit ); - } - } - } - } + if ( $monitor->{Activation} ) + { + Debug( "$monitor->{Name} has active string '$monitor->{Activation}'\n" ); + foreach my $code_string ( split( /,/, $monitor->{Activation} ) ) + { + #Debug( "Code string: $code_string\n" ); + my ( $invert, $unit_code, $modifier, $limit ) + = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); + $limit = 0 if ( !$limit ); + if ( $unit_code ) + { + if ( !$modifier || $modifier eq '+' ) + { + addToDeviceList( $unit_code, + "ON", + $monitor, + !$invert ? "start_active" + : "stop_active", + $limit + ); + } + if ( !$modifier || $modifier eq '-' ) + { + addToDeviceList( $unit_code, + "OFF", + $monitor, + !$invert ? "stop_active" + : "start_active", + $limit + ); + } + } + } + } + if ( $monitor->{AlarmInput} ) + { + Debug( "$monitor->{Name} has alarm input string '$monitor->{AlarmInput}'\n" ); + foreach my $code_string ( split( /,/, $monitor->{AlarmInput} ) ) + { + #Debug( "Code string: $code_string\n" ); + my ( $invert, $unit_code, $modifier, $limit ) + = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); + $limit = 0 if ( !$limit ); + if ( $unit_code ) + { + if ( !$modifier || $modifier eq '+' ) + { + addToDeviceList( $unit_code, + "ON", + $monitor, + !$invert ? "start_alarm" + : "stop_alarm", + $limit + ); + } + if ( !$modifier || $modifier eq '-' ) + { + addToDeviceList( $unit_code, + "OFF", + $monitor, + !$invert ? "stop_alarm" + : "start_alarm", + $limit + ); + } + } + } + } + if ( $monitor->{AlarmOutput} ) + { + Debug( "$monitor->{Name} has alarm output string '$monitor->{AlarmOutput}'\n" ); + foreach my $code_string ( split( /,/, $monitor->{AlarmOutput} ) ) + { + #Debug( "Code string: $code_string\n" ); + my ( $invert, $unit_code, $modifier, $limit ) + = ( $code_string =~ /^([!~])?(\d+)(?:([+-])(\d+)?)?$/ ); + $limit = 0 if ( !$limit ); + if ( $unit_code ) + { + if ( !$modifier || $modifier eq '+' ) + { + addToMonitorList( $monitor, + "ON", + $unit_code, + !$invert ? "on" + : "off", + $limit + ); + } + if ( !$modifier || $modifier eq '-' ) + { + addToMonitorList( $monitor, + "OFF", + $unit_code, + !$invert ? "off" + : "on", + $limit + ); + } + } + } + } zmMemInvalidate( $monitor ); - } + } } sub addPendingTask { - my $task = shift; + my $task = shift; - # Check whether we are just extending a previous pending task - # and remove it if it's there - foreach my $activation_time ( sort(keys(%pending_tasks) ) ) - { - my $pending_list = $pending_tasks{$activation_time}; - my $new_pending_list = []; - foreach my $pending_task ( @$pending_list ) - { - if ( $task->{type} ne $pending_task->{type} ) - { - push( @$new_pending_list, $pending_task ) - } - elsif ( $task->{type} eq "device" ) - { - if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} ) - || ( $task->{function} ne $pending_task->{function} )) - { - push( @$new_pending_list, $pending_task ) - } - } - elsif ( $task->{type} eq "monitor" ) - { - if (( $task->{device}->{appliance}->unit_code() != $pending_task->{device}->{appliance}->unit_code() ) - || ( $task->{function} ne $pending_task->{function} )) - { - push( @$new_pending_list, $pending_task ) - } - } - } - if ( @$new_pending_list ) - { - $pending_tasks{$activation_time} = $new_pending_list; - } - else - { - delete( $pending_tasks{$activation_time} ); - } - } + # Check whether we are just extending a previous pending task + # and remove it if it's there + foreach my $activation_time ( sort(keys(%pending_tasks) ) ) + { + my $pending_list = $pending_tasks{$activation_time}; + my $new_pending_list = []; + foreach my $pending_task ( @$pending_list ) + { + if ( $task->{type} ne $pending_task->{type} ) + { + push( @$new_pending_list, $pending_task ) + } + elsif ( $task->{type} eq "device" ) + { + if (( $task->{monitor}->{Id} != $pending_task->{monitor}->{Id} ) + || ( $task->{function} ne $pending_task->{function} )) + { + push( @$new_pending_list, $pending_task ) + } + } + elsif ( $task->{type} eq "monitor" ) + { + if (( $task->{device}->{appliance}->unit_code() + != $pending_task->{device}->{appliance}->unit_code() + ) + || ( $task->{function} ne $pending_task->{function} ) + ) + { + push( @$new_pending_list, $pending_task ) + } + } + } + if ( @$new_pending_list ) + { + $pending_tasks{$activation_time} = $new_pending_list; + } + else + { + delete( $pending_tasks{$activation_time} ); + } + } - my $end_time = time() + $task->{limit}; - my $pending_list = $pending_tasks{$end_time}; - if ( !$pending_list ) - { - $pending_list = $pending_tasks{$end_time} = []; - } - my $pending_task; - if ( $task->{type} eq "device" ) - { - $pending_task = { type=>$task->{type}, monitor=>$task->{monitor}, function=>$task->{function} }; - $pending_task->{function} =~ s/start/stop/; - } - elsif ( $task->{type} eq "monitor" ) - { - $pending_task = { type=>$task->{type}, device=>$task->{device}, function=>$task->{function} }; - $pending_task->{function} =~ s/on/off/; - } - push( @$pending_list, $pending_task ); + my $end_time = time() + $task->{limit}; + my $pending_list = $pending_tasks{$end_time}; + if ( !$pending_list ) + { + $pending_list = $pending_tasks{$end_time} = []; + } + my $pending_task; + if ( $task->{type} eq "device" ) + { + $pending_task = { type=>$task->{type}, + monitor=>$task->{monitor}, + function=>$task->{function} + }; + $pending_task->{function} =~ s/start/stop/; + } + elsif ( $task->{type} eq "monitor" ) + { + $pending_task = { type=>$task->{type}, + device=>$task->{device}, + function=>$task->{function} + }; + $pending_task->{function} =~ s/on/off/; + } + push( @$pending_list, $pending_task ); } sub processTask { - my $task = shift; + my $task = shift; - if ( $task->{type} eq "device" ) - { - my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ ); + if ( $task->{type} eq "device" ) + { + my ( $instruction, $class ) = ( $task->{function} =~ /^(.+)_(.+)$/ ); - if ( $class eq "active" ) - { - if ( $instruction eq "start" ) - { - zmMonitorEnable( $task->{monitor} ); - if ( $task->{limit} ) - { - addPendingTask( $task ); - } - } - elsif( $instruction eq "stop" ) - { - zmMonitorDisable( $task->{monitor} ); - } - } - elsif( $class eq "alarm" ) - { - if ( $instruction eq "start" ) - { - zmTriggerEventOn( $task->{monitor}, 0, main::CAUSE_STRING, $task->{address} ); - if ( $task->{limit} ) - { - addPendingTask( $task ); - } - } - elsif( $instruction eq "stop" ) - { - zmTriggerEventCancel( $task->{monitor} ); - } - } - } - elsif( $task->{type} eq "monitor" ) - { - if ( $task->{function} eq "on" ) - { - $task->{device}->{appliance}->on(); - if ( $task->{limit} ) - { - addPendingTask( $task ); - } - } - elsif ( $task->{function} eq "off" ) - { - $task->{device}->{appliance}->off(); - } - } + if ( $class eq "active" ) + { + if ( $instruction eq "start" ) + { + zmMonitorEnable( $task->{monitor} ); + if ( $task->{limit} ) + { + addPendingTask( $task ); + } + } + elsif( $instruction eq "stop" ) + { + zmMonitorDisable( $task->{monitor} ); + } + } + elsif( $class eq "alarm" ) + { + if ( $instruction eq "start" ) + { + zmTriggerEventOn( $task->{monitor}, + 0, + main::CAUSE_STRING, + $task->{address} + ); + if ( $task->{limit} ) + { + addPendingTask( $task ); + } + } + elsif( $instruction eq "stop" ) + { + zmTriggerEventCancel( $task->{monitor} ); + } + } + } + elsif( $task->{type} eq "monitor" ) + { + if ( $task->{function} eq "on" ) + { + $task->{device}->{appliance}->on(); + if ( $task->{limit} ) + { + addPendingTask( $task ); + } + } + elsif ( $task->{function} eq "off" ) + { + $task->{device}->{appliance}->off(); + } + } } sub dPrint { - my $dbg_level = shift; - if ( fileno(CLIENT) ) - { - print CLIENT @_ - } - if ( $dbg_level == ZoneMinder::Logger::DEBUG ) - { - Debug( @_ ); - } - elsif ( $dbg_level == ZoneMinder::Logger::INFO ) - { - Info( @_ ); - } - elsif ( $dbg_level == ZoneMinder::Logger::WARNING ) - { - Warning( @_ ); - } - elsif ( $dbg_level == ZoneMinder::Logger::ERROR ) - { - Error( @_ ); - } - elsif ( $dbg_level == ZoneMinder::Logger::FATAL ) - { - Fatal( @_ ); - } + my $dbg_level = shift; + if ( fileno(CLIENT) ) + { + print CLIENT @_ + } + if ( $dbg_level == ZoneMinder::Logger::DEBUG ) + { + Debug( @_ ); + } + elsif ( $dbg_level == ZoneMinder::Logger::INFO ) + { + Info( @_ ); + } + elsif ( $dbg_level == ZoneMinder::Logger::WARNING ) + { + Warning( @_ ); + } + elsif ( $dbg_level == ZoneMinder::Logger::ERROR ) + { + Error( @_ ); + } + elsif ( $dbg_level == ZoneMinder::Logger::FATAL ) + { + Fatal( @_ ); + } } sub x10listen { - foreach my $event ( @_ ) - { - #print( Data::Dumper( $_ )."\n" ); - if ( $event->house_code() eq ZM_X10_HOUSE_CODE ) - { - my $unit_code = $event->unit_code(); - my $device = $device_hash{$unit_code}; - if ( !$device ) - { - $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), status=>'unknown' }; - } - next if ( $event->func() !~ /(?:ON|OFF)/ ); - $device->{status} = $event->func(); - my $task_list = $device->{$event->func()."_list"}; - if ( $task_list ) - { - foreach my $task ( @$task_list ) - { - processTask( $task ); - } - } - } - Info( "Got event - ".$event->as_string()."\n" ); - } + foreach my $event ( @_ ) + { + #print( Data::Dumper( $_ )."\n" ); + if ( $event->house_code() eq $Config{ZM_X10_HOUSE_CODE} ) + { + my $unit_code = $event->unit_code(); + my $device = $device_hash{$unit_code}; + if ( !$device ) + { + $device = $device_hash{$unit_code} = { appliance=>$x10->Appliance( unit_code=>$unit_code ), + status=>'unknown' + }; + } + next if ( $event->func() !~ /(?:ON|OFF)/ ); + $device->{status} = $event->func(); + my $task_list = $device->{$event->func()."_list"}; + if ( $task_list ) + { + foreach my $task ( @$task_list ) + { + processTask( $task ); + } + } + } + Info( "Got event - ".$event->as_string()."\n" ); + } } 1; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0fedc2af9..9876e2a87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,18 +14,20 @@ add_executable(zma zma.cpp) add_executable(zmu zmu.cpp) add_executable(zmf zmf.cpp) add_executable(zms zms.cpp) -add_executable(nph-zms zms.cpp) -add_executable(zmstreamer zmstreamer.cpp) target_link_libraries(zmc zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zma zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zmu zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zmf zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) target_link_libraries(zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) -target_link_libraries(nph-zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) -target_link_libraries(zmstreamer zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS}) -install(TARGETS zmc zma zmu zmf zmstreamer RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) -install(TARGETS zms nph-zms RUNTIME DESTINATION "${ZM_CGIDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +# Generate man files for the binaries destined for the bin folder +FOREACH(CBINARY zma zmc zmf zmu) + POD2MAN(${CMAKE_CURRENT_SOURCE_DIR}/${CBINARY}.cpp zoneminder-${CBINARY} 8) +ENDFOREACH(CBINARY zma zmc zmf zmu) +install(TARGETS zmc zma zmu zmf RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(TARGETS zms RUNTIME DESTINATION "${ZM_CGIDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +install(CODE "execute_process(COMMAND ln -sf zms nph-zms WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})" ) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/nph-zms DESTINATION "${ZM_CGIDIR}") diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index 9314daac0..000000000 --- a/src/Makefile.am +++ /dev/null @@ -1,135 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -AM_CPPFLAGS = @MYSQL_CFLAGS@ @MARIADB_CFLAGS@ @FFMPEG_CFLAGS@ -Wall -finline-functions -fomit-frame-pointer -#AM_CXXFLAGS = -frepo - -CLEANFILES = *.rpo - -# This should be set to your CGI directory -cgidir = @CGI_PREFIX@ -# And these to the user and group of your webserver -webuser = @WEB_USER@ -webgroup = @WEB_GROUP@ - -bin_PROGRAMS = \ - zmc \ - zma \ - zmu \ - zms \ - zmf \ - zmstreamer - -zm_SOURCES = \ - zm_box.cpp \ - zm_buffer.cpp \ - zm_camera.cpp \ - zm_comms.cpp \ - zm_config.cpp \ - zm_coord.cpp \ - zm_curl_camera.cpp \ - zm.cpp \ - zm_db.cpp \ - zm_logger.cpp \ - zm_event.cpp \ - zm_exception.cpp \ - zm_file_camera.cpp \ - zm_ffmpeg_camera.cpp \ - zm_image.cpp \ - zm_jpeg.cpp \ - zm_libvlc_camera.cpp \ - zm_local_camera.cpp \ - zm_monitor.cpp \ - zm_ffmpeg.cpp \ - zm_mpeg.cpp \ - zm_poly.cpp \ - zm_regexp.cpp \ - zm_remote_camera.cpp \ - zm_remote_camera_http.cpp \ - zm_remote_camera_rtsp.cpp \ - zm_rtp.cpp \ - zm_rtp_ctrl.cpp \ - zm_rtp_data.cpp \ - zm_rtp_source.cpp \ - zm_rtsp.cpp \ - zm_rtsp_auth.cpp \ - zm_sdp.cpp \ - zm_signal.cpp \ - zm_stream.cpp \ - zm_thread.cpp \ - zm_time.cpp \ - zm_timer.cpp \ - zm_user.cpp \ - zm_utils.cpp \ - zm_zone.cpp - -zmc_SOURCES = zmc.cpp $(zm_SOURCES) -zma_SOURCES = zma.cpp $(zm_SOURCES) -zms_SOURCES = zms.cpp $(zm_SOURCES) -zmu_SOURCES = zmu.cpp $(zm_SOURCES) -zmf_SOURCES = zmf.cpp $(zm_SOURCES) -zmstreamer_SOURCES = zmstreamer.cpp $(zm_SOURCES) - -noinst_HEADERS = \ - jinclude.h \ - zm_box.h \ - zm_buffer.h \ - zm_camera.h \ - zm_comms.h \ - zm_config_defines.h \ - zm_config.h \ - zm_coord.h \ - zm_curl_camera.h \ - zm_db.h \ - zm_logger.h \ - zm_event.h \ - zm_exception.h \ - zmf.h \ - zm_file_camera.h \ - zm_ffmpeg_camera.h \ - zm_font.h \ - zm_font.h \ - zm.h \ - zm_image.h \ - zm_jpeg.h \ - zm_libvlc_camera.h \ - zm_local_camera.h \ - zm_mem_utils.h \ - zm_monitor.h \ - zm_ffmpeg.h \ - zm_mpeg.h \ - zm_poly.h \ - zm_regexp.h \ - zm_remote_camera.h \ - zm_remote_camera_http.h \ - zm_remote_camera_rtsp.h \ - zm_rgb.h \ - zm_rtp_ctrl.h \ - zm_rtp_data.h \ - zm_rtp.h \ - zm_rtp_source.h \ - zm_rtsp.h \ - zm_sdp.h \ - zm_signal.h \ - zm_stream.h \ - zm_thread.h \ - zm_time.h \ - zm_timer.h \ - zm_user.h \ - zm_utils.h \ - zm_zone.h - -EXTRA_DIST = \ - zm_config.h.in \ - zm_threaddata.cpp - -dist-hook: - @( rm $(distdir)/zm_config.h ) - -# Yes, you are correct. This is a HACK! -install-exec-hook: - ( cd $(DESTDIR)@bindir@; mkdir -p $(DESTDIR)$(cgidir); mv zms $(DESTDIR)$(cgidir) ) - ( cd $(DESTDIR)$(cgidir); chown $(webuser):$(webgroup) zms; ln -f zms nph-zms ) - -uninstall-hook: - ( cd $(DESTDIR)$(cgidir); rm -f zms nph-zms ) - diff --git a/src/zm.h b/src/zm.h index 9e4f47b5d..0c780b244 100644 --- a/src/zm.h +++ b/src/zm.h @@ -17,14 +17,24 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +#if !defined(PATH_MAX) +#define PATH_MAX 1024 +#endif + #ifndef ZM_H #define ZM_H #include "zm_config.h" +#ifdef SOLARIS +#undef DEFAULT_TYPE // pthread defines this which breaks StreamType DEFAULT_TYPE +#include // define strerror() and friends +#endif #include "zm_logger.h" #include +#include + extern const char* self; #endif // ZM_H diff --git a/src/zm_bigfont.h b/src/zm_bigfont.h new file mode 100644 index 000000000..96ba479dd --- /dev/null +++ b/src/zm_bigfont.h @@ -0,0 +1,6155 @@ +/***********************************************************/ +/* */ +/* Font file generated by schrorg */ +/* based on the font file generated by rthelen */ +/* using utils/mk_bigfont.pl */ +/* */ +/***********************************************************/ + +static unsigned int bigfontdata[] = { + + /* 0 0x00 '^A' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 1 0x01 '^B' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 2 0x02 '^C' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 3 0x03 '^D' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 4 0x04 '^E' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 5 0x05 '^F' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 6 0x06 '^G' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 7 0x07 '^H' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 8 0x08 '^I' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 9 0x09 '^J' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 10 0x0a '^K' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 11 0x0b '^L' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 12 0x0c '^M' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 13 0x0d '^N' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 14 0x0e '^O' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 15 0x0f '^P' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 16 0x10 '^Q' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 17 0x11 '^R' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 18 0x12 '^S' */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 19 0x13 '^T' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 20 0x14 '^U' */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 21 0x15 '^V' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 22 0x16 '^W' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 23 0x17 '^X' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 24 0x18 '^Y' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 25 0x19 '^Z' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 26 0x1a '^[' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 27 0x1b '^\' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 28 0x1c '^]' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 29 0x1d '^^' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 30 0x1e '^_' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 31 0x1f '^`' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 32 0x20 ' ' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 33 0x21 '!' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 34 0x22 '"' */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 35 0x23 '#' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 36 0x24 '$' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 37 0x25 '%' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 38 0x26 '&' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 39 0x27 ''' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 40 0x28 '(' */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 41 0x29 ')' */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 42 0x2a '*' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 43 0x2b '+' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 44 0x2c ',' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 45 0x2d '-' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 46 0x2e '.' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 47 0x2f '/' */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 48 0x30 '0' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 49 0x31 '1' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x3f0, /* 000000 0000 */ + 0x3f0, /* 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 50 0x32 '2' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 51 0x33 '3' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 52 0x34 '4' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x3f0, /* 000000 0000 */ + 0x3f0, /* 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 53 0x35 '5' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 54 0x36 '6' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 55 0x37 '7' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 56 0x38 '8' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 57 0x39 '9' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 58 0x3a ':' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 59 0x3b ';' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 60 0x3c '<' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 61 0x3d '=' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 62 0x3e '>' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 63 0x3f '?' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 64 0x40 '@' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3f30, /* 00 00 0000 */ + 0x3f30, /* 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 65 0x41 'A' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 66 0x42 'B' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 67 0x43 'C' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 68 0x44 'D' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 69 0x45 'E' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 70 0x46 'F' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 71 0x47 'G' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 72 0x48 'H' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 73 0x49 'I' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 74 0x4a 'J' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 75 0x4b 'K' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3c00, /* 00 0000000000 */ + 0x3c00, /* 00 0000000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 76 0x4c 'L' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 77 0x4d 'M' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3cf0, /* 00 00 0000 */ + 0x3cf0, /* 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 78 0x4e 'N' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 79 0x4f 'O' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 80 0x50 'P' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 81 0x51 'Q' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 82 0x52 'R' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 83 0x53 'S' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 84 0x54 'T' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 85 0x55 'U' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 86 0x56 'V' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 87 0x57 'W' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3cf0, /* 00 00 0000 */ + 0x3cf0, /* 00 00 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 88 0x58 'X' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 89 0x59 'Y' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 90 0x5a 'Z' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 91 0x5b '[' */ + 0xf0, /* 00000000 0000 */ + 0xf0, /* 00000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xf0, /* 00000000 0000 */ + 0xf0, /* 00000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 92 0x5c '\' */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc, /* 000000000000 00 */ + 0xc, /* 000000000000 00 */ + 0xc, /* 000000000000 00 */ + 0xc, /* 000000000000 00 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 93 0x5d ']' */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 94 0x5e '^' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 95 0x5f '_' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ffc, /* 00 00 */ + 0x3ffc, /* 00 00 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 96 0x60 '`' */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 97 0x61 'a' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 98 0x62 'b' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 99 0x63 'c' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 100 0x64 'd' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 101 0x65 'e' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 102 0x66 'f' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf0, /* 00000000 0000 */ + 0xf0, /* 00000000 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 103 0x67 'g' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 104 0x68 'h' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 105 0x69 'i' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 106 0x6a 'j' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3c00, /* 00 0000000000 */ + 0x3c00, /* 00 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 107 0x6b 'k' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3f00, /* 00 00000000 */ + 0x3f00, /* 00 00000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 108 0x6c 'l' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 109 0x6d 'm' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 110 0x6e 'n' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 111 0x6f 'o' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 112 0x70 'p' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 113 0x71 'q' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 114 0x72 'r' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 115 0x73 's' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 116 0x74 't' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xf0, /* 00000000 0000 */ + 0xf0, /* 00000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 117 0x75 'u' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 118 0x76 'v' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 119 0x77 'w' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 120 0x78 'x' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 121 0x79 'y' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 122 0x7a 'z' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 123 0x7b '{' */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 124 0x7c '|' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 125 0x7d '}' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 126 0x7e '~' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 127 0x7f '^?' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 128 0x80 '\200' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 129 0x81 '\201' */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 130 0x82 '\202' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 131 0x83 '\203' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 132 0x84 '\204' */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 133 0x85 '\205' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 134 0x86 '\206' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 135 0x87 '\207' */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 136 0x88 '\210' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 137 0x89 '\211' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 138 0x8a '\212' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 139 0x8b '\213' */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 140 0x8c '\214' */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 141 0x8d '\215' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 142 0x8e '\216' */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 143 0x8f '\217' */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 144 0x90 '\220' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 145 0x91 '\221' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 146 0x92 '\222' */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 147 0x93 '\223' */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 148 0x94 '\224' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 149 0x95 '\225' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 150 0x96 '\226' */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 151 0x97 '\227' */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 152 0x98 '\230' */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 153 0x99 '\231' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 154 0x9a '\232' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 155 0x9b '\233' */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 156 0x9c '\234' */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 157 0x9d '\235' */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 158 0x9e '\236' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 159 0x9f '\237' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 160 0xa0 '\240' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 161 0xa1 '\241' */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 162 0xa2 '\242' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 163 0xa3 '\243' */ + 0xf00, /* 0000 00000000 */ + 0xf00, /* 0000 00000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3f00, /* 00 00000000 */ + 0x3f00, /* 00 00000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 164 0xa4 '\244' */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 165 0xa5 '\245' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 166 0xa6 '\246' */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 167 0xa7 '\247' */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 168 0xa8 '\250' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3f00, /* 00 00000000 */ + 0x3f00, /* 00 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 169 0xa9 '\251' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3f00, /* 00 00000000 */ + 0x3f00, /* 00 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xf30, /* 0000 00 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x3f00, /* 00 00000000 */ + 0x3f00, /* 00 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 170 0xaa '\252' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3fcc, /* 00 00 00 */ + 0x3fcc, /* 00 00 00 */ + 0xcfc, /* 0000 00 00 */ + 0xcfc, /* 0000 00 00 */ + 0xcfc, /* 0000 00 00 */ + 0xcfc, /* 0000 00 00 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 171 0xab '\253' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 172 0xac '\254' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 173 0xad '\255' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 174 0xae '\256' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3fc0, /* 00 000000 */ + 0x3fc0, /* 00 000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x33f0, /* 00 00 0000 */ + 0x33f0, /* 00 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 175 0xaf '\257' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 176 0xb0 '\260' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3cf0, /* 00 00 0000 */ + 0x3cf0, /* 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3cf0, /* 00 00 0000 */ + 0x3cf0, /* 00 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 177 0xb1 '\261' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 178 0xb2 '\262' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 179 0xb3 '\263' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3f0, /* 000000 0000 */ + 0x3f0, /* 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 180 0xb4 '\264' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 181 0xb5 '\265' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x3f30, /* 00 00 0000 */ + 0x3f30, /* 00 00 0000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 182 0xb6 '\266' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xf0, /* 00000000 0000 */ + 0xf0, /* 00000000 0000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 183 0xb7 '\267' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 184 0xb8 '\270' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 185 0xb9 '\271' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 186 0xba '\272' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3c00, /* 00 0000000000 */ + 0x3c00, /* 00 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 187 0xbb '\273' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3f0, /* 000000 0000 */ + 0x3f0, /* 000000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x3f0, /* 000000 0000 */ + 0x3f0, /* 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 188 0xbc '\274' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 189 0xbd '\275' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3cf0, /* 00 00 0000 */ + 0x3cf0, /* 00 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 190 0xbe '\276' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x33f0, /* 00 00 0000 */ + 0x33f0, /* 00 00 0000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 191 0xbf '\277' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x30f0, /* 00 0000 0000 */ + 0x30f0, /* 00 0000 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0x3c30, /* 00 0000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 192 0xc0 '\300' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 193 0xc1 '\301' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 194 0xc2 '\302' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 195 0xc3 '\303' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xf0, /* 00000000 0000 */ + 0xf0, /* 00000000 0000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0xc00, /* 0000 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 196 0xc4 '\304' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3c00, /* 00 0000000000 */ + 0x3c00, /* 00 0000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 197 0xc5 '\305' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3000, /* 00 000000000000 */ + 0x3000, /* 00 000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 198 0xc6 '\306' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 199 0xc7 '\307' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 200 0xc8 '\310' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0xc30, /* 0000 0000 0000 */ + 0x30c0, /* 00 0000 000000 */ + 0x30c0, /* 00 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 201 0xc9 '\311' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 202 0xca '\312' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 203 0xcb '\313' */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 204 0xcc '\314' */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 205 0xcd '\315' */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 206 0xce '\316' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x33c0, /* 00 00 000000 */ + 0x33c0, /* 00 00 000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 207 0xcf '\317' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3330, /* 00 00 00 0000 */ + 0x3330, /* 00 00 00 0000 */ + 0x33f0, /* 00 00 0000 */ + 0x33f0, /* 00 00 0000 */ + 0x3300, /* 00 00 00000000 */ + 0x3300, /* 00 00 00000000 */ + 0xcf0, /* 0000 00 0000 */ + 0xcf0, /* 0000 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 208 0xd0 '\320' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 209 0xd1 '\321' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ffc, /* 00 00 */ + 0x3ffc, /* 00 00 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 210 0xd2 '\322' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 211 0xd3 '\323' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0x330, /* 000000 00 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 212 0xd4 '\324' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 213 0xd5 '\325' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3c0, /* 000000 000000 */ + 0x3c0, /* 000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0xc0, /* 00000000 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 214 0xd6 '\326' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ff0, /* 00 0000 */ + 0x3ff0, /* 00 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 215 0xd7 '\327' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x300, /* 000000 00000000 */ + 0x300, /* 000000 00000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 216 0xd8 '\330' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xcc0, /* 0000 00 000000 */ + 0xcc0, /* 0000 00 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0x3030, /* 00 000000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x30, /* 0000000000 0000 */ + 0x30, /* 0000000000 0000 */ + 0xfc0, /* 0000 000000 */ + 0xfc0, /* 0000 000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 217 0xd9 '\331' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ffc, /* 00 00 */ + 0x3ffc, /* 00 00 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ffc, /* 00 00 */ + 0x3ffc, /* 00 00 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x3ffc, /* 00 00 */ + 0x3ffc, /* 00 00 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 218 0xda '\332' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 219 0xdb '\333' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 220 0xdc '\334' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 221 0xdd '\335' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 222 0xde '\336' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 223 0xdf '\337' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 224 0xe0 '\340' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 225 0xe1 '\341' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 226 0xe2 '\342' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 227 0xe3 '\343' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 228 0xe4 '\344' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 229 0xe5 '\345' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 230 0xe6 '\346' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 231 0xe7 '\347' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 232 0xe8 '\350' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 233 0xe9 '\351' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 234 0xea '\352' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 235 0xeb '\353' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 236 0xec '\354' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 237 0xed '\355' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 238 0xee '\356' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 239 0xef '\357' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 240 0xf0 '\360' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 241 0xf1 '\361' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 242 0xf2 '\362' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 243 0xf3 '\363' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 244 0xf4 '\364' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 245 0xf5 '\365' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 246 0xf6 '\366' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 247 0xf7 '\367' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 248 0xf8 '\370' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 249 0xf9 '\371' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 250 0xfa '\372' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 251 0xfb '\373' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 252 0xfc '\374' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 253 0xfd '\375' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 254 0xfe '\376' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + + /* 255 0xff '\377' */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0xff0, /* 0000 0000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + 0x0, /* 0000000000000000 */ + +}; diff --git a/src/zm_box.h b/src/zm_box.h index 8a18b8847..f5fe69a38 100644 --- a/src/zm_box.h +++ b/src/zm_box.h @@ -23,7 +23,11 @@ #include "zm.h" #include "zm_coord.h" +#ifndef SOLARIS #include +#else +#include +#endif // // Class used for storing a box, which is defined as a region @@ -32,39 +36,39 @@ class Box { private: - Coord lo, hi; - Coord size; + Coord lo, hi; + Coord size; public: - inline Box() - { - } - inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { } - inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { } - inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { } - inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { } + inline Box() + { + } + inline Box( int p_size ) : lo( 0, 0 ), hi ( p_size-1, p_size-1 ), size( Coord::Range( hi, lo ) ) { } + inline Box( int p_x_size, int p_y_size ) : lo( 0, 0 ), hi ( p_x_size-1, p_y_size-1 ), size( Coord::Range( hi, lo ) ) { } + inline Box( int lo_x, int lo_y, int hi_x, int hi_y ) : lo( lo_x, lo_y ), hi( hi_x, hi_y ), size( Coord::Range( hi, lo ) ) { } + inline Box( const Coord &p_lo, const Coord &p_hi ) : lo( p_lo ), hi( p_hi ), size( Coord::Range( hi, lo ) ) { } - inline const Coord &Lo() const { return( lo ); } - inline int LoX() const { return( lo.X() ); } - inline int LoY() const { return( lo.Y() ); } - inline const Coord &Hi() const { return( hi ); } - inline int HiX() const { return( hi.X() ); } - inline int HiY() const { return( hi.Y() ); } - inline const Coord &Size() const { return( size ); } - inline int Width() const { return( size.X() ); } - inline int Height() const { return( size.Y() ); } - inline int Area() const { return( size.X()*size.Y() ); } + inline const Coord &Lo() const { return( lo ); } + inline int LoX() const { return( lo.X() ); } + inline int LoY() const { return( lo.Y() ); } + inline const Coord &Hi() const { return( hi ); } + inline int HiX() const { return( hi.X() ); } + inline int HiY() const { return( hi.Y() ); } + inline const Coord &Size() const { return( size ); } + inline int Width() const { return( size.X() ); } + inline int Height() const { return( size.Y() ); } + inline int Area() const { return( size.X()*size.Y() ); } - inline const Coord Centre() const - { - int mid_x = int(round(lo.X()+(size.X()/2.0))); - int mid_y = int(round(lo.Y()+(size.Y()/2.0))); - return( Coord( mid_x, mid_y ) ); - } - inline bool Inside( const Coord &coord ) const - { - return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() ); - } + inline const Coord Centre() const + { + int mid_x = int(round(lo.X()+(size.X()/2.0))); + int mid_y = int(round(lo.Y()+(size.Y()/2.0))); + return( Coord( mid_x, mid_y ) ); + } + inline bool Inside( const Coord &coord ) const + { + return( coord.X() >= lo.X() && coord.X() <= hi.X() && coord.Y() >= lo.Y() && coord.Y() <= hi.Y() ); + } }; #endif // ZM_BOX_H diff --git a/src/zm_buffer.cpp b/src/zm_buffer.cpp index 384f45bcc..b46cb2f15 100644 --- a/src/zm_buffer.cpp +++ b/src/zm_buffer.cpp @@ -18,52 +18,64 @@ */ #include +#include #include "zm.h" #include "zm_buffer.h" unsigned int Buffer::assign( const unsigned char *pStorage, unsigned int pSize ) { - if ( mAllocation < pSize ) - { - delete[] mStorage; - mAllocation = pSize; - mHead = mStorage = new unsigned char[pSize]; - } - mSize = pSize; - memcpy( mStorage, pStorage, mSize ); - mHead = mStorage; - mTail = mHead + mSize; - return( mSize ); + if ( mAllocation < pSize ) + { + delete[] mStorage; + mAllocation = pSize; + mHead = mStorage = new unsigned char[pSize]; + } + mSize = pSize; + memcpy( mStorage, pStorage, mSize ); + mHead = mStorage; + mTail = mHead + mSize; + return( mSize ); } unsigned int Buffer::expand( unsigned int count ) { - int spare = mAllocation - mSize; - int headSpace = mHead - mStorage; - int tailSpace = spare - headSpace; - int width = mTail - mHead; - if ( spare > (int)count ) + int spare = mAllocation - mSize; + int headSpace = mHead - mStorage; + int tailSpace = spare - headSpace; + int width = mTail - mHead; + if ( spare > (int)count ) + { + if ( tailSpace < (int)count ) { - if ( tailSpace < (int)count ) - { - memmove( mStorage, mHead, mSize ); - mHead = mStorage; - mTail = mHead + width; - } + memmove( mStorage, mHead, mSize ); + mHead = mStorage; + mTail = mHead + width; } - else + } + else + { + mAllocation += count; + unsigned char *newStorage = new unsigned char[mAllocation]; + if ( mStorage ) { - mAllocation += count; - unsigned char *newStorage = new unsigned char[mAllocation]; - if ( mStorage ) - { - memcpy( newStorage, mHead, mSize ); - delete[] mStorage; - } - mStorage = newStorage; - mHead = mStorage; - mTail = mHead + width; + memcpy( newStorage, mHead, mSize ); + delete[] mStorage; } - return( mSize ); + mStorage = newStorage; + mHead = mStorage; + mTail = mHead + width; + } + return( mSize ); +} + +int Buffer::read_into( int sd, unsigned int bytes ) { + // Make sure there is enough space + this->expand(bytes); + int bytes_read = read( sd, mTail, bytes ); + if ( bytes_read > 0 ) { + mTail += bytes_read; + mSize += bytes_read; + } + return bytes_read; } diff --git a/src/zm_buffer.h b/src/zm_buffer.h index af043ae13..620fce1a8 100644 --- a/src/zm_buffer.h +++ b/src/zm_buffer.h @@ -10,7 +10,7 @@ * 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 demTails. + * 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 @@ -27,182 +27,183 @@ class Buffer { protected: - unsigned char *mStorage; - unsigned int mAllocation; - unsigned int mSize; - unsigned char *mHead; - unsigned char *mTail; + unsigned char *mStorage; + unsigned int mAllocation; + unsigned int mSize; + unsigned char *mHead; + unsigned char *mTail; public: - Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 ) + Buffer() : mStorage( 0 ), mAllocation( 0 ), mSize( 0 ), mHead( 0 ), mTail( 0 ) + { + } + Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 ) + { + mHead = mStorage = new unsigned char[mAllocation]; + mTail = mHead; + } + Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize ) + { + mHead = mStorage = new unsigned char[mSize]; + memcpy( mStorage, pStorage, mSize ); + mTail = mHead + mSize; + } + Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize ) + { + mHead = mStorage = new unsigned char[mSize]; + memcpy( mStorage, buffer.mHead, mSize ); + mTail = mHead + mSize; + } + ~Buffer() + { + delete[] mStorage; + } + unsigned char *head() const { return( mHead ); } + unsigned char *tail() const { return( mTail ); } + unsigned int size() const { return( mSize ); } + bool empty() const { return( mSize == 0 ); } + unsigned int size( unsigned int pSize ) + { + if ( mSize < pSize ) { + expand( pSize-mSize ); } - Buffer( unsigned int pSize ) : mAllocation( pSize ), mSize( 0 ) - { - mHead = mStorage = new unsigned char[mAllocation]; - mTail = mHead; - } - Buffer( const unsigned char *pStorage, unsigned int pSize ) : mAllocation( pSize ), mSize( pSize ) - { - mHead = mStorage = new unsigned char[mSize]; - memcpy( mStorage, pStorage, mSize ); - mTail = mHead + mSize; - } - Buffer( const Buffer &buffer ) : mAllocation( buffer.mSize ), mSize( buffer.mSize ) - { - mHead = mStorage = new unsigned char[mSize]; - memcpy( mStorage, buffer.mHead, mSize ); - mTail = mHead + mSize; - } - ~Buffer() - { - delete[] mStorage; - } - unsigned char *head() const { return( mHead ); } - unsigned char *tail() const { return( mTail ); } - unsigned int size() const { return( mSize ); } - bool empty() const { return( mSize == 0 ); } - unsigned int size( unsigned int pSize ) - { - if ( mSize < pSize ) - { - expand( pSize-mSize ); - } - return( mSize ); - } - //unsigned int Allocation() const { return( mAllocation ); } + return( mSize ); + } + //unsigned int Allocation() const { return( mAllocation ); } - void clear() + void clear() + { + mSize = 0; + mHead = mTail = mStorage; + } + + unsigned int assign( const unsigned char *pStorage, unsigned int pSize ); + unsigned int assign( const Buffer &buffer ) + { + return( assign( buffer.mHead, buffer.mSize ) ); + } + + // Trim from the front of the buffer + unsigned int consume( unsigned int count ) + { + if ( count > mSize ) { - mSize = 0; + Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize ); + count = mSize; + } + mHead += count; + mSize -= count; + tidy( 0 ); + return( count ); + } + // Trim from the end of the buffer + unsigned int shrink( unsigned int count ) + { + if ( count > mSize ) + { + Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize ); + count = mSize; + } + mSize -= count; + if ( mTail > (mHead + mSize) ) + mTail = mHead + mSize; + tidy( 0 ); + return( count ); + } + // Add to the end of the buffer + unsigned int expand( unsigned int count ); + + // Return pointer to the first pSize bytes and advance the head + unsigned char *extract( unsigned int pSize ) + { + if ( pSize > mSize ) + { + Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize ); + pSize = mSize; + } + unsigned char *oldHead = mHead; + mHead += pSize; + mSize -= pSize; + tidy( 0 ); + return( oldHead ); + } + // Add bytes to the end of the buffer + unsigned int append( const unsigned char *pStorage, unsigned int pSize ) + { + expand( pSize ); + memcpy( mTail, pStorage, pSize ); + mTail += pSize; + mSize += pSize; + return( mSize ); + } + unsigned int append( const char *pStorage, unsigned int pSize ) + { + return( append( (const unsigned char *)pStorage, pSize ) ); + } + unsigned int append( const Buffer &buffer ) + { + return( append( buffer.mHead, buffer.mSize ) ); + } + void tidy( bool level=0 ) + { + if ( mHead != mStorage ) + { + if ( mSize == 0 ) mHead = mTail = mStorage; - } - - unsigned int assign( const unsigned char *pStorage, unsigned int pSize ); - unsigned int assign( const Buffer &buffer ) - { - return( assign( buffer.mHead, buffer.mSize ) ); - } - - // Trim from the front of the buffer - unsigned int consume( unsigned int count ) - { - if ( count > mSize ) + else if ( level ) + { + if ( (mHead-mStorage) > mSize ) { - Warning( "Attempt to consume %d bytes of buffer, size is only %d bytes", count, mSize ); - count = mSize; + memcpy( mStorage, mHead, mSize ); + mHead = mStorage; + mTail = mHead + mSize; } - mHead += count; - mSize -= count; - tidy( 0 ); - return( count ); + } } - // Trim from the end of the buffer - unsigned int shrink( unsigned int count ) - { - if ( count > mSize ) - { - Warning( "Attempt to shrink buffer by %d bytes, size is only %d bytes", count, mSize ); - count = mSize; - } - mSize -= count; - if ( mTail > (mHead + mSize) ) - mTail = mHead + mSize; - tidy( 0 ); - return( count ); - } - // Add to the end of the buffer - unsigned int expand( unsigned int count ); + } - // Return pointer to the first pSize bytes and advance the head - unsigned char *extract( unsigned int pSize ) - { - if ( pSize > mSize ) - { - Warning( "Attempt to extract %d bytes of buffer, size is only %d bytes", pSize, mSize ); - pSize = mSize; - } - unsigned char *oldHead = mHead; - mHead += pSize; - mSize -= pSize; - tidy( 0 ); - return( oldHead ); - } - // Add bytes to the end of the buffer - unsigned int append( const unsigned char *pStorage, unsigned int pSize ) - { - expand( pSize ); - memcpy( mTail, pStorage, pSize ); - mTail += pSize; - mSize += pSize; - return( mSize ); - } - unsigned int append( const char *pStorage, unsigned int pSize ) - { - return( append( (const unsigned char *)pStorage, pSize ) ); - } - unsigned int append( const Buffer &buffer ) - { - return( append( buffer.mHead, buffer.mSize ) ); - } - void tidy( bool level=0 ) - { - if ( mHead != mStorage ) - { - if ( mSize == 0 ) - mHead = mTail = mStorage; - else if ( level ) - { - if ( (mHead-mStorage) > mSize ) - { - memcpy( mStorage, mHead, mSize ); - mHead = mStorage; - mTail = mHead + mSize; - } - } - } - } - - Buffer &operator=( const Buffer &buffer ) - { - assign( buffer ); - return( *this ); - } - Buffer &operator+=( const Buffer &buffer ) - { - append( buffer ); - return( *this ); - } - Buffer &operator+=( unsigned int count ) - { - expand( count ); - return( *this ); - } - Buffer &operator-=( unsigned int count ) - { - consume( count ); - return( *this ); - } - operator unsigned char *() const - { - return( mHead ); - } - operator char *() const - { - return( (char *)mHead ); - } - unsigned char *operator+(int offset) const - { - return( (unsigned char *)(mHead+offset) ); - } - unsigned char operator[](int index) const - { - return( *(mHead+index) ); - } - operator int () const - { - return( (int)mSize ); - } + Buffer &operator=( const Buffer &buffer ) + { + assign( buffer ); + return( *this ); + } + Buffer &operator+=( const Buffer &buffer ) + { + append( buffer ); + return( *this ); + } + Buffer &operator+=( unsigned int count ) + { + expand( count ); + return( *this ); + } + Buffer &operator-=( unsigned int count ) + { + consume( count ); + return( *this ); + } + operator unsigned char *() const + { + return( mHead ); + } + operator char *() const + { + return( (char *)mHead ); + } + unsigned char *operator+(int offset) const + { + return( (unsigned char *)(mHead+offset) ); + } + unsigned char operator[](int index) const + { + return( *(mHead+index) ); + } + operator int () const + { + return( (int)mSize ); + } + int read_into( int sd, unsigned int bytes ); }; #endif // ZM_BUFFER_H diff --git a/src/zm_camera.cpp b/src/zm_camera.cpp index 18950d341..bbf2edcb6 100644 --- a/src/zm_camera.cpp +++ b/src/zm_camera.cpp @@ -21,29 +21,29 @@ #include "zm_camera.h" Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - id( p_id ), - type( p_type ), - width( p_width), - height( p_height ), - colours( p_colours ), - subpixelorder( p_subpixelorder ), - brightness( p_brightness ), - hue( p_hue ), - colour( p_colour ), - contrast( p_contrast ), - capture( p_capture ) + id( p_id ), + type( p_type ), + width( p_width), + height( p_height ), + colours( p_colours ), + subpixelorder( p_subpixelorder ), + brightness( p_brightness ), + hue( p_hue ), + colour( p_colour ), + contrast( p_contrast ), + capture( p_capture ) { - pixels = width * height; - imagesize = pixels * colours; - - Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture); - - /* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */ - if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) { - Fatal("Image size is not multiples of 16"); - } else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) { - Fatal("Image size is not multiples of 12 and 16"); - } + pixels = width * height; + imagesize = pixels * colours; + + Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture); + + /* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */ + if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) { + Fatal("Image size is not multiples of 16"); + } else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) { + Fatal("Image size is not multiples of 12 and 16"); + } } Camera::~Camera() diff --git a/src/zm_camera.h b/src/zm_camera.h index bddb66fc8..ba8224c09 100644 --- a/src/zm_camera.h +++ b/src/zm_camera.h @@ -32,52 +32,52 @@ class Camera { protected: - typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; + typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; - int id; - SourceType type; - unsigned int width; - unsigned int height; - unsigned int colours; - unsigned int subpixelorder; - unsigned int pixels; - unsigned int imagesize; - int brightness; - int hue; - int colour; - int contrast; - bool capture; + int id; + SourceType type; + unsigned int width; + unsigned int height; + unsigned int colours; + unsigned int subpixelorder; + unsigned int pixels; + unsigned int imagesize; + int brightness; + int hue; + int colour; + int contrast; + bool capture; public: - Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - virtual ~Camera(); + Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); + virtual ~Camera(); - int getId() const { return( id ); } - SourceType Type() const { return( type ); } - bool IsLocal() const { return( type == LOCAL_SRC ); } - bool IsRemote() const { return( type == REMOTE_SRC ); } - bool IsFile() const { return( type == FILE_SRC ); } - bool IsFfmpeg() const { return( type == FFMPEG_SRC ); } - bool IsLibvlc() const { return( type == LIBVLC_SRC ); } - bool IscURL() const { return( type == CURL_SRC ); } - unsigned int Width() const { return( width ); } - unsigned int Height() const { return( height ); } - unsigned int Colours() const { return( colours ); } - unsigned int SubpixelOrder() const { return( subpixelorder ); } - unsigned int Pixels() const { return( pixels ); } - unsigned int ImageSize() const { return( imagesize ); } + int getId() const { return( id ); } + SourceType Type() const { return( type ); } + bool IsLocal() const { return( type == LOCAL_SRC ); } + bool IsRemote() const { return( type == REMOTE_SRC ); } + bool IsFile() const { return( type == FILE_SRC ); } + bool IsFfmpeg() const { return( type == FFMPEG_SRC ); } + bool IsLibvlc() const { return( type == LIBVLC_SRC ); } + bool IscURL() const { return( type == CURL_SRC ); } + unsigned int Width() const { return( width ); } + unsigned int Height() const { return( height ); } + unsigned int Colours() const { return( colours ); } + unsigned int SubpixelOrder() const { return( subpixelorder ); } + unsigned int Pixels() const { return( pixels ); } + unsigned int ImageSize() const { return( imagesize ); } - virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } - virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } - virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); } - virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); } + virtual int Brightness( int/*p_brightness*/=-1 ) { return( -1 ); } + virtual int Hue( int/*p_hue*/=-1 ) { return( -1 ); } + virtual int Colour( int/*p_colour*/=-1 ) { return( -1 ); } + virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); } - bool CanCapture() const { return( capture ); } - - virtual int PrimeCapture() { return( 0 ); } - virtual int PreCapture()=0; - virtual int Capture( Image &image )=0; - virtual int PostCapture()=0; + bool CanCapture() const { return( capture ); } + + virtual int PrimeCapture() { return( 0 ); } + virtual int PreCapture()=0; + virtual int Capture( Image &image )=0; + virtual int PostCapture()=0; }; #endif // ZM_CAMERA_H diff --git a/src/zm_comms.cpp b/src/zm_comms.cpp index b30bed1d5..d5fb49c2e 100644 --- a/src/zm_comms.cpp +++ b/src/zm_comms.cpp @@ -25,100 +25,108 @@ #include #include //#include +#if defined(BSD) +#include +#else #include -#include +#endif + //#include #include #include -//#include -//#include #include +#include // for debug output +#include // for snprintf + +#ifdef SOLARIS +#include // define FIONREAD +#endif int CommsBase::readV( int iovcnt, /* const void *, int, */ ... ) { - va_list arg_ptr; - //struct iovec iov[iovcnt]; - struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); + va_list arg_ptr; + //struct iovec iov[iovcnt]; + struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); - va_start( arg_ptr, iovcnt ); - for ( int i = 0; i < iovcnt; i++ ) - { - iov[i].iov_base = va_arg( arg_ptr, void * ); - iov[i].iov_len = va_arg( arg_ptr, int ); - } - va_end( arg_ptr ); + va_start( arg_ptr, iovcnt ); + for ( int i = 0; i < iovcnt; i++ ) + { + iov[i].iov_base = va_arg( arg_ptr, void * ); + iov[i].iov_len = va_arg( arg_ptr, int ); + } + va_end( arg_ptr ); - int nBytes = ::readv( mRd, iov, iovcnt ); - if ( nBytes < 0 ) - Debug( 1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno) ); - return( nBytes ); + int nBytes = ::readv( mRd, iov, iovcnt ); + if ( nBytes < 0 ) + Debug( 1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno) ); + return( nBytes ); } int CommsBase::writeV( int iovcnt, /* const void *, int, */ ... ) { - va_list arg_ptr; - //struct iovec iov[iovcnt]; - struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); + va_list arg_ptr; + //struct iovec iov[iovcnt]; + struct iovec *iov = (struct iovec *)alloca( sizeof(struct iovec)*iovcnt ); - va_start( arg_ptr, iovcnt ); - for ( int i = 0; i < iovcnt; i++ ) - { - iov[i].iov_base = va_arg( arg_ptr, void * ); - iov[i].iov_len = va_arg( arg_ptr, int ); - } - va_end( arg_ptr ); + va_start( arg_ptr, iovcnt ); + for ( int i = 0; i < iovcnt; i++ ) + { + iov[i].iov_base = va_arg( arg_ptr, void * ); + iov[i].iov_len = va_arg( arg_ptr, int ); + } + va_end( arg_ptr ); - ssize_t nBytes = ::writev( mWd, iov, iovcnt ); - if ( nBytes < 0 ) - Debug( 1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno) ); - return( nBytes ); + ssize_t nBytes = ::writev( mWd, iov, iovcnt ); + if ( nBytes < 0 ) + Debug( 1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno) ); + return( nBytes ); } bool Pipe::open() { - if ( ::pipe( mFd ) < 0 ) - { - Error( "pipe(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } + if ( ::pipe( mFd ) < 0 ) + { + Error( "pipe(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } - return( true ); + return( true ); } bool Pipe::close() { - if ( mFd[0] > -1 ) ::close( mFd[0] ); - mFd[0] = -1; - if ( mFd[1] > -1 ) ::close( mFd[1] ); - mFd[1] = -1; - return( true ); + if ( mFd[0] > -1 ) ::close( mFd[0] ); + mFd[0] = -1; + if ( mFd[1] > -1 ) ::close( mFd[1] ); + mFd[1] = -1; + return( true ); } bool Pipe::setBlocking( bool blocking ) { - int flags; + int flags; - /* Now set it for non-blocking I/O */ - if ( (flags = fcntl( mFd[1], F_GETFL )) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - if ( blocking ) - { - flags &= ~O_NONBLOCK; - } - else - { - flags |= O_NONBLOCK; - } - if ( fcntl( mFd[1], F_SETFL, flags ) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } + /* Now set it for non-blocking I/O */ + if ( (flags = fcntl( mFd[1], F_GETFL )) < 0 ) + { + Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + if ( blocking ) + { + flags &= ~O_NONBLOCK; + } + else + { + flags |= O_NONBLOCK; + } + if ( fcntl( mFd[1], F_SETFL, flags ) < 0 ) + { + Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } - return( true ); + return( true ); } SockAddr::SockAddr( const struct sockaddr *addr ) : mAddr( addr ) @@ -127,33 +135,33 @@ SockAddr::SockAddr( const struct sockaddr *addr ) : mAddr( addr ) SockAddr *SockAddr::newSockAddr( const struct sockaddr &addr, socklen_t len ) { - if ( addr.sa_family == AF_INET && len == SockAddrInet::addrSize() ) - { - return( new SockAddrInet( (const struct sockaddr_in *)&addr ) ); - } - else if ( addr.sa_family == AF_UNIX && len == SockAddrUnix::addrSize() ) - { - return( new SockAddrUnix( (const struct sockaddr_un *)&addr ) ); - } - Error( "Unable to create new SockAddr from addr family %d with size %d", addr.sa_family, len ); - return( 0 ); + if ( addr.sa_family == AF_INET && len == SockAddrInet::addrSize() ) + { + return( new SockAddrInet( (const struct sockaddr_in *)&addr ) ); + } + else if ( addr.sa_family == AF_UNIX && len == SockAddrUnix::addrSize() ) + { + return( new SockAddrUnix( (const struct sockaddr_un *)&addr ) ); + } + Error( "Unable to create new SockAddr from addr family %d with size %d", addr.sa_family, len ); + return( 0 ); } SockAddr *SockAddr::newSockAddr( const SockAddr *addr ) { - if ( !addr ) - return( 0 ); - - if ( addr->getDomain() == AF_INET ) - { - return( new SockAddrInet( *(SockAddrInet *)addr ) ); - } - else if ( addr->getDomain() == AF_UNIX ) - { - return( new SockAddrUnix( *(SockAddrUnix *)addr ) ); - } - Error( "Unable to create new SockAddr from addr family %d", addr->getDomain() ); + if ( !addr ) return( 0 ); + + if ( addr->getDomain() == AF_INET ) + { + return( new SockAddrInet( *(SockAddrInet *)addr ) ); + } + else if ( addr->getDomain() == AF_UNIX ) + { + return( new SockAddrUnix( *(SockAddrUnix *)addr ) ); + } + Error( "Unable to create new SockAddr from addr family %d", addr->getDomain() ); + return( 0 ); } SockAddrInet::SockAddrInet() : SockAddr( (struct sockaddr *)&mAddrIn ) @@ -162,73 +170,73 @@ SockAddrInet::SockAddrInet() : SockAddr( (struct sockaddr *)&mAddrIn ) bool SockAddrInet::resolve( const char *host, const char *serv, const char *proto ) { - memset( &mAddrIn, 0, sizeof(mAddrIn) ); + memset( &mAddrIn, 0, sizeof(mAddrIn) ); - struct hostent *hostent=0; - if ( !(hostent = ::gethostbyname( host ) ) ) - { - Error( "gethostbyname( %s ), h_errno = %d", host, h_errno ); - return( false ); - } + struct hostent *hostent=0; + if ( !(hostent = ::gethostbyname( host ) ) ) + { + Error( "gethostbyname( %s ), h_errno = %d", host, h_errno ); + return( false ); + } - struct servent *servent=0; - if ( !(servent = ::getservbyname( serv, proto ) ) ) - { - Error( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) ); - return( false ); - } + struct servent *servent=0; + if ( !(servent = ::getservbyname( serv, proto ) ) ) + { + Error( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) ); + return( false ); + } - mAddrIn.sin_port = servent->s_port; - mAddrIn.sin_family = AF_INET; - mAddrIn.sin_addr.s_addr = ((struct in_addr *)(hostent->h_addr))->s_addr; + mAddrIn.sin_port = servent->s_port; + mAddrIn.sin_family = AF_INET; + mAddrIn.sin_addr.s_addr = ((struct in_addr *)(hostent->h_addr))->s_addr; - return( true ); + return( true ); } bool SockAddrInet::resolve( const char *host, int port, const char *proto ) { - memset( &mAddrIn, 0, sizeof(mAddrIn) ); + memset( &mAddrIn, 0, sizeof(mAddrIn) ); - struct hostent *hostent=0; - if ( !(hostent = ::gethostbyname( host ) ) ) - { - Error( "gethostbyname( %s ), h_errno = %d", host, h_errno ); - return( false ); - } + struct hostent *hostent=0; + if ( !(hostent = ::gethostbyname( host ) ) ) + { + Error( "gethostbyname( %s ), h_errno = %d", host, h_errno ); + return( false ); + } - mAddrIn.sin_port = htons(port); - mAddrIn.sin_family = AF_INET; - mAddrIn.sin_addr.s_addr = ((struct in_addr *)(hostent->h_addr))->s_addr; - return( true ); + mAddrIn.sin_port = htons(port); + mAddrIn.sin_family = AF_INET; + mAddrIn.sin_addr.s_addr = ((struct in_addr *)(hostent->h_addr))->s_addr; + return( true ); } bool SockAddrInet::resolve( const char *serv, const char *proto ) { - memset( &mAddrIn, 0, sizeof(mAddrIn) ); + memset( &mAddrIn, 0, sizeof(mAddrIn) ); - struct servent *servent=0; - if ( !(servent = ::getservbyname( serv, proto ) ) ) - { - Error( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) ); - return( false ); - } + struct servent *servent=0; + if ( !(servent = ::getservbyname( serv, proto ) ) ) + { + Error( "getservbyname( %s ), errno = %d, error = %s", serv, errno, strerror(errno) ); + return( false ); + } - mAddrIn.sin_port = servent->s_port; - mAddrIn.sin_family = AF_INET; - mAddrIn.sin_addr.s_addr = INADDR_ANY; + mAddrIn.sin_port = servent->s_port; + mAddrIn.sin_family = AF_INET; + mAddrIn.sin_addr.s_addr = INADDR_ANY; - return( true ); + return( true ); } bool SockAddrInet::resolve( int port, const char *proto ) { - memset( &mAddrIn, 0, sizeof(mAddrIn) ); + memset( &mAddrIn, 0, sizeof(mAddrIn) ); - mAddrIn.sin_port = htons(port); - mAddrIn.sin_family = AF_INET; - mAddrIn.sin_addr.s_addr = INADDR_ANY; + mAddrIn.sin_port = htons(port); + mAddrIn.sin_family = AF_INET; + mAddrIn.sin_addr.s_addr = INADDR_ANY; - return( true ); + return( true ); } SockAddrUnix::SockAddrUnix() : SockAddr( (struct sockaddr *)&mAddrUn ) @@ -237,313 +245,473 @@ SockAddrUnix::SockAddrUnix() : SockAddr( (struct sockaddr *)&mAddrUn ) bool SockAddrUnix::resolve( const char *path, const char *proto ) { - memset( &mAddrUn, 0, sizeof(mAddrUn) ); + memset( &mAddrUn, 0, sizeof(mAddrUn) ); - strncpy( mAddrUn.sun_path, path, sizeof(mAddrUn.sun_path) ); - mAddrUn.sun_family = AF_UNIX; + strncpy( mAddrUn.sun_path, path, sizeof(mAddrUn.sun_path) ); + mAddrUn.sun_family = AF_UNIX; - return( true ); + return( true ); } bool Socket::socket() { - if ( mSd >= 0 ) - return( true ); + if ( mSd >= 0 ) + return( true ); - if ( (mSd = ::socket( getDomain(), getType(), 0 ) ) < 0 ) - { - Error( "socket(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } + if ( (mSd = ::socket( getDomain(), getType(), 0 ) ) < 0 ) + { + Error( "socket(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } - int val = 1; + int val = 1; - (void)::setsockopt( mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val) ); - (void)::setsockopt( mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val) ); + (void)::setsockopt( mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val) ); + (void)::setsockopt( mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val) ); - mState = DISCONNECTED; + mState = DISCONNECTED; - return( true ); + return( true ); } bool Socket::connect() { - if ( !socket() ) - return( false ); + if ( !socket() ) + return( false ); - if ( ::connect( mSd, mRemoteAddr->getAddr(), getAddrSize() ) == -1 ) - { - Error( "connect(), errno = %d, error = %s", errno, strerror(errno) ); - close(); - return( false ); - } + if ( ::connect( mSd, mRemoteAddr->getAddr(), getAddrSize() ) == -1 ) + { + Error( "connect(), errno = %d, error = %s", errno, strerror(errno) ); + close(); + return( false ); + } - mState = CONNECTED; + mState = CONNECTED; - return( true ); + return( true ); } bool Socket::bind() { - if ( !socket() ) - return( false ); + if ( !socket() ) + return( false ); - if ( ::bind( mSd, mLocalAddr->getAddr(), getAddrSize() ) == -1 ) - { - Error( "bind(), errno = %d, error = %s", errno, strerror(errno) ); - close(); - return( false ); - } - return( true ); + if ( ::bind( mSd, mLocalAddr->getAddr(), getAddrSize() ) == -1 ) + { + Error( "bind(), errno = %d, error = %s", errno, strerror(errno) ); + close(); + return( false ); + } + return( true ); } bool Socket::listen() { - if ( ::listen( mSd, SOMAXCONN ) == -1 ) - { - Error( "listen(), errno = %d, error = %s", errno, strerror(errno) ); - close(); - return( false ); - } + if ( ::listen( mSd, SOMAXCONN ) == -1 ) + { + Error( "listen(), errno = %d, error = %s", errno, strerror(errno) ); + close(); + return( false ); + } - mState = LISTENING; + mState = LISTENING; - return( true ); + return( true ); } bool Socket::accept() { - struct sockaddr *rem_addr = mLocalAddr->getTempAddr(); - socklen_t rem_addr_size = getAddrSize(); + struct sockaddr *rem_addr = mLocalAddr->getTempAddr(); + socklen_t rem_addr_size = getAddrSize(); - int newSd = -1; - if ( (newSd = ::accept( mSd, rem_addr, &rem_addr_size )) == -1 ) - { - Error( "accept(), errno = %d, error = %s", errno, strerror(errno) ); - close(); - return( false ); - } + int newSd = -1; + if ( (newSd = ::accept( mSd, rem_addr, &rem_addr_size )) == -1 ) + { + Error( "accept(), errno = %d, error = %s", errno, strerror(errno) ); + close(); + return( false ); + } - ::close( mSd ); - mSd = newSd; + ::close( mSd ); + mSd = newSd; - mState = CONNECTED; + mState = CONNECTED; - return( true ); + return( true ); } bool Socket::accept( int &newSd ) { - struct sockaddr *rem_addr = mLocalAddr->getTempAddr(); - socklen_t rem_addr_size = getAddrSize(); + struct sockaddr *rem_addr = mLocalAddr->getTempAddr(); + socklen_t rem_addr_size = getAddrSize(); - newSd = -1; - if ( (newSd = ::accept( mSd, rem_addr, &rem_addr_size )) == -1 ) - { - Error( "accept(), errno = %d, error = %s", errno, strerror(errno) ); - close(); - return( false ); - } + newSd = -1; + if ( (newSd = ::accept( mSd, rem_addr, &rem_addr_size )) == -1 ) + { + Error( "accept(), errno = %d, error = %s", errno, strerror(errno) ); + close(); + return( false ); + } - return( true ); + return( true ); } bool Socket::close() { - if ( mSd > -1 ) ::close( mSd ); - mSd = -1; - mState = CLOSED; - return( true ); + if ( mSd > -1 ) ::close( mSd ); + mSd = -1; + mState = CLOSED; + return( true ); } int Socket::bytesToRead() const { - int bytes_to_read = 0; + int bytes_to_read = 0; - if ( ioctl( mSd, FIONREAD, &bytes_to_read ) < 0 ) - { - Error( "ioctl(), errno = %d, error = %s", errno, strerror(errno) ); - return( -1 ); - } - return( bytes_to_read ); + if ( ioctl( mSd, FIONREAD, &bytes_to_read ) < 0 ) + { + Error( "ioctl(), errno = %d, error = %s", errno, strerror(errno) ); + return( -1 ); + } + return( bytes_to_read ); } bool Socket::getBlocking( bool &blocking ) { - int flags; + int flags; - if ( (flags = fcntl( mSd, F_GETFL )) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - blocking = (flags & O_NONBLOCK); - return( true ); + if ( (flags = fcntl( mSd, F_GETFL )) < 0 ) + { + Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + blocking = (flags & O_NONBLOCK); + return( true ); } bool Socket::setBlocking( bool blocking ) { #if 0 - // ioctl is apparently not recommended - int ioctl_arg = !blocking; - if ( ioctl( mSd, FIONBIO, &ioctl_arg ) < 0 ) - { - Error( "ioctl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - return( true ); + // ioctl is apparently not recommended + int ioctl_arg = !blocking; + if ( ioctl( mSd, FIONBIO, &ioctl_arg ) < 0 ) + { + Error( "ioctl(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + return( true ); #endif - int flags; + int flags; - /* Now set it for non-blocking I/O */ - if ( (flags = fcntl( mSd, F_GETFL )) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - if ( blocking ) - { - flags &= ~O_NONBLOCK; - } - else - { - flags |= O_NONBLOCK; - } - if ( fcntl( mSd, F_SETFL, flags ) < 0 ) - { - Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } + /* Now set it for non-blocking I/O */ + if ( (flags = fcntl( mSd, F_GETFL )) < 0 ) + { + Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + if ( blocking ) + { + flags &= ~O_NONBLOCK; + } + else + { + flags |= O_NONBLOCK; + } + if ( fcntl( mSd, F_SETFL, flags ) < 0 ) + { + Error( "fcntl(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } - return( true ); + return( true ); } bool Socket::getSendBufferSize( int &buffersize ) const { - socklen_t optlen = sizeof(buffersize); - if ( getsockopt( mSd, SOL_SOCKET, SO_SNDBUF, &buffersize, &optlen ) < 0 ) - { - Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( -1 ); - } - return( buffersize ); + socklen_t optlen = sizeof(buffersize); + if ( getsockopt( mSd, SOL_SOCKET, SO_SNDBUF, &buffersize, &optlen ) < 0 ) + { + Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); + return( -1 ); + } + return( buffersize ); } bool Socket::getRecvBufferSize( int &buffersize ) const { - socklen_t optlen = sizeof(buffersize); - if ( getsockopt( mSd, SOL_SOCKET, SO_RCVBUF, &buffersize, &optlen ) < 0 ) - { - Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( -1 ); - } - return( buffersize ); + socklen_t optlen = sizeof(buffersize); + if ( getsockopt( mSd, SOL_SOCKET, SO_RCVBUF, &buffersize, &optlen ) < 0 ) + { + Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); + return( -1 ); + } + return( buffersize ); } bool Socket::setSendBufferSize( int buffersize ) { - if ( setsockopt( mSd, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) - { - Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - return( true ); + if ( setsockopt( mSd, SOL_SOCKET, SO_SNDBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) + { + Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + return( true ); } bool Socket::setRecvBufferSize( int buffersize ) { - if ( setsockopt( mSd, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) - { - Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - return( true ); + if ( setsockopt( mSd, SOL_SOCKET, SO_RCVBUF, (char *)&buffersize, sizeof(buffersize)) < 0 ) + { + Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + return( true ); } bool Socket::getRouting( bool &route ) const { - int dontRoute; - socklen_t optlen = sizeof(dontRoute); - if ( getsockopt( mSd, SOL_SOCKET, SO_DONTROUTE, &dontRoute, &optlen ) < 0 ) - { - Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - route = !dontRoute; - return( true ); + int dontRoute; + socklen_t optlen = sizeof(dontRoute); + if ( getsockopt( mSd, SOL_SOCKET, SO_DONTROUTE, &dontRoute, &optlen ) < 0 ) + { + Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + route = !dontRoute; + return( true ); } bool Socket::setRouting( bool route ) { - int dontRoute = !route; - if ( setsockopt( mSd, SOL_SOCKET, SO_DONTROUTE, (char *)&dontRoute, sizeof(dontRoute)) < 0 ) - { - Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - return( true ); + int dontRoute = !route; + if ( setsockopt( mSd, SOL_SOCKET, SO_DONTROUTE, (char *)&dontRoute, sizeof(dontRoute)) < 0 ) + { + Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + return( true ); } bool Socket::getNoDelay( bool &nodelay ) const { - int int_nodelay; - socklen_t optlen = sizeof(int_nodelay); - if ( getsockopt( mSd, IPPROTO_TCP, TCP_NODELAY, &int_nodelay, &optlen ) < 0 ) - { - Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - nodelay = int_nodelay; - return( true ); + int int_nodelay; + socklen_t optlen = sizeof(int_nodelay); + if ( getsockopt( mSd, IPPROTO_TCP, TCP_NODELAY, &int_nodelay, &optlen ) < 0 ) + { + Error( "getsockopt(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + nodelay = int_nodelay; + return( true ); } bool Socket::setNoDelay( bool nodelay ) { - int int_nodelay = nodelay; + int int_nodelay = nodelay; - if ( setsockopt( mSd, IPPROTO_TCP, TCP_NODELAY, (char *)&int_nodelay, sizeof(int_nodelay)) < 0 ) - { - Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); - return( false ); - } - return( true ); + if ( setsockopt( mSd, IPPROTO_TCP, TCP_NODELAY, (char *)&int_nodelay, sizeof(int_nodelay)) < 0 ) + { + Error( "setsockopt(), errno = %d, error = %s", errno, strerror(errno) ); + return( false ); + } + return( true ); +} + +bool InetSocket::connect( const char *host, const char *serv ) +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int s; + char buf[255]; + + mAddressFamily = AF_UNSPEC; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = getType(); + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + + s = getaddrinfo(host, serv, &hints, &result); + if (s != 0) { + Error( "connect(): getaddrinfo: %s", gai_strerror(s) ); + return( false ); + } + + /* getaddrinfo() returns a list of address structures. + * Try each address until we successfully connect(2). + * If socket(2) (or connect(2)) fails, we (close the socket + * and) try the next address. */ + + for (rp = result; rp != NULL; rp = rp->ai_next) { + if (mSd != -1) { + if (::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1) + break; /* Success */ + continue; + } + memset(&buf, 0, sizeof(buf)); + if (rp->ai_family == AF_INET) { + inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1); + } + else if (rp->ai_family == AF_INET6) { + inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1); + } + else { + strncpy(buf, "n/a", sizeof(buf)-1); + } + Debug( 1, "connect(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol); + mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (mSd == -1) + continue; + + int val = 1; + + (void)::setsockopt( mSd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val) ); + (void)::setsockopt( mSd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val) ); + mAddressFamily = rp->ai_family; /* save AF_ for ctrl and data connections */ + + if (::connect(mSd, rp->ai_addr, rp->ai_addrlen) != -1) + break; /* Success */ + + ::close(mSd); + } + + if (rp == NULL) { /* No address succeeded */ + Error( "connect(), Could not connect" ); + mAddressFamily = AF_UNSPEC; + return( false ); + } + + freeaddrinfo(result); /* No longer needed */ + + mState = CONNECTED; + + return( true ); +} + +bool InetSocket::connect( const char *host, int port ) +{ + char serv[8]; + snprintf(serv, sizeof(serv), "%d", port); + + return connect( host, serv ); +} + +bool InetSocket::bind( const char * host, const char * serv ) +{ + struct addrinfo hints; + struct addrinfo *result, *rp; + int s; + char buf[255]; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = getType(); + hints.ai_flags = AI_PASSIVE; /* For wildcard IP address */ + hints.ai_protocol = 0; /* Any protocol */ + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + s = getaddrinfo(host, serv, &hints, &result); + if (s != 0) { + Error( "bind(): getaddrinfo: %s", gai_strerror(s) ); + return( false ); + } + + /* getaddrinfo() returns a list of address structures. + * Try each address until we successfully bind(2). + * If socket(2) (or bind(2)) fails, we (close the socket + * and) try the next address. */ + for (rp = result; rp != NULL; rp = rp->ai_next) { + memset(&buf, 0, sizeof(buf)); + if (rp->ai_family == AF_INET) { + inet_ntop(AF_INET, &((struct sockaddr_in *)rp->ai_addr)->sin_addr, buf, sizeof(buf)-1); + } + else if (rp->ai_family == AF_INET6) { + inet_ntop(AF_INET6, &((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, buf, sizeof(buf)-1); + } + else { + strncpy(buf, "n/a", sizeof(buf)-1); + } + Debug( 1, "bind(): Trying '%s', family '%d', proto '%d'", buf, rp->ai_family, rp->ai_protocol); + mSd = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (mSd == -1) + continue; + + mState = DISCONNECTED; + if (::bind(mSd, rp->ai_addr, rp->ai_addrlen) == 0) + break; /* Success */ + + ::close(mSd); + mSd = -1; + } + + if (rp == NULL) { /* No address succeeded */ + Error( "bind(), Could not bind" ); + return( false ); + } + + freeaddrinfo(result); /* No longer needed */ + + return( true ); +} + +bool InetSocket::bind( const char * serv ) +{ + return bind( NULL, serv); +} + +bool InetSocket::bind( const char * host, int port ) +{ + char serv[8]; + snprintf(serv, sizeof(serv), "%d", port); + + return bind( host, serv ); +} + +bool InetSocket::bind( int port ) +{ + char serv[8]; + snprintf(serv, sizeof(serv), "%d", port); + + return bind( NULL, serv ); } bool TcpInetServer::listen() { - return( Socket::listen() ); + return( Socket::listen() ); } bool TcpInetServer::accept() { - return( Socket::accept() ); + return( Socket::accept() ); } bool TcpInetServer::accept( TcpInetSocket *&newSocket ) { - int newSd = -1; - newSocket = 0; + int newSd = -1; + newSocket = 0; - if ( !Socket::accept( newSd ) ) - return( false ); + if ( !Socket::accept( newSd ) ) + return( false ); - newSocket = new TcpInetSocket( *this, newSd ); + newSocket = new TcpInetSocket( *this, newSd ); - return( true ); + return( true ); } bool TcpUnixServer::accept( TcpUnixSocket *&newSocket ) { - int newSd = -1; - newSocket = 0; + int newSd = -1; + newSocket = 0; - if ( !Socket::accept( newSd ) ) - return( false ); + if ( !Socket::accept( newSd ) ) + return( false ); - newSocket = new TcpUnixSocket( *this, newSd ); + newSocket = new TcpUnixSocket( *this, newSd ); - return( true ); + return( true ); } Select::Select() : mHasTimeout( false ), mMaxFd( -1 ) @@ -552,160 +720,160 @@ Select::Select() : mHasTimeout( false ), mMaxFd( -1 ) Select::Select( struct timeval timeout ) : mMaxFd( -1 ) { - setTimeout( timeout ); + setTimeout( timeout ); } Select::Select( int timeout ) : mMaxFd( -1 ) { - setTimeout( timeout ); + setTimeout( timeout ); } Select::Select( double timeout ) : mMaxFd( -1 ) { - setTimeout( timeout ); + setTimeout( timeout ); } void Select::setTimeout( int timeout ) { - mTimeout.tv_sec = timeout; - mTimeout.tv_usec = 0; - mHasTimeout = true; + mTimeout.tv_sec = timeout; + mTimeout.tv_usec = 0; + mHasTimeout = true; } void Select::setTimeout( double timeout ) { - mTimeout.tv_sec = int(timeout); - mTimeout.tv_usec = suseconds_t((timeout-mTimeout.tv_sec)*1000000.0); - mHasTimeout = true; + mTimeout.tv_sec = int(timeout); + mTimeout.tv_usec = suseconds_t((timeout-mTimeout.tv_sec)*1000000.0); + mHasTimeout = true; } void Select::setTimeout( struct timeval timeout ) { - mTimeout = timeout; - mHasTimeout = true; + mTimeout = timeout; + mHasTimeout = true; } void Select::clearTimeout() { - mHasTimeout = false; + mHasTimeout = false; } void Select::calcMaxFd() { - mMaxFd = -1; - for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); iter++ ) - if ( (*iter)->getMaxDesc() > mMaxFd ) - mMaxFd = (*iter)->getMaxDesc(); - for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); iter++ ) - if ( (*iter)->getMaxDesc() > mMaxFd ) - mMaxFd = (*iter)->getMaxDesc(); + mMaxFd = -1; + for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); iter++ ) + if ( (*iter)->getMaxDesc() > mMaxFd ) + mMaxFd = (*iter)->getMaxDesc(); + for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); iter++ ) + if ( (*iter)->getMaxDesc() > mMaxFd ) + mMaxFd = (*iter)->getMaxDesc(); } bool Select::addReader( CommsBase *comms ) { - if ( !comms->isOpen() ) - { - Error( "Unable to add closed reader" ); - return( false ); - } - std::pair result = mReaders.insert( comms ); - if ( result.second ) - if ( comms->getMaxDesc() > mMaxFd ) - mMaxFd = comms->getMaxDesc(); - return( result.second ); + if ( !comms->isOpen() ) + { + Error( "Unable to add closed reader" ); + return( false ); + } + std::pair result = mReaders.insert( comms ); + if ( result.second ) + if ( comms->getMaxDesc() > mMaxFd ) + mMaxFd = comms->getMaxDesc(); + return( result.second ); } bool Select::deleteReader( CommsBase *comms ) { - if ( !comms->isOpen() ) - { - Error( "Unable to add closed reader" ); - return( false ); - } - if ( mReaders.erase( comms ) ) - { - calcMaxFd(); - return( true ); - } + if ( !comms->isOpen() ) + { + Error( "Unable to add closed reader" ); return( false ); + } + if ( mReaders.erase( comms ) ) + { + calcMaxFd(); + return( true ); + } + return( false ); } void Select::clearReaders() { - mReaders.clear(); - mMaxFd = -1; + mReaders.clear(); + mMaxFd = -1; } bool Select::addWriter( CommsBase *comms ) { - std::pair result = mWriters.insert( comms ); - if ( result.second ) - if ( comms->getMaxDesc() > mMaxFd ) - mMaxFd = comms->getMaxDesc(); - return( result.second ); + std::pair result = mWriters.insert( comms ); + if ( result.second ) + if ( comms->getMaxDesc() > mMaxFd ) + mMaxFd = comms->getMaxDesc(); + return( result.second ); } bool Select::deleteWriter( CommsBase *comms ) { - if ( mWriters.erase( comms ) ) - { - calcMaxFd(); - return( true ); - } - return( false ); + if ( mWriters.erase( comms ) ) + { + calcMaxFd(); + return( true ); + } + return( false ); } void Select::clearWriters() { - mWriters.clear(); - mMaxFd = -1; + mWriters.clear(); + mMaxFd = -1; } int Select::wait() { - struct timeval tempTimeout = mTimeout; - struct timeval *selectTimeout = mHasTimeout?&tempTimeout:NULL; + struct timeval tempTimeout = mTimeout; + struct timeval *selectTimeout = mHasTimeout?&tempTimeout:NULL; - fd_set rfds; - fd_set wfds; + fd_set rfds; + fd_set wfds; - mReadable.clear(); - FD_ZERO(&rfds); + mReadable.clear(); + FD_ZERO(&rfds); + for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); iter++ ) + FD_SET((*iter)->getReadDesc(),&rfds); + + mWriteable.clear(); + FD_ZERO(&wfds); + for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); iter++ ) + FD_SET((*iter)->getWriteDesc(),&wfds); + + int nFound = select( mMaxFd+1, &rfds, &wfds, NULL, selectTimeout ); + if( nFound == 0 ) + { + Debug( 1, "Select timed out" ); + } + else if ( nFound < 0) + { + Error( "Select error: %s", strerror(errno) ); + } + else + { for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); iter++ ) - FD_SET((*iter)->getReadDesc(),&rfds); - - mWriteable.clear(); - FD_ZERO(&wfds); + if ( FD_ISSET((*iter)->getReadDesc(),&rfds) ) + mReadable.push_back( *iter ); for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); iter++ ) - FD_SET((*iter)->getWriteDesc(),&wfds); - - int nFound = select( mMaxFd+1, &rfds, &wfds, NULL, selectTimeout ); - if( nFound == 0 ) - { - Debug( 1, "Select timed out" ); - } - else if ( nFound < 0) - { - Error( "Select error: %s", strerror(errno) ); - } - else - { - for ( CommsSet::iterator iter = mReaders.begin(); iter != mReaders.end(); iter++ ) - if ( FD_ISSET((*iter)->getReadDesc(),&rfds) ) - mReadable.push_back( *iter ); - for ( CommsSet::iterator iter = mWriters.begin(); iter != mWriters.end(); iter++ ) - if ( FD_ISSET((*iter)->getWriteDesc(),&rfds) ) - mWriteable.push_back( *iter ); - } - return( nFound ); + if ( FD_ISSET((*iter)->getWriteDesc(),&rfds) ) + mWriteable.push_back( *iter ); + } + return( nFound ); } const Select::CommsList &Select::getReadable() const { - return( mReadable ); + return( mReadable ); } const Select::CommsList &Select::getWriteable() const { - return( mWriteable ); + return( mWriteable ); } diff --git a/src/zm_comms.h b/src/zm_comms.h index 60d4a3ad4..f00691f70 100644 --- a/src/zm_comms.h +++ b/src/zm_comms.h @@ -22,6 +22,7 @@ #include "zm_exception.h" +#include #include #include #include @@ -30,860 +31,740 @@ #include #include +#if defined(BSD) +#include +#include +#include +#endif + class CommsException : public Exception { public: - CommsException( const std::string &message ) : Exception( message ) - { - } + CommsException( const std::string &message ) : Exception( message ) + { + } }; class CommsBase { protected: - const int &mRd; - const int &mWd; + const int &mRd; + const int &mWd; protected: - CommsBase( int &rd, int &wd ) : mRd( rd ), mWd( wd ) - { - } - virtual ~CommsBase() - { - } + CommsBase( int &rd, int &wd ) : mRd( rd ), mWd( wd ) + { + } + virtual ~CommsBase() + { + } public: - virtual bool close()=0; - virtual bool isOpen() const=0; - virtual bool isClosed() const=0; - virtual bool setBlocking( bool blocking )=0; + virtual bool close()=0; + virtual bool isOpen() const=0; + virtual bool isClosed() const=0; + virtual bool setBlocking( bool blocking )=0; public: - int getReadDesc() const - { - return( mRd ); - } - int getWriteDesc() const - { - return( mWd ); - } - int getMaxDesc() const - { - return( mRd>mWd?mRd:mWd ); - } + int getReadDesc() const + { + return( mRd ); + } + int getWriteDesc() const + { + return( mWd ); + } + int getMaxDesc() const + { + return( mRd>mWd?mRd:mWd ); + } - virtual int read( void *msg, int len ) - { - ssize_t nBytes = ::read( mRd, msg, len ); - if ( nBytes < 0 ) - Debug( 1, "Read of %d bytes max on rd %d failed: %s", len, mRd, strerror(errno) ); - return( nBytes ); - } - virtual int write( const void *msg, int len ) - { - ssize_t nBytes = ::write( mWd, msg, len ); - if ( nBytes < 0 ) - Debug( 1, "Write of %d bytes on wd %d failed: %s", len, mWd, strerror(errno) ); - return( nBytes ); - } - virtual int readV( const struct iovec *iov, int iovcnt ) - { - int nBytes = ::readv( mRd, iov, iovcnt ); - if ( nBytes < 0 ) - Debug( 1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno) ); - return( nBytes ); - } - virtual int writeV( const struct iovec *iov, int iovcnt ) - { - ssize_t nBytes = ::writev( mWd, iov, iovcnt ); - if ( nBytes < 0 ) - Debug( 1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno) ); - return( nBytes ); - } - virtual int readV( int iovcnt, /* const void *msg1, int len1, */ ... ); - virtual int writeV( int iovcnt, /* const void *msg1, int len1, */ ... ); + virtual int read( void *msg, int len ) + { + ssize_t nBytes = ::read( mRd, msg, len ); + if ( nBytes < 0 ) + Debug( 1, "Read of %d bytes max on rd %d failed: %s", len, mRd, strerror(errno) ); + return( nBytes ); + } + virtual int write( const void *msg, int len ) + { + ssize_t nBytes = ::write( mWd, msg, len ); + if ( nBytes < 0 ) + Debug( 1, "Write of %d bytes on wd %d failed: %s", len, mWd, strerror(errno) ); + return( nBytes ); + } + virtual int readV( const struct iovec *iov, int iovcnt ) + { + int nBytes = ::readv( mRd, iov, iovcnt ); + if ( nBytes < 0 ) + Debug( 1, "Readv of %d buffers max on rd %d failed: %s", iovcnt, mRd, strerror(errno) ); + return( nBytes ); + } + virtual int writeV( const struct iovec *iov, int iovcnt ) + { + ssize_t nBytes = ::writev( mWd, iov, iovcnt ); + if ( nBytes < 0 ) + Debug( 1, "Writev of %d buffers on wd %d failed: %s", iovcnt, mWd, strerror(errno) ); + return( nBytes ); + } + virtual int readV( int iovcnt, /* const void *msg1, int len1, */ ... ); + virtual int writeV( int iovcnt, /* const void *msg1, int len1, */ ... ); }; class Pipe : public CommsBase { protected: - int mFd[2]; + int mFd[2]; public: - Pipe() : CommsBase( mFd[0], mFd[1] ) - { - mFd[0] = -1; - mFd[1] = -1; - } - ~Pipe() - { - close(); - } + Pipe() : CommsBase( mFd[0], mFd[1] ) + { + mFd[0] = -1; + mFd[1] = -1; + } + ~Pipe() + { + close(); + } public: - bool open(); - bool close(); + bool open(); + bool close(); - bool isOpen() const - { - return( mFd[0] != -1 && mFd[1] != -1 ); - } - int getReadDesc() const - { - return( mFd[0] ); - } - int getWriteDesc() const - { - return( mFd[1] ); - } + bool isOpen() const + { + return( mFd[0] != -1 && mFd[1] != -1 ); + } + int getReadDesc() const + { + return( mFd[0] ); + } + int getWriteDesc() const + { + return( mFd[1] ); + } - bool setBlocking( bool blocking ); + bool setBlocking( bool blocking ); }; class SockAddr { private: - const struct sockaddr *mAddr; + const struct sockaddr *mAddr; public: - SockAddr( const struct sockaddr *addr ); - virtual ~SockAddr() - { - } + SockAddr( const struct sockaddr *addr ); + virtual ~SockAddr() + { + } - static SockAddr *newSockAddr( const struct sockaddr &addr, socklen_t len ); - static SockAddr *newSockAddr( const SockAddr *addr ); + static SockAddr *newSockAddr( const struct sockaddr &addr, socklen_t len ); + static SockAddr *newSockAddr( const SockAddr *addr ); - int getDomain() const - { - return( mAddr?mAddr->sa_family:AF_UNSPEC ); - } + int getDomain() const + { + return( mAddr?mAddr->sa_family:AF_UNSPEC ); + } - const struct sockaddr *getAddr() const - { - return( mAddr ); - } - virtual socklen_t getAddrSize() const=0; - virtual struct sockaddr *getTempAddr() const=0; + const struct sockaddr *getAddr() const + { + return( mAddr ); + } + virtual socklen_t getAddrSize() const=0; + virtual struct sockaddr *getTempAddr() const=0; }; class SockAddrInet : public SockAddr { private: - struct sockaddr_in mAddrIn; - struct sockaddr_in mTempAddrIn; + struct sockaddr_in mAddrIn; + struct sockaddr_in mTempAddrIn; public: - SockAddrInet(); - SockAddrInet( const SockAddrInet &addr ) : SockAddr( (const struct sockaddr *)&mAddrIn ), mAddrIn( addr.mAddrIn ) - { - } - SockAddrInet( const struct sockaddr_in *addr ) : SockAddr( (const struct sockaddr *)&mAddrIn ), mAddrIn( *addr ) - { - } + SockAddrInet(); + SockAddrInet( const SockAddrInet &addr ) : SockAddr( (const struct sockaddr *)&mAddrIn ), mAddrIn( addr.mAddrIn ) + { + } + SockAddrInet( const struct sockaddr_in *addr ) : SockAddr( (const struct sockaddr *)&mAddrIn ), mAddrIn( *addr ) + { + } - bool resolve( const char *host, const char *serv, const char *proto ); - bool resolve( const char *host, int port, const char *proto ); - bool resolve( const char *serv, const char *proto ); - bool resolve( int port, const char *proto ); + bool resolve( const char *host, const char *serv, const char *proto ); + bool resolve( const char *host, int port, const char *proto ); + bool resolve( const char *serv, const char *proto ); + bool resolve( int port, const char *proto ); - socklen_t getAddrSize() const - { - return( sizeof(mAddrIn) ); - } - struct sockaddr *getTempAddr() const - { - return( (sockaddr *)&mTempAddrIn ); - } + socklen_t getAddrSize() const + { + return( sizeof(mAddrIn) ); + } + struct sockaddr *getTempAddr() const + { + return( (sockaddr *)&mTempAddrIn ); + } public: - static socklen_t addrSize() - { - return( sizeof(sockaddr_in) ); - } + static socklen_t addrSize() + { + return( sizeof(sockaddr_in) ); + } }; class SockAddrUnix : public SockAddr { private: - struct sockaddr_un mAddrUn; - struct sockaddr_un mTempAddrUn; + struct sockaddr_un mAddrUn; + struct sockaddr_un mTempAddrUn; public: - SockAddrUnix(); - SockAddrUnix( const SockAddrUnix &addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( addr.mAddrUn ) - { - } - SockAddrUnix( const struct sockaddr_un *addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( *addr ) - { - } + SockAddrUnix(); + SockAddrUnix( const SockAddrUnix &addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( addr.mAddrUn ) + { + } + SockAddrUnix( const struct sockaddr_un *addr ) : SockAddr( (const struct sockaddr *)&mAddrUn ), mAddrUn( *addr ) + { + } - bool resolve( const char *path, const char *proto ); + bool resolve( const char *path, const char *proto ); - socklen_t getAddrSize() const - { - return( sizeof(mAddrUn) ); - } - struct sockaddr *getTempAddr() const - { - return( (sockaddr *)&mTempAddrUn ); - } + socklen_t getAddrSize() const + { + return( sizeof(mAddrUn) ); + } + struct sockaddr *getTempAddr() const + { + return( (sockaddr *)&mTempAddrUn ); + } public: - static socklen_t addrSize() - { - return( sizeof(sockaddr_un) ); - } + static socklen_t addrSize() + { + return( sizeof(sockaddr_un) ); + } }; class Socket : public CommsBase { protected: - typedef enum { CLOSED, DISCONNECTED, LISTENING, CONNECTED } State; + typedef enum { CLOSED, DISCONNECTED, LISTENING, CONNECTED } State; protected: - int mSd; - State mState; - SockAddr *mLocalAddr; - SockAddr *mRemoteAddr; + int mSd; + State mState; + SockAddr *mLocalAddr; + SockAddr *mRemoteAddr; protected: - Socket() : CommsBase( mSd, mSd ), mSd( -1 ), mState( CLOSED ), mLocalAddr( 0 ), mRemoteAddr( 0 ) - { - } - Socket( const Socket &socket, int newSd ) : CommsBase( mSd, mSd ), mSd( newSd ), mState( CONNECTED ), mLocalAddr( 0 ), mRemoteAddr( 0 ) - { - if ( socket.mLocalAddr ) - mLocalAddr = SockAddr::newSockAddr( mLocalAddr ); - if ( socket.mRemoteAddr ) - mRemoteAddr = SockAddr::newSockAddr( mRemoteAddr ); - } - virtual ~Socket() - { - close(); - delete mLocalAddr; - delete mRemoteAddr; - } + Socket() : CommsBase( mSd, mSd ), mSd( -1 ), mState( CLOSED ), mLocalAddr( 0 ), mRemoteAddr( 0 ) + { + } + Socket( const Socket &socket, int newSd ) : CommsBase( mSd, mSd ), mSd( newSd ), mState( CONNECTED ), mLocalAddr( 0 ), mRemoteAddr( 0 ) + { + if ( socket.mLocalAddr ) + mLocalAddr = SockAddr::newSockAddr( mLocalAddr ); + if ( socket.mRemoteAddr ) + mRemoteAddr = SockAddr::newSockAddr( mRemoteAddr ); + } + virtual ~Socket() + { + close(); + delete mLocalAddr; + delete mRemoteAddr; + } public: - bool isOpen() const - { - return( !isClosed() ); - } - bool isClosed() const - { - return( mState == CLOSED ); - } - bool isDisconnected() const - { - return( mState == DISCONNECTED ); - } - bool isConnected() const - { - return( mState == CONNECTED ); - } - virtual bool close(); + bool isOpen() const + { + return( !isClosed() ); + } + bool isClosed() const + { + return( mState == CLOSED ); + } + bool isDisconnected() const + { + return( mState == DISCONNECTED ); + } + bool isConnected() const + { + return( mState == CONNECTED ); + } + virtual bool close(); protected: - bool isListening() const - { - return( mState == LISTENING ); - } + bool isListening() const + { + return( mState == LISTENING ); + } protected: - virtual bool socket(); - virtual bool bind(); + virtual bool socket(); + virtual bool bind(); protected: - virtual bool connect(); - virtual bool listen(); - virtual bool accept(); - virtual bool accept( int & ); + virtual bool connect(); + virtual bool listen(); + virtual bool accept(); + virtual bool accept( int & ); public: - virtual int send( const void *msg, int len ) const - { - ssize_t nBytes = ::send( mSd, msg, len, 0 ); - if ( nBytes < 0 ) - Debug( 1, "Send of %d bytes on sd %d failed: %s", len, mSd, strerror(errno) ); - return( nBytes ); - } - virtual int recv( void *msg, int len ) const - { - ssize_t nBytes = ::recv( mSd, msg, len, 0 ); - if ( nBytes < 0 ) - Debug( 1, "Recv of %d bytes max on sd %d failed: %s", len, mSd, strerror(errno) ); - return( nBytes ); - } - virtual int send( const std::string &msg ) const - { - ssize_t nBytes = ::send( mSd, msg.data(), msg.size(), 0 ); - if ( nBytes < 0 ) - Debug( 1, "Send of string '%s' (%zd bytes) on sd %d failed: %s", msg.c_str(), msg.size(), mSd, strerror(errno) ); - return( nBytes ); - } - virtual int recv( std::string &msg ) const - { - char buffer[msg.capacity()]; - int nBytes = 0; - if ( (nBytes = ::recv( mSd, buffer, sizeof(buffer), 0 )) < 0 ) - { - Debug( 1, "Recv of %zd bytes max to string on sd %d failed: %s", sizeof(buffer), mSd, strerror(errno) ); - return( nBytes ); - } - buffer[nBytes] = '\0'; - msg = buffer; - return( nBytes ); - } - virtual int recv( std::string &msg, size_t maxLen ) const - { - char buffer[maxLen]; - int nBytes = 0; - if ( (nBytes = ::recv( mSd, buffer, sizeof(buffer), 0 )) < 0 ) - { - Debug( 1, "Recv of %zd bytes max to string on sd %d failed: %s", maxLen, mSd, strerror(errno) ); - return( nBytes ); - } - buffer[nBytes] = '\0'; - msg = buffer; - return( nBytes ); - } - virtual int bytesToRead() const; - - int getDesc() const - { - return( mSd ); - } - //virtual bool isOpen() const - //{ - //return( mSd != -1 ); - //} - - virtual int getDomain() const=0; - virtual int getType() const=0; - virtual const char *getProtocol() const=0; - - const SockAddr *getLocalAddr() const + virtual int send( const void *msg, int len ) const + { + ssize_t nBytes = ::send( mSd, msg, len, 0 ); + if ( nBytes < 0 ) + Debug( 1, "Send of %d bytes on sd %d failed: %s", len, mSd, strerror(errno) ); + return( nBytes ); + } + virtual int recv( void *msg, int len ) const + { + ssize_t nBytes = ::recv( mSd, msg, len, 0 ); + if ( nBytes < 0 ) + Debug( 1, "Recv of %d bytes max on sd %d failed: %s", len, mSd, strerror(errno) ); + return( nBytes ); + } + virtual int send( const std::string &msg ) const + { + ssize_t nBytes = ::send( mSd, msg.data(), msg.size(), 0 ); + if ( nBytes < 0 ) + Debug( 1, "Send of string '%s' (%zd bytes) on sd %d failed: %s", msg.c_str(), msg.size(), mSd, strerror(errno) ); + return( nBytes ); + } + virtual int recv( std::string &msg ) const + { + char buffer[msg.capacity()]; + int nBytes = 0; + if ( (nBytes = ::recv( mSd, buffer, sizeof(buffer), 0 )) < 0 ) { - return( mLocalAddr ); + Debug( 1, "Recv of %zd bytes max to string on sd %d failed: %s", sizeof(buffer), mSd, strerror(errno) ); + return( nBytes ); } - const SockAddr *getRemoteAddr() const + buffer[nBytes] = '\0'; + msg = buffer; + return( nBytes ); + } + virtual int recv( std::string &msg, size_t maxLen ) const + { + char buffer[maxLen]; + int nBytes = 0; + if ( (nBytes = ::recv( mSd, buffer, sizeof(buffer), 0 )) < 0 ) { - return( mRemoteAddr ); + Debug( 1, "Recv of %zd bytes max to string on sd %d failed: %s", maxLen, mSd, strerror(errno) ); + return( nBytes ); } - virtual socklen_t getAddrSize() const=0; + buffer[nBytes] = '\0'; + msg = buffer; + return( nBytes ); + } + virtual int bytesToRead() const; - bool getBlocking( bool &blocking ); - bool setBlocking( bool blocking ); + int getDesc() const + { + return( mSd ); + } + //virtual bool isOpen() const + //{ + //return( mSd != -1 ); + //} - bool getSendBufferSize( int & ) const; - bool getRecvBufferSize( int & ) const; + virtual int getDomain() const=0; + virtual int getType() const=0; + virtual const char *getProtocol() const=0; - bool setSendBufferSize( int ); - bool setRecvBufferSize( int ); + const SockAddr *getLocalAddr() const + { + return( mLocalAddr ); + } + const SockAddr *getRemoteAddr() const + { + return( mRemoteAddr ); + } + virtual socklen_t getAddrSize() const=0; - bool getRouting( bool & ) const; - bool setRouting( bool ); + bool getBlocking( bool &blocking ); + bool setBlocking( bool blocking ); - bool getNoDelay( bool & ) const; - bool setNoDelay( bool ); + bool getSendBufferSize( int & ) const; + bool getRecvBufferSize( int & ) const; + + bool setSendBufferSize( int ); + bool setRecvBufferSize( int ); + + bool getRouting( bool & ) const; + bool setRouting( bool ); + + bool getNoDelay( bool & ) const; + bool setNoDelay( bool ); }; class InetSocket : virtual public Socket { +protected: + int mAddressFamily; + public: - int getDomain() const - { - return( AF_INET ); - } - virtual socklen_t getAddrSize() const - { - return( SockAddrInet::addrSize() ); - } +int getDomain() const +{ + return( mAddressFamily ); +} +virtual socklen_t getAddrSize() const +{ + return( SockAddrInet::addrSize() ); +} protected: - bool resolveLocal( const char *host, const char *serv, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mLocalAddr = addr; - return( addr->resolve( host, serv, proto ) ); - } - bool resolveLocal( const char *host, int port, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mLocalAddr = addr; - return( addr->resolve( host, port, proto ) ); - } - bool resolveLocal( const char *serv, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mLocalAddr = addr; - return( addr->resolve( serv, proto ) ); - } - bool resolveLocal( int port, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mLocalAddr = addr; - return( addr->resolve( port, proto ) ); - } + bool connect( const char *host, const char *serv ); + bool connect( const char *host, int port ); - bool resolveRemote( const char *host, const char *serv, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mRemoteAddr = addr; - return( addr->resolve( host, serv, proto ) ); - } - bool resolveRemote( const char *host, int port, const char *proto ) - { - SockAddrInet *addr = new SockAddrInet; - mRemoteAddr = addr; - return( addr->resolve( host, port, proto ) ); - } - -protected: - bool bind( const SockAddrInet &addr ) - { - mLocalAddr = new SockAddrInet( addr ); - return( Socket::bind() ); - } - bool bind( const char *host, const char *serv ) - { - if ( !resolveLocal( host, serv, getProtocol() ) ) - return( false ); - return( Socket::bind() ); - } - bool bind( const char *host, int port ) - { - if ( !resolveLocal( host, port, getProtocol() ) ) - return( false ); - return( Socket::bind() ); - } - bool bind( const char *serv ) - { - if ( !resolveLocal( serv, getProtocol() ) ) - return( false ); - return( Socket::bind() ); - } - bool bind( int port ) - { - if ( !resolveLocal( port, getProtocol() ) ) - return( false ); - return( Socket::bind() ); - } - - bool connect( const SockAddrInet &addr ) - { - mRemoteAddr = new SockAddrInet( addr ); - return( Socket::connect() ); - } - bool connect( const char *host, const char *serv ) - { - if ( !resolveRemote( host, serv, getProtocol() ) ) - return( false ); - return( Socket::connect() ); - } - bool connect( const char *host, int port ) - { - if ( !resolveRemote( host, port, getProtocol() ) ) - return( false ); - return( Socket::connect() ); - } + bool bind( const char *host, const char *serv ); + bool bind( const char *host, int port ); + bool bind( const char *serv ); + bool bind( int port ); }; class UnixSocket : virtual public Socket { public: - int getDomain() const - { - return( AF_UNIX ); - } - virtual socklen_t getAddrSize() const - { - return( SockAddrUnix::addrSize() ); - } + int getDomain() const + { + return( AF_UNIX ); + } + virtual socklen_t getAddrSize() const + { + return( SockAddrUnix::addrSize() ); + } protected: - bool resolveLocal( const char *serv, const char *proto ) - { - SockAddrUnix *addr = new SockAddrUnix; - mLocalAddr = addr; - return( addr->resolve( serv, proto ) ); - } + bool resolveLocal( const char *serv, const char *proto ) + { + SockAddrUnix *addr = new SockAddrUnix; + mLocalAddr = addr; + return( addr->resolve( serv, proto ) ); + } - bool resolveRemote( const char *path, const char *proto ) - { - SockAddrUnix *addr = new SockAddrUnix; - mRemoteAddr = addr; - return( addr->resolve( path, proto ) ); - } + bool resolveRemote( const char *path, const char *proto ) + { + SockAddrUnix *addr = new SockAddrUnix; + mRemoteAddr = addr; + return( addr->resolve( path, proto ) ); + } protected: - bool bind( const char *path ) - { - if ( !UnixSocket::resolveLocal( path, getProtocol() ) ) - return( false ); - return( Socket::bind() ); - } + bool bind( const char *path ) + { + if ( !UnixSocket::resolveLocal( path, getProtocol() ) ) + return( false ); + return( Socket::bind() ); + } - bool connect( const char *path ) - { - if ( !UnixSocket::resolveRemote( path, getProtocol() ) ) - return( false ); - return( Socket::connect() ); - } + bool connect( const char *path ) + { + if ( !UnixSocket::resolveRemote( path, getProtocol() ) ) + return( false ); + return( Socket::connect() ); + } }; class UdpSocket : virtual public Socket { public: - int getType() const - { - return( SOCK_DGRAM ); - } - const char *getProtocol() const - { - return( "udp" ); - } + int getType() const + { + return( SOCK_DGRAM ); + } + const char *getProtocol() const + { + return( "udp" ); + } public: - virtual int sendto( const void *msg, int len, const SockAddr *addr=0 ) const - { - ssize_t nBytes = ::sendto( mSd, msg, len, 0, addr?addr->getAddr():NULL, addr?addr->getAddrSize():0 ); - if ( nBytes < 0 ) - Debug( 1, "Sendto of %d bytes on sd %d failed: %s", len, mSd, strerror(errno) ); - return( nBytes ); - } - virtual int recvfrom( void *msg, int len, SockAddr *addr=0 ) const - { - ssize_t nBytes = 0; - if ( addr ) - { - struct sockaddr sockAddr; - socklen_t sockLen; - nBytes = ::recvfrom( mSd, msg, len, 0, &sockAddr, &sockLen ); - if ( nBytes < 0 ) - { - Debug( 1, "Recvfrom of %d bytes max on sd %d (with address) failed: %s", len, mSd, strerror(errno) ); - } - else if ( sockLen ) - { - addr = SockAddr::newSockAddr( sockAddr, sockLen ); - } - } - else - { - nBytes = ::recvfrom( mSd, msg, len, 0, NULL, 0 ); - if ( nBytes < 0 ) - Debug( 1, "Recvfrom of %d bytes max on sd %d (no address) failed: %s", len, mSd, strerror(errno) ); - } - return( nBytes ); - } + virtual int sendto( const void *msg, int len, const SockAddr *addr=0 ) const + { + ssize_t nBytes = ::sendto( mSd, msg, len, 0, addr?addr->getAddr():NULL, addr?addr->getAddrSize():0 ); + if ( nBytes < 0 ) + Debug( 1, "Sendto of %d bytes on sd %d failed: %s", len, mSd, strerror(errno) ); + return( nBytes ); + } + virtual int recvfrom( void *msg, int len, SockAddr *addr=0 ) const + { + ssize_t nBytes = 0; + if ( addr ) + { + struct sockaddr sockAddr; + socklen_t sockLen; + nBytes = ::recvfrom( mSd, msg, len, 0, &sockAddr, &sockLen ); + if ( nBytes < 0 ) + { + Debug( 1, "Recvfrom of %d bytes max on sd %d (with address) failed: %s", len, mSd, strerror(errno) ); + } + else if ( sockLen ) + { + addr = SockAddr::newSockAddr( sockAddr, sockLen ); + } + } + else + { + nBytes = ::recvfrom( mSd, msg, len, 0, NULL, 0 ); + if ( nBytes < 0 ) + Debug( 1, "Recvfrom of %d bytes max on sd %d (no address) failed: %s", len, mSd, strerror(errno) ); + } + return( nBytes ); + } }; class UdpInetSocket : virtual public UdpSocket, virtual public InetSocket { public: - bool bind( const SockAddrInet &addr ) - { - return( InetSocket::bind( addr ) ); - } - bool bind( const char *host, const char *serv ) - { - return( InetSocket::bind( host, serv ) ); - } - bool bind( const char *host, int port ) - { - return( InetSocket::bind( host, port ) ); - } - bool bind( const char *serv ) - { - return( InetSocket::bind( serv ) ); - } - bool bind( int port ) - { - return( InetSocket::bind( port ) ); - } + bool bind( const char *host, const char *serv ) + { + return( InetSocket::bind( host, serv ) ); + } + bool bind( const char *host, int port ) + { + return( InetSocket::bind( host, port ) ); + } + bool bind( const char *serv ) + { + return( InetSocket::bind( serv ) ); + } + bool bind( int port ) + { + return( InetSocket::bind( port ) ); + } - bool connect( const SockAddrInet &addr ) - { - return( InetSocket::connect( addr ) ); - } - bool connect( const char *host, const char *serv ) - { - return( InetSocket::connect( host, serv ) ); - } - bool connect( const char *host, int port ) - { - return( InetSocket::connect( host, port ) ); - } + bool connect( const char *host, const char *serv ) + { + return( InetSocket::connect( host, serv ) ); + } + bool connect( const char *host, int port ) + { + return( InetSocket::connect( host, port ) ); + } }; class UdpUnixSocket : virtual public UdpSocket, virtual public UnixSocket { public: - bool bind( const char *path ) - { - return( UnixSocket::bind( path ) ); - } + bool bind( const char *path ) + { + return( UnixSocket::bind( path ) ); + } - bool connect( const char *path ) - { - return( UnixSocket::connect( path ) ); - } + bool connect( const char *path ) + { + return( UnixSocket::connect( path ) ); + } }; class UdpInetClient : public UdpInetSocket { -protected: - bool bind( const SockAddrInet &addr ) - { - return( UdpInetSocket::bind( addr ) ); - } - bool bind( const char *host, const char *serv ) - { - return( UdpInetSocket::bind( host, serv ) ); - } - bool bind( const char *host, int port ) - { - return( UdpInetSocket::bind( host, port ) ); - } - bool bind( const char *serv ) - { - return( UdpInetSocket::bind( serv ) ); - } - bool bind( int port ) - { - return( UdpInetSocket::bind( port ) ); - } - public: - bool connect( const SockAddrInet &addr ) - { - return( UdpInetSocket::connect( addr ) ); - } - bool connect( const char *host, const char *serv ) - { - return( UdpInetSocket::connect( host, serv ) ); - } - bool connect( const char *host, int port ) - { - return( UdpInetSocket::connect( host, port ) ); - } + bool connect( const char *host, const char *serv ) + { + return( UdpInetSocket::connect( host, serv ) ); + } + bool connect( const char *host, int port ) + { + return( UdpInetSocket::connect( host, port ) ); + } }; class UdpUnixClient : public UdpUnixSocket { public: - bool bind( const char *path ) - { - return( UdpUnixSocket::bind( path ) ); - } + bool bind( const char *path ) + { + return( UdpUnixSocket::bind( path ) ); + } public: - bool connect( const char *path ) - { - return( UdpUnixSocket::connect( path) ); - } + bool connect( const char *path ) + { + return( UdpUnixSocket::connect( path) ); + } }; class UdpInetServer : public UdpInetSocket { public: - bool bind( const SockAddrInet &addr ) - { - return( UdpInetSocket::bind( addr ) ); - } - bool bind( const char *host, const char *serv ) - { - return( UdpInetSocket::bind( host, serv ) ); - } - bool bind( const char *host, int port ) - { - return( UdpInetSocket::bind( host, port ) ); - } - bool bind( const char *serv ) - { - return( UdpInetSocket::bind( serv ) ); - } - bool bind( int port ) - { - return( UdpInetSocket::bind( port ) ); - } + bool bind( const char *host, const char *serv ) + { + return( UdpInetSocket::bind( host, serv ) ); + } + bool bind( const char *host, int port ) + { + return( UdpInetSocket::bind( host, port ) ); + } + bool bind( const char *serv ) + { + return( UdpInetSocket::bind( serv ) ); + } + bool bind( int port ) + { + return( UdpInetSocket::bind( port ) ); + } protected: - bool connect( const char *host, const char *serv ) - { - return( UdpInetSocket::connect( host, serv ) ); - } - bool connect( const char *host, int port ) - { - return( UdpInetSocket::connect( host, port ) ); - } + bool connect( const char *host, const char *serv ) + { + return( UdpInetSocket::connect( host, serv ) ); + } + bool connect( const char *host, int port ) + { + return( UdpInetSocket::connect( host, port ) ); + } }; class UdpUnixServer : public UdpUnixSocket { public: - bool bind( const char *path ) - { - return( UdpUnixSocket::bind( path ) ); - } + bool bind( const char *path ) + { + return( UdpUnixSocket::bind( path ) ); + } protected: - bool connect( const char *path ) - { - return( UdpUnixSocket::connect( path ) ); - } + bool connect( const char *path ) + { + return( UdpUnixSocket::connect( path ) ); + } }; class TcpSocket : virtual public Socket { public: - TcpSocket() - { - } - TcpSocket( const TcpSocket &socket, int newSd ) : Socket( socket, newSd ) - { - } + TcpSocket() + { + } + TcpSocket( const TcpSocket &socket, int newSd ) : Socket( socket, newSd ) + { + } public: - int getType() const - { - return( SOCK_STREAM ); - } - const char *getProtocol() const - { - return( "tcp" ); - } + int getType() const + { + return( SOCK_STREAM ); + } + const char *getProtocol() const + { + return( "tcp" ); + } }; class TcpInetSocket : virtual public TcpSocket, virtual public InetSocket { public: - TcpInetSocket() - { - } - TcpInetSocket( const TcpInetSocket &socket, int newSd ) : TcpSocket( socket, newSd ) - { - } + TcpInetSocket() + { + } + TcpInetSocket( const TcpInetSocket &socket, int newSd ) : TcpSocket( socket, newSd ) + { + } }; class TcpUnixSocket : virtual public TcpSocket, virtual public UnixSocket { public: - TcpUnixSocket() - { - } - TcpUnixSocket( const TcpUnixSocket &socket, int newSd ) : TcpSocket( socket, newSd ) - { - } + TcpUnixSocket() + { + } + TcpUnixSocket( const TcpUnixSocket &socket, int newSd ) : TcpSocket( socket, newSd ) + { + } }; class TcpInetClient : public TcpInetSocket { public: - bool connect( const char *host, const char *serv ) - { - return( TcpInetSocket::connect( host, serv ) ); - } - bool connect( const char *host, int port ) - { - return( TcpInetSocket::connect( host, port ) ); - } + bool connect( const char *host, const char *serv ) + { + return( TcpInetSocket::connect( host, serv ) ); + } + bool connect( const char *host, int port ) + { + return( TcpInetSocket::connect( host, port ) ); + } }; class TcpUnixClient : public TcpUnixSocket { public: - bool connect( const char *path ) - { - return( TcpUnixSocket::connect( path) ); - } + bool connect( const char *path ) + { + return( TcpUnixSocket::connect( path) ); + } }; class TcpInetServer : public TcpInetSocket { public: - bool bind( const char *host, const char *serv ) - { - return( TcpInetSocket::bind( host, serv ) ); - } - bool bind( const char *host, int port ) - { - return( TcpInetSocket::bind( host, port ) ); - } - bool bind( const char *serv ) - { - return( TcpInetSocket::bind( serv ) ); - } - bool bind( int port ) - { - return( TcpInetSocket::bind( port ) ); - } + bool bind( int port ) + { + return( TcpInetSocket::bind( port ) ); + } public: - bool isListening() const { return( Socket::isListening() ); } - bool listen(); - bool accept(); - bool accept( TcpInetSocket *&newSocket ); + bool isListening() const { return( Socket::isListening() ); } + bool listen(); + bool accept(); + bool accept( TcpInetSocket *&newSocket ); }; class TcpUnixServer : public TcpUnixSocket { public: - bool bind( const char *path ) - { - return( TcpUnixSocket::bind( path ) ); - } + bool bind( const char *path ) + { + return( TcpUnixSocket::bind( path ) ); + } public: - bool isListening() const { return( Socket::isListening() ); } - bool listen(); - bool accept(); - bool accept( TcpUnixSocket *&newSocket ); + bool isListening() const { return( Socket::isListening() ); } + bool listen(); + bool accept(); + bool accept( TcpUnixSocket *&newSocket ); }; class Select { public: - typedef std::set CommsSet; - typedef std::vector CommsList; + typedef std::set CommsSet; + typedef std::vector CommsList; protected: - CommsSet mReaders; - CommsSet mWriters; - CommsList mReadable; - CommsList mWriteable; - bool mHasTimeout; - struct timeval mTimeout; - int mMaxFd; + CommsSet mReaders; + CommsSet mWriters; + CommsList mReadable; + CommsList mWriteable; + bool mHasTimeout; + struct timeval mTimeout; + int mMaxFd; public: - Select(); - Select( struct timeval timeout ); - Select( int timeout ); - Select( double timeout ); + Select(); + Select( struct timeval timeout ); + Select( int timeout ); + Select( double timeout ); - void setTimeout( int timeout ); - void setTimeout( double timeout ); - void setTimeout( struct timeval timeout ); - void clearTimeout(); + void setTimeout( int timeout ); + void setTimeout( double timeout ); + void setTimeout( struct timeval timeout ); + void clearTimeout(); - void calcMaxFd(); + void calcMaxFd(); - bool addReader( CommsBase *comms ); - bool deleteReader( CommsBase *comms ); - void clearReaders(); + bool addReader( CommsBase *comms ); + bool deleteReader( CommsBase *comms ); + void clearReaders(); - bool addWriter( CommsBase *comms ); - bool deleteWriter( CommsBase *comms ); - void clearWriters(); + bool addWriter( CommsBase *comms ); + bool deleteWriter( CommsBase *comms ); + void clearWriters(); - int wait(); + int wait(); - const CommsList &getReadable() const; - const CommsList &getWriteable() const; + const CommsList &getReadable() const; + const CommsList &getWriteable() const; }; #endif // ZM_COMMS_H diff --git a/src/zm_config.cpp b/src/zm_config.cpp index 2b54cfeb1..c08b65e56 100644 --- a/src/zm_config.cpp +++ b/src/zm_config.cpp @@ -25,247 +25,280 @@ #include #include +#include "zm_utils.h" + void zmLoadConfig() { - FILE *cfg; - char line[512]; - char *val; - if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL ) - { - Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) ); - } - while ( fgets( line, sizeof(line), cfg ) != NULL ) - { - char *line_ptr = line; + FILE *cfg; + char line[512]; + if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL ) + { + Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) ); + } + while ( fgets( line, sizeof(line), cfg ) != NULL ) + { + char *line_ptr = line; - // Trim off any cr/lf line endings - int chomp_len = strcspn( line_ptr, "\r\n" ); - line_ptr[chomp_len] = '\0'; + // Trim off any cr/lf line endings + int chomp_len = strcspn( line_ptr, "\r\n" ); + line_ptr[chomp_len] = '\0'; - // Remove leading white space - int white_len = strspn( line_ptr, " \t" ); - line_ptr += white_len; + // Remove leading white space + int white_len = strspn( line_ptr, " \t" ); + line_ptr += white_len; - // Check for comment or empty line - if ( *line_ptr == '\0' || *line_ptr == '#' ) - continue; + // Check for comment or empty line + if ( *line_ptr == '\0' || *line_ptr == '#' ) + continue; - // Remove trailing white space - char *temp_ptr = line_ptr+strlen(line_ptr)-1; - while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) - { - *temp_ptr-- = '\0'; - temp_ptr--; - } + // Remove trailing white space + char *temp_ptr = line_ptr+strlen(line_ptr)-1; + while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) + { + *temp_ptr-- = '\0'; + temp_ptr--; + } - // Now look for the '=' in the middle of the line - temp_ptr = strchr( line_ptr, '=' ); - if ( !temp_ptr ) - { - Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line ); - continue; - } + // Now look for the '=' in the middle of the line + temp_ptr = strchr( line_ptr, '=' ); + if ( !temp_ptr ) + { + Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line ); + continue; + } - // Assign the name and value parts - char *name_ptr = line_ptr; - char *val_ptr = temp_ptr+1; + // Assign the name and value parts + char *name_ptr = line_ptr; + char *val_ptr = temp_ptr+1; - // Trim trailing space from the name part - do - { - *temp_ptr = '\0'; - temp_ptr--; - } - while ( *temp_ptr == ' ' || *temp_ptr == '\t' ); + // Trim trailing space from the name part + do + { + *temp_ptr = '\0'; + temp_ptr--; + } + while ( *temp_ptr == ' ' || *temp_ptr == '\t' ); - // Remove leading white space from the value part - white_len = strspn( val_ptr, " \t" ); - val_ptr += white_len; + // Remove leading white space from the value part + white_len = strspn( val_ptr, " \t" ); + val_ptr += white_len; - val = (char *)malloc( strlen(val_ptr)+1 ); - strncpy( val, val_ptr, strlen(val_ptr)+1 ); + if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) + staticConfig.DB_HOST = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 ) + staticConfig.DB_NAME = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 ) + staticConfig.DB_USER = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 ) + staticConfig.DB_PASS = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 ) + staticConfig.PATH_WEB = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_SERVER_HOST" ) == 0 ) + staticConfig.SERVER_NAME = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_SERVER_NAME" ) == 0 ) + staticConfig.SERVER_NAME = std::string(val_ptr); + else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 ) + staticConfig.SERVER_ID = atoi(val_ptr); + else + { + // We ignore this now as there may be more parameters than the + // c/c++ binaries are bothered about + // Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG ); + } + } // end foreach line of the config + fclose( cfg ); + zmDbConnect(); + config.Load(); + config.Assign(); - if ( strcasecmp( name_ptr, "ZM_DB_HOST" ) == 0 ) - staticConfig.DB_HOST = val; - else if ( strcasecmp( name_ptr, "ZM_DB_NAME" ) == 0 ) - staticConfig.DB_NAME = val; - else if ( strcasecmp( name_ptr, "ZM_DB_USER" ) == 0 ) - staticConfig.DB_USER = val; - else if ( strcasecmp( name_ptr, "ZM_DB_PASS" ) == 0 ) - staticConfig.DB_PASS = val; - else if ( strcasecmp( name_ptr, "ZM_PATH_WEB" ) == 0 ) - staticConfig.PATH_WEB = val; - else - { - // We ignore this now as there may be more parameters than the - // c/c++ binaries are bothered about - // Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG ); - } - } - fclose( cfg); - zmDbConnect(); - config.Load(); - config.Assign(); + // Populate the server config entries + if ( ! staticConfig.SERVER_ID ) { + if ( ! staticConfig.SERVER_NAME.empty() ) { + + Debug( 1, "Fetching ZM_SERVER_ID For Name = %s", staticConfig.SERVER_NAME.c_str() ); + std::string sql = stringtf("SELECT Id FROM Servers WHERE Name='%s'", staticConfig.SERVER_NAME.c_str() ); + if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { + staticConfig.SERVER_ID = atoi(dbrow[0]); + } else { + Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str() ); + } + + } // end if has SERVER_NAME + } else if ( staticConfig.SERVER_NAME.empty() ) { + Debug( 1, "Fetching ZM_SERVER_NAME For Id = %d", staticConfig.SERVER_ID ); + std::string sql = stringtf("SELECT Name FROM Servers WHERE Id='%d'", staticConfig.SERVER_ID ); + + if ( MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ) ) { + staticConfig.SERVER_NAME = std::string(dbrow[0]); + } else { + Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID ); + } + if ( staticConfig.SERVER_ID ) { + Debug( 3, "Multi-server configuration detected. Server is %d.", staticConfig.SERVER_ID ); + } else { + Debug( 3, "Single server configuration assumed because no Server ID or Name was specified." ); + } + } } StaticConfig staticConfig; ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type ) { - name = new char[strlen(p_name)+1]; - strcpy( name, p_name ); - value = new char[strlen(p_value)+1]; - strcpy( value, p_value ); - type = new char[strlen(p_type)+1]; - strcpy( type, p_type ); + name = new char[strlen(p_name)+1]; + strcpy( name, p_name ); + value = new char[strlen(p_value)+1]; + strcpy( value, p_value ); + type = new char[strlen(p_type)+1]; + strcpy( type, p_type ); - //Info( "Created new config item %s = %s (%s)\n", name, value, type ); + //Info( "Created new config item %s = %s (%s)\n", name, value, type ); - accessed = false; + accessed = false; } ConfigItem::~ConfigItem() { - delete[] name; - delete[] value; - delete[] type; + delete[] name; + delete[] value; + delete[] type; } void ConfigItem::ConvertValue() const { - if ( !strcmp( type, "boolean" ) ) - { - cfg_type = CFG_BOOLEAN; - cfg_value.boolean_value = (bool)strtol( value, 0, 0 ); - } - else if ( !strcmp( type, "integer" ) ) - { - cfg_type = CFG_INTEGER; - cfg_value.integer_value = strtol( value, 0, 10 ); - } - else if ( !strcmp( type, "hexadecimal" ) ) - { - cfg_type = CFG_INTEGER; - cfg_value.integer_value = strtol( value, 0, 16 ); - } - else if ( !strcmp( type, "decimal" ) ) - { - cfg_type = CFG_DECIMAL; - cfg_value.decimal_value = strtod( value, 0 ); - } - else - { - cfg_type = CFG_STRING; - cfg_value.string_value = value; - } - accessed = true; + if ( !strcmp( type, "boolean" ) ) + { + cfg_type = CFG_BOOLEAN; + cfg_value.boolean_value = (bool)strtol( value, 0, 0 ); + } + else if ( !strcmp( type, "integer" ) ) + { + cfg_type = CFG_INTEGER; + cfg_value.integer_value = strtol( value, 0, 10 ); + } + else if ( !strcmp( type, "hexadecimal" ) ) + { + cfg_type = CFG_INTEGER; + cfg_value.integer_value = strtol( value, 0, 16 ); + } + else if ( !strcmp( type, "decimal" ) ) + { + cfg_type = CFG_DECIMAL; + cfg_value.decimal_value = strtod( value, 0 ); + } + else + { + cfg_type = CFG_STRING; + cfg_value.string_value = value; + } + accessed = true; } bool ConfigItem::BooleanValue() const { - if ( !accessed ) - ConvertValue(); + if ( !accessed ) + ConvertValue(); - if ( cfg_type != CFG_BOOLEAN ) - { - Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); - exit( -1 ); - } + if ( cfg_type != CFG_BOOLEAN ) + { + Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); + exit( -1 ); + } - return( cfg_value.boolean_value ); + return( cfg_value.boolean_value ); } int ConfigItem::IntegerValue() const { - if ( !accessed ) - ConvertValue(); + if ( !accessed ) + ConvertValue(); - if ( cfg_type != CFG_INTEGER ) - { - Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); - exit( -1 ); - } + if ( cfg_type != CFG_INTEGER ) + { + Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); + exit( -1 ); + } - return( cfg_value.integer_value ); + return( cfg_value.integer_value ); } double ConfigItem::DecimalValue() const { - if ( !accessed ) - ConvertValue(); + if ( !accessed ) + ConvertValue(); - if ( cfg_type != CFG_DECIMAL ) - { - Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); - exit( -1 ); - } + if ( cfg_type != CFG_DECIMAL ) + { + Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); + exit( -1 ); + } - return( cfg_value.decimal_value ); + return( cfg_value.decimal_value ); } const char *ConfigItem::StringValue() const { - if ( !accessed ) - ConvertValue(); + if ( !accessed ) + ConvertValue(); - if ( cfg_type != CFG_STRING ) - { - Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); - exit( -1 ); - } + if ( cfg_type != CFG_STRING ) + { + Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type ); + exit( -1 ); + } - return( cfg_value.string_value ); + return( cfg_value.string_value ); } Config::Config() { - n_items = 0; - items = 0; + n_items = 0; + items = 0; } Config::~Config() { - if ( items ) - { - for ( int i = 0; i < n_items; i++ ) - { - delete items[i]; - } - delete[] items; - } + if ( items ) + { + for ( int i = 0; i < n_items; i++ ) + { + delete items[i]; + } + delete[] items; + } } void Config::Load() { - static char sql[ZM_SQL_SML_BUFSIZ]; + static char sql[ZM_SQL_SML_BUFSIZ]; - strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - n_items = mysql_num_rows( result ); + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + n_items = mysql_num_rows( result ); - if ( n_items <= ZM_MAX_CFG_ID ) - { - Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items ); - exit( -1 ); - } + if ( n_items <= ZM_MAX_CFG_ID ) + { + Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items ); + exit( -1 ); + } - items = new ConfigItem *[n_items]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] ); - } - mysql_free_result( result ); + items = new ConfigItem *[n_items]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] ); + } + mysql_free_result( result ); } void Config::Assign() @@ -275,27 +308,27 @@ ZM_CFG_ASSIGN_LIST const ConfigItem &Config::Item( int id ) { - if ( !n_items ) - { - Load(); - Assign(); - } + if ( !n_items ) + { + Load(); + Assign(); + } - if ( id < 0 || id > ZM_MAX_CFG_ID ) - { - Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id ); - exit( -1 ); - } + if ( id < 0 || id > ZM_MAX_CFG_ID ) + { + Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id ); + exit( -1 ); + } - ConfigItem *item = items[id]; - - if ( !item ) - { - Error( "Can't find config item %d", id ); - exit( -1 ); - } - - return( *item ); + ConfigItem *item = items[id]; + + if ( !item ) + { + Error( "Can't find config item %d", id ); + exit( -1 ); + } + + return( *item ); } Config config; diff --git a/src/zm_config.h.in b/src/zm_config.h.in index 1df1edf16..1deeff861 100644 --- a/src/zm_config.h.in +++ b/src/zm_config.h.in @@ -25,46 +25,48 @@ #include -#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file -#define ZM_VERSION "@VERSION@" // ZoneMinder Version +#define ZM_CONFIG "@ZM_CONFIG@" // Path to config file +#define ZM_VERSION "@VERSION@" // ZoneMinder Version -#define ZM_HAS_V4L1 @ZM_HAS_V4L1@ -#define ZM_HAS_V4L2 @ZM_HAS_V4L2@ -#define ZM_HAS_V4L @ZM_HAS_V4L@ +#define ZM_HAS_V4L1 @ZM_HAS_V4L1@ +#define ZM_HAS_V4L2 @ZM_HAS_V4L2@ +#define ZM_HAS_V4L @ZM_HAS_V4L@ #ifdef HAVE_LIBAVFORMAT -#define ZM_HAS_FFMPEG 1 +#define ZM_HAS_FFMPEG 1 #endif // HAVE_LIBAVFORMAT -#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling -#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling -#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling -#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT) -#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS) +#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling +#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling +#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling +#define ZM_MAX_IMAGE_DIM (ZM_MAX_IMAGE_WIDTH*ZM_MAX_IMAGE_HEIGHT) +#define ZM_MAX_IMAGE_SIZE (ZM_MAX_IMAGE_DIM*ZM_MAX_IMAGE_COLOURS) -#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP -#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP +#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP +#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP -#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements -#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer -#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer -#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer +#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements +#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer +#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer +#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer -#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer +#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer -#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle -#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc -#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc +#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle +#define ZM_SAMPLE_RATE int(1000000/ZM_MAX_FPS) // A general nyquist sample frequency for delays etc +#define ZM_SUSPENDED_RATE int(1000000/4) // A slower rate for when disabled etc extern void zmLoadConfig(); struct StaticConfig { - std::string DB_HOST; - std::string DB_NAME; - std::string DB_USER; - std::string DB_PASS; - std::string PATH_WEB; + std::string DB_HOST; + std::string DB_NAME; + std::string DB_USER; + std::string DB_PASS; + std::string PATH_WEB; + std::string SERVER_NAME; + unsigned int SERVER_ID; }; extern StaticConfig staticConfig; @@ -72,63 +74,63 @@ extern StaticConfig staticConfig; class ConfigItem { private: - char *name; - char *value; - char *type; + char *name; + char *value; + char *type; - mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type; - mutable union - { - bool boolean_value; - int integer_value; - double decimal_value; - char *string_value; - } cfg_value; - mutable bool accessed; + mutable enum { CFG_BOOLEAN, CFG_INTEGER, CFG_DECIMAL, CFG_STRING } cfg_type; + mutable union + { + bool boolean_value; + int integer_value; + double decimal_value; + char *string_value; + } cfg_value; + mutable bool accessed; public: - ConfigItem( const char *p_name, const char *p_value, const char *const p_type ); - ~ConfigItem(); - void ConvertValue() const; - bool BooleanValue() const; - int IntegerValue() const; - double DecimalValue() const; - const char *StringValue() const; + ConfigItem( const char *p_name, const char *p_value, const char *const p_type ); + ~ConfigItem(); + void ConvertValue() const; + bool BooleanValue() const; + int IntegerValue() const; + double DecimalValue() const; + const char *StringValue() const; - inline operator bool() const - { - return( BooleanValue() ); - } - inline operator int() const - { - return( IntegerValue() ); - } - inline operator double() const - { - return( DecimalValue() ); - } - inline operator const char *() const - { - return( StringValue() ); - } + inline operator bool() const + { + return( BooleanValue() ); + } + inline operator int() const + { + return( IntegerValue() ); + } + inline operator double() const + { + return( DecimalValue() ); + } + inline operator const char *() const + { + return( StringValue() ); + } }; class Config { public: - ZM_CFG_DECLARE_LIST + ZM_CFG_DECLARE_LIST private: - int n_items; - ConfigItem **items; + int n_items; + ConfigItem **items; public: - Config(); - ~Config(); + Config(); + ~Config(); - void Load(); - void Assign(); - const ConfigItem &Item( int id ); + void Load(); + void Assign(); + const ConfigItem &Item( int id ); }; extern Config config; diff --git a/src/zm_coord.h b/src/zm_coord.h index 6a6620529..7316244a2 100644 --- a/src/zm_coord.h +++ b/src/zm_coord.h @@ -28,40 +28,40 @@ class Coord { private: - int x, y; + int x, y; public: - inline Coord() : x(0), y(0) - { - } - inline Coord( int p_x, int p_y ) : x(p_x), y(p_y) - { - } - inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y) - { - } - inline int &X() { return( x ); } - inline const int &X() const { return( x ); } - inline int &Y() { return( y ); } - inline const int &Y() const { return( y ); } + inline Coord() : x(0), y(0) + { + } + inline Coord( int p_x, int p_y ) : x(p_x), y(p_y) + { + } + inline Coord( const Coord &p_coord ) : x(p_coord.x), y(p_coord.y) + { + } + inline int &X() { return( x ); } + inline const int &X() const { return( x ); } + inline int &Y() { return( y ); } + inline const int &Y() const { return( y ); } - inline static Coord Range( const Coord &coord1, const Coord &coord2 ) - { - Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 ); - return( result ); - } + inline static Coord Range( const Coord &coord1, const Coord &coord2 ) + { + Coord result( (coord1.x-coord2.x)+1, (coord1.y-coord2.y)+1 ); + return( result ); + } - inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); } - inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); } - inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); } - inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); } - inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); } - inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); } - inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); } - inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); } + inline bool operator==( const Coord &coord ) { return( x == coord.x && y == coord.y ); } + inline bool operator!=( const Coord &coord ) { return( x != coord.x || y != coord.y ); } + inline bool operator>( const Coord &coord ) { return( x > coord.x && y > coord.y ); } + inline bool operator>=( const Coord &coord ) { return( !(operator<(coord)) ); } + inline bool operator<( const Coord &coord ) { return( x < coord.x && y < coord.y ); } + inline bool operator<=( const Coord &coord ) { return( !(operator>(coord)) ); } + inline Coord &operator+=( const Coord &coord ) { x += coord.x; y += coord.y; return( *this ); } + inline Coord &operator-=( const Coord &coord ) { x -= coord.x; y -= coord.y; return( *this ); } - inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); } - inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); } + inline friend Coord operator+( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result += coord2; return( result ); } + inline friend Coord operator-( const Coord &coord1, const Coord &coord2 ) { Coord result( coord1 ); result -= coord2; return( result ); } }; #endif // ZM_COORD_H diff --git a/src/zm_curl_camera.cpp b/src/zm_curl_camera.cpp index da5f7180d..f9b6d0712 100644 --- a/src/zm_curl_camera.cpp +++ b/src/zm_curl_camera.cpp @@ -31,523 +31,523 @@ size_t content_length_match_len; size_t content_type_match_len; cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET ) + Camera( p_id, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), + mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET ) { - if ( capture ) - { - Initialise(); - } + if ( capture ) + { + Initialise(); + } } cURLCamera::~cURLCamera() { - if ( capture ) - { + if ( capture ) + { - Terminate(); - } + Terminate(); + } } void cURLCamera::Initialise() { - content_length_match_len = strlen(content_length_match); - content_type_match_len = strlen(content_type_match); + content_length_match_len = strlen(content_length_match); + content_type_match_len = strlen(content_type_match); - databuffer.expand(CURL_BUFFER_INITIAL_SIZE); + databuffer.expand(CURL_BUFFER_INITIAL_SIZE); - /* cURL initialization */ - cRet = curl_global_init(CURL_GLOBAL_ALL); - if(cRet != CURLE_OK) { - Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet)); - } + /* cURL initialization */ + cRet = curl_global_init(CURL_GLOBAL_ALL); + if(cRet != CURLE_OK) { + Fatal("libcurl initialization failed: ", curl_easy_strerror(cRet)); + } - Debug(2,"libcurl version: %s",curl_version()); + Debug(2,"libcurl version: %s",curl_version()); - /* Create the shared data mutex */ - nRet = pthread_mutex_init(&shareddata_mutex, NULL); - if(nRet != 0) { - Fatal("Shared data mutex creation failed: %s",strerror(nRet)); - } - /* Create the data available condition variable */ - nRet = pthread_cond_init(&data_available_cond, NULL); - if(nRet != 0) { - Fatal("Data available condition variable creation failed: %s",strerror(nRet)); - } - /* Create the request complete condition variable */ - nRet = pthread_cond_init(&request_complete_cond, NULL); - if(nRet != 0) { - Fatal("Request complete condition variable creation failed: %s",strerror(nRet)); - } + /* Create the shared data mutex */ + nRet = pthread_mutex_init(&shareddata_mutex, NULL); + if(nRet != 0) { + Fatal("Shared data mutex creation failed: %s",strerror(nRet)); + } + /* Create the data available condition variable */ + nRet = pthread_cond_init(&data_available_cond, NULL); + if(nRet != 0) { + Fatal("Data available condition variable creation failed: %s",strerror(nRet)); + } + /* Create the request complete condition variable */ + nRet = pthread_cond_init(&request_complete_cond, NULL); + if(nRet != 0) { + Fatal("Request complete condition variable creation failed: %s",strerror(nRet)); + } - /* Create the thread */ - nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this); - if(nRet != 0) { - Fatal("Thread creation failed: %s",strerror(nRet)); - } + /* Create the thread */ + nRet = pthread_create(&thread, NULL, thread_func_dispatcher, this); + if(nRet != 0) { + Fatal("Thread creation failed: %s",strerror(nRet)); + } } void cURLCamera::Terminate() { - /* Signal the thread to terminate */ - bTerminate = true; + /* Signal the thread to terminate */ + bTerminate = true; - /* Wait for thread termination */ - pthread_join(thread, NULL); + /* Wait for thread termination */ + pthread_join(thread, NULL); - /* Destroy condition variables */ - pthread_cond_destroy(&request_complete_cond); - pthread_cond_destroy(&data_available_cond); + /* Destroy condition variables */ + pthread_cond_destroy(&request_complete_cond); + pthread_cond_destroy(&data_available_cond); - /* Destroy mutex */ - pthread_mutex_destroy(&shareddata_mutex); + /* Destroy mutex */ + pthread_mutex_destroy(&shareddata_mutex); - /* cURL cleanup */ - curl_global_cleanup(); + /* cURL cleanup */ + curl_global_cleanup(); } int cURLCamera::PrimeCapture() { - //Info( "Priming capture from %s", mPath.c_str() ); - return 0; + //Info( "Priming capture from %s", mPath.c_str() ); + return 0; } int cURLCamera::PreCapture() { - // Nothing to do here - return( 0 ); + // Nothing to do here + return( 0 ); } int cURLCamera::Capture( Image &image ) { - bool frameComplete = false; + bool frameComplete = false; - /* MODE_STREAM specific variables */ - bool SubHeadersParsingComplete = false; - unsigned int frame_content_length = 0; - std::string frame_content_type; - bool need_more_data = false; + /* MODE_STREAM specific variables */ + bool SubHeadersParsingComplete = false; + unsigned int frame_content_length = 0; + std::string frame_content_type; + bool need_more_data = false; - /* Grab the mutex to ensure exclusive access to the shared data */ - lock(); + /* Grab the mutex to ensure exclusive access to the shared data */ + lock(); - while (!frameComplete) { + while (!frameComplete) { - /* If the work thread did a reset, reset our local variables */ - if(bReset) { - SubHeadersParsingComplete = false; - frame_content_length = 0; - frame_content_type.clear(); - need_more_data = false; - bReset = false; - } + /* If the work thread did a reset, reset our local variables */ + if(bReset) { + SubHeadersParsingComplete = false; + frame_content_length = 0; + frame_content_type.clear(); + need_more_data = false; + bReset = false; + } - if(mode == MODE_UNSET) { - /* Don't have a mode yet. Sleep while waiting for data */ - nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); - if(nRet != 0) { - Error("Failed waiting for available data condition variable: %s",strerror(nRet)); - return -20; - } - } + if(mode == MODE_UNSET) { + /* Don't have a mode yet. Sleep while waiting for data */ + nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); + if(nRet != 0) { + Error("Failed waiting for available data condition variable: %s",strerror(nRet)); + return -20; + } + } - if(mode == MODE_STREAM) { + if(mode == MODE_STREAM) { - /* Subheader parsing */ - while(!SubHeadersParsingComplete && !need_more_data) { + /* Subheader parsing */ + while(!SubHeadersParsingComplete && !need_more_data) { - size_t crlf_start, crlf_end, crlf_size; - std::string subheader; + size_t crlf_start, crlf_end, crlf_size; + std::string subheader; - /* Check if the buffer contains something */ - if(databuffer.empty()) { - /* Empty buffer, wait for data */ - need_more_data = true; - break; - } - - /* Find crlf start */ - crlf_start = memcspn(databuffer,"\r\n",databuffer.size()); - if(crlf_start == databuffer.size()) { - /* Not found, wait for more data */ - need_more_data = true; - break; - } + /* Check if the buffer contains something */ + if(databuffer.empty()) { + /* Empty buffer, wait for data */ + need_more_data = true; + break; + } + + /* Find crlf start */ + crlf_start = memcspn(databuffer,"\r\n",databuffer.size()); + if(crlf_start == databuffer.size()) { + /* Not found, wait for more data */ + need_more_data = true; + break; + } - /* See if we have enough data for determining crlf length */ - if(databuffer.size() < crlf_start+5) { - /* Need more data */ - need_more_data = true; - break; - } + /* See if we have enough data for determining crlf length */ + if(databuffer.size() < crlf_start+5) { + /* Need more data */ + need_more_data = true; + break; + } - /* Find crlf end and calculate crlf size */ - crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5); - crlf_size = (crlf_start + crlf_end) - crlf_start; + /* Find crlf end and calculate crlf size */ + crlf_end = memspn(((const char*)databuffer.head())+crlf_start,"\r\n",5); + crlf_size = (crlf_start + crlf_end) - crlf_start; - /* Is this the end of a previous stream? (This is just before the boundary) */ - if(crlf_start == 0) { - databuffer.consume(crlf_size); - continue; - } + /* Is this the end of a previous stream? (This is just before the boundary) */ + if(crlf_start == 0) { + databuffer.consume(crlf_size); + continue; + } - /* Check for invalid CRLF size */ - if(crlf_size > 4) { - Error("Invalid CRLF length"); - } + /* Check for invalid CRLF size */ + if(crlf_size > 4) { + Error("Invalid CRLF length"); + } - /* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */ - if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) { - /* This is the last header */ - SubHeadersParsingComplete = true; - } + /* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */ + if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) { + /* This is the last header */ + SubHeadersParsingComplete = true; + } - /* Copy the subheader, excluding the crlf */ - subheader.assign(databuffer, crlf_start); + /* Copy the subheader, excluding the crlf */ + subheader.assign(databuffer, crlf_start); - /* Advance the buffer past this one */ - databuffer.consume(crlf_start+crlf_size); + /* Advance the buffer past this one */ + databuffer.consume(crlf_start+crlf_size); - Debug(7,"Got subheader: %s",subheader.c_str()); + Debug(7,"Got subheader: %s",subheader.c_str()); - /* Find where the data in this header starts */ - size_t subheader_data_start = subheader.rfind(' '); - if(subheader_data_start == std::string::npos) { - subheader_data_start = subheader.find(':'); - } + /* Find where the data in this header starts */ + size_t subheader_data_start = subheader.rfind(' '); + if(subheader_data_start == std::string::npos) { + subheader_data_start = subheader.find(':'); + } - /* Extract the data into a string */ - std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos); + /* Extract the data into a string */ + std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos); - Debug(8,"Got subheader data: %s",subheader_data.c_str()); + Debug(8,"Got subheader data: %s",subheader_data.c_str()); - /* Check the header */ - if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) { - /* Found the content-length header */ - frame_content_length = atoi(subheader_data.c_str()); - Debug(6,"Got content-length subheader: %d",frame_content_length); - } else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) { - /* Found the content-type header */ - frame_content_type = subheader_data; - Debug(6,"Got content-type subheader: %s",frame_content_type.c_str()); - } + /* Check the header */ + if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) { + /* Found the content-length header */ + frame_content_length = atoi(subheader_data.c_str()); + Debug(6,"Got content-length subheader: %d",frame_content_length); + } else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) { + /* Found the content-type header */ + frame_content_type = subheader_data; + Debug(6,"Got content-type subheader: %s",frame_content_type.c_str()); + } - } + } - /* Attempt to extract the frame */ - if(!need_more_data) { - if(!SubHeadersParsingComplete) { - /* We haven't parsed all headers yet */ - need_more_data = true; - } else if(frame_content_length <= 0) { - /* Invalid frame */ - Error("Invalid frame: invalid content length"); - } else if(frame_content_type != "image/jpeg") { - /* Unsupported frame type */ - Error("Unsupported frame: %s",frame_content_type.c_str()); - } else if(frame_content_length > databuffer.size()) { - /* Incomplete frame, wait for more data */ - need_more_data = true; - } else { - /* All good. decode the image */ - image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); - frameComplete = true; - } - } + /* Attempt to extract the frame */ + if(!need_more_data) { + if(!SubHeadersParsingComplete) { + /* We haven't parsed all headers yet */ + need_more_data = true; + } else if(frame_content_length <= 0) { + /* Invalid frame */ + Error("Invalid frame: invalid content length"); + } else if(frame_content_type != "image/jpeg") { + /* Unsupported frame type */ + Error("Unsupported frame: %s",frame_content_type.c_str()); + } else if(frame_content_length > databuffer.size()) { + /* Incomplete frame, wait for more data */ + need_more_data = true; + } else { + /* All good. decode the image */ + image.DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder); + frameComplete = true; + } + } - /* Attempt to get more data */ - if(need_more_data) { - nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); - if(nRet != 0) { - Error("Failed waiting for available data condition variable: %s",strerror(nRet)); - return -18; - } - need_more_data = false; - } + /* Attempt to get more data */ + if(need_more_data) { + nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex); + if(nRet != 0) { + Error("Failed waiting for available data condition variable: %s",strerror(nRet)); + return -18; + } + need_more_data = false; + } - } else if(mode == MODE_SINGLE) { - /* Check if we have anything */ - if (!single_offsets.empty()) { - if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) { - /* Extract frame */ - image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); - single_offsets.pop_front(); - frameComplete = true; - } else { - /* This shouldn't happen */ - Error("Internal error. Attempting recovery"); - databuffer.consume(single_offsets.front()); - single_offsets.pop_front(); - } - } else { - /* Don't have a frame yet, wait for the request complete condition variable */ - nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex); - if(nRet != 0) { - Error("Failed waiting for request complete condition variable: %s",strerror(nRet)); - return -19; - } - } - } else { - /* Failed to match content-type */ - Fatal("Unable to match Content-Type. Check URL, username and password"); - } /* mode */ + } else if(mode == MODE_SINGLE) { + /* Check if we have anything */ + if (!single_offsets.empty()) { + if( (single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front()) ) { + /* Extract frame */ + image.DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder); + single_offsets.pop_front(); + frameComplete = true; + } else { + /* This shouldn't happen */ + Error("Internal error. Attempting recovery"); + databuffer.consume(single_offsets.front()); + single_offsets.pop_front(); + } + } else { + /* Don't have a frame yet, wait for the request complete condition variable */ + nRet = pthread_cond_wait(&request_complete_cond,&shareddata_mutex); + if(nRet != 0) { + Error("Failed waiting for request complete condition variable: %s",strerror(nRet)); + return -19; + } + } + } else { + /* Failed to match content-type */ + Fatal("Unable to match Content-Type. Check URL, username and password"); + } /* mode */ - } /* frameComplete loop */ + } /* frameComplete loop */ - /* Release the mutex */ - unlock(); + /* Release the mutex */ + unlock(); - if(!frameComplete) - return -1; + if(!frameComplete) + return -1; - return 0; + return 0; } int cURLCamera::PostCapture() { - // Nothing to do here - return( 0 ); + // Nothing to do here + return( 0 ); } size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata) { - lock(); + lock(); - /* Append the data we just received to our buffer */ - databuffer.append((const char*)buffer, size*nmemb); + /* Append the data we just received to our buffer */ + databuffer.append((const char*)buffer, size*nmemb); - /* Signal data available */ - nRet = pthread_cond_signal(&data_available_cond); - if(nRet != 0) { - Error("Failed signaling data available condition variable: %s",strerror(nRet)); - return -16; - } + /* Signal data available */ + nRet = pthread_cond_signal(&data_available_cond); + if(nRet != 0) { + Error("Failed signaling data available condition variable: %s",strerror(nRet)); + return -16; + } - unlock(); + unlock(); - /* Return bytes processed */ - return size*nmemb; + /* Return bytes processed */ + return size*nmemb; } size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata) { - std::string header; - header.assign((const char*)buffer, size*nmemb); - - Debug(4,"Got header: %s",header.c_str()); + std::string header; + header.assign((const char*)buffer, size*nmemb); + + Debug(4,"Got header: %s",header.c_str()); - /* Check Content-Type header */ - if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) { - size_t pos = header.find(';'); - if(pos != std::string::npos) { - header.erase(pos, std::string::npos); - } + /* Check Content-Type header */ + if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) { + size_t pos = header.find(';'); + if(pos != std::string::npos) { + header.erase(pos, std::string::npos); + } - pos = header.rfind(' '); - if(pos == std::string::npos) { - pos = header.find(':'); - } + pos = header.rfind(' '); + if(pos == std::string::npos) { + pos = header.find(':'); + } - std::string content_type = header.substr(pos+1, std::string::npos); - Debug(6,"Content-Type is: %s",content_type.c_str()); + std::string content_type = header.substr(pos+1, std::string::npos); + Debug(6,"Content-Type is: %s",content_type.c_str()); - lock(); + lock(); - const char* multipart_match = "multipart/x-mixed-replace"; - const char* image_jpeg_match = "image/jpeg"; - if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) { - Debug(7,"Content type matched as multipart/x-mixed-replace"); - mode = MODE_STREAM; - } else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) { - Debug(7,"Content type matched as image/jpeg"); - mode = MODE_SINGLE; - } + const char* multipart_match = "multipart/x-mixed-replace"; + const char* image_jpeg_match = "image/jpeg"; + if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) { + Debug(7,"Content type matched as multipart/x-mixed-replace"); + mode = MODE_STREAM; + } else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) { + Debug(7,"Content type matched as image/jpeg"); + mode = MODE_SINGLE; + } - unlock(); - } - - /* Return bytes processed */ - return size*nmemb; + unlock(); + } + + /* Return bytes processed */ + return size*nmemb; } void* cURLCamera::thread_func() { - long tRet; - double dSize; + long tRet; + double dSize; - c = curl_easy_init(); - if(c == NULL) { - Fatal("Failed getting easy handle from libcurl"); - } + c = curl_easy_init(); + if(c == NULL) { + Fatal("Failed getting easy handle from libcurl"); + } - /* Set URL */ - cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str()); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet)); - - /* Header callback */ - cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet)); + /* Set URL */ + cRet = curl_easy_setopt(c, CURLOPT_URL, mPath.c_str()); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl URL: %s", curl_easy_strerror(cRet)); + + /* Header callback */ + cRet = curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl header callback function: %s", curl_easy_strerror(cRet)); + cRet = curl_easy_setopt(c, CURLOPT_HEADERDATA, this); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl header callback object: %s", curl_easy_strerror(cRet)); - /* Data callback */ - cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet)); + /* Data callback */ + cRet = curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl data callback function: %s", curl_easy_strerror(cRet)); + cRet = curl_easy_setopt(c, CURLOPT_WRITEDATA, this); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl data callback object: %s", curl_easy_strerror(cRet)); - /* Progress callback */ - cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0); - if(cRet != CURLE_OK) - Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet)); - cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this); - if(cRet != CURLE_OK) - Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet)); + /* Progress callback */ + cRet = curl_easy_setopt(c, CURLOPT_NOPROGRESS, 0); + if(cRet != CURLE_OK) + Fatal("Failed enabling libcurl progress callback function: %s", curl_easy_strerror(cRet)); + cRet = curl_easy_setopt(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl progress callback function: %s", curl_easy_strerror(cRet)); + cRet = curl_easy_setopt(c, CURLOPT_PROGRESSDATA, this); + if(cRet != CURLE_OK) + Fatal("Failed setting libcurl progress callback object: %s", curl_easy_strerror(cRet)); - /* Set username and password */ - if(!mUser.empty()) { - cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str()); - if(cRet != CURLE_OK) - Error("Failed setting username: %s", curl_easy_strerror(cRet)); - } - if(!mPass.empty()) { - cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str()); - if(cRet != CURLE_OK) - Error("Failed setting password: %s", curl_easy_strerror(cRet)); - } + /* Set username and password */ + if(!mUser.empty()) { + cRet = curl_easy_setopt(c, CURLOPT_USERNAME, mUser.c_str()); + if(cRet != CURLE_OK) + Error("Failed setting username: %s", curl_easy_strerror(cRet)); + } + if(!mPass.empty()) { + cRet = curl_easy_setopt(c, CURLOPT_PASSWORD, mPass.c_str()); + if(cRet != CURLE_OK) + Error("Failed setting password: %s", curl_easy_strerror(cRet)); + } - /* Authenication preference */ - cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - if(cRet != CURLE_OK) - Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet)); + /* Authenication preference */ + cRet = curl_easy_setopt(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY); + if(cRet != CURLE_OK) + Warning("Failed setting libcurl acceptable http authenication methods: %s", curl_easy_strerror(cRet)); - /* Work loop */ - for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) { - tRet = 0; - while(!bTerminate) { - /* Do the work */ - cRet = curl_easy_perform(c); + /* Work loop */ + for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) { + tRet = 0; + while(!bTerminate) { + /* Do the work */ + cRet = curl_easy_perform(c); - if(mode == MODE_SINGLE) { - if(cRet != CURLE_OK) { - break; - } - /* Attempt to get the size of the file */ - cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize); - if(cRet != CURLE_OK) { - break; - } - /* We need to lock for the offsets array and the condition variable */ - lock(); - /* Push the size into our offsets array */ - if(dSize > 0) { - single_offsets.push_back(dSize); - } else { - Fatal("Unable to get the size of the image"); - } - /* Signal the request complete condition variable */ - tRet = pthread_cond_signal(&request_complete_cond); - if(tRet != 0) { - Error("Failed signaling request completed condition variable: %s",strerror(tRet)); - } - /* Unlock */ - unlock(); + if(mode == MODE_SINGLE) { + if(cRet != CURLE_OK) { + break; + } + /* Attempt to get the size of the file */ + cRet = curl_easy_getinfo(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize); + if(cRet != CURLE_OK) { + break; + } + /* We need to lock for the offsets array and the condition variable */ + lock(); + /* Push the size into our offsets array */ + if(dSize > 0) { + single_offsets.push_back(dSize); + } else { + Fatal("Unable to get the size of the image"); + } + /* Signal the request complete condition variable */ + tRet = pthread_cond_signal(&request_complete_cond); + if(tRet != 0) { + Error("Failed signaling request completed condition variable: %s",strerror(tRet)); + } + /* Unlock */ + unlock(); - } else if (mode == MODE_STREAM) { - break; - } - } + } else if (mode == MODE_STREAM) { + break; + } + } - /* Return value checking */ - if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) { - /* Aborted */ - break; - } else if (cRet != CURLE_OK) { - /* Some error */ - Error("cURL Request failed: %s",curl_easy_strerror(cRet)); - if(attempt < CURL_MAXRETRY) { - Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY); - /* Do a reset */ - lock(); - databuffer.clear(); - single_offsets.clear(); - mode = MODE_UNSET; - bReset = true; - unlock(); - } - tRet = -50; - } - } - - /* Cleanup */ - curl_easy_cleanup(c); - c = NULL; - - return (void*)tRet; + /* Return value checking */ + if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) { + /* Aborted */ + break; + } else if (cRet != CURLE_OK) { + /* Some error */ + Error("cURL Request failed: %s",curl_easy_strerror(cRet)); + if(attempt < CURL_MAXRETRY) { + Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY); + /* Do a reset */ + lock(); + databuffer.clear(); + single_offsets.clear(); + mode = MODE_UNSET; + bReset = true; + unlock(); + } + tRet = -50; + } + } + + /* Cleanup */ + curl_easy_cleanup(c); + c = NULL; + + return (void*)tRet; } int cURLCamera::lock() { - int nRet; + int nRet; - /* Lock shared data */ - nRet = pthread_mutex_lock(&shareddata_mutex); - if(nRet != 0) { - Error("Failed locking shared data mutex: %s",strerror(nRet)); - } - return nRet; + /* Lock shared data */ + nRet = pthread_mutex_lock(&shareddata_mutex); + if(nRet != 0) { + Error("Failed locking shared data mutex: %s",strerror(nRet)); + } + return nRet; } int cURLCamera::unlock() { - int nRet; + int nRet; - /* Unlock shared data */ - nRet = pthread_mutex_unlock(&shareddata_mutex); - if(nRet != 0) { - Error("Failed unlocking shared data mutex: %s",strerror(nRet)); - } - return nRet; + /* Unlock shared data */ + nRet = pthread_mutex_unlock(&shareddata_mutex); + if(nRet != 0) { + Error("Failed unlocking shared data mutex: %s",strerror(nRet)); + } + return nRet; } int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) { - /* Signal the curl thread to terminate */ - if(bTerminate) - return -10; - - return 0; + /* Signal the curl thread to terminate */ + if(bTerminate) + return -10; + + return 0; } /* These functions call the functions in the class for the correct object */ size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata) { - return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata); + return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata); } size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata) { - return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata); + return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata); } int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) { - return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow); + return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow); } void* thread_func_dispatcher(void* object) { - return ((cURLCamera*)object)->thread_func(); + return ((cURLCamera*)object)->thread_func(); } diff --git a/src/zm_curl_camera.h b/src/zm_curl_camera.h index dff2e3e52..f7c3540fe 100644 --- a/src/zm_curl_camera.h +++ b/src/zm_curl_camera.h @@ -42,55 +42,55 @@ class cURLCamera : public Camera { protected: - typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t; + typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t; - std::string mPath; - std::string mUser; - std::string mPass; + std::string mPath; + std::string mUser; + std::string mPass; - /* cURL object(s) */ - CURL* c; + /* cURL object(s) */ + CURL* c; - /* Shared data */ - volatile bool bTerminate; - volatile bool bReset; - volatile mode_t mode; - Buffer databuffer; - std::deque single_offsets; + /* Shared data */ + volatile bool bTerminate; + volatile bool bReset; + volatile mode_t mode; + Buffer databuffer; + std::deque single_offsets; - /* pthread objects */ - pthread_t thread; - pthread_mutex_t shareddata_mutex; - pthread_cond_t data_available_cond; - pthread_cond_t request_complete_cond; + /* pthread objects */ + pthread_t thread; + pthread_mutex_t shareddata_mutex; + pthread_cond_t data_available_cond; + pthread_cond_t request_complete_cond; public: - cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~cURLCamera(); + cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); + ~cURLCamera(); - const std::string &Path() const { return( mPath ); } - const std::string &Username() const { return( mUser ); } - const std::string &Password() const { return( mPass ); } + const std::string &Path() const { return( mPath ); } + const std::string &Username() const { return( mUser ); } + const std::string &Password() const { return( mPass ); } - void Initialise(); - void Terminate(); + void Initialise(); + void Terminate(); - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); - size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata); - size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata); - int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow); - int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data); - void* thread_func(); - int lock(); - int unlock(); + size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata); + size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata); + int progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow); + int debug_callback(CURL* handle, curl_infotype type, char* str, size_t strsize, void* data); + void* thread_func(); + int lock(); + int unlock(); private: - int nRet; - CURLcode cRet; + int nRet; + CURLcode cRet; }; diff --git a/src/zm_db.cpp b/src/zm_db.cpp index 26a33d6f5..8f2527fc2 100644 --- a/src/zm_db.cpp +++ b/src/zm_db.cpp @@ -29,38 +29,85 @@ int zmDbConnected = false; void zmDbConnect() { - if ( !mysql_init( &dbconn ) ) - { - Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - my_bool reconnect = 1; - if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) ) - Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) ); - std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" ); - if ( colonIndex != std::string::npos ) + if ( !mysql_init( &dbconn ) ) + { + Error( "Can't initialise database connection: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + my_bool reconnect = 1; + if ( mysql_options( &dbconn, MYSQL_OPT_RECONNECT, &reconnect ) ) + Fatal( "Can't set database auto reconnect option: %s", mysql_error( &dbconn ) ); + std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" ); + if ( colonIndex != std::string::npos ) + { + std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); + std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 ); + if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) ) { - std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); - std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 ); - if ( !mysql_real_connect( &dbconn, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) ) - { - Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); } - else + } + else + { + if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) ) { - if ( !mysql_real_connect( &dbconn, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) ) - { - Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + Error( "Can't connect to server: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); } - if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) - { - Error( "Can't select database: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - zmDbConnected = true; + } + if ( mysql_select_db( &dbconn, staticConfig.DB_NAME.c_str() ) ) + { + Error( "Can't select database: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + zmDbConnected = true; } +void zmDbClose() +{ + if ( zmDbConnected ) + { + mysql_close( &dbconn ); + // mysql_init() call implicitly mysql_library_init() but + // mysql_close() does not call mysql_library_end() + mysql_library_end(); + zmDbConnected = false; + } +} + +MYSQL_RES * zmDbFetch( const char * query ) { + if ( ! zmDbConnected ) { + Error( "Not connected." ); + return NULL; + } + + if ( mysql_query( &dbconn, query ) ) { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + return NULL; + } + Debug( 4, "Success running query: %s", query ); + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) { + Error( "Can't use query result: %s for query %s", mysql_error( &dbconn ), query ); + return NULL; + } + return result; +} // end MYSQL_RES * zmDbFetch( const char * query ); + +MYSQL_ROW zmDbFetchOne( const char *query ) { + MYSQL_RES *result = zmDbFetch( query ); + int n_rows = mysql_num_rows( result ); + if ( n_rows != 1 ) { + Error( "Bogus number of lines return from query, %d returned for query %s.", n_rows, query ); + return NULL; + } + + MYSQL_ROW dbrow = mysql_fetch_row( result ); + mysql_free_result( result ); + if ( ! dbrow ) { + Error("Error getting row from query %s. Error is %s", query, mysql_error( &dbconn ) ); + return NULL; + } + return dbrow; +} diff --git a/src/zm_db.h b/src/zm_db.h index dda2dd30f..6ec1b5e4e 100644 --- a/src/zm_db.h +++ b/src/zm_db.h @@ -30,6 +30,11 @@ extern MYSQL dbconn; extern int zmDbConnected; void zmDbConnect(); +void zmDbClose(); + +MYSQL_RES * zmDbFetch( const char *query ); +MYSQL_ROW zmDbFetchOne( const char *query ); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 1c7e13e7d..50028ffa6 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -36,6 +36,12 @@ #include "zm_event.h" #include "zm_monitor.h" +// sendfile tricks +extern "C" +{ +#include "zm_sendfile.h" +} + #include "zmf.h" #if HAVE_SYS_SENDFILE_H @@ -53,501 +59,458 @@ int Event::pre_alarm_count = 0; Event::PreAlarmData Event::pre_alarm_data[MAX_PRE_ALARM_FRAMES] = { { 0 } }; Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ) : - monitor( p_monitor ), - start_time( p_start_time ), - cause( p_cause ), - noteSetMap( p_noteSetMap ) + monitor( p_monitor ), + start_time( p_start_time ), + cause( p_cause ), + noteSetMap( p_noteSetMap ) { - if ( !initialised ) - Initialise(); + if ( !initialised ) + Initialise(); - std::string notes; - createNotes( notes ); + std::string notes; + createNotes( notes ); - bool untimedEvent = false; - if ( !start_time.tv_sec ) + bool untimedEvent = false; + if ( !start_time.tv_sec ) + { + untimedEvent = true; + gettimeofday( &start_time, 0 ); + } + + static char sql[ZM_SQL_MED_BUFSIZ]; + + struct tm *stime = localtime( &start_time.tv_sec ); + snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, Name, StartTime, Width, Height, Cause, Notes ) values ( %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s' )", monitor->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), notes.c_str() ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert event: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + id = mysql_insert_id( &dbconn ); + if ( untimedEvent ) + { + Warning( "Event %d has zero time, setting to current", id ); + } + end_time.tv_sec = 0; + frames = 0; + alarm_frames = 0; + tot_score = 0; + max_score = 0; + + if ( config.use_deep_storage ) + { + char *path_ptr = path; + path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d", config.dir_events, monitor->Id() ); + + int dt_parts[6]; + dt_parts[0] = stime->tm_year-100; + dt_parts[1] = stime->tm_mon+1; + dt_parts[2] = stime->tm_mday; + dt_parts[3] = stime->tm_hour; + dt_parts[4] = stime->tm_min; + dt_parts[5] = stime->tm_sec; + + char date_path[PATH_MAX] = ""; + char time_path[PATH_MAX] = ""; + char *time_path_ptr = time_path; + for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) { - untimedEvent = true; - gettimeofday( &start_time, 0 ); - } + path_ptr += snprintf( path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i] ); - static char sql[ZM_SQL_MED_BUFSIZ]; - - struct tm *stime = localtime( &start_time.tv_sec ); - snprintf( sql, sizeof(sql), "insert into Events ( MonitorId, Name, StartTime, Width, Height, Cause, Notes ) values ( %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s' )", monitor->Id(), start_time.tv_sec, monitor->Width(), monitor->Height(), cause.c_str(), notes.c_str() ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - id = mysql_insert_id( &dbconn ); - if ( untimedEvent ) - { - Warning( "Event %d has zero time, setting to current", id ); - } - end_time.tv_sec = 0; - frames = 0; - alarm_frames = 0; - tot_score = 0; - max_score = 0; - - if ( config.use_deep_storage ) - { - char *path_ptr = path; - path_ptr += snprintf( path_ptr, sizeof(path), "%s/%d", config.dir_events, monitor->Id() ); - - int dt_parts[6]; - dt_parts[0] = stime->tm_year-100; - dt_parts[1] = stime->tm_mon+1; - dt_parts[2] = stime->tm_mday; - dt_parts[3] = stime->tm_hour; - dt_parts[4] = stime->tm_min; - dt_parts[5] = stime->tm_sec; - - char date_path[PATH_MAX] = ""; - char time_path[PATH_MAX] = ""; - char *time_path_ptr = time_path; - for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) - { - path_ptr += snprintf( path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i] ); - - struct stat statbuf; - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { - Fatal( "Can't mkdir %s: %s", path, strerror(errno)); - } - } - if ( i == 2 ) - strncpy( date_path, path, sizeof(date_path) ); - else if ( i >= 3 ) - time_path_ptr += snprintf( time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i] ); - } - char id_file[PATH_MAX]; - // Create event id symlink - snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id ); - if ( symlink( time_path, id_file ) < 0 ) - Fatal( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); - // Create empty id tag file - snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); - if ( FILE *id_fp = fopen( id_file, "w" ) ) - fclose( id_fp ); - else - Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); - } - else - { - snprintf( path, sizeof(path), "%s/%d/%d", config.dir_events, monitor->Id(), id ); - - struct stat statbuf; - errno = 0; - stat( path, &statbuf ); + struct stat statbuf; + errno = 0; + if ( stat( path, &statbuf ) ) { if ( errno == ENOENT || errno == ENOTDIR ) { - if ( mkdir( path, 0755 ) ) - { - Error( "Can't mkdir %s: %s", path, strerror(errno)); - } + if ( mkdir( path, 0755 ) ) + { + Fatal( "Can't mkdir %s: %s", path, strerror(errno)); + } + } else { + Warning( "Error stat'ing %s, may be fatal. error is %s", path, strerror(errno)); } - char id_file[PATH_MAX]; - // Create empty id tag file - snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); - if ( FILE *id_fp = fopen( id_file, "w" ) ) - fclose( id_fp ); - else - Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); + } + if ( i == 2 ) + strncpy( date_path, path, sizeof(date_path) ); + else if ( i >= 3 ) + time_path_ptr += snprintf( time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i] ); } - last_db_frame = 0; + char id_file[PATH_MAX]; + // Create event id symlink + snprintf( id_file, sizeof(id_file), "%s/.%d", date_path, id ); + if ( symlink( time_path, id_file ) < 0 ) + Fatal( "Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); + // Create empty id tag file + snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); + if ( FILE *id_fp = fopen( id_file, "w" ) ) + fclose( id_fp ); + else + Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); + } + else + { + snprintf( path, sizeof(path), "%s/%d/%d", config.dir_events, monitor->Id(), id ); + + struct stat statbuf; + errno = 0; + stat( path, &statbuf ); + if ( errno == ENOENT || errno == ENOTDIR ) + { + if ( mkdir( path, 0755 ) ) + { + Error( "Can't mkdir %s: %s", path, strerror(errno)); + } + } + char id_file[PATH_MAX]; + // Create empty id tag file + snprintf( id_file, sizeof(id_file), "%s/.%d", path, id ); + if ( FILE *id_fp = fopen( id_file, "w" ) ) + fclose( id_fp ); + else + Fatal( "Can't fopen %s: %s", id_file, strerror(errno)); + } + last_db_frame = 0; } Event::~Event() { - if ( frames > last_db_frame ) - { - struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); - - Debug( 1, "Adding closing frame %d to DB", frames ); - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - } - - static char sql[ZM_SQL_MED_BUFSIZ]; - + if ( frames > last_db_frame ) + { struct DeltaTimeval delta_time; DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); - snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); + Debug( 1, "Adding closing frame %d to DB", frames ); + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ( %d, %d, from_unixtime( %ld ), %s%ld.%02ld )", id, frames, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); if ( mysql_query( &dbconn, sql ) ) { - Error( "Can't update event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); } + } + + static char sql[ZM_SQL_MED_BUFSIZ]; + + struct DeltaTimeval delta_time; + DELTA_TIMEVAL( delta_time, end_time, start_time, DT_PREC_2 ); + + snprintf( sql, sizeof(sql), "update Events set Name='%s%d', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", monitor->EventPrefix(), id, end_time.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't update event: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } void Event::createNotes( std::string ¬es ) { - notes.clear(); - for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); mapIter++ ) + notes.clear(); + for ( StringSetMap::const_iterator mapIter = noteSetMap.begin(); mapIter != noteSetMap.end(); mapIter++ ) + { + notes += mapIter->first; + notes += ": "; + const StringSet &stringSet = mapIter->second; + for ( StringSet::const_iterator setIter = stringSet.begin(); setIter != stringSet.end(); setIter++ ) { - notes += mapIter->first; - notes += ": "; - const StringSet &stringSet = mapIter->second; - for ( StringSet::const_iterator setIter = stringSet.begin(); setIter != stringSet.end(); setIter++ ) - { - if ( setIter != stringSet.begin() ) - notes += ", "; - notes += *setIter; - } + if ( setIter != stringSet.begin() ) + notes += ", "; + notes += *setIter; } + } } int Event::sd = -1; bool Event::OpenFrameSocket( int monitor_id ) { - if ( sd > 0 ) + if ( sd > 0 ) + { + close( sd ); + } + + sd = socket( AF_UNIX, SOCK_STREAM, 0); + if ( sd < 0 ) + { + Error( "Can't create socket: %s", strerror(errno) ); + return( false ); + } + + int socket_buffer_size = config.frame_socket_size; + if ( socket_buffer_size > 0 ) + { + if ( setsockopt( sd, SOL_SOCKET, SO_SNDBUF, &socket_buffer_size, sizeof(socket_buffer_size) ) < 0 ) { - close( sd ); + Error( "Can't get socket buffer size to %d, error = %s", socket_buffer_size, strerror(errno) ); + close( sd ); + sd = -1; + return( false ); } + } - sd = socket( AF_UNIX, SOCK_STREAM, 0); - if ( sd < 0 ) - { - Error( "Can't create socket: %s", strerror(errno) ); - return( false ); - } + int flags; + if ( (flags = fcntl( sd, F_GETFL )) < 0 ) + { + Error( "Can't get socket flags, error = %s", strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } + flags |= O_NONBLOCK; + if ( fcntl( sd, F_SETFL, flags ) < 0 ) + { + Error( "Can't set socket flags, error = %s", strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } - int socket_buffer_size = config.frame_socket_size; - if ( socket_buffer_size > 0 ) - { - if ( setsockopt( sd, SOL_SOCKET, SO_SNDBUF, &socket_buffer_size, sizeof(socket_buffer_size) ) < 0 ) - { - Error( "Can't get socket buffer size to %d, error = %s", socket_buffer_size, strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } - } + char sock_path[PATH_MAX] = ""; + snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); - int flags; - if ( (flags = fcntl( sd, F_GETFL )) < 0 ) - { - Error( "Can't get socket flags, error = %s", strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } - flags |= O_NONBLOCK; - if ( fcntl( sd, F_SETFL, flags ) < 0 ) - { - Error( "Can't set socket flags, error = %s", strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } + struct sockaddr_un addr; - char sock_path[PATH_MAX] = ""; - snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); + strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); + addr.sun_family = AF_UNIX; - struct sockaddr_un addr; + if ( connect( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) + { + Warning( "Can't connect to frame server: %s", strerror(errno) ); + close( sd ); + sd = -1; + return( false ); + } - strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); - addr.sun_family = AF_UNIX; - - if ( connect( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) - { - Warning( "Can't connect to frame server: %s", strerror(errno) ); - close( sd ); - sd = -1; - return( false ); - } - - Debug( 1, "Opened connection to frame server" ); - return( true ); + Debug( 1, "Opened connection to frame server" ); + return( true ); } bool Event::ValidateFrameSocket( int monitor_id ) { - if ( sd < 0 ) - { - return( OpenFrameSocket( monitor_id ) ); - } - return( true ); + if ( sd < 0 ) + { + return( OpenFrameSocket( monitor_id ) ); + } + return( true ); } bool Event::SendFrameImage( const Image *image, bool alarm_frame ) { - if ( !ValidateFrameSocket( monitor->Id() ) ) + if ( !ValidateFrameSocket( monitor->Id() ) ) + { + return( false ); + } + + static int jpg_buffer_size = 0; + static unsigned char jpg_buffer[ZM_MAX_IMAGE_SIZE]; + + image->EncodeJpeg( jpg_buffer, &jpg_buffer_size, (alarm_frame&&(config.jpeg_alarm_file_quality>config.jpeg_file_quality))?config.jpeg_alarm_file_quality:config.jpeg_file_quality ); + + static FrameHeader frame_header; + + frame_header.event_id = id; + if ( config.use_deep_storage ) + frame_header.event_time = start_time.tv_sec; + frame_header.frame_id = frames; + frame_header.alarm_frame = alarm_frame; + frame_header.image_length = jpg_buffer_size; + + struct iovec iovecs[2]; + iovecs[0].iov_base = &frame_header; + iovecs[0].iov_len = sizeof(frame_header); + iovecs[1].iov_base = jpg_buffer; + iovecs[1].iov_len = jpg_buffer_size; + + ssize_t writev_size = sizeof(frame_header)+jpg_buffer_size; + ssize_t writev_result = writev( sd, iovecs, sizeof(iovecs)/sizeof(*iovecs)); + if ( writev_result != writev_size ) + { + if ( writev_result < 0 ) { - return( false ); + if ( errno == EAGAIN ) + { + Warning( "Blocking write detected" ); + } + else + { + Error( "Can't write frame: %s", strerror(errno) ); + close( sd ); + sd = -1; + } } - - static int jpg_buffer_size = 0; - static unsigned char jpg_buffer[ZM_MAX_IMAGE_SIZE]; - - image->EncodeJpeg( jpg_buffer, &jpg_buffer_size, (alarm_frame&&(config.jpeg_alarm_file_quality>config.jpeg_file_quality))?config.jpeg_alarm_file_quality:config.jpeg_file_quality ); - - static FrameHeader frame_header; - - frame_header.event_id = id; - if ( config.use_deep_storage ) - frame_header.event_time = start_time.tv_sec; - frame_header.frame_id = frames; - frame_header.alarm_frame = alarm_frame; - frame_header.image_length = jpg_buffer_size; - - struct iovec iovecs[2]; - iovecs[0].iov_base = &frame_header; - iovecs[0].iov_len = sizeof(frame_header); - iovecs[1].iov_base = jpg_buffer; - iovecs[1].iov_len = jpg_buffer_size; - - ssize_t writev_size = sizeof(frame_header)+jpg_buffer_size; - ssize_t writev_result = writev( sd, iovecs, sizeof(iovecs)/sizeof(*iovecs)); - if ( writev_result != writev_size ) + else { - if ( writev_result < 0 ) - { - if ( errno == EAGAIN ) - { - Warning( "Blocking write detected" ); - } - else - { - Error( "Can't write frame: %s", strerror(errno) ); - close( sd ); - sd = -1; - } - } - else - { - Error( "Incomplete frame write: %zd of %zd bytes written", writev_result, writev_size ); - close( sd ); - sd = -1; - } - return( false ); + Error( "Incomplete frame write: %zd of %zd bytes written", writev_result, writev_size ); + close( sd ); + sd = -1; } - Debug( 1, "Wrote frame image, %d bytes", jpg_buffer_size ); + return( false ); + } + Debug( 1, "Wrote frame image, %d bytes", jpg_buffer_size ); - return( true ); + return( true ); } bool Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame ) { - if ( config.timestamp_on_capture ) - { - if ( !config.opt_frame_server || !SendFrameImage( image, alarm_frame) ) - { - if ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) - image->WriteJpeg( event_file, config.jpeg_alarm_file_quality ); - else - image->WriteJpeg( event_file ); - } - } - else - { - Image ts_image( *image ); - monitor->TimestampImage( &ts_image, ×tamp ); - if ( !config.opt_frame_server || !SendFrameImage( &ts_image, alarm_frame) ) - { - if ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) - ts_image.WriteJpeg( event_file, config.jpeg_alarm_file_quality ); - else - ts_image.WriteJpeg( event_file ); - } - } - return( true ); + Image* ImgToWrite; + Image* ts_image = NULL; + + if ( config.timestamp_on_capture ) // stash the image we plan to use in another pointer regardless if timestamped. + { + ts_image = new Image(*image); + monitor->TimestampImage( ts_image, ×tamp ); + ImgToWrite=ts_image; + } + else + ImgToWrite=image; + + if ( !config.opt_frame_server || !SendFrameImage(ImgToWrite, alarm_frame) ) + { + int thisquality = ( alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality) ) ? config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default + ImgToWrite->WriteJpeg( event_file, thisquality, (monitor->Exif() ? timestamp : (timeval){0,0}) ); // exif is only timestamp at present this switches on or off for write + } + if(ts_image) delete(ts_image); // clean up if used. + return( true ); } void Event::updateNotes( const StringSetMap &newNoteSetMap ) { - bool update = false; + bool update = false; - //Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() ); - if ( newNoteSetMap.size() > 0 ) + //Info( "Checking notes, %d <> %d", noteSetMap.size(), newNoteSetMap.size() ); + if ( newNoteSetMap.size() > 0 ) + { + if ( noteSetMap.size() == 0 ) { - if ( noteSetMap.size() == 0 ) + noteSetMap = newNoteSetMap; + update = true; + } + else + { + for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin(); newNoteSetMapIter != newNoteSetMap.end(); newNoteSetMapIter++ ) + { + const std::string &newNoteGroup = newNoteSetMapIter->first; + const StringSet &newNoteSet = newNoteSetMapIter->second; + //Info( "Got %d new strings", newNoteSet.size() ); + if ( newNoteSet.size() > 0 ) { - noteSetMap = newNoteSetMap; + StringSetMap::iterator noteSetMapIter = noteSetMap.find( newNoteGroup ); + if ( noteSetMapIter == noteSetMap.end() ) + { + //Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() ); + noteSetMap.insert( StringSetMap::value_type( newNoteGroup, newNoteSet ) ); update = true; - } - else - { - for ( StringSetMap::const_iterator newNoteSetMapIter = newNoteSetMap.begin(); newNoteSetMapIter != newNoteSetMap.end(); newNoteSetMapIter++ ) + } + else + { + StringSet ¬eSet = noteSetMapIter->second; + //Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() ); + for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin(); newNoteSetIter != newNoteSet.end(); newNoteSetIter++ ) { - const std::string &newNoteGroup = newNoteSetMapIter->first; - const StringSet &newNoteSet = newNoteSetMapIter->second; - //Info( "Got %d new strings", newNoteSet.size() ); - if ( newNoteSet.size() > 0 ) - { - StringSetMap::iterator noteSetMapIter = noteSetMap.find( newNoteGroup ); - if ( noteSetMapIter == noteSetMap.end() ) - { - //Info( "Can't find note group %s, copying %d strings", newNoteGroup.c_str(), newNoteSet.size() ); - noteSetMap.insert( StringSetMap::value_type( newNoteGroup, newNoteSet ) ); - update = true; - } - else - { - StringSet ¬eSet = noteSetMapIter->second; - //Info( "Found note group %s, got %d strings", newNoteGroup.c_str(), newNoteSet.size() ); - for ( StringSet::const_iterator newNoteSetIter = newNoteSet.begin(); newNoteSetIter != newNoteSet.end(); newNoteSetIter++ ) - { - const std::string &newNote = *newNoteSetIter; - StringSet::iterator noteSetIter = noteSet.find( newNote ); - if ( noteSetIter == noteSet.end() ) - { - noteSet.insert( newNote ); - update = true; - } - } - } - } + const std::string &newNote = *newNoteSetIter; + StringSet::iterator noteSetIter = noteSet.find( newNote ); + if ( noteSetIter == noteSet.end() ) + { + noteSet.insert( newNote ); + update = true; + } } + } } + } } + } - if ( update ) - { - std::string notes; - createNotes( notes ); + if ( update ) + { + std::string notes; + createNotes( notes ); - Debug( 2, "Updating notes for event %d, '%s'", id, notes.c_str() ); - static char sql[ZM_SQL_MED_BUFSIZ]; + Debug( 2, "Updating notes for event %d, '%s'", id, notes.c_str() ); + static char sql[ZM_SQL_MED_BUFSIZ]; #if USE_PREPARED_SQL - static MYSQL_STMT *stmt = 0; + static MYSQL_STMT *stmt = 0; - char notesStr[ZM_SQL_MED_BUFSIZ] = ""; - unsigned long notesLen = 0; + char notesStr[ZM_SQL_MED_BUFSIZ] = ""; + unsigned long notesLen = 0; - if ( !stmt ) - { - const char *sql = "update Events set Notes = ? where Id = ?"; + if ( !stmt ) + { + const char *sql = "update Events set Notes = ? where Id = ?"; - stmt = mysql_stmt_init( &dbconn ); - if ( mysql_stmt_prepare( stmt, sql, strlen(sql) ) ) - { - Fatal( "Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt) ); - } + stmt = mysql_stmt_init( &dbconn ); + if ( mysql_stmt_prepare( stmt, sql, strlen(sql) ) ) + { + Fatal( "Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt) ); + } - /* Get the parameter count from the statement */ - if ( mysql_stmt_param_count( stmt ) != 2 ) - { - Fatal( "Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count( stmt ), sql ); - } + /* Get the parameter count from the statement */ + if ( mysql_stmt_param_count( stmt ) != 2 ) + { + Fatal( "Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count( stmt ), sql ); + } - MYSQL_BIND bind[2]; - memset(bind, 0, sizeof(bind)); + MYSQL_BIND bind[2]; + memset(bind, 0, sizeof(bind)); - /* STRING PARAM */ - bind[0].buffer_type = MYSQL_TYPE_STRING; - bind[0].buffer = (char *)notesStr; - bind[0].buffer_length = sizeof(notesStr); - bind[0].is_null = 0; - bind[0].length = ¬esLen; + /* STRING PARAM */ + bind[0].buffer_type = MYSQL_TYPE_STRING; + bind[0].buffer = (char *)notesStr; + bind[0].buffer_length = sizeof(notesStr); + bind[0].is_null = 0; + bind[0].length = ¬esLen; - bind[1].buffer_type= MYSQL_TYPE_LONG; - bind[1].buffer= (char *)&id; - bind[1].is_null= 0; - bind[1].length= 0; + bind[1].buffer_type= MYSQL_TYPE_LONG; + bind[1].buffer= (char *)&id; + bind[1].is_null= 0; + bind[1].length= 0; - /* Bind the buffers */ - if ( mysql_stmt_bind_param( stmt, bind ) ) - { - Fatal( "Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt) ); - } - } - - strncpy( notesStr, notes.c_str(), sizeof(notesStr) ); - notesLen = notes.length(); - - if ( mysql_stmt_execute( stmt ) ) - { - Fatal( "Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt) ); - } -#else - static char escapedNotes[ZM_SQL_MED_BUFSIZ]; - - mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); - - snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert event: %s", mysql_error( &dbconn ) ); - } -#endif + /* Bind the buffers */ + if ( mysql_stmt_bind_param( stmt, bind ) ) + { + Fatal( "Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt) ); + } } + + strncpy( notesStr, notes.c_str(), sizeof(notesStr) ); + notesLen = notes.length(); + + if ( mysql_stmt_execute( stmt ) ) + { + Fatal( "Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt) ); + } +#else + static char escapedNotes[ZM_SQL_MED_BUFSIZ]; + + mysql_real_escape_string( &dbconn, escapedNotes, notes.c_str(), notes.length() ); + + snprintf( sql, sizeof(sql), "update Events set Notes = '%s' where Id = %d", escapedNotes, id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert event: %s", mysql_error( &dbconn ) ); + } +#endif + } } void Event::AddFrames( int n_frames, Image **images, struct timeval **timestamps ) { - for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) { - AddFramesInternal(n_frames, i, images, timestamps); - } + for (int i = 0; i < n_frames; i += ZM_SQL_BATCH_SIZE) { + AddFramesInternal(n_frames, i, images, timestamps); + } } void Event::AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ) { - static char sql[ZM_SQL_LGE_BUFSIZ]; - strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) ); - int frameCount = 0; - for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) + static char sql[ZM_SQL_LGE_BUFSIZ]; + strncpy( sql, "insert into Frames ( EventId, FrameId, TimeStamp, Delta ) values ", sizeof(sql) ); + int frameCount = 0; + for ( int i = start_frame; i < n_frames && i - start_frame < ZM_SQL_BATCH_SIZE; i++ ) + { + if ( !timestamps[i]->tv_sec ) { - if ( !timestamps[i]->tv_sec ) - { - Debug( 1, "Not adding pre-capture frame %d, zero timestamp", i ); - continue; - } - - frames++; - - static char event_file[PATH_MAX]; - snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); - - Debug( 1, "Writing pre-capture frame %d", frames ); - WriteFrameImage( images[i], *(timestamps[i]), event_file ); - - struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 ); - - int sql_len = strlen(sql); - snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %d, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); - - frameCount++; - } - - if ( frameCount ) - { - Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); - *(sql+strlen(sql)-2) = '\0'; - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert frames: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - last_db_frame = frames; - } - else - { - Debug( 1, "No valid pre-capture frames to add" ); - } -} - -void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *alarm_image ) -{ - if ( !timestamp.tv_sec ) - { - Debug( 1, "Not adding new frame, zero timestamp" ); - return; + Debug( 1, "Not adding pre-capture frame %d, zero timestamp", i ); + continue; } frames++; @@ -555,920 +518,975 @@ void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image * static char event_file[PATH_MAX]; snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); - Debug( 1, "Writing capture frame %d", frames ); - WriteFrameImage( image, timestamp, event_file ); + Debug( 1, "Writing pre-capture frame %d", frames ); + WriteFrameImage( images[i], *(timestamps[i]), event_file ); struct DeltaTimeval delta_time; - DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 ); + DELTA_TIMEVAL( delta_time, *(timestamps[i]), start_time, DT_PREC_2 ); - bool db_frame = (score>=0) || ((frames%config.bulk_frame_interval)==0) || !frames; + int sql_len = strlen(sql); + snprintf( sql+sql_len, sizeof(sql)-sql_len, "( %d, %d, from_unixtime(%ld), %s%ld.%02ld ), ", id, frames, timestamps[i]->tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec ); - if ( db_frame ) + frameCount++; + } + + if ( frameCount ) + { + Debug( 1, "Adding %d/%d frames to DB", frameCount, n_frames ); + *(sql+strlen(sql)-2) = '\0'; + if ( mysql_query( &dbconn, sql ) ) { - const char *frame_type = score>0?"Alarm":(score<0?"Bulk":"Normal"); - - Debug( 1, "Adding frame %d to DB", frames ); - static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type, timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - last_db_frame = frames; - - // We are writing a bulk frame - if ( score < 0 ) - { - snprintf( sql, sizeof(sql), "update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't update event: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - } + Error( "Can't insert frames: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); } + last_db_frame = frames; + } + else + { + Debug( 1, "No valid pre-capture frames to add" ); + } +} - end_time = timestamp; +void Event::AddFrame( Image *image, struct timeval timestamp, int score, Image *alarm_image ) +{ + if ( !timestamp.tv_sec ) + { + Debug( 1, "Not adding new frame, zero timestamp" ); + return; + } - if ( score > 0 ) + frames++; + + static char event_file[PATH_MAX]; + snprintf( event_file, sizeof(event_file), capture_file_format, path, frames ); + + Debug( 1, "Writing capture frame %d", frames ); + WriteFrameImage( image, timestamp, event_file ); + + struct DeltaTimeval delta_time; + DELTA_TIMEVAL( delta_time, timestamp, start_time, DT_PREC_2 ); + + const char *frame_type = score>0?"Alarm":(score<0?"Bulk":"Normal"); + if ( score < 0 ) + score = 0; + + bool db_frame = (strcmp(frame_type,"Bulk") != 0) || ((frames%config.bulk_frame_interval)==0) || !frames; + if ( db_frame ) + { + + Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, frame_type ); + static char sql[ZM_SQL_MED_BUFSIZ]; + snprintf( sql, sizeof(sql), "insert into Frames ( EventId, FrameId, Type, TimeStamp, Delta, Score ) values ( %d, %d, '%s', from_unixtime( %ld ), %s%ld.%02ld, %d )", id, frames, frame_type, timestamp.tv_sec, delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, score ); + if ( mysql_query( &dbconn, sql ) ) { - alarm_frames++; - - tot_score += score; - if ( score > (int)max_score ) - max_score = score; - - if ( alarm_image ) - { - snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames ); - - Debug( 1, "Writing analysis frame %d", frames ); - WriteFrameImage( alarm_image, timestamp, event_file, true ); - } + Error( "Can't insert frame: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); } - - /* This makes viewing the diagnostic images impossible because it keeps deleting them - if ( config.record_diag_images ) + last_db_frame = frames; + + // We are writing a Bulk frame + if ( !strcmp( frame_type,"Bulk") ) { - char diag_glob[PATH_MAX] = ""; - - snprintf( diag_glob, sizeof(diag_glob), "%s/%d/diag-*.jpg", config.dir_events, monitor->Id() ); - glob_t pglob; - int glob_status = glob( diag_glob, 0, 0, &pglob ); - if ( glob_status != 0 ) - { - if ( glob_status < 0 ) - { - Error( "Can't glob '%s': %s", diag_glob, strerror(errno) ); - } - else - { - Debug( 1, "Can't glob '%s': %d", diag_glob, glob_status ); - } - } - else - { - char new_diag_path[PATH_MAX] = ""; - for ( int i = 0; i < pglob.gl_pathc; i++ ) - { - char *diag_path = pglob.gl_pathv[i]; - - char *diag_file = strstr( diag_path, "diag-" ); - - if ( diag_file ) - { - snprintf( new_diag_path, sizeof(new_diag_path), general_file_format, path, frames, diag_file ); - - if ( rename( diag_path, new_diag_path ) < 0 ) - { - Error( "Can't rename '%s' to '%s': %s", diag_path, new_diag_path, strerror(errno) ); - } - } - } - } - globfree( &pglob ); + snprintf( sql, sizeof(sql), "update Events set Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d where Id = %d", delta_time.positive?"":"-", delta_time.sec, delta_time.fsec, frames, alarm_frames, tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score, id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't update event: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } - */ + } + + end_time = timestamp; + + // We are writing an Alarm frame + if ( !strcmp( frame_type,"Alarm") ) + { + alarm_frames++; + + tot_score += score; + if ( score > (int)max_score ) + max_score = score; + + if ( alarm_image ) + { + snprintf( event_file, sizeof(event_file), analyse_file_format, path, frames ); + + Debug( 1, "Writing analysis frame %d", frames ); + WriteFrameImage( alarm_image, timestamp, event_file, true ); + } + } + + /* This makes viewing the diagnostic images impossible because it keeps deleting them + if ( config.record_diag_images ) + { + char diag_glob[PATH_MAX] = ""; + + snprintf( diag_glob, sizeof(diag_glob), "%s/%d/diag-*.jpg", config.dir_events, monitor->Id() ); + glob_t pglob; + int glob_status = glob( diag_glob, 0, 0, &pglob ); + if ( glob_status != 0 ) + { + if ( glob_status < 0 ) + { + Error( "Can't glob '%s': %s", diag_glob, strerror(errno) ); + } + else + { + Debug( 1, "Can't glob '%s': %d", diag_glob, glob_status ); + } + } + else + { + char new_diag_path[PATH_MAX] = ""; + for ( int i = 0; i < pglob.gl_pathc; i++ ) + { + char *diag_path = pglob.gl_pathv[i]; + + char *diag_file = strstr( diag_path, "diag-" ); + + if ( diag_file ) + { + snprintf( new_diag_path, sizeof(new_diag_path), general_file_format, path, frames, diag_file ); + + if ( rename( diag_path, new_diag_path ) < 0 ) + { + Error( "Can't rename '%s' to '%s': %s", diag_path, new_diag_path, strerror(errno) ); + } + } + } + } + globfree( &pglob ); + } + */ } bool EventStream::loadInitialEventData( int monitor_id, time_t event_time ) { - static char sql[ZM_SQL_SML_BUFSIZ]; + static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time ); + snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %d and unix_timestamp( EndTime ) > %ld order by Id asc limit 1", monitor_id, event_time ); - if ( mysql_query( &dbconn, sql ) ) + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + MYSQL_ROW dbrow = mysql_fetch_row( result ); + + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + int init_event_id = atoi( dbrow[0] ); + + mysql_free_result( result ); + + loadEventData( init_event_id ); + + if ( event_time ) + { + curr_stream_time = event_time; + curr_frame_id = 1; + if ( event_time >= event_data->start_time ) { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - MYSQL_ROW dbrow = mysql_fetch_row( result ); - - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - int init_event_id = atoi( dbrow[0] ); - - mysql_free_result( result ); - - loadEventData( init_event_id ); - - if ( event_time ) - { - curr_stream_time = event_time; - curr_frame_id = 1; - if ( event_time >= event_data->start_time ) + for (unsigned int i = 0; i < event_data->frame_count; i++ ) + { + //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); + if ( event_data->frames[i].timestamp >= event_time ) { - for (unsigned int i = 0; i < event_data->frame_count; i++ ) - { - //Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time ); - if ( event_data->frames[i].timestamp >= event_time ) - { - curr_frame_id = i+1; - Debug( 3, "Set cst:%.2f", curr_stream_time ); - Debug( 3, "Set cfid:%d", curr_frame_id ); - break; - } - } - Debug( 3, "Skipping %ld frames", event_data->frame_count ); + curr_frame_id = i+1; + Debug( 3, "Set cst:%.2f", curr_stream_time ); + Debug( 3, "Set cfid:%d", curr_frame_id ); + break; } + } + Debug( 3, "Skipping %ld frames", event_data->frame_count ); } - return( true ); + } + return( true ); } -bool EventStream::loadInitialEventData( int init_event_id, int init_frame_id ) +bool EventStream::loadInitialEventData( int init_event_id, unsigned int init_frame_id ) { - loadEventData( init_event_id ); + loadEventData( init_event_id ); - if ( init_frame_id ) - { - curr_stream_time = event_data->frames[init_frame_id-1].timestamp; - curr_frame_id = init_frame_id; - } - else - { - curr_stream_time = event_data->start_time; - } + if ( init_frame_id ) + { + curr_stream_time = event_data->frames[init_frame_id-1].timestamp; + curr_frame_id = init_frame_id; + } + else + { + curr_stream_time = event_data->start_time; + } - return( true ); + return( true ); } bool EventStream::loadEventData( int event_id ) { - static char sql[ZM_SQL_MED_BUFSIZ]; + static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select M.Id, M.Name, E.Frames, unix_timestamp( StartTime ) as StartTimestamp, max(F.Delta)-min(F.Delta) as Duration from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where E.Id = %d group by E.Id", event_id ); + snprintf( sql, sizeof(sql), "select M.Id, M.Name, E.Frames, unix_timestamp( StartTime ) as StartTimestamp, max(F.Delta)-min(F.Delta) as Duration from Events as E inner join Monitors as M on E.MonitorId = M.Id inner join Frames as F on E.Id = F.EventId where E.Id = %d group by E.Id", event_id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - if ( !mysql_num_rows( result ) ) - { - Fatal( "Unable to load event %d, not found in DB", event_id ); - } + if ( !mysql_num_rows( result ) ) + { + Fatal( "Unable to load event %d, not found in DB", event_id ); + } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + MYSQL_ROW dbrow = mysql_fetch_row( result ); - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - delete event_data; - event_data = new EventData; - event_data->event_id = event_id; - event_data->monitor_id = atoi( dbrow[0] ); - event_data->start_time = atoi(dbrow[3]); - if ( config.use_deep_storage ) - { - struct tm *event_time = localtime( &event_data->start_time ); - if ( config.dir_events[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); - else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); - } + delete event_data; + event_data = new EventData; + event_data->event_id = event_id; + event_data->monitor_id = atoi( dbrow[0] ); + event_data->start_time = atoi(dbrow[3]); + if ( config.use_deep_storage ) + { + struct tm *event_time = localtime( &event_data->start_time ); + if ( config.dir_events[0] == '/' ) + snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); else + snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday, event_time->tm_hour, event_time->tm_min, event_time->tm_sec ); + } + else + { + if ( config.dir_events[0] == '/' ) + snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", config.dir_events, event_data->monitor_id, event_data->event_id ); + else + snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_data->event_id ); + } + event_data->frame_count = dbrow[2] == NULL ? 0 : atoi(dbrow[2]); + event_data->duration = atof(dbrow[4]); + + updateFrameRate( (double)event_data->frame_count/event_data->duration ); + + mysql_free_result( result ); + + snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + event_data->n_frames = mysql_num_rows( result ); + + event_data->frames = new FrameData[event_data->frame_count]; + int id, last_id = 0; + time_t timestamp, last_timestamp = event_data->start_time; + double delta, last_delta = 0.0; + while ( ( dbrow = mysql_fetch_row( result ) ) ) + { + id = atoi(dbrow[0]); + timestamp = atoi(dbrow[1]); + delta = atof(dbrow[2]); + int id_diff = id - last_id; + double frame_delta = (delta-last_delta)/id_diff; + if ( id_diff > 1 ) { - if ( config.dir_events[0] == '/' ) - snprintf( event_data->path, sizeof(event_data->path), "%s/%ld/%ld", config.dir_events, event_data->monitor_id, event_data->event_id ); - else - snprintf( event_data->path, sizeof(event_data->path), "%s/%s/%ld/%ld", staticConfig.PATH_WEB.c_str(), config.dir_events, event_data->monitor_id, event_data->event_id ); + for ( int i = last_id+1; i < id; i++ ) + { + event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta)); + event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time); + event_data->frames[i-1].delta = frame_delta; + event_data->frames[i-1].in_db = false; + } } - event_data->frame_count = dbrow[2] == NULL ? 0 : atoi(dbrow[2]); - event_data->duration = atof(dbrow[4]); + event_data->frames[id-1].timestamp = timestamp; + event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time); + event_data->frames[id-1].delta = id>1?frame_delta:0.0; + event_data->frames[id-1].in_db = true; + last_id = id; + last_delta = delta; + last_timestamp = timestamp; + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + //for ( int i = 0; i < 250; i++ ) + //{ + //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db ); + //} - mysql_free_result( result ); + mysql_free_result( result ); - snprintf( sql, sizeof(sql), "select FrameId, unix_timestamp( `TimeStamp` ), Delta from Frames where EventId = %d order by FrameId asc", event_id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( forceEventChange || mode == MODE_ALL_GAPLESS ) + { + if ( replay_rate > 0 ) + curr_stream_time = event_data->frames[0].timestamp; + else + curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp; + } + Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration ); - result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - event_data->n_frames = mysql_num_rows( result ); - - event_data->frames = new FrameData[event_data->frame_count]; - int id, last_id = 0; - time_t timestamp, last_timestamp = event_data->start_time; - double delta, last_delta = 0.0; - while ( ( dbrow = mysql_fetch_row( result ) ) ) - { - id = atoi(dbrow[0]); - timestamp = atoi(dbrow[1]); - delta = atof(dbrow[2]); - int id_diff = id - last_id; - double frame_delta = (delta-last_delta)/id_diff; - if ( id_diff > 1 ) - { - for ( int i = last_id+1; i < id; i++ ) - { - event_data->frames[i-1].timestamp = (time_t)(last_timestamp + ((i-last_id)*frame_delta)); - event_data->frames[i-1].offset = (time_t)(event_data->frames[i-1].timestamp-event_data->start_time); - event_data->frames[i-1].delta = frame_delta; - event_data->frames[i-1].in_db = false; - } - } - event_data->frames[id-1].timestamp = timestamp; - event_data->frames[id-1].offset = (time_t)(event_data->frames[id-1].timestamp-event_data->start_time); - event_data->frames[id-1].delta = id>1?frame_delta:0.0; - event_data->frames[id-1].in_db = true; - last_id = id; - last_delta = delta; - last_timestamp = timestamp; - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - //for ( int i = 0; i < 250; i++ ) - //{ - //Info( "%d -> %d @ %f (%d)", i+1, event_data->frames[i].timestamp, event_data->frames[i].delta, event_data->frames[i].in_db ); - //} - - mysql_free_result( result ); - - if ( forceEventChange || mode == MODE_ALL_GAPLESS ) - { - if ( replay_rate > 0 ) - curr_stream_time = event_data->frames[0].timestamp; - else - curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp; - } - Debug( 2, "Event:%ld, Frames:%ld, Duration: %.2f", event_data->event_id, event_data->frame_count, event_data->duration ); - - return( true ); + return( true ); } void EventStream::processCommand( const CmdMsg *msg ) { - Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ) - // Check for incoming command - switch( (MsgCommand)msg->msg_data[0] ) + Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ) + // Check for incoming command + switch( (MsgCommand)msg->msg_data[0] ) + { + case CMD_PAUSE : { - case CMD_PAUSE : - { - Debug( 1, "Got PAUSE command" ); + Debug( 1, "Got PAUSE command" ); - // Set paused flag - paused = true; - replay_rate = ZM_RATE_BASE; - last_frame_sent = TV_2_FLOAT( now ); - break; - } - case CMD_PLAY : - { - Debug( 1, "Got PLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } + // Set paused flag + paused = true; + replay_rate = ZM_RATE_BASE; + last_frame_sent = TV_2_FLOAT( now ); + break; + } + case CMD_PLAY : + { + Debug( 1, "Got PLAY command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + } - // If we are in single event mode and at the last frame, replay the current event - if ( (mode == MODE_SINGLE) && (curr_frame_id == event_data->frame_count) ) - curr_frame_id = 1; + // If we are in single event mode and at the last frame, replay the current event + if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) + curr_frame_id = 1; - replay_rate = ZM_RATE_BASE; - break; - } - case CMD_VARPLAY : - { - Debug( 1, "Got VARPLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; - break; - } - case CMD_STOP : - { - Debug( 1, "Got STOP command" ); + replay_rate = ZM_RATE_BASE; + break; + } + case CMD_VARPLAY : + { + Debug( 1, "Got VARPLAY command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + } + replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; + break; + } + case CMD_STOP : + { + Debug( 1, "Got STOP command" ); - // Clear paused flag - paused = false; - break; - } - case CMD_FASTFWD : - { - Debug( 1, "Got FAST FWD command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - // Set play rate - switch ( replay_rate ) - { - case 2 * ZM_RATE_BASE : - replay_rate = 5 * ZM_RATE_BASE; - break; - case 5 * ZM_RATE_BASE : - replay_rate = 10 * ZM_RATE_BASE; - break; - case 10 * ZM_RATE_BASE : - replay_rate = 25 * ZM_RATE_BASE; - break; - case 25 * ZM_RATE_BASE : - case 50 * ZM_RATE_BASE : - replay_rate = 50 * ZM_RATE_BASE; - break; - default : - replay_rate = 2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_SLOWFWD : - { - Debug( 1, "Got SLOW FWD command" ); - // Set paused flag - paused = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = 1; - break; - } - case CMD_SLOWREV : - { - Debug( 1, "Got SLOW REV command" ); - // Set paused flag - paused = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = -1; - break; - } - case CMD_FASTREV : - { - Debug( 1, "Got FAST REV command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - } - // Set play rate - switch ( replay_rate ) - { - case -2 * ZM_RATE_BASE : - replay_rate = -5 * ZM_RATE_BASE; - break; - case -5 * ZM_RATE_BASE : - replay_rate = -10 * ZM_RATE_BASE; - break; - case -10 * ZM_RATE_BASE : - replay_rate = -25 * ZM_RATE_BASE; - break; - case -25 * ZM_RATE_BASE : - case -50 * ZM_RATE_BASE : - replay_rate = -50 * ZM_RATE_BASE; - break; - default : - replay_rate = -2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_ZOOMIN : - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); - switch ( zoom ) - { - case 100: - zoom = 150; - break; - case 150: - zoom = 200; - break; - case 200: - zoom = 300; - break; - case 300: - zoom = 400; - break; - case 400: - default : - zoom = 500; - break; - } - break; - } - case CMD_ZOOMOUT : - { - Debug( 1, "Got ZOOM OUT command" ); - switch ( zoom ) - { - case 500: - zoom = 400; - break; - case 400: - zoom = 300; - break; - case 300: - zoom = 200; - break; - case 200: - zoom = 150; - break; - case 150: - default : - zoom = 100; - break; - } - break; - } - case CMD_PAN : - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got PAN command, to %d,%d", x, y ); - break; - } - case CMD_SCALE : - { - scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - Debug( 1, "Got SCALE command, to %d", scale ); - break; - } - case CMD_PREV : - { - Debug( 1, "Got PREV command" ); - if ( replay_rate >= 0 ) - curr_frame_id = 0; - else - curr_frame_id = event_data->frame_count+1; - paused = false; - forceEventChange = true; - break; - } - case CMD_NEXT : - { - Debug( 1, "Got NEXT command" ); - if ( replay_rate >= 0 ) - curr_frame_id = event_data->frame_count+1; - else - curr_frame_id = 0; - paused = false; - forceEventChange = true; - break; - } - case CMD_SEEK : - { - int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration); - Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id ); - break; - } - case CMD_QUERY : - { - Debug( 1, "Got QUERY command, sending STATUS" ); - break; - } + // Clear paused flag + paused = false; + break; + } + case CMD_FASTFWD : + { + Debug( 1, "Got FAST FWD command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + } + // Set play rate + switch ( replay_rate ) + { + case 2 * ZM_RATE_BASE : + replay_rate = 5 * ZM_RATE_BASE; + break; + case 5 * ZM_RATE_BASE : + replay_rate = 10 * ZM_RATE_BASE; + break; + case 10 * ZM_RATE_BASE : + replay_rate = 25 * ZM_RATE_BASE; + break; + case 25 * ZM_RATE_BASE : + case 50 * ZM_RATE_BASE : + replay_rate = 50 * ZM_RATE_BASE; + break; default : - { - // Do nothing, for now - } + replay_rate = 2 * ZM_RATE_BASE; + break; + } + break; } - struct { - int event; - int progress; - int rate; - int zoom; - bool paused; - } status_data; - - status_data.event = event_data->event_id; - status_data.progress = (int)event_data->frames[curr_frame_id-1].offset; - status_data.rate = replay_rate; - status_data.zoom = zoom; - status_data.paused = paused; - Debug( 2, "E:%d, P:%d, p:%d R:%d, Z:%d", - status_data.event, - status_data.paused, - status_data.progress, - status_data.rate, - status_data.zoom - ); - - DataMsg status_msg; - status_msg.msg_type = MSG_DATA_EVENT; - memcpy( &status_msg.msg_data, &status_data, sizeof(status_msg.msg_data) ); - if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) + case CMD_SLOWFWD : { - //if ( errno != EAGAIN ) - { - Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); - exit( -1 ); - } + Debug( 1, "Got SLOW FWD command" ); + // Set paused flag + paused = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = 1; + break; } + case CMD_SLOWREV : + { + Debug( 1, "Got SLOW REV command" ); + // Set paused flag + paused = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = -1; + break; + } + case CMD_FASTREV : + { + Debug( 1, "Got FAST REV command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + } + // Set play rate + switch ( replay_rate ) + { + case -2 * ZM_RATE_BASE : + replay_rate = -5 * ZM_RATE_BASE; + break; + case -5 * ZM_RATE_BASE : + replay_rate = -10 * ZM_RATE_BASE; + break; + case -10 * ZM_RATE_BASE : + replay_rate = -25 * ZM_RATE_BASE; + break; + case -25 * ZM_RATE_BASE : + case -50 * ZM_RATE_BASE : + replay_rate = -50 * ZM_RATE_BASE; + break; + default : + replay_rate = -2 * ZM_RATE_BASE; + break; + } + break; + } + case CMD_ZOOMIN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); + switch ( zoom ) + { + case 100: + zoom = 150; + break; + case 150: + zoom = 200; + break; + case 200: + zoom = 300; + break; + case 300: + zoom = 400; + break; + case 400: + default : + zoom = 500; + break; + } + break; + } + case CMD_ZOOMOUT : + { + Debug( 1, "Got ZOOM OUT command" ); + switch ( zoom ) + { + case 500: + zoom = 400; + break; + case 400: + zoom = 300; + break; + case 300: + zoom = 200; + break; + case 200: + zoom = 150; + break; + case 150: + default : + zoom = 100; + break; + } + break; + } + case CMD_PAN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got PAN command, to %d,%d", x, y ); + break; + } + case CMD_SCALE : + { + scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + Debug( 1, "Got SCALE command, to %d", scale ); + break; + } + case CMD_PREV : + { + Debug( 1, "Got PREV command" ); + if ( replay_rate >= 0 ) + curr_frame_id = 0; + else + curr_frame_id = event_data->frame_count+1; + paused = false; + forceEventChange = true; + break; + } + case CMD_NEXT : + { + Debug( 1, "Got NEXT command" ); + if ( replay_rate >= 0 ) + curr_frame_id = event_data->frame_count+1; + else + curr_frame_id = 0; + paused = false; + forceEventChange = true; + break; + } + case CMD_SEEK : + { + int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration); + Debug( 1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id ); + break; + } + case CMD_QUERY : + { + Debug( 1, "Got QUERY command, sending STATUS" ); + break; + } + case CMD_QUIT : + { + Info ("User initiated exit - CMD_QUIT"); + break; + } + default : + { + // Do nothing, for now + } + } + struct { + int event; + int progress; + int rate; + int zoom; + bool paused; + } status_data; - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + status_data.event = event_data->event_id; + status_data.progress = (int)event_data->frames[curr_frame_id-1].offset; + status_data.rate = replay_rate; + status_data.zoom = zoom; + status_data.paused = paused; + Debug( 2, "E:%d, P:%d, p:%d R:%d, Z:%d", + status_data.event, + status_data.paused, + status_data.progress, + status_data.rate, + status_data.zoom + ); + + DataMsg status_msg; + status_msg.msg_type = MSG_DATA_EVENT; + memcpy( &status_msg.msg_data, &status_data, sizeof(status_data) ); + if ( sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) ) < 0 ) + { + //if ( errno != EAGAIN ) + { + Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); + exit( -1 ); + } + } + // quit after sending a status, if this was a quit request + if ((MsgCommand)msg->msg_data[0]==CMD_QUIT) + exit(0); + + updateFrameRate( (double)event_data->frame_count/event_data->duration ); } void EventStream::checkEventLoaded() { - bool reload_event = false; - static char sql[ZM_SQL_SML_BUFSIZ]; + bool reload_event = false; + static char sql[ZM_SQL_SML_BUFSIZ]; - if ( curr_frame_id <= 0 ) + if ( curr_frame_id <= 0 ) + { + snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id ); + reload_event = true; + } + else if ( (unsigned int)curr_frame_id > event_data->frame_count ) + { + snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id ); + reload_event = true; + } + + if ( reload_event ) + { + if ( forceEventChange || mode != MODE_SINGLE ) { - snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id < %ld order by Id desc limit 1", event_data->monitor_id, event_data->event_id ); - reload_event = true; - } - else if ( (unsigned int)curr_frame_id > event_data->frame_count ) - { - snprintf( sql, sizeof(sql), "select Id from Events where MonitorId = %ld and Id > %ld order by Id asc limit 1", event_data->monitor_id, event_data->event_id ); - reload_event = true; - } + //Info( "SQL:%s", sql ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - if ( reload_event ) - { - if ( forceEventChange || mode != MODE_SINGLE ) - { - //Info( "SQL:%s", sql ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + MYSQL_ROW dbrow = mysql_fetch_row( result ); - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( dbrow ) + { + int event_id = atoi(dbrow[0]); + Debug( 1, "Loading new event %d", event_id ); - if ( dbrow ) - { - int event_id = atoi(dbrow[0]); - Debug( 1, "Loading new event %d", event_id ); + loadEventData( event_id ); - loadEventData( event_id ); - - Debug( 2, "Current frame id = %d", curr_frame_id ); - if ( replay_rate < 0 ) - curr_frame_id = event_data->frame_count; - else - curr_frame_id = 1; - Debug( 2, "New frame id = %d", curr_frame_id ); - } - else - { - if ( curr_frame_id <= 0 ) - curr_frame_id = 1; - else - curr_frame_id = event_data->frame_count; - paused = true; - } - mysql_free_result( result ); - forceEventChange = false; - } + Debug( 2, "Current frame id = %d", curr_frame_id ); + if ( replay_rate < 0 ) + curr_frame_id = event_data->frame_count; else - { - if ( curr_frame_id <= 0 ) - curr_frame_id = 1; - else - curr_frame_id = event_data->frame_count; - paused = true; - } + curr_frame_id = 1; + Debug( 2, "New frame id = %d", curr_frame_id ); + } + else + { + if ( curr_frame_id <= 0 ) + curr_frame_id = 1; + else + curr_frame_id = event_data->frame_count; + paused = true; + } + mysql_free_result( result ); + forceEventChange = false; } + else + { + if ( curr_frame_id <= 0 ) + curr_frame_id = 1; + else + curr_frame_id = event_data->frame_count; + paused = true; + } + } } bool EventStream::sendFrame( int delta_us ) { - Debug( 2, "Sending frame %d", curr_frame_id ); + Debug( 2, "Sending frame %d", curr_frame_id ); - static char filepath[PATH_MAX]; - static struct stat filestat; - FILE *fdj = NULL; - - snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); + static char filepath[PATH_MAX]; + static struct stat filestat; + FILE *fdj = NULL; + + snprintf( filepath, sizeof(filepath), Event::capture_file_format, event_data->path, curr_frame_id ); #if HAVE_LIBAVCODEC - if ( type == STREAM_MPEG ) + if ( type == STREAM_MPEG ) + { + Image image( filepath ); + + Image *send_image = prepareImage( &image ); + + if ( !vid_stream ) { - Image image( filepath ); + vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); + fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); + vid_stream->OpenStream(); + } + /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 ); + } + else +#endif // HAVE_LIBAVCODEC + { + static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; - Image *send_image = prepareImage( &image ); + int img_buffer_size = 0; + uint8_t *img_buffer = temp_img_buffer; - if ( !vid_stream ) - { - vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); - fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); - vid_stream->OpenStream(); - } - /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000 ); + bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); + + fprintf( stdout, "--ZoneMinderFrame\r\n" ); + + if ( type != STREAM_JPEG ) + send_raw = false; + + if ( send_raw ) + { + fdj = fopen( filepath, "rb" ); + if ( !fdj ) + { + Error( "Can't open %s: %s", filepath, strerror(errno) ); + return( false ); + } +#if HAVE_SENDFILE + if( fstat(fileno(fdj),&filestat) < 0 ) { + Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); + return( false ); + } +#else + img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); +#endif } else -#endif // HAVE_LIBAVCODEC { - static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; + Image image( filepath ); - int img_buffer_size = 0; - uint8_t *img_buffer = temp_img_buffer; + Image *send_image = prepareImage( &image ); - bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); - - fprintf( stdout, "--ZoneMinderFrame\r\n" ); - - if ( type != STREAM_JPEG ) - send_raw = false; - - if ( send_raw ) - { - fdj = fopen( filepath, "rb" ); - if ( !fdj ) - { - Error( "Can't open %s: %s", filepath, strerror(errno) ); - return( false ); - } -#if HAVE_SENDFILE - if( fstat(fileno(fdj),&filestat) < 0 ) { - Error( "Failed getting information about file %s: %s", filepath, strerror(errno) ); - return( false ); - } -#else - img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); -#endif - } - else - { - Image image( filepath ); - - Image *send_image = prepareImage( &image ); - - switch( type ) - { - case STREAM_JPEG : - send_image->EncodeJpeg( img_buffer, &img_buffer_size ); - break; - case STREAM_ZIP : + switch( type ) + { + case STREAM_JPEG : + send_image->EncodeJpeg( img_buffer, &img_buffer_size ); + break; + case STREAM_ZIP : #if HAVE_ZLIB_H - unsigned long zip_buffer_size; - send_image->Zip( img_buffer, &zip_buffer_size ); - img_buffer_size = zip_buffer_size; - break; + unsigned long zip_buffer_size; + send_image->Zip( img_buffer, &zip_buffer_size ); + img_buffer_size = zip_buffer_size; + break; #else - Error("zlib is required for zipped images. Falling back to raw image"); - type = STREAM_RAW; + Error("zlib is required for zipped images. Falling back to raw image"); + type = STREAM_RAW; #endif // HAVE_ZLIB_H - case STREAM_RAW : - img_buffer = (uint8_t*)(send_image->Buffer()); - img_buffer_size = send_image->Size(); - break; - default: - Fatal( "Unexpected frame type %d", type ); - break; - } - } - - switch( type ) - { - case STREAM_JPEG : - fprintf( stdout, "Content-Type: image/jpeg\r\n" ); - break; - case STREAM_RAW : - fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); - break; - case STREAM_ZIP : - fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); - break; - default : - Fatal( "Unexpected frame type %d", type ); - break; - } - - - if(send_raw) { -#if HAVE_SENDFILE - fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); - if(sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) { - /* sendfile() failed, use standard way instead */ - img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { - Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); - } - } -#else - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { - Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); - return( false ); - } -#endif - fclose(fdj); /* Close the file handle */ - } else { - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) - { - Error( "Unable to send stream frame: %s", strerror(errno) ); - return( false ); - } - } - - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); + case STREAM_RAW : + img_buffer = (uint8_t*)(send_image->Buffer()); + img_buffer_size = send_image->Size(); + break; + default: + Fatal( "Unexpected frame type %d", type ); + break; + } } - last_frame_sent = TV_2_FLOAT( now ); - return( true ); + + switch( type ) + { + case STREAM_JPEG : + fprintf( stdout, "Content-Type: image/jpeg\r\n" ); + break; + case STREAM_RAW : + fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); + break; + case STREAM_ZIP : + fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); + break; + default : + Fatal( "Unexpected frame type %d", type ); + break; + } + + + if(send_raw) { +#if HAVE_SENDFILE + fprintf( stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size ); + if(zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size) { + /* sendfile() failed, use standard way instead */ + img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { + Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); + return( false ); + } + } +#else + fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) { + Error("Unable to send raw frame %u: %s",curr_frame_id,strerror(errno)); + return( false ); + } +#endif + fclose(fdj); /* Close the file handle */ + } else { + fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) + { + Error( "Unable to send stream frame: %s", strerror(errno) ); + return( false ); + } + } + + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); + } + last_frame_sent = TV_2_FLOAT( now ); + return( true ); } void EventStream::runStream() { - Event::Initialise(); + Event::Initialise(); - openComms(); + openComms(); - checkInitialised(); + checkInitialised(); - updateFrameRate( (double)event_data->frame_count/event_data->duration ); + updateFrameRate( (double)event_data->frame_count/event_data->duration ); - if ( type == STREAM_JPEG ) - fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); + if ( type == STREAM_JPEG ) + fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); - if ( !event_data ) + if ( !event_data ) + { + sendTextFrame( "No event data found" ); + exit( 0 ); + } + + unsigned int delta_us = 0; + while( !zm_terminate ) + { + gettimeofday( &now, NULL ); + + while(checkCommandQueue()); + + if ( step != 0 ) + curr_frame_id += step; + + checkEventLoaded(); + + // Get current frame data + FrameData *frame_data = &event_data->frames[curr_frame_id-1]; + + //Info( "cst:%.2f", curr_stream_time ); + //Info( "cfid:%d", curr_frame_id ); + //Info( "fdt:%d", frame_data->timestamp ); + if ( !paused ) { - sendTextFrame( "No event data found" ); - exit( 0 ); + bool in_event = true; + double time_to_event = 0; + if ( replay_rate > 0 ) + { + time_to_event = event_data->frames[0].timestamp - curr_stream_time; + if ( time_to_event > 0 ) + in_event = false; + } + else if ( replay_rate < 0 ) + { + time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; + if ( time_to_event > 0 ) + in_event = false; + } + if ( !in_event ) + { + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + if ( actual_delta_time > 1 ) + { + static char frame_text[64]; + snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event ); + if ( !sendTextFrame( frame_text ) ) + zm_terminate = true; + } + //else + //{ + usleep( STREAM_PAUSE_WAIT ); + //curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); + curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); + //} + continue; + } } - unsigned int delta_us = 0; - while( !zm_terminate ) + // Figure out if we should send this frame + bool send_frame = false; + if ( !paused ) { - gettimeofday( &now, NULL ); - - while(checkCommandQueue()); - - if ( step != 0 ) - curr_frame_id += step; - - checkEventLoaded(); - - // Get current frame data - FrameData *frame_data = &event_data->frames[curr_frame_id-1]; - - //Info( "cst:%.2f", curr_stream_time ); - //Info( "cfid:%d", curr_frame_id ); - //Info( "fdt:%d", frame_data->timestamp ); - if ( !paused ) - { - bool in_event = true; - double time_to_event = 0; - if ( replay_rate > 0 ) - { - time_to_event = event_data->frames[0].timestamp - curr_stream_time; - if ( time_to_event > 0 ) - in_event = false; - } - else if ( replay_rate < 0 ) - { - time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp; - if ( time_to_event > 0 ) - in_event = false; - } - if ( !in_event ) - { - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( actual_delta_time > 1 ) - { - static char frame_text[64]; - snprintf( frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event ); - if ( !sendTextFrame( frame_text ) ) - zm_terminate = true; - } - //else - //{ - usleep( STREAM_PAUSE_WAIT ); - //curr_stream_time += (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000)); - curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); - //} - continue; - } - } - - // Figure out if we should send this frame - bool send_frame = false; - if ( !paused ) - { - // If we are streaming and this frame is due to be sent - if ( ((curr_frame_id-1)%frame_mod) == 0 ) - { - delta_us = (unsigned int)(frame_data->delta * 1000000); - if ( effective_fps < base_fps ) - delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); - send_frame = true; - } - } - else if ( step != 0 ) - { - // We are paused and are just stepping forward or backward one frame - step = 0; - send_frame = true; - } - else - { - // We are paused, and doing nothing - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( actual_delta_time > MAX_STREAM_DELAY ) - { - // Send keepalive - Debug( 2, "Sending keepalive frame" ); - send_frame = true; - } - } - - if ( send_frame ) - if ( !sendFrame( delta_us ) ) - zm_terminate = true; - - curr_stream_time = frame_data->timestamp; - - if ( !paused ) - { - curr_frame_id += replay_rate>0?1:-1; - if ( send_frame && type != STREAM_MPEG ) - { - Debug( 3, "dUs: %d", delta_us ); - usleep( delta_us ); - } - } - else - { - usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); - } + // If we are streaming and this frame is due to be sent + if ( ((curr_frame_id-1)%frame_mod) == 0 ) + { + delta_us = (unsigned int)(frame_data->delta * 1000000); + // if effective > base we should speed up frame delivery + delta_us = (unsigned int)((delta_us * base_fps)/effective_fps); + // but must not exceed maxfps + delta_us = max(delta_us, 1000000 / maxfps); + send_frame = true; + } } + else if ( step != 0 ) + { + // We are paused and are just stepping forward or backward one frame + step = 0; + send_frame = true; + } + else + { + // We are paused, and doing nothing + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + if ( actual_delta_time > MAX_STREAM_DELAY ) + { + // Send keepalive + Debug( 2, "Sending keepalive frame" ); + send_frame = true; + } + } + + if ( send_frame ) + if ( !sendFrame( delta_us ) ) + zm_terminate = true; + + curr_stream_time = frame_data->timestamp; + + if ( !paused ) + { + curr_frame_id += replay_rate>0?1:-1; + if ( send_frame && type != STREAM_MPEG ) + { + Debug( 3, "dUs: %d", delta_us ); + usleep( delta_us ); + } + } + else + { + usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); + } + } #if HAVE_LIBAVCODEC - if ( type == STREAM_MPEG ) - delete vid_stream; + if ( type == STREAM_MPEG ) + delete vid_stream; #endif // HAVE_LIBAVCODEC - closeComms(); + closeComms(); } diff --git a/src/zm_event.h b/src/zm_event.h index f50a5bf06..f3c5d7961 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -41,7 +41,7 @@ class Zone; class Monitor; -#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored +#define MAX_PRE_ALARM_FRAMES 16 // Maximum number of prealarm frames that can be stored // // Class describing events, i.e. captured periods of activity. @@ -51,219 +51,219 @@ class Event friend class EventStream; protected: - static bool initialised; - static char capture_file_format[PATH_MAX]; - static char analyse_file_format[PATH_MAX]; - static char general_file_format[PATH_MAX]; + static bool initialised; + static char capture_file_format[PATH_MAX]; + static char analyse_file_format[PATH_MAX]; + static char general_file_format[PATH_MAX]; protected: - static int sd; + static int sd; public: - typedef std::set StringSet; - typedef std::map StringSetMap; + typedef std::set StringSet; + typedef std::map StringSetMap; protected: - typedef enum { NORMAL, BULK, ALARM } FrameType; + typedef enum { NORMAL, BULK, ALARM } FrameType; - struct PreAlarmData - { - Image *image; - struct timeval timestamp; - unsigned int score; - Image *alarm_frame; - }; + struct PreAlarmData + { + Image *image; + struct timeval timestamp; + unsigned int score; + Image *alarm_frame; + }; - static int pre_alarm_count; - static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; + static int pre_alarm_count; + static PreAlarmData pre_alarm_data[MAX_PRE_ALARM_FRAMES]; protected: - unsigned int id; - Monitor *monitor; - struct timeval start_time; - struct timeval end_time; - std::string cause; - StringSetMap noteSetMap; - int frames; - int alarm_frames; - unsigned int tot_score; - unsigned int max_score; - char path[PATH_MAX]; + unsigned int id; + Monitor *monitor; + struct timeval start_time; + struct timeval end_time; + std::string cause; + StringSetMap noteSetMap; + int frames; + int alarm_frames; + unsigned int tot_score; + unsigned int max_score; + char path[PATH_MAX]; protected: - int last_db_frame; + int last_db_frame; protected: - static void Initialise() - { - if ( initialised ) - return; + static void Initialise() + { + if ( initialised ) + return; - snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits ); - snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits ); - snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits ); + snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits ); + snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits ); + snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits ); - initialised = true; - } + initialised = true; + } - void createNotes( std::string ¬es ); + void createNotes( std::string ¬es ); public: - static bool OpenFrameSocket( int ); - static bool ValidateFrameSocket( int ); + static bool OpenFrameSocket( int ); + static bool ValidateFrameSocket( int ); public: - Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ); - ~Event(); + Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap ); + ~Event(); - int Id() const { return( id ); } - const std::string &Cause() { return( cause ); } - int Frames() const { return( frames ); } - int AlarmFrames() const { return( alarm_frames ); } + int Id() const { return( id ); } + const std::string &Cause() { return( cause ); } + int Frames() const { return( frames ); } + int AlarmFrames() const { return( alarm_frames ); } - const struct timeval &StartTime() const { return( start_time ); } - const struct timeval &EndTime() const { return( end_time ); } - struct timeval &EndTime() { return( end_time ); } + const struct timeval &StartTime() const { return( start_time ); } + const struct timeval &EndTime() const { return( end_time ); } + struct timeval &EndTime() { return( end_time ); } - bool SendFrameImage( const Image *image, bool alarm_frame=false ); - bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ); + bool SendFrameImage( const Image *image, bool alarm_frame=false ); + bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false ); - void updateNotes( const StringSetMap &stringSetMap ); + void updateNotes( const StringSetMap &stringSetMap ); - void AddFrames( int n_frames, Image **images, struct timeval **timestamps ); - void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ); + void AddFrames( int n_frames, Image **images, struct timeval **timestamps ); + void AddFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ); private: - void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); + void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); public: - static const char *getSubPath( struct tm *time ) - { - static char subpath[PATH_MAX] = ""; - snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); - return( subpath ); - } - static const char *getSubPath( time_t *time ) - { - return( Event::getSubPath( localtime( time ) ) ); - } + static const char *getSubPath( struct tm *time ) + { + static char subpath[PATH_MAX] = ""; + snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); + return( subpath ); + } + static const char *getSubPath( time_t *time ) + { + return( Event::getSubPath( localtime( time ) ) ); + } public: - static int PreAlarmCount() - { - return( pre_alarm_count ); - } - static void EmptyPreAlarmFrames() - { - if ( pre_alarm_count > 0 ) - { - for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ ) - { - delete pre_alarm_data[i].image; - delete pre_alarm_data[i].alarm_frame; - } - memset( pre_alarm_data, 0, sizeof(pre_alarm_data) ); - } - pre_alarm_count = 0; - } - static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ) - { - pre_alarm_data[pre_alarm_count].image = new Image( *image ); - pre_alarm_data[pre_alarm_count].timestamp = timestamp; - pre_alarm_data[pre_alarm_count].score = score; - if ( alarm_frame ) - { - pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame ); - } - pre_alarm_count++; - } - void SavePreAlarmFrames() - { - for ( int i = 0; i < pre_alarm_count; i++ ) - { - AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame ); - } - EmptyPreAlarmFrames(); - } + static int PreAlarmCount() + { + return( pre_alarm_count ); + } + static void EmptyPreAlarmFrames() + { + if ( pre_alarm_count > 0 ) + { + for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ ) + { + delete pre_alarm_data[i].image; + delete pre_alarm_data[i].alarm_frame; + } + memset( pre_alarm_data, 0, sizeof(pre_alarm_data) ); + } + pre_alarm_count = 0; + } + static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ) + { + pre_alarm_data[pre_alarm_count].image = new Image( *image ); + pre_alarm_data[pre_alarm_count].timestamp = timestamp; + pre_alarm_data[pre_alarm_count].score = score; + if ( alarm_frame ) + { + pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame ); + } + pre_alarm_count++; + } + void SavePreAlarmFrames() + { + for ( int i = 0; i < pre_alarm_count; i++ ) + { + AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame ); + } + EmptyPreAlarmFrames(); + } }; class EventStream : public StreamBase { public: - typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode; + typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode; protected: - struct FrameData { - //unsigned long id; - time_t timestamp; - time_t offset; - double delta; - bool in_db; - }; + struct FrameData { + //unsigned long id; + time_t timestamp; + time_t offset; + double delta; + bool in_db; + }; - struct EventData - { - unsigned long event_id; - unsigned long monitor_id; - unsigned long frame_count; - time_t start_time; - double duration; - char path[PATH_MAX]; - int n_frames; - FrameData *frames; - }; + struct EventData + { + unsigned long event_id; + unsigned long monitor_id; + unsigned long frame_count; + time_t start_time; + double duration; + char path[PATH_MAX]; + int n_frames; + FrameData *frames; + }; protected: - static const int STREAM_PAUSE_WAIT = 250000; // Microseconds + static const int STREAM_PAUSE_WAIT = 250000; // Microseconds - static const StreamMode DEFAULT_MODE = MODE_SINGLE; + static const StreamMode DEFAULT_MODE = MODE_SINGLE; protected: - StreamMode mode; - bool forceEventChange; + StreamMode mode; + bool forceEventChange; protected: - int curr_frame_id; - double curr_stream_time; + int curr_frame_id; + double curr_stream_time; - EventData *event_data; + EventData *event_data; protected: - bool loadEventData( int event_id ); - bool loadInitialEventData( int init_event_id, int init_frame_id ); - bool loadInitialEventData( int monitor_id, time_t event_time ); + bool loadEventData( int event_id ); + bool loadInitialEventData( int init_event_id, unsigned int init_frame_id ); + bool loadInitialEventData( int monitor_id, time_t event_time ); - void checkEventLoaded(); - void processCommand( const CmdMsg *msg ); - bool sendFrame( int delta_us ); + void checkEventLoaded(); + void processCommand( const CmdMsg *msg ); + bool sendFrame( int delta_us ); public: - EventStream() - { - mode = DEFAULT_MODE; + EventStream() + { + mode = DEFAULT_MODE; - forceEventChange = false; + forceEventChange = false; - curr_frame_id = 0; - curr_stream_time = 0.0; + curr_frame_id = 0; + curr_stream_time = 0.0; - event_data = 0; - } - void setStreamStart( int init_event_id, int init_frame_id=0 ) - { - loadInitialEventData( init_event_id, init_frame_id ); - loadMonitor( event_data->monitor_id ); - } - void setStreamStart( int monitor_id, time_t event_time ) - { - loadInitialEventData( monitor_id, event_time ); - loadMonitor( monitor_id ); - } - void setStreamMode( StreamMode p_mode ) - { - mode = p_mode; - } - void runStream(); + event_data = 0; + } + void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) + { + loadInitialEventData( init_event_id, init_frame_id ); + loadMonitor( event_data->monitor_id ); + } + void setStreamStart( int monitor_id, time_t event_time ) + { + loadInitialEventData( monitor_id, event_time ); + loadMonitor( monitor_id ); + } + void setStreamMode( StreamMode p_mode ) + { + mode = p_mode; + } + void runStream(); }; #endif // ZM_EVENT_H diff --git a/src/zm_exception.h b/src/zm_exception.h index 4af60bfa6..a503be7c9 100644 --- a/src/zm_exception.h +++ b/src/zm_exception.h @@ -27,42 +27,42 @@ class Exception { protected: - typedef enum { INFO, WARNING, ERROR, FATAL } Severity; + typedef enum { INFO, WARNING, ERROR, FATAL } Severity; protected: - std::string mMessage; - Severity mSeverity; + std::string mMessage; + Severity mSeverity; public: - Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity ) - { - } + Exception( const std::string &message, Severity severity=ERROR ) : mMessage( message ), mSeverity( severity ) + { + } public: - const std::string &getMessage() const - { - return( mMessage ); - } - Severity getSeverity() const - { - return( mSeverity ); - } - bool isInfo() const - { - return( mSeverity == INFO ); - } - bool isWarning() const - { - return( mSeverity == WARNING ); - } - bool isError() const - { - return( mSeverity == ERROR ); - } - bool isFatal() const - { - return( mSeverity == FATAL ); - } + const std::string &getMessage() const + { + return( mMessage ); + } + Severity getSeverity() const + { + return( mSeverity ); + } + bool isInfo() const + { + return( mSeverity == INFO ); + } + bool isWarning() const + { + return( mSeverity == WARNING ); + } + bool isError() const + { + return( mSeverity == ERROR ); + } + bool isFatal() const + { + return( mSeverity == FATAL ); + } }; #endif // ZM_EXCEPTION_H diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index 6d08f8576..e7c9edbef 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -24,195 +24,219 @@ #if HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE #if HAVE_LIBAVUTIL -enum PixelFormat GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) { - enum PixelFormat pf; +enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) { + enum _AVPIXELFORMAT pf; - Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder); + Debug(8,"Colours: %d SubpixelOrder: %d",p_colours,p_subpixelorder); - switch(p_colours) { - case ZM_COLOUR_RGB24: - { - if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { - /* BGR subpixel order */ - pf = PIX_FMT_BGR24; - } else { - /* Assume RGB subpixel order */ - pf = PIX_FMT_RGB24; - } - break; - } - case ZM_COLOUR_RGB32: - { - if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - /* ARGB subpixel order */ - pf = PIX_FMT_ARGB; - } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - /* ABGR subpixel order */ - pf = PIX_FMT_ABGR; - } else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - /* BGRA subpixel order */ - pf = PIX_FMT_BGRA; - } else { - /* Assume RGBA subpixel order */ - pf = PIX_FMT_RGBA; - } - break; - } - case ZM_COLOUR_GRAY8: - pf = PIX_FMT_GRAY8; - break; - default: - Panic("Unexpected colours: %d",p_colours); - pf = PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */ - break; - } + switch(p_colours) { + case ZM_COLOUR_RGB24: + { + if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { + /* BGR subpixel order */ + pf = AV_PIX_FMT_BGR24; + } else { + /* Assume RGB subpixel order */ + pf = AV_PIX_FMT_RGB24; + } + break; + } + case ZM_COLOUR_RGB32: + { + if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + /* ARGB subpixel order */ + pf = AV_PIX_FMT_ARGB; + } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + /* ABGR subpixel order */ + pf = AV_PIX_FMT_ABGR; + } else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + /* BGRA subpixel order */ + pf = AV_PIX_FMT_BGRA; + } else { + /* Assume RGBA subpixel order */ + pf = AV_PIX_FMT_RGBA; + } + break; + } + case ZM_COLOUR_GRAY8: + pf = AV_PIX_FMT_GRAY8; + break; + default: + Panic("Unexpected colours: %d",p_colours); + pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */ + break; + } - return pf; + return pf; } #endif // HAVE_LIBAVUTIL #if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL), output_avframe(NULL) { - Debug(4,"SWScale object created"); + Debug(4,"SWScale object created"); - /* Allocate AVFrame for the input */ - input_avframe = avcodec_alloc_frame(); - if(input_avframe == NULL) { - Fatal("Failed allocating AVFrame for the input"); - } + /* Allocate AVFrame for the input */ +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + input_avframe = av_frame_alloc(); +#else + input_avframe = avcodec_alloc_frame(); +#endif + if(input_avframe == NULL) { + Fatal("Failed allocating AVFrame for the input"); + } - /* Allocate AVFrame for the output */ - output_avframe = avcodec_alloc_frame(); - if(output_avframe == NULL) { - Fatal("Failed allocating AVFrame for the output"); - } + /* Allocate AVFrame for the output */ +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + output_avframe = av_frame_alloc(); +#else + output_avframe = avcodec_alloc_frame(); +#endif + if(output_avframe == NULL) { + Fatal("Failed allocating AVFrame for the output"); + } } SWScale::~SWScale() { - /* Free up everything */ - av_free(input_avframe); - input_avframe = NULL; + /* Free up everything */ +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &input_avframe ); +#else + av_freep( &input_avframe ); +#endif + //input_avframe = NULL; - av_free(output_avframe); - output_avframe = NULL; +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &output_avframe ); +#else + av_freep( &output_avframe ); +#endif + //output_avframe = NULL; - if(swscale_ctx) { - sws_freeContext(swscale_ctx); - swscale_ctx = NULL; - } - - Debug(4,"SWScale object destroyed"); + if(swscale_ctx) { + sws_freeContext(swscale_ctx); + swscale_ctx = NULL; + } + + Debug(4,"SWScale object destroyed"); } -int SWScale::SetDefaults(enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height) { +int SWScale::SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { - /* Assign the defaults */ - default_input_pf = in_pf; - default_output_pf = out_pf; - default_width = width; - default_height = height; + /* Assign the defaults */ + default_input_pf = in_pf; + default_output_pf = out_pf; + default_width = width; + default_height = height; - gotdefaults = true; + gotdefaults = true; - return 0; + return 0; } -int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height) { - /* Parameter checking */ - if(in_buffer == NULL || out_buffer == NULL) { - Error("NULL Input or output buffer"); - return -1; - } - if(in_pf == 0 || out_pf == 0) { - Error("Invalid input or output pixel formats"); - return -2; - } - if(!width || !height) { - Error("Invalid width or height"); - return -3; - } +int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { + /* Parameter checking */ + if(in_buffer == NULL || out_buffer == NULL) { + Error("NULL Input or output buffer"); + return -1; + } + if(in_pf == 0 || out_pf == 0) { + Error("Invalid input or output pixel formats"); + return -2; + } + if(!width || !height) { + Error("Invalid width or height"); + return -3; + } -#if LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0, 8, 0) - /* Warn if the input or output pixelformat is not supported */ - if(!sws_isSupportedInput(in_pf)) { - Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff)); - } - if(!sws_isSupportedOutput(out_pf)) { - Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff)); - } +#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) + /* Warn if the input or output pixelformat is not supported */ + if(!sws_isSupportedInput(in_pf)) { + Warning("swscale does not support the input format: %c%c%c%c",(in_pf)&0xff,((in_pf)&0xff),((in_pf>>16)&0xff),((in_pf>>24)&0xff)); + } + if(!sws_isSupportedOutput(out_pf)) { + Warning("swscale does not support the output format: %c%c%c%c",(out_pf)&0xff,((out_pf>>8)&0xff),((out_pf>>16)&0xff),((out_pf>>24)&0xff)); + } #endif - /* Check the buffer sizes */ - size_t insize = avpicture_get_size(in_pf, width, height); - if(insize != in_buffer_size) { - Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size); - return -4; - } - size_t outsize = avpicture_get_size(out_pf, width, height); - if(outsize < out_buffer_size) { - Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); - return -5; - } + /* Check the buffer sizes */ +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + size_t insize = av_image_get_buffer_size(in_pf, width, height,1); +#else + size_t insize = avpicture_get_size(in_pf, width, height); +#endif + if(insize != in_buffer_size) { + Error("The input buffer size does not match the expected size for the input format. Required: %d Available: %d", insize, in_buffer_size); + return -4; + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + size_t outsize = av_image_get_buffer_size(out_pf, width, height,1); +#else + size_t outsize = avpicture_get_size(out_pf, width, height); +#endif + if(outsize < out_buffer_size) { + Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size); + return -5; + } - /* Get the context */ - swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL ); - if(swscale_ctx == NULL) { - Error("Failed getting swscale context"); - return -6; - } + /* Get the context */ + swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL ); + if(swscale_ctx == NULL) { + Error("Failed getting swscale context"); + return -6; + } - /* Fill in the buffers */ - if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) { - Error("Failed filling input frame with input buffer"); - return -7; - } - if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) { - Error("Failed filling output frame with output buffer"); - return -8; - } + /* Fill in the buffers */ + if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) { + Error("Failed filling input frame with input buffer"); + return -7; + } + if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) { + Error("Failed filling output frame with output buffer"); + return -8; + } - /* Do the conversion */ - if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) { - Error("swscale conversion failed"); - return -10; - } + /* Do the conversion */ + if(!sws_scale(swscale_ctx, input_avframe->data, input_avframe->linesize, 0, height, output_avframe->data, output_avframe->linesize ) ) { + Error("swscale conversion failed"); + return -10; + } - return 0; + return 0; } -int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height) { - if(img->Width() != width) { - Error("Source image width differs. Source: %d Output: %d",img->Width(), width); - return -12; - } +int SWScale::Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height) { + if(img->Width() != width) { + Error("Source image width differs. Source: %d Output: %d",img->Width(), width); + return -12; + } - if(img->Height() != height) { - Error("Source image height differs. Source: %d Output: %d",img->Height(), height); - return -13; - } + if(img->Height() != height) { + Error("Source image height differs. Source: %d Output: %d",img->Height(), height); + return -13; + } - return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height); + return Convert(img->Buffer(),img->Size(),out_buffer,out_buffer_size,in_pf,out_pf,width,height); } int SWScale::ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size) { - if(!gotdefaults) { - Error("Defaults are not set"); - return -24; - } + if(!gotdefaults) { + Error("Defaults are not set"); + return -24; + } - return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); + return Convert(img,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); } int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size) { - if(!gotdefaults) { - Error("Defaults are not set"); - return -24; - } + if(!gotdefaults) { + Error("Defaults are not set"); + return -24; + } - return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); + return Convert(in_buffer,in_buffer_size,out_buffer,out_buffer_size,default_input_pf,default_output_pf,default_width,default_height); } #endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index fd124ca37..894408437 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -32,53 +32,157 @@ extern "C" { #include #include #include -#if LIBAVUTIL_VERSION_INT > AV_VERSION_INT(50, 28, 0) + +/* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg + * The original source is vlc (in modules/codec/avcodec/avcommon_compat.h) + * a is the major version + * b and c the minor and micro versions of libav + * d and e the minor and micro versions of FFmpeg */ +#define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \ + ( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + +#if LIBAVUTIL_VERSION_CHECK(50, 29, 0, 29, 0) #include #else #include #endif + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) +#include +#endif #elif HAVE_FFMPEG_AVUTIL_H #include #include #include #include +#endif /* HAVE_LIBAVUTIL_AVUTIL_H */ + +#if defined(HAVE_LIBAVUTIL_AVUTIL_H) +#if LIBAVUTIL_VERSION_CHECK(51, 42, 0, 74, 100) + #define _AVPIXELFORMAT AVPixelFormat +#else + #define _AVPIXELFORMAT PixelFormat + #define AV_PIX_FMT_NONE PIX_FMT_NONE + #define AV_PIX_FMT_RGB444 PIX_FMT_RGB444 + #define AV_PIX_FMT_RGB555 PIX_FMT_RGB555 + #define AV_PIX_FMT_RGB565 PIX_FMT_RGB565 + #define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 + #define AV_PIX_FMT_RGB24 PIX_FMT_RGB24 + #define AV_PIX_FMT_BGRA PIX_FMT_BGRA + #define AV_PIX_FMT_ARGB PIX_FMT_ARGB + #define AV_PIX_FMT_ABGR PIX_FMT_ABGR + #define AV_PIX_FMT_RGBA PIX_FMT_RGBA + #define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8 + #define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422 + #define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P + #define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P + #define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P + #define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P + #define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P + #define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P + #define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422 + #define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P + #define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P + #define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422 + #define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411 + #define AV_PIX_FMT_BGR565 PIX_FMT_BGR565 + #define AV_PIX_FMT_BGR555 PIX_FMT_BGR555 + #define AV_PIX_FMT_BGR8 PIX_FMT_BGR8 + #define AV_PIX_FMT_BGR4 PIX_FMT_BGR4 + #define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE + #define AV_PIX_FMT_RGB8 PIX_FMT_RGB8 + #define AV_PIX_FMT_RGB4 PIX_FMT_RGB4 + #define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE + #define AV_PIX_FMT_NV12 PIX_FMT_NV12 + #define AV_PIX_FMT_NV21 PIX_FMT_NV21 + #define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1 + #define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1 + #define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE + #define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE + #define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P + #define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P + #define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P + //#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264 + //#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1 + //#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2 #endif +#endif /* HAVE_LIBAVUTIL_AVUTIL_H */ // AVCODEC #if HAVE_LIBAVCODEC_AVCODEC_H #include + +/* + * LIBAVCODEC_VERSION_CHECK checks for the right version of libav and FFmpeg + * The original source is vlc (in modules/codec/avcodec/avcommon_compat.h) + * a is the major version + * b and c the minor and micro versions of libav + * d and e the minor and micro versions of FFmpeg */ +#define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \ + ( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + #elif HAVE_FFMPEG_AVCODEC_H #include -#endif - +#endif /* HAVE_LIBAVCODEC_AVCODEC_H */ + #if defined(HAVE_LIBAVCODEC_AVCODEC_H) -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,25,0) - #define _AVCODECID AVCodecID +#if LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100) + #define _AVCODECID AVCodecID #else - #define _AVCODECID CodecID -#endif + #define _AVCODECID CodecID #endif +#endif /* HAVE_LIBAVCODEC_AVCODEC_H */ // AVFORMAT #if HAVE_LIBAVFORMAT_AVFORMAT_H #include + +/* LIBAVFORMAT_VERSION_CHECK checks for the right version of libav and FFmpeg + * The original source is vlc (in modules/codec/avcodec/avcommon_compat.h) + * a is the major version + * b and c the minor and micro versions of libav + * d and e the minor and micro versions of FFmpeg */ +#define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \ + ( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + #elif HAVE_FFMPEG_AVFORMAT_H #include -#endif +#endif /* HAVE_LIBAVFORMAT_AVFORMAT_H */ // AVDEVICE #if HAVE_LIBAVDEVICE_AVDEVICE_H #include + +/* LIBAVDEVICE_VERSION_CHECK checks for the right version of libav and FFmpeg + * a is the major version + * b and c the minor and micro versions of libav + * d and e the minor and micro versions of FFmpeg */ +#define LIBAVDEVICE_VERSION_CHECK(a, b, c, d, e) \ + ( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + #elif HAVE_FFMPEG_AVDEVICE_H #include -#endif +#endif /* HAVE_LIBAVDEVICE_AVDEVICE_H */ // SWSCALE #if HAVE_LIBSWSCALE_SWSCALE_H #include + +/* LIBSWSCALE_VERSION_CHECK checks for the right version of libav and FFmpeg + * a is the major version + * b and c the minor and micro versions of libav + * d and e the minor and micro versions of FFmpeg */ +#define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \ + ( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \ + (LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) ) + #elif HAVE_FFMPEG_SWSCALE_H #include -#endif +#endif /* HAVE_LIBSWSCALE_SWSCALE_H */ #ifdef __cplusplus } @@ -86,7 +190,7 @@ extern "C" { #if ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H ) -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) +#if !LIBAVFORMAT_VERSION_CHECK(52, 107, 0, 107, 0) #if defined(AVIO_WRONLY) #define AVIO_FLAG_WRITE AVIO_WRONLY #else @@ -94,14 +198,8 @@ extern "C" { #endif #endif -/* Fix for not having SWS_CPU_CAPS_SSE2 defined */ -#ifndef SWS_CPU_CAPS_SSE2 -#define SWS_CPU_CAPS_SSE2 0x02000000 -#endif - - #if HAVE_LIBAVUTIL -enum PixelFormat GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder); +enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder); #endif // HAVE_LIBAVUTIL @@ -109,27 +207,27 @@ enum PixelFormat GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixe #if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL class SWScale { public: - SWScale(); - ~SWScale(); - int SetDefaults(enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height); - int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); - int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); - int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height); - int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum PixelFormat in_pf, enum PixelFormat out_pf, unsigned int width, unsigned int height); + SWScale(); + ~SWScale(); + int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); + int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size); + int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size); + int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); + int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height); protected: - bool gotdefaults; - struct SwsContext* swscale_ctx; - AVFrame* input_avframe; - AVFrame* output_avframe; - enum PixelFormat default_input_pf; - enum PixelFormat default_output_pf; - unsigned int default_width; - unsigned int default_height; + bool gotdefaults; + struct SwsContext* swscale_ctx; + AVFrame* input_avframe; + AVFrame* output_avframe; + enum _AVPIXELFORMAT default_input_pf; + enum _AVPIXELFORMAT default_output_pf; + unsigned int default_width; + unsigned int default_height; }; #endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL -#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 25, 0) +#if !LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100) #define AV_CODEC_ID_NONE CODEC_ID_NONE #define AV_CODEC_ID_PCM_MULAW CODEC_ID_PCM_MULAW #define AV_CODEC_ID_PCM_ALAW CODEC_ID_PCM_ALAW @@ -162,21 +260,21 @@ protected: */ #ifdef __cplusplus - inline static const std::string av_make_error_string(int errnum) - { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(50, 12, 13) - av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); + inline static const std::string av_make_error_string(int errnum) + { + char errbuf[AV_ERROR_MAX_STRING_SIZE]; +#if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0) + av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE); #else - snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum); + snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum); #endif - return (std::string)errbuf; - } - - #undef av_err2str - #define av_err2str(errnum) av_make_error_string(errnum).c_str() + return (std::string)errbuf; + } - #endif // __cplusplus + #undef av_err2str + #define av_err2str(errnum) av_make_error_string(errnum).c_str() + + #endif // __cplusplus #endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H ) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 9a4ea5fcb..12456a560 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -27,66 +27,72 @@ #define AV_ERROR_MAX_STRING_SIZE 64 #endif -FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - mPath( p_path ), - mMethod( p_method ), - mOptions( p_options ) -{ - if ( capture ) - { - Initialise(); - } - - mFormatContext = NULL; - mVideoStreamId = -1; - mCodecContext = NULL; - mCodec = NULL; - mRawFrame = NULL; - mFrame = NULL; - frameCount = 0; - mIsOpening = false; - mCanCapture = false; - mOpenStart = 0; - mReopenThread = 0; - -#if HAVE_LIBSWSCALE - mConvertContext = NULL; +#ifdef SOLARIS +#include // for ESRCH +#include +#include #endif - /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - imagePixFormat = PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - imagePixFormat = PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - imagePixFormat = PIX_FMT_GRAY8; - } else { - Panic("Unexpected colours: %d",colours); - } - + +FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : + Camera( p_id, FFMPEG_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), + mPath( p_path ), + mMethod( p_method ), + mOptions( p_options ) +{ + if ( capture ) + { + Initialise(); + } + + mFormatContext = NULL; + mVideoStreamId = -1; + mCodecContext = NULL; + mCodec = NULL; + mRawFrame = NULL; + mFrame = NULL; + frameCount = 0; + mIsOpening = false; + mCanCapture = false; + mOpenStart = 0; + mReopenThread = 0; + +#if HAVE_LIBSWSCALE + mConvertContext = NULL; +#endif + /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + imagePixFormat = AV_PIX_FMT_RGBA; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + imagePixFormat = AV_PIX_FMT_RGB24; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + imagePixFormat = AV_PIX_FMT_GRAY8; + } else { + Panic("Unexpected colours: %d",colours); + } + } FfmpegCamera::~FfmpegCamera() { - CloseFfmpeg(); + CloseFfmpeg(); - if ( capture ) - { - Terminate(); - } + if ( capture ) + { + Terminate(); + } } void FfmpegCamera::Initialise() { - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else - av_log_set_level( AV_LOG_QUIET ); + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); - av_register_all(); + av_register_all(); } void FfmpegCamera::Terminate() @@ -95,350 +101,383 @@ void FfmpegCamera::Terminate() int FfmpegCamera::PrimeCapture() { - Info( "Priming capture from %s", mPath.c_str() ); + Info( "Priming capture from %s", mPath.c_str() ); - if (OpenFfmpeg() != 0){ - ReopenFfmpeg(); - } - return 0; + if (OpenFfmpeg() != 0){ + ReopenFfmpeg(); + } + return 0; } int FfmpegCamera::PreCapture() { - // Nothing to do here - return( 0 ); + // Nothing to do here + return( 0 ); } int FfmpegCamera::Capture( Image &image ) { - if (!mCanCapture){ - return -1; + if (!mCanCapture){ + return -1; + } + + // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. + if (mReopenThread != 0) { + void *retval = 0; + int ret; + + ret = pthread_join(mReopenThread, &retval); + if (ret != 0){ + Error("Could not join reopen thread."); } - // If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread. - if (mReopenThread != 0) { - void *retval = 0; - int ret; - - ret = pthread_tryjoin_np(mReopenThread, &retval); - if (ret != 0){ - Error("Could not join reopen thread."); - } - - Info( "Successfully reopened stream." ); - mReopenThread = 0; - } + Info( "Successfully reopened stream." ); + mReopenThread = 0; + } - AVPacket packet; - uint8_t* directbuffer; + AVPacket packet; + uint8_t* directbuffer; - /* Request a writeable buffer of the target image */ - directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); - if(directbuffer == NULL) { - Error("Failed requesting writeable buffer for the captured image."); - return (-1); - } - - int frameComplete = false; - while ( !frameComplete ) + /* Request a writeable buffer of the target image */ + directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); + if(directbuffer == NULL) { + Error("Failed requesting writeable buffer for the captured image."); + return (-1); + } + + int frameComplete = false; + while ( !frameComplete ) + { + int avResult = av_read_frame( mFormatContext, &packet ); + if ( avResult < 0 ) { - int avResult = av_read_frame( mFormatContext, &packet ); - if ( avResult < 0 ) - { - char errbuf[AV_ERROR_MAX_STRING_SIZE]; - av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE); - if ( - // Check if EOF. - (avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || - // Check for Connection failure. - (avResult == -110) - ) - { - Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf); - ReopenFfmpeg(); - } + char errbuf[AV_ERROR_MAX_STRING_SIZE]; + av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE); + if ( + // Check if EOF. + (avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) || + // Check for Connection failure. + (avResult == -110) + ) + { + Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf); + ReopenFfmpeg(); + } - Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf ); - return( -1 ); - } - Debug( 5, "Got packet from stream %d", packet.stream_index ); - if ( packet.stream_index == mVideoStreamId ) - { -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 25, 0) - if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 ) + Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf ); + return( -1 ); + } + Debug( 5, "Got packet from stream %d", packet.stream_index ); + if ( packet.stream_index == mVideoStreamId ) + { +#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) + if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 ) #else - if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 ) + if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 ) #endif - Fatal( "Unable to decode frame at frame %d", frameCount ); + Fatal( "Unable to decode frame at frame %d", frameCount ); - Debug( 4, "Decoded video packet at frame %d", frameCount ); + Debug( 4, "Decoded video packet at frame %d", frameCount ); - if ( frameComplete ) - { - Debug( 3, "Got frame %d", frameCount ); - - avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); + if ( frameComplete ) + { + Debug( 3, "Got frame %d", frameCount ); +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(mFrame->data, mFrame->linesize, + directbuffer, imagePixFormat, width, height, 1); +#else + avpicture_fill( (AVPicture *)mFrame, directbuffer, + imagePixFormat, width, height); +#endif #if HAVE_LIBSWSCALE - if(mConvertContext == NULL) { - if(config.cpu_extensions && sseversion >= 20) { - mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC | SWS_CPU_CAPS_SSE2, NULL, NULL, NULL ); - } else { - mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); - } - if(mConvertContext == NULL) - Fatal( "Unable to create conversion context for %s", mPath.c_str() ); - } - - if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) - Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); + if(mConvertContext == NULL) { + mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); + + if(mConvertContext == NULL) + Fatal( "Unable to create conversion context for %s", mPath.c_str() ); + } + + if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) + Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); #else // HAVE_LIBSWSCALE - Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); + Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); #endif // HAVE_LIBSWSCALE - frameCount++; - } - } - av_free_packet( &packet ); + frameCount++; + } } - return (0); +#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) + av_packet_unref( &packet); +#else + av_free_packet( &packet ); +#endif + } + return (0); } int FfmpegCamera::PostCapture() { - // Nothing to do here - return( 0 ); + // Nothing to do here + return( 0 ); } int FfmpegCamera::OpenFfmpeg() { - Debug ( 2, "OpenFfmpeg called." ); + Debug ( 2, "OpenFfmpeg called." ); - mOpenStart = time(NULL); - mIsOpening = true; + mOpenStart = time(NULL); + mIsOpening = true; - // Open the input, not necessarily a file -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) - Debug ( 1, "Calling av_open_input_file" ); - if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 ) + // Open the input, not necessarily a file +#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) + Debug ( 1, "Calling av_open_input_file" ); + if ( av_open_input_file( &mFormatContext, mPath.c_str(), NULL, 0, NULL ) !=0 ) #else - // Handle options - AVDictionary *opts = 0; - StringVector opVect = split(Options(), ","); - - // Set transport method as specified by method field, rtpUni is default - if ( Method() == "rtpMulti" ) - opVect.push_back("rtsp_transport=udp_multicast"); - else if ( Method() == "rtpRtsp" ) - opVect.push_back("rtsp_transport=tcp"); - else if ( Method() == "rtpRtspHttp" ) - opVect.push_back("rtsp_transport=http"); - - Debug(2, "Number of Options: %d",opVect.size()); - for (size_t i=0; i 1) { - parts[0] = trimSpaces(parts[0]); - parts[1] = trimSpaces(parts[1]); - if ( av_dict_set(&opts, parts[0].c_str(), parts[1].c_str(), 0) == 0 ) { - Debug(2, "set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str()); - } - else - { - Warning( "Error trying to set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str() ); - } - - } - } - Debug ( 1, "Calling avformat_open_input" ); - - mFormatContext = avformat_alloc_context( ); - mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback; - mFormatContext->interrupt_callback.opaque = this; - - if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) !=0 ) -#endif - { - mIsOpening = false; - Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) ); - return -1; + // Handle options + AVDictionary *opts = 0; + StringVector opVect = split(Options(), ","); + + // Set transport method as specified by method field, rtpUni is default + if ( Method() == "rtpMulti" ) + opVect.push_back("rtsp_transport=udp_multicast"); + else if ( Method() == "rtpRtsp" ) + opVect.push_back("rtsp_transport=tcp"); + else if ( Method() == "rtpRtspHttp" ) + opVect.push_back("rtsp_transport=http"); + + Debug(2, "Number of Options: %d",opVect.size()); + for (size_t i=0; i 1) { + parts[0] = trimSpaces(parts[0]); + parts[1] = trimSpaces(parts[1]); + if ( av_dict_set(&opts, parts[0].c_str(), parts[1].c_str(), 0) == 0 ) { + Debug(2, "set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str()); + } + else + { + Warning( "Error trying to set option %d '%s' to '%s'", i, parts[0].c_str(), parts[1].c_str() ); + } + } + else + { + Warning( "Unable to parse ffmpeg option %d '%s', expecting key=value", i, opVect[i].c_str() ); + } + } + Debug ( 1, "Calling avformat_open_input" ); + mFormatContext = avformat_alloc_context( ); + mFormatContext->interrupt_callback.callback = FfmpegInterruptCallback; + mFormatContext->interrupt_callback.opaque = this; + + if ( avformat_open_input( &mFormatContext, mPath.c_str(), NULL, &opts ) !=0 ) +#endif + { mIsOpening = false; - Debug ( 1, "Opened input" ); + Error( "Unable to open input %s due to: %s", mPath.c_str(), strerror(errno) ); + return -1; + } - // Locate stream info from avformat_open_input -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) - Debug ( 1, "Calling av_find_stream_info" ); - if ( av_find_stream_info( mFormatContext ) < 0 ) + AVDictionaryEntry *e; + if ((e = av_dict_get(opts, "", NULL, AV_DICT_IGNORE_SUFFIX)) != NULL) { + Warning( "Option %s not recognized by ffmpeg", e->key); + } + + mIsOpening = false; + Debug ( 1, "Opened input" ); + + // Locate stream info from avformat_open_input +#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0) + Debug ( 1, "Calling av_find_stream_info" ); + if ( av_find_stream_info( mFormatContext ) < 0 ) #else - Debug ( 1, "Calling avformat_find_stream_info" ); - if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 ) + Debug ( 1, "Calling avformat_find_stream_info" ); + if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 ) #endif - Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) ); - - Debug ( 1, "Got stream info" ); + Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) ); - // Find first video stream present - mVideoStreamId = -1; - for (unsigned int i=0; i < mFormatContext->nb_streams; i++ ) + Debug ( 1, "Got stream info" ); + + // Find first video stream present + mVideoStreamId = -1; + for (unsigned int i=0; i < mFormatContext->nb_streams; i++ ) + { +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) + if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) +#else + if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) +#endif { -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) - if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) -#else - if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) -#endif - { - mVideoStreamId = i; - break; - } + mVideoStreamId = i; + break; } - if ( mVideoStreamId == -1 ) - Fatal( "Unable to locate video stream in %s", mPath.c_str() ); + } + if ( mVideoStreamId == -1 ) + Fatal( "Unable to locate video stream in %s", mPath.c_str() ); - Debug ( 1, "Found video stream" ); + Debug ( 1, "Found video stream" ); - mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; + mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; - // Try and get the codec from the codec context - if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL ) - Fatal( "Can't find codec for video stream from %s", mPath.c_str() ); + // Try and get the codec from the codec context + if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL ) + Fatal( "Can't find codec for video stream from %s", mPath.c_str() ); - Debug ( 1, "Found decoder" ); + Debug ( 1, "Found decoder" ); - // Open the codec -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 7, 0) - Debug ( 1, "Calling avcodec_open" ); - if ( avcodec_open( mCodecContext, mCodec ) < 0 ) + // Open the codec +#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) + Debug ( 1, "Calling avcodec_open" ); + if ( avcodec_open( mCodecContext, mCodec ) < 0 ) #else - Debug ( 1, "Calling avcodec_open2" ); - if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) + Debug ( 1, "Calling avcodec_open2" ); + if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) #endif - Fatal( "Unable to open codec for video stream from %s", mPath.c_str() ); + Fatal( "Unable to open codec for video stream from %s", mPath.c_str() ); - Debug ( 1, "Opened codec" ); + Debug ( 1, "Opened codec" ); - // Allocate space for the native video frame - mRawFrame = avcodec_alloc_frame(); + // Allocate space for the native video frame +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + mRawFrame = av_frame_alloc(); +#else + mRawFrame = avcodec_alloc_frame(); +#endif - // Allocate space for the converted video frame - mFrame = avcodec_alloc_frame(); - - if(mRawFrame == NULL || mFrame == NULL) - Fatal( "Unable to allocate frame for %s", mPath.c_str() ); + // Allocate space for the converted video frame +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + mFrame = av_frame_alloc(); +#else + mFrame = avcodec_alloc_frame(); +#endif - Debug ( 1, "Allocated frames" ); - - int pSize = avpicture_get_size( imagePixFormat, width, height ); - if( (unsigned int)pSize != imagesize) { - Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); - } + if(mRawFrame == NULL || mFrame == NULL) + Fatal( "Unable to allocate frame for %s", mPath.c_str() ); - Debug ( 1, "Validated imagesize" ); - + Debug ( 1, "Allocated frames" ); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 ); +#else + int pSize = avpicture_get_size( imagePixFormat, width, height ); +#endif + + if( (unsigned int)pSize != imagesize) { + Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); + } + + Debug ( 1, "Validated imagesize" ); + #if HAVE_LIBSWSCALE - Debug ( 1, "Calling sws_isSupportedInput" ); - if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { - Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); - } - - if(!sws_isSupportedOutput(imagePixFormat)) { - Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); - } - + Debug ( 1, "Calling sws_isSupportedInput" ); + if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { + Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); + } + + if(!sws_isSupportedOutput(imagePixFormat)) { + Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); + } + #else // HAVE_LIBSWSCALE - Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); + Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" ); #endif // HAVE_LIBSWSCALE - mCanCapture = true; + mCanCapture = true; - return 0; + return 0; } int FfmpegCamera::ReopenFfmpeg() { - Debug(2, "ReopenFfmpeg called."); + Debug(2, "ReopenFfmpeg called."); - mCanCapture = false; - if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){ - // Log a fatal error and exit the process. - Fatal( "ReopenFfmpeg failed to create worker thread." ); - } + mCanCapture = false; + if (pthread_create( &mReopenThread, NULL, ReopenFfmpegThreadCallback, (void*) this) != 0){ + // Log a fatal error and exit the process. + Fatal( "ReopenFfmpeg failed to create worker thread." ); + } - return 0; + return 0; } int FfmpegCamera::CloseFfmpeg(){ - Debug(2, "CloseFfmpeg called."); + Debug(2, "CloseFfmpeg called."); - mCanCapture = false; + mCanCapture = false; - av_freep( &mFrame ); - av_freep( &mRawFrame ); - -#if HAVE_LIBSWSCALE - if ( mConvertContext ) - { - sws_freeContext( mConvertContext ); - mConvertContext = NULL; - } -#endif - - if ( mCodecContext ) - { - avcodec_close( mCodecContext ); - mCodecContext = NULL; // Freed by av_close_input_file - } - if ( mFormatContext ) - { -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) - av_close_input_file( mFormatContext ); +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &mFrame ); + av_frame_free( &mRawFrame ); #else - avformat_close_input( &mFormatContext ); + av_freep( &mFrame ); + av_freep( &mRawFrame ); +#endif + +#if HAVE_LIBSWSCALE + if ( mConvertContext ) + { + sws_freeContext( mConvertContext ); + mConvertContext = NULL; + } #endif - mFormatContext = NULL; - } - return 0; + if ( mCodecContext ) + { + avcodec_close( mCodecContext ); + mCodecContext = NULL; // Freed by av_close_input_file + } + if ( mFormatContext ) + { +#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0) + av_close_input_file( mFormatContext ); +#else + avformat_close_input( &mFormatContext ); +#endif + mFormatContext = NULL; + } + + return 0; } int FfmpegCamera::FfmpegInterruptCallback(void *ctx) { - FfmpegCamera* camera = reinterpret_cast(ctx); - if (camera->mIsOpening){ - int now = time(NULL); - if ((now - camera->mOpenStart) > config.ffmpeg_open_timeout) { - Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout ); - return 1; - } + FfmpegCamera* camera = reinterpret_cast(ctx); + if (camera->mIsOpening){ + int now = time(NULL); + if ((now - camera->mOpenStart) > config.ffmpeg_open_timeout) { + Error ( "Open video took more than %d seconds.", config.ffmpeg_open_timeout ); + return 1; } + } - return 0; + return 0; } void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){ - if (ctx == NULL) return NULL; + if (ctx == NULL) return NULL; - FfmpegCamera* camera = reinterpret_cast(ctx); + FfmpegCamera* camera = reinterpret_cast(ctx); - while (1){ - // Close current stream. - camera->CloseFfmpeg(); + while (1){ + // Close current stream. + camera->CloseFfmpeg(); - // Sleep if neccessary to not reconnect too fast. - int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart); - wait = wait < 0 ? 0 : wait; - if (wait > 0){ - Debug( 1, "Sleeping %d seconds before reopening stream.", wait ); - sleep(wait); - } - - if (camera->OpenFfmpeg() == 0){ - return NULL; - } + // Sleep if necessary to not reconnect too fast. + int wait = config.ffmpeg_open_timeout - (time(NULL) - camera->mOpenStart); + wait = wait < 0 ? 0 : wait; + if (wait > 0){ + Debug( 1, "Sleeping %d seconds before reopening stream.", wait ); + sleep(wait); } + + if (camera->OpenFfmpeg() == 0){ + return NULL; + } + } } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index 11b39d3e1..a8ed2a6a6 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -33,51 +33,51 @@ class FfmpegCamera : public Camera { protected: - std::string mPath; - std::string mMethod; - std::string mOptions; + std::string mPath; + std::string mMethod; + std::string mOptions; - int frameCount; + int frameCount; #if HAVE_LIBAVFORMAT - AVFormatContext *mFormatContext; - int mVideoStreamId; - AVCodecContext *mCodecContext; - AVCodec *mCodec; - AVFrame *mRawFrame; - AVFrame *mFrame; - PixelFormat imagePixFormat; + AVFormatContext *mFormatContext; + int mVideoStreamId; + AVCodecContext *mCodecContext; + AVCodec *mCodec; + AVFrame *mRawFrame; + AVFrame *mFrame; + _AVPIXELFORMAT imagePixFormat; - int OpenFfmpeg(); - int ReopenFfmpeg(); - int CloseFfmpeg(); - static int FfmpegInterruptCallback(void *ctx); - static void* ReopenFfmpegThreadCallback(void *ctx); - bool mIsOpening; - bool mCanCapture; - int mOpenStart; - pthread_t mReopenThread; + int OpenFfmpeg(); + int ReopenFfmpeg(); + int CloseFfmpeg(); + static int FfmpegInterruptCallback(void *ctx); + static void* ReopenFfmpegThreadCallback(void *ctx); + bool mIsOpening; + bool mCanCapture; + int mOpenStart; + pthread_t mReopenThread; #endif // HAVE_LIBAVFORMAT #if HAVE_LIBSWSCALE - struct SwsContext *mConvertContext; + struct SwsContext *mConvertContext; #endif public: - FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~FfmpegCamera(); + FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); + ~FfmpegCamera(); - const std::string &Path() const { return( mPath ); } - const std::string &Options() const { return( mOptions ); } - const std::string &Method() const { return( mMethod ); } + const std::string &Path() const { return( mPath ); } + const std::string &Options() const { return( mOptions ); } + const std::string &Method() const { return( mMethod ); } - void Initialise(); - void Terminate(); + void Initialise(); + void Terminate(); - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); }; #endif // ZM_FFMPEG_CAMERA_H diff --git a/src/zm_file_camera.cpp b/src/zm_file_camera.cpp index 28ce2b155..4292cea48 100644 --- a/src/zm_file_camera.cpp +++ b/src/zm_file_camera.cpp @@ -36,28 +36,28 @@ FileCamera::FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : Camera( p_id, FILE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ) { - strncpy( path, p_path, sizeof(path) ); - if ( capture ) - { - Initialise(); - } + strncpy( path, p_path, sizeof(path) ); + if ( capture ) + { + Initialise(); + } } FileCamera::~FileCamera() { - if ( capture ) - { - Terminate(); - } + if ( capture ) + { + Terminate(); + } } void FileCamera::Initialise() { - if ( !path[0] ) - { - Error( "No path specified for file image" ); - exit( -1 ); - } + if ( !path[0] ) + { + Error( "No path specified for file image" ); + exit( -1 ); + } } void FileCamera::Terminate() @@ -66,26 +66,26 @@ void FileCamera::Terminate() int FileCamera::PreCapture() { - struct stat statbuf; - if ( stat( path, &statbuf ) < 0 ) - { - Error( "Can't stat %s: %s", path, strerror(errno) ); - return( -1 ); - } + struct stat statbuf; + if ( stat( path, &statbuf ) < 0 ) + { + Error( "Can't stat %s: %s", path, strerror(errno) ); + return( -1 ); + } - while ( (time( 0 ) - statbuf.st_mtime) < 1 ) - { - usleep( 100000 ); - } - return( 0 ); + while ( (time( 0 ) - statbuf.st_mtime) < 1 ) + { + usleep( 100000 ); + } + return( 0 ); } int FileCamera::Capture( Image &image ) { - return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 ); + return( image.ReadJpeg( path, colours, subpixelorder )?0:-1 ); } int FileCamera::PostCapture() { - return( 0 ); + return( 0 ); } diff --git a/src/zm_file_camera.h b/src/zm_file_camera.h index 2afdbc1f2..19e5971b8 100644 --- a/src/zm_file_camera.h +++ b/src/zm_file_camera.h @@ -33,19 +33,19 @@ class FileCamera : public Camera { protected: - char path[PATH_MAX]; + char path[PATH_MAX]; public: - FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~FileCamera(); + FileCamera( int p_id, const char *p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); + ~FileCamera(); - const char *Path() const { return( path ); } + const char *Path() const { return( path ); } - void Initialise(); - void Terminate(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + void Initialise(); + void Terminate(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); }; #endif // ZM_FILE_CAMERA_H diff --git a/src/zm_font.h b/src/zm_font.h index 844ff4f1e..cdad72dc4 100644 --- a/src/zm_font.h +++ b/src/zm_font.h @@ -1,3337 +1,3337 @@ /**********************************************/ -/* */ -/* Font file generated by rthelen */ -/* */ +/* */ +/* Font file generated by rthelen */ +/* */ /**********************************************/ static unsigned char fontdata[] = { - /* 0 0x00 '^A' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 1 0x01 '^B' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 2 0x02 '^C' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 3 0x03 '^D' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 4 0x04 '^E' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 5 0x05 '^F' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 6 0x06 '^G' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 7 0x07 '^H' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 8 0x08 '^I' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 9 0x09 '^J' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 10 0x0a '^K' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 11 0x0b '^L' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 12 0x0c '^M' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 13 0x0d '^N' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 14 0x0e '^O' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 15 0x0f '^P' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 16 0x10 '^Q' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 17 0x11 '^R' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 18 0x12 '^S' */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x50, /* 0 0 0000 */ - 0x50, /* 0 0 0000 */ - 0x20, /* 00 00000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 19 0x13 '^T' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x7c, /* 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 20 0x14 '^U' */ - 0x18, /* 000 000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x7c, /* 0 00 */ - 0x78, /* 0 000 */ - 0x78, /* 0 000 */ - 0x7c, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 21 0x15 '^V' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 22 0x16 '^W' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 23 0x17 '^X' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 24 0x18 '^Y' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 25 0x19 '^Z' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 26 0x1a '^[' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 27 0x1b '^\' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 28 0x1c '^]' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 29 0x1d '^^' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 30 0x1e '^_' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 31 0x1f '^`' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 32 0x20 ' ' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 33 0x21 '!' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 34 0x22 '"' */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 35 0x23 '#' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x7c, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x7c, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 36 0x24 '$' */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x50, /* 0 0 0000 */ - 0x38, /* 00 000 */ - 0x14, /* 000 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 37 0x25 '%' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x58, /* 0 0 000 */ - 0x28, /* 00 0 000 */ - 0x34, /* 00 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x48, /* 0 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 38 0x26 '&' */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x48, /* 0 00 000 */ - 0x50, /* 0 0 0000 */ - 0x20, /* 00 00000 */ - 0x54, /* 0 0 0 00 */ - 0x48, /* 0 00 000 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 39 0x27 ''' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 40 0x28 '(' */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 41 0x29 ')' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 42 0x2a '*' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 43 0x2b '+' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 44 0x2c ',' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - - /* 45 0x2d '-' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 46 0x2e '.' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 000 000 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 47 0x2f '/' */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x20, /* 00 00000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - - /* 48 0x30 '0' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x64, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 49 0x31 '1' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x18, /* 000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 50 0x32 '2' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 51 0x33 '3' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x04, /* 00000 00 */ - 0x18, /* 000 000 */ - 0x04, /* 00000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 52 0x34 '4' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x18, /* 000 000 */ - 0x28, /* 00 0 000 */ - 0x48, /* 0 00 000 */ - 0x7c, /* 0 00 */ - 0x08, /* 0000 000 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 53 0x35 '5' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 54 0x36 '6' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 55 0x37 '7' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 56 0x38 '8' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 57 0x39 '9' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 58 0x3a ':' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x18, /* 000 000 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x18, /* 000 000 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 59 0x3b ';' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x30, /* 00 0000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - - /* 60 0x3c '<' */ - 0x00, /* 00000000 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 61 0x3d '=' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 62 0x3e '>' */ - 0x00, /* 00000000 */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 63 0x3f '?' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 64 0x40 '@' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x74, /* 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 65 0x41 'A' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 66 0x42 'B' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 67 0x43 'C' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 68 0x44 'D' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 69 0x45 'E' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 70 0x46 'F' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 71 0x47 'G' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x4c, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 72 0x48 'H' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 73 0x49 'I' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 74 0x4a 'J' */ - 0x00, /* 00000000 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 75 0x4b 'K' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x48, /* 0 00 000 */ - 0x50, /* 0 0 0000 */ - 0x60, /* 0 00000 */ - 0x50, /* 0 0 0000 */ - 0x48, /* 0 00 000 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 76 0x4c 'L' */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 77 0x4d 'M' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x6c, /* 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 78 0x4e 'N' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x64, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x4c, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 79 0x4f 'O' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 80 0x50 'P' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 81 0x51 'Q' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 82 0x52 'R' */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 83 0x53 'S' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x38, /* 00 000 */ - 0x04, /* 00000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 84 0x54 'T' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 85 0x55 'U' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 86 0x56 'V' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 87 0x57 'W' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x6c, /* 0 0 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 88 0x58 'X' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 89 0x59 'Y' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 90 0x5a 'Z' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x40, /* 0 000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 91 0x5b '[' */ - 0x0c, /* 0000 00 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x0c, /* 0000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 92 0x5c '\' */ - 0x20, /* 00 00000 */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x02, /* 000000 0 */ - 0x02, /* 000000 0 */ - 0x00, /* 00000000 */ - - /* 93 0x5d ']' */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x30, /* 00 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 94 0x5e '^' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 95 0x5f '_' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 96 0x60 '`' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 97 0x61 'a' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 98 0x62 'b' */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 99 0x63 'c' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 100 0x64 'd' */ - 0x00, /* 00000000 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 101 0x65 'e' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 102 0x66 'f' */ - 0x00, /* 00000000 */ - 0x0c, /* 0000 00 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 103 0x67 'g' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - - /* 104 0x68 'h' */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 105 0x69 'i' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 106 0x6a 'j' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x60, /* 0 00000 */ - 0x00, /* 00000000 */ - - /* 107 0x6b 'k' */ - 0x00, /* 00000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x48, /* 0 00 000 */ - 0x50, /* 0 0 0000 */ - 0x70, /* 0 0000 */ - 0x48, /* 0 00 000 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 108 0x6c 'l' */ - 0x00, /* 00000000 */ - 0x30, /* 00 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 109 0x6d 'm' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 110 0x6e 'n' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x58, /* 0 0 000 */ - 0x64, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 111 0x6f 'o' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 112 0x70 'p' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x78, /* 0 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - - /* 113 0x71 'q' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - - /* 114 0x72 'r' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x58, /* 0 0 000 */ - 0x64, /* 0 00 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 115 0x73 's' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x40, /* 0 000000 */ - 0x38, /* 00 000 */ - 0x04, /* 00000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 116 0x74 't' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x0c, /* 0000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 117 0x75 'u' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 118 0x76 'v' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 119 0x77 'w' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 120 0x78 'x' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 121 0x79 'y' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - - /* 122 0x7a 'z' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 123 0x7b '{' */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - - /* 124 0x7c '|' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 125 0x7d '}' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - - /* 126 0x7e '~' */ - 0x00, /* 00000000 */ - 0x34, /* 00 0 00 */ - 0x58, /* 0 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 127 0x7f '^?' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 128 0x80 '\200' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 129 0x81 '\201' */ - 0x28, /* 00 0 000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 130 0x82 '\202' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 131 0x83 '\203' */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x78, /* 0 000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 132 0x84 '\204' */ - 0x58, /* 0 0 000 */ - 0x44, /* 0 000 00 */ - 0x64, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x4c, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 133 0x85 '\205' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 134 0x86 '\206' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 135 0x87 '\207' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 136 0x88 '\210' */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 137 0x89 '\211' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 138 0x8a '\212' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 139 0x8b '\213' */ - 0x34, /* 00 0 00 */ - 0x58, /* 0 0 000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 140 0x8c '\214' */ - 0x18, /* 000 000 */ - 0x24, /* 00 00 00 */ - 0x18, /* 000 000 */ - 0x3c, /* 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 141 0x8d '\215' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - - /* 142 0x8e '\216' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 143 0x8f '\217' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 144 0x90 '\220' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 145 0x91 '\221' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x40, /* 0 000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 146 0x92 '\222' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 147 0x93 '\223' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 148 0x94 '\224' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 149 0x95 '\225' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 150 0x96 '\226' */ - 0x34, /* 00 0 00 */ - 0x58, /* 0 0 000 */ - 0x00, /* 00000000 */ - 0x58, /* 0 0 000 */ - 0x64, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 151 0x97 '\227' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 152 0x98 '\230' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 153 0x99 '\231' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 154 0x9a '\232' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 155 0x9b '\233' */ - 0x34, /* 00 0 00 */ - 0x58, /* 0 0 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 156 0x9c '\234' */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 157 0x9d '\235' */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 158 0x9e '\236' */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 159 0x9f '\237' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x34, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 160 0xa0 '\240' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 161 0xa1 '\241' */ - 0x18, /* 000 000 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 162 0xa2 '\242' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x50, /* 0 0 0000 */ - 0x54, /* 0 0 0 00 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 163 0xa3 '\243' */ - 0x30, /* 00 0000 */ - 0x48, /* 0 00 000 */ - 0x40, /* 0 000000 */ - 0x70, /* 0 0000 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x78, /* 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 164 0xa4 '\244' */ - 0x44, /* 0 000 00 */ - 0x24, /* 00 00 00 */ - 0x50, /* 0 0 0000 */ - 0x48, /* 0 00 000 */ - 0x24, /* 00 00 00 */ - 0x14, /* 000 0 00 */ - 0x48, /* 0 00 000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 165 0xa5 '\245' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x7c, /* 0 00 */ - 0x7c, /* 0 00 */ - 0x7c, /* 0 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 166 0xa6 '\246' */ - 0x3c, /* 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x3c, /* 00 00 */ - 0x14, /* 000 0 00 */ - 0x14, /* 000 0 00 */ - 0x14, /* 000 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 167 0xa7 '\247' */ - 0x18, /* 000 000 */ - 0x24, /* 00 00 00 */ - 0x44, /* 0 000 00 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x58, /* 0 0 000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 168 0xa8 '\250' */ - 0x00, /* 00000000 */ - 0x70, /* 0 0000 */ - 0x08, /* 0000 000 */ - 0x64, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x64, /* 0 00 00 */ - 0x58, /* 0 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 169 0xa9 '\251' */ - 0x00, /* 00000000 */ - 0x70, /* 0 0000 */ - 0x08, /* 0000 000 */ - 0x34, /* 00 0 00 */ - 0x44, /* 0 000 00 */ - 0x34, /* 00 0 00 */ - 0x08, /* 0000 000 */ - 0x70, /* 0 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 170 0xaa '\252' */ - 0x00, /* 00000000 */ - 0x7a, /* 0 0 0 */ - 0x2e, /* 00 0 0 */ - 0x2e, /* 00 0 0 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 171 0xab '\253' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 172 0xac '\254' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 173 0xad '\255' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 174 0xae '\256' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x50, /* 0 0 0000 */ - 0x50, /* 0 0 0000 */ - 0x78, /* 0 000 */ - 0x50, /* 0 0 0000 */ - 0x50, /* 0 0 0000 */ - 0x5c, /* 0 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 175 0xaf '\257' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x4c, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x64, /* 0 00 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 176 0xb0 '\260' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x6c, /* 0 0 00 */ - 0x54, /* 0 0 0 00 */ - 0x6c, /* 0 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 177 0xb1 '\261' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 178 0xb2 '\262' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 179 0xb3 '\263' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x04, /* 00000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 180 0xb4 '\264' */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x7c, /* 0 00 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 181 0xb5 '\265' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x74, /* 0 0 00 */ - 0x40, /* 0 000000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - - /* 182 0xb6 '\266' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x0c, /* 0000 00 */ - 0x14, /* 000 0 00 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 183 0xb7 '\267' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x24, /* 00 00 00 */ - 0x10, /* 000 0000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x24, /* 00 00 00 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 184 0xb8 '\270' */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 185 0xb9 '\271' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 186 0xba '\272' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x60, /* 0 00000 */ - 0x00, /* 00000000 */ - - /* 187 0xbb '\273' */ - 0x00, /* 00000000 */ - 0x1c, /* 000 00 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x1c, /* 000 00 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 188 0xbc '\274' */ - 0x00, /* 00000000 */ - 0x18, /* 000 000 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 189 0xbd '\275' */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x6c, /* 0 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 190 0xbe '\276' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x54, /* 0 0 0 00 */ - 0x5c, /* 0 0 00 */ - 0x50, /* 0 0 0000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 191 0xbf '\277' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x4c, /* 0 00 00 */ - 0x54, /* 0 0 0 00 */ - 0x64, /* 0 00 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 192 0xc0 '\300' */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x20, /* 00 00000 */ - 0x40, /* 0 000000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 193 0xc1 '\301' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x08, /* 0000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 194 0xc2 '\302' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x04, /* 00000 00 */ - 0x04, /* 00000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 195 0xc3 '\303' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x0c, /* 0000 00 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x50, /* 0 0 0000 */ - 0x20, /* 00 00000 */ - 0x20, /* 00 00000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 196 0xc4 '\304' */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x60, /* 0 00000 */ - 0x00, /* 00000000 */ - - /* 197 0xc5 '\305' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x40, /* 0 000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 198 0xc6 '\306' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 199 0xc7 '\307' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x24, /* 00 00 00 */ - 0x48, /* 0 00 000 */ - 0x48, /* 0 00 000 */ - 0x24, /* 00 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 200 0xc8 '\310' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x48, /* 0 00 000 */ - 0x24, /* 00 00 00 */ - 0x24, /* 00 00 00 */ - 0x48, /* 0 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 201 0xc9 '\311' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x54, /* 0 0 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 202 0xca '\312' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 203 0xcb '\313' */ - 0x10, /* 000 0000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 204 0xcc '\314' */ - 0x58, /* 0 0 000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x7c, /* 0 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 205 0xcd '\315' */ - 0x58, /* 0 0 000 */ - 0x38, /* 00 000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 206 0xce '\316' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x50, /* 0 0 0000 */ - 0x50, /* 0 0 0000 */ - 0x58, /* 0 0 000 */ - 0x50, /* 0 0 0000 */ - 0x50, /* 0 0 0000 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 207 0xcf '\317' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x54, /* 0 0 0 00 */ - 0x5c, /* 0 0 00 */ - 0x50, /* 0 0 0000 */ - 0x2c, /* 00 0 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 208 0xd0 '\320' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 209 0xd1 '\321' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 210 0xd2 '\322' */ - 0x00, /* 00000000 */ - 0x14, /* 000 0 00 */ - 0x28, /* 00 0 000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 211 0xd3 '\323' */ - 0x00, /* 00000000 */ - 0x14, /* 000 0 00 */ - 0x14, /* 000 0 00 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 212 0xd4 '\324' */ - 0x00, /* 00000000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x18, /* 000 000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 213 0xd5 '\325' */ - 0x00, /* 00000000 */ - 0x18, /* 000 000 */ - 0x08, /* 0000 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 214 0xd6 '\326' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x7c, /* 0 00 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 215 0xd7 '\327' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x10, /* 000 0000 */ - 0x28, /* 00 0 000 */ - 0x44, /* 0 000 00 */ - 0x28, /* 00 0 000 */ - 0x10, /* 000 0000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 216 0xd8 '\330' */ - 0x00, /* 00000000 */ - 0x28, /* 00 0 000 */ - 0x00, /* 00000000 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x44, /* 0 000 00 */ - 0x3c, /* 00 00 */ - 0x04, /* 00000 00 */ - 0x38, /* 00 000 */ - 0x00, /* 00000000 */ - - /* 217 0xd9 '\331' */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ - 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ - 0x00, /* 00000000 */ - 0x7e, /* 0 0 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 218 0xda '\332' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 219 0xdb '\333' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 220 0xdc '\334' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 221 0xdd '\335' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 222 0xde '\336' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 223 0xdf '\337' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 224 0xe0 '\340' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 225 0xe1 '\341' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 226 0xe2 '\342' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 227 0xe3 '\343' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 228 0xe4 '\344' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 229 0xe5 '\345' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 230 0xe6 '\346' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 231 0xe7 '\347' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 232 0xe8 '\350' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 233 0xe9 '\351' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 234 0xea '\352' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 235 0xeb '\353' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 236 0xec '\354' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 237 0xed '\355' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 238 0xee '\356' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 239 0xef '\357' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 240 0xf0 '\360' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 241 0xf1 '\361' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 242 0xf2 '\362' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 243 0xf3 '\363' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 244 0xf4 '\364' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 245 0xf5 '\365' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 246 0xf6 '\366' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 247 0xf7 '\367' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 248 0xf8 '\370' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 249 0xf9 '\371' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 250 0xfa '\372' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 251 0xfb '\373' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 252 0xfc '\374' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 253 0xfd '\375' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 254 0xfe '\376' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - - /* 255 0xff '\377' */ - 0x00, /* 00000000 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x3c, /* 00 00 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ - 0x00, /* 00000000 */ + /* 0 0x00 '^A' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 1 0x01 '^B' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 2 0x02 '^C' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 3 0x03 '^D' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 4 0x04 '^E' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 5 0x05 '^F' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 6 0x06 '^G' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 7 0x07 '^H' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 8 0x08 '^I' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 9 0x09 '^J' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 10 0x0a '^K' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 11 0x0b '^L' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 12 0x0c '^M' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 13 0x0d '^N' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 14 0x0e '^O' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 15 0x0f '^P' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 16 0x10 '^Q' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 17 0x11 '^R' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 18 0x12 '^S' */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 19 0x13 '^T' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x7c, /* 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 20 0x14 '^U' */ + 0x18, /* 000 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x78, /* 0 000 */ + 0x78, /* 0 000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 21 0x15 '^V' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 22 0x16 '^W' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 23 0x17 '^X' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 24 0x18 '^Y' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 25 0x19 '^Z' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 26 0x1a '^[' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 27 0x1b '^\' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 28 0x1c '^]' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 29 0x1d '^^' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 30 0x1e '^_' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 31 0x1f '^`' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 32 0x20 ' ' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 33 0x21 '!' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 34 0x22 '"' */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 35 0x23 '#' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 36 0x24 '$' */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x38, /* 00 000 */ + 0x14, /* 000 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 37 0x25 '%' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x58, /* 0 0 000 */ + 0x28, /* 00 0 000 */ + 0x34, /* 00 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 38 0x26 '&' */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x48, /* 0 00 000 */ + 0x50, /* 0 0 0000 */ + 0x20, /* 00 00000 */ + 0x54, /* 0 0 0 00 */ + 0x48, /* 0 00 000 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 39 0x27 ''' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 40 0x28 '(' */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 41 0x29 ')' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 42 0x2a '*' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 43 0x2b '+' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 44 0x2c ',' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 45 0x2d '-' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 46 0x2e '.' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 47 0x2f '/' */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + + /* 48 0x30 '0' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 49 0x31 '1' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x18, /* 000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 50 0x32 '2' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 51 0x33 '3' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x04, /* 00000 00 */ + 0x18, /* 000 000 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 52 0x34 '4' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x18, /* 000 000 */ + 0x28, /* 00 0 000 */ + 0x48, /* 0 00 000 */ + 0x7c, /* 0 00 */ + 0x08, /* 0000 000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 53 0x35 '5' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 54 0x36 '6' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 55 0x37 '7' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 56 0x38 '8' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 57 0x39 '9' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 58 0x3a ':' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 59 0x3b ';' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x30, /* 00 0000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 60 0x3c '<' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 61 0x3d '=' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 62 0x3e '>' */ + 0x00, /* 00000000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 63 0x3f '?' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 64 0x40 '@' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x74, /* 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 65 0x41 'A' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 66 0x42 'B' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 67 0x43 'C' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 68 0x44 'D' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 69 0x45 'E' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 70 0x46 'F' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 71 0x47 'G' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x4c, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 72 0x48 'H' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 73 0x49 'I' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 74 0x4a 'J' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 75 0x4b 'K' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x48, /* 0 00 000 */ + 0x50, /* 0 0 0000 */ + 0x60, /* 0 00000 */ + 0x50, /* 0 0 0000 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 76 0x4c 'L' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 77 0x4d 'M' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x6c, /* 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 78 0x4e 'N' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x64, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x4c, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 79 0x4f 'O' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 80 0x50 'P' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 81 0x51 'Q' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 82 0x52 'R' */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 83 0x53 'S' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x38, /* 00 000 */ + 0x04, /* 00000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 84 0x54 'T' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 85 0x55 'U' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 86 0x56 'V' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 87 0x57 'W' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x6c, /* 0 0 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 88 0x58 'X' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 89 0x59 'Y' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 90 0x5a 'Z' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 91 0x5b '[' */ + 0x0c, /* 0000 00 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x0c, /* 0000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 92 0x5c '\' */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x02, /* 000000 0 */ + 0x02, /* 000000 0 */ + 0x00, /* 00000000 */ + + /* 93 0x5d ']' */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x30, /* 00 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 94 0x5e '^' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 95 0x5f '_' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 96 0x60 '`' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 97 0x61 'a' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 98 0x62 'b' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 99 0x63 'c' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 100 0x64 'd' */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 101 0x65 'e' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 102 0x66 'f' */ + 0x00, /* 00000000 */ + 0x0c, /* 0000 00 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 103 0x67 'g' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 104 0x68 'h' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 105 0x69 'i' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 106 0x6a 'j' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x60, /* 0 00000 */ + 0x00, /* 00000000 */ + + /* 107 0x6b 'k' */ + 0x00, /* 00000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x48, /* 0 00 000 */ + 0x50, /* 0 0 0000 */ + 0x70, /* 0 0000 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 108 0x6c 'l' */ + 0x00, /* 00000000 */ + 0x30, /* 00 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 109 0x6d 'm' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 110 0x6e 'n' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x58, /* 0 0 000 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 111 0x6f 'o' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 112 0x70 'p' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x78, /* 0 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + + /* 113 0x71 'q' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + + /* 114 0x72 'r' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x58, /* 0 0 000 */ + 0x64, /* 0 00 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 115 0x73 's' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x40, /* 0 000000 */ + 0x38, /* 00 000 */ + 0x04, /* 00000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 116 0x74 't' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x0c, /* 0000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 117 0x75 'u' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 118 0x76 'v' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 119 0x77 'w' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 120 0x78 'x' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 121 0x79 'y' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 122 0x7a 'z' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 123 0x7b '{' */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + + /* 124 0x7c '|' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 125 0x7d '}' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 126 0x7e '~' */ + 0x00, /* 00000000 */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 127 0x7f '^?' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 128 0x80 '\200' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 129 0x81 '\201' */ + 0x28, /* 00 0 000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 130 0x82 '\202' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 131 0x83 '\203' */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x78, /* 0 000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 132 0x84 '\204' */ + 0x58, /* 0 0 000 */ + 0x44, /* 0 000 00 */ + 0x64, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x4c, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 133 0x85 '\205' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 134 0x86 '\206' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 135 0x87 '\207' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 136 0x88 '\210' */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 137 0x89 '\211' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 138 0x8a '\212' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 139 0x8b '\213' */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 140 0x8c '\214' */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x3c, /* 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 141 0x8d '\215' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + + /* 142 0x8e '\216' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 143 0x8f '\217' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 144 0x90 '\220' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 145 0x91 '\221' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x40, /* 0 000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 146 0x92 '\222' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 147 0x93 '\223' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 148 0x94 '\224' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 149 0x95 '\225' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 150 0x96 '\226' */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x58, /* 0 0 000 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 151 0x97 '\227' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 152 0x98 '\230' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 153 0x99 '\231' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 154 0x9a '\232' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 155 0x9b '\233' */ + 0x34, /* 00 0 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 156 0x9c '\234' */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 157 0x9d '\235' */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 158 0x9e '\236' */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 159 0x9f '\237' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x34, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 160 0xa0 '\240' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 161 0xa1 '\241' */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 162 0xa2 '\242' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x54, /* 0 0 0 00 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 163 0xa3 '\243' */ + 0x30, /* 00 0000 */ + 0x48, /* 0 00 000 */ + 0x40, /* 0 000000 */ + 0x70, /* 0 0000 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x78, /* 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 164 0xa4 '\244' */ + 0x44, /* 0 000 00 */ + 0x24, /* 00 00 00 */ + 0x50, /* 0 0 0000 */ + 0x48, /* 0 00 000 */ + 0x24, /* 00 00 00 */ + 0x14, /* 000 0 00 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 165 0xa5 '\245' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x7c, /* 0 00 */ + 0x7c, /* 0 00 */ + 0x7c, /* 0 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 166 0xa6 '\246' */ + 0x3c, /* 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x3c, /* 00 00 */ + 0x14, /* 000 0 00 */ + 0x14, /* 000 0 00 */ + 0x14, /* 000 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 167 0xa7 '\247' */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x44, /* 0 000 00 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x58, /* 0 0 000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 168 0xa8 '\250' */ + 0x00, /* 00000000 */ + 0x70, /* 0 0000 */ + 0x08, /* 0000 000 */ + 0x64, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x58, /* 0 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 169 0xa9 '\251' */ + 0x00, /* 00000000 */ + 0x70, /* 0 0000 */ + 0x08, /* 0000 000 */ + 0x34, /* 00 0 00 */ + 0x44, /* 0 000 00 */ + 0x34, /* 00 0 00 */ + 0x08, /* 0000 000 */ + 0x70, /* 0 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 170 0xaa '\252' */ + 0x00, /* 00000000 */ + 0x7a, /* 0 0 0 */ + 0x2e, /* 00 0 0 */ + 0x2e, /* 00 0 0 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 171 0xab '\253' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 172 0xac '\254' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 173 0xad '\255' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 174 0xae '\256' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x78, /* 0 000 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x5c, /* 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 175 0xaf '\257' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x4c, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 176 0xb0 '\260' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x6c, /* 0 0 00 */ + 0x54, /* 0 0 0 00 */ + 0x6c, /* 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 177 0xb1 '\261' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 178 0xb2 '\262' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 179 0xb3 '\263' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x04, /* 00000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 180 0xb4 '\264' */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x7c, /* 0 00 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 181 0xb5 '\265' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x74, /* 0 0 00 */ + 0x40, /* 0 000000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + + /* 182 0xb6 '\266' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x0c, /* 0000 00 */ + 0x14, /* 000 0 00 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 183 0xb7 '\267' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x24, /* 00 00 00 */ + 0x10, /* 000 0000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x24, /* 00 00 00 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 184 0xb8 '\270' */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 185 0xb9 '\271' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 186 0xba '\272' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x60, /* 0 00000 */ + 0x00, /* 00000000 */ + + /* 187 0xbb '\273' */ + 0x00, /* 00000000 */ + 0x1c, /* 000 00 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x1c, /* 000 00 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 188 0xbc '\274' */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 189 0xbd '\275' */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x6c, /* 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 190 0xbe '\276' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x54, /* 0 0 0 00 */ + 0x5c, /* 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 191 0xbf '\277' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x4c, /* 0 00 00 */ + 0x54, /* 0 0 0 00 */ + 0x64, /* 0 00 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 192 0xc0 '\300' */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x20, /* 00 00000 */ + 0x40, /* 0 000000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 193 0xc1 '\301' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x08, /* 0000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 194 0xc2 '\302' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x04, /* 00000 00 */ + 0x04, /* 00000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 195 0xc3 '\303' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x0c, /* 0000 00 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x50, /* 0 0 0000 */ + 0x20, /* 00 00000 */ + 0x20, /* 00 00000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 196 0xc4 '\304' */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x60, /* 0 00000 */ + 0x00, /* 00000000 */ + + /* 197 0xc5 '\305' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x40, /* 0 000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 198 0xc6 '\306' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 199 0xc7 '\307' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x24, /* 00 00 00 */ + 0x48, /* 0 00 000 */ + 0x48, /* 0 00 000 */ + 0x24, /* 00 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 200 0xc8 '\310' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x48, /* 0 00 000 */ + 0x24, /* 00 00 00 */ + 0x24, /* 00 00 00 */ + 0x48, /* 0 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 201 0xc9 '\311' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x54, /* 0 0 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 202 0xca '\312' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 203 0xcb '\313' */ + 0x10, /* 000 0000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 204 0xcc '\314' */ + 0x58, /* 0 0 000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x7c, /* 0 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 205 0xcd '\315' */ + 0x58, /* 0 0 000 */ + 0x38, /* 00 000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 206 0xce '\316' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x58, /* 0 0 000 */ + 0x50, /* 0 0 0000 */ + 0x50, /* 0 0 0000 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 207 0xcf '\317' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x54, /* 0 0 0 00 */ + 0x5c, /* 0 0 00 */ + 0x50, /* 0 0 0000 */ + 0x2c, /* 00 0 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 208 0xd0 '\320' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 209 0xd1 '\321' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 210 0xd2 '\322' */ + 0x00, /* 00000000 */ + 0x14, /* 000 0 00 */ + 0x28, /* 00 0 000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 211 0xd3 '\323' */ + 0x00, /* 00000000 */ + 0x14, /* 000 0 00 */ + 0x14, /* 000 0 00 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 212 0xd4 '\324' */ + 0x00, /* 00000000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x18, /* 000 000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 213 0xd5 '\325' */ + 0x00, /* 00000000 */ + 0x18, /* 000 000 */ + 0x08, /* 0000 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 214 0xd6 '\326' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x7c, /* 0 00 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 215 0xd7 '\327' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x10, /* 000 0000 */ + 0x28, /* 00 0 000 */ + 0x44, /* 0 000 00 */ + 0x28, /* 00 0 000 */ + 0x10, /* 000 0000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 216 0xd8 '\330' */ + 0x00, /* 00000000 */ + 0x28, /* 00 0 000 */ + 0x00, /* 00000000 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x44, /* 0 000 00 */ + 0x3c, /* 00 00 */ + 0x04, /* 00000 00 */ + 0x38, /* 00 000 */ + 0x00, /* 00000000 */ + + /* 217 0xd9 '\331' */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x7e, /* 0 0 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 218 0xda '\332' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 219 0xdb '\333' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 220 0xdc '\334' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 221 0xdd '\335' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 222 0xde '\336' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 223 0xdf '\337' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 224 0xe0 '\340' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 225 0xe1 '\341' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 226 0xe2 '\342' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 227 0xe3 '\343' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 228 0xe4 '\344' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 229 0xe5 '\345' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 230 0xe6 '\346' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 231 0xe7 '\347' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 232 0xe8 '\350' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 233 0xe9 '\351' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 234 0xea '\352' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 235 0xeb '\353' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 236 0xec '\354' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 237 0xed '\355' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 238 0xee '\356' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 239 0xef '\357' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 240 0xf0 '\360' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 241 0xf1 '\361' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 242 0xf2 '\362' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 243 0xf3 '\363' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 244 0xf4 '\364' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 245 0xf5 '\365' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 246 0xf6 '\366' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 247 0xf7 '\367' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 248 0xf8 '\370' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 249 0xf9 '\371' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 250 0xfa '\372' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 251 0xfb '\373' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 252 0xfc '\374' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 253 0xfd '\375' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 254 0xfe '\376' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + + /* 255 0xff '\377' */ + 0x00, /* 00000000 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x3c, /* 00 00 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ + 0x00, /* 00000000 */ }; diff --git a/src/zm_image.cpp b/src/zm_image.cpp index e2aaabc55..d76384dc3 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -18,6 +18,7 @@ // #include "zm.h" #include "zm_font.h" +#include "zm_bigfont.h" #include "zm_image.h" #include "zm_utils.h" #include "zm_rgb.h" @@ -62,2926 +63,3056 @@ imgbufcpy_fptr_t fptr_imgbufcpy; Image::Image() { - if ( !initialised ) - Initialise(); - width = 0; - height = 0; - pixels = 0; - colours = 0; - subpixelorder = 0; - size = 0; - allocation = 0; - buffer = 0; - buffertype = 0; - holdbuffer = 0; - text[0] = '\0'; + if ( !initialised ) + Initialise(); + width = 0; + height = 0; + pixels = 0; + colours = 0; + subpixelorder = 0; + size = 0; + allocation = 0; + buffer = 0; + buffertype = 0; + holdbuffer = 0; + text[0] = '\0'; } Image::Image( const char *filename ) { - if ( !initialised ) - Initialise(); - width = 0; - height = 0; - pixels = 0; - colours = 0; - subpixelorder = 0; - size = 0; - allocation = 0; - buffer = 0; - buffertype = 0; - holdbuffer = 0; - ReadJpeg( filename, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); - text[0] = '\0'; + if ( !initialised ) + Initialise(); + width = 0; + height = 0; + pixels = 0; + colours = 0; + subpixelorder = 0; + size = 0; + allocation = 0; + buffer = 0; + buffertype = 0; + holdbuffer = 0; + ReadJpeg( filename, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB); + text[0] = '\0'; } Image::Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer ) { - if ( !initialised ) - Initialise(); - width = p_width; - height = p_height; - pixels = width*height; - colours = p_colours; - subpixelorder = p_subpixelorder; - size = (width*height)*colours; - buffer = 0; - holdbuffer = 0; - if ( p_buffer ) - { - allocation = size; - buffertype = ZM_BUFTYPE_DONTFREE; - buffer = p_buffer; - } - else - { - AllocImgBuffer(size); - } - text[0] = '\0'; + if ( !initialised ) + Initialise(); + width = p_width; + height = p_height; + pixels = width*height; + colours = p_colours; + subpixelorder = p_subpixelorder; + size = pixels*colours; + buffer = 0; + holdbuffer = 0; + if ( p_buffer ) + { + allocation = size; + buffertype = ZM_BUFTYPE_DONTFREE; + buffer = p_buffer; + } + else + { + AllocImgBuffer(size); + } + text[0] = '\0'; } Image::Image( const Image &p_image ) { - if ( !initialised ) - Initialise(); - width = p_image.width; - height = p_image.height; - pixels = p_image.pixels; - colours = p_image.colours; - subpixelorder = p_image.subpixelorder; - size = allocation = p_image.size; - buffer = 0; - holdbuffer = 0; - AllocImgBuffer(allocation); - (*fptr_imgbufcpy)(buffer, p_image.buffer, size); - strncpy( text, p_image.text, sizeof(text) ); + if ( !initialised ) + Initialise(); + width = p_image.width; + height = p_image.height; + pixels = p_image.pixels; + colours = p_image.colours; + subpixelorder = p_image.subpixelorder; + size = p_image.size; // allocation is set in AllocImgBuffer + buffer = 0; + holdbuffer = 0; + AllocImgBuffer(size); + (*fptr_imgbufcpy)(buffer, p_image.buffer, size); + strncpy( text, p_image.text, sizeof(text) ); } Image::~Image() { - DumpImgBuffer(); + DumpImgBuffer(); + if ( initialised ) + { + delete[] y_table; + delete[] uv_table; + delete[] r_v_table; + delete[] g_v_table; + delete[] g_u_table; + delete[] b_u_table; + initialised = false; + } + if ( jpg_dcinfo ) + { + jpeg_destroy_decompress( jpg_dcinfo ); + delete jpg_dcinfo; + jpg_dcinfo = 0; + } } void Image::Initialise() { - /* Assign the blend pointer to function */ - if(config.fast_image_blends) { - if(config.cpu_extensions && sseversion >= 20) { - fptr_blend = &sse2_fastblend; /* SSE2 fast blend */ - Debug(2,"Blend: Using SSE2 fast blend function"); - } else { - fptr_blend = &std_fastblend; /* standard fast blend */ - Debug(2,"Blend: Using fast blend function"); - } - } else { - fptr_blend = &std_blend; - Debug(2,"Blend: Using standard blend function"); - } - - __attribute__((aligned(16))) uint8_t blend1[16] = {142,255,159,91,88,227,0,52,37,80,152,97,104,252,90,82}; - __attribute__((aligned(16))) uint8_t blend2[16] = {129,56,136,96,119,149,94,29,96,176,1,144,230,203,111,172}; - __attribute__((aligned(16))) uint8_t blendres[16]; - __attribute__((aligned(16))) uint8_t blendexp[16] = {141,231,157,92,91,217,11,49,45,92,133,103,119,246,92,93}; /* Expected results for 12.5% blend */ - - (*fptr_blend)(blend1,blend2,blendres,16,12.5); - - /* Compare results with expected results */ - for(int i=0;i<16;i++) { - if(abs(blendexp[i] - blendres[i]) > 3) { - Panic("Blend function failed self-test: Results differ from the expected results"); - } - } - - fptr_delta8_rgb = &std_delta8_rgb; - fptr_delta8_bgr = &std_delta8_bgr; - - /* Assign the delta functions */ - if(config.cpu_extensions) { - if(sseversion >= 35) { - /* SSSE3 available */ - fptr_delta8_rgba = &ssse3_delta8_rgba; - fptr_delta8_bgra = &ssse3_delta8_bgra; - fptr_delta8_argb = &ssse3_delta8_argb; - fptr_delta8_abgr = &ssse3_delta8_abgr; - fptr_delta8_gray8 = &sse2_delta8_gray8; - Debug(2,"Delta: Using SSSE3 delta functions"); - } else if(sseversion >= 20) { - /* SSE2 available */ - fptr_delta8_rgba = &sse2_delta8_rgba; - fptr_delta8_bgra = &sse2_delta8_bgra; - fptr_delta8_argb = &sse2_delta8_argb; - fptr_delta8_abgr = &sse2_delta8_abgr; - /* - ** On some systems, the 4 SSE2 algorithms above might be a little slower than - ** the standard algorithms, especially on early Pentium 4 processors. - ** In that case, comment out the 4 lines above and uncomment the 4 lines below - */ - // fptr_delta8_rgba = &std_delta8_rgba; - // fptr_delta8_bgra = &std_delta8_bgra; - // fptr_delta8_argb = &std_delta8_argb; - // fptr_delta8_abgr = &std_delta8_abgr; - fptr_delta8_gray8 = &sse2_delta8_gray8; - Debug(2,"Delta: Using SSE2 delta functions"); - } else { - /* No suitable SSE version available */ - fptr_delta8_rgba = &std_delta8_rgba; - fptr_delta8_bgra = &std_delta8_bgra; - fptr_delta8_argb = &std_delta8_argb; - fptr_delta8_abgr = &std_delta8_abgr; - fptr_delta8_gray8 = &std_delta8_gray8; - Debug(2,"Delta: Using standard delta functions"); - } - } else { - /* CPU extensions disabled */ - fptr_delta8_rgba = &std_delta8_rgba; - fptr_delta8_bgra = &std_delta8_bgra; - fptr_delta8_argb = &std_delta8_argb; - fptr_delta8_abgr = &std_delta8_abgr; - fptr_delta8_gray8 = &std_delta8_gray8; - Debug(2,"Delta: CPU extensions disabled, using standard delta functions"); - } - - /* Use SSSE3 deinterlace functions? */ - if(config.cpu_extensions && sseversion >= 35) { - fptr_deinterlace_4field_rgba = &ssse3_deinterlace_4field_rgba; - fptr_deinterlace_4field_bgra = &ssse3_deinterlace_4field_bgra; - fptr_deinterlace_4field_argb = &ssse3_deinterlace_4field_argb; - fptr_deinterlace_4field_abgr = &ssse3_deinterlace_4field_abgr; - fptr_deinterlace_4field_gray8 = &ssse3_deinterlace_4field_gray8; - Debug(2,"Deinterlace: Using SSSE3 delta functions"); - } else { - fptr_deinterlace_4field_rgba = &std_deinterlace_4field_rgba; - fptr_deinterlace_4field_bgra = &std_deinterlace_4field_bgra; - fptr_deinterlace_4field_argb = &std_deinterlace_4field_argb; - fptr_deinterlace_4field_abgr = &std_deinterlace_4field_abgr; - fptr_deinterlace_4field_gray8 = &std_deinterlace_4field_gray8; - Debug(2,"Deinterlace: Using standard delta functions"); - } - - /* Use SSE2 aligned memory copy? */ - if(config.cpu_extensions && sseversion >= 20) { - fptr_imgbufcpy = &sse2_aligned_memcpy; - Debug(2,"Image buffer copy: Using SSE2 aligned memcpy"); - } else { - fptr_imgbufcpy = &memcpy; - Debug(2,"Image buffer copy: Using standard memcpy"); - } - - /* Code below relocated from zm_local_camera */ - Debug( 3, "Setting up static colour tables" ); - - y_table = new unsigned char[256]; - for ( int i = 0; i <= 255; i++ ) - { - unsigned char c = i; - if ( c <= 16 ) - y_table[c] = 0; - else if ( c >= 235 ) - y_table[c] = 255; - else - y_table[c] = (255*(c-16))/219; - } + /* Assign the blend pointer to function */ + if(config.fast_image_blends) { + if(config.cpu_extensions && sseversion >= 20) { + fptr_blend = &sse2_fastblend; /* SSE2 fast blend */ + Debug(2,"Blend: Using SSE2 fast blend function"); + } else { + fptr_blend = &std_fastblend; /* standard fast blend */ + Debug(2,"Blend: Using fast blend function"); + } + } else { + fptr_blend = &std_blend; + Debug(2,"Blend: Using standard blend function"); + } + + __attribute__((aligned(16))) uint8_t blend1[16] = {142,255,159,91,88,227,0,52,37,80,152,97,104,252,90,82}; + __attribute__((aligned(16))) uint8_t blend2[16] = {129,56,136,96,119,149,94,29,96,176,1,144,230,203,111,172}; + __attribute__((aligned(16))) uint8_t blendres[16]; + __attribute__((aligned(16))) uint8_t blendexp[16] = {141,231,157,92,91,217,11,49,45,92,133,103,119,246,92,93}; /* Expected results for 12.5% blend */ + + (*fptr_blend)(blend1,blend2,blendres,16,12.5); + + /* Compare results with expected results */ + for(int i=0;i<16;i++) { + if(abs(blendexp[i] - blendres[i]) > 3) { + Panic("Blend function failed self-test: Results differ from the expected results"); + } + } + + fptr_delta8_rgb = &std_delta8_rgb; + fptr_delta8_bgr = &std_delta8_bgr; + + /* Assign the delta functions */ + if(config.cpu_extensions) { + if(sseversion >= 35) { + /* SSSE3 available */ + fptr_delta8_rgba = &ssse3_delta8_rgba; + fptr_delta8_bgra = &ssse3_delta8_bgra; + fptr_delta8_argb = &ssse3_delta8_argb; + fptr_delta8_abgr = &ssse3_delta8_abgr; + fptr_delta8_gray8 = &sse2_delta8_gray8; + Debug(2,"Delta: Using SSSE3 delta functions"); + } else if(sseversion >= 20) { + /* SSE2 available */ + fptr_delta8_rgba = &sse2_delta8_rgba; + fptr_delta8_bgra = &sse2_delta8_bgra; + fptr_delta8_argb = &sse2_delta8_argb; + fptr_delta8_abgr = &sse2_delta8_abgr; + /* + ** On some systems, the 4 SSE2 algorithms above might be a little slower than + ** the standard algorithms, especially on early Pentium 4 processors. + ** In that case, comment out the 4 lines above and uncomment the 4 lines below + */ + // fptr_delta8_rgba = &std_delta8_rgba; + // fptr_delta8_bgra = &std_delta8_bgra; + // fptr_delta8_argb = &std_delta8_argb; + // fptr_delta8_abgr = &std_delta8_abgr; + fptr_delta8_gray8 = &sse2_delta8_gray8; + Debug(2,"Delta: Using SSE2 delta functions"); + } else { + /* No suitable SSE version available */ + fptr_delta8_rgba = &std_delta8_rgba; + fptr_delta8_bgra = &std_delta8_bgra; + fptr_delta8_argb = &std_delta8_argb; + fptr_delta8_abgr = &std_delta8_abgr; + fptr_delta8_gray8 = &std_delta8_gray8; + Debug(2,"Delta: Using standard delta functions"); + } + } else { + /* CPU extensions disabled */ + fptr_delta8_rgba = &std_delta8_rgba; + fptr_delta8_bgra = &std_delta8_bgra; + fptr_delta8_argb = &std_delta8_argb; + fptr_delta8_abgr = &std_delta8_abgr; + fptr_delta8_gray8 = &std_delta8_gray8; + Debug(2,"Delta: CPU extensions disabled, using standard delta functions"); + } + + /* Use SSSE3 deinterlace functions? */ + if(config.cpu_extensions && sseversion >= 35) { + fptr_deinterlace_4field_rgba = &ssse3_deinterlace_4field_rgba; + fptr_deinterlace_4field_bgra = &ssse3_deinterlace_4field_bgra; + fptr_deinterlace_4field_argb = &ssse3_deinterlace_4field_argb; + fptr_deinterlace_4field_abgr = &ssse3_deinterlace_4field_abgr; + fptr_deinterlace_4field_gray8 = &ssse3_deinterlace_4field_gray8; + Debug(2,"Deinterlace: Using SSSE3 delta functions"); + } else { + fptr_deinterlace_4field_rgba = &std_deinterlace_4field_rgba; + fptr_deinterlace_4field_bgra = &std_deinterlace_4field_bgra; + fptr_deinterlace_4field_argb = &std_deinterlace_4field_argb; + fptr_deinterlace_4field_abgr = &std_deinterlace_4field_abgr; + fptr_deinterlace_4field_gray8 = &std_deinterlace_4field_gray8; + Debug(2,"Deinterlace: Using standard delta functions"); + } + + /* Use SSE2 aligned memory copy? */ + if(config.cpu_extensions && sseversion >= 20) { + fptr_imgbufcpy = &sse2_aligned_memcpy; + Debug(2,"Image buffer copy: Using SSE2 aligned memcpy"); + } else { + fptr_imgbufcpy = &memcpy; + Debug(2,"Image buffer copy: Using standard memcpy"); + } + + /* Code below relocated from zm_local_camera */ + Debug( 3, "Setting up static colour tables" ); + + y_table = new unsigned char[256]; + for ( int i = 0; i <= 255; i++ ) + { + unsigned char c = i; + if ( c <= 16 ) + y_table[c] = 0; + else if ( c >= 235 ) + y_table[c] = 255; + else + y_table[c] = (255*(c-16))/219; + } - uv_table = new signed char[256]; - for ( int i = 0; i <= 255; i++ ) - { - unsigned char c = i; - if ( c <= 16 ) - uv_table[c] = -127; - else if ( c >= 240 ) - uv_table[c] = 127; - else - uv_table[c] = (127*(c-128))/112; - } + uv_table = new signed char[256]; + for ( int i = 0; i <= 255; i++ ) + { + unsigned char c = i; + if ( c <= 16 ) + uv_table[c] = -127; + else if ( c >= 240 ) + uv_table[c] = 127; + else + uv_table[c] = (127*(c-128))/112; + } - r_v_table = new short[255]; - g_v_table = new short[255]; - g_u_table = new short[255]; - b_u_table = new short[255]; - for ( int i = 0; i < 255; i++ ) - { - r_v_table[i] = (1402*(i-128))/1000; - g_u_table[i] = (344*(i-128))/1000; - g_v_table[i] = (714*(i-128))/1000; - b_u_table[i] = (1772*(i-128))/1000; - } - - initialised = true; + r_v_table = new short[255]; + g_v_table = new short[255]; + g_u_table = new short[255]; + b_u_table = new short[255]; + for ( int i = 0; i < 255; i++ ) + { + r_v_table[i] = (1402*(i-128))/1000; + g_u_table[i] = (344*(i-128))/1000; + g_v_table[i] = (714*(i-128))/1000; + b_u_table[i] = (1772*(i-128))/1000; + } + + initialised = true; } -/* Requests a writeable buffer to the image. This is safer than buffer() because this way we can gurantee that a buffer of required size exists */ +/* Requests a writeable buffer to the image. This is safer than buffer() because this way we can guarantee that a buffer of required size exists */ uint8_t* Image::WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder) { - unsigned int newsize; + unsigned int newsize; - if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) { - Error("WriteBuffer called with unexpected colours: %d",p_colours); - return NULL; - } - - if(!p_height || !p_width) { - Error("WriteBuffer called with invaid width or height: %d %d",p_width,p_height); - return NULL; - } - - if(p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) { - newsize = (p_width * p_height) * p_colours; - - if(buffer == NULL) { - AllocImgBuffer(newsize); - } else { - if(allocation < newsize) { - if(holdbuffer) { - Error("Held buffer is undersized for requested buffer"); - return NULL; - } else { - /* Replace buffer with a bigger one */ - DumpImgBuffer(); - AllocImgBuffer(newsize); - } - } - } - - width = p_width; - height = p_height; - colours = p_colours; - subpixelorder = p_subpixelorder; - pixels = height*width; - size = newsize; - } - - return buffer; + if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) { + Error("WriteBuffer called with unexpected colours: %d",p_colours); + return NULL; + } + + if(!p_height || !p_width) { + Error("WriteBuffer called with invaid width or height: %d %d",p_width,p_height); + return NULL; + } + + if(p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) { + newsize = (p_width * p_height) * p_colours; + + if(buffer == NULL) { + AllocImgBuffer(newsize); + } else { + if(allocation < newsize) { + if(holdbuffer) { + Error("Held buffer is undersized for requested buffer"); + return NULL; + } else { + /* Replace buffer with a bigger one */ + //DumpImgBuffer(); // Done in AllocImgBuffer too + AllocImgBuffer(newsize); + } + } + } + + width = p_width; + height = p_height; + colours = p_colours; + subpixelorder = p_subpixelorder; + pixels = height*width; + size = newsize; + } + + return buffer; } /* Assign an existing buffer to the image instead of copying from a source buffer. The goal is to reduce the amount of memory copying and increase efficiency and buffer reusing. */ void Image::AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype) { - if(new_buffer == NULL) { - Error("Attempt to directly assign buffer from a NULL pointer"); - return; - } - - if(buffer_size < (unsigned int)((p_width*p_height)*p_colours)) { - Error("Attempt to directly assign buffer from an undersized buffer of size: %zu",buffer_size); - return; - } - - if(!p_height || !p_width) { - Error("Attempt to directly assign buffer with invalid width or height: %d %d",p_width,p_height); - return; - } - - if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) { - Error("Attempt to directly assign buffer with unexpected colours per pixel: %d",p_colours); - return; - } - - if(holdbuffer && buffer) { - if((unsigned int)((p_height*p_width)*p_colours) > allocation) { - Error("Held buffer is undersized for assigned buffer"); - return; - } else { - width = p_width; - height = p_height; - colours = p_colours; - subpixelorder = p_subpixelorder; - pixels = height*width; - size = pixels*colours; - - /* Copy into the held buffer */ - if(new_buffer != buffer) - (*fptr_imgbufcpy)(buffer, new_buffer, size); - - /* Free the new buffer */ - DumpBuffer(new_buffer, p_buffertype); - } - } else { - /* Free an existing buffer if any */ - DumpImgBuffer(); - - width = p_width; - height = p_height; - colours = p_colours; - subpixelorder = p_subpixelorder; - pixels = height*width; - size = pixels*colours; - - allocation = buffer_size; - buffertype = p_buffertype; - buffer = new_buffer; - } - + if(new_buffer == NULL) { + Error("Attempt to directly assign buffer from a NULL pointer"); + return; + } + + if(!p_height || !p_width) { + Error("Attempt to directly assign buffer with invalid width or height: %d %d",p_width,p_height); + return; + } + + if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) { + Error("Attempt to directly assign buffer with unexpected colours per pixel: %d",p_colours); + return; + } + + unsigned int new_buffer_size = ((p_width*p_height)*p_colours); + + if(buffer_size < new_buffer_size) { + Error("Attempt to directly assign buffer from an undersized buffer of size: %zu, needed %dx%d*%d colours = %zu",buffer_size, p_width, p_height, p_colours, new_buffer_size ); + return; + } + + if(holdbuffer && buffer) { + if(new_buffer_size > allocation) { + Error("Held buffer is undersized for assigned buffer"); + return; + } else { + width = p_width; + height = p_height; + colours = p_colours; + subpixelorder = p_subpixelorder; + pixels = height*width; + size = new_buffer_size; // was pixels*colours, but we already calculated it above as new_buffer_size + + /* Copy into the held buffer */ + if(new_buffer != buffer) + (*fptr_imgbufcpy)(buffer, new_buffer, size); + + /* Free the new buffer */ + DumpBuffer(new_buffer, p_buffertype); + } + } else { + /* Free an existing buffer if any */ + DumpImgBuffer(); + + width = p_width; + height = p_height; + colours = p_colours; + subpixelorder = p_subpixelorder; + pixels = height*width; + size = new_buffer_size; // was pixels*colours, but we already calculated it above as new_buffer_size + + allocation = buffer_size; + buffertype = p_buffertype; + buffer = new_buffer; + } + } void Image::Assign(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size) { - unsigned int new_size = (p_width * p_height) * p_colours; + unsigned int new_size = (p_width * p_height) * p_colours; - if(new_buffer == NULL) { - Error("Attempt to assign buffer from a NULL pointer"); - return; - } - - if(buffer_size < new_size) { - Error("Attempt to assign buffer from an undersized buffer of size: %zu",buffer_size); - return; - } - - if(!p_height || !p_width) { - Error("Attempt to assign buffer with invalid width or height: %d %d",p_width,p_height); - return; - } - - if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) { - Error("Attempt to assign buffer with unexpected colours per pixel: %d",p_colours); - return; - } - - if ( !buffer || p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) { + if(new_buffer == NULL) { + Error("Attempt to assign buffer from a NULL pointer"); + return; + } + + if(buffer_size < new_size) { + Error("Attempt to assign buffer from an undersized buffer of size: %zu",buffer_size); + return; + } + + if(!p_height || !p_width) { + Error("Attempt to assign buffer with invalid width or height: %d %d",p_width,p_height); + return; + } + + if(p_colours != ZM_COLOUR_GRAY8 && p_colours != ZM_COLOUR_RGB24 && p_colours != ZM_COLOUR_RGB32) { + Error("Attempt to assign buffer with unexpected colours per pixel: %d",p_colours); + return; + } + + if ( !buffer || p_width != width || p_height != height || p_colours != colours || p_subpixelorder != subpixelorder) { - if (holdbuffer && buffer) { - if (new_size > allocation) { - Error("Held buffer is undersized for assigned buffer"); - return; - } - } else { - if(new_size > allocation || !buffer) { - DumpImgBuffer(); - AllocImgBuffer(new_size); - } - } - - width = p_width; - height = p_height; - pixels = width*height; - colours = p_colours; - subpixelorder = p_subpixelorder; - size = new_size; - } - - if(new_buffer != buffer) - (*fptr_imgbufcpy)(buffer, new_buffer, size); - + if (holdbuffer && buffer) { + if (new_size > allocation) { + Error("Held buffer is undersized for assigned buffer"); + return; + } + } else { + if(new_size > allocation || !buffer) { + DumpImgBuffer(); + AllocImgBuffer(new_size); + } + } + + width = p_width; + height = p_height; + pixels = width*height; + colours = p_colours; + subpixelorder = p_subpixelorder; + size = new_size; + } + + if(new_buffer != buffer) + (*fptr_imgbufcpy)(buffer, new_buffer, size); + } void Image::Assign( const Image &image ) { - unsigned int new_size = (image.width * image.height) * image.colours; - - if(image.buffer == NULL) { - Error("Attempt to assign image with an empty buffer"); - return; - } + unsigned int new_size = (image.width * image.height) * image.colours; - if(image.colours != ZM_COLOUR_GRAY8 && image.colours != ZM_COLOUR_RGB24 && image.colours != ZM_COLOUR_RGB32) { - Error("Attempt to assign image with unexpected colours per pixel: %d",image.colours); - return; - } - - if ( !buffer || image.width != width || image.height != height || image.colours != colours || image.subpixelorder != subpixelorder) { + if(image.buffer == NULL) { + Error("Attempt to assign image with an empty buffer"); + return; + } + + if(image.colours != ZM_COLOUR_GRAY8 && image.colours != ZM_COLOUR_RGB24 && image.colours != ZM_COLOUR_RGB32) { + Error("Attempt to assign image with unexpected colours per pixel: %d",image.colours); + return; + } + + if ( !buffer || image.width != width || image.height != height || image.colours != colours || image.subpixelorder != subpixelorder) { - if (holdbuffer && buffer) { - if (new_size > allocation) { - Error("Held buffer is undersized for assigned buffer"); - return; - } - } else { - if(new_size > allocation || !buffer) { - DumpImgBuffer(); - AllocImgBuffer(new_size); - } - } - - width = image.width; - height = image.height; - pixels = width*height; - colours = image.colours; - subpixelorder = image.subpixelorder; - size = new_size; - } - - if(image.buffer != buffer) - (*fptr_imgbufcpy)(buffer, image.buffer, size); + if (holdbuffer && buffer) { + if (new_size > allocation) { + Error("Held buffer is undersized for assigned buffer"); + return; + } + } else { + if(new_size > allocation || !buffer) { + // DumpImgBuffer(); This is also done in AllocImgBuffer + AllocImgBuffer(new_size); + } + } + + width = image.width; + height = image.height; + pixels = width*height; + colours = image.colours; + subpixelorder = image.subpixelorder; + size = new_size; + } + + if(image.buffer != buffer) + (*fptr_imgbufcpy)(buffer, image.buffer, size); } Image *Image::HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits ) { - if ( colours != ZM_COLOUR_GRAY8 ) - { - Panic( "Attempt to highlight image edges when colours = %d", colours ); - } - - /* Convert the colour's RGBA subpixel order into the image's subpixel order */ - colour = rgb_convert(colour,p_subpixelorder); - - /* Create a new image of the target format */ - Image *high_image = new Image( width, height, p_colours, p_subpixelorder ); - uint8_t* high_buff = high_image->WriteBuffer(width, height, p_colours, p_subpixelorder); - - /* Set image to all black */ - high_image->Clear(); + if ( colours != ZM_COLOUR_GRAY8 ) + { + Panic( "Attempt to highlight image edges when colours = %d", colours ); + } + + /* Convert the colour's RGBA subpixel order into the image's subpixel order */ + colour = rgb_convert(colour,p_subpixelorder); + + /* Create a new image of the target format */ + Image *high_image = new Image( width, height, p_colours, p_subpixelorder ); + uint8_t* high_buff = high_image->WriteBuffer(width, height, p_colours, p_subpixelorder); + + /* Set image to all black */ + high_image->Clear(); - unsigned int lo_x = limits?limits->Lo().X():0; - unsigned int lo_y = limits?limits->Lo().Y():0; - unsigned int hi_x = limits?limits->Hi().X():width-1; - unsigned int hi_y = limits?limits->Hi().Y():height-1; - - if ( p_colours == ZM_COLOUR_GRAY8 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - const uint8_t* p = buffer + (y * width) + lo_x; - uint8_t* phigh = high_buff + (y * width) + lo_x; - for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) - { - bool edge = false; - if ( *p ) - { - if ( !edge && x > 0 && !*(p-1) ) edge = true; - if ( !edge && x < (width-1) && !*(p+1) ) edge = true; - if ( !edge && y > 0 && !*(p-width) ) edge = true; - if ( !edge && y < (height-1) && !*(p+width) ) edge = true; - } - if ( edge ) - { - *phigh = colour; - } - } - } - } - else if ( p_colours == ZM_COLOUR_RGB24 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - const uint8_t* p = buffer + (y * width) + lo_x; - uint8_t* phigh = high_buff + (((y * width) + lo_x) * 3); - for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh += 3 ) - { - bool edge = false; - if ( *p ) - { - if ( !edge && x > 0 && !*(p-1) ) edge = true; - if ( !edge && x < (width-1) && !*(p+1) ) edge = true; - if ( !edge && y > 0 && !*(p-width) ) edge = true; - if ( !edge && y < (height-1) && !*(p+width) ) edge = true; - } - if ( edge ) - { - RED_PTR_RGBA(phigh) = RED_VAL_RGBA(colour); - GREEN_PTR_RGBA(phigh) = GREEN_VAL_RGBA(colour); - BLUE_PTR_RGBA(phigh) = BLUE_VAL_RGBA(colour); - } - } - } - } - else if ( p_colours == ZM_COLOUR_RGB32 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - const uint8_t* p = buffer + (y * width) + lo_x; - Rgb* phigh = (Rgb*)(high_buff + (((y * width) + lo_x) * 4)); - for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) - { - bool edge = false; - if ( *p ) - { - if ( !edge && x > 0 && !*(p-1) ) edge = true; - if ( !edge && x < (width-1) && !*(p+1) ) edge = true; - if ( !edge && y > 0 && !*(p-width) ) edge = true; - if ( !edge && y < (height-1) && !*(p+width) ) edge = true; - } - if ( edge ) - { - *phigh = colour; - } - } - } - } - - return( high_image ); + unsigned int lo_x = limits?limits->Lo().X():0; + unsigned int lo_y = limits?limits->Lo().Y():0; + unsigned int hi_x = limits?limits->Hi().X():width-1; + unsigned int hi_y = limits?limits->Hi().Y():height-1; + + if ( p_colours == ZM_COLOUR_GRAY8 ) + { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + const uint8_t* p = buffer + (y * width) + lo_x; + uint8_t* phigh = high_buff + (y * width) + lo_x; + for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) + { + bool edge = false; + if ( *p ) + { + if ( !edge && x > 0 && !*(p-1) ) edge = true; + if ( !edge && x < (width-1) && !*(p+1) ) edge = true; + if ( !edge && y > 0 && !*(p-width) ) edge = true; + if ( !edge && y < (height-1) && !*(p+width) ) edge = true; + } + if ( edge ) + { + *phigh = colour; + } + } + } + } + else if ( p_colours == ZM_COLOUR_RGB24 ) + { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + const uint8_t* p = buffer + (y * width) + lo_x; + uint8_t* phigh = high_buff + (((y * width) + lo_x) * 3); + for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh += 3 ) + { + bool edge = false; + if ( *p ) + { + if ( !edge && x > 0 && !*(p-1) ) edge = true; + if ( !edge && x < (width-1) && !*(p+1) ) edge = true; + if ( !edge && y > 0 && !*(p-width) ) edge = true; + if ( !edge && y < (height-1) && !*(p+width) ) edge = true; + } + if ( edge ) + { + RED_PTR_RGBA(phigh) = RED_VAL_RGBA(colour); + GREEN_PTR_RGBA(phigh) = GREEN_VAL_RGBA(colour); + BLUE_PTR_RGBA(phigh) = BLUE_VAL_RGBA(colour); + } + } + } + } + else if ( p_colours == ZM_COLOUR_RGB32 ) + { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + const uint8_t* p = buffer + (y * width) + lo_x; + Rgb* phigh = (Rgb*)(high_buff + (((y * width) + lo_x) * 4)); + for ( unsigned int x = lo_x; x <= hi_x; x++, p++, phigh++ ) + { + bool edge = false; + if ( *p ) + { + if ( !edge && x > 0 && !*(p-1) ) edge = true; + if ( !edge && x < (width-1) && !*(p+1) ) edge = true; + if ( !edge && y > 0 && !*(p-width) ) edge = true; + if ( !edge && y < (height-1) && !*(p+width) ) edge = true; + } + if ( edge ) + { + *phigh = colour; + } + } + } + } + + return( high_image ); } bool Image::ReadRaw( const char *filename ) { - FILE *infile; - if ( (infile = fopen( filename, "rb" )) == NULL ) - { - Error( "Can't open %s: %s", filename, strerror(errno) ); - return( false ); - } + FILE *infile; + if ( (infile = fopen( filename, "rb" )) == NULL ) + { + Error( "Can't open %s: %s", filename, strerror(errno) ); + return( false ); + } - struct stat statbuf; - if ( fstat( fileno(infile), &statbuf ) < 0 ) - { - Error( "Can't fstat %s: %s", filename, strerror(errno) ); - return( false ); - } + struct stat statbuf; + if ( fstat( fileno(infile), &statbuf ) < 0 ) + { + Error( "Can't fstat %s: %s", filename, strerror(errno) ); + return( false ); + } - if ( statbuf.st_size != size ) - { - Error( "Raw file size mismatch, expected %d bytes, found %ld", size, statbuf.st_size ); - return( false ); - } + if ( statbuf.st_size != size ) + { + Error( "Raw file size mismatch, expected %d bytes, found %ld", size, statbuf.st_size ); + return( false ); + } - if ( fread( buffer, size, 1, infile ) < 1 ) - { - Fatal( "Unable to read from '%s': %s", filename, strerror(errno) ); - return( false ); - } + if ( fread( buffer, size, 1, infile ) < 1 ) + { + Fatal( "Unable to read from '%s': %s", filename, strerror(errno) ); + return( false ); + } - fclose( infile ); + fclose( infile ); - return( true ); + return( true ); } bool Image::WriteRaw( const char *filename ) const { - FILE *outfile; - if ( (outfile = fopen( filename, "wb" )) == NULL ) - { - Error( "Can't open %s: %s", filename, strerror(errno) ); - return( false ); - } + FILE *outfile; + if ( (outfile = fopen( filename, "wb" )) == NULL ) + { + Error( "Can't open %s: %s", filename, strerror(errno) ); + return( false ); + } - if ( fwrite( buffer, size, 1, outfile ) != 1 ) - { - Error( "Unable to write to '%s': %s", filename, strerror(errno) ); - return( false ); - } + if ( fwrite( buffer, size, 1, outfile ) != 1 ) + { + Error( "Unable to write to '%s': %s", filename, strerror(errno) ); + return( false ); + } - fclose( outfile ); + fclose( outfile ); - return( true ); + return( true ); } bool Image::ReadJpeg( const char *filename, unsigned int p_colours, unsigned int p_subpixelorder) { - unsigned int new_width, new_height, new_colours, new_subpixelorder; - struct jpeg_decompress_struct *cinfo = jpg_dcinfo; + unsigned int new_width, new_height, new_colours, new_subpixelorder; + struct jpeg_decompress_struct *cinfo = jpg_dcinfo; - if ( !cinfo ) - { - cinfo = jpg_dcinfo = new jpeg_decompress_struct; - cinfo->err = jpeg_std_error( &jpg_err.pub ); - jpg_err.pub.error_exit = zm_jpeg_error_exit; - jpg_err.pub.emit_message = zm_jpeg_emit_message; - jpeg_create_decompress( cinfo ); - } + if ( !cinfo ) + { + cinfo = jpg_dcinfo = new jpeg_decompress_struct; + cinfo->err = jpeg_std_error( &jpg_err.pub ); + jpg_err.pub.error_exit = zm_jpeg_error_exit; + jpg_err.pub.emit_message = zm_jpeg_emit_message; + jpeg_create_decompress( cinfo ); + } - FILE *infile; - if ( (infile = fopen( filename, "rb" )) == NULL ) - { - Error( "Can't open %s: %s", filename, strerror(errno) ); - return( false ); - } + FILE *infile; + if ( (infile = fopen( filename, "rb" )) == NULL ) + { + Error( "Can't open %s: %s", filename, strerror(errno) ); + return( false ); + } - if ( setjmp( jpg_err.setjmp_buffer ) ) - { - jpeg_abort_decompress( cinfo ); - fclose( infile ); - return( false ); - } + if ( setjmp( jpg_err.setjmp_buffer ) ) + { + jpeg_abort_decompress( cinfo ); + fclose( infile ); + return( false ); + } - jpeg_stdio_src( cinfo, infile ); + jpeg_stdio_src( cinfo, infile ); - jpeg_read_header( cinfo, TRUE ); + jpeg_read_header( cinfo, TRUE ); - if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) - { - Error( "Unexpected colours when reading jpeg image: %d", colours ); - jpeg_abort_decompress( cinfo ); - fclose( infile ); - return( false ); - } - - /* Check if the image has at least one huffman table defined. If not, use the standard ones */ - /* This is required for the MJPEG capture palette of USB devices */ - if(cinfo->dc_huff_tbl_ptrs[0] == NULL) { - zm_use_std_huff_tables(cinfo); - } + if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) + { + Error( "Unexpected colours when reading jpeg image: %d", colours ); + jpeg_abort_decompress( cinfo ); + fclose( infile ); + return( false ); + } + + /* Check if the image has at least one huffman table defined. If not, use the standard ones */ + /* This is required for the MJPEG capture palette of USB devices */ + if(cinfo->dc_huff_tbl_ptrs[0] == NULL) { + zm_use_std_huff_tables(cinfo); + } - new_width = cinfo->image_width; - new_height = cinfo->image_height; + new_width = cinfo->image_width; + new_height = cinfo->image_height; - if ( width != new_width || height != new_height ) - { - Debug(9,"Image dimensions differ. Old: %ux%u New: %ux%u",width,height,new_width,new_height); - } - - switch(p_colours) { - case ZM_COLOUR_GRAY8: - { - cinfo->out_color_space = JCS_GRAYSCALE; - new_colours = ZM_COLOUR_GRAY8; - new_subpixelorder = ZM_SUBPIX_ORDER_NONE; - break; - } - case ZM_COLOUR_RGB32: - { + if ( width != new_width || height != new_height ) + { + Debug(9,"Image dimensions differ. Old: %ux%u New: %ux%u",width,height,new_width,new_height); + } + + switch(p_colours) { + case ZM_COLOUR_GRAY8: + { + cinfo->out_color_space = JCS_GRAYSCALE; + new_colours = ZM_COLOUR_GRAY8; + new_subpixelorder = ZM_SUBPIX_ORDER_NONE; + break; + } + case ZM_COLOUR_RGB32: + { #ifdef JCS_EXTENSIONS - new_colours = ZM_COLOUR_RGB32; - if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - cinfo->out_color_space = JCS_EXT_BGRX; - new_subpixelorder = ZM_SUBPIX_ORDER_BGRA; - } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - cinfo->out_color_space = JCS_EXT_XRGB; - new_subpixelorder = ZM_SUBPIX_ORDER_ARGB; - } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - cinfo->out_color_space = JCS_EXT_XBGR; - new_subpixelorder = ZM_SUBPIX_ORDER_ABGR; - } else { - /* Assume RGBA */ - cinfo->out_color_space = JCS_EXT_RGBX; - new_subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } - break; + new_colours = ZM_COLOUR_RGB32; + if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + cinfo->out_color_space = JCS_EXT_BGRX; + new_subpixelorder = ZM_SUBPIX_ORDER_BGRA; + } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + cinfo->out_color_space = JCS_EXT_XRGB; + new_subpixelorder = ZM_SUBPIX_ORDER_ARGB; + } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + cinfo->out_color_space = JCS_EXT_XBGR; + new_subpixelorder = ZM_SUBPIX_ORDER_ABGR; + } else { + /* Assume RGBA */ + cinfo->out_color_space = JCS_EXT_RGBX; + new_subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } + break; #else - Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead."); + Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead."); #endif - } - case ZM_COLOUR_RGB24: - default: - { - new_colours = ZM_COLOUR_RGB24; - if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { -#ifdef JCS_EXTENSIONS - cinfo->out_color_space = JCS_EXT_BGR; - new_subpixelorder = ZM_SUBPIX_ORDER_BGR; + } + case ZM_COLOUR_RGB24: + default: + { + new_colours = ZM_COLOUR_RGB24; + if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { +#ifdef JCS_EXTENSIONS + cinfo->out_color_space = JCS_EXT_BGR; + new_subpixelorder = ZM_SUBPIX_ORDER_BGR; #else - Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead."); - cinfo->out_color_space = JCS_RGB; - new_subpixelorder = ZM_SUBPIX_ORDER_RGB; + Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead."); + cinfo->out_color_space = JCS_RGB; + new_subpixelorder = ZM_SUBPIX_ORDER_RGB; #endif - } else { - /* Assume RGB */ + } else { + /* Assume RGB */ /* #ifdef JCS_EXTENSIONS - cinfo->out_color_space = JCS_EXT_RGB; + cinfo->out_color_space = JCS_EXT_RGB; #else - cinfo->out_color_space = JCS_RGB; + cinfo->out_color_space = JCS_RGB; #endif */ - cinfo->out_color_space = JCS_RGB; - new_subpixelorder = ZM_SUBPIX_ORDER_RGB; - } - break; - } - } - - if(WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == NULL) { - Error("Failed requesting writeable buffer for reading JPEG image."); - jpeg_abort_decompress( cinfo ); - fclose( infile ); - return( false ); - } + cinfo->out_color_space = JCS_RGB; + new_subpixelorder = ZM_SUBPIX_ORDER_RGB; + } + break; + } + } + + if(WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == NULL) { + Error("Failed requesting writeable buffer for reading JPEG image."); + jpeg_abort_decompress( cinfo ); + fclose( infile ); + return( false ); + } - jpeg_start_decompress( cinfo ); + jpeg_start_decompress( cinfo ); - JSAMPROW row_pointer; /* pointer to a single row */ - int row_stride = width * colours; /* physical row width in buffer */ - while ( cinfo->output_scanline < cinfo->output_height ) - { - row_pointer = &buffer[cinfo->output_scanline * row_stride]; - jpeg_read_scanlines( cinfo, &row_pointer, 1 ); - } + JSAMPROW row_pointer; /* pointer to a single row */ + int row_stride = width * colours; /* physical row width in buffer */ + while ( cinfo->output_scanline < cinfo->output_height ) + { + row_pointer = &buffer[cinfo->output_scanline * row_stride]; + jpeg_read_scanlines( cinfo, &row_pointer, 1 ); + } - jpeg_finish_decompress( cinfo ); + jpeg_finish_decompress( cinfo ); - fclose( infile ); + fclose( infile ); - return( true ); + return( true ); } -bool Image::WriteJpeg( const char *filename, int quality_override ) const +// Multiple calling formats to permit inclusion (or not) of both quality_override and timestamp (exif), with suitable defaults. +// Note quality=zero means default + +bool Image::WriteJpeg( const char *filename, int quality_override) const { - if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) - { - Image temp_image( *this ); - temp_image.Colourise( ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); - return( temp_image.WriteJpeg( filename, quality_override ) ); - } + return Image::WriteJpeg(filename, quality_override, (timeval){0,0}); +} +bool Image::WriteJpeg( const char *filename) const +{ + return Image::WriteJpeg(filename, 0, (timeval){0,0}); +} +bool Image::WriteJpeg( const char *filename, struct timeval timestamp ) const +{ + return Image::WriteJpeg(filename,0,timestamp); +} - int quality = quality_override?quality_override:config.jpeg_file_quality; +bool Image::WriteJpeg( const char *filename, int quality_override, struct timeval timestamp ) const +{ + if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) + { + Image temp_image( *this ); + temp_image.Colourise( ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); + return( temp_image.WriteJpeg( filename, quality_override, timestamp) ); + } + int quality = quality_override?quality_override:config.jpeg_file_quality; - struct jpeg_compress_struct *cinfo = jpg_ccinfo[quality]; + struct jpeg_compress_struct *cinfo = jpg_ccinfo[quality]; - if ( !cinfo ) - { - cinfo = jpg_ccinfo[quality] = new jpeg_compress_struct; - cinfo->err = jpeg_std_error( &jpg_err.pub ); - jpg_err.pub.error_exit = zm_jpeg_error_exit; - jpg_err.pub.emit_message = zm_jpeg_emit_message; - jpeg_create_compress( cinfo ); - } + if ( !cinfo ) + { + cinfo = jpg_ccinfo[quality] = new jpeg_compress_struct; + cinfo->err = jpeg_std_error( &jpg_err.pub ); + jpg_err.pub.error_exit = zm_jpeg_error_exit; + jpg_err.pub.emit_message = zm_jpeg_emit_message; + jpeg_create_compress( cinfo ); + } - FILE *outfile; - if ( (outfile = fopen( filename, "wb" )) == NULL ) - { - Error( "Can't open %s: %s", filename, strerror(errno) ); - return( false ); - } - jpeg_stdio_dest( cinfo, outfile ); + FILE *outfile; + if ( (outfile = fopen( filename, "wb" )) == NULL ) + { + Error( "Can't open %s: %s", filename, strerror(errno) ); + return( false ); + } + jpeg_stdio_dest( cinfo, outfile ); - cinfo->image_width = width; /* image width and height, in pixels */ - cinfo->image_height = height; - - switch(colours) { - case ZM_COLOUR_GRAY8: - { - cinfo->input_components = 1; - cinfo->in_color_space = JCS_GRAYSCALE; - break; - } - case ZM_COLOUR_RGB32: - { + cinfo->image_width = width; /* image width and height, in pixels */ + cinfo->image_height = height; + + switch(colours) { + case ZM_COLOUR_GRAY8: + { + cinfo->input_components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; + break; + } + case ZM_COLOUR_RGB32: + { #ifdef JCS_EXTENSIONS - cinfo->input_components = 4; - if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - cinfo->in_color_space = JCS_EXT_BGRX; - } else if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - cinfo->in_color_space = JCS_EXT_XRGB; - } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - cinfo->in_color_space = JCS_EXT_XBGR; - } else { - /* Assume RGBA */ - cinfo->in_color_space = JCS_EXT_RGBX; - } + cinfo->input_components = 4; + if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + cinfo->in_color_space = JCS_EXT_BGRX; + } else if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + cinfo->in_color_space = JCS_EXT_XRGB; + } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + cinfo->in_color_space = JCS_EXT_XBGR; + } else { + /* Assume RGBA */ + cinfo->in_color_space = JCS_EXT_RGBX; + } #else - Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source"); - jpeg_abort_compress( cinfo ); - fclose(outfile); - return(false); + Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source"); + jpeg_abort_compress( cinfo ); + fclose(outfile); + return(false); #endif - break; - } - case ZM_COLOUR_RGB24: - default: - { - cinfo->input_components = 3; - if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { + break; + } + case ZM_COLOUR_RGB24: + default: + { + cinfo->input_components = 3; + if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { #ifdef JCS_EXTENSIONS - cinfo->in_color_space = JCS_EXT_BGR; + cinfo->in_color_space = JCS_EXT_BGR; #else - Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source"); - jpeg_abort_compress( cinfo ); - fclose(outfile); - return(false); + Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source"); + jpeg_abort_compress( cinfo ); + fclose(outfile); + return(false); #endif - } else { - /* Assume RGB */ + } else { + /* Assume RGB */ /* #ifdef JCS_EXTENSIONS - cinfo->out_color_space = JCS_EXT_RGB; + cinfo->out_color_space = JCS_EXT_RGB; #else - cinfo->out_color_space = JCS_RGB; + cinfo->out_color_space = JCS_RGB; #endif */ - cinfo->in_color_space = JCS_RGB; - } - break; - } - } - - jpeg_set_defaults( cinfo ); - jpeg_set_quality( cinfo, quality, FALSE ); - cinfo->dct_method = JDCT_FASTEST; + cinfo->in_color_space = JCS_RGB; + } + break; + } + } + + jpeg_set_defaults( cinfo ); + jpeg_set_quality( cinfo, quality, FALSE ); + cinfo->dct_method = JDCT_FASTEST; - jpeg_start_compress( cinfo, TRUE ); - if ( config.add_jpeg_comments && text[0] ) - { - jpeg_write_marker( cinfo, JPEG_COM, (const JOCTET *)text, strlen(text) ); - } + jpeg_start_compress( cinfo, TRUE ); + if ( config.add_jpeg_comments && text[0] ) + { + jpeg_write_marker( cinfo, JPEG_COM, (const JOCTET *)text, strlen(text) ); + } + // If we have a non-zero time (meaning a parameter was passed in), then form a simple exif segment with that time as DateTimeOriginal and SubsecTimeOriginal + // No timestamp just leave off the exif section. + if(timestamp.tv_sec) + { + #define EXIFTIMES_MS_OFFSET 0x36 // three decimal digits for milliseconds + #define EXIFTIMES_MS_LEN 0x03 + #define EXIFTIMES_OFFSET 0x3E // 19 characters format '2015:07:21 13:14:45' not including quotes + #define EXIFTIMES_LEN 0x13 // = 19 + #define EXIF_CODE 0xE1 - JSAMPROW row_pointer; /* pointer to a single row */ - int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ - while ( cinfo->next_scanline < cinfo->image_height ) - { - row_pointer = &buffer[cinfo->next_scanline * row_stride]; - jpeg_write_scanlines( cinfo, &row_pointer, 1 ); - } + char timebuf[64], msbuf[64]; + strftime(timebuf, sizeof timebuf, "%Y:%m:%d %H:%M:%S", localtime(&(timestamp.tv_sec))); + snprintf(msbuf, sizeof msbuf, "%06d",(int)(timestamp.tv_usec)); // we only use milliseconds because that's all defined in exif, but this is the whole microseconds because we have it + unsigned char exiftimes[82] = { + 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x69, 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x03, 0x90, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x91, 0x92, + 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00 }; + memcpy(&exiftimes[EXIFTIMES_OFFSET], timebuf,EXIFTIMES_LEN); + memcpy(&exiftimes[EXIFTIMES_MS_OFFSET], msbuf ,EXIFTIMES_MS_LEN); + jpeg_write_marker (cinfo, EXIF_CODE, (const JOCTET *)exiftimes, sizeof(exiftimes) ); + } - jpeg_finish_compress( cinfo ); + JSAMPROW row_pointer; /* pointer to a single row */ + int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ + while ( cinfo->next_scanline < cinfo->image_height ) + { + row_pointer = &buffer[cinfo->next_scanline * row_stride]; + jpeg_write_scanlines( cinfo, &row_pointer, 1 ); + } - fclose( outfile ); + jpeg_finish_compress( cinfo ); - return( true ); + fclose( outfile ); + + return( true ); } bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder) { - unsigned int new_width, new_height, new_colours, new_subpixelorder; - struct jpeg_decompress_struct *cinfo = jpg_dcinfo; + unsigned int new_width, new_height, new_colours, new_subpixelorder; + struct jpeg_decompress_struct *cinfo = jpg_dcinfo; - if ( !cinfo ) - { - cinfo = jpg_dcinfo = new jpeg_decompress_struct; - cinfo->err = jpeg_std_error( &jpg_err.pub ); - jpg_err.pub.error_exit = zm_jpeg_error_exit; - jpg_err.pub.emit_message = zm_jpeg_emit_message; - jpeg_create_decompress( cinfo ); - } + if ( !cinfo ) + { + cinfo = jpg_dcinfo = new jpeg_decompress_struct; + cinfo->err = jpeg_std_error( &jpg_err.pub ); + jpg_err.pub.error_exit = zm_jpeg_error_exit; + jpg_err.pub.emit_message = zm_jpeg_emit_message; + jpeg_create_decompress( cinfo ); + } - if ( setjmp( jpg_err.setjmp_buffer ) ) - { - jpeg_abort_decompress( cinfo ); - return( false ); - } + if ( setjmp( jpg_err.setjmp_buffer ) ) + { + jpeg_abort_decompress( cinfo ); + return( false ); + } - zm_jpeg_mem_src( cinfo, inbuffer, inbuffer_size ); + zm_jpeg_mem_src( cinfo, inbuffer, inbuffer_size ); - jpeg_read_header( cinfo, TRUE ); + jpeg_read_header( cinfo, TRUE ); - if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) - { - Error( "Unexpected colours when reading jpeg image: %d", colours ); - jpeg_abort_decompress( cinfo ); - return( false ); - } - - /* Check if the image has at least one huffman table defined. If not, use the standard ones */ - /* This is required for the MJPEG capture palette of USB devices */ - if(cinfo->dc_huff_tbl_ptrs[0] == NULL) { - zm_use_std_huff_tables(cinfo); - } + if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) + { + Error( "Unexpected colours when reading jpeg image: %d", colours ); + jpeg_abort_decompress( cinfo ); + return( false ); + } + + /* Check if the image has at least one huffman table defined. If not, use the standard ones */ + /* This is required for the MJPEG capture palette of USB devices */ + if(cinfo->dc_huff_tbl_ptrs[0] == NULL) { + zm_use_std_huff_tables(cinfo); + } - new_width = cinfo->image_width; - new_height = cinfo->image_height; + new_width = cinfo->image_width; + new_height = cinfo->image_height; - if ( width != new_width || height != new_height ) - { - Debug(9,"Image dimensions differ. Old: %ux%u New: %ux%u",width,height,new_width,new_height); - } - - switch(p_colours) { - case ZM_COLOUR_GRAY8: - { - cinfo->out_color_space = JCS_GRAYSCALE; - new_colours = ZM_COLOUR_GRAY8; - new_subpixelorder = ZM_SUBPIX_ORDER_NONE; - break; - } - case ZM_COLOUR_RGB32: - { + if ( width != new_width || height != new_height ) + { + Debug(9,"Image dimensions differ. Old: %ux%u New: %ux%u",width,height,new_width,new_height); + } + + switch(p_colours) { + case ZM_COLOUR_GRAY8: + { + cinfo->out_color_space = JCS_GRAYSCALE; + new_colours = ZM_COLOUR_GRAY8; + new_subpixelorder = ZM_SUBPIX_ORDER_NONE; + break; + } + case ZM_COLOUR_RGB32: + { #ifdef JCS_EXTENSIONS - new_colours = ZM_COLOUR_RGB32; - if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - cinfo->out_color_space = JCS_EXT_BGRX; - new_subpixelorder = ZM_SUBPIX_ORDER_BGRA; - } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - cinfo->out_color_space = JCS_EXT_XRGB; - new_subpixelorder = ZM_SUBPIX_ORDER_ARGB; - } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - cinfo->out_color_space = JCS_EXT_XBGR; - new_subpixelorder = ZM_SUBPIX_ORDER_ABGR; - } else { - /* Assume RGBA */ - cinfo->out_color_space = JCS_EXT_RGBX; - new_subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } - break; + new_colours = ZM_COLOUR_RGB32; + if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + cinfo->out_color_space = JCS_EXT_BGRX; + new_subpixelorder = ZM_SUBPIX_ORDER_BGRA; + } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + cinfo->out_color_space = JCS_EXT_XRGB; + new_subpixelorder = ZM_SUBPIX_ORDER_ARGB; + } else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + cinfo->out_color_space = JCS_EXT_XBGR; + new_subpixelorder = ZM_SUBPIX_ORDER_ABGR; + } else { + /* Assume RGBA */ + cinfo->out_color_space = JCS_EXT_RGBX; + new_subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } + break; #else - Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead."); + Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead."); #endif - } - case ZM_COLOUR_RGB24: - default: - { - new_colours = ZM_COLOUR_RGB24; - if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { -#ifdef JCS_EXTENSIONS - cinfo->out_color_space = JCS_EXT_BGR; - new_subpixelorder = ZM_SUBPIX_ORDER_BGR; + } + case ZM_COLOUR_RGB24: + default: + { + new_colours = ZM_COLOUR_RGB24; + if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) { +#ifdef JCS_EXTENSIONS + cinfo->out_color_space = JCS_EXT_BGR; + new_subpixelorder = ZM_SUBPIX_ORDER_BGR; #else - Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead."); - cinfo->out_color_space = JCS_RGB; - new_subpixelorder = ZM_SUBPIX_ORDER_RGB; + Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead."); + cinfo->out_color_space = JCS_RGB; + new_subpixelorder = ZM_SUBPIX_ORDER_RGB; #endif - } else { - /* Assume RGB */ + } else { + /* Assume RGB */ /* #ifdef JCS_EXTENSIONS - cinfo->out_color_space = JCS_EXT_RGB; + cinfo->out_color_space = JCS_EXT_RGB; #else - cinfo->out_color_space = JCS_RGB; + cinfo->out_color_space = JCS_RGB; #endif */ - cinfo->out_color_space = JCS_RGB; - new_subpixelorder = ZM_SUBPIX_ORDER_RGB; - } - break; - } - } - - if(WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == NULL) { - Error("Failed requesting writeable buffer for reading JPEG image."); - jpeg_abort_decompress( cinfo ); - return( false ); - } + cinfo->out_color_space = JCS_RGB; + new_subpixelorder = ZM_SUBPIX_ORDER_RGB; + } + break; + } + } + + if(WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == NULL) { + Error("Failed requesting writeable buffer for reading JPEG image."); + jpeg_abort_decompress( cinfo ); + return( false ); + } - jpeg_start_decompress( cinfo ); + jpeg_start_decompress( cinfo ); - JSAMPROW row_pointer; /* pointer to a single row */ - int row_stride = width * colours; /* physical row width in buffer */ - while ( cinfo->output_scanline < cinfo->output_height ) - { - row_pointer = &buffer[cinfo->output_scanline * row_stride]; - jpeg_read_scanlines( cinfo, &row_pointer, 1 ); - } + JSAMPROW row_pointer; /* pointer to a single row */ + int row_stride = width * colours; /* physical row width in buffer */ + while ( cinfo->output_scanline < cinfo->output_height ) + { + row_pointer = &buffer[cinfo->output_scanline * row_stride]; + jpeg_read_scanlines( cinfo, &row_pointer, 1 ); + } - jpeg_finish_decompress( cinfo ); + jpeg_finish_decompress( cinfo ); - return( true ); + return( true ); } bool Image::EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_override ) const { - if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) - { - Image temp_image( *this ); - temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); - return( temp_image.EncodeJpeg( outbuffer, outbuffer_size, quality_override ) ); - } + if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) + { + Image temp_image( *this ); + temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); + return( temp_image.EncodeJpeg( outbuffer, outbuffer_size, quality_override ) ); + } - int quality = quality_override?quality_override:config.jpeg_stream_quality; + int quality = quality_override?quality_override:config.jpeg_stream_quality; - struct jpeg_compress_struct *cinfo = jpg_ccinfo[quality]; + struct jpeg_compress_struct *cinfo = jpg_ccinfo[quality]; - if ( !cinfo ) - { - cinfo = jpg_ccinfo[quality] = new jpeg_compress_struct; - cinfo->err = jpeg_std_error( &jpg_err.pub ); - jpg_err.pub.error_exit = zm_jpeg_error_exit; - jpg_err.pub.emit_message = zm_jpeg_emit_message; - jpeg_create_compress( cinfo ); - } + if ( !cinfo ) + { + cinfo = jpg_ccinfo[quality] = new jpeg_compress_struct; + cinfo->err = jpeg_std_error( &jpg_err.pub ); + jpg_err.pub.error_exit = zm_jpeg_error_exit; + jpg_err.pub.emit_message = zm_jpeg_emit_message; + jpeg_create_compress( cinfo ); + } - zm_jpeg_mem_dest( cinfo, outbuffer, outbuffer_size ); + zm_jpeg_mem_dest( cinfo, outbuffer, outbuffer_size ); - cinfo->image_width = width; /* image width and height, in pixels */ - cinfo->image_height = height; + cinfo->image_width = width; /* image width and height, in pixels */ + cinfo->image_height = height; - switch(colours) { - case ZM_COLOUR_GRAY8: - { - cinfo->input_components = 1; - cinfo->in_color_space = JCS_GRAYSCALE; - break; - } - case ZM_COLOUR_RGB32: - { + switch(colours) { + case ZM_COLOUR_GRAY8: + { + cinfo->input_components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; + break; + } + case ZM_COLOUR_RGB32: + { #ifdef JCS_EXTENSIONS - cinfo->input_components = 4; - if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - cinfo->in_color_space = JCS_EXT_BGRX; - } else if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - cinfo->in_color_space = JCS_EXT_XRGB; - } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - cinfo->in_color_space = JCS_EXT_XBGR; - } else { - /* Assume RGBA */ - cinfo->in_color_space = JCS_EXT_RGBX; - } + cinfo->input_components = 4; + if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + cinfo->in_color_space = JCS_EXT_BGRX; + } else if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + cinfo->in_color_space = JCS_EXT_XRGB; + } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + cinfo->in_color_space = JCS_EXT_XBGR; + } else { + /* Assume RGBA */ + cinfo->in_color_space = JCS_EXT_RGBX; + } #else - Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source"); - jpeg_abort_compress( cinfo ); - return(false); + Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source"); + jpeg_abort_compress( cinfo ); + return(false); #endif - break; - } - case ZM_COLOUR_RGB24: - default: - { - cinfo->input_components = 3; - if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { + break; + } + case ZM_COLOUR_RGB24: + default: + { + cinfo->input_components = 3; + if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { #ifdef JCS_EXTENSIONS - cinfo->in_color_space = JCS_EXT_BGR; + cinfo->in_color_space = JCS_EXT_BGR; #else - Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source"); - jpeg_abort_compress( cinfo ); - return(false); + Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source"); + jpeg_abort_compress( cinfo ); + return(false); #endif - } else { - /* Assume RGB */ + } else { + /* Assume RGB */ /* #ifdef JCS_EXTENSIONS - cinfo->out_color_space = JCS_EXT_RGB; + cinfo->out_color_space = JCS_EXT_RGB; #else - cinfo->out_color_space = JCS_RGB; + cinfo->out_color_space = JCS_RGB; #endif */ - cinfo->in_color_space = JCS_RGB; - } - break; - } - } - - jpeg_set_defaults( cinfo ); - jpeg_set_quality( cinfo, quality, FALSE ); - cinfo->dct_method = JDCT_FASTEST; + cinfo->in_color_space = JCS_RGB; + } + break; + } + } + + jpeg_set_defaults( cinfo ); + jpeg_set_quality( cinfo, quality, FALSE ); + cinfo->dct_method = JDCT_FASTEST; - jpeg_start_compress( cinfo, TRUE ); + jpeg_start_compress( cinfo, TRUE ); - JSAMPROW row_pointer; /* pointer to a single row */ - int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ - while ( cinfo->next_scanline < cinfo->image_height ) - { - row_pointer = &buffer[cinfo->next_scanline * row_stride]; - jpeg_write_scanlines( cinfo, &row_pointer, 1 ); - } + JSAMPROW row_pointer; /* pointer to a single row */ + int row_stride = cinfo->image_width * colours; /* physical row width in buffer */ + while ( cinfo->next_scanline < cinfo->image_height ) + { + row_pointer = &buffer[cinfo->next_scanline * row_stride]; + jpeg_write_scanlines( cinfo, &row_pointer, 1 ); + } - jpeg_finish_compress( cinfo ); + jpeg_finish_compress( cinfo ); - return( true ); + return( true ); } #if HAVE_ZLIB_H bool Image::Unzip( const Bytef *inbuffer, unsigned long inbuffer_size ) { - unsigned long zip_size = size; - int result = uncompress( buffer, &zip_size, inbuffer, inbuffer_size ); - if ( result != Z_OK ) - { - Error( "Unzip failed, result = %d", result ); - return( false ); - } - if ( zip_size != (unsigned int)size ) - { - Error( "Unzip failed, size mismatch, expected %d bytes, got %ld", size, zip_size ); - return( false ); - } - return( true ); + unsigned long zip_size = size; + int result = uncompress( buffer, &zip_size, inbuffer, inbuffer_size ); + if ( result != Z_OK ) + { + Error( "Unzip failed, result = %d", result ); + return( false ); + } + if ( zip_size != (unsigned int)size ) + { + Error( "Unzip failed, size mismatch, expected %d bytes, got %ld", size, zip_size ); + return( false ); + } + return( true ); } bool Image::Zip( Bytef *outbuffer, unsigned long *outbuffer_size, int compression_level ) const { - int result = compress2( outbuffer, outbuffer_size, buffer, size, compression_level ); - if ( result != Z_OK ) - { - Error( "Zip failed, result = %d", result ); - return( false ); - } - return( true ); + int result = compress2( outbuffer, outbuffer_size, buffer, size, compression_level ); + if ( result != Z_OK ) + { + Error( "Zip failed, result = %d", result ); + return( false ); + } + return( true ); } #endif // HAVE_ZLIB_H bool Image::Crop( unsigned int lo_x, unsigned int lo_y, unsigned int hi_x, unsigned int hi_y ) { - unsigned int new_width = (hi_x-lo_x)+1; - unsigned int new_height = (hi_y-lo_y)+1; + unsigned int new_width = (hi_x-lo_x)+1; + unsigned int new_height = (hi_y-lo_y)+1; - if ( lo_x > hi_x || lo_y > hi_y ) - { - Error( "Invalid or reversed crop region %d,%d -> %d,%d", lo_x, lo_y, hi_x, hi_y ); - return( false ); - } - if ( lo_x < 0 || hi_x > (width-1) || ( lo_y < 0 || hi_y > (height-1) ) ) - { - Error( "Attempting to crop outside image, %d,%d -> %d,%d not in %d,%d", lo_x, lo_y, hi_x, hi_y, width-1, height-1 ); - return( false ); - } + if ( lo_x > hi_x || lo_y > hi_y ) + { + Error( "Invalid or reversed crop region %d,%d -> %d,%d", lo_x, lo_y, hi_x, hi_y ); + return( false ); + } + if ( lo_x < 0 || hi_x > (width-1) || ( lo_y < 0 || hi_y > (height-1) ) ) + { + Error( "Attempting to crop outside image, %d,%d -> %d,%d not in %d,%d", lo_x, lo_y, hi_x, hi_y, width-1, height-1 ); + return( false ); + } - if ( new_width == width && new_height == height ) - { - return( true ); - } + if ( new_width == width && new_height == height ) + { + return( true ); + } - unsigned int new_size = new_width*new_height*colours; - uint8_t *new_buffer = AllocBuffer(new_size); - - unsigned int new_stride = new_width*colours; - for ( unsigned int y = lo_y, ny = 0; y <= hi_y; y++, ny++ ) - { - unsigned char *pbuf = &buffer[((y*width)+lo_x)*colours]; - unsigned char *pnbuf = &new_buffer[(ny*new_width)*colours]; - memcpy( pnbuf, pbuf, new_stride ); - } + unsigned int new_size = new_width*new_height*colours; + uint8_t *new_buffer = AllocBuffer(new_size); + + unsigned int new_stride = new_width*colours; + for ( unsigned int y = lo_y, ny = 0; y <= hi_y; y++, ny++ ) + { + unsigned char *pbuf = &buffer[((y*width)+lo_x)*colours]; + unsigned char *pnbuf = &new_buffer[(ny*new_width)*colours]; + memcpy( pnbuf, pbuf, new_stride ); + } - AssignDirect(new_width, new_height, colours, subpixelorder, new_buffer, new_size, ZM_BUFTYPE_ZM); + AssignDirect(new_width, new_height, colours, subpixelorder, new_buffer, new_size, ZM_BUFTYPE_ZM); - return( true ); + return( true ); } bool Image::Crop( const Box &limits ) { - return( Crop( limits.LoX(), limits.LoY(), limits.HiX(), limits.HiY() ) ); + return( Crop( limits.LoX(), limits.LoY(), limits.HiX(), limits.HiY() ) ); } /* Far from complete */ /* Need to implement all possible of overlays possible */ void Image::Overlay( const Image &image ) { - if ( !(width == image.width && height == image.height) ) - { - Panic( "Attempt to overlay different sized images, expected %dx%d, got %dx%d", width, height, image.width, image.height ); - } - - if( colours == image.colours && subpixelorder != image.subpixelorder ) { - Warning("Attempt to overlay images of same format but with different subpixel order."); - } - - /* Grayscale ontop of grayscale - complete */ - if ( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_GRAY8 ) { - const uint8_t* const max_ptr = buffer+size; - const uint8_t* psrc = image.buffer; - uint8_t* pdest = buffer; - - while( pdest < max_ptr ) - { - if ( *psrc ) - { - *pdest = *psrc; - } - pdest++; - psrc++; - } - - /* RGB24 ontop of grayscale - convert to same format first - complete */ - } else if ( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_RGB24 ) { - Colourise(image.colours, image.subpixelorder); - - const uint8_t* const max_ptr = buffer+size; - const uint8_t* psrc = image.buffer; - uint8_t* pdest = buffer; - - while( pdest < max_ptr ) - { - if ( RED_PTR_RGBA(psrc) || GREEN_PTR_RGBA(psrc) || BLUE_PTR_RGBA(psrc) ) - { - RED_PTR_RGBA(pdest) = RED_PTR_RGBA(psrc); - GREEN_PTR_RGBA(pdest) = GREEN_PTR_RGBA(psrc); - BLUE_PTR_RGBA(pdest) = BLUE_PTR_RGBA(psrc); - } - pdest += 3; - psrc += 3; - } - - /* RGB32 ontop of grayscale - convert to same format first - complete */ - } else if( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_RGB32 ) { - Colourise(image.colours, image.subpixelorder); - - const Rgb* const max_ptr = (Rgb*)(buffer+size); - const Rgb* prsrc = (Rgb*)image.buffer; - Rgb* prdest = (Rgb*)buffer; - - if(subpixelorder == ZM_SUBPIX_ORDER_RGBA || subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - /* RGB\BGR\RGBA\BGRA subpixel order - Alpha byte is last */ - while (prdest < max_ptr) { - if ( RED_PTR_RGBA(prsrc) || GREEN_PTR_RGBA(prsrc) || BLUE_PTR_RGBA(prsrc) ) - { - *prdest = *prsrc; - } - prdest++; - prsrc++; - } - } else { - /* ABGR\ARGB subpixel order - Alpha byte is first */ - while (prdest < max_ptr) { - if ( RED_PTR_ABGR(prsrc) || GREEN_PTR_ABGR(prsrc) || BLUE_PTR_ABGR(prsrc) ) - { - *prdest = *prsrc; - } - prdest++; - prsrc++; - } - } - - /* Grayscale ontop of RGB24 - complete */ - } else if ( colours == ZM_COLOUR_RGB24 && image.colours == ZM_COLOUR_GRAY8 ) { - const uint8_t* const max_ptr = buffer+size; - const uint8_t* psrc = image.buffer; - uint8_t* pdest = buffer; - - while( pdest < max_ptr ) - { - if ( *psrc ) - { - RED_PTR_RGBA(pdest) = GREEN_PTR_RGBA(pdest) = BLUE_PTR_RGBA(pdest) = *psrc; - } - pdest += 3; - psrc++; - } - - /* RGB24 ontop of RGB24 - not complete. need to take care of different subpixel orders */ - } else if ( colours == ZM_COLOUR_RGB24 && image.colours == ZM_COLOUR_RGB24 ) { - const uint8_t* const max_ptr = buffer+size; - const uint8_t* psrc = image.buffer; - uint8_t* pdest = buffer; - - while( pdest < max_ptr ) - { - if ( RED_PTR_RGBA(psrc) || GREEN_PTR_RGBA(psrc) || BLUE_PTR_RGBA(psrc) ) - { - RED_PTR_RGBA(pdest) = RED_PTR_RGBA(psrc); - GREEN_PTR_RGBA(pdest) = GREEN_PTR_RGBA(psrc); - BLUE_PTR_RGBA(pdest) = BLUE_PTR_RGBA(psrc); - } - pdest += 3; - psrc += 3; - } - - /* RGB32 ontop of RGB24 - TO BE DONE */ - } else if ( colours == ZM_COLOUR_RGB24 && image.colours == ZM_COLOUR_RGB32 ) { - Error("Overlay of RGB32 ontop of RGB24 is not supported."); - - /* Grayscale ontop of RGB32 - complete */ - } else if ( colours == ZM_COLOUR_RGB32 && image.colours == ZM_COLOUR_GRAY8 ) { - const Rgb* const max_ptr = (Rgb*)(buffer+size); - Rgb* prdest = (Rgb*)buffer; - const uint8_t* psrc = image.buffer; - - if(subpixelorder == ZM_SUBPIX_ORDER_RGBA || subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - /* RGBA\BGRA subpixel order - Alpha byte is last */ - while (prdest < max_ptr) { - if ( *psrc ) - { - RED_PTR_RGBA(prdest) = GREEN_PTR_RGBA(prdest) = BLUE_PTR_RGBA(prdest) = *psrc; - } - prdest++; - psrc++; - } - } else { - /* ABGR\ARGB subpixel order - Alpha byte is first */ - while (prdest < max_ptr) { - if ( *psrc ) - { - RED_PTR_ABGR(prdest) = GREEN_PTR_ABGR(prdest) = BLUE_PTR_ABGR(prdest) = *psrc; - } - prdest++; - psrc++; - } - } - - /* RGB24 ontop of RGB32 - TO BE DONE */ - } else if ( colours == ZM_COLOUR_RGB32 && image.colours == ZM_COLOUR_RGB24 ) { - Error("Overlay of RGB24 ontop of RGB32 is not supported."); - - /* RGB32 ontop of RGB32 - not complete. need to take care of different subpixel orders */ - } else if ( colours == ZM_COLOUR_RGB32 && image.colours == ZM_COLOUR_RGB32 ) { - const Rgb* const max_ptr = (Rgb*)(buffer+size); - Rgb* prdest = (Rgb*)buffer; - const Rgb* prsrc = (Rgb*)image.buffer; - - if(image.subpixelorder == ZM_SUBPIX_ORDER_RGBA || image.subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - /* RGB\BGR\RGBA\BGRA subpixel order - Alpha byte is last */ - while (prdest < max_ptr) { - if ( RED_PTR_RGBA(prsrc) || GREEN_PTR_RGBA(prsrc) || BLUE_PTR_RGBA(prsrc) ) - { - *prdest = *prsrc; - } - prdest++; - prsrc++; - } - } else { - /* ABGR\ARGB subpixel order - Alpha byte is first */ - while (prdest < max_ptr) { - if ( RED_PTR_ABGR(prsrc) || GREEN_PTR_ABGR(prsrc) || BLUE_PTR_ABGR(prsrc) ) - { - *prdest = *prsrc; - } - prdest++; - prsrc++; - } - } - } - + if ( !(width == image.width && height == image.height) ) + { + Panic( "Attempt to overlay different sized images, expected %dx%d, got %dx%d", width, height, image.width, image.height ); + } + + if( colours == image.colours && subpixelorder != image.subpixelorder ) { + Warning("Attempt to overlay images of same format but with different subpixel order."); + } + + /* Grayscale ontop of grayscale - complete */ + if ( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_GRAY8 ) { + const uint8_t* const max_ptr = buffer+size; + const uint8_t* psrc = image.buffer; + uint8_t* pdest = buffer; + + while( pdest < max_ptr ) + { + if ( *psrc ) + { + *pdest = *psrc; + } + pdest++; + psrc++; + } + + /* RGB24 ontop of grayscale - convert to same format first - complete */ + } else if ( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_RGB24 ) { + Colourise(image.colours, image.subpixelorder); + + const uint8_t* const max_ptr = buffer+size; + const uint8_t* psrc = image.buffer; + uint8_t* pdest = buffer; + + while( pdest < max_ptr ) + { + if ( RED_PTR_RGBA(psrc) || GREEN_PTR_RGBA(psrc) || BLUE_PTR_RGBA(psrc) ) + { + RED_PTR_RGBA(pdest) = RED_PTR_RGBA(psrc); + GREEN_PTR_RGBA(pdest) = GREEN_PTR_RGBA(psrc); + BLUE_PTR_RGBA(pdest) = BLUE_PTR_RGBA(psrc); + } + pdest += 3; + psrc += 3; + } + + /* RGB32 ontop of grayscale - convert to same format first - complete */ + } else if( colours == ZM_COLOUR_GRAY8 && image.colours == ZM_COLOUR_RGB32 ) { + Colourise(image.colours, image.subpixelorder); + + const Rgb* const max_ptr = (Rgb*)(buffer+size); + const Rgb* prsrc = (Rgb*)image.buffer; + Rgb* prdest = (Rgb*)buffer; + + if(subpixelorder == ZM_SUBPIX_ORDER_RGBA || subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + /* RGB\BGR\RGBA\BGRA subpixel order - Alpha byte is last */ + while (prdest < max_ptr) { + if ( RED_PTR_RGBA(prsrc) || GREEN_PTR_RGBA(prsrc) || BLUE_PTR_RGBA(prsrc) ) + { + *prdest = *prsrc; + } + prdest++; + prsrc++; + } + } else { + /* ABGR\ARGB subpixel order - Alpha byte is first */ + while (prdest < max_ptr) { + if ( RED_PTR_ABGR(prsrc) || GREEN_PTR_ABGR(prsrc) || BLUE_PTR_ABGR(prsrc) ) + { + *prdest = *prsrc; + } + prdest++; + prsrc++; + } + } + + /* Grayscale ontop of RGB24 - complete */ + } else if ( colours == ZM_COLOUR_RGB24 && image.colours == ZM_COLOUR_GRAY8 ) { + const uint8_t* const max_ptr = buffer+size; + const uint8_t* psrc = image.buffer; + uint8_t* pdest = buffer; + + while( pdest < max_ptr ) + { + if ( *psrc ) + { + RED_PTR_RGBA(pdest) = GREEN_PTR_RGBA(pdest) = BLUE_PTR_RGBA(pdest) = *psrc; + } + pdest += 3; + psrc++; + } + + /* RGB24 ontop of RGB24 - not complete. need to take care of different subpixel orders */ + } else if ( colours == ZM_COLOUR_RGB24 && image.colours == ZM_COLOUR_RGB24 ) { + const uint8_t* const max_ptr = buffer+size; + const uint8_t* psrc = image.buffer; + uint8_t* pdest = buffer; + + while( pdest < max_ptr ) + { + if ( RED_PTR_RGBA(psrc) || GREEN_PTR_RGBA(psrc) || BLUE_PTR_RGBA(psrc) ) + { + RED_PTR_RGBA(pdest) = RED_PTR_RGBA(psrc); + GREEN_PTR_RGBA(pdest) = GREEN_PTR_RGBA(psrc); + BLUE_PTR_RGBA(pdest) = BLUE_PTR_RGBA(psrc); + } + pdest += 3; + psrc += 3; + } + + /* RGB32 ontop of RGB24 - TO BE DONE */ + } else if ( colours == ZM_COLOUR_RGB24 && image.colours == ZM_COLOUR_RGB32 ) { + Error("Overlay of RGB32 ontop of RGB24 is not supported."); + + /* Grayscale ontop of RGB32 - complete */ + } else if ( colours == ZM_COLOUR_RGB32 && image.colours == ZM_COLOUR_GRAY8 ) { + const Rgb* const max_ptr = (Rgb*)(buffer+size); + Rgb* prdest = (Rgb*)buffer; + const uint8_t* psrc = image.buffer; + + if(subpixelorder == ZM_SUBPIX_ORDER_RGBA || subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + /* RGBA\BGRA subpixel order - Alpha byte is last */ + while (prdest < max_ptr) { + if ( *psrc ) + { + RED_PTR_RGBA(prdest) = GREEN_PTR_RGBA(prdest) = BLUE_PTR_RGBA(prdest) = *psrc; + } + prdest++; + psrc++; + } + } else { + /* ABGR\ARGB subpixel order - Alpha byte is first */ + while (prdest < max_ptr) { + if ( *psrc ) + { + RED_PTR_ABGR(prdest) = GREEN_PTR_ABGR(prdest) = BLUE_PTR_ABGR(prdest) = *psrc; + } + prdest++; + psrc++; + } + } + + /* RGB24 ontop of RGB32 - TO BE DONE */ + } else if ( colours == ZM_COLOUR_RGB32 && image.colours == ZM_COLOUR_RGB24 ) { + Error("Overlay of RGB24 ontop of RGB32 is not supported."); + + /* RGB32 ontop of RGB32 - not complete. need to take care of different subpixel orders */ + } else if ( colours == ZM_COLOUR_RGB32 && image.colours == ZM_COLOUR_RGB32 ) { + const Rgb* const max_ptr = (Rgb*)(buffer+size); + Rgb* prdest = (Rgb*)buffer; + const Rgb* prsrc = (Rgb*)image.buffer; + + if(image.subpixelorder == ZM_SUBPIX_ORDER_RGBA || image.subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + /* RGB\BGR\RGBA\BGRA subpixel order - Alpha byte is last */ + while (prdest < max_ptr) { + if ( RED_PTR_RGBA(prsrc) || GREEN_PTR_RGBA(prsrc) || BLUE_PTR_RGBA(prsrc) ) + { + *prdest = *prsrc; + } + prdest++; + prsrc++; + } + } else { + /* ABGR\ARGB subpixel order - Alpha byte is first */ + while (prdest < max_ptr) { + if ( RED_PTR_ABGR(prsrc) || GREEN_PTR_ABGR(prsrc) || BLUE_PTR_ABGR(prsrc) ) + { + *prdest = *prsrc; + } + prdest++; + prsrc++; + } + } + } + } /* RGB32 compatible: complete */ void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) { - if ( !(width < image.width || height < image.height) ) - { - Panic( "Attempt to overlay image too big for destination, %dx%d > %dx%d", image.width, image.height, width, height ); - } + if ( !(width < image.width || height < image.height) ) + { + Panic( "Attempt to overlay image too big for destination, %dx%d > %dx%d", image.width, image.height, width, height ); + } - if ( !(width < (x+image.width) || height < (y+image.height)) ) - { - Panic( "Attempt to overlay image outside of destination bounds, %dx%d @ %dx%d > %dx%d", image.width, image.height, x, y, width, height ); - } + if ( !(width < (x+image.width) || height < (y+image.height)) ) + { + Panic( "Attempt to overlay image outside of destination bounds, %dx%d @ %dx%d > %dx%d", image.width, image.height, x, y, width, height ); + } - if ( !(colours == image.colours) ) - { - Panic( "Attempt to partial overlay differently coloured images, expected %d, got %d", colours, image.colours ); - } + if ( !(colours == image.colours) ) + { + Panic( "Attempt to partial overlay differently coloured images, expected %d, got %d", colours, image.colours ); + } - unsigned int lo_x = x; - unsigned int lo_y = y; - unsigned int hi_x = (x+image.width)-1; - unsigned int hi_y = (y+image.height-1); - if ( colours == ZM_COLOUR_GRAY8 ) - { - const uint8_t *psrc = image.buffer; - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - uint8_t *pdest = &buffer[(y*width)+lo_x]; - for ( unsigned int x = lo_x; x <= hi_x; x++ ) - { - *pdest++ = *psrc++; - } - } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - const uint8_t *psrc = image.buffer; - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - uint8_t *pdest = &buffer[colours*((y*width)+lo_x)]; - for ( unsigned int x = lo_x; x <= hi_x; x++ ) - { - *pdest++ = *psrc++; - *pdest++ = *psrc++; - *pdest++ = *psrc++; - } - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - const Rgb *psrc = (Rgb*)(image.buffer); - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - Rgb *pdest = (Rgb*)&buffer[((y*width)+lo_x)<<2]; - for ( unsigned int x = lo_x; x <= hi_x; x++ ) - { - *pdest++ = *psrc++; - } - } - } else { - Error("Overlay called with unexpected colours: %d", colours); - } - + unsigned int lo_x = x; + unsigned int lo_y = y; + unsigned int hi_x = (x+image.width)-1; + unsigned int hi_y = (y+image.height-1); + if ( colours == ZM_COLOUR_GRAY8 ) + { + const uint8_t *psrc = image.buffer; + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + uint8_t *pdest = &buffer[(y*width)+lo_x]; + for ( unsigned int x = lo_x; x <= hi_x; x++ ) + { + *pdest++ = *psrc++; + } + } + } + else if ( colours == ZM_COLOUR_RGB24 ) + { + const uint8_t *psrc = image.buffer; + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + uint8_t *pdest = &buffer[colours*((y*width)+lo_x)]; + for ( unsigned int x = lo_x; x <= hi_x; x++ ) + { + *pdest++ = *psrc++; + *pdest++ = *psrc++; + *pdest++ = *psrc++; + } + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + const Rgb *psrc = (Rgb*)(image.buffer); + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + Rgb *pdest = (Rgb*)&buffer[((y*width)+lo_x)<<2]; + for ( unsigned int x = lo_x; x <= hi_x; x++ ) + { + *pdest++ = *psrc++; + } + } + } else { + Error("Overlay called with unexpected colours: %d", colours); + } + } void Image::Blend( const Image &image, int transparency ) { #ifdef ZM_IMAGE_PROFILING - struct timespec start,end,diff; - unsigned long long executetime; - unsigned long milpixels; + struct timespec start,end,diff; + unsigned long long executetime; + unsigned long milpixels; #endif - uint8_t* new_buffer; - - if ( !(width == image.width && height == image.height && colours == image.colours && subpixelorder == image.subpixelorder) ) - { - Panic( "Attempt to blend different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, image.width, image.height, image.colours, image.subpixelorder ); - } - - if(transparency <= 0) - return; - - new_buffer = AllocBuffer(size); - + uint8_t* new_buffer; + + if ( !(width == image.width && height == image.height && colours == image.colours && subpixelorder == image.subpixelorder) ) + { + Panic( "Attempt to blend different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, image.width, image.height, image.colours, image.subpixelorder ); + } + + if(transparency <= 0) + return; + + new_buffer = AllocBuffer(size); + #ifdef ZM_IMAGE_PROFILING - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&start); + clock_gettime(CLOCK_THREAD_CPUTIME_ID,&start); #endif - - /* Do the blending */ - (*fptr_blend)(buffer, image.buffer, new_buffer, size, transparency); - + + /* Do the blending */ + (*fptr_blend)(buffer, image.buffer, new_buffer, size, transparency); + #ifdef ZM_IMAGE_PROFILING - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&end); - timespec_diff(&start,&end,&diff); - - executetime = (1000000000ull * diff.tv_sec) + diff.tv_nsec; - milpixels = (unsigned long)((long double)size)/((((long double)executetime)/1000)); - Debug(5, "Blend: %u colours blended in %llu nanoseconds, %lu million colours/s\n",size,executetime,milpixels); + clock_gettime(CLOCK_THREAD_CPUTIME_ID,&end); + timespec_diff(&start,&end,&diff); + + executetime = (1000000000ull * diff.tv_sec) + diff.tv_nsec; + milpixels = (unsigned long)((long double)size)/((((long double)executetime)/1000)); + Debug(5, "Blend: %u colours blended in %llu nanoseconds, %lu million colours/s\n",size,executetime,milpixels); #endif - - AssignDirect( width, height, colours, subpixelorder, new_buffer, size, ZM_BUFTYPE_ZM); + + AssignDirect( width, height, colours, subpixelorder, new_buffer, size, ZM_BUFTYPE_ZM); } Image *Image::Merge( unsigned int n_images, Image *images[] ) { - if ( n_images <= 0 ) return( 0 ); - if ( n_images == 1 ) return( new Image( *images[0] ) ); + if ( n_images <= 0 ) return( 0 ); + if ( n_images == 1 ) return( new Image( *images[0] ) ); - unsigned int width = images[0]->width; - unsigned int height = images[0]->height; - unsigned int colours = images[0]->colours; - for ( unsigned int i = 1; i < n_images; i++ ) - { - if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) - { - Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); - } - } + unsigned int width = images[0]->width; + unsigned int height = images[0]->height; + unsigned int colours = images[0]->colours; + for ( unsigned int i = 1; i < n_images; i++ ) + { + if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) + { + Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); + } + } - Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder); - unsigned int size = result->size; - for ( unsigned int i = 0; i < size; i++ ) - { - unsigned int total = 0; - uint8_t *pdest = result->buffer; - for ( unsigned int j = 0; j < n_images; j++ ) - { - uint8_t *psrc = images[j]->buffer; - total += *psrc; - psrc++; - } - *pdest = total/n_images; - pdest++; - } - return( result ); + Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder); + unsigned int size = result->size; + for ( unsigned int i = 0; i < size; i++ ) + { + unsigned int total = 0; + uint8_t *pdest = result->buffer; + for ( unsigned int j = 0; j < n_images; j++ ) + { + uint8_t *psrc = images[j]->buffer; + total += *psrc; + psrc++; + } + *pdest = total/n_images; + pdest++; + } + return( result ); } Image *Image::Merge( unsigned int n_images, Image *images[], double weight ) { - if ( n_images <= 0 ) return( 0 ); - if ( n_images == 1 ) return( new Image( *images[0] ) ); + if ( n_images <= 0 ) return( 0 ); + if ( n_images == 1 ) return( new Image( *images[0] ) ); - unsigned int width = images[0]->width; - unsigned int height = images[0]->height; - unsigned int colours = images[0]->colours; - for ( unsigned int i = 1; i < n_images; i++ ) - { - if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) - { - Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); - } - } + unsigned int width = images[0]->width; + unsigned int height = images[0]->height; + unsigned int colours = images[0]->colours; + for ( unsigned int i = 1; i < n_images; i++ ) + { + if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) + { + Panic( "Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); + } + } - Image *result = new Image( *images[0] ); - unsigned int size = result->size; - double factor = 1.0*weight; - for ( unsigned int i = 1; i < n_images; i++ ) - { - uint8_t *pdest = result->buffer; - uint8_t *psrc = images[i]->buffer; - for ( unsigned int j = 0; j < size; j++ ) - { - *pdest = (uint8_t)(((*pdest)*(1.0-factor))+((*psrc)*factor)); - pdest++; - psrc++; - } - factor *= weight; - } - return( result ); + Image *result = new Image( *images[0] ); + unsigned int size = result->size; + double factor = 1.0*weight; + for ( unsigned int i = 1; i < n_images; i++ ) + { + uint8_t *pdest = result->buffer; + uint8_t *psrc = images[i]->buffer; + for ( unsigned int j = 0; j < size; j++ ) + { + *pdest = (uint8_t)(((*pdest)*(1.0-factor))+((*psrc)*factor)); + pdest++; + psrc++; + } + factor *= weight; + } + return( result ); } Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb threshold, const Rgb ref_colour ) { - if ( n_images <= 0 ) return( 0 ); - if ( n_images == 1 ) return( new Image( *images[0] ) ); + if ( n_images <= 0 ) return( 0 ); + if ( n_images == 1 ) return( new Image( *images[0] ) ); - unsigned int width = images[0]->width; - unsigned int height = images[0]->height; - unsigned int colours = images[0]->colours; - for ( unsigned int i = 1; i < n_images; i++ ) - { - if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) + unsigned int width = images[0]->width; + unsigned int height = images[0]->height; + unsigned int colours = images[0]->colours; + for ( unsigned int i = 1; i < n_images; i++ ) + { + if ( !(width == images[i]->width && height == images[i]->height && colours == images[i]->colours) ) + { + Panic( "Attempt to highlight different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); + } + } + + Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder ); + unsigned int size = result->size; + for ( unsigned int c = 0; c < colours; c++ ) + { + for ( unsigned int i = 0; i < size; i++ ) + { + unsigned int count = 0; + uint8_t *pdest = result->buffer+c; + for ( unsigned int j = 0; j < n_images; j++ ) + { + uint8_t *psrc = images[j]->buffer+c; + +#ifndef SOLARIS + if ( (unsigned)abs((*psrc)-RGB_VAL(ref_colour,c)) >= RGB_VAL(threshold,c) ) +#else + if ( (unsigned)std::abs((*psrc)-RGB_VAL(ref_colour,c)) >= RGB_VAL(threshold,c) ) +#endif { - Panic( "Attempt to highlight different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d", width, height, colours, images[i]->width, images[i]->height, images[i]->colours, i ); + count++; } - } - - Image *result = new Image( width, height, images[0]->colours, images[0]->subpixelorder ); - unsigned int size = result->size; - for ( unsigned int c = 0; c < colours; c++ ) - { - for ( unsigned int i = 0; i < size; i++ ) - { - unsigned int count = 0; - uint8_t *pdest = result->buffer+c; - for ( unsigned int j = 0; j < n_images; j++ ) - { - uint8_t *psrc = images[j]->buffer+c; - - if ( (unsigned)abs((*psrc)-RGB_VAL(ref_colour,c)) >= RGB_VAL(threshold,c) ) - { - count++; - } - psrc += colours; - } - *pdest = (count*255)/n_images; - pdest += 3; - } - } - return( result ); + psrc += colours; + } + *pdest = (count*255)/n_images; + pdest += 3; + } + } + return( result ); } -/* New function to allow buffer re-using instead of allocationg memory for the delta image everytime */ +/* New function to allow buffer re-using instead of allocationg memory for the delta image every time */ void Image::Delta( const Image &image, Image* targetimage) const { #ifdef ZM_IMAGE_PROFILING - struct timespec start,end,diff; - unsigned long long executetime; - unsigned long milpixels; + struct timespec start,end,diff; + unsigned long long executetime; + unsigned long milpixels; #endif - - if ( !(width == image.width && height == image.height && colours == image.colours && subpixelorder == image.subpixelorder) ) - { - Panic( "Attempt to get delta of different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, image.width, image.height, image.colours, image.subpixelorder); - } - - uint8_t *pdiff = targetimage->WriteBuffer(width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE); - - if(pdiff == NULL) { - Panic("Failed requesting writeable buffer for storing the delta image"); - } - + + if ( !(width == image.width && height == image.height && colours == image.colours && subpixelorder == image.subpixelorder) ) + { + Panic( "Attempt to get delta of different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, image.width, image.height, image.colours, image.subpixelorder); + } + + uint8_t *pdiff = targetimage->WriteBuffer(width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE); + + if(pdiff == NULL) { + Panic("Failed requesting writeable buffer for storing the delta image"); + } + #ifdef ZM_IMAGE_PROFILING - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&start); + clock_gettime(CLOCK_THREAD_CPUTIME_ID,&start); #endif - - switch(colours) { - case ZM_COLOUR_RGB24: - { - if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { - /* BGR subpixel order */ - (*fptr_delta8_bgr)(buffer, image.buffer, pdiff, pixels); - } else { - /* Assume RGB subpixel order */ - (*fptr_delta8_rgb)(buffer, image.buffer, pdiff, pixels); - } - break; - } - case ZM_COLOUR_RGB32: - { - if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - /* ARGB subpixel order */ - (*fptr_delta8_argb)(buffer, image.buffer, pdiff, pixels); - } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - /* ABGR subpixel order */ - (*fptr_delta8_abgr)(buffer, image.buffer, pdiff, pixels); - } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - /* BGRA subpixel order */ - (*fptr_delta8_bgra)(buffer, image.buffer, pdiff, pixels); - } else { - /* Assume RGBA subpixel order */ - (*fptr_delta8_rgba)(buffer, image.buffer, pdiff, pixels); - } - break; - } - case ZM_COLOUR_GRAY8: - (*fptr_delta8_gray8)(buffer, image.buffer, pdiff, pixels); - break; - default: - Panic("Delta called with unexpected colours: %d",colours); - break; - } - + + switch(colours) { + case ZM_COLOUR_RGB24: + { + if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { + /* BGR subpixel order */ + (*fptr_delta8_bgr)(buffer, image.buffer, pdiff, pixels); + } else { + /* Assume RGB subpixel order */ + (*fptr_delta8_rgb)(buffer, image.buffer, pdiff, pixels); + } + break; + } + case ZM_COLOUR_RGB32: + { + if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + /* ARGB subpixel order */ + (*fptr_delta8_argb)(buffer, image.buffer, pdiff, pixels); + } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + /* ABGR subpixel order */ + (*fptr_delta8_abgr)(buffer, image.buffer, pdiff, pixels); + } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + /* BGRA subpixel order */ + (*fptr_delta8_bgra)(buffer, image.buffer, pdiff, pixels); + } else { + /* Assume RGBA subpixel order */ + (*fptr_delta8_rgba)(buffer, image.buffer, pdiff, pixels); + } + break; + } + case ZM_COLOUR_GRAY8: + (*fptr_delta8_gray8)(buffer, image.buffer, pdiff, pixels); + break; + default: + Panic("Delta called with unexpected colours: %d",colours); + break; + } + #ifdef ZM_IMAGE_PROFILING - clock_gettime(CLOCK_THREAD_CPUTIME_ID,&end); - timespec_diff(&start,&end,&diff); - - executetime = (1000000000ull * diff.tv_sec) + diff.tv_nsec; - milpixels = (unsigned long)((long double)pixels)/((((long double)executetime)/1000)); - Debug(5, "Delta: %u delta pixels generated in %llu nanoseconds, %lu million pixels/s\n",pixels,executetime,milpixels); + clock_gettime(CLOCK_THREAD_CPUTIME_ID,&end); + timespec_diff(&start,&end,&diff); + + executetime = (1000000000ull * diff.tv_sec) + diff.tv_nsec; + milpixels = (unsigned long)((long double)pixels)/((((long double)executetime)/1000)); + Debug(5, "Delta: %u delta pixels generated in %llu nanoseconds, %lu million pixels/s\n",pixels,executetime,milpixels); #endif } const Coord Image::centreCoord( const char *text ) const { - int index = 0; - int line_no = 0; - int text_len = strlen( text ); - int line_len = 0; - int max_line_len = 0; - const char *line = text; + int index = 0; + int line_no = 0; + int text_len = strlen( text ); + int line_len = 0; + int max_line_len = 0; + const char *line = text; - while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) + while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) + { + if ( line_len > max_line_len ) + max_line_len = line_len; + + index += line_len; + while ( text[index] == '\n' ) { - if ( line_len > max_line_len ) - max_line_len = line_len; - - index += line_len; - while ( text[index] == '\n' ) - { - index++; - } - line = text+index; - line_no++; + index++; } - int x = (width - (max_line_len * CHAR_WIDTH) ) / 2; - int y = (height - (line_no * LINE_HEIGHT) ) / 2; - return( Coord( x, y ) ); + line = text+index; + line_no++; + } + int x = (width - (max_line_len * CHAR_WIDTH) ) / 2; + int y = (height - (line_no * LINE_HEIGHT) ) / 2; + return( Coord( x, y ) ); } /* RGB32 compatible: complete */ -void Image::Annotate( const char *p_text, const Coord &coord, const Rgb fg_colour, const Rgb bg_colour ) +void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour ) { - strncpy( text, p_text, sizeof(text) ); + const uint8_t pixel_r_col = RED_VAL_RGBA(pixel_colour); + const uint8_t pixel_g_col = GREEN_VAL_RGBA(pixel_colour); + const uint8_t pixel_b_col = BLUE_VAL_RGBA(pixel_colour); + const uint8_t pixel_bw_col = pixel_colour & 0xff; + const Rgb pixel_rgb_col = rgb_convert(pixel_colour,subpixelorder); - unsigned int index = 0; - unsigned int line_no = 0; - unsigned int text_len = strlen( text ); - unsigned int line_len = 0; - const char *line = text; + unsigned char *ptr = &buffer[0]; + unsigned int i = 0; - const uint8_t fg_r_col = RED_VAL_RGBA(fg_colour); - const uint8_t fg_g_col = GREEN_VAL_RGBA(fg_colour); - const uint8_t fg_b_col = BLUE_VAL_RGBA(fg_colour); - const uint8_t fg_bw_col = fg_colour & 0xff; - const Rgb fg_rgb_col = rgb_convert(fg_colour,subpixelorder); - const bool fg_trans = (fg_colour == RGB_TRANSPARENT); - - const uint8_t bg_r_col = RED_VAL_RGBA(bg_colour); - const uint8_t bg_g_col = GREEN_VAL_RGBA(bg_colour); - const uint8_t bg_b_col = BLUE_VAL_RGBA(bg_colour); - const uint8_t bg_bw_col = bg_colour & 0xff; - const Rgb bg_rgb_col = rgb_convert(bg_colour,subpixelorder); - const bool bg_trans = (bg_colour == RGB_TRANSPARENT); - - while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) + for ( unsigned int y = 0; y < height; y++ ) + { + if ( colours == ZM_COLOUR_GRAY8 ) { - - unsigned int line_width = line_len * CHAR_WIDTH; - - unsigned int lo_line_x = coord.X(); - unsigned int lo_line_y = coord.Y() + (line_no * LINE_HEIGHT); - - unsigned int min_line_x = 0; - unsigned int max_line_x = width - line_width; - unsigned int min_line_y = 0; - unsigned int max_line_y = height - LINE_HEIGHT; - - if ( lo_line_x > max_line_x ) - lo_line_x = max_line_x; - if ( lo_line_x < min_line_x ) - lo_line_x = min_line_x; - if ( lo_line_y > max_line_y ) - lo_line_y = max_line_y; - if ( lo_line_y < min_line_y ) - lo_line_y = min_line_y; - - unsigned int hi_line_x = lo_line_x + line_width; - unsigned int hi_line_y = lo_line_y + LINE_HEIGHT; - - // Clip anything that runs off the right of the screen - if ( hi_line_x > width ) - hi_line_x = width; - if ( hi_line_y > height ) - hi_line_y = height; - - if ( colours == ZM_COLOUR_GRAY8 ) - { - unsigned char *ptr = &buffer[(lo_line_y*width)+lo_line_x]; - for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < CHAR_HEIGHT; y++, r++, ptr += width ) - { - unsigned char *temp_ptr = ptr; - for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) - { - int f = fontdata[(line[c] * CHAR_HEIGHT) + r]; - for ( unsigned int i = 0; i < CHAR_WIDTH && x < hi_line_x; i++, x++, temp_ptr++ ) - { - if ( f & (0x80 >> i) ) - { - if ( !fg_trans ) - *temp_ptr = fg_bw_col; - } - else if ( !bg_trans ) - { - *temp_ptr = bg_bw_col; - } - } - } - } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - unsigned int wc = width * colours; - - unsigned char *ptr = &buffer[((lo_line_y*width)+lo_line_x)*colours]; - for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < CHAR_HEIGHT; y++, r++, ptr += wc ) - { - unsigned char *temp_ptr = ptr; - for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) - { - int f = fontdata[(line[c] * CHAR_HEIGHT) + r]; - for ( unsigned int i = 0; i < CHAR_WIDTH && x < hi_line_x; i++, x++, temp_ptr += colours ) - { - if ( f & (0x80 >> i) ) - { - if ( !fg_trans ) - { - RED_PTR_RGBA(temp_ptr) = fg_r_col; - GREEN_PTR_RGBA(temp_ptr) = fg_g_col; - BLUE_PTR_RGBA(temp_ptr) = fg_b_col; - } - } - else if ( !bg_trans ) - { - RED_PTR_RGBA(temp_ptr) = bg_r_col; - GREEN_PTR_RGBA(temp_ptr) = bg_g_col; - BLUE_PTR_RGBA(temp_ptr) = bg_b_col; - } - } - } - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - unsigned int wc = width * colours; - - uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x)<<2]; - for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < CHAR_HEIGHT; y++, r++, ptr += wc ) - { - Rgb* temp_ptr = (Rgb*)ptr; - for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) - { - int f = fontdata[(line[c] * CHAR_HEIGHT) + r]; - for ( unsigned int i = 0; i < CHAR_WIDTH && x < hi_line_x; i++, x++, temp_ptr++ ) - { - if ( f & (0x80 >> i) ) - { - if ( !fg_trans ) - { - *temp_ptr = fg_rgb_col; - } - } - else if ( !bg_trans ) - { - *temp_ptr = bg_rgb_col; - } - } - } - } - - } else { - Panic("Annontate called with unexpected colours: %d",colours); - return; - } - - index += line_len; - while ( text[index] == '\n' ) - { - index++; - } - line = text+index; - line_no++; + for ( unsigned int x = 0; x < width; x++, ptr++ ) + { + if ( p_bitmask[i] ) + *ptr = pixel_bw_col; + i++; + } } + else if ( colours == ZM_COLOUR_RGB24 ) + { + for ( unsigned int x = 0; x < width; x++, ptr += colours ) + { + if ( p_bitmask[i] ) + { + RED_PTR_RGBA(ptr) = pixel_r_col; + GREEN_PTR_RGBA(ptr) = pixel_g_col; + BLUE_PTR_RGBA(ptr) = pixel_b_col; + } + i++; + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + for ( unsigned int x = 0; x < width; x++, ptr += colours ) + { + Rgb *temp_ptr = (Rgb*)ptr; + if ( p_bitmask[i] ) + *temp_ptr = pixel_rgb_col; + i++; + } + } else { + Panic("MaskPrivacy called with unexpected colours: %d", colours); + return; + } + + } } -void Image::Timestamp( const char *label, const time_t when, const Coord &coord ) +/* RGB32 compatible: complete */ +void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int size, const Rgb fg_colour, const Rgb bg_colour ) { - char time_text[64]; - strftime( time_text, sizeof(time_text), "%y/%m/%d %H:%M:%S", localtime( &when ) ); - char text[64]; - if ( label ) - { - snprintf( text, sizeof(text), "%s - %s", label, time_text ); - Annotate( text, coord ); - } - else - { - Annotate( time_text, coord ); - } + strncpy( text, p_text, sizeof(text) ); + + unsigned int index = 0; + unsigned int line_no = 0; + unsigned int text_len = strlen( text ); + unsigned int line_len = 0; + const char *line = text; + + const uint8_t fg_r_col = RED_VAL_RGBA(fg_colour); + const uint8_t fg_g_col = GREEN_VAL_RGBA(fg_colour); + const uint8_t fg_b_col = BLUE_VAL_RGBA(fg_colour); + const uint8_t fg_bw_col = fg_colour & 0xff; + const Rgb fg_rgb_col = rgb_convert(fg_colour,subpixelorder); + const bool fg_trans = (fg_colour == RGB_TRANSPARENT); + + const uint8_t bg_r_col = RED_VAL_RGBA(bg_colour); + const uint8_t bg_g_col = GREEN_VAL_RGBA(bg_colour); + const uint8_t bg_b_col = BLUE_VAL_RGBA(bg_colour); + const uint8_t bg_bw_col = bg_colour & 0xff; + const Rgb bg_rgb_col = rgb_convert(bg_colour,subpixelorder); + const bool bg_trans = (bg_colour == RGB_TRANSPARENT); + + int zm_text_bitmask = 0x80; + if (size == 2) + zm_text_bitmask = 0x8000; + + while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) + { + + unsigned int line_width = line_len * CHAR_WIDTH * size; + + unsigned int lo_line_x = coord.X(); + unsigned int lo_line_y = coord.Y() + (line_no * LINE_HEIGHT * size); + + unsigned int min_line_x = 0; + unsigned int max_line_x = width - line_width; + unsigned int min_line_y = 0; + unsigned int max_line_y = height - (LINE_HEIGHT * size); + + if ( lo_line_x > max_line_x ) + lo_line_x = max_line_x; + if ( lo_line_x < min_line_x ) + lo_line_x = min_line_x; + if ( lo_line_y > max_line_y ) + lo_line_y = max_line_y; + if ( lo_line_y < min_line_y ) + lo_line_y = min_line_y; + + unsigned int hi_line_x = lo_line_x + line_width; + unsigned int hi_line_y = lo_line_y + (LINE_HEIGHT * size); + + // Clip anything that runs off the right of the screen + if ( hi_line_x > width ) + hi_line_x = width; + if ( hi_line_y > height ) + hi_line_y = height; + + if ( colours == ZM_COLOUR_GRAY8 ) + { + unsigned char *ptr = &buffer[(lo_line_y*width)+lo_line_x]; + for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (CHAR_HEIGHT * size); y++, r++, ptr += width ) + { + unsigned char *temp_ptr = ptr; + for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) + { + int f; + if (size == 2) + f = bigfontdata[(line[c] * CHAR_HEIGHT * size) + r]; + else + f = fontdata[(line[c] * CHAR_HEIGHT) + r]; + for ( unsigned int i = 0; i < (CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) + { + if ( f & (zm_text_bitmask >> i) ) + { + if ( !fg_trans ) + *temp_ptr = fg_bw_col; + } + else if ( !bg_trans ) + { + *temp_ptr = bg_bw_col; + } + } + } + } + } + else if ( colours == ZM_COLOUR_RGB24 ) + { + unsigned int wc = width * colours; + + unsigned char *ptr = &buffer[((lo_line_y*width)+lo_line_x)*colours]; + for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (CHAR_HEIGHT * size); y++, r++, ptr += wc ) + { + unsigned char *temp_ptr = ptr; + for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) + { + int f; + if (size == 2) + f = bigfontdata[(line[c] * CHAR_HEIGHT * size) + r]; + else + f = fontdata[(line[c] * CHAR_HEIGHT) + r]; + for ( unsigned int i = 0; i < (CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) + { + if ( f & (zm_text_bitmask >> i) ) + { + if ( !fg_trans ) + { + RED_PTR_RGBA(temp_ptr) = fg_r_col; + GREEN_PTR_RGBA(temp_ptr) = fg_g_col; + BLUE_PTR_RGBA(temp_ptr) = fg_b_col; + } + } + else if ( !bg_trans ) + { + RED_PTR_RGBA(temp_ptr) = bg_r_col; + GREEN_PTR_RGBA(temp_ptr) = bg_g_col; + BLUE_PTR_RGBA(temp_ptr) = bg_b_col; + } + } + } + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + unsigned int wc = width * colours; + + uint8_t *ptr = &buffer[((lo_line_y*width)+lo_line_x)<<2]; + for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (CHAR_HEIGHT * size); y++, r++, ptr += wc ) + { + Rgb* temp_ptr = (Rgb*)ptr; + for ( unsigned int x = lo_line_x, c = 0; x < hi_line_x && c < line_len; c++ ) + { + int f; + if (size == 2) + f = bigfontdata[(line[c] * CHAR_HEIGHT * size) + r]; + else + f = fontdata[(line[c] * CHAR_HEIGHT) + r]; + for ( unsigned int i = 0; i < (CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) + { + if ( f & (zm_text_bitmask >> i) ) + { + if ( !fg_trans ) + { + *temp_ptr = fg_rgb_col; + } + } + else if ( !bg_trans ) + { + *temp_ptr = bg_rgb_col; + } + } + } + } + + } else { + Panic("Annotate called with unexpected colours: %d",colours); + return; + } + + index += line_len; + while ( text[index] == '\n' ) + { + index++; + } + line = text+index; + line_no++; + } +} + +void Image::Timestamp( const char *label, const time_t when, const Coord &coord, const int size ) +{ + char time_text[64]; + strftime( time_text, sizeof(time_text), "%y/%m/%d %H:%M:%S", localtime( &when ) ); + char text[64]; + if ( label ) + { + snprintf( text, sizeof(text), "%s - %s", label, time_text ); + Annotate( text, coord, size ); + } + else + { + Annotate( time_text, coord, size ); + } } /* RGB32 compatible: complete */ void Image::Colourise(const unsigned int p_reqcolours, const unsigned int p_reqsubpixelorder) { - Debug(9, "Colourise: Req colours: %u Req subpixel order: %u Current colours: %u Current subpixel order: %u",p_reqcolours,p_reqsubpixelorder,colours,subpixelorder); - - if ( colours != ZM_COLOUR_GRAY8) { - Warning("Target image is already colourised, colours: %u",colours); - return; - } - - if ( p_reqcolours == ZM_COLOUR_RGB32 ) { - /* RGB32 */ - Rgb* new_buffer = (Rgb*)AllocBuffer(pixels*sizeof(Rgb)); - - const uint8_t *psrc = buffer; - Rgb* pdest = new_buffer; - Rgb subpixel; - Rgb newpixel; - - if ( p_reqsubpixelorder == ZM_SUBPIX_ORDER_ABGR || p_reqsubpixelorder == ZM_SUBPIX_ORDER_ARGB) { - /* ARGB\ABGR subpixel order. alpha byte is first (mem+0), so we need to shift the pixel left in the end */ - for(unsigned int i=0;iLo().X():0; - unsigned int lo_y = limits?limits->Lo().Y():0; - unsigned int hi_x = limits?limits->Hi().X():width-1; - unsigned int hi_y = limits?limits->Hi().Y():height-1; - if ( colours == ZM_COLOUR_GRAY8 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - unsigned char *p = &buffer[(y*width)+lo_x]; - for ( unsigned int x = lo_x; x <= hi_x; x++, p++) - { - *p = colour; - } - } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - unsigned char *p = &buffer[colours*((y*width)+lo_x)]; - for ( unsigned int x = lo_x; x <= hi_x; x++, p += 3) - { - RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); - GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); - BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); - } - } - } - else if ( colours == ZM_COLOUR_RGB32 ) /* RGB32 */ - { - for ( unsigned int y = lo_y; y <= (unsigned int)hi_y; y++ ) - { - Rgb *p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; - - for ( unsigned int x = lo_x; x <= (unsigned int)hi_x; x++, p++) - { - /* Fast, copies the entire pixel in a single pass */ - *p = colour; - } - } - } + if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) + { + Panic( "Attempt to fill image with unexpected colours %d", colours ); + } + + /* Convert the colour's RGBA subpixel order into the image's subpixel order */ + colour = rgb_convert(colour,subpixelorder); + + unsigned int lo_x = limits?limits->Lo().X():0; + unsigned int lo_y = limits?limits->Lo().Y():0; + unsigned int hi_x = limits?limits->Hi().X():width-1; + unsigned int hi_y = limits?limits->Hi().Y():height-1; + if ( colours == ZM_COLOUR_GRAY8 ) + { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + unsigned char *p = &buffer[(y*width)+lo_x]; + for ( unsigned int x = lo_x; x <= hi_x; x++, p++) + { + *p = colour; + } + } + } + else if ( colours == ZM_COLOUR_RGB24 ) + { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + unsigned char *p = &buffer[colours*((y*width)+lo_x)]; + for ( unsigned int x = lo_x; x <= hi_x; x++, p += 3) + { + RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); + GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); + BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); + } + } + } + else if ( colours == ZM_COLOUR_RGB32 ) /* RGB32 */ + { + for ( unsigned int y = lo_y; y <= (unsigned int)hi_y; y++ ) + { + Rgb *p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; + + for ( unsigned int x = lo_x; x <= (unsigned int)hi_x; x++, p++) + { + /* Fast, copies the entire pixel in a single pass */ + *p = colour; + } + } + } } /* RGB32 compatible: complete */ void Image::Fill( Rgb colour, int density, const Box *limits ) { - /* Allow the faster version to be used if density is not used (density=1) */ - if(density <= 1) - return Fill(colour,limits); - - if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) - { - Panic( "Attempt to fill image with unexpected colours %d", colours ); - } - - /* Convert the colour's RGBA subpixel order into the image's subpixel order */ - colour = rgb_convert(colour,subpixelorder); + /* Allow the faster version to be used if density is not used (density=1) */ + if(density <= 1) + return Fill(colour,limits); + + if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) + { + Panic( "Attempt to fill image with unexpected colours %d", colours ); + } + + /* Convert the colour's RGBA subpixel order into the image's subpixel order */ + colour = rgb_convert(colour,subpixelorder); - unsigned int lo_x = limits?limits->Lo().X():0; - unsigned int lo_y = limits?limits->Lo().Y():0; - unsigned int hi_x = limits?limits->Hi().X():width-1; - unsigned int hi_y = limits?limits->Hi().Y():height-1; - if ( colours == ZM_COLOUR_GRAY8 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - unsigned char *p = &buffer[(y*width)+lo_x]; - for ( unsigned int x = lo_x; x <= hi_x; x++, p++) - { - if ( ( x == lo_x || x == hi_x || y == lo_y || y == hi_y ) || (!(x%density) && !(y%density) ) ) - *p = colour; - } - } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - unsigned char *p = &buffer[colours*((y*width)+lo_x)]; - for ( unsigned int x = lo_x; x <= hi_x; x++, p += 3) - { - if ( ( x == lo_x || x == hi_x || y == lo_y || y == hi_y ) || (!(x%density) && !(y%density) ) ) { - RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); - GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); - BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); - } - } - } - } - else if ( colours == ZM_COLOUR_RGB32 ) /* RGB32 */ - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - Rgb* p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; + unsigned int lo_x = limits?limits->Lo().X():0; + unsigned int lo_y = limits?limits->Lo().Y():0; + unsigned int hi_x = limits?limits->Hi().X():width-1; + unsigned int hi_y = limits?limits->Hi().Y():height-1; + if ( colours == ZM_COLOUR_GRAY8 ) + { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + unsigned char *p = &buffer[(y*width)+lo_x]; + for ( unsigned int x = lo_x; x <= hi_x; x++, p++) + { + if ( ( x == lo_x || x == hi_x || y == lo_y || y == hi_y ) || (!(x%density) && !(y%density) ) ) + *p = colour; + } + } + } + else if ( colours == ZM_COLOUR_RGB24 ) + { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + unsigned char *p = &buffer[colours*((y*width)+lo_x)]; + for ( unsigned int x = lo_x; x <= hi_x; x++, p += 3) + { + if ( ( x == lo_x || x == hi_x || y == lo_y || y == hi_y ) || (!(x%density) && !(y%density) ) ) { + RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); + GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); + BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); + } + } + } + } + else if ( colours == ZM_COLOUR_RGB32 ) /* RGB32 */ + { + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + Rgb* p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; - for ( unsigned int x = lo_x; x <= hi_x; x++, p++) - { - if ( ( x == lo_x || x == hi_x || y == lo_y || y == hi_y ) || (!(x%density) && !(y%density) ) ) - /* Fast, copies the entire pixel in a single pass */ - *p = colour; - } - } - } - + for ( unsigned int x = lo_x; x <= hi_x; x++, p++) + { + if ( ( x == lo_x || x == hi_x || y == lo_y || y == hi_y ) || (!(x%density) && !(y%density) ) ) + /* Fast, copies the entire pixel in a single pass */ + *p = colour; + } + } + } + } /* RGB32 compatible: complete */ void Image::Outline( Rgb colour, const Polygon &polygon ) { - if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) - { - Panic( "Attempt to outline image with unexpected colours %d", colours ); - } - - /* Convert the colour's RGBA subpixel order into the image's subpixel order */ - colour = rgb_convert(colour,subpixelorder); - - int n_coords = polygon.getNumCoords(); - for ( int j = 0, i = n_coords-1; j < n_coords; i = j++ ) - { - const Coord &p1 = polygon.getCoord( i ); - const Coord &p2 = polygon.getCoord( j ); + if ( !(colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB24 || colours == ZM_COLOUR_RGB32 ) ) + { + Panic( "Attempt to outline image with unexpected colours %d", colours ); + } + + /* Convert the colour's RGBA subpixel order into the image's subpixel order */ + colour = rgb_convert(colour,subpixelorder); + + int n_coords = polygon.getNumCoords(); + for ( int j = 0, i = n_coords-1; j < n_coords; i = j++ ) + { + const Coord &p1 = polygon.getCoord( i ); + const Coord &p2 = polygon.getCoord( j ); - int x1 = p1.X(); - int x2 = p2.X(); - int y1 = p1.Y(); - int y2 = p2.Y(); + int x1 = p1.X(); + int x2 = p2.X(); + int y1 = p1.Y(); + int y2 = p2.Y(); - double dx = x2 - x1; - double dy = y2 - y1; + double dx = x2 - x1; + double dy = y2 - y1; - double grad; + double grad; - //Debug( 9, "dx: %.2lf, dy: %.2lf", dx, dy ); - if ( fabs(dx) <= fabs(dy) ) - { - //Debug( 9, "dx <= dy" ); - if ( y1 != y2 ) - grad = dx/dy; - else - grad = width; + //Debug( 9, "dx: %.2lf, dy: %.2lf", dx, dy ); + if ( fabs(dx) <= fabs(dy) ) + { + //Debug( 9, "dx <= dy" ); + if ( y1 != y2 ) + grad = dx/dy; + else + grad = width; - double x; - int y, yinc = (y1 dy" ); - if ( x1 != x2 ) - grad = dy/dx; - else - grad = height; - //Debug( 9, "grad: %.2lf", grad ); + double x; + int y, yinc = (y1 dy" ); + if ( x1 != x2 ) + grad = dy/dx; + else + grad = height; + //Debug( 9, "grad: %.2lf", grad ); - double y; - int x, xinc = (x1= Logger::DEBUG9 ) - { - for ( int i = 0; i < n_global_edges; i++ ) - { - Debug( 9, "%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", i, global_edges[i].min_y, global_edges[i].max_y, global_edges[i].min_x, global_edges[i]._1_m ); - } - } + if ( logLevel() >= Logger::DEBUG9 ) + { + for ( int i = 0; i < n_global_edges; i++ ) + { + Debug( 9, "%d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", i, global_edges[i].min_y, global_edges[i].max_y, global_edges[i].min_x, global_edges[i]._1_m ); + } + } #endif - int n_active_edges = 0; - Edge active_edges[n_global_edges]; - int y = global_edges[0].min_y; - do - { - for ( int i = 0; i < n_global_edges; i++ ) - { - if ( global_edges[i].min_y == y ) - { - Debug( 9, "Moving global edge" ); - active_edges[n_active_edges++] = global_edges[i]; - if ( i < (n_global_edges-1) ) - { - //memcpy( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); - memmove( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); - i--; - } - n_global_edges--; - } - else - { - break; - } - } - qsort( active_edges, n_active_edges, sizeof(*active_edges), Edge::CompareX ); + int n_active_edges = 0; + Edge active_edges[n_global_edges]; + int y = global_edges[0].min_y; + do + { + for ( int i = 0; i < n_global_edges; i++ ) + { + if ( global_edges[i].min_y == y ) + { + Debug( 9, "Moving global edge" ); + active_edges[n_active_edges++] = global_edges[i]; + if ( i < (n_global_edges-1) ) + { + //memcpy( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); + memmove( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) ); + i--; + } + n_global_edges--; + } + else + { + break; + } + } + qsort( active_edges, n_active_edges, sizeof(*active_edges), Edge::CompareX ); #ifndef ZM_DBG_OFF - if ( logLevel() >= Logger::DEBUG9 ) - { - for ( int i = 0; i < n_active_edges; i++ ) - { - Debug( 9, "%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", y, i, active_edges[i].min_y, active_edges[i].max_y, active_edges[i].min_x, active_edges[i]._1_m ); - } - } + if ( logLevel() >= Logger::DEBUG9 ) + { + for ( int i = 0; i < n_active_edges; i++ ) + { + Debug( 9, "%d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f", y, i, active_edges[i].min_y, active_edges[i].max_y, active_edges[i].min_x, active_edges[i]._1_m ); + } + } #endif - if ( !(y%density) ) - { - //Debug( 9, "%d", y ); - for ( int i = 0; i < n_active_edges; ) - { - int lo_x = int(round(active_edges[i++].min_x)); - int hi_x = int(round(active_edges[i++].min_x)); - if( colours == ZM_COLOUR_GRAY8 ) { - unsigned char *p = &buffer[(y*width)+lo_x]; - for ( int x = lo_x; x <= hi_x; x++, p++) - { - if ( !(x%density) ) - { - //Debug( 9, " %d", x ); - *p = colour; - } - } - } else if( colours == ZM_COLOUR_RGB24 ) { - unsigned char *p = &buffer[colours*((y*width)+lo_x)]; - for ( int x = lo_x; x <= hi_x; x++, p += 3) - { - if ( !(x%density) ) - { - RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); - GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); - BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); - } - } - } else if( colours == ZM_COLOUR_RGB32 ) { - Rgb *p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; - for ( int x = lo_x; x <= hi_x; x++, p++) - { - if ( !(x%density) ) - { - /* Fast, copies the entire pixel in a single pass */ - *p = colour; - } - } - } - } - } - y++; - for ( int i = n_active_edges-1; i >= 0; i-- ) - { - if ( y >= active_edges[i].max_y ) // Or >= as per sheets - { - Debug( 9, "Deleting active_edge" ); - if ( i < (n_active_edges-1) ) - { - //memcpy( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); - memmove( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); - } - n_active_edges--; - } - else - { - active_edges[i].min_x += active_edges[i]._1_m; - } - } - } while ( n_global_edges || n_active_edges ); + if ( !(y%density) ) + { + //Debug( 9, "%d", y ); + for ( int i = 0; i < n_active_edges; ) + { + int lo_x = int(round(active_edges[i++].min_x)); + int hi_x = int(round(active_edges[i++].min_x)); + if( colours == ZM_COLOUR_GRAY8 ) { + unsigned char *p = &buffer[(y*width)+lo_x]; + for ( int x = lo_x; x <= hi_x; x++, p++) + { + if ( !(x%density) ) + { + //Debug( 9, " %d", x ); + *p = colour; + } + } + } else if( colours == ZM_COLOUR_RGB24 ) { + unsigned char *p = &buffer[colours*((y*width)+lo_x)]; + for ( int x = lo_x; x <= hi_x; x++, p += 3) + { + if ( !(x%density) ) + { + RED_PTR_RGBA(p) = RED_VAL_RGBA(colour); + GREEN_PTR_RGBA(p) = GREEN_VAL_RGBA(colour); + BLUE_PTR_RGBA(p) = BLUE_VAL_RGBA(colour); + } + } + } else if( colours == ZM_COLOUR_RGB32 ) { + Rgb *p = (Rgb*)&buffer[((y*width)+lo_x)<<2]; + for ( int x = lo_x; x <= hi_x; x++, p++) + { + if ( !(x%density) ) + { + /* Fast, copies the entire pixel in a single pass */ + *p = colour; + } + } + } + } + } + y++; + for ( int i = n_active_edges-1; i >= 0; i-- ) + { + if ( y >= active_edges[i].max_y ) // Or >= as per sheets + { + Debug( 9, "Deleting active_edge" ); + if ( i < (n_active_edges-1) ) + { + //memcpy( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); + memmove( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) ); + } + n_active_edges--; + } + else + { + active_edges[i].min_x += active_edges[i]._1_m; + } + } + } while ( n_global_edges || n_active_edges ); } void Image::Fill( Rgb colour, const Polygon &polygon ) { - Fill( colour, 1, polygon ); + Fill( colour, 1, polygon ); } /* RGB32 compatible: complete */ void Image::Rotate( int angle ) { - - angle %= 360; + + angle %= 360; - if ( !angle ) - { - return; - } - if ( angle%90 ) - { - return; - } - - unsigned int new_height = height; - unsigned int new_width = width; - uint8_t* rotate_buffer = AllocBuffer(size); + if ( !angle ) + { + return; + } + if ( angle%90 ) + { + return; + } + + unsigned int new_height = height; + unsigned int new_width = width; + uint8_t* rotate_buffer = AllocBuffer(size); - switch( angle ) - { - case 90 : - { - new_height = width; - new_width = height; + switch( angle ) + { + case 90 : + { + new_height = width; + new_width = height; - unsigned int line_bytes = new_width*colours; - unsigned char *s_ptr = buffer; + unsigned int line_bytes = new_width*colours; + unsigned char *s_ptr = buffer; - if ( colours == ZM_COLOUR_GRAY8 ) - { - unsigned char *d_ptr; - for ( unsigned int i = new_width; i > 0; i-- ) - { - d_ptr = rotate_buffer+(i-1); - for ( unsigned int j = new_height; j > 0; j-- ) - { - *d_ptr = *s_ptr++; - d_ptr += line_bytes; - } - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - Rgb* s_rptr = (Rgb*)s_ptr; - Rgb* d_rptr; - for ( unsigned int i = new_width; i > 0; i-- ) - { - d_rptr = (Rgb*)(rotate_buffer+((i-1)<<2)); - for ( unsigned int j = new_height; j > 0; j-- ) - { - *d_rptr = *s_rptr++; - d_rptr += new_width; - } - } - } - else /* Assume RGB24 */ - { - unsigned char *d_ptr; - for ( unsigned int i = new_width; i > 0; i-- ) - { - d_ptr = rotate_buffer+((i-1)*3); - for ( unsigned int j = new_height; j > 0; j-- ) - { - *d_ptr = *s_ptr++; - *(d_ptr+1) = *s_ptr++; - *(d_ptr+2) = *s_ptr++; - d_ptr += line_bytes; - } - } - } - break; - } - case 180 : - { - unsigned char *s_ptr = buffer+size; - unsigned char *d_ptr = rotate_buffer; + if ( colours == ZM_COLOUR_GRAY8 ) + { + unsigned char *d_ptr; + for ( unsigned int i = new_width; i > 0; i-- ) + { + d_ptr = rotate_buffer+(i-1); + for ( unsigned int j = new_height; j > 0; j-- ) + { + *d_ptr = *s_ptr++; + d_ptr += line_bytes; + } + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + Rgb* s_rptr = (Rgb*)s_ptr; + Rgb* d_rptr; + for ( unsigned int i = new_width; i > 0; i-- ) + { + d_rptr = (Rgb*)(rotate_buffer+((i-1)<<2)); + for ( unsigned int j = new_height; j > 0; j-- ) + { + *d_rptr = *s_rptr++; + d_rptr += new_width; + } + } + } + else /* Assume RGB24 */ + { + unsigned char *d_ptr; + for ( unsigned int i = new_width; i > 0; i-- ) + { + d_ptr = rotate_buffer+((i-1)*3); + for ( unsigned int j = new_height; j > 0; j-- ) + { + *d_ptr = *s_ptr++; + *(d_ptr+1) = *s_ptr++; + *(d_ptr+2) = *s_ptr++; + d_ptr += line_bytes; + } + } + } + break; + } + case 180 : + { + unsigned char *s_ptr = buffer+size; + unsigned char *d_ptr = rotate_buffer; - if ( colours == ZM_COLOUR_GRAY8 ) - { - while( s_ptr > buffer ) - { - s_ptr--; - *d_ptr++ = *s_ptr; - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - Rgb* s_rptr = (Rgb*)s_ptr; - Rgb* d_rptr = (Rgb*)d_ptr; - while( s_rptr > (Rgb*)buffer ) - { - s_rptr--; - *d_rptr++ = *s_rptr; - } - } - else /* Assume RGB24 */ - { - while( s_ptr > buffer ) - { - s_ptr -= 3; - *d_ptr++ = *s_ptr; - *d_ptr++ = *(s_ptr+1); - *d_ptr++ = *(s_ptr+2); - } - } - break; - } - case 270 : - { - new_height = width; - new_width = height; + if ( colours == ZM_COLOUR_GRAY8 ) + { + while( s_ptr > buffer ) + { + s_ptr--; + *d_ptr++ = *s_ptr; + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + Rgb* s_rptr = (Rgb*)s_ptr; + Rgb* d_rptr = (Rgb*)d_ptr; + while( s_rptr > (Rgb*)buffer ) + { + s_rptr--; + *d_rptr++ = *s_rptr; + } + } + else /* Assume RGB24 */ + { + while( s_ptr > buffer ) + { + s_ptr -= 3; + *d_ptr++ = *s_ptr; + *d_ptr++ = *(s_ptr+1); + *d_ptr++ = *(s_ptr+2); + } + } + break; + } + case 270 : + { + new_height = width; + new_width = height; - unsigned int line_bytes = new_width*colours; - unsigned char *s_ptr = buffer+size; + unsigned int line_bytes = new_width*colours; + unsigned char *s_ptr = buffer+size; - if ( colours == ZM_COLOUR_GRAY8 ) - { - unsigned char *d_ptr; - for ( unsigned int i = new_width; i > 0; i-- ) - { - d_ptr = rotate_buffer+(i-1); - for ( unsigned int j = new_height; j > 0; j-- ) - { - s_ptr--; - *d_ptr = *s_ptr; - d_ptr += line_bytes; - } - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - Rgb* s_rptr = (Rgb*)s_ptr; - Rgb* d_rptr; - for ( unsigned int i = new_width; i > 0; i-- ) - { - d_rptr = (Rgb*)(rotate_buffer+((i-1)<<2)); - for ( unsigned int j = new_height; j > 0; j-- ) - { - s_rptr--; - *d_rptr = *s_rptr; - d_rptr += new_width; - } - } - } - else /* Assume RGB24 */ - { - unsigned char *d_ptr; - for ( unsigned int i = new_width; i > 0; i-- ) - { - d_ptr = rotate_buffer+((i-1)*3); - for ( unsigned int j = new_height; j > 0; j-- ) - { - *(d_ptr+2) = *(--s_ptr); - *(d_ptr+1) = *(--s_ptr); - *d_ptr = *(--s_ptr); - d_ptr += line_bytes; - } - } - } - break; - } - } + if ( colours == ZM_COLOUR_GRAY8 ) + { + unsigned char *d_ptr; + for ( unsigned int i = new_width; i > 0; i-- ) + { + d_ptr = rotate_buffer+(i-1); + for ( unsigned int j = new_height; j > 0; j-- ) + { + s_ptr--; + *d_ptr = *s_ptr; + d_ptr += line_bytes; + } + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + Rgb* s_rptr = (Rgb*)s_ptr; + Rgb* d_rptr; + for ( unsigned int i = new_width; i > 0; i-- ) + { + d_rptr = (Rgb*)(rotate_buffer+((i-1)<<2)); + for ( unsigned int j = new_height; j > 0; j-- ) + { + s_rptr--; + *d_rptr = *s_rptr; + d_rptr += new_width; + } + } + } + else /* Assume RGB24 */ + { + unsigned char *d_ptr; + for ( unsigned int i = new_width; i > 0; i-- ) + { + d_ptr = rotate_buffer+((i-1)*3); + for ( unsigned int j = new_height; j > 0; j-- ) + { + *(d_ptr+2) = *(--s_ptr); + *(d_ptr+1) = *(--s_ptr); + *d_ptr = *(--s_ptr); + d_ptr += line_bytes; + } + } + } + break; + } + } - AssignDirect( new_width, new_height, colours, subpixelorder, rotate_buffer, size, ZM_BUFTYPE_ZM); - + AssignDirect( new_width, new_height, colours, subpixelorder, rotate_buffer, size, ZM_BUFTYPE_ZM); + } /* RGB32 compatible: complete */ void Image::Flip( bool leftright ) { - uint8_t* flip_buffer = AllocBuffer(size); - - unsigned int line_bytes = width*colours; - unsigned int line_bytes2 = 2*line_bytes; - if ( leftright ) - { - // Horizontal flip, left to right - unsigned char *s_ptr = buffer+line_bytes; - unsigned char *d_ptr = flip_buffer; - unsigned char *max_d_ptr = flip_buffer + size; + uint8_t* flip_buffer = AllocBuffer(size); + + unsigned int line_bytes = width*colours; + unsigned int line_bytes2 = 2*line_bytes; + if ( leftright ) + { + // Horizontal flip, left to right + unsigned char *s_ptr = buffer+line_bytes; + unsigned char *d_ptr = flip_buffer; + unsigned char *max_d_ptr = flip_buffer + size; - if ( colours == ZM_COLOUR_GRAY8 ) - { - while( d_ptr < max_d_ptr ) - { - for ( unsigned int j = 0; j < width; j++ ) - { - s_ptr--; - *d_ptr++ = *s_ptr; - } - s_ptr += line_bytes2; - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - Rgb* s_rptr = (Rgb*)s_ptr; - Rgb* d_rptr = (Rgb*)flip_buffer; - Rgb* max_d_rptr = (Rgb*)max_d_ptr; - while( d_rptr < max_d_rptr ) - { - for ( unsigned int j = 0; j < width; j++ ) - { - s_rptr--; - *d_rptr++ = *s_rptr; - } - s_rptr += width * 2; - } - } - else /* Assume RGB24 */ - { - while( d_ptr < max_d_ptr ) - { - for ( unsigned int j = 0; j < width; j++ ) - { - s_ptr -= 3; - *d_ptr++ = *s_ptr; - *d_ptr++ = *(s_ptr+1); - *d_ptr++ = *(s_ptr+2); - } - s_ptr += line_bytes2; - } - } - } - else - { - // Vertical flip, top to bottom - unsigned char *s_ptr = buffer+(height*line_bytes); - unsigned char *d_ptr = flip_buffer; + if ( colours == ZM_COLOUR_GRAY8 ) + { + while( d_ptr < max_d_ptr ) + { + for ( unsigned int j = 0; j < width; j++ ) + { + s_ptr--; + *d_ptr++ = *s_ptr; + } + s_ptr += line_bytes2; + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + Rgb* s_rptr = (Rgb*)s_ptr; + Rgb* d_rptr = (Rgb*)flip_buffer; + Rgb* max_d_rptr = (Rgb*)max_d_ptr; + while( d_rptr < max_d_rptr ) + { + for ( unsigned int j = 0; j < width; j++ ) + { + s_rptr--; + *d_rptr++ = *s_rptr; + } + s_rptr += width * 2; + } + } + else /* Assume RGB24 */ + { + while( d_ptr < max_d_ptr ) + { + for ( unsigned int j = 0; j < width; j++ ) + { + s_ptr -= 3; + *d_ptr++ = *s_ptr; + *d_ptr++ = *(s_ptr+1); + *d_ptr++ = *(s_ptr+2); + } + s_ptr += line_bytes2; + } + } + } + else + { + // Vertical flip, top to bottom + unsigned char *s_ptr = buffer+(height*line_bytes); + unsigned char *d_ptr = flip_buffer; - while( s_ptr > buffer ) - { - s_ptr -= line_bytes; - memcpy( d_ptr, s_ptr, line_bytes ); - d_ptr += line_bytes; - } - } - - AssignDirect( width, height, colours, subpixelorder, flip_buffer, size, ZM_BUFTYPE_ZM); - + while( s_ptr > buffer ) + { + s_ptr -= line_bytes; + memcpy( d_ptr, s_ptr, line_bytes ); + d_ptr += line_bytes; + } + } + + AssignDirect( width, height, colours, subpixelorder, flip_buffer, size, ZM_BUFTYPE_ZM); + } void Image::Scale( unsigned int factor ) { - if ( !factor ) - { - Error( "Bogus scale factor %d found", factor ); - return; - } - if ( factor == ZM_SCALE_BASE ) - { - return; - } + if ( !factor ) + { + Error( "Bogus scale factor %d found", factor ); + return; + } + if ( factor == ZM_SCALE_BASE ) + { + return; + } - unsigned int new_width = (width*factor)/ZM_SCALE_BASE; - unsigned int new_height = (height*factor)/ZM_SCALE_BASE; - - size_t scale_buffer_size = new_width * new_height * colours; - - uint8_t* scale_buffer = AllocBuffer(scale_buffer_size); - - if ( factor > ZM_SCALE_BASE ) - { - unsigned char *pd = scale_buffer; - unsigned int wc = width*colours; - unsigned int nwc = new_width*colours; - unsigned int h_count = ZM_SCALE_BASE/2; - unsigned int last_h_index = 0; - unsigned int last_w_index = 0; - unsigned int h_index; - for ( unsigned int y = 0; y < height; y++ ) - { - unsigned char *ps = &buffer[y*wc]; - unsigned int w_count = ZM_SCALE_BASE/2; - unsigned int w_index; - last_w_index = 0; - for ( unsigned int x = 0; x < width; x++ ) - { - w_count += factor; - w_index = w_count/ZM_SCALE_BASE; - for (unsigned int f = last_w_index; f < w_index; f++ ) - { - for ( unsigned int c = 0; c < colours; c++ ) - { - *pd++ = *(ps+c); - } - } - ps += colours; - last_w_index = w_index; - } - h_count += factor; - h_index = h_count/ZM_SCALE_BASE; - for ( unsigned int f = last_h_index+1; f < h_index; f++ ) - { - memcpy( pd, pd-nwc, nwc ); - pd += nwc; - } - last_h_index = h_index; - } - new_width = last_w_index; - new_height = last_h_index; - } - else - { - unsigned char *pd = scale_buffer; - unsigned int wc = width*colours; - unsigned int xstart = factor/2; - unsigned int ystart = factor/2; - unsigned int h_count = ystart; - unsigned int last_h_index = 0; - unsigned int last_w_index = 0; - unsigned int h_index; - for ( unsigned int y = 0; y < (unsigned int)height; y++ ) - { - h_count += factor; - h_index = h_count/ZM_SCALE_BASE; - if ( h_index > last_h_index ) - { - unsigned int w_count = xstart; - unsigned int w_index; - last_w_index = 0; + unsigned int new_width = (width*factor)/ZM_SCALE_BASE; + unsigned int new_height = (height*factor)/ZM_SCALE_BASE; + + size_t scale_buffer_size = (new_width+1) * (new_height+1) * colours; + + uint8_t* scale_buffer = AllocBuffer(scale_buffer_size); + + if ( factor > ZM_SCALE_BASE ) + { + unsigned char *pd = scale_buffer; + unsigned int wc = width*colours; + unsigned int nwc = new_width*colours; + unsigned int h_count = ZM_SCALE_BASE/2; + unsigned int last_h_index = 0; + unsigned int last_w_index = 0; + unsigned int h_index; + for ( unsigned int y = 0; y < height; y++ ) + { + unsigned char *ps = &buffer[y*wc]; + unsigned int w_count = ZM_SCALE_BASE/2; + unsigned int w_index; + last_w_index = 0; + for ( unsigned int x = 0; x < width; x++ ) + { + w_count += factor; + w_index = w_count/ZM_SCALE_BASE; + for (unsigned int f = last_w_index; f < w_index; f++ ) + { + for ( unsigned int c = 0; c < colours; c++ ) + { + *pd++ = *(ps+c); + } + } + ps += colours; + last_w_index = w_index; + } + h_count += factor; + h_index = h_count/ZM_SCALE_BASE; + for ( unsigned int f = last_h_index+1; f < h_index; f++ ) + { + memcpy( pd, pd-nwc, nwc ); + pd += nwc; + } + last_h_index = h_index; + } + new_width = last_w_index; + new_height = last_h_index; + } + else + { + unsigned char *pd = scale_buffer; + unsigned int wc = width*colours; + unsigned int xstart = factor/2; + unsigned int ystart = factor/2; + unsigned int h_count = ystart; + unsigned int last_h_index = 0; + unsigned int last_w_index = 0; + unsigned int h_index; + for ( unsigned int y = 0; y < (unsigned int)height; y++ ) + { + h_count += factor; + h_index = h_count/ZM_SCALE_BASE; + if ( h_index > last_h_index ) + { + unsigned int w_count = xstart; + unsigned int w_index; + last_w_index = 0; - unsigned char *ps = &buffer[y*wc]; - for ( unsigned int x = 0; x < (unsigned int)width; x++ ) - { - w_count += factor; - w_index = w_count/ZM_SCALE_BASE; - - if ( w_index > last_w_index ) - { - for ( unsigned int c = 0; c < colours; c++ ) - { - *pd++ = *ps++; - } - } - else - { - ps += colours; - } - last_w_index = w_index; - } - } - last_h_index = h_index; - } - new_width = last_w_index; - new_height = last_h_index; - } - - AssignDirect( new_width, new_height, colours, subpixelorder, scale_buffer, scale_buffer_size, ZM_BUFTYPE_ZM); - + unsigned char *ps = &buffer[y*wc]; + for ( unsigned int x = 0; x < (unsigned int)width; x++ ) + { + w_count += factor; + w_index = w_count/ZM_SCALE_BASE; + + if ( w_index > last_w_index ) + { + for ( unsigned int c = 0; c < colours; c++ ) + { + *pd++ = *ps++; + } + } + else + { + ps += colours; + } + last_w_index = w_index; + } + } + last_h_index = h_index; + } + new_width = last_w_index; + new_height = last_h_index; + } + + AssignDirect( new_width, new_height, colours, subpixelorder, scale_buffer, scale_buffer_size, ZM_BUFTYPE_ZM); + } void Image::Deinterlace_Discard() { - /* Simple deinterlacing. Copy the even lines into the odd lines */ - - if ( colours == ZM_COLOUR_GRAY8 ) - { - const uint8_t *psrc; - uint8_t *pdest; - for (unsigned int y = 0; y < (unsigned int)height; y += 2) - { - psrc = buffer + (y * width); - pdest = buffer + ((y+1) * width); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pdest++ = *psrc++; - } - } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - const uint8_t *psrc; - uint8_t *pdest; - for (unsigned int y = 0; y < (unsigned int)height; y += 2) - { - psrc = buffer + ((y * width) * 3); - pdest = buffer + (((y+1) * width) * 3); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pdest++ = *psrc++; - *pdest++ = *psrc++; - *pdest++ = *psrc++; - } - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - const Rgb *psrc; - Rgb *pdest; - for (unsigned int y = 0; y < (unsigned int)height; y += 2) - { - psrc = (Rgb*)(buffer + ((y * width) << 2)); - pdest = (Rgb*)(buffer + (((y+1) * width) << 2)); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pdest++ = *psrc++; - } - } - } else { - Error("Deinterlace called with unexpected colours: %d", colours); - } - + /* Simple deinterlacing. Copy the even lines into the odd lines */ + + if ( colours == ZM_COLOUR_GRAY8 ) + { + const uint8_t *psrc; + uint8_t *pdest; + for (unsigned int y = 0; y < (unsigned int)height; y += 2) + { + psrc = buffer + (y * width); + pdest = buffer + ((y+1) * width); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pdest++ = *psrc++; + } + } + } + else if ( colours == ZM_COLOUR_RGB24 ) + { + const uint8_t *psrc; + uint8_t *pdest; + for (unsigned int y = 0; y < (unsigned int)height; y += 2) + { + psrc = buffer + ((y * width) * 3); + pdest = buffer + (((y+1) * width) * 3); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pdest++ = *psrc++; + *pdest++ = *psrc++; + *pdest++ = *psrc++; + } + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + const Rgb *psrc; + Rgb *pdest; + for (unsigned int y = 0; y < (unsigned int)height; y += 2) + { + psrc = (Rgb*)(buffer + ((y * width) << 2)); + pdest = (Rgb*)(buffer + (((y+1) * width) << 2)); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pdest++ = *psrc++; + } + } + } else { + Error("Deinterlace called with unexpected colours: %d", colours); + } + } void Image::Deinterlace_Linear() { - /* Simple deinterlacing. The odd lines are average of the line above and line below */ - - const uint8_t *pbelow, *pabove; - uint8_t *pcurrent; - - if ( colours == ZM_COLOUR_GRAY8 ) - { - for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) - { - pabove = buffer + ((y-1) * width); - pbelow = buffer + ((y+1) * width); - pcurrent = buffer + (y * width); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pcurrent++ = (*pabove++ + *pbelow++) >> 1; - } - } - /* Special case for the last line */ - pcurrent = buffer + ((height-1) * width); - pabove = buffer + ((height-2) * width); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pcurrent++ = *pabove++; - } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) - { - pabove = buffer + (((y-1) * width) * 3); - pbelow = buffer + (((y+1) * width) * 3); - pcurrent = buffer + ((y * width) * 3); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pcurrent++ = (*pabove++ + *pbelow++) >> 1; - *pcurrent++ = (*pabove++ + *pbelow++) >> 1; - *pcurrent++ = (*pabove++ + *pbelow++) >> 1; - } - } - /* Special case for the last line */ - pcurrent = buffer + (((height-1) * width) * 3); - pabove = buffer + (((height-2) * width) * 3); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pcurrent++ = *pabove++; - *pcurrent++ = *pabove++; - *pcurrent++ = *pabove++; - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) - { - pabove = buffer + (((y-1) * width) << 2); - pbelow = buffer + (((y+1) * width) << 2); - pcurrent = buffer + ((y * width) << 2); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pcurrent++ = (*pabove++ + *pbelow++) >> 1; - *pcurrent++ = (*pabove++ + *pbelow++) >> 1; - *pcurrent++ = (*pabove++ + *pbelow++) >> 1; - *pcurrent++ = (*pabove++ + *pbelow++) >> 1; - } - } - /* Special case for the last line */ - pcurrent = buffer + (((height-1) * width) << 2); - pabove = buffer + (((height-2) * width) << 2); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pcurrent++ = *pabove++; - *pcurrent++ = *pabove++; - *pcurrent++ = *pabove++; - *pcurrent++ = *pabove++; - } - } else { - Error("Deinterlace called with unexpected colours: %d", colours); - } - + /* Simple deinterlacing. The odd lines are average of the line above and line below */ + + const uint8_t *pbelow, *pabove; + uint8_t *pcurrent; + + if ( colours == ZM_COLOUR_GRAY8 ) + { + for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) + { + pabove = buffer + ((y-1) * width); + pbelow = buffer + ((y+1) * width); + pcurrent = buffer + (y * width); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pcurrent++ = (*pabove++ + *pbelow++) >> 1; + } + } + /* Special case for the last line */ + pcurrent = buffer + ((height-1) * width); + pabove = buffer + ((height-2) * width); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pcurrent++ = *pabove++; + } + } + else if ( colours == ZM_COLOUR_RGB24 ) + { + for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) + { + pabove = buffer + (((y-1) * width) * 3); + pbelow = buffer + (((y+1) * width) * 3); + pcurrent = buffer + ((y * width) * 3); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pcurrent++ = (*pabove++ + *pbelow++) >> 1; + *pcurrent++ = (*pabove++ + *pbelow++) >> 1; + *pcurrent++ = (*pabove++ + *pbelow++) >> 1; + } + } + /* Special case for the last line */ + pcurrent = buffer + (((height-1) * width) * 3); + pabove = buffer + (((height-2) * width) * 3); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pcurrent++ = *pabove++; + *pcurrent++ = *pabove++; + *pcurrent++ = *pabove++; + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) + { + pabove = buffer + (((y-1) * width) << 2); + pbelow = buffer + (((y+1) * width) << 2); + pcurrent = buffer + ((y * width) << 2); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pcurrent++ = (*pabove++ + *pbelow++) >> 1; + *pcurrent++ = (*pabove++ + *pbelow++) >> 1; + *pcurrent++ = (*pabove++ + *pbelow++) >> 1; + *pcurrent++ = (*pabove++ + *pbelow++) >> 1; + } + } + /* Special case for the last line */ + pcurrent = buffer + (((height-1) * width) << 2); + pabove = buffer + (((height-2) * width) << 2); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pcurrent++ = *pabove++; + *pcurrent++ = *pabove++; + *pcurrent++ = *pabove++; + *pcurrent++ = *pabove++; + } + } else { + Error("Deinterlace called with unexpected colours: %d", colours); + } + } void Image::Deinterlace_Blend() { - /* Simple deinterlacing. Blend the fields together. 50% blend */ - - uint8_t *pabove, *pcurrent; - - if ( colours == ZM_COLOUR_GRAY8 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { - pabove = buffer + ((y-1) * width); - pcurrent = buffer + (y * width); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pabove = (*pabove + *pcurrent) >> 1; - *pcurrent++ = *pabove++; - } - } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { - pabove = buffer + (((y-1) * width) * 3); - pcurrent = buffer + ((y * width) * 3); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pabove = (*pabove + *pcurrent) >> 1; - *pcurrent++ = *pabove++; - *pabove = (*pabove + *pcurrent) >> 1; - *pcurrent++ = *pabove++; - *pabove = (*pabove + *pcurrent) >> 1; - *pcurrent++ = *pabove++; - } - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { - pabove = buffer + (((y-1) * width) << 2); - pcurrent = buffer + ((y * width) << 2); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - *pabove = (*pabove + *pcurrent) >> 1; - *pcurrent++ = *pabove++; - *pabove = (*pabove + *pcurrent) >> 1; - *pcurrent++ = *pabove++; - *pabove = (*pabove + *pcurrent) >> 1; - *pcurrent++ = *pabove++; - *pabove = (*pabove + *pcurrent) >> 1; - *pcurrent++ = *pabove++; - } - } - } else { - Error("Deinterlace called with unexpected colours: %d", colours); - } - + /* Simple deinterlacing. Blend the fields together. 50% blend */ + + uint8_t *pabove, *pcurrent; + + if ( colours == ZM_COLOUR_GRAY8 ) + { + for (unsigned int y = 1; y < (unsigned int)height; y += 2) + { + pabove = buffer + ((y-1) * width); + pcurrent = buffer + (y * width); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pabove = (*pabove + *pcurrent) >> 1; + *pcurrent++ = *pabove++; + } + } + } + else if ( colours == ZM_COLOUR_RGB24 ) + { + for (unsigned int y = 1; y < (unsigned int)height; y += 2) + { + pabove = buffer + (((y-1) * width) * 3); + pcurrent = buffer + ((y * width) * 3); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pabove = (*pabove + *pcurrent) >> 1; + *pcurrent++ = *pabove++; + *pabove = (*pabove + *pcurrent) >> 1; + *pcurrent++ = *pabove++; + *pabove = (*pabove + *pcurrent) >> 1; + *pcurrent++ = *pabove++; + } + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + for (unsigned int y = 1; y < (unsigned int)height; y += 2) + { + pabove = buffer + (((y-1) * width) << 2); + pcurrent = buffer + ((y * width) << 2); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + *pabove = (*pabove + *pcurrent) >> 1; + *pcurrent++ = *pabove++; + *pabove = (*pabove + *pcurrent) >> 1; + *pcurrent++ = *pabove++; + *pabove = (*pabove + *pcurrent) >> 1; + *pcurrent++ = *pabove++; + *pabove = (*pabove + *pcurrent) >> 1; + *pcurrent++ = *pabove++; + } + } + } else { + Error("Deinterlace called with unexpected colours: %d", colours); + } + } void Image::Deinterlace_Blend_CustomRatio(int divider) { - /* Simple deinterlacing. Blend the fields together at a custom ratio. */ - /* 1 = 50% blending */ - /* 2 = 25% blending */ - /* 3 = 12.% blending */ - /* 4 = 6.25% blending */ - - uint8_t *pabove, *pcurrent; - uint8_t subpix1, subpix2; - - if ( divider < 1 || divider > 4 ) { - Error("Deinterlace called with invalid blend ratio"); - } - - if ( colours == ZM_COLOUR_GRAY8 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { - pabove = buffer + ((y-1) * width); - pcurrent = buffer + (y * width); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; - subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; - *pcurrent++ = subpix1; - *pabove++ = subpix2; - } - } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { - pabove = buffer + (((y-1) * width) * 3); - pcurrent = buffer + ((y * width) * 3); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; - subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; - *pcurrent++ = subpix1; - *pabove++ = subpix2; - subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; - subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; - *pcurrent++ = subpix1; - *pabove++ = subpix2; - subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; - subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; - *pcurrent++ = subpix1; - *pabove++ = subpix2; - } - } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { - pabove = buffer + (((y-1) * width) << 2); - pcurrent = buffer + ((y * width) << 2); - for (unsigned int x = 0; x < (unsigned int)width; x++) { - subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; - subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; - *pcurrent++ = subpix1; - *pabove++ = subpix2; - subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; - subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; - *pcurrent++ = subpix1; - *pabove++ = subpix2; - subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; - subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; - *pcurrent++ = subpix1; - *pabove++ = subpix2; - subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; - subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; - *pcurrent++ = subpix1; - *pabove++ = subpix2; - } - } - } else { - Error("Deinterlace called with unexpected colours: %d", colours); - } - + /* Simple deinterlacing. Blend the fields together at a custom ratio. */ + /* 1 = 50% blending */ + /* 2 = 25% blending */ + /* 3 = 12.% blending */ + /* 4 = 6.25% blending */ + + uint8_t *pabove, *pcurrent; + uint8_t subpix1, subpix2; + + if ( divider < 1 || divider > 4 ) { + Error("Deinterlace called with invalid blend ratio"); + } + + if ( colours == ZM_COLOUR_GRAY8 ) + { + for (unsigned int y = 1; y < (unsigned int)height; y += 2) + { + pabove = buffer + ((y-1) * width); + pcurrent = buffer + (y * width); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; + subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; + *pcurrent++ = subpix1; + *pabove++ = subpix2; + } + } + } + else if ( colours == ZM_COLOUR_RGB24 ) + { + for (unsigned int y = 1; y < (unsigned int)height; y += 2) + { + pabove = buffer + (((y-1) * width) * 3); + pcurrent = buffer + ((y * width) * 3); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; + subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; + *pcurrent++ = subpix1; + *pabove++ = subpix2; + subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; + subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; + *pcurrent++ = subpix1; + *pabove++ = subpix2; + subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; + subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; + *pcurrent++ = subpix1; + *pabove++ = subpix2; + } + } + } + else if ( colours == ZM_COLOUR_RGB32 ) + { + for (unsigned int y = 1; y < (unsigned int)height; y += 2) + { + pabove = buffer + (((y-1) * width) << 2); + pcurrent = buffer + ((y * width) << 2); + for (unsigned int x = 0; x < (unsigned int)width; x++) { + subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; + subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; + *pcurrent++ = subpix1; + *pabove++ = subpix2; + subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; + subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; + *pcurrent++ = subpix1; + *pabove++ = subpix2; + subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; + subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; + *pcurrent++ = subpix1; + *pabove++ = subpix2; + subpix1 = ((*pabove - *pcurrent)>>divider) + *pcurrent; + subpix2 = ((*pcurrent - *pabove)>>divider) + *pabove; + *pcurrent++ = subpix1; + *pabove++ = subpix2; + } + } + } else { + Error("Deinterlace called with unexpected colours: %d", colours); + } + } void Image::Deinterlace_4Field(const Image* next_image, unsigned int threshold) { - if ( !(width == next_image->width && height == next_image->height && colours == next_image->colours && subpixelorder == next_image->subpixelorder) ) - { - Panic( "Attempt to deinterlace different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, next_image->width, next_image->height, next_image->colours, next_image->subpixelorder); - } - - switch(colours) { - case ZM_COLOUR_RGB24: - { - if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { - /* BGR subpixel order */ - std_deinterlace_4field_bgr(buffer, next_image->buffer, threshold, width, height); - } else { - /* Assume RGB subpixel order */ - std_deinterlace_4field_rgb(buffer, next_image->buffer, threshold, width, height); - } - break; - } - case ZM_COLOUR_RGB32: - { - if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - /* ARGB subpixel order */ - (*fptr_deinterlace_4field_argb)(buffer, next_image->buffer, threshold, width, height); - } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - /* ABGR subpixel order */ - (*fptr_deinterlace_4field_abgr)(buffer, next_image->buffer, threshold, width, height); - } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - /* BGRA subpixel order */ - (*fptr_deinterlace_4field_bgra)(buffer, next_image->buffer, threshold, width, height); - } else { - /* Assume RGBA subpixel order */ - (*fptr_deinterlace_4field_rgba)(buffer, next_image->buffer, threshold, width, height); - } - break; - } - case ZM_COLOUR_GRAY8: - (*fptr_deinterlace_4field_gray8)(buffer, next_image->buffer, threshold, width, height); - break; - default: - Panic("Deinterlace_4Field called with unexpected colours: %d",colours); - break; - } - + if ( !(width == next_image->width && height == next_image->height && colours == next_image->colours && subpixelorder == next_image->subpixelorder) ) + { + Panic( "Attempt to deinterlace different sized images, expected %dx%dx%d %d, got %dx%dx%d %d", width, height, colours, subpixelorder, next_image->width, next_image->height, next_image->colours, next_image->subpixelorder); + } + + switch(colours) { + case ZM_COLOUR_RGB24: + { + if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { + /* BGR subpixel order */ + std_deinterlace_4field_bgr(buffer, next_image->buffer, threshold, width, height); + } else { + /* Assume RGB subpixel order */ + std_deinterlace_4field_rgb(buffer, next_image->buffer, threshold, width, height); + } + break; + } + case ZM_COLOUR_RGB32: + { + if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + /* ARGB subpixel order */ + (*fptr_deinterlace_4field_argb)(buffer, next_image->buffer, threshold, width, height); + } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + /* ABGR subpixel order */ + (*fptr_deinterlace_4field_abgr)(buffer, next_image->buffer, threshold, width, height); + } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + /* BGRA subpixel order */ + (*fptr_deinterlace_4field_bgra)(buffer, next_image->buffer, threshold, width, height); + } else { + /* Assume RGBA subpixel order */ + (*fptr_deinterlace_4field_rgba)(buffer, next_image->buffer, threshold, width, height); + } + break; + } + case ZM_COLOUR_GRAY8: + (*fptr_deinterlace_4field_gray8)(buffer, next_image->buffer, threshold, width, height); + break; + default: + Panic("Deinterlace_4Field called with unexpected colours: %d",colours); + break; + } + } @@ -2993,343 +3124,343 @@ __attribute__((noinline,__target__("sse2"))) #endif void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - static uint32_t divider = 0; - static uint32_t clearmask = 0; - static double current_blendpercent = 0.0; - - if(current_blendpercent != blendpercent) { - /* Attempt to match the blending percent to one of the possible values */ - if(blendpercent < 2.34375) { - // 1.5625% blending - divider = 6; - clearmask = 0x03030303; - } else if(blendpercent < 4.6875) { - // 3.125% blending - divider = 5; - clearmask = 0x07070707; - } else if(blendpercent < 9.375) { - // 6.25% blending - divider = 4; - clearmask = 0x0F0F0F0F; - } else if(blendpercent < 18.75) { - // 12.5% blending - divider = 3; - clearmask = 0x1F1F1F1F; - } else if(blendpercent < 37.5) { - // 25% blending - divider = 2; - clearmask = 0x3F3F3F3F; - } else { - // 50% blending - divider = 1; - clearmask = 0x7F7F7F7F; - } - current_blendpercent = blendpercent; - } + static uint32_t divider = 0; + static uint32_t clearmask = 0; + static double current_blendpercent = 0.0; + + if(current_blendpercent != blendpercent) { + /* Attempt to match the blending percent to one of the possible values */ + if(blendpercent < 2.34375) { + // 1.5625% blending + divider = 6; + clearmask = 0x03030303; + } else if(blendpercent < 4.6875) { + // 3.125% blending + divider = 5; + clearmask = 0x07070707; + } else if(blendpercent < 9.375) { + // 6.25% blending + divider = 4; + clearmask = 0x0F0F0F0F; + } else if(blendpercent < 18.75) { + // 12.5% blending + divider = 3; + clearmask = 0x1F1F1F1F; + } else if(blendpercent < 37.5) { + // 25% blending + divider = 2; + clearmask = 0x3F3F3F3F; + } else { + // 50% blending + divider = 1; + clearmask = 0x7F7F7F7F; + } + current_blendpercent = blendpercent; + } - __asm__ __volatile__( - "movd %4, %%xmm3\n\t" - "movd %5, %%xmm4\n\t" - "pshufd $0x0, %%xmm3, %%xmm3\n\t" - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x10, %2\n\t" - "sse2_fastblend_iter:\n\t" - "movdqa (%0,%3),%%xmm0\n\t" - "movdqa %%xmm0,%%xmm2\n\t" - "movdqa (%1,%3),%%xmm1\n\t" - "psrlq %%xmm4,%%xmm0\n\t" - "psrlq %%xmm4,%%xmm1\n\t" - "pand %%xmm3,%%xmm1\n\t" - "pand %%xmm3,%%xmm0\n\t" - "psubb %%xmm0,%%xmm1\n\t" - "paddb %%xmm2,%%xmm1\n\t" - "movntdq %%xmm1,(%2,%3)\n\t" - "sub $0x10, %3\n\t" - "jnz sse2_fastblend_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (clearmask), "m" (divider) - : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "cc", "memory" - ); + __asm__ __volatile__( + "movd %4, %%xmm3\n\t" + "movd %5, %%xmm4\n\t" + "pshufd $0x0, %%xmm3, %%xmm3\n\t" + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x10, %2\n\t" + "sse2_fastblend_iter:\n\t" + "movdqa (%0,%3),%%xmm0\n\t" + "movdqa %%xmm0,%%xmm2\n\t" + "movdqa (%1,%3),%%xmm1\n\t" + "psrlq %%xmm4,%%xmm0\n\t" + "psrlq %%xmm4,%%xmm1\n\t" + "pand %%xmm3,%%xmm1\n\t" + "pand %%xmm3,%%xmm0\n\t" + "psubb %%xmm0,%%xmm1\n\t" + "paddb %%xmm2,%%xmm1\n\t" + "movntdq %%xmm1,(%2,%3)\n\t" + "sub $0x10, %3\n\t" + "jnz sse2_fastblend_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (clearmask), "m" (divider) + : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } __attribute__((noinline)) void std_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) { - static int divider = 0; - static double current_blendpercent = 0.0; - const uint8_t* const max_ptr = result + count; - - if(current_blendpercent != blendpercent) { - /* Attempt to match the blending percent to one of the possible values */ - if(blendpercent < 2.34375) { - // 1.5625% blending - divider = 6; - } else if(blendpercent < 4.6875) { - // 3.125% blending - divider = 5; - } else if(blendpercent < 9.375) { - // 6.25% blending - divider = 4; - } else if(blendpercent < 18.75) { - // 12.5% blending - divider = 3; - } else if(blendpercent < 37.5) { - // 25% blending - divider = 2; - } else { - // 50% blending - divider = 1; - } - current_blendpercent = blendpercent; - } - + static int divider = 0; + static double current_blendpercent = 0.0; + const uint8_t* const max_ptr = result + count; + + if(current_blendpercent != blendpercent) { + /* Attempt to match the blending percent to one of the possible values */ + if(blendpercent < 2.34375) { + // 1.5625% blending + divider = 6; + } else if(blendpercent < 4.6875) { + // 3.125% blending + divider = 5; + } else if(blendpercent < 9.375) { + // 6.25% blending + divider = 4; + } else if(blendpercent < 18.75) { + // 12.5% blending + divider = 3; + } else if(blendpercent < 37.5) { + // 25% blending + divider = 2; + } else { + // 50% blending + divider = 1; + } + current_blendpercent = blendpercent; + } + - while(result < max_ptr) { - result[0] = ((col2[0] - col1[0])>>divider) + col1[0]; - result[1] = ((col2[1] - col1[1])>>divider) + col1[1]; - result[2] = ((col2[2] - col1[2])>>divider) + col1[2]; - result[3] = ((col2[3] - col1[3])>>divider) + col1[3]; - result[4] = ((col2[4] - col1[4])>>divider) + col1[4]; - result[5] = ((col2[5] - col1[5])>>divider) + col1[5]; - result[6] = ((col2[6] - col1[6])>>divider) + col1[6]; - result[7] = ((col2[7] - col1[7])>>divider) + col1[7]; - result[8] = ((col2[8] - col1[8])>>divider) + col1[8]; - result[9] = ((col2[9] - col1[9])>>divider) + col1[9]; - result[10] = ((col2[10] - col1[10])>>divider) + col1[10]; - result[11] = ((col2[11] - col1[11])>>divider) + col1[11]; - result[12] = ((col2[12] - col1[12])>>divider) + col1[12]; - result[13] = ((col2[13] - col1[13])>>divider) + col1[13]; - result[14] = ((col2[14] - col1[14])>>divider) + col1[14]; - result[15] = ((col2[15] - col1[15])>>divider) + col1[15]; - - col1 += 16; - col2 += 16; - result += 16; - } + while(result < max_ptr) { + result[0] = ((col2[0] - col1[0])>>divider) + col1[0]; + result[1] = ((col2[1] - col1[1])>>divider) + col1[1]; + result[2] = ((col2[2] - col1[2])>>divider) + col1[2]; + result[3] = ((col2[3] - col1[3])>>divider) + col1[3]; + result[4] = ((col2[4] - col1[4])>>divider) + col1[4]; + result[5] = ((col2[5] - col1[5])>>divider) + col1[5]; + result[6] = ((col2[6] - col1[6])>>divider) + col1[6]; + result[7] = ((col2[7] - col1[7])>>divider) + col1[7]; + result[8] = ((col2[8] - col1[8])>>divider) + col1[8]; + result[9] = ((col2[9] - col1[9])>>divider) + col1[9]; + result[10] = ((col2[10] - col1[10])>>divider) + col1[10]; + result[11] = ((col2[11] - col1[11])>>divider) + col1[11]; + result[12] = ((col2[12] - col1[12])>>divider) + col1[12]; + result[13] = ((col2[13] - col1[13])>>divider) + col1[13]; + result[14] = ((col2[14] - col1[14])>>divider) + col1[14]; + result[15] = ((col2[15] - col1[15])>>divider) + col1[15]; + + col1 += 16; + col2 += 16; + result += 16; + } } __attribute__((noinline)) void std_blend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) { - double divide = blendpercent / 100.0; - double opacity = 1.0 - divide; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - *result++ = (*col1++ * opacity) + (*col2++ * divide); - - } + double divide = blendpercent / 100.0; + double opacity = 1.0 - divide; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + *result++ = (*col1++ * opacity) + (*col2++ * divide); + + } } /************************************************* DELTA FUNCTIONS *************************************************/ /* Grayscale */ __attribute__((noinline)) void std_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { - /* Loop unrolling is used to work on 16 bytes (16 grayscale pixels) at a time */ - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - result[0] = abs(col1[0] - col2[0]); - result[1] = abs(col1[1] - col2[1]); - result[2] = abs(col1[2] - col2[2]); - result[3] = abs(col1[3] - col2[3]); - result[4] = abs(col1[4] - col2[4]); - result[5] = abs(col1[5] - col2[5]); - result[6] = abs(col1[6] - col2[6]); - result[7] = abs(col1[7] - col2[7]); - result[8] = abs(col1[8] - col2[8]); - result[9] = abs(col1[9] - col2[9]); - result[10] = abs(col1[10] - col2[10]); - result[11] = abs(col1[11] - col2[11]); - result[12] = abs(col1[12] - col2[12]); - result[13] = abs(col1[13] - col2[13]); - result[14] = abs(col1[14] - col2[14]); - result[15] = abs(col1[15] - col2[15]); - - col1 += 16; - col2 += 16; - result += 16; - } + /* Loop unrolling is used to work on 16 bytes (16 grayscale pixels) at a time */ + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + result[0] = abs(col1[0] - col2[0]); + result[1] = abs(col1[1] - col2[1]); + result[2] = abs(col1[2] - col2[2]); + result[3] = abs(col1[3] - col2[3]); + result[4] = abs(col1[4] - col2[4]); + result[5] = abs(col1[5] - col2[5]); + result[6] = abs(col1[6] - col2[6]); + result[7] = abs(col1[7] - col2[7]); + result[8] = abs(col1[8] - col2[8]); + result[9] = abs(col1[9] - col2[9]); + result[10] = abs(col1[10] - col2[10]); + result[11] = abs(col1[11] - col2[11]); + result[12] = abs(col1[12] - col2[12]); + result[13] = abs(col1[13] - col2[13]); + result[14] = abs(col1[14] - col2[14]); + result[15] = abs(col1[15] - col2[15]); + + col1 += 16; + col2 += 16; + result += 16; + } } /* RGB24: RGB */ __attribute__((noinline)) void std_delta8_rgb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { - /* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */ - int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - r = abs(col1[0] - col2[0]); - g = abs(col1[1] - col2[1]); - b = abs(col1[2] - col2[2]); - result[0] = (r + r + b + g + g + g + g + g)>>3; - r = abs(col1[3] - col2[3]); - g = abs(col1[4] - col2[4]); - b = abs(col1[5] - col2[5]); - result[1] = (r + r + b + g + g + g + g + g)>>3; - r = abs(col1[6] - col2[6]); - g = abs(col1[7] - col2[7]); - b = abs(col1[8] - col2[8]); - result[2] = (r + r + b + g + g + g + g + g)>>3; - r = abs(col1[9] - col2[9]); - g = abs(col1[10] - col2[10]); - b = abs(col1[11] - col2[11]); - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 12; - col2 += 12; - result += 4; - } + /* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */ + int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + r = abs(col1[0] - col2[0]); + g = abs(col1[1] - col2[1]); + b = abs(col1[2] - col2[2]); + result[0] = (r + r + b + g + g + g + g + g)>>3; + r = abs(col1[3] - col2[3]); + g = abs(col1[4] - col2[4]); + b = abs(col1[5] - col2[5]); + result[1] = (r + r + b + g + g + g + g + g)>>3; + r = abs(col1[6] - col2[6]); + g = abs(col1[7] - col2[7]); + b = abs(col1[8] - col2[8]); + result[2] = (r + r + b + g + g + g + g + g)>>3; + r = abs(col1[9] - col2[9]); + g = abs(col1[10] - col2[10]); + b = abs(col1[11] - col2[11]); + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 12; + col2 += 12; + result += 4; + } } /* RGB24: BGR */ __attribute__((noinline)) void std_delta8_bgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { - /* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */ - int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - b = abs(col1[0] - col2[0]); - g = abs(col1[1] - col2[1]); - r = abs(col1[2] - col2[2]); - result[0] = (r + r + b + g + g + g + g + g)>>3; - b = abs(col1[3] - col2[3]); - g = abs(col1[4] - col2[4]); - r = abs(col1[5] - col2[5]); - result[1] = (r + r + b + g + g + g + g + g)>>3; - b = abs(col1[6] - col2[6]); - g = abs(col1[7] - col2[7]); - r = abs(col1[8] - col2[8]); - result[2] = (r + r + b + g + g + g + g + g)>>3; - b = abs(col1[9] - col2[9]); - g = abs(col1[10] - col2[10]); - r = abs(col1[11] - col2[11]); - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 12; - col2 += 12; - result += 4; - } + /* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */ + int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + b = abs(col1[0] - col2[0]); + g = abs(col1[1] - col2[1]); + r = abs(col1[2] - col2[2]); + result[0] = (r + r + b + g + g + g + g + g)>>3; + b = abs(col1[3] - col2[3]); + g = abs(col1[4] - col2[4]); + r = abs(col1[5] - col2[5]); + result[1] = (r + r + b + g + g + g + g + g)>>3; + b = abs(col1[6] - col2[6]); + g = abs(col1[7] - col2[7]); + r = abs(col1[8] - col2[8]); + result[2] = (r + r + b + g + g + g + g + g)>>3; + b = abs(col1[9] - col2[9]); + g = abs(col1[10] - col2[10]); + r = abs(col1[11] - col2[11]); + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 12; + col2 += 12; + result += 4; + } } /* RGB32: RGBA */ __attribute__((noinline)) void std_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { - /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ - int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - r = abs(col1[0] - col2[0]); - g = abs(col1[1] - col2[1]); - b = abs(col1[2] - col2[2]); - result[0] = (r + r + b + g + g + g + g + g)>>3; - r = abs(col1[4] - col2[4]); - g = abs(col1[5] - col2[5]); - b = abs(col1[6] - col2[6]); - result[1] = (r + r + b + g + g + g + g + g)>>3; - r = abs(col1[8] - col2[8]); - g = abs(col1[9] - col2[9]); - b = abs(col1[10] - col2[10]); - result[2] = (r + r + b + g + g + g + g + g)>>3; - r = abs(col1[12] - col2[12]); - g = abs(col1[13] - col2[13]); - b = abs(col1[14] - col2[14]); - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 16; - col2 += 16; - result += 4; - } + /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ + int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + r = abs(col1[0] - col2[0]); + g = abs(col1[1] - col2[1]); + b = abs(col1[2] - col2[2]); + result[0] = (r + r + b + g + g + g + g + g)>>3; + r = abs(col1[4] - col2[4]); + g = abs(col1[5] - col2[5]); + b = abs(col1[6] - col2[6]); + result[1] = (r + r + b + g + g + g + g + g)>>3; + r = abs(col1[8] - col2[8]); + g = abs(col1[9] - col2[9]); + b = abs(col1[10] - col2[10]); + result[2] = (r + r + b + g + g + g + g + g)>>3; + r = abs(col1[12] - col2[12]); + g = abs(col1[13] - col2[13]); + b = abs(col1[14] - col2[14]); + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 16; + col2 += 16; + result += 4; + } } /* RGB32: BGRA */ __attribute__((noinline)) void std_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { - /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ - int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - b = abs(col1[0] - col2[0]); - g = abs(col1[1] - col2[1]); - r = abs(col1[2] - col2[2]); - result[0] = (r + r + b + g + g + g + g + g)>>3; - b = abs(col1[4] - col2[4]); - g = abs(col1[5] - col2[5]); - r = abs(col1[6] - col2[6]); - result[1] = (r + r + b + g + g + g + g + g)>>3; - b = abs(col1[8] - col2[8]); - g = abs(col1[9] - col2[9]); - r = abs(col1[10] - col2[10]); - result[2] = (r + r + b + g + g + g + g + g)>>3; - b = abs(col1[12] - col2[12]); - g = abs(col1[13] - col2[13]); - r = abs(col1[14] - col2[14]); - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 16; - col2 += 16; - result += 4; - } + /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ + int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + b = abs(col1[0] - col2[0]); + g = abs(col1[1] - col2[1]); + r = abs(col1[2] - col2[2]); + result[0] = (r + r + b + g + g + g + g + g)>>3; + b = abs(col1[4] - col2[4]); + g = abs(col1[5] - col2[5]); + r = abs(col1[6] - col2[6]); + result[1] = (r + r + b + g + g + g + g + g)>>3; + b = abs(col1[8] - col2[8]); + g = abs(col1[9] - col2[9]); + r = abs(col1[10] - col2[10]); + result[2] = (r + r + b + g + g + g + g + g)>>3; + b = abs(col1[12] - col2[12]); + g = abs(col1[13] - col2[13]); + r = abs(col1[14] - col2[14]); + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 16; + col2 += 16; + result += 4; + } } /* RGB32: ARGB */ __attribute__((noinline)) void std_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { - /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ - int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - r = abs(col1[1] - col2[1]); - g = abs(col1[2] - col2[2]); - b = abs(col1[3] - col2[3]); - result[0] = (r + r + b + g + g + g + g + g)>>3; - r = abs(col1[5] - col2[5]); - g = abs(col1[6] - col2[6]); - b = abs(col1[7] - col2[7]); - result[1] = (r + r + b + g + g + g + g + g)>>3; - r = abs(col1[9] - col2[9]); - g = abs(col1[10] - col2[10]); - b = abs(col1[11] - col2[11]); - result[2] = (r + r + b + g + g + g + g + g)>>3; - r = abs(col1[13] - col2[13]); - g = abs(col1[14] - col2[14]); - b = abs(col1[15] - col2[15]); - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 16; - col2 += 16; - result += 4; - } + /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ + int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + r = abs(col1[1] - col2[1]); + g = abs(col1[2] - col2[2]); + b = abs(col1[3] - col2[3]); + result[0] = (r + r + b + g + g + g + g + g)>>3; + r = abs(col1[5] - col2[5]); + g = abs(col1[6] - col2[6]); + b = abs(col1[7] - col2[7]); + result[1] = (r + r + b + g + g + g + g + g)>>3; + r = abs(col1[9] - col2[9]); + g = abs(col1[10] - col2[10]); + b = abs(col1[11] - col2[11]); + result[2] = (r + r + b + g + g + g + g + g)>>3; + r = abs(col1[13] - col2[13]); + g = abs(col1[14] - col2[14]); + b = abs(col1[15] - col2[15]); + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 16; + col2 += 16; + result += 4; + } } /* RGB32: ABGR */ __attribute__((noinline)) void std_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { - /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ - int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - b = abs(col1[1] - col2[1]); - g = abs(col1[2] - col2[2]); - r = abs(col1[3] - col2[3]); - result[0] = (r + r + b + g + g + g + g + g)>>3; - b = abs(col1[5] - col2[5]); - g = abs(col1[6] - col2[6]); - r = abs(col1[7] - col2[7]); - result[1] = (r + r + b + g + g + g + g + g)>>3; - b = abs(col1[9] - col2[9]); - g = abs(col1[10] - col2[10]); - r = abs(col1[11] - col2[11]); - result[2] = (r + r + b + g + g + g + g + g)>>3; - b = abs(col1[13] - col2[13]); - g = abs(col1[14] - col2[14]); - r = abs(col1[15] - col2[15]); - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 16; - col2 += 16; - result += 4; - } + /* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */ + int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + b = abs(col1[1] - col2[1]); + g = abs(col1[2] - col2[2]); + r = abs(col1[3] - col2[3]); + result[0] = (r + r + b + g + g + g + g + g)>>3; + b = abs(col1[5] - col2[5]); + g = abs(col1[6] - col2[6]); + r = abs(col1[7] - col2[7]); + result[1] = (r + r + b + g + g + g + g + g)>>3; + b = abs(col1[9] - col2[9]); + g = abs(col1[10] - col2[10]); + r = abs(col1[11] - col2[11]); + result[2] = (r + r + b + g + g + g + g + g)>>3; + b = abs(col1[13] - col2[13]); + g = abs(col1[14] - col2[14]); + r = abs(col1[15] - col2[15]); + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 16; + col2 += 16; + result += 4; + } } /* Grayscale SSE2 */ @@ -3339,27 +3470,27 @@ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __asm__ __volatile__ ( - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x10, %2\n\t" - "sse2_delta8_gray8_iter:\n\t" - "movdqa (%0,%3), %%xmm1\n\t" - "movdqa (%1,%3), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm3\n\t" - "movdqa %%xmm2, %%xmm4\n\t" - "pmaxub %%xmm1, %%xmm2\n\t" - "pminub %%xmm3, %%xmm4\n\t" - "psubb %%xmm4, %%xmm2\n\t" - "movntdq %%xmm2, (%2,%3)\n\t" - "sub $0x10, %3\n\t" - "jnz sse2_delta8_gray8_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count) - : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "cc", "memory" - ); + __asm__ __volatile__ ( + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x10, %2\n\t" + "sse2_delta8_gray8_iter:\n\t" + "movdqa (%0,%3), %%xmm1\n\t" + "movdqa (%1,%3), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm3\n\t" + "movdqa %%xmm2, %%xmm4\n\t" + "pmaxub %%xmm1, %%xmm2\n\t" + "pminub %%xmm3, %%xmm4\n\t" + "psubb %%xmm4, %%xmm2\n\t" + "movntdq %%xmm2, (%2,%3)\n\t" + "sub $0x10, %3\n\t" + "jnz sse2_delta8_gray8_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count) + : "%xmm1", "%xmm2", "%xmm3", "%xmm4", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -3370,54 +3501,54 @@ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "mov $0xff, %%eax\n\t" - "movd %%eax, %%xmm0\n\t" - "pshufd $0x0, %%xmm0, %%xmm0\n\t" - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x4, %2\n\t" - "sse2_delta8_rgba_iter:\n\t" - "movdqa (%0,%3,4), %%xmm1\n\t" - "movdqa (%1,%3,4), %%xmm2\n\t" - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" - "movdqa %%xmm2, %%xmm6\n\t" - "pmaxub %%xmm1, %%xmm2\n\t" - "pminub %%xmm5, %%xmm6\n\t" - "psubb %%xmm6, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm3\n\t" - "psrld $0x8, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "pslld $0x2, %%xmm2\n\t" - "paddd %%xmm1, %%xmm2\n\t" - "movdqa %%xmm3, %%xmm1\n\t" - "pand %%xmm0, %%xmm1\n\t" - "paddd %%xmm1, %%xmm1\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x10, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "packssdw %%xmm1, %%xmm1\n\t" - "packuswb %%xmm1, %%xmm1\n\t" - "movd %%xmm1, %%eax\n\t" - "movnti %%eax, (%2,%3)\n\t" - "sub $0x4, %3\n\t" - "jnz sse2_delta8_rgba_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" - ); + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "mov $0xff, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x4, %2\n\t" + "sse2_delta8_rgba_iter:\n\t" + "movdqa (%0,%3,4), %%xmm1\n\t" + "movdqa (%1,%3,4), %%xmm2\n\t" + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" + "movdqa %%xmm2, %%xmm6\n\t" + "pmaxub %%xmm1, %%xmm2\n\t" + "pminub %%xmm5, %%xmm6\n\t" + "psubb %%xmm6, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm3\n\t" + "psrld $0x8, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "pslld $0x2, %%xmm2\n\t" + "paddd %%xmm1, %%xmm2\n\t" + "movdqa %%xmm3, %%xmm1\n\t" + "pand %%xmm0, %%xmm1\n\t" + "paddd %%xmm1, %%xmm1\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x10, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "packssdw %%xmm1, %%xmm1\n\t" + "packuswb %%xmm1, %%xmm1\n\t" + "movd %%xmm1, %%eax\n\t" + "movnti %%eax, (%2,%3)\n\t" + "sub $0x4, %3\n\t" + "jnz sse2_delta8_rgba_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -3428,54 +3559,54 @@ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "mov $0xff, %%eax\n\t" - "movd %%eax, %%xmm0\n\t" - "pshufd $0x0, %%xmm0, %%xmm0\n\t" - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x4, %2\n\t" - "sse2_delta8_bgra_iter:\n\t" - "movdqa (%0,%3,4), %%xmm1\n\t" - "movdqa (%1,%3,4), %%xmm2\n\t" - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" - "movdqa %%xmm2, %%xmm6\n\t" - "pmaxub %%xmm1, %%xmm2\n\t" - "pminub %%xmm5, %%xmm6\n\t" - "psubb %%xmm6, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm3\n\t" - "psrld $0x8, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "pslld $0x2, %%xmm2\n\t" - "paddd %%xmm1, %%xmm2\n\t" - "movdqa %%xmm3, %%xmm1\n\t" - "pand %%xmm0, %%xmm1\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x10, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "paddd %%xmm2, %%xmm2\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "packssdw %%xmm1, %%xmm1\n\t" - "packuswb %%xmm1, %%xmm1\n\t" - "movd %%xmm1, %%eax\n\t" - "movnti %%eax, (%2,%3)\n\t" - "sub $0x4, %3\n\t" - "jnz sse2_delta8_bgra_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" - ); + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "mov $0xff, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x4, %2\n\t" + "sse2_delta8_bgra_iter:\n\t" + "movdqa (%0,%3,4), %%xmm1\n\t" + "movdqa (%1,%3,4), %%xmm2\n\t" + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" + "movdqa %%xmm2, %%xmm6\n\t" + "pmaxub %%xmm1, %%xmm2\n\t" + "pminub %%xmm5, %%xmm6\n\t" + "psubb %%xmm6, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm3\n\t" + "psrld $0x8, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "pslld $0x2, %%xmm2\n\t" + "paddd %%xmm1, %%xmm2\n\t" + "movdqa %%xmm3, %%xmm1\n\t" + "pand %%xmm0, %%xmm1\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x10, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "paddd %%xmm2, %%xmm2\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "packssdw %%xmm1, %%xmm1\n\t" + "packuswb %%xmm1, %%xmm1\n\t" + "movd %%xmm1, %%eax\n\t" + "movnti %%eax, (%2,%3)\n\t" + "sub $0x4, %3\n\t" + "jnz sse2_delta8_bgra_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -3486,55 +3617,55 @@ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "mov $0xff, %%eax\n\t" - "movd %%eax, %%xmm0\n\t" - "pshufd $0x0, %%xmm0, %%xmm0\n\t" - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x4, %2\n\t" - "sse2_delta8_argb_iter:\n\t" - "movdqa (%0,%3,4), %%xmm1\n\t" - "movdqa (%1,%3,4), %%xmm2\n\t" - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" - "movdqa %%xmm2, %%xmm6\n\t" - "pmaxub %%xmm1, %%xmm2\n\t" - "pminub %%xmm5, %%xmm6\n\t" - "psubb %%xmm6, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm3\n\t" - "psrld $0x10, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "pslld $0x2, %%xmm2\n\t" - "paddd %%xmm1, %%xmm2\n\t" - "movdqa %%xmm3, %%xmm1\n\t" - "psrld $0x8, %%xmm1\n\t" - "pand %%xmm0, %%xmm1\n\t" - "paddd %%xmm1, %%xmm1\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x18, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "packssdw %%xmm1, %%xmm1\n\t" - "packuswb %%xmm1, %%xmm1\n\t" - "movd %%xmm1, %%eax\n\t" - "movnti %%eax, (%2,%3)\n\t" - "sub $0x4, %3\n\t" - "jnz sse2_delta8_argb_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" - ); + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "mov $0xff, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x4, %2\n\t" + "sse2_delta8_argb_iter:\n\t" + "movdqa (%0,%3,4), %%xmm1\n\t" + "movdqa (%1,%3,4), %%xmm2\n\t" + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" + "movdqa %%xmm2, %%xmm6\n\t" + "pmaxub %%xmm1, %%xmm2\n\t" + "pminub %%xmm5, %%xmm6\n\t" + "psubb %%xmm6, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm3\n\t" + "psrld $0x10, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "pslld $0x2, %%xmm2\n\t" + "paddd %%xmm1, %%xmm2\n\t" + "movdqa %%xmm3, %%xmm1\n\t" + "psrld $0x8, %%xmm1\n\t" + "pand %%xmm0, %%xmm1\n\t" + "paddd %%xmm1, %%xmm1\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x18, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "packssdw %%xmm1, %%xmm1\n\t" + "packuswb %%xmm1, %%xmm1\n\t" + "movd %%xmm1, %%eax\n\t" + "movnti %%eax, (%2,%3)\n\t" + "sub $0x4, %3\n\t" + "jnz sse2_delta8_argb_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -3545,55 +3676,55 @@ __attribute__((noinline,__target__("sse2"))) void sse2_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "mov $0xff, %%eax\n\t" - "movd %%eax, %%xmm0\n\t" - "pshufd $0x0, %%xmm0, %%xmm0\n\t" - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x4, %2\n\t" - "sse2_delta8_abgr_iter:\n\t" - "movdqa (%0,%3,4), %%xmm1\n\t" - "movdqa (%1,%3,4), %%xmm2\n\t" - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" - "movdqa %%xmm2, %%xmm6\n\t" - "pmaxub %%xmm1, %%xmm2\n\t" - "pminub %%xmm5, %%xmm6\n\t" - "psubb %%xmm6, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm3\n\t" - "psrld $0x10, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "pslld $0x2, %%xmm2\n\t" - "paddd %%xmm1, %%xmm2\n\t" - "movdqa %%xmm3, %%xmm1\n\t" - "psrld $0x8, %%xmm1\n\t" - "pand %%xmm0, %%xmm1\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x18, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "paddd %%xmm2, %%xmm2\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "packssdw %%xmm1, %%xmm1\n\t" - "packuswb %%xmm1, %%xmm1\n\t" - "movd %%xmm1, %%eax\n\t" - "movnti %%eax, (%2,%3)\n\t" - "sub $0x4, %3\n\t" - "jnz sse2_delta8_abgr_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" - ); + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "mov $0xff, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x4, %2\n\t" + "sse2_delta8_abgr_iter:\n\t" + "movdqa (%0,%3,4), %%xmm1\n\t" + "movdqa (%1,%3,4), %%xmm2\n\t" + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" + "movdqa %%xmm2, %%xmm6\n\t" + "pmaxub %%xmm1, %%xmm2\n\t" + "pminub %%xmm5, %%xmm6\n\t" + "psubb %%xmm6, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm3\n\t" + "psrld $0x10, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "pslld $0x2, %%xmm2\n\t" + "paddd %%xmm1, %%xmm2\n\t" + "movdqa %%xmm3, %%xmm1\n\t" + "psrld $0x8, %%xmm1\n\t" + "pand %%xmm0, %%xmm1\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x18, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "paddd %%xmm2, %%xmm2\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "packssdw %%xmm1, %%xmm1\n\t" + "packuswb %%xmm1, %%xmm1\n\t" + "movd %%xmm1, %%eax\n\t" + "movnti %%eax, (%2,%3)\n\t" + "sub $0x4, %3\n\t" + "jnz sse2_delta8_abgr_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -3603,52 +3734,52 @@ __attribute__((noinline,__target__("ssse3"))) #endif void ssse3_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "mov $0xff, %%eax\n\t" - "movd %%eax, %%xmm0\n\t" - "pshufd $0x0, %%xmm0, %%xmm0\n\t" - "movdqa %4, %%xmm5\n\t" - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x4, %2\n\t" - "ssse3_delta8_rgba_iter:\n\t" - "movdqa (%0,%3,4), %%xmm1\n\t" - "movdqa (%1,%3,4), %%xmm2\n\t" - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm3\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x8, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "pslld $0x2, %%xmm2\n\t" - "paddd %%xmm1, %%xmm2\n\t" - "movdqa %%xmm3, %%xmm1\n\t" - "pand %%xmm0, %%xmm1\n\t" - "paddd %%xmm1, %%xmm1\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x10, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "pshufb %%xmm5, %%xmm1\n\t" - "movd %%xmm1, %%eax\n\t" - "movnti %%eax, (%2,%3)\n\t" - "sub $0x4, %3\n\t" - "jnz ssse3_delta8_rgba_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" - ); + + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "mov $0xff, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "movdqa %4, %%xmm5\n\t" + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x4, %2\n\t" + "ssse3_delta8_rgba_iter:\n\t" + "movdqa (%0,%3,4), %%xmm1\n\t" + "movdqa (%1,%3,4), %%xmm2\n\t" + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm3\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x8, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "pslld $0x2, %%xmm2\n\t" + "paddd %%xmm1, %%xmm2\n\t" + "movdqa %%xmm3, %%xmm1\n\t" + "pand %%xmm0, %%xmm1\n\t" + "paddd %%xmm1, %%xmm1\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x10, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "pshufb %%xmm5, %%xmm1\n\t" + "movd %%xmm1, %%eax\n\t" + "movnti %%eax, (%2,%3)\n\t" + "sub $0x4, %3\n\t" + "jnz ssse3_delta8_rgba_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -3658,52 +3789,52 @@ __attribute__((noinline,__target__("ssse3"))) #endif void ssse3_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "mov $0xff, %%eax\n\t" - "movd %%eax, %%xmm0\n\t" - "pshufd $0x0, %%xmm0, %%xmm0\n\t" - "movdqa %4, %%xmm5\n\t" - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x4, %2\n\t" - "ssse3_delta8_bgra_iter:\n\t" - "movdqa (%0,%3,4), %%xmm1\n\t" - "movdqa (%1,%3,4), %%xmm2\n\t" - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm3\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x8, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "pslld $0x2, %%xmm2\n\t" - "paddd %%xmm1, %%xmm2\n\t" - "movdqa %%xmm3, %%xmm1\n\t" - "pand %%xmm0, %%xmm1\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x10, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "paddd %%xmm2, %%xmm2\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "pshufb %%xmm5, %%xmm1\n\t" - "movd %%xmm1, %%eax\n\t" - "movnti %%eax, (%2,%3)\n\t" - "sub $0x4, %3\n\t" - "jnz ssse3_delta8_bgra_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" - ); + + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "mov $0xff, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "movdqa %4, %%xmm5\n\t" + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x4, %2\n\t" + "ssse3_delta8_bgra_iter:\n\t" + "movdqa (%0,%3,4), %%xmm1\n\t" + "movdqa (%1,%3,4), %%xmm2\n\t" + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm3\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x8, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "pslld $0x2, %%xmm2\n\t" + "paddd %%xmm1, %%xmm2\n\t" + "movdqa %%xmm3, %%xmm1\n\t" + "pand %%xmm0, %%xmm1\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x10, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "paddd %%xmm2, %%xmm2\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "pshufb %%xmm5, %%xmm1\n\t" + "movd %%xmm1, %%eax\n\t" + "movnti %%eax, (%2,%3)\n\t" + "sub $0x4, %3\n\t" + "jnz ssse3_delta8_bgra_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -3713,53 +3844,53 @@ __attribute__((noinline,__target__("ssse3"))) #endif void ssse3_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "mov $0xff, %%eax\n\t" - "movd %%eax, %%xmm0\n\t" - "pshufd $0x0, %%xmm0, %%xmm0\n\t" - "movdqa %4, %%xmm5\n\t" - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x4, %2\n\t" - "ssse3_delta8_argb_iter:\n\t" - "movdqa (%0,%3,4), %%xmm1\n\t" - "movdqa (%1,%3,4), %%xmm2\n\t" - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm3\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x10, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "pslld $0x2, %%xmm2\n\t" - "paddd %%xmm1, %%xmm2\n\t" - "movdqa %%xmm3, %%xmm1\n\t" - "psrld $0x8, %%xmm1\n\t" - "pand %%xmm0, %%xmm1\n\t" - "paddd %%xmm1, %%xmm1\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x18, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "pshufb %%xmm5, %%xmm1\n\t" - "movd %%xmm1, %%eax\n\t" - "movnti %%eax, (%2,%3)\n\t" - "sub $0x4, %3\n\t" - "jnz ssse3_delta8_argb_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" - ); + + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "mov $0xff, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "movdqa %4, %%xmm5\n\t" + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x4, %2\n\t" + "ssse3_delta8_argb_iter:\n\t" + "movdqa (%0,%3,4), %%xmm1\n\t" + "movdqa (%1,%3,4), %%xmm2\n\t" + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm3\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x10, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "pslld $0x2, %%xmm2\n\t" + "paddd %%xmm1, %%xmm2\n\t" + "movdqa %%xmm3, %%xmm1\n\t" + "psrld $0x8, %%xmm1\n\t" + "pand %%xmm0, %%xmm1\n\t" + "paddd %%xmm1, %%xmm1\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x18, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "pshufb %%xmm5, %%xmm1\n\t" + "movd %%xmm1, %%eax\n\t" + "movnti %%eax, (%2,%3)\n\t" + "sub $0x4, %3\n\t" + "jnz ssse3_delta8_argb_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -3769,53 +3900,53 @@ __attribute__((noinline,__target__("ssse3"))) #endif void ssse3_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "mov $0xff, %%eax\n\t" - "movd %%eax, %%xmm0\n\t" - "pshufd $0x0, %%xmm0, %%xmm0\n\t" - "movdqa %4, %%xmm5\n\t" - "sub $0x10, %0\n\t" - "sub $0x10, %1\n\t" - "sub $0x4, %2\n\t" - "ssse3_delta8_abgr_iter:\n\t" - "movdqa (%0,%3,4), %%xmm1\n\t" - "movdqa (%1,%3,4), %%xmm2\n\t" - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm3\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x10, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "pslld $0x2, %%xmm2\n\t" - "paddd %%xmm1, %%xmm2\n\t" - "movdqa %%xmm3, %%xmm1\n\t" - "psrld $0x8, %%xmm1\n\t" - "pand %%xmm0, %%xmm1\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x18, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "paddd %%xmm2, %%xmm2\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "pshufb %%xmm5, %%xmm1\n\t" - "movd %%xmm1, %%eax\n\t" - "movnti %%eax, (%2,%3)\n\t" - "sub $0x4, %3\n\t" - "jnz ssse3_delta8_abgr_iter\n\t" - : - : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" - ); + + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "mov $0xff, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "movdqa %4, %%xmm5\n\t" + "sub $0x10, %0\n\t" + "sub $0x10, %1\n\t" + "sub $0x4, %2\n\t" + "ssse3_delta8_abgr_iter:\n\t" + "movdqa (%0,%3,4), %%xmm1\n\t" + "movdqa (%1,%3,4), %%xmm2\n\t" + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm3\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x10, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "pslld $0x2, %%xmm2\n\t" + "paddd %%xmm1, %%xmm2\n\t" + "movdqa %%xmm3, %%xmm1\n\t" + "psrld $0x8, %%xmm1\n\t" + "pand %%xmm0, %%xmm1\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x18, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "paddd %%xmm2, %%xmm2\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "pshufb %%xmm5, %%xmm1\n\t" + "movd %%xmm1, %%eax\n\t" + "movnti %%eax, (%2,%3)\n\t" + "sub $0x4, %3\n\t" + "jnz ssse3_delta8_abgr_iter\n\t" + : + : "r" (col1), "r" (col2), "r" (result), "r" (count), "m" (*movemask) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -3824,198 +3955,198 @@ void ssse3_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result /* RGB24 to grayscale */ __attribute__((noinline)) void std_convert_rgb_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - r = col1[0]; - g = col1[1]; - b = col1[2]; - result[0] = (r + r + b + g + g + g + g + g)>>3; - r = col1[3]; - g = col1[4]; - b = col1[5]; - result[1] = (r + r + b + g + g + g + g + g)>>3; - r = col1[6]; - g = col1[7]; - b = col1[8]; - result[2] = (r + r + b + g + g + g + g + g)>>3; - r = col1[9]; - g = col1[10]; - b = col1[11]; - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 12; - result += 4; - } + unsigned int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + r = col1[0]; + g = col1[1]; + b = col1[2]; + result[0] = (r + r + b + g + g + g + g + g)>>3; + r = col1[3]; + g = col1[4]; + b = col1[5]; + result[1] = (r + r + b + g + g + g + g + g)>>3; + r = col1[6]; + g = col1[7]; + b = col1[8]; + result[2] = (r + r + b + g + g + g + g + g)>>3; + r = col1[9]; + g = col1[10]; + b = col1[11]; + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 12; + result += 4; + } } /* BGR24 to grayscale */ __attribute__((noinline)) void std_convert_bgr_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - b = col1[0]; - g = col1[1]; - r = col1[2]; - result[0] = (r + r + b + g + g + g + g + g)>>3; - b = col1[3]; - g = col1[4]; - r = col1[5]; - result[1] = (r + r + b + g + g + g + g + g)>>3; - b = col1[6]; - g = col1[7]; - r = col1[8]; - result[2] = (r + r + b + g + g + g + g + g)>>3; - b = col1[9]; - g = col1[10]; - r = col1[11]; - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 12; - result += 4; - } + unsigned int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + b = col1[0]; + g = col1[1]; + r = col1[2]; + result[0] = (r + r + b + g + g + g + g + g)>>3; + b = col1[3]; + g = col1[4]; + r = col1[5]; + result[1] = (r + r + b + g + g + g + g + g)>>3; + b = col1[6]; + g = col1[7]; + r = col1[8]; + result[2] = (r + r + b + g + g + g + g + g)>>3; + b = col1[9]; + g = col1[10]; + r = col1[11]; + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 12; + result += 4; + } } /* RGBA to grayscale */ __attribute__((noinline)) void std_convert_rgba_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - r = col1[0]; - g = col1[1]; - b = col1[2]; - result[0] = (r + r + b + g + g + g + g + g)>>3; - r = col1[4]; - g = col1[5]; - b = col1[6]; - result[1] = (r + r + b + g + g + g + g + g)>>3; - r = col1[8]; - g = col1[9]; - b = col1[10]; - result[2] = (r + r + b + g + g + g + g + g)>>3; - r = col1[12]; - g = col1[13]; - b = col1[14]; - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 16; - result += 4; - } + unsigned int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + r = col1[0]; + g = col1[1]; + b = col1[2]; + result[0] = (r + r + b + g + g + g + g + g)>>3; + r = col1[4]; + g = col1[5]; + b = col1[6]; + result[1] = (r + r + b + g + g + g + g + g)>>3; + r = col1[8]; + g = col1[9]; + b = col1[10]; + result[2] = (r + r + b + g + g + g + g + g)>>3; + r = col1[12]; + g = col1[13]; + b = col1[14]; + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 16; + result += 4; + } } /* BGRA to grayscale */ __attribute__((noinline)) void std_convert_bgra_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - b = col1[0]; - g = col1[1]; - r = col1[2]; - result[0] = (r + r + b + g + g + g + g + g)>>3; - b = col1[4]; - g = col1[5]; - r = col1[6]; - result[1] = (r + r + b + g + g + g + g + g)>>3; - b = col1[8]; - g = col1[9]; - r = col1[10]; - result[2] = (r + r + b + g + g + g + g + g)>>3; - b = col1[12]; - g = col1[13]; - r = col1[14]; - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 16; - result += 4; - } + unsigned int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + b = col1[0]; + g = col1[1]; + r = col1[2]; + result[0] = (r + r + b + g + g + g + g + g)>>3; + b = col1[4]; + g = col1[5]; + r = col1[6]; + result[1] = (r + r + b + g + g + g + g + g)>>3; + b = col1[8]; + g = col1[9]; + r = col1[10]; + result[2] = (r + r + b + g + g + g + g + g)>>3; + b = col1[12]; + g = col1[13]; + r = col1[14]; + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 16; + result += 4; + } } /* ARGB to grayscale */ __attribute__((noinline)) void std_convert_argb_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - r = col1[1]; - g = col1[2]; - b = col1[3]; - result[0] = (r + r + b + g + g + g + g + g)>>3; - r = col1[5]; - g = col1[6]; - b = col1[7]; - result[1] = (r + r + b + g + g + g + g + g)>>3; - r = col1[9]; - g = col1[10]; - b = col1[11]; - result[2] = (r + r + b + g + g + g + g + g)>>3; - r = col1[13]; - g = col1[14]; - b = col1[15]; - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 16; - result += 4; - } + unsigned int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + r = col1[1]; + g = col1[2]; + b = col1[3]; + result[0] = (r + r + b + g + g + g + g + g)>>3; + r = col1[5]; + g = col1[6]; + b = col1[7]; + result[1] = (r + r + b + g + g + g + g + g)>>3; + r = col1[9]; + g = col1[10]; + b = col1[11]; + result[2] = (r + r + b + g + g + g + g + g)>>3; + r = col1[13]; + g = col1[14]; + b = col1[15]; + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 16; + result += 4; + } } /* ABGR to grayscale */ __attribute__((noinline)) void std_convert_abgr_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - b = col1[1]; - g = col1[2]; - r = col1[3]; - result[0] = (r + r + b + g + g + g + g + g)>>3; - b = col1[5]; - g = col1[6]; - r = col1[7]; - result[1] = (r + r + b + g + g + g + g + g)>>3; - b = col1[9]; - g = col1[10]; - r = col1[11]; - result[2] = (r + r + b + g + g + g + g + g)>>3; - b = col1[13]; - g = col1[14]; - r = col1[15]; - result[3] = (r + r + b + g + g + g + g + g)>>3; - - col1 += 16; - result += 4; - } + unsigned int r,g,b; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + b = col1[1]; + g = col1[2]; + r = col1[3]; + result[0] = (r + r + b + g + g + g + g + g)>>3; + b = col1[5]; + g = col1[6]; + r = col1[7]; + result[1] = (r + r + b + g + g + g + g + g)>>3; + b = col1[9]; + g = col1[10]; + r = col1[11]; + result[2] = (r + r + b + g + g + g + g + g)>>3; + b = col1[13]; + g = col1[14]; + r = col1[15]; + result[3] = (r + r + b + g + g + g + g + g)>>3; + + col1 += 16; + result += 4; + } } /* Converts a YUYV image into grayscale by extracting the Y channel */ __attribute__((noinline)) void std_convert_yuyv_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { - const uint16_t* yuvbuf = (const uint16_t*)col1; - const uint8_t* const max_ptr = result + count; - - while(result < max_ptr) { - result[0] = (uint8_t)yuvbuf[0]; - result[1] = (uint8_t)yuvbuf[1]; - result[2] = (uint8_t)yuvbuf[2]; - result[3] = (uint8_t)yuvbuf[3]; - result[4] = (uint8_t)yuvbuf[4]; - result[5] = (uint8_t)yuvbuf[5]; - result[6] = (uint8_t)yuvbuf[6]; - result[7] = (uint8_t)yuvbuf[7]; - result[8] = (uint8_t)yuvbuf[8]; - result[9] = (uint8_t)yuvbuf[9]; - result[10] = (uint8_t)yuvbuf[10]; - result[11] = (uint8_t)yuvbuf[11]; - result[12] = (uint8_t)yuvbuf[12]; - result[13] = (uint8_t)yuvbuf[13]; - result[14] = (uint8_t)yuvbuf[14]; - result[15] = (uint8_t)yuvbuf[15]; - - yuvbuf += 16; - result += 16; - } + const uint16_t* yuvbuf = (const uint16_t*)col1; + const uint8_t* const max_ptr = result + count; + + while(result < max_ptr) { + result[0] = (uint8_t)yuvbuf[0]; + result[1] = (uint8_t)yuvbuf[1]; + result[2] = (uint8_t)yuvbuf[2]; + result[3] = (uint8_t)yuvbuf[3]; + result[4] = (uint8_t)yuvbuf[4]; + result[5] = (uint8_t)yuvbuf[5]; + result[6] = (uint8_t)yuvbuf[6]; + result[7] = (uint8_t)yuvbuf[7]; + result[8] = (uint8_t)yuvbuf[8]; + result[9] = (uint8_t)yuvbuf[9]; + result[10] = (uint8_t)yuvbuf[10]; + result[11] = (uint8_t)yuvbuf[11]; + result[12] = (uint8_t)yuvbuf[12]; + result[13] = (uint8_t)yuvbuf[13]; + result[14] = (uint8_t)yuvbuf[14]; + result[15] = (uint8_t)yuvbuf[15]; + + yuvbuf += 16; + result += 16; + } } /* RGBA to grayscale SSSE3 */ @@ -4025,45 +4156,45 @@ __attribute__((noinline,__target__("ssse3"))) void ssse3_convert_rgba_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "mov $0xff, %%eax\n\t" - "movd %%eax, %%xmm0\n\t" - "pshufd $0x0, %%xmm0, %%xmm0\n\t" - "movdqa %3, %%xmm5\n\t" - "sub $0x10, %0\n\t" - "sub $0x4, %1\n\t" - "ssse3_convert_rgba_gray8_iter:\n\t" - "movdqa (%0,%2,4), %%xmm3\n\t" - "psrlq $0x3, %%xmm3\n\t" - "pand %%xmm4, %%xmm3\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x8, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "pslld $0x2, %%xmm2\n\t" - "paddd %%xmm1, %%xmm2\n\t" - "movdqa %%xmm3, %%xmm1\n\t" - "pand %%xmm0, %%xmm1\n\t" - "paddd %%xmm1, %%xmm1\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "movdqa %%xmm3, %%xmm2\n\t" - "psrld $0x10, %%xmm2\n\t" - "pand %%xmm0, %%xmm2\n\t" - "paddd %%xmm2, %%xmm1\n\t" - "pshufb %%xmm5, %%xmm1\n\t" - "movd %%xmm1, %%eax\n\t" - "movnti %%eax, (%1,%2)\n\t" - "sub $0x4, %2\n\t" - "jnz ssse3_convert_rgba_gray8_iter\n\t" - : - : "r" (col1), "r" (result), "r" (count), "m" (*movemask) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" - ); + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "mov $0xff, %%eax\n\t" + "movd %%eax, %%xmm0\n\t" + "pshufd $0x0, %%xmm0, %%xmm0\n\t" + "movdqa %3, %%xmm5\n\t" + "sub $0x10, %0\n\t" + "sub $0x4, %1\n\t" + "ssse3_convert_rgba_gray8_iter:\n\t" + "movdqa (%0,%2,4), %%xmm3\n\t" + "psrlq $0x3, %%xmm3\n\t" + "pand %%xmm4, %%xmm3\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x8, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "pslld $0x2, %%xmm2\n\t" + "paddd %%xmm1, %%xmm2\n\t" + "movdqa %%xmm3, %%xmm1\n\t" + "pand %%xmm0, %%xmm1\n\t" + "paddd %%xmm1, %%xmm1\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "movdqa %%xmm3, %%xmm2\n\t" + "psrld $0x10, %%xmm2\n\t" + "pand %%xmm0, %%xmm2\n\t" + "paddd %%xmm2, %%xmm1\n\t" + "pshufb %%xmm5, %%xmm1\n\t" + "movd %%xmm1, %%eax\n\t" + "movnti %%eax, (%1,%2)\n\t" + "sub $0x4, %2\n\t" + "jnz ssse3_convert_rgba_gray8_iter\n\t" + : + : "r" (col1), "r" (result), "r" (count), "m" (*movemask) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -4073,156 +4204,156 @@ __attribute__((noinline,__target__("ssse3"))) #endif void ssse3_convert_yuyv_gray8(const uint8_t* col1, uint8_t* result, unsigned long count) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - unsigned long i = 0; + unsigned long i = 0; - __attribute__((aligned(16))) static const uint8_t movemask1[16] = {0,2,4,6,8,10,12,14,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; - __attribute__((aligned(16))) static const uint8_t movemask2[16] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,2,4,6,8,10,12,14}; - - /* XMM0 - General purpose */ - /* XMM1 - General purpose */ - /* XMM2 - unused */ - /* XMM3 - shift mask 1 */ - /* XMM4 - shift mask 2 */ - /* XMM5 - unused*/ - /* XMM6 - unused */ - /* XMM7 - unused */ + __attribute__((aligned(16))) static const uint8_t movemask1[16] = {0,2,4,6,8,10,12,14,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + __attribute__((aligned(16))) static const uint8_t movemask2[16] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,2,4,6,8,10,12,14}; + + /* XMM0 - General purpose */ + /* XMM1 - General purpose */ + /* XMM2 - unused */ + /* XMM3 - shift mask 1 */ + /* XMM4 - shift mask 2 */ + /* XMM5 - unused*/ + /* XMM6 - unused */ + /* XMM7 - unused */ - __asm__ __volatile__ ( - "movdqa %4, %%xmm3\n\t" - "movdqa %5, %%xmm4\n\t" - "algo_ssse3_convert_yuyv_gray8:\n\t" - "movdqa (%0), %%xmm0\n\t" - "pshufb %%xmm3, %%xmm0\n\t" - "movdqa 0x10(%0), %%xmm1\n\t" - "pshufb %%xmm4, %%xmm1\n\t" - "por %%xmm1, %%xmm0\n\t" - "movntdq %%xmm0, (%1)\n\t" - "add $0x10, %3\n\t" - "add $0x10, %1\n\t" - "add $0x20, %0\n\t" - "cmp %2, %3\n\t" - "jb algo_ssse3_convert_yuyv_gray8\n\t" - : + __asm__ __volatile__ ( + "movdqa %4, %%xmm3\n\t" + "movdqa %5, %%xmm4\n\t" + "algo_ssse3_convert_yuyv_gray8:\n\t" + "movdqa (%0), %%xmm0\n\t" + "pshufb %%xmm3, %%xmm0\n\t" + "movdqa 0x10(%0), %%xmm1\n\t" + "pshufb %%xmm4, %%xmm1\n\t" + "por %%xmm1, %%xmm0\n\t" + "movntdq %%xmm0, (%1)\n\t" + "add $0x10, %3\n\t" + "add $0x10, %1\n\t" + "add $0x20, %0\n\t" + "cmp %2, %3\n\t" + "jb algo_ssse3_convert_yuyv_gray8\n\t" + : #if (defined(_DEBUG) && !defined(__x86_64__)) /* Use one less register to allow compilation to success on 32bit with omit frame pointer disabled */ - : "r" (col1), "r" (result), "m" (count), "r" (i), "m" (*movemask1), "m" (*movemask2) + : "r" (col1), "r" (result), "m" (count), "r" (i), "m" (*movemask1), "m" (*movemask2) #else - : "r" (col1), "r" (result), "r" (count), "r" (i), "m" (*movemask1), "m" (*movemask2) + : "r" (col1), "r" (result), "r" (count), "r" (i), "m" (*movemask1), "m" (*movemask2) #endif - : "%xmm3", "%xmm4", "cc", "memory" - ); + : "%xmm3", "%xmm4", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } /* YUYV to RGB24 - relocated from zm_local_camera.cpp */ __attribute__((noinline)) void zm_convert_yuyv_rgb(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - unsigned int y1,y2,u,v; - for(unsigned int i=0; i < count; i += 2, col1 += 4, result += 6) { - y1 = col1[0]; - u = col1[1]; - y2 = col1[2]; - v = col1[3]; + unsigned int r,g,b; + unsigned int y1,y2,u,v; + for(unsigned int i=0; i < count; i += 2, col1 += 4, result += 6) { + y1 = col1[0]; + u = col1[1]; + y2 = col1[2]; + v = col1[3]; - r = y1 + r_v_table[v]; - g = y1 - (g_u_table[u]+g_v_table[v]); - b = y1 + b_u_table[u]; - - result[0] = r<0?0:(r>255?255:r); - result[1] = g<0?0:(g>255?255:g); - result[2] = b<0?0:(b>255?255:b); - - r = y2 + r_v_table[v]; - g = y2 - (g_u_table[u]+g_v_table[v]); - b = y2 + b_u_table[u]; + r = y1 + r_v_table[v]; + g = y1 - (g_u_table[u]+g_v_table[v]); + b = y1 + b_u_table[u]; + + result[0] = r<0?0:(r>255?255:r); + result[1] = g<0?0:(g>255?255:g); + result[2] = b<0?0:(b>255?255:b); + + r = y2 + r_v_table[v]; + g = y2 - (g_u_table[u]+g_v_table[v]); + b = y2 + b_u_table[u]; - result[3] = r<0?0:(r>255?255:r); - result[4] = g<0?0:(g>255?255:g); - result[5] = b<0?0:(b>255?255:b); - } - + result[3] = r<0?0:(r>255?255:r); + result[4] = g<0?0:(g>255?255:g); + result[5] = b<0?0:(b>255?255:b); + } + } /* YUYV to RGBA - modified the one above */ __attribute__((noinline)) void zm_convert_yuyv_rgba(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - unsigned int y1,y2,u,v; - for(unsigned int i=0; i < count; i += 2, col1 += 4, result += 8) { - y1 = col1[0]; - u = col1[1]; - y2 = col1[2]; - v = col1[3]; + unsigned int r,g,b; + unsigned int y1,y2,u,v; + for(unsigned int i=0; i < count; i += 2, col1 += 4, result += 8) { + y1 = col1[0]; + u = col1[1]; + y2 = col1[2]; + v = col1[3]; - r = y1 + r_v_table[v]; - g = y1 - (g_u_table[u]+g_v_table[v]); - b = y1 + b_u_table[u]; - - result[0] = r<0?0:(r>255?255:r); - result[1] = g<0?0:(g>255?255:g); - result[2] = b<0?0:(b>255?255:b); - - r = y2 + r_v_table[v]; - g = y2 - (g_u_table[u]+g_v_table[v]); - b = y2 + b_u_table[u]; + r = y1 + r_v_table[v]; + g = y1 - (g_u_table[u]+g_v_table[v]); + b = y1 + b_u_table[u]; + + result[0] = r<0?0:(r>255?255:r); + result[1] = g<0?0:(g>255?255:g); + result[2] = b<0?0:(b>255?255:b); + + r = y2 + r_v_table[v]; + g = y2 - (g_u_table[u]+g_v_table[v]); + b = y2 + b_u_table[u]; - result[4] = r<0?0:(r>255?255:r); - result[5] = g<0?0:(g>255?255:g); - result[6] = b<0?0:(b>255?255:b); - } - + result[4] = r<0?0:(r>255?255:r); + result[5] = g<0?0:(g>255?255:g); + result[6] = b<0?0:(b>255?255:b); + } + } /* RGB555 to RGB24 - relocated from zm_local_camera.cpp */ __attribute__((noinline)) void zm_convert_rgb555_rgb(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - for(unsigned int i=0; i < count; i++, col1 += 2, result += 3) { - b = ((*col1)<<3)&0xf8; - g = (((*(col1+1))<<6)|((*col1)>>2))&0xf8; - r = ((*(col1+1))<<1)&0xf8; - result[0] = r; - result[1] = g; - result[2] = b; - } + unsigned int r,g,b; + for(unsigned int i=0; i < count; i++, col1 += 2, result += 3) { + b = ((*col1)<<3)&0xf8; + g = (((*(col1+1))<<6)|((*col1)>>2))&0xf8; + r = ((*(col1+1))<<1)&0xf8; + result[0] = r; + result[1] = g; + result[2] = b; + } } /* RGB555 to RGBA - modified the one above */ __attribute__((noinline)) void zm_convert_rgb555_rgba(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - for(unsigned int i=0; i < count; i++, col1 += 2, result += 4) { - b = ((*col1)<<3)&0xf8; - g = (((*(col1+1))<<6)|((*col1)>>2))&0xf8; - r = ((*(col1+1))<<1)&0xf8; - result[0] = r; - result[1] = g; - result[2] = b; - } + unsigned int r,g,b; + for(unsigned int i=0; i < count; i++, col1 += 2, result += 4) { + b = ((*col1)<<3)&0xf8; + g = (((*(col1+1))<<6)|((*col1)>>2))&0xf8; + r = ((*(col1+1))<<1)&0xf8; + result[0] = r; + result[1] = g; + result[2] = b; + } } /* RGB565 to RGB24 - relocated from zm_local_camera.cpp */ __attribute__((noinline)) void zm_convert_rgb565_rgb(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - for(unsigned int i=0; i < count; i++, col1 += 2, result += 3) { - b = ((*col1)<<3)&0xf8; - g = (((*(col1+1))<<5)|((*col1)>>3))&0xfc; - r = (*(col1+1))&0xf8; - result[0] = r; - result[1] = g; - result[2] = b; - } + unsigned int r,g,b; + for(unsigned int i=0; i < count; i++, col1 += 2, result += 3) { + b = ((*col1)<<3)&0xf8; + g = (((*(col1+1))<<5)|((*col1)>>3))&0xfc; + r = (*(col1+1))&0xf8; + result[0] = r; + result[1] = g; + result[2] = b; + } } /* RGB565 to RGBA - modified the one above */ __attribute__((noinline)) void zm_convert_rgb565_rgba(const uint8_t* col1, uint8_t* result, unsigned long count) { - unsigned int r,g,b; - for(unsigned int i=0; i < count; i++, col1 += 2, result += 4) { - b = ((*col1)<<3)&0xf8; - g = (((*(col1+1))<<5)|((*col1)>>3))&0xfc; - r = (*(col1+1))&0xf8; - result[0] = r; - result[1] = g; - result[2] = b; - } + unsigned int r,g,b; + for(unsigned int i=0; i < count; i++, col1 += 2, result += 4) { + b = ((*col1)<<3)&0xf8; + g = (((*(col1+1))<<5)|((*col1)>>3))&0xfc; + r = (*(col1+1))&0xf8; + result[0] = r; + result[1] = g; + result[2] = b; + } } /************************************************* DEINTERLACE FUNCTIONS *************************************************/ @@ -4230,461 +4361,461 @@ __attribute__((noinline)) void zm_convert_rgb565_rgba(const uint8_t* col1, uint8 /* Grayscale */ __attribute__((noinline)) void std_deinterlace_4field_gray8(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { - uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; - const uint8_t* const max_ptr = col1 + (width*(height-1)); - const uint8_t *max_ptr2; + uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; + const uint8_t* const max_ptr = col1 + (width*(height-1)); + const uint8_t *max_ptr2; - pcurrent = col1 + width; - pncurrent = col2 + width; - pabove = col1; - pnabove = col2; - pbelow = col1 + (width*2); - while(pcurrent < max_ptr) - { - max_ptr2 = pcurrent + width; - while(pcurrent < max_ptr2) { - if((unsigned int)((abs(*pnabove - *pabove) + abs(*pncurrent - *pcurrent)) >> 1) >= threshold) { - *pcurrent = (*pabove + *pbelow) >> 1; - } - pabove++; - pnabove++; - pcurrent++; - pncurrent++; - pbelow++; - } - pcurrent += width; - pncurrent += width; - pabove += width; - pnabove += width; - pbelow += width; - - } - - /* Special case for the last line */ - max_ptr2 = pcurrent + width; - while(pcurrent < max_ptr2) { - if((unsigned int)((abs(*pnabove - *pabove) + abs(*pncurrent - *pcurrent)) >> 1) >= threshold) { - *pcurrent = *pabove; - } - pabove++; - pnabove++; - pcurrent++; - pncurrent++; - } + pcurrent = col1 + width; + pncurrent = col2 + width; + pabove = col1; + pnabove = col2; + pbelow = col1 + (width*2); + while(pcurrent < max_ptr) + { + max_ptr2 = pcurrent + width; + while(pcurrent < max_ptr2) { + if((unsigned int)((abs(*pnabove - *pabove) + abs(*pncurrent - *pcurrent)) >> 1) >= threshold) { + *pcurrent = (*pabove + *pbelow) >> 1; + } + pabove++; + pnabove++; + pcurrent++; + pncurrent++; + pbelow++; + } + pcurrent += width; + pncurrent += width; + pabove += width; + pnabove += width; + pbelow += width; + + } + + /* Special case for the last line */ + max_ptr2 = pcurrent + width; + while(pcurrent < max_ptr2) { + if((unsigned int)((abs(*pnabove - *pabove) + abs(*pncurrent - *pcurrent)) >> 1) >= threshold) { + *pcurrent = *pabove; + } + pabove++; + pnabove++; + pcurrent++; + pncurrent++; + } } /* RGB */ __attribute__((noinline)) void std_deinterlace_4field_rgb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { - uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; - const unsigned int row_width = width*3; - const uint8_t* const max_ptr = col1 + (row_width * (height-1)); - const uint8_t *max_ptr2; - unsigned int b, g, r; - unsigned int delta1, delta2; + uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; + const unsigned int row_width = width*3; + const uint8_t* const max_ptr = col1 + (row_width * (height-1)); + const uint8_t *max_ptr2; + unsigned int b, g, r; + unsigned int delta1, delta2; - pcurrent = col1 + (width*3); - pncurrent = col2 + (width*3); - pabove = col1; - pnabove = col2; - pbelow = col1 + ((width*2)*3); - while(pcurrent < max_ptr) - { - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - r = abs(pnabove[0] - pabove[0]); - g = abs(pnabove[1] - pabove[1]); - b = abs(pnabove[2] - pabove[2]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - r = abs(pncurrent[0] - pcurrent[0]); - g = abs(pncurrent[1] - pcurrent[1]); - b = abs(pncurrent[2] - pcurrent[2]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; - pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; - pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; - } - pabove += 3; - pnabove += 3; - pcurrent += 3; - pncurrent += 3; - pbelow += 3; - } - pcurrent += row_width; - pncurrent += row_width; - pabove += row_width; - pnabove += row_width; - pbelow += row_width; - - } - - /* Special case for the last line */ - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - r = abs(pnabove[0] - pabove[0]); - g = abs(pnabove[1] - pabove[1]); - b = abs(pnabove[2] - pabove[2]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - r = abs(pncurrent[0] - pcurrent[0]); - g = abs(pncurrent[1] - pcurrent[1]); - b = abs(pncurrent[2] - pcurrent[2]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[0] = pabove[0]; - pcurrent[1] = pabove[1]; - pcurrent[2] = pabove[2]; - } - pabove += 3; - pnabove += 3; - pcurrent += 3; - pncurrent += 3; - } + pcurrent = col1 + (width*3); + pncurrent = col2 + (width*3); + pabove = col1; + pnabove = col2; + pbelow = col1 + ((width*2)*3); + while(pcurrent < max_ptr) + { + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + r = abs(pnabove[0] - pabove[0]); + g = abs(pnabove[1] - pabove[1]); + b = abs(pnabove[2] - pabove[2]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + r = abs(pncurrent[0] - pcurrent[0]); + g = abs(pncurrent[1] - pcurrent[1]); + b = abs(pncurrent[2] - pcurrent[2]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; + pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; + pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; + } + pabove += 3; + pnabove += 3; + pcurrent += 3; + pncurrent += 3; + pbelow += 3; + } + pcurrent += row_width; + pncurrent += row_width; + pabove += row_width; + pnabove += row_width; + pbelow += row_width; + + } + + /* Special case for the last line */ + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + r = abs(pnabove[0] - pabove[0]); + g = abs(pnabove[1] - pabove[1]); + b = abs(pnabove[2] - pabove[2]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + r = abs(pncurrent[0] - pcurrent[0]); + g = abs(pncurrent[1] - pcurrent[1]); + b = abs(pncurrent[2] - pcurrent[2]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[0] = pabove[0]; + pcurrent[1] = pabove[1]; + pcurrent[2] = pabove[2]; + } + pabove += 3; + pnabove += 3; + pcurrent += 3; + pncurrent += 3; + } } /* BGR */ __attribute__((noinline)) void std_deinterlace_4field_bgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { - uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; - const unsigned int row_width = width*3; - const uint8_t* const max_ptr = col1 + (row_width * (height-1)); - const uint8_t *max_ptr2; - unsigned int b, g, r; - unsigned int delta1, delta2; + uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; + const unsigned int row_width = width*3; + const uint8_t* const max_ptr = col1 + (row_width * (height-1)); + const uint8_t *max_ptr2; + unsigned int b, g, r; + unsigned int delta1, delta2; - pcurrent = col1 + (width*3); - pncurrent = col2 + (width*3); - pabove = col1; - pnabove = col2; - pbelow = col1 + ((width*2)*3); - while(pcurrent < max_ptr) - { - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - b = abs(pnabove[0] - pabove[0]); - g = abs(pnabove[1] - pabove[1]); - r = abs(pnabove[2] - pabove[2]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - b = abs(pncurrent[0] - pcurrent[0]); - g = abs(pncurrent[1] - pcurrent[1]); - r = abs(pncurrent[2] - pcurrent[2]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; - pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; - pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; - } - pabove += 3; - pnabove += 3; - pcurrent += 3; - pncurrent += 3; - pbelow += 3; - } - pcurrent += row_width; - pncurrent += row_width; - pabove += row_width; - pnabove += row_width; - pbelow += row_width; - - } - - /* Special case for the last line */ - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - b = abs(pnabove[0] - pabove[0]); - g = abs(pnabove[1] - pabove[1]); - r = abs(pnabove[2] - pabove[2]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - b = abs(pncurrent[0] - pcurrent[0]); - g = abs(pncurrent[1] - pcurrent[1]); - r = abs(pncurrent[2] - pcurrent[2]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[0] = pabove[0]; - pcurrent[1] = pabove[1]; - pcurrent[2] = pabove[2]; - } - pabove += 3; - pnabove += 3; - pcurrent += 3; - pncurrent += 3; - } + pcurrent = col1 + (width*3); + pncurrent = col2 + (width*3); + pabove = col1; + pnabove = col2; + pbelow = col1 + ((width*2)*3); + while(pcurrent < max_ptr) + { + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + b = abs(pnabove[0] - pabove[0]); + g = abs(pnabove[1] - pabove[1]); + r = abs(pnabove[2] - pabove[2]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + b = abs(pncurrent[0] - pcurrent[0]); + g = abs(pncurrent[1] - pcurrent[1]); + r = abs(pncurrent[2] - pcurrent[2]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; + pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; + pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; + } + pabove += 3; + pnabove += 3; + pcurrent += 3; + pncurrent += 3; + pbelow += 3; + } + pcurrent += row_width; + pncurrent += row_width; + pabove += row_width; + pnabove += row_width; + pbelow += row_width; + + } + + /* Special case for the last line */ + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + b = abs(pnabove[0] - pabove[0]); + g = abs(pnabove[1] - pabove[1]); + r = abs(pnabove[2] - pabove[2]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + b = abs(pncurrent[0] - pcurrent[0]); + g = abs(pncurrent[1] - pcurrent[1]); + r = abs(pncurrent[2] - pcurrent[2]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[0] = pabove[0]; + pcurrent[1] = pabove[1]; + pcurrent[2] = pabove[2]; + } + pabove += 3; + pnabove += 3; + pcurrent += 3; + pncurrent += 3; + } } /* RGBA */ __attribute__((noinline)) void std_deinterlace_4field_rgba(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { - uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; - const unsigned int row_width = width*4; - const uint8_t* const max_ptr = col1 + (row_width * (height-1)); - const uint8_t *max_ptr2; - unsigned int b, g, r; - unsigned int delta1, delta2; + uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; + const unsigned int row_width = width*4; + const uint8_t* const max_ptr = col1 + (row_width * (height-1)); + const uint8_t *max_ptr2; + unsigned int b, g, r; + unsigned int delta1, delta2; - pcurrent = col1 + row_width; - pncurrent = col2 + row_width; - pabove = col1; - pnabove = col2; - pbelow = col1 + (row_width*2); - while(pcurrent < max_ptr) - { - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - r = abs(pnabove[0] - pabove[0]); - g = abs(pnabove[1] - pabove[1]); - b = abs(pnabove[2] - pabove[2]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - r = abs(pncurrent[0] - pcurrent[0]); - g = abs(pncurrent[1] - pcurrent[1]); - b = abs(pncurrent[2] - pcurrent[2]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; - pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; - pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; - } - pabove += 4; - pnabove += 4; - pcurrent += 4; - pncurrent += 4; - pbelow += 4; - } - pcurrent += row_width; - pncurrent += row_width; - pabove += row_width; - pnabove += row_width; - pbelow += row_width; - - } - - /* Special case for the last line */ - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - r = abs(pnabove[0] - pabove[0]); - g = abs(pnabove[1] - pabove[1]); - b = abs(pnabove[2] - pabove[2]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - r = abs(pncurrent[0] - pcurrent[0]); - g = abs(pncurrent[1] - pcurrent[1]); - b = abs(pncurrent[2] - pcurrent[2]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[0] = pabove[0]; - pcurrent[1] = pabove[1]; - pcurrent[2] = pabove[2]; - } - pabove += 4; - pnabove += 4; - pcurrent += 4; - pncurrent += 4; - } + pcurrent = col1 + row_width; + pncurrent = col2 + row_width; + pabove = col1; + pnabove = col2; + pbelow = col1 + (row_width*2); + while(pcurrent < max_ptr) + { + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + r = abs(pnabove[0] - pabove[0]); + g = abs(pnabove[1] - pabove[1]); + b = abs(pnabove[2] - pabove[2]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + r = abs(pncurrent[0] - pcurrent[0]); + g = abs(pncurrent[1] - pcurrent[1]); + b = abs(pncurrent[2] - pcurrent[2]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; + pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; + pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; + } + pabove += 4; + pnabove += 4; + pcurrent += 4; + pncurrent += 4; + pbelow += 4; + } + pcurrent += row_width; + pncurrent += row_width; + pabove += row_width; + pnabove += row_width; + pbelow += row_width; + + } + + /* Special case for the last line */ + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + r = abs(pnabove[0] - pabove[0]); + g = abs(pnabove[1] - pabove[1]); + b = abs(pnabove[2] - pabove[2]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + r = abs(pncurrent[0] - pcurrent[0]); + g = abs(pncurrent[1] - pcurrent[1]); + b = abs(pncurrent[2] - pcurrent[2]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[0] = pabove[0]; + pcurrent[1] = pabove[1]; + pcurrent[2] = pabove[2]; + } + pabove += 4; + pnabove += 4; + pcurrent += 4; + pncurrent += 4; + } } /* BGRA */ __attribute__((noinline)) void std_deinterlace_4field_bgra(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { - uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; - const unsigned int row_width = width*4; - const uint8_t* const max_ptr = col1 + (row_width * (height-1)); - const uint8_t *max_ptr2; - unsigned int b, g, r; - unsigned int delta1, delta2; + uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; + const unsigned int row_width = width*4; + const uint8_t* const max_ptr = col1 + (row_width * (height-1)); + const uint8_t *max_ptr2; + unsigned int b, g, r; + unsigned int delta1, delta2; - pcurrent = col1 + row_width; - pncurrent = col2 + row_width; - pabove = col1; - pnabove = col2; - pbelow = col1 + (row_width*2); - while(pcurrent < max_ptr) - { - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - b = abs(pnabove[0] - pabove[0]); - g = abs(pnabove[1] - pabove[1]); - r = abs(pnabove[2] - pabove[2]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - b = abs(pncurrent[0] - pcurrent[0]); - g = abs(pncurrent[1] - pcurrent[1]); - r = abs(pncurrent[2] - pcurrent[2]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; - pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; - pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; - } - pabove += 4; - pnabove += 4; - pcurrent += 4; - pncurrent += 4; - pbelow += 4; - } - pcurrent += row_width; - pncurrent += row_width; - pabove += row_width; - pnabove += row_width; - pbelow += row_width; - - } - - /* Special case for the last line */ - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - b = abs(pnabove[0] - pabove[0]); - g = abs(pnabove[1] - pabove[1]); - r = abs(pnabove[2] - pabove[2]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - b = abs(pncurrent[0] - pcurrent[0]); - g = abs(pncurrent[1] - pcurrent[1]); - r = abs(pncurrent[2] - pcurrent[2]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[0] = pabove[0]; - pcurrent[1] = pabove[1]; - pcurrent[2] = pabove[2]; - } - pabove += 4; - pnabove += 4; - pcurrent += 4; - pncurrent += 4; - } + pcurrent = col1 + row_width; + pncurrent = col2 + row_width; + pabove = col1; + pnabove = col2; + pbelow = col1 + (row_width*2); + while(pcurrent < max_ptr) + { + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + b = abs(pnabove[0] - pabove[0]); + g = abs(pnabove[1] - pabove[1]); + r = abs(pnabove[2] - pabove[2]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + b = abs(pncurrent[0] - pcurrent[0]); + g = abs(pncurrent[1] - pcurrent[1]); + r = abs(pncurrent[2] - pcurrent[2]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[0] = (pabove[0] + pbelow[0]) >> 1; + pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; + pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; + } + pabove += 4; + pnabove += 4; + pcurrent += 4; + pncurrent += 4; + pbelow += 4; + } + pcurrent += row_width; + pncurrent += row_width; + pabove += row_width; + pnabove += row_width; + pbelow += row_width; + + } + + /* Special case for the last line */ + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + b = abs(pnabove[0] - pabove[0]); + g = abs(pnabove[1] - pabove[1]); + r = abs(pnabove[2] - pabove[2]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + b = abs(pncurrent[0] - pcurrent[0]); + g = abs(pncurrent[1] - pcurrent[1]); + r = abs(pncurrent[2] - pcurrent[2]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[0] = pabove[0]; + pcurrent[1] = pabove[1]; + pcurrent[2] = pabove[2]; + } + pabove += 4; + pnabove += 4; + pcurrent += 4; + pncurrent += 4; + } } /* ARGB */ __attribute__((noinline)) void std_deinterlace_4field_argb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { - uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; - const unsigned int row_width = width*4; - const uint8_t* const max_ptr = col1 + (row_width * (height-1)); - const uint8_t *max_ptr2; - unsigned int b, g, r; - unsigned int delta1, delta2; + uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; + const unsigned int row_width = width*4; + const uint8_t* const max_ptr = col1 + (row_width * (height-1)); + const uint8_t *max_ptr2; + unsigned int b, g, r; + unsigned int delta1, delta2; - pcurrent = col1 + row_width; - pncurrent = col2 + row_width; - pabove = col1; - pnabove = col2; - pbelow = col1 + (row_width*2); - while(pcurrent < max_ptr) - { - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - r = abs(pnabove[1] - pabove[1]); - g = abs(pnabove[2] - pabove[2]); - b = abs(pnabove[3] - pabove[3]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - r = abs(pncurrent[1] - pcurrent[1]); - g = abs(pncurrent[2] - pcurrent[2]); - b = abs(pncurrent[3] - pcurrent[3]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; - pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; - pcurrent[3] = (pabove[3] + pbelow[3]) >> 1; - } - pabove += 4; - pnabove += 4; - pcurrent += 4; - pncurrent += 4; - pbelow += 4; - } - pcurrent += row_width; - pncurrent += row_width; - pabove += row_width; - pnabove += row_width; - pbelow += row_width; - - } - - /* Special case for the last line */ - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - r = abs(pnabove[1] - pabove[1]); - g = abs(pnabove[2] - pabove[2]); - b = abs(pnabove[3] - pabove[3]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - r = abs(pncurrent[1] - pcurrent[1]); - g = abs(pncurrent[2] - pcurrent[2]); - b = abs(pncurrent[3] - pcurrent[3]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[1] = pabove[1]; - pcurrent[2] = pabove[2]; - pcurrent[3] = pabove[3]; - } - pabove += 4; - pnabove += 4; - pcurrent += 4; - pncurrent += 4; - } + pcurrent = col1 + row_width; + pncurrent = col2 + row_width; + pabove = col1; + pnabove = col2; + pbelow = col1 + (row_width*2); + while(pcurrent < max_ptr) + { + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + r = abs(pnabove[1] - pabove[1]); + g = abs(pnabove[2] - pabove[2]); + b = abs(pnabove[3] - pabove[3]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + r = abs(pncurrent[1] - pcurrent[1]); + g = abs(pncurrent[2] - pcurrent[2]); + b = abs(pncurrent[3] - pcurrent[3]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; + pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; + pcurrent[3] = (pabove[3] + pbelow[3]) >> 1; + } + pabove += 4; + pnabove += 4; + pcurrent += 4; + pncurrent += 4; + pbelow += 4; + } + pcurrent += row_width; + pncurrent += row_width; + pabove += row_width; + pnabove += row_width; + pbelow += row_width; + + } + + /* Special case for the last line */ + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + r = abs(pnabove[1] - pabove[1]); + g = abs(pnabove[2] - pabove[2]); + b = abs(pnabove[3] - pabove[3]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + r = abs(pncurrent[1] - pcurrent[1]); + g = abs(pncurrent[2] - pcurrent[2]); + b = abs(pncurrent[3] - pcurrent[3]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[1] = pabove[1]; + pcurrent[2] = pabove[2]; + pcurrent[3] = pabove[3]; + } + pabove += 4; + pnabove += 4; + pcurrent += 4; + pncurrent += 4; + } } /* ABGR */ __attribute__((noinline)) void std_deinterlace_4field_abgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { - uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; - const unsigned int row_width = width*4; - const uint8_t* const max_ptr = col1 + (row_width * (height-1)); - const uint8_t *max_ptr2; - unsigned int b, g, r; - unsigned int delta1, delta2; + uint8_t *pcurrent, *pabove, *pncurrent, *pnabove, *pbelow; + const unsigned int row_width = width*4; + const uint8_t* const max_ptr = col1 + (row_width * (height-1)); + const uint8_t *max_ptr2; + unsigned int b, g, r; + unsigned int delta1, delta2; - pcurrent = col1 + row_width; - pncurrent = col2 + row_width; - pabove = col1; - pnabove = col2; - pbelow = col1 + (row_width*2); - while(pcurrent < max_ptr) - { - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - b = abs(pnabove[1] - pabove[1]); - g = abs(pnabove[2] - pabove[2]); - r = abs(pnabove[3] - pabove[3]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - b = abs(pncurrent[1] - pcurrent[1]); - g = abs(pncurrent[2] - pcurrent[2]); - r = abs(pncurrent[3] - pcurrent[3]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; - pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; - pcurrent[3] = (pabove[3] + pbelow[3]) >> 1; - } - pabove += 4; - pnabove += 4; - pcurrent += 4; - pncurrent += 4; - pbelow += 4; - } - pcurrent += row_width; - pncurrent += row_width; - pabove += row_width; - pnabove += row_width; - pbelow += row_width; - - } - - /* Special case for the last line */ - max_ptr2 = pcurrent + row_width; - while(pcurrent < max_ptr2) { - b = abs(pnabove[1] - pabove[1]); - g = abs(pnabove[2] - pabove[2]); - r = abs(pnabove[3] - pabove[3]); - delta1 = (r + r + b + g + g + g + g + g)>>3; - b = abs(pncurrent[1] - pcurrent[1]); - g = abs(pncurrent[2] - pcurrent[2]); - r = abs(pncurrent[3] - pcurrent[3]); - delta2 = (r + r + b + g + g + g + g + g)>>3; - if(((delta1 + delta2) >> 1) >= threshold) { - pcurrent[1] = pabove[1]; - pcurrent[2] = pabove[2]; - pcurrent[3] = pabove[3]; - } - pabove += 4; - pnabove += 4; - pcurrent += 4; - pncurrent += 4; - } + pcurrent = col1 + row_width; + pncurrent = col2 + row_width; + pabove = col1; + pnabove = col2; + pbelow = col1 + (row_width*2); + while(pcurrent < max_ptr) + { + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + b = abs(pnabove[1] - pabove[1]); + g = abs(pnabove[2] - pabove[2]); + r = abs(pnabove[3] - pabove[3]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + b = abs(pncurrent[1] - pcurrent[1]); + g = abs(pncurrent[2] - pcurrent[2]); + r = abs(pncurrent[3] - pcurrent[3]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[1] = (pabove[1] + pbelow[1]) >> 1; + pcurrent[2] = (pabove[2] + pbelow[2]) >> 1; + pcurrent[3] = (pabove[3] + pbelow[3]) >> 1; + } + pabove += 4; + pnabove += 4; + pcurrent += 4; + pncurrent += 4; + pbelow += 4; + } + pcurrent += row_width; + pncurrent += row_width; + pabove += row_width; + pnabove += row_width; + pbelow += row_width; + + } + + /* Special case for the last line */ + max_ptr2 = pcurrent + row_width; + while(pcurrent < max_ptr2) { + b = abs(pnabove[1] - pabove[1]); + g = abs(pnabove[2] - pabove[2]); + r = abs(pnabove[3] - pabove[3]); + delta1 = (r + r + b + g + g + g + g + g)>>3; + b = abs(pncurrent[1] - pcurrent[1]); + g = abs(pncurrent[2] - pcurrent[2]); + r = abs(pncurrent[3] - pcurrent[3]); + delta2 = (r + r + b + g + g + g + g + g)>>3; + if(((delta1 + delta2) >> 1) >= threshold) { + pcurrent[1] = pabove[1]; + pcurrent[2] = pabove[2]; + pcurrent[3] = pabove[3]; + } + pabove += 4; + pnabove += 4; + pcurrent += 4; + pncurrent += 4; + } } /* Grayscale SSSE3 */ @@ -4694,132 +4825,132 @@ __attribute__((noinline,__target__("ssse3"))) void ssse3_deinterlace_4field_gray8(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - union { - uint32_t int32; - uint8_t int8a[4]; - } threshold_mask; - threshold_mask.int8a[0] = threshold; - threshold_mask.int8a[1] = 0; - threshold_mask.int8a[2] = threshold; - threshold_mask.int8a[3] = 0; + union { + uint32_t int32; + uint8_t int8a[4]; + } threshold_mask; + threshold_mask.int8a[0] = threshold; + threshold_mask.int8a[1] = 0; + threshold_mask.int8a[2] = threshold; + threshold_mask.int8a[3] = 0; - unsigned long row_width = width; - uint8_t* max_ptr = col1 + (row_width * (height-2)); - uint8_t* max_ptr2 = col1 + row_width; + unsigned long row_width = width; + uint8_t* max_ptr = col1 + (row_width * (height-2)); + uint8_t* max_ptr2 = col1 + row_width; - __asm__ __volatile__ ( - /* Load the threshold */ - "mov %5, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - /* Zero the temporary register */ - "pxor %%xmm0, %%xmm0\n\t" + __asm__ __volatile__ ( + /* Load the threshold */ + "mov %5, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + /* Zero the temporary register */ + "pxor %%xmm0, %%xmm0\n\t" - "algo_ssse3_deinterlace_4field_gray8:\n\t" + "algo_ssse3_deinterlace_4field_gray8:\n\t" - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "pmaxub %%xmm2, %%xmm1\n\t" - "pminub %%xmm5, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "pmaxub %%xmm2, %%xmm1\n\t" + "pminub %%xmm5, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "pmaxub %%xmm2, %%xmm1\n\t" - "pminub %%xmm6, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "pmaxub %%xmm2, %%xmm1\n\t" + "pminub %%xmm6, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together - "movdqa %%xmm1, %%xmm2\n\t" + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "movdqa %%xmm1, %%xmm2\n\t" - /* Do the comparison on words instead of bytes because we don't have unsigned comparison */ - "punpcklbw %%xmm0, %%xmm1\n\t" // Expand pixels 0-7 into words into xmm1 - "punpckhbw %%xmm0, %%xmm2\n\t" // Expand pixels 8-15 into words into xmm2 - "pcmpgtw %%xmm4, %%xmm1\n\t" // Compare average delta with threshold for pixels 0-7 - "pcmpgtw %%xmm4, %%xmm2\n\t" // Compare average delta with threshold for pixels 8-15 - "packsswb %%xmm2, %%xmm1\n\t" // Pack the comparison results into xmm1 + /* Do the comparison on words instead of bytes because we don't have unsigned comparison */ + "punpcklbw %%xmm0, %%xmm1\n\t" // Expand pixels 0-7 into words into xmm1 + "punpckhbw %%xmm0, %%xmm2\n\t" // Expand pixels 8-15 into words into xmm2 + "pcmpgtw %%xmm4, %%xmm1\n\t" // Compare average delta with threshold for pixels 0-7 + "pcmpgtw %%xmm4, %%xmm2\n\t" // Compare average delta with threshold for pixels 8-15 + "packsswb %%xmm2, %%xmm1\n\t" // Pack the comparison results into xmm1 - "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow - "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow - "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow + "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow + "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - "sub %4, %0\n\t" // Restore pcurrent to pabove - "sub %4, %1\n\t" // Restore pncurrent to pnabove + "sub %4, %0\n\t" // Restore pcurrent to pabove + "sub %4, %1\n\t" // Restore pncurrent to pnabove - /* Next pixels */ - "add $0x10, %0\n\t" // Add 16 to pcurrent - "add $0x10, %1\n\t" // Add 16 to pncurrent + /* Next pixels */ + "add $0x10, %0\n\t" // Add 16 to pcurrent + "add $0x10, %1\n\t" // Add 16 to pncurrent - /* Check if we reached the row end */ - "cmp %2, %0\n\t" - "jb algo_ssse3_deinterlace_4field_gray8\n\t" // Go for another iteration + /* Check if we reached the row end */ + "cmp %2, %0\n\t" + "jb algo_ssse3_deinterlace_4field_gray8\n\t" // Go for another iteration - /* Next row */ - "add %4, %0\n\t" // Add width to pcurrent - "add %4, %1\n\t" // Add width to pncurrent - "mov %0, %2\n\t" - "add %4, %2\n\t" // Add width to max_ptr2 + /* Next row */ + "add %4, %0\n\t" // Add width to pcurrent + "add %4, %1\n\t" // Add width to pncurrent + "mov %0, %2\n\t" + "add %4, %2\n\t" // Add width to max_ptr2 - /* Check if we reached the end */ - "cmp %3, %0\n\t" - "jb algo_ssse3_deinterlace_4field_gray8\n\t" // Go for another iteration + /* Check if we reached the end */ + "cmp %3, %0\n\t" + "jb algo_ssse3_deinterlace_4field_gray8\n\t" // Go for another iteration - /* Special case for the last line */ - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "pmaxub %%xmm2, %%xmm1\n\t" - "pminub %%xmm5, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Special case for the last line */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "pmaxub %%xmm2, %%xmm1\n\t" + "pminub %%xmm5, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "pmaxub %%xmm2, %%xmm1\n\t" - "pminub %%xmm6, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "pmaxub %%xmm2, %%xmm1\n\t" + "pminub %%xmm6, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together - "movdqa %%xmm1, %%xmm2\n\t" + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "movdqa %%xmm1, %%xmm2\n\t" - /* Do the comparison on words instead of bytes because we don't have unsigned comparison */ - "punpcklbw %%xmm0, %%xmm1\n\t" // Expand pixels 0-7 into words into xmm1 - "punpckhbw %%xmm0, %%xmm2\n\t" // Expand pixels 8-15 into words into xmm2 - "pcmpgtw %%xmm4, %%xmm1\n\t" // Compare average delta with threshold for pixels 0-7 - "pcmpgtw %%xmm4, %%xmm2\n\t" // Compare average delta with threshold for pixels 8-15 - "packsswb %%xmm2, %%xmm1\n\t" // Pack the comparison results into xmm1 + /* Do the comparison on words instead of bytes because we don't have unsigned comparison */ + "punpcklbw %%xmm0, %%xmm1\n\t" // Expand pixels 0-7 into words into xmm1 + "punpckhbw %%xmm0, %%xmm2\n\t" // Expand pixels 8-15 into words into xmm2 + "pcmpgtw %%xmm4, %%xmm1\n\t" // Compare average delta with threshold for pixels 0-7 + "pcmpgtw %%xmm4, %%xmm2\n\t" // Compare average delta with threshold for pixels 8-15 + "packsswb %%xmm2, %%xmm1\n\t" // Pack the comparison results into xmm1 - "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - : - : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_mask.int32) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" - ); + "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + : + : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_mask.int32) + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -4829,180 +4960,180 @@ __attribute__((noinline,__target__("ssse3"))) #endif void ssse3_deinterlace_4field_rgba(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __attribute__((aligned(16))) static const uint8_t movemask2[16] = {1,1,1,1,1,0,0,2,9,9,9,9,9,8,8,10}; + __attribute__((aligned(16))) static const uint8_t movemask2[16] = {1,1,1,1,1,0,0,2,9,9,9,9,9,8,8,10}; - const uint32_t threshold_val = threshold; + const uint32_t threshold_val = threshold; - unsigned long row_width = width*4; - uint8_t* max_ptr = col1 + (row_width * (height-2)); - uint8_t* max_ptr2 = col1 + row_width; + unsigned long row_width = width*4; + uint8_t* max_ptr = col1 + (row_width * (height-2)); + uint8_t* max_ptr2 = col1 + row_width; - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "movdqa %6, %%xmm3\n\t" - "mov %5, %%eax\n\t" + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "movdqa %6, %%xmm3\n\t" + "mov %5, %%eax\n\t" #if defined(__x86_64__) - "movd %%eax, %%xmm8\n\t" - "pshufd $0x0, %%xmm8, %%xmm8\n\t" + "movd %%eax, %%xmm8\n\t" + "pshufd $0x0, %%xmm8, %%xmm8\n\t" #endif - /* Zero the temporary register */ - "pxor %%xmm0, %%xmm0\n\t" + /* Zero the temporary register */ + "pxor %%xmm0, %%xmm0\n\t" - "algo_ssse3_deinterlace_4field_rgba:\n\t" + "algo_ssse3_deinterlace_4field_rgba:\n\t" - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) - "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else - "movd %%eax, %%xmm7\n\t" // Setup the threshold - "pshufd $0x0, %%xmm7, %%xmm7\n\t" + "movd %%eax, %%xmm7\n\t" // Setup the threshold + "pshufd $0x0, %%xmm7, %%xmm7\n\t" - "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif - "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow - "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow - "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow + "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow + "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - "sub %4, %0\n\t" // Restore pcurrent to pabove - "sub %4, %1\n\t" // Restore pncurrent to pnabove + "sub %4, %0\n\t" // Restore pcurrent to pabove + "sub %4, %1\n\t" // Restore pncurrent to pnabove - /* Next pixels */ - "add $0x10, %0\n\t" // Add 16 to pcurrent - "add $0x10, %1\n\t" // Add 16 to pncurrent + /* Next pixels */ + "add $0x10, %0\n\t" // Add 16 to pcurrent + "add $0x10, %1\n\t" // Add 16 to pncurrent - /* Check if we reached the row end */ - "cmp %2, %0\n\t" - "jb algo_ssse3_deinterlace_4field_rgba\n\t" // Go for another iteration + /* Check if we reached the row end */ + "cmp %2, %0\n\t" + "jb algo_ssse3_deinterlace_4field_rgba\n\t" // Go for another iteration - /* Next row */ - "add %4, %0\n\t" // Add width to pcurrent - "add %4, %1\n\t" // Add width to pncurrent - "mov %0, %2\n\t" - "add %4, %2\n\t" // Add width to max_ptr2 + /* Next row */ + "add %4, %0\n\t" // Add width to pcurrent + "add %4, %1\n\t" // Add width to pncurrent + "mov %0, %2\n\t" + "add %4, %2\n\t" // Add width to max_ptr2 - /* Check if we reached the end */ - "cmp %3, %0\n\t" - "jb algo_ssse3_deinterlace_4field_rgba\n\t" // Go for another iteration + /* Check if we reached the end */ + "cmp %3, %0\n\t" + "jb algo_ssse3_deinterlace_4field_rgba\n\t" // Go for another iteration - /* Special case for the last line */ - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Special case for the last line */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) - "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else - "movd %%eax, %%xmm7\n\t" // Setup the threshold - "pshufd $0x0, %%xmm7, %%xmm7\n\t" + "movd %%eax, %%xmm7\n\t" // Setup the threshold + "pshufd $0x0, %%xmm7, %%xmm7\n\t" - "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif - "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - : - : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) + "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + : + : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) #if defined(__x86_64__) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" #else - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" #endif - ); + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -5012,180 +5143,180 @@ __attribute__((noinline,__target__("ssse3"))) #endif void ssse3_deinterlace_4field_bgra(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __attribute__((aligned(16))) static const uint8_t movemask2[16] = {1,1,1,1,1,2,2,0,9,9,9,9,9,10,10,8}; + __attribute__((aligned(16))) static const uint8_t movemask2[16] = {1,1,1,1,1,2,2,0,9,9,9,9,9,10,10,8}; - const uint32_t threshold_val = threshold; + const uint32_t threshold_val = threshold; - unsigned long row_width = width*4; - uint8_t* max_ptr = col1 + (row_width * (height-2)); - uint8_t* max_ptr2 = col1 + row_width; + unsigned long row_width = width*4; + uint8_t* max_ptr = col1 + (row_width * (height-2)); + uint8_t* max_ptr2 = col1 + row_width; - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "movdqa %6, %%xmm3\n\t" - "mov %5, %%eax\n\t" + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "movdqa %6, %%xmm3\n\t" + "mov %5, %%eax\n\t" #if defined(__x86_64__) - "movd %%eax, %%xmm8\n\t" - "pshufd $0x0, %%xmm8, %%xmm8\n\t" + "movd %%eax, %%xmm8\n\t" + "pshufd $0x0, %%xmm8, %%xmm8\n\t" #endif - /* Zero the temporary register */ - "pxor %%xmm0, %%xmm0\n\t" + /* Zero the temporary register */ + "pxor %%xmm0, %%xmm0\n\t" - "algo_ssse3_deinterlace_4field_bgra:\n\t" + "algo_ssse3_deinterlace_4field_bgra:\n\t" - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) - "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else - "movd %%eax, %%xmm7\n\t" // Setup the threshold - "pshufd $0x0, %%xmm7, %%xmm7\n\t" + "movd %%eax, %%xmm7\n\t" // Setup the threshold + "pshufd $0x0, %%xmm7, %%xmm7\n\t" - "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif - "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow - "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow - "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow + "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow + "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - "sub %4, %0\n\t" // Restore pcurrent to pabove - "sub %4, %1\n\t" // Restore pncurrent to pnabove + "sub %4, %0\n\t" // Restore pcurrent to pabove + "sub %4, %1\n\t" // Restore pncurrent to pnabove - /* Next pixels */ - "add $0x10, %0\n\t" // Add 16 to pcurrent - "add $0x10, %1\n\t" // Add 16 to pncurrent + /* Next pixels */ + "add $0x10, %0\n\t" // Add 16 to pcurrent + "add $0x10, %1\n\t" // Add 16 to pncurrent - /* Check if we reached the row end */ - "cmp %2, %0\n\t" - "jb algo_ssse3_deinterlace_4field_bgra\n\t" // Go for another iteration + /* Check if we reached the row end */ + "cmp %2, %0\n\t" + "jb algo_ssse3_deinterlace_4field_bgra\n\t" // Go for another iteration - /* Next row */ - "add %4, %0\n\t" // Add width to pcurrent - "add %4, %1\n\t" // Add width to pncurrent - "mov %0, %2\n\t" - "add %4, %2\n\t" // Add width to max_ptr2 + /* Next row */ + "add %4, %0\n\t" // Add width to pcurrent + "add %4, %1\n\t" // Add width to pncurrent + "mov %0, %2\n\t" + "add %4, %2\n\t" // Add width to max_ptr2 - /* Check if we reached the end */ - "cmp %3, %0\n\t" - "jb algo_ssse3_deinterlace_4field_bgra\n\t" // Go for another iteration + /* Check if we reached the end */ + "cmp %3, %0\n\t" + "jb algo_ssse3_deinterlace_4field_bgra\n\t" // Go for another iteration - /* Special case for the last line */ - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Special case for the last line */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) - "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else - "movd %%eax, %%xmm7\n\t" // Setup the threshold - "pshufd $0x0, %%xmm7, %%xmm7\n\t" + "movd %%eax, %%xmm7\n\t" // Setup the threshold + "pshufd $0x0, %%xmm7, %%xmm7\n\t" - "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif - "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - : - : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) + "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + : + : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) #if defined(__x86_64__) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" #else - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" #endif - ); + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -5195,180 +5326,180 @@ __attribute__((noinline,__target__("ssse3"))) #endif void ssse3_deinterlace_4field_argb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __attribute__((aligned(16))) static const uint8_t movemask2[16] = {2,2,2,2,2,1,1,3,10,10,10,10,10,9,9,11}; + __attribute__((aligned(16))) static const uint8_t movemask2[16] = {2,2,2,2,2,1,1,3,10,10,10,10,10,9,9,11}; - const uint32_t threshold_val = threshold; + const uint32_t threshold_val = threshold; - unsigned long row_width = width*4; - uint8_t* max_ptr = col1 + (row_width * (height-2)); - uint8_t* max_ptr2 = col1 + row_width; + unsigned long row_width = width*4; + uint8_t* max_ptr = col1 + (row_width * (height-2)); + uint8_t* max_ptr2 = col1 + row_width; - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "movdqa %6, %%xmm3\n\t" - "mov %5, %%eax\n\t" + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "movdqa %6, %%xmm3\n\t" + "mov %5, %%eax\n\t" #if defined(__x86_64__) - "movd %%eax, %%xmm8\n\t" - "pshufd $0x0, %%xmm8, %%xmm8\n\t" + "movd %%eax, %%xmm8\n\t" + "pshufd $0x0, %%xmm8, %%xmm8\n\t" #endif - /* Zero the temporary register */ - "pxor %%xmm0, %%xmm0\n\t" + /* Zero the temporary register */ + "pxor %%xmm0, %%xmm0\n\t" - "algo_ssse3_deinterlace_4field_argb:\n\t" + "algo_ssse3_deinterlace_4field_argb:\n\t" - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) - "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else - "movd %%eax, %%xmm7\n\t" // Setup the threshold - "pshufd $0x0, %%xmm7, %%xmm7\n\t" + "movd %%eax, %%xmm7\n\t" // Setup the threshold + "pshufd $0x0, %%xmm7, %%xmm7\n\t" - "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif - "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow - "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow - "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow + "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow + "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - "sub %4, %0\n\t" // Restore pcurrent to pabove - "sub %4, %1\n\t" // Restore pncurrent to pnabove + "sub %4, %0\n\t" // Restore pcurrent to pabove + "sub %4, %1\n\t" // Restore pncurrent to pnabove - /* Next pixels */ - "add $0x10, %0\n\t" // Add 16 to pcurrent - "add $0x10, %1\n\t" // Add 16 to pncurrent + /* Next pixels */ + "add $0x10, %0\n\t" // Add 16 to pcurrent + "add $0x10, %1\n\t" // Add 16 to pncurrent - /* Check if we reached the row end */ - "cmp %2, %0\n\t" - "jb algo_ssse3_deinterlace_4field_argb\n\t" // Go for another iteration + /* Check if we reached the row end */ + "cmp %2, %0\n\t" + "jb algo_ssse3_deinterlace_4field_argb\n\t" // Go for another iteration - /* Next row */ - "add %4, %0\n\t" // Add width to pcurrent - "add %4, %1\n\t" // Add width to pncurrent - "mov %0, %2\n\t" - "add %4, %2\n\t" // Add width to max_ptr2 + /* Next row */ + "add %4, %0\n\t" // Add width to pcurrent + "add %4, %1\n\t" // Add width to pncurrent + "mov %0, %2\n\t" + "add %4, %2\n\t" // Add width to max_ptr2 - /* Check if we reached the end */ - "cmp %3, %0\n\t" - "jb algo_ssse3_deinterlace_4field_argb\n\t" // Go for another iteration + /* Check if we reached the end */ + "cmp %3, %0\n\t" + "jb algo_ssse3_deinterlace_4field_argb\n\t" // Go for another iteration - /* Special case for the last line */ - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Special case for the last line */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) - "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else - "movd %%eax, %%xmm7\n\t" // Setup the threshold - "pshufd $0x0, %%xmm7, %%xmm7\n\t" + "movd %%eax, %%xmm7\n\t" // Setup the threshold + "pshufd $0x0, %%xmm7, %%xmm7\n\t" - "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif - "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - : - : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) + "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + : + : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) #if defined(__x86_64__) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" #else - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" #endif - ); + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } @@ -5378,179 +5509,179 @@ __attribute__((noinline,__target__("ssse3"))) #endif void ssse3_deinterlace_4field_abgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - __attribute__((aligned(16))) static const uint8_t movemask2[16] = {2,2,2,2,2,3,3,1,10,10,10,10,10,11,11,9}; + __attribute__((aligned(16))) static const uint8_t movemask2[16] = {2,2,2,2,2,3,3,1,10,10,10,10,10,11,11,9}; - const uint32_t threshold_val = threshold; + const uint32_t threshold_val = threshold; - unsigned long row_width = width*4; - uint8_t* max_ptr = col1 + (row_width * (height-2)); - uint8_t* max_ptr2 = col1 + row_width; + unsigned long row_width = width*4; + uint8_t* max_ptr = col1 + (row_width * (height-2)); + uint8_t* max_ptr2 = col1 + row_width; - __asm__ __volatile__ ( - "mov $0x1F1F1F1F, %%eax\n\t" - "movd %%eax, %%xmm4\n\t" - "pshufd $0x0, %%xmm4, %%xmm4\n\t" - "movdqa %6, %%xmm3\n\t" - "mov %5, %%eax\n\t" + __asm__ __volatile__ ( + "mov $0x1F1F1F1F, %%eax\n\t" + "movd %%eax, %%xmm4\n\t" + "pshufd $0x0, %%xmm4, %%xmm4\n\t" + "movdqa %6, %%xmm3\n\t" + "mov %5, %%eax\n\t" #if defined(__x86_64__) - "movd %%eax, %%xmm8\n\t" - "pshufd $0x0, %%xmm8, %%xmm8\n\t" + "movd %%eax, %%xmm8\n\t" + "pshufd $0x0, %%xmm8, %%xmm8\n\t" #endif - /* Zero the temporary register */ - "pxor %%xmm0, %%xmm0\n\t" + /* Zero the temporary register */ + "pxor %%xmm0, %%xmm0\n\t" - "algo_ssse3_deinterlace_4field_abgr:\n\t" + "algo_ssse3_deinterlace_4field_abgr:\n\t" - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) - "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else - "movd %%eax, %%xmm7\n\t" // Setup the threshold - "pshufd $0x0, %%xmm7, %%xmm7\n\t" + "movd %%eax, %%xmm7\n\t" // Setup the threshold + "pshufd $0x0, %%xmm7, %%xmm7\n\t" - "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif - "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow - "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow - "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "movdqa (%0,%4), %%xmm2\n\t" // Load pbelow + "pavgb %%xmm5, %%xmm2\n\t" // Average pabove and pbelow + "pand %%xmm1, %%xmm2\n\t" // Filter out pixels in avg that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + "por %%xmm2, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - "sub %4, %0\n\t" // Restore pcurrent to pabove - "sub %4, %1\n\t" // Restore pncurrent to pnabove + "sub %4, %0\n\t" // Restore pcurrent to pabove + "sub %4, %1\n\t" // Restore pncurrent to pnabove - /* Next pixels */ - "add $0x10, %0\n\t" // Add 16 to pcurrent - "add $0x10, %1\n\t" // Add 16 to pncurrent + /* Next pixels */ + "add $0x10, %0\n\t" // Add 16 to pcurrent + "add $0x10, %1\n\t" // Add 16 to pncurrent - /* Check if we reached the row end */ - "cmp %2, %0\n\t" - "jb algo_ssse3_deinterlace_4field_abgr\n\t" // Go for another iteration + /* Check if we reached the row end */ + "cmp %2, %0\n\t" + "jb algo_ssse3_deinterlace_4field_abgr\n\t" // Go for another iteration - /* Next row */ - "add %4, %0\n\t" // Add width to pcurrent - "add %4, %1\n\t" // Add width to pncurrent - "mov %0, %2\n\t" - "add %4, %2\n\t" // Add width to max_ptr2 + /* Next row */ + "add %4, %0\n\t" // Add width to pcurrent + "add %4, %1\n\t" // Add width to pncurrent + "mov %0, %2\n\t" + "add %4, %2\n\t" // Add width to max_ptr2 - /* Check if we reached the end */ - "cmp %3, %0\n\t" - "jb algo_ssse3_deinterlace_4field_abgr\n\t" // Go for another iteration + /* Check if we reached the end */ + "cmp %3, %0\n\t" + "jb algo_ssse3_deinterlace_4field_abgr\n\t" // Go for another iteration - /* Special case for the last line */ - /* Load pabove into xmm1 and pnabove into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" - "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ + /* Special case for the last line */ + /* Load pabove into xmm1 and pnabove into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm5\n\t" /* Keep backup of pabove in xmm5 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" + "movdqa %%xmm1, %%xmm7\n\t" /* Backup of delta2 in xmm7 for now */ - /* Next row */ - "add %4, %0\n\t" - "add %4, %1\n\t" + /* Next row */ + "add %4, %0\n\t" + "add %4, %1\n\t" - /* Load pcurrent into xmm1 and pncurrent into xmm2 */ - "movdqa (%0), %%xmm1\n\t" - "movdqa (%1), %%xmm2\n\t" - "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ - "psrlq $0x3, %%xmm1\n\t" - "psrlq $0x3, %%xmm2\n\t" - "pand %%xmm4, %%xmm1\n\t" - "pand %%xmm4, %%xmm2\n\t" - "psubb %%xmm2, %%xmm1\n\t" - "pabsb %%xmm1, %%xmm2\n\t" - "movdqa %%xmm2, %%xmm1\n\t" - "punpckldq %%xmm1, %%xmm1\n\t" - "pshufb %%xmm3, %%xmm1\n\t" - "psadbw %%xmm0, %%xmm1\n\t" - "punpckhdq %%xmm2, %%xmm2\n\t" - "pshufb %%xmm3, %%xmm2\n\t" - "psadbw %%xmm0, %%xmm2\n\t" - "packuswb %%xmm2, %%xmm1\n\t" + /* Load pcurrent into xmm1 and pncurrent into xmm2 */ + "movdqa (%0), %%xmm1\n\t" + "movdqa (%1), %%xmm2\n\t" + "movdqa %%xmm1, %%xmm6\n\t" /* Keep backup of pcurrent in xmm6 */ + "psrlq $0x3, %%xmm1\n\t" + "psrlq $0x3, %%xmm2\n\t" + "pand %%xmm4, %%xmm1\n\t" + "pand %%xmm4, %%xmm2\n\t" + "psubb %%xmm2, %%xmm1\n\t" + "pabsb %%xmm1, %%xmm2\n\t" + "movdqa %%xmm2, %%xmm1\n\t" + "punpckldq %%xmm1, %%xmm1\n\t" + "pshufb %%xmm3, %%xmm1\n\t" + "psadbw %%xmm0, %%xmm1\n\t" + "punpckhdq %%xmm2, %%xmm2\n\t" + "pshufb %%xmm3, %%xmm2\n\t" + "psadbw %%xmm0, %%xmm2\n\t" + "packuswb %%xmm2, %%xmm1\n\t" - "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together + "pavgb %%xmm7, %%xmm1\n\t" // Average the two deltas together #if defined(__x86_64__) - "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm8, %%xmm1\n\t" // Compare average delta with the threshold #else - "movd %%eax, %%xmm7\n\t" // Setup the threshold - "pshufd $0x0, %%xmm7, %%xmm7\n\t" + "movd %%eax, %%xmm7\n\t" // Setup the threshold + "pshufd $0x0, %%xmm7, %%xmm7\n\t" - "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold + "pcmpgtd %%xmm7, %%xmm1\n\t" // Compare average delta with the threshold #endif - "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied - "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced + "pand %%xmm1, %%xmm5\n\t" // Filter out pixels in pabove that shouldn't be copied + "pandn %%xmm6, %%xmm1\n\t" // Filter out pixels in pcurrent that should be replaced - "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent - "movntdq %%xmm1, (%0)\n\t" // Write pcurrent - : - : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) + "por %%xmm5, %%xmm1\n\t" // Put the new values in pcurrent + "movntdq %%xmm1, (%0)\n\t" // Write pcurrent + : + : "r" (col1), "r" (col2), "r" (max_ptr2), "r" (max_ptr), "r" (row_width), "m" (threshold_val), "m" (*movemask2) #if defined(__x86_64__) - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "cc", "memory" #else - : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" + : "%eax", "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" #endif - ); + ); #else - Panic("SSE function called on a non x86\\x86-64 platform"); + Panic("SSE function called on a non x86\\x86-64 platform"); #endif } diff --git a/src/zm_image.h b/src/zm_image.h index 46c7a031b..016a64a3c 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -204,7 +204,12 @@ public: bool WriteRaw( const char *filename ) const; bool ReadJpeg( const char *filename, unsigned int p_colours, unsigned int p_subpixelorder); - bool WriteJpeg( const char *filename, int quality_override=0 ) const; + + bool WriteJpeg ( const char *filename) const; + bool WriteJpeg ( const char *filename, int quality_override ) const; + bool WriteJpeg ( const char *filename, struct timeval timestamp ) const; + bool WriteJpeg ( const char *filename, int quality_override, struct timeval timestamp ) const; + bool DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder); bool EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_override=0 ) const; @@ -226,10 +231,11 @@ public: void Delta( const Image &image, Image* targetimage) const; const Coord centreCoord( const char *text ) const; - void Annotate( const char *p_text, const Coord &coord, const Rgb fg_colour=RGB_WHITE, const Rgb bg_colour=RGB_BLACK ); + void MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour=0x00222222 ); + void Annotate( const char *p_text, const Coord &coord, const unsigned int size=1, const Rgb fg_colour=RGB_WHITE, const Rgb bg_colour=RGB_BLACK ); Image *HighlightEdges( Rgb colour, unsigned int p_colours, unsigned int p_subpixelorder, const Box *limits=0 ); //Image *HighlightEdges( Rgb colour, const Polygon &polygon ); - void Timestamp( const char *label, const time_t when, const Coord &coord ); + void Timestamp( const char *label, const time_t when, const Coord &coord, const int size ); void Colourise(const unsigned int p_reqcolours, const unsigned int p_reqsubpixelorder); void DeColourise(); diff --git a/src/zm_image_analyser.cpp b/src/zm_image_analyser.cpp index 1445e9864..8f03160d9 100644 --- a/src/zm_image_analyser.cpp +++ b/src/zm_image_analyser.cpp @@ -7,7 +7,7 @@ */ ImageAnalyser::ImageAnalyser(const ImageAnalyser& source) { - m_Detectors = source.m_Detectors; + m_Detectors = source.m_Detectors; } @@ -17,18 +17,18 @@ ImageAnalyser::ImageAnalyser(const ImageAnalyser& source) */ ImageAnalyser& ImageAnalyser::operator=(const ImageAnalyser& source) { - m_Detectors = source.m_Detectors; - return *this; + m_Detectors = source.m_Detectors; + return *this; } ImageAnalyser::~ImageAnalyser() { - for(DetectorsList::reverse_iterator It = m_Detectors.rbegin(); - It != m_Detectors.rend(); - ++It) - delete *It; + for(DetectorsList::reverse_iterator It = m_Detectors.rbegin(); + It != m_Detectors.rend(); + ++It) + delete *It; } @@ -42,23 +42,23 @@ ImageAnalyser::~ImageAnalyser() */ int ImageAnalyser::DoDetection(const Image &comp_image, Zone** zones, int n_numZones, Event::StringSetMap noteSetMap, std::string& det_cause) { - Event::StringSet zoneSet; - int score = 0; + Event::StringSet zoneSet; + int score = 0; - for(DetectorsList::iterator It = m_Detectors.begin(); - It != m_Detectors.end(); - ++It) + for(DetectorsList::iterator It = m_Detectors.begin(); + It != m_Detectors.end(); + ++It) + { + int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet); + if (detect_score) { - int detect_score = (*It)->Detect(comp_image, zones, n_numZones, zoneSet); - if (detect_score) - { - score += detect_score; - noteSetMap[(*It)->getDetectionCause()] = zoneSet; - if (det_cause.length()) - det_cause += ", "; - det_cause += (*It)->getDetectionCause(); - } + score += detect_score; + noteSetMap[(*It)->getDetectionCause()] = zoneSet; + if (det_cause.length()) + det_cause += ", "; + det_cause += (*It)->getDetectionCause(); } - return score; + } + return score; } diff --git a/src/zm_image_analyser.h b/src/zm_image_analyser.h index d1d0c1da0..c853c82a3 100644 --- a/src/zm_image_analyser.h +++ b/src/zm_image_analyser.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include "zm.h" @@ -23,18 +22,18 @@ using namespace std; //! Class for handling image detection. class ImageAnalyser { public: - - //!Default constructor. - ImageAnalyser() {}; + + //!Default constructor. + ImageAnalyser() {}; - //! Destructor. - ~ImageAnalyser(); + //! Destructor. + ~ImageAnalyser(); - //! Copy constructor. - ImageAnalyser(const ImageAnalyser& source); + //! Copy constructor. + ImageAnalyser(const ImageAnalyser& source); - //! Overloaded operator=. - ImageAnalyser& operator=(const ImageAnalyser& source); + //! Overloaded operator=. + ImageAnalyser& operator=(const ImageAnalyser& source); private: diff --git a/src/zm_jpeg.cpp b/src/zm_jpeg.cpp index 92f757ca1..c47bbe267 100644 --- a/src/zm_jpeg.cpp +++ b/src/zm_jpeg.cpp @@ -32,65 +32,65 @@ static int jpeg_err_count = 0; void zm_jpeg_error_exit( j_common_ptr cinfo ) { - static char buffer[JMSG_LENGTH_MAX]; - zm_error_ptr zmerr = (zm_error_ptr)cinfo->err; + static char buffer[JMSG_LENGTH_MAX]; + zm_error_ptr zmerr = (zm_error_ptr)cinfo->err; - (zmerr->pub.format_message)( cinfo, buffer ); + (zmerr->pub.format_message)( cinfo, buffer ); - Error( "%s", buffer ); - if ( ++jpeg_err_count == MAX_JPEG_ERRS ) - { - Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count ); - } + Error( "%s", buffer ); + if ( ++jpeg_err_count == MAX_JPEG_ERRS ) + { + Fatal( "Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count ); + } - longjmp( zmerr->setjmp_buffer, 1 ); + longjmp( zmerr->setjmp_buffer, 1 ); } void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level ) { - static char buffer[JMSG_LENGTH_MAX]; - zm_error_ptr zmerr = (zm_error_ptr)cinfo->err; + static char buffer[JMSG_LENGTH_MAX]; + zm_error_ptr zmerr = (zm_error_ptr)cinfo->err; - if ( msg_level < 0 ) - { - /* It's a warning message. Since corrupt files may generate many warnings, - * the policy implemented here is to show only the first warning, - * unless trace_level >= 3. - */ - if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 ) - { - (zmerr->pub.format_message)( cinfo, buffer ); - if (!strstr(buffer, "Corrupt JPEG data:")) - Warning( "%s", buffer ); - } - /* Always count warnings in num_warnings. */ - zmerr->pub.num_warnings++; - } - else - { - /* It's a trace message. Show it if trace_level >= msg_level. */ - if ( zmerr->pub.trace_level >= msg_level ) - { - (zmerr->pub.format_message)( cinfo, buffer ); - Debug( msg_level, "%s", buffer ); - } - } + if ( msg_level < 0 ) + { + /* It's a warning message. Since corrupt files may generate many warnings, + * the policy implemented here is to show only the first warning, + * unless trace_level >= 3. + */ + if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 ) + { + (zmerr->pub.format_message)( cinfo, buffer ); + if (!strstr(buffer, "Corrupt JPEG data:")) + Warning( "%s", buffer ); + } + /* Always count warnings in num_warnings. */ + zmerr->pub.num_warnings++; + } + else + { + /* It's a trace message. Show it if trace_level >= msg_level. */ + if ( zmerr->pub.trace_level >= msg_level ) + { + (zmerr->pub.format_message)( cinfo, buffer ); + Debug( msg_level, "%s", buffer ); + } + } } /* Expanded data destination object for memory */ typedef struct { - struct jpeg_destination_mgr pub; /* public fields */ + struct jpeg_destination_mgr pub; /* public fields */ - JOCTET *outbuffer; /* target buffer */ - int *outbuffer_size; - JOCTET *buffer; /* start of buffer */ + JOCTET *outbuffer; /* target buffer */ + int *outbuffer_size; + JOCTET *buffer; /* start of buffer */ } mem_destination_mgr; typedef mem_destination_mgr * mem_dest_ptr; -#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ +#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ /* * Initialize destination --- called by jpeg_start_compress @@ -99,15 +99,15 @@ typedef mem_destination_mgr * mem_dest_ptr; static void init_destination (j_compress_ptr cinfo) { - mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; - /* Allocate the output buffer --- it will be released when done with image */ - dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); + /* Allocate the output buffer --- it will be released when done with image */ + dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); - dest->pub.next_output_byte = dest->buffer; - dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; - *(dest->outbuffer_size) = 0; + *(dest->outbuffer_size) = 0; } @@ -136,15 +136,15 @@ static void init_destination (j_compress_ptr cinfo) static boolean empty_output_buffer (j_compress_ptr cinfo) { - mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; - memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE ); - *(dest->outbuffer_size) += OUTPUT_BUF_SIZE; + memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, OUTPUT_BUF_SIZE ); + *(dest->outbuffer_size) += OUTPUT_BUF_SIZE; - dest->pub.next_output_byte = dest->buffer; - dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; - return( TRUE ); + return( TRUE ); } /* @@ -158,14 +158,14 @@ static boolean empty_output_buffer (j_compress_ptr cinfo) static void term_destination (j_compress_ptr cinfo) { - mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; - size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest; + size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; - if ( datacount > 0 ) - { - memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount ); - *(dest->outbuffer_size) += datacount; - } + if ( datacount > 0 ) + { + memcpy( dest->outbuffer+*(dest->outbuffer_size), dest->buffer, datacount ); + *(dest->outbuffer_size) += datacount; + } } @@ -177,45 +177,45 @@ static void term_destination (j_compress_ptr cinfo) void zm_jpeg_mem_dest (j_compress_ptr cinfo, JOCTET *outbuffer, int *outbuffer_size ) { - mem_dest_ptr dest; + mem_dest_ptr dest; - /* 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(mem_destination_mgr)); - } + /* 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(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->outbuffer = outbuffer; - dest->outbuffer_size = outbuffer_size; + 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->outbuffer = outbuffer; + dest->outbuffer_size = outbuffer_size; } /* Expanded data source object for memory input */ typedef struct { - struct jpeg_source_mgr pub; /* public fields */ + struct jpeg_source_mgr pub; /* public fields */ - JOCTET * inbuffer; /* source stream */ - int inbuffer_size; - int inbuffer_size_hwm; /* High water mark */ + JOCTET * inbuffer; /* source stream */ + int inbuffer_size; + int inbuffer_size_hwm; /* High water mark */ - JOCTET * buffer; /* start of buffer */ - boolean start_of_data; /* have we gotten any data yet? */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_data; /* have we gotten any data yet? */ } mem_source_mgr; typedef mem_source_mgr * mem_src_ptr; -#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ /* * Initialize source --- called by jpeg_read_header @@ -224,14 +224,14 @@ typedef mem_source_mgr * mem_src_ptr; static void init_source (j_decompress_ptr cinfo) { - mem_src_ptr src = (mem_src_ptr) cinfo->src; + mem_src_ptr src = (mem_src_ptr) cinfo->src; - /* We reset the empty-input-file flag for each image, - * but we don't clear the input buffer. - * This is correct behavior for reading a series of images from one source. - */ - src->start_of_data = TRUE; - src->pub.bytes_in_buffer = 0; + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_data = TRUE; + src->pub.bytes_in_buffer = 0; } @@ -270,26 +270,26 @@ static void init_source (j_decompress_ptr cinfo) static boolean fill_input_buffer (j_decompress_ptr cinfo) { - mem_src_ptr src = (mem_src_ptr) cinfo->src; - size_t nbytes; + mem_src_ptr src = (mem_src_ptr) cinfo->src; + size_t nbytes; - memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size ); - nbytes = src->inbuffer_size; + memcpy( src->buffer, src->inbuffer, (size_t) src->inbuffer_size ); + nbytes = src->inbuffer_size; - if ( nbytes <= 0 ) - { - if ( src->start_of_data ) /* Treat empty input file as fatal error */ - ERREXIT(cinfo, JERR_INPUT_EMPTY); - WARNMS(cinfo, JWRN_JPEG_EOF); - /* Insert a fake EOI marker */ - src->buffer[0] = (JOCTET) 0xFF; - src->buffer[1] = (JOCTET) JPEG_EOI; - nbytes = 2; - } + if ( nbytes <= 0 ) + { + if ( src->start_of_data ) /* Treat empty input file as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } - src->pub.next_input_byte = src->buffer; - src->pub.bytes_in_buffer = nbytes; - src->start_of_data = FALSE; + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_data = FALSE; return( TRUE ); } @@ -309,25 +309,25 @@ static boolean fill_input_buffer (j_decompress_ptr cinfo) static void skip_input_data (j_decompress_ptr cinfo, long num_bytes) { - mem_src_ptr src = (mem_src_ptr) cinfo->src; + mem_src_ptr src = (mem_src_ptr) cinfo->src; - /* Just a dumb implementation for now. Could use fseek() except - * it doesn't work on pipes. Not clear that being smart is worth - * any trouble anyway --- large skips are infrequent. - */ - if ( num_bytes > 0 ) + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if ( num_bytes > 0 ) + { + while ( num_bytes > (long) src->pub.bytes_in_buffer ) { - while ( num_bytes > (long) src->pub.bytes_in_buffer ) - { - num_bytes -= (long) src->pub.bytes_in_buffer; - (void) fill_input_buffer(cinfo); - /* note we assume that fill_input_buffer will never return FALSE, - * so suspension need not be handled. - */ - } - src->pub.next_input_byte += (size_t) num_bytes; - src->pub.bytes_in_buffer -= (size_t) num_bytes; + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } } @@ -342,7 +342,7 @@ static void skip_input_data (j_decompress_ptr cinfo, long num_bytes) static void term_source (j_decompress_ptr cinfo) { - /* no work necessary here */ + /* no work necessary here */ } @@ -354,114 +354,114 @@ static void term_source (j_decompress_ptr cinfo) void zm_jpeg_mem_src( j_decompress_ptr cinfo, const JOCTET *inbuffer, int inbuffer_size ) { - mem_src_ptr src; - - /* The source object and input buffer are made permanent so that a series - * of JPEG images can be read from the same file by calling zm_jpeg_mem_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(mem_source_mgr)); - src = (mem_src_ptr) cinfo->src; - src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET)); - src->inbuffer_size_hwm = inbuffer_size; - } - else - { - src = (mem_src_ptr) cinfo->src; - if ( src->inbuffer_size_hwm < inbuffer_size ) - { - src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET)); - src->inbuffer_size_hwm = inbuffer_size; - } - } + mem_src_ptr src; + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling zm_jpeg_mem_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(mem_source_mgr)); src = (mem_src_ptr) cinfo->src; - src->pub.init_source = init_source; - src->pub.fill_input_buffer = fill_input_buffer; - src->pub.skip_input_data = skip_input_data; - src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ - src->pub.term_source = term_source; - src->inbuffer = (JOCTET *)inbuffer; - src->inbuffer_size = inbuffer_size; - src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ - src->pub.next_input_byte = NULL; /* until buffer loaded */ + src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET)); + src->inbuffer_size_hwm = inbuffer_size; + } + else + { + src = (mem_src_ptr) cinfo->src; + if ( src->inbuffer_size_hwm < inbuffer_size ) + { + src->buffer = (JOCTET *)(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, inbuffer_size * SIZEOF(JOCTET)); + src->inbuffer_size_hwm = inbuffer_size; + } + } + + src = (mem_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->inbuffer = (JOCTET *)inbuffer; + src->inbuffer_size = inbuffer_size; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ } void zm_use_std_huff_tables( j_decompress_ptr cinfo ) { - /* JPEG standard Huffman tables (cf. JPEG standard section K.3) */ - /* IMPORTANT: these are only valid for 8-bit data precision! */ - static const JHUFF_TBL dclumin = { - { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, - FALSE - }; - static const JHUFF_TBL dcchrome = { - { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, - FALSE - }; - static const JHUFF_TBL aclumin = { - { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }, - { 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 }, - FALSE - }; - static const JHUFF_TBL acchrome = { - { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }, - { 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 }, - FALSE - }; - - cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin; - cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome; - cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin; - cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome; - + /* JPEG standard Huffman tables (cf. JPEG standard section K.3) */ + /* IMPORTANT: these are only valid for 8-bit data precision! */ + static const JHUFF_TBL dclumin = { + { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + FALSE + }; + static const JHUFF_TBL dcchrome = { + { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + FALSE + }; + static const JHUFF_TBL aclumin = { + { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }, + { 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 }, + FALSE + }; + static const JHUFF_TBL acchrome = { + { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }, + { 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 }, + FALSE + }; + + cinfo->dc_huff_tbl_ptrs[0] = (JHUFF_TBL*)&dclumin; + cinfo->dc_huff_tbl_ptrs[1] = (JHUFF_TBL*)&dcchrome; + cinfo->ac_huff_tbl_ptrs[0] = (JHUFF_TBL*)&aclumin; + cinfo->ac_huff_tbl_ptrs[1] = (JHUFF_TBL*)&acchrome; + } } diff --git a/src/zm_jpeg.h b/src/zm_jpeg.h index 94290869a..b7807d0d3 100644 --- a/src/zm_jpeg.h +++ b/src/zm_jpeg.h @@ -32,8 +32,8 @@ extern "C" /* Stuff for overriden error handlers */ struct zm_error_mgr { - struct jpeg_error_mgr pub; - jmp_buf setjmp_buffer; + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; }; typedef struct zm_error_mgr *zm_error_ptr; diff --git a/src/zm_libvlc_camera.cpp b/src/zm_libvlc_camera.cpp index bfbcaa47d..26c4701fb 100644 --- a/src/zm_libvlc_camera.cpp +++ b/src/zm_libvlc_camera.cpp @@ -25,102 +25,102 @@ // Do all the buffer checking work here to avoid unnecessary locking void* LibvlcLockBuffer(void* opaque, void** planes) { - LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; - data->mutex.lock(); - - uint8_t* buffer = data->buffer; - data->buffer = data->prevBuffer; - data->prevBuffer = buffer; - - *planes = data->buffer; - return NULL; + LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; + data->mutex.lock(); + + uint8_t* buffer = data->buffer; + data->buffer = data->prevBuffer; + data->prevBuffer = buffer; + + *planes = data->buffer; + return NULL; } void LibvlcUnlockBuffer(void* opaque, void* picture, void *const *planes) { - LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; - - bool newFrame = false; - for(uint32_t i = 0; i < data->bufferSize; i++) + LibvlcPrivateData* data = (LibvlcPrivateData*)opaque; + + bool newFrame = false; + for(uint32_t i = 0; i < data->bufferSize; i++) + { + if(data->buffer[i] != data->prevBuffer[i]) { - if(data->buffer[i] != data->prevBuffer[i]) - { - newFrame = true; - break; - } - } - data->mutex.unlock(); - - time_t now; - time(&now); - // Return frames slightly faster than 1fps (if time() supports greater than one second resolution) - if(newFrame || difftime(now, data->prevTime) >= 0.8) - { - data->prevTime = now; - data->newImage.updateValueSignal(true); + newFrame = true; + break; } + } + data->mutex.unlock(); + + time_t now; + time(&now); + // Return frames slightly faster than 1fps (if time() supports greater than one second resolution) + if(newFrame || difftime(now, data->prevTime) >= 0.8) + { + data->prevTime = now; + data->newImage.updateValueSignal(true); + } } LibvlcCamera::LibvlcCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - mPath( p_path ), - mMethod( p_method ), - mOptions( p_options ) -{ - mLibvlcInstance = NULL; - mLibvlcMedia = NULL; - mLibvlcMediaPlayer = NULL; - mLibvlcData.buffer = NULL; - mLibvlcData.prevBuffer = NULL; + Camera( p_id, LIBVLC_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), + mPath( p_path ), + mMethod( p_method ), + mOptions( p_options ) +{ + mLibvlcInstance = NULL; + mLibvlcMedia = NULL; + mLibvlcMediaPlayer = NULL; + mLibvlcData.buffer = NULL; + mLibvlcData.prevBuffer = NULL; - /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_BGRA; - mTargetChroma = "RV32"; - mBpp = 4; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_BGR; - mTargetChroma = "RV24"; - mBpp = 3; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - mTargetChroma = "GREY"; - mBpp = 1; - } else { - Panic("Unexpected colours: %d",colours); - } - - if ( capture ) - { - Initialise(); - } + /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_BGRA; + mTargetChroma = "RV32"; + mBpp = 4; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_BGR; + mTargetChroma = "RV24"; + mBpp = 3; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + mTargetChroma = "GREY"; + mBpp = 1; + } else { + Panic("Unexpected colours: %d",colours); + } + + if ( capture ) + { + Initialise(); + } } LibvlcCamera::~LibvlcCamera() { - if ( capture ) - { - Terminate(); - } - if(mLibvlcMediaPlayer != NULL) - { - libvlc_media_player_release(mLibvlcMediaPlayer); - mLibvlcMediaPlayer = NULL; - } - if(mLibvlcMedia != NULL) - { - libvlc_media_release(mLibvlcMedia); - mLibvlcMedia = NULL; - } - if(mLibvlcInstance != NULL) - { - libvlc_release(mLibvlcInstance); - mLibvlcInstance = NULL; - } - if (mOptArgV != NULL) - { - delete[] mOptArgV; - } + if ( capture ) + { + Terminate(); + } + if(mLibvlcMediaPlayer != NULL) + { + libvlc_media_player_release(mLibvlcMediaPlayer); + mLibvlcMediaPlayer = NULL; + } + if(mLibvlcMedia != NULL) + { + libvlc_media_release(mLibvlcMedia); + mLibvlcMedia = NULL; + } + if(mLibvlcInstance != NULL) + { + libvlc_release(mLibvlcInstance); + mLibvlcInstance = NULL; + } + if (mOptArgV != NULL) + { + delete[] mOptArgV; + } } void LibvlcCamera::Initialise() @@ -129,91 +129,91 @@ void LibvlcCamera::Initialise() void LibvlcCamera::Terminate() { - libvlc_media_player_stop(mLibvlcMediaPlayer); - if(mLibvlcData.buffer != NULL) - { - zm_freealigned(mLibvlcData.buffer); - } - if(mLibvlcData.prevBuffer != NULL) - { - zm_freealigned(mLibvlcData.prevBuffer); - } + libvlc_media_player_stop(mLibvlcMediaPlayer); + if(mLibvlcData.buffer != NULL) + { + zm_freealigned(mLibvlcData.buffer); + } + if(mLibvlcData.prevBuffer != NULL) + { + zm_freealigned(mLibvlcData.prevBuffer); + } } int LibvlcCamera::PrimeCapture() { - Info("Priming capture from %s", mPath.c_str()); - - StringVector opVect = split(Options(), ","); - - // Set transport method as specified by method field, rtpUni is default - if ( Method() == "rtpMulti" ) - opVect.push_back("--rtsp-mcast"); - else if ( Method() == "rtpRtsp" ) - opVect.push_back("--rtsp-tcp"); - else if ( Method() == "rtpRtspHttp" ) - opVect.push_back("--rtsp-http"); + Info("Priming capture from %s", mPath.c_str()); + + StringVector opVect = split(Options(), ","); + + // Set transport method as specified by method field, rtpUni is default + if ( Method() == "rtpMulti" ) + opVect.push_back("--rtsp-mcast"); + else if ( Method() == "rtpRtsp" ) + opVect.push_back("--rtsp-tcp"); + else if ( Method() == "rtpRtspHttp" ) + opVect.push_back("--rtsp-http"); - if (opVect.size() > 0) - { - mOptArgV = new char*[opVect.size()]; - Debug(2, "Number of Options: %d",opVect.size()); - for (size_t i=0; i< opVect.size(); i++) { - opVect[i] = trimSpaces(opVect[i]); - mOptArgV[i] = (char *)opVect[i].c_str(); - Debug(2, "set option %d to '%s'", i, opVect[i].c_str()); - } + if (opVect.size() > 0) + { + mOptArgV = new char*[opVect.size()]; + Debug(2, "Number of Options: %d",opVect.size()); + for (size_t i=0; i< opVect.size(); i++) { + opVect[i] = trimSpaces(opVect[i]); + mOptArgV[i] = (char *)opVect[i].c_str(); + Debug(2, "set option %d to '%s'", i, opVect[i].c_str()); } + } - mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV); - if(mLibvlcInstance == NULL) - Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg()); - - mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str()); - if(mLibvlcMedia == NULL) - Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg()); - - mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia); - if(mLibvlcMediaPlayer == NULL) - Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg()); + mLibvlcInstance = libvlc_new (opVect.size(), (const char* const*)mOptArgV); + if(mLibvlcInstance == NULL) + Fatal("Unable to create libvlc instance due to: %s", libvlc_errmsg()); + + mLibvlcMedia = libvlc_media_new_location(mLibvlcInstance, mPath.c_str()); + if(mLibvlcMedia == NULL) + Fatal("Unable to open input %s due to: %s", mPath.c_str(), libvlc_errmsg()); + + mLibvlcMediaPlayer = libvlc_media_player_new_from_media(mLibvlcMedia); + if(mLibvlcMediaPlayer == NULL) + Fatal("Unable to create player for %s due to: %s", mPath.c_str(), libvlc_errmsg()); - libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp); - libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData); + libvlc_video_set_format(mLibvlcMediaPlayer, mTargetChroma.c_str(), width, height, width * mBpp); + libvlc_video_set_callbacks(mLibvlcMediaPlayer, &LibvlcLockBuffer, &LibvlcUnlockBuffer, NULL, &mLibvlcData); - mLibvlcData.bufferSize = width * height * mBpp; - // Libvlc wants 32 byte alignment for images (should in theory do this for all image lines) - mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); - mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); - - mLibvlcData.newImage.setValueImmediate(false); + mLibvlcData.bufferSize = width * height * mBpp; + // Libvlc wants 32 byte alignment for images (should in theory do this for all image lines) + mLibvlcData.buffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); + mLibvlcData.prevBuffer = (uint8_t*)zm_mallocaligned(32, mLibvlcData.bufferSize); + + mLibvlcData.newImage.setValueImmediate(false); - libvlc_media_player_play(mLibvlcMediaPlayer); - - return(0); + libvlc_media_player_play(mLibvlcMediaPlayer); + + return(0); } int LibvlcCamera::PreCapture() -{ - return(0); +{ + return(0); } // Should not return -1 as cancels capture. Always wait for image if available. int LibvlcCamera::Capture( Image &image ) { - while(!mLibvlcData.newImage.getValueImmediate()) - mLibvlcData.newImage.getUpdatedValue(1); + while(!mLibvlcData.newImage.getValueImmediate()) + mLibvlcData.newImage.getUpdatedValue(1); - mLibvlcData.mutex.lock(); - image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp); - mLibvlcData.newImage.setValueImmediate(false); - mLibvlcData.mutex.unlock(); - - return (0); + mLibvlcData.mutex.lock(); + image.Assign(width, height, colours, subpixelorder, mLibvlcData.buffer, width * height * mBpp); + mLibvlcData.newImage.setValueImmediate(false); + mLibvlcData.mutex.unlock(); + + return (0); } int LibvlcCamera::PostCapture() { - return(0); + return(0); } #endif // HAVE_LIBVLC diff --git a/src/zm_libvlc_camera.h b/src/zm_libvlc_camera.h index 96414cb88..d08b310f5 100644 --- a/src/zm_libvlc_camera.h +++ b/src/zm_libvlc_camera.h @@ -33,44 +33,44 @@ // Used by libvlc callbacks struct LibvlcPrivateData { - uint8_t* buffer; - uint8_t* prevBuffer; - time_t prevTime; - uint32_t bufferSize; - Mutex mutex; - ThreadData newImage; + uint8_t* buffer; + uint8_t* prevBuffer; + time_t prevTime; + uint32_t bufferSize; + Mutex mutex; + ThreadData newImage; }; class LibvlcCamera : public Camera { protected: - std::string mPath; - std::string mMethod; - std::string mOptions; - char **mOptArgV; - LibvlcPrivateData mLibvlcData; - std::string mTargetChroma; - uint8_t mBpp; + std::string mPath; + std::string mMethod; + std::string mOptions; + char **mOptArgV; + LibvlcPrivateData mLibvlcData; + std::string mTargetChroma; + uint8_t mBpp; - libvlc_instance_t *mLibvlcInstance; - libvlc_media_t *mLibvlcMedia; - libvlc_media_player_t *mLibvlcMediaPlayer; + libvlc_instance_t *mLibvlcInstance; + libvlc_media_t *mLibvlcMedia; + libvlc_media_player_t *mLibvlcMediaPlayer; public: - LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~LibvlcCamera(); + LibvlcCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); + ~LibvlcCamera(); - const std::string &Path() const { return( mPath ); } - const std::string &Options() const { return( mOptions ); } - const std::string &Method() const { return( mMethod ); } + const std::string &Path() const { return( mPath ); } + const std::string &Options() const { return( mOptions ); } + const std::string &Method() const { return( mMethod ); } - void Initialise(); - void Terminate(); + void Initialise(); + void Terminate(); - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); }; #endif // HAVE_LIBVLC diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index aa381434e..8df65d8b5 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -32,230 +32,233 @@ #include #include +/* Workaround for GNU/kFreeBSD */ +#if defined(__FreeBSD_kernel__) +#ifndef ENODATA +#define ENODATA ENOATTR +#endif +#endif + static unsigned int BigEndian; static int vidioctl( int fd, int request, void *arg ) { - int result = -1; - do - { - result = ioctl( fd, request, arg ); - } while ( result == -1 && errno == EINTR ); - return( result ); + int result = -1; + do + { + result = ioctl( fd, request, arg ); + } while ( result == -1 && errno == EINTR ); + return( result ); } #if HAVE_LIBSWSCALE -static PixelFormat getFfPixFormatFromV4lPalette( int v4l_version, int palette ) +static _AVPIXELFORMAT getFfPixFormatFromV4lPalette( int v4l_version, int palette ) { - PixelFormat pixFormat = PIX_FMT_NONE; - + _AVPIXELFORMAT pixFormat = AV_PIX_FMT_NONE; + #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) + if ( v4l_version == 2 ) + { + switch( palette ) { - switch( palette ) - { -#if defined(V4L2_PIX_FMT_RGB444) && defined(PIX_FMT_RGB444) - case V4L2_PIX_FMT_RGB444 : - pixFormat = PIX_FMT_RGB444; - break; +#if defined(V4L2_PIX_FMT_RGB444) && defined(AV_PIX_FMT_RGB444) + case V4L2_PIX_FMT_RGB444 : + pixFormat = AV_PIX_FMT_RGB444; + break; #endif // V4L2_PIX_FMT_RGB444 - case V4L2_PIX_FMT_RGB555 : - pixFormat = PIX_FMT_RGB555; - break; - case V4L2_PIX_FMT_RGB565 : - pixFormat = PIX_FMT_RGB565; - break; - case V4L2_PIX_FMT_BGR24 : - pixFormat = PIX_FMT_BGR24; - break; - case V4L2_PIX_FMT_RGB24 : - pixFormat = PIX_FMT_RGB24; - break; - case V4L2_PIX_FMT_BGR32 : - pixFormat = PIX_FMT_BGRA; - break; - case V4L2_PIX_FMT_RGB32 : - pixFormat = PIX_FMT_ARGB; - break; - case V4L2_PIX_FMT_GREY : - pixFormat = PIX_FMT_GRAY8; - break; - case V4L2_PIX_FMT_YUYV : - pixFormat = PIX_FMT_YUYV422; - break; - case V4L2_PIX_FMT_YUV422P : - pixFormat = PIX_FMT_YUV422P; - break; - case V4L2_PIX_FMT_YUV411P : - pixFormat = PIX_FMT_YUV411P; - break; + case V4L2_PIX_FMT_RGB555 : + pixFormat = AV_PIX_FMT_RGB555; + break; + case V4L2_PIX_FMT_RGB565 : + pixFormat = AV_PIX_FMT_RGB565; + break; + case V4L2_PIX_FMT_BGR24 : + pixFormat = AV_PIX_FMT_BGR24; + break; + case V4L2_PIX_FMT_RGB24 : + pixFormat = AV_PIX_FMT_RGB24; + break; + case V4L2_PIX_FMT_BGR32 : + pixFormat = AV_PIX_FMT_BGRA; + break; + case V4L2_PIX_FMT_RGB32 : + pixFormat = AV_PIX_FMT_ARGB; + break; + case V4L2_PIX_FMT_GREY : + pixFormat = AV_PIX_FMT_GRAY8; + break; + case V4L2_PIX_FMT_YUYV : + pixFormat = AV_PIX_FMT_YUYV422; + break; + case V4L2_PIX_FMT_YUV422P : + pixFormat = AV_PIX_FMT_YUV422P; + break; + case V4L2_PIX_FMT_YUV411P : + pixFormat = AV_PIX_FMT_YUV411P; + break; #ifdef V4L2_PIX_FMT_YUV444 - case V4L2_PIX_FMT_YUV444 : - pixFormat = PIX_FMT_YUV444P; - break; + case V4L2_PIX_FMT_YUV444 : + pixFormat = AV_PIX_FMT_YUV444P; + break; #endif // V4L2_PIX_FMT_YUV444 - case V4L2_PIX_FMT_YUV410 : - pixFormat = PIX_FMT_YUV410P; - break; - case V4L2_PIX_FMT_YUV420 : - pixFormat = PIX_FMT_YUV420P; - break; - case V4L2_PIX_FMT_JPEG : - case V4L2_PIX_FMT_MJPEG : - pixFormat = PIX_FMT_YUVJ444P; - break; - case V4L2_PIX_FMT_UYVY : - pixFormat = PIX_FMT_UYVY422; - break; - // These don't seem to have ffmpeg equivalents - // See if you can match any of the ones in the default clause below!? - case V4L2_PIX_FMT_RGB332 : - case V4L2_PIX_FMT_RGB555X : - case V4L2_PIX_FMT_RGB565X : - //case V4L2_PIX_FMT_Y16 : - //case V4L2_PIX_FMT_PAL8 : - case V4L2_PIX_FMT_YVU410 : - case V4L2_PIX_FMT_YVU420 : - case V4L2_PIX_FMT_Y41P : - //case V4L2_PIX_FMT_YUV555 : - //case V4L2_PIX_FMT_YUV565 : - //case V4L2_PIX_FMT_YUV32 : - case V4L2_PIX_FMT_NV12 : - case V4L2_PIX_FMT_NV21 : - case V4L2_PIX_FMT_YYUV : - case V4L2_PIX_FMT_HI240 : - case V4L2_PIX_FMT_HM12 : - //case V4L2_PIX_FMT_SBGGR8 : - //case V4L2_PIX_FMT_SGBRG8 : - //case V4L2_PIX_FMT_SBGGR16 : - case V4L2_PIX_FMT_DV : - case V4L2_PIX_FMT_MPEG : - case V4L2_PIX_FMT_WNVA : - case V4L2_PIX_FMT_SN9C10X : - case V4L2_PIX_FMT_PWC1 : - case V4L2_PIX_FMT_PWC2 : - case V4L2_PIX_FMT_ET61X251 : - //case V4L2_PIX_FMT_SPCA501 : - //case V4L2_PIX_FMT_SPCA505 : - //case V4L2_PIX_FMT_SPCA508 : - //case V4L2_PIX_FMT_SPCA561 : - //case V4L2_PIX_FMT_PAC207 : - //case V4L2_PIX_FMT_PJPG : - //case V4L2_PIX_FMT_YVYU : - default : - { - Fatal( "Can't find swscale format for palette %d", palette ); - break; - // These are all spare and may match some of the above - pixFormat = PIX_FMT_YUVJ420P; - pixFormat = PIX_FMT_YUVJ422P; - pixFormat = PIX_FMT_XVMC_MPEG2_MC; - pixFormat = PIX_FMT_XVMC_MPEG2_IDCT; - pixFormat = PIX_FMT_UYVY422; - pixFormat = PIX_FMT_UYYVYY411; - pixFormat = PIX_FMT_BGR565; - pixFormat = PIX_FMT_BGR555; - pixFormat = PIX_FMT_BGR8; - pixFormat = PIX_FMT_BGR4; - pixFormat = PIX_FMT_BGR4_BYTE; - pixFormat = PIX_FMT_RGB8; - pixFormat = PIX_FMT_RGB4; - pixFormat = PIX_FMT_RGB4_BYTE; - pixFormat = PIX_FMT_NV12; - pixFormat = PIX_FMT_NV21; - pixFormat = PIX_FMT_RGB32_1; - pixFormat = PIX_FMT_BGR32_1; - pixFormat = PIX_FMT_GRAY16BE; - pixFormat = PIX_FMT_GRAY16LE; - pixFormat = PIX_FMT_YUV440P; - pixFormat = PIX_FMT_YUVJ440P; - pixFormat = PIX_FMT_YUVA420P; - //pixFormat = PIX_FMT_VDPAU_H264; - //pixFormat = PIX_FMT_VDPAU_MPEG1; - //pixFormat = PIX_FMT_VDPAU_MPEG2; - } - } + case V4L2_PIX_FMT_YUV410 : + pixFormat = AV_PIX_FMT_YUV410P; + break; + case V4L2_PIX_FMT_YUV420 : + pixFormat = AV_PIX_FMT_YUV420P; + break; + case V4L2_PIX_FMT_JPEG : + case V4L2_PIX_FMT_MJPEG : + pixFormat = AV_PIX_FMT_YUVJ444P; + break; + case V4L2_PIX_FMT_UYVY : + pixFormat = AV_PIX_FMT_UYVY422; + break; + // These don't seem to have ffmpeg equivalents + // See if you can match any of the ones in the default clause below!? + case V4L2_PIX_FMT_RGB332 : + case V4L2_PIX_FMT_RGB555X : + case V4L2_PIX_FMT_RGB565X : + //case V4L2_PIX_FMT_Y16 : + //case V4L2_PIX_FMT_PAL8 : + case V4L2_PIX_FMT_YVU410 : + case V4L2_PIX_FMT_YVU420 : + case V4L2_PIX_FMT_Y41P : + //case V4L2_PIX_FMT_YUV555 : + //case V4L2_PIX_FMT_YUV565 : + //case V4L2_PIX_FMT_YUV32 : + case V4L2_PIX_FMT_NV12 : + case V4L2_PIX_FMT_NV21 : + case V4L2_PIX_FMT_YYUV : + case V4L2_PIX_FMT_HI240 : + case V4L2_PIX_FMT_HM12 : + //case V4L2_PIX_FMT_SBGGR8 : + //case V4L2_PIX_FMT_SGBRG8 : + //case V4L2_PIX_FMT_SBGGR16 : + case V4L2_PIX_FMT_DV : + case V4L2_PIX_FMT_MPEG : + case V4L2_PIX_FMT_WNVA : + case V4L2_PIX_FMT_SN9C10X : + case V4L2_PIX_FMT_PWC1 : + case V4L2_PIX_FMT_PWC2 : + case V4L2_PIX_FMT_ET61X251 : + //case V4L2_PIX_FMT_SPCA501 : + //case V4L2_PIX_FMT_SPCA505 : + //case V4L2_PIX_FMT_SPCA508 : + //case V4L2_PIX_FMT_SPCA561 : + //case V4L2_PIX_FMT_PAC207 : + //case V4L2_PIX_FMT_PJPG : + //case V4L2_PIX_FMT_YVYU : + default : + { + Fatal( "Can't find swscale format for palette %d", palette ); + break; + // These are all spare and may match some of the above + pixFormat = AV_PIX_FMT_YUVJ420P; + pixFormat = AV_PIX_FMT_YUVJ422P; + pixFormat = AV_PIX_FMT_UYVY422; + pixFormat = AV_PIX_FMT_UYYVYY411; + pixFormat = AV_PIX_FMT_BGR565; + pixFormat = AV_PIX_FMT_BGR555; + pixFormat = AV_PIX_FMT_BGR8; + pixFormat = AV_PIX_FMT_BGR4; + pixFormat = AV_PIX_FMT_BGR4_BYTE; + pixFormat = AV_PIX_FMT_RGB8; + pixFormat = AV_PIX_FMT_RGB4; + pixFormat = AV_PIX_FMT_RGB4_BYTE; + pixFormat = AV_PIX_FMT_NV12; + pixFormat = AV_PIX_FMT_NV21; + pixFormat = AV_PIX_FMT_RGB32_1; + pixFormat = AV_PIX_FMT_BGR32_1; + pixFormat = AV_PIX_FMT_GRAY16BE; + pixFormat = AV_PIX_FMT_GRAY16LE; + pixFormat = AV_PIX_FMT_YUV440P; + pixFormat = AV_PIX_FMT_YUVJ440P; + pixFormat = AV_PIX_FMT_YUVA420P; + //pixFormat = AV_PIX_FMT_VDPAU_H264; + //pixFormat = AV_PIX_FMT_VDPAU_MPEG1; + //pixFormat = AV_PIX_FMT_VDPAU_MPEG2; + } } + } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) + if ( v4l_version == 1 ) + { + switch( palette ) { - switch( palette ) - { - case VIDEO_PALETTE_RGB32 : - if(BigEndian) - pixFormat = PIX_FMT_ARGB; - else - pixFormat = PIX_FMT_BGRA; - break; - case VIDEO_PALETTE_RGB24 : - if(BigEndian) - pixFormat = PIX_FMT_RGB24; - else - pixFormat = PIX_FMT_BGR24; - break; - case VIDEO_PALETTE_GREY : - pixFormat = PIX_FMT_GRAY8; - break; - case VIDEO_PALETTE_RGB555 : - pixFormat = PIX_FMT_RGB555; - break; - case VIDEO_PALETTE_RGB565 : - pixFormat = PIX_FMT_RGB565; - break; - case VIDEO_PALETTE_YUYV : - case VIDEO_PALETTE_YUV422 : - pixFormat = PIX_FMT_YUYV422; - break; - case VIDEO_PALETTE_YUV422P : - pixFormat = PIX_FMT_YUV422P; - break; - case VIDEO_PALETTE_YUV420P : - pixFormat = PIX_FMT_YUV420P; - break; - default : - { - Fatal( "Can't find swscale format for palette %d", palette ); - break; - // These are all spare and may match some of the above - pixFormat = PIX_FMT_YUVJ420P; - pixFormat = PIX_FMT_YUVJ422P; - pixFormat = PIX_FMT_YUVJ444P; - pixFormat = PIX_FMT_XVMC_MPEG2_MC; - pixFormat = PIX_FMT_XVMC_MPEG2_IDCT; - pixFormat = PIX_FMT_UYVY422; - pixFormat = PIX_FMT_UYYVYY411; - pixFormat = PIX_FMT_BGR565; - pixFormat = PIX_FMT_BGR555; - pixFormat = PIX_FMT_BGR8; - pixFormat = PIX_FMT_BGR4; - pixFormat = PIX_FMT_BGR4_BYTE; - pixFormat = PIX_FMT_RGB8; - pixFormat = PIX_FMT_RGB4; - pixFormat = PIX_FMT_RGB4_BYTE; - pixFormat = PIX_FMT_NV12; - pixFormat = PIX_FMT_NV21; - pixFormat = PIX_FMT_RGB32_1; - pixFormat = PIX_FMT_BGR32_1; - pixFormat = PIX_FMT_GRAY16BE; - pixFormat = PIX_FMT_GRAY16LE; - pixFormat = PIX_FMT_YUV440P; - pixFormat = PIX_FMT_YUVJ440P; - pixFormat = PIX_FMT_YUVA420P; - //pixFormat = PIX_FMT_VDPAU_H264; - //pixFormat = PIX_FMT_VDPAU_MPEG1; - //pixFormat = PIX_FMT_VDPAU_MPEG2; - } - } + case VIDEO_PALETTE_RGB32 : + if(BigEndian) + pixFormat = AV_PIX_FMT_ARGB; + else + pixFormat = AV_PIX_FMT_BGRA; + break; + case VIDEO_PALETTE_RGB24 : + if(BigEndian) + pixFormat = AV_PIX_FMT_RGB24; + else + pixFormat = AV_PIX_FMT_BGR24; + break; + case VIDEO_PALETTE_GREY : + pixFormat = AV_PIX_FMT_GRAY8; + break; + case VIDEO_PALETTE_RGB555 : + pixFormat = AV_PIX_FMT_RGB555; + break; + case VIDEO_PALETTE_RGB565 : + pixFormat = AV_PIX_FMT_RGB565; + break; + case VIDEO_PALETTE_YUYV : + case VIDEO_PALETTE_YUV422 : + pixFormat = AV_PIX_FMT_YUYV422; + break; + case VIDEO_PALETTE_YUV422P : + pixFormat = AV_PIX_FMT_YUV422P; + break; + case VIDEO_PALETTE_YUV420P : + pixFormat = AV_PIX_FMT_YUV420P; + break; + default : + { + Fatal( "Can't find swscale format for palette %d", palette ); + break; + // These are all spare and may match some of the above + pixFormat = AV_PIX_FMT_YUVJ420P; + pixFormat = AV_PIX_FMT_YUVJ422P; + pixFormat = AV_PIX_FMT_YUVJ444P; + pixFormat = AV_PIX_FMT_UYVY422; + pixFormat = AV_PIX_FMT_UYYVYY411; + pixFormat = AV_PIX_FMT_BGR565; + pixFormat = AV_PIX_FMT_BGR555; + pixFormat = AV_PIX_FMT_BGR8; + pixFormat = AV_PIX_FMT_BGR4; + pixFormat = AV_PIX_FMT_BGR4_BYTE; + pixFormat = AV_PIX_FMT_RGB8; + pixFormat = AV_PIX_FMT_RGB4; + pixFormat = AV_PIX_FMT_RGB4_BYTE; + pixFormat = AV_PIX_FMT_NV12; + pixFormat = AV_PIX_FMT_NV21; + pixFormat = AV_PIX_FMT_RGB32_1; + pixFormat = AV_PIX_FMT_BGR32_1; + pixFormat = AV_PIX_FMT_GRAY16BE; + pixFormat = AV_PIX_FMT_GRAY16LE; + pixFormat = AV_PIX_FMT_YUV440P; + pixFormat = AV_PIX_FMT_YUVJ440P; + pixFormat = AV_PIX_FMT_YUVA420P; + //pixFormat = AV_PIX_FMT_VDPAU_H264; + //pixFormat = AV_PIX_FMT_VDPAU_MPEG1; + //pixFormat = AV_PIX_FMT_VDPAU_MPEG2; + } } + } #endif // ZM_HAS_V4L1 - return( pixFormat ); + return( pixFormat ); } #endif // HAVE_LIBSWSCALE #if ZM_HAS_V4L2 static char palette_desc[32]; -/* Automatic format selection prefered formats */ +/* Automatic format selection preferred formats */ static const uint32_t prefered_rgb32_formats[] = {V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_RGB32, V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420}; static const uint32_t prefered_rgb24_formats[] = {V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_RGB24, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420}; static const uint32_t prefered_gray8_formats[] = {V4L2_PIX_FMT_GREY, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_JPEG, V4L2_PIX_FMT_MJPEG, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV420}; @@ -284,1905 +287,1966 @@ AVFrame **LocalCamera::capturePictures = 0; LocalCamera *LocalCamera::last_camera = NULL; LocalCamera::LocalCamera( int p_id, const std::string &p_device, int p_channel, int p_standard, bool p_v4l_multi_buffer, unsigned int p_v4l_captures_per_frame, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras) : - Camera( p_id, LOCAL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - device( p_device ), - channel( p_channel ), - standard( p_standard ), - palette( p_palette ), - channel_index( 0 ), - extras ( p_extras ) + Camera( p_id, LOCAL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), + device( p_device ), + channel( p_channel ), + standard( p_standard ), + palette( p_palette ), + channel_index( 0 ), + extras ( p_extras ) { - // If we are the first, or only, input on this device then - // do the initial opening etc - device_prime = (camera_count++ == 0); - v4l_version = (p_method=="v4l2"?2:1); - v4l_multi_buffer = p_v4l_multi_buffer; - v4l_captures_per_frame = p_v4l_captures_per_frame; - - if ( capture ) + // If we are the first, or only, input on this device then + // do the initial opening etc + device_prime = (camera_count++ == 0); + v4l_version = (p_method=="v4l2"?2:1); + v4l_multi_buffer = p_v4l_multi_buffer; + v4l_captures_per_frame = p_v4l_captures_per_frame; + + if ( capture ) + { + if ( device_prime ) { - if ( device_prime ) - { - Debug( 2, "V4L support enabled, using V4L%d api", v4l_version ); - } + Debug( 2, "V4L support enabled, using V4L%d api", v4l_version ); + } - if ( !last_camera || channel != last_camera->channel ) - { - // We are the first, or only, input that uses this channel - channel_prime = true; - channel_index = channel_count++; - channels[channel_index] = channel; - standards[channel_index] = standard; - } - else - { - // We are the second, or subsequent, input using this channel - channel_prime = false; - } - + if ( !last_camera || channel != last_camera->channel ) + { + // We are the first, or only, input that uses this channel + channel_prime = true; + channel_index = channel_count++; + channels[channel_index] = channel; + standards[channel_index] = standard; + } + else + { + // We are the second, or subsequent, input using this channel + channel_prime = false; } - /* The V4L1 API doesn't care about endianness, we need to check the endianness of the machine */ - uint32_t checkval = 0xAABBCCDD; - if(*(unsigned char*)&checkval == 0xDD) { - BigEndian = 0; - Debug(2,"little-endian processor detected"); - } else if(*(unsigned char*)&checkval == 0xAA) { - BigEndian = 1; - Debug(2,"Big-endian processor detected"); - } else { - Error("Unable to detect the processor's endianness. Assuming little-endian."); - BigEndian = 0; - } - + } + + /* The V4L1 API doesn't care about endianness, we need to check the endianness of the machine */ + uint32_t checkval = 0xAABBCCDD; + if(*(unsigned char*)&checkval == 0xDD) { + BigEndian = 0; + Debug(2,"little-endian processor detected"); + } else if(*(unsigned char*)&checkval == 0xAA) { + BigEndian = 1; + Debug(2,"Big-endian processor detected"); + } else { + Error("Unable to detect the processor's endianness. Assuming little-endian."); + BigEndian = 0; + } + #if ZM_HAS_V4L2 - if( v4l_version == 2 && palette == 0 ) { - /* Use automatic format selection */ - Debug(2,"Using automatic format selection"); - palette = AutoSelectFormat(colours); - if(palette == 0) { - Error("Automatic format selection failed. Falling back to YUYV"); - palette = V4L2_PIX_FMT_YUYV; - } else { - if(capture) { - Info("Selected capture palette: %s (%c%c%c%c)", palette_desc, palette&0xff, (palette>>8)&0xff, (palette>>16)&0xff, (palette>>24)&0xff); - } - } - } + if( v4l_version == 2 && palette == 0 ) { + /* Use automatic format selection */ + Debug(2,"Using automatic format selection"); + palette = AutoSelectFormat(colours); + if(palette == 0) { + Error("Automatic format selection failed. Falling back to YUYV"); + palette = V4L2_PIX_FMT_YUYV; + } else { + if(capture) { + Info("Selected capture palette: %s (%c%c%c%c)", palette_desc, palette&0xff, (palette>>8)&0xff, (palette>>16)&0xff, (palette>>24)&0xff); + } + } + } #endif - - if( capture ) { - if ( last_camera ) { - if ( (p_method == "v4l2" && v4l_version != 2) || (p_method == "v4l1" && v4l_version != 1) ) - Fatal( "Different Video For Linux version used for monitors sharing same device" ); - - if ( standard != last_camera->standard ) - Warning( "Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong" ); - - if ( palette != last_camera->palette ) - Warning( "Different video palettes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); - - if ( width != last_camera->width || height != last_camera->height ) - Warning( "Different capture sizes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); - } - + + if( capture ) { + if ( last_camera ) { + if ( (p_method == "v4l2" && v4l_version != 2) || (p_method == "v4l1" && v4l_version != 1) ) + Fatal( "Different Video For Linux version used for monitors sharing same device" ); + + if ( standard != last_camera->standard ) + Warning( "Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong" ); + + if ( palette != last_camera->palette ) + Warning( "Different video palettes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); + + if ( width != last_camera->width || height != last_camera->height ) + Warning( "Different capture sizes defined for monitors sharing same device, results may be unpredictable or completely wrong" ); + } + #if HAVE_LIBSWSCALE - /* Get ffmpeg pixel format based on capture palette and endianness */ - capturePixFormat = getFfPixFormatFromV4lPalette( v4l_version, palette ); - imagePixFormat = PIX_FMT_NONE; + /* Get ffmpeg pixel format based on capture palette and endianness */ + capturePixFormat = getFfPixFormatFromV4lPalette( v4l_version, palette ); + imagePixFormat = AV_PIX_FMT_NONE; #endif // HAVE_LIBSWSCALE - } + } - /* V4L2 format matching */ + /* V4L2 format matching */ #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) { - /* Try to find a match for the selected palette and target colourspace */ - - /* RGB32 palette and 32bit target colourspace */ - if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_RGB32) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_ARGB; - - /* BGR32 palette and 32bit target colourspace */ - } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_RGB32) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_BGRA; - - /* RGB24 palette and 24bit target colourspace */ - } else if(palette == V4L2_PIX_FMT_RGB24 && colours == ZM_COLOUR_RGB24) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - - /* BGR24 palette and 24bit target colourspace */ - } else if(palette == V4L2_PIX_FMT_BGR24 && colours == ZM_COLOUR_RGB24) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_BGR; - - /* Grayscale palette and grayscale target colourspace */ - } else if(palette == V4L2_PIX_FMT_GREY && colours == ZM_COLOUR_GRAY8) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ - } else { - if( capture ) - Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); + if ( v4l_version == 2 ) { + /* Try to find a match for the selected palette and target colourspace */ + + /* RGB32 palette and 32bit target colourspace */ + if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_RGB32) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_ARGB; + + /* BGR32 palette and 32bit target colourspace */ + } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_RGB32) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_BGRA; + + /* RGB24 palette and 24bit target colourspace */ + } else if(palette == V4L2_PIX_FMT_RGB24 && colours == ZM_COLOUR_RGB24) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + + /* BGR24 palette and 24bit target colourspace */ + } else if(palette == V4L2_PIX_FMT_BGR24 && colours == ZM_COLOUR_RGB24) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_BGR; + + /* Grayscale palette and grayscale target colourspace */ + } else if(palette == V4L2_PIX_FMT_GREY && colours == ZM_COLOUR_GRAY8) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ + } else { + if( capture ) #if HAVE_LIBSWSCALE - /* Try using swscale for the conversion */ - conversion_type = 1; - Debug(2,"Using swscale for image conversion"); - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - imagePixFormat = PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - imagePixFormat = PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - imagePixFormat = PIX_FMT_GRAY8; - } else { - Panic("Unexpected colours: %d",colours); - } - if( capture ) { -#if LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(0, 8, 0) - if(!sws_isSupportedInput(capturePixFormat)) { - Error("swscale does not support the used capture format: %c%c%c%c",(capturePixFormat)&0xff,((capturePixFormat>>8)&0xff),((capturePixFormat>>16)&0xff),((capturePixFormat>>24)&0xff)); - conversion_type = 2; /* Try ZM format conversions */ - } - if(!sws_isSupportedOutput(imagePixFormat)) { - Error("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); - conversion_type = 2; /* Try ZM format conversions */ - } -#endif - } + Info("No direct match for the selected palette (%c%c%c%c) and target colorspace (%d). Format conversion is required, performance penalty expected", (capturePixFormat)&0xff,((capturePixFormat>>8)&0xff),((capturePixFormat>>16)&0xff),((capturePixFormat>>24)&0xff), colours ); #else - /* Don't have swscale, see what we can do */ - conversion_type = 2; + Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); #endif - /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ - if(colours == ZM_COLOUR_GRAY8 && palette == V4L2_PIX_FMT_YUYV) { - conversion_type = 2; - } - - /* JPEG */ - if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { - Debug(2,"Using JPEG image decoding"); - conversion_type = 3; - } - - if(conversion_type == 2) { - Debug(2,"Using ZM for image conversion"); - if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_GRAY8) { - conversion_fptr = &std_convert_argb_gray8; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_GRAY8) { - conversion_fptr = &std_convert_bgra_gray8; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_GRAY8) { - /* Fast YUYV->Grayscale conversion by extracting the Y channel */ - if(config.cpu_extensions && sseversion >= 35) { - conversion_fptr = &ssse3_convert_yuyv_gray8; - Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); - } else { - conversion_fptr = &std_convert_yuyv_gray8; - Debug(2,"Using standard YUYV->grayscale fast conversion"); - } - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_yuyv_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_yuyv_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_rgb555_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_rgb555_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_rgb565_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_rgb565_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else { - Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); - } - } - } - } +#if HAVE_LIBSWSCALE + /* Try using swscale for the conversion */ + conversion_type = 1; + Debug(2,"Using swscale for image conversion"); + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + imagePixFormat = AV_PIX_FMT_RGBA; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + imagePixFormat = AV_PIX_FMT_RGB24; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + imagePixFormat = AV_PIX_FMT_GRAY8; + } else { + Panic("Unexpected colours: %d",colours); + } + if( capture ) { +#if LIBSWSCALE_VERSION_CHECK(0, 8, 0, 8, 0) + if(!sws_isSupportedInput(capturePixFormat)) { + Error("swscale does not support the used capture format: %c%c%c%c",(capturePixFormat)&0xff,((capturePixFormat>>8)&0xff),((capturePixFormat>>16)&0xff),((capturePixFormat>>24)&0xff)); + conversion_type = 2; /* Try ZM format conversions */ + } + if(!sws_isSupportedOutput(imagePixFormat)) { + Error("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); + conversion_type = 2; /* Try ZM format conversions */ + } +#endif + } +#else + /* Don't have swscale, see what we can do */ + conversion_type = 2; +#endif + /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ + if(colours == ZM_COLOUR_GRAY8 && palette == V4L2_PIX_FMT_YUYV) { + conversion_type = 2; + } + + /* JPEG */ + if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { + Debug(2,"Using JPEG image decoding"); + conversion_type = 3; + } + + if(conversion_type == 2) { + Debug(2,"Using ZM for image conversion"); + if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_GRAY8) { + conversion_fptr = &std_convert_argb_gray8; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else if(palette == V4L2_PIX_FMT_BGR32 && colours == ZM_COLOUR_GRAY8) { + conversion_fptr = &std_convert_bgra_gray8; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_GRAY8) { + /* Fast YUYV->Grayscale conversion by extracting the Y channel */ + if(config.cpu_extensions && sseversion >= 35) { + conversion_fptr = &ssse3_convert_yuyv_gray8; + Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); + } else { + conversion_fptr = &std_convert_yuyv_gray8; + Debug(2,"Using standard YUYV->grayscale fast conversion"); + } + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_yuyv_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_yuyv_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_rgb555_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == V4L2_PIX_FMT_RGB555 && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_rgb555_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_rgb565_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == V4L2_PIX_FMT_RGB565 && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_rgb565_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else { + Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); + } + } + } + } #endif // ZM_HAS_V4L2 - /* V4L1 format matching */ + /* V4L1 format matching */ #if ZM_HAS_V4L1 - if ( v4l_version == 1) { - /* Try to find a match for the selected palette and target colourspace */ - - /* RGB32 palette and 32bit target colourspace */ - if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_RGB32) { - conversion_type = 0; - if(BigEndian) { - subpixelorder = ZM_SUBPIX_ORDER_ARGB; - } else { - subpixelorder = ZM_SUBPIX_ORDER_BGRA; - } - - /* RGB24 palette and 24bit target colourspace */ - } else if(palette == VIDEO_PALETTE_RGB24 && colours == ZM_COLOUR_RGB24) { - conversion_type = 0; - if(BigEndian) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else { - subpixelorder = ZM_SUBPIX_ORDER_BGR; - } - - /* Grayscale palette and grayscale target colourspace */ - } else if(palette == VIDEO_PALETTE_GREY && colours == ZM_COLOUR_GRAY8) { - conversion_type = 0; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ - } else { - if( capture ) - Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); + if ( v4l_version == 1) { + /* Try to find a match for the selected palette and target colourspace */ + + /* RGB32 palette and 32bit target colourspace */ + if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_RGB32) { + conversion_type = 0; + if(BigEndian) { + subpixelorder = ZM_SUBPIX_ORDER_ARGB; + } else { + subpixelorder = ZM_SUBPIX_ORDER_BGRA; + } + + /* RGB24 palette and 24bit target colourspace */ + } else if(palette == VIDEO_PALETTE_RGB24 && colours == ZM_COLOUR_RGB24) { + conversion_type = 0; + if(BigEndian) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else { + subpixelorder = ZM_SUBPIX_ORDER_BGR; + } + + /* Grayscale palette and grayscale target colourspace */ + } else if(palette == VIDEO_PALETTE_GREY && colours == ZM_COLOUR_GRAY8) { + conversion_type = 0; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + /* Unable to find a solution for the selected palette and target colourspace. Conversion required. Notify the user of performance penalty */ + } else { + if( capture ) + Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); #if HAVE_LIBSWSCALE - /* Try using swscale for the conversion */ - conversion_type = 1; - Debug(2,"Using swscale for image conversion"); - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - imagePixFormat = PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - imagePixFormat = PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - imagePixFormat = PIX_FMT_GRAY8; - } else { - Panic("Unexpected colours: %d",colours); - } - if( capture ) { - if(!sws_isSupportedInput(capturePixFormat)) { - Error("swscale does not support the used capture format"); - conversion_type = 2; /* Try ZM format conversions */ - } - if(!sws_isSupportedOutput(imagePixFormat)) { - Error("swscale does not support the target format"); - conversion_type = 2; /* Try ZM format conversions */ - } - } + /* Try using swscale for the conversion */ + conversion_type = 1; + Debug(2,"Using swscale for image conversion"); + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + imagePixFormat = AV_PIX_FMT_RGBA; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + imagePixFormat = AV_PIX_FMT_RGB24; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + imagePixFormat = AV_PIX_FMT_GRAY8; + } else { + Panic("Unexpected colours: %d",colours); + } + if( capture ) { + if(!sws_isSupportedInput(capturePixFormat)) { + Error("swscale does not support the used capture format"); + conversion_type = 2; /* Try ZM format conversions */ + } + if(!sws_isSupportedOutput(imagePixFormat)) { + Error("swscale does not support the target format"); + conversion_type = 2; /* Try ZM format conversions */ + } + } #else - /* Don't have swscale, see what we can do */ - conversion_type = 2; + /* Don't have swscale, see what we can do */ + conversion_type = 2; #endif - /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ - if(colours == ZM_COLOUR_GRAY8 && (palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422)) { - conversion_type = 2; - } - - if(conversion_type == 2) { - Debug(2,"Using ZM for image conversion"); - if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_GRAY8) { - if(BigEndian) { - conversion_fptr = &std_convert_argb_gray8; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else { - conversion_fptr = &std_convert_bgra_gray8; - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } - } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_GRAY8) { - /* Fast YUYV->Grayscale conversion by extracting the Y channel */ - if(config.cpu_extensions && sseversion >= 35) { - conversion_fptr = &ssse3_convert_yuyv_gray8; - Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); - } else { - conversion_fptr = &std_convert_yuyv_gray8; - Debug(2,"Using standard YUYV->grayscale fast conversion"); - } - subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_yuyv_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_yuyv_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_rgb555_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_rgb555_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB24) { - conversion_fptr = &zm_convert_rgb565_rgb; - subpixelorder = ZM_SUBPIX_ORDER_RGB; - } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB32) { - conversion_fptr = &zm_convert_rgb565_rgba; - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - } else { - Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); - } - } - } - } -#endif // ZM_HAS_V4L1 + /* Our YUYV->Grayscale conversion is a lot faster than swscale's */ + if(colours == ZM_COLOUR_GRAY8 && (palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422)) { + conversion_type = 2; + } + + if(conversion_type == 2) { + Debug(2,"Using ZM for image conversion"); + if(palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_GRAY8) { + if(BigEndian) { + conversion_fptr = &std_convert_argb_gray8; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else { + conversion_fptr = &std_convert_bgra_gray8; + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } + } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_GRAY8) { + /* Fast YUYV->Grayscale conversion by extracting the Y channel */ + if(config.cpu_extensions && sseversion >= 35) { + conversion_fptr = &ssse3_convert_yuyv_gray8; + Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); + } else { + conversion_fptr = &std_convert_yuyv_gray8; + Debug(2,"Using standard YUYV->grayscale fast conversion"); + } + subpixelorder = ZM_SUBPIX_ORDER_NONE; + } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_yuyv_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if((palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_yuyv_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_rgb555_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == VIDEO_PALETTE_RGB555 && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_rgb555_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB24) { + conversion_fptr = &zm_convert_rgb565_rgb; + subpixelorder = ZM_SUBPIX_ORDER_RGB; + } else if(palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB32) { + conversion_fptr = &zm_convert_rgb565_rgba; + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + } else { + Fatal("Unable to find a suitable format conversion for the selected palette and target colorspace."); + } + } + } + } +#endif // ZM_HAS_V4L1 - last_camera = this; - Debug(3,"Selected subpixelorder: %d",subpixelorder); + last_camera = this; + Debug(3,"Selected subpixelorder: %d",subpixelorder); #if HAVE_LIBSWSCALE - /* Initialize swscale stuff */ - if(capture && conversion_type == 1) { - tmpPicture = avcodec_alloc_frame(); - if ( !tmpPicture ) - Fatal( "Could not allocate temporary picture" ); - - int pSize = avpicture_get_size( imagePixFormat, width, height ); - if( (unsigned int)pSize != imagesize) { - Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); - } - - if(config.cpu_extensions && sseversion >= 20) { - imgConversionContext = sws_getContext(width, height, capturePixFormat, width, height, imagePixFormat, SWS_BICUBIC | SWS_CPU_CAPS_SSE2, NULL, NULL, NULL ); - } else { - imgConversionContext = sws_getContext(width, height, capturePixFormat, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); - } - - if ( !imgConversionContext ) - Fatal( "Unable to initialise image scaling context" ); - - } + /* Initialize swscale stuff */ + if(capture && conversion_type == 1) { +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + tmpPicture = av_frame_alloc(); +#else + tmpPicture = avcodec_alloc_frame(); +#endif + if ( !tmpPicture ) + Fatal( "Could not allocate temporary picture" ); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int pSize = av_image_get_buffer_size( imagePixFormat, width, height,1 ); +#else + int pSize = avpicture_get_size( imagePixFormat, width, height ); +#endif + if( (unsigned int)pSize != imagesize) { + Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); + } + + imgConversionContext = sws_getContext(width, height, capturePixFormat, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); + + if ( !imgConversionContext ) { + Fatal( "Unable to initialise image scaling context" ); + } + + } #endif } LocalCamera::~LocalCamera() { - if ( device_prime && capture ) - Terminate(); - + if ( device_prime && capture ) + Terminate(); + #if HAVE_LIBSWSCALE - /* Clean up swscale stuff */ - if(capture && conversion_type == 1) { - sws_freeContext(imgConversionContext); - imgConversionContext = NULL; - - av_free(tmpPicture); - tmpPicture = NULL; - } + /* Clean up swscale stuff */ + if(capture && conversion_type == 1) { + sws_freeContext(imgConversionContext); + imgConversionContext = NULL; + +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &tmpPicture ); +#else + av_freep( &tmpPicture ); +#endif + } #endif } void LocalCamera::Initialise() { #if HAVE_LIBSWSCALE - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else - av_log_set_level( AV_LOG_QUIET ); + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); #endif // HAVE_LIBSWSCALE - struct stat st; + struct stat st; - if ( stat( device.c_str(), &st ) < 0 ) - Fatal( "Failed to stat video device %s: %s", device.c_str(), strerror(errno) ); + if ( stat( device.c_str(), &st ) < 0 ) + Fatal( "Failed to stat video device %s: %s", device.c_str(), strerror(errno) ); - if ( !S_ISCHR(st.st_mode) ) - Fatal( "File %s is not device file: %s", device.c_str(), strerror(errno) ); + if ( !S_ISCHR(st.st_mode) ) + Fatal( "File %s is not device file: %s", device.c_str(), strerror(errno) ); - Debug( 3, "Opening video device %s", device.c_str() ); - //if ( (vid_fd = open( device.c_str(), O_RDWR|O_NONBLOCK, 0 )) < 0 ) - if ( (vid_fd = open( device.c_str(), O_RDWR, 0 )) < 0 ) - Fatal( "Failed to open video device %s: %s", device.c_str(), strerror(errno) ); + Debug( 3, "Opening video device %s", device.c_str() ); + //if ( (vid_fd = open( device.c_str(), O_RDWR|O_NONBLOCK, 0 )) < 0 ) + if ( (vid_fd = open( device.c_str(), O_RDWR, 0 )) < 0 ) + Fatal( "Failed to open video device %s: %s", device.c_str(), strerror(errno) ); #if ZM_HAS_V4L2 - Debug( 2, "V4L2 support enabled, using V4L%d api", v4l_version ); - if ( v4l_version == 2 ) + Debug( 2, "V4L2 support enabled, using V4L%d api", v4l_version ); + if ( v4l_version == 2 ) + { + struct v4l2_capability vid_cap; + + Debug( 3, "Checking video device capabilities" ); + if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) + Fatal( "Failed to query video device: %s", strerror(errno) ); + + if ( !(vid_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) ) + Fatal( "Video device is not video capture device" ); + + if ( !(vid_cap.capabilities & V4L2_CAP_STREAMING) ) + Fatal( "Video device does not support streaming i/o" ); + + Debug( 3, "Setting up video format" ); + + memset( &v4l2_data.fmt, 0, sizeof(v4l2_data.fmt) ); + v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if ( vidioctl( vid_fd, VIDIOC_G_FMT, &v4l2_data.fmt ) < 0 ) + Fatal( "Failed to get video format: %s", strerror(errno) ); + + Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); + Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); + Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); + Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); + Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); + Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); + Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); + Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); + Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); + + v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_data.fmt.fmt.pix.width = width; + v4l2_data.fmt.fmt.pix.height = height; + v4l2_data.fmt.fmt.pix.pixelformat = palette; + + if ( (extras & 0xff) != 0 ) + { + v4l2_data.fmt.fmt.pix.field = (v4l2_field)(extras & 0xff); + + if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { - struct v4l2_capability vid_cap; - - Debug( 3, "Checking video device capabilities" ); - if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) - Fatal( "Failed to query video device: %s", strerror(errno) ); - - if ( !(vid_cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) ) - Fatal( "Video device is not video capture device" ); - - if ( !(vid_cap.capabilities & V4L2_CAP_STREAMING) ) - Fatal( "Video device does not support streaming i/o" ); - - Debug( 3, "Setting up video format" ); - - memset( &v4l2_data.fmt, 0, sizeof(v4l2_data.fmt) ); - v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if ( vidioctl( vid_fd, VIDIOC_G_FMT, &v4l2_data.fmt ) < 0 ) - Fatal( "Failed to get video format: %s", strerror(errno) ); - - Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); - Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); - Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); - Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); - Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); - Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); - Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); - Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); - Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); - - v4l2_data.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_data.fmt.fmt.pix.width = width; - v4l2_data.fmt.fmt.pix.height = height; - v4l2_data.fmt.fmt.pix.pixelformat = palette; - - if ( (extras & 0xff) != 0 ) - { - v4l2_data.fmt.fmt.pix.field = (v4l2_field)(extras & 0xff); - - if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) - { - Warning( "Failed to set V4L2 field to %d, falling back to auto", (extras & 0xff) ); - v4l2_data.fmt.fmt.pix.field = V4L2_FIELD_ANY; - if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { - Fatal( "Failed to set video format: %s", strerror(errno) ); - } - } - } else { - if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { - Fatal( "Failed to set video format: %s", strerror(errno) ); - } - } - - /* Note VIDIOC_S_FMT may change width and height. */ - Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); - Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); - Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); - Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); - Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); - Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); - Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); - Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); - Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); - - /* Buggy driver paranoia. */ - unsigned int min; - min = v4l2_data.fmt.fmt.pix.width * 2; - if (v4l2_data.fmt.fmt.pix.bytesperline < min) - v4l2_data.fmt.fmt.pix.bytesperline = min; - min = v4l2_data.fmt.fmt.pix.bytesperline * v4l2_data.fmt.fmt.pix.height; - if (v4l2_data.fmt.fmt.pix.sizeimage < min) - v4l2_data.fmt.fmt.pix.sizeimage = min; - - v4l2_jpegcompression jpeg_comp; - if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { - if( vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0 ) { - if(errno == EINVAL) { - Debug(2, "JPEG compression options are not available"); - } else { - Warning("Failed to get JPEG compression options: %s", strerror(errno) ); - } - } else { - /* Set flags and quality. MJPEG should not have the huffman tables defined */ - if(palette == V4L2_PIX_FMT_MJPEG) { - jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; - } else { - jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | V4L2_JPEG_MARKER_DHT; - } - jpeg_comp.quality = 85; - - /* Update the JPEG options */ - if( vidioctl( vid_fd, VIDIOC_S_JPEGCOMP, &jpeg_comp ) < 0 ) { - Warning("Failed to set JPEG compression options: %s", strerror(errno) ); - } else { - if(vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0) { - Debug(3,"Failed to get updated JPEG compression options: %s", strerror(errno) ); - } else { - Debug(4, "JPEG quality: %d",jpeg_comp.quality); - Debug(4, "JPEG markers: %#x",jpeg_comp.jpeg_markers); - } - } - } - } - - Debug( 3, "Setting up request buffers" ); - - memset( &v4l2_data.reqbufs, 0, sizeof(v4l2_data.reqbufs) ); - if ( channel_count > 1 ) { - if ( v4l_multi_buffer ){ - v4l2_data.reqbufs.count = 2*channel_count; - } else { - v4l2_data.reqbufs.count = 1; - } - } else { - v4l2_data.reqbufs.count = 8; - } - - v4l2_data.reqbufs.type = v4l2_data.fmt.type; - v4l2_data.reqbufs.memory = V4L2_MEMORY_MMAP; - - if ( vidioctl( vid_fd, VIDIOC_REQBUFS, &v4l2_data.reqbufs ) < 0 ) - { - if ( errno == EINVAL ) - { - Fatal( "Unable to initialise memory mapping, unsupported in device" ); - } - else - { - Fatal( "Unable to initialise memory mapping: %s", strerror(errno) ); - } - } - - if ( v4l2_data.reqbufs.count < (v4l_multi_buffer?2:1) ) - Fatal( "Insufficient buffer memory %d on video device", v4l2_data.reqbufs.count ); - - Debug( 3, "Setting up data buffers: Channels %d MultiBuffer %d Buffers: %s", channel_count, v4l_multi_buffer, v4l2_data.reqbufs.count ); - - v4l2_data.buffers = new V4L2MappedBuffer[v4l2_data.reqbufs.count]; -#if HAVE_LIBSWSCALE - capturePictures = new AVFrame *[v4l2_data.reqbufs.count]; -#endif // HAVE_LIBSWSCALE - for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) - { - struct v4l2_buffer vid_buf; - - memset( &vid_buf, 0, sizeof(vid_buf) ); - - //vid_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - vid_buf.type = v4l2_data.fmt.type; - //vid_buf.memory = V4L2_MEMORY_MMAP; - vid_buf.memory = v4l2_data.reqbufs.memory; - vid_buf.index = i; - - if ( vidioctl( vid_fd, VIDIOC_QUERYBUF, &vid_buf ) < 0 ) - Fatal( "Unable to query video buffer: %s", strerror(errno) ); - - v4l2_data.buffers[i].length = vid_buf.length; - v4l2_data.buffers[i].start = mmap( NULL, vid_buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, vid_buf.m.offset ); - - if ( v4l2_data.buffers[i].start == MAP_FAILED ) - Fatal( "Can't map video buffer %d (%d bytes) to memory: %s(%d)", i, vid_buf.length, strerror(errno), errno ); - -#if HAVE_LIBSWSCALE - capturePictures[i] = avcodec_alloc_frame(); - if ( !capturePictures[i] ) - Fatal( "Could not allocate picture" ); - avpicture_fill( (AVPicture *)capturePictures[i], (uint8_t*)v4l2_data.buffers[i].start, capturePixFormat, v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height ); -#endif // HAVE_LIBSWSCALE - } - - Debug( 3, "Configuring video source" ); - - if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channel ) < 0 ) - { - Fatal( "Failed to set camera source %d: %s", channel, strerror(errno) ); - } - - struct v4l2_input input; - v4l2_std_id stdId; - - memset( &input, 0, sizeof(input) ); - - if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) - { - Fatal( "Failed to enumerate input %d: %s", channel, strerror(errno) ); - } - - if ( (input.std != V4L2_STD_UNKNOWN) && ((input.std & standard) == V4L2_STD_UNKNOWN) ) - { - Fatal( "Device does not support video standard %d", standard ); - } - - stdId = standard; - if ( (input.std != V4L2_STD_UNKNOWN) && vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) - { - Fatal( "Failed to set video standard %d: %s", standard, strerror(errno) ); - } - - Contrast(contrast); - Brightness(brightness); - Hue(hue); - Colour(colour); + Warning( "Failed to set V4L2 field to %d, falling back to auto", (extras & 0xff) ); + v4l2_data.fmt.fmt.pix.field = V4L2_FIELD_ANY; + if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { + Fatal( "Failed to set video format: %s", strerror(errno) ); + } } + } else { + if ( vidioctl( vid_fd, VIDIOC_S_FMT, &v4l2_data.fmt ) < 0 ) { + Fatal( "Failed to set video format: %s", strerror(errno) ); + } + } + + /* Note VIDIOC_S_FMT may change width and height. */ + Debug( 4, " v4l2_data.fmt.type = %08x", v4l2_data.fmt.type ); + Debug( 4, " v4l2_data.fmt.fmt.pix.width = %08x", v4l2_data.fmt.fmt.pix.width ); + Debug( 4, " v4l2_data.fmt.fmt.pix.height = %08x", v4l2_data.fmt.fmt.pix.height ); + Debug( 4, " v4l2_data.fmt.fmt.pix.pixelformat = %08x", v4l2_data.fmt.fmt.pix.pixelformat ); + Debug( 4, " v4l2_data.fmt.fmt.pix.field = %08x", v4l2_data.fmt.fmt.pix.field ); + Debug( 4, " v4l2_data.fmt.fmt.pix.bytesperline = %08x", v4l2_data.fmt.fmt.pix.bytesperline ); + Debug( 4, " v4l2_data.fmt.fmt.pix.sizeimage = %08x", v4l2_data.fmt.fmt.pix.sizeimage ); + Debug( 4, " v4l2_data.fmt.fmt.pix.colorspace = %08x", v4l2_data.fmt.fmt.pix.colorspace ); + Debug( 4, " v4l2_data.fmt.fmt.pix.priv = %08x", v4l2_data.fmt.fmt.pix.priv ); + + /* Buggy driver paranoia. */ + unsigned int min; + min = v4l2_data.fmt.fmt.pix.width * 2; + if (v4l2_data.fmt.fmt.pix.bytesperline < min) + v4l2_data.fmt.fmt.pix.bytesperline = min; + min = v4l2_data.fmt.fmt.pix.bytesperline * v4l2_data.fmt.fmt.pix.height; + if (v4l2_data.fmt.fmt.pix.sizeimage < min) + v4l2_data.fmt.fmt.pix.sizeimage = min; + + v4l2_jpegcompression jpeg_comp; + if(palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG) { + if( vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0 ) { + if(errno == EINVAL) { + Debug(2, "JPEG compression options are not available"); + } else { + Warning("Failed to get JPEG compression options: %s", strerror(errno) ); + } + } else { + /* Set flags and quality. MJPEG should not have the huffman tables defined */ + if(palette == V4L2_PIX_FMT_MJPEG) { + jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI; + } else { + jpeg_comp.jpeg_markers |= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | V4L2_JPEG_MARKER_DHT; + } + jpeg_comp.quality = 85; + + /* Update the JPEG options */ + if( vidioctl( vid_fd, VIDIOC_S_JPEGCOMP, &jpeg_comp ) < 0 ) { + Warning("Failed to set JPEG compression options: %s", strerror(errno) ); + } else { + if(vidioctl( vid_fd, VIDIOC_G_JPEGCOMP, &jpeg_comp ) < 0) { + Debug(3,"Failed to get updated JPEG compression options: %s", strerror(errno) ); + } else { + Debug(4, "JPEG quality: %d",jpeg_comp.quality); + Debug(4, "JPEG markers: %#x",jpeg_comp.jpeg_markers); + } + } + } + } + + Debug( 3, "Setting up request buffers" ); + + memset( &v4l2_data.reqbufs, 0, sizeof(v4l2_data.reqbufs) ); + if ( channel_count > 1 ) { + Debug( 3, "Channel count is %d", channel_count ); + if ( v4l_multi_buffer ){ + v4l2_data.reqbufs.count = 2*channel_count; + } else { + v4l2_data.reqbufs.count = 1; + } + } else { + v4l2_data.reqbufs.count = 8; + } + Debug( 3, "Request buffers count is %d", v4l2_data.reqbufs.count ); + + v4l2_data.reqbufs.type = v4l2_data.fmt.type; + v4l2_data.reqbufs.memory = V4L2_MEMORY_MMAP; + + if ( vidioctl( vid_fd, VIDIOC_REQBUFS, &v4l2_data.reqbufs ) < 0 ) + { + if ( errno == EINVAL ) + { + Fatal( "Unable to initialise memory mapping, unsupported in device" ); + } + else + { + Fatal( "Unable to initialise memory mapping: %s", strerror(errno) ); + } + } + + if ( v4l2_data.reqbufs.count < (v4l_multi_buffer?2:1) ) + Fatal( "Insufficient buffer memory %d on video device", v4l2_data.reqbufs.count ); + + Debug( 3, "Setting up data buffers: Channels %d MultiBuffer %d Buffers: %d", channel_count, v4l_multi_buffer, v4l2_data.reqbufs.count ); + + v4l2_data.buffers = new V4L2MappedBuffer[v4l2_data.reqbufs.count]; +#if HAVE_LIBSWSCALE + capturePictures = new AVFrame *[v4l2_data.reqbufs.count]; +#endif // HAVE_LIBSWSCALE + for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) + { + struct v4l2_buffer vid_buf; + + memset( &vid_buf, 0, sizeof(vid_buf) ); + + //vid_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vid_buf.type = v4l2_data.fmt.type; + //vid_buf.memory = V4L2_MEMORY_MMAP; + vid_buf.memory = v4l2_data.reqbufs.memory; + vid_buf.index = i; + + if ( vidioctl( vid_fd, VIDIOC_QUERYBUF, &vid_buf ) < 0 ) + Fatal( "Unable to query video buffer: %s", strerror(errno) ); + + v4l2_data.buffers[i].length = vid_buf.length; + v4l2_data.buffers[i].start = mmap( NULL, vid_buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, vid_buf.m.offset ); + + if ( v4l2_data.buffers[i].start == MAP_FAILED ) + Fatal( "Can't map video buffer %d (%d bytes) to memory: %s(%d)", i, vid_buf.length, strerror(errno), errno ); + +#if HAVE_LIBSWSCALE +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + capturePictures[i] = av_frame_alloc(); +#else + capturePictures[i] = avcodec_alloc_frame(); +#endif + if ( !capturePictures[i] ) + Fatal( "Could not allocate picture" ); +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(capturePictures[i]->data, + capturePictures[i]->linesize, + (uint8_t*)v4l2_data.buffers[i].start,capturePixFormat, + v4l2_data.fmt.fmt.pix.width, + v4l2_data.fmt.fmt.pix.height, 1); +#else + avpicture_fill( (AVPicture *)capturePictures[i], + (uint8_t*)v4l2_data.buffers[i].start, capturePixFormat, + v4l2_data.fmt.fmt.pix.width, + v4l2_data.fmt.fmt.pix.height ); +#endif +#endif // HAVE_LIBSWSCALE + } + + Debug( 3, "Configuring video source" ); + + if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channel ) < 0 ) + { + Fatal( "Failed to set camera source %d: %s", channel, strerror(errno) ); + } + + struct v4l2_input input; + v4l2_std_id stdId; + + memset( &input, 0, sizeof(input) ); + + if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) + { + Fatal( "Failed to enumerate input %d: %s", channel, strerror(errno) ); + } + + if ( (input.std != V4L2_STD_UNKNOWN) && ((input.std & standard) == V4L2_STD_UNKNOWN) ) + { + Fatal( "Device does not support video standard %d", standard ); + } + + stdId = standard; + if ( (input.std != V4L2_STD_UNKNOWN) && vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) + { + Fatal( "Failed to set video standard %d: %s", standard, strerror(errno) ); + } + + Contrast(contrast); + Brightness(brightness); + Hue(hue); + Colour(colour); + } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) + if ( v4l_version == 1 ) + { + Debug( 3, "Configuring picture attributes" ); + + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(vid_pic) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + Fatal( "Failed to get picture attributes: %s", strerror(errno) ); + + Debug( 4, "Old P:%d", vid_pic.palette ); + Debug( 4, "Old D:%d", vid_pic.depth ); + Debug( 4, "Old B:%d", vid_pic.brightness ); + Debug( 4, "Old h:%d", vid_pic.hue ); + Debug( 4, "Old Cl:%d", vid_pic.colour ); + Debug( 4, "Old Cn:%d", vid_pic.contrast ); + + switch (vid_pic.palette = palette) { - Debug( 3, "Configuring picture attributes" ); + case VIDEO_PALETTE_RGB32 : + { + vid_pic.depth = 32; + break; + } + case VIDEO_PALETTE_RGB24 : + { + vid_pic.depth = 24; + break; + } + case VIDEO_PALETTE_GREY : + { + vid_pic.depth = 8; + break; + } + case VIDEO_PALETTE_RGB565 : + case VIDEO_PALETTE_YUYV : + case VIDEO_PALETTE_YUV422 : + case VIDEO_PALETTE_YUV420P : + case VIDEO_PALETTE_YUV422P : + default: + { + vid_pic.depth = 16; + break; + } + } - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(vid_pic) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) - Fatal( "Failed to get picture attributes: %s", strerror(errno) ); + if ( brightness >= 0 ) vid_pic.brightness = brightness; + if ( hue >= 0 ) vid_pic.hue = hue; + if ( colour >= 0 ) vid_pic.colour = colour; + if ( contrast >= 0 ) vid_pic.contrast = contrast; - Debug( 4, "Old P:%d", vid_pic.palette ); - Debug( 4, "Old D:%d", vid_pic.depth ); - Debug( 4, "Old B:%d", vid_pic.brightness ); - Debug( 4, "Old h:%d", vid_pic.hue ); - Debug( 4, "Old Cl:%d", vid_pic.colour ); - Debug( 4, "Old Cn:%d", vid_pic.contrast ); + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + if ( config.strict_video_config ) + exit(-1); + } - switch (vid_pic.palette = palette) - { - case VIDEO_PALETTE_RGB32 : - { - vid_pic.depth = 32; - break; - } - case VIDEO_PALETTE_RGB24 : - { - vid_pic.depth = 24; - break; - } - case VIDEO_PALETTE_GREY : - { - vid_pic.depth = 8; - break; - } - case VIDEO_PALETTE_RGB565 : - case VIDEO_PALETTE_YUYV : - case VIDEO_PALETTE_YUV422 : - case VIDEO_PALETTE_YUV420P : - case VIDEO_PALETTE_YUV422P : - default: - { - vid_pic.depth = 16; - break; - } - } + Debug( 3, "Configuring window attributes" ); - if ( brightness >= 0 ) vid_pic.brightness = brightness; - if ( hue >= 0 ) vid_pic.hue = hue; - if ( colour >= 0 ) vid_pic.colour = colour; - if ( contrast >= 0 ) vid_pic.contrast = contrast; + struct video_window vid_win; + memset( &vid_win, 0, sizeof(vid_win) ); + if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) + { + Error( "Failed to get window attributes: %s", strerror(errno) ); + exit(-1); + } + Debug( 4, "Old X:%d", vid_win.x ); + Debug( 4, "Old Y:%d", vid_win.y ); + Debug( 4, "Old W:%d", vid_win.width ); + Debug( 4, "Old H:%d", vid_win.height ); + + vid_win.x = 0; + vid_win.y = 0; + vid_win.width = width; + vid_win.height = height; + vid_win.flags &= ~VIDEO_WINDOW_INTERLACE; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - if ( config.strict_video_config ) - exit(-1); - } + if ( ioctl( vid_fd, VIDIOCSWIN, &vid_win ) < 0 ) + { + Error( "Failed to set window attributes: %s", strerror(errno) ); + if ( config.strict_video_config ) + exit(-1); + } - Debug( 3, "Configuring window attributes" ); + Info( "vid_win.width = %08x", vid_win.width ); + Info( "vid_win.height = %08x", vid_win.height ); + Info( "vid_win.flags = %08x", vid_win.flags ); - struct video_window vid_win; - memset( &vid_win, 0, sizeof(vid_win) ); - if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) - { - Error( "Failed to get window attributes: %s", strerror(errno) ); - exit(-1); - } - Debug( 4, "Old X:%d", vid_win.x ); - Debug( 4, "Old Y:%d", vid_win.y ); - Debug( 4, "Old W:%d", vid_win.width ); - Debug( 4, "Old H:%d", vid_win.height ); - - vid_win.x = 0; - vid_win.y = 0; - vid_win.width = width; - vid_win.height = height; - vid_win.flags &= ~VIDEO_WINDOW_INTERLACE; + Debug( 3, "Setting up request buffers" ); + if ( ioctl( vid_fd, VIDIOCGMBUF, &v4l1_data.frames ) < 0 ) + Fatal( "Failed to setup memory: %s", strerror(errno) ); + if ( channel_count > 1 && !v4l_multi_buffer ) + v4l1_data.frames.frames = 1; + v4l1_data.buffers = new video_mmap[v4l1_data.frames.frames]; + Debug( 4, "vmb.frames = %d", v4l1_data.frames.frames ); + Debug( 4, "vmb.size = %d", v4l1_data.frames.size ); - if ( ioctl( vid_fd, VIDIOCSWIN, &vid_win ) < 0 ) - { - Error( "Failed to set window attributes: %s", strerror(errno) ); - if ( config.strict_video_config ) - exit(-1); - } + Debug( 3, "Setting up %d frame buffers", v4l1_data.frames.frames ); - Info( "vid_win.width = %08x", vid_win.width ); - Info( "vid_win.height = %08x", vid_win.height ); - Info( "vid_win.flags = %08x", vid_win.flags ); - - Debug( 3, "Setting up request buffers" ); - if ( ioctl( vid_fd, VIDIOCGMBUF, &v4l1_data.frames ) < 0 ) - Fatal( "Failed to setup memory: %s", strerror(errno) ); - if ( channel_count > 1 && !v4l_multi_buffer ) - v4l1_data.frames.frames = 1; - v4l1_data.buffers = new video_mmap[v4l1_data.frames.frames]; - Debug( 4, "vmb.frames = %d", v4l1_data.frames.frames ); - Debug( 4, "vmb.size = %d", v4l1_data.frames.size ); - - Debug( 3, "Setting up %d frame buffers", v4l1_data.frames.frames ); - - v4l1_data.bufptr = (unsigned char *)mmap( 0, v4l1_data.frames.size, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, 0 ); - if ( v4l1_data.bufptr == MAP_FAILED ) - Fatal( "Could not mmap video: %s", strerror(errno) ); + v4l1_data.bufptr = (unsigned char *)mmap( 0, v4l1_data.frames.size, PROT_READ|PROT_WRITE, MAP_SHARED, vid_fd, 0 ); + if ( v4l1_data.bufptr == MAP_FAILED ) + Fatal( "Could not mmap video: %s", strerror(errno) ); #if HAVE_LIBSWSCALE - capturePictures = new AVFrame *[v4l1_data.frames.frames]; - for ( int i = 0; i < v4l1_data.frames.frames; i++ ) - { - v4l1_data.buffers[i].frame = i; - v4l1_data.buffers[i].width = width; - v4l1_data.buffers[i].height = height; - v4l1_data.buffers[i].format = palette; + capturePictures = new AVFrame *[v4l1_data.frames.frames]; + for ( int i = 0; i < v4l1_data.frames.frames; i++ ) + { + v4l1_data.buffers[i].frame = i; + v4l1_data.buffers[i].width = width; + v4l1_data.buffers[i].height = height; + v4l1_data.buffers[i].format = palette; - capturePictures[i] = avcodec_alloc_frame(); - if ( !capturePictures[i] ) - Fatal( "Could not allocate picture" ); - avpicture_fill( (AVPicture *)capturePictures[i], (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], capturePixFormat, width, height ); - } +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + capturePictures[i] = av_frame_alloc(); +#else + capturePictures[i] = avcodec_alloc_frame(); +#endif + if ( !capturePictures[i] ) + Fatal( "Could not allocate picture" ); +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(capturePictures[i]->data, + capturePictures[i]->linesize, + (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], + capturePixFormat, width, height, 1); +#else + avpicture_fill( (AVPicture *)capturePictures[i], + (unsigned char *)v4l1_data.bufptr+v4l1_data.frames.offsets[i], + capturePixFormat, width, height ); +#endif + } #endif // HAVE_LIBSWSCALE - Debug( 3, "Configuring video source" ); + Debug( 3, "Configuring video source" ); - struct video_channel vid_src; - memset( &vid_src, 0, sizeof(vid_src) ); - vid_src.channel = channel; - if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) - Fatal( "Failed to get camera source: %s", strerror(errno) ); + struct video_channel vid_src; + memset( &vid_src, 0, sizeof(vid_src) ); + vid_src.channel = channel; + if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) + Fatal( "Failed to get camera source: %s", strerror(errno) ); - Debug( 4, "Old C:%d", vid_src.channel ); - Debug( 4, "Old F:%d", vid_src.norm ); - Debug( 4, "Old Fl:%x", vid_src.flags ); - Debug( 4, "Old T:%d", vid_src.type ); + Debug( 4, "Old C:%d", vid_src.channel ); + Debug( 4, "Old F:%d", vid_src.norm ); + Debug( 4, "Old Fl:%x", vid_src.flags ); + Debug( 4, "Old T:%d", vid_src.type ); - vid_src.norm = standard; - vid_src.flags = 0; - vid_src.type = VIDEO_TYPE_CAMERA; - if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) - { - Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); - if ( config.strict_video_config ) - exit(-1); - } - - if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) - Fatal( "Failed to get window data: %s", strerror(errno) ); - - Info( "vid_win.width = %08x", vid_win.width ); - Info( "vid_win.height = %08x", vid_win.height ); - Info( "vid_win.flags = %08x", vid_win.flags ); - - Debug( 4, "New X:%d", vid_win.x ); - Debug( 4, "New Y:%d", vid_win.y ); - Debug( 4, "New W:%d", vid_win.width ); - Debug( 4, "New H:%d", vid_win.height ); - - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) - Fatal( "Failed to get window data: %s", strerror(errno) ); - - Debug( 4, "New P:%d", vid_pic.palette ); - Debug( 4, "New D:%d", vid_pic.depth ); - Debug( 4, "New B:%d", vid_pic.brightness ); - Debug( 4, "New h:%d", vid_pic.hue ); - Debug( 4, "New Cl:%d", vid_pic.colour ); - Debug( 4, "New Cn:%d", vid_pic.contrast ); + vid_src.norm = standard; + vid_src.flags = 0; + vid_src.type = VIDEO_TYPE_CAMERA; + if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) + { + Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); + if ( config.strict_video_config ) + exit(-1); } + + if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win) < 0 ) + Fatal( "Failed to get window data: %s", strerror(errno) ); + + Info( "vid_win.width = %08x", vid_win.width ); + Info( "vid_win.height = %08x", vid_win.height ); + Info( "vid_win.flags = %08x", vid_win.flags ); + + Debug( 4, "New X:%d", vid_win.x ); + Debug( 4, "New Y:%d", vid_win.y ); + Debug( 4, "New W:%d", vid_win.width ); + Debug( 4, "New H:%d", vid_win.height ); + + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) + Fatal( "Failed to get window data: %s", strerror(errno) ); + + Debug( 4, "New P:%d", vid_pic.palette ); + Debug( 4, "New D:%d", vid_pic.depth ); + Debug( 4, "New B:%d", vid_pic.brightness ); + Debug( 4, "New h:%d", vid_pic.hue ); + Debug( 4, "New Cl:%d", vid_pic.colour ); + Debug( 4, "New Cn:%d", vid_pic.contrast ); + } #endif // ZM_HAS_V4L1 } void LocalCamera::Terminate() { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - Debug( 3, "Terminating video stream" ); - //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - // enum v4l2_buf_type type = v4l2_data.fmt.type; - enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; - if ( vidioctl( vid_fd, VIDIOC_STREAMOFF, &type ) < 0 ) - Error( "Failed to stop capture stream: %s", strerror(errno) ); + if ( v4l_version == 2 ) + { + Debug( 3, "Terminating video stream" ); + //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + // enum v4l2_buf_type type = v4l2_data.fmt.type; + enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; + if ( vidioctl( vid_fd, VIDIOC_STREAMOFF, &type ) < 0 ) + Error( "Failed to stop capture stream: %s", strerror(errno) ); - Debug( 3, "Unmapping video buffers" ); - for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) { + Debug( 3, "Unmapping video buffers" ); + for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) { #if HAVE_LIBSWSCALE - /* Free capture pictures */ - av_free(capturePictures[i]); - capturePictures[i] = NULL; + /* Free capture pictures */ +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &capturePictures[i] ); +#else + av_freep( &capturePictures[i] ); #endif - if ( munmap( v4l2_data.buffers[i].start, v4l2_data.buffers[i].length ) < 0 ) - Error( "Failed to munmap buffer %d: %s", i, strerror(errno) ); - } - - } - else +#endif + if ( munmap( v4l2_data.buffers[i].start, v4l2_data.buffers[i].length ) < 0 ) + Error( "Failed to munmap buffer %d: %s", i, strerror(errno) ); + } + + } + else #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { + if ( v4l_version == 1 ) + { #if HAVE_LIBSWSCALE - for(int i=0; i < v4l1_data.frames.frames; i++) { - /* Free capture pictures */ - av_free(capturePictures[i]); - capturePictures[i] = NULL; - } + for(int i=0; i < v4l1_data.frames.frames; i++) { + /* Free capture pictures */ +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &capturePictures[i] ); +#else + av_freep( &capturePictures[i] ); #endif - - Debug( 3, "Unmapping video buffers" ); - if ( munmap((char*)v4l1_data.bufptr, v4l1_data.frames.size) < 0 ) - Error( "Failed to munmap buffers: %s", strerror(errno) ); - - delete[] v4l1_data.buffers; } +#endif + + Debug( 3, "Unmapping video buffers" ); + if ( munmap((char*)v4l1_data.bufptr, v4l1_data.frames.size) < 0 ) + Error( "Failed to munmap buffers: %s", strerror(errno) ); + + delete[] v4l1_data.buffers; + } #endif // ZM_HAS_V4L1 - close( vid_fd ); - + close( vid_fd ); + } uint32_t LocalCamera::AutoSelectFormat(int p_colours) { - /* Automatic format selection */ - uint32_t selected_palette = 0; + /* Automatic format selection */ + uint32_t selected_palette = 0; #if ZM_HAS_V4L2 - char fmt_desc[64][32]; - uint32_t fmt_fcc[64]; - v4l2_fmtdesc fmtinfo; - unsigned int nIndex = 0; - //int nRet = 0; // compiler say it isn't used - int enum_fd; - - /* Open the device */ - if ((enum_fd = open( device.c_str(), O_RDWR, 0 )) < 0) { - Error( "Automatic format selection failed to open video device %s: %s", device.c_str(), strerror(errno) ); - return selected_palette; - } - - /* Enumerate available formats */ - memset(&fmtinfo, 0, sizeof(fmtinfo)); - fmtinfo.index = nIndex; - fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - while(vidioctl( enum_fd, VIDIOC_ENUM_FMT, &fmtinfo ) >= 0) { - /* Got a format. Copy it to the array */ - strcpy(fmt_desc[nIndex], (const char*)(fmtinfo.description)); - fmt_fcc[nIndex] = fmtinfo.pixelformat; - - Debug(6, "Got format: %s (%c%c%c%c) at index %d",fmt_desc[nIndex],fmt_fcc[nIndex]&0xff, (fmt_fcc[nIndex]>>8)&0xff, (fmt_fcc[nIndex]>>16)&0xff, (fmt_fcc[nIndex]>>24)&0xff ,nIndex); - - /* Proceed to the next index */ - memset(&fmtinfo, 0, sizeof(fmtinfo)); - fmtinfo.index = ++nIndex; - fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - } - - /* Select format */ - int nIndexUsed = -1; - int n_preferedformats = 0; - const uint32_t* preferedformats; - if(p_colours == ZM_COLOUR_RGB32) { - /* 32bit */ - preferedformats = prefered_rgb32_formats; - n_preferedformats = sizeof(prefered_rgb32_formats) / sizeof(uint32_t); - } else if(p_colours == ZM_COLOUR_GRAY8) { - /* Grayscale */ - preferedformats = prefered_gray8_formats; - n_preferedformats = sizeof(prefered_gray8_formats) / sizeof(uint32_t); - } else { - /* Assume 24bit */ - preferedformats = prefered_rgb24_formats; - n_preferedformats = sizeof(prefered_rgb24_formats) / sizeof(uint32_t); - } - for( unsigned int i=0; i < (unsigned int)n_preferedformats && nIndexUsed < 0; i++ ) { - for( unsigned int j=0; j < nIndex; j++ ) { - if( preferedformats[i] == fmt_fcc[j] ) { - /* Found a format! */ - nIndexUsed = j; - break; - } - } - } - - /* Have we found a match? */ - if(nIndexUsed >= 0) { - /* Found a match */ - selected_palette = fmt_fcc[nIndexUsed]; - strcpy(palette_desc,fmt_desc[nIndexUsed]); - } - - /* Close the device */ - close(enum_fd); - + char fmt_desc[64][32]; + uint32_t fmt_fcc[64]; + v4l2_fmtdesc fmtinfo; + unsigned int nIndex = 0; + //int nRet = 0; // compiler say it isn't used + int enum_fd; + + /* Open the device */ + if ((enum_fd = open( device.c_str(), O_RDWR, 0 )) < 0) { + Error( "Automatic format selection failed to open video device %s: %s", device.c_str(), strerror(errno) ); + return selected_palette; + } + + /* Enumerate available formats */ + memset(&fmtinfo, 0, sizeof(fmtinfo)); + fmtinfo.index = nIndex; + fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + // FIXME This will crash if there are more than 64 formats. + while(vidioctl( enum_fd, VIDIOC_ENUM_FMT, &fmtinfo ) >= 0) { + /* Got a format. Copy it to the array */ + strcpy(fmt_desc[nIndex], (const char*)(fmtinfo.description)); + fmt_fcc[nIndex] = fmtinfo.pixelformat; + + Debug(6, "Got format: %s (%c%c%c%c) at index %d",fmt_desc[nIndex],fmt_fcc[nIndex]&0xff, (fmt_fcc[nIndex]>>8)&0xff, (fmt_fcc[nIndex]>>16)&0xff, (fmt_fcc[nIndex]>>24)&0xff ,nIndex); + + /* Proceed to the next index */ + memset(&fmtinfo, 0, sizeof(fmtinfo)); + fmtinfo.index = ++nIndex; + fmtinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + + /* Select format */ + int nIndexUsed = -1; + unsigned int n_preferedformats = 0; + const uint32_t* preferedformats; + if(p_colours == ZM_COLOUR_RGB32) { + /* 32bit */ + preferedformats = prefered_rgb32_formats; + n_preferedformats = sizeof(prefered_rgb32_formats) / sizeof(uint32_t); + } else if(p_colours == ZM_COLOUR_GRAY8) { + /* Grayscale */ + preferedformats = prefered_gray8_formats; + n_preferedformats = sizeof(prefered_gray8_formats) / sizeof(uint32_t); + } else { + /* Assume 24bit */ + preferedformats = prefered_rgb24_formats; + n_preferedformats = sizeof(prefered_rgb24_formats) / sizeof(uint32_t); + } + for( unsigned int i=0; i < n_preferedformats && nIndexUsed < 0; i++ ) { + for( unsigned int j=0; j < nIndex; j++ ) { + if( preferedformats[i] == fmt_fcc[j] ) { + Debug(6, "Choosing format: %s (%c%c%c%c) at index %d",fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); + /* Found a format! */ + nIndexUsed = j; + break; + } else { + Debug(6, "No match for format: %s (%c%c%c%c) at index %d",fmt_desc[j],fmt_fcc[j]&0xff, (fmt_fcc[j]>>8)&0xff, (fmt_fcc[j]>>16)&0xff, (fmt_fcc[j]>>24)&0xff ,j); + } + } + } + + /* Have we found a match? */ + if(nIndexUsed >= 0) { + /* Found a match */ + selected_palette = fmt_fcc[nIndexUsed]; + strcpy(palette_desc,fmt_desc[nIndexUsed]); + } + + /* Close the device */ + close(enum_fd); + #endif /* ZM_HAS_V4L2 */ - return selected_palette; + return selected_palette; } #define capString(test,prefix,yesString,noString,capability) \ - (test) ? (prefix yesString " " capability "\n") : (prefix noString " " capability "\n") + (test) ? (prefix yesString " " capability "\n") : (prefix noString " " capability "\n") bool LocalCamera::GetCurrentSettings( const char *device, char *output, int version, bool verbose ) { - output[0] = 0; + output[0] = 0; - char queryDevice[PATH_MAX] = ""; - int devIndex = 0; - do + char queryDevice[PATH_MAX] = ""; + int devIndex = 0; + do + { + if ( device ) + strcpy( queryDevice, device ); + else + sprintf( queryDevice, "/dev/video%d", devIndex ); + if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) { - if ( device ) - strcpy( queryDevice, device ); - else - sprintf( queryDevice, "/dev/video%d", devIndex ); - if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) - { - if ( device ) - { - Error( "Failed to open video device %s: %s", queryDevice, strerror(errno) ); - if ( verbose ) - sprintf( output+strlen(output), "Error, failed to open video device %s: %s\n", queryDevice, strerror(errno) ); - else - sprintf( output+strlen(output), "error%d\n", errno ); - return( false ); - } - else - { - return( true ); - } - } + if ( device ) + { + Error( "Failed to open video device %s: %s", queryDevice, strerror(errno) ); if ( verbose ) - sprintf( output+strlen(output), "Video Device: %s\n", queryDevice ); + sprintf( output+strlen(output), "Error, failed to open video device %s: %s\n", queryDevice, strerror(errno) ); else - sprintf( output+strlen(output), "d:%s|", queryDevice ); + sprintf( output+strlen(output), "error%d\n", errno ); + return( false ); + } + else + { + return( true ); + } + } + if ( verbose ) + sprintf( output+strlen(output), "Video Device: %s\n", queryDevice ); + else + sprintf( output+strlen(output), "d:%s|", queryDevice ); #if ZM_HAS_V4L2 - if ( version == 2 ) - { - struct v4l2_capability vid_cap; - if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) - { - Error( "Failed to query video device: %s", strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to query video capabilities %s: %s\n", queryDevice, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } + if ( version == 2 ) + { + struct v4l2_capability vid_cap; + if ( vidioctl( vid_fd, VIDIOC_QUERYCAP, &vid_cap ) < 0 ) + { + Error( "Failed to query video device: %s", strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to query video capabilities %s: %s\n", queryDevice, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } - if ( verbose ) - { - sprintf( output+strlen(output), "General Capabilities\n" ); - sprintf( output+strlen(output), " Driver: %s\n", vid_cap.driver ); - sprintf( output+strlen(output), " Card: %s\n", vid_cap.card ); - sprintf( output+strlen(output), " Bus: %s\n", vid_cap.bus_info ); - sprintf( output+strlen(output), " Version: %u.%u.%u\n", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); - sprintf( output+strlen(output), " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.capabilities, - capString( vid_cap.capabilities&V4L2_CAP_VIDEO_CAPTURE, " ", "Supports", "Does not support", "video capture (X)" ), - capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT, " ", "Supports", "Does not support", "video output" ), - capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OVERLAY, " ", "Supports", "Does not support", "frame buffer overlay" ), - capString( vid_cap.capabilities&V4L2_CAP_VBI_CAPTURE, " ", "Supports", "Does not support", "VBI capture" ), - capString( vid_cap.capabilities&V4L2_CAP_VBI_OUTPUT, " ", "Supports", "Does not support", "VBI output" ), - capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_CAPTURE, " ", "Supports", "Does not support", "sliced VBI capture" ), - capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_OUTPUT, " ", "Supports", "Does not support", "sliced VBI output" ), + if ( verbose ) + { + sprintf( output+strlen(output), "General Capabilities\n" ); + sprintf( output+strlen(output), " Driver: %s\n", vid_cap.driver ); + sprintf( output+strlen(output), " Card: %s\n", vid_cap.card ); + sprintf( output+strlen(output), " Bus: %s\n", vid_cap.bus_info ); + sprintf( output+strlen(output), " Version: %u.%u.%u\n", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); + sprintf( output+strlen(output), " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.capabilities, + capString( vid_cap.capabilities&V4L2_CAP_VIDEO_CAPTURE, " ", "Supports", "Does not support", "video capture (X)" ), + capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT, " ", "Supports", "Does not support", "video output" ), + capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OVERLAY, " ", "Supports", "Does not support", "frame buffer overlay" ), + capString( vid_cap.capabilities&V4L2_CAP_VBI_CAPTURE, " ", "Supports", "Does not support", "VBI capture" ), + capString( vid_cap.capabilities&V4L2_CAP_VBI_OUTPUT, " ", "Supports", "Does not support", "VBI output" ), + capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_CAPTURE, " ", "Supports", "Does not support", "sliced VBI capture" ), + capString( vid_cap.capabilities&V4L2_CAP_SLICED_VBI_OUTPUT, " ", "Supports", "Does not support", "sliced VBI output" ), #ifdef V4L2_CAP_VIDEO_OUTPUT_OVERLAY - capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT_OVERLAY, " ", "Supports", "Does not support", "video output overlay" ), + capString( vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT_OVERLAY, " ", "Supports", "Does not support", "video output overlay" ), #else // V4L2_CAP_VIDEO_OUTPUT_OVERLAY - "", + "", #endif // V4L2_CAP_VIDEO_OUTPUT_OVERLAY - capString( vid_cap.capabilities&V4L2_CAP_TUNER, " ", "Has", "Does not have", "tuner" ), - capString( vid_cap.capabilities&V4L2_CAP_AUDIO, " ", "Has", "Does not have", "audio in and/or out" ), - capString( vid_cap.capabilities&V4L2_CAP_RADIO, " ", "Has", "Does not have", "radio" ), - capString( vid_cap.capabilities&V4L2_CAP_READWRITE, " ", "Supports", "Does not support", "read/write i/o (X)" ), - capString( vid_cap.capabilities&V4L2_CAP_ASYNCIO, " ", "Supports", "Does not support", "async i/o" ), - capString( vid_cap.capabilities&V4L2_CAP_STREAMING, " ", "Supports", "Does not support", "streaming i/o (X)" ) - ); - } - else - { - sprintf( output+strlen(output), "D:%s|", vid_cap.driver ); - sprintf( output+strlen(output), "C:%s|", vid_cap.card ); - sprintf( output+strlen(output), "B:%s|", vid_cap.bus_info ); - sprintf( output+strlen(output), "V:%u.%u.%u|", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); - sprintf( output+strlen(output), "T:0x%x|", vid_cap.capabilities ); - } + capString( vid_cap.capabilities&V4L2_CAP_TUNER, " ", "Has", "Does not have", "tuner" ), + capString( vid_cap.capabilities&V4L2_CAP_AUDIO, " ", "Has", "Does not have", "audio in and/or out" ), + capString( vid_cap.capabilities&V4L2_CAP_RADIO, " ", "Has", "Does not have", "radio" ), + capString( vid_cap.capabilities&V4L2_CAP_READWRITE, " ", "Supports", "Does not support", "read/write i/o (X)" ), + capString( vid_cap.capabilities&V4L2_CAP_ASYNCIO, " ", "Supports", "Does not support", "async i/o" ), + capString( vid_cap.capabilities&V4L2_CAP_STREAMING, " ", "Supports", "Does not support", "streaming i/o (X)" ) + ); + } + else + { + sprintf( output+strlen(output), "D:%s|", vid_cap.driver ); + sprintf( output+strlen(output), "C:%s|", vid_cap.card ); + sprintf( output+strlen(output), "B:%s|", vid_cap.bus_info ); + sprintf( output+strlen(output), "V:%u.%u.%u|", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff ); + sprintf( output+strlen(output), "T:0x%x|", vid_cap.capabilities ); + } + if ( verbose ) + sprintf( output+strlen(output), " Standards:\n" ); + else + sprintf( output+strlen(output), "S:" ); + struct v4l2_standard standard; + int standardIndex = 0; + do + { + memset( &standard, 0, sizeof(standard) ); + standard.index = standardIndex; + + if ( vidioctl( vid_fd, VIDIOC_ENUMSTD, &standard ) < 0 ) + { + if ( errno == EINVAL || errno == ENODATA || errno == ENOTTY ) + { + Debug( 6, "Done enumerating standard %d: %d %s", standard.index, errno, strerror(errno) ); + standardIndex = -1; + break; + } + else + { + Error( "Failed to enumerate standard %d: %d %s", standard.index, errno, strerror(errno) ); if ( verbose ) - sprintf( output+strlen(output), " Standards:\n" ); + sprintf( output, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno) ); else - sprintf( output+strlen(output), "S:" ); - struct v4l2_standard standard; - int standardIndex = 0; - do - { - memset( &standard, 0, sizeof(standard) ); - standard.index = standardIndex; - - if ( vidioctl( vid_fd, VIDIOC_ENUMSTD, &standard ) < 0 ) - { - if ( errno == EINVAL || errno == ENODATA ) - { - standardIndex = -1; - break; - } - else - { - Error( "Failed to enumerate standard %d: %s", standard.index, strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to enumerate standard %d: %s\n", standard.index, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - } - if ( verbose ) - sprintf( output+strlen(output), " %s\n", standard.name ); - else - sprintf( output+strlen(output), "%s/", standard.name ); - } - while ( standardIndex++ >= 0 ); - if ( !verbose && output[strlen(output)-1] == '/') - output[strlen(output)-1] = '|'; - - if ( verbose ) - sprintf( output+strlen(output), " Formats:\n" ); - else - sprintf( output+strlen(output), "F:" ); - struct v4l2_fmtdesc format; - int formatIndex = 0; - do - { - memset( &format, 0, sizeof(format) ); - format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - format.index = formatIndex; - - if ( vidioctl( vid_fd, VIDIOC_ENUM_FMT, &format ) < 0 ) - { - if ( errno == EINVAL ) - { - formatIndex = -1; - break; - } - else - { - Error( "Failed to enumerate format %d: %s", format.index, strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - } - if ( verbose ) - sprintf( output+strlen(output), " %s (%c%c%c%c)\n", format.description, format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); - else - sprintf( output+strlen(output), "%c%c%c%c/", format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); - } - while ( formatIndex++ >= 0 ); - if ( !verbose ) - output[strlen(output)-1] = '|'; - - if(verbose) - sprintf( output+strlen(output), "Crop Capabilities\n" ); - - struct v4l2_cropcap cropcap; - memset( &cropcap, 0, sizeof(cropcap) ); - cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if ( vidioctl( vid_fd, VIDIOC_CROPCAP, &cropcap ) < 0 ) - { - if(errno != EINVAL) { - /* Failed querying crop capability, write error to the log and continue as if crop is not supported */ - Error( "Failed to query crop capabilities: %s", strerror(errno) ); - } - - if(verbose) { - sprintf( output+strlen(output), " Cropping is not supported\n"); - } else { - /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ - sprintf( output+strlen(output), "B:%dx%d|",0,0); - } - } else { - struct v4l2_crop crop; - memset( &crop, 0, sizeof(crop) ); - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if ( vidioctl( vid_fd, VIDIOC_G_CROP, &crop ) < 0 ) - { - if ( errno != EINVAL ) - { - /* Failed querying crop sizes, write error to the log and continue as if crop is not supported */ - Error( "Failed to query crop: %s", strerror(errno) ); - } - - if ( verbose ) { - sprintf( output+strlen(output), " Cropping is not supported\n"); - } else { - /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ - sprintf( output+strlen(output), "B:%dx%d|",0,0); - } - } else { - /* Cropping supported */ - if ( verbose ) { - sprintf( output+strlen(output), " Bounds: %d x %d\n", cropcap.bounds.width, cropcap.bounds.height ); - sprintf( output+strlen(output), " Default: %d x %d\n", cropcap.defrect.width, cropcap.defrect.height ); - sprintf( output+strlen(output), " Current: %d x %d\n", crop.c.width, crop.c.height ); - } else { - sprintf( output+strlen(output), "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height ); - } - } - } /* Crop code */ - - struct v4l2_input input; - int inputIndex = 0; - do - { - memset( &input, 0, sizeof(input) ); - input.index = inputIndex; - - if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) - { - if ( errno == EINVAL ) - { - break; - } - else - { - Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - } - } - while ( inputIndex++ >= 0 ); - - if ( verbose ) - sprintf( output+strlen(output), "Inputs: %d\n", inputIndex ); - else - sprintf( output+strlen(output), "I:%d|", inputIndex ); - - inputIndex = 0; - do - { - memset( &input, 0, sizeof(input) ); - input.index = inputIndex; - - if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) - { - if ( errno == EINVAL ) - { - inputIndex = -1; - break; - } - else - { - Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - } - - if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &input.index ) < 0 ) - { - Error( "Failed to set video input %d: %s", input.index, strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - - if ( verbose ) - { - sprintf( output+strlen(output), " Input %d\n", input.index ); - sprintf( output+strlen(output), " Name: %s\n", input.name ); - sprintf( output+strlen(output), " Type: %s\n", input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); - sprintf( output+strlen(output), " Audioset: %08x\n", input.audioset ); - sprintf( output+strlen(output), " Standards: 0x%llx\n", input.std ); - } - else - { - sprintf( output+strlen(output), "i%d:%s|", input.index, input.name ); - sprintf( output+strlen(output), "i%dT:%s|", input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); - sprintf( output+strlen(output), "i%dS:%llx|", input.index, input.std ); - } - - if ( verbose ) - { - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)" ) ); - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)" ) ); - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "" ) ); - sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", "" ) ); - } - else - { - sprintf( output+strlen(output), "i%dSP:%d|", input.index, input.status&V4L2_IN_ST_NO_POWER?0:1 ); - sprintf( output+strlen(output), "i%dSS:%d|", input.index, input.status&V4L2_IN_ST_NO_SIGNAL?0:1 ); - sprintf( output+strlen(output), "i%dSC:%d|", input.index, input.status&V4L2_IN_ST_NO_COLOR?0:1 ); - sprintf( output+strlen(output), "i%dHP:%d|", input.index, input.status&V4L2_IN_ST_NO_H_LOCK?0:1 ); - } - } - while ( inputIndex++ >= 0 ); - if ( !verbose ) - output[strlen(output)-1] = '\n'; + sprintf( output, "error%d\n", errno ); + return( false ); + } } + if ( verbose ) + sprintf( output+strlen(output), " %s\n", standard.name ); + else + sprintf( output+strlen(output), "%s/", standard.name ); + } + while ( standardIndex++ >= 0 ); + if ( !verbose && output[strlen(output)-1] == '/') + output[strlen(output)-1] = '|'; + + if ( verbose ) + sprintf( output+strlen(output), " Formats:\n" ); + else + sprintf( output+strlen(output), "F:" ); + struct v4l2_fmtdesc format; + int formatIndex = 0; + do + { + memset( &format, 0, sizeof(format) ); + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + format.index = formatIndex; + + if ( vidioctl( vid_fd, VIDIOC_ENUM_FMT, &format ) < 0 ) + { + if ( errno == EINVAL ) + { + formatIndex = -1; + break; + } + else + { + Error( "Failed to enumerate format %d: %s", format.index, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + } + if ( verbose ) + sprintf( output+strlen(output), " %s (%c%c%c%c)\n", format.description, format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); + else + sprintf( output+strlen(output), "%c%c%c%c/", format.pixelformat&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>24)&0xff ); + } + while ( formatIndex++ >= 0 ); + if ( !verbose ) + output[strlen(output)-1] = '|'; + + if(verbose) + sprintf( output+strlen(output), "Crop Capabilities\n" ); + + struct v4l2_cropcap cropcap; + memset( &cropcap, 0, sizeof(cropcap) ); + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if ( vidioctl( vid_fd, VIDIOC_CROPCAP, &cropcap ) < 0 ) + { + if(errno != EINVAL) { + /* Failed querying crop capability, write error to the log and continue as if crop is not supported */ + Error( "Failed to query crop capabilities: %s", strerror(errno) ); + } + + if(verbose) { + sprintf( output+strlen(output), " Cropping is not supported\n"); + } else { + /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ + sprintf( output+strlen(output), "B:%dx%d|",0,0); + } + } else { + struct v4l2_crop crop; + memset( &crop, 0, sizeof(crop) ); + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if ( vidioctl( vid_fd, VIDIOC_G_CROP, &crop ) < 0 ) + { + if ( errno != EINVAL ) + { + /* Failed querying crop sizes, write error to the log and continue as if crop is not supported */ + Error( "Failed to query crop: %s", strerror(errno) ); + } + + if ( verbose ) { + sprintf( output+strlen(output), " Cropping is not supported\n"); + } else { + /* Send fake crop bounds to not confuse things parsing this, such as monitor probe */ + sprintf( output+strlen(output), "B:%dx%d|",0,0); + } + } else { + /* Cropping supported */ + if ( verbose ) { + sprintf( output+strlen(output), " Bounds: %d x %d\n", cropcap.bounds.width, cropcap.bounds.height ); + sprintf( output+strlen(output), " Default: %d x %d\n", cropcap.defrect.width, cropcap.defrect.height ); + sprintf( output+strlen(output), " Current: %d x %d\n", crop.c.width, crop.c.height ); + } else { + sprintf( output+strlen(output), "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height ); + } + } + } /* Crop code */ + + struct v4l2_input input; + int inputIndex = 0; + do + { + memset( &input, 0, sizeof(input) ); + input.index = inputIndex; + + if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) + { + if ( errno == EINVAL ) + { + break; + } + else + { + Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + } + } + while ( inputIndex++ >= 0 ); + + if ( verbose ) + sprintf( output+strlen(output), "Inputs: %d\n", inputIndex ); + else + sprintf( output+strlen(output), "I:%d|", inputIndex ); + + inputIndex = 0; + do + { + memset( &input, 0, sizeof(input) ); + input.index = inputIndex; + + if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) + { + if ( errno == EINVAL ) + { + inputIndex = -1; + break; + } + else + { + Error( "Failed to enumerate input %d: %s", input.index, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + } + + if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &input.index ) < 0 ) + { + Error( "Failed to set video input %d: %s", input.index, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + + if ( verbose ) + { + sprintf( output+strlen(output), " Input %d\n", input.index ); + sprintf( output+strlen(output), " Name: %s\n", input.name ); + sprintf( output+strlen(output), " Type: %s\n", input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); + sprintf( output+strlen(output), " Audioset: %08x\n", input.audioset ); + sprintf( output+strlen(output), " Standards: 0x%llx\n", input.std ); + } + else + { + sprintf( output+strlen(output), "i%d:%s|", input.index, input.name ); + sprintf( output+strlen(output), "i%dT:%s|", input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") ); + sprintf( output+strlen(output), "i%dS:%llx|", input.index, input.std ); + } + + if ( verbose ) + { + sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)" ) ); + sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)" ) ); + sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "" ) ); + sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", "" ) ); + } + else + { + sprintf( output+strlen(output), "i%dSP:%d|", input.index, input.status&V4L2_IN_ST_NO_POWER?0:1 ); + sprintf( output+strlen(output), "i%dSS:%d|", input.index, input.status&V4L2_IN_ST_NO_SIGNAL?0:1 ); + sprintf( output+strlen(output), "i%dSC:%d|", input.index, input.status&V4L2_IN_ST_NO_COLOR?0:1 ); + sprintf( output+strlen(output), "i%dHP:%d|", input.index, input.status&V4L2_IN_ST_NO_H_LOCK?0:1 ); + } + } + while ( inputIndex++ >= 0 ); + if ( !verbose ) + output[strlen(output)-1] = '\n'; + } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( version == 1 ) + if ( version == 1 ) + { + struct video_capability vid_cap; + memset( &vid_cap, 0, sizeof(video_capability) ); + if ( ioctl( vid_fd, VIDIOCGCAP, &vid_cap ) < 0 ) + { + Error( "Failed to get video capabilities: %s", strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to get video capabilities %s: %s\n", queryDevice, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + if ( verbose ) + { + sprintf( output+strlen(output), "Video Capabilities\n" ); + sprintf( output+strlen(output), " Name: %s\n", vid_cap.name ); + sprintf( output+strlen(output), " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.type, + vid_cap.type&VID_TYPE_CAPTURE?" Can capture\n":"", + vid_cap.type&VID_TYPE_TUNER?" Can tune\n":"", + vid_cap.type&VID_TYPE_TELETEXT?" Does teletext\n":"", + vid_cap.type&VID_TYPE_OVERLAY?" Overlay onto frame buffer\n":"", + vid_cap.type&VID_TYPE_CHROMAKEY?" Overlay by chromakey\n":"", + vid_cap.type&VID_TYPE_CLIPPING?" Can clip\n":"", + vid_cap.type&VID_TYPE_FRAMERAM?" Uses the frame buffer memory\n":"", + vid_cap.type&VID_TYPE_SCALES?" Scalable\n":"", + vid_cap.type&VID_TYPE_MONOCHROME?" Monochrome only\n":"", + vid_cap.type&VID_TYPE_SUBCAPTURE?" Can capture subareas of the image\n":"", + vid_cap.type&VID_TYPE_MPEG_DECODER?" Can decode MPEG streams\n":"", + vid_cap.type&VID_TYPE_MPEG_ENCODER?" Can encode MPEG streams\n":"", + vid_cap.type&VID_TYPE_MJPEG_DECODER?" Can decode MJPEG streams\n":"", + vid_cap.type&VID_TYPE_MJPEG_ENCODER?" Can encode MJPEG streams\n":"" + ); + sprintf( output+strlen(output), " Video Channels: %d\n", vid_cap.channels ); + sprintf( output+strlen(output), " Audio Channels: %d\n", vid_cap.audios ); + sprintf( output+strlen(output), " Maximum Width: %d\n", vid_cap.maxwidth ); + sprintf( output+strlen(output), " Maximum Height: %d\n", vid_cap.maxheight ); + sprintf( output+strlen(output), " Minimum Width: %d\n", vid_cap.minwidth ); + sprintf( output+strlen(output), " Minimum Height: %d\n", vid_cap.minheight ); + } + else + { + sprintf( output+strlen(output), "N:%s|", vid_cap.name ); + sprintf( output+strlen(output), "T:%d|", vid_cap.type ); + sprintf( output+strlen(output), "nC:%d|", vid_cap.channels ); + sprintf( output+strlen(output), "nA:%d|", vid_cap.audios ); + sprintf( output+strlen(output), "mxW:%d|", vid_cap.maxwidth ); + sprintf( output+strlen(output), "mxH:%d|", vid_cap.maxheight ); + sprintf( output+strlen(output), "mnW:%d|", vid_cap.minwidth ); + sprintf( output+strlen(output), "mnH:%d|", vid_cap.minheight ); + } + + struct video_window vid_win; + memset( &vid_win, 0, sizeof(video_window) ); + if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win ) < 0 ) + { + Error( "Failed to get window attributes: %s", strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to get window attributes: %s\n", strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + if ( verbose ) + { + sprintf( output+strlen(output), "Window Attributes\n" ); + sprintf( output+strlen(output), " X Offset: %d\n", vid_win.x ); + sprintf( output+strlen(output), " Y Offset: %d\n", vid_win.y ); + sprintf( output+strlen(output), " Width: %d\n", vid_win.width ); + sprintf( output+strlen(output), " Height: %d\n", vid_win.height ); + } + else + { + sprintf( output+strlen(output), "X:%d|", vid_win.x ); + sprintf( output+strlen(output), "Y:%d|", vid_win.y ); + sprintf( output+strlen(output), "W:%d|", vid_win.width ); + sprintf( output+strlen(output), "H:%d|", vid_win.height ); + } + + struct video_picture vid_pic; + memset( &vid_cap, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic ) < 0 ) + { + Error( "Failed to get picture attributes: %s", strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to get picture attributes: %s\n", strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); + } + if ( verbose ) + { + sprintf( output+strlen(output), "Picture Attributes\n" ); + sprintf( output+strlen(output), " Palette: %d - %s\n", vid_pic.palette, + vid_pic.palette==VIDEO_PALETTE_GREY?"Linear greyscale":( + vid_pic.palette==VIDEO_PALETTE_HI240?"High 240 cube (BT848)":( + vid_pic.palette==VIDEO_PALETTE_RGB565?"565 16 bit RGB":( + vid_pic.palette==VIDEO_PALETTE_RGB24?"24bit RGB":( + vid_pic.palette==VIDEO_PALETTE_RGB32?"32bit RGB":( + vid_pic.palette==VIDEO_PALETTE_RGB555?"555 15bit RGB":( + vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422 capture":( + vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( + vid_pic.palette==VIDEO_PALETTE_UYVY?"UVYV":( + vid_pic.palette==VIDEO_PALETTE_YUV420?"YUV420":( + vid_pic.palette==VIDEO_PALETTE_YUV411?"YUV411 capture":( + vid_pic.palette==VIDEO_PALETTE_RAW?"RAW capture (BT848)":( + vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( + vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422":( + vid_pic.palette==VIDEO_PALETTE_YUV422P?"YUV 4:2:2 Planar":( + vid_pic.palette==VIDEO_PALETTE_YUV411P?"YUV 4:1:1 Planar":( + vid_pic.palette==VIDEO_PALETTE_YUV420P?"YUV 4:2:0 Planar":( + vid_pic.palette==VIDEO_PALETTE_YUV410P?"YUV 4:1:0 Planar":"Unknown" + )))))))))))))))))); + sprintf( output+strlen(output), " Colour Depth: %d\n", vid_pic.depth ); + sprintf( output+strlen(output), " Brightness: %d\n", vid_pic.brightness ); + sprintf( output+strlen(output), " Hue: %d\n", vid_pic.hue ); + sprintf( output+strlen(output), " Colour :%d\n", vid_pic.colour ); + sprintf( output+strlen(output), " Contrast: %d\n", vid_pic.contrast ); + sprintf( output+strlen(output), " Whiteness: %d\n", vid_pic.whiteness ); + } + else + { + sprintf( output+strlen(output), "P:%d|", vid_pic.palette ); + sprintf( output+strlen(output), "D:%d|", vid_pic.depth ); + sprintf( output+strlen(output), "B:%d|", vid_pic.brightness ); + sprintf( output+strlen(output), "h:%d|", vid_pic.hue ); + sprintf( output+strlen(output), "Cl:%d|", vid_pic.colour ); + sprintf( output+strlen(output), "Cn:%d|", vid_pic.contrast ); + sprintf( output+strlen(output), "w:%d|", vid_pic.whiteness ); + } + + for ( int chan = 0; chan < vid_cap.channels; chan++ ) + { + struct video_channel vid_src; + memset( &vid_src, 0, sizeof(video_channel) ); + vid_src.channel = chan; + if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src ) < 0 ) { - struct video_capability vid_cap; - memset( &vid_cap, 0, sizeof(video_capability) ); - if ( ioctl( vid_fd, VIDIOCGCAP, &vid_cap ) < 0 ) - { - Error( "Failed to get video capabilities: %s", strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to get video capabilities %s: %s\n", queryDevice, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - if ( verbose ) - { - sprintf( output+strlen(output), "Video Capabilities\n" ); - sprintf( output+strlen(output), " Name: %s\n", vid_cap.name ); - sprintf( output+strlen(output), " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.type, - vid_cap.type&VID_TYPE_CAPTURE?" Can capture\n":"", - vid_cap.type&VID_TYPE_TUNER?" Can tune\n":"", - vid_cap.type&VID_TYPE_TELETEXT?" Does teletext\n":"", - vid_cap.type&VID_TYPE_OVERLAY?" Overlay onto frame buffer\n":"", - vid_cap.type&VID_TYPE_CHROMAKEY?" Overlay by chromakey\n":"", - vid_cap.type&VID_TYPE_CLIPPING?" Can clip\n":"", - vid_cap.type&VID_TYPE_FRAMERAM?" Uses the frame buffer memory\n":"", - vid_cap.type&VID_TYPE_SCALES?" Scalable\n":"", - vid_cap.type&VID_TYPE_MONOCHROME?" Monochrome only\n":"", - vid_cap.type&VID_TYPE_SUBCAPTURE?" Can capture subareas of the image\n":"", - vid_cap.type&VID_TYPE_MPEG_DECODER?" Can decode MPEG streams\n":"", - vid_cap.type&VID_TYPE_MPEG_ENCODER?" Can encode MPEG streams\n":"", - vid_cap.type&VID_TYPE_MJPEG_DECODER?" Can decode MJPEG streams\n":"", - vid_cap.type&VID_TYPE_MJPEG_ENCODER?" Can encode MJPEG streams\n":"" - ); - sprintf( output+strlen(output), " Video Channels: %d\n", vid_cap.channels ); - sprintf( output+strlen(output), " Audio Channels: %d\n", vid_cap.audios ); - sprintf( output+strlen(output), " Maximum Width: %d\n", vid_cap.maxwidth ); - sprintf( output+strlen(output), " Maximum Height: %d\n", vid_cap.maxheight ); - sprintf( output+strlen(output), " Minimum Width: %d\n", vid_cap.minwidth ); - sprintf( output+strlen(output), " Minimum Height: %d\n", vid_cap.minheight ); - } - else - { - sprintf( output+strlen(output), "N:%s|", vid_cap.name ); - sprintf( output+strlen(output), "T:%d|", vid_cap.type ); - sprintf( output+strlen(output), "nC:%d|", vid_cap.channels ); - sprintf( output+strlen(output), "nA:%d|", vid_cap.audios ); - sprintf( output+strlen(output), "mxW:%d|", vid_cap.maxwidth ); - sprintf( output+strlen(output), "mxH:%d|", vid_cap.maxheight ); - sprintf( output+strlen(output), "mnW:%d|", vid_cap.minwidth ); - sprintf( output+strlen(output), "mnH:%d|", vid_cap.minheight ); - } - - struct video_window vid_win; - memset( &vid_win, 0, sizeof(video_window) ); - if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win ) < 0 ) - { - Error( "Failed to get window attributes: %s", strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to get window attributes: %s\n", strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - if ( verbose ) - { - sprintf( output+strlen(output), "Window Attributes\n" ); - sprintf( output+strlen(output), " X Offset: %d\n", vid_win.x ); - sprintf( output+strlen(output), " Y Offset: %d\n", vid_win.y ); - sprintf( output+strlen(output), " Width: %d\n", vid_win.width ); - sprintf( output+strlen(output), " Height: %d\n", vid_win.height ); - } - else - { - sprintf( output+strlen(output), "X:%d|", vid_win.x ); - sprintf( output+strlen(output), "Y:%d|", vid_win.y ); - sprintf( output+strlen(output), "W:%d|", vid_win.width ); - sprintf( output+strlen(output), "H:%d|", vid_win.height ); - } - - struct video_picture vid_pic; - memset( &vid_cap, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic ) < 0 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to get picture attributes: %s\n", strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - if ( verbose ) - { - sprintf( output+strlen(output), "Picture Attributes\n" ); - sprintf( output+strlen(output), " Palette: %d - %s\n", vid_pic.palette, - vid_pic.palette==VIDEO_PALETTE_GREY?"Linear greyscale":( - vid_pic.palette==VIDEO_PALETTE_HI240?"High 240 cube (BT848)":( - vid_pic.palette==VIDEO_PALETTE_RGB565?"565 16 bit RGB":( - vid_pic.palette==VIDEO_PALETTE_RGB24?"24bit RGB":( - vid_pic.palette==VIDEO_PALETTE_RGB32?"32bit RGB":( - vid_pic.palette==VIDEO_PALETTE_RGB555?"555 15bit RGB":( - vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422 capture":( - vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( - vid_pic.palette==VIDEO_PALETTE_UYVY?"UVYV":( - vid_pic.palette==VIDEO_PALETTE_YUV420?"YUV420":( - vid_pic.palette==VIDEO_PALETTE_YUV411?"YUV411 capture":( - vid_pic.palette==VIDEO_PALETTE_RAW?"RAW capture (BT848)":( - vid_pic.palette==VIDEO_PALETTE_YUYV?"YUYV":( - vid_pic.palette==VIDEO_PALETTE_YUV422?"YUV422":( - vid_pic.palette==VIDEO_PALETTE_YUV422P?"YUV 4:2:2 Planar":( - vid_pic.palette==VIDEO_PALETTE_YUV411P?"YUV 4:1:1 Planar":( - vid_pic.palette==VIDEO_PALETTE_YUV420P?"YUV 4:2:0 Planar":( - vid_pic.palette==VIDEO_PALETTE_YUV410P?"YUV 4:1:0 Planar":"Unknown" - )))))))))))))))))); - sprintf( output+strlen(output), " Colour Depth: %d\n", vid_pic.depth ); - sprintf( output+strlen(output), " Brightness: %d\n", vid_pic.brightness ); - sprintf( output+strlen(output), " Hue: %d\n", vid_pic.hue ); - sprintf( output+strlen(output), " Colour :%d\n", vid_pic.colour ); - sprintf( output+strlen(output), " Contrast: %d\n", vid_pic.contrast ); - sprintf( output+strlen(output), " Whiteness: %d\n", vid_pic.whiteness ); - } - else - { - sprintf( output+strlen(output), "P:%d|", vid_pic.palette ); - sprintf( output+strlen(output), "D:%d|", vid_pic.depth ); - sprintf( output+strlen(output), "B:%d|", vid_pic.brightness ); - sprintf( output+strlen(output), "h:%d|", vid_pic.hue ); - sprintf( output+strlen(output), "Cl:%d|", vid_pic.colour ); - sprintf( output+strlen(output), "Cn:%d|", vid_pic.contrast ); - sprintf( output+strlen(output), "w:%d|", vid_pic.whiteness ); - } - - for ( int chan = 0; chan < vid_cap.channels; chan++ ) - { - struct video_channel vid_src; - memset( &vid_src, 0, sizeof(video_channel) ); - vid_src.channel = chan; - if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src ) < 0 ) - { - Error( "Failed to get channel %d attributes: %s", chan, strerror(errno) ); - if ( verbose ) - sprintf( output, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno) ); - else - sprintf( output, "error%d\n", errno ); - return( false ); - } - if ( verbose ) - { - sprintf( output+strlen(output), "Channel %d Attributes\n", chan ); - sprintf( output+strlen(output), " Name: %s\n", vid_src.name ); - sprintf( output+strlen(output), " Channel: %d\n", vid_src.channel ); - sprintf( output+strlen(output), " Flags: %d\n%s%s", vid_src.flags, - vid_src.flags&VIDEO_VC_TUNER?" Channel has a tuner\n":"", - vid_src.flags&VIDEO_VC_AUDIO?" Channel has audio\n":"" - ); - sprintf( output+strlen(output), " Type: %d - %s\n", vid_src.type, - vid_src.type==VIDEO_TYPE_TV?"TV":( - vid_src.type==VIDEO_TYPE_CAMERA?"Camera":"Unknown" - )); - sprintf( output+strlen(output), " Format: %d - %s\n", vid_src.norm, - vid_src.norm==VIDEO_MODE_PAL?"PAL":( - vid_src.norm==VIDEO_MODE_NTSC?"NTSC":( - vid_src.norm==VIDEO_MODE_SECAM?"SECAM":( - vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown" - )))); - } - else - { - sprintf( output+strlen(output), "n%d:%s|", chan, vid_src.name ); - sprintf( output+strlen(output), "C%d:%d|", chan, vid_src.channel ); - sprintf( output+strlen(output), "Fl%d:%x|", chan, vid_src.flags ); - sprintf( output+strlen(output), "T%d:%d|", chan, vid_src.type ); - sprintf( output+strlen(output), "F%d:%d%s|", chan, vid_src.norm, chan==(vid_cap.channels-1)?"":"," ); - } - } - if ( !verbose ) - output[strlen(output)-1] = '\n'; + Error( "Failed to get channel %d attributes: %s", chan, strerror(errno) ); + if ( verbose ) + sprintf( output, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno) ); + else + sprintf( output, "error%d\n", errno ); + return( false ); } -#endif // ZM_HAS_V4L1 - close( vid_fd ); - if ( device ) - break; + if ( verbose ) + { + sprintf( output+strlen(output), "Channel %d Attributes\n", chan ); + sprintf( output+strlen(output), " Name: %s\n", vid_src.name ); + sprintf( output+strlen(output), " Channel: %d\n", vid_src.channel ); + sprintf( output+strlen(output), " Flags: %d\n%s%s", vid_src.flags, + vid_src.flags&VIDEO_VC_TUNER?" Channel has a tuner\n":"", + vid_src.flags&VIDEO_VC_AUDIO?" Channel has audio\n":"" + ); + sprintf( output+strlen(output), " Type: %d - %s\n", vid_src.type, + vid_src.type==VIDEO_TYPE_TV?"TV":( + vid_src.type==VIDEO_TYPE_CAMERA?"Camera":"Unknown" + )); + sprintf( output+strlen(output), " Format: %d - %s\n", vid_src.norm, + vid_src.norm==VIDEO_MODE_PAL?"PAL":( + vid_src.norm==VIDEO_MODE_NTSC?"NTSC":( + vid_src.norm==VIDEO_MODE_SECAM?"SECAM":( + vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown" + )))); + } + else + { + sprintf( output+strlen(output), "n%d:%s|", chan, vid_src.name ); + sprintf( output+strlen(output), "C%d:%d|", chan, vid_src.channel ); + sprintf( output+strlen(output), "Fl%d:%x|", chan, vid_src.flags ); + sprintf( output+strlen(output), "T%d:%d|", chan, vid_src.type ); + sprintf( output+strlen(output), "F%d:%d%s|", chan, vid_src.norm, chan==(vid_cap.channels-1)?"":"," ); + } + } + if ( !verbose ) + output[strlen(output)-1] = '\n'; } - while ( ++devIndex < 32 ); - return( true ); +#endif // ZM_HAS_V4L1 + close( vid_fd ); + if ( device ) + break; + } + while ( ++devIndex < 32 ); + return( true ); } int LocalCamera::Brightness( int p_brightness ) { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) + if ( v4l_version == 2 ) + { + struct v4l2_control vid_control; + + memset( &vid_control, 0, sizeof(vid_control) ); + vid_control.id = V4L2_CID_BRIGHTNESS; + + if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) { - struct v4l2_control vid_control; - - memset( &vid_control, 0, sizeof(vid_control) ); - vid_control.id = V4L2_CID_BRIGHTNESS; - - if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) - { - if ( errno != EINVAL ) - Error( "Unable to query brightness: %s", strerror(errno) ) - else - Warning( "Brightness control is not supported" ) - //Info( "Brightness 1 %d", vid_control.value ); - } - else if ( p_brightness >= 0 ) - { - vid_control.value = p_brightness; - - //Info( "Brightness 2 %d", vid_control.value ); - /* The driver may clamp the value or return ERANGE, ignored here */ - if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) - { - if ( errno != ERANGE ) - Error( "Unable to set brightness: %s", strerror(errno) ) - else - Warning( "Given brightness value (%d) may be out-of-range", p_brightness ) - } - //Info( "Brightness 3 %d", vid_control.value ); - } - return( vid_control.value ); + if ( errno != EINVAL ) + Error( "Unable to query brightness: %s", strerror(errno) ) + else + Warning( "Brightness control is not supported" ) + //Info( "Brightness 1 %d", vid_control.value ); } + else if ( p_brightness >= 0 ) + { + vid_control.value = p_brightness; + + //Info( "Brightness 2 %d", vid_control.value ); + /* The driver may clamp the value or return ERANGE, ignored here */ + if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) + { + if ( errno != ERANGE ) + Error( "Unable to set brightness: %s", strerror(errno) ) + else + Warning( "Given brightness value (%d) may be out-of-range", p_brightness ) + } + //Info( "Brightness 3 %d", vid_control.value ); + } + return( vid_control.value ); + } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) + if ( v4l_version == 1 ) + { + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); - } - - if ( p_brightness >= 0 ) - { - vid_pic.brightness = p_brightness; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - return( -1 ); - } - } - return( vid_pic.brightness ); + Error( "Failed to get picture attributes: %s", strerror(errno) ); + return( -1 ); } + + if ( p_brightness >= 0 ) + { + vid_pic.brightness = p_brightness; + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + return( -1 ); + } + } + return( vid_pic.brightness ); + } #endif // ZM_HAS_V4L1 - return( -1 ); + return( -1 ); } int LocalCamera::Hue( int p_hue ) { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) + if ( v4l_version == 2 ) + { + struct v4l2_control vid_control; + + memset( &vid_control, 0, sizeof(vid_control) ); + vid_control.id = V4L2_CID_HUE; + + if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) { - struct v4l2_control vid_control; - - memset( &vid_control, 0, sizeof(vid_control) ); - vid_control.id = V4L2_CID_HUE; - - if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) - { - if ( errno != EINVAL ) - Error( "Unable to query hue: %s", strerror(errno) ) - else - Warning( "Hue control is not supported" ) - } - else if ( p_hue >= 0 ) - { - vid_control.value = p_hue; - - /* The driver may clamp the value or return ERANGE, ignored here */ - if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) - { - if ( errno != ERANGE ) - Error( "Unable to set hue: %s", strerror(errno) ) - else - Warning( "Given hue value (%d) may be out-of-range", p_hue ) - } - } - return( vid_control.value ); + if ( errno != EINVAL ) + Error( "Unable to query hue: %s", strerror(errno) ) + else + Warning( "Hue control is not supported" ) } + else if ( p_hue >= 0 ) + { + vid_control.value = p_hue; + + /* The driver may clamp the value or return ERANGE, ignored here */ + if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) + { + if ( errno != ERANGE ) + Error( "Unable to set hue: %s", strerror(errno) ) + else + Warning( "Given hue value (%d) may be out-of-range", p_hue ) + } + } + return( vid_control.value ); + } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) + if ( v4l_version == 1 ) + { + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); - } - - if ( p_hue >= 0 ) - { - vid_pic.hue = p_hue; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - return( -1 ); - } - } - return( vid_pic.hue ); + Error( "Failed to get picture attributes: %s", strerror(errno) ); + return( -1 ); } + + if ( p_hue >= 0 ) + { + vid_pic.hue = p_hue; + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + return( -1 ); + } + } + return( vid_pic.hue ); + } #endif // ZM_HAS_V4L1 - return( -1 ); + return( -1 ); } int LocalCamera::Colour( int p_colour ) { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) + if ( v4l_version == 2 ) + { + struct v4l2_control vid_control; + + memset( &vid_control, 0, sizeof(vid_control) ); + vid_control.id = V4L2_CID_SATURATION; + + if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) { - struct v4l2_control vid_control; - - memset( &vid_control, 0, sizeof(vid_control) ); - vid_control.id = V4L2_CID_SATURATION; - - if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) - { - if ( errno != EINVAL ) - Error( "Unable to query saturation: %s", strerror(errno) ) - else - Warning( "Saturation control is not supported" ) - } - else if ( p_colour >= 0 ) - { - vid_control.value = p_colour; - - /* The driver may clamp the value or return ERANGE, ignored here */ - if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) - { - if ( errno != ERANGE ) - Error( "Unable to set saturation: %s", strerror(errno) ) - else - Warning( "Given saturation value (%d) may be out-of-range", p_colour ) - } - } - return( vid_control.value ); + if ( errno != EINVAL ) + Error( "Unable to query saturation: %s", strerror(errno) ) + else + Warning( "Saturation control is not supported" ) } + else if ( p_colour >= 0 ) + { + vid_control.value = p_colour; + + /* The driver may clamp the value or return ERANGE, ignored here */ + if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) < 0 ) + { + if ( errno != ERANGE ) + Error( "Unable to set saturation: %s", strerror(errno) ) + else + Warning( "Given saturation value (%d) may be out-of-range", p_colour ) + } + } + return( vid_control.value ); + } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) + if ( v4l_version == 1 ) + { + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); - } - - if ( p_colour >= 0 ) - { - vid_pic.colour = p_colour; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - return( -1 ); - } - } - return( vid_pic.colour ); + Error( "Failed to get picture attributes: %s", strerror(errno) ); + return( -1 ); } + + if ( p_colour >= 0 ) + { + vid_pic.colour = p_colour; + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + return( -1 ); + } + } + return( vid_pic.colour ); + } #endif // ZM_HAS_V4L1 - return( -1 ); + return( -1 ); } int LocalCamera::Contrast( int p_contrast ) { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) + if ( v4l_version == 2 ) + { + struct v4l2_control vid_control; + + memset( &vid_control, 0, sizeof(vid_control) ); + vid_control.id = V4L2_CID_CONTRAST; + + if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) { - struct v4l2_control vid_control; - - memset( &vid_control, 0, sizeof(vid_control) ); - vid_control.id = V4L2_CID_CONTRAST; - - if ( vidioctl( vid_fd, VIDIOC_G_CTRL, &vid_control ) < 0 ) - { - if ( errno != EINVAL ) - Error( "Unable to query contrast: %s", strerror(errno) ) - else - Warning( "Contrast control is not supported" ) - } - else if ( p_contrast >= 0 ) - { - vid_control.value = p_contrast; - - /* The driver may clamp the value or return ERANGE, ignored here */ - if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) - { - if ( errno != ERANGE ) - Error( "Unable to set contrast: %s", strerror(errno) ) - else - Warning( "Given contrast value (%d) may be out-of-range", p_contrast ) - } - } - return( vid_control.value ); + if ( errno != EINVAL ) + Error( "Unable to query contrast: %s", strerror(errno) ) + else + Warning( "Contrast control is not supported" ) } + else if ( p_contrast >= 0 ) + { + vid_control.value = p_contrast; + + /* The driver may clamp the value or return ERANGE, ignored here */ + if ( vidioctl ( vid_fd, VIDIOC_S_CTRL, &vid_control ) ) + { + if ( errno != ERANGE ) + Error( "Unable to set contrast: %s", strerror(errno) ) + else + Warning( "Given contrast value (%d) may be out-of-range", p_contrast ) + } + } + return( vid_control.value ); + } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) + if ( v4l_version == 1 ) + { + struct video_picture vid_pic; + memset( &vid_pic, 0, sizeof(video_picture) ); + if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) { - struct video_picture vid_pic; - memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); - } - - if ( p_contrast >= 0 ) - { - vid_pic.contrast = p_contrast; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); - return( -1 ); - } - } - return( vid_pic.contrast ); + Error( "Failed to get picture attributes: %s", strerror(errno) ); + return( -1 ); } + + if ( p_contrast >= 0 ) + { + vid_pic.contrast = p_contrast; + if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) + { + Error( "Failed to set picture attributes: %s", strerror(errno) ); + return( -1 ); + } + } + return( vid_pic.contrast ); + } #endif // ZM_HAS_V4L1 - return( -1 ); + return( -1 ); } int LocalCamera::PrimeCapture() { - Initialise(); + Initialise(); - Debug( 2, "Priming capture" ); + Debug( 2, "Priming capture" ); +#if ZM_HAS_V4L2 + if ( v4l_version == 2 ) + { + Debug( 3, "Queueing buffers" ); + for ( unsigned int frame = 0; frame < v4l2_data.reqbufs.count; frame++ ) + { + struct v4l2_buffer vid_buf; + + memset( &vid_buf, 0, sizeof(vid_buf) ); + + vid_buf.type = v4l2_data.fmt.type; + vid_buf.memory = v4l2_data.reqbufs.memory; + vid_buf.index = frame; + + if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) + Fatal( "Failed to queue buffer %d: %s", frame, strerror(errno) ); + } + v4l2_data.bufptr = NULL; + + Debug( 3, "Starting video stream" ); + //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + //enum v4l2_buf_type type = v4l2_data.fmt.type; + enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; + if ( vidioctl( vid_fd, VIDIOC_STREAMON, &type ) < 0 ) + Fatal( "Failed to start capture stream: %s", strerror(errno) ); + } +#endif // ZM_HAS_V4L2 +#if ZM_HAS_V4L1 + if ( v4l_version == 1 ) + { + for ( int frame = 0; frame < v4l1_data.frames.frames; frame++ ) + { + Debug( 3, "Queueing frame %d", frame ); + if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[frame] ) < 0 ) + { + Error( "Capture failure for frame %d: %s", frame, strerror(errno) ); + return( -1 ); + } + } + } +#endif // ZM_HAS_V4L1 + + return( 0 ); +} + +int LocalCamera::PreCapture() +{ + Debug( 2, "Pre-capturing" ); + return( 0 ); +} + +int LocalCamera::Capture( Image &image ) +{ + Debug( 3, "Capturing" ); + static uint8_t* buffer = NULL; + static uint8_t* directbuffer = NULL; + static int capture_frame = -1; + int buffer_bytesused = 0; + + int captures_per_frame = 1; + if ( channel_count > 1 ) + captures_per_frame = v4l_captures_per_frame; + if ( captures_per_frame <= 0 ) { + captures_per_frame = 1; + Warning( "Invalid Captures Per Frame setting: %d", captures_per_frame ); + } + + + // Do the capture, unless we are the second or subsequent camera on a channel, in which case just reuse the buffer + if ( channel_prime ) + { #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { - Debug( 3, "Queueing buffers" ); - for ( unsigned int frame = 0; frame < v4l2_data.reqbufs.count; frame++ ) + static struct v4l2_buffer vid_buf; + + memset( &vid_buf, 0, sizeof(vid_buf) ); + + vid_buf.type = v4l2_data.fmt.type; + //vid_buf.memory = V4L2_MEMORY_MMAP; + vid_buf.memory = v4l2_data.reqbufs.memory; + + Debug( 3, "Capturing %d frames", captures_per_frame ); + while ( captures_per_frame ) + { + if ( vidioctl( vid_fd, VIDIOC_DQBUF, &vid_buf ) < 0 ) { - struct v4l2_buffer vid_buf; - - memset( &vid_buf, 0, sizeof(vid_buf) ); - - vid_buf.type = v4l2_data.fmt.type; - vid_buf.memory = v4l2_data.reqbufs.memory; - vid_buf.index = frame; - - if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) - Fatal( "Failed to queue buffer %d: %s", frame, strerror(errno) ); + if ( errno == EIO ) + Warning( "Capture failure, possible signal loss?: %s", strerror(errno) ) + else + Error( "Unable to capture frame %d: %s", vid_buf.index, strerror(errno) ) + return( -1 ); } - v4l2_data.bufptr = NULL; - Debug( 3, "Starting video stream" ); - //enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - //enum v4l2_buf_type type = v4l2_data.fmt.type; - enum v4l2_buf_type type = (v4l2_buf_type)v4l2_data.fmt.type; - if ( vidioctl( vid_fd, VIDIOC_STREAMON, &type ) < 0 ) - Fatal( "Failed to start capture stream: %s", strerror(errno) ); + v4l2_data.bufptr = &vid_buf; + capture_frame = v4l2_data.bufptr->index; + if ( --captures_per_frame ) + { + if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) + { + Error( "Unable to requeue buffer %d: %s", vid_buf.index, strerror(errno) ); + return( -1 ); + } + } + } + + Debug( 3, "Captured frame %d/%d from channel %d", capture_frame, v4l2_data.bufptr->sequence, channel ); + + buffer = (unsigned char *)v4l2_data.buffers[v4l2_data.bufptr->index].start; + buffer_bytesused = v4l2_data.bufptr->bytesused; + + if((v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) != (width * height)) { + Fatal("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d",v4l2_data.fmt.fmt.pix.width,v4l2_data.fmt.fmt.pix.height,width,height); + } + } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { - for ( int frame = 0; frame < v4l1_data.frames.frames; frame++ ) + Debug( 3, "Capturing %d frames", captures_per_frame ); + while ( captures_per_frame ) + { + Debug( 3, "Syncing frame %d", v4l1_data.active_frame ); + if ( ioctl( vid_fd, VIDIOCSYNC, &v4l1_data.active_frame ) < 0 ) { - Debug( 3, "Queueing frame %d", frame ); - if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[frame] ) < 0 ) - { - Error( "Capture failure for frame %d: %s", frame, strerror(errno) ); - return( -1 ); - } + Error( "Sync failure for frame %d buffer %d: %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); + return( -1 ); } + captures_per_frame--; + if ( captures_per_frame ) + { + Debug( 3, "Capturing frame %d", v4l1_data.active_frame ); + if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) + { + Error( "Capture failure for buffer %d (%d): %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); + return( -1 ); + } + } + } + capture_frame = v4l1_data.active_frame; + Debug( 3, "Captured %d for channel %d", capture_frame, channel ); + + buffer = v4l1_data.bufptr+v4l1_data.frames.offsets[capture_frame]; } #endif // ZM_HAS_V4L1 - - return( 0 ); -} - -int LocalCamera::PreCapture() -{ - Debug( 2, "Pre-capturing" ); - return( 0 ); -} - -int LocalCamera::Capture( Image &image ) -{ - Debug( 3, "Capturing" ); - static uint8_t* buffer = NULL; - static uint8_t* directbuffer = NULL; - static int capture_frame = -1; - int buffer_bytesused = 0; - - int captures_per_frame = 1; - if ( channel_count > 1 ) - captures_per_frame = v4l_captures_per_frame; - if ( captures_per_frame <= 0 ) { - captures_per_frame = 1; - Warning( "Invalid Captures Per Frame setting: %d", captures_per_frame ); - } - - // Do the capture, unless we are the second or subsequent camera on a channel, in which case just reuse the buffer - if ( channel_prime ) - { -#if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - static struct v4l2_buffer vid_buf; - - memset( &vid_buf, 0, sizeof(vid_buf) ); - - vid_buf.type = v4l2_data.fmt.type; - //vid_buf.memory = V4L2_MEMORY_MMAP; - vid_buf.memory = v4l2_data.reqbufs.memory; - - Debug( 3, "Capturing %d frames", captures_per_frame ); - while ( captures_per_frame ) - { - if ( vidioctl( vid_fd, VIDIOC_DQBUF, &vid_buf ) < 0 ) - { - if ( errno == EIO ) - Warning( "Capture failure, possible signal loss?: %s", strerror(errno) ) - else - Error( "Unable to capture frame %d: %s", vid_buf.index, strerror(errno) ) - return( -1 ); - } - - v4l2_data.bufptr = &vid_buf; - capture_frame = v4l2_data.bufptr->index; - if ( --captures_per_frame ) - { - if ( vidioctl( vid_fd, VIDIOC_QBUF, &vid_buf ) < 0 ) - { - Error( "Unable to requeue buffer %d: %s", vid_buf.index, strerror(errno) ); - return( -1 ); - } - } - } - - Debug( 3, "Captured frame %d/%d from channel %d", capture_frame, v4l2_data.bufptr->sequence, channel ); - - buffer = (unsigned char *)v4l2_data.buffers[v4l2_data.bufptr->index].start; - buffer_bytesused = v4l2_data.bufptr->bytesused; - - if((v4l2_data.fmt.fmt.pix.width * v4l2_data.fmt.fmt.pix.height) != (width * height)) { - Fatal("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d",v4l2_data.fmt.fmt.pix.width,v4l2_data.fmt.fmt.pix.height,width,height); - } - - } -#endif // ZM_HAS_V4L2 -#if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - Debug( 3, "Capturing %d frames", captures_per_frame ); - while ( captures_per_frame ) - { - Debug( 3, "Syncing frame %d", v4l1_data.active_frame ); - if ( ioctl( vid_fd, VIDIOCSYNC, &v4l1_data.active_frame ) < 0 ) - { - Error( "Sync failure for frame %d buffer %d: %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); - return( -1 ); - } - captures_per_frame--; - if ( captures_per_frame ) - { - Debug( 3, "Capturing frame %d", v4l1_data.active_frame ); - if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) - { - Error( "Capture failure for buffer %d (%d): %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); - return( -1 ); - } - } - } - capture_frame = v4l1_data.active_frame; - Debug( 3, "Captured %d for channel %d", capture_frame, channel ); - - buffer = v4l1_data.bufptr+v4l1_data.frames.offsets[capture_frame]; - } -#endif // ZM_HAS_V4L1 - } /* prime capture */ + } /* prime capture */ + + if(conversion_type != 0) { - if(conversion_type != 0) { - - Debug( 3, "Performing format conversion" ); - - /* Request a writeable buffer of the target image */ - directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); - if(directbuffer == NULL) { - Error("Failed requesting writeable buffer for the captured image."); - return (-1); - } + Debug( 3, "Performing format conversion" ); + + /* Request a writeable buffer of the target image */ + directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); + if(directbuffer == NULL) { + Error("Failed requesting writeable buffer for the captured image."); + return (-1); + } #if HAVE_LIBSWSCALE - if(conversion_type == 1) { - - Debug( 9, "Calling sws_scale to perform the conversion" ); - /* Use swscale to convert the image directly into the shared memory */ - avpicture_fill( (AVPicture *)tmpPicture, directbuffer, imagePixFormat, width, height ); - sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize ); - } -#endif - if(conversion_type == 2) { - - Debug( 9, "Calling the conversion function" ); - /* Call the image conversion function and convert directly into the shared memory */ - (*conversion_fptr)(buffer, directbuffer, pixels); - } - else if(conversion_type == 3) { - - Debug( 9, "Decoding the JPEG image" ); - /* JPEG decoding */ - image.DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder); - } - - } else { - Debug( 3, "No format conversion performed. Assigning the image" ); - - /* No conversion was performed, the image is in the V4L buffers and needs to be copied into the shared memory */ - image.Assign( width, height, colours, subpixelorder, buffer, imagesize); - - } - - return( 0 ); + if(conversion_type == 1) { + + Debug( 9, "Calling sws_scale to perform the conversion" ); + /* Use swscale to convert the image directly into the shared memory */ +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(tmpPicture->data, + tmpPicture->linesize, directbuffer, + imagePixFormat, width, height, 1); +#else + avpicture_fill( (AVPicture *)tmpPicture, directbuffer, + imagePixFormat, width, height ); +#endif + sws_scale( imgConversionContext, capturePictures[capture_frame]->data, capturePictures[capture_frame]->linesize, 0, height, tmpPicture->data, tmpPicture->linesize ); + } +#endif + if(conversion_type == 2) { + + Debug( 9, "Calling the conversion function" ); + /* Call the image conversion function and convert directly into the shared memory */ + (*conversion_fptr)(buffer, directbuffer, pixels); + } + else if(conversion_type == 3) { + + Debug( 9, "Decoding the JPEG image" ); + /* JPEG decoding */ + image.DecodeJpeg(buffer, buffer_bytesused, colours, subpixelorder); + } + + } else { + Debug( 3, "No format conversion performed. Assigning the image" ); + + /* No conversion was performed, the image is in the V4L buffers and needs to be copied into the shared memory */ + image.Assign( width, height, colours, subpixelorder, buffer, imagesize); + + } + + return( 0 ); } int LocalCamera::PostCapture() { - Debug( 2, "Post-capturing" ); - // Requeue the buffer unless we need to switch or are a duplicate camera on a channel - if ( channel_count > 1 || channel_prime ) - { + Debug( 2, "Post-capturing" ); + // Requeue the buffer unless we need to switch or are a duplicate camera on a channel + if ( channel_count > 1 || channel_prime ) + { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) + if ( v4l_version == 2 ) + { + if ( channel_count > 1 ) + { + int next_channel = (channel_index+1)%channel_count; + Debug( 3, "Switching video source to %d", channels[next_channel] ); + if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channels[next_channel] ) < 0 ) { - if ( channel_count > 1 ) - { - int next_channel = (channel_index+1)%channel_count; - Debug( 3, "Switching video source to %d", channels[next_channel] ); - if ( vidioctl( vid_fd, VIDIOC_S_INPUT, &channels[next_channel] ) < 0 ) - { - Error( "Failed to set camera source %d: %s", channels[next_channel], strerror(errno) ); - return( -1 ); - } - - v4l2_std_id stdId = standards[next_channel]; - if ( vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) - { - Error( "Failed to set video format %d: %s", standards[next_channel], strerror(errno) ); - return( -1 ); - } - } - if ( v4l2_data.bufptr ) { - Debug( 3, "Requeueing buffer %d", v4l2_data.bufptr->index ); - if ( vidioctl( vid_fd, VIDIOC_QBUF, v4l2_data.bufptr ) < 0 ) - { - Error( "Unable to requeue buffer %d: %s", v4l2_data.bufptr->index, strerror(errno) ) - return( -1 ); - } - } else { - Error( "Unable to requeue buffer due to not v4l2_data" ) - } + Error( "Failed to set camera source %d: %s", channels[next_channel], strerror(errno) ); + return( -1 ); } + + v4l2_std_id stdId = standards[next_channel]; + if ( vidioctl( vid_fd, VIDIOC_S_STD, &stdId ) < 0 ) + { + Error( "Failed to set video format %d: %s", standards[next_channel], strerror(errno) ); + return( -1 ); + } + } + if ( v4l2_data.bufptr ) { + Debug( 3, "Requeueing buffer %d", v4l2_data.bufptr->index ); + if ( vidioctl( vid_fd, VIDIOC_QBUF, v4l2_data.bufptr ) < 0 ) + { + Error( "Unable to requeue buffer %d: %s", v4l2_data.bufptr->index, strerror(errno) ) + return( -1 ); + } + } else { + Error( "Unable to requeue buffer due to not v4l2_data" ) + } + } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) + if ( v4l_version == 1 ) + { + if ( channel_count > 1 ) + { + Debug( 3, "Switching video source" ); + int next_channel = (channel_index+1)%channel_count; + struct video_channel vid_src; + memset( &vid_src, 0, sizeof(vid_src) ); + vid_src.channel = channel; + if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) { - if ( channel_count > 1 ) - { - Debug( 3, "Switching video source" ); - int next_channel = (channel_index+1)%channel_count; - struct video_channel vid_src; - memset( &vid_src, 0, sizeof(vid_src) ); - vid_src.channel = channel; - if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) - { - Error( "Failed to get camera source %d: %s", channel, strerror(errno) ); - return(-1); - } - - vid_src.channel = channels[next_channel]; - vid_src.norm = standards[next_channel]; - vid_src.flags = 0; - vid_src.type = VIDEO_TYPE_CAMERA; - if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) - { - Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); - return( -1 ); - } - } - Debug( 3, "Requeueing frame %d", v4l1_data.active_frame ); - if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) - { - Error( "Capture failure for frame %d: %s", v4l1_data.active_frame, strerror(errno) ); - return( -1 ); - } - v4l1_data.active_frame = (v4l1_data.active_frame+1)%v4l1_data.frames.frames; + Error( "Failed to get camera source %d: %s", channel, strerror(errno) ); + return(-1); } -#endif // ZM_HAS_V4L1 + + vid_src.channel = channels[next_channel]; + vid_src.norm = standards[next_channel]; + vid_src.flags = 0; + vid_src.type = VIDEO_TYPE_CAMERA; + if ( ioctl( vid_fd, VIDIOCSCHAN, &vid_src ) < 0 ) + { + Error( "Failed to set camera source %d: %s", channel, strerror(errno) ); + return( -1 ); + } + } + Debug( 3, "Requeueing frame %d", v4l1_data.active_frame ); + if ( ioctl( vid_fd, VIDIOCMCAPTURE, &v4l1_data.buffers[v4l1_data.active_frame] ) < 0 ) + { + Error( "Capture failure for frame %d: %s", v4l1_data.active_frame, strerror(errno) ); + return( -1 ); + } + v4l1_data.active_frame = (v4l1_data.active_frame+1)%v4l1_data.frames.frames; } - return( 0 ); +#endif // ZM_HAS_V4L1 + } + return( 0 ); } #endif // ZM_HAS_V4L diff --git a/src/zm_local_camera.h b/src/zm_local_camera.h index 0dbe44c21..db0a1c671 100644 --- a/src/zm_local_camera.h +++ b/src/zm_local_camera.h @@ -29,10 +29,18 @@ #ifdef HAVE_LINUX_VIDEODEV_H #include #endif // HAVE_LINUX_VIDEODEV_H +#ifdef HAVE_LIBV4L1_VIDEODEV_H +#include +#endif // HAVE_LIB4VL1_VIDEODEV_H #ifdef HAVE_LINUX_VIDEODEV2_H #include #endif // HAVE_LINUX_VIDEODEV2_H +// Required on systems with v4l1 but without v4l2 headers +#ifndef VIDEO_MAX_FRAME +#define VIDEO_MAX_FRAME 32 +#endif + #include "zm_ffmpeg.h" // @@ -44,99 +52,99 @@ class LocalCamera : public Camera { protected: #if ZM_HAS_V4L2 - struct V4L2MappedBuffer - { - void *start; - size_t length; - }; + struct V4L2MappedBuffer + { + void *start; + size_t length; + }; - struct V4L2Data - { - v4l2_cropcap cropcap; - v4l2_crop crop; - v4l2_format fmt; - v4l2_requestbuffers reqbufs; - V4L2MappedBuffer *buffers; - v4l2_buffer *bufptr; - }; + struct V4L2Data + { + v4l2_cropcap cropcap; + v4l2_crop crop; + v4l2_format fmt; + v4l2_requestbuffers reqbufs; + V4L2MappedBuffer *buffers; + v4l2_buffer *bufptr; + }; #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - struct V4L1Data - { - int active_frame; - video_mbuf frames; - video_mmap *buffers; - unsigned char *bufptr; - }; + struct V4L1Data + { + int active_frame; + video_mbuf frames; + video_mmap *buffers; + unsigned char *bufptr; + }; #endif // ZM_HAS_V4L1 protected: - std::string device; - int channel; - int standard; - int palette; - bool device_prime; - bool channel_prime; - int channel_index; - unsigned int extras; - - unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */ - convert_fptr_t conversion_fptr; /* Pointer to conversion function used */ - - uint32_t AutoSelectFormat(int p_colours); + std::string device; + int channel; + int standard; + int palette; + bool device_prime; + bool channel_prime; + int channel_index; + unsigned int extras; + + unsigned int conversion_type; /* 0 = no conversion needed, 1 = use libswscale, 2 = zm internal conversion, 3 = jpeg decoding */ + convert_fptr_t conversion_fptr; /* Pointer to conversion function used */ + + uint32_t AutoSelectFormat(int p_colours); - static int camera_count; - static int channel_count; - static int channels[VIDEO_MAX_FRAME]; - static int standards[VIDEO_MAX_FRAME]; - static int vid_fd; - static int v4l_version; - bool v4l_multi_buffer; - unsigned int v4l_captures_per_frame; + static int camera_count; + static int channel_count; + static int channels[VIDEO_MAX_FRAME]; + static int standards[VIDEO_MAX_FRAME]; + static int vid_fd; + static int v4l_version; + bool v4l_multi_buffer; + unsigned int v4l_captures_per_frame; #if ZM_HAS_V4L2 - static V4L2Data v4l2_data; + static V4L2Data v4l2_data; #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - static V4L1Data v4l1_data; + static V4L1Data v4l1_data; #endif // ZM_HAS_V4L1 #if HAVE_LIBSWSCALE - static AVFrame **capturePictures; - PixelFormat imagePixFormat; - PixelFormat capturePixFormat; - struct SwsContext *imgConversionContext; - AVFrame *tmpPicture; + static AVFrame **capturePictures; + _AVPIXELFORMAT imagePixFormat; + _AVPIXELFORMAT capturePixFormat; + struct SwsContext *imgConversionContext; + AVFrame *tmpPicture; #endif // HAVE_LIBSWSCALE - static LocalCamera *last_camera; + static LocalCamera *last_camera; public: - LocalCamera( int p_id, const std::string &device, int p_channel, int p_format, bool v4lmultibuffer, unsigned int v4lcapturesperframe, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras = 0); - ~LocalCamera(); + LocalCamera( int p_id, const std::string &device, int p_channel, int p_format, bool v4lmultibuffer, unsigned int v4lcapturesperframe, const std::string &p_method, int p_width, int p_height, int p_colours, int p_palette, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, unsigned int p_extras = 0); + ~LocalCamera(); - void Initialise(); - void Terminate(); + void Initialise(); + void Terminate(); - const std::string &Device() const { return( device ); } + const std::string &Device() const { return( device ); } - int Channel() const { return( channel ); } - int Standard() const { return( standard ); } - int Palette() const { return( palette ); } - int Extras() const { return( extras ); } + int Channel() const { return( channel ); } + int Standard() const { return( standard ); } + int Palette() const { return( palette ); } + int Extras() const { return( extras ); } - int Brightness( int p_brightness=-1 ); - int Hue( int p_hue=-1 ); - int Colour( int p_colour=-1 ); - int Contrast( int p_contrast=-1 ); + int Brightness( int p_brightness=-1 ); + int Hue( int p_hue=-1 ); + int Colour( int p_colour=-1 ); + int Contrast( int p_contrast=-1 ); - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); - static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose ); + static bool GetCurrentSettings( const char *device, char *output, int version, bool verbose ); }; #endif // ZM_HAS_V4L diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index 19886ef1c..4d66deee2 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -31,6 +31,10 @@ #include #include #include +#ifdef __FreeBSD__ +#include +#include +#endif bool Logger::smInitialised = false; Logger *Logger::smInstance = 0; @@ -41,568 +45,599 @@ Logger::IntMap Logger::smSyslogPriorities; #if 0 static void subtractTime( struct timeval * const tp1, struct timeval * const tp2 ) { - tp1->tv_sec -= tp2->tv_sec; - if ( tp1->tv_usec <= tp2->tv_usec ) - { - tp1->tv_sec--; - tp1->tv_usec = 1000000 - (tp2->tv_usec - tp1->tv_usec); - } - else - { - tp1->tv_usec = tp1->tv_usec - tp2->tv_usec; - } + tp1->tv_sec -= tp2->tv_sec; + if ( tp1->tv_usec <= tp2->tv_usec ) + { + tp1->tv_sec--; + tp1->tv_usec = 1000000 - (tp2->tv_usec - tp1->tv_usec); + } + else + { + tp1->tv_usec = tp1->tv_usec - tp2->tv_usec; + } } #endif void Logger::usrHandler( int sig ) { - Logger *logger = fetch(); - if ( sig == SIGUSR1 ) - logger->level( logger->level()+1 ); - else if ( sig == SIGUSR2 ) - logger->level( logger->level()-1 ); - Info( "Logger - Level changed to %d", logger->level() ); + Logger *logger = fetch(); + if ( sig == SIGUSR1 ) + logger->level( logger->level()+1 ); + else if ( sig == SIGUSR2 ) + logger->level( logger->level()-1 ); + Info( "Logger - Level changed to %d", logger->level() ); } Logger::Logger() : - mLevel( INFO ), - mTermLevel( NOLOG ), - mDatabaseLevel( NOLOG ), - mFileLevel( NOLOG ), - mSyslogLevel( NOLOG ), - mEffectiveLevel( NOLOG ), - //mLogPath( config.path_logs ), - //mLogFile( mLogPath+"/"+mId+".log" ), - mDbConnected( false ), - mLogFileFP( NULL ), - mHasTerm( false ), - mFlush( false ) + mLevel( INFO ), + mTermLevel( NOLOG ), + mDatabaseLevel( NOLOG ), + mFileLevel( NOLOG ), + mSyslogLevel( NOLOG ), + mEffectiveLevel( NOLOG ), + //mLogPath( config.path_logs ), + //mLogFile( mLogPath+"/"+mId+".log" ), + mDbConnected( false ), + mLogFileFP( NULL ), + mHasTerm( false ), + mFlush( false ) { - if ( smInstance ) + if ( smInstance ) + { + Panic( "Attempt to create second instance of Logger class" ); + } + + if ( !smInitialised ) + { + smCodes[INFO] = "INF"; + smCodes[WARNING] = "WAR"; + smCodes[ERROR] = "ERR"; + smCodes[FATAL] = "FAT"; + smCodes[PANIC] = "PNC"; + smCodes[NOLOG] = "OFF"; + + smSyslogPriorities[INFO] = LOG_INFO; + smSyslogPriorities[WARNING] = LOG_WARNING; + smSyslogPriorities[ERROR] = LOG_ERR; + smSyslogPriorities[FATAL] = LOG_ERR; + smSyslogPriorities[PANIC] = LOG_ERR; + + char code[4] = ""; + for ( int i = DEBUG1; i <= DEBUG9; i++ ) { - Panic( "Attempt to create second instance of Logger class" ); + snprintf( code, sizeof(code), "DB%d", i ); + smCodes[i] = code; + smSyslogPriorities[i] = LOG_DEBUG; } - if ( !smInitialised ) - { - smCodes[INFO] = "INF"; - smCodes[WARNING] = "WAR"; - smCodes[ERROR] = "ERR"; - smCodes[FATAL] = "FAT"; - smCodes[PANIC] = "PNC"; - smCodes[NOLOG] = "OFF"; + smInitialised = true; + } - smSyslogPriorities[INFO] = LOG_INFO; - smSyslogPriorities[WARNING] = LOG_WARNING; - smSyslogPriorities[ERROR] = LOG_ERR; - smSyslogPriorities[FATAL] = LOG_ERR; - smSyslogPriorities[PANIC] = LOG_ERR; - - char code[4] = ""; - for ( int i = DEBUG1; i <= DEBUG9; i++ ) - { - snprintf( code, sizeof(code), "DB%d", i ); - smCodes[i] = code; - smSyslogPriorities[i] = LOG_DEBUG; - } - - smInitialised = true; - } - - if ( fileno(stderr) && isatty(fileno(stderr)) ) - mHasTerm = true; + if ( fileno(stderr) && isatty(fileno(stderr)) ) + mHasTerm = true; } Logger::~Logger() { - terminate(); + terminate(); } void Logger::initialise( const std::string &id, const Options &options ) { - char *envPtr; + char *envPtr; - if ( !id.empty() ) - this->id( id ); + if ( !id.empty() ) + this->id( id ); - std::string tempLogFile; - if ( options.mLogPath.size() ) + std::string tempLogFile; + if ( options.mLogPath.size() ) + { + mLogPath = options.mLogPath; + tempLogFile = mLogPath+"/"+mId+".log"; + } + if ( options.mLogFile.size() ) + tempLogFile = options.mLogFile; + else + tempLogFile = mLogPath+"/"+mId+".log"; + if ( (envPtr = getTargettedEnv( "LOG_FILE" )) ) + tempLogFile = envPtr; + + Level tempLevel = INFO; + Level tempTermLevel = mTermLevel; + Level tempDatabaseLevel = mDatabaseLevel; + Level tempFileLevel = mFileLevel; + Level tempSyslogLevel = mSyslogLevel; + + if ( options.mTermLevel != NOOPT ) + tempTermLevel = options.mTermLevel; + if ( options.mDatabaseLevel != NOOPT ) + tempDatabaseLevel = options.mDatabaseLevel; + else + tempDatabaseLevel = config.log_level_database >= DEBUG1 ? DEBUG9 : config.log_level_database; + if ( options.mFileLevel != NOOPT ) + tempFileLevel = options.mFileLevel; + else + tempFileLevel = config.log_level_file >= DEBUG1 ? DEBUG9 : config.log_level_file; + if ( options.mSyslogLevel != NOOPT ) + tempSyslogLevel = options.mSyslogLevel; + else + tempSyslogLevel = config.log_level_syslog >= DEBUG1 ? DEBUG9 : config.log_level_syslog; + + // Legacy + if ( (envPtr = getenv( "LOG_PRINT" )) ) + tempTermLevel = atoi(envPtr) ? DEBUG9 : NOLOG; + + if ( (envPtr = getTargettedEnv( "LOG_LEVEL" )) ) + tempLevel = atoi(envPtr); + + if ( (envPtr = getTargettedEnv( "LOG_LEVEL_TERM" )) ) + tempTermLevel = atoi(envPtr); + if ( (envPtr = getTargettedEnv( "LOG_LEVEL_DATABASE" )) ) + tempDatabaseLevel = atoi(envPtr); + if ( (envPtr = getTargettedEnv( "LOG_LEVEL_FILE" )) ) + tempFileLevel = atoi(envPtr); + if ( (envPtr = getTargettedEnv( "LOG_LEVEL_SYSLOG" )) ) + tempSyslogLevel = atoi(envPtr); + + if ( config.log_debug ) + { + StringVector targets = split( config.log_debug_target, "|" ); + for ( unsigned int i = 0; i < targets.size(); i++ ) { - mLogPath = options.mLogPath; - tempLogFile = mLogPath+"/"+mId+".log"; + const std::string &target = targets[i]; + if ( target == mId || target == "_"+mId || target == "_"+mIdRoot || target == "_"+mIdRoot || target == "" ) + { + if ( config.log_debug_level > NOLOG ) + { + tempLevel = config.log_debug_level; + if ( config.log_debug_file[0] ) + { + tempLogFile = config.log_debug_file; + tempFileLevel = tempLevel; + } + } + } } - if ( options.mLogFile.size() ) - tempLogFile = options.mLogFile; - else - tempLogFile = mLogPath+"/"+mId+".log"; - if ( (envPtr = getTargettedEnv( "LOG_FILE" )) ) - tempLogFile = envPtr; + } - Level tempLevel = INFO; - Level tempTermLevel = mTermLevel; - Level tempDatabaseLevel = mDatabaseLevel; - Level tempFileLevel = mFileLevel; - Level tempSyslogLevel = mSyslogLevel; + logFile( tempLogFile ); - if ( options.mTermLevel != NOOPT ) - tempTermLevel = options.mTermLevel; - if ( options.mDatabaseLevel != NOOPT ) - tempDatabaseLevel = options.mDatabaseLevel; - else - tempDatabaseLevel = config.log_level_database >= DEBUG1 ? DEBUG9 : config.log_level_database; - if ( options.mFileLevel != NOOPT ) - tempFileLevel = options.mFileLevel; - else - tempFileLevel = config.log_level_file >= DEBUG1 ? DEBUG9 : config.log_level_file; - if ( options.mSyslogLevel != NOOPT ) - tempSyslogLevel = options.mSyslogLevel; - else - tempSyslogLevel = config.log_level_syslog >= DEBUG1 ? DEBUG9 : config.log_level_syslog; + termLevel( tempTermLevel ); + databaseLevel( tempDatabaseLevel ); + fileLevel( tempFileLevel ); + syslogLevel( tempSyslogLevel ); - // Legacy - if ( (envPtr = getenv( "LOG_PRINT" )) ) - tempTermLevel = atoi(envPtr) ? DEBUG9 : NOLOG; + level( tempLevel ); - if ( (envPtr = getTargettedEnv( "LOG_LEVEL" )) ) - tempLevel = atoi(envPtr); + mFlush = (envPtr = getenv( "LOG_FLUSH")) ? atoi( envPtr ) : false; - if ( (envPtr = getTargettedEnv( "LOG_LEVEL_TERM" )) ) - tempTermLevel = atoi(envPtr); - if ( (envPtr = getTargettedEnv( "LOG_LEVEL_DATABASE" )) ) - tempDatabaseLevel = atoi(envPtr); - if ( (envPtr = getTargettedEnv( "LOG_LEVEL_FILE" )) ) - tempFileLevel = atoi(envPtr); - if ( (envPtr = getTargettedEnv( "LOG_LEVEL_SYSLOG" )) ) - tempSyslogLevel = atoi(envPtr); + //mRuntime = (envPtr = getenv( "LOG_RUNTIME")) ? atoi( envPtr ) : false; - if ( config.log_debug ) + { + struct sigaction action; + memset( &action, 0, sizeof(action) ); + action.sa_handler = usrHandler; + action.sa_flags = SA_RESTART; + + if ( sigaction( SIGUSR1, &action, 0 ) < 0 ) { - StringVector targets = split( config.log_debug_target, "|" ); - for ( unsigned int i = 0; i < targets.size(); i++ ) - { - const std::string &target = targets[i]; - if ( target == mId || target == "_"+mId || target == "_"+mIdRoot || target == "_"+mIdRoot || target == "" ) - { - if ( config.log_debug_level > NOLOG ) - { - tempLevel = config.log_debug_level; - if ( config.log_debug_file[0] ) - { - tempLogFile = config.log_debug_file; - tempFileLevel = tempLevel; - } - } - } - } + Fatal( "sigaction(), error = %s", strerror(errno) ); } - - logFile( tempLogFile ); - - termLevel( tempTermLevel ); - databaseLevel( tempDatabaseLevel ); - fileLevel( tempFileLevel ); - syslogLevel( tempSyslogLevel ); - - level( tempLevel ); - - mFlush = (envPtr = getenv( "LOG_FLUSH")) ? atoi( envPtr ) : false; - - //mRuntime = (envPtr = getenv( "LOG_RUNTIME")) ? atoi( envPtr ) : false; - + if ( sigaction( SIGUSR2, &action, 0 ) < 0) { - struct sigaction action; - memset( &action, 0, sizeof(action) ); - action.sa_handler = usrHandler; - action.sa_flags = SA_RESTART; - - if ( sigaction( SIGUSR1, &action, 0 ) < 0 ) - { - Fatal( "sigaction(), error = %s", strerror(errno) ); - } - if ( sigaction( SIGUSR2, &action, 0 ) < 0) - { - Fatal( "sigaction(), error = %s", strerror(errno) ); - } + Fatal( "sigaction(), error = %s", strerror(errno) ); } + } - mInitialised = true; + mInitialised = true; - Debug( 1, "LogOpts: level=%s/%s, screen=%s, database=%s, logfile=%s->%s, syslog=%s", smCodes[mLevel].c_str(), smCodes[mEffectiveLevel].c_str(), smCodes[mTermLevel].c_str(), smCodes[mDatabaseLevel].c_str(), smCodes[mFileLevel].c_str(), mLogFile.c_str(), smCodes[mSyslogLevel].c_str() ); + Debug( 1, "LogOpts: level=%s/%s, screen=%s, database=%s, logfile=%s->%s, syslog=%s", smCodes[mLevel].c_str(), smCodes[mEffectiveLevel].c_str(), smCodes[mTermLevel].c_str(), smCodes[mDatabaseLevel].c_str(), smCodes[mFileLevel].c_str(), mLogFile.c_str(), smCodes[mSyslogLevel].c_str() ); } void Logger::terminate() { - Info( "Terminating Logger" ); + Debug(1, "Terminating Logger" ); - if ( mFileLevel > NOLOG ) - closeFile(); + if ( mFileLevel > NOLOG ) + closeFile(); - if ( mSyslogLevel > NOLOG ) - closeSyslog(); + if ( mSyslogLevel > NOLOG ) + closeSyslog(); + + if ( mDatabaseLevel > NOLOG ) + closeDatabase(); } bool Logger::boolEnv( const std::string &name, bool defaultValue ) { - const char *envPtr = getenv( name.c_str() ); - return( envPtr ? atoi( envPtr ) : defaultValue ); + const char *envPtr = getenv( name.c_str() ); + return( envPtr ? atoi( envPtr ) : defaultValue ); } int Logger::intEnv( const std::string &name, bool defaultValue ) { - const char *envPtr = getenv( name.c_str() ); - return( envPtr ? atoi( envPtr ) : defaultValue ); + const char *envPtr = getenv( name.c_str() ); + return( envPtr ? atoi( envPtr ) : defaultValue ); } std::string Logger::strEnv( const std::string &name, const std::string defaultValue ) { - const char *envPtr = getenv( name.c_str() ); - return( envPtr ? envPtr : defaultValue ); + const char *envPtr = getenv( name.c_str() ); + return( envPtr ? envPtr : defaultValue ); } char *Logger::getTargettedEnv( const std::string &name ) { - char *envPtr = NULL; - std::string envName; + char *envPtr = NULL; + std::string envName; - envName = name+"_"+mId; + envName = name+"_"+mId; + envPtr = getenv( envName.c_str() ); + if ( !envPtr && mId != mIdRoot ) + { + envName = name+"_"+mIdRoot; envPtr = getenv( envName.c_str() ); - if ( !envPtr && mId != mIdRoot ) - { - envName = name+"_"+mIdRoot; - envPtr = getenv( envName.c_str() ); - } - if ( !envPtr ) - envPtr = getenv( name.c_str() ); - return( envPtr ); + } + if ( !envPtr ) + envPtr = getenv( name.c_str() ); + return( envPtr ); } const std::string &Logger::id( const std::string &id ) { - std::string tempId = id; + std::string tempId = id; - size_t pos; - // Remove whitespace - while ( (pos = tempId.find_first_of( " \t" )) != std::string::npos ) + size_t pos; + // Remove whitespace + while ( (pos = tempId.find_first_of( " \t" )) != std::string::npos ) + { + tempId.replace( pos, 1, "" ); + } + // Replace non-alphanum with underscore + while ( (pos = tempId.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" )) != std::string::npos ) + { + tempId.replace( pos, 1, "_" ); + } + if ( mId != tempId ) + { + mId = tempId; + pos = mId.find( '_' ); + if ( pos != std::string::npos ) { - tempId.replace( pos, 1, "" ); + mIdRoot = mId.substr( 0, pos ); + if ( ++pos < mId.size() ) + mIdArgs = mId.substr( pos ); } - // Replace non-alphanum with underscore - while ( (pos = tempId.find_first_not_of( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_" )) != std::string::npos ) - { - tempId.replace( pos, 1, "_" ); - } - if ( mId != tempId ) - { - mId = tempId; - pos = mId.find( '_' ); - if ( pos != std::string::npos ) - { - mIdRoot = mId.substr( 0, pos ); - if ( ++pos < mId.size() ) - mIdArgs = mId.substr( pos ); - } - } - return( mId ); + } + return( mId ); } Logger::Level Logger::level( Logger::Level level ) { - if ( level > NOOPT ) - { - level = limit(level); - if ( mLevel != level ) - mLevel = level; + if ( level > NOOPT ) + { + level = limit(level); + if ( mLevel != level ) + mLevel = level; - mEffectiveLevel = NOLOG; - if ( mTermLevel > mEffectiveLevel ) - mEffectiveLevel = mTermLevel; - if ( mDatabaseLevel > mEffectiveLevel ) - mEffectiveLevel = mDatabaseLevel; - if ( mFileLevel > mEffectiveLevel ) - mEffectiveLevel = mFileLevel; - if ( mSyslogLevel > mEffectiveLevel ) - mEffectiveLevel = mSyslogLevel; - if ( mEffectiveLevel > mLevel) - mEffectiveLevel = mLevel; - } - return( mLevel ); + mEffectiveLevel = NOLOG; + if ( mTermLevel > mEffectiveLevel ) + mEffectiveLevel = mTermLevel; + if ( mDatabaseLevel > mEffectiveLevel ) + mEffectiveLevel = mDatabaseLevel; + if ( mFileLevel > mEffectiveLevel ) + mEffectiveLevel = mFileLevel; + if ( mSyslogLevel > mEffectiveLevel ) + mEffectiveLevel = mSyslogLevel; + if ( mEffectiveLevel > mLevel) + mEffectiveLevel = mLevel; + } + return( mLevel ); } Logger::Level Logger::termLevel( Logger::Level termLevel ) { - if ( termLevel > NOOPT ) - { - if ( !mHasTerm ) - termLevel = NOLOG; - termLevel = limit(termLevel); - if ( mTermLevel != termLevel ) - mTermLevel = termLevel; - } - return( mTermLevel ); + if ( termLevel > NOOPT ) + { + if ( !mHasTerm ) + termLevel = NOLOG; + termLevel = limit(termLevel); + if ( mTermLevel != termLevel ) + mTermLevel = termLevel; + } + return( mTermLevel ); } Logger::Level Logger::databaseLevel( Logger::Level databaseLevel ) { - if ( databaseLevel > NOOPT ) + if ( databaseLevel > NOOPT ) + { + databaseLevel = limit(databaseLevel); + if ( mDatabaseLevel != databaseLevel ) { - databaseLevel = limit(databaseLevel); - if ( mDatabaseLevel != databaseLevel ) + if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) + { + if ( !mDbConnected ) { - if ( databaseLevel > NOLOG && mDatabaseLevel <= NOLOG ) + if ( !mysql_init( &mDbConnection ) ) + { + Fatal( "Can't initialise database connection: %s", mysql_error( &mDbConnection ) ); + exit( mysql_errno( &mDbConnection ) ); + } + my_bool reconnect = 1; + if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) + Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); + std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" ); + if ( colonIndex != std::string::npos ) + { + std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); + std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 ); + if ( !mysql_real_connect( &mDbConnection, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) ) { - if ( !mDbConnected ) - { - if ( !mysql_init( &mDbConnection ) ) - { - Fatal( "Can't initialise database connection: %s", mysql_error( &mDbConnection ) ); - exit( mysql_errno( &mDbConnection ) ); - } - my_bool reconnect = 1; - if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) - Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); - std::string::size_type colonIndex = staticConfig.DB_HOST.find( ":/" ); - if ( colonIndex != std::string::npos ) - { - std::string dbHost = staticConfig.DB_HOST.substr( 0, colonIndex ); - std::string dbPort = staticConfig.DB_HOST.substr( colonIndex+1 ); - if ( !mysql_real_connect( &mDbConnection, dbHost.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, atoi(dbPort.c_str()), 0, 0 ) ) - { - Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); - exit( mysql_errno( &mDbConnection ) ); - } - } - else - { - if ( !mysql_real_connect( &mDbConnection, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) ) - { - Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); - exit( mysql_errno( &mDbConnection ) ); - } - } - unsigned long mysqlVersion = mysql_get_server_version( &mDbConnection ); - if ( mysqlVersion < 50019 ) - if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) - Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); - if ( mysql_select_db( &mDbConnection, staticConfig.DB_NAME.c_str() ) ) - { - Fatal( "Can't select database: %s", mysql_error( &mDbConnection ) ); - exit( mysql_errno( &mDbConnection ) ); - } - mDbConnected = true; - } + Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); + exit( mysql_errno( &mDbConnection ) ); } - mDatabaseLevel = databaseLevel; + } + else + { + if ( !mysql_real_connect( &mDbConnection, staticConfig.DB_HOST.c_str(), staticConfig.DB_USER.c_str(), staticConfig.DB_PASS.c_str(), 0, 0, 0, 0 ) ) + { + Fatal( "Can't connect to database: %s", mysql_error( &mDbConnection ) ); + exit( mysql_errno( &mDbConnection ) ); + } + } + unsigned long mysqlVersion = mysql_get_server_version( &mDbConnection ); + if ( mysqlVersion < 50019 ) + if ( mysql_options( &mDbConnection, MYSQL_OPT_RECONNECT, &reconnect ) ) + Fatal( "Can't set database auto reconnect option: %s", mysql_error( &mDbConnection ) ); + if ( mysql_select_db( &mDbConnection, staticConfig.DB_NAME.c_str() ) ) + { + Fatal( "Can't select database: %s", mysql_error( &mDbConnection ) ); + exit( mysql_errno( &mDbConnection ) ); + } + mDbConnected = true; } + } + mDatabaseLevel = databaseLevel; } - return( mDatabaseLevel ); + } + return( mDatabaseLevel ); } Logger::Level Logger::fileLevel( Logger::Level fileLevel ) { - if ( fileLevel > NOOPT ) + if ( fileLevel > NOOPT ) + { + fileLevel = limit(fileLevel); + if ( mFileLevel != fileLevel ) { - fileLevel = limit(fileLevel); - if ( mFileLevel != fileLevel ) - { - if ( mFileLevel > NOLOG ) - closeFile(); - mFileLevel = fileLevel; - if ( mFileLevel > NOLOG ) - openFile(); - } + if ( mFileLevel > NOLOG ) + closeFile(); + mFileLevel = fileLevel; + if ( mFileLevel > NOLOG ) + openFile(); } - return( mFileLevel ); + } + return( mFileLevel ); } Logger::Level Logger::syslogLevel( Logger::Level syslogLevel ) { - if ( syslogLevel > NOOPT ) + if ( syslogLevel > NOOPT ) + { + syslogLevel = limit(syslogLevel); + if ( mSyslogLevel != syslogLevel ) { - syslogLevel = limit(syslogLevel); - if ( mSyslogLevel != syslogLevel ) - { - if ( mSyslogLevel > NOLOG ) - closeSyslog(); - mSyslogLevel = syslogLevel; - if ( mSyslogLevel > NOLOG ) - openSyslog(); - } + if ( mSyslogLevel > NOLOG ) + closeSyslog(); + mSyslogLevel = syslogLevel; + if ( mSyslogLevel > NOLOG ) + openSyslog(); } - return( mSyslogLevel ); + } + return( mSyslogLevel ); } void Logger::logFile( const std::string &logFile ) { - bool addLogPid = false; - std::string tempLogFile = logFile; - if ( tempLogFile[tempLogFile.length()-1] == '+' ) - { - tempLogFile.resize(tempLogFile.length()-1); - addLogPid = true; - } - if ( addLogPid ) - mLogFile = stringtf( "%s.%05d", tempLogFile.c_str(), getpid() ); - else - mLogFile = tempLogFile; + bool addLogPid = false; + std::string tempLogFile = logFile; + if ( tempLogFile[tempLogFile.length()-1] == '+' ) + { + tempLogFile.resize(tempLogFile.length()-1); + addLogPid = true; + } + if ( addLogPid ) + mLogFile = stringtf( "%s.%05d", tempLogFile.c_str(), getpid() ); + else + mLogFile = tempLogFile; } void Logger::openFile() { - if ( mLogFile.size() && (mLogFileFP = fopen( mLogFile.c_str() ,"w" )) == (FILE *)NULL ) - { - mFileLevel = NOLOG; - Fatal( "fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno) ); - } + if ( mLogFile.size() && (mLogFileFP = fopen( mLogFile.c_str() ,"w" )) == (FILE *)NULL ) + { + mFileLevel = NOLOG; + Fatal( "fopen() for %s, error = %s", mLogFile.c_str(), strerror(errno) ); + } } void Logger::closeFile() { - if ( mLogFileFP ) + if ( mLogFileFP ) + { + fflush( mLogFileFP ); + if ( fclose( mLogFileFP ) < 0 ) { - fflush( mLogFileFP ); - if ( fclose( mLogFileFP ) < 0 ) - { - Fatal( "fclose(), error = %s",strerror(errno) ); - } - mLogFileFP = (FILE *)NULL; + Fatal( "fclose(), error = %s",strerror(errno) ); } + mLogFileFP = (FILE *)NULL; + } +} + +void Logger::closeDatabase() +{ + if ( mDbConnected ) + { + mysql_close( &mDbConnection ); + mDbConnected = false; + } } void Logger::openSyslog() { - (void) openlog( mId.c_str(), LOG_PID|LOG_NDELAY, LOG_LOCAL1 ); + (void) openlog( mId.c_str(), LOG_PID|LOG_NDELAY, LOG_LOCAL1 ); } void Logger::closeSyslog() { - (void) closelog(); + (void) closelog(); } -void Logger::logPrint( bool hex, const char * const file, const int line, const int level, const char *fstring, ... ) +void Logger::logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... ) { - if ( level <= mEffectiveLevel ) + if ( level <= mEffectiveLevel ) + { + char classString[4]; + char timeString[64]; + char logString[8192]; + va_list argPtr; + struct timeval timeVal; + + const char * const file = basename(filepath); + + if ( level < PANIC || level > DEBUG9 ) + Panic( "Invalid logger level %d", level ); + + strncpy( classString, smCodes[level].c_str(), sizeof(classString) ); + + gettimeofday( &timeVal, NULL ); + + #if 0 + if ( logRuntime ) { - char classString[4]; - char timeString[64]; - char logString[8192]; - va_list argPtr; - struct timeval timeVal; - - if ( level < PANIC || level > DEBUG9 ) - Panic( "Invalid logger level %d", level ); + static struct timeval logStart; - strncpy( classString, smCodes[level].c_str(), sizeof(classString) ); + subtractTime( &timeVal, &logStart ); - gettimeofday( &timeVal, NULL ); - - #if 0 - if ( logRuntime ) - { - static struct timeval logStart; - - subtractTime( &timeVal, &logStart ); - - snprintf( timeString, sizeof(timeString), "%ld.%03ld", timeVal.tv_sec, timeVal.tv_usec/1000 ); - } - else - { - #endif - char *timePtr = timeString; - timePtr += strftime( timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec) ); - snprintf( timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec ); - #if 0 - } - #endif - - pid_t tid; -#ifdef HAVE_SYSCALL - if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id -#endif // HAVE_SYSCALL - tid = getpid(); // Process id - - char *logPtr = logString; - logPtr += snprintf( logPtr, sizeof(logString), "%s %s[%d].%s-%s/%d [", - timeString, - mId.c_str(), - tid, - classString, - file, - line - ); - char *syslogStart = logPtr; - - va_start( argPtr, fstring ); - if ( hex ) - { - unsigned char *data = va_arg( argPtr, unsigned char * ); - int len = va_arg( argPtr, int ); - int i; - logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), "%d:", len ); - for ( i = 0; i < len; i++ ) - { - logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), " %02x", data[i] ); - } - } - else - { - logPtr += vsnprintf( logPtr, sizeof(logString)-(logPtr-logString), fstring, argPtr ); - } - va_end(argPtr); - char *syslogEnd = logPtr; - strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) ); - - if ( level <= mTermLevel ) - { - printf( "%s", logString ); - fflush( stdout ); - } - if ( level <= mFileLevel ) - { - fprintf( mLogFileFP, "%s", logString ); - if ( mFlush ) - fflush( mLogFileFP ); - } - *syslogEnd = '\0'; - if ( level <= mDatabaseLevel ) - { - char sql[ZM_SQL_MED_BUFSIZ]; - char escapedString[(strlen(syslogStart)*2)+1]; - - mysql_real_escape_string( &mDbConnection, escapedString, syslogStart, strlen(syslogStart) ); - snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), tid, level, classString, escapedString, file, line ); - if ( mysql_query( &mDbConnection, sql ) ) - { - databaseLevel( NOLOG ); - Fatal( "Can't insert log entry: %s", mysql_error( &mDbConnection ) ); - exit( mysql_errno( &mDbConnection ) ); - } - } - if ( level <= mSyslogLevel ) - { - int priority = smSyslogPriorities[level]; - //priority |= LOG_DAEMON; - syslog( priority, "%s [%s]", classString, syslogStart ); - } - - if ( level <= FATAL ) - { - if ( level <= PANIC ) - abort(); - exit( -1 ); - } + snprintf( timeString, sizeof(timeString), "%ld.%03ld", timeVal.tv_sec, timeVal.tv_usec/1000 ); } + else + { + #endif + char *timePtr = timeString; + timePtr += strftime( timePtr, sizeof(timeString), "%x %H:%M:%S", localtime(&timeVal.tv_sec) ); + snprintf( timePtr, sizeof(timeString)-(timePtr-timeString), ".%06ld", timeVal.tv_usec ); + #if 0 + } + #endif + + pid_t tid; +#ifdef __FreeBSD__ + long lwpid; + thr_self(&lwpid); + tid = lwpid; + + if (tid < 0 ) // Thread/Process id +#else +#ifdef HAVE_SYSCALL + #ifdef __FreeBSD_kernel__ + if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id + + # else + // SOLARIS doesn't have SYS_gettid; don't assume + #ifdef SYS_gettid + if ( (tid = syscall(SYS_gettid)) < 0 ) // Thread/Process id + #endif // SYS_gettid + #endif +#endif // HAVE_SYSCALL +#endif + tid = getpid(); // Process id + + char *logPtr = logString; + logPtr += snprintf( logPtr, sizeof(logString), "%s %s[%d].%s-%s/%d [", + timeString, + mId.c_str(), + tid, + classString, + file, + line + ); + char *syslogStart = logPtr; + + va_start( argPtr, fstring ); + if ( hex ) + { + unsigned char *data = va_arg( argPtr, unsigned char * ); + int len = va_arg( argPtr, int ); + int i; + logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), "%d:", len ); + for ( i = 0; i < len; i++ ) + { + logPtr += snprintf( logPtr, sizeof(logString)-(logPtr-logString), " %02x", data[i] ); + } + } + else + { + logPtr += vsnprintf( logPtr, sizeof(logString)-(logPtr-logString), fstring, argPtr ); + } + va_end(argPtr); + char *syslogEnd = logPtr; + strncpy( logPtr, "]\n", sizeof(logString)-(logPtr-logString) ); + + if ( level <= mTermLevel ) + { + printf( "%s", logString ); + fflush( stdout ); + } + if ( level <= mFileLevel ) + { + fprintf( mLogFileFP, "%s", logString ); + if ( mFlush ) + fflush( mLogFileFP ); + } + *syslogEnd = '\0'; + if ( level <= mDatabaseLevel ) + { + char sql[ZM_SQL_MED_BUFSIZ]; + char escapedString[(strlen(syslogStart)*2)+1]; + + mysql_real_escape_string( &mDbConnection, escapedString, syslogStart, strlen(syslogStart) ); + + snprintf( sql, sizeof(sql), "insert into Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) values ( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )", timeVal.tv_sec, timeVal.tv_usec, mId.c_str(), staticConfig.SERVER_ID, tid, level, classString, escapedString, file, line ); + if ( mysql_query( &mDbConnection, sql ) ) + { + databaseLevel( NOLOG ); + Error( "Can't insert log entry: %s", mysql_error( &mDbConnection ) ); + } + } + if ( level <= mSyslogLevel ) + { + int priority = smSyslogPriorities[level]; + //priority |= LOG_DAEMON; + syslog( priority, "%s [%s]", classString, syslogStart ); + } + + if ( level <= FATAL ) + { + if ( level <= PANIC ) + abort(); + exit( -1 ); + } + } } void logInit( const char *name, const Logger::Options &options ) { - if ( !Logger::smInstance ) - Logger::smInstance = new Logger(); - Logger::Options tempOptions = options; - tempOptions.mLogPath = config.path_logs; - Logger::smInstance->initialise( name, tempOptions ); + if ( !Logger::smInstance ) + Logger::smInstance = new Logger(); + Logger::Options tempOptions = options; + tempOptions.mLogPath = config.path_logs; + Logger::smInstance->initialise( name, tempOptions ); } void logTerm() { - Logger::fetch()->terminate(); + if ( Logger::smInstance ) + delete Logger::smInstance; } diff --git a/src/zm_logger.h b/src/zm_logger.h index 50dce970c..d30396056 100644 --- a/src/zm_logger.h +++ b/src/zm_logger.h @@ -33,188 +33,190 @@ class Logger { public: - enum { - NOOPT=-6, - NOLOG, - PANIC, - FATAL, - ERROR, - WARNING, - INFO, - DEBUG1, - DEBUG2, - DEBUG3, - DEBUG4, - DEBUG5, - DEBUG6, - DEBUG7, - DEBUG8, - DEBUG9 - }; + enum { + NOOPT=-6, + NOLOG, + PANIC, + FATAL, + ERROR, + WARNING, + INFO, + DEBUG1, + DEBUG2, + DEBUG3, + DEBUG4, + DEBUG5, + DEBUG6, + DEBUG7, + DEBUG8, + DEBUG9 + }; - typedef int Level; + typedef int Level; - typedef std::map StringMap; - typedef std::map IntMap; + typedef std::map StringMap; + typedef std::map IntMap; - class Options - { - public: - int mTermLevel; - int mDatabaseLevel; - int mFileLevel; - int mSyslogLevel; + class Options + { + public: + int mTermLevel; + int mDatabaseLevel; + int mFileLevel; + int mSyslogLevel; - std::string mLogPath; - std::string mLogFile; - - public: - Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) : - mTermLevel( termLevel ), - mDatabaseLevel( databaseLevel ), - mFileLevel( fileLevel ), - mSyslogLevel( syslogLevel ), - mLogPath( logPath ), - mLogFile( logFile ) - { - } - }; - -private: - static bool smInitialised; - static Logger *smInstance; - - static StringMap smCodes; - static IntMap smSyslogPriorities; - -private: - bool mInitialised; - - std::string mId; - std::string mIdRoot; - std::string mIdArgs; - - Level mLevel; // Level that is currently in operation - Level mTermLevel; // Maximum level output via terminal - Level mDatabaseLevel; // Maximum level output via database - Level mFileLevel; // Maximum level output via file - Level mSyslogLevel; // Maximum level output via syslog - Level mEffectiveLevel; // Level optimised to take account of maxima - - bool mDbConnected; - MYSQL mDbConnection; std::string mLogPath; std::string mLogFile; - FILE *mLogFileFP; - bool mHasTerm; - bool mFlush; + public: + Options( Level termLevel=NOOPT, Level databaseLevel=NOOPT, Level fileLevel=NOOPT, Level syslogLevel=NOOPT, const std::string &logPath=".", const std::string &logFile="" ) : + mTermLevel( termLevel ), + mDatabaseLevel( databaseLevel ), + mFileLevel( fileLevel ), + mSyslogLevel( syslogLevel ), + mLogPath( logPath ), + mLogFile( logFile ) + { + } + }; private: - static void usrHandler( int sig ); + static bool smInitialised; + static Logger *smInstance; -public: - friend void logInit( const char *name, const Options &options ); - - static Logger *fetch() - { - if ( !smInstance ) - { - smInstance = new Logger(); - Options options; - smInstance->initialise( "undef", options ); - } - return( smInstance ); - } + static StringMap smCodes; + static IntMap smSyslogPriorities; private: - Logger(); - ~Logger(); + bool mInitialised; -public: - void initialise( const std::string &id, const Options &options ); - void terminate(); + std::string mId; + std::string mIdRoot; + std::string mIdArgs; + + Level mLevel; // Level that is currently in operation + Level mTermLevel; // Maximum level output via terminal + Level mDatabaseLevel; // Maximum level output via database + Level mFileLevel; // Maximum level output via file + Level mSyslogLevel; // Maximum level output via syslog + Level mEffectiveLevel; // Level optimised to take account of maxima + + bool mDbConnected; + MYSQL mDbConnection; + std::string mLogPath; + std::string mLogFile; + FILE *mLogFileFP; + + bool mHasTerm; + bool mFlush; private: - int limit( int level ) - { - if ( level > DEBUG9 ) - return( DEBUG9 ); - if ( level < NOLOG ) - return( NOLOG ); - return( level ); - } - - bool boolEnv( const std::string &name, bool defaultValue=false ); - int intEnv( const std::string &name, bool defaultValue=0 ); - std::string strEnv( const std::string &name, const std::string defaultValue="" ); - char *getTargettedEnv( const std::string &name ); - - void loadEnv(); + static void usrHandler( int sig ); public: - const std::string &id() const + friend void logInit( const char *name, const Options &options ); + friend void logTerm(); + + static Logger *fetch() + { + if ( !smInstance ) { - return( mId ); + smInstance = new Logger(); + Options options; + smInstance->initialise( "undef", options ); } - - const std::string &id( const std::string &id ); - - Level level() const - { - return( mLevel ); - } - Level level( Level=NOOPT ); - - bool debugOn() - { - return( mEffectiveLevel >= DEBUG1 ); - } - - Level termLevel( Level=NOOPT ); - Level databaseLevel( Level=NOOPT ); - Level fileLevel( Level=NOOPT ); - Level syslogLevel( Level=NOOPT ); + return( smInstance ); + } private: - void logFile( const std::string &logFile ); - void openFile(); - void closeFile(); - void openSyslog(); - void closeSyslog(); + Logger(); + ~Logger(); public: - void logPrint( bool hex, const char * const file, const int line, const int level, const char *fstring, ... ); + void initialise( const std::string &id, const Options &options ); + void terminate(); + +private: + int limit( int level ) + { + if ( level > DEBUG9 ) + return( DEBUG9 ); + if ( level < NOLOG ) + return( NOLOG ); + return( level ); + } + + bool boolEnv( const std::string &name, bool defaultValue=false ); + int intEnv( const std::string &name, bool defaultValue=0 ); + std::string strEnv( const std::string &name, const std::string defaultValue="" ); + char *getTargettedEnv( const std::string &name ); + + void loadEnv(); + +public: + const std::string &id() const + { + return( mId ); + } + + const std::string &id( const std::string &id ); + + Level level() const + { + return( mLevel ); + } + Level level( Level=NOOPT ); + + bool debugOn() + { + return( mEffectiveLevel >= DEBUG1 ); + } + + Level termLevel( Level=NOOPT ); + Level databaseLevel( Level=NOOPT ); + Level fileLevel( Level=NOOPT ); + Level syslogLevel( Level=NOOPT ); + +private: + void logFile( const std::string &logFile ); + void openFile(); + void closeFile(); + void openSyslog(); + void closeSyslog(); + void closeDatabase(); + +public: + void logPrint( bool hex, const char * const filepath, const int line, const int level, const char *fstring, ... ); }; void logInit( const char *name, const Logger::Options &options=Logger::Options() ); void logTerm(); inline const std::string &logId() { - return( Logger::fetch()->id() ); + return( Logger::fetch()->id() ); } inline Logger::Level logLevel() { - return( Logger::fetch()->level() ); + return( Logger::fetch()->level() ); } inline void logCapLevel( Logger::Level level ) { - Logger::fetch()->level( level ); + Logger::fetch()->level( level ); } inline Logger::Level logDebugging() { - return( Logger::fetch()->debugOn() ); + return( Logger::fetch()->debugOn() ); } #define logPrintf(logLevel,params...) {\ - if ( logLevel <= Logger::fetch()->level() )\ - Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\ - } + if ( logLevel <= Logger::fetch()->level() )\ + Logger::fetch()->logPrint( false, __FILE__, __LINE__, logLevel, ##params );\ + } #define logHexdump(logLevel,data,len) {\ - if ( logLevel <= Logger::fetch()->level() )\ - Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\ - } + if ( logLevel <= Logger::fetch()->level() )\ + Logger::fetch()->logPrint( true, __FILE__, __LINE__, logLevel, "%p (%d)", data, len );\ + } /* Debug compiled out */ #ifndef DBG_OFF @@ -226,19 +228,19 @@ inline Logger::Level logDebugging() #endif /* Standard debug calls */ -#define Info(params...) logPrintf(Logger::INFO,##params) +#define Info(params...) logPrintf(Logger::INFO,##params) #define Warning(params...) logPrintf(Logger::WARNING,##params) -#define Error(params...) logPrintf(Logger::ERROR,##params) -#define Fatal(params...) logPrintf(Logger::FATAL,##params) -#define Panic(params...) logPrintf(Logger::PANIC,##params) -#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__) -#define Log() Info("Log") +#define Error(params...) logPrintf(Logger::ERROR,##params) +#define Fatal(params...) logPrintf(Logger::FATAL,##params) +#define Panic(params...) logPrintf(Logger::PANIC,##params) +#define Mark() Info("Mark/%s/%d",__FILE__,__LINE__) +#define Log() Info("Log") #ifdef __GNUC__ -#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__)) -#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__)) +#define Enter(level) logPrintf(level,("Entering %s",__PRETTY_FUNCTION__)) +#define Exit(level) logPrintf(level,("Exiting %s",__PRETTY_FUNCTION__)) #else -#define Enter(level) -#define Exit(level) +#define Enter(level) +#define Exit(level) #endif #endif // ZM_LOGGER_H diff --git a/src/zm_mem_utils.h b/src/zm_mem_utils.h index 8ce1ceea1..351fa3ac8 100644 --- a/src/zm_mem_utils.h +++ b/src/zm_mem_utils.h @@ -24,138 +24,138 @@ #include "zm.h" inline void* zm_mallocaligned(unsigned int reqalignment, size_t reqsize) { - uint8_t* retptr; + uint8_t* retptr; #if HAVE_POSIX_MEMALIGN - if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0) - return NULL; - - return retptr; + if(posix_memalign((void**)&retptr,reqalignment,reqsize) != 0) + return NULL; + + return retptr; #else - uint8_t* alloc; - retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*)); - - if(retptr == NULL) - return NULL; - - alloc = retptr + sizeof(void*); - - if(((long)alloc % reqalignment) != 0) - alloc = alloc + (reqalignment - ((long)alloc % reqalignment)); - - /* Store a pointer before to the start of the block, just before returned aligned memory */ - *(void**)(alloc - sizeof(void*)) = retptr; - - return alloc; + uint8_t* alloc; + retptr = (uint8_t*)malloc(reqsize+reqalignment+sizeof(void*)); + + if(retptr == NULL) + return NULL; + + alloc = retptr + sizeof(void*); + + if(((long)alloc % reqalignment) != 0) + alloc = alloc + (reqalignment - ((long)alloc % reqalignment)); + + /* Store a pointer before to the start of the block, just before returned aligned memory */ + *(void**)(alloc - sizeof(void*)) = retptr; + + return alloc; #endif } inline void zm_freealigned(void* ptr) { #if HAVE_POSIX_MEMALIGN - free(ptr); + free(ptr); #else - /* Start of block is stored before the block if it was allocated by zm_mallocaligned */ - free(*(void**)((uint8_t*)ptr - sizeof(void*))); + /* Start of block is stored before the block if it was allocated by zm_mallocaligned */ + free(*(void**)((uint8_t*)ptr - sizeof(void*))); #endif } inline char *mempbrk( register const char *s, const char *accept, size_t limit ) { - if ( limit <= 0 || !s || !accept || !*accept ) - return( 0 ); - - register unsigned int i,j; - size_t acc_len = strlen( accept ); - - for ( i = 0; i < limit; s++, i++ ) - { - for ( j = 0; j < acc_len; j++ ) - { - if ( *s == accept[j] ) - { - return( (char *)s ); - } - } - } + if ( limit <= 0 || !s || !accept || !*accept ) return( 0 ); + + register unsigned int i,j; + size_t acc_len = strlen( accept ); + + for ( i = 0; i < limit; s++, i++ ) + { + for ( j = 0; j < acc_len; j++ ) + { + if ( *s == accept[j] ) + { + return( (char *)s ); + } + } + } + return( 0 ); } inline char *memstr( register const char *s, const char *n, size_t limit ) { - if ( limit <= 0 || !s || !n ) - return( 0 ); - - if ( !*n ) - return( (char *)s ); - - register unsigned int i,j,k; - size_t n_len = strlen( n ); - - for ( i = 0; i < limit; i++, s++ ) - { - if ( *s != *n ) - continue; - j = 1; - k = 1; - while ( true ) - { - if ( k >= n_len ) - return( (char *)s ); - if ( s[j++] != n[k++] ) - break; - } - } + if ( limit <= 0 || !s || !n ) return( 0 ); + + if ( !*n ) + return( (char *)s ); + + register unsigned int i,j,k; + size_t n_len = strlen( n ); + + for ( i = 0; i < limit; i++, s++ ) + { + if ( *s != *n ) + continue; + j = 1; + k = 1; + while ( true ) + { + if ( k >= n_len ) + return( (char *)s ); + if ( s[j++] != n[k++] ) + break; + } + } + return( 0 ); } inline size_t memspn( register const char *s, const char *accept, size_t limit ) { - if ( limit <= 0 || !s || !accept || !*accept ) - return( 0 ); + if ( limit <= 0 || !s || !accept || !*accept ) + return( 0 ); - register unsigned int i,j; - size_t acc_len = strlen( accept ); + register unsigned int i,j; + size_t acc_len = strlen( accept ); - for ( i = 0; i < limit; s++, i++ ) + for ( i = 0; i < limit; s++, i++ ) + { + register bool found = false; + for ( j = 0; j < acc_len; j++ ) { - register bool found = false; - for ( j = 0; j < acc_len; j++ ) - { - if ( *s == accept[j] ) - { - found = true; - break; - } - } - if ( !found ) - { - return( i ); - } + if ( *s == accept[j] ) + { + found = true; + break; + } } - return( limit ); + if ( !found ) + { + return( i ); + } + } + return( limit ); } inline size_t memcspn( register const char *s, const char *reject, size_t limit ) { - if ( limit <= 0 || !s || !reject ) - return( 0 ); + if ( limit <= 0 || !s || !reject ) + return( 0 ); - if ( !*reject ) - return( limit ); - - register unsigned int i,j; - size_t rej_len = strlen( reject ); - - for ( i = 0; i < limit; s++, i++ ) - { - for ( j = 0; j < rej_len; j++ ) - { - if ( *s == reject[j] ) - { - return( i ); - } - } - } + if ( !*reject ) return( limit ); + + register unsigned int i,j; + size_t rej_len = strlen( reject ); + + for ( i = 0; i < limit; s++, i++ ) + { + for ( j = 0; j < rej_len; j++ ) + { + if ( *s == reject[j] ) + { + return( i ); + } + } + } + return( limit ); } #endif // ZM_MEM_UTILS_H diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 825716cd7..f99ebd82d 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -55,15 +55,20 @@ #include #endif // ZM_MEM_MAPPED +// SOLARIS - we don't have MAP_LOCKED on openSolaris/illumos +#ifndef MAP_LOCKED +#define MAP_LOCKED 0 +#endif + //============================================================================= std::vector split(const std::string &s, char delim) { - std::vector elems; - std::stringstream ss(s); - std::string item; - while(std::getline(ss, item, delim)) { - elems.push_back(trimSpaces(item)); - } - return elems; + std::vector elems; + std::stringstream ss(s); + std::string item; + while(std::getline(ss, item, delim)) { + elems.push_back(trimSpaces(item)); + } + return elems; } //============================================================================= @@ -71,4197 +76,4251 @@ std::vector split(const std::string &s, char delim) { Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) : id( p_id ) { - strncpy( name, p_name, sizeof(name) ); + strncpy( name, p_name, sizeof(name) ); #if ZM_MEM_MAPPED - map_fd = -1; - snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); + map_fd = -1; + snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); #else // ZM_MEM_MAPPED - shm_id = 0; + shm_id = 0; #endif // ZM_MEM_MAPPED - mem_size = 0; - mem_ptr = 0; + mem_size = 0; + mem_ptr = 0; - last_event = 0; - last_state = IDLE; + last_event = 0; + last_state = IDLE; - last_connect_time = 0; - connected = false; + last_connect_time = 0; + connected = false; } Monitor::MonitorLink::~MonitorLink() { - disconnect(); + disconnect(); } bool Monitor::MonitorLink::connect() { - if ( !last_connect_time || (time( 0 ) - last_connect_time) > 60 ) - { - last_connect_time = time( 0 ); + if ( !last_connect_time || (time( 0 ) - last_connect_time) > 60 ) + { + last_connect_time = time( 0 ); - mem_size = sizeof(SharedData) + sizeof(TriggerData); + mem_size = sizeof(SharedData) + sizeof(TriggerData); - Debug( 1, "link.mem.size=%d", mem_size ); + Debug( 1, "link.mem.size=%d", mem_size ); #if ZM_MEM_MAPPED - map_fd = open( mem_file, O_RDWR, (mode_t)0600 ); - if ( map_fd < 0 ) - { - Debug( 3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno) ); - disconnect(); - return( false ); - } - - struct stat map_stat; - if ( fstat( map_fd, &map_stat ) < 0 ) - { - Error( "Can't stat linked memory map file %s: %s", mem_file, strerror(errno) ); - disconnect(); - return( false ); - } - - if ( map_stat.st_size == 0 ) - { - Error( "Linked memory map file %s is empty: %s", mem_file, strerror(errno) ); - disconnect(); - return( false ); - } - else if ( map_stat.st_size < mem_size ) - { - Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); - disconnect(); - return( false ); - } - - mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); - if ( mem_ptr == MAP_FAILED ) - { - Error( "Can't map file %s (%d bytes) to memory: %s", mem_file, mem_size, strerror(errno) ); - disconnect(); - return( false ); - } -#else // ZM_MEM_MAPPED - shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, 0700 ); - if ( shm_id < 0 ) - { - Debug( 3, "Can't shmget link memory: %s", strerror(errno) ); - connected = false; - return( false ); - } - mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); - if ( mem_ptr < 0 ) - { - Debug( 3, "Can't shmat link memory: %s", strerror(errno) ); - connected = false; - return( false ); - } -#endif // ZM_MEM_MAPPED - - shared_data = (SharedData *)mem_ptr; - trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); - - if ( !shared_data->valid ) - { - Debug( 3, "Linked memory not initialised by capture daemon" ); - disconnect(); - return( false ); - } - - last_state = shared_data->state; - last_event = shared_data->last_event; - connected = true; - - return( true ); - } - return( false ); -} - -bool Monitor::MonitorLink::disconnect() -{ - if ( connected ) - { - connected = false; - -#if ZM_MEM_MAPPED - if ( mem_ptr > 0 ) - { - msync( mem_ptr, mem_size, MS_ASYNC ); - munmap( mem_ptr, mem_size ); - } - if ( map_fd >= 0 ) - close( map_fd ); - - map_fd = -1; -#else // ZM_MEM_MAPPED - struct shmid_ds shm_data; - if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) - { - Debug( 3, "Can't shmctl: %s", strerror(errno) ); - return( false ); - } - - shm_id = 0; - - if ( shm_data.shm_nattch <= 1 ) - { - if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) - { - Debug( 3, "Can't shmctl: %s", strerror(errno) ); - return( false ); - } - } - - if ( shmdt( mem_ptr ) < 0 ) - { - Debug( 3, "Can't shmdt: %s", strerror(errno) ); - return( false ); - } - -#endif // ZM_MEM_MAPPED - mem_size = 0; - mem_ptr = 0; - } - return( true ); -} - -bool Monitor::MonitorLink::isAlarmed() -{ - if ( !connected ) - { - return( false ); - } - return( shared_data->state == ALARM ); -} - -bool Monitor::MonitorLink::inAlarm() -{ - if ( !connected ) - { - return( false ); - } - return( shared_data->state == ALARM || shared_data->state == ALERT ); -} - -bool Monitor::MonitorLink::hasAlarmed() -{ - if ( shared_data->state == ALARM ) - { - return( true ); - } - else if( shared_data->last_event != (unsigned int)last_event ) - { - last_event = shared_data->last_event; - return( true ); - } - return( false ); -} - -Monitor::Monitor( - int p_id, - const char *p_name, - int p_function, - bool p_enabled, - const char *p_linked_monitors, - Camera *p_camera, - int p_orientation, - unsigned int p_deinterlacing, - const char *p_event_prefix, - const char *p_label_format, - const Coord &p_label_coord, - int p_image_buffer_count, - int p_warmup_count, - int p_pre_event_count, - int p_post_event_count, - int p_stream_replay_buffer, - int p_alarm_frame_count, - int p_section_length, - int p_frame_skip, - int p_motion_frame_skip, - int p_capture_delay, - int p_alarm_capture_delay, - int p_fps_report_interval, - int p_ref_blend_perc, - int p_alarm_ref_blend_perc, - bool p_track_motion, - Rgb p_signal_check_colour, - Purpose p_purpose, - int p_n_zones, - Zone *p_zones[] -) : id( p_id ), - function( (Function)p_function ), - enabled( p_enabled ), - width( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Height():p_camera->Width() ), - height( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Width():p_camera->Height() ), - orientation( (Orientation)p_orientation ), - deinterlacing( p_deinterlacing ), - label_coord( p_label_coord ), - image_buffer_count( p_image_buffer_count ), - warmup_count( p_warmup_count ), - pre_event_count( p_pre_event_count ), - post_event_count( p_post_event_count ), - stream_replay_buffer( p_stream_replay_buffer ), - section_length( p_section_length ), - frame_skip( p_frame_skip ), - motion_frame_skip( p_motion_frame_skip ), - capture_delay( p_capture_delay ), - alarm_capture_delay( p_alarm_capture_delay ), - alarm_frame_count( p_alarm_frame_count ), - fps_report_interval( p_fps_report_interval ), - ref_blend_perc( p_ref_blend_perc ), - alarm_ref_blend_perc( p_alarm_ref_blend_perc ), - track_motion( p_track_motion ), - signal_check_colour( p_signal_check_colour ), - delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ), - ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ), - purpose( p_purpose ), - last_motion_score(0), - camera( p_camera ), - n_zones( p_n_zones ), - zones( p_zones ) -{ - strncpy( name, p_name, sizeof(name) ); - - strncpy( event_prefix, p_event_prefix, sizeof(event_prefix) ); - strncpy( label_format, p_label_format, sizeof(label_format) ); - - // Change \n to actual line feeds - char *token_ptr = label_format; - const char *token_string = "\n"; - while( ( token_ptr = strstr( token_ptr, token_string ) ) ) - { - if ( *(token_ptr+1) ) - { - *token_ptr = '\n'; - token_ptr++; - strcpy( token_ptr, token_ptr+1 ); - } - else - { - *token_ptr = '\0'; - break; - } - } - - fps = 0.0; - event_count = 0; - image_count = 0; - ready_count = warmup_count; - first_alarm_count = 0; - last_alarm_count = 0; - state = IDLE; - - if ( alarm_frame_count < 1 ) - alarm_frame_count = 1; - else if ( alarm_frame_count > MAX_PRE_ALARM_FRAMES ) - alarm_frame_count = MAX_PRE_ALARM_FRAMES; - - auto_resume_time = 0; - - if ( strcmp( config.event_close_mode, "time" ) == 0 ) - event_close_mode = CLOSE_TIME; - else if ( strcmp( config.event_close_mode, "alarm" ) == 0 ) - event_close_mode = CLOSE_ALARM; - else - event_close_mode = CLOSE_IDLE; - - Debug( 1, "monitor purpose=%d", purpose ); - - mem_size = sizeof(SharedData) - + sizeof(TriggerData) - + (image_buffer_count*sizeof(struct timeval)) - + (image_buffer_count*camera->ImageSize()) - + 64; /* Padding used to permit aligning the images buffer to 16 byte boundary */ - - Debug( 1, "mem.size=%d", mem_size ); - mem_ptr = NULL; - - if ( purpose == CAPTURE ) { - this->connect(); - if ( ! mem_ptr ) exit(-1); - memset( mem_ptr, 0, mem_size ); - shared_data->size = sizeof(SharedData); - shared_data->active = enabled; - shared_data->signal = false; - shared_data->state = IDLE; - shared_data->last_write_index = image_buffer_count; - shared_data->last_read_index = image_buffer_count; - shared_data->last_write_time = 0; - shared_data->last_event = 0; - shared_data->action = (Action)0; - shared_data->brightness = -1; - shared_data->hue = -1; - shared_data->colour = -1; - shared_data->contrast = -1; - shared_data->alarm_x = -1; - shared_data->alarm_y = -1; - shared_data->format = camera->SubpixelOrder(); - shared_data->imagesize = camera->ImageSize(); - trigger_data->size = sizeof(TriggerData); - trigger_data->trigger_state = TRIGGER_CANCEL; - trigger_data->trigger_score = 0; - trigger_data->trigger_cause[0] = 0; - trigger_data->trigger_text[0] = 0; - trigger_data->trigger_showtext[0] = 0; - shared_data->valid = true; - } else if ( purpose == ANALYSIS ) { - this->connect(); - if ( ! mem_ptr ) exit(-1); - shared_data->state = IDLE; - shared_data->last_read_time = 0; - shared_data->alarm_x = -1; - shared_data->alarm_y = -1; - } - - if ( ( ! mem_ptr ) || ! shared_data->valid ) - { - if ( purpose != QUERY ) - { - Error( "Shared data not initialised by capture daemon" ); - exit( -1 ); - } - else - { - Warning( "Shared data not initialised by capture daemon, some query functions may not be available or produce invalid results" ); - } - } - - // Will this not happen everytime a monitor is instantiated? Seems like all the calls to the Monitor constructor pass a zero for n_zones, then load zones after.. - if ( !n_zones ) { - Debug( 1, "Monitor %s has no zones, adding one.", name ); - n_zones = 1; - zones = new Zone *[1]; - Coord coords[4] = { Coord( 0, 0 ), Coord( width-1, 0 ), Coord( width-1, height-1 ), Coord( 0, height-1 ) }; - zones[0] = new Zone( this, 0, "All", Zone::ACTIVE, Polygon( sizeof(coords)/sizeof(*coords), coords ), RGB_RED, Zone::BLOBS ); - } - start_time = last_fps_time = time( 0 ); - - event = 0; - - Debug( 1, "Monitor %s has function %d", name, function ); - Debug( 1, "Monitor %s LBF = '%s', LBX = %d, LBY = %d", name, label_format, label_coord.X(), label_coord.Y() ); - Debug( 1, "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion ); - - if ( purpose == ANALYSIS ) - { - static char path[PATH_MAX]; - - strncpy( path, config.dir_events, sizeof(path) ); - - struct stat statbuf; - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { - Error( "Can't make %s: %s", path, strerror(errno)); - } - } - - snprintf( path, sizeof(path), "%s/%d", config.dir_events, id ); - - errno = 0; - stat( path, &statbuf ); - if ( errno == ENOENT || errno == ENOTDIR ) - { - if ( mkdir( path, 0755 ) ) - { - Error( "Can't make %s: %s", path, strerror(errno)); - } - char temp_path[PATH_MAX]; - snprintf( temp_path, sizeof(temp_path), "%d", id ); - if ( chdir( config.dir_events ) < 0 ) - Fatal( "Can't change directory to '%s': %s", config.dir_events, strerror(errno) ); - if ( symlink( temp_path, name ) < 0 ) - Fatal( "Can't symlink '%s' to '%s': %s", temp_path, name, strerror(errno) ); - if ( chdir( ".." ) < 0 ) - Fatal( "Can't change to parent directory: %s", strerror(errno) ); - } - - while( shared_data->last_write_index == (unsigned int)image_buffer_count - && shared_data->last_write_time == 0) - { - Warning( "Waiting for capture daemon" ); - sleep( 1 ); - } - ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize()); - - n_linked_monitors = 0; - linked_monitors = 0; - ReloadLinkedMonitors( p_linked_monitors ); - } -} - -bool Monitor::connect() { -#if ZM_MEM_MAPPED - snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); - map_fd = open( mem_file, O_RDWR|O_CREAT, (mode_t)0600 ); + map_fd = open( mem_file, O_RDWR, (mode_t)0600 ); if ( map_fd < 0 ) - Fatal( "Can't open memory map file %s, probably not enough space free: %s", mem_file, strerror(errno) ); + { + Debug( 3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno) ); + disconnect(); + return( false ); + } + while ( map_fd <= 2 ) { + int new_map_fd = dup(map_fd); + Warning( "Got one of the stdio fds for our mmap handle. map_fd was %d, new one is %d", map_fd, new_map_fd ); + close(map_fd); + map_fd = new_map_fd; + } struct stat map_stat; if ( fstat( map_fd, &map_stat ) < 0 ) - Fatal( "Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file, strerror(errno) ); - if ( map_stat.st_size != mem_size && purpose == CAPTURE ) { - // Allocate the size - if ( ftruncate( map_fd, mem_size ) < 0 ) { - Fatal( "Can't extend memory map file %s to %d bytes: %s", mem_file, mem_size, strerror(errno) ); - } - } else if ( map_stat.st_size == 0 ) { - Error( "Got empty memory map file size %ld, is the zmc process for this monitor running?", map_stat.st_size, mem_size ); - return false; - } else if ( map_stat.st_size != mem_size ) { - Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); - return false; - } else { - mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 ); - if ( mem_ptr == MAP_FAILED ) { - if ( errno == EAGAIN ) { - Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size ); - mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); - Debug( 1, "Mapped file %s (%d bytes) to locked memory, unlocked", mem_file, mem_size ); - } - } - if ( mem_ptr == MAP_FAILED ) - Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno ); + { + Error( "Can't stat linked memory map file %s: %s", mem_file, strerror(errno) ); + disconnect(); + return( false ); + } + + if ( map_stat.st_size == 0 ) + { + Error( "Linked memory map file %s is empty: %s", mem_file, strerror(errno) ); + disconnect(); + return( false ); + } + else if ( map_stat.st_size < mem_size ) + { + Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); + disconnect(); + return( false ); + } + + mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); + if ( mem_ptr == MAP_FAILED ) + { + Error( "Can't map file %s (%d bytes) to memory: %s", mem_file, mem_size, strerror(errno) ); + disconnect(); + return( false ); } #else // ZM_MEM_MAPPED - shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700 ); - if ( shm_id < 0 ) { - Error( "Can't shmget, probably not enough shared memory space free: %s", strerror(errno)); - exit( -1 ); + shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, 0700 ); + if ( shm_id < 0 ) + { + Debug( 3, "Can't shmget link memory: %s", strerror(errno) ); + connected = false; + return( false ); } mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); if ( mem_ptr < 0 ) { - Error( "Can't shmat: %s", strerror(errno)); - exit( -1 ); + Debug( 3, "Can't shmat link memory: %s", strerror(errno) ); + connected = false; + return( false ); } #endif // ZM_MEM_MAPPED + shared_data = (SharedData *)mem_ptr; trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); - struct timeval *shared_timestamps = (struct timeval *)((char *)trigger_data + sizeof(TriggerData)); - unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval))); - - if(((unsigned long)shared_images % 16) != 0) { - /* Align images buffer to nearest 16 byte boundary */ - Debug(3,"Aligning shared memory images to the next 16 byte boundary"); - shared_images = (uint8_t*)((unsigned long)shared_images + (16 - ((unsigned long)shared_images % 16))); - } - image_buffer = new Snapshot[image_buffer_count]; - for ( int i = 0; i < image_buffer_count; i++ ) + + if ( !shared_data->valid ) { - image_buffer[i].timestamp = &(shared_timestamps[i]); - image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) ); - image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ + Debug( 3, "Linked memory not initialised by capture daemon" ); + disconnect(); + return( false ); } - if ( (deinterlacing & 0xff) == 4) + + last_state = shared_data->state; + last_event = shared_data->last_event; + connected = true; + + return( true ); + } + return( false ); +} + +bool Monitor::MonitorLink::disconnect() +{ + if ( connected ) + { + connected = false; + +#if ZM_MEM_MAPPED + if ( mem_ptr > 0 ) { - /* Four field motion adaptive deinterlacing in use */ - /* Allocate a buffer for the next image */ - next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); - next_buffer.timestamp = new struct timeval; + msync( mem_ptr, mem_size, MS_ASYNC ); + munmap( mem_ptr, mem_size ); } - return true; + if ( map_fd >= 0 ) + close( map_fd ); + + map_fd = -1; +#else // ZM_MEM_MAPPED + struct shmid_ds shm_data; + if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) + { + Debug( 3, "Can't shmctl: %s", strerror(errno) ); + return( false ); + } + + shm_id = 0; + + if ( shm_data.shm_nattch <= 1 ) + { + if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) + { + Debug( 3, "Can't shmctl: %s", strerror(errno) ); + return( false ); + } + } + + if ( shmdt( mem_ptr ) < 0 ) + { + Debug( 3, "Can't shmdt: %s", strerror(errno) ); + return( false ); + } + +#endif // ZM_MEM_MAPPED + mem_size = 0; + mem_ptr = 0; + } + return( true ); +} + +bool Monitor::MonitorLink::isAlarmed() +{ + if ( !connected ) + { + return( false ); + } + return( shared_data->state == ALARM ); +} + +bool Monitor::MonitorLink::inAlarm() +{ + if ( !connected ) + { + return( false ); + } + return( shared_data->state == ALARM || shared_data->state == ALERT ); +} + +bool Monitor::MonitorLink::hasAlarmed() +{ + if ( shared_data->state == ALARM ) + { + return( true ); + } + else if( shared_data->last_event != (unsigned int)last_event ) + { + last_event = shared_data->last_event; + } + return( false ); +} + +Monitor::Monitor( + int p_id, + const char *p_name, + const unsigned int p_server_id, + int p_function, + bool p_enabled, + const char *p_linked_monitors, + Camera *p_camera, + int p_orientation, + unsigned int p_deinterlacing, + const char *p_event_prefix, + const char *p_label_format, + const Coord &p_label_coord, + int p_label_size, + int p_image_buffer_count, + int p_warmup_count, + int p_pre_event_count, + int p_post_event_count, + int p_stream_replay_buffer, + int p_alarm_frame_count, + int p_section_length, + int p_frame_skip, + int p_motion_frame_skip, + double p_analysis_fps, + unsigned int p_analysis_update_delay, + int p_capture_delay, + int p_alarm_capture_delay, + int p_fps_report_interval, + int p_ref_blend_perc, + int p_alarm_ref_blend_perc, + bool p_track_motion, + Rgb p_signal_check_colour, + bool p_embed_exif, + Purpose p_purpose, + int p_n_zones, + Zone *p_zones[] +) : id( p_id ), + server_id( p_server_id ), + function( (Function)p_function ), + enabled( p_enabled ), + width( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Height():p_camera->Width() ), + height( (p_orientation==ROTATE_90||p_orientation==ROTATE_270)?p_camera->Width():p_camera->Height() ), + orientation( (Orientation)p_orientation ), + deinterlacing( p_deinterlacing ), + label_coord( p_label_coord ), + label_size( p_label_size ), + image_buffer_count( p_image_buffer_count ), + warmup_count( p_warmup_count ), + pre_event_count( p_pre_event_count ), + post_event_count( p_post_event_count ), + stream_replay_buffer( p_stream_replay_buffer ), + section_length( p_section_length ), + frame_skip( p_frame_skip ), + motion_frame_skip( p_motion_frame_skip ), + analysis_fps( p_analysis_fps ), + analysis_update_delay( p_analysis_update_delay ), + capture_delay( p_capture_delay ), + alarm_capture_delay( p_alarm_capture_delay ), + alarm_frame_count( p_alarm_frame_count ), + fps_report_interval( p_fps_report_interval ), + ref_blend_perc( p_ref_blend_perc ), + alarm_ref_blend_perc( p_alarm_ref_blend_perc ), + track_motion( p_track_motion ), + signal_check_colour( p_signal_check_colour ), + embed_exif( p_embed_exif ), + delta_image( width, height, ZM_COLOUR_GRAY8, ZM_SUBPIX_ORDER_NONE ), + ref_image( width, height, p_camera->Colours(), p_camera->SubpixelOrder() ), + purpose( p_purpose ), + last_motion_score(0), + camera( p_camera ), + n_zones( p_n_zones ), + zones( p_zones ), + timestamps( 0 ), + images( 0 ), + privacy_bitmask( NULL ) +{ + strncpy( name, p_name, sizeof(name)-1 ); + + strncpy( event_prefix, p_event_prefix, sizeof(event_prefix)-1 ); + strncpy( label_format, p_label_format, sizeof(label_format)-1 ); + + // Change \n to actual line feeds + char *token_ptr = label_format; + const char *token_string = "\n"; + while( ( token_ptr = strstr( token_ptr, token_string ) ) ) + { + if ( *(token_ptr+1) ) + { + *token_ptr = '\n'; + token_ptr++; + strcpy( token_ptr, token_ptr+1 ); + } + else + { + *token_ptr = '\0'; + break; + } + } + + fps = 0.0; + event_count = 0; + image_count = 0; + ready_count = warmup_count; + first_alarm_count = 0; + last_alarm_count = 0; + state = IDLE; + + if ( alarm_frame_count < 1 ) + alarm_frame_count = 1; + else if ( alarm_frame_count > MAX_PRE_ALARM_FRAMES ) + alarm_frame_count = MAX_PRE_ALARM_FRAMES; + + auto_resume_time = 0; + + if ( strcmp( config.event_close_mode, "time" ) == 0 ) + event_close_mode = CLOSE_TIME; + else if ( strcmp( config.event_close_mode, "alarm" ) == 0 ) + event_close_mode = CLOSE_ALARM; + else + event_close_mode = CLOSE_IDLE; + + Debug( 1, "monitor purpose=%d", purpose ); + + mem_size = sizeof(SharedData) + + sizeof(TriggerData) + + (image_buffer_count*sizeof(struct timeval)) + + (image_buffer_count*camera->ImageSize()) + + 64; /* Padding used to permit aligning the images buffer to 16 byte boundary */ + + Debug( 1, "mem.size=%d", mem_size ); + mem_ptr = NULL; + + if ( purpose == CAPTURE ) { + this->connect(); + if ( ! mem_ptr ) exit(-1); + memset( mem_ptr, 0, mem_size ); + shared_data->size = sizeof(SharedData); + shared_data->active = enabled; + shared_data->signal = false; + shared_data->state = IDLE; + shared_data->last_write_index = image_buffer_count; + shared_data->last_read_index = image_buffer_count; + shared_data->last_write_time = 0; + shared_data->last_event = 0; + shared_data->action = (Action)0; + shared_data->brightness = -1; + shared_data->hue = -1; + shared_data->colour = -1; + shared_data->contrast = -1; + shared_data->alarm_x = -1; + shared_data->alarm_y = -1; + shared_data->format = camera->SubpixelOrder(); + shared_data->imagesize = camera->ImageSize(); + trigger_data->size = sizeof(TriggerData); + trigger_data->trigger_state = TRIGGER_CANCEL; + trigger_data->trigger_score = 0; + trigger_data->trigger_cause[0] = 0; + trigger_data->trigger_text[0] = 0; + trigger_data->trigger_showtext[0] = 0; + shared_data->valid = true; + } else if ( purpose == ANALYSIS ) { + this->connect(); + if ( ! mem_ptr ) exit(-1); + shared_data->state = IDLE; + shared_data->last_read_time = 0; + shared_data->alarm_x = -1; + shared_data->alarm_y = -1; + } + + if ( ( ! mem_ptr ) || ! shared_data->valid ) + { + if ( purpose != QUERY ) + { + Error( "Shared data not initialised by capture daemon for monitor %s", name ); + exit( -1 ); + } + } + + // Will this not happen every time a monitor is instantiated? Seems like all the calls to the Monitor constructor pass a zero for n_zones, then load zones after.. + if ( !n_zones ) { + Debug( 1, "Monitor %s has no zones, adding one.", name ); + n_zones = 1; + zones = new Zone *[1]; + Coord coords[4] = { Coord( 0, 0 ), Coord( width-1, 0 ), Coord( width-1, height-1 ), Coord( 0, height-1 ) }; + zones[0] = new Zone( this, 0, "All", Zone::ACTIVE, Polygon( sizeof(coords)/sizeof(*coords), coords ), RGB_RED, Zone::BLOBS ); + } + start_time = last_fps_time = time( 0 ); + + event = 0; + + Debug( 1, "Monitor %s has function %d", name, function ); + Debug( 1, "Monitor %s LBF = '%s', LBX = %d, LBY = %d, LBS = %d", name, label_format, label_coord.X(), label_coord.Y(), label_size ); + Debug( 1, "Monitor %s IBC = %d, WUC = %d, pEC = %d, PEC = %d, EAF = %d, FRI = %d, RBP = %d, ARBP = %d, FM = %d", name, image_buffer_count, warmup_count, pre_event_count, post_event_count, alarm_frame_count, fps_report_interval, ref_blend_perc, alarm_ref_blend_perc, track_motion ); + + if ( purpose == ANALYSIS ) + { + static char path[PATH_MAX]; + + strncpy( path, config.dir_events, sizeof(path) ); + + struct stat statbuf; + errno = 0; + stat( path, &statbuf ); + if ( errno == ENOENT || errno == ENOTDIR ) + { + if ( mkdir( path, 0755 ) ) + { + Error( "Can't make %s: %s", path, strerror(errno)); + } + } + + snprintf( path, sizeof(path), "%s/%d", config.dir_events, id ); + + errno = 0; + stat( path, &statbuf ); + if ( errno == ENOENT || errno == ENOTDIR ) + { + if ( mkdir( path, 0755 ) ) + { + Error( "Can't make %s: %s", path, strerror(errno)); + } + char temp_path[PATH_MAX]; + snprintf( temp_path, sizeof(temp_path), "%d", id ); + if ( chdir( config.dir_events ) < 0 ) + Fatal( "Can't change directory to '%s': %s", config.dir_events, strerror(errno) ); + if ( symlink( temp_path, name ) < 0 ) + Fatal( "Can't symlink '%s' to '%s': %s", temp_path, name, strerror(errno) ); + if ( chdir( ".." ) < 0 ) + Fatal( "Can't change to parent directory: %s", strerror(errno) ); + } + + while( shared_data->last_write_index == (unsigned int)image_buffer_count + && shared_data->last_write_time == 0) + { + Warning( "Waiting for capture daemon" ); + sleep( 1 ); + } + ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(), image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize()); + + n_linked_monitors = 0; + linked_monitors = 0; + + adaptive_skip = true; + + ReloadLinkedMonitors( p_linked_monitors ); + } +} + +bool Monitor::connect() { +#if ZM_MEM_MAPPED + snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", config.path_map, id ); + map_fd = open( mem_file, O_RDWR|O_CREAT, (mode_t)0600 ); + if ( map_fd < 0 ) + Fatal( "Can't open memory map file %s, probably not enough space free: %s", mem_file, strerror(errno) ); + + struct stat map_stat; + if ( fstat( map_fd, &map_stat ) < 0 ) + Fatal( "Can't stat memory map file %s: %s, is the zmc process for this monitor running?", mem_file, strerror(errno) ); + if ( map_stat.st_size != mem_size && purpose == CAPTURE ) { + // Allocate the size + if ( ftruncate( map_fd, mem_size ) < 0 ) { + Fatal( "Can't extend memory map file %s to %d bytes: %s", mem_file, mem_size, strerror(errno) ); + } + } else if ( map_stat.st_size == 0 ) { + Error( "Got empty memory map file size %ld, is the zmc process for this monitor running?", map_stat.st_size, mem_size ); + return false; + } else if ( map_stat.st_size != mem_size ) { + Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size ); + return false; + } else { +#ifdef MAP_LOCKED + mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, map_fd, 0 ); + if ( mem_ptr == MAP_FAILED ) { + if ( errno == EAGAIN ) { + Debug( 1, "Unable to map file %s (%d bytes) to locked memory, trying unlocked", mem_file, mem_size ); +#endif + mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 ); + Debug( 1, "Mapped file %s (%d bytes) to locked memory, unlocked", mem_file, mem_size ); +#ifdef MAP_LOCKED + } + } +#endif + if ( mem_ptr == MAP_FAILED ) + Fatal( "Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno ); + } +#else // ZM_MEM_MAPPED + shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, IPC_CREAT|0700 ); + if ( shm_id < 0 ) { + Error( "Can't shmget, probably not enough shared memory space free: %s", strerror(errno)); + exit( -1 ); + } + mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 ); + if ( mem_ptr < 0 ) + { + Error( "Can't shmat: %s", strerror(errno)); + exit( -1 ); + } +#endif // ZM_MEM_MAPPED + shared_data = (SharedData *)mem_ptr; + trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData)); + struct timeval *shared_timestamps = (struct timeval *)((char *)trigger_data + sizeof(TriggerData)); + unsigned char *shared_images = (unsigned char *)((char *)shared_timestamps + (image_buffer_count*sizeof(struct timeval))); + + if(((unsigned long)shared_images % 16) != 0) { + /* Align images buffer to nearest 16 byte boundary */ + Debug(3,"Aligning shared memory images to the next 16 byte boundary"); + shared_images = (uint8_t*)((unsigned long)shared_images + (16 - ((unsigned long)shared_images % 16))); + } + image_buffer = new Snapshot[image_buffer_count]; + for ( int i = 0; i < image_buffer_count; i++ ) + { + image_buffer[i].timestamp = &(shared_timestamps[i]); + image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) ); + image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */ + } + if ( (deinterlacing & 0xff) == 4) + { + /* Four field motion adaptive deinterlacing in use */ + /* Allocate a buffer for the next image */ + next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); + next_buffer.timestamp = new struct timeval; + } + + if ( ( purpose == ANALYSIS ) && analysis_fps ) + { + // Size of pre event buffer must be greater than pre_event_count + // if alarm_frame_count > 1, because in this case the buffer contains + // alarmed images that must be discarded when event is created + pre_event_buffer_count = pre_event_count + alarm_frame_count - 1; + pre_event_buffer = new Snapshot[pre_event_buffer_count]; + for ( int i = 0; i < pre_event_buffer_count; i++ ) + { + pre_event_buffer[i].timestamp = new struct timeval; + pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); + } + } + + return true; } Monitor::~Monitor() { - if ( mem_ptr ) { - if ( event ) - Info( "%s: %03d - Closing event %d, shutting down", name, image_count, event->Id() ); - closeEvent(); - - if ( (deinterlacing & 0xff) == 4) - { - delete next_buffer.image; - delete next_buffer.timestamp; - } - for ( int i = 0; i < image_buffer_count; i++ ) - { - delete image_buffer[i].image; - } - delete[] image_buffer; - - } // end if mem_ptr - - for ( int i = 0; i < n_zones; i++ ) - { - delete zones[i]; + if ( timestamps ) { + delete[] timestamps; + timestamps = 0; + } + if ( images ) { + delete[] images; + images = 0; + } + if ( privacy_bitmask ) { + delete[] privacy_bitmask; + privacy_bitmask = NULL; } - delete[] zones; + if ( mem_ptr ) { + if ( event ) + Info( "%s: %03d - Closing event %d, shutting down", name, image_count, event->Id() ); + closeEvent(); - delete camera; + if ( (deinterlacing & 0xff) == 4) + { + delete next_buffer.image; + delete next_buffer.timestamp; + } + for ( int i = 0; i < image_buffer_count; i++ ) + { + delete image_buffer[i].image; + } + delete[] image_buffer; + } // end if mem_ptr - if ( mem_ptr ) { - if ( purpose == ANALYSIS ) - { - shared_data->state = state = IDLE; - shared_data->last_read_index = image_buffer_count; - shared_data->last_read_time = 0; - } - else if ( purpose == CAPTURE ) - { - shared_data->valid = false; - memset( mem_ptr, 0, mem_size ); - } + for ( int i = 0; i < n_zones; i++ ) + { + delete zones[i]; + } + delete[] zones; + + delete camera; + + if ( mem_ptr ) { + if ( purpose == ANALYSIS ) + { + shared_data->state = state = IDLE; + shared_data->last_read_index = image_buffer_count; + shared_data->last_read_time = 0; + + if ( analysis_fps ) + { + for ( int i = 0; i < pre_event_buffer_count; i++ ) + { + delete pre_event_buffer[i].image; + delete pre_event_buffer[i].timestamp; + } + delete[] pre_event_buffer; + } + } + else if ( purpose == CAPTURE ) + { + shared_data->valid = false; + memset( mem_ptr, 0, mem_size ); + } #if ZM_MEM_MAPPED - if ( msync( mem_ptr, mem_size, MS_SYNC ) < 0 ) - Error( "Can't msync: %s", strerror(errno) ); - if ( munmap( mem_ptr, mem_size ) < 0 ) - Fatal( "Can't munmap: %s", strerror(errno) ); - close( map_fd ); + if ( msync( mem_ptr, mem_size, MS_SYNC ) < 0 ) + Error( "Can't msync: %s", strerror(errno) ); + if ( munmap( mem_ptr, mem_size ) < 0 ) + Fatal( "Can't munmap: %s", strerror(errno) ); + close( map_fd ); #else // ZM_MEM_MAPPED - struct shmid_ds shm_data; - if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) { - Error( "Can't shmctl: %s", strerror(errno) ); - exit( -1 ); - } - if ( shm_data.shm_nattch <= 1 ) { - if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) { - Error( "Can't shmctl: %s", strerror(errno) ); - exit( -1 ); - } - } + struct shmid_ds shm_data; + if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) { + Error( "Can't shmctl: %s", strerror(errno) ); + exit( -1 ); + } + if ( shm_data.shm_nattch <= 1 ) { + if ( shmctl( shm_id, IPC_RMID, 0 ) < 0 ) { + Error( "Can't shmctl: %s", strerror(errno) ); + exit( -1 ); + } + } #endif // ZM_MEM_MAPPED - } // end if mem_ptr + } // end if mem_ptr } void Monitor::AddZones( int p_n_zones, Zone *p_zones[] ) { - for ( int i = 0; i < n_zones; i++ ) - delete zones[i]; - delete[] zones; - n_zones = p_n_zones; - zones = p_zones; + for ( int i = 0; i < n_zones; i++ ) + delete zones[i]; + delete[] zones; + n_zones = p_n_zones; + zones = p_zones; +} + +void Monitor::AddPrivacyBitmask( Zone *p_zones[] ) +{ + if ( privacy_bitmask ) + delete[] privacy_bitmask; + privacy_bitmask = NULL; + Image *privacy_image = NULL; + + for ( int i = 0; i < n_zones; i++ ) + { + if ( p_zones[i]->IsPrivacy() ) + { + if ( !privacy_image ) + { + privacy_image = new Image( width, height, 1, ZM_SUBPIX_ORDER_NONE); + privacy_image->Clear(); + } + privacy_image->Fill( 0xff, p_zones[i]->GetPolygon() ); + privacy_image->Outline( 0xff, p_zones[i]->GetPolygon() ); + } + } // end foreach zone + if ( privacy_image ) + privacy_bitmask = privacy_image->Buffer(); } Monitor::State Monitor::GetState() const { - return( (State)shared_data->state ); + return( (State)shared_data->state ); } -int Monitor::GetImage( int index, int scale ) const +int Monitor::GetImage( int index, int scale ) { - if ( index < 0 || index > image_buffer_count ) - { - index = shared_data->last_write_index; + if ( index < 0 || index > image_buffer_count ) + { + index = shared_data->last_write_index; + } + + if ( index != image_buffer_count ) + { + Image *image; + // If we are going to be modifying the snapshot before writing, then we need to copy it + if ( ( scale != ZM_SCALE_BASE ) || ( !config.timestamp_on_capture ) ) { + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; + + alarm_image.Assign( *snap_image ); + + + //write_image.Assign( *snap_image ); + + if ( scale != ZM_SCALE_BASE ) { + alarm_image.Scale( scale ); + } + + if ( !config.timestamp_on_capture ) { + TimestampImage( &alarm_image, snap->timestamp ); + } + image = &alarm_image; + } else { + image = image_buffer[index].image; } - if ( index != image_buffer_count ) - { - Snapshot *snap = &image_buffer[index]; - Image snap_image( *(snap->image) ); - - if ( scale != ZM_SCALE_BASE ) - { - snap_image.Scale( scale ); - } - - static char filename[PATH_MAX]; - snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); - if ( !config.timestamp_on_capture ) - { - TimestampImage( &snap_image, snap->timestamp ); - } - snap_image.WriteJpeg( filename ); - } - else - { - Error( "Unable to generate image, no images in buffer" ); - } - return( 0 ); + static char filename[PATH_MAX]; + snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); + image->WriteJpeg( filename ); + } + else + { + Error( "Unable to generate image, no images in buffer" ); + } + return( 0 ); } struct timeval Monitor::GetTimestamp( int index ) const { - if ( index < 0 || index > image_buffer_count ) - { - index = shared_data->last_write_index; - } + if ( index < 0 || index > image_buffer_count ) + { + index = shared_data->last_write_index; + } - if ( index != image_buffer_count ) - { - Snapshot *snap = &image_buffer[index]; + if ( index != image_buffer_count ) + { + Snapshot *snap = &image_buffer[index]; - return( *(snap->timestamp) ); - } - else - { - static struct timeval null_tv = { 0, 0 }; + return( *(snap->timestamp) ); + } + else + { + static struct timeval null_tv = { 0, 0 }; - return( null_tv ); - } + return( null_tv ); + } } unsigned int Monitor::GetLastReadIndex() const { - return( shared_data->last_read_index!=(unsigned int)image_buffer_count?shared_data->last_read_index:-1 ); + return( shared_data->last_read_index!=(unsigned int)image_buffer_count?shared_data->last_read_index:-1 ); } unsigned int Monitor::GetLastWriteIndex() const { - return( shared_data->last_write_index!=(unsigned int)image_buffer_count?shared_data->last_write_index:-1 ); + return( shared_data->last_write_index!=(unsigned int)image_buffer_count?shared_data->last_write_index:-1 ); } unsigned int Monitor::GetLastEvent() const { - return( shared_data->last_event ); + return( shared_data->last_event ); } double Monitor::GetFPS() const { - int index1 = shared_data->last_write_index; - if ( index1 == image_buffer_count ) - { - return( 0.0 ); - } - Snapshot *snap1 = &image_buffer[index1]; - if ( !snap1->timestamp || !snap1->timestamp->tv_sec ) - { - return( 0.0 ); - } - struct timeval time1 = *snap1->timestamp; + int index1 = shared_data->last_write_index; + if ( index1 == image_buffer_count ) + { + return( 0.0 ); + } + Snapshot *snap1 = &image_buffer[index1]; + if ( !snap1->timestamp || !snap1->timestamp->tv_sec ) + { + return( 0.0 ); + } + struct timeval time1 = *snap1->timestamp; - int image_count = image_buffer_count; - int index2 = (index1+1)%image_buffer_count; - if ( index2 == image_buffer_count ) + int image_count = image_buffer_count; + int index2 = (index1+1)%image_buffer_count; + if ( index2 == image_buffer_count ) + { + return( 0.0 ); + } + Snapshot *snap2 = &image_buffer[index2]; + while ( !snap2->timestamp || !snap2->timestamp->tv_sec ) + { + if ( index1 == index2 ) { - return( 0.0 ); + return( 0.0 ); } - Snapshot *snap2 = &image_buffer[index2]; - while ( !snap2->timestamp || !snap2->timestamp->tv_sec ) + index2 = (index2+1)%image_buffer_count; + snap2 = &image_buffer[index2]; + image_count--; + } + struct timeval time2 = *snap2->timestamp; + + double time_diff = tvDiffSec( time2, time1 ); + + double curr_fps = image_count/time_diff; + + if ( curr_fps < 0.0 ) + { + //Error( "Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count ); + return( 0.0 ); + } + return( curr_fps ); +} + +useconds_t Monitor::GetAnalysisRate() +{ + double capturing_fps = GetFPS(); + if ( !analysis_fps ) + { + return( 0 ); + } + else if ( analysis_fps > capturing_fps ) + { + Warning( "Analysis fps (%.2f) is greater than capturing fps (%.2f)", analysis_fps, capturing_fps ); + return( 0 ); + } + else + { + return( ( 1000000 / analysis_fps ) - ( 1000000 / capturing_fps ) ); + } +} + +void Monitor::UpdateAdaptiveSkip() +{ + if ( config.opt_adaptive_skip ) + { + double capturing_fps = GetFPS(); + if ( adaptive_skip && analysis_fps && ( analysis_fps < capturing_fps ) ) { - if ( index1 == index2 ) - { - return( 0.0 ); - } - index2 = (index2+1)%image_buffer_count; - snap2 = &image_buffer[index2]; - image_count--; + Info( "Analysis fps (%.2f) is lower than capturing fps (%.2f), disabling adaptive skip feature", analysis_fps, capturing_fps ); + adaptive_skip = false; } - struct timeval time2 = *snap2->timestamp; - - double time_diff = tvDiffSec( time2, time1 ); - - double curr_fps = image_count/time_diff; - - if ( curr_fps < 0.0 ) + else if ( !adaptive_skip && ( !analysis_fps || ( analysis_fps >= capturing_fps ) ) ) { - //Error( "Negative FPS %f, time_diff = %lf (%d:%ld.%ld - %d:%ld.%ld), ibc: %d", curr_fps, time_diff, index2, time2.tv_sec, time2.tv_usec, index1, time1.tv_sec, time1.tv_usec, image_buffer_count ); - return( 0.0 ); + Info( "Enabling adaptive skip feature" ); + adaptive_skip = true; } - return( curr_fps ); + } + else + { + adaptive_skip = false; + } } void Monitor::ForceAlarmOn( int force_score, const char *force_cause, const char *force_text ) { - trigger_data->trigger_state = TRIGGER_ON; - trigger_data->trigger_score = force_score; - strncpy( trigger_data->trigger_cause, force_cause, sizeof(trigger_data->trigger_cause) ); - strncpy( trigger_data->trigger_text, force_text, sizeof(trigger_data->trigger_text) ); + trigger_data->trigger_state = TRIGGER_ON; + trigger_data->trigger_score = force_score; + strncpy( trigger_data->trigger_cause, force_cause, sizeof(trigger_data->trigger_cause) ); + strncpy( trigger_data->trigger_text, force_text, sizeof(trigger_data->trigger_text) ); } void Monitor::ForceAlarmOff() { - trigger_data->trigger_state = TRIGGER_OFF; + trigger_data->trigger_state = TRIGGER_OFF; } void Monitor::CancelForced() { - trigger_data->trigger_state = TRIGGER_CANCEL; + trigger_data->trigger_state = TRIGGER_CANCEL; } void Monitor::actionReload() { - shared_data->action |= RELOAD; + shared_data->action |= RELOAD; } void Monitor::actionEnable() { - shared_data->action |= RELOAD; + shared_data->action |= RELOAD; - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "update Monitors set Enabled = 1 where Id = '%d'", id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf( sql, sizeof(sql), "update Monitors set Enabled = 1 where Id = '%d'", id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } void Monitor::actionDisable() { - shared_data->action |= RELOAD; + shared_data->action |= RELOAD; - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "update Monitors set Enabled = 0 where Id = '%d'", id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf( sql, sizeof(sql), "update Monitors set Enabled = 0 where Id = '%d'", id ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } void Monitor::actionSuspend() { - shared_data->action |= SUSPEND; + shared_data->action |= SUSPEND; } void Monitor::actionResume() { - shared_data->action |= RESUME; + shared_data->action |= RESUME; } int Monitor::actionBrightness( int p_brightness ) { - if ( purpose != CAPTURE ) + if ( purpose != CAPTURE ) + { + if ( p_brightness >= 0 ) { - if ( p_brightness >= 0 ) - { - shared_data->brightness = p_brightness; - shared_data->action |= SET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & SET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to set brightness" ); - return( -1 ); - } - } - } + shared_data->brightness = p_brightness; + shared_data->action |= SET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & SET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); else { - shared_data->action |= GET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & GET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to get brightness" ); - return( -1 ); - } - } + Warning( "Timed out waiting to set brightness" ); + return( -1 ); } - return( shared_data->brightness ); + } } - return( camera->Brightness( p_brightness ) ); + else + { + shared_data->action |= GET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & GET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); + else + { + Warning( "Timed out waiting to get brightness" ); + return( -1 ); + } + } + } + return( shared_data->brightness ); + } + return( camera->Brightness( p_brightness ) ); } int Monitor::actionContrast( int p_contrast ) { - if ( purpose != CAPTURE ) + if ( purpose != CAPTURE ) + { + if ( p_contrast >= 0 ) { - if ( p_contrast >= 0 ) - { - shared_data->contrast = p_contrast; - shared_data->action |= SET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & SET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to set contrast" ); - return( -1 ); - } - } - } + shared_data->contrast = p_contrast; + shared_data->action |= SET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & SET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); else { - shared_data->action |= GET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & GET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to get contrast" ); - return( -1 ); - } - } + Warning( "Timed out waiting to set contrast" ); + return( -1 ); } - return( shared_data->contrast ); + } } - return( camera->Contrast( p_contrast ) ); + else + { + shared_data->action |= GET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & GET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); + else + { + Warning( "Timed out waiting to get contrast" ); + return( -1 ); + } + } + } + return( shared_data->contrast ); + } + return( camera->Contrast( p_contrast ) ); } int Monitor::actionHue( int p_hue ) { - if ( purpose != CAPTURE ) + if ( purpose != CAPTURE ) + { + if ( p_hue >= 0 ) { - if ( p_hue >= 0 ) - { - shared_data->hue = p_hue; - shared_data->action |= SET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & SET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to set hue" ); - return( -1 ); - } - } - } + shared_data->hue = p_hue; + shared_data->action |= SET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & SET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); else { - shared_data->action |= GET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & GET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to get hue" ); - return( -1 ); - } - } + Warning( "Timed out waiting to set hue" ); + return( -1 ); } - return( shared_data->hue ); + } } - return( camera->Hue( p_hue ) ); + else + { + shared_data->action |= GET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & GET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); + else + { + Warning( "Timed out waiting to get hue" ); + return( -1 ); + } + } + } + return( shared_data->hue ); + } + return( camera->Hue( p_hue ) ); } int Monitor::actionColour( int p_colour ) { - if ( purpose != CAPTURE ) + if ( purpose != CAPTURE ) + { + if ( p_colour >= 0 ) { - if ( p_colour >= 0 ) - { - shared_data->colour = p_colour; - shared_data->action |= SET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & SET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to set colour" ); - return( -1 ); - } - } - } + shared_data->colour = p_colour; + shared_data->action |= SET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & SET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); else { - shared_data->action |= GET_SETTINGS; - int wait_loops = 10; - while ( shared_data->action & GET_SETTINGS ) - { - if ( wait_loops-- ) - usleep( 100000 ); - else - { - Warning( "Timed out waiting to get colour" ); - return( -1 ); - } - } + Warning( "Timed out waiting to set colour" ); + return( -1 ); } - return( shared_data->colour ); + } } - return( camera->Colour( p_colour ) ); + else + { + shared_data->action |= GET_SETTINGS; + int wait_loops = 10; + while ( shared_data->action & GET_SETTINGS ) + { + if ( wait_loops-- ) + usleep( 100000 ); + else + { + Warning( "Timed out waiting to get colour" ); + return( -1 ); + } + } + } + return( shared_data->colour ); + } + return( camera->Colour( p_colour ) ); } void Monitor::DumpZoneImage( const char *zone_string ) { - int exclude_id = 0; - int extra_colour = 0; - Polygon extra_zone; + int exclude_id = 0; + int extra_colour = 0; + Polygon extra_zone; - if ( zone_string ) + if ( zone_string ) + { + if ( !Zone::ParseZoneString( zone_string, exclude_id, extra_colour, extra_zone ) ) { - if ( !Zone::ParseZoneString( zone_string, exclude_id, extra_colour, extra_zone ) ) - { - Error( "Failed to parse zone string, ignoring" ); - } + Error( "Failed to parse zone string, ignoring" ); } + } - int index = shared_data->last_write_index; - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; + int index = shared_data->last_write_index; + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; - Image zone_image( *snap_image ); - if(zone_image.Colours() == ZM_COLOUR_GRAY8) { - zone_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); - } - - for( int i = 0; i < n_zones; i++ ) + Image zone_image( *snap_image ); + if(zone_image.Colours() == ZM_COLOUR_GRAY8) { + zone_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB ); + } + + for( int i = 0; i < n_zones; i++ ) + { + if ( exclude_id && (!extra_colour || extra_zone.getNumCoords()) && zones[i]->Id() == exclude_id ) + continue; + + Rgb colour; + if ( exclude_id && !extra_zone.getNumCoords() && zones[i]->Id() == exclude_id ) { - if ( exclude_id && (!extra_colour || extra_zone.getNumCoords()) && zones[i]->Id() == exclude_id ) - continue; - - Rgb colour; - if ( exclude_id && !extra_zone.getNumCoords() && zones[i]->Id() == exclude_id ) - { - colour = extra_colour; - } - else - { - if ( zones[i]->IsActive() ) - { - colour = RGB_RED; - } - else if ( zones[i]->IsInclusive() ) - { - colour = RGB_ORANGE; - } - else if ( zones[i]->IsExclusive() ) - { - colour = RGB_PURPLE; - } - else if ( zones[i]->IsPreclusive() ) - { - colour = RGB_BLUE; - } - else - { - colour = RGB_WHITE; - } - } - zone_image.Fill( colour, 2, zones[i]->GetPolygon() ); - zone_image.Outline( colour, zones[i]->GetPolygon() ); + colour = extra_colour; } - - if ( extra_zone.getNumCoords() ) + else { - zone_image.Fill( extra_colour, 2, extra_zone ); - zone_image.Outline( extra_colour, extra_zone ); + if ( zones[i]->IsActive() ) + { + colour = RGB_RED; + } + else if ( zones[i]->IsInclusive() ) + { + colour = RGB_ORANGE; + } + else if ( zones[i]->IsExclusive() ) + { + colour = RGB_PURPLE; + } + else if ( zones[i]->IsPreclusive() ) + { + colour = RGB_BLUE; + } + else + { + colour = RGB_WHITE; + } } + zone_image.Fill( colour, 2, zones[i]->GetPolygon() ); + zone_image.Outline( colour, zones[i]->GetPolygon() ); + } - static char filename[PATH_MAX]; - snprintf( filename, sizeof(filename), "Zones%d.jpg", id ); - zone_image.WriteJpeg( filename ); + if ( extra_zone.getNumCoords() ) + { + zone_image.Fill( extra_colour, 2, extra_zone ); + zone_image.Outline( extra_colour, extra_zone ); + } + + static char filename[PATH_MAX]; + snprintf( filename, sizeof(filename), "Zones%d.jpg", id ); + zone_image.WriteJpeg( filename ); } void Monitor::DumpImage( Image *dump_image ) const { - if ( image_count && !(image_count%10) ) - { - static char filename[PATH_MAX]; - static char new_filename[PATH_MAX]; - snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); - snprintf( new_filename, sizeof(new_filename), "Monitor%d-new.jpg", id ); - dump_image->WriteJpeg( new_filename ); - rename( new_filename, filename ); - } + if ( image_count && !(image_count%10) ) + { + static char filename[PATH_MAX]; + static char new_filename[PATH_MAX]; + snprintf( filename, sizeof(filename), "Monitor%d.jpg", id ); + snprintf( new_filename, sizeof(new_filename), "Monitor%d-new.jpg", id ); + dump_image->WriteJpeg( new_filename ); + rename( new_filename, filename ); + } } bool Monitor::CheckSignal( const Image *image ) { - static bool static_undef = true; - /* RGB24 colors */ - static uint8_t red_val; - static uint8_t green_val; - static uint8_t blue_val; - static uint8_t grayscale_val; /* 8bit grayscale color */ - static Rgb colour_val; /* RGB32 color */ - static int usedsubpixorder; + static bool static_undef = true; + /* RGB24 colors */ + static uint8_t red_val; + static uint8_t green_val; + static uint8_t blue_val; + static uint8_t grayscale_val; /* 8bit grayscale color */ + static Rgb colour_val; /* RGB32 color */ + static int usedsubpixorder; - if ( config.signal_check_points > 0 ) + if ( config.signal_check_points > 0 ) + { + if ( static_undef ) { - if ( static_undef ) - { - static_undef = false; - usedsubpixorder = camera->SubpixelOrder(); - colour_val = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */ - colour_val = rgb_convert(colour_val, usedsubpixorder); - red_val = RED_VAL_BGRA(signal_check_colour); - green_val = GREEN_VAL_BGRA(signal_check_colour); - blue_val = BLUE_VAL_BGRA(signal_check_colour); - grayscale_val = signal_check_colour & 0xff; /* Clear all bytes but lowest byte */ - } - - const uint8_t *buffer = image->Buffer(); - int pixels = image->Pixels(); - int width = image->Width(); - int colours = image->Colours(); - - int index = 0; - for ( int i = 0; i < config.signal_check_points; i++ ) - { - while( true ) - { - index = (int)(((long long)rand()*(long long)(pixels-1))/RAND_MAX); - if ( !config.timestamp_on_capture || !label_format[0] ) - break; - // Avoid sampling the rows with timestamp in - if ( index < (label_coord.Y()*width) || index >= (label_coord.Y()+Image::LINE_HEIGHT)*width ) - break; - } - - if(colours == ZM_COLOUR_GRAY8) { - if ( *(buffer+index) != grayscale_val ) - return true; - - } else if(colours == ZM_COLOUR_RGB24) { - const uint8_t *ptr = buffer+(index*colours); - - if ( usedsubpixorder == ZM_SUBPIX_ORDER_BGR) { - if ( (RED_PTR_BGRA(ptr) != red_val) || (GREEN_PTR_BGRA(ptr) != green_val) || (BLUE_PTR_BGRA(ptr) != blue_val) ) - return true; - } else { - /* Assume RGB */ - if ( (RED_PTR_RGBA(ptr) != red_val) || (GREEN_PTR_RGBA(ptr) != green_val) || (BLUE_PTR_RGBA(ptr) != blue_val) ) - return true; - } - - } else if(colours == ZM_COLOUR_RGB32) { - if ( usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR) { - if ( ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val) ) - return true; - } else { - /* Assume RGBA or BGRA */ - if ( RGBA_BGRA_ZEROALPHA(*(((const Rgb*)buffer)+index)) != RGBA_BGRA_ZEROALPHA(colour_val) ) - return true; - } - } - - } - return( false ); + static_undef = false; + usedsubpixorder = camera->SubpixelOrder(); + colour_val = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */ + colour_val = rgb_convert(colour_val, usedsubpixorder); + red_val = RED_VAL_BGRA(signal_check_colour); + green_val = GREEN_VAL_BGRA(signal_check_colour); + blue_val = BLUE_VAL_BGRA(signal_check_colour); + grayscale_val = signal_check_colour & 0xff; /* Clear all bytes but lowest byte */ } - return( true ); + + const uint8_t *buffer = image->Buffer(); + int pixels = image->Pixels(); + int width = image->Width(); + int colours = image->Colours(); + + int index = 0; + for ( int i = 0; i < config.signal_check_points; i++ ) + { + while( true ) + { + index = (int)(((long long)rand()*(long long)(pixels-1))/RAND_MAX); + if ( !config.timestamp_on_capture || !label_format[0] ) + break; + // Avoid sampling the rows with timestamp in + if ( index < (label_coord.Y()*width) || index >= (label_coord.Y()+Image::LINE_HEIGHT)*width ) + break; + } + + if(colours == ZM_COLOUR_GRAY8) { + if ( *(buffer+index) != grayscale_val ) + return true; + + } else if(colours == ZM_COLOUR_RGB24) { + const uint8_t *ptr = buffer+(index*colours); + + if ( usedsubpixorder == ZM_SUBPIX_ORDER_BGR) { + if ( (RED_PTR_BGRA(ptr) != red_val) || (GREEN_PTR_BGRA(ptr) != green_val) || (BLUE_PTR_BGRA(ptr) != blue_val) ) + return true; + } else { + /* Assume RGB */ + if ( (RED_PTR_RGBA(ptr) != red_val) || (GREEN_PTR_RGBA(ptr) != green_val) || (BLUE_PTR_RGBA(ptr) != blue_val) ) + return true; + } + + } else if(colours == ZM_COLOUR_RGB32) { + if ( usedsubpixorder == ZM_SUBPIX_ORDER_ARGB || usedsubpixorder == ZM_SUBPIX_ORDER_ABGR) { + if ( ARGB_ABGR_ZEROALPHA(*(((const Rgb*)buffer)+index)) != ARGB_ABGR_ZEROALPHA(colour_val) ) + return true; + } else { + /* Assume RGBA or BGRA */ + if ( RGBA_BGRA_ZEROALPHA(*(((const Rgb*)buffer)+index)) != RGBA_BGRA_ZEROALPHA(colour_val) ) + return true; + } + } + + } + return( false ); + } + return( true ); } bool Monitor::Analyse() { - if ( shared_data->last_read_index == shared_data->last_write_index ) + if ( shared_data->last_read_index == shared_data->last_write_index ) + { + return( false ); + } + + struct timeval now; + gettimeofday( &now, NULL ); + + if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) + { + fps = double(fps_report_interval)/(now.tv_sec-last_fps_time); + Info( "%s: %d - Analysing at %.2f fps", name, image_count, fps ); + last_fps_time = now.tv_sec; + } + + int index; + if ( adaptive_skip ) + { + int read_margin = shared_data->last_read_index - shared_data->last_write_index; + if ( read_margin < 0 ) read_margin += image_buffer_count; + + int step = 1; + if ( read_margin > 0 ) { - return( false ); + step = (9*image_buffer_count)/(5*read_margin); } - struct timeval now; - gettimeofday( &now, NULL ); + int pending_frames = shared_data->last_write_index - shared_data->last_read_index; + if ( pending_frames < 0 ) pending_frames += image_buffer_count; - if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) + Debug( 4, "RI:%d, WI: %d, PF = %d, RM = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step ); + if ( step <= pending_frames ) { - fps = double(fps_report_interval)/(now.tv_sec-last_fps_time); - Info( "%s: %d - Processing at %.2f fps", name, image_count, fps ); - last_fps_time = now.tv_sec; - } - - int index; - if ( config.opt_adaptive_skip ) - { - int read_margin = shared_data->last_read_index - shared_data->last_write_index; - if ( read_margin < 0 ) read_margin += image_buffer_count; - - int step = 1; - if ( read_margin > 0 ) - { - step = (9*image_buffer_count)/(5*read_margin); - } - - int pending_frames = shared_data->last_write_index - shared_data->last_read_index; - if ( pending_frames < 0 ) pending_frames += image_buffer_count; - - Debug( 4, "RI:%d, WI: %d, PF = %d, RM = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step ); - if ( step <= pending_frames ) - { - index = (shared_data->last_read_index+step)%image_buffer_count; - } - else - { - if ( pending_frames ) - { - Warning( "Approaching buffer overrun, consider slowing capture, simplifying analysis or increasing ring buffer size" ); - } - index = shared_data->last_write_index%image_buffer_count; - } + index = (shared_data->last_read_index+step)%image_buffer_count; } else { - index = shared_data->last_write_index%image_buffer_count; + if ( pending_frames ) + { + Warning( "Approaching buffer overrun, consider slowing capture, simplifying analysis or increasing ring buffer size" ); + } + index = shared_data->last_write_index%image_buffer_count; } + } + else + { + index = shared_data->last_write_index%image_buffer_count; + } - Snapshot *snap = &image_buffer[index]; - struct timeval *timestamp = snap->timestamp; - Image *snap_image = snap->image; + Snapshot *snap = &image_buffer[index]; + struct timeval *timestamp = snap->timestamp; + Image *snap_image = snap->image; - if ( shared_data->action ) + if ( shared_data->action ) + { + if ( shared_data->action & RELOAD ) { - if ( shared_data->action & RELOAD ) - { - Info( "Received reload indication at count %d", image_count ); - shared_data->action &= ~RELOAD; - Reload(); - } - if ( shared_data->action & SUSPEND ) - { - if ( Active() ) - { - Info( "Received suspend indication at count %d", image_count ); - shared_data->active = false; - //closeEvent(); - } - if ( config.max_suspend_time ) - { - auto_resume_time = now.tv_sec + config.max_suspend_time; - } - shared_data->action &= ~SUSPEND; - } - if ( shared_data->action & RESUME ) - { - if ( Enabled() && !Active() ) - { - Info( "Received resume indication at count %d", image_count ); - shared_data->active = true; - ref_image = *snap_image; - ready_count = image_count+(warmup_count/2); - shared_data->alarm_x = shared_data->alarm_y = -1; - } - shared_data->action &= ~RESUME; - } + Info( "Received reload indication at count %d", image_count ); + shared_data->action &= ~RELOAD; + Reload(); } - if ( auto_resume_time && (now.tv_sec >= auto_resume_time) ) + if ( shared_data->action & SUSPEND ) { - Info( "Auto resuming at count %d", image_count ); + if ( Active() ) + { + Info( "Received suspend indication at count %d", image_count ); + shared_data->active = false; + //closeEvent(); + } + if ( config.max_suspend_time ) + { + auto_resume_time = now.tv_sec + config.max_suspend_time; + } + shared_data->action &= ~SUSPEND; + } + if ( shared_data->action & RESUME ) + { + if ( Enabled() && !Active() ) + { + Info( "Received resume indication at count %d", image_count ); shared_data->active = true; ref_image = *snap_image; ready_count = image_count+(warmup_count/2); - auto_resume_time = 0; + shared_data->alarm_x = shared_data->alarm_y = -1; + } + shared_data->action &= ~RESUME; } + } + if ( auto_resume_time && (now.tv_sec >= auto_resume_time) ) + { + Info( "Auto resuming at count %d", image_count ); + shared_data->active = true; + ref_image = *snap_image; + ready_count = image_count+(warmup_count/2); + auto_resume_time = 0; + } - static bool static_undef = true; - static struct timeval **timestamps; - static Image **images; - static int last_section_mod = 0; - static bool last_signal; + static bool static_undef = true; + static int last_section_mod = 0; + static bool last_signal; - if ( static_undef ) + if ( static_undef ) + { + static_undef = false; + timestamps = new struct timeval *[pre_event_count]; + images = new Image *[pre_event_count]; + last_signal = shared_data->signal; + } + + if ( Enabled() ) + { + bool signal = shared_data->signal; + bool signal_change = (signal != last_signal); + if ( trigger_data->trigger_state != TRIGGER_OFF ) { - static_undef = false; - timestamps = new struct timeval *[pre_event_count]; - images = new Image *[pre_event_count]; - last_signal = shared_data->signal; - } + unsigned int score = 0; + if ( Ready() ) + { + std::string cause; + Event::StringSetMap noteSetMap; - if ( Enabled() ) - { - bool signal = shared_data->signal; - bool signal_change = (signal != last_signal); - if ( trigger_data->trigger_state != TRIGGER_OFF ) + if ( trigger_data->trigger_state == TRIGGER_ON ) { - unsigned int score = 0; - if ( Ready() ) + score += trigger_data->trigger_score; + if ( !event ) + { + if ( cause.length() ) + cause += ", "; + cause += trigger_data->trigger_cause; + } + Event::StringSet noteSet; + noteSet.insert( trigger_data->trigger_text ); + noteSetMap[trigger_data->trigger_cause] = noteSet; + } + if ( signal_change ) + { + const char *signalText; + if ( !signal ) + signalText = "Lost"; + else + { + signalText = "Reacquired"; + score += 100; + } + Warning( "%s: %s", SIGNAL_CAUSE, signalText ); + if ( event && !signal ) + { + Info( "%s: %03d - Closing event %d, signal loss", name, image_count, event->Id() ); + closeEvent(); + last_section_mod = 0; + } + if ( !event ) + { + if ( cause.length() ) + cause += ", "; + cause += SIGNAL_CAUSE; + } + Event::StringSet noteSet; + noteSet.insert( signalText ); + noteSetMap[SIGNAL_CAUSE] = noteSet; + shared_data->state = state = IDLE; + shared_data->active = signal; + ref_image = *snap_image; + } + else if ( signal && Active() && (function == MODECT || function == MOCORD) ) + { + Event::StringSet zoneSet; + int motion_score = last_motion_score; + if ( !(image_count % (motion_frame_skip+1) ) ) + { + // Get new score. + motion_score = last_motion_score = DetectMotion( *snap_image, zoneSet ); + } + //int motion_score = DetectBlack( *snap_image, zoneSet ); + if ( motion_score ) + { + if ( !event ) { - std::string cause; - Event::StringSetMap noteSetMap; + score += motion_score; + if ( cause.length() ) + cause += ", "; + cause += MOTION_CAUSE; + } + else + { + score += motion_score; + } + noteSetMap[MOTION_CAUSE] = zoneSet; - if ( trigger_data->trigger_state == TRIGGER_ON ) + } + shared_data->active = signal; + } + if ( (!signal_change && signal) && n_linked_monitors > 0 ) + { + bool first_link = true; + Event::StringSet noteSet; + for ( int i = 0; i < n_linked_monitors; i++ ) + { + if ( linked_monitors[i]->isConnected() ) + { + if ( linked_monitors[i]->hasAlarmed() ) + { + if ( !event ) { - score += trigger_data->trigger_score; - if ( !event ) - { - if ( cause.length() ) - cause += ", "; - cause += trigger_data->trigger_cause; - } - Event::StringSet noteSet; - noteSet.insert( trigger_data->trigger_text ); - noteSetMap[trigger_data->trigger_cause] = noteSet; + if ( first_link ) + { + if ( cause.length() ) + cause += ", "; + cause += LINKED_CAUSE; + first_link = false; + } } - if ( signal_change ) + noteSet.insert( linked_monitors[i]->Name() ); + score += 50; + } + } + else + { + linked_monitors[i]->connect(); + } + } + if ( noteSet.size() > 0 ) + noteSetMap[LINKED_CAUSE] = noteSet; + } + if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) + { + if ( event ) + { + int section_mod = timestamp->tv_sec%section_length; + if ( section_mod < last_section_mod ) + { + if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) + { + if ( state == TAPE ) { - const char *signalText; - if ( !signal ) - signalText = "Lost"; - else - { - signalText = "Reacquired"; - score += 100; - } - Warning( "%s: %s", SIGNAL_CAUSE, signalText ); - if ( event && !signal ) - { - Info( "%s: %03d - Closing event %d, signal loss", name, image_count, event->Id() ); - closeEvent(); - last_section_mod = 0; - } - if ( !event ) - { - if ( cause.length() ) - cause += ", "; - cause += SIGNAL_CAUSE; - } - Event::StringSet noteSet; - noteSet.insert( signalText ); - noteSetMap[SIGNAL_CAUSE] = noteSet; - shared_data->state = state = IDLE; - shared_data->active = signal; - ref_image = *snap_image; + shared_data->state = state = IDLE; + Info( "%s: %03d - Closing event %d, section end", name, image_count, event->Id() ) } - else if ( signal && Active() && (function == MODECT || function == MOCORD) ) - { - Event::StringSet zoneSet; - int motion_score = last_motion_score; - if ( !(image_count % (motion_frame_skip+1) ) ) - { - // Get new score. - motion_score = last_motion_score = DetectMotion( *snap_image, zoneSet ); - } - //int motion_score = DetectBlack( *snap_image, zoneSet ); - if ( motion_score ) - { - if ( !event ) - { - score += motion_score; - if ( cause.length() ) - cause += ", "; - cause += MOTION_CAUSE; - } - else - { - score += motion_score; - } - noteSetMap[MOTION_CAUSE] = zoneSet; + else + Info( "%s: %03d - Closing event %d, section end forced ", name, image_count, event->Id() ); + closeEvent(); + last_section_mod = 0; + } + } + else + { + last_section_mod = section_mod; + } + } + if ( !event ) + { - } - shared_data->active = signal; + // Create event + event = new Event( this, *timestamp, "Continuous", noteSetMap ); + shared_data->last_event = event->Id(); + + Info( "%s: %03d - Opening new event %d, section start", name, image_count, event->Id() ); + + /* To prevent cancelling out an existing alert\prealarm\alarm state */ + if ( state == IDLE ) + { + shared_data->state = state = TAPE; + } + + //if ( config.overlap_timed_events ) + if ( false ) + { + int pre_index; + int pre_event_images = pre_event_count; + + if ( analysis_fps ) + { + // If analysis fps is set, + // compute the index for pre event images in the dedicated buffer + pre_index = image_count%pre_event_buffer_count; + + // Seek forward the next filled slot in to the buffer (oldest data) + // from the current position + while ( pre_event_images && !pre_event_buffer[pre_index].timestamp->tv_sec ) + { + pre_index = (pre_index + 1)%pre_event_buffer_count; + // Slot is empty, removing image from counter + pre_event_images--; } - if ( (!signal_change && signal) && n_linked_monitors > 0 ) + } + else + { + // If analysis fps is not set (analysis performed at capturing framerate), + // compute the index for pre event images in the capturing buffer + pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count; + + // Seek forward the next filled slot in to the buffer (oldest data) + // from the current position + while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) { - bool first_link = true; - Event::StringSet noteSet; - for ( int i = 0; i < n_linked_monitors; i++ ) - { - if ( linked_monitors[i]->isConnected() ) - { - if ( linked_monitors[i]->hasAlarmed() ) - { - if ( !event ) - { - if ( first_link ) - { - if ( cause.length() ) - cause += ", "; - cause += LINKED_CAUSE; - first_link = false; - } - } - noteSet.insert( linked_monitors[i]->Name() ); - score += 50; - } - } - else - { - linked_monitors[i]->connect(); - } - } - if ( noteSet.size() > 0 ) - noteSetMap[LINKED_CAUSE] = noteSet; + pre_index = (pre_index + 1)%image_buffer_count; + // Slot is empty, removing image from counter + pre_event_images--; } - if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) + } + + if ( pre_event_images ) + { + if ( analysis_fps ) + for ( int i = 0; i < pre_event_images; i++ ) + { + timestamps[i] = pre_event_buffer[pre_index].timestamp; + images[i] = pre_event_buffer[pre_index].image; + pre_index = (pre_index + 1)%pre_event_buffer_count; + } + else + for ( int i = 0; i < pre_event_images; i++ ) + { + timestamps[i] = image_buffer[pre_index].timestamp; + images[i] = image_buffer[pre_index].image; + pre_index = (pre_index + 1)%image_buffer_count; + } + + event->AddFrames( pre_event_images, images, timestamps ); + } + } + } + } + if ( score ) + { + if ( (state == IDLE || state == TAPE || state == PREALARM ) ) + { + if ( Event::PreAlarmCount() >= (alarm_frame_count-1) ) + { + Info( "%s: %03d - Gone into alarm state", name, image_count ); + shared_data->state = state = ALARM; + if ( signal_change || (function != MOCORD && state != ALERT) ) + { + int pre_index; + int pre_event_images = pre_event_count; + + if ( analysis_fps ) { - if ( event ) - { - int section_mod = timestamp->tv_sec%section_length; - if ( section_mod < last_section_mod ) - { - if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) - { - if ( state == TAPE ) - { - shared_data->state = state = IDLE; - Info( "%s: %03d - Closing event %d, section end", name, image_count, event->Id() ) - } - else - Info( "%s: %03d - Closing event %d, section end forced ", name, image_count, event->Id() ); - closeEvent(); - last_section_mod = 0; - } - } - else - { - last_section_mod = section_mod; - } - } - if ( !event ) - { + // If analysis fps is set, + // compute the index for pre event images in the dedicated buffer + pre_index = image_count%pre_event_buffer_count; - // Create event - event = new Event( this, *timestamp, "Continuous", noteSetMap ); - shared_data->last_event = event->Id(); + // Seek forward the next filled slot in to the buffer (oldest data) + // from the current position + while ( pre_event_images && !pre_event_buffer[pre_index].timestamp->tv_sec ) + { + pre_index = (pre_index + 1)%pre_event_buffer_count; + // Slot is empty, removing image from counter + pre_event_images--; + } - Info( "%s: %03d - Opening new event %d, section start", name, image_count, event->Id() ); - - /* To prevent cancelling out an existing alert\prealarm\alarm state */ - if ( state == IDLE ) - { - shared_data->state = state = TAPE; - } - - //if ( config.overlap_timed_events ) - if ( false ) - { - int pre_index = ((index+image_buffer_count)-pre_event_count)%image_buffer_count; - int pre_event_images = pre_event_count; - while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) - { - pre_index = (pre_index+1)%image_buffer_count; - pre_event_images--; - } - - if ( pre_event_images ) - { - for ( int i = 0; i < pre_event_images; i++ ) - { - timestamps[i] = image_buffer[pre_index].timestamp; - images[i] = image_buffer[pre_index].image; - - pre_index = (pre_index+1)%image_buffer_count; - } - event->AddFrames( pre_event_images, images, timestamps ); - } - } - } - } - if ( score ) - { - if ( (state == IDLE || state == TAPE || state == PREALARM ) ) - { - if ( Event::PreAlarmCount() >= (alarm_frame_count-1) ) - { - Info( "%s: %03d - Gone into alarm state", name, image_count ); - shared_data->state = state = ALARM; - if ( signal_change || (function != MOCORD && state != ALERT) ) - { - int pre_index; - if ( alarm_frame_count > 1 ) - pre_index = ((index+image_buffer_count)-((alarm_frame_count-1)+pre_event_count))%image_buffer_count; - else - pre_index = ((index+image_buffer_count)-pre_event_count)%image_buffer_count; - - int pre_event_images = pre_event_count; - while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) - { - pre_index = (pre_index+1)%image_buffer_count; - pre_event_images--; - } - - event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap ); - shared_data->last_event = event->Id(); - - Info( "%s: %03d - Opening new event %d, alarm start", name, image_count, event->Id() ); - - if ( pre_event_images ) - { - for ( int i = 0; i < pre_event_images; i++ ) - { - timestamps[i] = image_buffer[pre_index].timestamp; - images[i] = image_buffer[pre_index].image; - - pre_index = (pre_index+1)%image_buffer_count; - } - event->AddFrames( pre_event_images, images, timestamps ); - } - if ( alarm_frame_count ) - { - event->SavePreAlarmFrames(); - } - } - } - else if ( state != PREALARM ) - { - Info( "%s: %03d - Gone into prealarm state", name, image_count ); - shared_data->state = state = PREALARM; - } - } - else if ( state == ALERT ) - { - Info( "%s: %03d - Gone back into alarm state", name, image_count ); - shared_data->state = state = ALARM; - } - last_alarm_count = image_count; + event = new Event( this, *(pre_event_buffer[pre_index].timestamp), cause, noteSetMap ); } else { - if ( state == ALARM ) - { - Info( "%s: %03d - Gone into alert state", name, image_count ); - shared_data->state = state = ALERT; - } - else if ( state == ALERT ) - { - if ( image_count-last_alarm_count > post_event_count ) - { - Info( "%s: %03d - Left alarm state (%d) - %d(%d) images", name, image_count, event->Id(), event->Frames(), event->AlarmFrames() ); - //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) - if ( function != MOCORD || event_close_mode == CLOSE_ALARM ) - { - shared_data->state = state = IDLE; - Info( "%s: %03d - Closing event %d, alarm end%s", name, image_count, event->Id(), (function==MOCORD)?", section truncated":"" ); - closeEvent(); - } - else - { - shared_data->state = state = TAPE; - } - } - } - if ( state == PREALARM ) - { - if ( function != MOCORD ) - { - shared_data->state = state = IDLE; - } - else - { - shared_data->state = state = TAPE; - } - } - if ( Event::PreAlarmCount() ) - Event::EmptyPreAlarmFrames(); + // If analysis fps is not set (analysis performed at capturing framerate), + // compute the index for pre event images in the capturing buffer + if ( alarm_frame_count > 1 ) + pre_index = ((index + image_buffer_count) - ((alarm_frame_count - 1) + pre_event_count))%image_buffer_count; + else + pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count; + + // Seek forward the next filled slot in to the buffer (oldest data) + // from the current position + while ( pre_event_images && !image_buffer[pre_index].timestamp->tv_sec ) + { + pre_index = (pre_index + 1)%image_buffer_count; + // Slot is empty, removing image from counter + pre_event_images--; + } + + event = new Event( this, *(image_buffer[pre_index].timestamp), cause, noteSetMap ); } - if ( state != IDLE ) + shared_data->last_event = event->Id(); + + Info( "%s: %03d - Opening new event %d, alarm start", name, image_count, event->Id() ); + + if ( pre_event_images ) { - if ( state == PREALARM || state == ALARM ) + if ( analysis_fps ) + for ( int i = 0; i < pre_event_images; i++ ) { - if ( config.create_analysis_images ) - { - bool got_anal_image = false; - Image alarm_image( *snap_image ); - for( int i = 0; i < n_zones; i++ ) - { - if ( zones[i]->Alarmed() ) - { - if ( zones[i]->AlarmImage() ) - { - alarm_image.Overlay( *(zones[i]->AlarmImage()) ); - got_anal_image = true; - } - if ( config.record_event_stats && state == ALARM ) - { - zones[i]->RecordStats( event ); - } - } - } - if ( got_anal_image ) - { - if ( state == PREALARM ) - Event::AddPreAlarmFrame( snap_image, *timestamp, score, &alarm_image ); - else - event->AddFrame( snap_image, *timestamp, score, &alarm_image ); - } - else - { - if ( state == PREALARM ) - Event::AddPreAlarmFrame( snap_image, *timestamp, score ); - else - event->AddFrame( snap_image, *timestamp, score ); - } - } - else - { - for( int i = 0; i < n_zones; i++ ) - { - if ( zones[i]->Alarmed() ) - { - if ( config.record_event_stats && state == ALARM ) - { - zones[i]->RecordStats( event ); - } - } - } - if ( state == PREALARM ) - Event::AddPreAlarmFrame( snap_image, *timestamp, score ); - else - event->AddFrame( snap_image, *timestamp, score ); - } - if ( event && noteSetMap.size() > 0 ) - event->updateNotes( noteSetMap ); + timestamps[i] = pre_event_buffer[pre_index].timestamp; + images[i] = pre_event_buffer[pre_index].image; + pre_index = (pre_index + 1)%pre_event_buffer_count; } - else if ( state == ALERT ) + else + for ( int i = 0; i < pre_event_images; i++ ) { - event->AddFrame( snap_image, *timestamp ); - if ( noteSetMap.size() > 0 ) - event->updateNotes( noteSetMap ); - } - else if ( state == TAPE ) - { - if ( !(image_count%(frame_skip+1)) ) - { - if ( config.bulk_frame_interval > 1 ) - { - event->AddFrame( snap_image, *timestamp, (event->Frames()AddFrame( snap_image, *timestamp ); - } - } + timestamps[i] = image_buffer[pre_index].timestamp; + images[i] = image_buffer[pre_index].image; + pre_index = (pre_index + 1)%image_buffer_count; } + + event->AddFrames( pre_event_images, images, timestamps ); } + if ( alarm_frame_count ) + { + event->SavePreAlarmFrames(); + } + } } + else if ( state != PREALARM ) + { + Info( "%s: %03d - Gone into prealarm state", name, image_count ); + shared_data->state = state = PREALARM; + } + } + else if ( state == ALERT ) + { + Info( "%s: %03d - Gone back into alarm state", name, image_count ); + shared_data->state = state = ALARM; + } + last_alarm_count = image_count; } else { - if ( event ) + if ( state == ALARM ) + { + Info( "%s: %03d - Gone into alert state", name, image_count ); + shared_data->state = state = ALERT; + } + else if ( state == ALERT ) + { + if ( image_count-last_alarm_count > post_event_count ) { - Info( "%s: %03d - Closing event %d, trigger off", name, image_count, event->Id() ); + Info( "%s: %03d - Left alarm state (%d) - %d(%d) images", name, image_count, event->Id(), event->Frames(), event->AlarmFrames() ); + //if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE ) + if ( function != MOCORD || event_close_mode == CLOSE_ALARM ) + { + shared_data->state = state = IDLE; + Info( "%s: %03d - Closing event %d, alarm end%s", name, image_count, event->Id(), (function==MOCORD)?", section truncated":"" ); closeEvent(); + } + else + { + shared_data->state = state = TAPE; + } } - shared_data->state = state = IDLE; - last_section_mod = 0; + } + if ( state == PREALARM ) + { + if ( function != MOCORD ) + { + shared_data->state = state = IDLE; + } + else + { + shared_data->state = state = TAPE; + } + } + if ( Event::PreAlarmCount() ) + Event::EmptyPreAlarmFrames(); } - if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) + if ( state != IDLE ) { - if ( state == ALARM ) { - ref_image.Blend( *snap_image, alarm_ref_blend_perc ); - } else { - ref_image.Blend( *snap_image, ref_blend_perc ); + if ( state == PREALARM || state == ALARM ) + { + if ( config.create_analysis_images ) + { + bool got_anal_image = false; + alarm_image.Assign( *snap_image ); + for( int i = 0; i < n_zones; i++ ) + { + if ( zones[i]->Alarmed() ) + { + if ( zones[i]->AlarmImage() ) + { + alarm_image.Overlay( *(zones[i]->AlarmImage()) ); + got_anal_image = true; + } + if ( config.record_event_stats && state == ALARM ) + { + zones[i]->RecordStats( event ); + } + } + } + if ( got_anal_image ) + { + if ( state == PREALARM ) + Event::AddPreAlarmFrame( snap_image, *timestamp, score, &alarm_image ); + else + event->AddFrame( snap_image, *timestamp, score, &alarm_image ); + } + else + { + if ( state == PREALARM ) + Event::AddPreAlarmFrame( snap_image, *timestamp, score ); + else + event->AddFrame( snap_image, *timestamp, score ); + } } + else + { + for( int i = 0; i < n_zones; i++ ) + { + if ( zones[i]->Alarmed() ) + { + if ( config.record_event_stats && state == ALARM ) + { + zones[i]->RecordStats( event ); + } + } + } + if ( state == PREALARM ) + Event::AddPreAlarmFrame( snap_image, *timestamp, score ); + else + event->AddFrame( snap_image, *timestamp, score ); + } + if ( event && noteSetMap.size() > 0 ) + event->updateNotes( noteSetMap ); + } + else if ( state == ALERT ) + { + event->AddFrame( snap_image, *timestamp ); + if ( noteSetMap.size() > 0 ) + event->updateNotes( noteSetMap ); + } + else if ( state == TAPE ) + { + if ( !(image_count%(frame_skip+1)) ) + { + if ( config.bulk_frame_interval > 1 ) + { + event->AddFrame( snap_image, *timestamp, (event->Frames()AddFrame( snap_image, *timestamp ); + } + } + } } - last_signal = signal; + } } + else + { + if ( event ) + { + Info( "%s: %03d - Closing event %d, trigger off", name, image_count, event->Id() ); + closeEvent(); + } + shared_data->state = state = IDLE; + last_section_mod = 0; + } + if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) + { + if ( state == ALARM ) { + ref_image.Blend( *snap_image, alarm_ref_blend_perc ); + } else { + ref_image.Blend( *snap_image, ref_blend_perc ); + } + } + last_signal = signal; + } - shared_data->last_read_index = index%image_buffer_count; - //shared_data->last_read_time = image_buffer[index].timestamp->tv_sec; - shared_data->last_read_time = now.tv_sec; - image_count++; + shared_data->last_read_index = index%image_buffer_count; + //shared_data->last_read_time = image_buffer[index].timestamp->tv_sec; + shared_data->last_read_time = now.tv_sec; - return( true ); + if ( analysis_fps ) + { + // If analysis fps is set, add analysed image to dedicated pre event buffer + int pre_index = image_count%pre_event_buffer_count; + pre_event_buffer[pre_index].image->Assign(*snap->image); + memcpy( pre_event_buffer[pre_index].timestamp, snap->timestamp, sizeof(struct timeval) ); + } + + image_count++; + + return( true ); } void Monitor::Reload() { - Debug( 1, "Reloading monitor %s", name ); + Debug( 1, "Reloading monitor %s", name ); - if ( event ) - Info( "%s: %03d - Closing event %d, reloading", name, image_count, event->Id() ); + if ( event ) + Info( "%s: %03d - Closing event %d, reloading", name, image_count, event->Id() ); - closeEvent(); + closeEvent(); - static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = '%d'", id ); + static char sql[ZM_SQL_MED_BUFSIZ]; + snprintf( sql, sizeof(sql), "select Function+0, Enabled, LinkedMonitors, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, WarmupCount, PreEventCount, PostEventCount, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = '%d'", id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - if ( n_monitors != 1 ) - { - Error( "Bogus number of monitors, %d, returned. Can't reload", n_monitors ); - return; - } + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + if ( n_monitors != 1 ) + { + Error( "Bogus number of monitors, %d, returned. Can't reload", n_monitors ); + return; + } - if ( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) - { - int index = 0; - function = (Function)atoi(dbrow[index++]); - enabled = atoi(dbrow[index++]); - const char *p_linked_monitors = dbrow[index++]; - strncpy( event_prefix, dbrow[index++], sizeof(event_prefix) ); - strncpy( label_format, dbrow[index++], sizeof(label_format) ); - label_coord = Coord( atoi(dbrow[index]), atoi(dbrow[index+1]) ); index += 2; - warmup_count = atoi(dbrow[index++]); - pre_event_count = atoi(dbrow[index++]); - post_event_count = atoi(dbrow[index++]); - alarm_frame_count = atoi(dbrow[index++]); - section_length = atoi(dbrow[index++]); - frame_skip = atoi(dbrow[index++]); - motion_frame_skip = atoi(dbrow[index++]); - capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; - alarm_capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; - fps_report_interval = atoi(dbrow[index++]); - ref_blend_perc = atoi(dbrow[index++]); - alarm_ref_blend_perc = atoi(dbrow[index++]); - track_motion = atoi(dbrow[index++]); - + if ( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) + { + int index = 0; + function = (Function)atoi(dbrow[index++]); + enabled = atoi(dbrow[index++]); + const char *p_linked_monitors = dbrow[index++]; + strncpy( event_prefix, dbrow[index++], sizeof(event_prefix) ); + strncpy( label_format, dbrow[index++], sizeof(label_format) ); + label_coord = Coord( atoi(dbrow[index]), atoi(dbrow[index+1]) ); index += 2; + label_size = atoi(dbrow[index++]); + warmup_count = atoi(dbrow[index++]); + pre_event_count = atoi(dbrow[index++]); + post_event_count = atoi(dbrow[index++]); + alarm_frame_count = atoi(dbrow[index++]); + section_length = atoi(dbrow[index++]); + frame_skip = atoi(dbrow[index++]); + motion_frame_skip = atoi(dbrow[index++]); + analysis_fps = dbrow[index] ? strtod(dbrow[index], NULL) : 0; index++; + analysis_update_delay = strtoul(dbrow[index++], NULL, 0); + capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; + alarm_capture_delay = (dbrow[index]&&atof(dbrow[index])>0.0)?int(DT_PREC_3/atof(dbrow[index])):0; index++; + fps_report_interval = atoi(dbrow[index++]); + ref_blend_perc = atoi(dbrow[index++]); + alarm_ref_blend_perc = atoi(dbrow[index++]); + track_motion = atoi(dbrow[index++]); + - if ( dbrow[index][0] == '#' ) - signal_check_colour = strtol(dbrow[index]+1,0,16); - else - signal_check_colour = strtol(dbrow[index],0,16); - index++; + if ( dbrow[index][0] == '#' ) + signal_check_colour = strtol(dbrow[index]+1,0,16); + else + signal_check_colour = strtol(dbrow[index],0,16); + index++; - shared_data->state = state = IDLE; - shared_data->alarm_x = shared_data->alarm_y = -1; - if ( enabled ) - shared_data->active = true; - ready_count = image_count+warmup_count; + shared_data->state = state = IDLE; + shared_data->alarm_x = shared_data->alarm_y = -1; + if ( enabled ) + shared_data->active = true; + ready_count = image_count+warmup_count; - ReloadLinkedMonitors( p_linked_monitors ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - mysql_free_result( result ); + ReloadLinkedMonitors( p_linked_monitors ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + mysql_free_result( result ); - ReloadZones(); + ReloadZones(); } void Monitor::ReloadZones() { - Debug( 1, "Reloading zones for monitor %s", name ); - for( int i = 0; i < n_zones; i++ ) - { - delete zones[i]; - } - delete[] zones; - zones = 0; - n_zones = Zone::Load( this, zones ); - //DumpZoneImage(); + Debug( 1, "Reloading zones for monitor %s", name ); + for( int i = 0; i < n_zones; i++ ) + { + delete zones[i]; + } + delete[] zones; + zones = 0; + n_zones = Zone::Load( this, zones ); + //DumpZoneImage(); } void Monitor::ReloadLinkedMonitors( const char *p_linked_monitors ) { - Debug( 1, "Reloading linked monitors for monitor %s, '%s'", name, p_linked_monitors ); - if ( n_linked_monitors ) + Debug( 1, "Reloading linked monitors for monitor %s, '%s'", name, p_linked_monitors ); + if ( n_linked_monitors ) + { + for( int i = 0; i < n_linked_monitors; i++ ) { - for( int i = 0; i < n_linked_monitors; i++ ) - { - delete linked_monitors[i]; - } - delete[] linked_monitors; - linked_monitors = 0; + delete linked_monitors[i]; } + delete[] linked_monitors; + linked_monitors = 0; + } - n_linked_monitors = 0; - if ( p_linked_monitors ) + n_linked_monitors = 0; + if ( p_linked_monitors ) + { + int n_link_ids = 0; + unsigned int link_ids[256]; + + char link_id_str[8]; + char *dest_ptr = link_id_str; + const char *src_ptr = p_linked_monitors; + while( 1 ) { - int n_link_ids = 0; - unsigned int link_ids[256]; - - char link_id_str[8]; - char *dest_ptr = link_id_str; - const char *src_ptr = p_linked_monitors; - while( 1 ) + dest_ptr = link_id_str; + while( *src_ptr >= '0' && *src_ptr <= '9' ) + { + if ( (dest_ptr-link_id_str) < (unsigned int)(sizeof(link_id_str)-1) ) { - dest_ptr = link_id_str; - while( *src_ptr >= '0' && *src_ptr <= '9' ) - { - if ( (dest_ptr-link_id_str) < (unsigned int)(sizeof(link_id_str)-1) ) - { - *dest_ptr++ = *src_ptr++; - } - else - { - break; - } - } - // Add the link monitor - if ( dest_ptr != link_id_str ) - { - *dest_ptr = '\0'; - unsigned int link_id = atoi(link_id_str); - if ( link_id > 0 && link_id != id) - { - Debug( 3, "Found linked monitor id %d", link_id ); - int j; - for ( j = 0; j < n_link_ids; j++ ) - { - if ( link_ids[j] == link_id ) - break; - } - if ( j == n_link_ids ) // Not already found - { - link_ids[n_link_ids++] = link_id; - } - } - } - if ( !*src_ptr ) - break; - while( *src_ptr && (*src_ptr < '0' || *src_ptr > '9') ) - src_ptr++; - if ( !*src_ptr ) - break; + *dest_ptr++ = *src_ptr++; } - if ( n_link_ids > 0 ) + else { - Debug( 1, "Linking to %d monitors", n_link_ids ); - linked_monitors = new MonitorLink *[n_link_ids]; - int count = 0; - for ( int i = 0; i < n_link_ids; i++ ) - { - Debug( 1, "Checking linked monitor %d", link_ids[i] ); - - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id, Name from Monitors where Id = %d and Function != 'None' and Function != 'Monitor' and Enabled = 1", link_ids[i] ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - if ( n_monitors == 1 ) - { - MYSQL_ROW dbrow = mysql_fetch_row( result ); - Debug( 1, "Linking to monitor %d", link_ids[i] ); - linked_monitors[count++] = new MonitorLink( link_ids[i], dbrow[1] ); - } - else - { - Warning( "Can't link to monitor %d, invalid id, function or not enabled", link_ids[i] ); - } - mysql_free_result( result ); - } - n_linked_monitors = count; + break; } + } + // Add the link monitor + if ( dest_ptr != link_id_str ) + { + *dest_ptr = '\0'; + unsigned int link_id = atoi(link_id_str); + if ( link_id > 0 && link_id != id) + { + Debug( 3, "Found linked monitor id %d", link_id ); + int j; + for ( j = 0; j < n_link_ids; j++ ) + { + if ( link_ids[j] == link_id ) + break; + } + if ( j == n_link_ids ) // Not already found + { + link_ids[n_link_ids++] = link_id; + } + } + } + if ( !*src_ptr ) + break; + while( *src_ptr && (*src_ptr < '0' || *src_ptr > '9') ) + src_ptr++; + if ( !*src_ptr ) + break; } + if ( n_link_ids > 0 ) + { + Debug( 1, "Linking to %d monitors", n_link_ids ); + linked_monitors = new MonitorLink *[n_link_ids]; + int count = 0; + for ( int i = 0; i < n_link_ids; i++ ) + { + Debug( 1, "Checking linked monitor %d", link_ids[i] ); + + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf( sql, sizeof(sql), "select Id, Name from Monitors where Id = %d and Function != 'None' and Function != 'Monitor' and Enabled = 1", link_ids[i] ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + if ( n_monitors == 1 ) + { + MYSQL_ROW dbrow = mysql_fetch_row( result ); + Debug( 1, "Linking to monitor %d", link_ids[i] ); + linked_monitors[count++] = new MonitorLink( link_ids[i], dbrow[1] ); + } + else + { + Warning( "Can't link to monitor %d, invalid id, function or not enabled", link_ids[i] ); + } + mysql_free_result( result ); + } + n_linked_monitors = count; + } + } } #if ZM_HAS_V4L int Monitor::LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ) { - static char sql[ZM_SQL_MED_BUFSIZ]; - if ( !device[0] ) - { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' order by Device, Channel", sizeof(sql) ); + std::string sql = "select Id, Name, ServerId, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Function != 'None' and Type = 'Local'"; + if ( device[0] ) { + sql += " AND Device='"; + sql += device; + sql += "'"; + } + if ( staticConfig.SERVER_ID ) { + sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); + } + Debug( 1, "Loading Local Monitors with %s", sql.c_str() ); + + MYSQL_RES *result = zmDbFetch( sql.c_str() ); + if ( !result ) { + Error( "Can't load local monitors: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); + delete[] monitors; + monitors = new Monitor *[n_monitors]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; + + int id = atoi(dbrow[col]); col++; + const char *name = dbrow[col]; col++; + unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + const char *linked_monitors = dbrow[col]; col++; + + const char *device = dbrow[col]; col++; + int channel = atoi(dbrow[col]); col++; + int format = atoi(dbrow[col]); col++; + bool v4l_multi_buffer = config.v4l_multi_buffer; + if ( dbrow[col] ) { + if (*dbrow[col] == '0' ) { + v4l_multi_buffer = false; + } else if ( *dbrow[col] == '1' ) { + v4l_multi_buffer = true; + } } + col++; + + int v4l_captures_per_frame = 0; + if ( dbrow[col] ) { + v4l_captures_per_frame = atoi(dbrow[col]); + } else { + v4l_captures_per_frame = config.captures_per_frame; + } +Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); + col++; + const char *method = dbrow[col]; col++; + + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + int palette = atoi(dbrow[col]); col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; + + const char *event_prefix = dbrow[col]; col++; + const char *label_format = dbrow[col]; col++; + + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; + int label_size = atoi(dbrow[col]); col++; + + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++; + unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0); + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; + + int signal_check_colour; + if ( dbrow[col][0] == '#' ) + signal_check_colour = strtol(dbrow[col]+1,0,16); else - { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LCapturesPerFrame, Method, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Function != 'None' and Type = 'Local' and Device = '%s' order by Channel", device ); - } - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + signal_check_colour = strtol(dbrow[col],0,16); + col++; + bool embed_exif = (*dbrow[col] != '0'); col++; - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; + int extras = (deinterlacing>>24)&0xff; - int id = atoi(dbrow[col]); col++; - const char *name = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; + Camera *camera = new LocalCamera( + id, + device, + channel, + format, + v4l_multi_buffer, + v4l_captures_per_frame, + method, + width, + height, + colours, + palette, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE, + extras + ); - const char *device = dbrow[col]; col++; - int channel = atoi(dbrow[col]); col++; - int format = atoi(dbrow[col]); col++; - bool v4l_multi_buffer = (*dbrow[col] == 48 ? false : true); col++; - int v4l_captures_per_frame = atoi(dbrow[col]); col++; - const char *method = dbrow[col]; col++; + monitors[i] = new Monitor( + id, + name, + server_id, + function, + enabled, + linked_monitors, + camera, + orientation, + deinterlacing, + event_prefix, + label_format, + Coord( label_x, label_y ), + label_size, + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + analysis_fps, + analysis_update_delay, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + signal_check_colour, + embed_exif, + purpose, + 0, + 0 + ); + Zone **zones = 0; + int n_zones = Zone::Load( monitors[i], zones ); + monitors[i]->AddZones( n_zones, zones ); + monitors[i]->AddPrivacyBitmask( zones ); + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - int palette = atoi(dbrow[col]); col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - const char *event_prefix = dbrow[col]; col++; - const char *label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - - int signal_check_colour; - if ( dbrow[col][0] == '#' ) - signal_check_colour = strtol(dbrow[col]+1,0,16); - else - signal_check_colour = strtol(dbrow[col],0,16); - col++; - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - int extras = (deinterlacing>>24)&0xff; - - Camera *camera = new LocalCamera( - id, - device, - channel, - format, - v4l_multi_buffer, - v4l_captures_per_frame, - method, - cam_width, - cam_height, - colours, - palette, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE, - extras - ); - - monitors[i] = new Monitor( - id, - name, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix, - label_format, - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - signal_check_colour, - purpose, - 0, - 0 - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); - - return( n_monitors ); + return( n_monitors ); } #endif // ZM_HAS_V4L int Monitor::LoadRemoteMonitors( const char *protocol, const char *host, const char *port, const char *path, Monitor **&monitors, Purpose purpose ) { - static char sql[ZM_SQL_MED_BUFSIZ]; - if ( !protocol ) + std::string sql = "select Id, Name, ServerId, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Remote'"; + if ( staticConfig.SERVER_ID ) { + sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); + } + + if ( protocol ) { + sql += stringtf(" AND Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path ); + } + + Debug( 1, "Loading Remote Monitors with %s", sql.c_str() ); + MYSQL_RES *result = zmDbFetch( sql.c_str() ); + if ( !result ) { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); + delete[] monitors; + monitors = new Monitor *[n_monitors]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; + + int id = atoi(dbrow[col]); col++; + std::string name = dbrow[col]; col++; + unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + const char *linked_monitors = dbrow[col]; col++; + + std::string protocol = dbrow[col]; col++; + std::string method = dbrow[col]; col++; + std::string host = dbrow[col]; col++; + std::string port = dbrow[col]; col++; + std::string path = dbrow[col]; col++; + + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + /* int palette = atoi(dbrow[col]); */ col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + bool rtsp_describe = (*dbrow[col] != '0'); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; + + std::string event_prefix = dbrow[col]; col++; + std::string label_format = dbrow[col]; col++; + + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; + int label_size = atoi(dbrow[col]); col++; + + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++; + unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0); + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; + bool embed_exif = (*dbrow[col] != '0'); col++; + + Camera *camera = 0; + if ( protocol == "http" ) { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote'", sizeof(sql) ); + camera = new RemoteCameraHttp( + id, + method, + host, // Host + port, // Port + path, // Path + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); } +#if HAVE_LIBAVFORMAT + else if ( protocol == "rtsp" ) + { + camera = new RemoteCameraRtsp( + id, + method, + host, // Host + port, // Port + path, // Path + width, + height, + rtsp_describe, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); + } +#endif // HAVE_LIBAVFORMAT else { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Protocol, Method, Host, Port, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Remote' and Protocol = '%s' and Host = '%s' and Port = '%s' and Path = '%s'", protocol, host, port, path ); - } - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + Fatal( "Unexpected remote camera protocol '%s'", protocol.c_str() ); } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; + monitors[i] = new Monitor( + id, + name.c_str(), + server_id, + function, + enabled, + linked_monitors, + camera, + orientation, + deinterlacing, + event_prefix.c_str(), + label_format.c_str(), + Coord( label_x, label_y ), + label_size, + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + analysis_fps, + analysis_update_delay, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + RGB_WHITE, + embed_exif, + purpose, + 0, + 0 - int id = atoi(dbrow[col]); col++; - std::string name = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; + ); + Zone **zones = 0; + int n_zones = Zone::Load( monitors[i], zones ); + monitors[i]->AddZones( n_zones, zones ); + monitors[i]->AddPrivacyBitmask( zones ); + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); - std::string protocol = dbrow[col]; col++; - std::string method = dbrow[col]; col++; - std::string host = dbrow[col]; col++; - std::string port = dbrow[col]; col++; - std::string path = dbrow[col]; col++; - - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - /* int palette = atoi(dbrow[col]); */ col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; - - std::string event_prefix = dbrow[col]; col++; - std::string label_format = dbrow[col]; col++; - - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; - - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; - - - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); - - Camera *camera = 0; - if ( protocol == "http" ) - { - camera = new RemoteCameraHttp( - id, - method, - host, // Host - port, // Port - path, // Path - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } -#if HAVE_LIBAVFORMAT - else if ( protocol == "rtsp" ) - { - camera = new RemoteCameraRtsp( - id, - method, - host, // Host - port, // Port - path, // Path - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } -#endif // HAVE_LIBAVFORMAT - else - { - Fatal( "Unexpected remote camera protocol '%s'", protocol.c_str() ); - } - - monitors[i] = new Monitor( - id, - name.c_str(), - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix.c_str(), - label_format.c_str(), - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - RGB_WHITE, - purpose, - 0, - 0 - - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); - - return( n_monitors ); + return( n_monitors ); } int Monitor::LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { - static char sql[ZM_SQL_MED_BUFSIZ]; - if ( !file[0] ) - { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File'", sizeof(sql) ); - } - else - { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'File' and Path = '%s'", file ); - } - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + std::string sql = "select Id, Name, ServerId, Function+0, Enabled, LinkedMonitors, Path, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'File'"; + if ( file[0] ) { + sql += " AND Path='"; + sql += file; + sql += "'"; + } + if ( staticConfig.SERVER_ID ) { + sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); + } + Debug( 1, "Loading File Monitors with %s", sql.c_str() ); + MYSQL_RES *result = zmDbFetch( sql.c_str() ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); + delete[] monitors; + monitors = new Monitor *[n_monitors]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; + int id = atoi(dbrow[col]); col++; + const char *name = dbrow[col]; col++; + unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + const char *linked_monitors = dbrow[col]; col++; - int id = atoi(dbrow[col]); col++; - const char *name = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; + const char *path = dbrow[col]; col++; - const char *path = dbrow[col]; col++; + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + /* int palette = atoi(dbrow[col]); */ col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - /* int palette = atoi(dbrow[col]); */ col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; + const char *event_prefix = dbrow[col]; col++; + const char *label_format = dbrow[col]; col++; - const char *event_prefix = dbrow[col]; col++; - const char *label_format = dbrow[col]; col++; + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; + int label_size = atoi(dbrow[col]); col++; - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++; + unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0); + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; + bool embed_exif = (*dbrow[col] != '0'); col++; - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; + Camera *camera = new FileCamera( + id, + path, // File + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); + monitors[i] = new Monitor( + id, + name, + server_id, + function, + enabled, + linked_monitors, + camera, + orientation, + deinterlacing, + event_prefix, + label_format, + Coord( label_x, label_y ), + label_size, + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + analysis_fps, + analysis_update_delay, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + embed_exif, + RGB_WHITE, + purpose, + 0, + 0 + ); + Zone **zones = 0; + int n_zones = Zone::Load( monitors[i], zones ); + monitors[i]->AddZones( n_zones, zones ); + monitors[i]->AddPrivacyBitmask( zones ); + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); - Camera *camera = new FileCamera( - id, - path, // File - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - - monitors[i] = new Monitor( - id, - name, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix, - label_format, - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - RGB_WHITE, - purpose, - 0, - 0 - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); - - return( n_monitors ); + return( n_monitors ); } #if HAVE_LIBAVFORMAT int Monitor::LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ) { - static char sql[ZM_SQL_MED_BUFSIZ]; - if ( !file[0] ) - { - strncpy( sql, "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg'", sizeof(sql) ); - } - else - { - snprintf( sql, sizeof(sql), "select Id, Name, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion from Monitors where Function != 'None' and Type = 'Ffmpeg' and Path = '%s'", file ); - } - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + std::string sql = "select Id, Name, ServerId, Function+0, Enabled, LinkedMonitors, Path, Method, Options, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif from Monitors where Function != 'None' and Type = 'Ffmpeg'"; + if ( file[0] ) { + sql += " AND Path = '"; + sql += file; + sql += "'"; + } + if ( staticConfig.SERVER_ID ) { + sql += stringtf( " AND ServerId=%d", staticConfig.SERVER_ID ); + } + Debug( 1, "Loading FFMPEG Monitors with %s", sql.c_str() ); + MYSQL_RES *result = zmDbFetch( sql.c_str() ); + if ( ! result ) { + Error( "Cannot load FfmpegMonitors" ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - delete[] monitors; - monitors = new Monitor *[n_monitors]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); + delete[] monitors; + monitors = new Monitor *[n_monitors]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; - int id = atoi(dbrow[col]); col++; - const char *name = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - const char *linked_monitors = dbrow[col]; col++; + int id = atoi(dbrow[col]); col++; + const char *name = dbrow[col]; col++; + unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + const char *linked_monitors = dbrow[col]; col++; - const char *path = dbrow[col]; col++; - const char *method = dbrow[col]; col++; - const char *options = dbrow[col]; col++; + const char *path = dbrow[col]; col++; + const char *method = dbrow[col]; col++; + const char *options = dbrow[col]; col++; - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - /* int palette = atoi(dbrow[col]); */ col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + /* int palette = atoi(dbrow[col]); */ col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; - const char *event_prefix = dbrow[col]; col++; - const char *label_format = dbrow[col]; col++; + const char *event_prefix = dbrow[col]; col++; + const char *label_format = dbrow[col]; col++; - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; + int label_size = atoi(dbrow[col]); col++; - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++; + unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0); + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; + bool embed_exif = (*dbrow[col] != '0'); col++; - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); + Camera *camera = new FfmpegCamera( + id, + path, // File + method, + options, + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); - Camera *camera = new FfmpegCamera( - id, - path, // File - method, - options, - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); + monitors[i] = new Monitor( + id, + name, + server_id, + function, + enabled, + linked_monitors, + camera, + orientation, + deinterlacing, + event_prefix, + label_format, + Coord( label_x, label_y ), + label_size, + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + analysis_fps, + analysis_update_delay, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + embed_exif, + RGB_WHITE, + purpose, + 0, + 0 + ); + Zone **zones = 0; + int n_zones = Zone::Load( monitors[i], zones ); + monitors[i]->AddZones( n_zones, zones ); + monitors[i]->AddPrivacyBitmask( zones ); + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); - monitors[i] = new Monitor( - id, - name, - function, - enabled, - linked_monitors, - camera, - orientation, - deinterlacing, - event_prefix, - label_format, - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - RGB_WHITE, - purpose, - 0, - 0 - ); - Zone **zones = 0; - int n_zones = Zone::Load( monitors[i], zones ); - monitors[i]->AddZones( n_zones, zones ); - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name, n_zones ); - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); - - return( n_monitors ); + return( n_monitors ); } #endif // HAVE_LIBAVFORMAT -Monitor *Monitor::Load( int id, bool load_zones, Purpose purpose ) +Monitor *Monitor::Load( unsigned int p_id, bool load_zones, Purpose purpose ) { - static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id, Name, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour from Monitors where Id = %d", id ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + std::string sql = stringtf( "select Id, Name, ServerId, Type, Function+0, Enabled, LinkedMonitors, Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, Protocol, Method, Host, Port, Path, Options, User, Pass, Width, Height, Colours, Palette, Orientation+0, Deinterlacing, RTSPDescribe, Brightness, Contrast, Hue, Colour, EventPrefix, LabelFormat, LabelX, LabelY, LabelSize, ImageBufferCount, WarmupCount, PreEventCount, PostEventCount, StreamReplayBuffer, AlarmFrameCount, SectionLength, FrameSkip, MotionFrameSkip, AnalysisFPS, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS, FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, SignalCheckColour, Exif from Monitors where Id = %d", p_id ); + + MYSQL_ROW dbrow = zmDbFetchOne( sql.c_str() ); + if ( ! dbrow ) { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + Monitor *monitor = 0; + unsigned int col = 0; + + unsigned int id = atoi(dbrow[col]); col++; + std::string name = dbrow[col]; col++; + unsigned int server_id = dbrow[col] ? atoi(dbrow[col]) : 0; col++; + std::string type = dbrow[col]; col++; + int function = atoi(dbrow[col]); col++; + int enabled = atoi(dbrow[col]); col++; + std::string linked_monitors = dbrow[col]; col++; + + std::string device = dbrow[col]; col++; + int channel = atoi(dbrow[col]); col++; + int format = atoi(dbrow[col]); col++; + + bool v4l_multi_buffer = config.v4l_multi_buffer; + if ( dbrow[col] ) { + if (*dbrow[col] == '0' ) { + v4l_multi_buffer = false; + } else if ( *dbrow[col] == '1' ) { + v4l_multi_buffer = true; } + } + col++; - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); - Monitor *monitor = 0; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; - - int id = atoi(dbrow[col]); col++; - std::string name = dbrow[col]; col++; - std::string type = dbrow[col]; col++; - int function = atoi(dbrow[col]); col++; - int enabled = atoi(dbrow[col]); col++; - std::string linked_monitors = dbrow[col]; col++; - - std::string device = dbrow[col]; col++; - int channel = atoi(dbrow[col]); col++; - int format = atoi(dbrow[col]); col++; - - bool v4l_multi_buffer; - if ( dbrow[col] ) { - if (*dbrow[col] == '0' ) { - v4l_multi_buffer = false; - } else if ( *dbrow[col] == '1' ) { - v4l_multi_buffer = true; - } - } else { - v4l_multi_buffer = config.v4l_multi_buffer; - } - col++; - - int v4l_captures_per_frame = 0; - if ( dbrow[col] ) { - v4l_captures_per_frame = atoi(dbrow[col]); - } else { - v4l_captures_per_frame = config.captures_per_frame; - } + int v4l_captures_per_frame = 0; + if ( dbrow[col] ) { + v4l_captures_per_frame = atoi(dbrow[col]); + } else { + v4l_captures_per_frame = config.captures_per_frame; + } Debug( 1, "Got %d for v4l_captures_per_frame", v4l_captures_per_frame ); - col++; + col++; - std::string protocol = dbrow[col]; col++; - std::string method = dbrow[col]; col++; - std::string host = dbrow[col]; col++; - std::string port = dbrow[col]; col++; - std::string path = dbrow[col]; col++; - std::string options = dbrow[col]; col++; - std::string user = dbrow[col]; col++; - std::string pass = dbrow[col]; col++; + std::string protocol = dbrow[col]; col++; + std::string method = dbrow[col]; col++; + std::string host = dbrow[col]; col++; + std::string port = dbrow[col]; col++; + std::string path = dbrow[col]; col++; + std::string options = dbrow[col]; col++; + std::string user = dbrow[col]; col++; + std::string pass = dbrow[col]; col++; - int width = atoi(dbrow[col]); col++; - int height = atoi(dbrow[col]); col++; - int colours = atoi(dbrow[col]); col++; - int palette = atoi(dbrow[col]); col++; - Orientation orientation = (Orientation)atoi(dbrow[col]); col++; - unsigned int deinterlacing = atoi(dbrow[col]); col++; - int brightness = atoi(dbrow[col]); col++; - int contrast = atoi(dbrow[col]); col++; - int hue = atoi(dbrow[col]); col++; - int colour = atoi(dbrow[col]); col++; + int width = atoi(dbrow[col]); col++; + int height = atoi(dbrow[col]); col++; + int colours = atoi(dbrow[col]); col++; + int palette = atoi(dbrow[col]); col++; + Orientation orientation = (Orientation)atoi(dbrow[col]); col++; + unsigned int deinterlacing = atoi(dbrow[col]); col++; + bool rtsp_describe = (*dbrow[col] != '0'); col++; + int brightness = atoi(dbrow[col]); col++; + int contrast = atoi(dbrow[col]); col++; + int hue = atoi(dbrow[col]); col++; + int colour = atoi(dbrow[col]); col++; - std::string event_prefix = dbrow[col]; col++; - std::string label_format = dbrow[col]; col++; + std::string event_prefix = dbrow[col]; col++; + std::string label_format = dbrow[col]; col++; - int label_x = atoi(dbrow[col]); col++; - int label_y = atoi(dbrow[col]); col++; + int label_x = atoi(dbrow[col]); col++; + int label_y = atoi(dbrow[col]); col++; + int label_size = atoi(dbrow[col]); col++; - int image_buffer_count = atoi(dbrow[col]); col++; - int warmup_count = atoi(dbrow[col]); col++; - int pre_event_count = atoi(dbrow[col]); col++; - int post_event_count = atoi(dbrow[col]); col++; - int stream_replay_buffer = atoi(dbrow[col]); col++; - int alarm_frame_count = atoi(dbrow[col]); col++; - int section_length = atoi(dbrow[col]); col++; - int frame_skip = atoi(dbrow[col]); col++; - int motion_frame_skip = atoi(dbrow[col]); col++; - int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; - int fps_report_interval = atoi(dbrow[col]); col++; - int ref_blend_perc = atoi(dbrow[col]); col++; - int alarm_ref_blend_perc = atoi(dbrow[col]); col++; - int track_motion = atoi(dbrow[col]); col++; + int image_buffer_count = atoi(dbrow[col]); col++; + int warmup_count = atoi(dbrow[col]); col++; + int pre_event_count = atoi(dbrow[col]); col++; + int post_event_count = atoi(dbrow[col]); col++; + int stream_replay_buffer = atoi(dbrow[col]); col++; + int alarm_frame_count = atoi(dbrow[col]); col++; + int section_length = atoi(dbrow[col]); col++; + int frame_skip = atoi(dbrow[col]); col++; + int motion_frame_skip = atoi(dbrow[col]); col++; + double analysis_fps = dbrow[col] ? strtod(dbrow[col], NULL) : 0; col++; + unsigned int analysis_update_delay = strtoul(dbrow[col++], NULL, 0); + int capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int alarm_capture_delay = (dbrow[col]&&atof(dbrow[col])>0.0)?int(DT_PREC_3/atof(dbrow[col])):0; col++; + int fps_report_interval = atoi(dbrow[col]); col++; + int ref_blend_perc = atoi(dbrow[col]); col++; + int alarm_ref_blend_perc = atoi(dbrow[col]); col++; + int track_motion = atoi(dbrow[col]); col++; - int signal_check_colour; - if ( dbrow[col][0] == '#' ) - signal_check_colour = strtol(dbrow[col]+1,0,16); - else - signal_check_colour = strtol(dbrow[col],0,16); + int signal_check_colour; + if ( dbrow[col][0] == '#' ) + signal_check_colour = strtol(dbrow[col]+1,0,16); + else + signal_check_colour = strtol(dbrow[col],0,16); + col++; + bool embed_exif = (*dbrow[col] != '0'); col++; - int cam_width = ((orientation==ROTATE_90||orientation==ROTATE_270)?height:width); - int cam_height = ((orientation==ROTATE_90||orientation==ROTATE_270)?width:height); + int extras = (deinterlacing>>24)&0xff; - int extras = (deinterlacing>>24)&0xff; - - Camera *camera = 0; - if ( type == "Local" ) - { + Camera *camera = 0; + if ( type == "Local" ) + { #if ZM_HAS_V4L - camera = new LocalCamera( - id, - device.c_str(), - channel, - format, - v4l_multi_buffer, - v4l_captures_per_frame, - method, - cam_width, - cam_height, - colours, - palette, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE, - extras - ); + camera = new LocalCamera( + id, + device.c_str(), + channel, + format, + v4l_multi_buffer, + v4l_captures_per_frame, + method, + width, + height, + colours, + palette, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE, + extras + ); #else // ZM_HAS_V4L - Fatal( "You must have video4linux libraries and headers installed to use local analog or USB cameras for monitor %d", id ); + Fatal( "You must have video4linux libraries and headers installed to use local analog or USB cameras for monitor %d", id ); #endif // ZM_HAS_V4L - } - else if ( type == "Remote" ) - { - if ( protocol == "http" ) - { - camera = new RemoteCameraHttp( - id, - method.c_str(), - host.c_str(), - port.c_str(), - path.c_str(), - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } - else if ( protocol == "rtsp" ) - { -#if HAVE_LIBAVFORMAT - camera = new RemoteCameraRtsp( - id, - method.c_str(), - host.c_str(), - port.c_str(), - path.c_str(), - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); -#else // HAVE_LIBAVFORMAT - Fatal( "You must have ffmpeg libraries installed to use remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); -#endif // HAVE_LIBAVFORMAT - } - else - { - Fatal( "Unexpected remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); - } - } - else if ( type == "File" ) - { - camera = new FileCamera( - id, - path.c_str(), - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); - } - else if ( type == "Ffmpeg" ) - { -#if HAVE_LIBAVFORMAT - camera = new FfmpegCamera( - id, - path.c_str(), - method, - options, - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); -#else // HAVE_LIBAVFORMAT - Fatal( "You must have ffmpeg libraries installed to use ffmpeg cameras for monitor %d", id ); -#endif // HAVE_LIBAVFORMAT - } - else if (type == "Libvlc") - { -#if HAVE_LIBVLC - camera = new LibvlcCamera( - id, - path.c_str(), - method, - options, - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); -#else // HAVE_LIBVLC - Fatal( "You must have vlc libraries installed to use vlc cameras for monitor %d", id ); -#endif // HAVE_LIBVLC - } - else if ( type == "cURL" ) - { -#if HAVE_LIBCURL - camera = new cURLCamera( - id, - path.c_str(), - user.c_str(), - pass.c_str(), - cam_width, - cam_height, - colours, - brightness, - contrast, - hue, - colour, - purpose==CAPTURE - ); -#else // HAVE_LIBCURL - Fatal( "You must have libcurl installed to use ffmpeg cameras for monitor %d", id ); -#endif // HAVE_LIBCURL - } - else - { - Fatal( "Bogus monitor type '%s' for monitor %d", type.c_str(), id ); - } - monitor = new Monitor( - id, - name.c_str(), - function, - enabled, - linked_monitors.c_str(), - camera, - orientation, - deinterlacing, - event_prefix.c_str(), - label_format.c_str(), - Coord( label_x, label_y ), - image_buffer_count, - warmup_count, - pre_event_count, - post_event_count, - stream_replay_buffer, - alarm_frame_count, - section_length, - frame_skip, - motion_frame_skip, - capture_delay, - alarm_capture_delay, - fps_report_interval, - ref_blend_perc, - alarm_ref_blend_perc, - track_motion, - signal_check_colour, - purpose, - 0, - 0 - - ); - - int n_zones = 0; - if ( load_zones ) - { - Zone **zones = 0; - n_zones = Zone::Load( monitor, zones ); - monitor->AddZones( n_zones, zones ); - } - Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); - } - if ( mysql_errno( &dbconn ) ) + } + else if ( type == "Remote" ) + { + if ( protocol == "http" ) { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); + camera = new RemoteCameraHttp( + id, + method.c_str(), + host.c_str(), + port.c_str(), + path.c_str(), + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); } - // Yadda yadda - mysql_free_result( result ); + else if ( protocol == "rtsp" ) + { +#if HAVE_LIBAVFORMAT + camera = new RemoteCameraRtsp( + id, + method.c_str(), + host.c_str(), + port.c_str(), + path.c_str(), + width, + height, + rtsp_describe, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); +#else // HAVE_LIBAVFORMAT + Fatal( "You must have ffmpeg libraries installed to use remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); +#endif // HAVE_LIBAVFORMAT + } + else + { + Fatal( "Unexpected remote camera protocol '%s' for monitor %d", protocol.c_str(), id ); + } + } + else if ( type == "File" ) + { + camera = new FileCamera( + id, + path.c_str(), + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); + } + else if ( type == "Ffmpeg" ) + { +#if HAVE_LIBAVFORMAT + camera = new FfmpegCamera( + id, + path.c_str(), + method, + options, + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); +#else // HAVE_LIBAVFORMAT + Fatal( "You must have ffmpeg libraries installed to use ffmpeg cameras for monitor %d", id ); +#endif // HAVE_LIBAVFORMAT + } + else if (type == "Libvlc") + { +#if HAVE_LIBVLC + camera = new LibvlcCamera( + id, + path.c_str(), + method, + options, + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); +#else // HAVE_LIBVLC + Fatal( "You must have vlc libraries installed to use vlc cameras for monitor %d", id ); +#endif // HAVE_LIBVLC + } + else if ( type == "cURL" ) + { +#if HAVE_LIBCURL + camera = new cURLCamera( + id, + path.c_str(), + user.c_str(), + pass.c_str(), + width, + height, + colours, + brightness, + contrast, + hue, + colour, + purpose==CAPTURE + ); +#else // HAVE_LIBCURL + Fatal( "You must have libcurl installed to use ffmpeg cameras for monitor %d", id ); +#endif // HAVE_LIBCURL + } + else + { + Fatal( "Bogus monitor type '%s' for monitor %d", type.c_str(), id ); + } + monitor = new Monitor( + id, + name.c_str(), + server_id, + function, + enabled, + linked_monitors.c_str(), + camera, + orientation, + deinterlacing, + event_prefix.c_str(), + label_format.c_str(), + Coord( label_x, label_y ), + label_size, + image_buffer_count, + warmup_count, + pre_event_count, + post_event_count, + stream_replay_buffer, + alarm_frame_count, + section_length, + frame_skip, + motion_frame_skip, + analysis_fps, + analysis_update_delay, + capture_delay, + alarm_capture_delay, + fps_report_interval, + ref_blend_perc, + alarm_ref_blend_perc, + track_motion, + signal_check_colour, + embed_exif, + purpose, + 0, + 0 - return( monitor ); + ); + + int n_zones = 0; + if ( load_zones ) + { + Zone **zones = 0; + n_zones = Zone::Load( monitor, zones ); + monitor->AddZones( n_zones, zones ); + monitor->AddPrivacyBitmask( zones ); + } + Debug( 1, "Loaded monitor %d(%s), %d zones", id, name.c_str(), n_zones ); + return( monitor ); } int Monitor::Capture() { - static int FirstCapture = 1; - int captureResult; + static int FirstCapture = 1; + int captureResult; - if ( function != EXTDECT ) { - shared_data->last_write_index ++; - return 0; + int index = image_count%image_buffer_count; + Image* capture_image = image_buffer[index].image; + + if ( (deinterlacing & 0xff) == 4) { + if ( FirstCapture != 1 ) { + /* Copy the next image into the shared memory */ + capture_image->CopyBuffer(*(next_buffer.image)); + } + + /* Capture a new next image */ + captureResult = camera->Capture(*(next_buffer.image)); + + if ( FirstCapture ) { + FirstCapture = 0; + return 0; + } + + } else { + /* Capture directly into image buffer, avoiding the need to memcpy() */ + captureResult = camera->Capture(*capture_image); } - int index = image_count%image_buffer_count; - Image* capture_image = image_buffer[index].image; - - if ( (deinterlacing & 0xff) == 4) { - if ( FirstCapture != 1 ) { - /* Copy the next image into the shared memory */ - capture_image->CopyBuffer(*(next_buffer.image)); - } - - /* Capture a new next image */ - captureResult = camera->Capture(*(next_buffer.image)); - - if ( FirstCapture ) { - FirstCapture = 0; - return 0; - } - - } else { - /* Capture directly into image buffer, avoiding the need to memcpy() */ - captureResult = camera->Capture(*capture_image); - } + if ( captureResult != 0 ) + { + // Unable to capture image for temporary reason + // Fake a signal loss image + Rgb signalcolor; + signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */ + capture_image->Fill(signalcolor); + captureResult = 0; + } else { + captureResult = 1; + } + + if ( captureResult == 1 ) + { - if ( captureResult != 0 ) - { - // Unable to capture image for temporary reason - // Fake a signal loss image - Rgb signalcolor; - signalcolor = rgb_convert(signal_check_colour, ZM_SUBPIX_ORDER_BGR); /* HTML colour code is actually BGR in memory, we want RGB */ - capture_image->Fill(signalcolor); - captureResult = 0; - } else { - captureResult = 1; + /* Deinterlacing */ + if ( (deinterlacing & 0xff) == 1 ) { + capture_image->Deinterlace_Discard(); + } else if ( (deinterlacing & 0xff) == 2 ) { + capture_image->Deinterlace_Linear(); + } else if ( (deinterlacing & 0xff) == 3 ) { + capture_image->Deinterlace_Blend(); + } else if ( (deinterlacing & 0xff) == 4 ) { + capture_image->Deinterlace_4Field( next_buffer.image, (deinterlacing>>8)&0xff ); + } else if ( (deinterlacing & 0xff) == 5 ) { + capture_image->Deinterlace_Blend_CustomRatio( (deinterlacing>>8)&0xff ); } - if ( captureResult == 1 ) + + if ( orientation != ROTATE_0 ) { - - /* Deinterlacing */ - if ( (deinterlacing & 0xff) == 1 ) { - capture_image->Deinterlace_Discard(); - } else if ( (deinterlacing & 0xff) == 2 ) { - capture_image->Deinterlace_Linear(); - } else if ( (deinterlacing & 0xff) == 3 ) { - capture_image->Deinterlace_Blend(); - } else if ( (deinterlacing & 0xff) == 4 ) { - capture_image->Deinterlace_4Field( next_buffer.image, (deinterlacing>>8)&0xff ); - } else if ( (deinterlacing & 0xff) == 5 ) { - capture_image->Deinterlace_Blend_CustomRatio( (deinterlacing>>8)&0xff ); - } - - - if ( orientation != ROTATE_0 ) + switch ( orientation ) + { + case ROTATE_0 : { - switch ( orientation ) - { - case ROTATE_0 : - { - // No action required - break; - } - case ROTATE_90 : - case ROTATE_180 : - case ROTATE_270 : - { - capture_image->Rotate( (orientation-1)*90 ); - break; - } - case FLIP_HORI : - case FLIP_VERT : - { - capture_image->Flip( orientation==FLIP_HORI ); - break; - } - } + // No action required + break; } - + case ROTATE_90 : + case ROTATE_180 : + case ROTATE_270 : + { + capture_image->Rotate( (orientation-1)*90 ); + break; + } + case FLIP_HORI : + case FLIP_VERT : + { + capture_image->Flip( orientation==FLIP_HORI ); + break; + } + } } - if ( true ) { - if ( capture_image->Size() != camera->ImageSize() ) - { - Error( "Captured image does not match expected size, check width, height and colour depth" ); - return( -1 ); - } + } + if ( true ) { - if ( ((unsigned int)index == shared_data->last_read_index) && (function > MONITOR) ) - { - Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count ); - time_t now = time(0); - double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec); - time_t last_read_delta = now - shared_data->last_read_time; - if ( last_read_delta > (image_buffer_count/approxFps) ) - { - Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta ) - shared_data->last_read_index = image_buffer_count; - } - } - - gettimeofday( image_buffer[index].timestamp, NULL ); - if ( config.timestamp_on_capture ) - { - TimestampImage( capture_image, image_buffer[index].timestamp ); - } - shared_data->signal = CheckSignal(capture_image); - shared_data->last_write_index = index; - shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; - - image_count++; - - if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) - { - time_t now = image_buffer[index].timestamp->tv_sec; - fps = double(fps_report_interval)/(now-last_fps_time); - //Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time ); - //Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps ); - Info( "%s: %d - Capturing at %.2lf fps", name, image_count, fps ); - last_fps_time = now; - } - - if ( shared_data->action & GET_SETTINGS ) - { - shared_data->brightness = camera->Brightness(); - shared_data->hue = camera->Hue(); - shared_data->colour = camera->Colour(); - shared_data->contrast = camera->Contrast(); - shared_data->action &= ~GET_SETTINGS; - } - if ( shared_data->action & SET_SETTINGS ) - { - camera->Brightness( shared_data->brightness ); - camera->Hue( shared_data->hue ); - camera->Colour( shared_data->colour ); - camera->Contrast( shared_data->contrast ); - shared_data->action &= ~SET_SETTINGS; - } - return( 0 ); + if ( capture_image->Size() > camera->ImageSize() ) + { + Error( "Captured image %d does not match expected size %d check width, height and colour depth",capture_image->Size(),camera->ImageSize() ); + return( -1 ); } - shared_data->signal = false; - return( -1 ); + + if ( ((unsigned int)index == shared_data->last_read_index) && (function > MONITOR) ) + { + Warning( "Buffer overrun at index %d, image %d, slow down capture, speed up analysis or increase ring buffer size", index, image_count ); + time_t now = time(0); + double approxFps = double(image_buffer_count)/double(now-image_buffer[index].timestamp->tv_sec); + time_t last_read_delta = now - shared_data->last_read_time; + if ( last_read_delta > (image_buffer_count/approxFps) ) + { + Warning( "Last image read from shared memory %ld seconds ago, zma may have gone away", last_read_delta ) + shared_data->last_read_index = image_buffer_count; + } + } + + if ( privacy_bitmask ) + capture_image->MaskPrivacy( privacy_bitmask ); + + gettimeofday( image_buffer[index].timestamp, NULL ); + if ( config.timestamp_on_capture ) + { + TimestampImage( capture_image, image_buffer[index].timestamp ); + } + shared_data->signal = CheckSignal(capture_image); + shared_data->last_write_index = index; + shared_data->last_write_time = image_buffer[index].timestamp->tv_sec; + + image_count++; + + if ( image_count && fps_report_interval && !(image_count%fps_report_interval) ) + { + time_t now = image_buffer[index].timestamp->tv_sec; + fps = double(fps_report_interval)/(now-last_fps_time); + //Info( "%d -> %d -> %d", fps_report_interval, now, last_fps_time ); + //Info( "%d -> %d -> %lf -> %lf", now-last_fps_time, fps_report_interval/(now-last_fps_time), double(fps_report_interval)/(now-last_fps_time), fps ); + Info( "%s: %d - Capturing at %.2lf fps", name, image_count, fps ); + last_fps_time = now; + } + + if ( shared_data->action & GET_SETTINGS ) + { + shared_data->brightness = camera->Brightness(); + shared_data->hue = camera->Hue(); + shared_data->colour = camera->Colour(); + shared_data->contrast = camera->Contrast(); + shared_data->action &= ~GET_SETTINGS; + } + if ( shared_data->action & SET_SETTINGS ) + { + camera->Brightness( shared_data->brightness ); + camera->Hue( shared_data->hue ); + camera->Colour( shared_data->colour ); + camera->Contrast( shared_data->contrast ); + shared_data->action &= ~SET_SETTINGS; + } + return( 0 ); + } + shared_data->signal = false; + return( -1 ); } void Monitor::TimestampImage( Image *ts_image, const struct timeval *ts_time ) const { - if ( label_format[0] ) - { - // Expand the strftime macros first - char label_time_text[256]; - strftime( label_time_text, sizeof(label_time_text), label_format, localtime( &ts_time->tv_sec ) ); + if ( label_format[0] ) + { + // Expand the strftime macros first + char label_time_text[256]; + strftime( label_time_text, sizeof(label_time_text), label_format, localtime( &ts_time->tv_sec ) ); - char label_text[1024]; - const char *s_ptr = label_time_text; - char *d_ptr = label_text; - while ( *s_ptr && ((d_ptr-label_text) < (unsigned int)sizeof(label_text)) ) + char label_text[1024]; + const char *s_ptr = label_time_text; + char *d_ptr = label_text; + while ( *s_ptr && ((d_ptr-label_text) < (unsigned int)sizeof(label_text)) ) + { + if ( *s_ptr == '%' ) + { + bool found_macro = false; + switch ( *(s_ptr+1) ) { - if ( *s_ptr == '%' ) - { - bool found_macro = false; - switch ( *(s_ptr+1) ) - { - case 'N' : - d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", name ); - found_macro = true; - break; - case 'Q' : - d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", trigger_data->trigger_showtext ); - found_macro = true; - break; - case 'f' : - d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%02ld", ts_time->tv_usec/10000 ); - found_macro = true; - break; - } - if ( found_macro ) - { - s_ptr += 2; - continue; - } - } - *d_ptr++ = *s_ptr++; + case 'N' : + d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", name ); + found_macro = true; + break; + case 'Q' : + d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%s", trigger_data->trigger_showtext ); + found_macro = true; + break; + case 'f' : + d_ptr += snprintf( d_ptr, sizeof(label_text)-(d_ptr-label_text), "%02ld", ts_time->tv_usec/10000 ); + found_macro = true; + break; } - *d_ptr = '\0'; - ts_image->Annotate( label_text, label_coord ); + if ( found_macro ) + { + s_ptr += 2; + continue; + } + } + *d_ptr++ = *s_ptr++; } + *d_ptr = '\0'; + ts_image->Annotate( label_text, label_coord, label_size ); + } } bool Monitor::closeEvent() { - if ( event ) + if ( event ) + { + if ( function == RECORD || function == MOCORD ) { - if ( function == RECORD || function == MOCORD ) - { - gettimeofday( &(event->EndTime()), NULL ); - } - delete event; - event = 0; - return( true ); + gettimeofday( &(event->EndTime()), NULL ); } - return( false ); + delete event; + event = 0; + return( true ); + } + return( false ); } -//----------------------------------------- - -/* - * NOTE Nextime's comment: - * - * OurCheckAlarms seems to be called only by DetectBlack method, and DetectBlack - * method is only called in a commented line instead of DetectMotion in zm_monitor.cpp. - * - * Probably this is just a dead code used for debugghing purpose, so, instead of fixing it - * it seems to be safe to just comment it out. - * - * Anyway, the issues with this code is that it assumes the image to be an RGB24 image, - * so, as i've discussed on IRC with mastertheknife, changes needed are: - * - * Check if the image is 24 or 32 bits ( pImage->Colours() says 3 for 24 and 4 for 32 bits, - * comparing it with ZM_COLOUR_RGB24 or ZM_COLOUR_RGB32 is the way ), and then - * manage che check using RGB_VAL_RED() and so on macros instead of just RED(). - * - * Be carefull that in 32 bit images we need to check also where the alpha channel is, so, - * (RGBA and BGRA) or (ABGR and ARGB) aren't the same! - * - * To check black pixels in 32 bit images i can do a more efficient way using - * RGBA_ZERO_ALPHA(pixel) == RGBA_ZERO_ALPHA(RGB_BLACK), but before of that i need to - * check where the alpha channel is and maybe convert it. - * Maybe this won't work as they assign "23" to black_thr, so, they are not checking - * if the pixel is black, but just "quasi" black is enough. - * - * Anyway, for the moment, comment out whole part. - */ - -/* -bool Monitor::OurCheckAlarms( Zone *zone, const Image *pImage ) -{ - Info("Entering OurCheckAlarms >>>>>>>>>>>>>>>>>>>>>"); - unsigned char black_thr = 23; - int min_alarm_score = 10; - int max_alarm_score = 99; - //bool alarm = false; - unsigned int score; - Polygon zone_polygon = zone->GetPolygon(); - Info("Got polygon of a zone. It has %d vertices.", zone_polygon.getNumCoords()); - - zone->ResetStats(); - Info("ResetStats done."); - - if ( !zone->CheckOverloadCount() ) - { - Info("CheckOverloadCount() return false, we'll return false."); - return( false ); - } - - Image *pMaskImage = new Image(pImage->Width(), pImage->Height(), ZM_COLOUR_GRAY8, pImage->SubpixelOrder()); - Info("Mask image created."); - - pMaskImage->Fill(BLACK); - Info("Mask image filled with BLACK."); - if (pImage->Colours() == ZM_COLOUR_GRAY8) - { - Info("Analysed image is not colored! Set score = 0."); - score = 0; - } - else - { - Info("Start processing image."); - //Process image - unsigned char *buffer = (unsigned char*)pImage->Buffer(); - unsigned char *mask_buffer = (unsigned char*)pMaskImage->Buffer(); - - int black_pixels_count = 0; - Info("Loop for black pixels counting and mask filling."); - while (buffer < (pImage->Buffer() + pImage->Size())) - { - if ( (RED(buffer) < black_thr) && (GREEN(buffer) < black_thr) && (BLUE(buffer) < black_thr) ) - { - *mask_buffer = WHITE; - black_pixels_count++; - } - buffer += pImage->Colours(); - mask_buffer++; - } - - if ( !black_pixels_count ) - { - delete pMaskImage; - return( false ); - } - score = (100*black_pixels_count)/zone_polygon.Area(); - Info("Number of black pixels is %d, zone polygon area is %d, score is %d", black_pixels_count, zone_polygon.Area(), score); - - if ( min_alarm_score && ( score < min_alarm_score) ) - { - delete pMaskImage; - return( false ); - } - if ( max_alarm_score && (score > max_alarm_score) ) - { - zone->SetOverloadCount(zone->GetOverloadFrames()); - delete pMaskImage; - return( false ); - } - } - - zone->SetScore(score); - Info("Score have been set in zone."); - //Get mask - Rgb alarm_colour = RGB_RED; - Image *tempImage = pMaskImage->HighlightEdges(alarm_colour, &zone_polygon.Extent() ); - Info("After HighlightEdges"); - - zone->SetAlarmImage(tempImage); - Info("After SetAlarmImage"); - delete pMaskImage; - Info("After Delete pMaskImage"); - delete tempImage; - - Info("Leaving OurCheckAlarms >>>>>>>>>>>>>>>>>>>>>>>>>>>>"); - return true; -} - -unsigned int Monitor::DetectBlack(const Image &comp_image, Event::StringSet &zoneSet ) -{ - Info("Entering DetectBlack >>>>>>>>>>>>>>>>>>>>>>>>>>"); - bool alarm = false; - unsigned int score = 0; - - if ( n_zones <= 0 ) return( alarm ); - -// Coord alarm_centre; -// int top_score = -1; - - // Find all alarm pixels in active zones - Info("Number of zones to process %d", n_zones); - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) - { - Zone *zone = zones[n_zone]; - if ( !zone->IsActive() ) - { - continue; - } - Debug( 3, "Checking active zone %s", zone->Label() ); - Info( "Checking active zone %s", zone->Label() ); - if ( OurCheckAlarms( zone, &comp_image ) ) - { - Info("OurCheckAlarm is TRUE!!!!!!"); - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - Info( "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); -// if ( config.opt_control && track_motion ) -// { -// if ( (int)zone->Score() > top_score ) -// { -// top_score = zone->Score(); -// alarm_centre = zone->GetAlarmCentre(); -// } -// } - } - Info( "Finish checking active zone %s", zone->Label() ); - } - - -// if ( top_score > 0 ) -// { -// shared_data->alarm_x = alarm_centre.X(); -// shared_data->alarm_y = alarm_centre.Y(); -// -// Info( "Got alarm centre at %d,%d, at count %d", shared_data->alarm_x, shared_data->alarm_y, image_count ); -// } -// else -// { -// shared_data->alarm_x = shared_data->alarm_y = -1; -// } - - // This is a small and innocent hack to prevent scores of 0 being returned in alarm state - Info("Leaving DetectBlack <<<<<<<<<<<<<<<<<<<<<<<<<<<"); - return( score?score:alarm ); -} - -*/ -//----------------------------------------------------------------------------------------------- - - - unsigned int Monitor::DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ) { - bool alarm = false; - unsigned int score = 0; + bool alarm = false; + unsigned int score = 0; - if ( n_zones <= 0 ) return( alarm ); + if ( n_zones <= 0 ) return( alarm ); - if ( config.record_diag_images ) + if ( config.record_diag_images ) + { + static char diag_path[PATH_MAX] = ""; + if ( !diag_path[0] ) { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", config.dir_events, id ); - } - ref_image.WriteJpeg( diag_path ); + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-r.jpg", config.dir_events, id ); } + ref_image.WriteJpeg( diag_path ); + } - ref_image.Delta( comp_image, &delta_image); + ref_image.Delta( comp_image, &delta_image); - if ( config.record_diag_images ) + if ( config.record_diag_images ) + { + static char diag_path[PATH_MAX] = ""; + if ( !diag_path[0] ) { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", config.dir_events, id ); - } - delta_image.WriteJpeg( diag_path ); + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-d.jpg", config.dir_events, id ); } + delta_image.WriteJpeg( diag_path ); + } - // Blank out all exclusion zones - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + // Blank out all exclusion zones + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + // need previous alarmed state for preclusive zone, so don't clear just yet + if (!zone->IsPreclusive()) + zone->ClearAlarm(); + if ( !zone->IsInactive() ) { - Zone *zone = zones[n_zone]; - // need previous alarmed state for preclusive zone, so don't clear just yet - if (!zone->IsPreclusive()) - zone->ClearAlarm(); - if ( !zone->IsInactive() ) - { - continue; - } - Debug( 3, "Blanking inactive zone %s", zone->Label() ); - delta_image.Fill( RGB_BLACK, zone->GetPolygon() ); + continue; } + Debug( 3, "Blanking inactive zone %s", zone->Label() ); + delta_image.Fill( RGB_BLACK, zone->GetPolygon() ); + } - // Check preclusive zones first - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + // Check preclusive zones first + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + if ( !zone->IsPreclusive() ) { - Zone *zone = zones[n_zone]; - if ( !zone->IsPreclusive() ) - { - continue; + continue; + } + int old_zone_score = zone->Score(); + bool old_zone_alarmed = zone->Alarmed(); + Debug( 3, "Checking preclusive zone %s - old score: %d, state: %s", zone->Label(),old_zone_score, zone->Alarmed()?"alarmed":"quiet" ); + if ( zone->CheckAlarms( &delta_image ) ) + { + alarm = true; + score += zone->Score(); + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( zone->Label() ); + //zone->ResetStats(); + } else { + // check if end of alarm + if (old_zone_alarmed) { + Debug(3, "Preclusive Zone %s alarm Ends. Prevíous score: %d", zone->Label(), old_zone_score); + if (old_zone_score > 0) { + zone->SetExtendAlarmCount(zone->GetExtendAlarmFrames()); } - int old_zone_score = zone->Score(); - bool old_zone_alarmed = zone->Alarmed(); - Debug( 3, "Checking preclusive zone %s - old score: %d, state: %s", zone->Label(),old_zone_score, zone->Alarmed()?"alarmed":"quiet" ); - if ( zone->CheckAlarms( &delta_image ) ) - { - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - //zone->ResetStats(); + if (zone->CheckExtendAlarmCount()) { + alarm=true; + zone->SetAlarm(); } else { - // check if end of alarm - if (old_zone_alarmed) { - Debug(3, "Preclusive Zone %s alarm Ends. Prevíous score: %d", zone->Label(), old_zone_score); - if (old_zone_score > 0) { - zone->SetExtendAlarmCount(zone->GetExtendAlarmFrames()); - } - if (zone->CheckExtendAlarmCount()) { - alarm=true; - zone->SetAlarm(); - } else { - zone->ClearAlarm(); - } - } + zone->ClearAlarm(); } + } } + } - Coord alarm_centre; - int top_score = -1; + Coord alarm_centre; + int top_score = -1; + + if ( alarm ) + { + alarm = false; + score = 0; + } + else + { + // Find all alarm pixels in active zones + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + if ( !zone->IsActive() || zone->IsPreclusive()) + { + continue; + } + Debug( 3, "Checking active zone %s", zone->Label() ); + if ( zone->CheckAlarms( &delta_image ) ) + { + alarm = true; + score += zone->Score(); + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( zone->Label() ); + if ( config.opt_control && track_motion ) + { + if ( (int)zone->Score() > top_score ) + { + top_score = zone->Score(); + alarm_centre = zone->GetAlarmCentre(); + } + } + } + } if ( alarm ) { - alarm = false; - score = 0; + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + if ( !zone->IsInclusive() ) + { + continue; + } + Debug( 3, "Checking inclusive zone %s", zone->Label() ); + if ( zone->CheckAlarms( &delta_image ) ) + { + alarm = true; + score += zone->Score(); + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( zone->Label() ); + if ( config.opt_control && track_motion ) + { + if ( zone->Score() > (unsigned int)top_score ) + { + top_score = zone->Score(); + alarm_centre = zone->GetAlarmCentre(); + } + } + } + } } else { - // Find all alarm pixels in active zones - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + // Find all alarm pixels in exclusive zones + for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) + { + Zone *zone = zones[n_zone]; + if ( !zone->IsExclusive() ) { - Zone *zone = zones[n_zone]; - if ( !zone->IsActive() || zone->IsPreclusive()) - { - continue; - } - Debug( 3, "Checking active zone %s", zone->Label() ); - if ( zone->CheckAlarms( &delta_image ) ) - { - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - if ( config.opt_control && track_motion ) - { - if ( (int)zone->Score() > top_score ) - { - top_score = zone->Score(); - alarm_centre = zone->GetAlarmCentre(); - } - } - } + continue; } - - if ( alarm ) + Debug( 3, "Checking exclusive zone %s", zone->Label() ); + if ( zone->CheckAlarms( &delta_image ) ) { - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) - { - Zone *zone = zones[n_zone]; - if ( !zone->IsInclusive() ) - { - continue; - } - Debug( 3, "Checking inclusive zone %s", zone->Label() ); - if ( zone->CheckAlarms( &delta_image ) ) - { - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - if ( config.opt_control && track_motion ) - { - if ( zone->Score() > (unsigned int)top_score ) - { - top_score = zone->Score(); - alarm_centre = zone->GetAlarmCentre(); - } - } - } - } - } - else - { - // Find all alarm pixels in exclusive zones - for ( int n_zone = 0; n_zone < n_zones; n_zone++ ) - { - Zone *zone = zones[n_zone]; - if ( !zone->IsExclusive() ) - { - continue; - } - Debug( 3, "Checking exclusive zone %s", zone->Label() ); - if ( zone->CheckAlarms( &delta_image ) ) - { - alarm = true; - score += zone->Score(); - zone->SetAlarm(); - Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); - zoneSet.insert( zone->Label() ); - } - } + alarm = true; + score += zone->Score(); + zone->SetAlarm(); + Debug( 3, "Zone is alarmed, zone score = %d", zone->Score() ); + zoneSet.insert( zone->Label() ); } + } } + } - if ( top_score > 0 ) - { - shared_data->alarm_x = alarm_centre.X(); - shared_data->alarm_y = alarm_centre.Y(); + if ( top_score > 0 ) + { + shared_data->alarm_x = alarm_centre.X(); + shared_data->alarm_y = alarm_centre.Y(); - Info( "Got alarm centre at %d,%d, at count %d", shared_data->alarm_x, shared_data->alarm_y, image_count ); - } - else - { - shared_data->alarm_x = shared_data->alarm_y = -1; - } + Info( "Got alarm centre at %d,%d, at count %d", shared_data->alarm_x, shared_data->alarm_y, image_count ); + } + else + { + shared_data->alarm_x = shared_data->alarm_y = -1; + } - // This is a small and innocent hack to prevent scores of 0 being returned in alarm state - return( score?score:alarm ); + // This is a small and innocent hack to prevent scores of 0 being returned in alarm state + return( score?score:alarm ); } bool Monitor::DumpSettings( char *output, bool verbose ) { - output[0] = 0; + output[0] = 0; - sprintf( output+strlen(output), "Id : %d\n", id ); - sprintf( output+strlen(output), "Name : %s\n", name ); - sprintf( output+strlen(output), "Type : %s\n", camera->IsLocal()?"Local":(camera->IsRemote()?"Remote":"File") ); + sprintf( output+strlen(output), "Id : %d\n", id ); + sprintf( output+strlen(output), "Name : %s\n", name ); + sprintf( output+strlen(output), "Type : %s\n", camera->IsLocal()?"Local":(camera->IsRemote()?"Remote":"File") ); #if ZM_HAS_V4L - if ( camera->IsLocal() ) - { - sprintf( output+strlen(output), "Device : %s\n", ((LocalCamera *)camera)->Device().c_str() ); - sprintf( output+strlen(output), "Channel : %d\n", ((LocalCamera *)camera)->Channel() ); - sprintf( output+strlen(output), "Standard : %d\n", ((LocalCamera *)camera)->Standard() ); - } - else + if ( camera->IsLocal() ) + { + sprintf( output+strlen(output), "Device : %s\n", ((LocalCamera *)camera)->Device().c_str() ); + sprintf( output+strlen(output), "Channel : %d\n", ((LocalCamera *)camera)->Channel() ); + sprintf( output+strlen(output), "Standard : %d\n", ((LocalCamera *)camera)->Standard() ); + } + else #endif // ZM_HAS_V4L - if ( camera->IsRemote() ) - { - sprintf( output+strlen(output), "Protocol : %s\n", ((RemoteCamera *)camera)->Protocol().c_str() ); - sprintf( output+strlen(output), "Host : %s\n", ((RemoteCamera *)camera)->Host().c_str() ); - sprintf( output+strlen(output), "Port : %s\n", ((RemoteCamera *)camera)->Port().c_str() ); - sprintf( output+strlen(output), "Path : %s\n", ((RemoteCamera *)camera)->Path().c_str() ); - } - else if ( camera->IsFile() ) - { - sprintf( output+strlen(output), "Path : %s\n", ((FileCamera *)camera)->Path() ); - } + if ( camera->IsRemote() ) + { + sprintf( output+strlen(output), "Protocol : %s\n", ((RemoteCamera *)camera)->Protocol().c_str() ); + sprintf( output+strlen(output), "Host : %s\n", ((RemoteCamera *)camera)->Host().c_str() ); + sprintf( output+strlen(output), "Port : %s\n", ((RemoteCamera *)camera)->Port().c_str() ); + sprintf( output+strlen(output), "Path : %s\n", ((RemoteCamera *)camera)->Path().c_str() ); + } + else if ( camera->IsFile() ) + { + sprintf( output+strlen(output), "Path : %s\n", ((FileCamera *)camera)->Path() ); + } #if HAVE_LIBAVFORMAT - else if ( camera->IsFfmpeg() ) - { - sprintf( output+strlen(output), "Path : %s\n", ((FfmpegCamera *)camera)->Path().c_str() ); - } + else if ( camera->IsFfmpeg() ) + { + sprintf( output+strlen(output), "Path : %s\n", ((FfmpegCamera *)camera)->Path().c_str() ); + } #endif // HAVE_LIBAVFORMAT - sprintf( output+strlen(output), "Width : %d\n", camera->Width() ); - sprintf( output+strlen(output), "Height : %d\n", camera->Height() ); + sprintf( output+strlen(output), "Width : %d\n", camera->Width() ); + sprintf( output+strlen(output), "Height : %d\n", camera->Height() ); #if ZM_HAS_V4L - if ( camera->IsLocal() ) - { - sprintf( output+strlen(output), "Palette : %d\n", ((LocalCamera *)camera)->Palette() ); - } + if ( camera->IsLocal() ) + { + sprintf( output+strlen(output), "Palette : %d\n", ((LocalCamera *)camera)->Palette() ); + } #endif // ZM_HAS_V4L - sprintf( output+strlen(output), "Colours : %d\n", camera->Colours() ); - sprintf( output+strlen(output), "Subpixel Order : %d\n", camera->SubpixelOrder() ); - sprintf( output+strlen(output), "Event Prefix : %s\n", event_prefix ); - sprintf( output+strlen(output), "Label Format : %s\n", label_format ); - sprintf( output+strlen(output), "Label Coord : %d,%d\n", label_coord.X(), label_coord.Y() ); - sprintf( output+strlen(output), "Image Buffer Count : %d\n", image_buffer_count ); - sprintf( output+strlen(output), "Warmup Count : %d\n", warmup_count ); - sprintf( output+strlen(output), "Pre Event Count : %d\n", pre_event_count ); - sprintf( output+strlen(output), "Post Event Count : %d\n", post_event_count ); - sprintf( output+strlen(output), "Stream Replay Buffer : %d\n", stream_replay_buffer ); - sprintf( output+strlen(output), "Alarm Frame Count : %d\n", alarm_frame_count ); - sprintf( output+strlen(output), "Section Length : %d\n", section_length ); - sprintf( output+strlen(output), "Maximum FPS : %.2f\n", capture_delay?DT_PREC_3/capture_delay:0.0 ); - sprintf( output+strlen(output), "Alarm Maximum FPS : %.2f\n", alarm_capture_delay?DT_PREC_3/alarm_capture_delay:0.0 ); - sprintf( output+strlen(output), "Reference Blend %%ge : %d\n", ref_blend_perc ); - sprintf( output+strlen(output), "Alarm Reference Blend %%ge : %d\n", alarm_ref_blend_perc ); - sprintf( output+strlen(output), "Track Motion : %d\n", track_motion ); - sprintf( output+strlen(output), "Function: %d - %s\n", function, - function==NONE?"None":( - function==MONITOR?"Monitor Only":( - function==MODECT?"Motion Detection":( - function==RECORD?"Continuous Record":( - function==MOCORD?"Continuous Record with Motion Detection":( - function==NODECT?"Externally Triggered only, no Motion Detection":"Unknown" - )))))); - sprintf( output+strlen(output), "Zones : %d\n", n_zones ); - for ( int i = 0; i < n_zones; i++ ) - { - zones[i]->DumpSettings( output+strlen(output), verbose ); - } - return( true ); + sprintf( output+strlen(output), "Colours : %d\n", camera->Colours() ); + sprintf( output+strlen(output), "Subpixel Order : %d\n", camera->SubpixelOrder() ); + sprintf( output+strlen(output), "Event Prefix : %s\n", event_prefix ); + sprintf( output+strlen(output), "Label Format : %s\n", label_format ); + sprintf( output+strlen(output), "Label Coord : %d,%d\n", label_coord.X(), label_coord.Y() ); + sprintf( output+strlen(output), "Label Size : %d\n", label_size ); + sprintf( output+strlen(output), "Image Buffer Count : %d\n", image_buffer_count ); + sprintf( output+strlen(output), "Warmup Count : %d\n", warmup_count ); + sprintf( output+strlen(output), "Pre Event Count : %d\n", pre_event_count ); + sprintf( output+strlen(output), "Post Event Count : %d\n", post_event_count ); + sprintf( output+strlen(output), "Stream Replay Buffer : %d\n", stream_replay_buffer ); + sprintf( output+strlen(output), "Alarm Frame Count : %d\n", alarm_frame_count ); + sprintf( output+strlen(output), "Section Length : %d\n", section_length ); + sprintf( output+strlen(output), "Maximum FPS : %.2f\n", capture_delay?DT_PREC_3/capture_delay:0.0 ); + sprintf( output+strlen(output), "Alarm Maximum FPS : %.2f\n", alarm_capture_delay?DT_PREC_3/alarm_capture_delay:0.0 ); + sprintf( output+strlen(output), "Reference Blend %%ge : %d\n", ref_blend_perc ); + sprintf( output+strlen(output), "Alarm Reference Blend %%ge : %d\n", alarm_ref_blend_perc ); + sprintf( output+strlen(output), "Track Motion : %d\n", track_motion ); + sprintf( output+strlen(output), "Function: %d - %s\n", function, + function==NONE?"None":( + function==MONITOR?"Monitor Only":( + function==MODECT?"Motion Detection":( + function==RECORD?"Continuous Record":( + function==MOCORD?"Continuous Record with Motion Detection":( + function==NODECT?"Externally Triggered only, no Motion Detection":"Unknown" + )))))); + sprintf( output+strlen(output), "Zones : %d\n", n_zones ); + for ( int i = 0; i < n_zones; i++ ) + { + zones[i]->DumpSettings( output+strlen(output), verbose ); + } + return( true ); } bool MonitorStream::checkSwapPath( const char *path, bool create_path ) { - uid_t uid = getuid(); - gid_t gid = getgid(); + uid_t uid = getuid(); + gid_t gid = getgid(); - struct stat stat_buf; - if ( stat( path, &stat_buf ) < 0 ) + struct stat stat_buf; + if ( stat( path, &stat_buf ) < 0 ) + { + if ( create_path && errno == ENOENT ) { - if ( create_path && errno == ENOENT ) - { - Debug( 3, "Swap path '%s' missing, creating", path ); - if ( mkdir( path, 0755 ) ) - { - Error( "Can't mkdir %s: %s", path, strerror(errno)); - return( false ); - } - if ( stat( path, &stat_buf ) < 0 ) - { - Error( "Can't stat '%s': %s", path, strerror(errno) ); - return( false ); - } - } - else - { - Error( "Can't stat '%s': %s", path, strerror(errno) ); - return( false ); - } - } - if ( !S_ISDIR(stat_buf.st_mode) ) - { - Error( "Swap image path '%s' is not a directory", path ); + Debug( 3, "Swap path '%s' missing, creating", path ); + if ( mkdir( path, 0755 ) ) + { + Error( "Can't mkdir %s: %s", path, strerror(errno)); return( false ); - } - - mode_t mask = 0; - if ( uid == stat_buf.st_uid ) - { - // If we are the owner - mask = 00700; - } - else if ( gid == stat_buf.st_gid ) - { - // If we are in the owner group - mask = 00070; + } + if ( stat( path, &stat_buf ) < 0 ) + { + Error( "Can't stat '%s': %s", path, strerror(errno) ); + return( false ); + } } else { - // We are neither the owner nor in the group - mask = 00007; + Error( "Can't stat '%s': %s", path, strerror(errno) ); + return( false ); } + } + if ( !S_ISDIR(stat_buf.st_mode) ) + { + Error( "Swap image path '%s' is not a directory", path ); + return( false ); + } - if ( (stat_buf.st_mode & mask) != mask ) - { - Error( "Insufficient permissions on swap image path '%s'", path ); - return( false ); - } - return( true ); + mode_t mask = 0; + if ( uid == stat_buf.st_uid ) + { + // If we are the owner + mask = 00700; + } + else if ( gid == stat_buf.st_gid ) + { + // If we are in the owner group + mask = 00070; + } + else + { + // We are neither the owner nor in the group + mask = 00007; + } + + if ( (stat_buf.st_mode & mask) != mask ) + { + Error( "Insufficient permissions on swap image path '%s'", path ); + return( false ); + } + return( true ); } void MonitorStream::processCommand( const CmdMsg *msg ) { - Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ); - // Check for incoming command - switch( (MsgCommand)msg->msg_data[0] ) + Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] ); + // Check for incoming command + switch( (MsgCommand)msg->msg_data[0] ) + { + case CMD_PAUSE : { - case CMD_PAUSE : - { - Debug( 1, "Got PAUSE command" ); + Debug( 1, "Got PAUSE command" ); - // Set paused flag - paused = true; - // Set delayed flag - delayed = true; - last_frame_sent = TV_2_FLOAT( now ); - break; - } - case CMD_PLAY : - { - Debug( 1, "Got PLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - // Set delayed_play flag - delayed = true; - } - replay_rate = ZM_RATE_BASE; - break; - } - case CMD_VARPLAY : - { - Debug( 1, "Got VARPLAY command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - // Set delayed_play flag - delayed = true; - } - replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; - break; - } - case CMD_STOP : - { - Debug( 1, "Got STOP command" ); + // Set paused flag + paused = true; + // Set delayed flag + delayed = true; + last_frame_sent = TV_2_FLOAT( now ); + break; + } + case CMD_PLAY : + { + Debug( 1, "Got PLAY command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + // Set delayed_play flag + delayed = true; + } + replay_rate = ZM_RATE_BASE; + break; + } + case CMD_VARPLAY : + { + Debug( 1, "Got VARPLAY command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + // Set delayed_play flag + delayed = true; + } + replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; + break; + } + case CMD_STOP : + { + Debug( 1, "Got STOP command" ); - // Clear paused flag - paused = false; - // Clear delayed_play flag - delayed = false; - break; - } - case CMD_FASTFWD : - { - Debug( 1, "Got FAST FWD command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - // Set delayed_play flag - delayed = true; - } - // Set play rate - switch ( replay_rate ) - { - case 2 * ZM_RATE_BASE : - replay_rate = 5 * ZM_RATE_BASE; - break; - case 5 * ZM_RATE_BASE : - replay_rate = 10 * ZM_RATE_BASE; - break; - case 10 * ZM_RATE_BASE : - replay_rate = 25 * ZM_RATE_BASE; - break; - case 25 * ZM_RATE_BASE : - case 50 * ZM_RATE_BASE : - replay_rate = 50 * ZM_RATE_BASE; - break; - default : - replay_rate = 2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_SLOWFWD : - { - Debug( 1, "Got SLOW FWD command" ); - // Set paused flag - paused = true; - // Set delayed flag - delayed = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = 1; - break; - } - case CMD_SLOWREV : - { - Debug( 1, "Got SLOW REV command" ); - // Set paused flag - paused = true; - // Set delayed flag - delayed = true; - // Set play rate - replay_rate = ZM_RATE_BASE; - // Set step - step = -1; - break; - } - case CMD_FASTREV : - { - Debug( 1, "Got FAST REV command" ); - if ( paused ) - { - // Clear paused flag - paused = false; - // Set delayed_play flag - delayed = true; - } - // Set play rate - switch ( replay_rate ) - { - case -2 * ZM_RATE_BASE : - replay_rate = -5 * ZM_RATE_BASE; - break; - case -5 * ZM_RATE_BASE : - replay_rate = -10 * ZM_RATE_BASE; - break; - case -10 * ZM_RATE_BASE : - replay_rate = -25 * ZM_RATE_BASE; - break; - case -25 * ZM_RATE_BASE : - case -50 * ZM_RATE_BASE : - replay_rate = -50 * ZM_RATE_BASE; - break; - default : - replay_rate = -2 * ZM_RATE_BASE; - break; - } - break; - } - case CMD_ZOOMIN : - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); - switch ( zoom ) - { - case 100: - zoom = 150; - break; - case 150: - zoom = 200; - break; - case 200: - zoom = 300; - break; - case 300: - zoom = 400; - break; - case 400: - default : - zoom = 500; - break; - } - break; - } - case CMD_ZOOMOUT : - { - Debug( 1, "Got ZOOM OUT command" ); - switch ( zoom ) - { - case 500: - zoom = 400; - break; - case 400: - zoom = 300; - break; - case 300: - zoom = 200; - break; - case 200: - zoom = 150; - break; - case 150: - default : - zoom = 100; - break; - } - break; - } - case CMD_PAN : - { - x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; - Debug( 1, "Got PAN command, to %d,%d", x, y ); - break; - } - case CMD_SCALE : - { - scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; - Debug( 1, "Got SCALE command, to %d", scale ); - break; - } - case CMD_QUERY : - { - Debug( 1, "Got QUERY command, sending STATUS" ); - break; - } + // Clear paused flag + paused = false; + // Clear delayed_play flag + delayed = false; + break; + } + case CMD_FASTFWD : + { + Debug( 1, "Got FAST FWD command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + // Set delayed_play flag + delayed = true; + } + // Set play rate + switch ( replay_rate ) + { + case 2 * ZM_RATE_BASE : + replay_rate = 5 * ZM_RATE_BASE; + break; + case 5 * ZM_RATE_BASE : + replay_rate = 10 * ZM_RATE_BASE; + break; + case 10 * ZM_RATE_BASE : + replay_rate = 25 * ZM_RATE_BASE; + break; + case 25 * ZM_RATE_BASE : + case 50 * ZM_RATE_BASE : + replay_rate = 50 * ZM_RATE_BASE; + break; default : - { - Error( "Got unexpected command %d", msg->msg_data[0] ); - break; - } + replay_rate = 2 * ZM_RATE_BASE; + break; + } + break; } - - struct { - int id; - int state; - double fps; - int buffer_level; - int rate; - double delay; - int zoom; - bool delayed; - bool paused; - bool enabled; - bool forced; - } status_data; - - status_data.id = monitor->Id(); - status_data.fps = monitor->GetFPS(); - status_data.state = monitor->shared_data->state; - if ( playback_buffer > 0 ) - status_data.buffer_level = (MOD_ADD( (temp_write_index-temp_read_index), 0, temp_image_buffer_count )*100)/temp_image_buffer_count; - else - status_data.buffer_level = 0; - status_data.delayed = delayed; - status_data.paused = paused; - status_data.rate = replay_rate; - status_data.delay = TV_2_FLOAT( now ) - TV_2_FLOAT( last_frame_timestamp ); - status_data.zoom = zoom; - //status_data.enabled = monitor->shared_data->active; - status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF; - status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON; - Debug( 2, "L:%d, D:%d, P:%d, R:%d, d:%.3f, Z:%d, E:%d F:%d", - status_data.buffer_level, - status_data.delayed, - status_data.paused, - status_data.rate, - status_data.delay, - status_data.zoom, - status_data.enabled, - status_data.forced - ); - - DataMsg status_msg; - status_msg.msg_type = MSG_DATA_WATCH; - memcpy( &status_msg.msg_data, &status_data, sizeof(status_msg.msg_data) ); - int nbytes = 0; - if ( (nbytes = sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) )) < 0 ) + case CMD_SLOWFWD : { - //if ( errno != EAGAIN ) - { - Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); - //exit( -1 ); - } + Debug( 1, "Got SLOW FWD command" ); + // Set paused flag + paused = true; + // Set delayed flag + delayed = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = 1; + break; } + case CMD_SLOWREV : + { + Debug( 1, "Got SLOW REV command" ); + // Set paused flag + paused = true; + // Set delayed flag + delayed = true; + // Set play rate + replay_rate = ZM_RATE_BASE; + // Set step + step = -1; + break; + } + case CMD_FASTREV : + { + Debug( 1, "Got FAST REV command" ); + if ( paused ) + { + // Clear paused flag + paused = false; + // Set delayed_play flag + delayed = true; + } + // Set play rate + switch ( replay_rate ) + { + case -2 * ZM_RATE_BASE : + replay_rate = -5 * ZM_RATE_BASE; + break; + case -5 * ZM_RATE_BASE : + replay_rate = -10 * ZM_RATE_BASE; + break; + case -10 * ZM_RATE_BASE : + replay_rate = -25 * ZM_RATE_BASE; + break; + case -25 * ZM_RATE_BASE : + case -50 * ZM_RATE_BASE : + replay_rate = -50 * ZM_RATE_BASE; + break; + default : + replay_rate = -2 * ZM_RATE_BASE; + break; + } + break; + } + case CMD_ZOOMIN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got ZOOM IN command, to %d,%d", x, y ); + switch ( zoom ) + { + case 100: + zoom = 150; + break; + case 150: + zoom = 200; + break; + case 200: + zoom = 300; + break; + case 300: + zoom = 400; + break; + case 400: + default : + zoom = 500; + break; + } + break; + } + case CMD_ZOOMOUT : + { + Debug( 1, "Got ZOOM OUT command" ); + switch ( zoom ) + { + case 500: + zoom = 400; + break; + case 400: + zoom = 300; + break; + case 300: + zoom = 200; + break; + case 200: + zoom = 150; + break; + case 150: + default : + zoom = 100; + break; + } + break; + } + case CMD_PAN : + { + x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4]; + Debug( 1, "Got PAN command, to %d,%d", x, y ); + break; + } + case CMD_SCALE : + { + scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; + Debug( 1, "Got SCALE command, to %d", scale ); + break; + } + case CMD_QUIT : + { + Info ("User initiated exit - CMD_QUIT"); + break; + } + case CMD_QUERY : + { + Debug( 1, "Got QUERY command, sending STATUS" ); + break; + } + default : + { + Error( "Got unexpected command %d", msg->msg_data[0] ); + break; + } + } - updateFrameRate( monitor->GetFPS() ); + struct { + int id; + int state; + double fps; + int buffer_level; + int rate; + double delay; + int zoom; + bool delayed; + bool paused; + bool enabled; + bool forced; + } status_data; + + status_data.id = monitor->Id(); + status_data.fps = monitor->GetFPS(); + status_data.state = monitor->shared_data->state; + if ( playback_buffer > 0 ) + status_data.buffer_level = (MOD_ADD( (temp_write_index-temp_read_index), 0, temp_image_buffer_count )*100)/temp_image_buffer_count; + else + status_data.buffer_level = 0; + status_data.delayed = delayed; + status_data.paused = paused; + status_data.rate = replay_rate; + status_data.delay = TV_2_FLOAT( now ) - TV_2_FLOAT( last_frame_timestamp ); + status_data.zoom = zoom; + //status_data.enabled = monitor->shared_data->active; + status_data.enabled = monitor->trigger_data->trigger_state!=Monitor::TRIGGER_OFF; + status_data.forced = monitor->trigger_data->trigger_state==Monitor::TRIGGER_ON; + Debug( 2, "L:%d, D:%d, P:%d, R:%d, d:%.3f, Z:%d, E:%d F:%d", + status_data.buffer_level, + status_data.delayed, + status_data.paused, + status_data.rate, + status_data.delay, + status_data.zoom, + status_data.enabled, + status_data.forced + ); + + DataMsg status_msg; + status_msg.msg_type = MSG_DATA_WATCH; + memcpy( &status_msg.msg_data, &status_data, sizeof(status_data) ); + int nbytes = 0; + if ( (nbytes = sendto( sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr) )) < 0 ) + { + //if ( errno != EAGAIN ) + { + Error( "Can't sendto on sd %d: %s", sd, strerror(errno) ); + //exit( -1 ); + } + } + + // quit after sending a status, if this was a quit request + if ((MsgCommand)msg->msg_data[0]==CMD_QUIT) + exit(0); + + updateFrameRate( monitor->GetFPS() ); } bool MonitorStream::sendFrame( const char *filepath, struct timeval *timestamp ) { - bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); + bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)); - if ( type != STREAM_JPEG ) - send_raw = false; - if ( !config.timestamp_on_capture && timestamp ) - send_raw = false; + if ( type != STREAM_JPEG ) + send_raw = false; + if ( !config.timestamp_on_capture && timestamp ) + send_raw = false; - if ( !send_raw ) + if ( !send_raw ) + { + Image temp_image( filepath ); + + return( sendFrame( &temp_image, timestamp ) ); + } + else + { + int img_buffer_size = 0; + static unsigned char img_buffer[ZM_MAX_IMAGE_SIZE]; + + FILE *fdj = NULL; + if ( (fdj = fopen( filepath, "r" )) ) { - Image temp_image( filepath ); - - return( sendFrame( &temp_image, timestamp ) ); + img_buffer_size = fread( img_buffer, 1, sizeof(img_buffer), fdj ); + fclose( fdj ); } else { - int img_buffer_size = 0; - static unsigned char img_buffer[ZM_MAX_IMAGE_SIZE]; - - FILE *fdj = NULL; - if ( (fdj = fopen( filepath, "r" )) ) - { - img_buffer_size = fread( img_buffer, 1, sizeof(img_buffer), fdj ); - fclose( fdj ); - } - else - { - Error( "Can't open %s: %s", filepath, strerror(errno) ); - return( false ); - } - - // Calculate how long it takes to actually send the frame - struct timeval frameStartTime; - gettimeofday( &frameStartTime, NULL ); - - fprintf( stdout, "--ZoneMinderFrame\r\n" ); - fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) - { - if ( !zm_terminate ) - Error( "Unable to send stream frame: %s", strerror(errno) ); - return( false ); - } - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); - - struct timeval frameEndTime; - gettimeofday( &frameEndTime, NULL ); - - int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); - if ( frameSendTime > 1000/maxfps ) - { - maxfps /= 2; - Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); - } - - last_frame_sent = TV_2_FLOAT( now ); - - return( true ); + Error( "Can't open %s: %s", filepath, strerror(errno) ); + return( false ); } - return( false ); + + // Calculate how long it takes to actually send the frame + struct timeval frameStartTime; + gettimeofday( &frameStartTime, NULL ); + + fprintf( stdout, "--ZoneMinderFrame\r\n" ); + fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); + fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) + { + if ( !zm_terminate ) + Error( "Unable to send stream frame: %s", strerror(errno) ); + return( false ); + } + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); + + struct timeval frameEndTime; + gettimeofday( &frameEndTime, NULL ); + + int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); + if ( frameSendTime > 1000/maxfps ) + { + maxfps /= 2; + Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); + } + + last_frame_sent = TV_2_FLOAT( now ); + + return( true ); + } + return( false ); } bool MonitorStream::sendFrame( Image *image, struct timeval *timestamp ) { - Image *send_image = prepareImage( image ); - if ( !config.timestamp_on_capture && timestamp ) - monitor->TimestampImage( send_image, timestamp ); + Image *send_image = prepareImage( image ); + if ( !config.timestamp_on_capture && timestamp ) + monitor->TimestampImage( send_image, timestamp ); #if HAVE_LIBAVCODEC - if ( type == STREAM_MPEG ) + if ( type == STREAM_MPEG ) + { + if ( !vid_stream ) { - if ( !vid_stream ) - { - vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); - fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); - vid_stream->OpenStream(); - } - static struct timeval base_time; - struct DeltaTimeval delta_time; - if ( !frame_count ) - base_time = *timestamp; - DELTA_TIMEVAL( delta_time, *timestamp, base_time, DT_PREC_3 ); - /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.delta ); + vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height() ); + fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); + vid_stream->OpenStream(); } - else + static struct timeval base_time; + struct DeltaTimeval delta_time; + if ( !frame_count ) + base_time = *timestamp; + DELTA_TIMEVAL( delta_time, *timestamp, base_time, DT_PREC_3 ); + /* double pts = */ vid_stream->EncodeFrame( send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.delta ); + } + else #endif // HAVE_LIBAVCODEC + { + static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; + + int img_buffer_size = 0; + unsigned char *img_buffer = temp_img_buffer; + + // Calculate how long it takes to actually send the frame + struct timeval frameStartTime; + gettimeofday( &frameStartTime, NULL ); + + fprintf( stdout, "--ZoneMinderFrame\r\n" ); + switch( type ) { - static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE]; - - int img_buffer_size = 0; - unsigned char *img_buffer = temp_img_buffer; - - // Calculate how long it takes to actually send the frame - struct timeval frameStartTime; - gettimeofday( &frameStartTime, NULL ); - - fprintf( stdout, "--ZoneMinderFrame\r\n" ); - switch( type ) - { - case STREAM_JPEG : - send_image->EncodeJpeg( img_buffer, &img_buffer_size ); - fprintf( stdout, "Content-Type: image/jpeg\r\n" ); - break; - case STREAM_RAW : - fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); - img_buffer = (uint8_t*)send_image->Buffer(); - img_buffer_size = send_image->Size(); - break; - case STREAM_ZIP : - fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); - unsigned long zip_buffer_size; - send_image->Zip( img_buffer, &zip_buffer_size ); - img_buffer_size = zip_buffer_size; - break; - default : - Fatal( "Unexpected frame type %d", type ); - break; - } - fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); - if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) - { - if ( !zm_terminate ) - Error( "Unable to send stream frame: %s", strerror(errno) ); - return( false ); - } - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); - - struct timeval frameEndTime; - gettimeofday( &frameEndTime, NULL ); - - int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); - if ( frameSendTime > 1000/maxfps ) - { - maxfps /= 1.5; - Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); - } + case STREAM_JPEG : + send_image->EncodeJpeg( img_buffer, &img_buffer_size ); + fprintf( stdout, "Content-Type: image/jpeg\r\n" ); + break; + case STREAM_RAW : + fprintf( stdout, "Content-Type: image/x-rgb\r\n" ); + img_buffer = (uint8_t*)send_image->Buffer(); + img_buffer_size = send_image->Size(); + break; + case STREAM_ZIP : + fprintf( stdout, "Content-Type: image/x-rgbz\r\n" ); + unsigned long zip_buffer_size; + send_image->Zip( img_buffer, &zip_buffer_size ); + img_buffer_size = zip_buffer_size; + break; + default : + Fatal( "Unexpected frame type %d", type ); + break; } - last_frame_sent = TV_2_FLOAT( now ); - return( true ); + fprintf( stdout, "Content-Length: %d\r\n\r\n", img_buffer_size ); + if ( fwrite( img_buffer, img_buffer_size, 1, stdout ) != 1 ) + { + if ( !zm_terminate ) + Error( "Unable to send stream frame: %s", strerror(errno) ); + return( false ); + } + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); + + struct timeval frameEndTime; + gettimeofday( &frameEndTime, NULL ); + + int frameSendTime = tvDiffMsec( frameStartTime, frameEndTime ); + if ( frameSendTime > 1000/maxfps ) + { + maxfps /= 1.5; + Error( "Frame send time %d msec too slow, throttling maxfps to %.2f", frameSendTime, maxfps ); + } + } + last_frame_sent = TV_2_FLOAT( now ); + return( true ); } void MonitorStream::runStream() { - if ( type == STREAM_SINGLE ) + if ( type == STREAM_SINGLE ) + { + // Not yet migrated over to stream class + monitor->SingleImage( scale ); + return; + } + + openComms(); + + checkInitialised(); + + updateFrameRate( monitor->GetFPS() ); + + if ( type == STREAM_JPEG ) + fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); + + int last_read_index = monitor->image_buffer_count; + + time_t stream_start_time; + time( &stream_start_time ); + + frame_count = 0; + + temp_image_buffer = 0; + temp_image_buffer_count = playback_buffer; + temp_read_index = temp_image_buffer_count; + temp_write_index = temp_image_buffer_count; + + char *swap_path = 0; + bool buffered_playback = false; + + // 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id + const int max_swap_len_suffix = 15; + + int swap_path_length = strlen(config.path_swap)+1; // +1 for NULL terminator + + if ( connkey && playback_buffer > 0 ) { + + if ( swap_path_length + max_swap_len_suffix > PATH_MAX ) { + Error( "Swap Path is too long. %d > %d ", swap_path_length+max_swap_len_suffix, PATH_MAX ); + } else { + swap_path = (char *)malloc( swap_path_length+max_swap_len_suffix ); + Debug( 3, "Checking swap image path %s", config.path_swap ); + strncpy( swap_path, config.path_swap, swap_path_length ); + if ( checkSwapPath( swap_path, false ) ) { + snprintf( &(swap_path[swap_path_length]), max_swap_len_suffix, "/zmswap-m%d", monitor->Id() ); + if ( checkSwapPath( swap_path, true ) ) { + snprintf( &(swap_path[swap_path_length]), max_swap_len_suffix, "/zmswap-q%06d", connkey ); + if ( checkSwapPath( swap_path, true ) ) { + buffered_playback = true; + } + } + } + + if ( !buffered_playback ) { + Error( "Unable to validate swap image path, disabling buffered playback" ); + } else { + Debug( 2, "Assigning temporary buffer" ); + temp_image_buffer = new SwapImage[temp_image_buffer_count]; + memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count ); + Debug( 2, "Assigned temporary buffer" ); + } + } + } + + float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs) + while ( !zm_terminate ) + { + bool got_command = false; + if ( feof( stdout ) || ferror( stdout ) || !monitor->ShmValid() ) { - // Not yet migrated over to stream class - monitor->SingleImage( scale ); - return; + break; } - openComms(); + gettimeofday( &now, NULL ); - checkInitialised(); + if ( connkey ) + { + while(checkCommandQueue()) { + got_command = true; + } + } - updateFrameRate( monitor->GetFPS() ); + //bool frame_sent = false; + if ( buffered_playback && delayed ) + { + if ( temp_read_index == temp_write_index ) + { + // Go back to live viewing + Debug( 1, "Exceeded temporary streaming buffer" ); + // Clear paused flag + paused = false; + // Clear delayed_play flag + delayed = false; + replay_rate = ZM_RATE_BASE; + } + else + { + if ( !paused ) + { + int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); + //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); + SwapImage *swap_image = &temp_image_buffer[temp_index]; - if ( type == STREAM_JPEG ) - fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" ); + if ( !swap_image->valid ) + { + paused = true; + delayed = true; + temp_read_index = MOD_ADD( temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count ); + } + else + { + //Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) ); + double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate; + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - int last_read_index = monitor->image_buffer_count; + //Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) ); + // If the next frame is due + if ( actual_delta_time > expected_delta_time ) + { + //Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time ); + if ( temp_index%frame_mod == 0 ) + { + Debug( 2, "Sending delayed frame %d", temp_index ); + // Send the next frame + if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) + zm_terminate = true; + memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); + //frame_sent = true; + } + temp_read_index = MOD_ADD( temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count ); + } + } + } + else if ( step != 0 ) + { + temp_read_index = MOD_ADD( temp_read_index, (step>0?1:-1), temp_image_buffer_count ); - time_t stream_start_time; - time( &stream_start_time ); + SwapImage *swap_image = &temp_image_buffer[temp_read_index]; - frame_count = 0; + // Send the next frame + if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) ) + zm_terminate = true; + memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); + //frame_sent = true; + step = 0; + } + else + { + int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); - temp_image_buffer = 0; - temp_image_buffer_count = playback_buffer; - temp_read_index = temp_image_buffer_count; - temp_write_index = temp_image_buffer_count; + double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; + if ( got_command || actual_delta_time > 5 ) + { + // Send keepalive + Debug( 2, "Sending keepalive frame %d", temp_index ); + // Send the next frame + if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) + zm_terminate = true; + //frame_sent = true; + } + } + } + if ( temp_read_index == temp_write_index ) + { + // Go back to live viewing + Warning( "Rewound over write index, resuming live play" ); + // Clear paused flag + paused = false; + // Clear delayed_play flag + delayed = false; + replay_rate = ZM_RATE_BASE; + } + } + if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) + { + int index = monitor->shared_data->last_write_index%monitor->image_buffer_count; + last_read_index = monitor->shared_data->last_write_index; + //Debug( 1, "%d: %x - %x", index, image_buffer[index].image, image_buffer[index].image->buffer ); + if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) + { + if ( !paused && !delayed ) + { + // Send the next frame + Monitor::Snapshot *snap = &monitor->image_buffer[index]; + if ( !sendFrame( snap->image, snap->timestamp ) ) + zm_terminate = true; + memcpy( &last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp) ); + //frame_sent = true; + + temp_read_index = temp_write_index; + } + } + if ( buffered_playback ) + { + if ( monitor->shared_data->valid ) + { + if ( monitor->image_buffer[index].timestamp->tv_sec ) + { + int temp_index = temp_write_index%temp_image_buffer_count; + Debug( 2, "Storing frame %d", temp_index ); + if ( !temp_image_buffer[temp_index].valid ) + { + snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path, temp_index ); + temp_image_buffer[temp_index].valid = true; + } + memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) ); + monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality ); + temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count ); + if ( temp_write_index == temp_read_index ) + { + // Go back to live viewing + Warning( "Exceeded temporary buffer, resuming live play" ); + // Clear paused flag + paused = false; + // Clear delayed_play flag + delayed = false; + replay_rate = ZM_RATE_BASE; + } + } + else + { + Warning( "Unable to store frame as timestamp invalid" ); + } + } + else + { + Warning( "Unable to store frame as shared memory invalid" ); + } + } + frame_count++; + } + usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); + if ( ttl ) + { + if ( (now.tv_sec - stream_start_time) > ttl ) + { + break; + } + } + if ( (TV_2_FLOAT( now ) - last_frame_sent) > max_secs_since_last_sent_frame ) + { + Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame ); + break; + } + } + if ( buffered_playback ) + { char swap_path[PATH_MAX] = ""; - bool buffered_playback = false; - if ( connkey && playback_buffer > 0 ) + snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", config.path_swap, monitor->Id(), connkey ); + Debug( 1, "Cleaning swap files from %s", swap_path ); + struct stat stat_buf; + if ( stat( swap_path, &stat_buf ) < 0 ) { - Debug( 2, "Checking swap image location" ); - Debug( 3, "Checking swap image path" ); - strncpy( swap_path, config.path_swap, sizeof(swap_path) ); - if ( checkSwapPath( swap_path, false ) ) - { - snprintf( &(swap_path[strlen(swap_path)]), sizeof(swap_path)-strlen(swap_path), "/zmswap-m%d", monitor->Id() ); - if ( checkSwapPath( swap_path, true ) ) - { - snprintf( &(swap_path[strlen(swap_path)]), sizeof(swap_path)-strlen(swap_path), "/zmswap-q%06d", connkey ); - if ( checkSwapPath( swap_path, true ) ) - { - buffered_playback = true; - } - } - } + if ( errno != ENOENT ) + { + Error( "Can't stat '%s': %s", swap_path, strerror(errno) ); + } + } + else if ( !S_ISDIR(stat_buf.st_mode) ) + { + Error( "Swap image path '%s' is not a directory", swap_path ); + } + else + { + char glob_pattern[PATH_MAX] = ""; - if ( !buffered_playback ) + snprintf( glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path ); + glob_t pglob; + int glob_status = glob( glob_pattern, 0, 0, &pglob ); + if ( glob_status != 0 ) + { + if ( glob_status < 0 ) { - Error( "Unable to validate swap image path, disabling buffered playback" ); + Error( "Can't glob '%s': %s", glob_pattern, strerror(errno) ); } else { - Debug( 2, "Assigning temporary buffer" ); - temp_image_buffer = new SwapImage[temp_image_buffer_count]; - memset( temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count ); - Debug( 2, "Assigned temporary buffer" ); + Debug( 1, "Can't glob '%s': %d", glob_pattern, glob_status ); } + } + else + { + for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) + { + if ( unlink( pglob.gl_pathv[i] ) < 0 ) + { + Error( "Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno) ); + } + } + } + globfree( &pglob ); + if ( rmdir( swap_path ) < 0 ) + { + Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) ); + } } - - float max_secs_since_last_sent_frame = 10.0; //should be > keep alive amount (5 secs) - while ( !zm_terminate ) - { - bool got_command = false; - if ( feof( stdout ) || ferror( stdout ) || !monitor->ShmValid() ) - { - break; - } - - gettimeofday( &now, NULL ); - - if ( connkey ) - { - while(checkCommandQueue()) { - got_command = true; - } - } - - //bool frame_sent = false; - if ( buffered_playback && delayed ) - { - if ( temp_read_index == temp_write_index ) - { - // Go back to live viewing - Debug( 1, "Exceeded temporary streaming buffer" ); - // Clear paused flag - paused = false; - // Clear delayed_play flag - delayed = false; - replay_rate = ZM_RATE_BASE; - } - else - { - if ( !paused ) - { - int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); - //Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index ); - SwapImage *swap_image = &temp_image_buffer[temp_index]; - - if ( !swap_image->valid ) - { - paused = true; - delayed = true; - temp_read_index = MOD_ADD( temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count ); - } - else - { - //Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) ); - double expected_delta_time = ((TV_2_FLOAT( swap_image->timestamp ) - TV_2_FLOAT( last_frame_timestamp )) * ZM_RATE_BASE)/replay_rate; - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - - //Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) ); - // If the next frame is due - if ( actual_delta_time > expected_delta_time ) - { - //Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time ); - if ( temp_index%frame_mod == 0 ) - { - Debug( 2, "Sending delayed frame %d", temp_index ); - // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) - zm_terminate = true; - memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); - //frame_sent = true; - } - temp_read_index = MOD_ADD( temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count ); - } - } - } - else if ( step != 0 ) - { - temp_read_index = MOD_ADD( temp_read_index, (step>0?1:-1), temp_image_buffer_count ); - - SwapImage *swap_image = &temp_image_buffer[temp_read_index]; - - // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_read_index].file_name, &temp_image_buffer[temp_read_index].timestamp ) ) - zm_terminate = true; - memcpy( &last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp) ); - //frame_sent = true; - step = 0; - } - else - { - int temp_index = MOD_ADD( temp_read_index, 0, temp_image_buffer_count ); - - double actual_delta_time = TV_2_FLOAT( now ) - last_frame_sent; - if ( got_command || actual_delta_time > 5 ) - { - // Send keepalive - Debug( 2, "Sending keepalive frame %d", temp_index ); - // Send the next frame - if ( !sendFrame( temp_image_buffer[temp_index].file_name, &temp_image_buffer[temp_index].timestamp ) ) - zm_terminate = true; - //frame_sent = true; - } - } - } - if ( temp_read_index == temp_write_index ) - { - // Go back to live viewing - Warning( "Rewound over write index, resuming live play" ); - // Clear paused flag - paused = false; - // Clear delayed_play flag - delayed = false; - replay_rate = ZM_RATE_BASE; - } - } - if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) - { - int index = monitor->shared_data->last_write_index%monitor->image_buffer_count; - last_read_index = monitor->shared_data->last_write_index; - //Debug( 1, "%d: %x - %x", index, image_buffer[index].image, image_buffer[index].image->buffer ); - if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) - { - if ( !paused && !delayed ) - { - // Send the next frame - Monitor::Snapshot *snap = &monitor->image_buffer[index]; - - if ( !sendFrame( snap->image, snap->timestamp ) ) - zm_terminate = true; - memcpy( &last_frame_timestamp, snap->timestamp, sizeof(last_frame_timestamp) ); - //frame_sent = true; - - temp_read_index = temp_write_index; - } - } - if ( buffered_playback ) - { - if ( monitor->shared_data->valid ) - { - if ( monitor->image_buffer[index].timestamp->tv_sec ) - { - int temp_index = temp_write_index%temp_image_buffer_count; - Debug( 2, "Storing frame %d", temp_index ); - if ( !temp_image_buffer[temp_index].valid ) - { - snprintf( temp_image_buffer[temp_index].file_name, sizeof(temp_image_buffer[0].file_name), "%s/zmswap-i%05d.jpg", swap_path, temp_index ); - temp_image_buffer[temp_index].valid = true; - } - memcpy( &(temp_image_buffer[temp_index].timestamp), monitor->image_buffer[index].timestamp, sizeof(temp_image_buffer[0].timestamp) ); - monitor->image_buffer[index].image->WriteJpeg( temp_image_buffer[temp_index].file_name, config.jpeg_file_quality ); - temp_write_index = MOD_ADD( temp_write_index, 1, temp_image_buffer_count ); - if ( temp_write_index == temp_read_index ) - { - // Go back to live viewing - Warning( "Exceeded temporary buffer, resuming live play" ); - // Clear paused flag - paused = false; - // Clear delayed_play flag - delayed = false; - replay_rate = ZM_RATE_BASE; - } - } - else - { - Warning( "Unable to store frame as timestamp invalid" ); - } - } - else - { - Warning( "Unable to store frame as shared memory invalid" ); - } - } - frame_count++; - } - usleep( (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))) ); - if ( ttl ) - { - if ( (now.tv_sec - stream_start_time) > ttl ) - { - break; - } - } - if ( (TV_2_FLOAT( now ) - last_frame_sent) > max_secs_since_last_sent_frame ) - { - Error( "Terminating, last frame sent time %f secs more than maximum of %f", TV_2_FLOAT( now ) - last_frame_sent, max_secs_since_last_sent_frame ); - break; - } - } - if ( buffered_playback ) - { - char swap_path[PATH_MAX] = ""; - - snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", config.path_swap, monitor->Id(), connkey ); - Debug( 1, "Cleaning swap files from %s", swap_path ); - struct stat stat_buf; - if ( stat( swap_path, &stat_buf ) < 0 ) - { - if ( errno != ENOENT ) - { - Error( "Can't stat '%s': %s", swap_path, strerror(errno) ); - } - } - else if ( !S_ISDIR(stat_buf.st_mode) ) - { - Error( "Swap image path '%s' is not a directory", swap_path ); - } - else - { - char glob_pattern[PATH_MAX] = ""; - - snprintf( glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path ); - glob_t pglob; - int glob_status = glob( glob_pattern, 0, 0, &pglob ); - if ( glob_status != 0 ) - { - if ( glob_status < 0 ) - { - Error( "Can't glob '%s': %s", glob_pattern, strerror(errno) ); - } - else - { - Debug( 1, "Can't glob '%s': %d", glob_pattern, glob_status ); - } - } - else - { - for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) - { - if ( unlink( pglob.gl_pathv[i] ) < 0 ) - { - Error( "Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno) ); - } - } - } - globfree( &pglob ); - if ( rmdir( swap_path ) < 0 ) - { - Error( "Can't rmdir '%s': %s", swap_path, strerror(errno) ); - } - } - } - closeComms(); + } + if ( swap_path ) free( swap_path ); + closeComms(); } void Monitor::SingleImage( int scale) { - int img_buffer_size = 0; - static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE]; - Image scaled_image; - int index = shared_data->last_write_index%image_buffer_count; - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; + int img_buffer_size = 0; + static JOCTET img_buffer[ZM_MAX_IMAGE_SIZE]; + Image scaled_image; + int index = shared_data->last_write_index%image_buffer_count; + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; - if ( scale != ZM_SCALE_BASE ) - { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); - snap_image = &scaled_image; - } - if ( !config.timestamp_on_capture ) - { - TimestampImage( snap_image, snap->timestamp ); - } - snap_image->EncodeJpeg( img_buffer, &img_buffer_size ); - - fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); - fwrite( img_buffer, img_buffer_size, 1, stdout ); + if ( scale != ZM_SCALE_BASE ) + { + scaled_image.Assign( *snap_image ); + scaled_image.Scale( scale ); + snap_image = &scaled_image; + } + if ( !config.timestamp_on_capture ) + { + TimestampImage( snap_image, snap->timestamp ); + } + snap_image->EncodeJpeg( img_buffer, &img_buffer_size ); + + fprintf( stdout, "Content-Length: %d\r\n", img_buffer_size ); + fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); + fwrite( img_buffer, img_buffer_size, 1, stdout ); } void Monitor::SingleImageRaw( int scale) { - Image scaled_image; - int index = shared_data->last_write_index%image_buffer_count; - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; + Image scaled_image; + int index = shared_data->last_write_index%image_buffer_count; + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; - if ( scale != ZM_SCALE_BASE ) - { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); - snap_image = &scaled_image; - } - if ( !config.timestamp_on_capture ) - { - TimestampImage( snap_image, snap->timestamp ); - } - - fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() ); - fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" ); - fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout ); + if ( scale != ZM_SCALE_BASE ) + { + scaled_image.Assign( *snap_image ); + scaled_image.Scale( scale ); + snap_image = &scaled_image; + } + if ( !config.timestamp_on_capture ) + { + TimestampImage( snap_image, snap->timestamp ); + } + + fprintf( stdout, "Content-Length: %d\r\n", snap_image->Size() ); + fprintf( stdout, "Content-Type: image/x-rgb\r\n\r\n" ); + fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout ); } void Monitor::SingleImageZip( int scale) { - unsigned long img_buffer_size = 0; - static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; - Image scaled_image; - int index = shared_data->last_write_index%image_buffer_count; - Snapshot *snap = &image_buffer[index]; - Image *snap_image = snap->image; + unsigned long img_buffer_size = 0; + static Bytef img_buffer[ZM_MAX_IMAGE_SIZE]; + Image scaled_image; + int index = shared_data->last_write_index%image_buffer_count; + Snapshot *snap = &image_buffer[index]; + Image *snap_image = snap->image; - if ( scale != ZM_SCALE_BASE ) - { - scaled_image.Assign( *snap_image ); - scaled_image.Scale( scale ); - snap_image = &scaled_image; - } - if ( !config.timestamp_on_capture ) - { - TimestampImage( snap_image, snap->timestamp ); - } - snap_image->Zip( img_buffer, &img_buffer_size ); - - fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size ); - fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); - fwrite( img_buffer, img_buffer_size, 1, stdout ); + if ( scale != ZM_SCALE_BASE ) + { + scaled_image.Assign( *snap_image ); + scaled_image.Scale( scale ); + snap_image = &scaled_image; + } + if ( !config.timestamp_on_capture ) + { + TimestampImage( snap_image, snap->timestamp ); + } + snap_image->Zip( img_buffer, &img_buffer_size ); + + fprintf( stdout, "Content-Length: %ld\r\n", img_buffer_size ); + fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" ); + fwrite( img_buffer, img_buffer_size, 1, stdout ); } diff --git a/src/zm_monitor.h b/src/zm_monitor.h index f3164dd51..57fd7a369 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -50,381 +50,396 @@ class Monitor friend class MonitorStream; public: - typedef enum - { - QUERY=0, - CAPTURE, - ANALYSIS - } Purpose; + typedef enum + { + QUERY=0, + CAPTURE, + ANALYSIS + } Purpose; - typedef enum - { - NONE=1, - MONITOR, - MODECT, - RECORD, - MOCORD, - NODECT, - EXTDECT - } Function; + typedef enum + { + NONE=1, + MONITOR, + MODECT, + RECORD, + MOCORD, + NODECT + } Function; - typedef enum - { - ROTATE_0=1, - ROTATE_90, - ROTATE_180, - ROTATE_270, - FLIP_HORI, - FLIP_VERT - } Orientation; + typedef enum + { + ROTATE_0=1, + ROTATE_90, + ROTATE_180, + ROTATE_270, + FLIP_HORI, + FLIP_VERT + } Orientation; - typedef enum - { - IDLE, - PREALARM, - ALARM, - ALERT, - TAPE - } State; + typedef enum + { + IDLE, + PREALARM, + ALARM, + ALERT, + TAPE + } State; protected: - typedef std::set ZoneSet; + typedef std::set ZoneSet; - typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action; + typedef enum { GET_SETTINGS=0x1, SET_SETTINGS=0x2, RELOAD=0x4, SUSPEND=0x10, RESUME=0x20 } Action; - typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode; + typedef enum { CLOSE_TIME, CLOSE_IDLE, CLOSE_ALARM } EventCloseMode; - /* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */ - typedef struct - { - uint32_t size; /* +0 */ - uint32_t last_write_index; /* +4 */ - uint32_t last_read_index; /* +8 */ - uint32_t state; /* +12 */ - uint32_t last_event; /* +16 */ - uint32_t action; /* +20 */ - int32_t brightness; /* +24 */ - int32_t hue; /* +28 */ - int32_t colour; /* +32 */ - int32_t contrast; /* +36 */ - int32_t alarm_x; /* +40 */ - int32_t alarm_y; /* +44 */ - uint8_t valid; /* +48 */ - uint8_t active; /* +49 */ - uint8_t signal; /* +50 */ - uint8_t format; /* +51 */ - uint32_t imagesize; /* +52 */ - uint32_t epadding1; /* +56 */ - uint32_t epadding2; /* +60 */ - /* - ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. - ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. - */ - union { /* +64 */ - time_t last_write_time; - uint64_t extrapad1; - }; - union { /* +72 */ - time_t last_read_time; - uint64_t extrapad2; - }; - uint8_t control_state[256]; /* +80 */ - - } SharedData; + /* sizeof(SharedData) expected to be 336 bytes on 32bit and 64bit */ + typedef struct + { + uint32_t size; /* +0 */ + uint32_t last_write_index; /* +4 */ + uint32_t last_read_index; /* +8 */ + uint32_t state; /* +12 */ + uint32_t last_event; /* +16 */ + uint32_t action; /* +20 */ + int32_t brightness; /* +24 */ + int32_t hue; /* +28 */ + int32_t colour; /* +32 */ + int32_t contrast; /* +36 */ + int32_t alarm_x; /* +40 */ + int32_t alarm_y; /* +44 */ + uint8_t valid; /* +48 */ + uint8_t active; /* +49 */ + uint8_t signal; /* +50 */ + uint8_t format; /* +51 */ + uint32_t imagesize; /* +52 */ + uint32_t epadding1; /* +56 */ + uint32_t epadding2; /* +60 */ + /* + ** This keeps 32bit time_t and 64bit time_t identical and compatible as long as time is before 2038. + ** Shared memory layout should be identical for both 32bit and 64bit and is multiples of 16. + */ + union { /* +64 */ + time_t last_write_time; + uint64_t extrapad1; + }; + union { /* +72 */ + time_t last_read_time; + uint64_t extrapad2; + }; + uint8_t control_state[256]; /* +80 */ + + } SharedData; - typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState; - - /* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */ - typedef struct - { - uint32_t size; - uint32_t trigger_state; - uint32_t trigger_score; - uint32_t padding; - char trigger_cause[32]; - char trigger_text[256]; - char trigger_showtext[256]; - } TriggerData; + typedef enum { TRIGGER_CANCEL, TRIGGER_ON, TRIGGER_OFF } TriggerState; + + /* sizeof(TriggerData) expected to be 560 on 32bit & and 64bit */ + typedef struct + { + uint32_t size; + uint32_t trigger_state; + uint32_t trigger_score; + uint32_t padding; + char trigger_cause[32]; + char trigger_text[256]; + char trigger_showtext[256]; + } TriggerData; - /* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */ - struct Snapshot - { - struct timeval *timestamp; - Image *image; - void* padding; - }; + /* sizeof(Snapshot) expected to be 16 bytes on 32bit and 32 bytes on 64bit */ + struct Snapshot + { + struct timeval *timestamp; + Image *image; + void* padding; + }; - class MonitorLink - { - protected: - unsigned int id; - char name[64]; + class MonitorLink + { + protected: + unsigned int id; + char name[64]; - bool connected; - time_t last_connect_time; + bool connected; + time_t last_connect_time; #if ZM_MEM_MAPPED - int map_fd; - char mem_file[PATH_MAX]; + int map_fd; + char mem_file[PATH_MAX]; #else // ZM_MEM_MAPPED - int shm_id; + int shm_id; #endif // ZM_MEM_MAPPED - unsigned long mem_size; - unsigned char *mem_ptr; + off_t mem_size; + unsigned char *mem_ptr; - volatile SharedData *shared_data; - volatile TriggerData *trigger_data; + volatile SharedData *shared_data; + volatile TriggerData *trigger_data; - int last_state; - int last_event; + int last_state; + int last_event; - public: - MonitorLink( int p_id, const char *p_name ); - ~MonitorLink(); + public: + MonitorLink( int p_id, const char *p_name ); + ~MonitorLink(); - inline int Id() const - { - return( id ); - } - inline const char *Name() const - { - return( name ); - } + inline int Id() const + { + return( id ); + } + inline const char *Name() const + { + return( name ); + } - inline bool isConnected() const - { - return( connected ); - } - inline time_t getLastConnectTime() const - { - return( last_connect_time ); - } + inline bool isConnected() const + { + return( connected ); + } + inline time_t getLastConnectTime() const + { + return( last_connect_time ); + } - bool connect(); - bool disconnect(); + bool connect(); + bool disconnect(); - bool isAlarmed(); - bool inAlarm(); - bool hasAlarmed(); - }; + bool isAlarmed(); + bool inAlarm(); + bool hasAlarmed(); + }; protected: - // These are read from the DB and thereafter remain unchanged - unsigned int id; - char name[64]; - Function function; // What the monitor is doing - bool enabled; // Whether the monitor is enabled or asleep - unsigned int width; // Normally the same as the camera, but not if partly rotated - unsigned int height; // Normally the same as the camera, but not if partly rotated - bool v4l_multi_buffer; - unsigned int v4l_captures_per_frame; - Orientation orientation; // Whether the image has to be rotated at all - unsigned int deinterlacing; - int brightness; // The statically saved brightness of the camera - int contrast; // The statically saved contrast of the camera - int hue; // The statically saved hue of the camera - int colour; // The statically saved colour of the camera - char event_prefix[64]; // The prefix applied to event names as they are created - char label_format[64]; // The format of the timestamp on the images - Coord label_coord; // The coordinates of the timestamp on the images - int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count - int warmup_count; // How many images to process before looking for events - int pre_event_count; // How many images to hold and prepend to an alarm event - int post_event_count; // How many unalarmed images must occur before the alarm state is reset - int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now - int section_length; // How long events should last in continuous modes - int frame_skip; // How many frames to skip in continuous modes - int motion_frame_skip; // How many frames to skip in motion detection - int capture_delay; // How long we wait between capture frames - int alarm_capture_delay; // How long we wait between capture frames when in alarm state - int alarm_frame_count; // How many alarm frames are required before an event is triggered - int fps_report_interval; // How many images should be captured/processed between reporting the current FPS - int ref_blend_perc; // Percentage of new image going into reference image. - int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. - bool track_motion; // Whether this monitor tries to track detected motion - Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected + // These are read from the DB and thereafter remain unchanged + unsigned int id; + char name[64]; + unsigned int server_id; + Function function; // What the monitor is doing + bool enabled; // Whether the monitor is enabled or asleep + unsigned int width; // Normally the same as the camera, but not if partly rotated + unsigned int height; // Normally the same as the camera, but not if partly rotated + bool v4l_multi_buffer; + unsigned int v4l_captures_per_frame; + Orientation orientation; // Whether the image has to be rotated at all + unsigned int deinterlacing; + int brightness; // The statically saved brightness of the camera + int contrast; // The statically saved contrast of the camera + int hue; // The statically saved hue of the camera + int colour; // The statically saved colour of the camera + char event_prefix[64]; // The prefix applied to event names as they are created + char label_format[64]; // The format of the timestamp on the images + Coord label_coord; // The coordinates of the timestamp on the images + int label_size; // Size of the timestamp on the images + int image_buffer_count; // Size of circular image buffer, at least twice the size of the pre_event_count + int pre_event_buffer_count; // Size of dedicated circular pre event buffer used when analysis is not performed at capturing framerate, + // value is pre_event_count + alarm_frame_count - 1 + int warmup_count; // How many images to process before looking for events + int pre_event_count; // How many images to hold and prepend to an alarm event + int post_event_count; // How many unalarmed images must occur before the alarm state is reset + int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now + int section_length; // How long events should last in continuous modes + bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor + int frame_skip; // How many frames to skip in continuous modes + int motion_frame_skip; // How many frames to skip in motion detection + double analysis_fps; // Target framerate for video analysis + unsigned int analysis_update_delay; // How long we wait before updating analysis parameters + int capture_delay; // How long we wait between capture frames + int alarm_capture_delay; // How long we wait between capture frames when in alarm state + int alarm_frame_count; // How many alarm frames are required before an event is triggered + int fps_report_interval; // How many images should be captured/processed between reporting the current FPS + int ref_blend_perc; // Percentage of new image going into reference image. + int alarm_ref_blend_perc; // Percentage of new image going into reference image during alarm. + bool track_motion; // Whether this monitor tries to track detected motion + Rgb signal_check_colour; // The colour that the camera will emit when no video signal detected + bool embed_exif; // Whether to embed Exif data into each image frame or not - double fps; - Image delta_image; - Image ref_image; + double fps; + Image delta_image; + Image ref_image; + Image alarm_image; // Used in creating analysis images, will be initialized in Analysis + Image write_image; // Used when creating snapshot images - Purpose purpose; // What this monitor has been created to do - int event_count; - int image_count; - int ready_count; - int first_alarm_count; - int last_alarm_count; - int buffer_count; - int prealarm_count; - State state; - time_t start_time; - time_t last_fps_time; - time_t auto_resume_time; - unsigned int last_motion_score; + Purpose purpose; // What this monitor has been created to do + int event_count; + int image_count; + int ready_count; + int first_alarm_count; + int last_alarm_count; + int buffer_count; + int prealarm_count; + State state; + time_t start_time; + time_t last_fps_time; + time_t auto_resume_time; + unsigned int last_motion_score; - EventCloseMode event_close_mode; + EventCloseMode event_close_mode; #if ZM_MEM_MAPPED - int map_fd; - char mem_file[PATH_MAX]; + int map_fd; + char mem_file[PATH_MAX]; #else // ZM_MEM_MAPPED - int shm_id; + int shm_id; #endif // ZM_MEM_MAPPED - unsigned long mem_size; - unsigned char *mem_ptr; + off_t mem_size; + unsigned char *mem_ptr; - SharedData *shared_data; - TriggerData *trigger_data; + SharedData *shared_data; + TriggerData *trigger_data; - Snapshot *image_buffer; - Snapshot next_buffer; /* Used by four field deinterlacing */ + Snapshot *image_buffer; + Snapshot next_buffer; /* Used by four field deinterlacing */ + Snapshot *pre_event_buffer; - Camera *camera; + Camera *camera; - Event *event; + Event *event; - int n_zones; - Zone **zones; + int n_zones; + Zone **zones; - int iDoNativeMotDet; + struct timeval **timestamps; + Image **images; - int n_linked_monitors; - MonitorLink **linked_monitors; + const unsigned char *privacy_bitmask; + + int n_linked_monitors; + MonitorLink **linked_monitors; public: // OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info. //bool OurCheckAlarms( Zone *zone, const Image *pImage ); - Monitor( int p_id, const char *p_name, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 ); - ~Monitor(); + Monitor( int p_id, const char *p_name, unsigned int p_server_id, int p_function, bool p_enabled, const char *p_linked_monitors, Camera *p_camera, int p_orientation, unsigned int p_deinterlacing, const char *p_event_prefix, const char *p_label_format, const Coord &p_label_coord, int label_size, int p_image_buffer_count, int p_warmup_count, int p_pre_event_count, int p_post_event_count, int p_stream_replay_buffer, int p_alarm_frame_count, int p_section_length, int p_frame_skip, int p_motion_frame_skip, double p_analysis_fps, unsigned int p_analysis_update_delay, int p_capture_delay, int p_alarm_capture_delay, int p_fps_report_interval, int p_ref_blend_perc, int p_alarm_ref_blend_perc, bool p_track_motion, Rgb p_signal_check_colour, bool p_embed_exif, Purpose p_purpose, int p_n_zones=0, Zone *p_zones[]=0 ); + ~Monitor(); - void AddZones( int p_n_zones, Zone *p_zones[] ); + void AddZones( int p_n_zones, Zone *p_zones[] ); + void AddPrivacyBitmask( Zone *p_zones[] ); - bool connect(); - inline int ShmValid() const - { - return( shared_data->valid ); - } + bool connect(); + inline int ShmValid() const + { + return( shared_data->valid ); + } - inline int Id() const - { - return( id ); - } - inline const char *Name() const - { - return( name ); - } - inline Function GetFunction() const - { - return( function ); - } - inline bool Enabled() - { - if ( function <= MONITOR ) - return( false ); - return( enabled ); - } - inline const char *EventPrefix() const - { - return( event_prefix ); - } - inline bool Ready() - { - if ( function <= MONITOR ) - return( false ); - return( image_count > ready_count ); - } - inline bool Active() - { - if ( function <= MONITOR ) - return( false ); - return( enabled && shared_data->active ); - } + inline int Id() const + { + return( id ); + } + inline const char *Name() const + { + return( name ); + } + inline Function GetFunction() const + { + return( function ); + } + inline bool Enabled() + { + if ( function <= MONITOR ) + return( false ); + return( enabled ); + } + inline const char *EventPrefix() const + { + return( event_prefix ); + } + inline bool Ready() + { + if ( function <= MONITOR ) + return( false ); + return( image_count > ready_count ); + } + inline bool Active() + { + if ( function <= MONITOR ) + return( false ); + return( enabled && shared_data->active ); + } + inline bool Exif() + { + return( embed_exif ); + } - unsigned int Width() const { return( width ); } - unsigned int Height() const { return( height ); } - unsigned int Colours() const { return( camera->Colours() ); } - unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); } - + unsigned int Width() const { return( width ); } + unsigned int Height() const { return( height ); } + unsigned int Colours() const { return( camera->Colours() ); } + unsigned int SubpixelOrder() const { return( camera->SubpixelOrder() ); } + - State GetState() const; - int GetImage( int index=-1, int scale=100 ) const; - struct timeval GetTimestamp( int index=-1 ) const; - int GetCaptureDelay() const { return( capture_delay ); } - int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); } - unsigned int GetLastReadIndex() const; - unsigned int GetLastWriteIndex() const; - unsigned int GetLastEvent() const; - double GetFPS() const; - void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" ); - void ForceAlarmOff(); - void CancelForced(); - TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); } + State GetState() const; + int GetImage( int index=-1, int scale=100 ); + struct timeval GetTimestamp( int index=-1 ) const; + void UpdateAdaptiveSkip(); + useconds_t GetAnalysisRate(); + unsigned int GetAnalysisUpdateDelay() const { return( analysis_update_delay ); } + int GetCaptureDelay() const { return( capture_delay ); } + int GetAlarmCaptureDelay() const { return( alarm_capture_delay ); } + unsigned int GetLastReadIndex() const; + unsigned int GetLastWriteIndex() const; + unsigned int GetLastEvent() const; + double GetFPS() const; + void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" ); + void ForceAlarmOff(); + void CancelForced(); + TriggerState GetTriggerState() const { return( (TriggerState)(trigger_data?trigger_data->trigger_state:TRIGGER_CANCEL )); } - void actionReload(); - void actionEnable(); - void actionDisable(); - void actionSuspend(); - void actionResume(); + void actionReload(); + void actionEnable(); + void actionDisable(); + void actionSuspend(); + void actionResume(); - int actionBrightness( int p_brightness=-1 ); - int actionHue( int p_hue=-1 ); - int actionColour( int p_colour=-1 ); - int actionContrast( int p_contrast=-1 ); + int actionBrightness( int p_brightness=-1 ); + int actionHue( int p_hue=-1 ); + int actionColour( int p_colour=-1 ); + int actionContrast( int p_contrast=-1 ); - inline int PrimeCapture() - { - if ( function == EXTDECT ) - return( 0 ); - return( camera->PrimeCapture() ); - } - inline int PreCapture() - { - if ( function == EXTDECT ) - return( 0 ); - return( camera->PreCapture() ); - } - int Capture(); - int PostCapture() - { - if ( function == EXTDECT ) - return( 0 ); - return( camera->PostCapture() ); - } + inline int PrimeCapture() + { + return( camera->PrimeCapture() ); + } + inline int PreCapture() + { + return( camera->PreCapture() ); + } + int Capture(); + int PostCapture() + { + return( camera->PostCapture() ); + } - unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ); + unsigned int DetectMotion( const Image &comp_image, Event::StringSet &zoneSet ); // DetectBlack seems to be unused. Check it on zm_monitor.cpp for more info. //unsigned int DetectBlack( const Image &comp_image, Event::StringSet &zoneSet ); - bool CheckSignal( const Image *image ); - bool Analyse(); - void DumpImage( Image *dump_image ) const; - void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const; - bool closeEvent(); + bool CheckSignal( const Image *image ); + bool Analyse(); + void DumpImage( Image *dump_image ) const; + void TimestampImage( Image *ts_image, const struct timeval *ts_time ) const; + bool closeEvent(); - void Reload(); - void ReloadZones(); - void ReloadLinkedMonitors( const char * ); + void Reload(); + void ReloadZones(); + void ReloadLinkedMonitors( const char * ); - bool DumpSettings( char *output, bool verbose ); - void DumpZoneImage( const char *zone_string=0 ); + bool DumpSettings( char *output, bool verbose ); + void DumpZoneImage( const char *zone_string=0 ); #if ZM_HAS_V4L - static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ); + static int LoadLocalMonitors( const char *device, Monitor **&monitors, Purpose purpose ); #endif // ZM_HAS_V4L - static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose ); - static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ); + static int LoadRemoteMonitors( const char *protocol, const char *host, const char*port, const char*path, Monitor **&monitors, Purpose purpose ); + static int LoadFileMonitors( const char *file, Monitor **&monitors, Purpose purpose ); #if HAVE_LIBAVFORMAT - static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ); + static int LoadFfmpegMonitors( const char *file, Monitor **&monitors, Purpose purpose ); #endif // HAVE_LIBAVFORMAT - static Monitor *Load( int id, bool load_zones, Purpose purpose ); - //void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y ); - //void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 ); - //void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 ); - //void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 ); - void SingleImage( int scale=100 ); - void SingleImageRaw( int scale=100 ); - void SingleImageZip( int scale=100 ); + static Monitor *Load( unsigned int id, bool load_zones, Purpose purpose ); + //void writeStreamImage( Image *image, struct timeval *timestamp, int scale, int mag, int x, int y ); + //void StreamImages( int scale=100, int maxfps=10, time_t ttl=0, int msq_id=0 ); + //void StreamImagesRaw( int scale=100, int maxfps=10, time_t ttl=0 ); + //void StreamImagesZip( int scale=100, int maxfps=10, time_t ttl=0 ); + void SingleImage( int scale=100 ); + void SingleImageRaw( int scale=100 ); + void SingleImageZip( int scale=100 ); #if HAVE_LIBAVCODEC - //void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 ); + //void StreamMpeg( const char *format, int scale=100, int maxfps=10, int bitrate=100000 ); #endif // HAVE_LIBAVCODEC }; @@ -433,51 +448,51 @@ public: class MonitorStream : public StreamBase { protected: - typedef struct SwapImage { - bool valid; - struct timeval timestamp; - char file_name[PATH_MAX]; - } SwapImage; + typedef struct SwapImage { + bool valid; + struct timeval timestamp; + char file_name[PATH_MAX]; + } SwapImage; private: - SwapImage *temp_image_buffer; - int temp_image_buffer_count; - int temp_read_index; - int temp_write_index; + SwapImage *temp_image_buffer; + int temp_image_buffer_count; + int temp_read_index; + int temp_write_index; protected: - time_t ttl; + time_t ttl; protected: - int playback_buffer; - bool delayed; + int playback_buffer; + bool delayed; - int frame_count; + int frame_count; protected: - bool checkSwapPath( const char *path, bool create_path ); + bool checkSwapPath( const char *path, bool create_path ); - bool sendFrame( const char *filepath, struct timeval *timestamp ); - bool sendFrame( Image *image, struct timeval *timestamp ); - void processCommand( const CmdMsg *msg ); + bool sendFrame( const char *filepath, struct timeval *timestamp ); + bool sendFrame( Image *image, struct timeval *timestamp ); + void processCommand( const CmdMsg *msg ); public: - MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 ) - { - } - void setStreamBuffer( int p_playback_buffer ) - { - playback_buffer = p_playback_buffer; - } - void setStreamTTL( time_t p_ttl ) - { - ttl = p_ttl; - } - void setStreamStart( int monitor_id ) - { - loadMonitor( monitor_id ); - } - void runStream(); + MonitorStream() : playback_buffer( 0 ), delayed( false ), frame_count( 0 ) + { + } + void setStreamBuffer( int p_playback_buffer ) + { + playback_buffer = p_playback_buffer; + } + void setStreamTTL( time_t p_ttl ) + { + ttl = p_ttl; + } + bool setStreamStart( int monitor_id ) + { + return loadMonitor( monitor_id ); + } + void runStream(); }; #endif // ZM_MONITOR_H diff --git a/src/zm_mpeg.cpp b/src/zm_mpeg.cpp index 60bb22c1d..bf2ac9952 100644 --- a/src/zm_mpeg.cpp +++ b/src/zm_mpeg.cpp @@ -34,242 +34,242 @@ extern "C" bool VideoStream::initialised = false; VideoStream::MimeData VideoStream::mime_data[] = { - { "asf", "video/x-ms-asf" }, - { "swf", "application/x-shockwave-flash" }, - { "flv", "video/x-flv" }, - { "mov", "video/quicktime" } + { "asf", "video/x-ms-asf" }, + { "swf", "application/x-shockwave-flash" }, + { "flv", "video/x-flv" }, + { "mov", "video/quicktime" } }; void VideoStream::Initialise( ) { - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else - av_log_set_level( AV_LOG_QUIET ); - - av_register_all( ); -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 13, 0) - avformat_network_init(); + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); + + av_register_all( ); +#if LIBAVFORMAT_VERSION_CHECK(53, 13, 0, 19, 0) + avformat_network_init(); #endif - initialised = true; + initialised = true; } void VideoStream::SetupFormat( ) { - /* allocate the output media context */ - ofc = NULL; -#if ((LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 5, 0)) && (LIBAVFORMAT_VERSION_MICRO >= 100)) - avformat_alloc_output_context2( &ofc, NULL, format, filename ); + /* allocate the output media context */ + ofc = NULL; +#if (LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 2, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) + avformat_alloc_output_context2( &ofc, NULL, format, filename ); #else - AVFormatContext *s= avformat_alloc_context(); - if(!s) - { - Fatal( "avformat_alloc_context failed %d \"%s\"", (size_t)ofc, av_err2str((size_t)ofc) ); - } - - AVOutputFormat *oformat; - if (format) { -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52, 45, 0) - oformat = av_guess_format(format, NULL, NULL); -#else - oformat = guess_format(format, NULL, NULL); -#endif - if (!oformat) { - Fatal( "Requested output format '%s' is not a suitable output format", format ); - } - } else { -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52, 45, 0) - oformat = av_guess_format(NULL, filename, NULL); -#else - oformat = guess_format(NULL, filename, NULL); -#endif - if (!oformat) { - Fatal( "Unable to find a suitable output format for '%s'", format ); - } - } - s->oformat = oformat; - - if (s->oformat->priv_data_size > 0) { - s->priv_data = av_mallocz(s->oformat->priv_data_size); - if (!s->priv_data) - { - Fatal( "Could not allocate private data for output format." ); - } -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52, 92, 0) - if (s->oformat->priv_class) { - *(const AVClass**)s->priv_data = s->oformat->priv_class; - av_opt_set_defaults(s->priv_data); - } -#endif - } - else - { - s->priv_data = NULL; - } - - if(filename) - { - snprintf( s->filename, sizeof(s->filename), filename ); - } - - ofc = s; -#endif - if ( !ofc ) - { - Fatal( "avformat_alloc_..._context failed: %d", ofc ); - } + AVFormatContext *s= avformat_alloc_context(); + if(!s) + { + Fatal( "avformat_alloc_context failed %d \"%s\"", (size_t)ofc, av_err2str((size_t)ofc) ); + } - of = ofc->oformat; - Debug( 1, "Using output format: %s (%s)", of->name, of->long_name ); + AVOutputFormat *oformat; + if (format) { +#if LIBAVFORMAT_VERSION_CHECK(52, 45, 0, 45, 0) + oformat = av_guess_format(format, NULL, NULL); +#else + oformat = guess_format(format, NULL, NULL); +#endif + if (!oformat) { + Fatal( "Requested output format '%s' is not a suitable output format", format ); + } + } else { +#if LIBAVFORMAT_VERSION_CHECK(52, 45, 0, 45, 0) + oformat = av_guess_format(NULL, filename, NULL); +#else + oformat = guess_format(NULL, filename, NULL); +#endif + if (!oformat) { + Fatal( "Unable to find a suitable output format for '%s'", format ); + } + } + s->oformat = oformat; + + if (s->oformat->priv_data_size > 0) { + s->priv_data = av_mallocz(s->oformat->priv_data_size); + if (!s->priv_data) + { + Fatal( "Could not allocate private data for output format." ); + } +#if LIBAVFORMAT_VERSION_CHECK(52, 92, 0, 92, 0) + if (s->oformat->priv_class) { + *(const AVClass**)s->priv_data = s->oformat->priv_class; + av_opt_set_defaults(s->priv_data); + } +#endif + } + else + { + s->priv_data = NULL; + } + + if(filename) + { + snprintf( s->filename, sizeof(s->filename), "%s", filename ); + } + + ofc = s; +#endif + if ( !ofc ) + { + Fatal( "avformat_alloc_..._context failed: %d", ofc ); + } + + of = ofc->oformat; + Debug( 1, "Using output format: %s (%s)", of->name, of->long_name ); } void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ) { - /* ffmpeg format matching */ - switch(colours) { - case ZM_COLOUR_RGB24: - { - if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { - /* BGR subpixel order */ - pf = PIX_FMT_BGR24; - } else { - /* Assume RGB subpixel order */ - pf = PIX_FMT_RGB24; - } - break; - } - case ZM_COLOUR_RGB32: - { - if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { - /* ARGB subpixel order */ - pf = PIX_FMT_ARGB; - } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { - /* ABGR subpixel order */ - pf = PIX_FMT_ABGR; - } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { - /* BGRA subpixel order */ - pf = PIX_FMT_BGRA; - } else { - /* Assume RGBA subpixel order */ - pf = PIX_FMT_RGBA; - } - break; - } - case ZM_COLOUR_GRAY8: - pf = PIX_FMT_GRAY8; - break; - default: - Panic("Unexpected colours: %d",colours); - break; - } + /* ffmpeg format matching */ + switch(colours) { + case ZM_COLOUR_RGB24: + { + if(subpixelorder == ZM_SUBPIX_ORDER_BGR) { + /* BGR subpixel order */ + pf = AV_PIX_FMT_BGR24; + } else { + /* Assume RGB subpixel order */ + pf = AV_PIX_FMT_RGB24; + } + break; + } + case ZM_COLOUR_RGB32: + { + if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) { + /* ARGB subpixel order */ + pf = AV_PIX_FMT_ARGB; + } else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) { + /* ABGR subpixel order */ + pf = AV_PIX_FMT_ABGR; + } else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + /* BGRA subpixel order */ + pf = AV_PIX_FMT_BGRA; + } else { + /* Assume RGBA subpixel order */ + pf = AV_PIX_FMT_RGBA; + } + break; + } + case ZM_COLOUR_GRAY8: + pf = AV_PIX_FMT_GRAY8; + break; + default: + Panic("Unexpected colours: %d",colours); + break; + } - if ( strcmp( "rtp", of->name ) == 0 ) - { - // RTP must have a packet_size. - // Not sure what this value should be really... - ofc->packet_size = width*height; - - if ( of->video_codec == AV_CODEC_ID_NONE) - { - // RTP does not have a default codec in ffmpeg <= 0.8. - of->video_codec = AV_CODEC_ID_MPEG4; - } - } - - _AVCODECID codec_id = of->video_codec; - if ( codec_name ) - { - AVCodec *a = avcodec_find_encoder_by_name(codec_name); - if ( a ) - { - codec_id = a->id; - } - else - { -#if ((LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 11, 0)) && (LIBAVFORMAT_VERSION_MICRO >= 100)) - Debug( 1, "Could not find codec \"%s\". Using default \"%s\"", codec_name, avcodec_get_name( codec_id ) ); + if ( strcmp( "rtp", of->name ) == 0 ) + { + // RTP must have a packet_size. + // Not sure what this value should be really... + ofc->packet_size = width*height; + + if ( of->video_codec == AV_CODEC_ID_NONE) + { + // RTP does not have a default codec in ffmpeg <= 0.8. + of->video_codec = AV_CODEC_ID_MPEG4; + } + } + + _AVCODECID codec_id = of->video_codec; + if ( codec_name ) + { + AVCodec *a = avcodec_find_encoder_by_name(codec_name); + if ( a ) + { + codec_id = a->id; + } + else + { +#if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) + Debug( 1, "Could not find codec \"%s\". Using default \"%s\"", codec_name, avcodec_get_name( codec_id ) ); #else - Debug( 1, "Could not find codec \"%s\". Using default \"%d\"", codec_name, codec_id ); + Debug( 1, "Could not find codec \"%s\". Using default \"%d\"", codec_name, codec_id ); #endif - } - } + } + } - /* add the video streams using the default format codecs - and initialize the codecs */ - ost = NULL; - if ( codec_id != AV_CODEC_ID_NONE ) - { - codec = avcodec_find_encoder( codec_id ); - if ( !codec ) - { -#if ((LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 11, 0)) && (LIBAVFORMAT_VERSION_MICRO >= 100)) - Fatal( "Could not find encoder for '%s'", avcodec_get_name( codec_id ) ); + /* add the video streams using the default format codecs + and initialize the codecs */ + ost = NULL; + if ( codec_id != AV_CODEC_ID_NONE ) + { + codec = avcodec_find_encoder( codec_id ); + if ( !codec ) + { +#if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) + Fatal( "Could not find encoder for '%s'", avcodec_get_name( codec_id ) ); #else - Fatal( "Could not find encoder for '%d'", codec_id ); + Fatal( "Could not find encoder for '%d'", codec_id ); #endif - } + } -#if ((LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 11, 0)) && (LIBAVFORMAT_VERSION_MICRO >= 100)) - Debug( 1, "Found encoder for '%s'", avcodec_get_name( codec_id ) ); +#if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100)) + Debug( 1, "Found encoder for '%s'", avcodec_get_name( codec_id ) ); #else - Debug( 1, "Found encoder for '%d'", codec_id ); + Debug( 1, "Found encoder for '%d'", codec_id ); #endif -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 10, 0) - ost = avformat_new_stream( ofc, codec ); +#if LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0) + ost = avformat_new_stream( ofc, codec ); #else - ost = av_new_stream( ofc, 0 ); + ost = av_new_stream( ofc, 0 ); #endif - - if ( !ost ) - { - Fatal( "Could not alloc stream" ); - } - ost->id = ofc->nb_streams - 1; + + if ( !ost ) + { + Fatal( "Could not alloc stream" ); + } + ost->id = ofc->nb_streams - 1; - Debug( 1, "Allocated stream" ); + Debug( 1, "Allocated stream" ); - AVCodecContext *c = ost->codec; + AVCodecContext *c = ost->codec; - c->codec_id = codec->id; - c->codec_type = codec->type; + c->codec_id = codec->id; + c->codec_type = codec->type; - c->pix_fmt = strcmp( "mjpeg", ofc->oformat->name ) == 0 ? PIX_FMT_YUVJ422P : PIX_FMT_YUV420P; - if ( bitrate <= 100 ) - { - // Quality based bitrate control (VBR). Scale is 1..31 where 1 is best. - // This gets rid of artifacts in the beginning of the movie; and well, even quality. - c->flags |= CODEC_FLAG_QSCALE; - c->global_quality = FF_QP2LAMBDA * (31 - (31 * (bitrate / 100.0))); - } - else - { - c->bit_rate = bitrate; - } + c->pix_fmt = strcmp( "mjpeg", ofc->oformat->name ) == 0 ? AV_PIX_FMT_YUVJ422P : AV_PIX_FMT_YUV420P; + if ( bitrate <= 100 ) + { + // Quality based bitrate control (VBR). Scale is 1..31 where 1 is best. + // This gets rid of artifacts in the beginning of the movie; and well, even quality. + c->flags |= CODEC_FLAG_QSCALE; + c->global_quality = FF_QP2LAMBDA * (31 - (31 * (bitrate / 100.0))); + } + else + { + c->bit_rate = bitrate; + } - /* resolution must be a multiple of two */ - c->width = width; - c->height = height; - /* time base: this is the fundamental unit of time (in seconds) in terms - of which frame timestamps are represented. for fixed-fps content, - timebase should be 1/framerate and timestamp increments should be - identically 1. */ - c->time_base.den = frame_rate; - c->time_base.num = 1; - - Debug( 1, "Will encode in %d fps.", c->time_base.den ); - - /* emit one intra frame every second */ - c->gop_size = frame_rate; + /* resolution must be a multiple of two */ + c->width = width; + c->height = height; + /* time base: this is the fundamental unit of time (in seconds) in terms + of which frame timestamps are represented. for fixed-fps content, + timebase should be 1/framerate and timestamp increments should be + identically 1. */ + c->time_base.den = frame_rate; + c->time_base.num = 1; + + Debug( 1, "Will encode in %d fps.", c->time_base.den ); + + /* emit one intra frame every second */ + c->gop_size = frame_rate; - // some formats want stream headers to be seperate - if ( of->flags & AVFMT_GLOBALHEADER ) - c->flags |= CODEC_FLAG_GLOBAL_HEADER; - } - else - { - Fatal( "of->video_codec == AV_CODEC_ID_NONE" ); - } + // some formats want stream headers to be separate + if ( of->flags & AVFMT_GLOBALHEADER ) + c->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + else + { + Fatal( "of->video_codec == AV_CODEC_ID_NONE" ); + } } void VideoStream::SetParameters( ) @@ -278,476 +278,536 @@ void VideoStream::SetParameters( ) const char *VideoStream::MimeType( ) const { - for ( unsigned int i = 0; i < sizeof (mime_data) / sizeof (*mime_data); i++ ) - { - if ( strcmp( format, mime_data[i].format ) == 0 ) - { - Debug( 1, "MimeType is \"%s\"", mime_data[i].mime_type ); - return ( mime_data[i].mime_type); - } - } - const char *mime_type = of->mime_type; - if ( !mime_type ) - { - std::string mime = "video/"; - mime = mime.append( format ); - mime_type = mime.c_str( ); - Warning( "Unable to determine mime type for '%s' format, using '%s' as default", format, mime_type ); - } + for ( unsigned int i = 0; i < sizeof (mime_data) / sizeof (*mime_data); i++ ) + { + if ( strcmp( format, mime_data[i].format ) == 0 ) + { + Debug( 1, "MimeType is \"%s\"", mime_data[i].mime_type ); + return ( mime_data[i].mime_type); + } + } + const char *mime_type = of->mime_type; + if ( !mime_type ) + { + std::string mime = "video/"; + mime = mime.append( format ); + mime_type = mime.c_str( ); + Warning( "Unable to determine mime type for '%s' format, using '%s' as default", format, mime_type ); + } - Debug( 1, "MimeType is \"%s\"", mime_type ); + Debug( 1, "MimeType is \"%s\"", mime_type ); - return ( mime_type); + return ( mime_type); } void VideoStream::OpenStream( ) { - int avRet; + int avRet; - /* now that all the parameters are set, we can open the - video codecs and allocate the necessary encode buffers */ - if ( ost ) - { - AVCodecContext *c = ost->codec; - - /* open the codec */ -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 7, 0) - if ( (avRet = avcodec_open( c, codec )) < 0 ) -#else - if ( (avRet = avcodec_open2( c, codec, 0 )) < 0 ) -#endif - { - Fatal( "Could not open codec. Error code %d \"%s\"", avRet, av_err2str( avRet ) ); - } - - Debug( 1, "Opened codec" ); - - /* allocate the encoded raw picture */ - opicture = avcodec_alloc_frame( ); - if ( !opicture ) - { - Panic( "Could not allocate opicture" ); - } - - int size = avpicture_get_size( c->pix_fmt, c->width, c->height ); - uint8_t *opicture_buf = (uint8_t *)av_malloc( size ); - if ( !opicture_buf ) - { - av_free( opicture ); - Panic( "Could not allocate opicture_buf" ); - } - avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt, c->width, c->height ); - - /* if the output format is not identical to the input format, then a temporary - picture is needed too. It is then converted to the required - output format */ - tmp_opicture = NULL; - if ( c->pix_fmt != pf ) - { - tmp_opicture = avcodec_alloc_frame( ); - if ( !tmp_opicture ) - { - Panic( "Could not allocate tmp_opicture" ); - } - int size = avpicture_get_size( pf, c->width, c->height ); - uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size ); - if ( !tmp_opicture_buf ) - { - av_free( tmp_opicture ); - Panic( "Could not allocate tmp_opicture_buf" ); - } - avpicture_fill( (AVPicture *)tmp_opicture, tmp_opicture_buf, pf, c->width, c->height ); - } - } - - /* open the output file, if needed */ - if ( !(of->flags & AVFMT_NOFILE) ) - { - int ret; -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 14, 0) - ret = avio_open2( &ofc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL ); -#elif LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52, 102, 0) - ret = avio_open( &ofc->pb, filename, AVIO_FLAG_WRITE ); -#else - ret = url_fopen( &ofc->pb, filename, AVIO_FLAG_WRITE ); -#endif - if ( ret < 0 ) - { - Fatal( "Could not open '%s'", filename ); - } - - Debug( 1, "Opened output \"%s\"", filename ); - } - else - { - Fatal( "of->flags & AVFMT_NOFILE" ); - } - - video_outbuf = NULL; - if ( !(of->flags & AVFMT_RAWPICTURE) ) - { - /* allocate output buffer */ - /* XXX: API change will be done */ - // TODO: Make buffer dynamic. - video_outbuf_size = 4000000; - video_outbuf = (uint8_t *)malloc( video_outbuf_size ); - } - -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52, 100, 1) - av_dump_format(ofc, 0, filename, 1); -#else - dump_format(ofc, 0, filename, 1); -#endif + /* now that all the parameters are set, we can open the + video codecs and allocate the necessary encode buffers */ + if ( ost ) + { + AVCodecContext *c = ost->codec; -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 4, 0) - int ret = av_write_header( ofc ); + /* open the codec */ +#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) + if ( (avRet = avcodec_open( c, codec )) < 0 ) #else - int ret = avformat_write_header( ofc, NULL ); + if ( (avRet = avcodec_open2( c, codec, 0 )) < 0 ) +#endif + { + Fatal( "Could not open codec. Error code %d \"%s\"", avRet, av_err2str( avRet ) ); + } + + Debug( 1, "Opened codec" ); + + /* allocate the encoded raw picture */ +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + opicture = av_frame_alloc( ); +#else + opicture = avcodec_alloc_frame( ); +#endif + if ( !opicture ) + { + Panic( "Could not allocate opicture" ); + } + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int size = av_image_get_buffer_size( c->pix_fmt, c->width, + c->height, 1 ); +#else + int size = avpicture_get_size( c->pix_fmt, c->width, c->height ); +#endif + + uint8_t *opicture_buf = (uint8_t *)av_malloc( size ); + if ( !opicture_buf ) + { +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &opicture ); +#else + av_freep( &opicture ); +#endif + Panic( "Could not allocate opicture_buf" ); + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(opicture->data, opicture->linesize, + opicture_buf, c->pix_fmt, c->width, c->height, 1); +#else + avpicture_fill( (AVPicture *)opicture, opicture_buf, c->pix_fmt, + c->width, c->height ); +#endif + + /* if the output format is not identical to the input format, then a temporary + picture is needed too. It is then converted to the required + output format */ + tmp_opicture = NULL; + if ( c->pix_fmt != pf ) + { +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + tmp_opicture = av_frame_alloc( ); +#else + tmp_opicture = avcodec_alloc_frame( ); +#endif + if ( !tmp_opicture ) + { + Panic( "Could not allocate tmp_opicture" ); + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int size = av_image_get_buffer_size( pf, c->width, + c->height,1 ); +#else + int size = avpicture_get_size( pf, c->width, c->height ); +#endif + uint8_t *tmp_opicture_buf = (uint8_t *)av_malloc( size ); + if ( !tmp_opicture_buf ) + { +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &tmp_opicture ); +#else + av_freep( &tmp_opicture ); +#endif + Panic( "Could not allocate tmp_opicture_buf" ); + } +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(tmp_opicture->data, + tmp_opicture->linesize, tmp_opicture_buf, pf, + c->width, c->height, 1); +#else + avpicture_fill( (AVPicture *)tmp_opicture, + tmp_opicture_buf, pf, c->width, c->height ); +#endif + } + } + + /* open the output file, if needed */ + if ( !(of->flags & AVFMT_NOFILE) ) + { + int ret; +#if LIBAVFORMAT_VERSION_CHECK(53, 15, 0, 21, 0) + ret = avio_open2( &ofc->pb, filename, AVIO_FLAG_WRITE, NULL, NULL ); +#elif LIBAVFORMAT_VERSION_CHECK(52, 102, 0, 102, 0) + ret = avio_open( &ofc->pb, filename, AVIO_FLAG_WRITE ); +#else + ret = url_fopen( &ofc->pb, filename, AVIO_FLAG_WRITE ); #endif - if ( ret < 0 ) { - Fatal( "?_write_header failed with error %d \"%s\"", ret, av_err2str( ret ) ); + Fatal( "Could not open '%s'", filename ); } + + Debug( 1, "Opened output \"%s\"", filename ); + } + else + { + Fatal( "of->flags & AVFMT_NOFILE" ); + } + + video_outbuf = NULL; + if ( !(of->flags & AVFMT_RAWPICTURE) ) + { + /* allocate output buffer */ + /* XXX: API change will be done */ + // TODO: Make buffer dynamic. + video_outbuf_size = 4000000; + video_outbuf = (uint8_t *)malloc( video_outbuf_size ); + if ( video_outbuf == NULL ) { + Fatal("Unable to malloc memory for outbuf"); + } + } + +#if LIBAVFORMAT_VERSION_CHECK(52, 101, 0, 101, 0) + av_dump_format(ofc, 0, filename, 1); +#else + dump_format(ofc, 0, filename, 1); +#endif + +#if !LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 4, 0) + int ret = av_write_header( ofc ); +#else + int ret = avformat_write_header( ofc, NULL ); +#endif + + if ( ret < 0 ) + { + Fatal( "?_write_header failed with error %d \"%s\"", ret, av_err2str( ret ) ); + } } VideoStream::VideoStream( const char *in_filename, const char *in_format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ) : - filename(in_filename), - format(in_format), - last_pts( -1 ), - streaming_thread(0), - do_streaming(true), - buffer_copy(NULL), - buffer_copy_lock(new pthread_mutex_t), - buffer_copy_used(0), - packet_index(0) + filename(in_filename), + format(in_format), + last_pts( -1 ), + streaming_thread(0), + do_streaming(true), + buffer_copy(NULL), + buffer_copy_lock(new pthread_mutex_t), + buffer_copy_size(0), + buffer_copy_used(0), + packet_index(0) { - if ( !initialised ) - { - Initialise( ); - } - - if ( format ) - { - int length = strlen(format); - codec_and_format = new char[length+1];; - strcpy( codec_and_format, format ); - format = codec_and_format; - codec_name = NULL; - char *f = strchr(codec_and_format, '/'); - if (f != NULL) - { - *f = 0; - codec_name = f+1; - } - } + if ( !initialised ) + { + Initialise( ); + } + + if ( format ) + { + int length = strlen(format); + codec_and_format = new char[length+1];; + strcpy( codec_and_format, format ); + format = codec_and_format; + codec_name = NULL; + char *f = strchr(codec_and_format, '/'); + if (f != NULL) + { + *f = 0; + codec_name = f+1; + } + } - SetupFormat( ); - SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate ); - SetParameters( ); - - // Allocate buffered packets. - packet_buffers = new AVPacket*[2]; - packet_buffers[0] = new AVPacket(); - packet_buffers[1] = new AVPacket(); - packet_index = 0; - - // Initialize mutex used by streaming thread. - if ( pthread_mutex_init( buffer_copy_lock, NULL ) != 0 ) - { - Fatal("pthread_mutex_init failed"); - } + SetupFormat( ); + SetupCodec( colours, subpixelorder, width, height, bitrate, frame_rate ); + SetParameters( ); + + // Allocate buffered packets. + packet_buffers = new AVPacket*[2]; + packet_buffers[0] = new AVPacket(); + packet_buffers[1] = new AVPacket(); + packet_index = 0; + + // Initialize mutex used by streaming thread. + if ( pthread_mutex_init( buffer_copy_lock, NULL ) != 0 ) + { + Fatal("pthread_mutex_init failed"); + } } VideoStream::~VideoStream( ) { - Debug( 1, "VideoStream destructor." ); - - // Stop streaming thread. - if ( streaming_thread ) - { - do_streaming = false; - void* thread_exit_code; - - Debug( 1, "Asking streaming thread to exit." ); - - // Wait for thread to exit. - pthread_join(streaming_thread, &thread_exit_code); - } - - if ( buffer_copy != NULL ) - { - av_free( buffer_copy ); - } + Debug( 1, "VideoStream destructor." ); + + // Stop streaming thread. + if ( streaming_thread ) + { + do_streaming = false; + void* thread_exit_code; - if ( buffer_copy_lock ) - { - if ( pthread_mutex_destroy( buffer_copy_lock ) != 0 ) - { - Error( "pthread_mutex_destroy failed" ); - } - delete buffer_copy_lock; - } + Debug( 1, "Asking streaming thread to exit." ); - if (packet_buffers) { - delete packet_buffers[0]; - delete packet_buffers[1]; - delete[] packet_buffers; + // Wait for thread to exit. + pthread_join(streaming_thread, &thread_exit_code); + } + + if ( buffer_copy != NULL ) + { + av_free( buffer_copy ); + } + + if ( buffer_copy_lock ) + { + if ( pthread_mutex_destroy( buffer_copy_lock ) != 0 ) + { + Error( "pthread_mutex_destroy failed" ); } - - /* close each codec */ - if ( ost ) - { - avcodec_close( ost->codec ); - av_free( opicture->data[0] ); - av_free( opicture ); - if ( tmp_opicture ) - { - av_free( tmp_opicture->data[0] ); - av_free( tmp_opicture ); - } - av_free( video_outbuf ); - } - - /* write the trailer, if any */ - av_write_trailer( ofc ); - - /* free the streams */ - for ( unsigned int i = 0; i < ofc->nb_streams; i++ ) - { - av_freep( &ofc->streams[i] ); - } - - if ( !(of->flags & AVFMT_NOFILE) ) - { - /* close the output file */ -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) - avio_close( ofc->pb ); + delete buffer_copy_lock; + } + + if (packet_buffers) { + delete packet_buffers[0]; + delete packet_buffers[1]; + delete[] packet_buffers; + } + + /* close each codec */ + if ( ost ) + { + avcodec_close( ost->codec ); + av_free( opicture->data[0] ); +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &opicture ); #else - url_fclose( ofc->pb ); + av_freep( &opicture ); #endif - } + if ( tmp_opicture ) + { + av_free( tmp_opicture->data[0] ); +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &tmp_opicture ); +#else + av_freep( &tmp_opicture ); +#endif + } + av_free( video_outbuf ); + } - /* free the stream */ - av_free( ofc ); - - /* free format and codec_name data. */ - if ( codec_and_format ) - { - delete codec_and_format; - } + /* write the trailer, if any */ + av_write_trailer( ofc ); + + /* free the streams */ + for ( unsigned int i = 0; i < ofc->nb_streams; i++ ) + { + av_freep( &ofc->streams[i] ); + } + + if ( !(of->flags & AVFMT_NOFILE) ) + { + /* close the output file */ +#if LIBAVFORMAT_VERSION_CHECK(52, 105, 0, 105, 0) + avio_close( ofc->pb ); +#else + url_fclose( ofc->pb ); +#endif + } + + /* free the stream */ + av_free( ofc ); + + /* free format and codec_name data. */ + if ( codec_and_format ) + { + delete codec_and_format; + } } double VideoStream::EncodeFrame( const uint8_t *buffer, int buffer_size, bool _add_timestamp, unsigned int _timestamp ) { - if ( pthread_mutex_lock( buffer_copy_lock ) != 0 ) - { - Fatal( "EncodeFrame: pthread_mutex_lock failed." ); - } - - if (buffer_copy_size < buffer_size) - { - if ( buffer_copy ) - { - av_free( buffer_copy ); - } - - // Allocate a buffer to store source images for the streaming thread to encode. - buffer_copy = (uint8_t *)av_malloc( buffer_size ); - if ( !buffer_copy ) - { - Panic( "Could not allocate buffer_copy" ); - } - buffer_copy_size = buffer_size; - } - - add_timestamp = _add_timestamp; - timestamp = _timestamp; - buffer_copy_used = buffer_size; - memcpy(buffer_copy, buffer, buffer_size); - - if ( pthread_mutex_unlock( buffer_copy_lock ) != 0 ) - { - Fatal( "EncodeFrame: pthread_mutex_unlock failed." ); - } - - if ( streaming_thread == 0 ) - { - Debug( 1, "Starting streaming thread" ); - - // Start a thread for streaming encoded video. - if (pthread_create( &streaming_thread, NULL, StreamingThreadCallback, (void*) this) != 0){ - // Log a fatal error and exit the process. - Fatal( "VideoStream failed to create streaming thread." ); - } - } - - //return ActuallyEncodeFrame( buffer, buffer_size, add_timestamp, timestamp); - - return _timestamp; + if ( pthread_mutex_lock( buffer_copy_lock ) != 0 ) + { + Fatal( "EncodeFrame: pthread_mutex_lock failed." ); + } + + if (buffer_copy_size < buffer_size) + { + if ( buffer_copy ) + { + av_free( buffer_copy ); + } + + // Allocate a buffer to store source images for the streaming thread to encode. + buffer_copy = (uint8_t *)av_malloc( buffer_size ); + if ( !buffer_copy ) + { + Panic( "Could not allocate buffer_copy" ); + } + buffer_copy_size = buffer_size; + } + + add_timestamp = _add_timestamp; + timestamp = _timestamp; + buffer_copy_used = buffer_size; + memcpy(buffer_copy, buffer, buffer_size); + + if ( pthread_mutex_unlock( buffer_copy_lock ) != 0 ) + { + Fatal( "EncodeFrame: pthread_mutex_unlock failed." ); + } + + if ( streaming_thread == 0 ) + { + Debug( 1, "Starting streaming thread" ); + + // Start a thread for streaming encoded video. + if (pthread_create( &streaming_thread, NULL, StreamingThreadCallback, (void*) this) != 0){ + // Log a fatal error and exit the process. + Fatal( "VideoStream failed to create streaming thread." ); + } + } + + //return ActuallyEncodeFrame( buffer, buffer_size, add_timestamp, timestamp); + + return _timestamp; } double VideoStream::ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp, unsigned int timestamp ) { #ifdef HAVE_LIBSWSCALE - static struct SwsContext *img_convert_ctx = 0; + static struct SwsContext *img_convert_ctx = 0; #endif // HAVE_LIBSWSCALE - AVCodecContext *c = ost->codec; + AVCodecContext *c = ost->codec; - if ( c->pix_fmt != pf ) - { - memcpy( tmp_opicture->data[0], buffer, buffer_size ); + if ( c->pix_fmt != pf ) + { + memcpy( tmp_opicture->data[0], buffer, buffer_size ); #ifdef HAVE_LIBSWSCALE - if ( !img_convert_ctx ) - { - img_convert_ctx = sws_getCachedContext( NULL, c->width, c->height, pf, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL ); - if ( !img_convert_ctx ) - Panic( "Unable to initialise image scaling context" ); - } - sws_scale( img_convert_ctx, tmp_opicture->data, tmp_opicture->linesize, 0, c->height, opicture->data, opicture->linesize ); + if ( !img_convert_ctx ) + { + img_convert_ctx = sws_getCachedContext( NULL, c->width, c->height, pf, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL ); + if ( !img_convert_ctx ) + Panic( "Unable to initialise image scaling context" ); + } + sws_scale( img_convert_ctx, tmp_opicture->data, tmp_opicture->linesize, 0, c->height, opicture->data, opicture->linesize ); #else // HAVE_LIBSWSCALE - Fatal( "swscale is required for MPEG mode" ); + Fatal( "swscale is required for MPEG mode" ); #endif // HAVE_LIBSWSCALE - } - else - { - memcpy( opicture->data[0], buffer, buffer_size ); - } - AVFrame *opicture_ptr = opicture; - - AVPacket *pkt = packet_buffers[packet_index]; - av_init_packet( pkt ); - int got_packet = 0; - if ( of->flags & AVFMT_RAWPICTURE ) - { -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 2, 1) - pkt->flags |= AV_PKT_FLAG_KEY; + } + else + { + memcpy( opicture->data[0], buffer, buffer_size ); + } + AVFrame *opicture_ptr = opicture; + + AVPacket *pkt = packet_buffers[packet_index]; + av_init_packet( pkt ); + int got_packet = 0; + if ( of->flags & AVFMT_RAWPICTURE ) + { +#if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2) + pkt->flags |= AV_PKT_FLAG_KEY; #else - pkt->flags |= PKT_FLAG_KEY; + pkt->flags |= PKT_FLAG_KEY; #endif - pkt->stream_index = ost->index; - pkt->data = (uint8_t *)opicture_ptr; - pkt->size = sizeof (AVPicture); - got_packet = 1; - } - else - { - opicture_ptr->pts = c->frame_number; - opicture_ptr->quality = c->global_quality; + pkt->stream_index = ost->index; + pkt->data = (uint8_t *)opicture_ptr; + pkt->size = sizeof (AVPicture); + got_packet = 1; + } + else + { + opicture_ptr->pts = c->frame_number; + opicture_ptr->quality = c->global_quality; -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(54, 0, 0) - int ret = avcodec_encode_video2( c, pkt, opicture_ptr, &got_packet ); - if ( ret != 0 ) - { - Fatal( "avcodec_encode_video2 failed with errorcode %d \"%s\"", ret, av_err2str( ret ) ); - } +#if LIBAVFORMAT_VERSION_CHECK(54, 1, 0, 2, 100) + int ret = avcodec_encode_video2( c, pkt, opicture_ptr, &got_packet ); + if ( ret != 0 ) + { + Fatal( "avcodec_encode_video2 failed with errorcode %d \"%s\"", ret, av_err2str( ret ) ); + } #else - int out_size = avcodec_encode_video( c, video_outbuf, video_outbuf_size, opicture_ptr ); - got_packet = out_size > 0 ? 1 : 0; - pkt->data = got_packet ? video_outbuf : NULL; - pkt->size = got_packet ? out_size : 0; + int out_size = avcodec_encode_video( c, video_outbuf, video_outbuf_size, opicture_ptr ); + got_packet = out_size > 0 ? 1 : 0; + pkt->data = got_packet ? video_outbuf : NULL; + pkt->size = got_packet ? out_size : 0; #endif - if ( got_packet ) - { - if ( c->coded_frame->key_frame ) - { -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) - pkt->flags |= AV_PKT_FLAG_KEY; -#else - pkt->flags |= PKT_FLAG_KEY; -#endif - } + if ( got_packet ) + { +// if ( c->coded_frame->key_frame ) +// { +//#if LIBAVCODEC_VERSION_CHECK(52, 30, 2, 30, 2) +// pkt->flags |= AV_PKT_FLAG_KEY; +//#else +// pkt->flags |= PKT_FLAG_KEY; +//#endif +// } - if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) - { - pkt->pts = av_rescale_q( pkt->pts, c->time_base, ost->time_base ); - } - if ( pkt->dts != (int64_t)AV_NOPTS_VALUE ) - { - pkt->dts = av_rescale_q( pkt->dts, c->time_base, ost->time_base ); - } - pkt->duration = av_rescale_q( pkt->duration, c->time_base, ost->time_base ); - pkt->stream_index = ost->index; - } - } - - return ( opicture_ptr->pts); + if ( pkt->pts != (int64_t)AV_NOPTS_VALUE ) + { + pkt->pts = av_rescale_q( pkt->pts, c->time_base, ost->time_base ); + } + if ( pkt->dts != (int64_t)AV_NOPTS_VALUE ) + { + pkt->dts = av_rescale_q( pkt->dts, c->time_base, ost->time_base ); + } + pkt->duration = av_rescale_q( pkt->duration, c->time_base, ost->time_base ); + pkt->stream_index = ost->index; + } + } + + return ( opicture_ptr->pts); } int VideoStream::SendPacket(AVPacket *packet) { - - int ret = av_write_frame( ofc, packet ); - if ( ret != 0 ) - { - Fatal( "Error %d while writing video frame: %s", ret, av_err2str( errno ) ); - } - av_free_packet(packet); - return ret; + + int ret = av_write_frame( ofc, packet ); + if ( ret != 0 ) + { + Fatal( "Error %d while writing video frame: %s", ret, av_err2str( errno ) ); + } +#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) + av_packet_unref( packet ); +#else + av_free_packet( packet ); +#endif + return ret; } void *VideoStream::StreamingThreadCallback(void *ctx){ - - Debug( 1, "StreamingThreadCallback started" ); - - if (ctx == NULL) return NULL; + + Debug( 1, "StreamingThreadCallback started" ); + + if (ctx == NULL) return NULL; - VideoStream* videoStream = reinterpret_cast(ctx); - - const uint64_t nanosecond_multiplier = 1000000000; - - uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->ost->codec->time_base.num) / (videoStream->ost->codec->time_base.den) ); - uint64_t frame_count = 0; - timespec start_time; - clock_gettime(CLOCK_MONOTONIC, &start_time); - uint64_t start_time_ns = (start_time.tv_sec*nanosecond_multiplier) + start_time.tv_nsec; - while(videoStream->do_streaming) - { - timespec current_time; - clock_gettime(CLOCK_MONOTONIC, ¤t_time); - uint64_t current_time_ns = (current_time.tv_sec*nanosecond_multiplier) + current_time.tv_nsec; - uint64_t target_ns = start_time_ns + (target_interval_ns * frame_count); - - if ( current_time_ns < target_ns ) - { - // It's not time to render a frame yet. - usleep( (target_ns - current_time_ns) * 0.001 ); - } - - // By sending the last rendered frame we deliver frames to the client more accurate. - // If we're encoding the frame before sending it there will be lag. - // Since this lag is not constant the client may skip frames. - - // Get the last rendered packet. - AVPacket *packet = videoStream->packet_buffers[videoStream->packet_index]; - if (packet->size) { - videoStream->SendPacket(packet); - } - av_free_packet(packet); - videoStream->packet_index = videoStream->packet_index ? 0 : 1; - - // Lock buffer and render next frame. - - if ( pthread_mutex_lock( videoStream->buffer_copy_lock ) != 0 ) - { - Fatal( "StreamingThreadCallback: pthread_mutex_lock failed." ); - } - - if ( videoStream->buffer_copy ) - { - // Encode next frame. - videoStream->ActuallyEncodeFrame( videoStream->buffer_copy, videoStream->buffer_copy_used, videoStream->add_timestamp, videoStream->timestamp ); - } - - if ( pthread_mutex_unlock( videoStream->buffer_copy_lock ) != 0 ) - { - Fatal( "StreamingThreadCallback: pthread_mutex_unlock failed." ); - } - - frame_count++; - } - - return 0; + VideoStream* videoStream = reinterpret_cast(ctx); + + const uint64_t nanosecond_multiplier = 1000000000; + + uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->ost->codec->time_base.num) / (videoStream->ost->codec->time_base.den) ); + uint64_t frame_count = 0; + timespec start_time; + clock_gettime(CLOCK_MONOTONIC, &start_time); + uint64_t start_time_ns = (start_time.tv_sec*nanosecond_multiplier) + start_time.tv_nsec; + while(videoStream->do_streaming) + { + timespec current_time; + clock_gettime(CLOCK_MONOTONIC, ¤t_time); + uint64_t current_time_ns = (current_time.tv_sec*nanosecond_multiplier) + current_time.tv_nsec; + uint64_t target_ns = start_time_ns + (target_interval_ns * frame_count); + + if ( current_time_ns < target_ns ) + { + // It's not time to render a frame yet. + usleep( (target_ns - current_time_ns) * 0.001 ); + } + + // By sending the last rendered frame we deliver frames to the client more accurate. + // If we're encoding the frame before sending it there will be lag. + // Since this lag is not constant the client may skip frames. + + // Get the last rendered packet. + AVPacket *packet = videoStream->packet_buffers[videoStream->packet_index]; + if (packet->size) { + videoStream->SendPacket(packet); + } +#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) + av_packet_unref( packet); +#else + av_free_packet( packet ); +#endif + videoStream->packet_index = videoStream->packet_index ? 0 : 1; + + // Lock buffer and render next frame. + + if ( pthread_mutex_lock( videoStream->buffer_copy_lock ) != 0 ) + { + Fatal( "StreamingThreadCallback: pthread_mutex_lock failed." ); + } + + if ( videoStream->buffer_copy ) + { + // Encode next frame. + videoStream->ActuallyEncodeFrame( videoStream->buffer_copy, videoStream->buffer_copy_used, videoStream->add_timestamp, videoStream->timestamp ); + } + + if ( pthread_mutex_unlock( videoStream->buffer_copy_lock ) != 0 ) + { + Fatal( "StreamingThreadCallback: pthread_mutex_unlock failed." ); + } + + frame_count++; + } + + return 0; } #endif // HAVE_LIBAVCODEC diff --git a/src/zm_mpeg.h b/src/zm_mpeg.h index 2a09a2942..e2ec33592 100644 --- a/src/zm_mpeg.h +++ b/src/zm_mpeg.h @@ -27,60 +27,60 @@ class VideoStream { protected: - struct MimeData - { - const char *format; - const char *mime_type; - }; + struct MimeData + { + const char *format; + const char *mime_type; + }; protected: - static bool initialised; - static struct MimeData mime_data[]; + static bool initialised; + static struct MimeData mime_data[]; protected: - char *codec_and_format; - const char *filename; - const char *format; - const char *codec_name; - enum PixelFormat pf; - AVOutputFormat *of; - AVFormatContext *ofc; - AVStream *ost; - AVCodec *codec; - AVFrame *opicture; - AVFrame *tmp_opicture; - uint8_t *video_outbuf; - int video_outbuf_size; - double last_pts; - - pthread_t streaming_thread; - bool do_streaming; - uint8_t *buffer_copy; - bool add_timestamp; - unsigned int timestamp; - pthread_mutex_t *buffer_copy_lock; - int buffer_copy_size; - int buffer_copy_used; - AVPacket** packet_buffers; - int packet_index; - int SendPacket(AVPacket *packet); - static void* StreamingThreadCallback(void *ctx); + char *codec_and_format; + const char *filename; + const char *format; + const char *codec_name; + enum _AVPIXELFORMAT pf; + AVOutputFormat *of; + AVFormatContext *ofc; + AVStream *ost; + AVCodec *codec; + AVFrame *opicture; + AVFrame *tmp_opicture; + uint8_t *video_outbuf; + int video_outbuf_size; + double last_pts; + + pthread_t streaming_thread; + bool do_streaming; + uint8_t *buffer_copy; + bool add_timestamp; + unsigned int timestamp; + pthread_mutex_t *buffer_copy_lock; + int buffer_copy_size; + int buffer_copy_used; + AVPacket** packet_buffers; + int packet_index; + int SendPacket(AVPacket *packet); + static void* StreamingThreadCallback(void *ctx); protected: - static void Initialise(); + static void Initialise(); - void SetupFormat( ); - void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ); - void SetParameters(); - void ActuallyOpenStream(); - double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); + void SetupFormat( ); + void SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ); + void SetParameters(); + void ActuallyOpenStream(); + double ActuallyEncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); public: - VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ); - ~VideoStream(); - const char *MimeType() const; - void OpenStream(); - double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); + VideoStream( const char *filename, const char *format, int bitrate, double frame_rate, int colours, int subpixelorder, int width, int height ); + ~VideoStream(); + const char *MimeType() const; + void OpenStream(); + double EncodeFrame( const uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); }; #endif // HAVE_LIBAVCODEC diff --git a/src/zm_poly.cpp b/src/zm_poly.cpp index eef5dd56a..cddcced46 100644 --- a/src/zm_poly.cpp +++ b/src/zm_poly.cpp @@ -20,99 +20,103 @@ #include "zm.h" #include "zm_poly.h" +#ifndef SOLARIS #include +#else +#include +#endif void Polygon::calcArea() { - double float_area = 0.0L; - for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) - { - double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L; - float_area += trap_area; - //printf( "%.2f (%.2f)\n", float_area, trap_area ); - } - area = (int)round(fabs(float_area)); + double float_area = 0.0L; + for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) + { + double trap_area = ((coords[i].X()-coords[j].X())*((coords[i].Y()+coords[j].Y())))/2.0L; + float_area += trap_area; + //printf( "%.2f (%.2f)\n", float_area, trap_area ); + } + area = (int)round(fabs(float_area)); } void Polygon::calcCentre() { - if ( !area && n_coords ) - calcArea(); - double float_x = 0.0L, float_y = 0.0L; - for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) - { - float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2))); - float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2))); - } - float_x /= (6*area); - float_y /= (6*area); - //printf( "%.2f,%.2f\n", float_x, float_y ); - centre = Coord( (int)round(float_x), (int)round(float_y) ); + if ( !area && n_coords ) + calcArea(); + double float_x = 0.0L, float_y = 0.0L; + for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) + { + float_x += ((coords[i].Y()-coords[j].Y())*((coords[i].X()*2)+(coords[i].X()*coords[j].X())+(coords[j].X()*2))); + float_y += ((coords[j].X()-coords[i].X())*((coords[i].Y()*2)+(coords[i].Y()*coords[j].Y())+(coords[j].Y()*2))); + } + float_x /= (6*area); + float_y /= (6*area); + //printf( "%.2f,%.2f\n", float_x, float_y ); + centre = Coord( (int)round(float_x), (int)round(float_y) ); } Polygon::Polygon( int p_n_coords, const Coord *p_coords ) : n_coords( p_n_coords ) { - coords = new Coord[n_coords]; + coords = new Coord[n_coords]; - int min_x = -1; - int max_x = -1; - int min_y = -1; - int max_y = -1; - for( int i = 0; i < n_coords; i++ ) - { - coords[i] = p_coords[i]; - if ( min_x == -1 || coords[i].X() < min_x ) - min_x = coords[i].X(); - if ( max_x == -1 || coords[i].X() > max_x ) - max_x = coords[i].X(); - if ( min_y == -1 || coords[i].Y() < min_y ) - min_y = coords[i].Y(); - if ( max_y == -1 || coords[i].Y() > max_y ) - max_y = coords[i].Y(); - } - extent = Box( min_x, min_y, max_x, max_y ); - calcArea(); - calcCentre(); + int min_x = -1; + int max_x = -1; + int min_y = -1; + int max_y = -1; + for( int i = 0; i < n_coords; i++ ) + { + coords[i] = p_coords[i]; + if ( min_x == -1 || coords[i].X() < min_x ) + min_x = coords[i].X(); + if ( max_x == -1 || coords[i].X() > max_x ) + max_x = coords[i].X(); + if ( min_y == -1 || coords[i].Y() < min_y ) + min_y = coords[i].Y(); + if ( max_y == -1 || coords[i].Y() > max_y ) + max_y = coords[i].Y(); + } + extent = Box( min_x, min_y, max_x, max_y ); + calcArea(); + calcCentre(); } Polygon::Polygon( const Polygon &p_polygon ) : n_coords( p_polygon.n_coords ), extent( p_polygon.extent ), area( p_polygon.area ), centre( p_polygon.centre ) { - coords = new Coord[n_coords]; - for( int i = 0; i < n_coords; i++ ) - { - coords[i] = p_polygon.coords[i]; - } + coords = new Coord[n_coords]; + for( int i = 0; i < n_coords; i++ ) + { + coords[i] = p_polygon.coords[i]; + } } Polygon &Polygon::operator=( const Polygon &p_polygon ) { - if ( n_coords < p_polygon.n_coords ) - { - delete[] coords; - coords = new Coord[p_polygon.n_coords]; - } - n_coords = p_polygon.n_coords; - for( int i = 0; i < n_coords; i++ ) - { - coords[i] = p_polygon.coords[i]; - } - extent = p_polygon.extent; - area = p_polygon.area; - centre = p_polygon.centre; - return( *this ); + if ( n_coords < p_polygon.n_coords ) + { + delete[] coords; + coords = new Coord[p_polygon.n_coords]; + } + n_coords = p_polygon.n_coords; + for( int i = 0; i < n_coords; i++ ) + { + coords[i] = p_polygon.coords[i]; + } + extent = p_polygon.extent; + area = p_polygon.area; + centre = p_polygon.centre; + return( *this ); } bool Polygon::isInside( const Coord &coord ) const { - bool inside = false; - for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) - { - if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) ) - || ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y()))) - && (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X())) - { - inside = !inside; - } - } - return( inside ); + bool inside = false; + for ( int i = 0, j = n_coords-1; i < n_coords; j = i++ ) + { + if ( (((coords[i].Y() <= coord.Y()) && (coord.Y() < coords[j].Y()) ) + || ((coords[j].Y() <= coord.Y()) && (coord.Y() < coords[i].Y()))) + && (coord.X() < (coords[j].X() - coords[i].X()) * (coord.Y() - coords[i].Y()) / (coords[j].Y() - coords[i].Y()) + coords[i].X())) + { + inside = !inside; + } + } + return( inside ); } diff --git a/src/zm_poly.h b/src/zm_poly.h index 854a3c322..7ba3043fd 100644 --- a/src/zm_poly.h +++ b/src/zm_poly.h @@ -33,93 +33,93 @@ class Polygon { protected: - struct Edge - { - int min_y; - int max_y; - double min_x; - double _1_m; + struct Edge + { + int min_y; + int max_y; + double min_x; + double _1_m; - static int CompareYX( const void *p1, const void *p2 ) - { - const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; - if ( e1->min_y == e2->min_y ) - return( int(e1->min_x - e2->min_x) ); - else - return( int(e1->min_y - e2->min_y) ); - } - static int CompareX( const void *p1, const void *p2 ) - { - const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; - return( int(e1->min_x - e2->min_x) ); - } - }; + static int CompareYX( const void *p1, const void *p2 ) + { + const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; + if ( e1->min_y == e2->min_y ) + return( int(e1->min_x - e2->min_x) ); + else + return( int(e1->min_y - e2->min_y) ); + } + static int CompareX( const void *p1, const void *p2 ) + { + const Edge *e1 = (const Edge *)p1, *e2 = (const Edge *)p2; + return( int(e1->min_x - e2->min_x) ); + } + }; - struct Slice - { - int min_x; - int max_x; - int n_edges; - int *edges; + struct Slice + { + int min_x; + int max_x; + int n_edges; + int *edges; - Slice() - { - n_edges = 0; - edges = 0; - } - ~Slice() - { - delete edges; - } - }; + Slice() + { + n_edges = 0; + edges = 0; + } + ~Slice() + { + delete edges; + } + }; protected: - int n_coords; - Coord *coords; - Box extent; - int area; - Coord centre; - Edge *edges; - Slice *slices; + int n_coords; + Coord *coords; + Box extent; + int area; + Coord centre; + Edge *edges; + Slice *slices; protected: - void initialiseEdges(); - void calcArea(); - void calcCentre(); + void initialiseEdges(); + void calcArea(); + void calcCentre(); public: - inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 ) - { - } - Polygon( int p_n_coords, const Coord *p_coords ); - Polygon( const Polygon &p_polygon ); - ~Polygon() - { - delete[] coords; - } + inline Polygon() : n_coords( 0 ), coords( 0 ), area( 0 ) + { + } + Polygon( int p_n_coords, const Coord *p_coords ); + Polygon( const Polygon &p_polygon ); + ~Polygon() + { + delete[] coords; + } - Polygon &operator=( const Polygon &p_polygon ); + Polygon &operator=( const Polygon &p_polygon ); - inline int getNumCoords() const { return( n_coords ); } - inline const Coord &getCoord( int index ) const - { - return( coords[index] ); - } + inline int getNumCoords() const { return( n_coords ); } + inline const Coord &getCoord( int index ) const + { + return( coords[index] ); + } - inline const Box &Extent() const { return( extent ); } - inline int LoX() const { return( extent.LoX() ); } - inline int HiX() const { return( extent.HiX() ); } - inline int LoY() const { return( extent.LoY() ); } - inline int HiY() const { return( extent.HiY() ); } - inline int Width() const { return( extent.Width() ); } - inline int Height() const { return( extent.Height() ); } + inline const Box &Extent() const { return( extent ); } + inline int LoX() const { return( extent.LoX() ); } + inline int HiX() const { return( extent.HiX() ); } + inline int LoY() const { return( extent.LoY() ); } + inline int HiY() const { return( extent.HiY() ); } + inline int Width() const { return( extent.Width() ); } + inline int Height() const { return( extent.Height() ); } - inline int Area() const { return( area ); } - inline const Coord &Centre() const - { - return( centre ); - } - bool isInside( const Coord &coord ) const; + inline int Area() const { return( area ); } + inline const Coord &Centre() const + { + return( centre ); + } + bool isInside( const Coord &coord ) const; }; #endif // ZM_POLY_H diff --git a/src/zm_regexp.cpp b/src/zm_regexp.cpp index 43f738217..cfa686688 100644 --- a/src/zm_regexp.cpp +++ b/src/zm_regexp.cpp @@ -26,99 +26,99 @@ RegExpr::RegExpr( const char *pattern, int flags, int p_max_matches ) : max_matches( p_max_matches ), match_buffers( 0 ), match_lengths( 0 ), match_valid( 0 ) { - const char *errstr; - int erroffset = 0; - if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) ) - { - Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset ); - } + const char *errstr; + int erroffset = 0; + if ( !(regex = pcre_compile( pattern, flags, &errstr, &erroffset, 0 )) ) + { + Panic( "pcre_compile(%s): %s at %d", pattern, errstr, erroffset ); + } - regextra = pcre_study( regex, 0, &errstr ); - if ( errstr ) - { - Panic( "pcre_study(%s): %s", pattern, errstr ); - } + regextra = pcre_study( regex, 0, &errstr ); + if ( errstr ) + { + Panic( "pcre_study(%s): %s", pattern, errstr ); + } - if ( (ok = (bool)regex) ) - { - match_vectors = new int[3*max_matches]; - memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches ); - match_buffers = new char *[max_matches]; - memset( match_buffers, 0, sizeof(*match_buffers)*max_matches ); - match_lengths = new int[max_matches]; - memset( match_lengths, 0, sizeof(*match_lengths)*max_matches ); - match_valid = new bool[max_matches]; - memset( match_valid, 0, sizeof(*match_valid)*max_matches ); - } - n_matches = 0; + if ( (ok = (bool)regex) ) + { + match_vectors = new int[3*max_matches]; + memset( match_vectors, 0, sizeof(*match_vectors)*3*max_matches ); + match_buffers = new char *[max_matches]; + memset( match_buffers, 0, sizeof(*match_buffers)*max_matches ); + match_lengths = new int[max_matches]; + memset( match_lengths, 0, sizeof(*match_lengths)*max_matches ); + match_valid = new bool[max_matches]; + memset( match_valid, 0, sizeof(*match_valid)*max_matches ); + } + n_matches = 0; } RegExpr::~RegExpr() { - for ( int i = 0; i < max_matches; i++ ) - { - if ( match_buffers[i] ) - { - delete[] match_buffers[i]; - } - } - delete[] match_valid; - delete[] match_lengths; - delete[] match_buffers; - delete[] match_vectors; + for ( int i = 0; i < max_matches; i++ ) + { + if ( match_buffers[i] ) + { + delete[] match_buffers[i]; + } + } + delete[] match_valid; + delete[] match_lengths; + delete[] match_buffers; + delete[] match_vectors; } int RegExpr::Match( const char *subject_string, int subject_length, int flags ) { - match_string = subject_string; + match_string = subject_string; - n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches ); + n_matches = pcre_exec( regex, regextra, subject_string, subject_length, 0, flags, match_vectors, 2*max_matches ); - if ( n_matches <= 0 ) - { - if ( n_matches < PCRE_ERROR_NOMATCH ) - { - Error( "Error %d executing regular expression", n_matches ); - } - return( n_matches = 0 ); - } + if ( n_matches <= 0 ) + { + if ( n_matches < PCRE_ERROR_NOMATCH ) + { + Error( "Error %d executing regular expression", n_matches ); + } + return( n_matches = 0 ); + } - for( int i = 0; i < max_matches; i++ ) - { - match_valid[i] = false; - } - return( n_matches ); + for( int i = 0; i < max_matches; i++ ) + { + match_valid[i] = false; + } + return( n_matches ); } const char *RegExpr::MatchString( int match_index ) const { - if ( match_index > n_matches ) - { - return( 0 ); - } - if ( !match_valid[match_index] ) - { - int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index]; - if ( match_lengths[match_index] < (match_len+1) ) - { - delete[] match_buffers[match_index]; - match_buffers[match_index] = new char[match_len+1]; - match_lengths[match_index] = match_len+1; - } - memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len ); - match_buffers[match_index][match_len] = '\0'; - match_valid[match_index] = true; - } - return( match_buffers[match_index] ); + if ( match_index > n_matches ) + { + return( 0 ); + } + if ( !match_valid[match_index] ) + { + int match_len = match_vectors[(2*match_index)+1]-match_vectors[2*match_index]; + if ( match_lengths[match_index] < (match_len+1) ) + { + delete[] match_buffers[match_index]; + match_buffers[match_index] = new char[match_len+1]; + match_lengths[match_index] = match_len+1; + } + memcpy( match_buffers[match_index], match_string+match_vectors[2*match_index], match_len ); + match_buffers[match_index][match_len] = '\0'; + match_valid[match_index] = true; + } + return( match_buffers[match_index] ); } int RegExpr::MatchLength( int match_index ) const { - if ( match_index > n_matches ) - { - return( 0 ); - } - return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] ); + if ( match_index > n_matches ) + { + return( 0 ); + } + return( match_vectors[(2*match_index)+1]-match_vectors[2*match_index] ); } #endif // HAVE_LIBPCRE diff --git a/src/zm_regexp.h b/src/zm_regexp.h index f1c9d705d..1b1a9d518 100644 --- a/src/zm_regexp.h +++ b/src/zm_regexp.h @@ -35,29 +35,29 @@ class RegExpr { protected: - pcre *regex; - pcre_extra *regextra; - int max_matches; - int *match_vectors; - mutable char **match_buffers; - int *match_lengths; - bool *match_valid; + pcre *regex; + pcre_extra *regextra; + int max_matches; + int *match_vectors; + mutable char **match_buffers; + int *match_lengths; + bool *match_valid; protected: - const char *match_string; - int n_matches; - + const char *match_string; + int n_matches; + protected: - bool ok; + bool ok; public: - RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 ); - ~RegExpr(); - bool Ok() const { return( ok ); } - int MatchCount() const { return( n_matches ); } - int Match( const char *subject_string, int subject_length, int flags=0 ); - const char *MatchString( int match_index ) const; - int MatchLength( int match_index ) const; + RegExpr( const char *pattern, int cflags=0, int p_max_matches=32 ); + ~RegExpr(); + bool Ok() const { return( ok ); } + int MatchCount() const { return( n_matches ); } + int Match( const char *subject_string, int subject_length, int flags=0 ); + const char *MatchString( int match_index ) const; + int MatchLength( int match_index ) const; }; #endif // HAVE_LIBPCRE diff --git a/src/zm_remote_camera.cpp b/src/zm_remote_camera.cpp index 5d04e013e..b82589cd6 100644 --- a/src/zm_remote_camera.cpp +++ b/src/zm_remote_camera.cpp @@ -22,58 +22,66 @@ #include "zm_utils.h" RemoteCamera::RemoteCamera( int p_id, const std::string &p_protocol, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), - protocol( p_protocol ), - host( p_host ), - port( p_port ), - path( p_path ), - hp( 0 ) + Camera( p_id, REMOTE_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture ), + protocol( p_protocol ), + host( p_host ), + port( p_port ), + path( p_path ), + hp( 0 ) { - if ( path[0] != '/' ) - path = '/'+path; + if ( path[0] != '/' ) + path = '/'+path; } RemoteCamera::~RemoteCamera() { - if(hp != NULL) { - freeaddrinfo(hp); - hp = NULL; - } + if(hp != NULL) { + freeaddrinfo(hp); + hp = NULL; + } } void RemoteCamera::Initialise() { - if( protocol.empty() ) - Fatal( "No protocol specified for remote camera" ); + if( protocol.empty() ) + Fatal( "No protocol specified for remote camera" ); - if( host.empty() ) - Fatal( "No host specified for remote camera" ); + if( host.empty() ) + Fatal( "No host specified for remote camera" ); - if( port.empty() ) - Fatal( "No port specified for remote camera" ); + if( port.empty() ) + Fatal( "No port specified for remote camera" ); - //if( path.empty() ) - //Fatal( "No path specified for remote camera" ); + //if( path.empty() ) + //Fatal( "No path specified for remote camera" ); - // Cache as much as we can to speed things up - std::string::size_type authIndex = host.rfind( '@' ); + // Cache as much as we can to speed things up + std::string::size_type authIndex = host.rfind( '@' ); - if ( authIndex != std::string::npos ) - { - auth = host.substr( 0, authIndex ); - host.erase( 0, authIndex+1 ); - auth64 = base64Encode( auth ); - } + if ( authIndex != std::string::npos ) + { + auth = host.substr( 0, authIndex ); + host.erase( 0, authIndex+1 ); + auth64 = base64Encode( auth ); - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; + authIndex = auth.rfind( ':' ); + username = auth.substr(0,authIndex); + password = auth.substr( authIndex+1, auth.length() ); - int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp); - if ( ret != 0 ) - { - Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) ); - } + } + + mNeedAuth = false; + mAuthenticator = new zm::Authenticator(username,password); + + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp); + if ( ret != 0 ) + { + Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) ); + } } diff --git a/src/zm_remote_camera.h b/src/zm_remote_camera.h index 2738e4a5d..647782fd1 100644 --- a/src/zm_remote_camera.h +++ b/src/zm_remote_camera.h @@ -21,6 +21,7 @@ #define ZM_REMOTE_CAMERA_H #include "zm_camera.h" +#include "zm_rtsp_auth.h" #include #include @@ -34,33 +35,44 @@ class RemoteCamera : public Camera { protected: - std::string protocol; - std::string host; - std::string port; - std::string path; - std::string auth; - std::string auth64; + std::string protocol; + std::string host; + std::string port; + std::string path; + std::string auth; + std::string username; + std::string password; + std::string auth64; + // Reworked authentication system + // First try without authentication, even if we have a username and password + // on receiving a 401 response, select authentication method (basic or digest) + // fill required fields and set needAuth + // subsequent requests can set the required authentication header. + bool mNeedAuth; + zm::Authenticator* mAuthenticator; protected: - struct addrinfo *hp; + struct addrinfo *hp; public: - RemoteCamera( int p_id, const std::string &p_proto, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - virtual ~RemoteCamera(); + RemoteCamera( int p_id, const std::string &p_proto, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); + virtual ~RemoteCamera(); - const std::string &Protocol() const { return( protocol ); } - const std::string &Host() const { return( host ); } - const std::string &Port() const { return( port ); } - const std::string &Path() const { return( path ); } - const std::string &Auth() const { return( auth ); } + const std::string &Protocol() const { return( protocol ); } + const std::string &Host() const { return( host ); } + const std::string &Port() const { return( port ); } + const std::string &Path() const { return( path ); } + const std::string &Auth() const { return( auth ); } + const std::string &Username() const { return( username ); } + const std::string &Password() const { return( password ); } - virtual void Initialise(); - virtual void Terminate() = 0; - virtual int Connect() = 0; - virtual int Disconnect() = 0; - virtual int PreCapture() = 0; - virtual int Capture( Image &image ) = 0; - virtual int PostCapture() = 0; + virtual void Initialise(); + virtual void Terminate() = 0; + virtual int Connect() = 0; + virtual int Disconnect() = 0; + virtual int PreCapture() = 0; + virtual int Capture( Image &image ) = 0; + virtual int PostCapture() = 0; }; #endif // ZM_REMOTE_CAMERA_H diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index 07d0e6a41..9454b8d69 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -18,6 +18,7 @@ // #include "zm_remote_camera_http.h" +#include "zm_rtsp_auth.h" #include "zm_mem_utils.h" @@ -26,1136 +27,1166 @@ #include #include +#ifdef SOLARIS +#include // FIONREAD and friends +#endif + RemoteCameraHttp::RemoteCameraHttp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - RemoteCamera( p_id, "http", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ) + RemoteCamera( p_id, "http", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ) { - sd = -1; + sd = -1; - timeout.tv_sec = 0; - timeout.tv_usec = 0; + timeout.tv_sec = 0; + timeout.tv_usec = 0; - if ( p_method == "simple" ) - method = SIMPLE; - else if ( p_method == "regexp" ) - method = REGEXP; - else - Fatal( "Unrecognised method '%s' when creating HTTP camera %d", p_method.c_str(), id ); - if ( capture ) - { - Initialise(); - } + if ( p_method == "simple" ) + method = SIMPLE; + else if ( p_method == "regexp" ) + method = REGEXP; + else + Fatal( "Unrecognised method '%s' when creating HTTP camera %d", p_method.c_str(), id ); + if ( capture ) + { + Initialise(); + } } RemoteCameraHttp::~RemoteCameraHttp() { - if ( capture ) - { - Terminate(); - } + if ( capture ) + { + Terminate(); + } } void RemoteCameraHttp::Initialise() { - RemoteCamera::Initialise(); + RemoteCamera::Initialise(); - if ( request.empty() ) - { - request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); - request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); - request += stringtf( "Host: %s\r\n", host .c_str()); - if ( strcmp( config.http_version, "1.0" ) == 0 ) - request += stringtf( "Connection: Keep-Alive\r\n" ); - if ( !auth.empty() ) - request += stringtf( "Authorization: Basic %s\r\n", auth64.c_str() ); - request += "\r\n"; - Debug( 2, "Request: %s", request.c_str() ); - } + if ( request.empty() ) + { + request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); + request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); + request += stringtf( "Host: %s\r\n", host.c_str()); + if ( strcmp( config.http_version, "1.0" ) == 0 ) + request += stringtf( "Connection: Keep-Alive\r\n" ); + if ( !auth.empty() ) + request += stringtf( "Authorization: Basic %s\r\n", auth64.c_str() ); + request += "\r\n"; + Debug( 2, "Request: %s", request.c_str() ); + } - if ( !timeout.tv_sec ) - { - timeout.tv_sec = config.http_timeout/1000; - timeout.tv_usec = (config.http_timeout%1000)*1000; - } + if ( !timeout.tv_sec ) + { + timeout.tv_sec = config.http_timeout/1000; + timeout.tv_usec = (config.http_timeout%1000)*1000; + } - int max_size = width*height*colours; + int max_size = width*height*colours; - buffer.size( max_size ); + buffer.size( max_size ); - mode = SINGLE_IMAGE; - format = UNDEF; - state = HEADER; + mode = SINGLE_IMAGE; + format = UNDEF; + state = HEADER; } int RemoteCameraHttp::Connect() { - struct addrinfo *p; + struct addrinfo *p; - for(p = hp; p != NULL; p = p->ai_next) + for(p = hp; p != NULL; p = p->ai_next) + { + sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol ); + if ( sd < 0 ) { - sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol ); - if ( sd < 0 ) - { - Warning("Can't create socket: %s", strerror(errno) ); - continue; - } - - if ( connect( sd, p->ai_addr, p->ai_addrlen ) < 0 ) - { - close(sd); - sd = -1; - Warning("Can't connect to remote camera: %s", strerror(errno) ); - continue; - } - - /* If we got here, we must have connected successfully */ - break; + Warning("Can't create socket: %s", strerror(errno) ); + continue; } - if(p == NULL) { - Error("Unable to connect to the remote camera, aborting"); - return( -1 ); + if ( connect( sd, p->ai_addr, p->ai_addrlen ) < 0 ) + { + close(sd); + sd = -1; + Warning("Can't connect to remote camera: %s", strerror(errno) ); + continue; } - Debug( 3, "Connected to host, socket = %d", sd ); - return( sd ); + /* If we got here, we must have connected successfully */ + break; + } + + if(p == NULL) { + Error("Unable to connect to the remote camera, aborting"); + return( -1 ); + } + + Debug( 3, "Connected to host, socket = %d", sd ); + return( sd ); } int RemoteCameraHttp::Disconnect() { - close( sd ); - sd = -1; - Debug( 3, "Disconnected from host" ); - return( 0 ); + close( sd ); + sd = -1; + Debug( 3, "Disconnected from host" ); + return( 0 ); } int RemoteCameraHttp::SendRequest() { - if ( write( sd, request.data(), request.length() ) < 0 ) - { - Error( "Can't write: %s", strerror(errno) ); - Disconnect(); - return( -1 ); - } - format = UNDEF; - state = HEADER; - Debug( 3, "Request sent" ); - return( 0 ); + Debug( 2, "Sending request: %s", request.c_str() ); + if ( write( sd, request.data(), request.length() ) < 0 ) + { + Error( "Can't write: %s", strerror(errno) ); + Disconnect(); + return( -1 ); + } + format = UNDEF; + state = HEADER; + Debug( 3, "Request sent" ); + return( 0 ); } +/* Return codes are as follows: + * -1 means there was an error + * 0 means no bytes were returned but there wasn't actually an error. + * > 0 is the # of bytes read. + */ + int RemoteCameraHttp::ReadData( Buffer &buffer, int bytes_expected ) { - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(sd, &rfds); + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(sd, &rfds); - struct timeval temp_timeout = timeout; + struct timeval temp_timeout = timeout; - int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); - if( n_found == 0 ) + int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); + if( n_found == 0 ) + { + Debug( 4, "Select timed out timeout was %d secs %d usecs", temp_timeout.tv_sec, temp_timeout.tv_usec ); + // Why are we disconnecting? It's just a timeout, meaning that data wasn't available. + //Disconnect(); + return( 0 ); + } + else if ( n_found < 0) + { + Error( "Select error: %s", strerror(errno) ); + return( -1 ); + } + + int total_bytes_to_read = 0; + + if ( bytes_expected ) + { + total_bytes_to_read = bytes_expected; + } + else + { + if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) { - Warning( "Select timed out" ); - Disconnect(); - return( 0 ); - } - else if ( n_found < 0) - { - Error( "Select error: %s", strerror(errno) ); - return( -1 ); + Error( "Can't ioctl(): %s", strerror(errno) ); + return( -1 ); } - int total_bytes_to_read = 0; - - if ( bytes_expected ) + if ( total_bytes_to_read == 0 ) { - total_bytes_to_read = bytes_expected; + // If socket is closed locally, then select will fail, but if it is closed remotely + // then we have an exception on our socket.. but no data. + Debug( 3, "Socket closed remotely" ); + //Disconnect(); // Disconnect is done outside of ReadData now. + return( -1 ); } - else + + // There can be lots of bytes available. I've seen 4MB or more. This will vastly inflate our buffer size unnecessarily. + if ( total_bytes_to_read > ZM_NETWORK_BUFSIZ ) { + total_bytes_to_read = ZM_NETWORK_BUFSIZ; + Debug(3, "Just getting 32K" ); + } else { + Debug(3, "Just getting %d", total_bytes_to_read ); + } + } + Debug( 3, "Expecting %d bytes", total_bytes_to_read ); + + int total_bytes_read = 0; + do + { + int bytes_read = buffer.read_into( sd, total_bytes_to_read ); + if ( bytes_read < 0) { - if ( ioctl( sd, FIONREAD, &total_bytes_to_read ) < 0 ) - { - Error( "Can't ioctl(): %s", strerror(errno) ); - return( -1 ); - } - - if ( total_bytes_to_read == 0 ) - { - Debug( 3, "Socket closed" ); - Disconnect(); - return( 0 ); - } + Error( "Read error: %s", strerror(errno) ); + return( -1 ); } - Debug( 3, "Expecting %d bytes", total_bytes_to_read ); - - int total_bytes_read = 0; - do + else if ( bytes_read == 0) { - static unsigned char temp_buffer[ZM_NETWORK_BUFSIZ]; - int bytes_to_read = (unsigned int)total_bytes_to_read>(unsigned int)sizeof(temp_buffer)?sizeof(temp_buffer):total_bytes_to_read; - int bytes_read = read( sd, temp_buffer, bytes_to_read ); - - if ( bytes_read < 0) - { - Error( "Read error: %s", strerror(errno) ); - return( -1 ); - } - else if ( bytes_read == 0) - { - Debug( 3, "Socket closed" ); - Disconnect(); - return( 0 ); - } - else if ( bytes_read < bytes_to_read ) - { - Error( "Incomplete read, expected %d, got %d", bytes_to_read, bytes_read ); - return( -1 ); - } - Debug( 3, "Read %d bytes", bytes_read ); - buffer.append( temp_buffer, bytes_read ); - total_bytes_read += bytes_read; - total_bytes_to_read -= bytes_read; + Debug( 2, "Socket closed" ); + //Disconnect(); // Disconnect is done outside of ReadData now. + return( -1 ); } - while ( total_bytes_to_read ); + else if ( bytes_read < total_bytes_to_read ) + { + Error( "Incomplete read, expected %d, got %d", total_bytes_to_read, bytes_read ); + return( -1 ); + } + Debug( 3, "Read %d bytes", bytes_read ); + total_bytes_read += bytes_read; + total_bytes_to_read -= bytes_read; + } + while ( total_bytes_to_read ); - return( total_bytes_read ); + Debug( 4, buffer ); + + return( total_bytes_read ); } int RemoteCameraHttp::GetResponse() { + int buffer_len; #if HAVE_LIBPCRE + if ( method == REGEXP ) + { + const char *header = 0; + int header_len = 0; + const char *http_version = 0; + int status_code = 0; + const char *status_mesg = 0; + const char *connection_type = ""; + int content_length = 0; + const char *content_type = ""; + const char *content_boundary = ""; + const char *subheader = 0; + int subheader_len = 0; + //int subcontent_length = 0; + //const char *subcontent_type = ""; + + while ( true ) + { + switch( state ) + { + case HEADER : + { + static RegExpr *header_expr = 0; + static RegExpr *status_expr = 0; + static RegExpr *connection_expr = 0; + static RegExpr *content_length_expr = 0; + static RegExpr *content_type_expr = 0; + + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to read header data" ); + return( -1 ); + } + if ( !header_expr ) + header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL ); + if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) + { + header = header_expr->MatchString( 1 ); + header_len = header_expr->MatchLength( 1 ); + Debug( 4, "Captured header (%d bytes):\n'%s'", header_len, header ); + + if ( !status_expr ) + status_expr = new RegExpr( "^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS ); + if ( status_expr->Match( header, header_len ) < 4 ) + { + Error( "Unable to extract HTTP status from header" ); + return( -1 ); + } + http_version = status_expr->MatchString( 1 ); + status_code = atoi( status_expr->MatchString( 2 ) ); + status_mesg = status_expr->MatchString( 3 ); + + if ( status_code == 401 ) { + if ( mNeedAuth ) { + Error( "Failed authentication: " ); + return( -1 ); + } + mNeedAuth = true; + std::string Header = header; + + mAuthenticator->checkAuthResponse(Header); + if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { + Debug( 2, "Need Digest Authentication" ); + request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); + request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); + request += stringtf( "Host: %s\r\n", host.c_str()); + if ( strcmp( config.http_version, "1.0" ) == 0 ) + request += stringtf( "Connection: Keep-Alive\r\n" ); + request += mAuthenticator->getAuthHeader( "GET", path.c_str() ); + request += "\r\n"; + + Debug( 2, "New request header: %s", request.c_str() ); + return( 0 ); + } + + } else if ( status_code < 200 || status_code > 299 ) { + Error( "Invalid response status %d: %s\n%s", status_code, status_mesg, (char *)buffer ); + return( -1 ); + } + Debug( 3, "Got status '%d' (%s), http version %s", status_code, status_mesg, http_version ); + + if ( !connection_expr ) + connection_expr = new RegExpr( "Connection: ?(.+?)\r?\n", PCRE_CASELESS ); + if ( connection_expr->Match( header, header_len ) == 2 ) + { + connection_type = connection_expr->MatchString( 1 ); + Debug( 3, "Got connection '%s'", connection_type ); + } + + if ( !content_length_expr ) + content_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); + if ( content_length_expr->Match( header, header_len ) == 2 ) + { + content_length = atoi( content_length_expr->MatchString( 1 ) ); + Debug( 3, "Got content length '%d'", content_length ); + } + + if ( !content_type_expr ) + content_type_expr = new RegExpr( "Content-type: ?(.+?)(?:; ?boundary=(.+?))?\r?\n", PCRE_CASELESS ); + if ( content_type_expr->Match( header, header_len ) >= 2 ) + { + content_type = content_type_expr->MatchString( 1 ); + Debug( 3, "Got content type '%s'\n", content_type ); + if ( content_type_expr->MatchCount() > 2 ) + { + content_boundary = content_type_expr->MatchString( 2 ); + Debug( 3, "Got content boundary '%s'", content_boundary ); + } + } + + if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = JPEG; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "image/x-rgb" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = X_RGB; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = X_RGBZ; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) + { + // Image stream, so start processing + if ( !content_boundary[0] ) + { + Error( "No content boundary found in header '%s'", header ); + return( -1 ); + } + mode = MULTI_IMAGE; + state = SUBHEADER; + } + //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) + //{ + //// MPEG stream, coming soon! + //} + else + { + Error( "Unrecognised content type '%s'", content_type ); + return( -1 ); + } + buffer.consume( header_len ); + } + else + { + Debug( 3, "Unable to extract header from stream, retrying" ); + //return( -1 ); + } + break; + } + case SUBHEADER : + { + static RegExpr *subheader_expr = 0; + static RegExpr *subcontent_length_expr = 0; + static RegExpr *subcontent_type_expr = 0; + + if ( !subheader_expr ) + { + char subheader_pattern[256] = ""; + snprintf( subheader_pattern, sizeof(subheader_pattern), "^((?:\r?\n){0,2}?(?:--)?%s\r?\n.+?\r?\n\r?\n)", content_boundary ); + subheader_expr = new RegExpr( subheader_pattern, PCRE_DOTALL ); + } + if ( subheader_expr->Match( (char *)buffer, (int)buffer ) == 2 ) + { + subheader = subheader_expr->MatchString( 1 ); + subheader_len = subheader_expr->MatchLength( 1 ); + Debug( 4, "Captured subheader (%d bytes):'%s'", subheader_len, subheader ); + + if ( !subcontent_length_expr ) + subcontent_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); + if ( subcontent_length_expr->Match( subheader, subheader_len ) == 2 ) + { + content_length = atoi( subcontent_length_expr->MatchString( 1 ) ); + Debug( 3, "Got subcontent length '%d'", content_length ); + } + + if ( !subcontent_type_expr ) + subcontent_type_expr = new RegExpr( "Content-type: ?(.+?)\r?\n", PCRE_CASELESS ); + if ( subcontent_type_expr->Match( subheader, subheader_len ) == 2 ) + { + content_type = subcontent_type_expr->MatchString( 1 ); + Debug( 3, "Got subcontent type '%s'", content_type ); + } + + buffer.consume( subheader_len ); + state = CONTENT; + } + else + { + Debug( 3, "Unable to extract subheader from stream, retrying" ); + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to extract subheader data" ); + return( -1 ); + } + } + break; + } + case CONTENT : + { + + // if content_type is something like image/jpeg;size=, this will strip the ;size= + char * semicolon = strchr( (char *)content_type, ';' ); + if ( semicolon ) { + *semicolon = '\0'; + } + + if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) + { + format = JPEG; + } + else if ( !strcasecmp( content_type, "image/x-rgb" ) ) + { + format = X_RGB; + } + else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) + { + format = X_RGBZ; + } + else + { + Error( "Found unsupported content type '%s'", content_type ); + return( -1 ); + } + + if ( content_length ) + { + while ( (long)buffer.size() < content_length ) + { +Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_length ); + if ( ReadData( buffer ) < 0 ) { + Error( "Unable to read content" ); + return( -1 ); + } + } + Debug( 3, "Got end of image by length, content-length = %d", content_length ); + } + else + { + while ( !content_length ) + { + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to read content" ); + return( -1 ); + } + static RegExpr *content_expr = 0; + if ( mode == MULTI_IMAGE ) + { + if ( !content_expr ) + { + char content_pattern[256] = ""; + snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary ); + content_expr = new RegExpr( content_pattern, PCRE_DOTALL ); + } + if ( content_expr->Match( buffer, buffer.size() ) == 2 ) + { + content_length = content_expr->MatchLength( 1 ); + Debug( 3, "Got end of image by pattern, content-length = %d", content_length ); + } + } + } + } + if ( mode == SINGLE_IMAGE ) + { + state = HEADER; + Disconnect(); + } + else + { + state = SUBHEADER; + } + Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() ); + return( content_length ); + } + case HEADERCONT : + case SUBHEADERCONT : + { + // Ignore + break; + } + } + } + } + else +#endif // HAVE_LIBPCRE + { if ( method == REGEXP ) { - const char *header = 0; - int header_len = 0; - const char *http_version = 0; - int status_code = 0; - const char *status_mesg = 0; - const char *connection_type = ""; - int content_length = 0; - const char *content_type = ""; - const char *content_boundary = ""; - const char *subheader = 0; - int subheader_len = 0; - //int subcontent_length = 0; - //const char *subcontent_type = ""; - - while ( true ) - { - switch( state ) - { - case HEADER : - { - static RegExpr *header_expr = 0; - static RegExpr *status_expr = 0; - static RegExpr *connection_expr = 0; - static RegExpr *content_length_expr = 0; - static RegExpr *content_type_expr = 0; - - int buffer_len = ReadData( buffer ); - if ( buffer_len == 0 ) - { - Error( "Connection dropped by remote end" ); - return( 0 ); - } - else if ( buffer_len < 0 ) - { - Error( "Unable to read header data" ); - return( -1 ); - } - if ( !header_expr ) - header_expr = new RegExpr( "^(.+?\r?\n\r?\n)", PCRE_DOTALL ); - if ( header_expr->Match( (char*)buffer, buffer.size() ) == 2 ) - { - header = header_expr->MatchString( 1 ); - header_len = header_expr->MatchLength( 1 ); - Debug( 4, "Captured header (%d bytes):\n'%s'", header_len, header ); - - if ( !status_expr ) - status_expr = new RegExpr( "^HTTP/(1\\.[01]) +([0-9]+) +(.+?)\r?\n", PCRE_CASELESS ); - if ( status_expr->Match( header, header_len ) < 4 ) - { - Error( "Unable to extract HTTP status from header" ); - return( -1 ); - } - http_version = status_expr->MatchString( 1 ); - status_code = atoi( status_expr->MatchString( 2 ) ); - status_mesg = status_expr->MatchString( 3 ); - - if ( status_code < 200 || status_code > 299 ) - { - Error( "Invalid response status %d: %s", status_code, status_mesg ); - return( -1 ); - } - Debug( 3, "Got status '%d' (%s), http version %s", status_code, status_mesg, http_version ); - - if ( !connection_expr ) - connection_expr = new RegExpr( "Connection: ?(.+?)\r?\n", PCRE_CASELESS ); - if ( connection_expr->Match( header, header_len ) == 2 ) - { - connection_type = connection_expr->MatchString( 1 ); - Debug( 3, "Got connection '%s'", connection_type ); - } - - if ( !content_length_expr ) - content_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); - if ( content_length_expr->Match( header, header_len ) == 2 ) - { - content_length = atoi( content_length_expr->MatchString( 1 ) ); - Debug( 3, "Got content length '%d'", content_length ); - } - - if ( !content_type_expr ) - content_type_expr = new RegExpr( "Content-type: ?(.+?)(?:; ?boundary=(.+?))?\r?\n", PCRE_CASELESS ); - if ( content_type_expr->Match( header, header_len ) >= 2 ) - { - content_type = content_type_expr->MatchString( 1 ); - Debug( 3, "Got content type '%s'\n", content_type ); - if ( content_type_expr->MatchCount() > 2 ) - { - content_boundary = content_type_expr->MatchString( 2 ); - Debug( 3, "Got content boundary '%s'", content_boundary ); - } - } - - if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = JPEG; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "image/x-rgb" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = X_RGB; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = X_RGBZ; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) - { - // Image stream, so start processing - if ( !content_boundary[0] ) - { - Error( "No content boundary found in header '%s'", header ); - return( -1 ); - } - mode = MULTI_IMAGE; - state = SUBHEADER; - } - //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) - //{ - //// MPEG stream, coming soon! - //} - else - { - Error( "Unrecognised content type '%s'", content_type ); - return( -1 ); - } - buffer.consume( header_len ); - } - else - { - Debug( 3, "Unable to extract header from stream, retrying" ); - //return( -1 ); - } - break; - } - case SUBHEADER : - { - static RegExpr *subheader_expr = 0; - static RegExpr *subcontent_length_expr = 0; - static RegExpr *subcontent_type_expr = 0; - - if ( !subheader_expr ) - { - char subheader_pattern[256] = ""; - snprintf( subheader_pattern, sizeof(subheader_pattern), "^((?:\r?\n){0,2}?(?:--)?%s\r?\n.+?\r?\n\r?\n)", content_boundary ); - subheader_expr = new RegExpr( subheader_pattern, PCRE_DOTALL ); - } - if ( subheader_expr->Match( (char *)buffer, (int)buffer ) == 2 ) - { - subheader = subheader_expr->MatchString( 1 ); - subheader_len = subheader_expr->MatchLength( 1 ); - Debug( 4, "Captured subheader (%d bytes):'%s'", subheader_len, subheader ); - - if ( !subcontent_length_expr ) - subcontent_length_expr = new RegExpr( "Content-length: ?([0-9]+)\r?\n", PCRE_CASELESS ); - if ( subcontent_length_expr->Match( subheader, subheader_len ) == 2 ) - { - content_length = atoi( subcontent_length_expr->MatchString( 1 ) ); - Debug( 3, "Got subcontent length '%d'", content_length ); - } - - if ( !subcontent_type_expr ) - subcontent_type_expr = new RegExpr( "Content-type: ?(.+?)\r?\n", PCRE_CASELESS ); - if ( subcontent_type_expr->Match( subheader, subheader_len ) == 2 ) - { - content_type = subcontent_type_expr->MatchString( 1 ); - Debug( 3, "Got subcontent type '%s'", content_type ); - } - - buffer.consume( subheader_len ); - state = CONTENT; - } - else - { - Debug( 3, "Unable to extract subheader from stream, retrying" ); - int buffer_len = ReadData( buffer ); - if ( buffer_len == 0 ) - { - Error( "Connection dropped by remote end" ); - return( 0 ); - } - else if ( buffer_len < 0 ) - { - return( -1 ); - } - } - break; - } - case CONTENT : - { - if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) - { - format = JPEG; - } - else if ( !strcasecmp( content_type, "image/x-rgb" ) ) - { - format = X_RGB; - } - else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) - { - format = X_RGBZ; - } - else - { - Error( "Found unsupported content type '%s'", content_type ); - return( -1 ); - } - - if ( content_length ) - { - while ( (long)buffer.size() < content_length ) - { - int buffer_len = ReadData( buffer ); - if ( buffer_len == 0 ) - { - Error( "Connection dropped by remote end" ); - return( 0 ); - } - else if ( buffer_len < 0 ) - { - Error( "Unable to read content" ); - return( -1 ); - } - } - Debug( 3, "Got end of image by length, content-length = %d", content_length ); - } - else - { - while ( !content_length ) - { - int buffer_len = ReadData( buffer ); - if ( buffer_len == 0 ) - { - if ( mode == MULTI_IMAGE ) - { - Error( "Connection dropped by remote end" ); - return( 0 ); - } - } - else if ( buffer_len < 0 ) - { - Error( "Unable to read content" ); - return( -1 ); - } - static RegExpr *content_expr = 0; - if ( buffer_len ) - { - if ( mode == MULTI_IMAGE ) - { - if ( !content_expr ) - { - char content_pattern[256] = ""; - snprintf( content_pattern, sizeof(content_pattern), "^(.+?)(?:\r?\n)*(?:--)?%s\r?\n", content_boundary ); - content_expr = new RegExpr( content_pattern, PCRE_DOTALL ); - } - if ( content_expr->Match( buffer, buffer.size() ) == 2 ) - { - content_length = content_expr->MatchLength( 1 ); - Debug( 3, "Got end of image by pattern, content-length = %d", content_length ); - } - } - } - else - { - content_length = buffer.size(); - Debug( 3, "Got end of image by closure, content-length = %d", content_length ); - if ( mode == SINGLE_IMAGE ) - { - if ( !content_expr ) - { - content_expr = new RegExpr( "^(.+?)(?:\r?\n){1,2}?$", PCRE_DOTALL ); - } - if ( content_expr->Match( buffer, buffer.size() ) == 2 ) - { - content_length = content_expr->MatchLength( 1 ); - Debug( 3, "Trimmed end of image, new content-length = %d", content_length ); - } - } - } - } - } - if ( mode == SINGLE_IMAGE ) - { - state = HEADER; - Disconnect(); - } - else - { - state = SUBHEADER; - } - Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() ); - return( content_length ); - } - case HEADERCONT : - case SUBHEADERCONT : - { - // Ignore - break; - } - } - } + Warning( "Unable to use netcam regexps as not compiled with libpcre" ); } - else -#endif // HAVE_LIBPCRE + static const char *http_match = "HTTP/"; + static const char *connection_match = "Connection:"; + static const char *content_length_match = "Content-length:"; + static const char *content_type_match = "Content-type:"; + static const char *boundary_match = "boundary="; + static const char *authenticate_match = "WWW-Authenticate:"; + static int http_match_len = 0; + static int connection_match_len = 0; + static int content_length_match_len = 0; + static int content_type_match_len = 0; + static int boundary_match_len = 0; + static int authenticate_match_len = 0; + + if ( !http_match_len ) + http_match_len = strlen( http_match ); + if ( !connection_match_len ) + connection_match_len = strlen( connection_match ); + if ( !content_length_match_len ) + content_length_match_len = strlen( content_length_match ); + if ( !content_type_match_len ) + content_type_match_len = strlen( content_type_match ); + if ( !boundary_match_len ) + boundary_match_len = strlen( boundary_match ); + if ( !authenticate_match_len ) + authenticate_match_len = strlen( authenticate_match ); + + static int n_headers; + //static char *headers[32]; + + static int n_subheaders; + //static char *subheaders[32]; + + static char *http_header; + static char *connection_header; + static char *content_length_header; + static char *content_type_header; + static char *boundary_header; + static char *authenticate_header; + static char subcontent_length_header[32]; + static char subcontent_type_header[64]; + + static char http_version[16]; + static char status_code[16]; + static char status_mesg[256]; + static char connection_type[32]; + static int content_length; + static char content_type[32]; + static char content_boundary[64]; + static int content_boundary_len; + + while ( true ) { - if ( method == REGEXP ) + switch( state ) + { + case HEADER : { - Warning( "Unable to use netcam regexps as not compiled with libpcre" ); + n_headers = 0; + http_header = 0; + connection_header = 0; + content_length_header = 0; + content_type_header = 0; + authenticate_header = 0; + + http_version[0] = '\0'; + status_code [0]= '\0'; + status_mesg [0]= '\0'; + connection_type [0]= '\0'; + content_length = 0; + content_type[0] = '\0'; + content_boundary[0] = '\0'; + content_boundary_len = 0; } - static const char *http_match = "HTTP/"; - static const char *connection_match = "Connection:"; - static const char *content_length_match = "Content-length:"; - static const char *content_type_match = "Content-type:"; - static const char *boundary_match = "boundary="; - static int http_match_len = 0; - static int connection_match_len = 0; - static int content_length_match_len = 0; - static int content_type_match_len = 0; - static int boundary_match_len = 0; - - if ( !http_match_len ) - http_match_len = strlen( http_match ); - if ( !connection_match_len ) - connection_match_len = strlen( connection_match ); - if ( !content_length_match_len ) - content_length_match_len = strlen( content_length_match ); - if ( !content_type_match_len ) - content_type_match_len = strlen( content_type_match ); - if ( !boundary_match_len ) - boundary_match_len = strlen( boundary_match ); - - static int n_headers; - //static char *headers[32]; - - static int n_subheaders; - //static char *subheaders[32]; - - static char *http_header; - static char *connection_header; - static char *content_length_header; - static char *content_type_header; - static char *boundary_header; - static char subcontent_length_header[32]; - static char subcontent_type_header[64]; - - static char http_version[16]; - static char status_code[16]; - static char status_mesg[256]; - static char connection_type[32]; - static int content_length; - static char content_type[32]; - static char content_boundary[64]; - static int content_boundary_len; - - while ( true ) + case HEADERCONT : { - switch( state ) - { - case HEADER : - { - n_headers = 0; - http_header = 0; - connection_header = 0; - content_length_header = 0; - content_type_header = 0; - - http_version[0] = '\0'; - status_code [0]= '\0'; - status_mesg [0]= '\0'; - connection_type [0]= '\0'; - content_length = 0; - content_type[0] = '\0'; - content_boundary[0] = '\0'; - content_boundary_len = 0; - } - case HEADERCONT : - { - int buffer_len = ReadData( buffer ); - if ( buffer_len == 0 ) - { - Error( "Connection dropped by remote end" ); - return( 0 ); - } - else if ( buffer_len < 0 ) - { - Error( "Unable to read header" ); - return( -1 ); - } - - char *crlf = 0; - char *header_ptr = (char *)buffer; - int header_len = buffer.size(); - bool all_headers = false; - - while( true ) - { - int crlf_len = memspn( header_ptr, "\r\n", header_len ); - if ( n_headers ) - { - if ( (crlf_len == 2 && !strncmp( header_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len )) ) - { - *header_ptr = '\0'; - header_ptr += crlf_len; - header_len -= buffer.consume( header_ptr-(char *)buffer ); - all_headers = true; - break; - } - } - if ( crlf_len ) - { - if ( header_len == crlf_len ) - { - break; - } - else - { - *header_ptr = '\0'; - header_ptr += crlf_len; - header_len -= buffer.consume( header_ptr-(char *)buffer ); - } - } - - Debug( 6, "%s", header_ptr ); - if ( (crlf = mempbrk( header_ptr, "\r\n", header_len )) ) - { - //headers[n_headers++] = header_ptr; - n_headers++; - - if ( !http_header && (strncasecmp( header_ptr, http_match, http_match_len ) == 0) ) - { - http_header = header_ptr+http_match_len; - Debug( 6, "Got http header '%s'", header_ptr ); - } - else if ( !connection_header && (strncasecmp( header_ptr, connection_match, connection_match_len) == 0) ) - { - connection_header = header_ptr+connection_match_len; - Debug( 6, "Got connection header '%s'", header_ptr ); - } - else if ( !content_length_header && (strncasecmp( header_ptr, content_length_match, content_length_match_len) == 0) ) - { - content_length_header = header_ptr+content_length_match_len; - Debug( 6, "Got content length header '%s'", header_ptr ); - } - else if ( !content_type_header && (strncasecmp( header_ptr, content_type_match, content_type_match_len) == 0) ) - { - content_type_header = header_ptr+content_type_match_len; - Debug( 6, "Got content type header '%s'", header_ptr ); - } - else - { - Debug( 6, "Got ignored header '%s'", header_ptr ); - } - header_ptr = crlf; - header_len -= buffer.consume( header_ptr-(char *)buffer ); - } - else - { - // No end of line found - break; - } - } - - if ( all_headers ) - { - char *start_ptr, *end_ptr; - - if ( !http_header ) - { - Error( "Unable to extract HTTP status from header" ); - return( -1 ); - } - - start_ptr = http_header; - end_ptr = start_ptr+strspn( start_ptr, "10." ); - - memset( http_version, 0, sizeof(http_version) ); - strncpy( http_version, start_ptr, end_ptr-start_ptr ); - - start_ptr = end_ptr; - start_ptr += strspn( start_ptr, " " ); - end_ptr = start_ptr+strspn( start_ptr, "0123456789" ); - - memset( status_code, 0, sizeof(status_code) ); - strncpy( status_code, start_ptr, end_ptr-start_ptr ); - int status = atoi( status_code ); - - start_ptr = end_ptr; - start_ptr += strspn( start_ptr, " " ); - strcpy( status_mesg, start_ptr ); - - if ( status < 200 || status > 299 ) - { - Error( "Invalid response status %s: %s", status_code, status_mesg ); - return( -1 ); - } - Debug( 3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version ); - - if ( connection_header ) - { - memset( connection_type, 0, sizeof(connection_type) ); - start_ptr = connection_header + strspn( connection_header, " " ); - strcpy( connection_type, start_ptr ); - Debug( 3, "Got connection '%s'", connection_type ); - } - if ( content_length_header ) - { - start_ptr = content_length_header + strspn( content_length_header, " " ); - content_length = atoi( start_ptr ); - Debug( 3, "Got content length '%d'", content_length ); - } - if ( content_type_header ) - { - memset( content_type, 0, sizeof(content_type) ); - start_ptr = content_type_header + strspn( content_type_header, " " ); - if ( (end_ptr = strchr( start_ptr, ';' )) ) - { - strncpy( content_type, start_ptr, end_ptr-start_ptr ); - Debug( 3, "Got content type '%s'", content_type ); - - start_ptr = end_ptr + strspn( end_ptr, "; " ); - - if ( strncasecmp( start_ptr, boundary_match, boundary_match_len ) == 0 ) - { - start_ptr += boundary_match_len; - start_ptr += strspn( start_ptr, "-" ); - content_boundary_len = sprintf( content_boundary, "--%s", start_ptr ); - Debug( 3, "Got content boundary '%s'", content_boundary ); - } - else - { - Error( "No content boundary found in header '%s'", content_type_header ); - } - } - else - { - strcpy( content_type, start_ptr ); - Debug( 3, "Got content type '%s'", content_type ); - } - } - - if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = JPEG; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "image/x-rgb" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = X_RGB; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) - { - // Single image - mode = SINGLE_IMAGE; - format = X_RGBZ; - state = CONTENT; - } - else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) - { - // Image stream, so start processing - if ( !content_boundary[0] ) - { - Error( "No content boundary found in header '%s'", content_type_header ); - return( -1 ); - } - mode = MULTI_IMAGE; - state = SUBHEADER; - } - //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) - //{ - //// MPEG stream, coming soon! - //} - else - { - Error( "Unrecognised content type '%s'", content_type ); - return( -1 ); - } - } - else - { - Debug( 3, "Unable to extract entire header from stream, continuing" ); - state = HEADERCONT; - //return( -1 ); - } - break; - } - case SUBHEADER : - { - n_subheaders = 0; - boundary_header = 0; - subcontent_length_header[0] = '\0'; - subcontent_type_header[0] = '\0'; - content_length = 0; - content_type[0] = '\0'; - } - case SUBHEADERCONT : - { - char *crlf = 0; - char *subheader_ptr = (char *)buffer; - int subheader_len = buffer.size(); - bool all_headers = false; - - while( true ) - { - int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len ); - if ( n_subheaders ) - { - if ( (crlf_len == 2 && !strncmp( subheader_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) - { - *subheader_ptr = '\0'; - subheader_ptr += crlf_len; - subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); - all_headers = true; - break; - } - } - if ( crlf_len ) - { - if ( subheader_len == crlf_len ) - { - break; - } - else - { - *subheader_ptr = '\0'; - subheader_ptr += crlf_len; - subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); - } - } - - Debug( 6, "%d: %s", subheader_len, subheader_ptr ); - - if ( (crlf = mempbrk( subheader_ptr, "\r\n", subheader_len )) ) - { - //subheaders[n_subheaders++] = subheader_ptr; - n_subheaders++; - - if ( !boundary_header && (strncasecmp( subheader_ptr, content_boundary, content_boundary_len ) == 0) ) - { - boundary_header = subheader_ptr; - Debug( 4, "Got boundary subheader '%s'", subheader_ptr ); - } - else if ( !subcontent_length_header[0] && (strncasecmp( subheader_ptr, content_length_match, content_length_match_len) == 0) ) - { - strncpy( subcontent_length_header, subheader_ptr+content_length_match_len, sizeof(subcontent_length_header) ); - *(subcontent_length_header+strcspn( subcontent_length_header, "\r\n" )) = '\0'; - Debug( 4, "Got content length subheader '%s'", subcontent_length_header ); - } - else if ( !subcontent_type_header[0] && (strncasecmp( subheader_ptr, content_type_match, content_type_match_len) == 0) ) - { - strncpy( subcontent_type_header, subheader_ptr+content_type_match_len, sizeof(subcontent_type_header) ); - *(subcontent_type_header+strcspn( subcontent_type_header, "\r\n" )) = '\0'; - Debug( 4, "Got content type subheader '%s'", subcontent_type_header ); - } - else - { - Debug( 6, "Got ignored subheader '%s' found", subheader_ptr ); - } - subheader_ptr = crlf; - subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); - } - else - { - // No line end found - break; - } - } - - if ( all_headers && boundary_header ) - { - char *start_ptr/*, *end_ptr*/; - - Debug( 3, "Got boundary '%s'", boundary_header ); - - if ( subcontent_length_header[0] ) - { - start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " ); - content_length = atoi( start_ptr ); - Debug( 3, "Got subcontent length '%d'", content_length ); - } - if ( subcontent_type_header[0] ) - { - memset( content_type, 0, sizeof(content_type) ); - start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " ); - strcpy( content_type, start_ptr ); - Debug( 3, "Got subcontent type '%s'", content_type ); - } - state = CONTENT; - } - else - { - Debug( 3, "Unable to extract subheader from stream, retrying" ); - int buffer_len = ReadData( buffer ); - if ( buffer_len == 0 ) - { - Error( "Connection dropped by remote end" ); - return( 0 ); - } - else if ( buffer_len < 0 ) - { - Error( "Unable to read subheader" ); - return( -1 ); - } - state = SUBHEADERCONT; - } - break; - } - case CONTENT : - { - if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) - { - format = JPEG; - } - else if ( !strcasecmp( content_type, "image/x-rgb" ) ) - { - format = X_RGB; - } - else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) - { - format = X_RGBZ; - } - else - { - Error( "Found unsupported content type '%s'", content_type ); - return( -1 ); - } - - if ( format == JPEG && buffer.size() >= 2 ) - { - if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) - { - Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); - return( -1 ); - } - } - - if ( content_length ) - { - while ( (long)buffer.size() < content_length ) - { - //int buffer_len = ReadData( buffer, content_length-buffer.size() ); - int buffer_len = ReadData( buffer ); - if ( buffer_len == 0 ) - { - Error( "Connection dropped by remote end" ); - return( 0 ); - } - else if ( buffer_len < 0 ) - { - Error( "Unable to read content" ); - return( -1 ); - } - } - Debug( 3, "Got end of image by length, content-length = %d", content_length ); - } - else - { - int content_pos = 0; - while ( !content_length ) - { - int buffer_len = ReadData( buffer ); - if ( buffer_len == 0 ) - { - if ( mode == MULTI_IMAGE ) - { - Error( "Connection dropped by remote end" ); - return( 0 ); - } - } - else if ( buffer_len < 0 ) - { - Error( "Unable to read content" ); - return( -1 ); - } - int buffer_size = buffer.size(); - if ( buffer_len ) - { - if ( mode == MULTI_IMAGE ) - { - while ( char *start_ptr = (char *)memstr( (char *)buffer+content_pos, "\r\n--", buffer_size-content_pos ) ) - { - content_length = start_ptr - (char *)buffer; - Debug( 3, "Got end of image by pattern (crlf--), content-length = %d", content_length ); - break; - } - } - } - else - { - content_length = buffer_size; - Debug( 3, "Got end of image by closure, content-length = %d", content_length ); - if ( mode == SINGLE_IMAGE ) - { - char *end_ptr = (char *)buffer+buffer_size; - - while( *end_ptr == '\r' || *end_ptr == '\n' ) - { - content_length--; - end_ptr--; - } - - if ( end_ptr != ((char *)buffer+buffer_size) ) - { - Debug( 3, "Trimmed end of image, new content-length = %d", content_length ); - } - } - } - } - } - if ( mode == SINGLE_IMAGE ) - { - state = HEADER; - Disconnect(); - } - else - { - state = SUBHEADER; - } - - if ( format == JPEG && buffer.size() >= 2 ) - { - if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) - { - Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); - return( -1 ); - } - } - - Debug( 3, "Returning %d (%d) bytes of captured content", content_length, buffer.size() ); - return( content_length ); - } + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to read header" ); + return( -1 ); } + + char *crlf = 0; + char *header_ptr = (char *)buffer; + int header_len = buffer.size(); + bool all_headers = false; + + while( true ) + { + int crlf_len = memspn( header_ptr, "\r\n", header_len ); + if ( n_headers ) + { + if ( (crlf_len == 2 && !strncmp( header_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( header_ptr, "\r\n\r\n", crlf_len )) ) + { + *header_ptr = '\0'; + header_ptr += crlf_len; + header_len -= buffer.consume( header_ptr-(char *)buffer ); + all_headers = true; + break; + } + } + if ( crlf_len ) + { + if ( header_len == crlf_len ) + { + break; + } + else + { + *header_ptr = '\0'; + header_ptr += crlf_len; + header_len -= buffer.consume( header_ptr-(char *)buffer ); + } + } + + Debug( 6, "%s", header_ptr ); + if ( (crlf = mempbrk( header_ptr, "\r\n", header_len )) ) + { + //headers[n_headers++] = header_ptr; + n_headers++; + + if ( !http_header && (strncasecmp( header_ptr, http_match, http_match_len ) == 0) ) + { + http_header = header_ptr+http_match_len; + Debug( 6, "Got http header '%s'", header_ptr ); + } + else if ( !connection_header && (strncasecmp( header_ptr, connection_match, connection_match_len) == 0) ) + { + connection_header = header_ptr+connection_match_len; + Debug( 6, "Got connection header '%s'", header_ptr ); + } + else if ( !content_length_header && (strncasecmp( header_ptr, content_length_match, content_length_match_len) == 0) ) + { + content_length_header = header_ptr+content_length_match_len; + Debug( 6, "Got content length header '%s'", header_ptr ); + } + + else if ( !authenticate_header && (strncasecmp( header_ptr, authenticate_match, authenticate_match_len) == 0) ) + { + authenticate_header = header_ptr; + Debug( 6, "Got authenticate header '%s'", header_ptr ); + } + else if ( !content_type_header && (strncasecmp( header_ptr, content_type_match, content_type_match_len) == 0) ) + { + content_type_header = header_ptr+content_type_match_len; + Debug( 6, "Got content type header '%s'", header_ptr ); + } + else + { + Debug( 6, "Got ignored header '%s'", header_ptr ); + } + header_ptr = crlf; + header_len -= buffer.consume( header_ptr-(char *)buffer ); + } + else + { + // No end of line found + break; + } + } + + if ( all_headers ) + { + char *start_ptr, *end_ptr; + + if ( !http_header ) + { + Error( "Unable to extract HTTP status from header" ); + return( -1 ); + } + + start_ptr = http_header; + end_ptr = start_ptr+strspn( start_ptr, "10." ); + + memset( http_version, 0, sizeof(http_version) ); + strncpy( http_version, start_ptr, end_ptr-start_ptr ); + + start_ptr = end_ptr; + start_ptr += strspn( start_ptr, " " ); + end_ptr = start_ptr+strspn( start_ptr, "0123456789" ); + + memset( status_code, 0, sizeof(status_code) ); + strncpy( status_code, start_ptr, end_ptr-start_ptr ); + int status = atoi( status_code ); + + start_ptr = end_ptr; + start_ptr += strspn( start_ptr, " " ); + strcpy( status_mesg, start_ptr ); + + if ( status == 401 ) { + if ( mNeedAuth ) { + Error( "Failed authentication: " ); + return( -1 ); + } + if ( ! authenticate_header ) { + Error( "Failed authentication, but don't have an authentication header: " ); + return( -1 ); + } + mNeedAuth = true; + std::string Header = authenticate_header; + Debug(2, "Checking for digest auth in %s", authenticate_header ); + + mAuthenticator->checkAuthResponse(Header); + if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { + Debug( 2, "Need Digest Authentication" ); + request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); + request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); + request += stringtf( "Host: %s\r\n", host.c_str()); + if ( strcmp( config.http_version, "1.0" ) == 0 ) + request += stringtf( "Connection: Keep-Alive\r\n" ); + request += mAuthenticator->getAuthHeader( "GET", path.c_str() ); + request += "\r\n"; + + Debug( 2, "New request header: %s", request.c_str() ); + return( 0 ); + } else { + Debug( 2, "Need some other kind of Authentication" ); + } + } else if ( status < 200 || status > 299 ) + { + Error( "Invalid response status %s: %s", status_code, status_mesg ); + return( -1 ); + } + Debug( 3, "Got status '%d' (%s), http version %s", status, status_mesg, http_version ); + + if ( connection_header ) + { + memset( connection_type, 0, sizeof(connection_type) ); + start_ptr = connection_header + strspn( connection_header, " " ); + strcpy( connection_type, start_ptr ); + Debug( 3, "Got connection '%s'", connection_type ); + } + if ( content_length_header ) + { + start_ptr = content_length_header + strspn( content_length_header, " " ); + content_length = atoi( start_ptr ); + Debug( 3, "Got content length '%d'", content_length ); + } + if ( content_type_header ) + { + memset( content_type, 0, sizeof(content_type) ); + start_ptr = content_type_header + strspn( content_type_header, " " ); + if ( (end_ptr = strchr( start_ptr, ';' )) ) + { + strncpy( content_type, start_ptr, end_ptr-start_ptr ); + Debug( 3, "Got content type '%s'", content_type ); + + start_ptr = end_ptr + strspn( end_ptr, "; " ); + + if ( strncasecmp( start_ptr, boundary_match, boundary_match_len ) == 0 ) + { + start_ptr += boundary_match_len; + start_ptr += strspn( start_ptr, "-" ); + content_boundary_len = sprintf( content_boundary, "--%s", start_ptr ); + Debug( 3, "Got content boundary '%s'", content_boundary ); + } + else + { + Error( "No content boundary found in header '%s'", content_type_header ); + } + } + else + { + strcpy( content_type, start_ptr ); + Debug( 3, "Got content type '%s'", content_type ); + } + } + + if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = JPEG; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "image/x-rgb" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = X_RGB; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) + { + // Single image + mode = SINGLE_IMAGE; + format = X_RGBZ; + state = CONTENT; + } + else if ( !strcasecmp( content_type, "multipart/x-mixed-replace" ) ) + { + // Image stream, so start processing + if ( !content_boundary[0] ) + { + Error( "No content boundary found in header '%s'", content_type_header ); + return( -1 ); + } + mode = MULTI_IMAGE; + state = SUBHEADER; + } + //else if ( !strcasecmp( content_type, "video/mpeg" ) || !strcasecmp( content_type, "video/mpg" ) ) + //{ + //// MPEG stream, coming soon! + //} + else + { + Error( "Unrecognised content type '%s'", content_type ); + return( -1 ); + } + } + else + { + Debug( 3, "Unable to extract entire header from stream, continuing" ); + state = HEADERCONT; + //return( -1 ); + } + break; } + case SUBHEADER : + { + n_subheaders = 0; + boundary_header = 0; + subcontent_length_header[0] = '\0'; + subcontent_type_header[0] = '\0'; + content_length = 0; + content_type[0] = '\0'; + } + case SUBHEADERCONT : + { + char *crlf = 0; + char *subheader_ptr = (char *)buffer; + int subheader_len = buffer.size(); + bool all_headers = false; + + while( true ) + { + int crlf_len = memspn( subheader_ptr, "\r\n", subheader_len ); + if ( n_subheaders ) + { + if ( (crlf_len == 2 && !strncmp( subheader_ptr, "\n\n", crlf_len )) || (crlf_len == 4 && !strncmp( subheader_ptr, "\r\n\r\n", crlf_len )) ) + { + *subheader_ptr = '\0'; + subheader_ptr += crlf_len; + subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); + all_headers = true; + break; + } + } + if ( crlf_len ) + { + if ( subheader_len == crlf_len ) + { + break; + } + else + { + *subheader_ptr = '\0'; + subheader_ptr += crlf_len; + subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); + } + } + + Debug( 6, "%d: %s", subheader_len, subheader_ptr ); + + if ( (crlf = mempbrk( subheader_ptr, "\r\n", subheader_len )) ) + { + //subheaders[n_subheaders++] = subheader_ptr; + n_subheaders++; + + if ( !boundary_header && (strncasecmp( subheader_ptr, content_boundary, content_boundary_len ) == 0) ) + { + boundary_header = subheader_ptr; + Debug( 4, "Got boundary subheader '%s'", subheader_ptr ); + } + else if ( !subcontent_length_header[0] && (strncasecmp( subheader_ptr, content_length_match, content_length_match_len) == 0) ) + { + strncpy( subcontent_length_header, subheader_ptr+content_length_match_len, sizeof(subcontent_length_header) ); + *(subcontent_length_header+strcspn( subcontent_length_header, "\r\n" )) = '\0'; + Debug( 4, "Got content length subheader '%s'", subcontent_length_header ); + } + else if ( !subcontent_type_header[0] && (strncasecmp( subheader_ptr, content_type_match, content_type_match_len) == 0) ) + { + strncpy( subcontent_type_header, subheader_ptr+content_type_match_len, sizeof(subcontent_type_header) ); + *(subcontent_type_header+strcspn( subcontent_type_header, "\r\n" )) = '\0'; + Debug( 4, "Got content type subheader '%s'", subcontent_type_header ); + } + else + { + Debug( 6, "Got ignored subheader '%s' found", subheader_ptr ); + } + subheader_ptr = crlf; + subheader_len -= buffer.consume( subheader_ptr-(char *)buffer ); + } + else + { + // No line end found + break; + } + } + + if ( all_headers && boundary_header ) + { + char *start_ptr/*, *end_ptr*/; + + Debug( 3, "Got boundary '%s'", boundary_header ); + + if ( subcontent_length_header[0] ) + { + start_ptr = subcontent_length_header + strspn( subcontent_length_header, " " ); + content_length = atoi( start_ptr ); + Debug( 3, "Got subcontent length '%d'", content_length ); + } + if ( subcontent_type_header[0] ) + { + memset( content_type, 0, sizeof(content_type) ); + start_ptr = subcontent_type_header + strspn( subcontent_type_header, " " ); + strcpy( content_type, start_ptr ); + Debug( 3, "Got subcontent type '%s'", content_type ); + } + state = CONTENT; + } + else + { + Debug( 3, "Unable to extract subheader from stream, retrying" ); + while ( ! ( buffer_len = ReadData( buffer ) ) ) { + } + if ( buffer_len < 0 ) { + Error( "Unable to read subheader" ); + return( -1 ); + } + state = SUBHEADERCONT; + } + break; + } + case CONTENT : + { + + // if content_type is something like image/jpeg;size=, this will strip the ;size= + char * semicolon = strchr( content_type, ';' ); + if ( semicolon ) { + *semicolon = '\0'; + } + + if ( !strcasecmp( content_type, "image/jpeg" ) || !strcasecmp( content_type, "image/jpg" ) ) + { + format = JPEG; + } + else if ( !strcasecmp( content_type, "image/x-rgb" ) ) + { + format = X_RGB; + } + else if ( !strcasecmp( content_type, "image/x-rgbz" ) ) + { + format = X_RGBZ; + } + else + { + Error( "Found unsupported content type '%s'", content_type ); + return( -1 ); + } + + if ( format == JPEG && buffer.size() >= 2 ) + { + if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) + { + Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); + return( -1 ); + } + } + + if ( content_length ) + { + while ( (long)buffer.size() < content_length ) + { + //int buffer_len = ReadData( buffer, content_length-buffer.size() ); + if ( ReadData( buffer ) < 0 ) { + Error( "Unable to read content" ); + return( -1 ); + } + } + Debug( 3, "Got end of image by length, content-length = %d", content_length ); + } + else + { + int content_pos = 0; + while ( !content_length ) + { + buffer_len = ReadData( buffer ); + if ( buffer_len < 0 ) + { + Error( "Unable to read content" ); + return( -1 ); + } + int buffer_size = buffer.size(); + if ( buffer_len ) + { + if ( mode == MULTI_IMAGE ) + { + while ( char *start_ptr = (char *)memstr( (char *)buffer+content_pos, "\r\n--", buffer_size-content_pos ) ) + { + content_length = start_ptr - (char *)buffer; + Debug( 3, "Got end of image by pattern (crlf--), content-length = %d", content_length ); + break; + } + } + } + else + { + content_length = buffer_size; + Debug( 3, "Got end of image by closure, content-length = %d", content_length ); + if ( mode == SINGLE_IMAGE ) + { + char *end_ptr = (char *)buffer+buffer_size; + + while( *end_ptr == '\r' || *end_ptr == '\n' ) + { + content_length--; + end_ptr--; + } + + if ( end_ptr != ((char *)buffer+buffer_size) ) + { + Debug( 3, "Trimmed end of image, new content-length = %d", content_length ); + } + } + } + } + } + if ( mode == SINGLE_IMAGE ) + { + state = HEADER; + Disconnect(); + } + else + { + state = SUBHEADER; + } + + if ( format == JPEG && buffer.size() >= 2 ) + { + if ( buffer[0] != 0xff || buffer[1] != 0xd8 ) + { + Error( "Found bogus jpeg header '%02x%02x'", buffer[0], buffer[1] ); + return( -1 ); + } + } + + Debug( 3, "Returning %d bytes, buffer size: (%d) bytes of captured content", content_length, buffer.size() ); + return( content_length ); + } + } } - return( 0 ); + } + return( 0 ); } int RemoteCameraHttp::PreCapture() { + if ( sd < 0 ) + { + Connect(); if ( sd < 0 ) { - Connect(); - if ( sd < 0 ) - { - Error( "Unable to connect to camera" ); - return( -1 ); - } - mode = SINGLE_IMAGE; - buffer.clear(); + Error( "Unable to connect to camera" ); + return( -1 ); } - if ( mode == SINGLE_IMAGE ) + mode = SINGLE_IMAGE; + buffer.clear(); + } + if ( mode == SINGLE_IMAGE ) + { + if ( SendRequest() < 0 ) { - if ( SendRequest() < 0 ) - { - Error( "Unable to send request" ); - Disconnect(); - return( -1 ); - } + Error( "Unable to send request" ); + Disconnect(); + return( -1 ); } - return( 0 ); + } + return( 0 ); } int RemoteCameraHttp::Capture( Image &image ) { - int content_length = GetResponse(); - if ( content_length == 0 ) + int content_length = GetResponse(); + if ( content_length == 0 ) + { + Warning( "Unable to capture image, retrying" ); + return( 1 ); + } + if ( content_length < 0 ) + { + Error( "Unable to get response, disconnecting" ); + Disconnect(); + return( -1 ); + } + switch( format ) + { + case JPEG : { - Warning( "Unable to capture image, retrying" ); - return( 1 ); - } - if ( content_length < 0 ) - { - Error( "Unable to get response" ); + if ( !image.DecodeJpeg( buffer.extract( content_length ), content_length, colours, subpixelorder ) ) + { + Error( "Unable to decode jpeg" ); Disconnect(); return( -1 ); + } + break; } - switch( format ) + case X_RGB : { - case JPEG : - { - if ( !image.DecodeJpeg( buffer.extract( content_length ), content_length, colours, subpixelorder ) ) - { - Error( "Unable to decode jpeg" ); - Disconnect(); - return( -1 ); - } - break; - } - case X_RGB : - { - if ( content_length != (long)image.Size() ) - { - Error( "Image length mismatch, expected %d bytes, content length was %d", image.Size(), content_length ); - Disconnect(); - return( -1 ); - } - image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); - break; - } - case X_RGBZ : - { - if ( !image.Unzip( buffer.extract( content_length ), content_length ) ) - { - Error( "Unable to unzip RGB image" ); - Disconnect(); - return( -1 ); - } - image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); - break; - } - default : - { - Error( "Unexpected image format encountered" ); - Disconnect(); - return( -1 ); - } + if ( content_length != (long)image.Size() ) + { + Error( "Image length mismatch, expected %d bytes, content length was %d", image.Size(), content_length ); + Disconnect(); + return( -1 ); + } + image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); + break; } - return( 0 ); + case X_RGBZ : + { + if ( !image.Unzip( buffer.extract( content_length ), content_length ) ) + { + Error( "Unable to unzip RGB image" ); + Disconnect(); + return( -1 ); + } + image.Assign( width, height, colours, subpixelorder, buffer, imagesize ); + break; + } + default : + { + Error( "Unexpected image format encountered" ); + Disconnect(); + return( -1 ); + } + } + return( 0 ); } int RemoteCameraHttp::PostCapture() { - return( 0 ); + return( 0 ); } diff --git a/src/zm_remote_camera_http.h b/src/zm_remote_camera_http.h index 5fe065cf9..bfadecce5 100644 --- a/src/zm_remote_camera_http.h +++ b/src/zm_remote_camera_http.h @@ -33,31 +33,31 @@ class RemoteCameraHttp : public RemoteCamera { protected: - std::string request; - struct timeval timeout; - //struct hostent *hp; - //struct sockaddr_in sa; - int sd; - Buffer buffer; - enum { SINGLE_IMAGE, MULTI_IMAGE } mode; - enum { UNDEF, JPEG, X_RGB, X_RGBZ } format; - enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state; - enum { SIMPLE, REGEXP } method; + std::string request; + struct timeval timeout; + //struct hostent *hp; + //struct sockaddr_in sa; + int sd; + Buffer buffer; + enum { SINGLE_IMAGE, MULTI_IMAGE } mode; + enum { UNDEF, JPEG, X_RGB, X_RGBZ } format; + enum { HEADER, HEADERCONT, SUBHEADER, SUBHEADERCONT, CONTENT } state; + enum { SIMPLE, REGEXP } method; public: - RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~RemoteCameraHttp(); + RemoteCameraHttp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); + ~RemoteCameraHttp(); - void Initialise(); - void Terminate() { Disconnect(); } - int Connect(); - int Disconnect(); - int SendRequest(); - int ReadData( Buffer &buffer, int bytes_expected=0 ); - int GetResponse(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); + void Initialise(); + void Terminate() { Disconnect(); } + int Connect(); + int Disconnect(); + int SendRequest(); + int ReadData( Buffer &buffer, int bytes_expected=0 ); + int GetResponse(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); }; #endif // ZM_REMOTE_CAMERA_HTTP_H diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index aeec18379..42df5c9ac 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -28,328 +28,355 @@ #include #include -RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : - RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ), - rtspThread( 0 ) -{ - if ( p_method == "rtpUni" ) - method = RtspThread::RTP_UNICAST; - else if ( p_method == "rtpMulti" ) - method = RtspThread::RTP_MULTICAST; - else if ( p_method == "rtpRtsp" ) - method = RtspThread::RTP_RTSP; - else if ( p_method == "rtpRtspHttp" ) - method = RtspThread::RTP_RTSP_HTTP; - else - Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id ); +RemoteCameraRtsp::RemoteCameraRtsp( int p_id, const std::string &p_method, const std::string &p_host, const std::string &p_port, const std::string &p_path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) : + RemoteCamera( p_id, "rtsp", p_host, p_port, p_path, p_width, p_height, p_colours, p_brightness, p_contrast, p_hue, p_colour, p_capture ), + rtsp_describe( p_rtsp_describe ), + rtspThread( 0 ) - if ( capture ) - { - Initialise(); - } - - mFormatContext = NULL; - mVideoStreamId = -1; - mCodecContext = NULL; - mCodec = NULL; - mRawFrame = NULL; - mFrame = NULL; - frameCount = 0; - -#if HAVE_LIBSWSCALE - mConvertContext = NULL; +{ + if ( p_method == "rtpUni" ) + method = RtspThread::RTP_UNICAST; + else if ( p_method == "rtpMulti" ) + method = RtspThread::RTP_MULTICAST; + else if ( p_method == "rtpRtsp" ) + method = RtspThread::RTP_RTSP; + else if ( p_method == "rtpRtspHttp" ) + method = RtspThread::RTP_RTSP_HTTP; + else + Fatal( "Unrecognised method '%s' when creating RTSP camera %d", p_method.c_str(), id ); + + if ( capture ) + { + Initialise(); + } + + mFormatContext = NULL; + mVideoStreamId = -1; + mCodecContext = NULL; + mCodec = NULL; + mRawFrame = NULL; + mFrame = NULL; + frameCount = 0; + +#if HAVE_LIBSWSCALE + mConvertContext = NULL; #endif - /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ - if(colours == ZM_COLOUR_RGB32) { - subpixelorder = ZM_SUBPIX_ORDER_RGBA; - imagePixFormat = PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { - subpixelorder = ZM_SUBPIX_ORDER_RGB; - imagePixFormat = PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { - subpixelorder = ZM_SUBPIX_ORDER_NONE; - imagePixFormat = PIX_FMT_GRAY8; - } else { - Panic("Unexpected colours: %d",colours); - } - + /* Has to be located inside the constructor so other components such as zma will receive correct colours and subpixel order */ + if(colours == ZM_COLOUR_RGB32) { + subpixelorder = ZM_SUBPIX_ORDER_RGBA; + imagePixFormat = AV_PIX_FMT_RGBA; + } else if(colours == ZM_COLOUR_RGB24) { + subpixelorder = ZM_SUBPIX_ORDER_RGB; + imagePixFormat = AV_PIX_FMT_RGB24; + } else if(colours == ZM_COLOUR_GRAY8) { + subpixelorder = ZM_SUBPIX_ORDER_NONE; + imagePixFormat = AV_PIX_FMT_GRAY8; + } else { + Panic("Unexpected colours: %d",colours); + } + } RemoteCameraRtsp::~RemoteCameraRtsp() { - av_freep( &mFrame ); - av_freep( &mRawFrame ); - +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + av_frame_free( &mFrame ); + av_frame_free( &mRawFrame ); +#else + av_freep( &mFrame ); + av_freep( &mRawFrame ); +#endif + #if HAVE_LIBSWSCALE - if ( mConvertContext ) - { - sws_freeContext( mConvertContext ); - mConvertContext = NULL; - } + if ( mConvertContext ) + { + sws_freeContext( mConvertContext ); + mConvertContext = NULL; + } #endif - if ( mCodecContext ) - { - avcodec_close( mCodecContext ); - mCodecContext = NULL; // Freed by av_close_input_file - } + if ( mCodecContext ) + { + avcodec_close( mCodecContext ); + mCodecContext = NULL; // Freed by avformat_free_context in the destructor of RtspThread class + } - if ( capture ) - { - Terminate(); - } + if ( capture ) + { + Terminate(); + } } void RemoteCameraRtsp::Initialise() { - RemoteCamera::Initialise(); + RemoteCamera::Initialise(); - int max_size = width*height*colours; + int max_size = width*height*colours; - buffer.size( max_size ); + buffer.size( max_size ); - if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); - else - av_log_set_level( AV_LOG_QUIET ); + if ( logDebugging() ) + av_log_set_level( AV_LOG_DEBUG ); + else + av_log_set_level( AV_LOG_QUIET ); - av_register_all(); + av_register_all(); - Connect(); + Connect(); } void RemoteCameraRtsp::Terminate() { - Disconnect(); + Disconnect(); } int RemoteCameraRtsp::Connect() { - rtspThread = new RtspThread( id, method, protocol, host, port, path, auth ); + rtspThread = new RtspThread( id, method, protocol, host, port, path, auth, rtsp_describe ); - rtspThread->start(); + rtspThread->start(); - return( 0 ); + return( 0 ); } int RemoteCameraRtsp::Disconnect() { - if ( rtspThread ) - { - rtspThread->stop(); - rtspThread->join(); - delete rtspThread; - rtspThread = 0; - } - return( 0 ); + if ( rtspThread ) + { + rtspThread->stop(); + rtspThread->join(); + delete rtspThread; + rtspThread = 0; + } + return( 0 ); } int RemoteCameraRtsp::PrimeCapture() { - Debug( 2, "Waiting for sources" ); - for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ ) + Debug( 2, "Waiting for sources" ); + for ( int i = 0; i < 100 && !rtspThread->hasSources(); i++ ) + { + usleep( 100000 ); + } + if ( !rtspThread->hasSources() ) + Fatal( "No RTSP sources" ); + + Debug( 2, "Got sources" ); + + mFormatContext = rtspThread->getFormatContext(); + + // Find first video stream present + mVideoStreamId = -1; + + for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) + if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) +#else + if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) +#endif { - usleep( 100000 ); + mVideoStreamId = i; + break; } - if ( !rtspThread->hasSources() ) - Fatal( "No RTSP sources" ); + if ( mVideoStreamId == -1 ) + Fatal( "Unable to locate video stream" ); - Debug( 2, "Got sources" ); + // Get a pointer to the codec context for the video stream + mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; - mFormatContext = rtspThread->getFormatContext(); + // Find the decoder for the video stream + mCodec = avcodec_find_decoder( mCodecContext->codec_id ); + if ( mCodec == NULL ) + Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id ); - // Find first video stream present - mVideoStreamId = -1; - - for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) - if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) + // Open codec +#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0) + if ( avcodec_open( mCodecContext, mCodec ) < 0 ) #else - if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) + if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) #endif - { - mVideoStreamId = i; - break; - } - if ( mVideoStreamId == -1 ) - Fatal( "Unable to locate video stream" ); + Panic( "Can't open codec" ); - // Get a pointer to the codec context for the video stream - mCodecContext = mFormatContext->streams[mVideoStreamId]->codec; - - // Find the decoder for the video stream - mCodec = avcodec_find_decoder( mCodecContext->codec_id ); - if ( mCodec == NULL ) - Panic( "Unable to locate codec %d decoder", mCodecContext->codec_id ); - - // Open codec -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 7, 0) - if ( avcodec_open( mCodecContext, mCodec ) < 0 ) + // Allocate space for the native video frame +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + mRawFrame = av_frame_alloc(); #else - if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 ) + mRawFrame = avcodec_alloc_frame(); #endif - Panic( "Can't open codec" ); - // Allocate space for the native video frame - mRawFrame = avcodec_alloc_frame(); + // Allocate space for the converted video frame +#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) + mFrame = av_frame_alloc(); +#else + mFrame = avcodec_alloc_frame(); +#endif - // Allocate space for the converted video frame - mFrame = avcodec_alloc_frame(); - - if(mRawFrame == NULL || mFrame == NULL) - Fatal( "Unable to allocate frame(s)"); - - int pSize = avpicture_get_size( imagePixFormat, width, height ); - if( (unsigned int)pSize != imagesize) { - Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); - } -/* + if(mRawFrame == NULL || mFrame == NULL) + Fatal( "Unable to allocate frame(s)"); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + int pSize = av_image_get_buffer_size( imagePixFormat, width, height, 1 ); +#else + int pSize = avpicture_get_size( imagePixFormat, width, height ); +#endif + + if( (unsigned int)pSize != imagesize) { + Fatal("Image size mismatch. Required: %d Available: %d",pSize,imagesize); + } +/* #if HAVE_LIBSWSCALE - if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { - Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); - } + if(!sws_isSupportedInput(mCodecContext->pix_fmt)) { + Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff)); + } - if(!sws_isSupportedOutput(imagePixFormat)) { - Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); - } - + if(!sws_isSupportedOutput(imagePixFormat)) { + Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff)); + } + #else // HAVE_LIBSWSCALE - Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); + Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); #endif // HAVE_LIBSWSCALE */ - return( 0 ); + return( 0 ); } int RemoteCameraRtsp::PreCapture() { - if ( !rtspThread->isRunning() ) - return( -1 ); - if ( !rtspThread->hasSources() ) - { - Error( "Cannot precapture, no RTP sources" ); - return( -1 ); - } - return( 0 ); + if ( !rtspThread->isRunning() ) + return( -1 ); + if ( !rtspThread->hasSources() ) + { + Error( "Cannot precapture, no RTP sources" ); + return( -1 ); + } + return( 0 ); } int RemoteCameraRtsp::Capture( Image &image ) { - AVPacket packet; - uint8_t* directbuffer; - int frameComplete = false; - - /* Request a writeable buffer of the target image */ - directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); - if(directbuffer == NULL) { - Error("Failed requesting writeable buffer for the captured image."); - return (-1); - } - - while ( true ) + AVPacket packet; + uint8_t* directbuffer; + int frameComplete = false; + + /* Request a writeable buffer of the target image */ + directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); + if(directbuffer == NULL) { + Error("Failed requesting writeable buffer for the captured image."); + return (-1); + } + + while ( true ) + { + buffer.clear(); + if ( !rtspThread->isRunning() ) + return (-1); + + if ( rtspThread->getFrame( buffer ) ) { - buffer.clear(); - if ( !rtspThread->isRunning() ) - return (-1); + Debug( 3, "Read frame %d bytes", buffer.size() ); + Debug( 4, "Address %p", buffer.head() ); + Hexdump( 4, buffer.head(), 16 ); - if ( rtspThread->getFrame( buffer ) ) + if ( !buffer.size() ) + return( -1 ); + + if(mCodecContext->codec_id == AV_CODEC_ID_H264) + { + // SPS and PPS frames should be saved and appended to IDR frames + int nalType = (buffer.head()[3] & 0x1f); + + // SPS + if(nalType == 7) { - Debug( 3, "Read frame %d bytes", buffer.size() ); - Debug( 4, "Address %p", buffer.head() ); - Hexdump( 4, buffer.head(), 16 ); + lastSps = buffer; + continue; + } + // PPS + else if(nalType == 8) + { + lastPps = buffer; + continue; + } + // IDR + else if(nalType == 5) + { + buffer += lastSps; + buffer += lastPps; + } + } - if ( !buffer.size() ) - return( -1 ); - - if(mCodecContext->codec_id == AV_CODEC_ID_H264) - { - // SPS and PPS frames should be saved and appended to IDR frames - int nalType = (buffer.head()[3] & 0x1f); - - // SPS - if(nalType == 7) - { - lastSps = buffer; - continue; - } - // PPS - else if(nalType == 8) - { - lastPps = buffer; - continue; - } - // IDR - else if(nalType == 5) - { - buffer += lastSps; - buffer += lastPps; - } - } - - av_init_packet( &packet ); - - while ( !frameComplete && buffer.size() > 0 ) - { - packet.data = buffer.head(); - packet.size = buffer.size(); -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 25, 0) - int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ); + av_init_packet( &packet ); + + while ( !frameComplete && buffer.size() > 0 ) + { + packet.data = buffer.head(); + packet.size = buffer.size(); +#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) + int len = avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ); #else - int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ); + int len = avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ); #endif - if ( len < 0 ) - { - Error( "Error while decoding frame %d", frameCount ); - Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() ); - buffer.clear(); - continue; - } - Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() ); - //if ( buffer.size() < 400 ) - //Hexdump( 0, buffer.head(), buffer.size() ); - - buffer -= len; + if ( len < 0 ) + { + Error( "Error while decoding frame %d", frameCount ); + Hexdump( Logger::ERROR, buffer.head(), buffer.size()>256?256:buffer.size() ); + buffer.clear(); + continue; + } + Debug( 2, "Frame: %d - %d/%d", frameCount, len, buffer.size() ); + //if ( buffer.size() < 400 ) + //Hexdump( 0, buffer.head(), buffer.size() ); + + buffer -= len; - } - if ( frameComplete ) { - - Debug( 3, "Got frame %d", frameCount ); - - avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height); - -#if HAVE_LIBSWSCALE - if(mConvertContext == NULL) { - if(config.cpu_extensions && sseversion >= 20) { - mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC | SWS_CPU_CAPS_SSE2, NULL, NULL, NULL ); - } else { - mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); - } - if(mConvertContext == NULL) - Fatal( "Unable to create conversion context"); - } - - if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) - Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); -#else // HAVE_LIBSWSCALE - Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); -#endif // HAVE_LIBSWSCALE - - frameCount++; - - } /* frame complete */ - - av_free_packet( &packet ); - } /* getFrame() */ - - if(frameComplete) - return (0); - } - return (0) ; + if ( frameComplete ) { + + Debug( 3, "Got frame %d", frameCount ); + +#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) + av_image_fill_arrays(mFrame->data, mFrame->linesize, + directbuffer, imagePixFormat, width, height, 1); +#else + avpicture_fill( (AVPicture *)mFrame, directbuffer, + imagePixFormat, width, height); +#endif + +#if HAVE_LIBSWSCALE + if(mConvertContext == NULL) { + mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); + + if(mConvertContext == NULL) + Fatal( "Unable to create conversion context"); + } + + if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 ) + Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount ); +#else // HAVE_LIBSWSCALE + Fatal( "You must compile ffmpeg with the --enable-swscale option to use RTSP cameras" ); +#endif // HAVE_LIBSWSCALE + + frameCount++; + + } /* frame complete */ + +#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100) + av_packet_unref( &packet); +#else + av_free_packet( &packet ); +#endif + } /* getFrame() */ + + if(frameComplete) + return (0); + + } + return (0) ; } int RemoteCameraRtsp::PostCapture() { - return( 0 ); + return( 0 ); } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_remote_camera_rtsp.h b/src/zm_remote_camera_rtsp.h index b5a1e3c89..20261f82d 100644 --- a/src/zm_remote_camera_rtsp.h +++ b/src/zm_remote_camera_rtsp.h @@ -35,49 +35,51 @@ class RemoteCameraRtsp : public RemoteCamera { protected: - struct sockaddr_in rtsp_sa; - struct sockaddr_in rtcp_sa; - int rtsp_sd; - int rtp_sd; - int rtcp_sd; + struct sockaddr_in rtsp_sa; + struct sockaddr_in rtcp_sa; + int rtsp_sd; + int rtp_sd; + int rtcp_sd; + bool rtsp_describe; - Buffer buffer; - Buffer lastSps; - Buffer lastPps; + Buffer buffer; + Buffer lastSps; + Buffer lastPps; - RtspThread::RtspMethod method; + RtspThread::RtspMethod method; - RtspThread *rtspThread; + RtspThread *rtspThread; - int frameCount; - + int frameCount; + #if HAVE_LIBAVFORMAT - AVFormatContext *mFormatContext; - int mVideoStreamId; - AVCodecContext *mCodecContext; - AVCodec *mCodec; - AVFrame *mRawFrame; - AVFrame *mFrame; - PixelFormat imagePixFormat; + AVFormatContext *mFormatContext; + int mVideoStreamId; + AVCodecContext *mCodecContext; + AVCodec *mCodec; + AVFrame *mRawFrame; + AVFrame *mFrame; + _AVPIXELFORMAT imagePixFormat; #endif // HAVE_LIBAVFORMAT #if HAVE_LIBSWSCALE - struct SwsContext *mConvertContext; + struct SwsContext *mConvertContext; #endif public: - RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); - ~RemoteCameraRtsp(); + RemoteCameraRtsp( int p_id, const std::string &method, const std::string &host, const std::string &port, const std::string &path, int p_width, int p_height, bool p_rtsp_describe, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ); + ~RemoteCameraRtsp(); - void Initialise(); - void Terminate(); - int Connect(); - int Disconnect(); + void Initialise(); + void Terminate(); + int Connect(); + int Disconnect(); + + int PrimeCapture(); + int PreCapture(); + int Capture( Image &image ); + int PostCapture(); - int PrimeCapture(); - int PreCapture(); - int Capture( Image &image ); - int PostCapture(); }; #endif // ZM_REMOTE_CAMERA_RTSP_H diff --git a/src/zm_rgb.h b/src/zm_rgb.h index bfef10501..af74d872b 100644 --- a/src/zm_rgb.h +++ b/src/zm_rgb.h @@ -20,80 +20,80 @@ #ifndef ZM_RGB_H #define ZM_RGB_H -typedef uint32_t Rgb; // RGB colour type +typedef uint32_t Rgb; // RGB colour type -#define WHITE 0xff -#define WHITE_R 0xff -#define WHITE_G 0xff -#define WHITE_B 0xff +#define WHITE 0xff +#define WHITE_R 0xff +#define WHITE_G 0xff +#define WHITE_B 0xff -#define BLACK 0x00 -#define BLACK_R 0x00 -#define BLACK_G 0x00 -#define BLACK_B 0x00 +#define BLACK 0x00 +#define BLACK_R 0x00 +#define BLACK_G 0x00 +#define BLACK_B 0x00 -#define RGB_WHITE (0x00ffffff) -#define RGB_BLACK (0x00000000) -#define RGB_RED (0x000000ff) -#define RGB_GREEN (0x0000ff00) -#define RGB_BLUE (0x00ff0000) -#define RGB_ORANGE (0x0000a5ff) -#define RGB_PURPLE (0x00800080) -#define RGB_TRANSPARENT (0x01000000) +#define RGB_WHITE (0x00ffffff) +#define RGB_BLACK (0x00000000) +#define RGB_RED (0x000000ff) +#define RGB_GREEN (0x0000ff00) +#define RGB_BLUE (0x00ff0000) +#define RGB_ORANGE (0x0000a5ff) +#define RGB_PURPLE (0x00800080) +#define RGB_TRANSPARENT (0x01000000) -#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff) +#define RGB_VAL(v,c) (((v)>>(16-((c)*8)))&0xff) /* RGB or RGBA macros */ -#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff) -#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff) -#define RED_VAL_RGBA(v) ((v)&0xff) -#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff) -#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr)) -#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1)) -#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2)) -#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3)) +#define BLUE_VAL_RGBA(v) (((v)>>16)&0xff) +#define GREEN_VAL_RGBA(v) (((v)>>8)&0xff) +#define RED_VAL_RGBA(v) ((v)&0xff) +#define ALPHA_VAL_RGBA(v) ((v)>>24)&0xff) +#define RED_PTR_RGBA(ptr) (*((uint8_t*)ptr)) +#define GREEN_PTR_RGBA(ptr) (*((uint8_t*)ptr+1)) +#define BLUE_PTR_RGBA(ptr) (*((uint8_t*)ptr+2)) +#define ALPHA_PTR_RGBA(ptr) (*((uint8_t*)ptr+3)) /* BGR or BGRA */ -#define RED_VAL_BGRA(v) (((v)>>16)&0xff) -#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff) -#define BLUE_VAL_BGRA(v) ((v)&0xff) -#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff) -#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2)) -#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1)) -#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr)) -#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3)) +#define RED_VAL_BGRA(v) (((v)>>16)&0xff) +#define GREEN_VAL_BGRA(v) (((v)>>8)&0xff) +#define BLUE_VAL_BGRA(v) ((v)&0xff) +#define ALPHA_VAL_BGRA(v) ((v)>>24)&0xff) +#define RED_PTR_BGRA(ptr) (*((uint8_t*)ptr+2)) +#define GREEN_PTR_BGRA(ptr) (*((uint8_t*)ptr+1)) +#define BLUE_PTR_BGRA(ptr) (*((uint8_t*)ptr)) +#define ALPHA_PTR_BGRA(ptr) (*((uint8_t*)ptr+3)) /* ARGB */ -#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff) -#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff) -#define RED_VAL_ARGB(v) (((v)>>8)&0xff) -#define ALPHA_VAL_ARGB(v) ((v)&0xff) -#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1)) -#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2)) -#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3)) -#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr)) +#define BLUE_VAL_ARGB(v) (((v)>>24)&0xff) +#define GREEN_VAL_ARGB(v) (((v)>>16)&0xff) +#define RED_VAL_ARGB(v) (((v)>>8)&0xff) +#define ALPHA_VAL_ARGB(v) ((v)&0xff) +#define RED_PTR_ARGB(ptr) (*((uint8_t*)ptr+1)) +#define GREEN_PTR_ARGB(ptr) (*((uint8_t*)ptr+2)) +#define BLUE_PTR_ARGB(ptr) (*((uint8_t*)ptr+3)) +#define ALPHA_PTR_ARGB(ptr) (*((uint8_t*)ptr)) /* ABGR */ -#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff) -#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff) -#define RED_VAL_ABGR(v) (((v)>>24)&0xff) -#define ALPHA_VAL_ABGR(v) ((v)&0xff) -#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3)) -#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2)) -#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1)) -#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr)) +#define BLUE_VAL_ABGR(v) (((v)>>8)&0xff) +#define GREEN_VAL_ABGR(v) (((v)>>16)&0xff) +#define RED_VAL_ABGR(v) (((v)>>24)&0xff) +#define ALPHA_VAL_ABGR(v) ((v)&0xff) +#define RED_PTR_ABGR(ptr) (*((uint8_t*)ptr+3)) +#define GREEN_PTR_ABGR(ptr) (*((uint8_t*)ptr+2)) +#define BLUE_PTR_ABGR(ptr) (*((uint8_t*)ptr+1)) +#define ALPHA_PTR_ABGR(ptr) (*((uint8_t*)ptr)) -#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff) -#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00) +#define RGBA_BGRA_ZEROALPHA(v) ((v)&0x00ffffff) +#define ARGB_ABGR_ZEROALPHA(v) ((v)&0xffffff00) /* ITU-R BT.709: Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B) */ /* ITU-R BT.601: Y = (0.299 * R) + (0.587 * G) + (0.114 * B) */ /* The formulas below produce an almost identical result to the weighted algorithms from the ITU-R BT.601 standard and the newer ITU-R BT.709 standard, but a lot faster */ -// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3) -// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3) -// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3) -// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3) +// #define RGB_FASTLUM_SINGLE_ITU709(v) ((RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3) +// #define RGB_FASTLUM_VALUES_ITU709(ra,ga,ba) (((ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga)+(ga))>>3) +// #define RGB_FASTLUM_SINGLE_ITU601(v) ((RED(v)+RED(v)+RED(v)+BLUE(v)+GREEN(v)+GREEN(v)+GREEN(v)+GREEN(v))>>3) +// #define RGB_FASTLUM_VALUES_ITU601(ra,ga,ba) (((ra)+(ra)+(ra)+(ba)+(ga)+(ga)+(ga)+(ga))>>3) /* ZM colours */ #define ZM_COLOUR_RGB32 4 @@ -112,46 +112,46 @@ typedef uint32_t Rgb; // RGB colour type /* A macro to use default subpixel order for a specified colour. */ /* for grayscale it will use NONE, for 3 colours it will use R,G,B, for 4 colours it will use R,G,B,A */ -#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1) +#define ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(c) ((c)<<1) /* Convert RGB colour value into BGR\ARGB\ABGR */ inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) { - Rgb result; - - switch(p_subpixorder) { - - case ZM_SUBPIX_ORDER_BGR: - case ZM_SUBPIX_ORDER_BGRA: - { - BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col); - GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col); - RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col); - } - break; - case ZM_SUBPIX_ORDER_ARGB: - { - BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col); - GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col); - RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col); - } - break; - case ZM_SUBPIX_ORDER_ABGR: - { - BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col); - GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col); - RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col); - } - break; - /* Grayscale */ - case ZM_SUBPIX_ORDER_NONE: - result = p_col & 0xff; - break; - default: - return p_col; - break; - } - - return result; + Rgb result; + + switch(p_subpixorder) { + + case ZM_SUBPIX_ORDER_BGR: + case ZM_SUBPIX_ORDER_BGRA: + { + BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col); + GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col); + RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col); + } + break; + case ZM_SUBPIX_ORDER_ARGB: + { + BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col); + GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col); + RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col); + } + break; + case ZM_SUBPIX_ORDER_ABGR: + { + BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col); + GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col); + RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col); + } + break; + /* Grayscale */ + case ZM_SUBPIX_ORDER_NONE: + result = p_col & 0xff; + break; + default: + return p_col; + break; + } + + return result; } #endif // ZM_RGB_H diff --git a/src/zm_rtp_ctrl.cpp b/src/zm_rtp_ctrl.cpp index 618d09f19..012a377fc 100644 --- a/src/zm_rtp_ctrl.cpp +++ b/src/zm_rtp_ctrl.cpp @@ -34,316 +34,340 @@ RtpCtrlThread::RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m int RtpCtrlThread::recvPacket( const unsigned char *packet, ssize_t packetLen ) { - const RtcpPacket *rtcpPacket; - rtcpPacket = (RtcpPacket *)packet; + const RtcpPacket *rtcpPacket; + rtcpPacket = (RtcpPacket *)packet; - int consumed = 0; + int consumed = 0; - //printf( "C: " ); - //for ( int i = 0; i < packetLen; i++ ) - //printf( "%02x ", (unsigned char)packet[i] ); - //printf( "\n" ); - int ver = rtcpPacket->header.version; - int count = rtcpPacket->header.count; - int pt = rtcpPacket->header.pt; - int len = ntohs(rtcpPacket->header.lenN); + //printf( "C: " ); + //for ( int i = 0; i < packetLen; i++ ) + //printf( "%02x ", (unsigned char)packet[i] ); + //printf( "\n" ); + int ver = rtcpPacket->header.version; + int count = rtcpPacket->header.count; + int pt = rtcpPacket->header.pt; + int len = ntohs(rtcpPacket->header.lenN); - Debug( 5, "RTCP Ver: %d", ver ); - Debug( 5, "RTCP Count: %d", count ); - Debug( 5, "RTCP Pt: %d", pt ); - Debug( 5, "RTCP len: %d", len ); + Debug( 5, "RTCP Ver: %d", ver ); + Debug( 5, "RTCP Count: %d", count ); + Debug( 5, "RTCP Pt: %d", pt ); + Debug( 5, "RTCP len: %d", len ); - switch( pt ) + switch( pt ) + { + case RTCP_SR : { - case RTCP_SR : - { - uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN); + uint32_t ssrc = ntohl(rtcpPacket->body.sr.ssrcN); - Debug( 5, "RTCP Got SR (%x)", ssrc ); - if ( mRtpSource.getSsrc() ) - { - if ( ssrc != mRtpSource.getSsrc() ) - { - Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); - return( -1 ); - } - } - else if ( ssrc ) - { - mRtpSource.setSsrc( ssrc ); - } - - if ( len > 1 ) - { - //printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts ); - uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN); - uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN); - //printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts ); - //printf( "Pkts:$sendpkts, Octs:$sendocts\n" ); - uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN); - - mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime ); - } - break; - } - case RTCP_SDES : + Debug( 5, "RTCP Got SR (%x)", ssrc ); + if ( mRtpSource.getSsrc() ) + { + if ( ssrc != mRtpSource.getSsrc() ) { - ssize_t contentLen = packetLen - sizeof(rtcpPacket->header); - while ( contentLen ) - { - Debug( 5, "RTCP CL: %zd", contentLen ); - uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN); + Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); + return( -1 ); + } + } + else if ( ssrc ) + { + mRtpSource.setSsrc( ssrc ); + } - Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count ); - if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) - { - Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); - return( -1 ); - } + if ( len > 1 ) + { + //printf( "NTPts:%d.%d, RTPts:%d\n", $ntptsmsb, $ntptslsb, $rtpts ); + uint16_t ntptsmsb = ntohl(rtcpPacket->body.sr.ntpSecN); + uint16_t ntptslsb = ntohl(rtcpPacket->body.sr.ntpFracN); + //printf( "NTPts:%x.%04x, RTPts:%x\n", $ntptsmsb, $ntptslsb, $rtpts ); + //printf( "Pkts:$sendpkts, Octs:$sendocts\n" ); + uint32_t rtpTime = ntohl(rtcpPacket->body.sr.rtpTsN); - unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item; - for ( int i = 0; i < count; i++ ) - { - RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr; - Debug( 5, "RTCP Item length %d", item->len ); - switch( item->type ) - { - case RTCP_SDES_CNAME : - { - std::string cname( item->data, item->len ); - Debug( 5, "RTCP Got CNAME %s", cname.c_str() ); - break; - } - case RTCP_SDES_END : - case RTCP_SDES_NAME : - case RTCP_SDES_EMAIL : - case RTCP_SDES_PHONE : - case RTCP_SDES_LOC : - case RTCP_SDES_TOOL : - case RTCP_SDES_NOTE : - case RTCP_SDES_PRIV : - default : - { - Error( "Received unexpected SDES item type %d, ignoring", item->type ); - return( -1 ); - } - } - int paddedLen = 4+2+item->len+1; // Add null byte - paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4 - Debug( 5, "RTCP PL:%d", paddedLen ); - sdesPtr += paddedLen; - contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0; - } - } - break; - } - case RTCP_BYE : - { - Debug( 5, "RTCP Got BYE" ); - mStop = true; - break; - } - case RTCP_APP : - { - // Ignoring as per RFC 3550 - Debug( 5, "Received RTCP_APP packet, ignoring."); - break; - } - case RTCP_RR : - default : - { - Error( "Received unexpected packet type %d, ignoring", pt ); - return( -1 ); - } + mRtpSource.updateRtcpData( ntptsmsb, ntptslsb, rtpTime ); + } + break; } - consumed = sizeof(uint32_t)*(len+1); - return( consumed ); + case RTCP_SDES : + { + ssize_t contentLen = packetLen - sizeof(rtcpPacket->header); + while ( contentLen ) + { + Debug( 5, "RTCP CL: %zd", contentLen ); + uint32_t ssrc = ntohl(rtcpPacket->body.sdes.srcN); + + Debug( 5, "RTCP Got SDES (%x), %d items", ssrc, count ); + if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) + { + Warning( "Discarding packet for unrecognised ssrc %x", ssrc ); + return( -1 ); + } + + unsigned char *sdesPtr = (unsigned char *)&rtcpPacket->body.sdes.item; + for ( int i = 0; i < count; i++ ) + { + RtcpSdesItem *item = (RtcpSdesItem *)sdesPtr; + Debug( 5, "RTCP Item length %d", item->len ); + switch( item->type ) + { + case RTCP_SDES_CNAME : + { + std::string cname( item->data, item->len ); + Debug( 5, "RTCP Got CNAME %s", cname.c_str() ); + break; + } + case RTCP_SDES_END : + case RTCP_SDES_NAME : + case RTCP_SDES_EMAIL : + case RTCP_SDES_PHONE : + case RTCP_SDES_LOC : + case RTCP_SDES_TOOL : + case RTCP_SDES_NOTE : + case RTCP_SDES_PRIV : + default : + { + Error( "Received unexpected SDES item type %d, ignoring", item->type ); + return( -1 ); + } + } + int paddedLen = 4+2+item->len+1; // Add null byte + paddedLen = (((paddedLen-1)/4)+1)*4; // Round to nearest multiple of 4 + Debug( 5, "RTCP PL:%d", paddedLen ); + sdesPtr += paddedLen; + contentLen = ( paddedLen <= contentLen ) ? ( contentLen - paddedLen ) : 0; + } + } + break; + } + case RTCP_BYE : + { + Debug( 5, "RTCP Got BYE" ); + mStop = true; + break; + } + case RTCP_APP : + { + // Ignoring as per RFC 3550 + Debug( 5, "Received RTCP_APP packet, ignoring."); + break; + } + case RTCP_RR : + { + Error( "Received RTCP_RR packet." ); + return( -1 ); + } + default : + { + // Ignore unknown packet types. Some cameras do this by design. + Debug( 5, "Received unexpected packet type %d, ignoring", pt ); + break; + } + } + consumed = sizeof(uint32_t)*(len+1); + return( consumed ); } int RtpCtrlThread::generateRr( const unsigned char *packet, ssize_t packetLen ) { - RtcpPacket *rtcpPacket = (RtcpPacket *)packet; + RtcpPacket *rtcpPacket = (RtcpPacket *)packet; - int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]); - int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; + int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.rr)+sizeof(rtcpPacket->body.rr.rr[0]); + int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; - rtcpPacket->header.version = RTP_VERSION; - rtcpPacket->header.p = 0; - rtcpPacket->header.pt = RTCP_RR; - rtcpPacket->header.count = 1; - rtcpPacket->header.lenN = htons(wordLen-1); + rtcpPacket->header.version = RTP_VERSION; + rtcpPacket->header.p = 0; + rtcpPacket->header.pt = RTCP_RR; + rtcpPacket->header.count = 1; + rtcpPacket->header.lenN = htons(wordLen-1); - mRtpSource.updateRtcpStats(); + mRtpSource.updateRtcpStats(); - Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 ); - Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() ); - Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() ); - Debug( 5, "Jitter = %d", mRtpSource.getJitter() ); - Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() ); + Debug( 5, "Ssrc = %d", mRtspThread.getSsrc()+1 ); + Debug( 5, "Ssrc_1 = %d", mRtpSource.getSsrc() ); + Debug( 5, "Last Seq = %d", mRtpSource.getMaxSeq() ); + Debug( 5, "Jitter = %d", mRtpSource.getJitter() ); + Debug( 5, "Last SR = %d", mRtpSource.getLastSrTimestamp() ); - rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1); - rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc()); - rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets(); - rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction(); - rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq()); - rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter()); - rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp()); - rtcpPacket->body.rr.rr[0].dlsrN = 0; + rtcpPacket->body.rr.ssrcN = htonl(mRtspThread.getSsrc()+1); + rtcpPacket->body.rr.rr[0].ssrcN = htonl(mRtpSource.getSsrc()); + rtcpPacket->body.rr.rr[0].lost = mRtpSource.getLostPackets(); + rtcpPacket->body.rr.rr[0].fraction = mRtpSource.getLostFraction(); + rtcpPacket->body.rr.rr[0].lastSeqN = htonl(mRtpSource.getMaxSeq()); + rtcpPacket->body.rr.rr[0].jitterN = htonl(mRtpSource.getJitter()); + rtcpPacket->body.rr.rr[0].lsrN = htonl(mRtpSource.getLastSrTimestamp()); + rtcpPacket->body.rr.rr[0].dlsrN = 0; - return( wordLen*sizeof(uint32_t) ); + return( wordLen*sizeof(uint32_t) ); } int RtpCtrlThread::generateSdes( const unsigned char *packet, ssize_t packetLen ) { - RtcpPacket *rtcpPacket = (RtcpPacket *)packet; + RtcpPacket *rtcpPacket = (RtcpPacket *)packet; - const std::string &cname = mRtpSource.getCname(); + const std::string &cname = mRtpSource.getCname(); - int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size(); - int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; + int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.sdes)+sizeof(rtcpPacket->body.sdes.item[0])+cname.size(); + int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; - rtcpPacket->header.version = RTP_VERSION; - rtcpPacket->header.p = 0; - rtcpPacket->header.pt = RTCP_SDES; - rtcpPacket->header.count = 1; - rtcpPacket->header.lenN = htons(wordLen-1); + rtcpPacket->header.version = RTP_VERSION; + rtcpPacket->header.p = 0; + rtcpPacket->header.pt = RTCP_SDES; + rtcpPacket->header.count = 1; + rtcpPacket->header.lenN = htons(wordLen-1); - rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1); - rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME; - rtcpPacket->body.sdes.item[0].len = cname.size(); - memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() ); + rtcpPacket->body.sdes.srcN = htonl(mRtpSource.getSsrc()+1); + rtcpPacket->body.sdes.item[0].type = RTCP_SDES_CNAME; + rtcpPacket->body.sdes.item[0].len = cname.size(); + memcpy( rtcpPacket->body.sdes.item[0].data, cname.data(), cname.size() ); - return( wordLen*sizeof(uint32_t) ); + return( wordLen*sizeof(uint32_t) ); } int RtpCtrlThread::generateBye( const unsigned char *packet, ssize_t packetLen ) { - RtcpPacket *rtcpPacket = (RtcpPacket *)packet; + RtcpPacket *rtcpPacket = (RtcpPacket *)packet; - int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]); - int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; + int byteLen = sizeof(rtcpPacket->header)+sizeof(rtcpPacket->body.bye)+sizeof(rtcpPacket->body.bye.srcN[0]); + int wordLen = ((byteLen-1)/sizeof(uint32_t))+1; - rtcpPacket->header.version = RTP_VERSION; - rtcpPacket->header.p = 0; - rtcpPacket->header.pt = RTCP_BYE; - rtcpPacket->header.count = 1; - rtcpPacket->header.lenN = htons(wordLen-1); + rtcpPacket->header.version = RTP_VERSION; + rtcpPacket->header.p = 0; + rtcpPacket->header.pt = RTCP_BYE; + rtcpPacket->header.count = 1; + rtcpPacket->header.lenN = htons(wordLen-1); - rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc()); + rtcpPacket->body.bye.srcN[0] = htonl(mRtpSource.getSsrc()); - return( wordLen*sizeof(uint32_t) ); + return( wordLen*sizeof(uint32_t) ); } int RtpCtrlThread::recvPackets( unsigned char *buffer, ssize_t nBytes ) { - unsigned char *bufferPtr = buffer; + unsigned char *bufferPtr = buffer; - // u_int32 len; /* length of compound RTCP packet in words */ - // rtcp_t *r; /* RTCP header */ - // rtcp_t *end; /* end of compound RTCP packet */ + // u_int32 len; /* length of compound RTCP packet in words */ + // rtcp_t *r; /* RTCP header */ + // rtcp_t *end; /* end of compound RTCP packet */ - // if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) { - // /* something wrong with packet format */ - // } - // end = (rtcp_t *)((u_int32 *)r + len); + // if ((*(u_int16 *)r & RTCP_VALID_MASK) != RTCP_VALID_VALUE) { + // /* something wrong with packet format */ + // } + // end = (rtcp_t *)((u_int32 *)r + len); - // do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1); - // while (r < end && r->common.version == 2); + // do r = (rtcp_t *)((u_int32 *)r + r->common.length + 1); + // while (r < end && r->common.version == 2); - // if (r != end) { - // /* something wrong with packet format */ - // } + // if (r != end) { + // /* something wrong with packet format */ + // } - while ( nBytes > 0 ) - { - int consumed = recvPacket( bufferPtr, nBytes ); - if ( consumed <= 0 ) - break; - bufferPtr += consumed; - nBytes -= consumed; - } - return( nBytes ); + while ( nBytes > 0 ) + { + int consumed = recvPacket( bufferPtr, nBytes ); + if ( consumed <= 0 ) + break; + bufferPtr += consumed; + nBytes -= consumed; + } + return( nBytes ); } int RtpCtrlThread::run() { - Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() ); - SockAddrInet localAddr, remoteAddr; + Debug( 2, "Starting control thread %x on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalCtrlPort() ); + SockAddrInet localAddr, remoteAddr; - bool sendReports; - UdpInetSocket rtpCtrlServer; - if ( mRtpSource.getLocalHost() != "" ) + bool sendReports; + UdpInetSocket rtpCtrlServer; + if ( mRtpSource.getLocalHost() != "" ) + { + if ( !rtpCtrlServer.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ) ) + Fatal( "Failed to bind RTCP server" ); + sendReports = false; + Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); + } + else + { + if ( !rtpCtrlServer.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalCtrlPort() ) ) + Fatal( "Failed to bind RTCP server" ); + Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); + if ( !rtpCtrlServer.connect( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ) ) + Fatal( "Failed to connect RTCP server" ); + Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ); + sendReports = true; + } + + // The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets. + // Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response + Select select( 10 ); + select.addReader( &rtpCtrlServer ); + + unsigned char buffer[ZM_NETWORK_BUFSIZ]; + + time_t last_receive = time(NULL); + bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true. + + while ( !mStop && select.wait() >= 0 ) { + + time_t now = time(NULL); + Select::CommsList readable = select.getReadable(); + if ( readable.size() == 0 ) { - localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort(), "udp" ); - if ( !rtpCtrlServer.bind( localAddr ) ) - Fatal( "Failed to bind RTCP server" ); - sendReports = false; - Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); + if ( ! timeout ) { + // With this code here, we will send an SDES and RR packet every 10 seconds + ssize_t nBytes; + unsigned char *bufferPtr = buffer; + bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); + bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); + Debug( 3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc(), ( now-last_receive) ); + if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) + Error( "Unable to send: %s", strerror( errno ) ); + timeout = true; + continue; + } else { + //Error( "RTCP timed out" ); + Debug(1, "RTCP timed out. Time since last receive: %d", ( now-last_receive) ); + continue; + //break; + } + } else { + timeout = false; + last_receive = time(NULL); } - else + for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ ) { - localAddr.resolve( mRtpSource.getLocalCtrlPort(), "udp" ); - if ( !rtpCtrlServer.bind( localAddr ) ) - Fatal( "Failed to bind RTCP server" ); - Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalCtrlPort() ); - remoteAddr.resolve( mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort(), "udp" ); - if ( !rtpCtrlServer.connect( remoteAddr ) ) - Fatal( "Failed to connect RTCP server" ); - Debug( 3, "Connected to %s:%d", mRtpSource.getRemoteHost().c_str(), mRtpSource.getRemoteCtrlPort() ); - sendReports = true; - } + if ( UdpInetSocket *socket = dynamic_cast(*iter) ) + { + ssize_t nBytes = socket->recv( buffer, sizeof(buffer) ); + Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() ); - Select select( 10 ); - select.addReader( &rtpCtrlServer ); - - unsigned char buffer[ZM_NETWORK_BUFSIZ]; - while ( !mStop && select.wait() >= 0 ) - { - if ( mStop ) - break; - Select::CommsList readable = select.getReadable(); - if ( readable.size() == 0 ) + if ( nBytes ) { - Error( "RTCP timed out" ); - break; - } - for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ ) - { - if ( UdpInetSocket *socket = dynamic_cast(*iter) ) - { - ssize_t nBytes = socket->recv( buffer, sizeof(buffer) ); - Debug( 4, "Read %zd bytes on sd %d", nBytes, socket->getReadDesc() ); + recvPackets( buffer, nBytes ); - if ( nBytes ) - { - recvPackets( buffer, nBytes ); - - if ( sendReports ) - { - unsigned char *bufferPtr = buffer; - bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); - bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); - Debug( 4, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() ); - if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) - Error( "Unable to send: %s", strerror( errno ) ); - //Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() ); - } - } - else - { - mStop = true; - break; - } - } - else - { - Panic( "Barfed" ); - } + if ( sendReports ) + { + unsigned char *bufferPtr = buffer; + bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); + bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) ); + Debug( 3, "Sending %zd bytes on sd %d", bufferPtr-buffer, rtpCtrlServer.getWriteDesc() ); + if ( (nBytes = rtpCtrlServer.send( buffer, bufferPtr-buffer )) < 0 ) + Error( "Unable to send: %s", strerror( errno ) ); + //Debug( 4, "Sent %d bytes on sd %d", nBytes, rtpCtrlServer.getWriteDesc() ); + } + } else { + // Here is another case of not receiving some data causing us to terminate... why? Sometimes there are pauses in the interwebs. + mStop = true; + break; } + } + else + { + Panic( "Barfed" ); + } } - rtpCtrlServer.close(); - mRtspThread.stop(); - return( 0 ); + } + rtpCtrlServer.close(); + mRtspThread.stop(); + return( 0 ); } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_rtp_ctrl.h b/src/zm_rtp_ctrl.h index 9b44b752b..f487d1191 100644 --- a/src/zm_rtp_ctrl.h +++ b/src/zm_rtp_ctrl.h @@ -25,7 +25,7 @@ #include "zm_thread.h" // Defined in ffmpeg rtp.h -//#define RTP_MAX_SDES 255 // maximum text length for SDES +//#define RTP_MAX_SDES 255 // maximum text length for SDES // Big-endian mask for version, padding bit and packet type pair #define RTCP_VALID_MASK (0xc000 | 0x2000 | 0xfe) @@ -39,119 +39,119 @@ class RtpCtrlThread : public Thread friend class RtspThread; private: - typedef enum + typedef enum + { + RTCP_SR = 200, + RTCP_RR = 201, + RTCP_SDES = 202, + RTCP_BYE = 203, + RTCP_APP = 204 + } RtcpType; + + typedef enum + { + RTCP_SDES_END = 0, + RTCP_SDES_CNAME = 1, + RTCP_SDES_NAME = 2, + RTCP_SDES_EMAIL = 3, + RTCP_SDES_PHONE = 4, + RTCP_SDES_LOC = 5, + RTCP_SDES_TOOL = 6, + RTCP_SDES_NOTE = 7, + RTCP_SDES_PRIV = 8 + } RtcpSdesType; + + struct RtcpCommonHeader + { + uint8_t count:5; // varies by packet type + uint8_t p:1; // padding flag + uint8_t version:2; // protocol version + uint8_t pt; // RTCP packet type + uint16_t lenN; // pkt len in words, w/o this word, network order + }; + + // Reception report block + struct RtcpRr + { + uint32_t ssrcN; // data source being reported + int32_t lost:24; // cumul. no. pkts lost (signed!) + uint32_t fraction:8; // fraction lost since last SR/RR + uint32_t lastSeqN; // extended last seq. no. received, network order + uint32_t jitterN; // interarrival jitter, network order + uint32_t lsrN; // last SR packet from this source, network order + uint32_t dlsrN; // delay since last SR packet, network order + }; + + // SDES item + struct RtcpSdesItem + { + uint8_t type; // type of item (rtcp_sdes_type_t) + uint8_t len; // length of item (in octets) + char data[]; // text, not null-terminated + }; + + // RTCP packet + struct RtcpPacket + { + RtcpCommonHeader header; // common header + union { - RTCP_SR = 200, - RTCP_RR = 201, - RTCP_SDES = 202, - RTCP_BYE = 203, - RTCP_APP = 204 - } RtcpType; + // Sender Report (SR) + struct Sr + { + uint32_t ssrcN; // sender generating this report, network order + uint32_t ntpSecN; // NTP timestamp, network order + uint32_t ntpFracN; + uint32_t rtpTsN; // RTP timestamp, network order + uint32_t pSentN; // packets sent, network order + uint32_t oSentN; // octets sent, network order + RtcpRr rr[]; // variable-length list + } sr; - typedef enum - { - RTCP_SDES_END = 0, - RTCP_SDES_CNAME = 1, - RTCP_SDES_NAME = 2, - RTCP_SDES_EMAIL = 3, - RTCP_SDES_PHONE = 4, - RTCP_SDES_LOC = 5, - RTCP_SDES_TOOL = 6, - RTCP_SDES_NOTE = 7, - RTCP_SDES_PRIV = 8 - } RtcpSdesType; + // Reception Report (RR) + struct Rr + { + uint32_t ssrcN; // receiver generating this report + RtcpRr rr[]; // variable-length list + } rr; - struct RtcpCommonHeader - { - uint8_t count:5; // varies by packet type - uint8_t p:1; // padding flag - uint8_t version:2; // protocol version - uint8_t pt; // RTCP packet type - uint16_t lenN; // pkt len in words, w/o this word, network order - }; + // source description (SDES) + struct Sdes + { + uint32_t srcN; // first SSRC/CSRC + RtcpSdesItem item[]; // list of SDES items + } sdes; - // Reception report block - struct RtcpRr - { - uint32_t ssrcN; // data source being reported - int32_t lost:24; // cumul. no. pkts lost (signed!) - uint32_t fraction:8; // fraction lost since last SR/RR - uint32_t lastSeqN; // extended last seq. no. received, network order - uint32_t jitterN; // interarrival jitter, network order - uint32_t lsrN; // last SR packet from this source, network order - uint32_t dlsrN; // delay since last SR packet, network order - }; - - // SDES item - struct RtcpSdesItem - { - uint8_t type; // type of item (rtcp_sdes_type_t) - uint8_t len; // length of item (in octets) - char data[]; // text, not null-terminated - }; - - // RTCP packet - struct RtcpPacket - { - RtcpCommonHeader header; // common header - union - { - // Sender Report (SR) - struct Sr - { - uint32_t ssrcN; // sender generating this report, network order - uint32_t ntpSecN; // NTP timestamp, network order - uint32_t ntpFracN; - uint32_t rtpTsN; // RTP timestamp, network order - uint32_t pSentN; // packets sent, network order - uint32_t oSentN; // octets sent, network order - RtcpRr rr[]; // variable-length list - } sr; - - // Reception Report (RR) - struct Rr - { - uint32_t ssrcN; // receiver generating this report - RtcpRr rr[]; // variable-length list - } rr; - - // source description (SDES) - struct Sdes - { - uint32_t srcN; // first SSRC/CSRC - RtcpSdesItem item[]; // list of SDES items - } sdes; - - // BYE - struct Bye - { - uint32_t srcN[]; // list of sources - // can't express trailing text for reason (what does this mean? it's not even english!) - } bye; - } body; - }; + // BYE + struct + { + uint32_t srcN[]; // list of sources + // can't express trailing text for reason (what does this mean? it's not even english!) + } bye; + } body; + }; private: - RtspThread &mRtspThread; - RtpSource &mRtpSource; - int mPort; - bool mStop; + RtspThread &mRtspThread; + RtpSource &mRtpSource; + int mPort; + bool mStop; private: - int recvPacket( const unsigned char *packet, ssize_t packetLen ); - int generateRr( const unsigned char *packet, ssize_t packetLen ); - int generateSdes( const unsigned char *packet, ssize_t packetLen ); - int generateBye( const unsigned char *packet, ssize_t packetLen ); - int recvPackets( unsigned char *buffer, ssize_t nBytes ); - int run(); + int recvPacket( const unsigned char *packet, ssize_t packetLen ); + int generateRr( const unsigned char *packet, ssize_t packetLen ); + int generateSdes( const unsigned char *packet, ssize_t packetLen ); + int generateBye( const unsigned char *packet, ssize_t packetLen ); + int recvPackets( unsigned char *buffer, ssize_t nBytes ); + int run(); public: - RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ); + RtpCtrlThread( RtspThread &rtspThread, RtpSource &rtpSource ); - void stop() - { - mStop = true; - } + void stop() + { + mStop = true; + } }; #endif // ZM_RTP_CTRL_H diff --git a/src/zm_rtp_data.cpp b/src/zm_rtp_data.cpp index 257f46947..5742c60b1 100644 --- a/src/zm_rtp_data.cpp +++ b/src/zm_rtp_data.cpp @@ -33,88 +33,92 @@ RtpDataThread::RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ) : m bool RtpDataThread::recvPacket( const unsigned char *packet, size_t packetLen ) { - const RtpDataHeader *rtpHeader; - rtpHeader = (RtpDataHeader *)packet; + const RtpDataHeader *rtpHeader; + rtpHeader = (RtpDataHeader *)packet; - //printf( "D: " ); - //for ( int i = 0; i < 32; i++ ) - //printf( "%02x ", (unsigned char)packet[i] ); - //printf( "\n" ); + //printf( "D: " ); + //for ( int i = 0; i < 32; i++ ) + //printf( "%02x ", (unsigned char)packet[i] ); + //printf( "\n" ); - Debug( 5, "Ver: %d", rtpHeader->version ); - Debug( 5, "P: %d", rtpHeader->p ); - Debug( 5, "Pt: %d", rtpHeader->pt ); - Debug( 5, "Mk: %d", rtpHeader->m ); - Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) ); - Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) ); - Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) ); + Debug( 5, "Ver: %d", rtpHeader->version ); + Debug( 5, "P: %d", rtpHeader->p ); + Debug( 5, "Pt: %d", rtpHeader->pt ); + Debug( 5, "Mk: %d", rtpHeader->m ); + Debug( 5, "Seq: %d", ntohs(rtpHeader->seqN) ); + Debug( 5, "T/S: %x", ntohl(rtpHeader->timestampN) ); + Debug( 5, "SSRC: %x", ntohl(rtpHeader->ssrcN) ); - //unsigned short seq = ntohs(rtpHeader->seqN); - unsigned long ssrc = ntohl(rtpHeader->ssrcN); + //unsigned short seq = ntohs(rtpHeader->seqN); + unsigned long ssrc = ntohl(rtpHeader->ssrcN); - if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) - { - Warning( "Discarding packet for unrecognised ssrc %lx", ssrc ); - return( false ); - } + if ( mRtpSource.getSsrc() && (ssrc != mRtpSource.getSsrc()) ) + { + Warning( "Discarding packet for unrecognised ssrc %lx", ssrc ); + return( false ); + } - return( mRtpSource.handlePacket( packet, packetLen ) ); + return( mRtpSource.handlePacket( packet, packetLen ) ); } int RtpDataThread::run() { - Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() ); + Debug( 2, "Starting data thread %d on port %d", mRtpSource.getSsrc(), mRtpSource.getLocalDataPort() ); - SockAddrInet localAddr; - UdpInetServer rtpDataSocket; - if ( mRtpSource.getLocalHost() != "" ) - localAddr.resolve( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort(), "udp" ); - else - localAddr.resolve( mRtpSource.getLocalDataPort(), "udp" ); - if ( !rtpDataSocket.bind( localAddr ) ) - Fatal( "Failed to bind RTP server" ); + SockAddrInet localAddr; + UdpInetServer rtpDataSocket; + if ( mRtpSource.getLocalHost() != "" ) { + if ( !rtpDataSocket.bind( mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ) ) + Fatal( "Failed to bind RTP server" ); Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ); + } + else + { + if ( !rtpDataSocket.bind( mRtspThread.getAddressFamily() == AF_INET6 ? "::" : "0.0.0.0", mRtpSource.getLocalDataPort() ) ) + Fatal( "Failed to bind RTP server" ); + Debug( 3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort() ); + } - Select select( 3 ); - select.addReader( &rtpDataSocket ); + Select select( 3 ); + select.addReader( &rtpDataSocket ); - unsigned char buffer[ZM_NETWORK_BUFSIZ]; - while ( !mStop && select.wait() >= 0 ) - { - if ( mStop ) - break; - Select::CommsList readable = select.getReadable(); - if ( readable.size() == 0 ) + unsigned char buffer[ZM_NETWORK_BUFSIZ]; + while ( !mStop && select.wait() >= 0 ) + { + if ( mStop ) + break; + Select::CommsList readable = select.getReadable(); + if ( readable.size() == 0 ) + { + Error( "RTP timed out" ); + mStop = true; + break; + } + for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ ) + { + if ( UdpInetServer *socket = dynamic_cast(*iter) ) + { + int nBytes = socket->recv( buffer, sizeof(buffer) ); + Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() ); + if ( nBytes ) { - Error( "RTP timed out" ); - mStop = true; - break; + recvPacket( buffer, nBytes ); } - for ( Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); iter++ ) + else { - if ( UdpInetServer *socket = dynamic_cast(*iter) ) - { - int nBytes = socket->recv( buffer, sizeof(buffer) ); - Debug( 4, "Got %d bytes on sd %d", nBytes, socket->getReadDesc() ); - if ( nBytes ) - { - recvPacket( buffer, nBytes ); - } - else - { - mStop = true; - break; - } - } - else - { - Panic( "Barfed" ); - } + mStop = true; + break; } - } - rtpDataSocket.close(); - mRtspThread.stop(); - return( 0 ); + } + else + { + Panic( "Barfed" ); + } + } + } + rtpDataSocket.close(); + mRtspThread.stop(); + return( 0 ); } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_rtp_data.h b/src/zm_rtp_data.h index 6c957a3ad..378c5e2e6 100644 --- a/src/zm_rtp_data.h +++ b/src/zm_rtp_data.h @@ -30,16 +30,16 @@ class RtpSource; struct RtpDataHeader { - uint8_t cc:4; // CSRC count - uint8_t x:1; // header extension flag - uint8_t p:1; // padding flag - uint8_t version:2; // protocol version - uint8_t pt:7; // payload type - uint8_t m:1; // marker bit - uint16_t seqN; // sequence number, network order - uint32_t timestampN; // timestamp, network order - uint32_t ssrcN; // synchronization source, network order - uint32_t csrc[]; // optional CSRC list + uint8_t cc:4; // CSRC count + uint8_t x:1; // header extension flag + uint8_t p:1; // padding flag + uint8_t version:2; // protocol version + uint8_t pt:7; // payload type + uint8_t m:1; // marker bit + uint16_t seqN; // sequence number, network order + uint32_t timestampN; // timestamp, network order + uint32_t ssrcN; // synchronization source, network order + uint32_t csrc[]; // optional CSRC list }; class RtpDataThread : public Thread @@ -47,21 +47,21 @@ class RtpDataThread : public Thread friend class RtspThread; private: - RtspThread &mRtspThread; - RtpSource &mRtpSource; - bool mStop; + RtspThread &mRtspThread; + RtpSource &mRtpSource; + bool mStop; private: - bool recvPacket( const unsigned char *packet, size_t packetLen ); - int run(); + bool recvPacket( const unsigned char *packet, size_t packetLen ); + int run(); public: - RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ); + RtpDataThread( RtspThread &rtspThread, RtpSource &rtpSource ); - void stop() - { - mStop = true; - } + void stop() + { + mStop = true; + } }; #endif // ZM_RTP_DATA_H diff --git a/src/zm_rtp_source.cpp b/src/zm_rtp_source.cpp index a61bfffe3..bbdd59a6a 100644 --- a/src/zm_rtp_source.cpp +++ b/src/zm_rtp_source.cpp @@ -27,358 +27,358 @@ #if HAVE_LIBAVCODEC RtpSource::RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ) : - mId( id ), - mSsrc( ssrc ), - mLocalHost( localHost ), - mRemoteHost( remoteHost ), - mRtpClock( rtpClock ), - mCodecId( codecId ), - mFrame( 65536 ), - mFrameCount( 0 ), - mFrameGood( true ), - mFrameReady( false ), - mFrameProcessed( false ) + mId( id ), + mSsrc( ssrc ), + mLocalHost( localHost ), + mRemoteHost( remoteHost ), + mRtpClock( rtpClock ), + mCodecId( codecId ), + mFrame( 65536 ), + mFrameCount( 0 ), + mFrameGood( true ), + mFrameReady( false ), + mFrameProcessed( false ) { - char hostname[256] = ""; - gethostname( hostname, sizeof(hostname) ); + char hostname[256] = ""; + gethostname( hostname, sizeof(hostname) ); - mCname = stringtf( "zm-%d@%s", mId, hostname ); - Debug( 3, "RTP CName = %s", mCname.c_str() ); + mCname = stringtf( "zm-%d@%s", mId, hostname ); + Debug( 3, "RTP CName = %s", mCname.c_str() ); - init( seq ); - mMaxSeq = seq - 1; - mProbation = MIN_SEQUENTIAL; + init( seq ); + mMaxSeq = seq - 1; + mProbation = MIN_SEQUENTIAL; - mLocalPortChans[0] = localPortBase; - mLocalPortChans[1] = localPortBase+1; + mLocalPortChans[0] = localPortBase; + mLocalPortChans[1] = localPortBase+1; - mRemotePortChans[0] = remotePortBase; - mRemotePortChans[1] = remotePortBase+1; + mRemotePortChans[0] = remotePortBase; + mRemotePortChans[1] = remotePortBase+1; - mRtpFactor = mRtpClock; + mRtpFactor = mRtpClock; - mBaseTimeReal = tvNow(); - mBaseTimeNtp = tvZero(); - mBaseTimeRtp = rtpTime; + mBaseTimeReal = tvNow(); + mBaseTimeNtp = tvZero(); + mBaseTimeRtp = rtpTime; - mLastSrTimeReal = tvZero(); - mLastSrTimeNtp = tvZero(); - mLastSrTimeRtp = 0; - - if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4) - Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." ); + mLastSrTimeReal = tvZero(); + mLastSrTimeNtp = tvZero(); + mLastSrTimeRtp = 0; + + if(mCodecId != AV_CODEC_ID_H264 && mCodecId != AV_CODEC_ID_MPEG4) + Warning( "The device is using a codec that may not be supported. Do not be surprised if things don't work." ); } void RtpSource::init( uint16_t seq ) { - Debug( 3, "Initialising sequence" ); - mBaseSeq = seq; - mMaxSeq = seq; - mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false - mCycles = 0; - mReceivedPackets = 0; - mReceivedPrior = 0; - mExpectedPrior = 0; - // other initialization - mJitter = 0; - mTransit = 0; + Debug( 3, "Initialising sequence" ); + mBaseSeq = seq; + mMaxSeq = seq; + mBadSeq = RTP_SEQ_MOD + 1; // so seq == mBadSeq is false + mCycles = 0; + mReceivedPackets = 0; + mReceivedPrior = 0; + mExpectedPrior = 0; + // other initialization + mJitter = 0; + mTransit = 0; } bool RtpSource::updateSeq( uint16_t seq ) { - uint16_t uDelta = seq - mMaxSeq; + uint16_t uDelta = seq - mMaxSeq; - // Source is not valid until MIN_SEQUENTIAL packets with - // sequential sequence numbers have been received. - Debug( 5, "Seq: %d", seq ); + // Source is not valid until MIN_SEQUENTIAL packets with + // sequential sequence numbers have been received. + Debug( 5, "Seq: %d", seq ); - if ( mProbation) + if ( mProbation) + { + // packet is in sequence + if ( seq == mMaxSeq + 1) { - // packet is in sequence - if ( seq == mMaxSeq + 1) - { - Debug( 3, "Sequence in probation %d, in sequence", mProbation ); - mProbation--; - mMaxSeq = seq; - if ( mProbation == 0 ) - { - init( seq ); - mReceivedPackets++; - return( true ); - } - } - else - { - Warning( "Sequence in probation %d, out of sequence", mProbation ); - mProbation = MIN_SEQUENTIAL - 1; - mMaxSeq = seq; - return( false ); - } + Debug( 3, "Sequence in probation %d, in sequence", mProbation ); + mProbation--; + mMaxSeq = seq; + if ( mProbation == 0 ) + { + init( seq ); + mReceivedPackets++; return( true ); - } - else if ( uDelta < MAX_DROPOUT ) - { - if ( uDelta == 1 ) - { - Debug( 3, "Packet in sequence, gap %d", uDelta ); - } - else - { - Warning( "Packet in sequence, gap %d", uDelta ); - } - - // in order, with permissible gap - if ( seq < mMaxSeq ) - { - // Sequence number wrapped - count another 64K cycle. - mCycles += RTP_SEQ_MOD; - } - mMaxSeq = seq; - } - else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) - { - Warning( "Packet out of sequence, gap %d", uDelta ); - // the sequence number made a very large jump - if ( seq == mBadSeq ) - { - Debug( 3, "Restarting sequence" ); - // Two sequential packets -- assume that the other side - // restarted without telling us so just re-sync - // (i.e., pretend this was the first packet). - init( seq ); - } - else - { - mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1); - return( false ); - } + } } else { - Warning( "Packet duplicate or reordered, gap %d", uDelta ); - // duplicate or reordered packet - return( false ); + Warning( "Sequence in probation %d, out of sequence", mProbation ); + mProbation = MIN_SEQUENTIAL - 1; + mMaxSeq = seq; + return( false ); } - mReceivedPackets++; - return( uDelta==1?true:false ); + return( true ); + } + else if ( uDelta < MAX_DROPOUT ) + { + if ( uDelta == 1 ) + { + Debug( 3, "Packet in sequence, gap %d", uDelta ); + } + else + { + Warning( "Packet in sequence, gap %d", uDelta ); + } + + // in order, with permissible gap + if ( seq < mMaxSeq ) + { + // Sequence number wrapped - count another 64K cycle. + mCycles += RTP_SEQ_MOD; + } + mMaxSeq = seq; + } + else if ( uDelta <= RTP_SEQ_MOD - MAX_MISORDER ) + { + Warning( "Packet out of sequence, gap %d", uDelta ); + // the sequence number made a very large jump + if ( seq == mBadSeq ) + { + Debug( 3, "Restarting sequence" ); + // Two sequential packets -- assume that the other side + // restarted without telling us so just re-sync + // (i.e., pretend this was the first packet). + init( seq ); + } + else + { + mBadSeq = (seq + 1) & (RTP_SEQ_MOD-1); + return( false ); + } + } + else + { + Warning( "Packet duplicate or reordered, gap %d", uDelta ); + // duplicate or reordered packet + return( false ); + } + mReceivedPackets++; + return( uDelta==1?true:false ); } void RtpSource::updateJitter( const RtpDataHeader *header ) { - if ( mRtpFactor > 0 ) - { - Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) ); - uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor ); - Debug( 5, "Local RTP time = %x", localTimeRtp ); - Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) ); - uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); - Debug( 5, "Packet transit RTP time = %x", packetTransit ); + if ( mRtpFactor > 0 ) + { + Debug( 5, "Delta rtp = %.6f", tvDiffSec( mBaseTimeReal ) ); + uint32_t localTimeRtp = mBaseTimeRtp + uint32_t( tvDiffSec( mBaseTimeReal ) * mRtpFactor ); + Debug( 5, "Local RTP time = %x", localTimeRtp ); + Debug( 5, "Packet RTP time = %x", ntohl(header->timestampN) ); + uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN); + Debug( 5, "Packet transit RTP time = %x", packetTransit ); - if ( mTransit > 0 ) - { - // Jitter - int d = packetTransit - mTransit; - Debug( 5, "Jitter D = %d", d ); - if ( d < 0 ) - d = -d; - //mJitter += (1./16.) * ((double)d - mJitter); - mJitter += d - ((mJitter + 8) >> 4); - } - mTransit = packetTransit; - } - else + if ( mTransit > 0 ) { - mJitter = 0; + // Jitter + int d = packetTransit - mTransit; + Debug( 5, "Jitter D = %d", d ); + if ( d < 0 ) + d = -d; + //mJitter += (1./16.) * ((double)d - mJitter); + mJitter += d - ((mJitter + 8) >> 4); } - Debug( 5, "RTP Jitter: %d", mJitter ); + mTransit = packetTransit; + } + else + { + mJitter = 0; + } + Debug( 5, "RTP Jitter: %d", mJitter ); } void RtpSource::updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ) { - struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) ); + struct timeval ntpTime = tvMake( ntpTimeSecs, suseconds_t((USEC_PER_SEC*(ntpTimeFrac>>16))/(1<<16)) ); + Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); + + if ( mBaseTimeNtp.tv_sec == 0 ) + { + mBaseTimeReal = tvNow(); + mBaseTimeNtp = ntpTime; + mBaseTimeRtp = rtpTime; + } + else if ( !mRtpClock ) + { + Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime ); Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); - - if ( mBaseTimeNtp.tv_sec == 0 ) - { - mBaseTimeReal = tvNow(); - mBaseTimeNtp = ntpTime; - mBaseTimeRtp = rtpTime; - } - else if ( !mRtpClock ) - { - Debug( 5, "lastSrNtpTime: %ld.%06ld, rtpTime: %x", mLastSrTimeNtp.tv_sec, mLastSrTimeNtp.tv_usec, rtpTime ); - Debug( 5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime ); - double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime ); - uint32_t diffRtpTime = rtpTime - mBaseTimeRtp; + double diffNtpTime = tvDiffSec( mBaseTimeNtp, ntpTime ); + uint32_t diffRtpTime = rtpTime - mBaseTimeRtp; - //Debug( 5, "Real-diff: %.6f", diffRealTime ); - Debug( 5, "NTP-diff: %.6f", diffNtpTime ); - Debug( 5, "RTP-diff: %d", diffRtpTime ); + //Debug( 5, "Real-diff: %.6f", diffRealTime ); + Debug( 5, "NTP-diff: %.6f", diffNtpTime ); + Debug( 5, "RTP-diff: %d", diffRtpTime ); - mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime); + mRtpFactor = (uint32_t)(diffRtpTime / diffNtpTime); - Debug( 5, "RTPfactor: %d", mRtpFactor ); - } - mLastSrTimeNtpSecs = ntpTimeSecs; - mLastSrTimeNtpFrac = ntpTimeFrac; - mLastSrTimeNtp = ntpTime; - mLastSrTimeRtp = rtpTime; + Debug( 5, "RTPfactor: %d", mRtpFactor ); + } + mLastSrTimeNtpSecs = ntpTimeSecs; + mLastSrTimeNtpFrac = ntpTimeFrac; + mLastSrTimeNtp = ntpTime; + mLastSrTimeRtp = rtpTime; } void RtpSource::updateRtcpStats() { - uint32_t extendedMax = mCycles + mMaxSeq; - mExpectedPackets = extendedMax - mBaseSeq + 1; + uint32_t extendedMax = mCycles + mMaxSeq; + mExpectedPackets = extendedMax - mBaseSeq + 1; - Debug( 5, "Expected packets = %d", mExpectedPackets ); + Debug( 5, "Expected packets = %d", mExpectedPackets ); - // The number of packets lost is defined to be the number of packets - // expected less the number of packets actually received: - mLostPackets = mExpectedPackets - mReceivedPackets; - Debug( 5, "Lost packets = %d", mLostPackets ); + // The number of packets lost is defined to be the number of packets + // expected less the number of packets actually received: + mLostPackets = mExpectedPackets - mReceivedPackets; + Debug( 5, "Lost packets = %d", mLostPackets ); - uint32_t expectedInterval = mExpectedPackets - mExpectedPrior; - Debug( 5, "Expected interval = %d", expectedInterval ); - mExpectedPrior = mExpectedPackets; - uint32_t receivedInterval = mReceivedPackets - mReceivedPrior; - Debug( 5, "Received interval = %d", receivedInterval ); - mReceivedPrior = mReceivedPackets; - uint32_t lostInterval = expectedInterval - receivedInterval; - Debug( 5, "Lost interval = %d", lostInterval ); + uint32_t expectedInterval = mExpectedPackets - mExpectedPrior; + Debug( 5, "Expected interval = %d", expectedInterval ); + mExpectedPrior = mExpectedPackets; + uint32_t receivedInterval = mReceivedPackets - mReceivedPrior; + Debug( 5, "Received interval = %d", receivedInterval ); + mReceivedPrior = mReceivedPackets; + uint32_t lostInterval = expectedInterval - receivedInterval; + Debug( 5, "Lost interval = %d", lostInterval ); - if ( expectedInterval == 0 || lostInterval <= 0 ) - mLostFraction = 0; - else - mLostFraction = (lostInterval << 8) / expectedInterval; - Debug( 5, "Lost fraction = %d", mLostFraction ); + if ( expectedInterval == 0 || lostInterval <= 0 ) + mLostFraction = 0; + else + mLostFraction = (lostInterval << 8) / expectedInterval; + Debug( 5, "Lost fraction = %d", mLostFraction ); } bool RtpSource::handlePacket( const unsigned char *packet, size_t packetLen ) { - const RtpDataHeader *rtpHeader; - rtpHeader = (RtpDataHeader *)packet; - int rtpHeaderSize = 12 + rtpHeader->cc * 4; - // No need to check for nal type as non fragmented packets already have 001 start sequence appended - bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40); - bool thisM = rtpHeader->m || h264FragmentEnd; + const RtpDataHeader *rtpHeader; + rtpHeader = (RtpDataHeader *)packet; + int rtpHeaderSize = 12 + rtpHeader->cc * 4; + // No need to check for nal type as non fragmented packets already have 001 start sequence appended + bool h264FragmentEnd = (mCodecId == AV_CODEC_ID_H264) && (packet[rtpHeaderSize+1] & 0x40); + bool thisM = rtpHeader->m || h264FragmentEnd; - if ( updateSeq( ntohs(rtpHeader->seqN) ) ) + if ( updateSeq( ntohs(rtpHeader->seqN) ) ) + { + Hexdump( 4, packet+rtpHeaderSize, 16 ); + + if ( mFrameGood ) { - Hexdump( 4, packet+rtpHeaderSize, 16 ); - - if ( mFrameGood ) + int extraHeader = 0; + + if( mCodecId == AV_CODEC_ID_H264 ) + { + int nalType = (packet[rtpHeaderSize] & 0x1f); + + switch (nalType) { - int extraHeader = 0; - - if( mCodecId == AV_CODEC_ID_H264 ) + case 24: + { + extraHeader = 2; + break; + } + case 25: case 26: case 27: + { + extraHeader = 3; + break; + } + // FU-A and FU-B + case 28: case 29: + { + // Is this NAL the first NAL in fragmentation sequence + if ( packet[rtpHeaderSize+1] & 0x80 ) { - int nalType = (packet[rtpHeaderSize] & 0x1f); - - switch (nalType) - { - case 24: - { - extraHeader = 2; - break; - } - case 25: case 26: case 27: - { - extraHeader = 3; - break; - } - // FU-A and FU-B - case 28: case 29: - { - // Is this NAL the first NAL in fragmentation sequence - if ( packet[rtpHeaderSize+1] & 0x80 ) - { - // Now we will form new header of frame - mFrame.append( "\x0\x0\x1\x0", 4 ); - // Reconstruct NAL header from FU headers - *(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) | - (packet[rtpHeaderSize] & 0xe0); - } - - extraHeader = 2; - break; - } - } - - // Append NAL frame start code - if ( !mFrame.size() ) - mFrame.append( "\x0\x0\x1", 3 ); + // Now we will form new header of frame + mFrame.append( "\x0\x0\x1\x0", 4 ); + // Reconstruct NAL header from FU headers + *(mFrame+3) = (packet[rtpHeaderSize+1] & 0x1f) | + (packet[rtpHeaderSize] & 0xe0); } - mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader ); - } - - Hexdump( 4, mFrame.head(), 16 ); - - if ( thisM ) - { - if ( mFrameGood ) - { - Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() ); - - mFrameProcessed.setValueImmediate( false ); - mFrameReady.updateValueSignal( true ); - if ( !mFrameProcessed.getValueImmediate() ) - { - for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ ) - if( count > 1 ) - return( false ); - } - mFrameCount++; - } - else - { - Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() ); - } - mFrame.clear(); + + extraHeader = 2; + break; + } } + + // Append NAL frame start code + if ( !mFrame.size() ) + mFrame.append( "\x0\x0\x1", 3 ); + } + mFrame.append( packet+rtpHeaderSize+extraHeader, packetLen-rtpHeaderSize-extraHeader ); } - else - { - if ( mFrame.size() ) - { - Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() ); - } - else - { - Warning( "Discarding frame %d", mFrameCount ); - } - mFrameGood = false; - mFrame.clear(); - } + + Hexdump( 4, mFrame.head(), 16 ); + if ( thisM ) { - mFrameGood = true; - prevM = true; + if ( mFrameGood ) + { + Debug( 2, "Got new frame %d, %d bytes", mFrameCount, mFrame.size() ); + + mFrameProcessed.setValueImmediate( false ); + mFrameReady.updateValueSignal( true ); + if ( !mFrameProcessed.getValueImmediate() ) + { + for ( int count = 0; !mFrameProcessed.getUpdatedValue( 1 ); count++ ) + if( count > 1 ) + return( false ); + } + mFrameCount++; + } + else + { + Warning( "Discarding incomplete frame %d, %d bytes", mFrameCount, mFrame.size() ); + } + mFrame.clear(); + } + } + else + { + if ( mFrame.size() ) + { + Warning( "Discarding partial frame %d, %d bytes", mFrameCount, mFrame.size() ); } else - prevM = false; + { + Warning( "Discarding frame %d", mFrameCount ); + } + mFrameGood = false; + mFrame.clear(); + } + if ( thisM ) + { + mFrameGood = true; + prevM = true; + } + else + prevM = false; - updateJitter( rtpHeader ); + updateJitter( rtpHeader ); - return( true ); + return( true ); } bool RtpSource::getFrame( Buffer &buffer ) { - Debug( 3, "Getting frame" ); - if ( !mFrameReady.getValueImmediate() ) - { - // Allow for a couple of spurious returns - for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ ) - if ( count > 1 ) - return( false ); - } - buffer = mFrame; - mFrameReady.setValueImmediate( false ); - mFrameProcessed.updateValueSignal( true ); - Debug( 3, "Copied %d bytes", buffer.size() ); - return( true ); + Debug( 3, "Getting frame" ); + if ( !mFrameReady.getValueImmediate() ) + { + // Allow for a couple of spurious returns + for ( int count = 0; !mFrameReady.getUpdatedValue( 1 ); count++ ) + if ( count > 1 ) + return( false ); + } + buffer = mFrame; + mFrameReady.setValueImmediate( false ); + mFrameProcessed.updateValueSignal( true ); + Debug( 3, "Copied %d bytes", buffer.size() ); + return( true ); } #endif // HAVE_LIBAVCODEC diff --git a/src/zm_rtp_source.h b/src/zm_rtp_source.h index cd41424ac..b86577e01 100644 --- a/src/zm_rtp_source.h +++ b/src/zm_rtp_source.h @@ -35,152 +35,152 @@ struct RtpDataHeader; class RtpSource { public: - typedef enum { EMPTY, FILLING, READY } FrameState; + typedef enum { EMPTY, FILLING, READY } FrameState; private: - static const int RTP_SEQ_MOD = 1<<16; - static const int MAX_DROPOUT = 3000; - static const int MAX_MISORDER = 100; - static const int MIN_SEQUENTIAL = 2; + static const int RTP_SEQ_MOD = 1<<16; + static const int MAX_DROPOUT = 3000; + static const int MAX_MISORDER = 100; + static const int MIN_SEQUENTIAL = 2; private: - // Identity - int mId; // General id (usually monitor id) - std::string mCname; // Canonical name, for SDES + // Identity + int mId; // General id (usually monitor id) + std::string mCname; // Canonical name, for SDES - // RTP/RTCP fields - uint32_t mSsrc; - uint16_t mMaxSeq; // highest seq. number seen - uint32_t mCycles; // shifted count of seq. number cycles - uint32_t mBaseSeq; // base seq number - uint32_t mBadSeq; // last 'bad' seq number + 1 - uint32_t mProbation; // sequ. packets till source is valid - uint32_t mReceivedPackets; // packets received - uint32_t mExpectedPrior; // packet expected at last interval - uint32_t mReceivedPrior; // packet received at last interval - uint32_t mTransit; // relative trans time for prev pkt - uint32_t mJitter; // estimated jitter - - // Ports/Channels - std::string mLocalHost; - int mLocalPortChans[2]; - std::string mRemoteHost; - int mRemotePortChans[2]; + // RTP/RTCP fields + uint32_t mSsrc; + uint16_t mMaxSeq; // highest seq. number seen + uint32_t mCycles; // shifted count of seq. number cycles + uint32_t mBaseSeq; // base seq number + uint32_t mBadSeq; // last 'bad' seq number + 1 + uint32_t mProbation; // sequ. packets till source is valid + uint32_t mReceivedPackets; // packets received + uint32_t mExpectedPrior; // packet expected at last interval + uint32_t mReceivedPrior; // packet received at last interval + uint32_t mTransit; // relative trans time for prev pkt + uint32_t mJitter; // estimated jitter + + // Ports/Channels + std::string mLocalHost; + int mLocalPortChans[2]; + std::string mRemoteHost; + int mRemotePortChans[2]; - // Time keys - uint32_t mRtpClock; - uint32_t mRtpFactor; - struct timeval mBaseTimeReal; - struct timeval mBaseTimeNtp; - uint32_t mBaseTimeRtp; + // Time keys + uint32_t mRtpClock; + uint32_t mRtpFactor; + struct timeval mBaseTimeReal; + struct timeval mBaseTimeNtp; + uint32_t mBaseTimeRtp; - struct timeval mLastSrTimeReal; - uint32_t mLastSrTimeNtpSecs; - uint32_t mLastSrTimeNtpFrac; - struct timeval mLastSrTimeNtp; - uint32_t mLastSrTimeRtp; + struct timeval mLastSrTimeReal; + uint32_t mLastSrTimeNtpSecs; + uint32_t mLastSrTimeNtpFrac; + struct timeval mLastSrTimeNtp; + uint32_t mLastSrTimeRtp; - // Stats, intermittently updated - uint32_t mExpectedPackets; - uint32_t mLostPackets; - uint8_t mLostFraction; + // Stats, intermittently updated + uint32_t mExpectedPackets; + uint32_t mLostPackets; + uint8_t mLostFraction; - _AVCODECID mCodecId; + _AVCODECID mCodecId; - Buffer mFrame; - int mFrameCount; - bool mFrameGood; - bool prevM; - ThreadData mFrameReady; - ThreadData mFrameProcessed; + Buffer mFrame; + int mFrameCount; + bool mFrameGood; + bool prevM; + ThreadData mFrameReady; + ThreadData mFrameProcessed; private: - void init( uint16_t seq ); + void init( uint16_t seq ); public: - RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ); - - bool updateSeq( uint16_t seq ); - void updateJitter( const RtpDataHeader *header ); - void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ); - void updateRtcpStats(); + RtpSource( int id, const std::string &localHost, int localPortBase, const std::string &remoteHost, int remotePortBase, uint32_t ssrc, uint16_t seq, uint32_t rtpClock, uint32_t rtpTime, _AVCODECID codecId ); + + bool updateSeq( uint16_t seq ); + void updateJitter( const RtpDataHeader *header ); + void updateRtcpData( uint32_t ntpTimeSecs, uint32_t ntpTimeFrac, uint32_t rtpTime ); + void updateRtcpStats(); - bool handlePacket( const unsigned char *packet, size_t packetLen ); + bool handlePacket( const unsigned char *packet, size_t packetLen ); - uint32_t getSsrc() const - { - return( mSsrc ); - } - void setSsrc( uint32_t ssrc ) - { - mSsrc = ssrc; - } + uint32_t getSsrc() const + { + return( mSsrc ); + } + void setSsrc( uint32_t ssrc ) + { + mSsrc = ssrc; + } - bool getFrame( Buffer &buffer ); + bool getFrame( Buffer &buffer ); - const std::string &getCname() const - { - return( mCname ); - } + const std::string &getCname() const + { + return( mCname ); + } - const std::string &getLocalHost() const - { - return( mLocalHost ); - } + const std::string &getLocalHost() const + { + return( mLocalHost ); + } - int getLocalDataPort() const - { - return( mLocalPortChans[0] ); - } + int getLocalDataPort() const + { + return( mLocalPortChans[0] ); + } - int getLocalCtrlPort() const - { - return( mLocalPortChans[1] ); - } + int getLocalCtrlPort() const + { + return( mLocalPortChans[1] ); + } - const std::string &getRemoteHost() const - { - return( mRemoteHost ); - } + const std::string &getRemoteHost() const + { + return( mRemoteHost ); + } - int getRemoteDataPort() const - { - return( mRemotePortChans[0] ); - } + int getRemoteDataPort() const + { + return( mRemotePortChans[0] ); + } - int getRemoteCtrlPort() const - { - return( mRemotePortChans[1] ); - } + int getRemoteCtrlPort() const + { + return( mRemotePortChans[1] ); + } - uint32_t getMaxSeq() const - { - return( mCycles + mMaxSeq ); - } + uint32_t getMaxSeq() const + { + return( mCycles + mMaxSeq ); + } - uint32_t getExpectedPackets() const - { - return( mExpectedPackets ); - } - - uint32_t getLostPackets() const - { - return( mLostPackets ); - } + uint32_t getExpectedPackets() const + { + return( mExpectedPackets ); + } + + uint32_t getLostPackets() const + { + return( mLostPackets ); + } - uint8_t getLostFraction() const - { - return( mLostFraction ); - } + uint8_t getLostFraction() const + { + return( mLostFraction ); + } - uint32_t getJitter() const - { - return( mJitter >> 4 ); - } + uint32_t getJitter() const + { + return( mJitter >> 4 ); + } - uint32_t getLastSrTimestamp() const - { - return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) ); - } + uint32_t getLastSrTimestamp() const + { + return( ((mLastSrTimeNtpSecs&0xffff)<<16)|(mLastSrTimeNtpFrac>>16) ); + } }; #endif // HAVE_LIBAVCODEC diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp index 384e1c80c..67f347d81 100644 --- a/src/zm_rtsp.cpp +++ b/src/zm_rtsp.cpp @@ -26,7 +26,6 @@ #include "zm_rtp_data.h" #include "zm_rtp_ctrl.h" #include "zm_db.h" -#include "zm_sdp.h" #include #include @@ -39,800 +38,860 @@ RtspThread::PortSet RtspThread::smAssignedPorts; bool RtspThread::sendCommand( std::string message ) { - if ( mNeedAuth ) { - StringVector parts = split( message, " " ); - if (parts.size() > 1) - message += mAuthenticator->getAuthHeader(parts[0], parts[1]); - } - message += stringtf( "User-Agent: ZoneMinder/%s\r\n", ZM_VERSION ); - message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq ); - Debug( 2, "Sending RTSP message: %s", message.c_str() ); - if ( mMethod == RTP_RTSP_HTTP ) + if ( mNeedAuth ) { + StringVector parts = split( message, " " ); + if (parts.size() > 1) + message += mAuthenticator->getAuthHeader(parts[0], parts[1]); + } + message += stringtf( "User-Agent: ZoneMinder/%s\r\n", ZM_VERSION ); + message += stringtf( "CSeq: %d\r\n\r\n", ++mSeq ); + Debug( 2, "Sending RTSP message: %s", message.c_str() ); + if ( mMethod == RTP_RTSP_HTTP ) + { + message = base64Encode( message ); + Debug( 2, "Sending encoded RTSP message: %s", message.c_str() ); + if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) { - message = base64Encode( message ); - Debug( 2, "Sending encoded RTSP message: %s", message.c_str() ); - if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) - { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( false ); - } + Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); + return( false ); } - else + } + else + { + if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) { - if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) - { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( false ); - } - } - return( true ); -} - -// find WWW-Authenticate header, send to Authenticator to extract required subfields -void RtspThread::checkAuthResponse(std::string &response) -{ - std::string authLine; - StringVector lines = split( response, "\r\n" ); - const char* authenticate_match = "WWW-Authenticate:"; - size_t authenticate_match_len = strlen(authenticate_match); - - for ( size_t i = 0; i < lines.size(); i++ ) - { - // stop at end of headers - if (lines[i].length()==0) - break; - - if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) - { - authLine = lines[i]; - Debug( 2, "Found auth line at %d", i); - break; - } - } - if (!authLine.empty()) - { - Debug( 2, "Analyze auth line %s", authLine.c_str()); - mAuthenticator->authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) ); + Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); + return( false ); } + } + return( true ); } bool RtspThread::recvResponse( std::string &response ) { - if ( mRtspSocket.recv( response ) < 0 ) - Error( "Recv failed; %s", strerror(errno) ); - Debug( 2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size() ); - float respVer = 0; - respCode = -1; - char respText[ZM_NETWORK_BUFSIZ]; - if ( sscanf( response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) + if ( mRtspSocket.recv( response ) < 0 ) + Error( "Recv failed; %s", strerror(errno) ); + Debug( 2, "Received RTSP response: %s (%zd bytes)", response.c_str(), response.size() ); + float respVer = 0; + respCode = -1; + char respText[ZM_NETWORK_BUFSIZ]; + if ( sscanf( response.c_str(), "RTSP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) + { + if ( isalnum(response[0]) ) { - if ( isalnum(response[0]) ) - { - Error( "Response parse failure in '%s'", response.c_str() ); - } - else - { - Error( "Response parse failure, %zd bytes follow", response.size() ); - if ( response.size() ) - Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); - } - return( false ); + Error( "Response parse failure in '%s'", response.c_str() ); } - if ( respCode == 401) + else { - Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry"); - checkAuthResponse(response); - mNeedAuth = true; - return( false ); - } - else if ( respCode != 200 ) - { - Error( "Unexpected response code %d, text is '%s'", respCode, respText ); - return( false ); + Error( "Response parse failure, %zd bytes follow", response.size() ); + if ( response.size() ) + Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); } - return( true ); + return( false ); + } + if ( respCode == 401) + { + Debug( 2, "Got 401 access denied response code, check WWW-Authenticate header and retry"); + mAuthenticator->checkAuthResponse(response); + mNeedAuth = true; + return( false ); + } + else if ( respCode != 200 ) + { + Error( "Unexpected response code %d, text is '%s'", respCode, respText ); + return( false ); + } + return( true ); } int RtspThread::requestPorts() { - if ( !smMinDataPort ) + if ( !smMinDataPort ) + { + char sql[ZM_SQL_SML_BUFSIZ]; + strncpy( sql, "select Id from Monitors where Function != 'None' and Type = 'Remote' and Protocol = 'rtsp' and Method = 'rtpUni' order by Id asc", sizeof(sql) ); + if ( mysql_query( &dbconn, sql ) ) { - char sql[ZM_SQL_SML_BUFSIZ]; - strncpy( sql, "select Id from Monitors where Function != 'None' and Type = 'Remote' and Protocol = 'rtsp' and Method = 'rtpUni' order by Id asc", sizeof(sql) ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int nMonitors = mysql_num_rows( result ); - int position = 0; - if ( nMonitors ) - { - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int id = atoi(dbrow[0]); - if ( mId == id ) - { - position = i; - break; - } - } - } - else - { - // Minor hack for testing when not strictly enabled - nMonitors = 1; - position = 0; - } - int portRange = int(((config.max_rtp_port-config.min_rtp_port)+1)/nMonitors); - smMinDataPort = config.min_rtp_port + (position * portRange); - smMaxDataPort = smMinDataPort + portRange - 1; - Debug( 2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort ); - } - for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) { - PortSet::const_iterator iter = smAssignedPorts.find( i ); - if ( iter == smAssignedPorts.end() ) - { - smAssignedPorts.insert( i ); - return( i ); - } + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); } - Panic( "Can assign RTP port, no ports left in pool" ); - return( -1 ); + int nMonitors = mysql_num_rows( result ); + int position = 0; + if ( nMonitors ) + { + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int id = atoi(dbrow[0]); + if ( mId == id ) + { + position = i; + break; + } + } + } + else + { + // Minor hack for testing when not strictly enabled + nMonitors = 1; + position = 0; + } + mysql_free_result(result); + int portRange = int(((config.max_rtp_port-config.min_rtp_port)+1)/nMonitors); + smMinDataPort = config.min_rtp_port + (position * portRange); + smMaxDataPort = smMinDataPort + portRange - 1; + Debug( 2, "Assigned RTP port range is %d-%d", smMinDataPort, smMaxDataPort ); + } + for ( int i = smMinDataPort; i <= smMaxDataPort; i++ ) + { + PortSet::const_iterator iter = smAssignedPorts.find( i ); + if ( iter == smAssignedPorts.end() ) + { + smAssignedPorts.insert( i ); + return( i ); + } + } + Panic( "Can assign RTP port, no ports left in pool" ); + return( -1 ); } void RtspThread::releasePorts( int port ) { - if ( port > 0 ) - smAssignedPorts.erase( port ); + if ( port > 0 ) + smAssignedPorts.erase( port ); } -RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth) : - mId( id ), - mMethod( method ), - mProtocol( protocol ), - mHost( host ), - mPort( port ), - mPath( path ), - mFormatContext( 0 ), - mSeq( 0 ), - mSession( 0 ), - mSsrc( 0 ), - mDist( UNDEFINED ), - mRtpTime( 0 ), - mStop( false ) +RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe) : + mId( id ), + mMethod( method ), + mProtocol( protocol ), + mHost( host ), + mPort( port ), + mPath( path ), + mRtspDescribe( rtsp_describe ), + mSessDesc( 0 ), + mFormatContext( 0 ), + mSeq( 0 ), + mSession( 0 ), + mSsrc( 0 ), + mDist( UNDEFINED ), + mRtpTime( 0 ), + mStop( false ) { - mUrl = mProtocol+"://"+mHost+":"+mPort; - if ( !mPath.empty() ) - { - if ( mPath[0] == '/' ) - mUrl += mPath; - else - mUrl += '/'+mPath; - } - - mSsrc = rand(); - - Debug( 2, "RTSP Local SSRC is %x", mSsrc ); - - if ( mMethod == RTP_RTSP_HTTP ) - mHttpSession = stringtf( "%d", rand() ); - - mNeedAuth = false; - StringVector parts = split(auth,":"); - if (parts.size() > 1) - mAuthenticator = new Authenticator(parts[0], parts[1]); + mUrl = mProtocol+"://"+mHost+":"+mPort; + if ( !mPath.empty() ) + { + if ( mPath[0] == '/' ) + mUrl += mPath; else - mAuthenticator = new Authenticator(parts[0], ""); + mUrl += '/'+mPath; + } + + mSsrc = rand(); + + Debug( 2, "RTSP Local SSRC is %x", mSsrc ); + + if ( mMethod == RTP_RTSP_HTTP ) + mHttpSession = stringtf( "%d", rand() ); + + mNeedAuth = false; + StringVector parts = split(auth,":"); + if (parts.size() > 1) + mAuthenticator = new zm::Authenticator(parts[0], parts[1]); + else + mAuthenticator = new zm::Authenticator(parts[0], ""); } RtspThread::~RtspThread() { + if ( mFormatContext ) + { +#if LIBAVFORMAT_VERSION_CHECK(52, 96, 0, 96, 0) + avformat_free_context( mFormatContext ); +#else + av_free_format_context( mFormatContext ); +#endif + mFormatContext = NULL; + } + if ( mSessDesc ) + { + delete mSessDesc; + mSessDesc = NULL; + } + delete mAuthenticator; } int RtspThread::run() { - std::string message; - std::string response; + std::string message; + std::string response; - response.reserve( ZM_NETWORK_BUFSIZ ); + response.reserve( ZM_NETWORK_BUFSIZ ); - if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) - Fatal( "Unable to connect RTSP socket" ); + if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) ) + Fatal( "Unable to connect RTSP socket" ); + //Select select( 0.25 ); + //select.addReader( &mRtspSocket ); + //while ( select.wait() ) + //{ + //mRtspSocket.recv( response ); + //Debug( 4, "Drained %d bytes from RTSP socket", response.size() ); + //} + + + bool authTried = false; + if ( mMethod == RTP_RTSP_HTTP ) + { + if ( !mRtspSocket2.connect( mHost.c_str(), mPort.c_str() ) ) + Fatal( "Unable to connect auxiliary RTSP/HTTP socket" ); //Select select( 0.25 ); - //select.addReader( &mRtspSocket ); + //select.addReader( &mRtspSocket2 ); //while ( select.wait() ) //{ - //mRtspSocket.recv( response ); - //Debug( 4, "Drained %d bytes from RTSP socket", response.size() ); + //mRtspSocket2.recv( response ); + //Debug( 4, "Drained %d bytes from HTTP socket", response.size() ); //} - - bool authTried = false; - if ( mMethod == RTP_RTSP_HTTP ) + //possibly retry sending the message for authentication + int respCode = -1; + char respText[256]; + do { + message = "GET "+mPath+" HTTP/1.0\r\n"; + message += "X-SessionCookie: "+mHttpSession+"\r\n"; + if ( mNeedAuth ) { + message += mAuthenticator->getAuthHeader("GET", mPath); + authTried = true; + } + message += "Accept: application/x-rtsp-tunnelled\r\n"; + message += "\r\n"; + Debug( 2, "Sending HTTP message: %s", message.c_str() ); + if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) + { + Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); + return( -1 ); + } + if ( mRtspSocket.recv( response ) < 0 ) + { + Error( "Recv failed; %s", strerror(errno) ); + return( -1 ); + } + + Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() ); + float respVer = 0; + respCode = -1; + if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) + { + if ( isalnum(response[0]) ) + { + Error( "Response parse failure in '%s'", response.c_str() ); + } + else + { + Error( "Response parse failure, %zd bytes follow", response.size() ); + if ( response.size() ) + Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); + } + return( -1 ); + } + // If Server requests authentication, check WWW-Authenticate header and fill required fields + // for requested authentication method + if (respCode == 401 && !authTried) { + mNeedAuth = true; + mAuthenticator->checkAuthResponse(response); + Debug(2, "Processed 401 response"); + mRtspSocket.close(); + if ( !mRtspSocket.connect( mHost.c_str(), mPort.c_str() ) ) + Fatal( "Unable to reconnect RTSP socket" ); + Debug(2, "connection should be reopened now"); + } + + } while (respCode == 401 && !authTried); + + if ( respCode != 200 ) { - if ( !mRtspSocket2.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) - Fatal( "Unable to connect auxiliary RTSP/HTTP socket" ); - //Select select( 0.25 ); - //select.addReader( &mRtspSocket2 ); - //while ( select.wait() ) - //{ - //mRtspSocket2.recv( response ); - //Debug( 4, "Drained %d bytes from HTTP socket", response.size() ); - //} - - //possibly retry sending the message for authentication - int respCode = -1; - char respText[256]; - do { - message = "GET "+mPath+" HTTP/1.0\r\n"; - message += "X-SessionCookie: "+mHttpSession+"\r\n"; - if ( mNeedAuth ) { - message += mAuthenticator->getAuthHeader("GET", mPath); - authTried = true; - } - message += "Accept: application/x-rtsp-tunnelled\r\n"; - message += "\r\n"; - Debug( 2, "Sending HTTP message: %s", message.c_str() ); - if ( mRtspSocket.send( message.c_str(), message.size() ) != (int)message.length() ) - { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( -1 ); - } - if ( mRtspSocket.recv( response ) < 0 ) - { - Error( "Recv failed; %s", strerror(errno) ); - return( -1 ); - } - - Debug( 2, "Received HTTP response: %s (%zd bytes)", response.c_str(), response.size() ); - float respVer = 0; - respCode = -1; - if ( sscanf( response.c_str(), "HTTP/%f %3d %[^\r\n]\r\n", &respVer, &respCode, respText ) != 3 ) - { - if ( isalnum(response[0]) ) - { - Error( "Response parse failure in '%s'", response.c_str() ); - } - else - { - Error( "Response parse failure, %zd bytes follow", response.size() ); - if ( response.size() ) - Hexdump( Logger::ERROR, response.data(), min(response.size(),16) ); - } - return( -1 ); - } - // If Server requests authentication, check WWW-Authenticate header and fill required fields - // for requested authentication method - if (respCode == 401 && !authTried) { - mNeedAuth = true; - checkAuthResponse(response); - Debug(2, "Processed 401 response"); - mRtspSocket.close(); - if ( !mRtspSocket.connect( mHost.c_str(), strtol( mPort.c_str(), NULL, 10 ) ) ) - Fatal( "Unable to reconnect RTSP socket" ); - Debug(2, "connection should be reopened now"); - } - - } while (respCode == 401 && !authTried); - - if ( respCode != 200 ) - { - Error( "Unexpected response code %d, text is '%s'", respCode, respText ); - return( -1 ); - } - - message = "POST "+mPath+" HTTP/1.0\r\n"; - message += "X-SessionCookie: "+mHttpSession+"\r\n"; - if ( mNeedAuth ) - message += mAuthenticator->getAuthHeader("POST", mPath); - message += "Content-Length: 32767\r\n"; - message += "Content-Type: application/x-rtsp-tunnelled\r\n"; - message += "\r\n"; - Debug( 2, "Sending HTTP message: %s", message.c_str() ); - if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) - { - Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); - return( -1 ); - } + Error( "Unexpected response code %d, text is '%s'", respCode, respText ); + return( -1 ); } - std::string localHost = ""; - int localPorts[2] = { 0, 0 }; + message = "POST "+mPath+" HTTP/1.0\r\n"; + message += "X-SessionCookie: "+mHttpSession+"\r\n"; + if ( mNeedAuth ) + message += mAuthenticator->getAuthHeader("POST", mPath); + message += "Content-Length: 32767\r\n"; + message += "Content-Type: application/x-rtsp-tunnelled\r\n"; + message += "\r\n"; + Debug( 2, "Sending HTTP message: %s", message.c_str() ); + if ( mRtspSocket2.send( message.c_str(), message.size() ) != (int)message.length() ) + { + Error( "Unable to send message '%s': %s", message.c_str(), strerror(errno) ); + return( -1 ); + } + } - // Request supported RTSP commands by the server - message = "OPTIONS * RTSP/1.0\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); + std::string localHost = ""; + int localPorts[2] = { 0, 0 }; - char publicLine[256] = ""; - StringVector lines = split( response, "\r\n" ); + // Request supported RTSP commands by the server + message = "OPTIONS "+mUrl+" RTSP/1.0\r\n"; + if ( !sendCommand( message ) ) + return( -1 ); + + // A negative return here may indicate auth failure, but we will have setup the auth mechanisms so we need to retry. + if ( !recvResponse( response ) ) { + if ( mNeedAuth ) { + Debug( 2, "Resending OPTIONS due to possible auth requirement" ); + if ( !sendCommand( message ) ) + return( -1 ); + if ( !recvResponse( response ) ) + return( -1 ); + } else { + return( -1 ); + } + } // end if failed response maybe due to auth + + char publicLine[256] = ""; + StringVector lines = split( response, "\r\n" ); + for ( size_t i = 0; i < lines.size(); i++ ) + sscanf( lines[i].c_str(), "Public: %[^\r\n]\r\n", publicLine ); + + // Check if the server supports the GET_PARAMETER command + // If yes, it is likely that the server will request this command as a keepalive message + bool sendKeepalive = false; + if ( publicLine[0] && strstr(publicLine, "GET_PARAMETER") ) + sendKeepalive = true; + + message = "DESCRIBE "+mUrl+" RTSP/1.0\r\n"; + bool res; + do { + if (mNeedAuth) + authTried = true; + sendCommand( message ); + sleep( 1 ); + res = recvResponse( response ); + if (!res && respCode==401) + mNeedAuth = true; + } while (!res && respCode==401 && !authTried); + + const std::string endOfHeaders = "\r\n\r\n"; + size_t sdpStart = response.find( endOfHeaders ); + if( sdpStart == std::string::npos ) + return( -1 ); + + if ( mRtspDescribe ) + { + std::string DescHeader = response.substr( 0,sdpStart ); + Debug( 1, "Processing DESCRIBE response header '%s'", DescHeader.c_str() ); + + lines = split( DescHeader, "\r\n" ); for ( size_t i = 0; i < lines.size(); i++ ) - sscanf( lines[i].c_str(), "Public: %[^\r\n]\r\n", publicLine ); + { + // If the device sends us a url value for Content-Base in the response header, we should use that instead + if ( ( lines[i].size() > 13 ) && ( lines[i].substr( 0, 13 ) == "Content-Base:" ) ) + { + mUrl = trimSpaces( lines[i].substr( 13 ) ); + Info("Received new Content-Base in DESCRIBE response header. Updated device Url to: '%s'", mUrl.c_str() ); + break; + } + } + } - // Check if the server supports the GET_PARAMETER command - // If yes, it is likely that the server will request this command as a keepalive message - bool sendKeepalive = false; - if ( publicLine[0] && strstr(publicLine, "GET_PARAMETER") ) - sendKeepalive = true; + sdpStart += endOfHeaders.length(); - message = "DESCRIBE "+mUrl+" RTSP/1.0\r\n"; - bool res; - do { - if (mNeedAuth) - authTried = true; - sendCommand( message ); - sleep( 1 ); - res = recvResponse( response ); - if (!res && respCode==401) - mNeedAuth = true; - } while (!res && respCode==401 && !authTried); + std::string sdp = response.substr( sdpStart ); + Debug( 1, "Processing SDP '%s'", sdp.c_str() ); - const std::string endOfHeaders = "\r\n\r\n"; - size_t sdpStart = response.find( endOfHeaders ); - if( sdpStart == std::string::npos ) - return( -1 ); - sdpStart += endOfHeaders.length(); - - std::string sdp = response.substr( sdpStart ); - Debug( 1, "Processing SDP '%s'", sdp.c_str() ); - - SessionDescriptor *sessDesc = 0; - try - { - sessDesc = new SessionDescriptor( mUrl, sdp ); - mFormatContext = sessDesc->generateFormatContext(); - } - catch( const Exception &e ) - { - Error( e.getMessage().c_str() ); - return( -1 ); - } + try + { + mSessDesc = new SessionDescriptor( mUrl, sdp ); + mFormatContext = mSessDesc->generateFormatContext(); + } + catch( const Exception &e ) + { + Error( e.getMessage().c_str() ); + return( -1 ); + } #if 0 - // New method using ffmpeg native functions - std::string authUrl = mUrl; - if ( !mAuth.empty() ) - authUrl.insert( authUrl.find( "://" )+3, mAuth+"@" ); + // New method using ffmpeg native functions + std::string authUrl = mUrl; + if ( !mAuth.empty() ) + authUrl.insert( authUrl.find( "://" )+3, mAuth+"@" ); - if ( av_open_input_file( &mFormatContext, authUrl.c_str(), NULL, 0, NULL ) != 0 ) - { - Error( "Unable to open input '%s'", authUrl.c_str() ); - return( -1 ); - } + if ( av_open_input_file( &mFormatContext, authUrl.c_str(), NULL, 0, NULL ) != 0 ) + { + Error( "Unable to open input '%s'", authUrl.c_str() ); + return( -1 ); + } #endif - uint32_t rtpClock = 0; - std::string trackUrl = mUrl; - - _AVCODECID codecId; - - if ( mFormatContext->nb_streams >= 1 ) + uint32_t rtpClock = 0; + std::string trackUrl = mUrl; + std::string controlUrl; + + _AVCODECID codecId; + + if ( mFormatContext->nb_streams >= 1 ) + { + for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) { - for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) - { - SessionDescriptor::MediaDescriptor *mediaDesc = sessDesc->getStream( i ); -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) - if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) + SessionDescriptor::MediaDescriptor *mediaDesc = mSessDesc->getStream( i ); +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) + if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) #else - if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) + if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) #endif - { - // Check if control Url is absolute or relative - std::string controlUrl = mediaDesc->getControlUrl(); - if (std::equal(trackUrl.begin(), trackUrl.end(), controlUrl.begin())) - { - trackUrl = controlUrl; - } - else - { - trackUrl += "/" + controlUrl; - } - rtpClock = mediaDesc->getClock(); - codecId = mFormatContext->streams[i]->codec->codec_id; - // Hackery pokery - //rtpClock = mFormatContext->streams[i]->codec->sample_rate; - break; - } + { + // Check if control Url is absolute or relative + controlUrl = mediaDesc->getControlUrl(); + if (std::equal(trackUrl.begin(), trackUrl.end(), controlUrl.begin())) + { + trackUrl = controlUrl; } + else + { + if ( *trackUrl.rbegin() != '/') { + trackUrl += "/" + controlUrl; + } else { + trackUrl += controlUrl; + } + } + rtpClock = mediaDesc->getClock(); + codecId = mFormatContext->streams[i]->codec->codec_id; + // Hackery pokery + //rtpClock = mFormatContext->streams[i]->codec->sample_rate; + break; + } } + } - switch( mMethod ) + switch( mMethod ) + { + case RTP_UNICAST : { - case RTP_UNICAST : - { - localPorts[0] = requestPorts(); - localPorts[1] = localPorts[0]+1; + localPorts[0] = requestPorts(); + localPorts[1] = localPorts[0]+1; - message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;unicast;client_port="+stringtf( "%d", localPorts[0] )+"-"+stringtf( "%d", localPorts[1] )+"\r\n"; - break; - } - case RTP_MULTICAST : - { - message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;multicast\r\n"; - break; - } - case RTP_RTSP : - case RTP_RTSP_HTTP : - { - message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP/TCP;unicast\r\n"; - break; - } - default: - { - Panic( "Got unexpected method %d", mMethod ); - break; - } + message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;unicast;client_port="+stringtf( "%d", localPorts[0] )+"-"+stringtf( "%d", localPorts[1] )+"\r\n"; + break; } - - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); - - lines = split( response, "\r\n" ); - char *session = 0; - int timeout = 0; - char transport[256] = ""; - - for ( size_t i = 0; i < lines.size(); i++ ) + case RTP_MULTICAST : { - sscanf( lines[i].c_str(), "Session: %a[0-9a-fA-F]; timeout=%d", &session, &timeout ); - sscanf( lines[i].c_str(), "Transport: %s", transport ); + message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP;multicast\r\n"; + break; } - - if ( !session ) - Fatal( "Unable to get session identifier from response '%s'", response.c_str() ); - - Debug( 2, "Got RTSP session %s, timeout %d secs", session, timeout ); - - if ( !transport[0] ) - Fatal( "Unable to get transport details from response '%s'", response.c_str() ); - - Debug( 2, "Got RTSP transport %s", transport ); - - std::string method = ""; - int remotePorts[2] = { 0, 0 }; - int remoteChannels[2] = { 0, 0 }; - std::string distribution = ""; - unsigned long ssrc = 0; - StringVector parts = split( transport, ";" ); - for ( size_t i = 0; i < parts.size(); i++ ) + case RTP_RTSP : + case RTP_RTSP_HTTP : { - if ( parts[i] == "unicast" || parts[i] == "multicast" ) - distribution = parts[i]; - else if ( startsWith( parts[i], "server_port=" ) ) - { - method = "RTP/UNICAST"; - StringVector subparts = split( parts[i], "=" ); - StringVector ports = split( subparts[1], "-" ); - remotePorts[0] = strtol( ports[0].c_str(), NULL, 10 ); - remotePorts[1] = strtol( ports[1].c_str(), NULL, 10 ); - } - else if ( startsWith( parts[i], "interleaved=" ) ) - { - method = "RTP/RTSP"; - StringVector subparts = split( parts[i], "=" ); - StringVector channels = split( subparts[1], "-" ); - remoteChannels[0] = strtol( channels[0].c_str(), NULL, 10 ); - remoteChannels[1] = strtol( channels[1].c_str(), NULL, 10 ); - } - else if ( startsWith( parts[i], "port=" ) ) - { - method = "RTP/MULTICAST"; - StringVector subparts = split( parts[i], "=" ); - StringVector ports = split( subparts[1], "-" ); - localPorts[0] = strtol( ports[0].c_str(), NULL, 10 ); - localPorts[1] = strtol( ports[1].c_str(), NULL, 10 ); - } - else if ( startsWith( parts[i], "destination=" ) ) - { - StringVector subparts = split( parts[i], "=" ); - localHost = subparts[1]; - } - else if ( startsWith( parts[i], "ssrc=" ) ) - { - StringVector subparts = split( parts[i], "=" ); - ssrc = strtoll( subparts[1].c_str(), NULL, 16 ); - } + message = "SETUP "+trackUrl+" RTSP/1.0\r\nTransport: RTP/AVP/TCP;unicast\r\n"; + break; } - - Debug( 2, "RTSP Method is %s", method.c_str() ); - Debug( 2, "RTSP Distribution is %s", distribution.c_str() ); - Debug( 2, "RTSP SSRC is %lx", ssrc ); - Debug( 2, "RTSP Local Host is %s", localHost.c_str() ); - Debug( 2, "RTSP Local Ports are %d/%d", localPorts[0], localPorts[1] ); - Debug( 2, "RTSP Remote Ports are %d/%d", remotePorts[0], remotePorts[1] ); - Debug( 2, "RTSP Remote Channels are %d/%d", remoteChannels[0], remoteChannels[1] ); - - message = "PLAY "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\nRange: npt=0.000-\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); - - lines = split( response, "\r\n" ); - char *rtpInfo = 0; - for ( size_t i = 0; i < lines.size(); i++ ) + default: { - sscanf( lines[i].c_str(), "RTP-Info: %as", &rtpInfo ); + Panic( "Got unexpected method %d", mMethod ); + break; } + } - if ( !rtpInfo ) - Fatal( "Unable to get RTP Info identifier from response '%s'", response.c_str() ); + if ( !sendCommand( message ) ) + return( -1 ); + if ( !recvResponse( response ) ) + return( -1 ); - Debug( 2, "Got RTP Info %s", rtpInfo ); + lines = split( response, "\r\n" ); + std::string session; + int timeout = 0; + char transport[256] = ""; - int seq = 0; - unsigned long rtpTime = 0; - parts = split( rtpInfo, ";" ); - for ( size_t i = 0; i < parts.size(); i++ ) + for ( size_t i = 0; i < lines.size(); i++ ) + { + if ( ( lines[i].size() > 8 ) && ( lines[i].substr( 0, 8 ) == "Session:" ) ) { - if ( startsWith( parts[i], "seq=" ) ) + StringVector sessionLine = split( lines[i].substr(9), ";" ); + session = trimSpaces( sessionLine[0] ); + if ( sessionLine.size() == 2 ) + sscanf( trimSpaces( sessionLine[1] ).c_str(), "timeout=%d", &timeout ); + } + sscanf( lines[i].c_str(), "Transport: %s", transport ); + } + + if ( session.empty() ) + Fatal( "Unable to get session identifier from response '%s'", response.c_str() ); + + Debug( 2, "Got RTSP session %s, timeout %d secs", session.c_str(), timeout ); + + if ( !transport[0] ) + Fatal( "Unable to get transport details from response '%s'", response.c_str() ); + + Debug( 2, "Got RTSP transport %s", transport ); + + std::string method = ""; + int remotePorts[2] = { 0, 0 }; + int remoteChannels[2] = { 0, 0 }; + std::string distribution = ""; + unsigned long ssrc = 0; + StringVector parts = split( transport, ";" ); + for ( size_t i = 0; i < parts.size(); i++ ) + { + if ( parts[i] == "unicast" || parts[i] == "multicast" ) + distribution = parts[i]; + else if ( startsWith( parts[i], "server_port=" ) ) + { + method = "RTP/UNICAST"; + StringVector subparts = split( parts[i], "=" ); + StringVector ports = split( subparts[1], "-" ); + remotePorts[0] = strtol( ports[0].c_str(), NULL, 10 ); + remotePorts[1] = strtol( ports[1].c_str(), NULL, 10 ); + } + else if ( startsWith( parts[i], "interleaved=" ) ) + { + method = "RTP/RTSP"; + StringVector subparts = split( parts[i], "=" ); + StringVector channels = split( subparts[1], "-" ); + remoteChannels[0] = strtol( channels[0].c_str(), NULL, 10 ); + remoteChannels[1] = strtol( channels[1].c_str(), NULL, 10 ); + } + else if ( startsWith( parts[i], "port=" ) ) + { + method = "RTP/MULTICAST"; + StringVector subparts = split( parts[i], "=" ); + StringVector ports = split( subparts[1], "-" ); + localPorts[0] = strtol( ports[0].c_str(), NULL, 10 ); + localPorts[1] = strtol( ports[1].c_str(), NULL, 10 ); + } + else if ( startsWith( parts[i], "destination=" ) ) + { + StringVector subparts = split( parts[i], "=" ); + localHost = subparts[1]; + } + else if ( startsWith( parts[i], "ssrc=" ) ) + { + StringVector subparts = split( parts[i], "=" ); + ssrc = strtoll( subparts[1].c_str(), NULL, 16 ); + } + } + + Debug( 2, "RTSP Method is %s", method.c_str() ); + Debug( 2, "RTSP Distribution is %s", distribution.c_str() ); + Debug( 2, "RTSP SSRC is %lx", ssrc ); + Debug( 2, "RTSP Local Host is %s", localHost.c_str() ); + Debug( 2, "RTSP Local Ports are %d/%d", localPorts[0], localPorts[1] ); + Debug( 2, "RTSP Remote Ports are %d/%d", remotePorts[0], remotePorts[1] ); + Debug( 2, "RTSP Remote Channels are %d/%d", remoteChannels[0], remoteChannels[1] ); + + message = "PLAY "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\nRange: npt=0.000-\r\n"; + if ( !sendCommand( message ) ) + return( -1 ); + if ( !recvResponse( response ) ) + return( -1 ); + + lines = split( response, "\r\n" ); + std::string rtpInfo; + for ( size_t i = 0; i < lines.size(); i++ ) + { + if ( ( lines[i].size() > 9 ) && ( lines[i].substr( 0, 9 ) == "RTP-Info:" ) ) + rtpInfo = trimSpaces( lines[i].substr( 9 ) ); + // Check for a timeout again. Some rtsp devices don't send a timeout until after the PLAY command is sent + if ( ( lines[i].size() > 8 ) && ( lines[i].substr( 0, 8 ) == "Session:" ) && ( timeout == 0 ) ) + { + StringVector sessionLine = split( lines[i].substr(9), ";" ); + if ( sessionLine.size() == 2 ) + sscanf( trimSpaces( sessionLine[1] ).c_str(), "timeout=%d", &timeout ); + if ( timeout > 0 ) + Debug( 2, "Got timeout %d secs from PLAY command response", timeout ); + } + } + + int seq = 0; + unsigned long rtpTime = 0; + StringVector streams; + if ( rtpInfo.empty() ) + { + Debug( 1, "RTP Info Empty. Starting values for Sequence and Rtptime shall be zero."); + } + else + { + Debug( 2, "Got RTP Info %s", rtpInfo.c_str() ); + // More than one stream can be included in the RTP Info + streams = split( rtpInfo.c_str(), "," ); + for ( size_t i = 0; i < streams.size(); i++ ) + { + // We want the stream that matches the trackUrl we are using + if ( streams[i].find(controlUrl.c_str()) != std::string::npos ) + { + // Parse the sequence and rtptime values + parts = split( streams[i].c_str(), ";" ); + for ( size_t j = 0; j < parts.size(); j++ ) { - StringVector subparts = split( parts[i], "=" ); + if ( startsWith( parts[j], "seq=" ) ) + { + StringVector subparts = split( parts[j], "=" ); seq = strtol( subparts[1].c_str(), NULL, 10 ); - } - else if ( startsWith( parts[i], "rtptime=" ) ) - { - StringVector subparts = split( parts[i], "=" ); + } + else if ( startsWith( parts[j], "rtptime=" ) ) + { + StringVector subparts = split( parts[j], "=" ); rtpTime = strtol( subparts[1].c_str(), NULL, 10 ); + } } + break; + } } + } - Debug( 2, "RTSP Seq is %d", seq ); - Debug( 2, "RTSP Rtptime is %ld", rtpTime ); + Debug( 2, "RTSP Seq is %d", seq ); + Debug( 2, "RTSP Rtptime is %ld", rtpTime ); - time_t lastKeepalive = time(NULL); - message = "GET_PARAMETER "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; + time_t lastKeepalive = time(NULL); + time_t now; + message = "GET_PARAMETER "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - switch( mMethod ) + switch( mMethod ) + { + case RTP_UNICAST : { - case RTP_UNICAST : + RtpSource *source = new RtpSource( mId, "", localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId ); + mSources[ssrc] = source; + RtpDataThread rtpDataThread( *this, *source ); + RtpCtrlThread rtpCtrlThread( *this, *source ); + + rtpDataThread.start(); + rtpCtrlThread.start(); + + while( !mStop ) + { + now = time(NULL); + // Send a keepalive message if the server supports this feature and we are close to the timeout expiration +Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) ); + if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) ) { - RtpSource *source = new RtpSource( mId, "", localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId ); - mSources[ssrc] = source; - RtpDataThread rtpDataThread( *this, *source ); - RtpCtrlThread rtpCtrlThread( *this, *source ); - - rtpDataThread.start(); - rtpCtrlThread.start(); - - while( !mStop ) - { - // Send a keepalive message if the server supports this feature and we are close to the timeout expiration - if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) - { - if ( !sendCommand( message ) ) - return( -1 ); - lastKeepalive = time(NULL); - } - usleep( 100000 ); - } + if ( !sendCommand( message ) ) + return( -1 ); + lastKeepalive = now; + } + usleep( 100000 ); + } #if 0 - message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); + message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; + if ( !sendCommand( message ) ) + return( -1 ); + if ( !recvResponse( response ) ) + return( -1 ); #endif - message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); + message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; + if ( !sendCommand( message ) ) + return( -1 ); + if ( !recvResponse( response ) ) + return( -1 ); - rtpDataThread.stop(); - rtpCtrlThread.stop(); + rtpDataThread.stop(); + rtpCtrlThread.stop(); - //rtpDataThread.kill( SIGTERM ); - //rtpCtrlThread.kill( SIGTERM ); + //rtpDataThread.kill( SIGTERM ); + //rtpCtrlThread.kill( SIGTERM ); - rtpDataThread.join(); - rtpCtrlThread.join(); - - delete mSources[ssrc]; - mSources.clear(); + rtpDataThread.join(); + rtpCtrlThread.join(); + + delete mSources[ssrc]; + mSources.clear(); - releasePorts( localPorts[0] ); + releasePorts( localPorts[0] ); - break; - } - case RTP_RTSP : - case RTP_RTSP_HTTP : - { - RtpSource *source = new RtpSource( mId, "", remoteChannels[0], mHost, remoteChannels[0], ssrc, seq, rtpClock, rtpTime, codecId ); - mSources[ssrc] = source; - // These never actually run - RtpDataThread rtpDataThread( *this, *source ); - RtpCtrlThread rtpCtrlThread( *this, *source ); - - Select select( double(config.http_timeout)/1000.0 ); - select.addReader( &mRtspSocket ); - - Buffer buffer( ZM_NETWORK_BUFSIZ ); - std::string keepaliveMessage = "OPTIONS * RTSP/1.0\r\n"; - std::string keepaliveResponse = "RTSP/1.0 200 OK\r\n"; - while ( !mStop && select.wait() >= 0 ) - { - Select::CommsList readable = select.getReadable(); - if ( readable.size() == 0 ) - { - Error( "RTSP timed out" ); - break; - } - - static char tempBuffer[ZM_NETWORK_BUFSIZ]; - ssize_t nBytes = mRtspSocket.recv( tempBuffer, sizeof(tempBuffer) ); - buffer.append( tempBuffer, nBytes ); - Debug( 4, "Read %zd bytes on sd %d, %d total", nBytes, mRtspSocket.getReadDesc(), buffer.size() ); - - while( buffer.size() > 0 ) - { - if ( buffer[0] == '$' ) - { - if ( buffer.size() < 4 ) - break; - unsigned char channel = buffer[1]; - unsigned short len = ntohs( *((unsigned short *)(buffer+2)) ); - - Debug( 4, "Got %d bytes left, expecting %d byte packet on channel %d", buffer.size(), len, channel ); - if ( (unsigned short)buffer.size() < (len+4) ) - { - Debug( 4, "Missing %d bytes, rereading", (len+4)-buffer.size() ); - break; - } - if ( channel == remoteChannels[0] ) - { - Debug( 4, "Got %d bytes on data channel %d, packet length is %d", buffer.size(), channel, len ); - Hexdump( 4, (char *)buffer, 16 ); - rtpDataThread.recvPacket( buffer+4, len ); - Debug( 4, "Received" ); - } - else if ( channel == remoteChannels[1] ) - { -// len = ntohs( *((unsigned short *)(buffer+2)) ); -// Debug( 4, "Got %d bytes on control channel %d", nBytes, channel ); - Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len ); - Hexdump( 4, (char *)buffer, 16 ); - rtpCtrlThread.recvPackets( buffer+4, len ); - } - else - { - Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] ); - buffer.clear(); - break; - } - buffer.consume( len+4 ); - nBytes -= len+4; - } - else - { - if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) - { - Debug( 4, "Got keepalive response '%s'", (char *)buffer ); - //buffer.consume( keepaliveResponse.size() ); - if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) - { - int discardBytes = charPtr-(char *)buffer; - buffer -= discardBytes; - } - else - { - buffer.clear(); - } - } - else - { - if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) - { - int discardBytes = charPtr-(char *)buffer; - Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes ); - Hexdump( -1, (char *)buffer, discardBytes ); - buffer -= discardBytes; - } - else - { - Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() ); - Hexdump( -1, (char *)buffer, 32 ); - buffer.clear(); - } - } - } - } - // Send a keepalive message if the server supports this feature and we are close to the timeout expiration - // FIXME: Is this really necessary when using tcp ? - if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) - { - if ( !sendCommand( message ) ) - return( -1 ); - lastKeepalive = time(NULL); - } - buffer.tidy( 1 ); - } -#if 0 - message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); -#endif - message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); - - delete mSources[ssrc]; - mSources.clear(); - - break; - } - case RTP_MULTICAST : - { - RtpSource *source = new RtpSource( mId, localHost, localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId ); - mSources[ssrc] = source; - RtpDataThread rtpDataThread( *this, *source ); - RtpCtrlThread rtpCtrlThread( *this, *source ); - - rtpDataThread.start(); - rtpCtrlThread.start(); - - while( !mStop ) - { - // Send a keepalive message if the server supports this feature and we are close to the timeout expiration - if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) - { - if ( !sendCommand( message ) ) - return( -1 ); - lastKeepalive = time(NULL); - } - usleep( 100000 ); - } -#if 0 - message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); -#endif - message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; - if ( !sendCommand( message ) ) - return( -1 ); - if ( !recvResponse( response ) ) - return( -1 ); - - rtpDataThread.stop(); - rtpCtrlThread.stop(); - - rtpDataThread.join(); - rtpCtrlThread.join(); - - delete mSources[ssrc]; - mSources.clear(); - - releasePorts( localPorts[0] ); - break; - } - default: - { - Panic( "Got unexpected method %d", mMethod ); - break; - } + break; } + case RTP_RTSP : + case RTP_RTSP_HTTP : + { + RtpSource *source = new RtpSource( mId, "", remoteChannels[0], mHost, remoteChannels[0], ssrc, seq, rtpClock, rtpTime, codecId ); + mSources[ssrc] = source; + // These never actually run + RtpDataThread rtpDataThread( *this, *source ); + RtpCtrlThread rtpCtrlThread( *this, *source ); - return( 0 ); + Select select( double(config.http_timeout)/1000.0 ); + select.addReader( &mRtspSocket ); + + Buffer buffer( ZM_NETWORK_BUFSIZ ); + std::string keepaliveMessage = "OPTIONS "+mUrl+" RTSP/1.0\r\n"; + std::string keepaliveResponse = "RTSP/1.0 200 OK\r\n"; + while ( !mStop && select.wait() >= 0 ) + { + Select::CommsList readable = select.getReadable(); + if ( readable.size() == 0 ) + { + Error( "RTSP timed out" ); + break; + } + + static char tempBuffer[ZM_NETWORK_BUFSIZ]; + ssize_t nBytes = mRtspSocket.recv( tempBuffer, sizeof(tempBuffer) ); + buffer.append( tempBuffer, nBytes ); + Debug( 4, "Read %zd bytes on sd %d, %d total", nBytes, mRtspSocket.getReadDesc(), buffer.size() ); + + while( buffer.size() > 0 ) + { + if ( buffer[0] == '$' ) + { + if ( buffer.size() < 4 ) + break; + unsigned char channel = buffer[1]; + unsigned short len = ntohs( *((unsigned short *)(buffer+2)) ); + + Debug( 4, "Got %d bytes left, expecting %d byte packet on channel %d", buffer.size(), len, channel ); + if ( (unsigned short)buffer.size() < (len+4) ) + { + Debug( 4, "Missing %d bytes, rereading", (len+4)-buffer.size() ); + break; + } + if ( channel == remoteChannels[0] ) + { + Debug( 4, "Got %d bytes on data channel %d, packet length is %d", buffer.size(), channel, len ); + Hexdump( 4, (char *)buffer, 16 ); + rtpDataThread.recvPacket( buffer+4, len ); + Debug( 4, "Received" ); + } + else if ( channel == remoteChannels[1] ) + { +// len = ntohs( *((unsigned short *)(buffer+2)) ); +// Debug( 4, "Got %d bytes on control channel %d", nBytes, channel ); + Debug( 4, "Got %d bytes on control channel %d, packet length is %d", buffer.size(), channel, len ); + Hexdump( 4, (char *)buffer, 16 ); + rtpCtrlThread.recvPackets( buffer+4, len ); + } + else + { + Error( "Unexpected channel selector %d in RTSP interleaved data", buffer[1] ); + buffer.clear(); + break; + } + buffer.consume( len+4 ); + nBytes -= len+4; + } + else + { + if ( keepaliveResponse.compare( 0, keepaliveResponse.size(), (char *)buffer, keepaliveResponse.size() ) == 0 ) + { + Debug( 4, "Got keepalive response '%s'", (char *)buffer ); + //buffer.consume( keepaliveResponse.size() ); + if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) + { + int discardBytes = charPtr-(char *)buffer; + buffer -= discardBytes; + } + else + { + buffer.clear(); + } + } + else + { + if ( char *charPtr = (char *)memchr( (char *)buffer, '$', buffer.size() ) ) + { + int discardBytes = charPtr-(char *)buffer; + Warning( "Unexpected format RTSP interleaved data, resyncing by %d bytes", discardBytes ); + Hexdump( -1, (char *)buffer, discardBytes ); + buffer -= discardBytes; + } + else + { + Warning( "Unexpected format RTSP interleaved data, dumping %d bytes", buffer.size() ); + Hexdump( -1, (char *)buffer, 32 ); + buffer.clear(); + } + } + } + } + // Send a keepalive message if the server supports this feature and we are close to the timeout expiration + // FIXME: Is this really necessary when using tcp ? + now = time(NULL); + // Send a keepalive message if the server supports this feature and we are close to the timeout expiration +Debug(5, "sendkeepalive %d, timeout %d, now: %d last: %d since: %d", sendKeepalive, timeout, now, lastKeepalive, (now-lastKeepalive) ); + if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) ) + { + if ( !sendCommand( message ) ) + return( -1 ); + lastKeepalive = now; + } + buffer.tidy( 1 ); + } +#if 0 + message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; + if ( !sendCommand( message ) ) + return( -1 ); + if ( !recvResponse( response ) ) + return( -1 ); +#endif + // Send a teardown message but don't expect a response as this may not be implemented on the server when using TCP + message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; + if ( !sendCommand( message ) ) + return( -1 ); + + delete mSources[ssrc]; + mSources.clear(); + + break; + } + case RTP_MULTICAST : + { + RtpSource *source = new RtpSource( mId, localHost, localPorts[0], mHost, remotePorts[0], ssrc, seq, rtpClock, rtpTime, codecId ); + mSources[ssrc] = source; + RtpDataThread rtpDataThread( *this, *source ); + RtpCtrlThread rtpCtrlThread( *this, *source ); + + rtpDataThread.start(); + rtpCtrlThread.start(); + + while( !mStop ) + { + // Send a keepalive message if the server supports this feature and we are close to the timeout expiration + if ( sendKeepalive && (timeout > 0) && ((time(NULL)-lastKeepalive) > (timeout-5)) ) + { + if ( !sendCommand( message ) ) + return( -1 ); + lastKeepalive = time(NULL); + } + usleep( 100000 ); + } +#if 0 + message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; + if ( !sendCommand( message ) ) + return( -1 ); + if ( !recvResponse( response ) ) + return( -1 ); +#endif + message = "TEARDOWN "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n"; + if ( !sendCommand( message ) ) + return( -1 ); + if ( !recvResponse( response ) ) + return( -1 ); + + rtpDataThread.stop(); + rtpCtrlThread.stop(); + + rtpDataThread.join(); + rtpCtrlThread.join(); + + delete mSources[ssrc]; + mSources.clear(); + + releasePorts( localPorts[0] ); + break; + } + default: + { + Panic( "Got unexpected method %d", mMethod ); + break; + } + } + + return( 0 ); } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_rtsp.h b/src/zm_rtsp.h index 4ecf99a92..656db65e3 100644 --- a/src/zm_rtsp.h +++ b/src/zm_rtsp.h @@ -26,6 +26,7 @@ #include "zm_thread.h" #include "zm_rtp_source.h" #include "zm_rtsp_auth.h" +#include "zm_sdp.h" #include #include @@ -33,107 +34,114 @@ class RtspThread : public Thread { public: - typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod; - typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist; + typedef enum { RTP_UNICAST, RTP_MULTICAST, RTP_RTSP, RTP_RTSP_HTTP } RtspMethod; + typedef enum { UNDEFINED, UNICAST, MULTICAST } RtspDist; private: - typedef std::set PortSet; - typedef std::set SsrcSet; - typedef std::map SourceMap; + typedef std::set PortSet; + typedef std::set SsrcSet; + typedef std::map SourceMap; private: - static int smMinDataPort; - static int smMaxDataPort; - static PortSet smLocalSsrcs; - static PortSet smAssignedPorts; + static int smMinDataPort; + static int smMaxDataPort; + static PortSet smLocalSsrcs; + static PortSet smAssignedPorts; private: - int mId; - RtspMethod mMethod; - std::string mProtocol; - std::string mHost; - std::string mPort; - std::string mPath; - std::string mUrl; - - // Reworked authentication system - // First try without authentication, even if we have a username and password - // on receiving a 401 response, select authentication method (basic or digest) - // fill required fields and set needAuth - // subsequent requests can set the required authentication header. - bool mNeedAuth; - int respCode; - Authenticator* mAuthenticator; + int mId; + + RtspMethod mMethod; + std::string mProtocol; + std::string mHost; + std::string mPort; + std::string mPath; + bool mRtspDescribe; + std::string mUrl; + + // Reworked authentication system + // First try without authentication, even if we have a username and password + // on receiving a 401 response, select authentication method (basic or digest) + // fill required fields and set needAuth + // subsequent requests can set the required authentication header. + bool mNeedAuth; + int respCode; + zm::Authenticator* mAuthenticator; - std::string mHttpSession; ///< Only for RTSP over HTTP sessions + std::string mHttpSession; ///< Only for RTSP over HTTP sessions - TcpInetClient mRtspSocket; - TcpInetClient mRtspSocket2; + TcpInetClient mRtspSocket; + TcpInetClient mRtspSocket2; - SourceMap mSources; + SourceMap mSources; - AVFormatContext *mFormatContext; + SessionDescriptor *mSessDesc; + AVFormatContext *mFormatContext; - uint16_t mSeq; - uint32_t mSession; - uint32_t mSsrc; + uint16_t mSeq; + uint32_t mSession; + uint32_t mSsrc; - int mRemotePorts[2]; - int mRemoteChannels[2]; - RtspDist mDist; + int mRemotePorts[2]; + int mRemoteChannels[2]; + RtspDist mDist; - unsigned long mRtpTime; + unsigned long mRtpTime; - bool mStop; + bool mStop; private: - bool sendCommand( std::string message ); - bool recvResponse( std::string &response ); - void checkAuthResponse(std::string &response); + bool sendCommand( std::string message ); + bool recvResponse( std::string &response ); + void checkAuthResponse(std::string &response); public: - RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth); - ~RtspThread(); + RtspThread( int id, RtspMethod method, const std::string &protocol, const std::string &host, const std::string &port, const std::string &path, const std::string &auth, bool rtsp_describe ); + ~RtspThread(); public: - int requestPorts(); - void releasePorts( int port ); + int requestPorts(); + void releasePorts( int port ); - bool isValidSsrc( uint32_t ssrc ); - bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header ); + bool isValidSsrc( uint32_t ssrc ); + bool updateSsrc( uint32_t ssrc, const RtpDataHeader *header ); - uint32_t getSsrc() const - { - return( mSsrc ); - } + uint32_t getSsrc() const + { + return( mSsrc ); + } - bool hasSources() const - { - return( !mSources.empty() ); - } + bool hasSources() const + { + return( !mSources.empty() ); + } - AVFormatContext *getFormatContext() - { - return( mFormatContext ); - } - - bool getFrame( Buffer &frame ) - { - SourceMap::iterator iter = mSources.begin(); - if ( iter == mSources.end() ) - return( false ); - return( iter->second->getFrame( frame ) ); - } - int run(); - void stop() - { - mStop = true; - } - bool stopped() const - { - return( mStop ); - } + AVFormatContext *getFormatContext() + { + return( mFormatContext ); + } + + bool getFrame( Buffer &frame ) + { + SourceMap::iterator iter = mSources.begin(); + if ( iter == mSources.end() ) + return( false ); + return( iter->second->getFrame( frame ) ); + } + int run(); + void stop() + { + mStop = true; + } + bool stopped() const + { + return( mStop ); + } + int getAddressFamily () + { + return mRtspSocket.getDomain(); + } }; #endif // ZM_RTSP_H diff --git a/src/zm_rtsp_auth.cpp b/src/zm_rtsp_auth.cpp index da5a23552..4f816b64a 100644 --- a/src/zm_rtsp_auth.cpp +++ b/src/zm_rtsp_auth.cpp @@ -24,160 +24,210 @@ #include #include +namespace zm { + Authenticator::Authenticator(std::string &username, std::string password) { #ifdef HAVE_GCRYPT_H - // Special initialisation for libgcrypt - if ( !gcry_check_version( GCRYPT_VERSION ) ) - { - Fatal( "Unable to initialise libgcrypt" ); - } - gcry_control( GCRYCTL_DISABLE_SECMEM, 0 ); - gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 ); + // Special initialisation for libgcrypt + if ( !gcry_check_version( GCRYPT_VERSION ) ) + { + Fatal( "Unable to initialise libgcrypt" ); + } + gcry_control( GCRYCTL_DISABLE_SECMEM, 0 ); + gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 ); #endif // HAVE_GCRYPT_H - - fAuthMethod = AUTH_UNDEFINED; - fUsername = username; - fPassword = password; + + fAuthMethod = AUTH_UNDEFINED; + fUsername = username; + fPassword = password; + nc = 1; + fCnonce = "0a4f113b"; } Authenticator::~Authenticator() { - reset(); + reset(); } void Authenticator::reset() { - fRealm.clear(); - fNonce.clear(); - fUsername.clear(); - fPassword.clear(); - fAuthMethod = AUTH_UNDEFINED; + fRealm.clear(); + fNonce.clear(); + fUsername.clear(); + fPassword.clear(); + fAuthMethod = AUTH_UNDEFINED; } void Authenticator::authHandleHeader(std::string headerData) { - const char* basic_match = "Basic "; - const char* digest_match = "Digest "; - size_t digest_match_len = strlen(digest_match); - - // Check if basic auth - if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0) + const char* basic_match = "Basic "; + const char* digest_match = "Digest "; + size_t digest_match_len = strlen(digest_match); + + // Check if basic auth + if (strncasecmp(headerData.c_str(),basic_match,strlen(basic_match)) == 0) + { + fAuthMethod = AUTH_BASIC; + Debug( 2, "Set authMethod to Basic"); + } + // Check if digest auth + else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0) + { + fAuthMethod = AUTH_DIGEST; + Debug( 2, "Set authMethod to Digest"); + StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ","); + // subparts are key="value" + for ( size_t i = 0; i < subparts.size(); i++ ) { - fAuthMethod = AUTH_BASIC; - Debug( 2, "Set authMethod to Basic"); - } - // Check if digest auth - else if (strncasecmp( headerData.c_str(),digest_match,digest_match_len ) == 0) - { - fAuthMethod = AUTH_DIGEST; - Debug( 2, "Set authMethod to Digest"); - StringVector subparts = split(headerData.substr(digest_match_len, headerData.length() - digest_match_len), ","); - // subparts are key="value" - for ( size_t i = 0; i < subparts.size(); i++ ) - { - StringVector kvPair = split( trimSpaces( subparts[i] ), "=" ); - std::string key = trimSpaces( kvPair[0] ); - if (key == "realm") { - fRealm = trimSet( kvPair[1], "\""); - continue; - } - if (key == "nonce") { - fNonce = trimSet( kvPair[1], "\""); - continue; - } - } - Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s", username().c_str(), fRealm.c_str(), fNonce.c_str()); + StringVector kvPair = split( trimSpaces( subparts[i] ), "=" ); + std::string key = trimSpaces( kvPair[0] ); + if (key == "realm") { + fRealm = trimSet( kvPair[1], "\""); + continue; + } + if (key == "nonce") { + fNonce = trimSet( kvPair[1], "\""); + continue; + } + if (key == "qop") { + fQop = trimSet( kvPair[1], "\""); + continue; + } } + Debug( 2, "Auth data completed. User: %s, realm: %s, nonce: %s, qop: %s", username().c_str(), fRealm.c_str(), fNonce.c_str(), fQop.c_str() ); + } } std::string Authenticator::quote(std::string src) { - return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\""); + return replaceAll(replaceAll(src, "\\", "\\\\"), "\"", "\\\""); } std::string Authenticator::getAuthHeader(std::string method, std::string uri) { - std::string result = "Authorization: "; - if (fAuthMethod == AUTH_BASIC) - { - result += "Basic " + base64Encode( username() + ":" + password() ); + std::string result = "Authorization: "; + if (fAuthMethod == AUTH_BASIC) + { + result += "Basic " + base64Encode( username() + ":" + password() ); + } + else if (fAuthMethod == AUTH_DIGEST) + { + result += std::string("Digest ") + + "username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " + + "nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\""; + if ( ! fQop.empty() ) { + result += ", qop=" + fQop; + result += ", nc=" + stringtf("%08x",nc); + result += ", cnonce=\"" + fCnonce + "\""; } - else if (fAuthMethod == AUTH_DIGEST) - { - result += std::string("Digest ") + - "username=\"" + quote(username()) + "\", realm=\"" + quote(realm()) + "\", " + - "nonce=\"" + quote(nonce()) + "\", uri=\"" + quote(uri) + "\", " + - "response=\"" + computeDigestResponse(method, uri) + "\""; - - //Authorization: Digest username="zm", - // realm="NC-336PW-HD-1080P", - // nonce="de8859d97609a6fcc16eaba490dcfd80", - // uri="rtsp://10.192.16.8:554/live/0/h264.sdp", - // response="4092120557d3099a163bd51a0d59744d", - // algorithm=MD5, - // opaque="5ccc069c403ebaf9f0171e9517f40e41", - // qop="auth", - // cnonce="c8051140765877dc", - // nc=00000001 - - } - result += "\r\n"; - return result; + result += ", response=\"" + computeDigestResponse(method, uri) + "\""; + result += ", algorithm=\"MD5\""; + + //Authorization: Digest username="zm", + // realm="NC-336PW-HD-1080P", + // nonce="de8859d97609a6fcc16eaba490dcfd80", + // uri="rtsp://10.192.16.8:554/live/0/h264.sdp", + // response="4092120557d3099a163bd51a0d59744d", + // algorithm=MD5, + // opaque="5ccc069c403ebaf9f0171e9517f40e41", + // qop="auth", + // cnonce="c8051140765877dc", + // nc=00000001 + + } + result += "\r\n"; + return result; } std::string Authenticator::computeDigestResponse(std::string &method, std::string &uri) { #if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT - // The "response" field is computed as: - // md5(md5(::)::md5(:)) - size_t md5len = 16; - unsigned char md5buf[md5len]; - char md5HexBuf[md5len*2+1]; - - // Step 1: md5(::) - std::string ha1Data = username() + ":" + realm() + ":" + password(); + // The "response" field is computed as: + // md5(md5(::)::md5(:)) + size_t md5len = 16; + unsigned char md5buf[md5len]; + char md5HexBuf[md5len*2+1]; + + // Step 1: md5(::) + std::string ha1Data = username() + ":" + realm() + ":" + password(); + Debug( 2, "HA1 pre-md5: %s", ha1Data.c_str() ); #if HAVE_DECL_MD5 - MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf); + MD5((unsigned char*)ha1Data.c_str(), ha1Data.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() }; - gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len ); + gnutls_datum_t md5dataha1 = { (unsigned char*)ha1Data.c_str(), ha1Data.length() }; + gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha1, md5buf, &md5len ); #endif - for ( unsigned int j = 0; j < md5len; j++ ) - { - sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] ); - } - md5HexBuf[md5len*2]='\0'; - std::string ha1Hash = md5HexBuf; - - // Step 2: md5(:) - std::string ha2Data = method + ":" + uri; + for ( unsigned int j = 0; j < md5len; j++ ) + { + sprintf(&md5HexBuf[2*j], "%02x", md5buf[j] ); + } + md5HexBuf[md5len*2]='\0'; + std::string ha1Hash = md5HexBuf; + + // Step 2: md5(:) + std::string ha2Data = method + ":" + uri; + Debug( 2, "HA2 pre-md5: %s", ha2Data.c_str() ); #if HAVE_DECL_MD5 - MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf ); + MD5((unsigned char*)ha2Data.c_str(), ha2Data.length(), md5buf ); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() }; - gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len ); + gnutls_datum_t md5dataha2 = { (unsigned char*)ha2Data.c_str(), ha2Data.length() }; + gnutls_fingerprint( GNUTLS_DIG_MD5, &md5dataha2, md5buf, &md5len ); #endif - for ( unsigned int j = 0; j < md5len; j++ ) - { - sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] ); - } - md5HexBuf[md5len*2]='\0'; - std::string ha2Hash = md5HexBuf; + for ( unsigned int j = 0; j < md5len; j++ ) + { + sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] ); + } + md5HexBuf[md5len*2]='\0'; + std::string ha2Hash = md5HexBuf; - // Step 3: md5(ha1::ha2) - std::string digestData = ha1Hash + ":" + nonce() + ":" + ha2Hash; + // Step 3: md5(ha1::ha2) + std::string digestData = ha1Hash + ":" + nonce(); + if ( ! fQop.empty() ) { + digestData += ":" + stringtf("%08x", nc) + ":"+fCnonce + ":" + fQop; + nc ++; + // if qop was specified, then we have to include t and a cnonce and an nccount + } + digestData += ":" + ha2Hash; + Debug( 2, "pre-md5: %s", digestData.c_str() ); #if HAVE_DECL_MD5 - MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf); + MD5((unsigned char*)digestData.c_str(), digestData.length(), md5buf); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() }; - gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len ); + gnutls_datum_t md5datadigest = { (unsigned char*)digestData.c_str(), digestData.length() }; + gnutls_fingerprint( GNUTLS_DIG_MD5, &md5datadigest, md5buf, &md5len ); #endif - for ( unsigned int j = 0; j < md5len; j++ ) - { - sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] ); - } - md5HexBuf[md5len*2]='\0'; + for ( unsigned int j = 0; j < md5len; j++ ) + { + sprintf( &md5HexBuf[2*j], "%02x", md5buf[j] ); + } + md5HexBuf[md5len*2]='\0'; - return md5HexBuf; + return md5HexBuf; #else // HAVE_DECL_MD5 - Error( "You need to build with gnutls or openssl installed to use digest authentication" ); + Error( "You need to build with gnutls or openssl installed to use digest authentication" ); + return( 0 ); #endif // HAVE_DECL_MD5 - return( 0 ); } + +void Authenticator::checkAuthResponse(std::string &response) { + std::string authLine; + StringVector lines = split( response, "\r\n" ); + const char* authenticate_match = "WWW-Authenticate:"; + size_t authenticate_match_len = strlen(authenticate_match); + + for ( size_t i = 0; i < lines.size(); i++ ) { + // stop at end of headers + if (lines[i].length()==0) + break; + + if (strncasecmp(lines[i].c_str(),authenticate_match,authenticate_match_len) == 0) { + authLine = lines[i]; + Debug( 2, "Found auth line at %d", i); + break; + } + } + if (!authLine.empty()) { + Debug( 2, "Analyze auth line %s", authLine.c_str()); + authHandleHeader( trimSpaces(authLine.substr(authenticate_match_len,authLine.length()-authenticate_match_len)) ); + } else { + Debug( 2, "Didn't find auth line in %s", authLine.c_str()); + } +} + +} // namespace zm diff --git a/src/zm_rtsp_auth.h b/src/zm_rtsp_auth.h index 032216b9c..9249a15e2 100644 --- a/src/zm_rtsp_auth.h +++ b/src/zm_rtsp_auth.h @@ -32,30 +32,38 @@ #include #endif // HAVE_GCRYPT_H || HAVE_LIBCRYPTO -class Authenticator -{ -public: - typedef enum { AUTH_UNDEFINED, AUTH_BASIC, AUTH_DIGEST } RtspAuthMethod; - Authenticator(std::string &username, std::string password); - virtual ~Authenticator(); - void reset(); +namespace zm { - std::string realm() { return fRealm; } - std::string nonce() { return fNonce; } - std::string username() { return fUsername; } - - std::string computeDigestResponse( std::string &cmd, std::string &url ); - void authHandleHeader( std::string headerData ); - std::string getAuthHeader( std::string method, std::string path ); - +enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 }; +class Authenticator { +public: + Authenticator(std::string &username, std::string password); + virtual ~Authenticator(); + void reset(); + + std::string realm() { return fRealm; } + std::string nonce() { return fNonce; } + std::string username() { return fUsername; } + AuthMethod auth_method() const { return fAuthMethod; } + + std::string computeDigestResponse( std::string &cmd, std::string &url ); + void authHandleHeader( std::string headerData ); + std::string getAuthHeader( std::string method, std::string path ); + void checkAuthResponse(std::string &response); + private: std::string password() { return fPassword; } - RtspAuthMethod fAuthMethod; + AuthMethod fAuthMethod; std::string fRealm; std::string fNonce; + std::string fCnonce; + std::string fQop; std::string fUsername; std::string fPassword; std::string quote( std::string src ); + int nc; }; +} // namespace zm + #endif // ZM_RTSP_AUTH_H diff --git a/src/zm_sdp.cpp b/src/zm_sdp.cpp index 077be08b9..fa8a602ee 100644 --- a/src/zm_sdp.cpp +++ b/src/zm_sdp.cpp @@ -23,453 +23,491 @@ #include "zm_sdp.h" -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { - { 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 }, - { 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 }, - { 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 }, - { 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 }, - { 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 }, - { 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 }, - { 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 }, - { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 }, - { 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 }, - { 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 }, - { 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, - { 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, - { 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 }, - { 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, - { 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 }, - { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 }, - { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 }, - { 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 }, - { 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 }, - { -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 } + { 0, "PCMU", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_MULAW, 8000, 1 }, + { 3, "GSM", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 4, "G723", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 5, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 6, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 16000, 1 }, + { 7, "LPC", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 8, "PCMA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_ALAW, 8000, 1 }, + { 9, "G722", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 10, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 2 }, + { 11, "L16", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_PCM_S16BE, 44100, 1 }, + { 12, "QCELP", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_QCELP, 8000, 1 }, + { 13, "CN", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP2, -1, -1 }, + { 14, "MPA", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_MP3, -1, -1 }, + { 15, "G728", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 16, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 11025, 1 }, + { 17, "DVI4", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 22050, 1 }, + { 18, "G729", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_NONE, 8000, 1 }, + { 25, "CelB", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, + { 26, "JPEG", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MJPEG, 90000, -1 }, + { 28, "nv", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_NONE, 90000, -1 }, + { 31, "H261", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H261, 90000, -1 }, + { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG1VIDEO, 90000, -1 }, + { 32, "MPV", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG2VIDEO, 90000, -1 }, + { 33, "MP2T", AVMEDIA_TYPE_DATA, AV_CODEC_ID_MPEG2TS, 90000, -1 }, + { 34, "H263", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H263, 90000, -1 }, + { -1, "", AVMEDIA_TYPE_UNKNOWN, AV_CODEC_ID_NONE, -1, -1 } }; SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = { - { "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, - { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, - { "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, - { "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB } + { "MP4V-ES", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_MPEG4 }, + { "mpeg4-generic", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AAC }, + { "H264", AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 }, + { "AMR", AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AMR_NB }, + { "vnd.onvif.metadata", AVMEDIA_TYPE_DATA, AV_CODEC_ID_NONE } }; #else SessionDescriptor::StaticPayloadDesc SessionDescriptor::smStaticPayloads[] = { - { 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 }, - { 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, - { 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, - { 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, - { 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 }, - { 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, - { 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 }, - { 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, - { 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 }, - { 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 }, - { 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 }, - { 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, - { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 }, - { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 }, - { 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, - { 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 }, - { 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 }, - { 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, - { 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, - { 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 }, - { 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, - { 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 }, - { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 }, - { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 }, - { 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 }, - { 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 }, - { -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 } + { 0, "PCMU", CODEC_TYPE_AUDIO, CODEC_ID_PCM_MULAW, 8001, 1 }, + { 3, "GSM", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 4, "G723", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 5, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 6, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 16000, 1 }, + { 7, "LPC", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 8, "PCMA", CODEC_TYPE_AUDIO, CODEC_ID_PCM_ALAW, 8000, 1 }, + { 9, "G722", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 10, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 2 }, + { 11, "L16", CODEC_TYPE_AUDIO, CODEC_ID_PCM_S16BE, 44100, 1 }, + { 12, "QCELP", CODEC_TYPE_AUDIO, CODEC_ID_QCELP, 8000, 1 }, + { 13, "CN", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP2, -1, -1 }, + { 14, "MPA", CODEC_TYPE_AUDIO, CODEC_ID_MP3, -1, -1 }, + { 15, "G728", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 16, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 11025, 1 }, + { 17, "DVI4", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 22050, 1 }, + { 18, "G729", CODEC_TYPE_AUDIO, CODEC_ID_NONE, 8000, 1 }, + { 25, "CelB", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, + { 26, "JPEG", CODEC_TYPE_VIDEO, CODEC_ID_MJPEG, 90000, -1 }, + { 28, "nv", CODEC_TYPE_VIDEO, CODEC_ID_NONE, 90000, -1 }, + { 31, "H261", CODEC_TYPE_VIDEO, CODEC_ID_H261, 90000, -1 }, + { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG1VIDEO, 90000, -1 }, + { 32, "MPV", CODEC_TYPE_VIDEO, CODEC_ID_MPEG2VIDEO, 90000, -1 }, + { 33, "MP2T", CODEC_TYPE_DATA, CODEC_ID_MPEG2TS, 90000, -1 }, + { 34, "H263", CODEC_TYPE_VIDEO, CODEC_ID_H263, 90000, -1 }, + { -1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1 } }; SessionDescriptor::DynamicPayloadDesc SessionDescriptor::smDynamicPayloads[] = { - { "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 }, - { "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC }, - { "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 }, - { "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB } + { "MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4 }, + { "mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_AAC }, + { "H264", CODEC_TYPE_VIDEO, CODEC_ID_H264 }, + { "AMR", CODEC_TYPE_AUDIO, CODEC_ID_AMR_NB }, + { "vnd.onvif.metadata", CODEC_TYPE_DATA, CODEC_ID_NONE } }; #endif SessionDescriptor::ConnInfo::ConnInfo( const std::string &connInfo ) : - mTtl( 16 ), - mNoAddresses( 0 ) + mTtl( 16 ), + mNoAddresses( 0 ) { - StringVector tokens = split( connInfo, " " ); - if ( tokens.size() < 3 ) - throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" ); - mNetworkType = tokens[0]; - if ( mNetworkType != "IN" ) - throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); - mAddressType = tokens[1]; - if ( mAddressType != "IP4" ) - throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" ); - StringVector addressTokens = split( tokens[2], "/" ); - if ( addressTokens.size() < 1 ) - throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" ); - mAddress = addressTokens[0]; - if ( addressTokens.size() >= 2 ) - mTtl = atoi(addressTokens[1].c_str()); - if ( addressTokens.size() >= 3 ) - mNoAddresses = atoi(addressTokens[2].c_str()); + StringVector tokens = split( connInfo, " " ); + if ( tokens.size() < 3 ) + throw Exception( "Unable to parse SDP connection info from '"+connInfo+"'" ); + mNetworkType = tokens[0]; + if ( mNetworkType != "IN" ) + throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); + mAddressType = tokens[1]; + if ( mAddressType != "IP4" && mAddressType != "IP6" ) + throw Exception( "Invalid SDP address type '"+mAddressType+"' in connection info '"+connInfo+"'" ); + StringVector addressTokens = split( tokens[2], "/" ); + if ( addressTokens.size() < 1 ) + throw Exception( "Invalid SDP address '"+tokens[2]+"' in connection info '"+connInfo+"'" ); + mAddress = addressTokens[0]; + if ( addressTokens.size() >= 2 ) + mTtl = atoi(addressTokens[1].c_str()); + if ( addressTokens.size() >= 3 ) + mNoAddresses = atoi(addressTokens[2].c_str()); } SessionDescriptor::BandInfo::BandInfo( const std::string &bandInfo ) : - mValue( 0 ) + mValue( 0 ) { - StringVector tokens = split( bandInfo, ":" ); - if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" ); - mType = tokens[0]; - //if ( mNetworkType != "IN" ) - //throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); - mValue = atoi(tokens[1].c_str()); + StringVector tokens = split( bandInfo, ":" ); + if ( tokens.size() < 2 ) + throw Exception( "Unable to parse SDP bandwidth info from '"+bandInfo+"'" ); + mType = tokens[0]; + //if ( mNetworkType != "IN" ) + //throw Exception( "Invalid SDP network type '"+mNetworkType+"' in connection info '"+connInfo+"'" ); + mValue = atoi(tokens[1].c_str()); } SessionDescriptor::MediaDescriptor::MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ) : - mType( type ), - mPort( port ), - mNumPorts( numPorts ), - mTransport( transport ), - mPayloadType( payloadType ), - mFrameRate( 0.0 ), - mClock( 0 ), - mWidth( 0 ), - mHeight( 0 ), - mSprops( "" ), - mConnInfo( 0 ) + mType( type ), + mPort( port ), + mNumPorts( numPorts ), + mTransport( transport ), + mPayloadType( payloadType ), + mFrameRate( 0.0 ), + mClock( 0 ), + mWidth( 0 ), + mHeight( 0 ), + mSprops( "" ), + mConnInfo( 0 ) { } SessionDescriptor::SessionDescriptor( const std::string &url, const std::string &sdp ) : - mUrl( url ), - mConnInfo( 0 ), - mBandInfo( 0 ) + mUrl( url ), + mConnInfo( 0 ), + mBandInfo( 0 ) { - MediaDescriptor *currMedia = 0; + MediaDescriptor *currMedia = 0; - StringVector lines = split( sdp, "\r\n" ); - for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ ) + StringVector lines = split( sdp, "\r\n" ); + for ( StringVector::const_iterator iter = lines.begin(); iter != lines.end(); iter++ ) + { + std::string line = *iter; + if ( line.empty() ) + break; + + Debug( 3, "Processing SDP line '%s'", line.c_str() ); + const char sdpType = line[0]; + if ( line[1] != '=' ) + throw Exception( "Invalid SDP format at '"+line+"'" ); + + line.erase( 0, 2 ); + switch( sdpType ) { - std::string line = *iter; - if ( line.empty() ) - break; - - Debug( 3, "Processing SDP line '%s'", line.c_str() ); - const char sdpType = line[0]; - if ( line[1] != '=' ) - throw Exception( "Invalid SDP format at '"+line+"'" ); - - line.erase( 0, 2 ); - switch( sdpType ) + case 'v' : + mVersion = line; + break; + case 'o' : + mOwner = line; + break; + case 's' : + mName = line; + break; + case 'i' : + mInfo = line; + break; + case 'c' : + // This prevent a memory leak if the field appears more than one time + if ( mConnInfo ) + delete mConnInfo; + mConnInfo = new ConnInfo( line ); + break; + case 'b' : + // This prevent a memory leak if the field appears more than one time + if ( mBandInfo ) + delete mBandInfo; + mBandInfo = new BandInfo( line ); + break; + case 't' : + mTimeInfo = line; + break; + case 'a' : + { + mAttributes.push_back( line ); + StringVector tokens = split( line, ":", 2 ); + std::string attrName = tokens[0]; + if ( currMedia ) { - case 'v' : - mVersion = line; - break; - case 'o' : - mOwner = line; - break; - case 's' : - mName = line; - break; - case 'i' : - mInfo = line; - break; - case 'c' : - mConnInfo = new ConnInfo( line ); - break; - case 'b' : - mBandInfo = new BandInfo( line ); - break; - case 't' : - mTimeInfo = line; - break; - case 'a' : + if ( attrName == "control" ) + { + if ( tokens.size() < 2 ) + throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" ); + currMedia->setControlUrl( tokens[1] ); + } + else if ( attrName == "range" ) + { + } + else if ( attrName == "rtpmap" ) + { + // a=rtpmap:96 MP4V-ES/90000 + if ( tokens.size() < 2 ) + throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" ); + StringVector attrTokens = split( tokens[1], " " ); + int payloadType = atoi(attrTokens[0].c_str()); + if ( payloadType != currMedia->getPayloadType() ) + throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); + std::string payloadDesc = attrTokens[1]; + //currMedia->setPayloadType( payloadType ); + if ( attrTokens.size() > 1 ) { - mAttributes.push_back( line ); - StringVector tokens = split( line, ":", 2 ); - std::string attrName = tokens[0]; - if ( currMedia ) - { - if ( attrName == "control" ) - { - if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP control attribute '"+line+"' for media '"+currMedia->getType()+"'" ); - currMedia->setControlUrl( tokens[1] ); - } - else if ( attrName == "range" ) - { - } - else if ( attrName == "rtpmap" ) - { - // a=rtpmap:96 MP4V-ES/90000 - if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP rtpmap attribute '"+line+"' for media '"+currMedia->getType()+"'" ); - StringVector attrTokens = split( tokens[1], " " ); - int payloadType = atoi(attrTokens[0].c_str()); - if ( payloadType != currMedia->getPayloadType() ) - throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); - std::string payloadDesc = attrTokens[1]; - //currMedia->setPayloadType( payloadType ); - if ( attrTokens.size() > 1 ) - { - StringVector payloadTokens = split( attrTokens[1], "/" ); - std::string payloadDesc = payloadTokens[0]; - int payloadClock = atoi(payloadTokens[1].c_str()); - currMedia->setPayloadDesc( payloadDesc ); - currMedia->setClock( payloadClock ); - } - } - else if ( attrName == "framesize" ) - { - // a=framesize:96 320-240 - if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" ); - StringVector attrTokens = split( tokens[1], " " ); - int payloadType = atoi(attrTokens[0].c_str()); - if ( payloadType != currMedia->getPayloadType() ) - throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); - //currMedia->setPayloadType( payloadType ); - StringVector sizeTokens = split( attrTokens[1], "-" ); - int width = atoi(sizeTokens[0].c_str()); - int height = atoi(sizeTokens[1].c_str()); - currMedia->setFrameSize( width, height ); - } - else if ( attrName == "framerate" ) - { - // a=framerate:5.0 - if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" ); - double frameRate = atof(tokens[1].c_str()); - currMedia->setFrameRate( frameRate ); - } - else if ( attrName == "fmtp" ) - { - // a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F - if ( tokens.size() < 2 ) - throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" ); - StringVector attrTokens = split( tokens[1], " ", 2 ); - int payloadType = atoi(attrTokens[0].c_str()); - if ( payloadType != currMedia->getPayloadType() ) - throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); - //currMedia->setPayloadType( payloadType ); - if ( attrTokens.size() > 1 ) - { - StringVector attr2Tokens = split( attrTokens[1], "; " ); - for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) - { - StringVector attr3Tokens = split( attr2Tokens[i], "=" ); - //Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() ); - if ( attr3Tokens[0] == "profile-level-id" ) - { - } - else if ( attr3Tokens[0] == "config" ) - { - } - else if ( attr3Tokens[0] == "sprop-parameter-sets" ) - { - size_t t = attr2Tokens[i].find("="); - char *c = (char *)attr2Tokens[i].c_str() + t + 1; - Debug(4, "sprop-parameter-sets value %s", c); - currMedia->setSprops(std::string(c)); - } - else if ( attr3Tokens[0] == "sprop-parameter-sets" ) - { - size_t t = attr2Tokens[i].find("="); - char *c = (char *)attr2Tokens[i].c_str() + t + 1; - Debug(4, "sprop-parameter-sets value %s", c); - currMedia->setSprops(std::string(c)); - } - else - { - Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() ) - } - } - } - } - else if ( attrName == "mpeg4-iod" ) - { - // a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA" - } - else if ( attrName == "mpeg4-esid" ) - { - // a=mpeg4-esid:201 - } - else - { - Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() ) - } - } - else - { - Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() ); - } - break; + StringVector payloadTokens = split( attrTokens[1], "/" ); + std::string payloadDesc = payloadTokens[0]; + int payloadClock = atoi(payloadTokens[1].c_str()); + currMedia->setPayloadDesc( payloadDesc ); + currMedia->setClock( payloadClock ); } - case 'm' : + } + else if ( attrName == "framesize" ) + { + // a=framesize:96 320-240 + if ( tokens.size() < 2 ) + throw Exception( "Unable to parse SDP framesize attribute '"+line+"' for media '"+currMedia->getType()+"'" ); + StringVector attrTokens = split( tokens[1], " " ); + int payloadType = atoi(attrTokens[0].c_str()); + if ( payloadType != currMedia->getPayloadType() ) + throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); + //currMedia->setPayloadType( payloadType ); + StringVector sizeTokens = split( attrTokens[1], "-" ); + int width = atoi(sizeTokens[0].c_str()); + int height = atoi(sizeTokens[1].c_str()); + currMedia->setFrameSize( width, height ); + } + else if ( attrName == "framerate" ) + { + // a=framerate:5.0 + if ( tokens.size() < 2 ) + throw Exception( "Unable to parse SDP framerate attribute '"+line+"' for media '"+currMedia->getType()+"'" ); + double frameRate = atof(tokens[1].c_str()); + currMedia->setFrameRate( frameRate ); + } + else if ( attrName == "fmtp" ) + { + // a=fmtp:96 profile-level-id=247; config=000001B0F7000001B509000001000000012008D48D8803250F042D14440F + if ( tokens.size() < 2 ) + throw Exception( "Unable to parse SDP fmtp attribute '"+line+"' for media '"+currMedia->getType()+"'" ); + StringVector attrTokens = split( tokens[1], " ", 2 ); + int payloadType = atoi(attrTokens[0].c_str()); + if ( payloadType != currMedia->getPayloadType() ) + throw Exception( stringtf( "Payload type mismatch, expected %d, got %d in '%s'", currMedia->getPayloadType(), payloadType, line.c_str() ) ); + //currMedia->setPayloadType( payloadType ); + if ( attrTokens.size() > 1 ) { - StringVector tokens = split( line, " " ); - if ( tokens.size() < 4 ) - throw Exception( "Can't parse SDP media description '"+line+"'" ); - std::string mediaType = tokens[0]; - if ( mediaType != "audio" && mediaType != "video" ) - throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" ); - StringVector portTokens = split( tokens[1], "/" ); - int mediaPort = atoi(portTokens[0].c_str()); - int mediaNumPorts = 1; - if ( portTokens.size() > 1 ) - mediaNumPorts = atoi(portTokens[1].c_str()); - std::string mediaTransport = tokens[2]; - if ( mediaTransport != "RTP/AVP" ) - throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" ); - int payloadType = atoi(tokens[3].c_str()); - currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType ); - mMediaList.push_back( currMedia ); - break; + StringVector attr2Tokens = split( attrTokens[1], "; " ); + for ( unsigned int i = 0; i < attr2Tokens.size(); i++ ) + { + StringVector attr3Tokens = split( attr2Tokens[i], "=" ); + //Info( "Name = %s, Value = %s", attr3Tokens[0].c_str(), attr3Tokens[1].c_str() ); + if ( attr3Tokens[0] == "profile-level-id" ) + { + } + else if ( attr3Tokens[0] == "config" ) + { + } + else if ( attr3Tokens[0] == "sprop-parameter-sets" ) + { + size_t t = attr2Tokens[i].find("="); + char *c = (char *)attr2Tokens[i].c_str() + t + 1; + Debug(4, "sprop-parameter-sets value %s", c); + currMedia->setSprops(std::string(c)); + } + else if ( attr3Tokens[0] == "sprop-parameter-sets" ) + { + size_t t = attr2Tokens[i].find("="); + char *c = (char *)attr2Tokens[i].c_str() + t + 1; + Debug(4, "sprop-parameter-sets value %s", c); + currMedia->setSprops(std::string(c)); + } + else + { + Debug( 3, "Ignoring SDP fmtp attribute '%s' for media '%s'", attr3Tokens[0].c_str(), currMedia->getType().c_str() ) + } + } } + } + else if ( attrName == "mpeg4-iod" ) + { + // a=mpeg4-iod: "data:application/mpeg4-iod;base64,AoEAAE8BAf73AQOAkwABQHRkYXRhOmFwcGxpY2F0aW9uL21wZWc0LW9kLWF1O2Jhc2U2NCxBVGdCR3dVZkF4Y0F5U1FBWlFRTklCRUVrK0FBQWEyd0FBR3RzQVlCQkFFWkFwOERGUUJsQlFRTlFCVUFDN2dBQVBvQUFBRDZBQVlCQXc9PQQNAQUABAAAAAAAAAAAAAYJAQAAAAAAAAAAA0IAAkA+ZGF0YTphcHBsaWNhdGlvbi9tcGVnNC1iaWZzLWF1O2Jhc2U2NCx3QkFTZ1RBcUJYSmhCSWhRUlFVL0FBPT0EEgINAAACAAAAAAAAAAAFAwAAQAYJAQAAAAAAAAAA" + } + else if ( attrName == "mpeg4-esid" ) + { + // a=mpeg4-esid:201 + } + else + { + Debug( 3, "Ignoring SDP attribute '%s' for media '%s'", line.c_str(), currMedia->getType().c_str() ) + } } + else + { + Debug( 3, "Ignoring general SDP attribute '%s'", line.c_str() ); + } + break; + } + case 'm' : + { + StringVector tokens = split( line, " " ); + if ( tokens.size() < 4 ) + throw Exception( "Can't parse SDP media description '"+line+"'" ); + std::string mediaType = tokens[0]; + if ( mediaType != "audio" && mediaType != "video" && mediaType != "application" ) + throw Exception( "Unsupported media type '"+mediaType+"' in SDP media attribute '"+line+"'" ); + StringVector portTokens = split( tokens[1], "/" ); + int mediaPort = atoi(portTokens[0].c_str()); + int mediaNumPorts = 1; + if ( portTokens.size() > 1 ) + mediaNumPorts = atoi(portTokens[1].c_str()); + std::string mediaTransport = tokens[2]; + if ( mediaTransport != "RTP/AVP" ) + throw Exception( "Unsupported media transport '"+mediaTransport+"' in SDP media attribute '"+line+"'" ); + int payloadType = atoi(tokens[3].c_str()); + currMedia = new MediaDescriptor( mediaType, mediaPort, mediaNumPorts, mediaTransport, payloadType ); + mMediaList.push_back( currMedia ); + break; + } } + } +} + +SessionDescriptor::~SessionDescriptor() +{ + if ( mConnInfo ) + delete mConnInfo; + if ( mBandInfo ) + delete mBandInfo; + for ( unsigned int i = 0; i < mMediaList.size(); i++ ) + delete mMediaList[i]; } AVFormatContext *SessionDescriptor::generateFormatContext() const { - AVFormatContext *formatContext = avformat_alloc_context(); + AVFormatContext *formatContext = avformat_alloc_context(); - strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) ); + strncpy( formatContext->filename, mUrl.c_str(), sizeof(formatContext->filename) ); /* - if ( mName.length() ) - strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) ); - if ( mInfo.length() ) - strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) ); + if ( mName.length() ) + strncpy( formatContext->title, mName.c_str(), sizeof(formatContext->title) ); + if ( mInfo.length() ) + strncpy( formatContext->comment, mInfo.c_str(), sizeof(formatContext->comment) ); */ - //formatContext->nb_streams = mMediaList.size(); - for ( unsigned int i = 0; i < mMediaList.size(); i++ ) + //formatContext->nb_streams = mMediaList.size(); + for ( unsigned int i = 0; i < mMediaList.size(); i++ ) + { + const MediaDescriptor *mediaDesc = mMediaList[i]; +#if !LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0) + AVStream *stream = av_new_stream( formatContext, i ); +#else + AVStream *stream = avformat_new_stream( formatContext, NULL ); + stream->id = i; +#endif + + Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) + if ( mediaDesc->getType() == "video" ) + stream->codec->codec_type = AVMEDIA_TYPE_VIDEO; + else if ( mediaDesc->getType() == "audio" ) + stream->codec->codec_type = AVMEDIA_TYPE_AUDIO; + else if ( mediaDesc->getType() == "application" ) + stream->codec->codec_type = AVMEDIA_TYPE_DATA; +#else + if ( mediaDesc->getType() == "video" ) + stream->codec->codec_type = CODEC_TYPE_VIDEO; + else if ( mediaDesc->getType() == "audio" ) + stream->codec->codec_type = CODEC_TYPE_AUDIO; + else if ( mediaDesc->getType() == "application" ) + stream->codec->codec_type = CODEC_TYPE_DATA; +#endif + +#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) + std::string codec_name; +#endif + if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC ) { - const MediaDescriptor *mediaDesc = mMediaList[i]; -#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 8, 0) - AVStream *stream = av_new_stream( formatContext, i ); + // Look in static table + for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ ) + { + if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() ) + { + Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName ); +#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) + codec_name = std::string( smStaticPayloads[i].payloadName ); #else - AVStream *stream = avformat_new_stream( formatContext, NULL ); - stream->id = i; + strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; #endif - - Debug( 1, "Looking for codec for %s payload type %d / %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,2,1) - if ( mediaDesc->getType() == "video" ) - stream->codec->codec_type = AVMEDIA_TYPE_VIDEO; - else if ( mediaDesc->getType() == "audio" ) - stream->codec->codec_type = AVMEDIA_TYPE_AUDIO; + stream->codec->codec_type = smStaticPayloads[i].codecType; + stream->codec->codec_id = smStaticPayloads[i].codecId; + stream->codec->sample_rate = smStaticPayloads[i].clockRate; + break; + } + } + } + else + { + // Look in dynamic table + for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) + { + if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() ) + { + Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName ); +#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) + codec_name = std::string( smStaticPayloads[i].payloadName ); #else - if ( mediaDesc->getType() == "video" ) - stream->codec->codec_type = CODEC_TYPE_VIDEO; - else if ( mediaDesc->getType() == "audio" ) - stream->codec->codec_type = CODEC_TYPE_AUDIO; + strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; #endif - - if ( mediaDesc->getPayloadType() < PAYLOAD_TYPE_DYNAMIC ) - { - // Look in static table - for ( unsigned int i = 0; i < (sizeof(smStaticPayloads)/sizeof(*smStaticPayloads)); i++ ) - { - if ( smStaticPayloads[i].payloadType == mediaDesc->getPayloadType() ) - { - Debug( 1, "Got static payload type %d, %s", smStaticPayloads[i].payloadType, smStaticPayloads[i].payloadName ); - strncpy( stream->codec->codec_name, smStaticPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; - stream->codec->codec_type = smStaticPayloads[i].codecType; - stream->codec->codec_id = smStaticPayloads[i].codecId; - stream->codec->sample_rate = smStaticPayloads[i].clockRate; - break; - } - } - } - else - { - // Look in dynamic table - for ( unsigned int i = 0; i < (sizeof(smDynamicPayloads)/sizeof(*smDynamicPayloads)); i++ ) - { - if ( smDynamicPayloads[i].payloadName == mediaDesc->getPayloadDesc() ) - { - Debug( 1, "Got dynamic payload type %d, %s", mediaDesc->getPayloadType(), smDynamicPayloads[i].payloadName ); - strncpy( stream->codec->codec_name, smDynamicPayloads[i].payloadName, sizeof(stream->codec->codec_name) );; - stream->codec->codec_type = smDynamicPayloads[i].codecType; - stream->codec->codec_id = smDynamicPayloads[i].codecId; - stream->codec->sample_rate = mediaDesc->getClock(); - break; - } - } - } - if ( !stream->codec->codec_name[0] ) - { - Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); - //return( 0 ); - } - if ( mediaDesc->getWidth() ) - stream->codec->width = mediaDesc->getWidth(); - if ( mediaDesc->getHeight() ) - stream->codec->height = mediaDesc->getHeight(); - if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size()) - { - uint8_t start_sequence[]= { 0, 0, 1 }; - stream->codec->extradata_size= 0; - stream->codec->extradata= NULL; - char pvalue[1024], *value = pvalue; - - strcpy(pvalue, mediaDesc->getSprops().c_str()); - - while (*value) { - char base64packet[1024]; - uint8_t decoded_packet[1024]; - uint32_t packet_size; - char *dst = base64packet; - - while (*value && *value != ',' - && (dst - base64packet) < (long)(sizeof(base64packet)) - 1) { - *dst++ = *value++; - } - *dst++ = '\0'; - - if (*value == ',') - value++; - - packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet)); - Hexdump(4, (char *)decoded_packet, packet_size); - if (packet_size) { - uint8_t *dest = - (uint8_t *)av_malloc(packet_size + sizeof(start_sequence) + - stream->codec->extradata_size + - FF_INPUT_BUFFER_PADDING_SIZE); - if(dest) { - if(stream->codec->extradata_size) { - // av_realloc? - memcpy(dest, stream->codec->extradata, stream->codec->extradata_size); - av_free(stream->codec->extradata); - } - - memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence)); - memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); - memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+ - packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); - - stream->codec->extradata= dest; - stream->codec->extradata_size+= sizeof(start_sequence)+packet_size; -// } else { -// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!"); -// return AVERROR(ENOMEM); - } - } - } + stream->codec->codec_type = smDynamicPayloads[i].codecType; + stream->codec->codec_id = smDynamicPayloads[i].codecId; + stream->codec->sample_rate = mediaDesc->getClock(); + break; } + } } - return( formatContext ); +#if LIBAVCODEC_VERSION_CHECK(55, 50, 3, 60, 103) + if ( codec_name.empty() ) +#else + if ( !stream->codec->codec_name[0] ) +#endif + { + Warning( "Can't find payload details for %s payload type %d, name %s", mediaDesc->getType().c_str(), mediaDesc->getPayloadType(), mediaDesc->getPayloadDesc().c_str() ); + //return( 0 ); + } + if ( mediaDesc->getWidth() ) + stream->codec->width = mediaDesc->getWidth(); + if ( mediaDesc->getHeight() ) + stream->codec->height = mediaDesc->getHeight(); + if ( stream->codec->codec_id == AV_CODEC_ID_H264 && mediaDesc->getSprops().size()) + { + uint8_t start_sequence[]= { 0, 0, 1 }; + stream->codec->extradata_size= 0; + stream->codec->extradata= NULL; + char pvalue[1024], *value = pvalue; + + strcpy(pvalue, mediaDesc->getSprops().c_str()); + + while (*value) { + char base64packet[1024]; + uint8_t decoded_packet[1024]; + uint32_t packet_size; + char *dst = base64packet; + + while (*value && *value != ',' + && (dst - base64packet) < (long)(sizeof(base64packet)) - 1) { + *dst++ = *value++; + } + *dst++ = '\0'; + + if (*value == ',') + value++; + + packet_size= av_base64_decode(decoded_packet, (const char *)base64packet, (int)sizeof(decoded_packet)); + Hexdump(4, (char *)decoded_packet, packet_size); + if (packet_size) { + uint8_t *dest = + (uint8_t *)av_malloc(packet_size + sizeof(start_sequence) + + stream->codec->extradata_size + + FF_INPUT_BUFFER_PADDING_SIZE); + if(dest) { + if(stream->codec->extradata_size) { + // av_realloc? + memcpy(dest, stream->codec->extradata, stream->codec->extradata_size); + av_free(stream->codec->extradata); + } + + memcpy(dest+stream->codec->extradata_size, start_sequence, sizeof(start_sequence)); + memcpy(dest+stream->codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); + memset(dest+stream->codec->extradata_size+sizeof(start_sequence)+ + packet_size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + + stream->codec->extradata= dest; + stream->codec->extradata_size+= sizeof(start_sequence)+packet_size; +// } else { +// av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!"); +// return AVERROR(ENOMEM); + } + } + } + } + } + + return( formatContext ); } #endif // HAVE_LIBAVFORMAT diff --git a/src/zm_sdp.h b/src/zm_sdp.h index 28d2d8c52..2d08905c8 100644 --- a/src/zm_sdp.h +++ b/src/zm_sdp.h @@ -34,203 +34,204 @@ class SessionDescriptor { protected: - enum { PAYLOAD_TYPE_DYNAMIC=96 }; + enum { PAYLOAD_TYPE_DYNAMIC=96 }; - struct StaticPayloadDesc - { - int payloadType; - const char payloadName[6]; -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 64, 0) - AVMediaType codecType; + struct StaticPayloadDesc + { + int payloadType; + const char payloadName[6]; +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) + AVMediaType codecType; #else - enum CodecType codecType; + enum CodecType codecType; #endif - _AVCODECID codecId; - int clockRate; - int autoChannels; - }; + _AVCODECID codecId; + int clockRate; + int autoChannels; + }; - struct DynamicPayloadDesc - { - const char payloadName[32]; -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 64, 0) - AVMediaType codecType; + struct DynamicPayloadDesc + { + const char payloadName[32]; +#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0)) + AVMediaType codecType; #else - enum CodecType codecType; + enum CodecType codecType; #endif - _AVCODECID codecId; + _AVCODECID codecId; - //int clockRate; - //int autoChannels; - }; + //int clockRate; + //int autoChannels; + }; public: - class ConnInfo - { - protected: - std::string mNetworkType; - std::string mAddressType; - std::string mAddress; - int mTtl; - int mNoAddresses; + class ConnInfo + { + protected: + std::string mNetworkType; + std::string mAddressType; + std::string mAddress; + int mTtl; + int mNoAddresses; - public: - ConnInfo( const std::string &connInfo ); - }; + public: + ConnInfo( const std::string &connInfo ); + }; - class BandInfo - { - protected: - std::string mType; - int mValue; + class BandInfo + { + protected: + std::string mType; + int mValue; - public: - BandInfo( const std::string &bandInfo ); - }; + public: + BandInfo( const std::string &bandInfo ); + }; - class MediaDescriptor - { - protected: - std::string mType; - int mPort; - int mNumPorts; - std::string mTransport; - int mPayloadType; + class MediaDescriptor + { + protected: + std::string mType; + int mPort; + int mNumPorts; + std::string mTransport; + int mPayloadType; - std::string mPayloadDesc; - std::string mControlUrl; - double mFrameRate; - int mClock; - int mWidth; - int mHeight; - std::string mSprops; - - ConnInfo *mConnInfo; - - public: - MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ); - - const std::string &getType() const - { - return( mType ); - } - int getPort() const - { - return( mPort ); - } - int getNumPorts() const - { - return( mNumPorts ); - } - const std::string &getTransport() const - { - return( mTransport ); - } - const int getPayloadType() const - { - return( mPayloadType ); - } - - const std::string &getPayloadDesc() const - { - return( mPayloadDesc ); - } - void setPayloadDesc( const std::string &payloadDesc ) - { - mPayloadDesc = payloadDesc; - } - - const std::string &getControlUrl() const - { - return( mControlUrl ); - } - void setControlUrl( const std::string &controlUrl ) - { - mControlUrl = controlUrl; - } - - const int getClock() const - { - return( mClock ); - } - void setClock( int clock ) - { - mClock = clock; - } - - void setFrameSize( int width, int height ) - { - mWidth = width; - mHeight = height; - } - int getWidth() const - { - return( mWidth ); - } - int getHeight() const - { - return( mHeight ); - } - - void setSprops(const std::string props) - { - mSprops = props; - } - const std::string getSprops() const - { - return ( mSprops ); - } - const double getFrameRate() const - { - return( mFrameRate ); - } - void setFrameRate( double frameRate ) - { - mFrameRate = frameRate; - } - }; - - typedef std::vector MediaList; - -protected: - static StaticPayloadDesc smStaticPayloads[]; - static DynamicPayloadDesc smDynamicPayloads[]; - -protected: - std::string mUrl; - - std::string mVersion; - std::string mOwner; - std::string mName; - std::string mInfo; + std::string mPayloadDesc; + std::string mControlUrl; + double mFrameRate; + int mClock; + int mWidth; + int mHeight; + std::string mSprops; ConnInfo *mConnInfo; - BandInfo *mBandInfo; - std::string mTimeInfo; - StringVector mAttributes; - MediaList mMediaList; + public: + MediaDescriptor( const std::string &type, int port, int numPorts, const std::string &transport, int payloadType ); + + const std::string &getType() const + { + return( mType ); + } + int getPort() const + { + return( mPort ); + } + int getNumPorts() const + { + return( mNumPorts ); + } + const std::string &getTransport() const + { + return( mTransport ); + } + const int getPayloadType() const + { + return( mPayloadType ); + } + + const std::string &getPayloadDesc() const + { + return( mPayloadDesc ); + } + void setPayloadDesc( const std::string &payloadDesc ) + { + mPayloadDesc = payloadDesc; + } + + const std::string &getControlUrl() const + { + return( mControlUrl ); + } + void setControlUrl( const std::string &controlUrl ) + { + mControlUrl = controlUrl; + } + + const int getClock() const + { + return( mClock ); + } + void setClock( int clock ) + { + mClock = clock; + } + + void setFrameSize( int width, int height ) + { + mWidth = width; + mHeight = height; + } + int getWidth() const + { + return( mWidth ); + } + int getHeight() const + { + return( mHeight ); + } + + void setSprops(const std::string props) + { + mSprops = props; + } + const std::string getSprops() const + { + return ( mSprops ); + } + const double getFrameRate() const + { + return( mFrameRate ); + } + void setFrameRate( double frameRate ) + { + mFrameRate = frameRate; + } + }; + + typedef std::vector MediaList; + +protected: + static StaticPayloadDesc smStaticPayloads[]; + static DynamicPayloadDesc smDynamicPayloads[]; + +protected: + std::string mUrl; + + std::string mVersion; + std::string mOwner; + std::string mName; + std::string mInfo; + + ConnInfo *mConnInfo; + BandInfo *mBandInfo; + std::string mTimeInfo; + StringVector mAttributes; + + MediaList mMediaList; public: - SessionDescriptor( const std::string &url, const std::string &sdp ); + SessionDescriptor( const std::string &url, const std::string &sdp ); + ~SessionDescriptor(); - const std::string &getUrl() const - { - return( mUrl ); - } + const std::string &getUrl() const + { + return( mUrl ); + } - int getNumStreams() const - { - return( mMediaList.size() ); - } - MediaDescriptor *getStream( int index ) - { - if ( index < 0 || (unsigned int)index >= mMediaList.size() ) - return( 0 ); - return( mMediaList[index] ); - } + int getNumStreams() const + { + return( mMediaList.size() ); + } + MediaDescriptor *getStream( int index ) + { + if ( index < 0 || (unsigned int)index >= mMediaList.size() ) + return( 0 ); + return( mMediaList[index] ); + } - AVFormatContext *generateFormatContext() const; + AVFormatContext *generateFormatContext() const; }; #if 0 v=0 @@ -253,7 +254,7 @@ a=mpeg4-esid:201 m=audio 0 RTP/AVP 0 b=AS:64 a=control:trackID=2 - + #endif #endif // ZM_SDP_H diff --git a/src/zm_sendfile.h b/src/zm_sendfile.h new file mode 100644 index 000000000..0e35388ea --- /dev/null +++ b/src/zm_sendfile.h @@ -0,0 +1,31 @@ +#ifdef HAVE_SENDFILE4_SUPPORT +#include +int zm_sendfile(int out_fd, int in_fd, off_t *offset, size_t size) { + int err; + + err = sendfile(out_fd, in_fd, offset, size); + if (err < 0) + return -errno; + + return err; +} +#elif HAVE_SENDFILE7_SUPPORT +#include +#include +#include +int zm_sendfile(int out_fd, int in_fd, off_t *offset, off_t size) { + int err; + err = sendfile(in_fd, out_fd, *offset, size, NULL, &size, 0); + if (err && errno != EAGAIN) + return -errno; + + if (size) { + *offset += size; + return size; + } + + return -EAGAIN; +} +#else +#error "Your platform does not support sendfile. Sorry." +#endif diff --git a/src/zm_signal.cpp b/src/zm_signal.cpp index 6db4697ed..bbc40e916 100644 --- a/src/zm_signal.cpp +++ b/src/zm_signal.cpp @@ -47,15 +47,12 @@ RETSIGTYPE zm_die_handler(int signal, siginfo_t * info, void *context) RETSIGTYPE zm_die_handler(int signal) #endif { -#if (defined(__i386__) || defined(__x86_64__)) - void *cr2 = 0; - void *ip = 0; -#endif Error("Got signal %d (%s), crashing", signal, strsignal(signal)); - #if (defined(__i386__) || defined(__x86_64__)) // Get more information if available -#if ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) + #if ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) + void *ip = 0; + void *cr2 = 0; if (info && context) { Debug(1, @@ -64,13 +61,20 @@ RETSIGTYPE zm_die_handler(int signal) info->si_uid, info->si_status); ucontext_t *uc = (ucontext_t *) context; -#if defined(__x86_64__) cr2 = info->si_addr; + #if defined(__x86_64__) + #ifdef __FreeBSD_kernel__ + ip = (void *)(uc->uc_mcontext.mc_rip); + #else ip = (void *)(uc->uc_mcontext.gregs[REG_RIP]); -#else - cr2 = info->si_addr; + #endif + #else + #ifdef __FreeBSD_kernel__ + ip = (void *)(uc->uc_mcontext.mc_eip); + #else ip = (void *)(uc->uc_mcontext.gregs[REG_EIP]); -#endif // defined(__x86_64__) + #endif + #endif // defined(__x86_64__) // Print the signal address and instruction pointer if available if (ip) { @@ -79,11 +83,11 @@ RETSIGTYPE zm_die_handler(int signal) Error("Signal address is %p, no instruction pointer", cr2); } } -#endif // ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) + #endif // ( HAVE_SIGINFO_T && HAVE_UCONTEXT_T ) // Print backtrace if enabled and available -#if ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS ) + #if ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS ) void *trace[TRACE_SIZE]; int trace_size = 0; trace_size = backtrace(trace, TRACE_SIZE); @@ -104,7 +108,7 @@ RETSIGTYPE zm_die_handler(int signal) Info("Backtrace complete, please execute the following command for more information"); Info(cmd); -#endif // ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS ) + #endif // ( !defined(ZM_NO_CRASHTRACE) && HAVE_DECL_BACKTRACE && HAVE_DECL_BACKTRACE_SYMBOLS ) #endif // (defined(__i386__) || defined(__x86_64__) exit(signal); } diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index 30a420bb1..8bbfda4e9 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -18,6 +18,10 @@ // #include +#include +#include +#include +#include #include "zm.h" #include "zm_mpeg.h" @@ -28,299 +32,335 @@ StreamBase::~StreamBase() { #if HAVE_LIBAVCODEC - if ( vid_stream ) - { - delete vid_stream; - vid_stream = NULL; - } + if ( vid_stream ) + { + delete vid_stream; + vid_stream = NULL; + } #endif - closeComms(); + closeComms(); } bool StreamBase::loadMonitor( int monitor_id ) { - if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) ) - { - Fatal( "Unable to load monitor id %d for streaming", monitor_id ); - return( false ); - } - monitor->connect(); - return( true ); + if ( !(monitor = Monitor::Load( monitor_id, false, Monitor::QUERY )) ) + { + Fatal( "Unable to load monitor id %d for streaming", monitor_id ); + return( false ); + } + monitor->connect(); + return( true ); } bool StreamBase::checkInitialised() { - if ( !monitor ) - { - Fatal( "Cannot stream, not initialised" ); - return( false ); - } - return( true ); + if ( !monitor ) + { + Fatal( "Cannot stream, not initialised" ); + return( false ); + } + return( true ); } void StreamBase::updateFrameRate( double fps ) { - base_fps = fps; - effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE; - frame_mod = 1; - Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod ); - // Min frame repeat? - while( effective_fps > maxfps ) - { - effective_fps /= 2.0; - frame_mod *= 2; - } - Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod ); + base_fps = fps; + effective_fps = (base_fps*abs(replay_rate))/ZM_RATE_BASE; + frame_mod = 1; + Debug( 3, "FPS:%.2f, MXFPS:%.2f, BFPS:%.2f, EFPS:%.2f, FM:%d", fps, maxfps, base_fps, effective_fps, frame_mod ); + // Min frame repeat? + while( effective_fps > maxfps ) + { + effective_fps /= 2.0; + frame_mod *= 2; + } + Debug( 3, "aEFPS:%.2f, aFM:%d", effective_fps, frame_mod ); } bool StreamBase::checkCommandQueue() { - if ( sd >= 0 ) + if ( sd >= 0 ) + { + CmdMsg msg; + memset( &msg, 0, sizeof(msg) ); + int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 ); + if ( nbytes < 0 ) { - CmdMsg msg; - memset( &msg, 0, sizeof(msg) ); - int nbytes = recvfrom( sd, &msg, sizeof(msg), MSG_DONTWAIT, 0, 0 ); - if ( nbytes < 0 ) - { - if ( errno != EAGAIN ) - { - Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) ); - } - } - //else if ( (nbytes != sizeof(msg)) ) - //{ - //Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes ); - //} - else - { - processCommand( &msg ); - return( true ); - } + if ( errno != EAGAIN ) + { + Fatal( "recvfrom(), errno = %d, error = %s", errno, strerror(errno) ); + } } - return( false ); + //else if ( (nbytes != sizeof(msg)) ) + //{ + //Error( "Partial message received, expected %d bytes, got %d", sizeof(msg), nbytes ); + //} + else + { + processCommand( &msg ); + return( true ); + } + } + return( false ); } Image *StreamBase::prepareImage( Image *image ) { - static int last_scale = 0; - static int last_zoom = 0; - static int last_x = 0; - static int last_y = 0; + static int last_scale = 0; + static int last_zoom = 0; + static int last_x = 0; + static int last_y = 0; - if ( !last_scale ) - last_scale = scale; - if ( !last_zoom ) - last_zoom = zoom; - - // Do not bother to scale zoomed in images, just crop them and let the browser scale - // Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream. - bool optimisedScaling = false; - - bool image_copied = false; - - int mag = (scale * zoom) / ZM_SCALE_BASE; - int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag; - Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag ); - - int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE; - int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag; - Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag ); - - int base_image_width = image->Width(), base_image_height = image->Height(); - Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height ); - - int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE; - Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height ); - - int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE; - Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height ); - - int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE; - Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height ); - - int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE; - Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height ); - - int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE; - Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height ); - - int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE; - Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height ); - - int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag; - Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height ); - - int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag; - Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height ); - - if ( mag != ZM_SCALE_BASE ) - { - if ( act_mag != ZM_SCALE_BASE ) - { - Debug( 3, "Magnifying by %d", mag ); - if ( !image_copied ) - { - static Image copy_image; - copy_image.Assign( *image ); - image = ©_image; - image_copied = true; - } - image->Scale( mag ); - } - } - - Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() ); - - if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height ) - { - static Box last_crop; - - if ( mag != last_mag || x != last_x || y != last_y ) - { - Debug( 3, "Got click at %d,%d x %d", x, y, mag ); - - //if ( !last_mag ) - //last_mag = mag; - - if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) ) - last_crop = Box(); - - Debug( 3, "Recalculating crop" ); - // Recalculate crop parameters, as %ges - int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image - click_x += ( x * 100 ) / last_virt_image_width; - int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image - click_y += ( y * 100 ) / last_virt_image_height; - Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y ); - - // Convert the click locations to the current image pixels - click_x = ( click_x * act_image_width ) / 100; - click_y = ( click_y * act_image_height ) / 100; - Debug( 3, "Got readjusted click at %d,%d", click_x, click_y ); - - int lo_x = click_x - (send_image_width/2); - if ( lo_x < 0 ) - lo_x = 0; - int hi_x = lo_x + (send_image_width-1); - if ( hi_x >= act_image_width ) - { - hi_x = act_image_width - 1; - lo_x = hi_x - (send_image_width - 1); - } - - int lo_y = click_y - (send_image_height/2); - if ( lo_y < 0 ) - lo_y = 0; - int hi_y = lo_y + (send_image_height-1); - if ( hi_y >= act_image_height ) - { - hi_y = act_image_height - 1; - lo_y = hi_y - (send_image_height - 1); - } - last_crop = Box( lo_x, lo_y, hi_x, hi_y ); - } - Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() ); - if ( !image_copied ) - { - static Image copy_image; - copy_image.Assign( *image ); - image = ©_image; - image_copied = true; - } - image->Crop( last_crop ); - } + if ( !last_scale ) last_scale = scale; + if ( !last_zoom ) last_zoom = zoom; - last_x = x; - last_y = y; - return( image ); + // Do not bother to scale zoomed in images, just crop them and let the browser scale + // Works in FF2 but breaks FF3 which doesn't like image sizes changing in mid stream. + bool optimisedScaling = false; + + bool image_copied = false; + + int mag = (scale * zoom) / ZM_SCALE_BASE; + int act_mag = optimisedScaling?(mag > ZM_SCALE_BASE?ZM_SCALE_BASE:mag):mag; + Debug( 3, "Scaling by %d, zooming by %d = magnifying by %d(%d)", scale, zoom, mag, act_mag ); + + int last_mag = (last_scale * last_zoom) / ZM_SCALE_BASE; + int last_act_mag = last_mag > ZM_SCALE_BASE?ZM_SCALE_BASE:last_mag; + Debug( 3, "Last scaling by %d, zooming by %d = magnifying by %d(%d)", last_scale, last_zoom, last_mag, last_act_mag ); + + int base_image_width = image->Width(), base_image_height = image->Height(); + Debug( 3, "Base image width = %d, height = %d", base_image_width, base_image_height ); + + int virt_image_width = (base_image_width * mag) / ZM_SCALE_BASE, virt_image_height = (base_image_height * mag) / ZM_SCALE_BASE; + Debug( 3, "Virtual image width = %d, height = %d", virt_image_width, virt_image_height ); + + int last_virt_image_width = (base_image_width * last_mag) / ZM_SCALE_BASE, last_virt_image_height = (base_image_height * last_mag) / ZM_SCALE_BASE; + Debug( 3, "Last virtual image width = %d, height = %d", last_virt_image_width, last_virt_image_height ); + + int act_image_width = (base_image_width * act_mag ) / ZM_SCALE_BASE, act_image_height = (base_image_height * act_mag ) / ZM_SCALE_BASE; + Debug( 3, "Actual image width = %d, height = %d", act_image_width, act_image_height ); + + int last_act_image_width = (base_image_width * last_act_mag ) / ZM_SCALE_BASE, last_act_image_height = (base_image_height * last_act_mag ) / ZM_SCALE_BASE; + Debug( 3, "Last actual image width = %d, height = %d", last_act_image_width, last_act_image_height ); + + int disp_image_width = (image->Width() * scale) / ZM_SCALE_BASE, disp_image_height = (image->Height() * scale) / ZM_SCALE_BASE; + Debug( 3, "Display image width = %d, height = %d", disp_image_width, disp_image_height ); + + int last_disp_image_width = (image->Width() * last_scale) / ZM_SCALE_BASE, last_disp_image_height = (image->Height() * last_scale) / ZM_SCALE_BASE; + Debug( 3, "Last display image width = %d, height = %d", last_disp_image_width, last_disp_image_height ); + + int send_image_width = (disp_image_width * act_mag ) / mag, send_image_height = (disp_image_height * act_mag ) / mag; + Debug( 3, "Send image width = %d, height = %d", send_image_width, send_image_height ); + + int last_send_image_width = (last_disp_image_width * last_act_mag ) / last_mag, last_send_image_height = (last_disp_image_height * last_act_mag ) / last_mag; + Debug( 3, "Last send image width = %d, height = %d", last_send_image_width, last_send_image_height ); + + if ( mag != ZM_SCALE_BASE ) + { + if ( act_mag != ZM_SCALE_BASE ) + { + Debug( 3, "Magnifying by %d", mag ); + if ( !image_copied ) + { + static Image copy_image; + copy_image.Assign( *image ); + image = ©_image; + image_copied = true; + } + image->Scale( mag ); + } + } + + Debug( 3, "Real image width = %d, height = %d", image->Width(), image->Height() ); + + if ( disp_image_width < virt_image_width || disp_image_height < virt_image_height ) + { + static Box last_crop; + + if ( mag != last_mag || x != last_x || y != last_y ) + { + Debug( 3, "Got click at %d,%d x %d", x, y, mag ); + + //if ( !last_mag ) + //last_mag = mag; + + if ( !(last_disp_image_width < last_virt_image_width || last_disp_image_height < last_virt_image_height) ) + last_crop = Box(); + + Debug( 3, "Recalculating crop" ); + // Recalculate crop parameters, as %ges + int click_x = (last_crop.LoX() * 100 ) / last_act_image_width; // Initial crop offset from last image + click_x += ( x * 100 ) / last_virt_image_width; + int click_y = (last_crop.LoY() * 100 ) / last_act_image_height; // Initial crop offset from last image + click_y += ( y * 100 ) / last_virt_image_height; + Debug( 3, "Got adjusted click at %d%%,%d%%", click_x, click_y ); + + // Convert the click locations to the current image pixels + click_x = ( click_x * act_image_width ) / 100; + click_y = ( click_y * act_image_height ) / 100; + Debug( 3, "Got readjusted click at %d,%d", click_x, click_y ); + + int lo_x = click_x - (send_image_width/2); + if ( lo_x < 0 ) + lo_x = 0; + int hi_x = lo_x + (send_image_width-1); + if ( hi_x >= act_image_width ) + { + hi_x = act_image_width - 1; + lo_x = hi_x - (send_image_width - 1); + } + + int lo_y = click_y - (send_image_height/2); + if ( lo_y < 0 ) + lo_y = 0; + int hi_y = lo_y + (send_image_height-1); + if ( hi_y >= act_image_height ) + { + hi_y = act_image_height - 1; + lo_y = hi_y - (send_image_height - 1); + } + last_crop = Box( lo_x, lo_y, hi_x, hi_y ); + } + Debug( 3, "Cropping to %d,%d -> %d,%d", last_crop.LoX(), last_crop.LoY(), last_crop.HiX(), last_crop.HiY() ); + if ( !image_copied ) + { + static Image copy_image; + copy_image.Assign( *image ); + image = ©_image; + image_copied = true; + } + image->Crop( last_crop ); + } + last_scale = scale; + last_zoom = zoom; + last_x = x; + last_y = y; + + return( image ); } bool StreamBase::sendTextFrame( const char *frame_text ) { - Debug( 2, "Sending text frame '%s'", frame_text ); + Debug( 2, "Sending text frame '%s'", frame_text ); - Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() ); - image.Annotate( frame_text, image.centreCoord( frame_text ) ); + Image image( monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder() ); + image.Annotate( frame_text, image.centreCoord( frame_text ) ); - if ( scale != 100 ) - { - image.Scale( scale ); - } + if ( scale != 100 ) + { + image.Scale( scale ); + } #if HAVE_LIBAVCODEC - if ( type == STREAM_MPEG ) + if ( type == STREAM_MPEG ) + { + if ( !vid_stream ) { - if ( !vid_stream ) - { - vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() ); - fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); - vid_stream->OpenStream(); - } - /* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() ); + vid_stream = new VideoStream( "pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height() ); + fprintf( stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType() ); + vid_stream->OpenStream(); } - else + /* double pts = */ vid_stream->EncodeFrame( image.Buffer(), image.Size() ); + } + else #endif // HAVE_LIBAVCODEC + { + static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; + int n_bytes = 0; + + image.EncodeJpeg( buffer, &n_bytes ); + + fprintf( stdout, "--ZoneMinderFrame\r\n" ); + fprintf( stdout, "Content-Length: %d\r\n", n_bytes ); + fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); + if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 ) { - static unsigned char buffer[ZM_MAX_IMAGE_SIZE]; - int n_bytes = 0; - - image.EncodeJpeg( buffer, &n_bytes ); - - fprintf( stdout, "--ZoneMinderFrame\r\n" ); - fprintf( stdout, "Content-Length: %d\r\n", n_bytes ); - fprintf( stdout, "Content-Type: image/jpeg\r\n\r\n" ); - if ( fwrite( buffer, n_bytes, 1, stdout ) != 1 ) - { - Error( "Unable to send stream text frame: %s", strerror(errno) ); - return( false ); - } - fprintf( stdout, "\r\n\r\n" ); - fflush( stdout ); + Error( "Unable to send stream text frame: %s", strerror(errno) ); + return( false ); } - last_frame_sent = TV_2_FLOAT( now ); - return( true ); + fprintf( stdout, "\r\n\r\n" ); + fflush( stdout ); + } + last_frame_sent = TV_2_FLOAT( now ); + return( true ); } void StreamBase::openComms() { - if ( connkey > 0 ) - { - sd = socket( AF_UNIX, SOCK_DGRAM, 0 ); - if ( sd < 0 ) - { - Fatal( "Can't create socket: %s", strerror(errno) ); - } + if ( connkey > 0 ) + { - snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey ); - unlink( loc_sock_path ); - - strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) ); - loc_addr.sun_family = AF_UNIX; - if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)) < 0 ) - { - Fatal( "Can't bind: %s", strerror(errno) ); - } - - snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey ); - strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) ); - rem_addr.sun_family = AF_UNIX; + unsigned int length = snprintf( sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", config.path_socks, connkey); + if ( length >= sizeof(sock_path_lock) ) { + Warning("Socket lock path was truncated."); + length = sizeof(sock_path_lock)-1; } + + lock_fd = open(sock_path_lock, O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR); + if ( lock_fd <= 0 ) + { + Error("Unable to open sock lock file %s: %s", sock_path_lock, strerror(errno) ); + lock_fd = 0; + } + else if ( flock(lock_fd, LOCK_EX) != 0 ) + { + Error("Unable to lock sock lock file %s: %s", sock_path_lock, strerror(errno) ); + close(lock_fd); + lock_fd = 0; + } + else + { + Debug( 1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd); + } + + sd = socket( AF_UNIX, SOCK_DGRAM, 0 ); + if ( sd < 0 ) + { + Fatal( "Can't create socket: %s", strerror(errno) ); + } + + length = snprintf( loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", config.path_socks, connkey ); + if ( length >= sizeof(loc_sock_path) ) { + Warning("Socket path was truncated."); + length = sizeof(loc_sock_path)-1; + } + unlink( loc_sock_path ); + if ( sizeof(loc_addr.sun_path) < length ) { + Error("Not enough space %d in loc_addr.sun_path for socket file %s", sizeof(loc_addr.sun_path), loc_sock_path ); + } + + strncpy( loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path) ); + loc_addr.sun_family = AF_UNIX; + if ( bind( sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)+1 ) < 0 ) + { + Fatal( "Can't bind: %s", strerror(errno) ); + } + + snprintf( rem_sock_path, sizeof(rem_sock_path), "%s/zms-%06dw.sock", config.path_socks, connkey ); + strncpy( rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path) ); + rem_addr.sun_family = AF_UNIX; + } // end if connKey > 0 } void StreamBase::closeComms() { - if ( connkey > 0 ) + if ( connkey > 0 ) + { + if ( sd >= 0 ) { - if ( sd >= 0 ) - { - close( sd ); - sd = -1; - } - if ( loc_sock_path[0] ) - { - unlink( loc_sock_path ); - } + close( sd ); + sd = -1; } + if ( loc_sock_path[0] ) + { + unlink( loc_sock_path ); + } + if (lock_fd > 0) + { + close(lock_fd); //close it rather than unlock it incase it got deleted. + unlink(sock_path_lock); + } + } } diff --git a/src/zm_stream.h b/src/zm_stream.h index ef442ab8e..c7df53d16 100644 --- a/src/zm_stream.h +++ b/src/zm_stream.h @@ -33,146 +33,149 @@ class Monitor; class StreamBase { public: - typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType; + typedef enum { STREAM_JPEG, STREAM_RAW, STREAM_ZIP, STREAM_SINGLE, STREAM_MPEG } StreamType; protected: - static const int MAX_STREAM_DELAY = 5; // Seconds + static const int MAX_STREAM_DELAY = 5; // Seconds - static const StreamType DEFAULT_TYPE = STREAM_JPEG; - enum { DEFAULT_RATE=ZM_RATE_BASE }; - enum { DEFAULT_SCALE=ZM_SCALE_BASE }; - enum { DEFAULT_ZOOM=ZM_SCALE_BASE }; - enum { DEFAULT_MAXFPS=10 }; - enum { DEFAULT_BITRATE=100000 }; + static const StreamType DEFAULT_TYPE = STREAM_JPEG; + enum { DEFAULT_RATE=ZM_RATE_BASE }; + enum { DEFAULT_SCALE=ZM_SCALE_BASE }; + enum { DEFAULT_ZOOM=ZM_SCALE_BASE }; + enum { DEFAULT_MAXFPS=10 }; + enum { DEFAULT_BITRATE=100000 }; protected: - typedef struct { - int msg_type; - char msg_data[16]; - } CmdMsg; + typedef struct { + int msg_type; + char msg_data[16]; + } CmdMsg; - typedef struct { - int msg_type; - char msg_data[256]; - } DataMsg; + typedef struct { + int msg_type; + char msg_data[256]; + } DataMsg; - typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType; - typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUERY=99 } MsgCommand; + typedef enum { MSG_CMD=1, MSG_DATA_WATCH, MSG_DATA_EVENT } MsgType; + typedef enum { CMD_NONE=0, CMD_PAUSE, CMD_PLAY, CMD_STOP, CMD_FASTFWD, CMD_SLOWFWD, CMD_SLOWREV, CMD_FASTREV, CMD_ZOOMIN, CMD_ZOOMOUT, CMD_PAN, CMD_SCALE, CMD_PREV, CMD_NEXT, CMD_SEEK, CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, CMD_QUERY=99 } MsgCommand; protected: - Monitor *monitor; + Monitor *monitor; - StreamType type; - const char *format; - int replay_rate; - int scale; - int zoom; - double maxfps; - int bitrate; - unsigned short x, y; + StreamType type; + const char *format; + int replay_rate; + int scale; + int zoom; + double maxfps; + int bitrate; + unsigned short x, y; protected: - int connkey; - int sd; - char loc_sock_path[PATH_MAX]; - struct sockaddr_un loc_addr; - char rem_sock_path[PATH_MAX]; - struct sockaddr_un rem_addr; + int connkey; + int sd; + char loc_sock_path[PATH_MAX]; + struct sockaddr_un loc_addr; + char rem_sock_path[PATH_MAX]; + struct sockaddr_un rem_addr; + char sock_path_lock[PATH_MAX]; + int lock_fd; protected: - bool paused; - int step; + bool paused; + int step; - struct timeval now; + struct timeval now; - double base_fps; - double effective_fps; - int frame_mod; + double base_fps; + double effective_fps; + int frame_mod; - double last_frame_sent; - struct timeval last_frame_timestamp; + double last_frame_sent; + struct timeval last_frame_timestamp; -#if HAVE_LIBAVCODEC - VideoStream *vid_stream; -#endif // HAVE_LIBAVCODEC +#if HAVE_LIBAVCODEC + VideoStream *vid_stream; +#endif // HAVE_LIBAVCODEC - CmdMsg msg; + CmdMsg msg; protected: - bool loadMonitor( int monitor_id ); - bool checkInitialised(); - void updateFrameRate( double fps ); - Image *prepareImage( Image *image ); - bool sendTextFrame( const char *text ); - bool checkCommandQueue(); - virtual void processCommand( const CmdMsg *msg )=0; + bool loadMonitor( int monitor_id ); + bool checkInitialised(); + void updateFrameRate( double fps ); + Image *prepareImage( Image *image ); + bool sendTextFrame( const char *text ); + bool checkCommandQueue(); + virtual void processCommand( const CmdMsg *msg )=0; public: - StreamBase() - { - monitor = 0; + StreamBase() + { + monitor = 0; - type = DEFAULT_TYPE; - format = ""; - replay_rate = DEFAULT_RATE; - scale = DEFAULT_SCALE; - zoom = DEFAULT_ZOOM; - maxfps = DEFAULT_MAXFPS; - bitrate = DEFAULT_BITRATE; + type = DEFAULT_TYPE; + format = ""; + replay_rate = DEFAULT_RATE; + scale = DEFAULT_SCALE; + zoom = DEFAULT_ZOOM; + maxfps = DEFAULT_MAXFPS; + bitrate = DEFAULT_BITRATE; - paused = false; - step = 0; - x = 0; - y = 0; + paused = false; + step = 0; + x = 0; + y = 0; - connkey = 0; - sd = -1; - memset( &loc_sock_path, 0, sizeof(loc_sock_path) ); - memset( &loc_addr, 0, sizeof(loc_addr) ); - memset( &rem_sock_path, 0, sizeof(rem_sock_path) ); - memset( &rem_addr, 0, sizeof(rem_addr) ); + connkey = 0; + sd = -1; + lock_fd = 0; + memset( &loc_sock_path, 0, sizeof(loc_sock_path) ); + memset( &loc_addr, 0, sizeof(loc_addr) ); + memset( &rem_sock_path, 0, sizeof(rem_sock_path) ); + memset( &rem_addr, 0, sizeof(rem_addr) ); - base_fps = 0.0; - effective_fps = 0.0; - frame_mod = 1; + base_fps = 0.0; + effective_fps = 0.0; + frame_mod = 1; -#if HAVE_LIBAVCODEC - vid_stream = 0; -#endif // HAVE_LIBAVCODEC - } - virtual ~StreamBase(); +#if HAVE_LIBAVCODEC + vid_stream = 0; +#endif // HAVE_LIBAVCODEC + } + virtual ~StreamBase(); - void setStreamType( StreamType p_type ) - { - type = p_type; - } - void setStreamFormat( const char *p_format ) - { - format = p_format; - } - void setStreamScale( int p_scale ) - { - scale = p_scale; - } - void setStreamReplayRate( int p_rate ) - { - replay_rate = p_rate; - } - void setStreamMaxFPS( double p_maxfps ) - { - maxfps = p_maxfps; - } - void setStreamBitrate( int p_bitrate ) - { - bitrate = p_bitrate; - } - void setStreamQueue( int p_connkey ) - { - connkey = p_connkey; - } - virtual void openComms(); - virtual void closeComms(); - virtual void runStream()=0; + void setStreamType( StreamType p_type ) + { + type = p_type; + } + void setStreamFormat( const char *p_format ) + { + format = p_format; + } + void setStreamScale( int p_scale ) + { + scale = p_scale; + } + void setStreamReplayRate( int p_rate ) + { + replay_rate = p_rate; + } + void setStreamMaxFPS( double p_maxfps ) + { + maxfps = p_maxfps; + } + void setStreamBitrate( int p_bitrate ) + { + bitrate = p_bitrate; + } + void setStreamQueue( int p_connkey ) + { + connkey = p_connkey; + } + virtual void openComms(); + virtual void closeComms(); + virtual void runStream()=0; }; #endif // ZM_STREAM_H diff --git a/src/zm_thread.cpp b/src/zm_thread.cpp index 09cf0da3f..7d84037d9 100644 --- a/src/zm_thread.cpp +++ b/src/zm_thread.cpp @@ -29,311 +29,310 @@ struct timespec getTimeout( int secs ) { - struct timespec timeout; - struct timeval temp_timeout; - gettimeofday( &temp_timeout, 0 ); - timeout.tv_sec = temp_timeout.tv_sec + secs; - timeout.tv_nsec = temp_timeout.tv_usec*1000; - return( timeout ); + struct timespec timeout; + struct timeval temp_timeout; + gettimeofday( &temp_timeout, 0 ); + timeout.tv_sec = temp_timeout.tv_sec + secs; + timeout.tv_nsec = temp_timeout.tv_usec*1000; + return( timeout ); } struct timespec getTimeout( double secs ) { - struct timespec timeout; - struct timeval temp_timeout; - gettimeofday( &temp_timeout, 0 ); - timeout.tv_sec = temp_timeout.tv_sec + int(secs); - timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs))); - if ( timeout.tv_nsec > 1000000000 ) - { - timeout.tv_sec += 1; - timeout.tv_nsec -= 1000000000; - } - return( timeout ); + struct timespec timeout; + struct timeval temp_timeout; + gettimeofday( &temp_timeout, 0 ); + timeout.tv_sec = temp_timeout.tv_sec + int(secs); + timeout.tv_nsec = temp_timeout.tv_usec += (long int)(1000000000.0*(secs-int(secs))); + if ( timeout.tv_nsec > 1000000000 ) + { + timeout.tv_sec += 1; + timeout.tv_nsec -= 1000000000; + } + return( timeout ); } Mutex::Mutex() { - if ( pthread_mutex_init( &mMutex, NULL ) < 0 ) - throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) ); + if ( pthread_mutex_init( &mMutex, NULL ) < 0 ) + throw ThreadException( stringtf( "Unable to create pthread mutex: %s", strerror(errno) ) ); } Mutex::~Mutex() { - if ( locked() ) - Warning( "Destroying mutex when locked" ); - if ( pthread_mutex_destroy( &mMutex ) < 0 ) - throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) ); + if ( locked() ) + Warning( "Destroying mutex when locked" ); + if ( pthread_mutex_destroy( &mMutex ) < 0 ) + throw ThreadException( stringtf( "Unable to destroy pthread mutex: %s", strerror(errno) ) ); } void Mutex::lock() { - if ( pthread_mutex_lock( &mMutex ) < 0 ) - throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) ); + if ( pthread_mutex_lock( &mMutex ) < 0 ) + throw ThreadException( stringtf( "Unable to lock pthread mutex: %s", strerror(errno) ) ); } void Mutex::lock( int secs ) { - struct timespec timeout = getTimeout( secs ); - if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 ) - throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) ); + struct timespec timeout = getTimeout( secs ); + if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 ) + throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) ); } void Mutex::lock( double secs ) { - struct timespec timeout = getTimeout( secs ); - if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 ) - throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) ); + struct timespec timeout = getTimeout( secs ); + if ( pthread_mutex_timedlock( &mMutex, &timeout ) < 0 ) + throw ThreadException( stringtf( "Unable to timedlock pthread mutex: %s", strerror(errno) ) ); } void Mutex::unlock() { - if ( pthread_mutex_unlock( &mMutex ) < 0 ) - throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) ); + if ( pthread_mutex_unlock( &mMutex ) < 0 ) + throw ThreadException( stringtf( "Unable to unlock pthread mutex: %s", strerror(errno) ) ); } bool Mutex::locked() { - int state = pthread_mutex_trylock( &mMutex ); - if ( state != 0 && state != EBUSY ) - throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) ); - if ( state != EBUSY ) - unlock(); - return( state == EBUSY ); + int state = pthread_mutex_trylock( &mMutex ); + if ( state != 0 && state != EBUSY ) + throw ThreadException( stringtf( "Unable to trylock pthread mutex: %s", strerror(errno) ) ); + if ( state != EBUSY ) + unlock(); + return( state == EBUSY ); } Condition::Condition( Mutex &mutex ) : mMutex( mutex ) { - if ( pthread_cond_init( &mCondition, NULL ) < 0 ) - throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) ); + if ( pthread_cond_init( &mCondition, NULL ) < 0 ) + throw ThreadException( stringtf( "Unable to create pthread condition: %s", strerror(errno) ) ); } Condition::~Condition() { - if ( pthread_cond_destroy( &mCondition ) < 0 ) - throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) ); + if ( pthread_cond_destroy( &mCondition ) < 0 ) + throw ThreadException( stringtf( "Unable to destroy pthread condition: %s", strerror(errno) ) ); } void Condition::wait() { - // Locking done outside of this function - if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 ) - throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) ); + // Locking done outside of this function + if ( pthread_cond_wait( &mCondition, mMutex.getMutex() ) < 0 ) + throw ThreadException( stringtf( "Unable to wait pthread condition: %s", strerror(errno) ) ); } bool Condition::wait( int secs ) { - // Locking done outside of this function - Debug( 8, "Waiting for %d seconds", secs ); - struct timespec timeout = getTimeout( secs ); - if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT ) - throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) ); - return( errno != ETIMEDOUT ); + // Locking done outside of this function + Debug( 8, "Waiting for %d seconds", secs ); + struct timespec timeout = getTimeout( secs ); + if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT ) + throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) ); + return( errno != ETIMEDOUT ); } bool Condition::wait( double secs ) { - // Locking done outside of this function - struct timespec timeout = getTimeout( secs ); - if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT ) - throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) ); - return( errno != ETIMEDOUT ); + // Locking done outside of this function + struct timespec timeout = getTimeout( secs ); + if ( pthread_cond_timedwait( &mCondition, mMutex.getMutex(), &timeout ) < 0 && errno != ETIMEDOUT ) + throw ThreadException( stringtf( "Unable to timedwait pthread condition: %s", strerror(errno) ) ); + return( errno != ETIMEDOUT ); } void Condition::signal() { - if ( pthread_cond_signal( &mCondition ) < 0 ) - throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) ); + if ( pthread_cond_signal( &mCondition ) < 0 ) + throw ThreadException( stringtf( "Unable to signal pthread condition: %s", strerror(errno) ) ); } void Condition::broadcast() { - if ( pthread_cond_broadcast( &mCondition ) < 0 ) - throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) ); + if ( pthread_cond_broadcast( &mCondition ) < 0 ) + throw ThreadException( stringtf( "Unable to broadcast pthread condition: %s", strerror(errno) ) ); } template const T ThreadData::getValue() const { - mMutex.lock(); - const T valueCopy = mValue; - mMutex.unlock(); - return( valueCopy ); + mMutex.lock(); + const T valueCopy = mValue; + mMutex.unlock(); + return( valueCopy ); } template T ThreadData::setValue( const T value ) { - mMutex.lock(); - const T valueCopy = mValue = value; - mMutex.unlock(); - return( valueCopy ); + mMutex.lock(); + const T valueCopy = mValue = value; + mMutex.unlock(); + return( valueCopy ); } template const T ThreadData::getUpdatedValue() const { - Debug( 8, "Waiting for value update, %p", this ); - mMutex.lock(); - mChanged = false; - //do { - mCondition.wait(); - //} while ( !mChanged ); - const T valueCopy = mValue; - mMutex.unlock(); - Debug( 9, "Got value update, %p", this ); - return( valueCopy ); + Debug( 8, "Waiting for value update, %p", this ); + mMutex.lock(); + mChanged = false; + //do { + mCondition.wait(); + //} while ( !mChanged ); + const T valueCopy = mValue; + mMutex.unlock(); + Debug( 9, "Got value update, %p", this ); + return( valueCopy ); } template const T ThreadData::getUpdatedValue( double secs ) const { - Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this ); - mMutex.lock(); - mChanged = false; - //do { - mCondition.wait( secs ); - //} while ( !mChanged ); - const T valueCopy = mValue; - mMutex.unlock(); - Debug( 9, "Got value update, %p", this ); - return( valueCopy ); + Debug( 8, "Waiting for value update, %.2f secs, %p", secs, this ); + mMutex.lock(); + mChanged = false; + //do { + mCondition.wait( secs ); + //} while ( !mChanged ); + const T valueCopy = mValue; + mMutex.unlock(); + Debug( 9, "Got value update, %p", this ); + return( valueCopy ); } template const T ThreadData::getUpdatedValue( int secs ) const { - Debug( 8, "Waiting for value update, %d secs, %p", secs, this ); - mMutex.lock(); - mChanged = false; - //do { - mCondition.wait( secs ); - //} while ( !mChanged ); - const T valueCopy = mValue; - mMutex.unlock(); - Debug( 9, "Got value update, %p", this ); - return( valueCopy ); + Debug( 8, "Waiting for value update, %d secs, %p", secs, this ); + mMutex.lock(); + mChanged = false; + //do { + mCondition.wait( secs ); + //} while ( !mChanged ); + const T valueCopy = mValue; + mMutex.unlock(); + Debug( 9, "Got value update, %p", this ); + return( valueCopy ); } template void ThreadData::updateValueSignal( const T value ) { - Debug( 8, "Updating value with signal, %p", this ); - mMutex.lock(); - mValue = value; - mChanged = true; - mCondition.signal(); - mMutex.unlock(); - Debug( 9, "Updated value, %p", this ); + Debug( 8, "Updating value with signal, %p", this ); + mMutex.lock(); + mValue = value; + mChanged = true; + mCondition.signal(); + mMutex.unlock(); + Debug( 9, "Updated value, %p", this ); } template void ThreadData::updateValueBroadcast( const T value ) { - Debug( 8, "Updating value with broadcast, %p", this ); - mMutex.lock(); - mValue = value; - mChanged = true; - mCondition.broadcast(); - mMutex.unlock(); - Debug( 9, "Updated value, %p", this ); + Debug( 8, "Updating value with broadcast, %p", this ); + mMutex.lock(); + mValue = value; + mChanged = true; + mCondition.broadcast(); + mMutex.unlock(); + Debug( 9, "Updated value, %p", this ); } Thread::Thread() : - mThreadCondition( mThreadMutex ), - mPid( -1 ), - mStarted( false ), - mRunning( false ) + mThreadCondition( mThreadMutex ), + mPid( -1 ), + mStarted( false ), + mRunning( false ) { - Debug( 1, "Creating thread" ); + Debug( 1, "Creating thread" ); } Thread::~Thread() { - Debug( 1, "Destroying thread %d", mPid ); - if ( mStarted ) - join(); + Debug( 1, "Destroying thread %d", mPid ); + if ( mStarted ) + join(); } void *Thread::mThreadFunc( void *arg ) { - Debug( 2, "Invoking thread" ); + Debug( 2, "Invoking thread" ); - Thread *thisPtr = (Thread *)arg; - void *status = 0; - try - { - thisPtr->mThreadMutex.lock(); - thisPtr->mPid = thisPtr->id(); - thisPtr->mThreadCondition.signal(); - thisPtr->mThreadMutex.unlock(); - thisPtr->mRunning = true; - int run=(thisPtr->run()); - status = (void *)&run; - thisPtr->mRunning = false; - Debug( 2, "Exiting thread, status %p", status ); - } - catch ( const ThreadException &e ) - { - Error( "%s", e.getMessage().c_str() ); - thisPtr->mRunning = false; - status = (void *)-1; - Debug( 2, "Exiting thread after exception, status %p", status ); - } - return( status ); + Thread *thisPtr = (Thread *)arg; + thisPtr->status = 0; + try + { + thisPtr->mThreadMutex.lock(); + thisPtr->mPid = thisPtr->id(); + thisPtr->mThreadCondition.signal(); + thisPtr->mThreadMutex.unlock(); + thisPtr->mRunning = true; + thisPtr->status = thisPtr->run(); + thisPtr->mRunning = false; + Debug( 2, "Exiting thread, status %p", (void *)&(thisPtr->status) ); + return (void *)&(thisPtr->status); + } + catch ( const ThreadException &e ) + { + Error( "%s", e.getMessage().c_str() ); + thisPtr->mRunning = false; + Debug( 2, "Exiting thread after exception, status %p", (void *)-1 ); + return (void *)-1; + } } void Thread::start() { - Debug( 1, "Starting thread" ); - if ( isThread() ) - throw ThreadException( "Can't self start thread" ); - mThreadMutex.lock(); - if ( !mStarted ) - { - pthread_attr_t threadAttrs; - pthread_attr_init( &threadAttrs ); - pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM ); + Debug( 1, "Starting thread" ); + if ( isThread() ) + throw ThreadException( "Can't self start thread" ); + mThreadMutex.lock(); + if ( !mStarted ) + { + pthread_attr_t threadAttrs; + pthread_attr_init( &threadAttrs ); + pthread_attr_setscope( &threadAttrs, PTHREAD_SCOPE_SYSTEM ); - mStarted = true; - if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 ) - throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) ); - pthread_attr_destroy( &threadAttrs ); - } - else - { - Error( "Attempt to start already running thread %d", mPid ); - } - mThreadCondition.wait(); - mThreadMutex.unlock(); - Debug( 1, "Started thread %d", mPid ); + mStarted = true; + if ( pthread_create( &mThread, &threadAttrs, mThreadFunc, this ) < 0 ) + throw ThreadException( stringtf( "Can't create thread: %s", strerror(errno) ) ); + pthread_attr_destroy( &threadAttrs ); + } + else + { + Error( "Attempt to start already running thread %d", mPid ); + } + mThreadCondition.wait(); + mThreadMutex.unlock(); + Debug( 1, "Started thread %d", mPid ); } void Thread::join() { - Debug( 1, "Joining thread %d", mPid ); - if ( isThread() ) - throw ThreadException( "Can't self join thread" ); - mThreadMutex.lock(); - if ( mPid >= 0 ) + Debug( 1, "Joining thread %d", mPid ); + if ( isThread() ) + throw ThreadException( "Can't self join thread" ); + mThreadMutex.lock(); + if ( mPid >= 0 ) + { + if ( mStarted ) { - if ( mStarted ) - { - void *threadStatus = 0; - if ( pthread_join( mThread, &threadStatus ) < 0 ) - throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) ); - mStarted = false; - Debug( 1, "Thread %d exited, status %p", mPid, threadStatus ); - } - else - { - Warning( "Attempt to join already finished thread %d", mPid ); - } + void *threadStatus = 0; + if ( pthread_join( mThread, &threadStatus ) < 0 ) + throw ThreadException( stringtf( "Can't join sender thread: %s", strerror(errno) ) ); + mStarted = false; + Debug( 1, "Thread %d exited, status %p", mPid, threadStatus ); } else { - Warning( "Attempt to join non-started thread %d", mPid ); + Warning( "Attempt to join already finished thread %d", mPid ); } - mThreadMutex.unlock(); - Debug( 1, "Joined thread %d", mPid ); + } + else + { + Warning( "Attempt to join non-started thread %d", mPid ); + } + mThreadMutex.unlock(); + Debug( 1, "Joined thread %d", mPid ); } void Thread::kill( int signal ) { - pthread_kill( mThread, signal ); + pthread_kill( mThread, signal ); } // Some explicit template instantiations diff --git a/src/zm_thread.h b/src/zm_thread.h index 6a0d169f0..15c13bb17 100644 --- a/src/zm_thread.h +++ b/src/zm_thread.h @@ -28,13 +28,35 @@ #endif // HAVE_SYS_SYSCALL_H #include "zm_exception.h" #include "zm_utils.h" +#ifdef __FreeBSD__ +#include +#endif class ThreadException : public Exception { +private: +#ifndef SOLARIS + pid_t pid() { + pid_t tid; +#ifdef __FreeBSD__ + long lwpid; + thr_self(&lwpid); + tid = lwpid; +#else + #ifdef __FreeBSD_kernel__ + if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id + # else + tid=syscall(SYS_gettid); + #endif +#endif + return tid; + } +#else + pthread_t pid() { return( pthread_self() ); } +#endif public: - ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)syscall(SYS_gettid) ) ) - { - } + ThreadException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) { + } }; class Mutex @@ -42,190 +64,215 @@ class Mutex friend class Condition; private: - pthread_mutex_t mMutex; + pthread_mutex_t mMutex; public: - Mutex(); - ~Mutex(); + Mutex(); + ~Mutex(); private: - pthread_mutex_t *getMutex() - { - return( &mMutex ); - } + pthread_mutex_t *getMutex() + { + return( &mMutex ); + } public: - void lock(); - void lock( int secs ); - void lock( double secs ); - void unlock(); - bool locked(); + void lock(); + void lock( int secs ); + void lock( double secs ); + void unlock(); + bool locked(); }; class ScopedMutex { private: - Mutex &mMutex; + Mutex &mMutex; public: - ScopedMutex( Mutex &mutex ) : mMutex( mutex ) - { - mMutex.lock(); - } - ~ScopedMutex() - { - mMutex.unlock(); - } + ScopedMutex( Mutex &mutex ) : mMutex( mutex ) + { + mMutex.lock(); + } + ~ScopedMutex() + { + mMutex.unlock(); + } private: - ScopedMutex( const ScopedMutex & ); + ScopedMutex( const ScopedMutex & ); }; class Condition { private: - Mutex &mMutex; - pthread_cond_t mCondition; + Mutex &mMutex; + pthread_cond_t mCondition; public: - Condition( Mutex &mutex ); - ~Condition(); + Condition( Mutex &mutex ); + ~Condition(); - void wait(); - bool wait( int secs ); - bool wait( double secs ); - void signal(); - void broadcast(); + void wait(); + bool wait( int secs ); + bool wait( double secs ); + void signal(); + void broadcast(); }; class Semaphore : public Condition { private: - Mutex mMutex; + Mutex mMutex; public: - Semaphore() : Condition( mMutex ) - { - } + Semaphore() : Condition( mMutex ) + { + } - void wait() - { - mMutex.lock(); - Condition::wait(); - mMutex.unlock(); - } - bool wait( int secs ) - { - mMutex.lock(); - bool result = Condition::wait( secs ); - mMutex.unlock(); - return( result ); - } - bool wait( double secs ) - { - mMutex.lock(); - bool result = Condition::wait( secs ); - mMutex.unlock(); - return( result ); - } - void signal() - { - mMutex.lock(); - Condition::signal(); - mMutex.unlock(); - } - void broadcast() - { - mMutex.lock(); - Condition::broadcast(); - mMutex.unlock(); - } + void wait() + { + mMutex.lock(); + Condition::wait(); + mMutex.unlock(); + } + bool wait( int secs ) + { + mMutex.lock(); + bool result = Condition::wait( secs ); + mMutex.unlock(); + return( result ); + } + bool wait( double secs ) + { + mMutex.lock(); + bool result = Condition::wait( secs ); + mMutex.unlock(); + return( result ); + } + void signal() + { + mMutex.lock(); + Condition::signal(); + mMutex.unlock(); + } + void broadcast() + { + mMutex.lock(); + Condition::broadcast(); + mMutex.unlock(); + } }; template class ThreadData { private: - T mValue; - mutable bool mChanged; - mutable Mutex mMutex; - mutable Condition mCondition; + T mValue; + mutable bool mChanged; + mutable Mutex mMutex; + mutable Condition mCondition; public: - __attribute__((used)) ThreadData() : mCondition( mMutex ) - { - } - __attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex ) - { - } - //~ThreadData() {} + __attribute__((used)) ThreadData() : mCondition( mMutex ) + { + } + __attribute__((used)) ThreadData( T value ) : mValue( value ), mCondition( mMutex ) + { + } + //~ThreadData() {} - __attribute__((used)) operator T() const - { - return( getValue() ); - } - __attribute__((used)) const T operator=( const T value ) - { - return( setValue( value ) ); - } + __attribute__((used)) operator T() const + { + return( getValue() ); + } + __attribute__((used)) const T operator=( const T value ) + { + return( setValue( value ) ); + } - __attribute__((used)) const T getValueImmediate() const - { - return( mValue ); - } - __attribute__((used)) T setValueImmediate( const T value ) - { - return( mValue = value ); - } - __attribute__((used)) const T getValue() const; - __attribute__((used)) T setValue( const T value ); - __attribute__((used)) const T getUpdatedValue() const; - __attribute__((used)) const T getUpdatedValue( double secs ) const; - __attribute__((used)) const T getUpdatedValue( int secs ) const; - __attribute__((used)) void updateValueSignal( const T value ); - __attribute__((used)) void updateValueBroadcast( const T value ); + __attribute__((used)) const T getValueImmediate() const + { + return( mValue ); + } + __attribute__((used)) T setValueImmediate( const T value ) + { + return( mValue = value ); + } + __attribute__((used)) const T getValue() const; + __attribute__((used)) T setValue( const T value ); + __attribute__((used)) const T getUpdatedValue() const; + __attribute__((used)) const T getUpdatedValue( double secs ) const; + __attribute__((used)) const T getUpdatedValue( int secs ) const; + __attribute__((used)) void updateValueSignal( const T value ); + __attribute__((used)) void updateValueBroadcast( const T value ); }; class Thread { public: - typedef void *(*ThreadFunc)( void * ); + typedef void *(*ThreadFunc)( void * ); protected: - pthread_t mThread; + pthread_t mThread; - Mutex mThreadMutex; - Condition mThreadCondition; - pid_t mPid; - bool mStarted; - bool mRunning; + Mutex mThreadMutex; + Condition mThreadCondition; +#ifndef SOLARIS + pid_t mPid; +#else + pthread_t mPid; +#endif + bool mStarted; + bool mRunning; + int status; // Used in various funcions to get around return a local variable protected: - Thread(); - virtual ~Thread(); + Thread(); + virtual ~Thread(); - pid_t id() const - { - return( (pid_t)syscall(SYS_gettid) ); - } - void exit( int status = 0 ) - { - //INFO( "Exiting" ); - pthread_exit( (void *)&status ); - } - static void *mThreadFunc( void *arg ); +#ifndef SOLARIS + pid_t id() const + { + pid_t tid; +#ifdef __FreeBSD__ + long lwpid; + thr_self(&lwpid); + tid = lwpid; +#else + #ifdef __FreeBSD_kernel__ + if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id + + #else + tid=syscall(SYS_gettid); + #endif +#endif +return tid; + } +#else + pthread_t id() const + { + return( pthread_self() ); + } +#endif + void exit( int p_status = 0 ) + { + //INFO( "Exiting" ); + pthread_exit( (void *)&p_status ); + } + static void *mThreadFunc( void *arg ); public: - virtual int run() = 0; + virtual int run() = 0; - void start(); - void join(); - void kill( int signal ); - bool isThread() - { - return( mPid > -1 && pthread_equal( pthread_self(), mThread ) ); - } - bool isStarted() const { return( mStarted ); } - bool isRunning() const { return( mRunning ); } + void start(); + void join(); + void kill( int signal ); + bool isThread() + { + return( mPid > -1 && pthread_equal( pthread_self(), mThread ) ); + } + bool isStarted() const { return( mStarted ); } + bool isRunning() const { return( mRunning ); } }; #endif // ZM_THREAD_H diff --git a/src/zm_time.h b/src/zm_time.h index 646133feb..df79e7fff 100644 --- a/src/zm_time.h +++ b/src/zm_time.h @@ -29,48 +29,48 @@ struct DeltaTimeval { - bool positive; - unsigned long delta; - unsigned long sec; - unsigned long fsec; - unsigned long prec; + bool positive; + unsigned long delta; + unsigned long sec; + unsigned long fsec; + unsigned long prec; }; -#define DT_GRAN_1000000 1000000 -#define DT_PREC_6 DT_GRAN_1000000 -#define DT_GRAN_100000 100000 -#define DT_PREC_5 DT_GRAN_100000 -#define DT_GRAN_10000 10000 -#define DT_PREC_4 DT_GRAN_10000 -#define DT_GRAN_1000 1000 -#define DT_PREC_3 DT_GRAN_1000 -#define DT_GRAN_100 100 -#define DT_PREC_2 DT_GRAN_100 -#define DT_GRAN_10 10 -#define DT_PREC_1 DT_GRAN_10 +#define DT_GRAN_1000000 1000000 +#define DT_PREC_6 DT_GRAN_1000000 +#define DT_GRAN_100000 100000 +#define DT_PREC_5 DT_GRAN_100000 +#define DT_GRAN_10000 10000 +#define DT_PREC_4 DT_GRAN_10000 +#define DT_GRAN_1000 1000 +#define DT_PREC_3 DT_GRAN_1000 +#define DT_GRAN_100 100 +#define DT_PREC_2 DT_GRAN_100 +#define DT_GRAN_10 10 +#define DT_PREC_1 DT_GRAN_10 -#define DT_MAXGRAN DT_GRAN_1000000 +#define DT_MAXGRAN DT_GRAN_1000000 // This obviously wouldn't work for massive deltas but as it's mostly // for frames it will only usually be a fraction of a second or so #define DELTA_TIMEVAL( result, time1, time2, precision ) \ { \ - int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \ - result.positive = (delta>=0); \ - result.delta = abs(delta); \ - result.sec = result.delta/(precision); \ - result.fsec = result.delta%(precision); \ - result.prec = (precision); \ + int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \ + result.positive = (delta>=0); \ + result.delta = abs(delta); \ + result.sec = result.delta/(precision); \ + result.fsec = result.delta%(precision); \ + result.prec = (precision); \ } #define TIMEVAL_INTERVAL( result, time1, time2, precision ) \ { \ - int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \ - result.positive = (delta>=0); \ - result.delta = abs(delta); \ - result.sec = result.delta/(precision); \ - result.fsec = result.delta%(precision); \ - result.prec = (precision); \ + int delta = (((time1).tv_sec-(time2).tv_sec)*(precision))+(((time1).tv_usec-(time2).tv_usec)/(DT_MAXGRAN/(precision))); \ + result.positive = (delta>=0); \ + result.delta = abs(delta); \ + result.sec = result.delta/(precision); \ + result.fsec = result.delta%(precision); \ + result.prec = (precision); \ } #define USEC_PER_SEC 1000000 @@ -82,128 +82,128 @@ typedef typeof(tv.tv_usec) ast_suseconds_t; inline int tvDiffUsec( struct timeval first, struct timeval last ) { - return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC ); + return( (last.tv_sec - first.tv_sec) * USEC_PER_SEC) + ((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC ); } inline int tvDiffUsec( struct timeval first ) { - struct timeval now; - gettimeofday( &now, NULL ); - return( tvDiffUsec( first, now ) ); + struct timeval now; + gettimeofday( &now, NULL ); + return( tvDiffUsec( first, now ) ); } inline int tvDiffMsec( struct timeval first, struct timeval last ) { - return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC ); + return( (last.tv_sec - first.tv_sec) * MSEC_PER_SEC) + (((MSEC_PER_SEC + last.tv_usec - first.tv_usec) / MSEC_PER_SEC) - MSEC_PER_SEC ); } inline int tvDiffMsec( struct timeval first ) { - struct timeval now; - gettimeofday( &now, NULL ); - return( tvDiffMsec( first, now ) ); + struct timeval now; + gettimeofday( &now, NULL ); + return( tvDiffMsec( first, now ) ); } inline double tvDiffSec( struct timeval first, struct timeval last ) { - return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) ); + return( double(last.tv_sec - first.tv_sec) + double(((USEC_PER_SEC + last.tv_usec - first.tv_usec) - USEC_PER_SEC) / (1.0*USEC_PER_SEC) ) ); } inline double tvDiffSec( struct timeval first ) { - struct timeval now; - gettimeofday( &now, NULL ); - return( tvDiffSec( first, now ) ); + struct timeval now; + gettimeofday( &now, NULL ); + return( tvDiffSec( first, now ) ); } inline struct timeval tvZero() { - struct timeval t = { 0, 0 }; - return( t ); + struct timeval t = { 0, 0 }; + return( t ); } inline int tvIsZero( const struct timeval t ) { - return( t.tv_sec == 0 && t.tv_usec == 0 ); + return( t.tv_sec == 0 && t.tv_usec == 0 ); } inline int tvCmp( struct timeval t1, struct timeval t2 ) { - if ( t1.tv_sec < t2.tv_sec ) - return( -1 ); - if ( t1.tv_sec > t2.tv_sec ) - return( 1 ); - if ( t1.tv_usec < t2.tv_usec ) - return( -1 ); - if ( t1.tv_usec > t2.tv_usec ) - return( 1 ); - return( 0 ); + if ( t1.tv_sec < t2.tv_sec ) + return( -1 ); + if ( t1.tv_sec > t2.tv_sec ) + return( 1 ); + if ( t1.tv_usec < t2.tv_usec ) + return( -1 ); + if ( t1.tv_usec > t2.tv_usec ) + return( 1 ); + return( 0 ); } inline int tvEq( struct timeval t1, struct timeval t2 ) { - return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec ); + return( t1.tv_sec == t2.tv_sec && t1.tv_usec == t2.tv_usec ); } inline struct timeval tvNow( void ) { - struct timeval t; - gettimeofday( &t, NULL ); - return( t ); + struct timeval t; + gettimeofday( &t, NULL ); + return( t ); } inline struct timeval tvCheck( struct timeval &t ) { - if ( t.tv_usec >= USEC_PER_SEC ) - { - Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec ); - t.tv_sec += t.tv_usec / USEC_PER_SEC; - t.tv_usec %= USEC_PER_SEC; - } - else if ( t.tv_usec < 0 ) - { - Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec ); - t.tv_usec = 0; - } - return( t ); + if ( t.tv_usec >= USEC_PER_SEC ) + { + Warning( "Timestamp too large %ld.%ld\n", t.tv_sec, (long int) t.tv_usec ); + t.tv_sec += t.tv_usec / USEC_PER_SEC; + t.tv_usec %= USEC_PER_SEC; + } + else if ( t.tv_usec < 0 ) + { + Warning( "Got negative timestamp %ld.%ld\n", t.tv_sec, (long int)t.tv_usec ); + t.tv_usec = 0; + } + return( t ); } // Add t2 to t1 inline struct timeval tvAdd( struct timeval t1, struct timeval t2 ) { - tvCheck(t1); - tvCheck(t2); - t1.tv_sec += t2.tv_sec; - t1.tv_usec += t2.tv_usec; - if ( t1.tv_usec >= USEC_PER_SEC ) - { - t1.tv_sec++; - t1.tv_usec -= USEC_PER_SEC; - } - return( t1 ); + tvCheck(t1); + tvCheck(t2); + t1.tv_sec += t2.tv_sec; + t1.tv_usec += t2.tv_usec; + if ( t1.tv_usec >= USEC_PER_SEC ) + { + t1.tv_sec++; + t1.tv_usec -= USEC_PER_SEC; + } + return( t1 ); } // Subtract t2 from t1 inline struct timeval tvSub( struct timeval t1, struct timeval t2 ) { - tvCheck(t1); - tvCheck(t2); - t1.tv_sec -= t2.tv_sec; - t1.tv_usec -= t2.tv_usec; - if ( t1.tv_usec < 0 ) - { - t1.tv_sec--; - t1.tv_usec += USEC_PER_SEC; - } - return( t1 ) ; + tvCheck(t1); + tvCheck(t2); + t1.tv_sec -= t2.tv_sec; + t1.tv_usec -= t2.tv_usec; + if ( t1.tv_usec < 0 ) + { + t1.tv_sec--; + t1.tv_usec += USEC_PER_SEC; + } + return( t1 ) ; } inline struct timeval tvMake( time_t sec, suseconds_t usec ) { - struct timeval t; - t.tv_sec = sec; - t.tv_usec = usec; - return( t ); + struct timeval t; + t.tv_sec = sec; + t.tv_usec = usec; + return( t ); } #endif // ZM_TIME_H diff --git a/src/zm_timer.cpp b/src/zm_timer.cpp index fbac45435..a067308b7 100644 --- a/src/zm_timer.cpp +++ b/src/zm_timer.cpp @@ -24,96 +24,96 @@ int Timer::TimerThread::mNextTimerId = 0; Timer::TimerThread::TimerThread( Timer &timer, int duration, bool repeat ) : - mTimerId( 0 ), - mTimer( timer ), - mDuration( duration ), - mRepeat( repeat ), - mReset( false ), - mExpiryFlag( true ) + mTimerId( 0 ), + mTimer( timer ), + mDuration( duration ), + mRepeat( repeat ), + mReset( false ), + mExpiryFlag( true ) { - mAccessMutex.lock(); - mTimerId = mNextTimerId++; - Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" ); - mAccessMutex.unlock(); + mAccessMutex.lock(); + mTimerId = mNextTimerId++; + Debug( 5, "Creating timer %d for %d seconds%s", mTimerId, mDuration, mRepeat?", repeating":"" ); + mAccessMutex.unlock(); } Timer::TimerThread::~TimerThread() { - cancel(); + cancel(); } void Timer::TimerThread::cancel() { - mAccessMutex.lock(); - if ( mRunning ) - { - Debug( 4, "Cancelling timer %d", mTimerId ); - mRepeat = false; - mReset = false; - mExpiryFlag.updateValueSignal( false ); - } - mAccessMutex.unlock(); + mAccessMutex.lock(); + if ( mRunning ) + { + Debug( 4, "Cancelling timer %d", mTimerId ); + mRepeat = false; + mReset = false; + mExpiryFlag.updateValueSignal( false ); + } + mAccessMutex.unlock(); } void Timer::TimerThread::reset() { - mAccessMutex.lock(); - if ( mRunning ) - { - Debug( 4, "Resetting timer" ); - mReset = true; - mExpiryFlag.updateValueSignal( false ); - } - else - { - Error( "Attempting to reset expired timer %d", mTimerId ); - } - mAccessMutex.unlock(); + mAccessMutex.lock(); + if ( mRunning ) + { + Debug( 4, "Resetting timer" ); + mReset = true; + mExpiryFlag.updateValueSignal( false ); + } + else + { + Error( "Attempting to reset expired timer %d", mTimerId ); + } + mAccessMutex.unlock(); } int Timer::TimerThread::run() { - Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration ); - bool timerExpired = false; - do + Debug( 4, "Starting timer %d for %d seconds", mTimerId, mDuration ); + bool timerExpired = false; + do + { + mAccessMutex.lock(); + mReset = false; + mExpiryFlag.setValue( true ); + mAccessMutex.unlock(); + timerExpired = mExpiryFlag.getUpdatedValue( mDuration ); + mAccessMutex.lock(); + if ( timerExpired ) { - mAccessMutex.lock(); - mReset = false; - mExpiryFlag.setValue( true ); - mAccessMutex.unlock(); - timerExpired = mExpiryFlag.getUpdatedValue( mDuration ); - mAccessMutex.lock(); - if ( timerExpired ) - { - Debug( 4, "Timer %d expired", mTimerId ); - mTimer.expire(); - } - else - { - Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" ); - } - mAccessMutex.unlock(); - } while ( mRepeat || (mReset && !timerExpired) ); - return( timerExpired ); + Debug( 4, "Timer %d expired", mTimerId ); + mTimer.expire(); + } + else + { + Debug( 4, "Timer %d %s", mTimerId, mReset?"reset":"cancelled" ); + } + mAccessMutex.unlock(); + } while ( mRepeat || (mReset && !timerExpired) ); + return( timerExpired ); } Timer::Timer( int timeout, bool repeat ) : mTimerThread( *this, timeout, repeat ) { - mTimerThread.start(); + mTimerThread.start(); } Timer::~Timer() { - //cancel(); + //cancel(); } void Timer::Timer::cancel() { - mTimerThread.cancel(); + mTimerThread.cancel(); } void Timer::Timer::reset() { - mTimerThread.reset(); + mTimerThread.reset(); } diff --git a/src/zm_timer.h b/src/zm_timer.h index 6c7663b87..2213d3cd0 100644 --- a/src/zm_timer.h +++ b/src/zm_timer.h @@ -30,61 +30,81 @@ class Timer { private: - class TimerException : public Exception + class TimerException : public Exception + { + private: +#ifndef SOLARIS + pid_t pid() { + pid_t tid; +#ifdef __FreeBSD__ + long lwpid; + thr_self(&lwpid); + tid = lwpid; +#else + #ifdef __FreeBSD_kernel__ + if ( (syscall(SYS_thr_self, &tid)) < 0 ) // Thread/Process id + #else + tid=syscall(SYS_gettid); + #endif +#endif + return tid; + } +#else + pthread_t pid() { return( pthread_self() ); } +#endif + public: + TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)pid() ) ) { - public: - TimerException( const std::string &message ) : Exception( stringtf( "(%d) "+message, (long int)syscall(SYS_gettid) ) ) - { - } - }; + } + }; - class TimerThread : public Thread + class TimerThread : public Thread + { + private: + typedef ThreadData ExpiryFlag; + + private: + static int mNextTimerId; + + private: + int mTimerId; + Timer &mTimer; + int mDuration; + int mRepeat; + int mReset; + ExpiryFlag mExpiryFlag; + Mutex mAccessMutex; + + private: + void quit() { - private: - typedef ThreadData ExpiryFlag; + cancel(); + } - private: - static int mNextTimerId; + public: + TimerThread( Timer &timer, int timeout, bool repeat ); + ~TimerThread(); - private: - int mTimerId; - Timer &mTimer; - int mDuration; - int mRepeat; - int mReset; - ExpiryFlag mExpiryFlag; - Mutex mAccessMutex; - - private: - void quit() - { - cancel(); - } - - public: - TimerThread( Timer &timer, int timeout, bool repeat ); - ~TimerThread(); - - void cancel(); - void reset(); - int run(); - }; - -protected: - TimerThread mTimerThread; - -protected: - Timer( int timeout, bool repeat=false ); - -public: - virtual ~Timer(); - -protected: - virtual void expire()=0; - -public: void cancel(); void reset(); + int run(); + }; + +protected: + TimerThread mTimerThread; + +protected: + Timer( int timeout, bool repeat=false ); + +public: + virtual ~Timer(); + +protected: + virtual void expire()=0; + +public: + void cancel(); + void reset(); }; #endif // ZM_TIMER_H diff --git a/src/zm_user.cpp b/src/zm_user.cpp index 36cb8a37e..73f5c7932 100644 --- a/src/zm_user.cpp +++ b/src/zm_user.cpp @@ -29,119 +29,119 @@ User::User() { - username[0] = password[0] = 0; - enabled = false; - stream = events = control = monitors = system = PERM_NONE; - monitor_ids = 0; + username[0] = password[0] = 0; + enabled = false; + stream = events = control = monitors = system = PERM_NONE; + monitor_ids = 0; } User::User( MYSQL_ROW &dbrow ) { - int index = 0; - strncpy( username, dbrow[index++], sizeof(username) ); - strncpy( password, dbrow[index++], sizeof(password) ); - enabled = (bool)atoi( dbrow[index++] ); - stream = (Permission)atoi( dbrow[index++] ); - events = (Permission)atoi( dbrow[index++] ); - control = (Permission)atoi( dbrow[index++] ); - monitors = (Permission)atoi( dbrow[index++] ); - system = (Permission)atoi( dbrow[index++] ); - monitor_ids = 0; - char *monitor_ids_str = dbrow[index++]; - if ( monitor_ids_str && *monitor_ids_str ) - { - monitor_ids = new int[strlen(monitor_ids_str)]; - int n_monitor_ids = 0; - const char *ptr = monitor_ids_str; - do - { - int id = 0; - while( isdigit( *ptr ) ) - { - id *= 10; - id += *ptr-'0'; - ptr++; - } - if ( id ) - { - monitor_ids[n_monitor_ids++] = id; - if ( !*ptr ) - break; - } - while ( !isdigit( *ptr ) ) - ptr++; - } while( *ptr ); - monitor_ids[n_monitor_ids] = 0; - } + int index = 0; + strncpy( username, dbrow[index++], sizeof(username) ); + strncpy( password, dbrow[index++], sizeof(password) ); + enabled = (bool)atoi( dbrow[index++] ); + stream = (Permission)atoi( dbrow[index++] ); + events = (Permission)atoi( dbrow[index++] ); + control = (Permission)atoi( dbrow[index++] ); + monitors = (Permission)atoi( dbrow[index++] ); + system = (Permission)atoi( dbrow[index++] ); + monitor_ids = 0; + char *monitor_ids_str = dbrow[index++]; + if ( monitor_ids_str && *monitor_ids_str ) + { + monitor_ids = new int[strlen(monitor_ids_str)]; + int n_monitor_ids = 0; + const char *ptr = monitor_ids_str; + do + { + int id = 0; + while( isdigit( *ptr ) ) + { + id *= 10; + id += *ptr-'0'; + ptr++; + } + if ( id ) + { + monitor_ids[n_monitor_ids++] = id; + if ( !*ptr ) + break; + } + while ( !isdigit( *ptr ) ) + ptr++; + } while( *ptr ); + monitor_ids[n_monitor_ids] = 0; + } } User::~User() { - delete monitor_ids; + delete monitor_ids; } bool User::canAccess( int monitor_id ) { - if ( !monitor_ids ) - { - return( true ); - } - for ( int i = 0; monitor_ids[i]; i++ ) - { - if ( monitor_ids[i] == monitor_id ) - { - return( true ); - } - } - return( false ); + if ( !monitor_ids ) + { + return( true ); + } + for ( int i = 0; monitor_ids[i]; i++ ) + { + if ( monitor_ids[i] == monitor_id ) + { + return( true ); + } + } + return( false ); } // Function to load a user from username and password // Please note that in auth relay mode = none, password is NULL User *zmLoadUser( const char *username, const char *password ) { - char sql[ZM_SQL_SML_BUFSIZ] = ""; - char safer_username[65]; // current db username size is 32 - char safer_password[129]; // current db password size is 64 + char sql[ZM_SQL_SML_BUFSIZ] = ""; + char safer_username[65]; // current db username size is 32 + char safer_password[129]; // current db password size is 64 - // According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator. - mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) ); + // According to docs, size of safer_whatever must be 2*length+1 due to unicode conversions + null terminator. + mysql_real_escape_string(&dbconn, safer_username, username, strlen( username ) ); - if ( password ) { - mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) ); - snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password ); - } else { - snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username ); - } + if ( password ) { + mysql_real_escape_string(&dbconn, safer_password, password, strlen( password ) ); + snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Password = password('%s') and Enabled = 1", safer_username, safer_password ); + } else { + snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Username = '%s' and Enabled = 1", safer_username ); + } - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_users = mysql_num_rows( result ); + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_users = mysql_num_rows( result ); - if ( n_users != 1 ) - { - Warning( "Unable to authenticate user %s", username ); - return( 0 ); - } + if ( n_users != 1 ) + { + Warning( "Unable to authenticate user %s", username ); + return( 0 ); + } - MYSQL_ROW dbrow = mysql_fetch_row( result ); + MYSQL_ROW dbrow = mysql_fetch_row( result ); - User *user = new User( dbrow ); - Info( "Authenticated user '%s'", user->getUsername() ); + User *user = new User( dbrow ); + Info( "Authenticated user '%s'", user->getUsername() ); - mysql_free_result( result ); + mysql_free_result( result ); - return( user ); + return( user ); } // Function to validate an authentication string @@ -149,102 +149,102 @@ User *zmLoadAuthUser( const char *auth, bool use_remote_addr ) { #if HAVE_DECL_MD5 || HAVE_DECL_GNUTLS_FINGERPRINT #ifdef HAVE_GCRYPT_H - // Special initialisation for libgcrypt - if ( !gcry_check_version( GCRYPT_VERSION ) ) - { - Fatal( "Unable to initialise libgcrypt" ); - } - gcry_control( GCRYCTL_DISABLE_SECMEM, 0 ); - gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 ); + // Special initialisation for libgcrypt + if ( !gcry_check_version( GCRYPT_VERSION ) ) + { + Fatal( "Unable to initialise libgcrypt" ); + } + gcry_control( GCRYCTL_DISABLE_SECMEM, 0 ); + gcry_control( GCRYCTL_INITIALIZATION_FINISHED, 0 ); #endif // HAVE_GCRYPT_H - const char *remote_addr = ""; - if ( use_remote_addr ) - { - remote_addr = getenv( "REMOTE_ADDR" ); - if ( !remote_addr ) - { - Warning( "Can't determine remote address, using null" ); - remote_addr = ""; - } - } + const char *remote_addr = ""; + if ( use_remote_addr ) + { + remote_addr = getenv( "REMOTE_ADDR" ); + if ( !remote_addr ) + { + Warning( "Can't determine remote address, using null" ); + remote_addr = ""; + } + } - Debug( 1, "Attempting to authenticate user from auth string '%s'", auth ); - char sql[ZM_SQL_SML_BUFSIZ] = ""; - snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" ); + Debug( 1, "Attempting to authenticate user from auth string '%s'", auth ); + char sql[ZM_SQL_SML_BUFSIZ] = ""; + snprintf( sql, sizeof(sql), "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1" ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_users = mysql_num_rows( result ); + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_users = mysql_num_rows( result ); - if ( n_users < 1 ) - { - Warning( "Unable to authenticate user" ); - return( 0 ); - } + if ( n_users < 1 ) + { + Warning( "Unable to authenticate user" ); + return( 0 ); + } - while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) - { - const char *user = dbrow[0]; - const char *pass = dbrow[1]; + while( MYSQL_ROW dbrow = mysql_fetch_row( result ) ) + { + const char *user = dbrow[0]; + const char *pass = dbrow[1]; - char auth_key[512] = ""; - char auth_md5[32+1] = ""; - size_t md5len = 16; - unsigned char md5sum[md5len]; + char auth_key[512] = ""; + char auth_md5[32+1] = ""; + size_t md5len = 16; + unsigned char md5sum[md5len]; - time_t now = time( 0 ); - int max_tries = 2; + time_t now = time( 0 ); + int max_tries = 2; - for ( int i = 0; i < max_tries; i++, now -= (60*60) ) - { - struct tm *now_tm = localtime( &now ); + for ( int i = 0; i < max_tries; i++, now -= (60*60) ) + { + struct tm *now_tm = localtime( &now ); - snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d", - config.auth_hash_secret, - user, - pass, - remote_addr, - now_tm->tm_hour, - now_tm->tm_mday, - now_tm->tm_mon, - now_tm->tm_year - ); + snprintf( auth_key, sizeof(auth_key), "%s%s%s%s%d%d%d%d", + config.auth_hash_secret, + user, + pass, + remote_addr, + now_tm->tm_hour, + now_tm->tm_mday, + now_tm->tm_mon, + now_tm->tm_year + ); #if HAVE_DECL_MD5 - MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum ); + MD5( (unsigned char *)auth_key, strlen(auth_key), md5sum ); #elif HAVE_DECL_GNUTLS_FINGERPRINT - gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) }; - gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len ); + gnutls_datum_t md5data = { (unsigned char *)auth_key, strlen(auth_key) }; + gnutls_fingerprint( GNUTLS_DIG_MD5, &md5data, md5sum, &md5len ); #endif - auth_md5[0] = '\0'; - for ( unsigned int j = 0; j < md5len; j++ ) - { - sprintf( &auth_md5[2*j], "%02x", md5sum[j] ); - } - Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 ); + auth_md5[0] = '\0'; + for ( unsigned int j = 0; j < md5len; j++ ) + { + sprintf( &auth_md5[2*j], "%02x", md5sum[j] ); + } + Debug( 1, "Checking auth_key '%s' -> auth_md5 '%s'", auth_key, auth_md5 ); - if ( !strcmp( auth, auth_md5 ) ) - { - // We have a match - User *user = new User( dbrow ); - Info( "Authenticated user '%s'", user->getUsername() ); - return( user ); - } - } - } + if ( !strcmp( auth, auth_md5 ) ) + { + // We have a match + User *user = new User( dbrow ); + Debug(1, "Authenticated user '%s'", user->getUsername() ); + return( user ); + } + } + } #else // HAVE_DECL_MD5 - Error( "You need to build with gnutls or openssl installed to use hash based authentication" ); + Error( "You need to build with gnutls or openssl installed to use hash based authentication" ); #endif // HAVE_DECL_MD5 - return( 0 ); + return( 0 ); } diff --git a/src/zm_user.h b/src/zm_user.h index 95fb71474..09f5a2f0a 100644 --- a/src/zm_user.h +++ b/src/zm_user.h @@ -39,33 +39,33 @@ class User { public: - typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission; + typedef enum { PERM_NONE=1, PERM_VIEW, PERM_EDIT } Permission; protected: - char username[32+1]; - char password[64+1]; - bool enabled; - Permission stream; - Permission events; - Permission control; - Permission monitors; - Permission system; - int *monitor_ids; + char username[32+1]; + char password[64+1]; + bool enabled; + Permission stream; + Permission events; + Permission control; + Permission monitors; + Permission system; + int *monitor_ids; public: - User(); - User( MYSQL_ROW &dbrow ); - ~User(); + User(); + User( MYSQL_ROW &dbrow ); + ~User(); - const char *getUsername() const { return( username ); } - const char *getPassword() const { return( password ); } - bool isEnabled() const { return( enabled ); } - Permission getStream() const { return( stream ); } - Permission getEvents() const { return( events ); } - Permission getControl() const { return( control ); } - Permission getMonitors() const { return( monitors ); } - Permission getSystem() const { return( system ); } - bool canAccess( int monitor_id ); + const char *getUsername() const { return( username ); } + const char *getPassword() const { return( password ); } + bool isEnabled() const { return( enabled ); } + Permission getStream() const { return( stream ); } + Permission getEvents() const { return( events ); } + Permission getControl() const { return( control ); } + Permission getMonitors() const { return( monitors ); } + Permission getSystem() const { return( system ); } + bool canAccess( int monitor_id ); }; User *zmLoadUser( const char *username, const char *password=0 ); diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index e0c65ca61..67af64a66 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -28,256 +28,256 @@ unsigned int sseversion = 0; std::string trimSet(std::string str, std::string trimset) { - // Trim Both leading and trailing sets - size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces - size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af + // Trim Both leading and trailing sets + size_t startpos = str.find_first_not_of(trimset); // Find the first character position after excluding leading blank spaces + size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af - // if all spaces or empty return an empty string - if(( std::string::npos == startpos ) || ( std::string::npos == endpos)) - { - return std::string(""); - } - else - return str.substr( startpos, endpos-startpos+1 ); + // if all spaces or empty return an empty string + if(( std::string::npos == startpos ) || ( std::string::npos == endpos)) + { + return std::string(""); + } + else + return str.substr( startpos, endpos-startpos+1 ); } std::string trimSpaces(std::string str) { - return trimSet(str, " \t"); + return trimSet(str, " \t"); } std::string replaceAll(std::string str, std::string from, std::string to) { - if(from.empty()) - return str; - size_t start_pos = 0; - while((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' - } + if(from.empty()) return str; + size_t start_pos = 0; + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' + } + return str; } const std::string stringtf( const char *format, ... ) { - va_list ap; - char tempBuffer[8192]; - std::string tempString; + va_list ap; + char tempBuffer[8192]; + std::string tempString; - va_start(ap, format ); - vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap ); - va_end(ap); + va_start(ap, format ); + vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap ); + va_end(ap); - tempString = tempBuffer; + tempString = tempBuffer; - return( tempString ); + return( tempString ); } const std::string stringtf( const std::string &format, ... ) { - va_list ap; - char tempBuffer[8192]; - std::string tempString; + va_list ap; + char tempBuffer[8192]; + std::string tempString; - va_start(ap, format ); - vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap ); - va_end(ap); + va_start(ap, format ); + vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap ); + va_end(ap); - tempString = tempBuffer; + tempString = tempBuffer; - return( tempString ); + return( tempString ); } bool startsWith( const std::string &haystack, const std::string &needle ) { - return( haystack.substr( 0, needle.length() ) == needle ); + return( haystack.substr( 0, needle.length() ) == needle ); } StringVector split( const std::string &string, const std::string chars, int limit ) { - StringVector stringVector; - std::string tempString = string; - std::string::size_type startIndex = 0; - std::string::size_type endIndex = 0; + StringVector stringVector; + std::string tempString = string; + std::string::size_type startIndex = 0; + std::string::size_type endIndex = 0; - //Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit ); - do + //Info( "Looking for '%s' in '%s', limit %d", chars.c_str(), string.c_str(), limit ); + do + { + // Find delimiters + endIndex = string.find_first_of( chars, startIndex ); + //Info( "Got endIndex at %d", endIndex ); + if ( endIndex > 0 ) { - // Find delimiters - endIndex = string.find_first_of( chars, startIndex ); - //Info( "Got endIndex at %d", endIndex ); - if ( endIndex > 0 ) - { - //Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() ); - stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) ); - } - if ( endIndex == std::string::npos ) - break; - // Find non-delimiters - startIndex = tempString.find_first_not_of( chars, endIndex ); - if ( limit && (stringVector.size() == (unsigned int)(limit-1)) ) - { - stringVector.push_back( string.substr( startIndex ) ); - break; - } - //Info( "Got new startIndex at %d", startIndex ); - } while ( startIndex != std::string::npos ); - //Info( "Finished with %d strings", stringVector.size() ); + //Info( "Adding '%s'", string.substr( startIndex, endIndex-startIndex ).c_str() ); + stringVector.push_back( string.substr( startIndex, endIndex-startIndex ) ); + } + if ( endIndex == std::string::npos ) + break; + // Find non-delimiters + startIndex = tempString.find_first_not_of( chars, endIndex ); + if ( limit && (stringVector.size() == (unsigned int)(limit-1)) ) + { + stringVector.push_back( string.substr( startIndex ) ); + break; + } + //Info( "Got new startIndex at %d", startIndex ); + } while ( startIndex != std::string::npos ); + //Info( "Finished with %d strings", stringVector.size() ); - return( stringVector ); + return( stringVector ); } const std::string join(const StringVector v, const char * delim ) { - std::stringstream ss; + std::stringstream ss; - for(size_t i = 0; i < v.size(); ++i) { - if(i != 0) - ss << ","; - ss << v[i]; - } - return ss.str(); + for(size_t i = 0; i < v.size(); ++i) { + if(i != 0) + ss << ","; + ss << v[i]; + } + return ss.str(); } const std::string base64Encode( const std::string &inString ) { - static char base64_table[64] = { '\0' }; + static char base64_table[64] = { '\0' }; - if ( !base64_table[0] ) - { - int i = 0; - for ( char c = 'A'; c <= 'Z'; c++ ) - base64_table[i++] = c; - for ( char c = 'a'; c <= 'z'; c++ ) - base64_table[i++] = c; - for ( char c = '0'; c <= '9'; c++ ) - base64_table[i++] = c; - base64_table[i++] = '+'; - base64_table[i++] = '/'; - } + if ( !base64_table[0] ) + { + int i = 0; + for ( char c = 'A'; c <= 'Z'; c++ ) + base64_table[i++] = c; + for ( char c = 'a'; c <= 'z'; c++ ) + base64_table[i++] = c; + for ( char c = '0'; c <= '9'; c++ ) + base64_table[i++] = c; + base64_table[i++] = '+'; + base64_table[i++] = '/'; + } - std::string outString; - outString.reserve( 2 * inString.size() ); + std::string outString; + outString.reserve( 2 * inString.size() ); - const char *inPtr = inString.c_str(); - while( *inPtr ) - { - unsigned char selection = *inPtr >> 2; - unsigned char remainder = (*inPtr++ & 0x03) << 4; - outString += base64_table[selection]; + const char *inPtr = inString.c_str(); + while( *inPtr ) + { + unsigned char selection = *inPtr >> 2; + unsigned char remainder = (*inPtr++ & 0x03) << 4; + outString += base64_table[selection]; - if ( *inPtr ) - { - selection = remainder | (*inPtr >> 4); - remainder = (*inPtr++ & 0x0f) << 2; - outString += base64_table[selection]; - - if ( *inPtr ) - { - selection = remainder | (*inPtr >> 6); - outString += base64_table[selection]; - selection = (*inPtr++ & 0x3f); - outString += base64_table[selection]; - } - else - { - outString += base64_table[remainder]; - outString += '='; - } - } - else - { - outString += base64_table[remainder]; - outString += '='; - outString += '='; - } - } - return( outString ); + if ( *inPtr ) + { + selection = remainder | (*inPtr >> 4); + remainder = (*inPtr++ & 0x0f) << 2; + outString += base64_table[selection]; + + if ( *inPtr ) + { + selection = remainder | (*inPtr >> 6); + outString += base64_table[selection]; + selection = (*inPtr++ & 0x3f); + outString += base64_table[selection]; + } + else + { + outString += base64_table[remainder]; + outString += '='; + } + } + else + { + outString += base64_table[remainder]; + outString += '='; + outString += '='; + } + } + return( outString ); } int split(const char* string, const char delim, std::vector& items) { - if(string == NULL) - return -1; + if(string == NULL) + return -1; - if(string[0] == 0) - return -2; + if(string[0] == 0) + return -2; - std::string str(string); - size_t pos; - - while(true) { - pos = str.find(delim); - items.push_back(str.substr(0, pos)); - str.erase(0, pos+1); + std::string str(string); + size_t pos; + + while(true) { + pos = str.find(delim); + items.push_back(str.substr(0, pos)); + str.erase(0, pos+1); - if(pos == std::string::npos) - break; - } + if(pos == std::string::npos) + break; + } - return items.size(); + return items.size(); } int pairsplit(const char* string, const char delim, std::string& name, std::string& value) { - if(string == NULL) - return -1; + if(string == NULL) + return -1; - if(string[0] == 0) - return -2; + if(string[0] == 0) + return -2; - std::string str(string); - size_t pos = str.find(delim); + std::string str(string); + size_t pos = str.find(delim); - if(pos == std::string::npos || pos == 0 || pos >= str.length()) - return -3; + if(pos == std::string::npos || pos == 0 || pos >= str.length()) + return -3; - name = str.substr(0, pos); - value = str.substr(pos+1, std::string::npos); + name = str.substr(0, pos); + value = str.substr(pos+1, std::string::npos); - return 0; + return 0; } /* Sets sse_version */ void ssedetect() { #if (defined(__i386__) || defined(__x86_64__)) - /* x86 or x86-64 processor */ - uint32_t r_edx, r_ecx; - - __asm__ __volatile__( + /* x86 or x86-64 processor */ + uint32_t r_edx, r_ecx; + + __asm__ __volatile__( #if defined(__i386__) - "pushl %%ebx;\n\t" + "pushl %%ebx;\n\t" #endif - "mov $0x1,%%eax\n\t" - "cpuid\n\t" + "mov $0x1,%%eax\n\t" + "cpuid\n\t" #if defined(__i386__) - "popl %%ebx;\n\t" + "popl %%ebx;\n\t" #endif - : "=d" (r_edx), "=c" (r_ecx) - : - : "%eax" + : "=d" (r_edx), "=c" (r_ecx) + : + : "%eax" #if !defined(__i386__) - , "%ebx" + , "%ebx" #endif - ); - - if (r_ecx & 0x00000200) { - sseversion = 35; /* SSSE3 */ - Debug(1,"Detected a x86\\x86-64 processor with SSSE3"); - } else if (r_ecx & 0x00000001) { - sseversion = 30; /* SSE3 */ - Debug(1,"Detected a x86\\x86-64 processor with SSE3"); - } else if (r_edx & 0x04000000) { - sseversion = 20; /* SSE2 */ - Debug(1,"Detected a x86\\x86-64 processor with SSE2"); - } else if (r_edx & 0x02000000) { - sseversion = 10; /* SSE */ - Debug(1,"Detected a x86\\x86-64 processor with SSE"); - } else { - sseversion = 0; - Debug(1,"Detected a x86\\x86-64 processor"); - } - + ); + + if (r_ecx & 0x00000200) { + sseversion = 35; /* SSSE3 */ + Debug(1,"Detected a x86\\x86-64 processor with SSSE3"); + } else if (r_ecx & 0x00000001) { + sseversion = 30; /* SSE3 */ + Debug(1,"Detected a x86\\x86-64 processor with SSE3"); + } else if (r_edx & 0x04000000) { + sseversion = 20; /* SSE2 */ + Debug(1,"Detected a x86\\x86-64 processor with SSE2"); + } else if (r_edx & 0x02000000) { + sseversion = 10; /* SSE */ + Debug(1,"Detected a x86\\x86-64 processor with SSE"); + } else { + sseversion = 0; + Debug(1,"Detected a x86\\x86-64 processor"); + } + #else - /* Non x86 or x86-64 processor, SSE2 is not available */ - Debug(1,"Detected a non x86\\x86-64 processor"); - sseversion = 0; + /* Non x86 or x86-64 processor, SSE2 is not available */ + Debug(1,"Detected a non x86\\x86-64 processor"); + sseversion = 0; #endif } @@ -288,60 +288,60 @@ __attribute__((noinline,__target__("sse2"))) #endif void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) { #if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE)) - if(bytes > 128) { - unsigned int remainder = bytes % 128; - const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder); + if(bytes > 128) { + unsigned int remainder = bytes % 128; + const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder); - __asm__ __volatile__( - "sse2_copy_iter:\n\t" - "movdqa (%0),%%xmm0\n\t" - "movdqa 0x10(%0),%%xmm1\n\t" - "movdqa 0x20(%0),%%xmm2\n\t" - "movdqa 0x30(%0),%%xmm3\n\t" - "movdqa 0x40(%0),%%xmm4\n\t" - "movdqa 0x50(%0),%%xmm5\n\t" - "movdqa 0x60(%0),%%xmm6\n\t" - "movdqa 0x70(%0),%%xmm7\n\t" - "movntdq %%xmm0,(%1)\n\t" - "movntdq %%xmm1,0x10(%1)\n\t" - "movntdq %%xmm2,0x20(%1)\n\t" - "movntdq %%xmm3,0x30(%1)\n\t" - "movntdq %%xmm4,0x40(%1)\n\t" - "movntdq %%xmm5,0x50(%1)\n\t" - "movntdq %%xmm6,0x60(%1)\n\t" - "movntdq %%xmm7,0x70(%1)\n\t" - "add $0x80, %0\n\t" - "add $0x80, %1\n\t" - "cmp %2, %0\n\t" - "jb sse2_copy_iter\n\t" - "test %3, %3\n\t" - "jz sse2_copy_finish\n\t" - "cld\n\t" - "rep movsb\n\t" - "sse2_copy_finish:\n\t" - : - : "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder) - : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" - ); + __asm__ __volatile__( + "sse2_copy_iter:\n\t" + "movdqa (%0),%%xmm0\n\t" + "movdqa 0x10(%0),%%xmm1\n\t" + "movdqa 0x20(%0),%%xmm2\n\t" + "movdqa 0x30(%0),%%xmm3\n\t" + "movdqa 0x40(%0),%%xmm4\n\t" + "movdqa 0x50(%0),%%xmm5\n\t" + "movdqa 0x60(%0),%%xmm6\n\t" + "movdqa 0x70(%0),%%xmm7\n\t" + "movntdq %%xmm0,(%1)\n\t" + "movntdq %%xmm1,0x10(%1)\n\t" + "movntdq %%xmm2,0x20(%1)\n\t" + "movntdq %%xmm3,0x30(%1)\n\t" + "movntdq %%xmm4,0x40(%1)\n\t" + "movntdq %%xmm5,0x50(%1)\n\t" + "movntdq %%xmm6,0x60(%1)\n\t" + "movntdq %%xmm7,0x70(%1)\n\t" + "add $0x80, %0\n\t" + "add $0x80, %1\n\t" + "cmp %2, %0\n\t" + "jb sse2_copy_iter\n\t" + "test %3, %3\n\t" + "jz sse2_copy_finish\n\t" + "cld\n\t" + "rep movsb\n\t" + "sse2_copy_finish:\n\t" + : + : "S" (src), "D" (dest), "r" (lastsrc), "c" (remainder) + : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "cc", "memory" + ); - } else { - /* Standard memcpy */ - __asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory"); - } + } else { + /* Standard memcpy */ + __asm__ __volatile__("cld; rep movsb" :: "S"(src), "D"(dest), "c"(bytes) : "cc", "memory"); + } #else - /* Non x86\x86-64 platform, use memcpy */ - memcpy(dest,src,bytes); + /* Non x86\x86-64 platform, use memcpy */ + memcpy(dest,src,bytes); #endif - return dest; + return dest; } void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff) { - if (((end->tv_nsec)-(start->tv_nsec))<0) { - diff->tv_sec = end->tv_sec-start->tv_sec-1; - diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec; - } else { - diff->tv_sec = end->tv_sec-start->tv_sec; - diff->tv_nsec = end->tv_nsec-start->tv_nsec; - } + if (((end->tv_nsec)-(start->tv_nsec))<0) { + diff->tv_sec = end->tv_sec-start->tv_sec-1; + diff->tv_nsec = 1000000000+end->tv_nsec-start->tv_nsec; + } else { + diff->tv_sec = end->tv_sec-start->tv_sec; + diff->tv_nsec = end->tv_nsec-start->tv_nsec; + } } diff --git a/src/zm_utils.h b/src/zm_utils.h index 063c74b7d..a6cb2e150 100644 --- a/src/zm_utils.h +++ b/src/zm_utils.h @@ -46,12 +46,12 @@ int pairsplit(const char* string, const char delim, std::string& name, std::stri inline int max( int a, int b ) { - return( a>=b?a:b ); + return( a>=b?a:b ); } inline int min( int a, int b ) { - return( a<=b?a:b ); + return( a<=b?a:b ); } void ssedetect(); diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 90c954d9e..f3d1120bc 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -25,170 +25,170 @@ void Zone::Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames ) { - monitor = p_monitor; + monitor = p_monitor; - id = p_id; - label = new char[strlen(p_label)+1]; - strcpy( label, p_label ); - type = p_type; - polygon = p_polygon; - alarm_rgb = p_alarm_rgb; - check_method = p_check_method; - min_pixel_threshold = p_min_pixel_threshold; - max_pixel_threshold = p_max_pixel_threshold; - min_alarm_pixels = p_min_alarm_pixels; - max_alarm_pixels = p_max_alarm_pixels; - filter_box = p_filter_box; - min_filter_pixels = p_min_filter_pixels; - max_filter_pixels = p_max_filter_pixels; - min_blob_pixels = p_min_blob_pixels; - max_blob_pixels = p_max_blob_pixels; - min_blobs = p_min_blobs; - max_blobs = p_max_blobs; - overload_frames = p_overload_frames; - extend_alarm_frames = p_extend_alarm_frames; + id = p_id; + label = new char[strlen(p_label)+1]; + strcpy( label, p_label ); + type = p_type; + polygon = p_polygon; + alarm_rgb = p_alarm_rgb; + check_method = p_check_method; + min_pixel_threshold = p_min_pixel_threshold; + max_pixel_threshold = p_max_pixel_threshold; + min_alarm_pixels = p_min_alarm_pixels; + max_alarm_pixels = p_max_alarm_pixels; + filter_box = p_filter_box; + min_filter_pixels = p_min_filter_pixels; + max_filter_pixels = p_max_filter_pixels; + min_blob_pixels = p_min_blob_pixels; + max_blob_pixels = p_max_blob_pixels; + min_blobs = p_min_blobs; + max_blobs = p_max_blobs; + overload_frames = p_overload_frames; + extend_alarm_frames = p_extend_alarm_frames; - Debug( 1, "Initialised zone %d/%s - %d - %dx%d - Rgb:%06x, CM:%d, MnAT:%d, MxAT:%d, MnAP:%d, MxAP:%d, FB:%dx%d, MnFP:%d, MxFP:%d, MnBS:%d, MxBS:%d, MnB:%d, MxB:%d, OF: %d, AF: %d", id, label, type, polygon.Width(), polygon.Height(), alarm_rgb, check_method, min_pixel_threshold, max_pixel_threshold, min_alarm_pixels, max_alarm_pixels, filter_box.X(), filter_box.Y(), min_filter_pixels, max_filter_pixels, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs, overload_frames, extend_alarm_frames ); + Debug( 1, "Initialised zone %d/%s - %d - %dx%d - Rgb:%06x, CM:%d, MnAT:%d, MxAT:%d, MnAP:%d, MxAP:%d, FB:%dx%d, MnFP:%d, MxFP:%d, MnBS:%d, MxBS:%d, MnB:%d, MxB:%d, OF: %d, AF: %d", id, label, type, polygon.Width(), polygon.Height(), alarm_rgb, check_method, min_pixel_threshold, max_pixel_threshold, min_alarm_pixels, max_alarm_pixels, filter_box.X(), filter_box.Y(), min_filter_pixels, max_filter_pixels, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs, overload_frames, extend_alarm_frames ); - alarmed = false; - pixel_diff = 0; - alarm_pixels = 0; - alarm_filter_pixels = 0; - alarm_blob_pixels = 0; - alarm_blobs = 0; - min_blob_size = 0; - max_blob_size = 0; - image = 0; - score = 0; + alarmed = false; + pixel_diff = 0; + alarm_pixels = 0; + alarm_filter_pixels = 0; + alarm_blob_pixels = 0; + alarm_blobs = 0; + min_blob_size = 0; + max_blob_size = 0; + image = 0; + score = 0; - overload_count = 0; - extend_alarm_count = 0; + overload_count = 0; + extend_alarm_count = 0; - pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE); - pg_image->Clear(); - pg_image->Fill( 0xff, polygon ); - pg_image->Outline( 0xff, polygon ); + pg_image = new Image( monitor->Width(), monitor->Height(), 1, ZM_SUBPIX_ORDER_NONE); + pg_image->Clear(); + pg_image->Fill( 0xff, polygon ); + pg_image->Outline( 0xff, polygon ); - ranges = new Range[monitor->Height()]; - for ( unsigned int y = 0; y < monitor->Height(); y++) - { - ranges[y].lo_x = -1; - ranges[y].hi_x = 0; - ranges[y].off_x = 0; - const uint8_t *ppoly = pg_image->Buffer( 0, y ); - for ( unsigned int x = 0; x < monitor->Width(); x++, ppoly++ ) - { - if ( *ppoly ) - { - if ( ranges[y].lo_x == -1 ) - { - ranges[y].lo_x = x; - } - if ( (unsigned int)ranges[y].hi_x < x ) - { - ranges[y].hi_x = x; - } - } - } - } - - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", config.dir_events, monitor->Name(), id); - } - pg_image->WriteJpeg( diag_path ); - } + ranges = new Range[monitor->Height()]; + for ( unsigned int y = 0; y < monitor->Height(); y++) + { + ranges[y].lo_x = -1; + ranges[y].hi_x = 0; + ranges[y].off_x = 0; + const uint8_t *ppoly = pg_image->Buffer( 0, y ); + for ( unsigned int x = 0; x < monitor->Width(); x++, ppoly++ ) + { + if ( *ppoly ) + { + if ( ranges[y].lo_x == -1 ) + { + ranges[y].lo_x = x; + } + if ( (unsigned int)ranges[y].hi_x < x ) + { + ranges[y].hi_x = x; + } + } + } + } + + if ( config.record_diag_images ) + { + static char diag_path[PATH_MAX] = ""; + if ( !diag_path[0] ) + { + snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-poly.jpg", config.dir_events, monitor->Name(), id); + } + pg_image->WriteJpeg( diag_path ); + } } Zone::~Zone() { - delete[] label; - delete image; - delete pg_image; - delete[] ranges; + delete[] label; + delete image; + delete pg_image; + delete[] ranges; } void Zone::RecordStats( const Event *event ) { static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "insert into Stats set MonitorId=%d, ZoneId=%d, EventId=%d, FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", monitor->Id(), id, event->Id(), event->Frames()+1, pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't insert event stats: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + snprintf( sql, sizeof(sql), "insert into Stats set MonitorId=%d, ZoneId=%d, EventId=%d, FrameId=%d, PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, MinX=%d, MinY=%d, MaxX=%d, MaxY=%d, Score=%d", monitor->Id(), id, event->Id(), event->Frames()+1, pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, min_blob_size, max_blob_size, alarm_box.LoX(), alarm_box.LoY(), alarm_box.HiX(), alarm_box.HiY(), score ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't insert event stats: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } } //============================================================================= bool Zone::CheckOverloadCount() { - Info("Overloaded count: %d, Overloaded frames: %d", overload_count, overload_frames); - if ( overload_count ) - { - Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); - Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); - overload_count--; - return( false ); - } - return true; + Info("Overloaded count: %d, Overloaded frames: %d", overload_count, overload_frames); + if ( overload_count ) + { + Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); + Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); + overload_count--; + return( false ); + } + return true; } void Zone::SetScore(unsigned int nScore) { - score = nScore; + score = nScore; } void Zone::SetAlarmImage(const Image* srcImage) { - delete image; - image = new Image(*srcImage); + delete image; + image = new Image(*srcImage); } int Zone::GetOverloadCount() { - return overload_count; + return overload_count; } void Zone::SetOverloadCount(int nOverCount) { - overload_count = nOverCount; + overload_count = nOverCount; } int Zone::GetOverloadFrames() { - return overload_frames; + return overload_frames; } int Zone::GetExtendAlarmCount() { - return extend_alarm_count; + return extend_alarm_count; } void Zone::SetExtendAlarmCount(int nExtendAlarmCount) { - extend_alarm_count = nExtendAlarmCount; + extend_alarm_count = nExtendAlarmCount; } int Zone::GetExtendAlarmFrames() { - return extend_alarm_frames; + return extend_alarm_frames; } bool Zone::CheckExtendAlarmCount() { - Info("ExtendAlarm count: %d, ExtendAlarm frames: %d", extend_alarm_count, extend_alarm_frames); - if ( extend_alarm_count ) - { - Debug( 3, "In extend mode, %d frames of %d remaining", extend_alarm_count, extend_alarm_frames ); - extend_alarm_count--; - return( true ); - } - return false; + Info("ExtendAlarm count: %d, ExtendAlarm frames: %d", extend_alarm_count, extend_alarm_frames); + if ( extend_alarm_count ) + { + Debug( 3, "In extend mode, %d frames of %d remaining", extend_alarm_count, extend_alarm_frames ); + extend_alarm_count--; + return( true ); + } + return false; } @@ -198,916 +198,920 @@ bool Zone::CheckExtendAlarmCount() bool Zone::CheckAlarms( const Image *delta_image ) { - ResetStats(); + ResetStats(); - if ( overload_count ) - { - Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); - Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); - overload_count--; - return( false ); - } + if ( overload_count ) + { + Info( "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); + Debug( 4, "In overload mode, %d frames of %d remaining", overload_count, overload_frames ); + overload_count--; + return( false ); + } - delete image; - // Get the difference image - Image *diff_image = image = new Image( *delta_image ); - int diff_width = diff_image->Width(); - uint8_t* diff_buff = (uint8_t*)diff_image->Buffer(); - uint8_t* pdiff; - const uint8_t* ppoly; + delete image; + // Get the difference image + Image *diff_image = image = new Image( *delta_image ); + int diff_width = diff_image->Width(); + uint8_t* diff_buff = (uint8_t*)diff_image->Buffer(); + uint8_t* pdiff; + const uint8_t* ppoly; - unsigned int pixel_diff_count = 0; + unsigned int pixel_diff_count = 0; - int alarm_lo_x = 0; - int alarm_hi_x = 0; - int alarm_lo_y = 0; - int alarm_hi_y = 0; + int alarm_lo_x = 0; + int alarm_hi_x = 0; + int alarm_lo_y = 0; + int alarm_hi_y = 0; - int alarm_mid_x = -1; - int alarm_mid_y = -1; - - unsigned int lo_y = polygon.LoY(); - unsigned int lo_x = polygon.LoX(); - unsigned int hi_x = polygon.HiX(); - unsigned int hi_y = polygon.HiY(); + int alarm_mid_x = -1; + int alarm_mid_y = -1; + + unsigned int lo_y = polygon.LoY(); + unsigned int lo_x = polygon.LoX(); + unsigned int hi_x = polygon.HiX(); + unsigned int hi_y = polygon.HiY(); - Debug( 4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label, lo_y, hi_y ); - - - Debug( 5, "Checking for alarmed pixels" ); - /* if(config.cpu_extensions && sseversion >= 20) { - sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); - } else { - std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); - } */ - std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); - - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", config.dir_events, monitor->Name(), id, 1 ); - } - diff_image->WriteJpeg( diag_path ); - } - - if ( pixel_diff_count && alarm_pixels ) - pixel_diff = pixel_diff_count/alarm_pixels; - Debug( 5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff ); + Debug( 4, "Checking alarms for zone %d/%s in lines %d -> %d", id, label, lo_y, hi_y ); + + + Debug( 5, "Checking for alarmed pixels" ); + /* if(config.cpu_extensions && sseversion >= 20) { + sse2_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); + } else { + std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); + } */ + std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count); + + if ( config.record_diag_images ) + { + static char diag_path[PATH_MAX] = ""; + if ( !diag_path[0] ) + { + snprintf( diag_path, sizeof(diag_path), "%s/%s/diag-%d-%d.jpg", config.dir_events, monitor->Name(), id, 1 ); + } + diff_image->WriteJpeg( diag_path ); + } + + if ( pixel_diff_count && alarm_pixels ) + pixel_diff = pixel_diff_count/alarm_pixels; + Debug( 5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d", alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff ); - if( alarm_pixels ) { - if( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) { - /* Not enough pixels alarmed */ - return (false); - } else if( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) { - /* Too many pixels alarmed */ - overload_count = overload_frames; - return (false); - } - } else { - /* No alarmed pixels */ - return (false); - } - - score = (100*alarm_pixels)/polygon.Area(); - if(score < 1) - score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ - Debug( 5, "Current score is %d", score ); - - if ( check_method >= FILTERED_PIXELS ) - { - int bx = filter_box.X(); - int by = filter_box.Y(); - int bx1 = bx-1; - int by1 = by-1; + if( alarm_pixels ) { + if( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) { + /* Not enough pixels alarmed */ + return (false); + } else if( max_alarm_pixels && (alarm_pixels > (unsigned int)max_alarm_pixels) ) { + /* Too many pixels alarmed */ + overload_count = overload_frames; + return (false); + } + } else { + /* No alarmed pixels */ + return (false); + } + + score = (100*alarm_pixels)/polygon.Area(); + if(score < 1) + score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ + Debug( 5, "Current score is %d", score ); + + if ( check_method >= FILTERED_PIXELS ) + { + int bx = filter_box.X(); + int by = filter_box.Y(); + int bx1 = bx-1; + int by1 = by-1; - Debug( 5, "Checking for filtered pixels" ); - if ( bx > 1 || by > 1 ) - { - // Now remove any pixels smaller than our filter size - unsigned char *cpdiff; - int ldx, hdx, ldy, hdy; - bool block; - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - int lo_x = ranges[y].lo_x; - int hi_x = ranges[y].hi_x; + Debug( 5, "Checking for filtered pixels" ); + if ( bx > 1 || by > 1 ) + { + // Now remove any pixels smaller than our filter size + unsigned char *cpdiff; + int ldx, hdx, ldy, hdy; + bool block; + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + int lo_x = ranges[y].lo_x; + int hi_x = ranges[y].hi_x; - pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); + pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); - for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) - { - if ( *pdiff == WHITE ) - { - // Check participation in an X block - ldx = (x>=(lo_x+bx1))?-bx1:lo_x-x; - hdx = (x<=(hi_x-bx1))?0:((hi_x-x)-bx1); - ldy = (y>=(lo_y+by1))?-by1:lo_y-y; - hdy = (y<=(hi_y-by1))?0:((hi_y-y)-by1); - block = false; - for ( int dy = ldy; !block && dy <= hdy; dy++ ) - { - for ( int dx = ldx; !block && dx <= hdx; dx++ ) - { - block = true; - for ( int dy2 = 0; block && dy2 < by; dy2++ ) - { - for ( int dx2 = 0; block && dx2 < bx; dx2++ ) - { - cpdiff = diff_buff + (((y+dy+dy2)*diff_width) + (x+dx+dx2)); - if ( !*cpdiff ) - { - block = false; - } - } - } - } - } - if ( !block ) - { - *pdiff = BLACK; - continue; - } - alarm_filter_pixels++; - } - } - } - } - else - { - alarm_filter_pixels = alarm_pixels; - } - - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 2 ); - } - diff_image->WriteJpeg( diag_path ); - } - - Debug( 5, "Got %d filtered pixels, need %d -> %d", alarm_filter_pixels, min_filter_pixels, max_filter_pixels ); - - if( alarm_filter_pixels ) { - if( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) { - /* Not enough pixels alarmed */ - return (false); - } else if( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) { - /* Too many pixels alarmed */ - overload_count = overload_frames; - return (false); - } - } else { - /* No filtered pixels */ - return (false); - } - - score = (100*alarm_filter_pixels)/(polygon.Area()); - if(score < 1) - score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ - Debug( 5, "Current score is %d", score ); + for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) + { + if ( *pdiff == WHITE ) + { + // Check participation in an X block + ldx = (x>=(lo_x+bx1))?-bx1:lo_x-x; + hdx = (x<=(hi_x-bx1))?0:((hi_x-x)-bx1); + ldy = (y>=(lo_y+by1))?-by1:lo_y-y; + hdy = (y<=(hi_y-by1))?0:((hi_y-y)-by1); + block = false; + for ( int dy = ldy; !block && dy <= hdy; dy++ ) + { + for ( int dx = ldx; !block && dx <= hdx; dx++ ) + { + block = true; + for ( int dy2 = 0; block && dy2 < by; dy2++ ) + { + for ( int dx2 = 0; block && dx2 < bx; dx2++ ) + { + cpdiff = diff_buff + (((y+dy+dy2)*diff_width) + (x+dx+dx2)); + if ( !*cpdiff ) + { + block = false; + } + } + } + } + } + if ( !block ) + { + *pdiff = BLACK; + continue; + } + alarm_filter_pixels++; + } + } + } + } + else + { + alarm_filter_pixels = alarm_pixels; + } + + if ( config.record_diag_images ) + { + static char diag_path[PATH_MAX] = ""; + if ( !diag_path[0] ) + { + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 2 ); + } + diff_image->WriteJpeg( diag_path ); + } + + Debug( 5, "Got %d filtered pixels, need %d -> %d", alarm_filter_pixels, min_filter_pixels, max_filter_pixels ); + + if( alarm_filter_pixels ) { + if( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) { + /* Not enough pixels alarmed */ + return (false); + } else if( max_filter_pixels && (alarm_filter_pixels > max_filter_pixels) ) { + /* Too many pixels alarmed */ + overload_count = overload_frames; + return (false); + } + } else { + /* No filtered pixels */ + return (false); + } + + score = (100*alarm_filter_pixels)/(polygon.Area()); + if(score < 1) + score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ + Debug( 5, "Current score is %d", score ); - if ( check_method >= BLOBS ) - { - Debug( 5, "Checking for blob pixels" ); - typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats; - BlobStats blob_stats[256]; - memset( blob_stats, 0, sizeof(BlobStats)*256 ); - uint8_t *spdiff; - uint8_t last_x, last_y; - BlobStats *bsx, *bsy; - BlobStats *bsm, *bss; - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - int lo_x = ranges[y].lo_x; - int hi_x = ranges[y].hi_x; + if ( check_method >= BLOBS ) + { + Debug( 5, "Checking for blob pixels" ); + typedef struct { unsigned char tag; int count; int lo_x; int hi_x; int lo_y; int hi_y; } BlobStats; + BlobStats blob_stats[256]; + memset( blob_stats, 0, sizeof(BlobStats)*256 ); + uint8_t *spdiff; + uint8_t last_x, last_y; + BlobStats *bsx, *bsy; + BlobStats *bsm, *bss; + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + int lo_x = ranges[y].lo_x; + int hi_x = ranges[y].hi_x; - pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); - for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) - { - if ( *pdiff == WHITE ) - { - Debug( 9, "Got white pixel at %d,%d (%p)", x, y, pdiff ); - //last_x = (x>lo_x)?*(pdiff-1):0; - //last_y = (y>lo_y&&x>=last_lo_x&&x<=last_hi_x)?*(pdiff-diff_width):0; - - last_x = 0; - if(x > 0) { - if((x-1) >= lo_x) { - last_x = *(pdiff-1); - } - } - - last_y = 0; - if(y > 0) { - if((y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x) { - last_y = *(pdiff-diff_width); - } - } - - if ( last_x ) - { - Debug( 9, "Left neighbour is %d", last_x ); - bsx = &blob_stats[last_x]; - if ( last_y ) - { - Debug( 9, "Top neighbour is %d", last_y ); - bsy = &blob_stats[last_y]; - if ( last_x == last_y ) - { - Debug( 9, "Matching neighbours, setting to %d", last_x ); - // Add to the blob from the x side (either side really) - *pdiff = last_x; - alarm_blob_pixels++; - bsx->count++; - if ( x > bsx->hi_x ) bsx->hi_x = x; - if ( (int)y > bsx->hi_y ) bsx->hi_y = y; - } - else - { - // Aggregate blobs - bsm = bsx->count>=bsy->count?bsx:bsy; - bss = bsm==bsx?bsy:bsx; - - Debug( 9, "Different neighbours, setting pixels of %d to %d", bss->tag, bsm->tag ); - Debug( 9, "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); - Debug( 9, "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); - // Now change all those pixels to the other setting - int changed = 0; - for ( int sy = bss->lo_y; sy <= bss->hi_y; sy++) - { - int lo_sx = bss->lo_x>=ranges[sy].lo_x?bss->lo_x:ranges[sy].lo_x; - int hi_sx = bss->hi_x<=ranges[sy].hi_x?bss->hi_x:ranges[sy].hi_x; - - Debug( 9, "Changing %d, %d->%d", sy, lo_sx, hi_sx ); - Debug( 9, "Range %d, %d->%d", sy, ranges[sy].lo_x, ranges[sy].hi_x ); - spdiff = diff_buff + ((diff_width * sy) + lo_sx); - for ( int sx = lo_sx; sx <= hi_sx; sx++, spdiff++ ) - { - Debug( 9, "Pixel at %d,%d (%p) is %d", sx, sy, spdiff, *spdiff ); - if ( *spdiff == bss->tag ) - { - Debug( 9, "Setting pixel" ); - *spdiff = bsm->tag; - changed++; - } - } - } - *pdiff = bsm->tag; - alarm_blob_pixels++; - if ( !changed ) - { - Info( "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); - Info( "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); - Error( "No pixels changed, exiting" ); - exit( -1 ); - } - - // Merge the slave blob into the master - bsm->count += bss->count+1; - if ( x > bsm->hi_x ) bsm->hi_x = x; - if ( (int)y > bsm->hi_y ) bsm->hi_y = y; - if ( bss->lo_x < bsm->lo_x ) bsm->lo_x = bss->lo_x; - if ( bss->lo_y < bsm->lo_y ) bsm->lo_y = bss->lo_y; - if ( bss->hi_x > bsm->hi_x ) bsm->hi_x = bss->hi_x; - if ( bss->hi_y > bsm->hi_y ) bsm->hi_y = bss->hi_y; - - alarm_blobs--; - - Debug( 6, "Merging blob %d with %d at %d,%d, %d current blobs", bss->tag, bsm->tag, x, y, alarm_blobs ); - - // Clear out the old blob - bss->tag = 0; - bss->count = 0; - bss->lo_x = 0; - bss->lo_y = 0; - bss->hi_x = 0; - bss->hi_y = 0; - } - } - else - { - Debug( 9, "Setting to left neighbour %d", last_x ); - // Add to the blob from the x side - *pdiff = last_x; - alarm_blob_pixels++; - bsx->count++; - if ( x > bsx->hi_x ) bsx->hi_x = x; - if ( (int)y > bsx->hi_y ) bsx->hi_y = y; - } - } - else - { - if ( last_y ) - { - Debug( 9, "Top neighbour is %d", last_y ); - Debug( 9, "Setting to top neighbour %d", last_y ); - - // Add to the blob from the y side - BlobStats *bsy = &blob_stats[last_y]; - - *pdiff = last_y; - alarm_blob_pixels++; - bsy->count++; - if ( x > bsy->hi_x ) bsy->hi_x = x; - if ( (int)y > bsy->hi_y ) bsy->hi_y = y; - } - else - { - // Create a new blob - int i; - for ( i = (WHITE-1); i > 0; i-- ) - { - BlobStats *bs = &blob_stats[i]; - // See if we can recycle one first, only if it's at least two rows up - if ( bs->count && bs->hi_y < (int)(y-1) ) - { - if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) - { - if ( config.create_analysis_images || config.record_diag_images ) - { - for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) - { - spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); - for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) - { - if ( *spdiff == bs->tag ) - { - *spdiff = BLACK; - } - } - } - } - alarm_blobs--; - alarm_blob_pixels -= bs->count; - - Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); - - bs->tag = 0; - bs->count = 0; - bs->lo_x = 0; - bs->lo_y = 0; - bs->hi_x = 0; - bs->hi_y = 0; - } - } - if ( !bs->count ) - { - Debug( 9, "Creating new blob %d", i ); - *pdiff = i; - alarm_blob_pixels++; - bs->tag = i; - bs->count++; - bs->lo_x = bs->hi_x = x; - bs->lo_y = bs->hi_y = y; - alarm_blobs++; - - Debug( 6, "Created blob %d at %d,%d, %d current blobs", bs->tag, x, y, alarm_blobs ); - break; - } - } - if ( i == 0 ) - { - Warning( "Max blob count reached. Unable to allocate new blobs so terminating. Zone settings may be too sensitive." ); - x = hi_x+1; - y = hi_y+1; - } - } - } - } - } - } - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 ); - } - diff_image->WriteJpeg( diag_path ); - } - - if ( !alarm_blobs ) - { - return( false ); - } - - Debug( 5, "Got %d raw blob pixels, %d raw blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); - - // Now eliminate blobs under the threshold - for ( int i = 1; i < WHITE; i++ ) - { - BlobStats *bs = &blob_stats[i]; - if ( bs->count ) - { - if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) - { - if ( config.create_analysis_images || config.record_diag_images ) - { - for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) - { - spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); - for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) - { - if ( *spdiff == bs->tag ) - { - *spdiff = BLACK; - } - } - } - } - alarm_blobs--; - alarm_blob_pixels -= bs->count; - - Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); - - bs->tag = 0; - bs->count = 0; - bs->lo_x = 0; - bs->lo_y = 0; - bs->hi_x = 0; - bs->hi_y = 0; - } - else - { - Debug( 6, "Preserved blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); - if ( !min_blob_size || bs->count < min_blob_size ) min_blob_size = bs->count; - if ( !max_blob_size || bs->count > max_blob_size ) max_blob_size = bs->count; - } - } - } - if ( config.record_diag_images ) - { - static char diag_path[PATH_MAX] = ""; - if ( !diag_path[0] ) - { - snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 ); - } - diff_image->WriteJpeg( diag_path ); - } - Debug( 5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); + pdiff = (uint8_t*)diff_image->Buffer( lo_x, y ); + for ( int x = lo_x; x <= hi_x; x++, pdiff++ ) + { + if ( *pdiff == WHITE ) + { + Debug( 9, "Got white pixel at %d,%d (%p)", x, y, pdiff ); + //last_x = (x>lo_x)?*(pdiff-1):0; + //last_y = (y>lo_y&&x>=last_lo_x&&x<=last_hi_x)?*(pdiff-diff_width):0; - if( alarm_blobs ) { - if( min_blobs && (alarm_blobs < min_blobs) ) { - /* Not enough pixels alarmed */ - return (false); - } else if(max_blobs && (alarm_blobs > max_blobs) ) { - /* Too many pixels alarmed */ - overload_count = overload_frames; - return (false); - } - } else { - /* No blobs */ - return (false); - } - - score = (100*alarm_blob_pixels)/(polygon.Area()); - if(score < 1) - score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ - Debug( 5, "Current score is %d", score ); + last_x = 0; + if(x > 0) { + if((x-1) >= lo_x) { + last_x = *(pdiff-1); + } + } + + last_y = 0; + if(y > 0) { + if((y-1) >= lo_y && ranges[(y-1)].lo_x <= x && ranges[(y-1)].hi_x >= x) { + last_y = *(pdiff-diff_width); + } + } + + if ( last_x ) + { + Debug( 9, "Left neighbour is %d", last_x ); + bsx = &blob_stats[last_x]; + if ( last_y ) + { + Debug( 9, "Top neighbour is %d", last_y ); + bsy = &blob_stats[last_y]; + if ( last_x == last_y ) + { + Debug( 9, "Matching neighbours, setting to %d", last_x ); + // Add to the blob from the x side (either side really) + *pdiff = last_x; + alarm_blob_pixels++; + bsx->count++; + if ( x > bsx->hi_x ) bsx->hi_x = x; + if ( (int)y > bsx->hi_y ) bsx->hi_y = y; + } + else + { + // Aggregate blobs + bsm = bsx->count>=bsy->count?bsx:bsy; + bss = bsm==bsx?bsy:bsx; - alarm_lo_x = polygon.HiX()+1; - alarm_hi_x = polygon.LoX()-1; - alarm_lo_y = polygon.HiY()+1; - alarm_hi_y = polygon.LoY()-1; - for ( int i = 1; i < WHITE; i++ ) - { - BlobStats *bs = &blob_stats[i]; - if ( bs->count ) - { - if ( bs->count == max_blob_size ) - { - if ( config.weighted_alarm_centres ) - { - unsigned long x_total = 0; - unsigned long y_total = 0; + Debug( 9, "Different neighbours, setting pixels of %d to %d", bss->tag, bsm->tag ); + Debug( 9, "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); + Debug( 9, "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); + // Now change all those pixels to the other setting + int changed = 0; + for ( int sy = bss->lo_y; sy <= bss->hi_y; sy++) + { + int lo_sx = bss->lo_x>=ranges[sy].lo_x?bss->lo_x:ranges[sy].lo_x; + int hi_sx = bss->hi_x<=ranges[sy].hi_x?bss->hi_x:ranges[sy].hi_x; - for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) - { - spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); - for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) - { - if ( *spdiff == bs->tag ) - { - x_total += sx; - y_total += sy; - } - } - } - alarm_mid_x = int(round(x_total/bs->count)); - alarm_mid_y = int(round(y_total/bs->count)); - } - else - { - alarm_mid_x = int((bs->hi_x+bs->lo_x+1)/2); - alarm_mid_y = int((bs->hi_y+bs->lo_y+1)/2); - } - } + Debug( 9, "Changing %d, %d->%d", sy, lo_sx, hi_sx ); + Debug( 9, "Range %d, %d->%d", sy, ranges[sy].lo_x, ranges[sy].hi_x ); + spdiff = diff_buff + ((diff_width * sy) + lo_sx); + for ( int sx = lo_sx; sx <= hi_sx; sx++, spdiff++ ) + { + Debug( 9, "Pixel at %d,%d (%p) is %d", sx, sy, spdiff, *spdiff ); + if ( *spdiff == bss->tag ) + { + Debug( 9, "Setting pixel" ); + *spdiff = bsm->tag; + changed++; + } + } + } + *pdiff = bsm->tag; + alarm_blob_pixels++; + if ( !changed ) + { + Info( "Master blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bsm->tag, bsm->count, bsm->lo_x, bsm->hi_x, bsm->lo_y, bsm->hi_y ); + Info( "Slave blob t:%d, c:%d, lx:%d, hx:%d, ly:%d, hy:%d", bss->tag, bss->count, bss->lo_x, bss->hi_x, bss->lo_y, bss->hi_y ); + Error( "No pixels changed, exiting" ); + exit( -1 ); + } - if ( alarm_lo_x > bs->lo_x ) alarm_lo_x = bs->lo_x; - if ( alarm_lo_y > bs->lo_y ) alarm_lo_y = bs->lo_y; - if ( alarm_hi_x < bs->hi_x ) alarm_hi_x = bs->hi_x; - if ( alarm_hi_y < bs->hi_y ) alarm_hi_y = bs->hi_y; - } - } - } - else - { - alarm_mid_x = int((alarm_hi_x+alarm_lo_x+1)/2); - alarm_mid_y = int((alarm_hi_y+alarm_lo_y+1)/2); - } - } + // Merge the slave blob into the master + bsm->count += bss->count+1; + if ( x > bsm->hi_x ) bsm->hi_x = x; + if ( (int)y > bsm->hi_y ) bsm->hi_y = y; + if ( bss->lo_x < bsm->lo_x ) bsm->lo_x = bss->lo_x; + if ( bss->lo_y < bsm->lo_y ) bsm->lo_y = bss->lo_y; + if ( bss->hi_x > bsm->hi_x ) bsm->hi_x = bss->hi_x; + if ( bss->hi_y > bsm->hi_y ) bsm->hi_y = bss->hi_y; - if ( type == INCLUSIVE ) - { - // score >>= 1; - score /= 2; - } - else if ( type == EXCLUSIVE ) - { - // score <<= 1; - score *= 2; - - } + alarm_blobs--; - Debug( 5, "Adjusted score is %d", score ); + Debug( 6, "Merging blob %d with %d at %d,%d, %d current blobs", bss->tag, bsm->tag, x, y, alarm_blobs ); - // Now outline the changed region - if ( score ) - { - alarm_box = Box( Coord( alarm_lo_x, alarm_lo_y ), Coord( alarm_hi_x, alarm_hi_y ) ); + // Clear out the old blob + bss->tag = 0; + bss->count = 0; + bss->lo_x = 0; + bss->lo_y = 0; + bss->hi_x = 0; + bss->hi_y = 0; + } + } + else + { + Debug( 9, "Setting to left neighbour %d", last_x ); + // Add to the blob from the x side + *pdiff = last_x; + alarm_blob_pixels++; + bsx->count++; + if ( x > bsx->hi_x ) bsx->hi_x = x; + if ( (int)y > bsx->hi_y ) bsx->hi_y = y; + } + } + else + { + if ( last_y ) + { + Debug( 9, "Top neighbour is %d", last_y ); + Debug( 9, "Setting to top neighbour %d", last_y ); + + // Add to the blob from the y side + BlobStats *bsy = &blob_stats[last_y]; - //if ( monitor->followMotion() ) - if ( true ) - { - alarm_centre = Coord( alarm_mid_x, alarm_mid_y ); - } - else - { - alarm_centre = alarm_box.Centre(); - } + *pdiff = last_y; + alarm_blob_pixels++; + bsy->count++; + if ( x > bsy->hi_x ) bsy->hi_x = x; + if ( (int)y > bsy->hi_y ) bsy->hi_y = y; + } + else + { + // Create a new blob + int i; + for ( i = (WHITE-1); i > 0; i-- ) + { + BlobStats *bs = &blob_stats[i]; + // See if we can recycle one first, only if it's at least two rows up + if ( bs->count && bs->hi_y < (int)(y-1) ) + { + if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) + { + if ( config.create_analysis_images || config.record_diag_images ) + { + for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) + { + spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); + for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) + { + if ( *spdiff == bs->tag ) + { + *spdiff = BLACK; + } + } + } + } + alarm_blobs--; + alarm_blob_pixels -= bs->count; + + Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); - if ( (type < PRECLUSIVE) && check_method >= BLOBS && config.create_analysis_images ) - { + bs->tag = 0; + bs->count = 0; + bs->lo_x = 0; + bs->lo_y = 0; + bs->hi_x = 0; + bs->hi_y = 0; + } + } + if ( !bs->count ) + { + Debug( 9, "Creating new blob %d", i ); + *pdiff = i; + alarm_blob_pixels++; + bs->tag = i; + bs->count++; + bs->lo_x = bs->hi_x = x; + bs->lo_y = bs->hi_y = y; + alarm_blobs++; - // First mask out anything we don't want - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - pdiff = diff_buff + ((diff_width * y) + lo_x); + Debug( 6, "Created blob %d at %d,%d, %d current blobs", bs->tag, x, y, alarm_blobs ); + break; + } + } + if ( i == 0 ) + { + Warning( "Max blob count reached. Unable to allocate new blobs so terminating. Zone settings may be too sensitive." ); + x = hi_x+1; + y = hi_y+1; + } + } + } + } + } + } + if ( config.record_diag_images ) + { + static char diag_path[PATH_MAX] = ""; + if ( !diag_path[0] ) + { + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 3 ); + } + diff_image->WriteJpeg( diag_path ); + } - int lo_x2 = ranges[y].lo_x; - int hi_x2 = ranges[y].hi_x; + if ( !alarm_blobs ) + { + return( false ); + } + + Debug( 5, "Got %d raw blob pixels, %d raw blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); - int lo_gap = lo_x2-lo_x; - if ( lo_gap > 0 ) - { - if ( lo_gap == 1 ) - { - *pdiff++ = BLACK; - } - else - { - memset( pdiff, BLACK, lo_gap ); - pdiff += lo_gap; - } - } + // Now eliminate blobs under the threshold + for ( int i = 1; i < WHITE; i++ ) + { + BlobStats *bs = &blob_stats[i]; + if ( bs->count ) + { + if ( (min_blob_pixels && bs->count < min_blob_pixels) || (max_blob_pixels && bs->count > max_blob_pixels) ) + { + if ( config.create_analysis_images || config.record_diag_images ) + { + for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) + { + spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); + for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) + { + if ( *spdiff == bs->tag ) + { + *spdiff = BLACK; + } + } + } + } + alarm_blobs--; + alarm_blob_pixels -= bs->count; + + Debug( 6, "Eliminated blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); - ppoly = pg_image->Buffer( lo_x2, y ); - for ( int x = lo_x2; x <= hi_x2; x++, pdiff++, ppoly++ ) - { - if ( !*ppoly ) - { - *pdiff = BLACK; - } - } + bs->tag = 0; + bs->count = 0; + bs->lo_x = 0; + bs->lo_y = 0; + bs->hi_x = 0; + bs->hi_y = 0; + } + else + { + Debug( 6, "Preserved blob %d, %d pixels (%d,%d - %d,%d), %d current blobs", i, bs->count, bs->lo_x, bs->lo_y, bs->hi_x, bs->hi_y, alarm_blobs ); + if ( !min_blob_size || bs->count < min_blob_size ) min_blob_size = bs->count; + if ( !max_blob_size || bs->count > max_blob_size ) max_blob_size = bs->count; + } + } + } + if ( config.record_diag_images ) + { + static char diag_path[PATH_MAX] = ""; + if ( !diag_path[0] ) + { + snprintf( diag_path, sizeof(diag_path), "%s/%d/diag-%d-%d.jpg", config.dir_events, monitor->Id(), id, 4 ); + } + diff_image->WriteJpeg( diag_path ); + } + Debug( 5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d", alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs ); + + if( alarm_blobs ) { + if( min_blobs && (alarm_blobs < min_blobs) ) { + /* Not enough pixels alarmed */ + return (false); + } else if(max_blobs && (alarm_blobs > max_blobs) ) { + /* Too many pixels alarmed */ + overload_count = overload_frames; + return (false); + } + } else { + /* No blobs */ + return (false); + } + + score = (100*alarm_blob_pixels)/(polygon.Area()); + if(score < 1) + score = 1; /* Fix for score of 0 when frame meets thresholds but alarmed area is not big enough */ + Debug( 5, "Current score is %d", score ); - int hi_gap = hi_x-hi_x2; - if ( hi_gap > 0 ) - { - if ( hi_gap == 1 ) - { - *pdiff = BLACK; - } - else - { - memset( pdiff, BLACK, hi_gap ); - } - } - } - - if( monitor->Colours() == ZM_COLOUR_GRAY8 ) { - image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() ); - } else { - image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() ); - } - - // Only need to delete this when 'image' becomes detached and points somewhere else - delete diff_image; - } - else - { - delete image; - image = 0; - } + alarm_lo_x = polygon.HiX()+1; + alarm_hi_x = polygon.LoX()-1; + alarm_lo_y = polygon.HiY()+1; + alarm_hi_y = polygon.LoY()-1; + for ( int i = 1; i < WHITE; i++ ) + { + BlobStats *bs = &blob_stats[i]; + if ( bs->count ) + { + if ( bs->count == max_blob_size ) + { + if ( config.weighted_alarm_centres ) + { + unsigned long x_total = 0; + unsigned long y_total = 0; - Debug( 1, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d", Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score ); - } - return( true ); + for ( int sy = bs->lo_y; sy <= bs->hi_y; sy++ ) + { + spdiff = diff_buff + ((diff_width * sy) + bs->lo_x); + for ( int sx = bs->lo_x; sx <= bs->hi_x; sx++, spdiff++ ) + { + if ( *spdiff == bs->tag ) + { + x_total += sx; + y_total += sy; + } + } + } + alarm_mid_x = int(round(x_total/bs->count)); + alarm_mid_y = int(round(y_total/bs->count)); + } + else + { + alarm_mid_x = int((bs->hi_x+bs->lo_x+1)/2); + alarm_mid_y = int((bs->hi_y+bs->lo_y+1)/2); + } + } + + if ( alarm_lo_x > bs->lo_x ) alarm_lo_x = bs->lo_x; + if ( alarm_lo_y > bs->lo_y ) alarm_lo_y = bs->lo_y; + if ( alarm_hi_x < bs->hi_x ) alarm_hi_x = bs->hi_x; + if ( alarm_hi_y < bs->hi_y ) alarm_hi_y = bs->hi_y; + } + } + } + else + { + alarm_mid_x = int((alarm_hi_x+alarm_lo_x+1)/2); + alarm_mid_y = int((alarm_hi_y+alarm_lo_y+1)/2); + } + } + + if ( type == INCLUSIVE ) + { + // score >>= 1; + score /= 2; + } + else if ( type == EXCLUSIVE ) + { + // score <<= 1; + score *= 2; + + } + + Debug( 5, "Adjusted score is %d", score ); + + // Now outline the changed region + if ( score ) + { + alarm_box = Box( Coord( alarm_lo_x, alarm_lo_y ), Coord( alarm_hi_x, alarm_hi_y ) ); + + //if ( monitor->followMotion() ) + if ( true ) + { + alarm_centre = Coord( alarm_mid_x, alarm_mid_y ); + } + else + { + alarm_centre = alarm_box.Centre(); + } + + if ( (type < PRECLUSIVE) && check_method >= BLOBS && config.create_analysis_images ) + { + + // First mask out anything we don't want + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + pdiff = diff_buff + ((diff_width * y) + lo_x); + + int lo_x2 = ranges[y].lo_x; + int hi_x2 = ranges[y].hi_x; + + int lo_gap = lo_x2-lo_x; + if ( lo_gap > 0 ) + { + if ( lo_gap == 1 ) + { + *pdiff++ = BLACK; + } + else + { + memset( pdiff, BLACK, lo_gap ); + pdiff += lo_gap; + } + } + + ppoly = pg_image->Buffer( lo_x2, y ); + for ( int x = lo_x2; x <= hi_x2; x++, pdiff++, ppoly++ ) + { + if ( !*ppoly ) + { + *pdiff = BLACK; + } + } + + int hi_gap = hi_x-hi_x2; + if ( hi_gap > 0 ) + { + if ( hi_gap == 1 ) + { + *pdiff = BLACK; + } + else + { + memset( pdiff, BLACK, hi_gap ); + } + } + } + + if( monitor->Colours() == ZM_COLOUR_GRAY8 ) { + image = diff_image->HighlightEdges( alarm_rgb, ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB, &polygon.Extent() ); + } else { + image = diff_image->HighlightEdges( alarm_rgb, monitor->Colours(), monitor->SubpixelOrder(), &polygon.Extent() ); + } + + // Only need to delete this when 'image' becomes detached and points somewhere else + delete diff_image; + } + else + { + delete image; + image = 0; + } + + Debug( 1, "%s: Pixel Diff: %d, Alarm Pixels: %d, Filter Pixels: %d, Blob Pixels: %d, Blobs: %d, Score: %d", Label(), pixel_diff, alarm_pixels, alarm_filter_pixels, alarm_blob_pixels, alarm_blobs, score ); + } + return( true ); } bool Zone::ParsePolygonString( const char *poly_string, Polygon &polygon ) { - Debug( 3, "Parsing polygon string '%s'", poly_string ); + Debug( 3, "Parsing polygon string '%s'", poly_string ); - char *str_ptr = new char[strlen(poly_string)+1]; - char *str = str_ptr; - strcpy( str, poly_string ); + char *str_ptr = new char[strlen(poly_string)+1]; + char *str = str_ptr; + strcpy( str, poly_string ); - char *ws; - int n_coords = 0; - int max_n_coords = strlen(str)/4; - Coord *coords = new Coord[max_n_coords]; - while( true ) - { - if ( *str == '\0' ) - { - break; - } - ws = strchr( str, ' ' ); - if ( ws ) - { - *ws = '\0'; - } - char *cp = strchr( str, ',' ); - if ( !cp ) - { - Error( "Bogus coordinate %s found in polygon string", str ); - delete[] coords; - delete[] str_ptr; - return( false ); - } - else - { - *cp = '\0'; - char *xp = str; - char *yp = cp+1; + char *ws; + int n_coords = 0; + int max_n_coords = strlen(str)/4; + Coord *coords = new Coord[max_n_coords]; + while( true ) + { + if ( *str == '\0' ) + { + break; + } + ws = strchr( str, ' ' ); + if ( ws ) + { + *ws = '\0'; + } + char *cp = strchr( str, ',' ); + if ( !cp ) + { + Error( "Bogus coordinate %s found in polygon string", str ); + delete[] coords; + delete[] str_ptr; + return( false ); + } + else + { + *cp = '\0'; + char *xp = str; + char *yp = cp+1; - int x = atoi(xp); - int y = atoi(yp); + int x = atoi(xp); + int y = atoi(yp); - Debug( 3, "Got coordinate %d,%d from polygon string", x, y ); + Debug( 3, "Got coordinate %d,%d from polygon string", x, y ); #if 0 - if ( x < 0 ) - x = 0; - else if ( x >= width ) - x = width-1; - if ( y < 0 ) - y = 0; - else if ( y >= height ) - y = height-1; + if ( x < 0 ) + x = 0; + else if ( x >= width ) + x = width-1; + if ( y < 0 ) + y = 0; + else if ( y >= height ) + y = height-1; #endif - coords[n_coords++] = Coord( x, y ); - } - if ( ws ) - str = ws+1; - else - break; - } - polygon = Polygon( n_coords, coords ); + coords[n_coords++] = Coord( x, y ); + } + if ( ws ) + str = ws+1; + else + break; + } + polygon = Polygon( n_coords, coords ); - Debug( 3, "Successfully parsed polygon string" ); - //printf( "Area: %d\n", pg.Area() ); - //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); + Debug( 3, "Successfully parsed polygon string" ); + //printf( "Area: %d\n", pg.Area() ); + //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); - delete[] coords; - delete[] str_ptr; + delete[] coords; + delete[] str_ptr; - return( true ); + return( true ); } bool Zone::ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ) { - Debug( 3, "Parsing zone string '%s'", zone_string ); + Debug( 3, "Parsing zone string '%s'", zone_string ); - char *str_ptr = new char[strlen(zone_string)+1]; - char *str = str_ptr; - strcpy( str, zone_string ); + char *str_ptr = new char[strlen(zone_string)+1]; + char *str = str_ptr; + strcpy( str, zone_string ); - char *ws = strchr( str, ' ' ); - if ( !ws ) - { - Debug( 3, "No initial whitespace found in zone string '%s', finishing", str ); - } - zone_id = strtol( str, 0, 10 ); - Debug( 3, "Got zone %d from zone string", zone_id ); - if ( !ws ) - { - delete str_ptr; - return( true ); - } + char *ws = strchr( str, ' ' ); + if ( !ws ) + { + Debug( 3, "No initial whitespace found in zone string '%s', finishing", str ); + } + zone_id = strtol( str, 0, 10 ); + Debug( 3, "Got zone %d from zone string", zone_id ); + if ( !ws ) + { + delete str_ptr; + return( true ); + } - *ws = '\0'; - str = ws+1; + *ws = '\0'; + str = ws+1; - ws = strchr( str, ' ' ); - if ( !ws ) - { - Debug( 3, "No secondary whitespace found in zone string '%s', finishing", zone_string ); - } - colour = strtol( str, 0, 16 ); - Debug( 3, "Got colour %06x from zone string", colour ); - if ( !ws ) - { - delete str_ptr; - return( true ); - } - *ws = '\0'; - str = ws+1; + ws = strchr( str, ' ' ); + if ( !ws ) + { + Debug( 3, "No secondary whitespace found in zone string '%s', finishing", zone_string ); + } + colour = strtol( str, 0, 16 ); + Debug( 3, "Got colour %06x from zone string", colour ); + if ( !ws ) + { + delete str_ptr; + return( true ); + } + *ws = '\0'; + str = ws+1; - bool result = ParsePolygonString( str, polygon ); + bool result = ParsePolygonString( str, polygon ); - //printf( "Area: %d\n", pg.Area() ); - //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); + //printf( "Area: %d\n", pg.Area() ); + //printf( "Centre: %d,%d\n", pg.Centre().X(), pg.Centre().Y() ); - delete[] str_ptr; + delete[] str_ptr; - return( result ); + return( result ); } int Zone::Load( Monitor *monitor, Zone **&zones ) { - static char sql[ZM_SQL_MED_BUFSIZ]; - snprintf( sql, sizeof(sql), "select Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs,OverloadFrames,ExtendAlarmFrames from Zones where MonitorId = %d order by Type, Id", monitor->Id() ); - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + static char sql[ZM_SQL_MED_BUFSIZ]; + snprintf( sql, sizeof(sql), "select Id,Name,Type+0,Units,Coords,AlarmRGB,CheckMethod+0,MinPixelThreshold,MaxPixelThreshold,MinAlarmPixels,MaxAlarmPixels,FilterX,FilterY,MinFilterPixels,MaxFilterPixels,MinBlobPixels,MaxBlobPixels,MinBlobs,MaxBlobs,OverloadFrames,ExtendAlarmFrames from Zones where MonitorId = %d order by Type, Id", monitor->Id() ); + if ( mysql_query( &dbconn, sql ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_zones = mysql_num_rows( result ); - Debug( 1, "Got %d zones for monitor %s", n_zones, monitor->Name() ); - delete[] zones; - zones = new Zone *[n_zones]; - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int col = 0; + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_zones = mysql_num_rows( result ); + Debug( 1, "Got %d zones for monitor %s", n_zones, monitor->Name() ); + delete[] zones; + zones = new Zone *[n_zones]; + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int col = 0; - int Id = atoi(dbrow[col++]); - const char *Name = dbrow[col++]; - int Type = atoi(dbrow[col++]); - const char *Units = dbrow[col++]; - const char *Coords = dbrow[col++]; - int AlarmRGB = dbrow[col]?atoi(dbrow[col]):0; col++; - int CheckMethod = atoi(dbrow[col++]); - int MinPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; - int MinAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int FilterX = dbrow[col]?atoi(dbrow[col]):0; col++; - int FilterY = dbrow[col]?atoi(dbrow[col]):0; col++; - int MinFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MinBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; - int MinBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; - int MaxBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; - int OverloadFrames = dbrow[col]?atoi(dbrow[col]):0; col++; - int ExtendAlarmFrames = dbrow[col]?atoi(dbrow[col]):0; col++; - - /* HTML colour code is actually BGR in memory, we want RGB */ - AlarmRGB = rgb_convert(AlarmRGB, ZM_SUBPIX_ORDER_BGR); + int Id = atoi(dbrow[col++]); + const char *Name = dbrow[col++]; + int Type = atoi(dbrow[col++]); + const char *Units = dbrow[col++]; + const char *Coords = dbrow[col++]; + int AlarmRGB = dbrow[col]?atoi(dbrow[col]):0; col++; + int CheckMethod = atoi(dbrow[col++]); + int MinPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxPixelThreshold = dbrow[col]?atoi(dbrow[col]):0; col++; + int MinAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxAlarmPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int FilterX = dbrow[col]?atoi(dbrow[col]):0; col++; + int FilterY = dbrow[col]?atoi(dbrow[col]):0; col++; + int MinFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxFilterPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MinBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxBlobPixels = dbrow[col]?atoi(dbrow[col]):0; col++; + int MinBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; + int MaxBlobs = dbrow[col]?atoi(dbrow[col]):0; col++; + int OverloadFrames = dbrow[col]?atoi(dbrow[col]):0; col++; + int ExtendAlarmFrames = dbrow[col]?atoi(dbrow[col]):0; col++; + + /* HTML colour code is actually BGR in memory, we want RGB */ + AlarmRGB = rgb_convert(AlarmRGB, ZM_SUBPIX_ORDER_BGR); - Debug( 5, "Parsing polygon %s", Coords ); - Polygon polygon; - if ( !ParsePolygonString( Coords, polygon ) ) { - Error( "Unable to parse polygon string '%s' for zone %d/%s for monitor %s, ignoring", Coords, Id, Name, monitor->Name() ); - continue; + Debug( 5, "Parsing polygon %s", Coords ); + Polygon polygon; + if ( !ParsePolygonString( Coords, polygon ) ) { + Error( "Unable to parse polygon string '%s' for zone %d/%s for monitor %s, ignoring", Coords, Id, Name, monitor->Name() ); + continue; + } + + if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width() + || polygon.LoY() < 0 || polygon.HiY() >= (int)monitor->Height() ) { + Error( "Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), ignoring", Id, Name, monitor->Name(), polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY() ); + continue; + } + + if ( false && !strcmp( Units, "Percent" ) ) + { + MinAlarmPixels = (MinAlarmPixels*polygon.Area())/100; + MaxAlarmPixels = (MaxAlarmPixels*polygon.Area())/100; + MinFilterPixels = (MinFilterPixels*polygon.Area())/100; + MaxFilterPixels = (MaxFilterPixels*polygon.Area())/100; + MinBlobPixels = (MinBlobPixels*polygon.Area())/100; + MaxBlobPixels = (MaxBlobPixels*polygon.Area())/100; + } + + if ( atoi(dbrow[2]) == Zone::INACTIVE ) + { + zones[i] = new Zone( monitor, Id, Name, polygon ); + } + else if ( atoi(dbrow[2]) == Zone::PRIVACY ) + { + zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon ); } - - if ( polygon.LoX() < 0 || polygon.HiX() >= (int)monitor->Width() - || polygon.LoY() < 0 || polygon.HiY() >= (int)monitor->Height() ) { - Error( "Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), ignoring", Id, Name, monitor->Name(), polygon.LoX(), polygon.LoY(), polygon.HiX(), polygon.HiY() ); - continue; - } - - if ( false && !strcmp( Units, "Percent" ) ) - { - MinAlarmPixels = (MinAlarmPixels*polygon.Area())/100; - MaxAlarmPixels = (MaxAlarmPixels*polygon.Area())/100; - MinFilterPixels = (MinFilterPixels*polygon.Area())/100; - MaxFilterPixels = (MaxFilterPixels*polygon.Area())/100; - MinBlobPixels = (MinBlobPixels*polygon.Area())/100; - MaxBlobPixels = (MaxBlobPixels*polygon.Area())/100; - } - - if ( atoi(dbrow[2]) == Zone::INACTIVE ) - { - zones[i] = new Zone( monitor, Id, Name, polygon ); - } - else - { - zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames ); - } - } - if ( mysql_errno( &dbconn ) ) - { - Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - // Yadda yadda - mysql_free_result( result ); - return( n_zones ); + { + zones[i] = new Zone( monitor, Id, Name, (Zone::ZoneType)Type, polygon, AlarmRGB, (Zone::CheckMethod)CheckMethod, MinPixelThreshold, MaxPixelThreshold, MinAlarmPixels, MaxAlarmPixels, Coord( FilterX, FilterY ), MinFilterPixels, MaxFilterPixels, MinBlobPixels, MaxBlobPixels, MinBlobs, MaxBlobs, OverloadFrames, ExtendAlarmFrames ); + } + } + if ( mysql_errno( &dbconn ) ) + { + Error( "Can't fetch row: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + // Yadda yadda + mysql_free_result( result ); + return( n_zones ); } bool Zone::DumpSettings( char *output, bool /*verbose*/ ) { - output[0] = 0; + output[0] = 0; - sprintf( output+strlen(output), " Id : %d\n", id ); - sprintf( output+strlen(output), " Label : %s\n", label ); - sprintf( output+strlen(output), " Type: %d - %s\n", type, - type==ACTIVE?"Active":( - type==INCLUSIVE?"Inclusive":( - type==EXCLUSIVE?"Exclusive":( - type==PRECLUSIVE?"Preclusive":( - type==INACTIVE?"Inactive":"Unknown" - ))))); - sprintf( output+strlen(output), " Shape : %d points\n", polygon.getNumCoords() ); - for ( int i = 0; i < polygon.getNumCoords(); i++ ) - { - sprintf( output+strlen(output), " %i: %d,%d\n", i, polygon.getCoord( i ).X(), polygon.getCoord( i ).Y() ); - } - sprintf( output+strlen(output), " Alarm RGB : %06x\n", alarm_rgb ); - sprintf( output+strlen(output), " Check Method: %d - %s\n", check_method, - check_method==ALARMED_PIXELS?"Alarmed Pixels":( - check_method==FILTERED_PIXELS?"FilteredPixels":( - check_method==BLOBS?"Blobs":"Unknown" - ))); - sprintf( output+strlen(output), " Min Pixel Threshold : %d\n", min_pixel_threshold ); - sprintf( output+strlen(output), " Max Pixel Threshold : %d\n", max_pixel_threshold ); - sprintf( output+strlen(output), " Min Alarm Pixels : %d\n", min_alarm_pixels ); - sprintf( output+strlen(output), " Max Alarm Pixels : %d\n", max_alarm_pixels ); - sprintf( output+strlen(output), " Filter Box : %d,%d\n", filter_box.X(), filter_box.Y() ); - sprintf( output+strlen(output), " Min Filter Pixels : %d\n", min_filter_pixels ); - sprintf( output+strlen(output), " Max Filter Pixels : %d\n", max_filter_pixels ); - sprintf( output+strlen(output), " Min Blob Pixels : %d\n", min_blob_pixels ); - sprintf( output+strlen(output), " Max Blob Pixels : %d\n", max_blob_pixels ); - sprintf( output+strlen(output), " Min Blobs : %d\n", min_blobs ); - sprintf( output+strlen(output), " Max Blobs : %d\n", max_blobs ); - return( true ); + sprintf( output+strlen(output), " Id : %d\n", id ); + sprintf( output+strlen(output), " Label : %s\n", label ); + sprintf( output+strlen(output), " Type: %d - %s\n", type, + type==ACTIVE?"Active":( + type==INCLUSIVE?"Inclusive":( + type==EXCLUSIVE?"Exclusive":( + type==PRECLUSIVE?"Preclusive":( + type==INACTIVE?"Inactive":( + type==PRIVACY?"Privacy":"Unknown" + )))))); + sprintf( output+strlen(output), " Shape : %d points\n", polygon.getNumCoords() ); + for ( int i = 0; i < polygon.getNumCoords(); i++ ) + { + sprintf( output+strlen(output), " %i: %d,%d\n", i, polygon.getCoord( i ).X(), polygon.getCoord( i ).Y() ); + } + sprintf( output+strlen(output), " Alarm RGB : %06x\n", alarm_rgb ); + sprintf( output+strlen(output), " Check Method: %d - %s\n", check_method, + check_method==ALARMED_PIXELS?"Alarmed Pixels":( + check_method==FILTERED_PIXELS?"FilteredPixels":( + check_method==BLOBS?"Blobs":"Unknown" + ))); + sprintf( output+strlen(output), " Min Pixel Threshold : %d\n", min_pixel_threshold ); + sprintf( output+strlen(output), " Max Pixel Threshold : %d\n", max_pixel_threshold ); + sprintf( output+strlen(output), " Min Alarm Pixels : %d\n", min_alarm_pixels ); + sprintf( output+strlen(output), " Max Alarm Pixels : %d\n", max_alarm_pixels ); + sprintf( output+strlen(output), " Filter Box : %d,%d\n", filter_box.X(), filter_box.Y() ); + sprintf( output+strlen(output), " Min Filter Pixels : %d\n", min_filter_pixels ); + sprintf( output+strlen(output), " Max Filter Pixels : %d\n", max_filter_pixels ); + sprintf( output+strlen(output), " Min Blob Pixels : %d\n", min_blob_pixels ); + sprintf( output+strlen(output), " Max Blob Pixels : %d\n", max_blob_pixels ); + sprintf( output+strlen(output), " Min Blobs : %d\n", min_blobs ); + sprintf( output+strlen(output), " Max Blobs : %d\n", max_blobs ); + return( true ); } void Zone::std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum) { - uint32_t pixelsalarmed = 0; - uint32_t pixelsdifference = 0; - uint8_t *pdiff; - const uint8_t *ppoly; - uint8_t calc_max_pixel_threshold = 255; - unsigned int lo_y; - unsigned int hi_y; - unsigned int lo_x; - unsigned int hi_x; - - if(max_pixel_threshold) - calc_max_pixel_threshold = max_pixel_threshold; - - lo_y = polygon.LoY(); - hi_y = polygon.HiY(); - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { - lo_x = ranges[y].lo_x; - hi_x = ranges[y].hi_x; - - Debug( 7, "Checking line %d from %d -> %d", y, lo_x, hi_x ); - pdiff = (uint8_t*)pdiff_image->Buffer( lo_x, y ); - ppoly = ppoly_image->Buffer( lo_x, y ); - - for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) - { - if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) - { - pixelsalarmed++; - pixelsdifference += *pdiff; - *pdiff = WHITE; - } - else - { - *pdiff = BLACK; - } - } - } - - /* Store the results */ - *pixel_count = pixelsalarmed; - *pixel_sum = pixelsdifference; + uint32_t pixelsalarmed = 0; + uint32_t pixelsdifference = 0; + uint8_t *pdiff; + const uint8_t *ppoly; + uint8_t calc_max_pixel_threshold = 255; + unsigned int lo_y; + unsigned int hi_y; + unsigned int lo_x; + unsigned int hi_x; + + if(max_pixel_threshold) + calc_max_pixel_threshold = max_pixel_threshold; + + lo_y = polygon.LoY(); + hi_y = polygon.HiY(); + for ( unsigned int y = lo_y; y <= hi_y; y++ ) + { + lo_x = ranges[y].lo_x; + hi_x = ranges[y].hi_x; + + Debug( 7, "Checking line %d from %d -> %d", y, lo_x, hi_x ); + pdiff = (uint8_t*)pdiff_image->Buffer( lo_x, y ); + ppoly = ppoly_image->Buffer( lo_x, y ); + + for ( unsigned int x = lo_x; x <= hi_x; x++, pdiff++, ppoly++ ) + { + if ( *ppoly && (*pdiff > min_pixel_threshold) && (*pdiff <= calc_max_pixel_threshold) ) + { + pixelsalarmed++; + pixelsdifference += *pdiff; + *pdiff = WHITE; + } + else + { + *pdiff = BLACK; + } + } + } + + /* Store the results */ + *pixel_count = pixelsalarmed; + *pixel_sum = pixelsdifference; Debug( 7, "STORED"); } diff --git a/src/zm_zone.h b/src/zm_zone.h index c431f88a0..9b8a63747 100644 --- a/src/zm_zone.h +++ b/src/zm_zone.h @@ -35,136 +35,141 @@ class Monitor; class Zone { protected: - struct Range - { - int lo_x; - int hi_x; - int off_x; - }; + struct Range + { + int lo_x; + int hi_x; + int off_x; + }; public: - typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE } ZoneType; - typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod; + typedef enum { ACTIVE=1, INCLUSIVE, EXCLUSIVE, PRECLUSIVE, INACTIVE, PRIVACY } ZoneType; + typedef enum { ALARMED_PIXELS=1, FILTERED_PIXELS, BLOBS } CheckMethod; protected: - // Inputs - Monitor *monitor; + // Inputs + Monitor *monitor; - int id; - char *label; - ZoneType type; - Polygon polygon; - Rgb alarm_rgb; - CheckMethod check_method; + int id; + char *label; + ZoneType type; + Polygon polygon; + Rgb alarm_rgb; + CheckMethod check_method; - int min_pixel_threshold; - int max_pixel_threshold; + int min_pixel_threshold; + int max_pixel_threshold; - int min_alarm_pixels; - int max_alarm_pixels; + int min_alarm_pixels; + int max_alarm_pixels; - Coord filter_box; - int min_filter_pixels; - int max_filter_pixels; + Coord filter_box; + int min_filter_pixels; + int max_filter_pixels; - int min_blob_pixels; - int max_blob_pixels; - int min_blobs; - int max_blobs; + int min_blob_pixels; + int max_blob_pixels; + int min_blobs; + int max_blobs; - int overload_frames; - int extend_alarm_frames; + int overload_frames; + int extend_alarm_frames; - // Outputs/Statistics - bool alarmed; - int pixel_diff; - unsigned int alarm_pixels; - int alarm_filter_pixels; - int alarm_blob_pixels; - int alarm_blobs; - int min_blob_size; - int max_blob_size; - Box alarm_box; - Coord alarm_centre; - unsigned int score; - Image *pg_image; - Range *ranges; - Image *image; + // Outputs/Statistics + bool alarmed; + int pixel_diff; + unsigned int alarm_pixels; + int alarm_filter_pixels; + int alarm_blob_pixels; + int alarm_blobs; + int min_blob_size; + int max_blob_size; + Box alarm_box; + Coord alarm_centre; + unsigned int score; + Image *pg_image; + Range *ranges; + Image *image; - int overload_count; - int extend_alarm_count; + int overload_count; + int extend_alarm_count; protected: - void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames ); - void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum); - + void Setup( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold, int p_max_pixel_threshold, int p_min_alarm_pixels, int p_max_alarm_pixels, const Coord &p_filter_box, int p_min_filter_pixels, int p_max_filter_pixels, int p_min_blob_pixels, int p_max_blob_pixels, int p_min_blobs, int p_max_blobs, int p_overload_frames, int p_extend_alarm_frames ); + void std_alarmedpixels(Image* pdiff_image, const Image* ppoly_image, unsigned int* pixel_count, unsigned int* pixel_sum); + public: - Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 ) - { - Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames ); - } - Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0) - { - Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames ); - } - Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon ) - { - Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 ); - } + Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0 ) + { + Setup( p_monitor, p_id, p_label, p_type, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames ); + } + Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon, const Rgb p_alarm_rgb, CheckMethod p_check_method, int p_min_pixel_threshold=15, int p_max_pixel_threshold=0, int p_min_alarm_pixels=50, int p_max_alarm_pixels=75000, const Coord &p_filter_box=Coord( 3, 3 ), int p_min_filter_pixels=50, int p_max_filter_pixels=50000, int p_min_blob_pixels=10, int p_max_blob_pixels=0, int p_min_blobs=0, int p_max_blobs=0, int p_overload_frames=0, int p_extend_alarm_frames=0) + { + Setup( p_monitor, p_id, p_label, Zone::ACTIVE, p_polygon, p_alarm_rgb, p_check_method, p_min_pixel_threshold, p_max_pixel_threshold, p_min_alarm_pixels, p_max_alarm_pixels, p_filter_box, p_min_filter_pixels, p_max_filter_pixels, p_min_blob_pixels, p_max_blob_pixels, p_min_blobs, p_max_blobs, p_overload_frames, p_extend_alarm_frames ); + } + Zone( Monitor *p_monitor, int p_id, const char *p_label, const Polygon &p_polygon ) + { + Setup( p_monitor, p_id, p_label, Zone::INACTIVE, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 ); + } + Zone( Monitor *p_monitor, int p_id, const char *p_label, ZoneType p_type, const Polygon &p_polygon ) + { + Setup( p_monitor, p_id, p_label, p_type, p_polygon, RGB_BLACK, (Zone::CheckMethod)0, 0, 0, 0, 0, Coord( 0, 0 ), 0, 0, 0, 0, 0, 0, 0, 0 ); + } public: - ~Zone(); + ~Zone(); - inline int Id() const { return( id ); } - inline const char *Label() const { return( label ); } - inline ZoneType Type() const { return( type ); } - inline bool IsActive() const { return( type == ACTIVE ); } - inline bool IsInclusive() const { return( type == INCLUSIVE ); } - inline bool IsExclusive() const { return( type == EXCLUSIVE ); } - inline bool IsPreclusive() const { return( type == PRECLUSIVE ); } - inline bool IsInactive() const { return( type == INACTIVE ); } - inline const Image *AlarmImage() const { return( image ); } - inline const Polygon &GetPolygon() const { return( polygon ); } - inline bool Alarmed() const { return( alarmed ); } - inline void SetAlarm() { alarmed = true; } - inline void ClearAlarm() { alarmed = false; } - inline Coord GetAlarmCentre() const { return( alarm_centre ); } - inline unsigned int Score() const { return( score ); } + inline int Id() const { return( id ); } + inline const char *Label() const { return( label ); } + inline ZoneType Type() const { return( type ); } + inline bool IsActive() const { return( type == ACTIVE ); } + inline bool IsInclusive() const { return( type == INCLUSIVE ); } + inline bool IsExclusive() const { return( type == EXCLUSIVE ); } + inline bool IsPreclusive() const { return( type == PRECLUSIVE ); } + inline bool IsInactive() const { return( type == INACTIVE ); } + inline bool IsPrivacy() const { return( type == PRIVACY ); } + inline const Image *AlarmImage() const { return( image ); } + inline const Polygon &GetPolygon() const { return( polygon ); } + inline bool Alarmed() const { return( alarmed ); } + inline void SetAlarm() { alarmed = true; } + inline void ClearAlarm() { alarmed = false; } + inline Coord GetAlarmCentre() const { return( alarm_centre ); } + inline unsigned int Score() const { return( score ); } - inline void ResetStats() - { - alarmed = false; - pixel_diff = 0; - alarm_pixels = 0; - alarm_filter_pixels = 0; - alarm_blob_pixels = 0; - alarm_blobs = 0; - min_blob_size = 0; - max_blob_size = 0; - score = 0; - } - void RecordStats( const Event *event ); - bool CheckAlarms( const Image *delta_image ); - bool DumpSettings( char *output, bool verbose ); + inline void ResetStats() + { + alarmed = false; + pixel_diff = 0; + alarm_pixels = 0; + alarm_filter_pixels = 0; + alarm_blob_pixels = 0; + alarm_blobs = 0; + min_blob_size = 0; + max_blob_size = 0; + score = 0; + } + void RecordStats( const Event *event ); + bool CheckAlarms( const Image *delta_image ); + bool DumpSettings( char *output, bool verbose ); - static bool ParsePolygonString( const char *polygon_string, Polygon &polygon ); - static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ); - static int Load( Monitor *monitor, Zone **&zones ); - //================================================= - bool CheckOverloadCount(); - int GetOverloadCount(); - void SetOverloadCount(int nOverCount); - int GetOverloadFrames(); - //================================================= - bool CheckExtendAlarmCount(); - int GetExtendAlarmCount(); - void SetExtendAlarmCount(int nOverCount); - int GetExtendAlarmFrames(); - void SetScore(unsigned int nScore); - void SetAlarmImage(const Image* srcImage); + static bool ParsePolygonString( const char *polygon_string, Polygon &polygon ); + static bool ParseZoneString( const char *zone_string, int &zone_id, int &colour, Polygon &polygon ); + static int Load( Monitor *monitor, Zone **&zones ); + //================================================= + bool CheckOverloadCount(); + int GetOverloadCount(); + void SetOverloadCount(int nOverCount); + int GetOverloadFrames(); + //================================================= + bool CheckExtendAlarmCount(); + int GetExtendAlarmCount(); + void SetExtendAlarmCount(int nOverCount); + int GetExtendAlarmFrames(); + void SetScore(unsigned int nScore); + void SetAlarmImage(const Image* srcImage); - inline const Image *getPgImage() const { return( pg_image ); } - inline const Range *getRanges() const { return( ranges ); } + inline const Image *getPgImage() const { return( pg_image ); } + inline const Range *getRanges() const { return( ranges ); } }; diff --git a/src/zma.cpp b/src/zma.cpp index 9a6658e54..03ffe1726 100644 --- a/src/zma.cpp +++ b/src/zma.cpp @@ -17,6 +17,38 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +/* + +=head1 NAME + +zma - The ZoneMinder Analysis daemon + +=head1 SYNOPSIS + + zma -m + zma --monitor + zma -h + zma --help + zma -v + zma --version + +=head1 DESCRIPTION + +This is the component that goes through the captured frames and checks them +for motion which might generate an alarm or event. It generally keeps up with +the Capture daemon but if very busy may skip some frames to prevent it falling +behind. + +=head1 OPTIONS + + -m, --monitor_id - ID of the monitor to analyse + -h, --help - Display usage information + -v, --version - Print the installed version of ZoneMinder + +=cut + +*/ + #include #include @@ -27,115 +59,146 @@ void Usage() { - fprintf( stderr, "zma -m \n" ); - fprintf( stderr, "Options:\n" ); - fprintf( stderr, " -m, --monitor : Specify which monitor to use\n" ); - fprintf( stderr, " -h, --help : This screen\n" ); - exit( 0 ); + fprintf( stderr, "zma -m \n" ); + fprintf( stderr, "Options:\n" ); + fprintf( stderr, " -m, --monitor : Specify which monitor to use\n" ); + fprintf( stderr, " -h, --help : This screen\n" ); + fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" ); + exit( 0 ); } int main( int argc, char *argv[] ) { - self = argv[0]; + self = argv[0]; - srand( getpid() * time( 0 ) ); + srand( getpid() * time( 0 ) ); - int id = -1; + int id = -1; - static struct option long_options[] = { - {"monitor", 1, 0, 'm'}, - {"help", 0, 0, 'h'}, - {0, 0, 0, 0} - }; + static struct option long_options[] = { + {"monitor", 1, 0, 'm'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'v'}, + {0, 0, 0, 0} + }; - while (1) - { - int option_index = 0; + while (1) + { + int option_index = 0; - int c = getopt_long (argc, argv, "m:h", long_options, &option_index); - if (c == -1) - { - break; - } + int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index); + if (c == -1) + { + break; + } - switch (c) - { - case 'm': - id = atoi(optarg); - break; - case 'h': - case '?': - Usage(); - break; - default: - //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); - break; - } - } + switch (c) + { + case 'm': + id = atoi(optarg); + break; + case 'h': + case '?': + Usage(); + break; + case 'v': + std::cout << ZM_VERSION << "\n"; + exit(0); + default: + //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); + break; + } + } - if (optind < argc) - { - fprintf( stderr, "Extraneous options, " ); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - Usage(); - } + if (optind < argc) + { + fprintf( stderr, "Extraneous options, " ); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + Usage(); + } - if ( id < 0 ) - { - fprintf( stderr, "Bogus monitor %d\n", id ); - Usage(); - exit( 0 ); - } + if ( id < 0 ) + { + fprintf( stderr, "Bogus monitor %d\n", id ); + Usage(); + exit( 0 ); + } - char log_id_string[16]; - snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id ); + char log_id_string[16]; + snprintf( log_id_string, sizeof(log_id_string), "zma_m%d", id ); - zmLoadConfig(); + zmLoadConfig(); - logInit( log_id_string ); - - ssedetect(); + logInit( log_id_string ); + + ssedetect(); - Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS ); + Monitor *monitor = Monitor::Load( id, true, Monitor::ANALYSIS ); - if ( monitor ) - { - Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() ); + if ( monitor ) + { + Info( "In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled() ); - if ( config.opt_frame_server ) - { - Event::OpenFrameSocket( monitor->Id() ); - } + if ( config.opt_frame_server ) + { + Event::OpenFrameSocket( monitor->Id() ); + } - zmSetDefaultHupHandler(); - zmSetDefaultTermHandler(); - zmSetDefaultDieHandler(); + zmSetDefaultHupHandler(); + zmSetDefaultTermHandler(); + zmSetDefaultDieHandler(); - sigset_t block_set; - sigemptyset( &block_set ); + sigset_t block_set; + sigemptyset( &block_set ); - while( !zm_terminate ) - { - // Process the next image - sigprocmask( SIG_BLOCK, &block_set, 0 ); - if ( !monitor->Analyse() ) - { - usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE ); - } - if ( zm_reload ) - { - monitor->Reload(); - zm_reload = false; - } - sigprocmask( SIG_UNBLOCK, &block_set, 0 ); - } - delete monitor; - } - else - { - fprintf( stderr, "Can't find monitor with id of %d\n", id ); - } - return( 0 ); + useconds_t analysis_rate = monitor->GetAnalysisRate(); + unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay(); + time_t last_analysis_update_time, cur_time; + monitor->UpdateAdaptiveSkip(); + last_analysis_update_time = time( 0 ); + + while( !zm_terminate ) + { + // Process the next image + sigprocmask( SIG_BLOCK, &block_set, 0 ); + + // Some periodic updates are required for variable capturing framerate + if ( analysis_update_delay ) + { + cur_time = time( 0 ); + if ( ( cur_time - last_analysis_update_time ) > analysis_update_delay ) + { + analysis_rate = monitor->GetAnalysisRate(); + monitor->UpdateAdaptiveSkip(); + last_analysis_update_time = cur_time; + } + } + + if ( !monitor->Analyse() ) + { + usleep( monitor->Active()?ZM_SAMPLE_RATE:ZM_SUSPENDED_RATE ); + } + else if ( analysis_rate ) + { + usleep( analysis_rate ); + } + + if ( zm_reload ) + { + monitor->Reload(); + zm_reload = false; + } + sigprocmask( SIG_UNBLOCK, &block_set, 0 ); + } + delete monitor; + } + else + { + fprintf( stderr, "Can't find monitor with id of %d\n", id ); + } + logTerm(); + zmDbClose(); + return( 0 ); } diff --git a/src/zmc.cpp b/src/zmc.cpp index d84d30505..42b6b7aff 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -17,9 +17,55 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +/* + +=head1 NAME + +zmc - The ZoneMinder Capture daemon + +=head1 SYNOPSIS + + zmc -d + zmc --device + zmc -r -H -P -p + zmc -f + zmc --file + zmc -m + zmc --monitor + zmc -h + zmc --help + zmc -v + zmc --version + +=head1 DESCRIPTION + +This binary's job is to sit on a video device and suck frames off it as fast as +possible, this should run at more or less constant speed. + +=head1 OPTIONS + + -d, --device - For local cameras, device to access. e.g /dev/video0 etc + -r -H -P -p - For remote cameras + -f, --file - For local images, jpg file to access. + -m, --monitor_id - ID of the monitor to analyse + -h, --help - Display usage information + -v, --version - Print the installed version of ZoneMinder + +=cut + +*/ + #include #include +#if defined(__FreeBSD__) +#include +#else #include +#endif + +#if !defined(MAXINT) +#define MAXINT INT_MAX +#endif #include "zm.h" #include "zm_db.h" @@ -29,278 +75,290 @@ void Usage() { - fprintf( stderr, "zmc -d or -r -H -P -p or -f or -m \n" ); + fprintf( stderr, "zmc -d or -r -H -P -p or -f or -m \n" ); - fprintf( stderr, "Options:\n" ); - fprintf( stderr, " -d, --device : For local cameras, device to access. E.g /dev/video0 etc\n" ); - fprintf( stderr, " -r -H -P -p : For remote cameras\n" ); - fprintf( stderr, " -f, --file : For local images, jpg file to access.\n" ); - fprintf( stderr, " -m, --monitor : For sources associated with a single monitor\n" ); - fprintf( stderr, " -h, --help : This screen\n" ); - exit( 0 ); + fprintf( stderr, "Options:\n" ); +#if defined(BSD) + fprintf( stderr, " -d, --device : For local cameras, device to access. E.g /dev/bktr0 etc\n" ); +#else + fprintf( stderr, " -d, --device : For local cameras, device to access. E.g /dev/video0 etc\n" ); +#endif + fprintf( stderr, " -r -H -P -p : For remote cameras\n" ); + fprintf( stderr, " -f, --file : For local images, jpg file to access.\n" ); + fprintf( stderr, " -m, --monitor : For sources associated with a single monitor\n" ); + fprintf( stderr, " -h, --help : This screen\n" ); + fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" ); + exit( 0 ); } int main( int argc, char *argv[] ) { - self = argv[0]; + self = argv[0]; - srand( getpid() * time( 0 ) ); + srand( getpid() * time( 0 ) ); - const char *device = ""; - const char *protocol = ""; - const char *host = ""; - const char *port = ""; - const char *path = ""; - const char *file = ""; - int monitor_id = -1; + const char *device = ""; + const char *protocol = ""; + const char *host = ""; + const char *port = ""; + const char *path = ""; + const char *file = ""; + int monitor_id = -1; - static struct option long_options[] = { - {"device", 1, 0, 'd'}, - {"protocol", 1, 0, 'r'}, - {"host", 1, 0, 'H'}, - {"port", 1, 0, 'P'}, - {"path", 1, 0, 'p'}, - {"file", 1, 0, 'f'}, - {"monitor", 1, 0, 'm'}, - {"help", 0, 0, 'h'}, - {0, 0, 0, 0} - }; + static struct option long_options[] = { + {"device", 1, 0, 'd'}, + {"protocol", 1, 0, 'r'}, + {"host", 1, 0, 'H'}, + {"port", 1, 0, 'P'}, + {"path", 1, 0, 'p'}, + {"file", 1, 0, 'f'}, + {"monitor", 1, 0, 'm'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'v'}, + {0, 0, 0, 0} + }; - while (1) - { - int option_index = 0; + while (1) + { + int option_index = 0; - int c = getopt_long (argc, argv, "d:H:P:p:f:m:h", long_options, &option_index); - if (c == -1) - { - break; - } + int c = getopt_long (argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index); + if (c == -1) + { + break; + } - switch (c) - { - case 'd': - device = optarg; - break; - case 'H': - host = optarg; - break; - case 'P': - port = optarg; - break; - case 'p': - path = optarg; - break; - case 'f': - file = optarg; - break; - case 'm': - monitor_id = atoi(optarg); - break; - case 'h': - case '?': - Usage(); - break; - default: - //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); - break; - } - } + switch (c) + { + case 'd': + device = optarg; + break; + case 'H': + host = optarg; + break; + case 'P': + port = optarg; + break; + case 'p': + path = optarg; + break; + case 'f': + file = optarg; + break; + case 'm': + monitor_id = atoi(optarg); + break; + case 'h': + case '?': + Usage(); + break; + case 'v': + std::cout << ZM_VERSION << "\n"; + exit(0); + default: + //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); + break; + } + } - if (optind < argc) - { - fprintf( stderr, "Extraneous options, " ); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - Usage(); - } + if (optind < argc) + { + fprintf( stderr, "Extraneous options, " ); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + Usage(); + } - int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) ); - if ( modes > 1 ) - { - fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" ); - Usage(); - exit( 0 ); - } + int modes = ( device[0]?1:0 + host[0]?1:0 + file[0]?1:0 + (monitor_id>0?1:0) ); + if ( modes > 1 ) + { + fprintf( stderr, "Only one of device, host/port/path, file or monitor id allowed\n" ); + Usage(); + exit( 0 ); + } - if ( modes < 1 ) - { - fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" ); - Usage(); - exit( 0 ); - } + if ( modes < 1 ) + { + fprintf( stderr, "One of device, host/port/path, file or monitor id must be specified\n" ); + Usage(); + exit( 0 ); + } - char log_id_string[32] = ""; - if ( device[0] ) - { - const char *slash_ptr = strrchr( device, '/' ); - snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device ); - } - else if ( host[0] ) - { - snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host ); - } - else if ( file[0] ) - { - const char *slash_ptr = strrchr( file, '/' ); - snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file ); - } - else - { - snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id ); - } + char log_id_string[32] = ""; + if ( device[0] ) + { + const char *slash_ptr = strrchr( device, '/' ); + snprintf( log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device ); + } + else if ( host[0] ) + { + snprintf( log_id_string, sizeof(log_id_string), "zmc_h%s", host ); + } + else if ( file[0] ) + { + const char *slash_ptr = strrchr( file, '/' ); + snprintf( log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file ); + } + else + { + snprintf( log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id ); + } - zmLoadConfig(); + zmLoadConfig(); - logInit( log_id_string ); - - ssedetect(); + logInit( log_id_string ); + + ssedetect(); - Monitor **monitors = 0; - int n_monitors = 0; + Monitor **monitors = 0; + int n_monitors = 0; #if ZM_HAS_V4L - if ( device[0] ) - { - n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE ); - } - else + if ( device[0] ) + { + n_monitors = Monitor::LoadLocalMonitors( device, monitors, Monitor::CAPTURE ); + } + else #endif // ZM_HAS_V4L - if ( host[0] ) - { - if ( !port ) - port = "80"; - n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE ); - } - else if ( file[0] ) - { - n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE ); - } - else - { - Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE ); - if ( monitor ) - { - monitors = new Monitor *[1]; - monitors[0] = monitor; - n_monitors = 1; - } - } + if ( host[0] ) + { + if ( !port ) + port = "80"; + n_monitors = Monitor::LoadRemoteMonitors( protocol, host, port, path, monitors, Monitor::CAPTURE ); + } + else if ( file[0] ) + { + n_monitors = Monitor::LoadFileMonitors( file, monitors, Monitor::CAPTURE ); + } + else + { + Monitor *monitor = Monitor::Load( monitor_id, true, Monitor::CAPTURE ); + if ( monitor ) + { + monitors = new Monitor *[1]; + monitors[0] = monitor; + n_monitors = 1; + } + } - if ( !n_monitors ) - { - Error( "No monitors found" ); - exit ( -1 ); - } + if ( !n_monitors ) + { + Error( "No monitors found" ); + exit ( -1 ); + } - Info( "Starting Capture" ); + Info( "Starting Capture version %s", ZM_VERSION ); - zmSetDefaultTermHandler(); - zmSetDefaultDieHandler(); + zmSetDefaultTermHandler(); + zmSetDefaultDieHandler(); - sigset_t block_set; - sigemptyset( &block_set ); + sigset_t block_set; + sigemptyset( &block_set ); - sigaddset( &block_set, SIGUSR1 ); - sigaddset( &block_set, SIGUSR2 ); + sigaddset( &block_set, SIGUSR1 ); + sigaddset( &block_set, SIGUSR2 ); - if ( monitors[0]->PrimeCapture() < 0 ) - { - Error( "Failed to prime capture of initial monitor" ); - exit( -1 ); - } + if ( monitors[0]->PrimeCapture() < 0 ) + { + Error( "Failed to prime capture of initial monitor" ); + exit( -1 ); + } - long *capture_delays = new long[n_monitors]; - long *alarm_capture_delays = new long[n_monitors]; - long *next_delays = new long[n_monitors]; - struct timeval * last_capture_times = new struct timeval[n_monitors]; - for ( int i = 0; i < n_monitors; i++ ) - { - last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; - capture_delays[i] = monitors[i]->GetCaptureDelay(); - alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); - } + long *capture_delays = new long[n_monitors]; + long *alarm_capture_delays = new long[n_monitors]; + long *next_delays = new long[n_monitors]; + struct timeval * last_capture_times = new struct timeval[n_monitors]; + for ( int i = 0; i < n_monitors; i++ ) + { + last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0; + capture_delays[i] = monitors[i]->GetCaptureDelay(); + alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay(); + } - int result = 0; - struct timeval now; - struct DeltaTimeval delta_time; - while( !zm_terminate ) - { - sigprocmask( SIG_BLOCK, &block_set, 0 ); - for ( int i = 0; i < n_monitors; i++ ) - { - long min_delay = MAXINT; + int result = 0; + struct timeval now; + struct DeltaTimeval delta_time; + while( !zm_terminate ) + { + sigprocmask( SIG_BLOCK, &block_set, 0 ); + for ( int i = 0; i < n_monitors; i++ ) + { + long min_delay = MAXINT; - gettimeofday( &now, NULL ); - for ( int j = 0; j < n_monitors; j++ ) - { - if ( last_capture_times[j].tv_sec ) - { - DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 ); - if ( monitors[i]->GetState() == Monitor::ALARM ) - next_delays[j] = alarm_capture_delays[j]-delta_time.delta; - else - next_delays[j] = capture_delays[j]-delta_time.delta; - if ( next_delays[j] < 0 ) - next_delays[j] = 0; - } - else - { - next_delays[j] = 0; - } - if ( next_delays[j] <= min_delay ) - { - min_delay = next_delays[j]; - } - } + gettimeofday( &now, NULL ); + for ( int j = 0; j < n_monitors; j++ ) + { + if ( last_capture_times[j].tv_sec ) + { + DELTA_TIMEVAL( delta_time, now, last_capture_times[j], DT_PREC_3 ); + if ( monitors[i]->GetState() == Monitor::ALARM ) + next_delays[j] = alarm_capture_delays[j]-delta_time.delta; + else + next_delays[j] = capture_delays[j]-delta_time.delta; + if ( next_delays[j] < 0 ) + next_delays[j] = 0; + } + else + { + next_delays[j] = 0; + } + if ( next_delays[j] <= min_delay ) + { + min_delay = next_delays[j]; + } + } - if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) - { - if ( monitors[i]->PreCapture() < 0 ) - { - Error( "Failed to pre-capture monitor %d (%d/%d)", monitors[i]->Id(), i, n_monitors ); - zm_terminate = true; - result = -1; - break; - } - if ( monitors[i]->Capture() < 0 ) - { - Error( "Failed to capture image from monitor %d (%d/%d)", monitors[i]->Id(), i, n_monitors ); - zm_terminate = true; - result = -1; - break; - } - if ( monitors[i]->PostCapture() < 0 ) - { - Error( "Failed to post-capture monitor %d (%d/%d)", monitors[i]->Id(), i, n_monitors ); - zm_terminate = true; - result = -1; - break; - } + if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) + { + if ( monitors[i]->PreCapture() < 0 ) + { + Error( "Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors ); + zm_terminate = true; + result = -1; + break; + } + if ( monitors[i]->Capture() < 0 ) + { + Error( "Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors ); + zm_terminate = true; + result = -1; + break; + } + if ( monitors[i]->PostCapture() < 0 ) + { + Error( "Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors ); + zm_terminate = true; + result = -1; + break; + } - if ( next_delays[i] > 0 ) - { - gettimeofday( &now, NULL ); - DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 ); - long sleep_time = next_delays[i]-delta_time.delta; - if ( sleep_time > 0 ) - { - usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) ); - } - } - gettimeofday( &(last_capture_times[i]), NULL ); - } - } - sigprocmask( SIG_UNBLOCK, &block_set, 0 ); - } - for ( int i = 0; i < n_monitors; i++ ) - { - delete monitors[i]; - } - delete monitors; - delete [] alarm_capture_delays; - delete [] capture_delays; - delete [] next_delays; - delete [] last_capture_times; + if ( next_delays[i] > 0 ) + { + gettimeofday( &now, NULL ); + DELTA_TIMEVAL( delta_time, now, last_capture_times[i], DT_PREC_3 ); + long sleep_time = next_delays[i]-delta_time.delta; + if ( sleep_time > 0 ) + { + usleep( sleep_time*(DT_MAXGRAN/DT_PREC_3) ); + } + } + gettimeofday( &(last_capture_times[i]), NULL ); + } + } + sigprocmask( SIG_UNBLOCK, &block_set, 0 ); + } + for ( int i = 0; i < n_monitors; i++ ) + { + delete monitors[i]; + } + delete [] monitors; + delete [] alarm_capture_delays; + delete [] capture_delays; + delete [] next_delays; + delete [] last_capture_times; - return( result ); + logTerm(); + zmDbClose(); + + return( result ); } diff --git a/src/zmf.cpp b/src/zmf.cpp index b10092a5c..a739927f1 100644 --- a/src/zmf.cpp +++ b/src/zmf.cpp @@ -17,6 +17,39 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +/* + +=head1 NAME + +zmf - The ZoneMinder Frame daemon + +=head1 SYNOPSIS + + zmf -m + zmf --monitor + zmf -h + zmf --help + zmf -v + zmf --version + +=head1 DESCRIPTION + +This is an optional daemon that can run in concert with the Analysis daemon and +whose function it is to actually write captured frames to disk. This frees up +the Analysis daemon to do more analysis (!) and so keep up with the Capture +daemon better. If it isn't running or dies then the Analysis daemon just writes +them itself. + +=head1 OPTIONS + + -m, --monitor_id - ID of the monitor to use + -h, --help - Display usage information + -v, --version - Print the installed version of ZoneMinder + +=cut + +*/ + #include #include #include @@ -40,271 +73,278 @@ int OpenSocket( int monitor_id ) { - int sd = socket( AF_UNIX, SOCK_STREAM, 0); - if ( sd < 0 ) - { - Error( "Can't create socket: %s", strerror(errno) ); - return( -1 ); - } + int sd = socket( AF_UNIX, SOCK_STREAM, 0); + if ( sd < 0 ) + { + Error( "Can't create socket: %s", strerror(errno) ); + return( -1 ); + } - char sock_path[PATH_MAX] = ""; - snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); - if ( unlink( sock_path ) < 0 ) - { - Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) ); - } + char sock_path[PATH_MAX] = ""; + snprintf( sock_path, sizeof(sock_path), "%s/zmf-%d.sock", config.path_socks, monitor_id ); + if ( unlink( sock_path ) < 0 ) + { + Warning( "Can't unlink '%s': %s", sock_path, strerror(errno) ); + } - struct sockaddr_un addr; + struct sockaddr_un addr; - strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); - addr.sun_family = AF_UNIX; + strncpy( addr.sun_path, sock_path, sizeof(addr.sun_path) ); + addr.sun_family = AF_UNIX; - if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) - { - Error( "Can't bind: %s", strerror(errno) ); - exit( -1 ); - } + if ( bind( sd, (struct sockaddr *)&addr, strlen(addr.sun_path)+sizeof(addr.sun_family)) < 0 ) + { + Error( "Can't bind: %s", strerror(errno) ); + exit( -1 ); + } - if ( listen( sd, SOMAXCONN ) < 0 ) - { - Error( "Can't listen: %s", strerror(errno) ); - return( -1 ); - } + if ( listen( sd, SOMAXCONN ) < 0 ) + { + Error( "Can't listen: %s", strerror(errno) ); + return( -1 ); + } - struct sockaddr_un rem_addr; - socklen_t rem_addr_len = sizeof(rem_addr); - int new_sd = -1; - if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 ) - { - Error( "Can't accept: %s", strerror(errno) ); - exit( -1 ); - } - close( sd ); + struct sockaddr_un rem_addr; + socklen_t rem_addr_len = sizeof(rem_addr); + int new_sd = -1; + if ( (new_sd = accept( sd, (struct sockaddr *)&rem_addr, &rem_addr_len )) < 0 ) + { + Error( "Can't accept: %s", strerror(errno) ); + exit( -1 ); + } + close( sd ); - sd = new_sd; + sd = new_sd; - Info( "Frame server socket open, awaiting images" ); - return( sd ); + Info( "Frame server socket open, awaiting images" ); + return( sd ); } int ReopenSocket( int &sd, int monitor_id ) { - close( sd ); - return( sd = OpenSocket( monitor_id ) ); + close( sd ); + return( sd = OpenSocket( monitor_id ) ); } void Usage() { - fprintf( stderr, "zmf -m \n" ); - fprintf( stderr, "Options:\n" ); - fprintf( stderr, " -m, --monitor : Specify which monitor to use\n" ); - fprintf( stderr, " -h, --help : This screen\n" ); - exit( 0 ); + fprintf( stderr, "zmf -m \n" ); + fprintf( stderr, "Options:\n" ); + fprintf( stderr, " -m, --monitor : Specify which monitor to use\n" ); + fprintf( stderr, " -h, --help : This screen\n" ); + fprintf( stderr, " -v, --version : Report the installed version of ZoneMinder\n" ); + exit( 0 ); } int main( int argc, char *argv[] ) { - self = argv[0]; + self = argv[0]; - srand( getpid() * time( 0 ) ); + srand( getpid() * time( 0 ) ); - int id = -1; + int id = -1; - static struct option long_options[] = { - {"monitor", 1, 0, 'm'}, - {"help", 0, 0, 'h'}, - {0, 0, 0, 0} - }; + static struct option long_options[] = { + {"monitor", 1, 0, 'm'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'v'}, + {0, 0, 0, 0} + }; - while (1) - { - int option_index = 0; + while (1) + { + int option_index = 0; - int c = getopt_long (argc, argv, "m:h", long_options, &option_index); - if (c == -1) - { - break; - } + int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index); + if (c == -1) + { + break; + } - switch (c) - { - case 'm': - id = atoi(optarg); - break; - case 'h': - case '?': - Usage(); - break; - default: - //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); - break; - } - } + switch (c) + { + case 'm': + id = atoi(optarg); + break; + case 'h': + case '?': + Usage(); + break; + case 'v': + std::cout << ZM_VERSION << "\n"; + exit(0); + default: + //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); + break; + } + } - if (optind < argc) - { - fprintf( stderr, "Extraneous options, " ); - while (optind < argc) - printf ("%s ", argv[optind++]); - printf ("\n"); - Usage(); - } + if (optind < argc) + { + fprintf( stderr, "Extraneous options, " ); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + Usage(); + } - if ( id < 0 ) - { - fprintf( stderr, "Bogus monitor %d\n", id ); - Usage(); - exit( 0 ); - } + if ( id < 0 ) + { + fprintf( stderr, "Bogus monitor %d\n", id ); + Usage(); + exit( 0 ); + } - char log_id_string[16]; - snprintf( log_id_string, sizeof(log_id_string), "m%d", id ); + char log_id_string[16]; + snprintf( log_id_string, sizeof(log_id_string), "m%d", id ); - zmLoadConfig(); + zmLoadConfig(); - logInit( "zmf" ); - - ssedetect(); + logInit( "zmf" ); + + ssedetect(); - Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY ); + Monitor *monitor = Monitor::Load( id, false, Monitor::QUERY ); - if ( !monitor ) - { - fprintf( stderr, "Can't find monitor with id of %d\n", id ); - exit( -1 ); - } + if ( !monitor ) + { + fprintf( stderr, "Can't find monitor with id of %d\n", id ); + exit( -1 ); + } - char capt_path[PATH_MAX]; - char anal_path[PATH_MAX]; - snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); - snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); - zmSetDefaultTermHandler(); - zmSetDefaultDieHandler(); + char capt_path[PATH_MAX]; + char anal_path[PATH_MAX]; + snprintf( capt_path, sizeof(capt_path), "%s/%d/%%s/%%0%dd-capture.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); + snprintf( anal_path, sizeof(anal_path), "%s/%d/%%s/%%0%dd-analyse.jpg", config.dir_events, monitor->Id(), config.event_image_digits ); + zmSetDefaultTermHandler(); + zmSetDefaultDieHandler(); - sigset_t block_set; - sigemptyset( &block_set ); + sigset_t block_set; + sigemptyset( &block_set ); - int sd = OpenSocket( monitor->Id() ); + int sd = OpenSocket( monitor->Id() ); - FrameHeader frame_header = { 0, 0, false, 0 }; - //unsigned char *image_data = 0; + FrameHeader frame_header = { 0, 0, false, 0 }; + //unsigned char *image_data = 0; - fd_set rfds; + fd_set rfds; - struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 0; - while( 1 ) - { - struct timeval temp_timeout = timeout; + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + while( 1 ) + { + struct timeval temp_timeout = timeout; - FD_ZERO(&rfds); - FD_SET(sd, &rfds); - int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); - if( n_found == 0 ) - { - Debug( 1, "Select timed out" ); - continue; - } - else if ( n_found < 0) - { - Error( "Select error: %s", strerror(errno) ); - ReopenSocket( sd, monitor->Id() ); - continue; - } + FD_ZERO(&rfds); + FD_SET(sd, &rfds); + int n_found = select( sd+1, &rfds, NULL, NULL, &temp_timeout ); + if( n_found == 0 ) + { + Debug( 1, "Select timed out" ); + continue; + } + else if ( n_found < 0) + { + Error( "Select error: %s", strerror(errno) ); + ReopenSocket( sd, monitor->Id() ); + continue; + } - sigprocmask( SIG_BLOCK, &block_set, 0 ); + sigprocmask( SIG_BLOCK, &block_set, 0 ); - int n_bytes = read( sd, &frame_header, sizeof(frame_header) ); - if ( n_bytes != sizeof(frame_header) ) - { - if ( n_bytes < 0 ) - { - Error( "Can't read frame header: %s", strerror(errno) ); - } - else if ( n_bytes > 0 ) - { - Error( "Incomplete read of frame header, %d bytes only", n_bytes ); - } - else - { - Warning( "Socket closed at remote end" ); - } - ReopenSocket( sd, monitor->Id() ); - continue; - } - Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length ); - static unsigned char image_data[ZM_MAX_IMAGE_SIZE]; + int n_bytes = read( sd, &frame_header, sizeof(frame_header) ); + if ( n_bytes != sizeof(frame_header) ) + { + if ( n_bytes < 0 ) + { + Error( "Can't read frame header: %s", strerror(errno) ); + } + else if ( n_bytes > 0 ) + { + Error( "Incomplete read of frame header, %d bytes only", n_bytes ); + } + else + { + Warning( "Socket closed at remote end" ); + } + ReopenSocket( sd, monitor->Id() ); + continue; + } + Debug( 1, "Read frame header, expecting %ld bytes of image", frame_header.image_length ); + static unsigned char image_data[ZM_MAX_IMAGE_SIZE]; - // Read for pipe and loop until bytes expected have been read or an error occures - int bytes_read = 0; - do - { - n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read ); - if (n_bytes < 0) break; // break on error - if (n_bytes < (int)frame_header.image_length) - { - // print some informational messages - if (bytes_read == 0) - { - Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length); - } - else if (bytes_read+n_bytes == (int)frame_header.image_length) - { - Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length); - } - else - { - Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes); - } - } - bytes_read+= n_bytes; - } while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) ); - - // Print errors if there was a problem - if ( n_bytes < 1 ) + // Read for pipe and loop until bytes expected have been read or an error occurs + int bytes_read = 0; + do + { + n_bytes = read( sd, image_data+bytes_read, frame_header.image_length-bytes_read ); + if (n_bytes < 0) break; // break on error + if (n_bytes < (int)frame_header.image_length) + { + // print some informational messages + if (bytes_read == 0) { - Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length); - if ( n_bytes < 0 ) - { - Error( "Can't read frame image data: %s", strerror(errno) ); - } - else - { - Warning( "Socket closed at remote end" ); - } - ReopenSocket( sd, monitor->Id() ); - continue; - } - - static char subpath[PATH_MAX] = ""; - if ( config.use_deep_storage ) + Debug(4,"Image read : Short read %d bytes of %d expected bytes",n_bytes,frame_header.image_length); + } + else if (bytes_read+n_bytes == (int)frame_header.image_length) { - struct tm *time = localtime( &frame_header.event_time ); - snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); + Debug(5,"Image read : Read rest of short read: %d bytes read total of %d bytes",n_bytes,frame_header.image_length); } else { - snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id ); + Debug(6,"Image read : continuing, read %d bytes (%d so far)", n_bytes, bytes_read+n_bytes); } + } + bytes_read+= n_bytes; + } while (n_bytes>0 && (bytes_read < (ssize_t)frame_header.image_length) ); - static char path[PATH_MAX] = ""; - snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id ); - Debug( 1, "Got image, writing to %s", path ); + // Print errors if there was a problem + if ( n_bytes < 1 ) + { + Error( "Only read %d bytes of %d\n", bytes_read, frame_header.image_length); + if ( n_bytes < 0 ) + { + Error( "Can't read frame image data: %s", strerror(errno) ); + } + else + { + Warning( "Socket closed at remote end" ); + } + ReopenSocket( sd, monitor->Id() ); + continue; + } - FILE *fd = 0; - if ( (fd = fopen( path, "w" )) < 0 ) - { - Error( "Can't fopen '%s': %s", path, strerror(errno) ); - exit( -1 ); - } - if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) ) - { - Error( "Can't fwrite image data: %s", strerror(errno) ); - exit( -1 ); - } - fclose( fd ); + static char subpath[PATH_MAX] = ""; + if ( config.use_deep_storage ) + { + struct tm *time = localtime( &frame_header.event_time ); + snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec ); + } + else + { + snprintf( subpath, sizeof(subpath), "%ld", frame_header.event_id ); + } - sigprocmask( SIG_UNBLOCK, &block_set, 0 ); - } + static char path[PATH_MAX] = ""; + snprintf( path, sizeof(path), frame_header.alarm_frame?anal_path:capt_path, subpath, frame_header.frame_id ); + Debug( 1, "Got image, writing to %s", path ); + + FILE *fd = 0; + if ( (fd = fopen( path, "w" )) < 0 ) + { + Error( "Can't fopen '%s': %s", path, strerror(errno) ); + exit( -1 ); + } + if ( 0 == fwrite( image_data, frame_header.image_length, 1, fd ) ) + { + Error( "Can't fwrite image data: %s", strerror(errno) ); + exit( -1 ); + } + fclose( fd ); + + sigprocmask( SIG_UNBLOCK, &block_set, 0 ); + } + logTerm(); + zmDbClose(); } diff --git a/src/zmf.h b/src/zmf.h index f00ffa891..211577309 100644 --- a/src/zmf.h +++ b/src/zmf.h @@ -22,11 +22,11 @@ struct FrameHeader { - unsigned long event_id; - time_t event_time; - unsigned long frame_id; - bool alarm_frame; - unsigned long image_length; + unsigned long event_id; + time_t event_time; + unsigned long frame_id; + bool alarm_frame; + unsigned long image_length; }; #endif // ZMFILE_H diff --git a/src/zms.cpp b/src/zms.cpp index 0eab29756..e40fcdf95 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -28,302 +28,318 @@ bool ValidateAccess( User *user, int mon_id ) { - bool allowed = true; + bool allowed = true; - if ( mon_id > 0 ) - { - if ( user->getStream() < User::PERM_VIEW ) - allowed = false; - if ( !user->canAccess( mon_id ) ) - allowed = false; - } - else - { - if ( user->getEvents() < User::PERM_VIEW ) - allowed = false; - } - if ( !allowed ) - { - Error( "Error, insufficient privileges for requested action" ); - exit( -1 ); - } - return( allowed ); + if ( mon_id > 0 ) + { + if ( user->getStream() < User::PERM_VIEW ) + allowed = false; + if ( !user->canAccess( mon_id ) ) + allowed = false; + } + else + { + if ( user->getEvents() < User::PERM_VIEW ) + allowed = false; + } + if ( !allowed ) + { + Error( "Error, insufficient privileges for requested action" ); + exit( -1 ); + } + return( allowed ); } int main( int argc, const char *argv[] ) { - self = argv[0]; + self = argv[0]; - srand( getpid() * time( 0 ) ); + srand( getpid() * time( 0 ) ); - enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR; - enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG; - char format[32] = ""; - int monitor_id = 0; - time_t event_time = 0; - int event_id = 0; - int frame_id = 1; - unsigned int scale = 100; - unsigned int rate = 100; - double maxfps = 10.0; - unsigned int bitrate = 100000; - unsigned int ttl = 0; - EventStream::StreamMode replay = EventStream::MODE_SINGLE; - char username[64] = ""; - char password[64] = ""; - char auth[64] = ""; - unsigned int connkey = 0; - unsigned int playback_buffer = 0; + enum { ZMS_MONITOR, ZMS_EVENT } source = ZMS_MONITOR; + enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG; + char format[32] = ""; + int monitor_id = 0; + time_t event_time = 0; + int event_id = 0; + unsigned int frame_id = 1; + unsigned int scale = 100; + unsigned int rate = 100; + double maxfps = 10.0; + unsigned int bitrate = 100000; + unsigned int ttl = 0; + EventStream::StreamMode replay = EventStream::MODE_SINGLE; + char username[64] = ""; + char password[64] = ""; + char auth[64] = ""; + unsigned int connkey = 0; + unsigned int playback_buffer = 0; - bool nph = false; - const char *basename = strrchr( argv[0], '/' ); - if (basename) //if we found a / lets skip past it - basename++; - else //argv[0] will not always contain the full path, but rather just the script name - basename = argv[0]; - const char *nph_prefix = "nph-"; - if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) ) - { - nph = true; - } - - zmLoadConfig(); + bool nph = false; + const char *basename = strrchr( argv[0], '/' ); + if (basename) //if we found a / lets skip past it + basename++; + else //argv[0] will not always contain the full path, but rather just the script name + basename = argv[0]; + const char *nph_prefix = "nph-"; + if ( basename && !strncmp( basename, nph_prefix, strlen(nph_prefix) ) ) + { + nph = true; + } + + zmLoadConfig(); - logInit( "zms" ); - - ssedetect(); + logInit( "zms" ); + + ssedetect(); - zmSetDefaultTermHandler(); - zmSetDefaultDieHandler(); + zmSetDefaultTermHandler(); + zmSetDefaultDieHandler(); - const char *query = getenv( "QUERY_STRING" ); - if ( query ) - { - Debug( 1, "Query: %s", query ); - - char temp_query[1024]; - strncpy( temp_query, query, sizeof(temp_query) ); - char *q_ptr = temp_query; - char *parms[16]; // Shouldn't be more than this - int parm_no = 0; - while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) ) - { - parm_no++; - q_ptr = NULL; - } - - for ( int p = 0; p < parm_no; p++ ) - { - char *name = strtok( parms[p], "=" ); - char *value = strtok( NULL, "=" ); - if ( !value ) - value = (char *)""; - if ( !strcmp( name, "source" ) ) - { - source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR; - } - else if ( !strcmp( name, "mode" ) ) - { - mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG; - mode = !strcmp( value, "raw" )?ZMS_RAW:mode; - mode = !strcmp( value, "zip" )?ZMS_ZIP:mode; - mode = !strcmp( value, "single" )?ZMS_SINGLE:mode; - } - else if ( !strcmp( name, "format" ) ) - strncpy( format, value, sizeof(format) ); - else if ( !strcmp( name, "monitor" ) ) - monitor_id = atoi( value ); - else if ( !strcmp( name, "time" ) ) - event_time = atoi( value ); - else if ( !strcmp( name, "event" ) ) - event_id = strtoull( value, (char **)NULL, 10 ); - else if ( !strcmp( name, "frame" ) ) - frame_id = strtoull( value, (char **)NULL, 10 ); - else if ( !strcmp( name, "scale" ) ) - scale = atoi( value ); - else if ( !strcmp( name, "rate" ) ) - rate = atoi( value ); - else if ( !strcmp( name, "maxfps" ) ) - maxfps = atof( value ); - else if ( !strcmp( name, "bitrate" ) ) - bitrate = atoi( value ); - else if ( !strcmp( name, "ttl" ) ) - ttl = atoi(value); - else if ( !strcmp( name, "replay" ) ) - { - replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE; - replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay; - } - else if ( !strcmp( name, "connkey" ) ) - connkey = atoi(value); - else if ( !strcmp( name, "buffer" ) ) - playback_buffer = atoi(value); - else if ( config.opt_use_auth ) - { - if ( strcmp( config.auth_relay, "none" ) == 0 ) - { - if ( !strcmp( name, "user" ) ) - { - strncpy( username, value, sizeof(username) ); - } - } - else - { - //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) - { - if ( !strcmp( name, "auth" ) ) - { - strncpy( auth, value, sizeof(auth) ); - } - } - //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) - { - if ( !strcmp( name, "user" ) ) - { - strncpy( username, value, sizeof(username) ); - } - if ( !strcmp( name, "pass" ) ) - { - strncpy( password, value, sizeof(password) ); - } - } - } - } - } - } - - if ( config.opt_use_auth ) - { - User *user = 0; - - if ( strcmp( config.auth_relay, "none" ) == 0 ) - { - if ( *username ) - { - user = zmLoadUser( username ); - } - } - else - { - //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) - { - if ( *auth ) - { - user = zmLoadAuthUser( auth, config.auth_hash_ips ); - } - } - //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) - { - if ( *username && *password ) - { - user = zmLoadUser( username, password ); - } - } - } - if ( !user ) - { - Error( "Unable to authenticate user" ); - return( -1 ); - } - ValidateAccess( user, monitor_id ); - } - - setbuf( stdout, 0 ); - if ( nph ) - { - fprintf( stdout, "HTTP/1.0 200 OK\r\n" ); - } - fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION ); - - time_t now = time( 0 ); - char date_string[64]; - strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) ); - - fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" ); - fprintf( stdout, "Last-Modified: %s\r\n", date_string ); - fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" ); - fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" ); - fprintf( stdout, "Pragma: no-cache\r\n"); - // Removed as causing more problems than it fixed. - //if ( !nph ) - //{ - //fprintf( stdout, "Content-Length: 0\r\n"); - //} - - if ( source == ZMS_MONITOR ) - { - MonitorStream stream; - stream.setStreamScale( scale ); - stream.setStreamReplayRate( rate ); - stream.setStreamMaxFPS( maxfps ); - stream.setStreamTTL( ttl ); - stream.setStreamQueue( connkey ); - stream.setStreamBuffer( playback_buffer ); - stream.setStreamStart( monitor_id ); - - if ( mode == ZMS_JPEG ) - { - stream.setStreamType( MonitorStream::STREAM_JPEG ); - } - else if ( mode == ZMS_RAW ) - { - stream.setStreamType( MonitorStream::STREAM_RAW ); - } - else if ( mode == ZMS_ZIP ) - { - stream.setStreamType( MonitorStream::STREAM_ZIP ); - } - else if ( mode == ZMS_SINGLE ) - { - stream.setStreamType( MonitorStream::STREAM_SINGLE ); - } - else - { -#if HAVE_LIBAVCODEC - stream.setStreamFormat( format ); - stream.setStreamBitrate( bitrate ); - stream.setStreamType( MonitorStream::STREAM_MPEG ); -#else // HAVE_LIBAVCODEC - Error( "MPEG streaming of '%s' attempted while disabled", query ); - fprintf( stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n" ); - return( -1 ); -#endif // HAVE_LIBAVCODEC - } - stream.runStream(); - } - else if ( source == ZMS_EVENT ) + const char *query = getenv( "QUERY_STRING" ); + if ( query ) + { + Debug( 1, "Query: %s", query ); + + char temp_query[1024]; + strncpy( temp_query, query, sizeof(temp_query) ); + char *q_ptr = temp_query; + char *parms[16]; // Shouldn't be more than this + int parm_no = 0; + while( (parm_no < 16) && (parms[parm_no] = strtok( q_ptr, "&" )) ) { - EventStream stream; - stream.setStreamScale( scale ); - stream.setStreamReplayRate( rate ); - stream.setStreamMaxFPS( maxfps ); - stream.setStreamMode( replay ); - stream.setStreamQueue( connkey ); - if ( monitor_id && event_time ) - { - stream.setStreamStart( monitor_id, event_time ); - } - else - { - stream.setStreamStart( event_id, frame_id ); - } - if ( mode == ZMS_JPEG ) - { - stream.setStreamType( EventStream::STREAM_JPEG ); - } - else - { -#if HAVE_LIBAVCODEC - stream.setStreamFormat( format ); - stream.setStreamBitrate( bitrate ); - stream.setStreamType( EventStream::STREAM_MPEG ); -#else // HAVE_LIBAVCODEC - Error( "MPEG streaming of '%s' attempted while disabled", query ); - fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" ); - return( -1 ); -#endif // HAVE_LIBAVCODEC - } - stream.runStream(); + parm_no++; + q_ptr = NULL; } - return( 0 ); + + for ( int p = 0; p < parm_no; p++ ) + { + char *name = strtok( parms[p], "=" ); + char *value = strtok( NULL, "=" ); + if ( !value ) + value = (char *)""; + if ( !strcmp( name, "source" ) ) + { + source = !strcmp( value, "event" )?ZMS_EVENT:ZMS_MONITOR; + } + else if ( !strcmp( name, "mode" ) ) + { + mode = !strcmp( value, "jpeg" )?ZMS_JPEG:ZMS_MPEG; + mode = !strcmp( value, "raw" )?ZMS_RAW:mode; + mode = !strcmp( value, "zip" )?ZMS_ZIP:mode; + mode = !strcmp( value, "single" )?ZMS_SINGLE:mode; + } + else if ( !strcmp( name, "format" ) ) + strncpy( format, value, sizeof(format) ); + else if ( !strcmp( name, "monitor" ) ) + monitor_id = atoi( value ); + else if ( !strcmp( name, "time" ) ) + event_time = atoi( value ); + else if ( !strcmp( name, "event" ) ) + event_id = strtoull( value, (char **)NULL, 10 ); + else if ( !strcmp( name, "frame" ) ) + frame_id = strtoull( value, (char **)NULL, 10 ); + else if ( !strcmp( name, "scale" ) ) + scale = atoi( value ); + else if ( !strcmp( name, "rate" ) ) + rate = atoi( value ); + else if ( !strcmp( name, "maxfps" ) ) + maxfps = atof( value ); + else if ( !strcmp( name, "bitrate" ) ) + bitrate = atoi( value ); + else if ( !strcmp( name, "ttl" ) ) + ttl = atoi(value); + else if ( !strcmp( name, "replay" ) ) + { + replay = !strcmp( value, "gapless" )?EventStream::MODE_ALL_GAPLESS:EventStream::MODE_SINGLE; + replay = !strcmp( value, "all" )?EventStream::MODE_ALL:replay; + } + else if ( !strcmp( name, "connkey" ) ) + connkey = atoi(value); + else if ( !strcmp( name, "buffer" ) ) + playback_buffer = atoi(value); + else if ( config.opt_use_auth ) + { + if ( strcmp( config.auth_relay, "none" ) == 0 ) + { + if ( !strcmp( name, "user" ) ) + { + strncpy( username, value, sizeof(username) ); + } + } + else + { + //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) + { + if ( !strcmp( name, "auth" ) ) + { + strncpy( auth, value, sizeof(auth) ); + } + } + //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) + { + if ( !strcmp( name, "user" ) ) + { + strncpy( username, value, sizeof(username) ); + } + if ( !strcmp( name, "pass" ) ) + { + strncpy( password, value, sizeof(password) ); + } + } + } + } + } + } + + if ( config.opt_use_auth ) + { + User *user = 0; + + if ( strcmp( config.auth_relay, "none" ) == 0 ) + { + if ( *username ) + { + user = zmLoadUser( username ); + } + } + else + { + //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) + { + if ( *auth ) + { + user = zmLoadAuthUser( auth, config.auth_hash_ips ); + } + } + //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) + { + if ( *username && *password ) + { + user = zmLoadUser( username, password ); + } + } + } + if ( !user ) + { + Error( "Unable to authenticate user" ); + logTerm(); + zmDbClose(); + return( -1 ); + } + ValidateAccess( user, monitor_id ); + } + + setbuf( stdout, 0 ); + if ( nph ) + { + fprintf( stdout, "HTTP/1.0 200 OK\r\n" ); + } + fprintf( stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION ); + + time_t now = time( 0 ); + char date_string[64]; + strftime( date_string, sizeof(date_string)-1, "%a, %d %b %Y %H:%M:%S GMT", gmtime( &now ) ); + + fprintf( stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" ); + fprintf( stdout, "Last-Modified: %s\r\n", date_string ); + fprintf( stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n" ); + fprintf( stdout, "Cache-Control: post-check=0, pre-check=0\r\n" ); + fprintf( stdout, "Pragma: no-cache\r\n"); + // Removed as causing more problems than it fixed. + //if ( !nph ) + //{ + //fprintf( stdout, "Content-Length: 0\r\n"); + //} + + if ( source == ZMS_MONITOR ) + { + MonitorStream stream; + stream.setStreamScale( scale ); + stream.setStreamReplayRate( rate ); + stream.setStreamMaxFPS( maxfps ); + stream.setStreamTTL( ttl ); + stream.setStreamQueue( connkey ); + stream.setStreamBuffer( playback_buffer ); + if ( ! stream.setStreamStart( monitor_id ) ) { + Error( "Unable to connect to zmc process for monitor %d", monitor_id ); + fprintf( stderr, "Unable to connect to zmc process. Please ensure that it is running." ); + logTerm(); + zmDbClose(); + return( -1 ); + } + + if ( mode == ZMS_JPEG ) + { + stream.setStreamType( MonitorStream::STREAM_JPEG ); + } + else if ( mode == ZMS_RAW ) + { + stream.setStreamType( MonitorStream::STREAM_RAW ); + } + else if ( mode == ZMS_ZIP ) + { + stream.setStreamType( MonitorStream::STREAM_ZIP ); + } + else if ( mode == ZMS_SINGLE ) + { + stream.setStreamType( MonitorStream::STREAM_SINGLE ); + } + else + { +#if HAVE_LIBAVCODEC + stream.setStreamFormat( format ); + stream.setStreamBitrate( bitrate ); + stream.setStreamType( MonitorStream::STREAM_MPEG ); +#else // HAVE_LIBAVCODEC + Error( "MPEG streaming of '%s' attempted while disabled", query ); + fprintf( stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n" ); + logTerm(); + zmDbClose(); + return( -1 ); +#endif // HAVE_LIBAVCODEC + } + stream.runStream(); + } + else if ( source == ZMS_EVENT ) + { + EventStream stream; + stream.setStreamScale( scale ); + stream.setStreamReplayRate( rate ); + stream.setStreamMaxFPS( maxfps ); + stream.setStreamMode( replay ); + stream.setStreamQueue( connkey ); + if ( monitor_id && event_time ) + { + stream.setStreamStart( monitor_id, event_time ); + } + else + { + stream.setStreamStart( event_id, frame_id ); + } + if ( mode == ZMS_JPEG ) + { + stream.setStreamType( EventStream::STREAM_JPEG ); + } + else + { +#if HAVE_LIBAVCODEC + stream.setStreamFormat( format ); + stream.setStreamBitrate( bitrate ); + stream.setStreamType( EventStream::STREAM_MPEG ); +#else // HAVE_LIBAVCODEC + Error( "MPEG streaming of '%s' attempted while disabled", query ); + fprintf( stderr, "MPEG streaming is disabled.\nYou should ensure the ffmpeg libraries are installed and detected and rebuild to use this functionality.\n" ); + logTerm(); + zmDbClose(); + return( -1 ); +#endif // HAVE_LIBAVCODEC + } + stream.runStream(); + } + + logTerm(); + zmDbClose(); + + return( 0 ); } diff --git a/src/zmstreamer.cpp b/src/zmstreamer.cpp index ea61c7ff9..b33c61246 100644 --- a/src/zmstreamer.cpp +++ b/src/zmstreamer.cpp @@ -19,6 +19,52 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +/* + +=head1 NAME + +zmstreamer - eyeZM video streamer + +=head1 SYNOPSIS + + zmstreamer -e + zmstreamer -o + zmstreamer -u + zmstreamer -f + zmstreamer -s + zmstreamer -b + zmstreamer -m + zmstreamer -d + zmstreamer -i + zmstreamer -? + zmstreamer -h + zmstreamer -v + +=head1 DESCRIPTION + +*DEPRECIATED* The xml skin and all files associated with the xml skin are now +depreciated. Please use the ZoneMinder API instead. + +This binary works in conjunction with the XML skin to stream video to iPhones +running the eyeZm app. + +=head1 OPTIONS + + -e - Specify output mode: mpeg/jpg/zip/single/raw. + -o - Specify output format. + -u - Specify buffer size in ms. + -f - Specify maximum framerate. + -s - Specify scale. + -b - Specify bitrate. + -m - Specify monitor id. + -d - 0 = off, 1 = no streaming, 2 = with streaming. + -i, -?, -h - Display usage information + -v - Print the installed version of ZoneMinder + +=cut + +*/ + #include #include @@ -33,7 +79,7 @@ #include "zm_stream.h" // Possible command-line options -#define OPTIONS "e:o:u:f:s:b:m:d:i:?" +#define OPTIONS "e:o:u:f:s:b:m:d:i:?:h:v" // Default ZMS values #define ZMS_DEFAULT_DEBUG 0 @@ -46,151 +92,161 @@ #define ZMS_DEFAULT_BUFFER 1000 int main(int argc, char** argv) { - self = argv[0]; - // Set initial values to the default values - int debug = ZMS_DEFAULT_DEBUG; - int id = ZMS_DEFAULT_ID; - int bitrate = ZMS_DEFAULT_BITRATE; - int scale = ZMS_DEFAULT_SCALE; - char mode[32]; - sprintf(mode, "%s", ZMS_DEFAULT_MODE); - char format[32]; - sprintf(format, "%s", ZMS_DEFAULT_FORMAT); - double maxfps = ZMS_DEFAULT_FPS; - int buffer = ZMS_DEFAULT_BUFFER; + self = argv[0]; + // Set initial values to the default values + int debug = ZMS_DEFAULT_DEBUG; + int id = ZMS_DEFAULT_ID; + int bitrate = ZMS_DEFAULT_BITRATE; + int scale = ZMS_DEFAULT_SCALE; + char mode[32]; + sprintf(mode, "%s", ZMS_DEFAULT_MODE); + char format[32]; + sprintf(format, "%s", ZMS_DEFAULT_FORMAT); + double maxfps = ZMS_DEFAULT_FPS; + int buffer = ZMS_DEFAULT_BUFFER; - // Parse command-line options - int arg; - while ((arg = getopt(argc, argv, OPTIONS)) != -1) { - switch (arg) { - case 'e': - sprintf(mode, "%s", optarg); - break; - case 'o': - sprintf(format, "%s", optarg); - break; - case 'u': - buffer = atoi(optarg); - break; - case 'f': - maxfps = atof(optarg); - break; - case 's': - scale = atoi(optarg); - break; - case 'b': - bitrate = atoi(optarg); - break; - case 'm': - id = atoi(optarg); - break; - case 'd': - debug = atoi(optarg); - break; - case 'i': - case '?': - printf("-e : Specify output mode: mpeg/jpg/zip/single/raw. Default = %s\n", ZMS_DEFAULT_MODE); - printf("-o : Specify output format. Default = %s\n", ZMS_DEFAULT_FORMAT); - printf("-u : Specify buffer size in ms. Default = %d\n", ZMS_DEFAULT_BUFFER); - printf("-f : Specify maximum framerate. Default = %lf\n", ZMS_DEFAULT_FPS); - printf("-s : Specify scale. Default = %d\n", ZMS_DEFAULT_SCALE); - printf("-b : Specify bitrate. Default = %d\n", ZMS_DEFAULT_BITRATE); - printf("-m : Specify monitor id. Default = %d\n", ZMS_DEFAULT_ID); - printf("-d : 0 = off, 1 = no streaming, 2 = with streaming. Default = 0\n"); - printf("-i or -? : This information\n"); - return EXIT_SUCCESS; - } + // Parse command-line options + int arg; + while ((arg = getopt(argc, argv, OPTIONS)) != -1) { + switch (arg) { + case 'e': + sprintf(mode, "%s", optarg); + break; + case 'o': + sprintf(format, "%s", optarg); + break; + case 'u': + buffer = atoi(optarg); + break; + case 'f': + maxfps = atof(optarg); + break; + case 's': + scale = atoi(optarg); + break; + case 'b': + bitrate = atoi(optarg); + break; + case 'm': + id = atoi(optarg); + break; + case 'd': + debug = atoi(optarg); + break; + case 'h': + case 'i': + case '?': + printf("-e : Specify output mode: mpeg/jpg/zip/single/raw. Default = %s\n", ZMS_DEFAULT_MODE); + printf("-o : Specify output format. Default = %s\n", ZMS_DEFAULT_FORMAT); + printf("-u : Specify buffer size in ms. Default = %d\n", ZMS_DEFAULT_BUFFER); + printf("-f : Specify maximum framerate. Default = %lf\n", ZMS_DEFAULT_FPS); + printf("-s : Specify scale. Default = %d\n", ZMS_DEFAULT_SCALE); + printf("-b : Specify bitrate. Default = %d\n", ZMS_DEFAULT_BITRATE); + printf("-m : Specify monitor id. Default = %d\n", ZMS_DEFAULT_ID); + printf("-d : 0 = off, 1 = no streaming, 2 = with streaming. Default = 0\n"); + printf("-i or -? or -h: This information\n"); + printf("-v : This installed version of ZoneMinder\n"); + return EXIT_SUCCESS; + case 'v': + std::cout << ZM_VERSION << "\n"; + exit(0); } + } - // Set stream type - StreamBase::StreamType streamtype; - if (!strcasecmp("raw", mode)) - streamtype = MonitorStream::STREAM_RAW; - else if (!strcasecmp("mpeg", mode)) - streamtype = MonitorStream::STREAM_MPEG; - else if (!strcasecmp("jpg", mode)) - streamtype = MonitorStream::STREAM_JPEG; - else if (!strcasecmp("single", mode)) - streamtype = MonitorStream::STREAM_SINGLE; - else if (!strcasecmp("zip", mode)) - streamtype = MonitorStream::STREAM_ZIP; - else - streamtype = MonitorStream::STREAM_MPEG; + // Set stream type + StreamBase::StreamType streamtype; + if (!strcasecmp("raw", mode)) + streamtype = MonitorStream::STREAM_RAW; + else if (!strcasecmp("mpeg", mode)) + streamtype = MonitorStream::STREAM_MPEG; + else if (!strcasecmp("jpg", mode)) + streamtype = MonitorStream::STREAM_JPEG; + else if (!strcasecmp("single", mode)) + streamtype = MonitorStream::STREAM_SINGLE; + else if (!strcasecmp("zip", mode)) + streamtype = MonitorStream::STREAM_ZIP; + else + streamtype = MonitorStream::STREAM_MPEG; - if (debug) { - // Show stream parameters - printf("Stream parameters:\n"); - switch (streamtype) { - case MonitorStream::STREAM_MPEG: - printf("Output mode (-e) = %s\n", "mpeg"); - printf("Output format (-o) = %s\n", format); - break; - default: - printf("Output mode (-e) = %s\n", mode); - } - printf("Buffer size (-u) = %d ms\n", buffer); - printf("Maximum FPS (-f) = %lf FPS\n", maxfps); - printf("Scale (-s) = %d%%\n", scale); - printf("Bitrate (-b) = %d bps\n", bitrate); - printf("Monitor Id (-m) = %d\n", id); + if (debug) { + // Show stream parameters + printf("Stream parameters:\n"); + switch (streamtype) { + case MonitorStream::STREAM_MPEG: + printf("Output mode (-e) = %s\n", "mpeg"); + printf("Output format (-o) = %s\n", format); + break; + default: + printf("Output mode (-e) = %s\n", mode); } + printf("Buffer size (-u) = %d ms\n", buffer); + printf("Maximum FPS (-f) = %lf FPS\n", maxfps); + printf("Scale (-s) = %d%%\n", scale); + printf("Bitrate (-b) = %d bps\n", bitrate); + printf("Monitor Id (-m) = %d\n", id); + } - if (debug) { - // Set ZM debugger to print to stdout - printf("Setting up ZoneMinder debugger to print to stdout..."); - setenv("ZM_DBG_PRINT", "1", 1); - printf("Done.\n"); - } - - // Loading ZM configurations - printf("Loading ZoneMinder configurations..."); - zmLoadConfig(); + if (debug) { + // Set ZM debugger to print to stdout + printf("Setting up ZoneMinder debugger to print to stdout..."); + setenv("ZM_DBG_PRINT", "1", 1); printf("Done.\n"); + } - logInit("zmstreamer"); - - ssedetect(); + // Loading ZM configurations + printf("Loading ZoneMinder configurations..."); + zmLoadConfig(); + printf("Done.\n"); - // Setting stream parameters - MonitorStream stream; - stream.setStreamScale(scale); // default = 100 (scale) - stream.setStreamReplayRate(100); // default = 100 (rate) - stream.setStreamMaxFPS(maxfps); // default = 10 (maxfps) - if (debug) stream.setStreamTTL(1); - else stream.setStreamTTL(0); // default = 0 (ttl) - stream.setStreamQueue(0); // default = 0 (connkey) - stream.setStreamBuffer(buffer); // default = 0 (buffer) - stream.setStreamStart(id); // default = 0 (monitor_id) - stream.setStreamType(streamtype); - if (streamtype == MonitorStream::STREAM_MPEG) { + logInit("zmstreamer"); + + ssedetect(); + + // Setting stream parameters + MonitorStream stream; + stream.setStreamScale(scale); // default = 100 (scale) + stream.setStreamReplayRate(100); // default = 100 (rate) + stream.setStreamMaxFPS(maxfps); // default = 10 (maxfps) + if (debug) stream.setStreamTTL(1); + else stream.setStreamTTL(0); // default = 0 (ttl) + stream.setStreamQueue(0); // default = 0 (connkey) + stream.setStreamBuffer(buffer); // default = 0 (buffer) + stream.setStreamStart(id); // default = 0 (monitor_id) + stream.setStreamType(streamtype); + if (streamtype == MonitorStream::STREAM_MPEG) { #if HAVE_LIBAVCODEC - if (debug) printf("HAVE_LIBAVCODEC is set\n"); - stream.setStreamFormat(format); // default = "" (format) - stream.setStreamBitrate(bitrate); // default = 100000 (bitrate) + if (debug) printf("HAVE_LIBAVCODEC is set\n"); + stream.setStreamFormat(format); // default = "" (format) + stream.setStreamBitrate(bitrate); // default = 100000 (bitrate) #else - fprintf(stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n"); - return EXIT_FAILURE; + fprintf(stderr, "MPEG streaming is disabled.\nYou should configure with the --with-ffmpeg option and rebuild to use this functionality.\n"); + logTerm(); + zmDbClose(); + return EXIT_FAILURE; #endif - } + } - if (debug != 1) { - if (debug) printf("Running stream..."); + if (debug != 1) { + if (debug) printf("Running stream..."); - // Output headers - fprintf(stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION); - time_t now = time(0); - char date_string[64]; - strftime(date_string, sizeof (date_string) - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); - fprintf(stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"); - fprintf(stdout, "Last-Modified: %s\r\n", date_string); - fprintf(stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n"); - fprintf(stdout, "Cache-Control: post-check=0, pre-check=0\r\n"); - fprintf(stdout, "Pragma: no-cache\r\n"); + // Output headers + fprintf(stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION); + time_t now = time(0); + char date_string[64]; + strftime(date_string, sizeof (date_string) - 1, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); + fprintf(stdout, "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"); + fprintf(stdout, "Last-Modified: %s\r\n", date_string); + fprintf(stdout, "Cache-Control: no-store, no-cache, must-revalidate\r\n"); + fprintf(stdout, "Cache-Control: post-check=0, pre-check=0\r\n"); + fprintf(stdout, "Pragma: no-cache\r\n"); - // Run stream - stream.runStream(); - } - if (debug) printf("Done.\n"); + // Run stream + stream.runStream(); + } + if (debug) printf("Done.\n"); - return (EXIT_SUCCESS); + logTerm(); + zmDbClose(); + + return (EXIT_SUCCESS); } diff --git a/src/zmu.cpp b/src/zmu.cpp index bc032639f..0c5a5a41e 100644 --- a/src/zmu.cpp +++ b/src/zmu.cpp @@ -17,6 +17,75 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +/* + +=head1 NAME + +zmc - The ZoneMinder Utility + +=head1 SYNOPSIS + + zmu -d device_path [-v] [function] [-U -P] + zmu --device device_path [-v] [function] [-U -P] + + zmu -m monitor_id [-v] [function] [-U -P] + zmu --monitor monitor_id [-v] [function] [-U -P] + +=head1 DESCRIPTION + +This binary is a handy command line interface to several useful functions. It's +not really meant to be used by anyone except the web page (there's only limited +'help' in it so far) but can be if necessary, especially for debugging video +problems. + +=head1 OPTIONS + +General options: + -v, --verbose - Produce more verbose output + -l, --list - List the current status of active (or all with -v) monitors + -h, --help - Display usage information + -v, --version - Print the installed version of ZoneMinder + +Options for use with devices: + -d, --device [device_path] - Get the current video device settings for [device_path] or all devices + -V, --version - Set the Video 4 Linux API version to use for the query, use 1 or 2 + -q, --query - Query the current settings for the device + +Options for use with monitors: + -m, --monitor - Specify which monitor to address, default 1 if absent + -q, --query - Query the current settings for the monitor + -s, --state - Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm, + 3 = alert, 4 = tape + -B, --brightness [value] - Output the current brightness, set to value if given + -C, --contrast [value] - Output the current contrast, set to value if given + -H, --hue [value] - Output the current hue, set to value if given + -O, --colour [value] - Output the current colour, set to value if given + -i, --image [image_index] - Write captured image to disk as .jpg, last image captured + or specified ring buffer index if given. + -S, --scale - With --image specify any scaling (in %%) to be applied to the image + -t, --timestamp [image_index] - Output captured image timestamp, last image captured or specified + ring buffer index if given + -R, --read_index - Output ring buffer read index + -W, --write_index - Output ring buffer write index + -e, --event - Output last event index + -f, --fps - Output last Frames Per Second captured reading + -z, --zones - Write last captured image overlaid with zones to -Zones.jpg + -a, --alarm - Force alarm in monitor, this will trigger recording until cancelled with -c + -n, --noalarm - Force no alarms in monitor, this will prevent alarms until cancelled with -c + -c, --cancel - Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n + -L, --reload - Signal monitor to reload settings + -E, --enable - Enable detection, wake monitor up + -D, --disable - Disable detection, put monitor to sleep + -u, --suspend - Suspend detection, useful to prevent bogus alarms when panning etc + -r, --resume - Resume detection after a suspend + -U, --username - When running in authenticated mode the username and + -P, --password - password combination of the given user + -A, --auth - Pass authentication hash string instead of user details + +=cut + +*/ + #include #include "zm.h" @@ -28,731 +97,739 @@ void Usage( int status=-1 ) { - fprintf( stderr, "zmu <-d device_path> [-v] [function] [-U -P]\n" ); - fprintf( stderr, "zmu <-m monitor_id> [-v] [function] [-U -P]\n" ); - fprintf( stderr, "General options:\n" ); - fprintf( stderr, " -h, --help : This screen\n" ); - fprintf( stderr, " -v, --verbose : Produce more verbose output\n" ); - fprintf( stderr, " -l, --list : List the current status of active (or all with -v) monitors\n" ); - fprintf( stderr, "Options for use with devices:\n" ); - fprintf( stderr, " -d, --device [device_path] : Get the current video device settings for [device_path] or all devices\n" ); - fprintf( stderr, " -V, --version : Set the Video 4 Linux API version to use for the query, use 1 or 2\n" ); - fprintf( stderr, " -q, --query : Query the current settings for the device\n" ); - fprintf( stderr, "Options for use with monitors:\n" ); - fprintf( stderr, " -m, --monitor : Specify which monitor to address, default 1 if absent\n" ); - fprintf( stderr, " -q, --query : Query the current settings for the monitor\n" ); - fprintf( stderr, " -s, --state : Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm,\n" ); - fprintf( stderr, " 3 = alert, 4 = tape\n" ); - fprintf( stderr, " -B, --brightness [value] : Output the current brightness, set to value if given \n" ); - fprintf( stderr, " -C, --contrast [value] : Output the current contrast, set to value if given \n" ); - fprintf( stderr, " -H, --hue [value] : Output the current hue, set to value if given \n" ); - fprintf( stderr, " -O, --colour [value] : Output the current colour, set to value if given \n" ); - fprintf( stderr, " -i, --image [image_index] : Write captured image to disk as .jpg, last image captured\n" ); - fprintf( stderr, " or specified ring buffer index if given.\n" ); - fprintf( stderr, " -S, --scale : With --image specify any scaling (in %%) to be applied to the image\n" ); - fprintf( stderr, " -t, --timestamp [image_index] : Output captured image timestamp, last image captured or specified\n" ); - fprintf( stderr, " ring buffer index if given\n" ); - fprintf( stderr, " -R, --read_index : Output ring buffer read index\n" ); - fprintf( stderr, " -W, --write_index : Output ring buffer write index\n" ); - fprintf( stderr, " -e, --event : Output last event index\n" ); - fprintf( stderr, " -f, --fps : Output last Frames Per Second captured reading\n" ); - fprintf( stderr, " -z, --zones : Write last captured image overlaid with zones to -Zones.jpg\n" ); - fprintf( stderr, " -a, --alarm : Force alarm in monitor, this will trigger recording until cancelled with -c\n" ); - fprintf( stderr, " -n, --noalarm : Force no alarms in monitor, this will prevent alarms until cancelled with -c\n" ); - fprintf( stderr, " -c, --cancel : Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n\n" ); - fprintf( stderr, " -L, --reload : Signal monitor to reload settings\n" ); - fprintf( stderr, " -E, --enable : Enable detection, wake monitor up\n" ); - fprintf( stderr, " -D, --disable : Disable detection, put monitor to sleep\n" ); - fprintf( stderr, " -u, --suspend : Suspend detection, useful to prevent bogus alarms when panning etc\n" ); - fprintf( stderr, " -r, --resume : Resume detection after a suspend\n" ); - fprintf( stderr, " -U, --username : When running in authenticated mode the username and\n" ); - fprintf( stderr, " -P, --password : password combination of the given user\n" ); - fprintf( stderr, " -A, --auth : Pass authentication hash string instead of user details\n" ); + fprintf( stderr, "zmu <-d device_path> [-v] [function] [-U -P]\n" ); + fprintf( stderr, "zmu <-m monitor_id> [-v] [function] [-U -P]\n" ); + fprintf( stderr, "General options:\n" ); + fprintf( stderr, " -h, --help : This screen\n" ); + fprintf( stderr, " -v, --verbose : Produce more verbose output\n" ); + fprintf( stderr, " -l, --list : List the current status of active (or all with -v) monitors\n" ); + fprintf( stderr, "Options for use with devices:\n" ); + fprintf( stderr, " -d, --device [device_path] : Get the current video device settings for [device_path] or all devices\n" ); + fprintf( stderr, " -V, --version : Set the Video 4 Linux API version to use for the query, use 1 or 2\n" ); + fprintf( stderr, " -q, --query : Query the current settings for the device\n" ); + fprintf( stderr, "Options for use with monitors:\n" ); + fprintf( stderr, " -m, --monitor : Specify which monitor to address, default 1 if absent\n" ); + fprintf( stderr, " -q, --query : Query the current settings for the monitor\n" ); + fprintf( stderr, " -s, --state : Output the current monitor state, 0 = idle, 1 = prealarm, 2 = alarm,\n" ); + fprintf( stderr, " 3 = alert, 4 = tape\n" ); + fprintf( stderr, " -B, --brightness [value] : Output the current brightness, set to value if given \n" ); + fprintf( stderr, " -C, --contrast [value] : Output the current contrast, set to value if given \n" ); + fprintf( stderr, " -H, --hue [value] : Output the current hue, set to value if given \n" ); + fprintf( stderr, " -O, --colour [value] : Output the current colour, set to value if given \n" ); + fprintf( stderr, " -i, --image [image_index] : Write captured image to disk as .jpg, last image captured\n" ); + fprintf( stderr, " or specified ring buffer index if given.\n" ); + fprintf( stderr, " -S, --scale : With --image specify any scaling (in %%) to be applied to the image\n" ); + fprintf( stderr, " -t, --timestamp [image_index] : Output captured image timestamp, last image captured or specified\n" ); + fprintf( stderr, " ring buffer index if given\n" ); + fprintf( stderr, " -R, --read_index : Output ring buffer read index\n" ); + fprintf( stderr, " -W, --write_index : Output ring buffer write index\n" ); + fprintf( stderr, " -e, --event : Output last event index\n" ); + fprintf( stderr, " -f, --fps : Output last Frames Per Second captured reading\n" ); + fprintf( stderr, " -z, --zones : Write last captured image overlaid with zones to -Zones.jpg\n" ); + fprintf( stderr, " -a, --alarm : Force alarm in monitor, this will trigger recording until cancelled with -c\n" ); + fprintf( stderr, " -n, --noalarm : Force no alarms in monitor, this will prevent alarms until cancelled with -c\n" ); + fprintf( stderr, " -c, --cancel : Cancel a forced alarm/noalarm in monitor, required after being enabled with -a or -n\n" ); + fprintf( stderr, " -L, --reload : Signal monitor to reload settings\n" ); + fprintf( stderr, " -E, --enable : Enable detection, wake monitor up\n" ); + fprintf( stderr, " -D, --disable : Disable detection, put monitor to sleep\n" ); + fprintf( stderr, " -u, --suspend : Suspend detection, useful to prevent bogus alarms when panning etc\n" ); + fprintf( stderr, " -r, --resume : Resume detection after a suspend\n" ); + fprintf( stderr, " -U, --username : When running in authenticated mode the username and\n" ); + fprintf( stderr, " -P, --password : password combination of the given user\n" ); + fprintf( stderr, " -A, --auth : Pass authentication hash string instead of user details\n" ); - exit( status ); + exit( status ); } typedef enum { - ZMU_BOGUS = 0x00000000, - ZMU_STATE = 0x00000001, - ZMU_IMAGE = 0x00000002, - ZMU_TIME = 0x00000004, - ZMU_READ_IDX = 0x00000008, - ZMU_WRITE_IDX = 0x00000010, - ZMU_EVENT = 0x00000020, - ZMU_FPS = 0x00000040, - ZMU_ZONES = 0x00000080, - ZMU_ALARM = 0x00000100, - ZMU_NOALARM = 0x00000200, - ZMU_CANCEL = 0x00000400, - ZMU_QUERY = 0x00000800, - ZMU_BRIGHTNESS = 0x00001000, - ZMU_CONTRAST = 0x00002000, - ZMU_HUE = 0x00004000, - ZMU_COLOUR = 0x00008000, - ZMU_RELOAD = 0x00010000, - ZMU_ENABLE = 0x00100000, - ZMU_DISABLE = 0x00200000, - ZMU_SUSPEND = 0x00400000, - ZMU_RESUME = 0x00800000, - ZMU_LIST = 0x10000000, + ZMU_BOGUS = 0x00000000, + ZMU_STATE = 0x00000001, + ZMU_IMAGE = 0x00000002, + ZMU_TIME = 0x00000004, + ZMU_READ_IDX = 0x00000008, + ZMU_WRITE_IDX = 0x00000010, + ZMU_EVENT = 0x00000020, + ZMU_FPS = 0x00000040, + ZMU_ZONES = 0x00000080, + ZMU_ALARM = 0x00000100, + ZMU_NOALARM = 0x00000200, + ZMU_CANCEL = 0x00000400, + ZMU_QUERY = 0x00000800, + ZMU_BRIGHTNESS = 0x00001000, + ZMU_CONTRAST = 0x00002000, + ZMU_HUE = 0x00004000, + ZMU_COLOUR = 0x00008000, + ZMU_RELOAD = 0x00010000, + ZMU_ENABLE = 0x00100000, + ZMU_DISABLE = 0x00200000, + ZMU_SUSPEND = 0x00400000, + ZMU_RESUME = 0x00800000, + ZMU_LIST = 0x10000000, } Function; bool ValidateAccess( User *user, int mon_id, int function ) { - bool allowed = true; - if ( function & (ZMU_STATE|ZMU_IMAGE|ZMU_TIME|ZMU_READ_IDX|ZMU_WRITE_IDX|ZMU_FPS) ) - { - if ( user->getStream() < User::PERM_VIEW ) - allowed = false; - } - if ( function & ZMU_EVENT ) - { - if ( user->getEvents() < User::PERM_VIEW ) - allowed = false; - } - if ( function & (ZMU_ZONES|ZMU_QUERY|ZMU_LIST) ) - { - if ( user->getMonitors() < User::PERM_VIEW ) - allowed = false; - } - if ( function & (ZMU_ALARM|ZMU_NOALARM|ZMU_CANCEL|ZMU_RELOAD|ZMU_ENABLE|ZMU_DISABLE|ZMU_SUSPEND|ZMU_RESUME|ZMU_BRIGHTNESS|ZMU_CONTRAST|ZMU_HUE|ZMU_COLOUR) ) - { - if ( user->getMonitors() < User::PERM_EDIT ) - allowed = false; - } - if ( mon_id > 0 ) - { - if ( !user->canAccess( mon_id ) ) - { - allowed = false; - } - } - if ( !allowed ) - { - fprintf( stderr, "Error, insufficient privileges for requested action\n" ); - exit( -1 ); - } - return( allowed ); + bool allowed = true; + if ( function & (ZMU_STATE|ZMU_IMAGE|ZMU_TIME|ZMU_READ_IDX|ZMU_WRITE_IDX|ZMU_FPS) ) + { + if ( user->getStream() < User::PERM_VIEW ) + allowed = false; + } + if ( function & ZMU_EVENT ) + { + if ( user->getEvents() < User::PERM_VIEW ) + allowed = false; + } + if ( function & (ZMU_ZONES|ZMU_QUERY|ZMU_LIST) ) + { + if ( user->getMonitors() < User::PERM_VIEW ) + allowed = false; + } + if ( function & (ZMU_ALARM|ZMU_NOALARM|ZMU_CANCEL|ZMU_RELOAD|ZMU_ENABLE|ZMU_DISABLE|ZMU_SUSPEND|ZMU_RESUME|ZMU_BRIGHTNESS|ZMU_CONTRAST|ZMU_HUE|ZMU_COLOUR) ) + { + if ( user->getMonitors() < User::PERM_EDIT ) + allowed = false; + } + if ( mon_id > 0 ) + { + if ( !user->canAccess( mon_id ) ) + { + allowed = false; + } + } + if ( !allowed ) + { + fprintf( stderr, "Error, insufficient privileges for requested action\n" ); + exit( -1 ); + } + return( allowed ); } int main( int argc, char *argv[] ) { - self = argv[0]; + if ( access(ZM_CONFIG, R_OK) != 0 ) + { + fprintf( stderr, "Can't open %s: %s\n", ZM_CONFIG, strerror(errno) ); + exit( -1 ); + } - srand( getpid() * time( 0 ) ); + self = argv[0]; - static struct option long_options[] = { - {"device", 2, 0, 'd'}, - {"monitor", 1, 0, 'm'}, - {"verbose", 0, 0, 'v'}, - {"image", 2, 0, 'i'}, - {"scale", 1, 0, 'S'}, - {"timestamp", 2, 0, 't'}, - {"state", 0, 0, 's'}, - {"brightness", 2, 0, 'B'}, - {"contrast", 2, 0, 'C'}, - {"hue", 2, 0, 'H'}, - {"contrast", 2, 0, 'O'}, - {"read_index", 0, 0, 'R'}, - {"write_index", 0, 0, 'W'}, - {"event", 0, 0, 'e'}, - {"fps", 0, 0, 'f'}, - {"zones", 2, 0, 'z'}, - {"alarm", 0, 0, 'a'}, - {"noalarm", 0, 0, 'n'}, - {"cancel", 0, 0, 'c'}, - {"reload", 0, 0, 'L'}, - {"enable", 0, 0, 'E'}, - {"disable", 0, 0, 'D'}, - {"suspend", 0, 0, 'u'}, - {"resume", 0, 0, 'r'}, - {"query", 0, 0, 'q'}, - {"username", 1, 0, 'U'}, - {"password", 1, 0, 'P'}, - {"auth", 1, 0, 'A'}, - {"version", 1, 0, 'V'}, - {"help", 0, 0, 'h'}, - {"list", 0, 0, 'l'}, - {0, 0, 0, 0} - }; + srand( getpid() * time( 0 ) ); - const char *device = 0; - int mon_id = 0; - bool verbose = false; - int function = ZMU_BOGUS; + static struct option long_options[] = { + {"device", 2, 0, 'd'}, + {"monitor", 1, 0, 'm'}, + {"verbose", 0, 0, 'v'}, + {"image", 2, 0, 'i'}, + {"scale", 1, 0, 'S'}, + {"timestamp", 2, 0, 't'}, + {"state", 0, 0, 's'}, + {"brightness", 2, 0, 'B'}, + {"contrast", 2, 0, 'C'}, + {"hue", 2, 0, 'H'}, + {"contrast", 2, 0, 'O'}, + {"read_index", 0, 0, 'R'}, + {"write_index", 0, 0, 'W'}, + {"event", 0, 0, 'e'}, + {"fps", 0, 0, 'f'}, + {"zones", 2, 0, 'z'}, + {"alarm", 0, 0, 'a'}, + {"noalarm", 0, 0, 'n'}, + {"cancel", 0, 0, 'c'}, + {"reload", 0, 0, 'L'}, + {"enable", 0, 0, 'E'}, + {"disable", 0, 0, 'D'}, + {"suspend", 0, 0, 'u'}, + {"resume", 0, 0, 'r'}, + {"query", 0, 0, 'q'}, + {"username", 1, 0, 'U'}, + {"password", 1, 0, 'P'}, + {"auth", 1, 0, 'A'}, + {"version", 1, 0, 'V'}, + {"help", 0, 0, 'h'}, + {"list", 0, 0, 'l'}, + {0, 0, 0, 0} + }; - int image_idx = -1; - int scale = -1; - int brightness = -1; - int contrast = -1; - int hue = -1; - int colour = -1; - char *zoneString = 0; - char *username = 0; - char *password = 0; - char *auth = 0; + const char *device = 0; + int mon_id = 0; + bool verbose = false; + int function = ZMU_BOGUS; + + int image_idx = -1; + int scale = -1; + int brightness = -1; + int contrast = -1; + int hue = -1; + int colour = -1; + char *zoneString = 0; + char *username = 0; + char *password = 0; + char *auth = 0; #if ZM_HAS_V4L #if ZM_HAS_V4L2 - int v4lVersion = 2; + int v4lVersion = 2; #elif ZM_HAS_V4L1 - int v4lVersion = 1; + int v4lVersion = 1; #endif // ZM_HAS_V4L2/1 #endif // ZM_HAS_V4L - while (1) - { - int option_index = 0; + while (1) + { + int option_index = 0; - int c = getopt_long (argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::U:P:A:V:", long_options, &option_index); - if (c == -1) - { - break; - } + int c = getopt_long (argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::U:P:A:V:", long_options, &option_index); + if (c == -1) + { + break; + } - switch (c) - { - case 'd': - if ( optarg ) - device = optarg; - break; - case 'm': - mon_id = atoi(optarg); - break; - case 'v': - verbose = true; - break; - case 's': - function |= ZMU_STATE; - break; - case 'i': - function |= ZMU_IMAGE; - if ( optarg ) - image_idx = atoi( optarg ); - break; - case 'S': - scale = atoi(optarg); - break; - case 't': - function |= ZMU_TIME; - if ( optarg ) - image_idx = atoi( optarg ); - break; - case 'R': - function |= ZMU_READ_IDX; - break; - case 'W': - function |= ZMU_WRITE_IDX; - break; - case 'e': - function |= ZMU_EVENT; - break; - case 'f': - function |= ZMU_FPS; - break; - case 'z': - function |= ZMU_ZONES; - if ( optarg ) - zoneString = optarg; - break; - case 'a': - function |= ZMU_ALARM; - break; - case 'n': - function |= ZMU_NOALARM; - break; - case 'c': - function |= ZMU_CANCEL; - break; - case 'L': - function |= ZMU_RELOAD; - break; - case 'E': - function |= ZMU_ENABLE; - break; - case 'D': - function |= ZMU_DISABLE; - break; - case 'u': - function |= ZMU_SUSPEND; - break; - case 'r': - function |= ZMU_RESUME; - break; - case 'q': - function |= ZMU_QUERY; - break; - case 'B': - function |= ZMU_BRIGHTNESS; - if ( optarg ) - brightness = atoi( optarg ); - break; - case 'C': - function |= ZMU_CONTRAST; - if ( optarg ) - contrast = atoi( optarg ); - break; - case 'H': - function |= ZMU_HUE; - if ( optarg ) - hue = atoi( optarg ); - break; - case 'O': - function |= ZMU_COLOUR; - if ( optarg ) - colour = atoi( optarg ); - break; - case 'U': - username = optarg; - break; - case 'P': - password = optarg; - break; - case 'A': - auth = optarg; - break; + switch (c) + { + case 'd': + if ( optarg ) + device = optarg; + break; + case 'm': + mon_id = atoi(optarg); + break; + case 'v': + verbose = true; + break; + case 's': + function |= ZMU_STATE; + break; + case 'i': + function |= ZMU_IMAGE; + if ( optarg ) + image_idx = atoi( optarg ); + break; + case 'S': + scale = atoi(optarg); + break; + case 't': + function |= ZMU_TIME; + if ( optarg ) + image_idx = atoi( optarg ); + break; + case 'R': + function |= ZMU_READ_IDX; + break; + case 'W': + function |= ZMU_WRITE_IDX; + break; + case 'e': + function |= ZMU_EVENT; + break; + case 'f': + function |= ZMU_FPS; + break; + case 'z': + function |= ZMU_ZONES; + if ( optarg ) + zoneString = optarg; + break; + case 'a': + function |= ZMU_ALARM; + break; + case 'n': + function |= ZMU_NOALARM; + break; + case 'c': + function |= ZMU_CANCEL; + break; + case 'L': + function |= ZMU_RELOAD; + break; + case 'E': + function |= ZMU_ENABLE; + break; + case 'D': + function |= ZMU_DISABLE; + break; + case 'u': + function |= ZMU_SUSPEND; + break; + case 'r': + function |= ZMU_RESUME; + break; + case 'q': + function |= ZMU_QUERY; + break; + case 'B': + function |= ZMU_BRIGHTNESS; + if ( optarg ) + brightness = atoi( optarg ); + break; + case 'C': + function |= ZMU_CONTRAST; + if ( optarg ) + contrast = atoi( optarg ); + break; + case 'H': + function |= ZMU_HUE; + if ( optarg ) + hue = atoi( optarg ); + break; + case 'O': + function |= ZMU_COLOUR; + if ( optarg ) + colour = atoi( optarg ); + break; + case 'U': + username = optarg; + break; + case 'P': + password = optarg; + break; + case 'A': + auth = optarg; + break; #if ZM_HAS_V4L - case 'V': - v4lVersion = (atoi(optarg)==1)?1:2; - break; + case 'V': + v4lVersion = (atoi(optarg)==1)?1:2; + break; #endif // ZM_HAS_V4L - case 'h': - Usage( 0 ); - break; - case 'l': - function |= ZMU_LIST; - break; - case '?': - Usage(); - break; - default: - //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); - break; - } - } + case 'h': + Usage( 0 ); + break; + case 'l': + function |= ZMU_LIST; + break; + case '?': + Usage(); + break; + default: + //fprintf( stderr, "?? getopt returned character code 0%o ??\n", c ); + break; + } + } - if (optind < argc) - { - fprintf( stderr, "Extraneous options, " ); - while (optind < argc) - fprintf( stderr, "%s ", argv[optind++]); - fprintf( stderr, "\n"); - Usage(); - } + if (optind < argc) + { + fprintf( stderr, "Extraneous options, " ); + while (optind < argc) + fprintf( stderr, "%s ", argv[optind++]); + fprintf( stderr, "\n"); + Usage(); + } - if ( device && !(function&ZMU_QUERY) ) - { - fprintf( stderr, "Error, -d option cannot be used with this option\n" ); - Usage(); - } - if ( scale != -1 && !(function&ZMU_IMAGE) ) - { - fprintf( stderr, "Error, -S option cannot be used with this option\n" ); - Usage(); - } - //printf( "Monitor %d, Function %d\n", mon_id, function ); + if ( device && !(function&ZMU_QUERY) ) + { + fprintf( stderr, "Error, -d option cannot be used with this option\n" ); + Usage(); + } + if ( scale != -1 && !(function&ZMU_IMAGE) ) + { + fprintf( stderr, "Error, -S option cannot be used with this option\n" ); + Usage(); + } + //printf( "Monitor %d, Function %d\n", mon_id, function ); - zmLoadConfig(); + zmLoadConfig(); - logInit( "zmu" ); + logInit( "zmu" ); - zmSetDefaultTermHandler(); - zmSetDefaultDieHandler(); + zmSetDefaultTermHandler(); + zmSetDefaultDieHandler(); - User *user = 0; + User *user = 0; - if ( config.opt_use_auth ) - { - if ( strcmp( config.auth_relay, "none" ) == 0 ) - { - if ( !username ) - { - fprintf( stderr, "Error, username must be supplied\n" ); - exit( -1 ); - } + if ( config.opt_use_auth ) + { + if ( strcmp( config.auth_relay, "none" ) == 0 ) + { + if ( !username ) + { + fprintf( stderr, "Error, username must be supplied\n" ); + exit( -1 ); + } - if ( username ) - { - user = zmLoadUser( username ); - } - } - else - { - if ( !(username && password) && !auth ) - { - fprintf( stderr, "Error, username and password or auth string must be supplied\n" ); - exit( -1 ); - } + if ( username ) + { + user = zmLoadUser( username ); + } + } + else + { + if ( !(username && password) && !auth ) + { + fprintf( stderr, "Error, username and password or auth string must be supplied\n" ); + exit( -1 ); + } - //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) - { - if ( auth ) - { - user = zmLoadAuthUser( auth, false ); - } - } - //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) - { - if ( username && password ) - { - user = zmLoadUser( username, password ); - } - } - } - if ( !user ) - { - fprintf( stderr, "Error, unable to authenticate user\n" ); - exit( -1 ); - } - ValidateAccess( user, mon_id, function ); - } - + //if ( strcmp( config.auth_relay, "hashed" ) == 0 ) + { + if ( auth ) + { + user = zmLoadAuthUser( auth, false ); + } + } + //else if ( strcmp( config.auth_relay, "plain" ) == 0 ) + { + if ( username && password ) + { + user = zmLoadUser( username, password ); + } + } + } + if ( !user ) + { + fprintf( stderr, "Error, unable to authenticate user\n" ); + exit( -1 ); + } + ValidateAccess( user, mon_id, function ); + } + - if ( mon_id > 0 ) - { - Monitor *monitor = Monitor::Load( mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY ); - if ( monitor ) - { - if ( verbose ) - { - printf( "Monitor %d(%s)\n", monitor->Id(), monitor->Name() ); - } - if ( ! monitor->connect() ) { - Error( "Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name() ); - exit( -1 ); - } + if ( mon_id > 0 ) + { + Monitor *monitor = Monitor::Load( mon_id, function&(ZMU_QUERY|ZMU_ZONES), Monitor::QUERY ); + if ( monitor ) + { + if ( verbose ) + { + printf( "Monitor %d(%s)\n", monitor->Id(), monitor->Name() ); + } + if ( ! monitor->connect() ) { + Error( "Can't connect to capture daemon: %d %s", monitor->Id(), monitor->Name() ); + exit( -1 ); + } - char separator = ' '; - bool have_output = false; - if ( function & ZMU_STATE ) - { - Monitor::State state = monitor->GetState(); - if ( verbose ) - printf( "Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle") ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%d", state ); - have_output = true; - } - } - if ( function & ZMU_TIME ) - { - struct timeval timestamp = monitor->GetTimestamp( image_idx ); - if ( verbose ) - { - char timestamp_str[64] = "None"; - if ( timestamp.tv_sec ) - strftime( timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime( ×tamp.tv_sec ) ); - if ( image_idx == -1 ) - printf( "Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000 ); - else - printf( "Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000 ); - } - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000 ); - have_output = true; - } - } - if ( function & ZMU_READ_IDX ) - { - if ( verbose ) - printf( "Last read index: %d\n", monitor->GetLastReadIndex() ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%d", monitor->GetLastReadIndex() ); - have_output = true; - } - } - if ( function & ZMU_WRITE_IDX ) - { - if ( verbose ) - printf( "Last write index: %d\n", monitor->GetLastWriteIndex() ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%d", monitor->GetLastWriteIndex() ); - have_output = true; - } - } - if ( function & ZMU_EVENT ) - { - if ( verbose ) - printf( "Last event id: %d\n", monitor->GetLastEvent() ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%d", monitor->GetLastEvent() ); - have_output = true; - } - } - if ( function & ZMU_FPS ) - { - if ( verbose ) - printf( "Current capture rate: %.2f frames per second\n", monitor->GetFPS() ); - else - { - if ( have_output ) printf( "%c", separator ); - printf( "%.2f", monitor->GetFPS() ); - have_output = true; - } - } - if ( function & ZMU_IMAGE ) - { - if ( verbose ) - { - if ( image_idx == -1 ) - printf( "Dumping last image captured to Monitor%d.jpg", monitor->Id() ); - else - printf( "Dumping buffer image %d to Monitor%d.jpg", image_idx, monitor->Id() ); - if ( scale != -1 ) - printf( ", scaling by %d%%", scale ); - printf( "\n" ); - } - monitor->GetImage( image_idx, scale>0?scale:100 ); - } - if ( function & ZMU_ZONES ) - { - if ( verbose ) - printf( "Dumping zone image to Zones%d.jpg\n", monitor->Id() ); - monitor->DumpZoneImage( zoneString ); - } - if ( function & ZMU_ALARM ) - { - if ( verbose ) - printf( "Forcing alarm on\n" ); - monitor->ForceAlarmOn( config.forced_alarm_score, "Forced Web" ); - } - if ( function & ZMU_NOALARM ) - { - if ( verbose ) - printf( "Forcing alarm off\n" ); - monitor->ForceAlarmOff(); - } - if ( function & ZMU_CANCEL ) - { - if ( verbose ) - printf( "Cancelling forced alarm on/off\n" ); - monitor->CancelForced(); - } - if ( function & ZMU_RELOAD ) - { - if ( verbose ) - printf( "Reloading monitor settings\n" ); - monitor->actionReload(); - } - if ( function & ZMU_ENABLE ) - { - if ( verbose ) - printf( "Enabling event generation\n" ); - monitor->actionEnable(); - } - if ( function & ZMU_DISABLE ) - { - if ( verbose ) - printf( "Disabling event generation\n" ); - monitor->actionDisable(); - } - if ( function & ZMU_SUSPEND ) - { - if ( verbose ) - printf( "Suspending event generation\n" ); - monitor->actionSuspend(); - } - if ( function & ZMU_RESUME ) - { - if ( verbose ) - printf( "Resuming event generation\n" ); - monitor->actionResume(); - } - if ( function & ZMU_QUERY ) - { - char monString[16382] = ""; - monitor->DumpSettings( monString, verbose ); - printf( "%s\n", monString ); - } - if ( function & ZMU_BRIGHTNESS ) - { - if ( verbose ) - { - if ( brightness >= 0 ) - printf( "New brightness: %d\n", monitor->actionBrightness( brightness ) ); - else - printf( "Current brightness: %d\n", monitor->actionBrightness() ); - } - else - { - if ( have_output ) printf( "%c", separator ); - if ( brightness >= 0 ) - printf( "%d", monitor->actionBrightness( brightness ) ); - else - printf( "%d", monitor->actionBrightness() ); - have_output = true; - } - } - if ( function & ZMU_CONTRAST ) - { - if ( verbose ) - { - if ( contrast >= 0 ) - printf( "New brightness: %d\n", monitor->actionContrast( contrast ) ); - else - printf( "Current contrast: %d\n", monitor->actionContrast() ); - } - else - { - if ( have_output ) printf( "%c", separator ); - if ( contrast >= 0 ) - printf( "%d", monitor->actionContrast( contrast ) ); - else - printf( "%d", monitor->actionContrast() ); - have_output = true; - } - } - if ( function & ZMU_HUE ) - { - if ( verbose ) - { - if ( hue >= 0 ) - printf( "New hue: %d\n", monitor->actionHue( hue ) ); - else - printf( "Current hue: %d\n", monitor->actionHue() ); - } - else - { - if ( have_output ) printf( "%c", separator ); - if ( hue >= 0 ) - printf( "%d", monitor->actionHue( hue ) ); - else - printf( "%d", monitor->actionHue() ); - have_output = true; - } - } - if ( function & ZMU_COLOUR ) - { - if ( verbose ) - { - if ( colour >= 0 ) - printf( "New colour: %d\n", monitor->actionColour( colour ) ); - else - printf( "Current colour: %d\n", monitor->actionColour() ); - } - else - { - if ( have_output ) printf( "%c", separator ); - if ( colour >= 0 ) - printf( "%d", monitor->actionColour( colour ) ); - else - printf( "%d", monitor->actionColour() ); - have_output = true; - } - } - if ( have_output ) - { - printf( "\n" ); - } - if ( !function ) - { - Usage(); - } - delete monitor; - } - else - { - fprintf( stderr, "Error, invalid monitor id %d\n", mon_id ); - exit( -1 ); - } - } - else - { - if ( function & ZMU_QUERY ) - { + char separator = ' '; + bool have_output = false; + if ( function & ZMU_STATE ) + { + Monitor::State state = monitor->GetState(); + if ( verbose ) + printf( "Current state: %s\n", state==Monitor::ALARM?"Alarm":(state==Monitor::ALERT?"Alert":"Idle") ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%d", state ); + have_output = true; + } + } + if ( function & ZMU_TIME ) + { + struct timeval timestamp = monitor->GetTimestamp( image_idx ); + if ( verbose ) + { + char timestamp_str[64] = "None"; + if ( timestamp.tv_sec ) + strftime( timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime( ×tamp.tv_sec ) ); + if ( image_idx == -1 ) + printf( "Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000 ); + else + printf( "Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000 ); + } + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000 ); + have_output = true; + } + } + if ( function & ZMU_READ_IDX ) + { + if ( verbose ) + printf( "Last read index: %d\n", monitor->GetLastReadIndex() ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%d", monitor->GetLastReadIndex() ); + have_output = true; + } + } + if ( function & ZMU_WRITE_IDX ) + { + if ( verbose ) + printf( "Last write index: %d\n", monitor->GetLastWriteIndex() ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%d", monitor->GetLastWriteIndex() ); + have_output = true; + } + } + if ( function & ZMU_EVENT ) + { + if ( verbose ) + printf( "Last event id: %d\n", monitor->GetLastEvent() ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%d", monitor->GetLastEvent() ); + have_output = true; + } + } + if ( function & ZMU_FPS ) + { + if ( verbose ) + printf( "Current capture rate: %.2f frames per second\n", monitor->GetFPS() ); + else + { + if ( have_output ) printf( "%c", separator ); + printf( "%.2f", monitor->GetFPS() ); + have_output = true; + } + } + if ( function & ZMU_IMAGE ) + { + if ( verbose ) + { + if ( image_idx == -1 ) + printf( "Dumping last image captured to Monitor%d.jpg", monitor->Id() ); + else + printf( "Dumping buffer image %d to Monitor%d.jpg", image_idx, monitor->Id() ); + if ( scale != -1 ) + printf( ", scaling by %d%%", scale ); + printf( "\n" ); + } + monitor->GetImage( image_idx, scale>0?scale:100 ); + } + if ( function & ZMU_ZONES ) + { + if ( verbose ) + printf( "Dumping zone image to Zones%d.jpg\n", monitor->Id() ); + monitor->DumpZoneImage( zoneString ); + } + if ( function & ZMU_ALARM ) + { + if ( verbose ) + printf( "Forcing alarm on\n" ); + monitor->ForceAlarmOn( config.forced_alarm_score, "Forced Web" ); + } + if ( function & ZMU_NOALARM ) + { + if ( verbose ) + printf( "Forcing alarm off\n" ); + monitor->ForceAlarmOff(); + } + if ( function & ZMU_CANCEL ) + { + if ( verbose ) + printf( "Cancelling forced alarm on/off\n" ); + monitor->CancelForced(); + } + if ( function & ZMU_RELOAD ) + { + if ( verbose ) + printf( "Reloading monitor settings\n" ); + monitor->actionReload(); + } + if ( function & ZMU_ENABLE ) + { + if ( verbose ) + printf( "Enabling event generation\n" ); + monitor->actionEnable(); + } + if ( function & ZMU_DISABLE ) + { + if ( verbose ) + printf( "Disabling event generation\n" ); + monitor->actionDisable(); + } + if ( function & ZMU_SUSPEND ) + { + if ( verbose ) + printf( "Suspending event generation\n" ); + monitor->actionSuspend(); + } + if ( function & ZMU_RESUME ) + { + if ( verbose ) + printf( "Resuming event generation\n" ); + monitor->actionResume(); + } + if ( function & ZMU_QUERY ) + { + char monString[16382] = ""; + monitor->DumpSettings( monString, verbose ); + printf( "%s\n", monString ); + } + if ( function & ZMU_BRIGHTNESS ) + { + if ( verbose ) + { + if ( brightness >= 0 ) + printf( "New brightness: %d\n", monitor->actionBrightness( brightness ) ); + else + printf( "Current brightness: %d\n", monitor->actionBrightness() ); + } + else + { + if ( have_output ) printf( "%c", separator ); + if ( brightness >= 0 ) + printf( "%d", monitor->actionBrightness( brightness ) ); + else + printf( "%d", monitor->actionBrightness() ); + have_output = true; + } + } + if ( function & ZMU_CONTRAST ) + { + if ( verbose ) + { + if ( contrast >= 0 ) + printf( "New brightness: %d\n", monitor->actionContrast( contrast ) ); + else + printf( "Current contrast: %d\n", monitor->actionContrast() ); + } + else + { + if ( have_output ) printf( "%c", separator ); + if ( contrast >= 0 ) + printf( "%d", monitor->actionContrast( contrast ) ); + else + printf( "%d", monitor->actionContrast() ); + have_output = true; + } + } + if ( function & ZMU_HUE ) + { + if ( verbose ) + { + if ( hue >= 0 ) + printf( "New hue: %d\n", monitor->actionHue( hue ) ); + else + printf( "Current hue: %d\n", monitor->actionHue() ); + } + else + { + if ( have_output ) printf( "%c", separator ); + if ( hue >= 0 ) + printf( "%d", monitor->actionHue( hue ) ); + else + printf( "%d", monitor->actionHue() ); + have_output = true; + } + } + if ( function & ZMU_COLOUR ) + { + if ( verbose ) + { + if ( colour >= 0 ) + printf( "New colour: %d\n", monitor->actionColour( colour ) ); + else + printf( "Current colour: %d\n", monitor->actionColour() ); + } + else + { + if ( have_output ) printf( "%c", separator ); + if ( colour >= 0 ) + printf( "%d", monitor->actionColour( colour ) ); + else + printf( "%d", monitor->actionColour() ); + have_output = true; + } + } + if ( have_output ) + { + printf( "\n" ); + } + if ( !function ) + { + Usage(); + } + delete monitor; + } + else + { + fprintf( stderr, "Error, invalid monitor id %d\n", mon_id ); + exit( -1 ); + } + } + else + { + if ( function & ZMU_QUERY ) + { #if ZM_HAS_V4L - char vidString[0x10000] = ""; - bool ok = LocalCamera::GetCurrentSettings( device, vidString, v4lVersion, verbose ); - printf( "%s", vidString ); - exit( ok?0:-1 ); + char vidString[0x10000] = ""; + bool ok = LocalCamera::GetCurrentSettings( device, vidString, v4lVersion, verbose ); + printf( "%s", vidString ); + exit( ok?0:-1 ); #else // ZM_HAS_V4L - fprintf( stderr, "Error, video4linux is required for device querying\n" ); - exit( -1 ); + fprintf( stderr, "Error, video4linux is required for device querying\n" ); + exit( -1 ); #endif // ZM_HAS_V4L - } + } - if ( function & ZMU_LIST ) - { - char sql[ZM_SQL_SML_BUFSIZ]; - strncpy( sql, "select Id, Function+0 from Monitors", sizeof(sql) ); - if ( !verbose ) - { - strncat( sql, " where Function != 'None'", sizeof(sql)-strlen(sql) ); - } - strncat( sql, " order by Id asc", sizeof(sql)-strlen(sql) ); + if ( function & ZMU_LIST ) + { + std::string sql = "select Id, Function+0 from Monitors"; + if ( !verbose ) + { + sql += "where Function != 'None'"; + } + sql += " order by Id asc"; - if ( mysql_query( &dbconn, sql ) ) - { - Error( "Can't run query: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } + if ( mysql_query( &dbconn, sql.c_str() ) ) + { + Error( "Can't run query: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } - MYSQL_RES *result = mysql_store_result( &dbconn ); - if ( !result ) - { - Error( "Can't use query result: %s", mysql_error( &dbconn ) ); - exit( mysql_errno( &dbconn ) ); - } - int n_monitors = mysql_num_rows( result ); - Debug( 1, "Got %d monitors", n_monitors ); + MYSQL_RES *result = mysql_store_result( &dbconn ); + if ( !result ) + { + Error( "Can't use query result: %s", mysql_error( &dbconn ) ); + exit( mysql_errno( &dbconn ) ); + } + int n_monitors = mysql_num_rows( result ); + Debug( 1, "Got %d monitors", n_monitors ); - printf( "%4s%5s%6s%9s%14s%6s%6s%8s%8s\n", "Id", "Func", "State", "TrgState", "LastImgTim", "RdIdx", "WrIdx", "LastEvt", "FrmRate" ); - for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) - { - int mon_id = atoi(dbrow[0]); - int function = atoi(dbrow[1]); - if ( !user || user->canAccess( mon_id ) ) - { - if ( function > 1 ) - { - Monitor *monitor = Monitor::Load( mon_id, false, Monitor::QUERY ); - if ( monitor && monitor->connect() ) - { - struct timeval tv = monitor->GetTimestamp(); - printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", - monitor->Id(), - function, - monitor->GetState(), - monitor->GetTriggerState(), - tv.tv_sec, tv.tv_usec/10000, - monitor->GetLastReadIndex(), - monitor->GetLastWriteIndex(), - monitor->GetLastEvent(), - monitor->GetFPS() - ); - delete monitor; - } - } - else - { - struct timeval tv = { 0, 0 }; - printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", - mon_id, - function, - 0, - 0, - tv.tv_sec, tv.tv_usec/10000, - 0, - 0, - 0, - 0.0 - ); - } - } - } - mysql_free_result( result ); - } - } - delete user; + printf( "%4s%5s%6s%9s%14s%6s%6s%8s%8s\n", "Id", "Func", "State", "TrgState", "LastImgTim", "RdIdx", "WrIdx", "LastEvt", "FrmRate" ); + for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) + { + int mon_id = atoi(dbrow[0]); + int function = atoi(dbrow[1]); + if ( !user || user->canAccess( mon_id ) ) + { + if ( function > 1 ) + { + Monitor *monitor = Monitor::Load( mon_id, false, Monitor::QUERY ); + if ( monitor && monitor->connect() ) + { + struct timeval tv = monitor->GetTimestamp(); + printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", + monitor->Id(), + function, + monitor->GetState(), + monitor->GetTriggerState(), + tv.tv_sec, tv.tv_usec/10000, + monitor->GetLastReadIndex(), + monitor->GetLastWriteIndex(), + monitor->GetLastEvent(), + monitor->GetFPS() + ); + delete monitor; + } + } + else + { + struct timeval tv = { 0, 0 }; + printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n", + mon_id, + function, + 0, + 0, + tv.tv_sec, tv.tv_usec/10000, + 0, + 0, + 0, + 0.0 + ); + } + } + } + mysql_free_result( result ); + } + } + delete user; - return( 0 ); + logTerm(); + zmDbClose(); + + return( 0 ); } diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh new file mode 100755 index 000000000..49c91acfc --- /dev/null +++ b/utils/do_debian_package.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +if [ "$1" == "clean" ]; then + +read -p "Do you really want to delete existing packages? [y/N]" +[[ $REPLY == [yY] ]] && { rm -fr zoneminder*.build zoneminder*.changes zoneminder*.deb; echo "Existing package files deleted"; } || { echo "Packages have NOT been deleted"; } +exit; + +fi + + +DATE=`date -R` +DISTRO=$1 +SNAPSHOT=$2 +if [ "$SNAPSHOT" == "stable" ]; then +SNAPSHOT=""; +fi; + + +TYPE=$3 +if [ "$TYPE" == "" ]; then +TYPE="source"; +fi; +BRANCH=$4 + + +if [ ! -d 'zoneminder_release' ]; then + git clone https://github.com/ZoneMinder/ZoneMinder.git zoneminder_release +fi; +if [ "$BRANCH" != "" ]; then + cd zoneminder_release + if [ "$BRANCH" == "stable" ]; then + BRANCH=$(git describe --tags $(git rev-list --tags --max-count=1)); + echo "Latest stable branch is $BRANCH"; + + fi + git checkout $BRANCH + cd ../ +fi; +VERSION=`cat zoneminder_release/version` +if [ $VERSION == "" ]; then + exit 1; +fi; +echo "Doing $TYPE release zoneminder_$VERSION-$DISTRO-$SNAPSHOT"; +mv zoneminder_release zoneminder_$VERSION-$DISTRO-$SNAPSHOT.orig +cd zoneminder_$VERSION-$DISTRO-$SNAPSHOT.orig +git submodule init +git submodule update --init --recursive +if [ $DISTRO == "trusty" ]; then +ln -sf distros/ubuntu1204 debian +else +ln -sf distros/ubuntu1604 debian +fi; + +# Auto-install all ZoneMinder's depedencies using the Debian control file +sudo apt-get install devscripts equivs +sudo mk-build-deps -ir ./debian/control + +if [ -z `hostname -d` ] ; then + AUTHOR="`getent passwd $USER | cut -d ':' -f 5 | cut -d ',' -f 1` <`whoami`@`hostname`.local>" +else + AUTHOR="`getent passwd $USER | cut -d ':' -f 5 | cut -d ',' -f 1` <`whoami`@`hostname`>" +fi + +cat < debian/changelog +zoneminder ($VERSION-$DISTRO-$SNAPSHOT) $DISTRO; urgency=medium + + * + + -- $AUTHOR $DATE + +EOF +#rm -rf .git +#rm .gitignore +#cd ../ +#tar zcf zoneminder_$VERSION-$DISTRO.orig.tar.gz zoneminder_$VERSION-$DISTRO-$SNAPSHOT.orig +#cd zoneminder_$VERSION-$DISTRO-$SNAPSHOT.orig +if [ $TYPE == "binary" ]; then + debuild +else + if [ $TYPE == "local" ]; then + debuild -i -us -uc -b + else + debuild -S -sa + fi; +fi; + +cd ../ + +read -p "Do you want to keep the checked out version of Zoneminder (incase you want to modify it later) [y/N]" +[[ $REPLY == [yY] ]] && { mv zoneminder_$VERSION-$DISTRO-$SNAPSHOT.orig zoneminder_release; echo "The checked out copy is preserved in zoneminder_release"; } || { rm -fr zoneminder_$VERSION-$DISTRO-$SNAPSHOT.orig; echo "The checked out copy has been deleted"; } +echo "Done!" + + + diff --git a/utils/docker/apache-vhost b/utils/docker/apache-vhost index 622e4b5e4..5f59c01b2 100644 --- a/utils/docker/apache-vhost +++ b/utils/docker/apache-vhost @@ -6,8 +6,7 @@ AllowOverride None Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch - Order allow,deny - Allow from all + require all granted diff --git a/utils/docker/phpdate.ini b/utils/docker/phpdate.ini new file mode 100644 index 000000000..d35b47361 --- /dev/null +++ b/utils/docker/phpdate.ini @@ -0,0 +1,5 @@ +[Date] +; Defines the default timezone used by the date functions +; http://php.net/date.timezone +date.timezone = GMT + diff --git a/utils/docker/start.sh b/utils/docker/start.sh index 76ad3dcbb..0772f7b9b 100644 --- a/utils/docker/start.sh +++ b/utils/docker/start.sh @@ -1,5 +1,11 @@ #!/bin/bash +# Prepare proper amount of shared memory +# For H.264 cameras it may be necessary to increase the amout of shared memory +# to 2048 megabytes. +umount /dev/shm +mount -t tmpfs -o rw,nosuid,nodev,noexec,relatime,size=512M tmpfs /dev/shm + # Start MySQL /usr/bin/mysqld_safe & @@ -25,7 +31,13 @@ done mysql -u root < db/zm_create.sql # Add the ZoneMinder DB user -mysql -u root -e "grant insert,select,update,delete,lock tables,alter on zm.* to 'zm'@'localhost' identified by 'zm'" +mysql -u root -e "grant insert,select,update,delete,lock tables,alter on zm.* to 'zmuser'@'localhost' identified by 'zmpass';" + +# Activate CGI +a2enmod cgi + +# Activate modrewrite +a2enmod rewrite # Restart apache service apache2 restart diff --git a/utils/mk_bigfont.pl b/utils/mk_bigfont.pl new file mode 100755 index 000000000..ff2b1c30c --- /dev/null +++ b/utils/mk_bigfont.pl @@ -0,0 +1,67 @@ +#!/usr/bin/env perl +# +# ========================================================================== +# +# ZoneMinder Big Font Generate Script +# Copyright (C) 2015 Robin Därmann +# +# 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. +# +# ========================================================================== + +# Basically, this script reads src/zm_font.h and duplicates every bit of every +# character found in that file. It then duplicates every generated line, so +# that every character effectively gets double sized. +# Output goes to STDOUT so it would be useful to redirect it to a file, i.e. to +# src/zm_bigfont.c + +use strict; +use warnings; + +my $in_head = 1; + +open F, '<', '../src/zm_font.h'; + +print <) { + $in_head-- if $line =~ /^$/ and $in_head; + next while $in_head; + unless ($line =~ /^\s+(0x..), \/\* (........)/) { + $line =~ s/static unsigned char fontdata/static unsigned int bigfontdata/; + print $line; + next; + } + my $code = $1; + my $bincode = $2; + $bincode = "$1$1$2$2$3$3$4$4$5$5$6$6$7$7$8$8" if $bincode =~ /(.)(.)(.)(.)(.)(.)(.)(.)$/; + $bincode =~ s/ /1/g; + my $intcode = unpack("N", pack("B32", substr("0" x 32 . $bincode, -32))); + my $hexcode = sprintf("%#x", $intcode); + $hexcode =~ s/^0$/0x0/; + $bincode =~ s/1/ /g; + print sprintf("\t%10s, /* %s */\n", $hexcode, $bincode); + print sprintf("\t%10s, /* %s */\n", $hexcode, $bincode); +} + +close F; diff --git a/utils/zmeditconfigdata.sh b/utils/zmeditconfigdata.sh new file mode 100755 index 000000000..71b73bb6e --- /dev/null +++ b/utils/zmeditconfigdata.sh @@ -0,0 +1,100 @@ +#!/bin/bash +# This script allows the package maintainer to change the default value +# of any variable specified in ConfigData.pm without writing a patch. +# Run this script from your build folder, before running configure or cmake. + +usage() +{ +cat < /dev/null + + if [ $? -ne 0 ]; then + echo + echo "ERROR: The script cannot find the required command \"${CMD}\"." + echo + exit 1 + fi +done + +escape() +{ +escaped="" +local temp="$(printf %q "$1")" +escaped="$(echo $temp | sed 's/\//\\\//g')" +} + +# Assign variables once they are properly escaped +escape $1 +variable=$escaped +escape $2 +default=$escaped + +# Set the path to ConfigData +if [ -n "$3" ]; then + configdata="$3/ConfigData.pm.in" +else + configdata="./scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in" +fi + +# Check to make sure we can find ConfigData +if [ ! -e "$configdata" ]; then + echo "CONFIGDATA FILE NOT FOUND: $configdata" + exit 1 +fi + +# Now that we've found ConfidData, verify the supplied variable +# is defined inside the ConfigData file. +if [ -z "$(grep $variable $configdata)" ]; then + echo "ZONEMINDER VARIABLE NOT FOUND: $variable" + exit 1 +fi + +# Update the supplied variable with the new default value. +# Don't stare too closely. You will burn your eyes out. +sed -i '/.*'${variable}'.*/{ + $!{ N + s/\(.*'${variable}'.*\n.*\)\"\(.*\)\"/\1\"'${default}'\"/ + t yes + P + D + :yes + + } + }' $configdata + +if [ "$?" != "0" ]; then + echo "SED RETURNED FAILURE" + exit 1 +fi + + + diff --git a/version b/version index cfc730712..034552a83 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.28.0 +1.30.0 diff --git a/web/CMakeLists.txt b/web/CMakeLists.txt index f7bb3ec0c..b9e75a7e8 100644 --- a/web/CMakeLists.txt +++ b/web/CMakeLists.txt @@ -17,6 +17,7 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/includes/config.php" DESTINATION "${Z if(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/api/app/Config/core.php" DESTINATION "${ZM_WEBDIR}/api/app/Config") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/api/app/Config/database.php" DESTINATION "${ZM_WEBDIR}/api/app/Config") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/api/app/Config/bootstrap.php" DESTINATION "${ZM_WEBDIR}/api/app/Config") endif(NOT (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)) # Install the mootools symlinks (if its not in the source directory) diff --git a/web/Makefile.am b/web/Makefile.am deleted file mode 100644 index 077a4ff91..000000000 --- a/web/Makefile.am +++ /dev/null @@ -1,35 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -# This should be set to your web directory -webdir = @WEB_PREFIX@ -# And these to the user and group of your webserver -webuser = @WEB_USER@ -webgroup = @WEB_GROUP@ - -SUBDIRS = \ - ajax \ - css \ - graphics \ - includes \ - js \ - lang \ - skins \ - tools \ - views - -dist_web_DATA = \ - index.php - -# Yes, you are correct. This is a HACK! -install-data-hook: - ( cd $(DESTDIR)$(webdir); chown $(webuser):$(webgroup) $(dist_web_DATA) ) - ( cd $(DESTDIR)$(webdir); chown -R $(webuser):$(webgroup) $(SUBDIRS) ) - @-( cd $(DESTDIR)$(webdir); if ! test -e events; then mkdir events; fi; chown $(webuser):$(webgroup) events; chmod u+w events ) - @-( cd $(DESTDIR)$(webdir); if ! test -e images; then mkdir images; fi; chown $(webuser):$(webgroup) images; chmod u+w images ) - @-( cd $(DESTDIR)$(webdir); if ! test -e sounds; then mkdir sounds; fi; chown $(webuser):$(webgroup) sounds; chmod u+w sounds ) - @-( cd $(DESTDIR)$(webdir); if ! test -e tools; then mkdir tools; fi; chown $(webuser):$(webgroup) tools; chmod u+w tools ) - @-( cd $(DESTDIR)$(webdir); if ! test -e temp; then mkdir temp; fi; chown $(webuser):$(webgroup) temp; chmod u+w temp ) - -uninstall-hook: - @-( cd $(DESTDIR)$(webdir); rm -rf $(SUBDIRS) ) - @-( cd $(DESTDIR)$(webdir); rm -rf events images sounds tools temp ) diff --git a/web/ajax/Makefile.am b/web/ajax/Makefile.am deleted file mode 100644 index ef79e6cae..000000000 --- a/web/ajax/Makefile.am +++ /dev/null @@ -1,12 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/ajax - -dist_web_DATA = \ - alarm.php \ - control.php \ - event.php \ - log.php \ - status.php \ - stream.php \ - zone.php diff --git a/web/ajax/alarm.php b/web/ajax/alarm.php index b1f5f1496..2d4690866 100644 --- a/web/ajax/alarm.php +++ b/web/ajax/alarm.php @@ -1,5 +1,4 @@ diff --git a/web/ajax/control.php b/web/ajax/control.php index 7e2625b70..abdc8c8ef 100644 --- a/web/ajax/control.php +++ b/web/ajax/control.php @@ -1,5 +1,6 @@ Id().'.sock'; if ( @socket_connect( $socket, $sock_file ) ) { $options = array(); @@ -36,7 +37,7 @@ if ( canView( 'Control', $_REQUEST['id'] ) ) } else { - $ctrlCommand .= " --id=".$monitor['Id']; + $ctrlCommand .= " --id=".$monitor->Id(); // Can't connect so use script $ctrlStatus = ''; diff --git a/web/ajax/event.php b/web/ajax/event.php index d5cc2b09d..1953acab0 100644 --- a/web/ajax/event.php +++ b/web/ajax/event.php @@ -1,4 +1,6 @@ Id()] = $server; + } + $minTime = isset($_POST['minTime'])?$_POST['minTime']:NULL; $maxTime = isset($_POST['maxTime'])?$_POST['maxTime']:NULL; $limit = isset($_POST['limit'])?$_POST['limit']:100; @@ -38,7 +45,7 @@ switch ( $_REQUEST['task'] ) $sortField = isset($_POST['sortField'])?$_POST['sortField']:'TimeKey'; $sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc':'desc'; - $filterFields = array( 'Component', 'Pid', 'Level', 'File', 'Line' ); + $filterFields = array( 'Component', 'ServerId', 'Pid', 'Level', 'File', 'Line' ); $total = dbFetchOne( "SELECT count(*) AS Total FROM Logs", 'Total' ); $sql = 'SELECT * FROM Logs'; @@ -66,6 +73,7 @@ switch ( $_REQUEST['task'] ) $logs = array(); foreach ( dbFetchAll( $sql, NULL, $values ) as $log ) { $log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] ); + $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $logs[] = $log; } $options = array(); @@ -96,6 +104,12 @@ switch ( $_REQUEST['task'] ) else $options[$field][$value] = "DB".$value; } + elseif ( $field == 'ServerId' ) + { + foreach( dbFetchAll( $sql, $field, array_values($fieldValues) ) as $value ) + $options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : ''; + + } else { foreach( dbFetchAll( $sql, $field, array_values( $fieldValues ) ) as $value ) @@ -136,6 +150,13 @@ switch ( $_REQUEST['task'] ) $sortField = isset($_POST['sortField'])?$_POST['sortField']:'TimeKey'; $sortOrder = isset($_POST['sortOrder'])?$_POST['sortOrder']:'asc'; + $servers = Server::find_all(); + $servers_by_Id = array(); + # There is probably a better way to do this. + foreach ( $servers as $server ) { + $servers_by_Id[$server->Id()] = $server; + } + $sql = "select * from Logs"; $where = array(); $values = array(); @@ -188,13 +209,14 @@ switch ( $_REQUEST['task'] ) } $exportKey = substr(md5(rand()),0,8); $exportFile = "zm-log.$exportExt"; - $exportPath = "temp/zm-log-$exportKey.$exportExt"; + $exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt"; if ( !($exportFP = fopen( $exportPath, "w" )) ) - Fatal( "Unable to open log export file $exportFile" ); + Fatal( "Unable to open log export file $exportPath" ); $logs = array(); foreach ( dbFetchAll( $sql, NULL, $values ) as $log ) { $log['DateTime'] = preg_replace( '/^\d+/', strftime( "%Y-%m-%d %H:%M:%S", intval($log['TimeKey']) ), $log['TimeKey'] ); + $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $logs[] = $log; } switch( $format ) @@ -212,10 +234,20 @@ switch ( $_REQUEST['task'] ) } case 'tsv' : { - fprintf( $exportFP, $SLANG['DateTime']."\t".$SLANG['Component']."\t".$SLANG['Pid']."\t".$SLANG['Level']."\t".$SLANG['Message']."\t".$SLANG['File']."\t".$SLANG['Line']."\n" ); + # This line doesn't need fprintf, it could use fwrite + fprintf( $exportFP, join( "\t", + translate('DateTime'), + translate('Component'), + translate('Server'), + translate('Pid'), + translate('Level'), + translate('Message'), + translate('File'), + translate('Line') + )."\n" ); foreach ( $logs as $log ) { - fprintf( $exportFP, "%s\t%s\t%d\t%s\t%s\t%s\t%s\n", $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); + fprintf( $exportFP, "%s\t%s\t%s\t%d\t%s\t%s\t%s\t%s\n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); } break; } @@ -225,7 +257,7 @@ switch ( $_REQUEST['task'] ) ' - '.$SLANG['ZoneMinderLog'].' + '.translate('ZoneMinderLog').' -

'.$SLANG['ZoneMinderLog'].'

+

'.translate('ZoneMinderLog').'

'.htmlspecialchars(preg_match( '/%/', DATE_FMT_CONSOLE_LONG )?strftime( DATE_FMT_CONSOLE_LONG ):date( DATE_FMT_CONSOLE_LONG )).'

-

'.count($logs).' '.$SLANG['Logs'].'

+

'.count($logs).' '.translate('Logs').'

- + ' ); foreach ( $logs as $log ) { @@ -275,7 +307,7 @@ tr.log-dbg td { elseif ( $classLevel > Logger::DEBUG ) $classLevel = Logger::DEBUG; $logClass = 'log-'.strtolower(Logger::$codes[$classLevel]); - fprintf( $exportFP, " \n", $logClass, $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); + fprintf( $exportFP, " \n", $logClass, $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], $log['Message'], $log['File'], $log['Line'] ); } fwrite( $exportFP, ' @@ -288,7 +320,7 @@ tr.log-dbg td { { fwrite( $exportFP, ' - + '.$_POST['selector'].'' ); foreach ( $filter as $field=>$value ) if ( $value != '' ) @@ -298,7 +330,7 @@ tr.log-dbg td { ' ); fwrite( $exportFP, ' - '.$SLANG['DateTime'].''.$SLANG['Component'].''.$SLANG['Pid'].''.$SLANG['Level'].''.$SLANG['Message'].''.$SLANG['File'].''.$SLANG['Line'].' + '.translate('DateTime').''.translate('Component').''.translate('Pid').''.translate('Level').''.translate('Message').''.translate('File').''.translate('Line').' ' ); @@ -308,12 +340,13 @@ tr.log-dbg td { " %s %s + %s %d %s %s %d - \n", $log['DateTime'], $log['Component'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] ); + \n", $log['DateTime'], $log['Component'], $log['Server'], $log['Pid'], $log['Code'], utf8_decode( $log['Message'] ), $log['File'], $log['Line'] ); } fwrite( $exportFP, ' @@ -361,7 +394,7 @@ tr.log-dbg td { } $exportFile = "zm-log.$exportExt"; - $exportPath = "temp/zm-log-$exportKey.$exportExt"; + $exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt"; header( "Pragma: public" ); header( "Expires: 0" ); diff --git a/web/ajax/status.php b/web/ajax/status.php index b29460365..f7ed3d099 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -8,7 +8,7 @@ $statusData = array( "elements" => array( "MonitorCount" => array( "sql" => "count(*)" ), "ActiveMonitorCount" => array( "sql" => "count(if(Function != 'None',1,NULL))" ), - "State" => array( "func" => "daemonCheck()?".$SLANG['Running'].":".$SLANG['Stopped'] ), + "State" => array( "func" => "daemonCheck()?".translate('Running').":".translate('Stopped') ), "Load" => array( "func" => "getLoad()" ), "Disk" => array( "func" => "getDiskPercent()" ), ), @@ -44,6 +44,7 @@ $statusData = array( "LabelFormat" => true, "LabelX" => true, "LabelY" => true, + "LabelSize" => true, "ImageBufferCount" => true, "WarmupCount" => true, "PreEventCount" => true, diff --git a/web/ajax/stream.php b/web/ajax/stream.php index 398f81147..5e9798011 100644 --- a/web/ajax/stream.php +++ b/web/ajax/stream.php @@ -48,7 +48,7 @@ switch ( $_REQUEST['command'] ) $remSockFile = ZM_PATH_SOCKS.'/zms-'.sprintf("%06d",$_REQUEST['connkey']).'s.sock'; $max_socket_tries = 10; while ( !file_exists($remSockFile) && $max_socket_tries-- ) //sometimes we are too fast for our own good, if it hasn't been setup yet give it a second. - sleep(1); + usleep(200000); if ( !@socket_sendto( $socket, $msg, strlen($msg), 0, $remSockFile ) ) { diff --git a/web/ajax/zone.php b/web/ajax/zone.php index 905ef7ac1..bfa77a922 100644 --- a/web/ajax/zone.php +++ b/web/ajax/zone.php @@ -17,7 +17,7 @@ if ( canView( 'Monitors' ) ) { $wd = getcwd(); chdir( ZM_DIR_IMAGES ); - $hiColor = "0x00ff00"; + $hiColor = '0x00ff00'; $command = getZmuCommand( " -m ".$_REQUEST['mid']." -z" ); if ( !isset($_REQUEST['zid']) ) diff --git a/web/api/.htaccess b/web/api/.htaccess index 6cc85be06..7139a2766 100644 --- a/web/api/.htaccess +++ b/web/api/.htaccess @@ -2,4 +2,5 @@ RewriteEngine on RewriteRule ^$ app/webroot/ [L] RewriteRule (.*) app/webroot/$1 [L] + RewriteBase /zm/api diff --git a/web/api/CMakeLists.txt b/web/api/CMakeLists.txt index 56bc82a50..3d865527b 100644 --- a/web/api/CMakeLists.txt +++ b/web/api/CMakeLists.txt @@ -10,3 +10,6 @@ configure_file(app/Config/database.php.default "${CMAKE_CURRENT_BINARY_DIR}/app/ # Configure core.php configure_file(app/Config/core.php.default "${CMAKE_CURRENT_BINARY_DIR}/app/Config/core.php" @ONLY) + +# Configure bootstrap.php +configure_file(app/Config/bootstrap.php.in "${CMAKE_CURRENT_BINARY_DIR}/app/Config/bootstrap.php" @ONLY) diff --git a/web/api/README.md b/web/api/README.md index 6a1d6a325..30b0b5cf5 100644 --- a/web/api/README.md +++ b/web/api/README.md @@ -11,4 +11,4 @@ In adition, Security.salt and Security.cipherSeed in app/Config/core.php should be changed. The API can run on a dedicated / separate instance, so long as it can access -the database as ocnfigured in app/Config/database.php +the database as configured in app/Config/database.php diff --git a/web/api/app/.htaccess b/web/api/app/.htaccess index fc3aac4b2..1af74d971 100644 --- a/web/api/app/.htaccess +++ b/web/api/app/.htaccess @@ -2,4 +2,5 @@ RewriteEngine on RewriteRule ^$ webroot/ [L] RewriteRule (.*) webroot/$1 [L] - \ No newline at end of file + RewriteBase /zm/api + diff --git a/web/api/app/Config/bootstrap.php b/web/api/app/Config/bootstrap.php.in similarity index 79% rename from web/api/app/Config/bootstrap.php rename to web/api/app/Config/bootstrap.php.in index b8932f3a6..7210fd35c 100644 --- a/web/api/app/Config/bootstrap.php +++ b/web/api/app/Config/bootstrap.php.in @@ -69,6 +69,7 @@ Cache::config('default', array('engine' => 'File')); * CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit * */ +CakePlugin::load('Crud'); /** * You can attach event listeners to the request lifecycle as Dispatcher Filter. By default CakePHP bundles two filters: @@ -106,3 +107,39 @@ CakeLog::config('error', array( 'types' => array('warning', 'error', 'critical', 'alert', 'emergency'), 'file' => 'error', )); + +Configure::write('ZM_CONFIG', '@ZM_CONFIG@'); +Configure::write('ZM_VERSION', '@VERSION@'); +Configure::write('ZM_API_VERSION', '@API_VERSION@'); + +loadConfigFile(); + +function loadConfigFile() { + $configFile = Configure::read('ZM_CONFIG'); + $localConfigFile = basename($configFile); + if ( file_exists( $localConfigFile ) && filesize( $localConfigFile ) > 0 ) + { + if ( php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR']) ) + print( "Warning, overriding installed $localConfigFile file with local copy\n" ); + else + error_log( "Warning, overriding installed $localConfigFile file with local copy" ); + $configFile = $localConfigFile; + } + + $cfg = fopen( $configFile, "r") or die("Could not open config file."); + while ( !feof($cfg) ) + { + $str = fgets( $cfg, 256 ); + if ( preg_match( '/^\s*$/', $str )) + continue; + elseif ( preg_match( '/^\s*#/', $str )) + continue; + elseif ( preg_match( '/^\s*([^=\s]+)\s*=\s*(.*?)\s*$/', $str, $matches )) { + Configure::write( $matches[1], $matches[2] ); + define( $matches[1], $matches[2] ); + } + } + fclose( $cfg ); +} + + diff --git a/web/api/app/Config/core.php.default b/web/api/app/Config/core.php.default index 156f404df..43736a61f 100644 --- a/web/api/app/Config/core.php.default +++ b/web/api/app/Config/core.php.default @@ -216,7 +216,8 @@ * */ Configure::write('Session', array( - 'defaults' => 'php' + 'defaults' => 'php', + 'cookie'=>'ZMSESSID' )); /** diff --git a/web/api/app/Config/database.php.default b/web/api/app/Config/database.php.default index 184cf7280..be4e2a902 100644 --- a/web/api/app/Config/database.php.default +++ b/web/api/app/Config/database.php.default @@ -67,10 +67,9 @@ class DATABASE_CONFIG { public $default = array( 'datasource' => 'Database/Mysql', 'persistent' => false, - 'host' => '@ZM_DB_HOST@', - 'login' => '@ZM_DB_USER@', - 'password' => '@ZM_DB_PASS@', - 'database' => '@ZM_DB_NAME@', + 'login' => ZM_DB_USER, + 'password' => ZM_DB_PASS, + 'database' => ZM_DB_NAME, 'prefix' => '', //'encoding' => 'utf8', ); @@ -85,4 +84,18 @@ class DATABASE_CONFIG { 'prefix' => '', //'encoding' => 'utf8', ); + + public function __construct() { + if (strpos(ZM_DB_HOST, ':')): + $array = explode(':', ZM_DB_HOST, 2); + if (is_numeric($array[1])): + $this->default['host'] = $array[0]; + $this->default['port'] = $array[1]; + else: + $this->default['unix_socket'] = $array[1]; + endif; + else: + $this->default['host'] = ZM_DB_HOST; + endif; + } } diff --git a/web/api/app/Config/routes.php b/web/api/app/Config/routes.php index ed2d1f2ca..0f9343644 100644 --- a/web/api/app/Config/routes.php +++ b/web/api/app/Config/routes.php @@ -28,6 +28,14 @@ Router::mapResources('configs'); Router::mapResources('events'); Router::mapResources('frames'); + Router::mapResources('host'); + Router::mapResources('logs'); + Router::mapResources('states'); + Router::mapResources('zonepresets'); + + /* Add new API to retrieve camera controls - for PTZ */ + /* refer to https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-105233112 */ + Router::mapResources('controls'); Router::parseExtensions(); /** diff --git a/web/api/app/Controller/AppController.php b/web/api/app/Controller/AppController.php index a827969e1..5b39597b3 100644 --- a/web/api/app/Controller/AppController.php +++ b/web/api/app/Controller/AppController.php @@ -18,8 +18,8 @@ * @since CakePHP(tm) v 0.2.9 * @license http://www.opensource.org/licenses/mit-license.php MIT License */ - App::uses('Controller', 'Controller'); +App::uses('CrudControllerTrait', 'Crud.Lib'); /** * Application Controller @@ -31,4 +31,90 @@ App::uses('Controller', 'Controller'); * @link http://book.cakephp.org/2.0/en/controllers.html#the-app-controller */ class AppController extends Controller { + use CrudControllerTrait; + + public $components = [ + 'Session', // We are going to use SessionHelper to check PHP session vars + 'RequestHandler', + 'Crud.Crud' => [ + 'actions' => [ + 'index' => 'Crud.Index', + 'add' => 'Crud.Add', + 'edit' => 'Crud.Edit', + 'view' => 'Crud.View', + 'keyvalue' => 'Crud.List', + 'category' => 'Crud.Category' + ], + 'listeners' => ['Api', 'ApiTransformation'] + ] + ]; + + // Global beforeFilter function + //Zoneminder sets the username session variable + // to the logged in user. If this variable is set + // then you are logged in + // its pretty simple to extend this to also check + // for role and deny API access in future + // Also checking to do this only if ZM_OPT_USE_AUTH is on + public function beforeFilter() { + $this->loadModel('Config'); + + $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_API')); + $config = $this->Config->find('first', $options); + $zmOptApi = $config['Config']['Value']; + + if ($zmOptApi !='1') + { + throw new UnauthorizedException(__('API Disabled')); + return; + } + + $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH')); + $config = $this->Config->find('first', $options); + $zmOptAuth = $config['Config']['Value']; + if (!$this->Session->Read('user.Username') && ($zmOptAuth=='1')) + { + throw new UnauthorizedException(__('Not Authenticated')); + return; + } + else + { + $this->loadModel('User'); + $loggedinUser = $this->Session->Read('user.Username'); + $isEnabled = $this->Session->Read('user.Enabled'); + // this will likely never happen as if its + // not enabled, login will fail and Not Auth will be returned + // however, keeping this here for now + if ($isEnabled != "1" && $zmOptAuth=="1") + { + throw new UnauthorizedException(__('User is not enabled')); + return; + } + + if ($zmOptAuth=='1') + { + $options = array ('conditions' => array ('User.Username' => $loggedinUser)); + $userMonitors = $this->User->find('first', $options); + $this->Session->Write('allowedMonitors',$userMonitors['User']['MonitorIds']); + $this->Session->Write('streamPermission',$userMonitors['User']['Stream']); + $this->Session->Write('eventPermission',$userMonitors['User']['Events']); + $this->Session->Write('controlPermission',$userMonitors['User']['Control']); + $this->Session->Write('systemPermission',$userMonitors['User']['System']); + $this->Session->Write('monitorPermission',$userMonitors['User']['Monitors']); + } + else // if auth is not on, you can do everything + { + //$userMonitors = $this->User->find('first', $options); + $this->Session->Write('allowedMonitors',''); + $this->Session->Write('streamPermission','View'); + $this->Session->Write('eventPermission','Edit'); + $this->Session->Write('controlPermission','Edit'); + $this->Session->Write('systemPermission','Edit'); + $this->Session->Write('monitorPermission','Edit'); + } + } + + + } + } diff --git a/web/api/app/Controller/Component/ConfigParserComponent.php b/web/api/app/Controller/Component/ConfigParserComponent.php new file mode 100644 index 000000000..56a2039a0 --- /dev/null +++ b/web/api/app/Controller/Component/ConfigParserComponent.php @@ -0,0 +1,80 @@ +$hint"; + } + return $string; + } + + public function getLabel($name) { + $width = 'col-md-4'; + + $string = '
'; + $string .= ''; + $label = sprintf($string, $name, $width, $name); + $label .= '
'; + + return $label; + } + + public function getInput($name, $type, $id) { + if ($type == 'checkbox') { + $string = '
'; + } elseif ($type == 'text') { + $string = '
'; + } elseif ($type == 'textarea') { + $string = ''; + } elseif ($type == 'select') { + $string = ' '; + } + break; + default: + $string .= $this->getInput($name, 'text', $id); + } + $string .= "\n"; + } + return $string; + } + + +} +?> diff --git a/web/api/app/Controller/Component/FilterComponent.php b/web/api/app/Controller/Component/FilterComponent.php new file mode 100644 index 000000000..bf6e423f7 --- /dev/null +++ b/web/api/app/Controller/Component/FilterComponent.php @@ -0,0 +1,34 @@ + $value) { + // If the named param contains an array, we want to turn it into an IN condition + // Otherwise, we add it right into the $conditions array + if (is_array($value)) { + $array = array(); + + foreach ($value as $term) { + array_push($array, $term); + } + + $query = array($attribute => $array); + array_push($conditions, $query); + } else { + array_push($conditions, array($attribute => $value)); + } + } + + } + + return $conditions; + } + +} +?> diff --git a/web/api/app/Controller/Component/ImageComponent.php b/web/api/app/Controller/Component/ImageComponent.php new file mode 100644 index 000000000..f8ed7a533 --- /dev/null +++ b/web/api/app/Controller/Component/ImageComponent.php @@ -0,0 +1,100 @@ +getEventPath($event); + + $captImage = sprintf( "%0".$config['ZM_EVENT_IMAGE_DIGITS']."d-capture.jpg", $frame['Frame']['FrameId'] ); + $captPath = $eventPath.'/'.$captImage; + $thumbCaptPath = $config['ZM_DIR_IMAGES'].'/'.$event['Event']['Id'].'-'.$captImage; + + $analImage = sprintf( "%0".$config['ZM_EVENT_IMAGE_DIGITS']."d-analyse.jpg", $frame['Frame']['FrameId'] ); + $analPath = $eventPath.'/'.$analImage; + $analFile = $config['ZM_DIR_EVENTS']."/".$analPath; + $thumbAnalPath = $config['ZM_DIR_IMAGES'].'/'.$event['Event']['Id'].'-'.$analImage; + + $alarmFrame = $frame['Frame']['Type']=='Alarm'; + + $hasAnalImage = $alarmFrame && file_exists( $analFile ) && filesize( $analFile ); + $isAnalImage = $hasAnalImage && !$captureOnly; + + + if ( !$config['ZM_WEB_SCALE_THUMBS'] || $scale >= 100 || !function_exists( 'imagecreatefromjpeg' ) ) { + $imagePath = $thumbPath = $isAnalImage?$analPath:$captPath; + $imageFile = $config['ZM_DIR_EVENTS']."/".$imagePath; + $thumbFile = $config['ZM_DIR_EVENTS']."/".$thumbPath; + } else { + if ( version_compare( phpversion(), "4.3.10", ">=") ) + $fraction = sprintf( "%.3F", $scale/100 ); + else + $fraction = sprintf( "%.3f", $scale/100 ); + $scale = (int)round( $scale ); + + $thumbCaptPath = preg_replace( "/\.jpg$/", "-$scale.jpg", $thumbCaptPath ); + $thumbAnalPath = preg_replace( "/\.jpg$/", "-$scale.jpg", $thumbAnalPath ); + + if ( $isAnalImage ) + { + $imagePath = $analPath; + $thumbPath = $thumbAnalPath; + } + else + { + $imagePath = $captPath; + $thumbPath = $thumbCaptPath; + } + + $imageFile = $config['ZM_DIR_EVENTS']."/".$imagePath; + //$thumbFile = ZM_DIR_EVENTS."/".$thumbPath; + $thumbFile = $thumbPath; + // This segment of code results in errors when trying to get Events API + // This actually seems to be generating images for the angular UI web view + // and should not be a part of the API anyway + // I've commented it so events APIs continue to work + // I did ask Kyle about this, but I don't have an answer from him + // Either way, it does no harm to remove it -- as the UI of master + // does not use API code anyway + /* + if ( $overwrite || !file_exists( $thumbFile ) || !filesize( $thumbFile ) ) + { + // Get new dimensions + list( $imageWidth, $imageHeight ) = getimagesize( $imageFile ); + $thumbWidth = $imageWidth * $fraction; + $thumbHeight = $imageHeight * $fraction; + + // Resample + $thumbImage = imagecreatetruecolor( $thumbWidth, $thumbHeight ); + $image = imagecreatefromjpeg( $imageFile ); + imagecopyresampled( $thumbImage, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $imageWidth, $imageHeight ); + + if ( !imagejpeg( $thumbImage, $thumbFile ) ) + Error( "Can't create thumbnail '$thumbPath'" ); + } + */ + } + + /* + $imageData = array( + 'eventPath' => $eventPath, + 'imagePath' => $imagePath, + 'thumbPath' => $thumbPath, + 'imageFile' => $imageFile, + 'thumbFile' => $thumbFile, + 'imageClass' => $alarmFrame?"alarm":"normal", + 'isAnalImage' => $isAnalImage, + 'hasAnalImage' => $hasAnalImage, + ); + + return( $imageData ); + */ + + } + + // Take the StartTime of an Event and return + // the path to its location on the filesystem + public function getEventPath( $event ) { + return $event['Event']['MonitorId'].'/'.strftime( "%y/%m/%d/%H/%M/%S", strtotime($event['Event']['StartTime']) ); + } +} +?> diff --git a/web/api/app/Controller/Component/ScalerComponent.php b/web/api/app/Controller/Component/ScalerComponent.php new file mode 100644 index 000000000..30678dad6 --- /dev/null +++ b/web/api/app/Controller/Component/ScalerComponent.php @@ -0,0 +1,27 @@ + diff --git a/web/api/app/Controller/ConfigsController.php b/web/api/app/Controller/ConfigsController.php index a6759a512..48f50ae8e 100644 --- a/web/api/app/Controller/ConfigsController.php +++ b/web/api/app/Controller/ConfigsController.php @@ -15,18 +15,21 @@ class ConfigsController extends AppController { public $components = array('RequestHandler'); /** + * resolves the issue of not returning all config parameters + * refer https://github.com/ZoneMinder/ZoneMinder/issues/953 * index method * * @return void - */ - public function index() { - $this->Config->recursive = 0; - $configs = $this->Config->find('all'); - $this->set(array( - 'configs' => $configs, - '_serialize' => array('configs') - )); - } + */ + public function index() { + $this->Config->recursive = 0; + $configs = $this->Config->find('all'); + $this->set(array( + 'configs' => $configs, + '_serialize' => array('configs') + )); + } + /** * view method @@ -47,6 +50,19 @@ class ConfigsController extends AppController { )); } + public function viewByName($name = null) { + $config = $this->Config->findByName($name, array('fields' => 'Value')); + + if (!$config) { + throw new NotFoundException(__('Invalid config')); + } + + $this->set(array( + 'config' => $config['Config'], + '_serialize' => array('config') + )); + } + /** * edit method * @@ -88,4 +104,24 @@ class ConfigsController extends AppController { } else { return $this->flash(__('The config could not be deleted. Please, try again.'), array('action' => 'index')); } - }} + } + +/** + * categories method + * + * return a list of distinct categories + */ + + public function categories($category = null) { + $categories = $this->Config->find('all', array( + 'fields' => array('DISTINCT Config.Category'), + 'conditions' => array('Config.Category !=' => 'hidden'), + 'recursive' => 0 + )); + $this->set(array( + 'categories' => $categories, + '_serialize' => array('categories') + )); + } +} + diff --git a/web/api/app/Controller/ControlsController.php b/web/api/app/Controller/ControlsController.php new file mode 100644 index 000000000..879142f75 --- /dev/null +++ b/web/api/app/Controller/ControlsController.php @@ -0,0 +1,59 @@ +Control->recursive = 0; + $controls = $this->Control->find('all'); + $this->set(array( + 'controls' => $controls, + '_serialize' => array('controls') + )); + } + +/** + * view method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function view($id = null) { + if (!$this->Control->exists($id)) { + throw new NotFoundException(__('Invalid control')); + } + $options = array('conditions' => array('Control.' . $this->Control->primaryKey => $id)); + $control = $this->Control->find('first', $options); + $this->set(array( + 'control' => $control, + '_serialize' => array('control') + )); + } +} + diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index 394e61caf..f3e054c42 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -12,20 +12,79 @@ class EventsController extends AppController { * * @var array */ - public $components = array('RequestHandler'); + public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator'); + +public function beforeFilter() { + parent::beforeFilter(); + $canView = $this->Session->Read('eventPermission'); + if ($canView =='None') + { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + +} /** * index method * * @return void + * This also creates a thumbnail for each event. */ public function index() { $this->Event->recursive = -1; - $events = $this->Event->find('all'); - $this->set(array( - 'events' => $events, - '_serialize' => array('events') + + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + + if (!empty($allowedMonitors)) + { + $mon_options = array('Event.MonitorId' => $allowedMonitors); + } + else + { + $mon_options=''; + } + + + if ($this->request->params['named']) { + $this->FilterComponent = $this->Components->load('Filter'); + $conditions = $this->FilterComponent->buildFilter($this->request->params['named']); + } else { + $conditions = array(); + } + + // How many events to return + $this->loadModel('Config'); + $limit = $this->Config->find('list', array( + 'conditions' => array('Name' => 'ZM_WEB_EVENTS_PER_PAGE'), + 'fields' => array('Name', 'Value') )); + $this->Paginator->settings = array( + // https://github.com/ZoneMinder/ZoneMinder/issues/995 + // 'limit' => $limit['ZM_WEB_EVENTS_PER_PAGE'], + // 25 events per page which is what the above + // default is, is way too low for an API + // changing this to 100 so we don't kill ZM + // with many event APIs. In future, we can + // make a nice ZM_API_ITEMS_PER_PAGE for all pagination + // API + + 'limit' => '100', + 'order' => array('StartTime', 'MaxScore'), + 'paramType' => 'querystring', + 'conditions' => array (array($conditions, $mon_options)) + ); + $events = $this->Paginator->paginate('Event'); + + // For each event, get its thumbnail data (path, width, height) + foreach ($events as $key => $value) { + //$thumbData = $this->createThumbnail($value['Event']['Id']); + $thumbData = ""; + $events[$key]['thumbData'] = $thumbData; + + } + + $this->set(compact('events')); } /** @@ -35,18 +94,55 @@ class EventsController extends AppController { * @param string $id * @return void */ - public function view($id = null) { - $this->Event->recursive = -1; - if (!$this->Event->exists($id)) { - throw new NotFoundException(__('Invalid event')); - } - $options = array('conditions' => array('Event.' . $this->Event->primaryKey => $id)); - $event = $this->Event->find('first', $options); - $this->set(array( - 'event' => $event, - '_serialize' => array('event') - )); - } + public function view($id = null) +{ + $this->loadModel('Config'); + $configs = $this->Config->find('list', array( + 'fields' => array('Name', 'Value'), + 'conditions' => array('Name' => array('ZM_DIR_EVENTS')) + )); + + $this->Event->recursive = 1; + if (!$this->Event->exists($id)) { + throw new NotFoundException(__('Invalid event')); + } + + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + + if (!empty($allowedMonitors)) + { + $mon_options = array('Event.MonitorId' => $allowedMonitors); + } + else + { + $mon_options=''; + } + + $options = array('conditions' => array(array('Event.' . $this->Event->primaryKey => $id), $mon_options)); + $event = $this->Event->find('first', $options); + + $path = $configs['ZM_DIR_EVENTS'].'/'.$this->Image->getEventPath($event).'/'; + $event['Event']['BasePath'] = $path; + + # Get the previous and next events for any monitor + $this->Event->id = $id; + $event_neighbors = $this->Event->find('neighbors'); + $event['Event']['Next'] = $event_neighbors['next']['Event']['Id']; + $event['Event']['Prev'] = $event_neighbors['prev']['Event']['Id']; + + # Also get the previous and next events for the same monitor + $event_monitor_neighbors = $this->Event->find('neighbors', array( + 'conditions'=>array('Event.MonitorId'=>$event['Event']['MonitorId']) + )); + $event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id']; + $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; + + $this->set(array( + 'event' => $event, + '_serialize' => array('event') + )); + } + /** * add method @@ -54,6 +150,13 @@ class EventsController extends AppController { * @return void */ public function add() { + + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + if ($this->request->is('post')) { $this->Event->create(); if ($this->Event->save($this->request->data)) { @@ -72,6 +175,13 @@ class EventsController extends AppController { * @return void */ public function edit($id = null) { + + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + $this->Event->id = $id; if (!$this->Event->exists($id)) { @@ -98,14 +208,168 @@ class EventsController extends AppController { * @return void */ public function delete($id = null) { + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } $this->Event->id = $id; if (!$this->Event->exists()) { throw new NotFoundException(__('Invalid event')); } $this->request->allowMethod('post', 'delete'); if ($this->Event->delete()) { + //$this->loadModel('Frame'); + //$this->Event->Frame->delete(); return $this->flash(__('The event has been deleted.'), array('action' => 'index')); } else { return $this->flash(__('The event could not be deleted. Please, try again.'), array('action' => 'index')); } - }} + } + + public function search() { + $this->Event->recursive = -1; + $conditions = array(); + + foreach ($this->params['named'] as $param_name => $value) { + // Transform params into mysql + if (preg_match("/interval/i", $value, $matches)) { + $condition = array("$param_name >= (date_sub(now(), $value))"); + } else { + $condition = array($param_name => $value); + } + array_push($conditions, $condition); + } + + $results = $this->Event->find('all', array( + 'conditions' => $conditions + )); + + $this->set(array( + 'results' => $results, + '_serialize' => array('results') + )); + + + } + + // format expected: + // you can changed AlarmFrames to any other named params + // consoleEvents/1 hour/AlarmFrames >=: 1/AlarmFrames <=: 20.json + + public function consoleEvents($interval = null) { + $this->Event->recursive = -1; + $results = array(); + + $moreconditions =""; + foreach ($this->request->params['named'] as $name => $param) { + $moreconditions = $moreconditions . " AND ".$name.$param; + } + + $query = $this->Event->query("select MonitorId, COUNT(*) AS Count from Events WHERE (StartTime >= (DATE_SUB(NOW(), interval $interval)) $moreconditions) GROUP BY MonitorId;"); + + foreach ($query as $result) { + $results[$result['Events']['MonitorId']] = $result[0]['Count']; + } + + $this->set(array( + 'results' => $results, + '_serialize' => array('results') + )); + } + + // Create a thumbnail and return the thumbnail's data for a given event id. + public function createThumbnail($id = null) { + $this->Event->recursive = -1; + + if (!$this->Event->exists($id)) { + throw new NotFoundException(__('Invalid event')); + } + + $event = $this->Event->find('first', array( + 'conditions' => array('Id' => $id) + )); + + // Find the max Frame for this Event. Error out otherwise. + $this->loadModel('Frame'); + if (! $frame = $this->Frame->find('first', array( + 'conditions' => array( + 'EventId' => $event['Event']['Id'], + 'Score' => $event['Event']['MaxScore'] + ) + ))) { + throw new NotFoundException(__("Can not find Frame for Event " . $event['Event']['Id'])); + } + + $this->loadModel('Config'); + + // Get the config options required for reScale and getImageSrc + // The $bw, $thumbs and unset() code is a workaround / temporary + // until I have a better way of handing per-bandwidth config options + $bw = (isset($_COOKIE['zmBandwidth']) ? strtoupper(substr($_COOKIE['zmBandwidth'], 0, 1)) : 'L'); + $thumbs = "ZM_WEB_${bw}_SCALE_THUMBS"; + + $config = $this->Config->find('list', array( + 'conditions' => array('OR' => array( + 'Name' => array('ZM_WEB_LIST_THUMB_WIDTH', + 'ZM_WEB_LIST_THUMB_HEIGHT', + 'ZM_EVENT_IMAGE_DIGITS', + 'ZM_DIR_IMAGES', + "$thumbs", + 'ZM_DIR_EVENTS' + ) + )), + 'fields' => array('Name', 'Value') + )); + $config['ZM_WEB_SCALE_THUMBS'] = $config[$thumbs]; + unset($config[$thumbs]); + + // reScale based on either the width, or the hight, of the event. + if ( $config['ZM_WEB_LIST_THUMB_WIDTH'] ) { + $thumbWidth = $config['ZM_WEB_LIST_THUMB_WIDTH']; + $scale = (100 * $thumbWidth) / $event['Event']['Width']; + $thumbHeight = $this->Scaler->reScale( $event['Event']['Height'], $scale ); + } + elseif ( $config['ZM_WEB_LIST_THUMB_HEIGHT'] ) { + $thumbHeight = $config['ZM_WEB_LIST_THUMB_HEIGHT']; + $scale = (100*$thumbHeight)/$event['Event']['Height']; + $thumbWidth = $this->Scaler->reScale( $event['Event']['Width'], $scale ); + } + else { + throw new NotFoundException(__('No thumbnail width or height specified, please check in Options->Web')); + } + + $imageData = $this->Image->getImageSrc( $event, $frame, $scale, $config ); + $thumbData['Path'] = $imageData['thumbPath']; + $thumbData['Width'] = (int)$thumbWidth; + $thumbData['Height'] = (int)$thumbHeight; + + return( $thumbData ); + + } + + public function archive($id = null) { + $this->Event->recursive = -1; + if (!$this->Event->exists($id)) { + throw new NotFoundException(__('Invalid event')); + } + + // Get the current value of Archive + $archived = $this->Event->find('first', array( + 'fields' => array('Event.Archived'), + 'conditions' => array('Event.Id' => $id) + )); + // If 0, 1, if 1, 0 + $archiveVal = (($archived['Event']['Archived'] == 0) ? 1 : 0); + + // Save the new value + $this->Event->id = $id; + $this->Event->saveField('Archived', $archiveVal); + + $this->set(array( + 'archived' => $archiveVal, + '_serialize' => array('archived') + )); + } + +} diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php new file mode 100644 index 000000000..57b210a7e --- /dev/null +++ b/web/api/app/Controller/HostController.php @@ -0,0 +1,115 @@ +set(array( + 'result' => $result, + '_serialize' => array('result') + )); + } + + function getLoad() { + $load = sys_getloadavg(); + + $this->set(array( + 'load' => $load, + '_serialize' => array('load') + )); + } + + // If $mid is set, only return disk usage for that monitor + // Else, return an array of total disk usage, and per-monitor + // usage. + function getDiskPercent($mid = null) { + $this->loadModel('Config'); + $this->loadModel('Monitor'); + + // If $mid is passed, see if it is valid + if ($mid) { + if (!$this->Monitor->exists($mid)) { + throw new NotFoundException(__('Invalid monitor')); + } + } + + $zm_dir_events = $this->Config->find('list', array( + 'conditions' => array('Name' => 'ZM_DIR_EVENTS'), + 'fields' => array('Name', 'Value') + )); + $zm_dir_events = $zm_dir_events['ZM_DIR_EVENTS' ]; + + // Test to see if $zm_dir_events is relative or absolute + if ('/' === "" || strrpos($zm_dir_events, '/', -strlen($zm_dir_events)) !== TRUE) { + // relative - so add the full path + $zm_dir_events = Configure::read('ZM_PATH_WEB') . '/' . $zm_dir_events; + } + + if ($mid) { + // Get disk usage for $mid + $usage = shell_exec ("du -sh0 $zm_dir_events/$mid | awk '{print $1}'"); + } else { + $monitors = $this->Monitor->find('all', array( + 'fields' => array('Id', 'Name', 'WebColour') + )); + $usage = array(); + + // Add each monitor's usage to array + foreach ($monitors as $key => $value) { + $id = $value['Monitor']['Id']; + $name = $value['Monitor']['Name']; + $color = $value['Monitor']['WebColour']; + + $space = shell_exec ("du -s0 $zm_dir_events/$id | awk '{print $1}'"); + if ($space == null) { + $space = 0; + } + $space = $space/1024/1024; + + $usage[$name] = array( + 'space' => rtrim($space), + 'color' => $color + ); + } + + // Add total usage to array + $space = shell_exec( "df $zm_dir_events |tail -n1 | awk '{print $3 }'"); + $space = $space/1024/1024; + $usage['Total'] = array( + 'space' => rtrim($space), + 'color' => '#F7464A' + ); + } + + $this->set(array( + 'usage' => $usage, + '_serialize' => array('usage') + )); + } + + function getVersion() { + $version = Configure::read('ZM_VERSION'); + // not going to use the ZM_API_VERSION + // requires recompilation and dependency on ZM upgrade + //$apiversion = Configure::read('ZM_API_VERSION'); + $apiversion = '1.0'; + + $this->set(array( + 'version' => $version, + 'apiversion' => $apiversion, + '_serialize' => array('version', 'apiversion') + )); + } +} diff --git a/web/api/app/Controller/LogsController.php b/web/api/app/Controller/LogsController.php new file mode 100644 index 000000000..bb8f47b80 --- /dev/null +++ b/web/api/app/Controller/LogsController.php @@ -0,0 +1,104 @@ + 100, + 'order' => array( 'Log.TimeKey' => 'asc' ), + 'paramType' => 'querystring' + ); + +/** + * index method + * + * @return void + */ + public function index() { + $this->Log->recursive = -1; + $this->Paginator->settings = $this->paginate; + + $logs = $this->Paginator->paginate('Log'); + $this->set(compact('logs')); + } + +/** + * view method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function view($id = null) { + if (!$this->Log->exists($id)) { + throw new NotFoundException(__('Invalid log')); + } + $options = array('conditions' => array('Log.' . $this->Log->primaryKey => $id)); + $this->set('log', $this->Log->find('first', $options)); + } + +/** + * add method + * + * @return void + */ + public function add() { + if ($this->request->is('post')) { + $this->Log->create(); + if ($this->Log->save($this->request->data)) { + return $this->flash(__('The log has been saved.'), array('action' => 'index')); + } + } + } + +/** + * edit method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function edit($id = null) { + if (!$this->Log->exists($id)) { + throw new NotFoundException(__('Invalid log')); + } + if ($this->request->is(array('post', 'put'))) { + if ($this->Log->save($this->request->data)) { + return $this->flash(__('The log has been saved.'), array('action' => 'index')); + } + } else { + $options = array('conditions' => array('Log.' . $this->Log->primaryKey => $id)); + $this->request->data = $this->Log->find('first', $options); + } + } + +/** + * delete method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function delete($id = null) { + $this->Log->id = $id; + if (!$this->Log->exists()) { + throw new NotFoundException(__('Invalid log')); + } + $this->request->allowMethod('post', 'delete'); + if ($this->Log->delete()) { + return $this->flash(__('The log has been deleted.'), array('action' => 'index')); + } else { + return $this->flash(__('The log could not be deleted. Please, try again.'), array('action' => 'index')); + } + }} diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 3e9556a19..7bf36de75 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -16,6 +16,19 @@ class MonitorsController extends AppController { */ public $components = array('Paginator', 'RequestHandler'); + +public function beforeFilter() { + parent::beforeFilter(); + $canView = $this->Session->Read('monitorPermission'); + if ($canView =='None') + { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + +} + + /** * index method * @@ -23,7 +36,17 @@ class MonitorsController extends AppController { */ public function index() { $this->Monitor->recursive = 0; - $monitors = $this->Monitor->find('all'); + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + + if (!empty($allowedMonitors)) + { + $options = array('conditions'=>array('Monitor.Id'=> $allowedMonitors)); + } + else + { + $options=''; + } + $monitors = $this->Monitor->find('all',$options); $this->set(array( 'monitors' => $monitors, '_serialize' => array('monitors') @@ -42,7 +65,21 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - $options = array('conditions' => array('Monitor.' . $this->Monitor->primaryKey => $id)); + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + if (!empty($allowedMonitors)) + { + $restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors); + } + else + { + $restricted = ''; + } + + $options = array('conditions' => array( + array('Monitor.' . $this->Monitor->primaryKey => $id), + $restricted + ) + ); $monitor = $this->Monitor->find('first', $options); $this->set(array( 'monitor' => $monitor, @@ -57,8 +94,16 @@ class MonitorsController extends AppController { */ public function add() { if ($this->request->is('post')) { + + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + $this->Monitor->create(); if ($this->Monitor->save($this->request->data)) { + $this->daemonControl($this->Monitor->id, 'start'); return $this->flash(__('The monitor has been saved.'), array('action' => 'index')); } } @@ -77,7 +122,11 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } if ($this->Monitor->save($this->request->data)) { $message = 'Saved'; } else { @@ -88,6 +137,9 @@ class MonitorsController extends AppController { 'message' => $message, '_serialize' => array('message') )); + // - restart this monitor after change + // We don't pass the request data as the monitor object because it may be a subset of the full monitor array + $this->daemonControl( $this->Monitor->id, 'restart' ); } /** @@ -102,10 +154,191 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists()) { throw new NotFoundException(__('Invalid monitor')); } + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } $this->request->allowMethod('post', 'delete'); + + $this->daemonControl($this->Monitor->id, 'stop'); + if ($this->Monitor->delete()) { return $this->flash(__('The monitor has been deleted.'), array('action' => 'index')); } else { return $this->flash(__('The monitor could not be deleted. Please, try again.'), array('action' => 'index')); } - }} + } + + public function sourceTypes() { + $sourceTypes = $this->Monitor->query("describe Monitors Type;"); + + preg_match('/^enum\((.*)\)$/', $sourceTypes[0]['COLUMNS']['Type'], $matches); + foreach( explode(',', $matches[1]) as $value ) { + $enum[] = trim( $value, "'" ); + } + + $this->set(array( + 'sourceTypes' => $enum, + '_serialize' => array('sourceTypes') + )); + } + + // arm/disarm alarms + // expected format: http(s):/portal-api-url/monitors/alarm/id:M/command:C.json + // where M=monitorId + // where C=on|off|status + public function alarm() + { + $id = $this->request->params['named']['id']; + $cmd = strtolower($this->request->params['named']['command']); + if (!$this->Monitor->exists($id)) { + throw new NotFoundException(__('Invalid monitor')); + } + if ( $cmd != 'on' && $cmd != 'off' && $cmd != 'status') + { + throw new BadRequestException(__('Invalid command')); + } + $zm_path_bin = Configure::read('ZM_PATH_BIN'); + + switch ($cmd) + { + case "on": + $q = '-a'; + $verbose = "-v"; + break; + case "off": + $q = "-c"; + $verbose = "-v"; + break; + case "status": + $verbose = ""; // zmu has a bug - gives incorrect verbose output in this case + $q = "-s"; + break; + } + + // form auth key based on auth credentials + $this->loadModel('Config'); + $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH')); + $config = $this->Config->find('first', $options); + $zmOptAuth = $config['Config']['Value']; + + + $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')); + $config = $this->Config->find('first', $options); + $zmAuthRelay = $config['Config']['Value']; + + $auth=""; + if ($zmOptAuth) + { + if ($zmAuthRelay == 'hashed') + { + $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_SECRET')); + $config = $this->Config->find('first', $options); + $zmAuthHashSecret = $config['Config']['Value']; + + $time = localtime(); + $ak = $zmAuthHashSecret.$this->Session->Read('username').$this->Session->Read('passwordHash').$time[2].$time[3].$time[4].$time[5]; + $ak = md5($ak); + $auth = " -A ".$ak; + } + elseif ($zmAuthRelay == 'plain') + { + $auth = " -U " .$this->Session->Read('username')." -P ".$this->Session->Read('password'); + + } + elseif ($zmAuthRelay == 'none') + { + $auth = " -U " .$this->Session->Read('username'); + } + } + + $shellcmd = escapeshellcmd("$zm_path_bin/zmu $verbose -m$id $q $auth"); + $status = exec ($shellcmd); + + $this->set(array( + 'status' => $status, + '_serialize' => array('status'), + )); + + + } + + // Check if a daemon is running for the monitor id + public function daemonStatus() { + $id = $this->request->params['named']['id']; + $daemon = $this->request->params['named']['daemon']; + + if (!$this->Monitor->exists($id)) { + throw new NotFoundException(__('Invalid monitor')); + } + + $monitor = $this->Monitor->find('first', array( + 'fields' => array('Id', 'Type', 'Device'), + 'conditions' => array('Id' => $id) + )); + + // Clean up the returned array + $monitor = Set::extract('/Monitor/.', $monitor); + + // Pass -d for local, otherwise -m + if ($monitor[0]['Type'] == 'Local') { + $args = "-d ". $monitor[0]['Device']; + } else { + $args = "-m ". $monitor[0]['Id']; + } + + // Build the command, and execute it + $zm_path_bin = Configure::read('ZM_PATH_BIN'); + $command = escapeshellcmd("$zm_path_bin/zmdc.pl status $daemon $args"); + $status = exec( $command ); + + // If 'not' is present, the daemon is not running, so return false + // https://github.com/ZoneMinder/ZoneMinder/issues/799#issuecomment-108996075 + // Also sending back the status text so we can check if the monitor is in pending + // state which means there may be an error + $statustext = $status; + $status = (strpos($status, 'not')) ? false : true; + + $this->set(array( + 'status' => $status, + 'statustext' => $statustext, + '_serialize' => array('status','statustext'), + )); + } + + public function daemonControl($id, $command, $monitor=null, $daemon=null) { + $args = ''; + $daemons = array(); + + if (!$monitor) { + // Need to see if it is local or remote + $monitor = $this->Monitor->find('first', array( + 'fields' => array('Type', 'Function'), + 'conditions' => array('Id' => $id) + )); + $monitor = $monitor['Monitor']; + } + + if ($monitor['Type'] == 'Local') { + $args = "-d " . $monitor['Device']; + } else { + $args = "-m " . $id; + } + + if ($monitor['Function'] == 'Monitor') { + array_push($daemons, 'zmc'); + } else { + array_push($daemons, 'zmc', 'zma'); + } + + $zm_path_bin = Configure::read('ZM_PATH_BIN'); + + foreach ($daemons as $daemon) { + $shellcmd = escapeshellcmd("$zm_path_bin/zmdc.pl $command $daemon $args"); + $status = exec( $shellcmd ); + } + } + +} + diff --git a/web/api/app/Controller/ServersController.php b/web/api/app/Controller/ServersController.php new file mode 100644 index 000000000..88a5bec90 --- /dev/null +++ b/web/api/app/Controller/ServersController.php @@ -0,0 +1,155 @@ +Session->Read('streamPermission'); + if ($canView =='None') { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + +} + + +/** + * index method + * + * @return void + */ + public function index() { + $this->Server->recursive = 0; + + $options=''; + $servers = $this->Server->find('all',$options); + $this->set(array( + 'servers' => $servers, + '_serialize' => array('servers') + )); + } + +/** + * view method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function view($id = null) { + $this->Server->recursive = 0; + if (!$this->Server->exists($id)) { + throw new NotFoundException(__('Invalid server')); + } + $restricted = ''; + + $options = array('conditions' => array( + array('Server.' . $this->Server->primaryKey => $id), + $restricted + ) + ); + $server = $this->Server->find('first', $options); + $this->set(array( + 'server' => $server, + '_serialize' => array('server') + )); + } + +/** + * add method + * + * @return void + */ + public function add() { + if ($this->request->is('post')) { + + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + + $this->Server->create(); + if ($this->Server->save($this->request->data)) { + # Might be nice to send it a start request + #$this->daemonControl($this->Server->id, 'start', $this->request->data); + return $this->flash(__('The server has been saved.'), array('action' => 'index')); + } + } + } + +/** + * edit method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function edit($id = null) { + $this->Server->id = $id; + + if (!$this->Server->exists($id)) { + throw new NotFoundException(__('Invalid server')); + } + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + if ($this->Server->save($this->request->data)) { + $message = 'Saved'; + } else { + $message = 'Error'; + } + + $this->set(array( + 'message' => $message, + '_serialize' => array('message') + )); + // - restart this server after change + #$this->daemonControl($this->Server->id, 'restart', $this->request->data); + } + +/** + * delete method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function delete($id = null) { + $this->Server->id = $id; + if (!$this->Server->exists()) { + throw new NotFoundException(__('Invalid server')); + } + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + $this->request->allowMethod('post', 'delete'); + + #$this->daemonControl($this->Server->id, 'stop'); + + if ($this->Server->delete()) { + return $this->flash(__('The server has been deleted.'), array('action' => 'index')); + } else { + return $this->flash(__('The server could not be deleted. Please, try again.'), array('action' => 'index')); + } + } +} diff --git a/web/api/app/Controller/StatesController.php b/web/api/app/Controller/StatesController.php new file mode 100644 index 000000000..051837b27 --- /dev/null +++ b/web/api/app/Controller/StatesController.php @@ -0,0 +1,156 @@ +Session->Read('systemPermission'); + if ($canView =='None') + { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + +} + + +/** + * index method + * + * @return void + */ + public function index() { + $this->State->recursive = 0; + $states = $this->State->find('all'); + $this->set(array( + 'states' => $states, + '_serialize' => array('states') + )); + } + +/** + * view method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function view($id = null) { + if (!$this->State->exists($id)) { + throw new NotFoundException(__('Invalid state')); + } + $options = array('conditions' => array('State.' . $this->State->primaryKey => $id)); + $this->set('state', $this->State->find('first', $options)); + } + +/** + * add method + * + * @return void + */ + public function add() { + + if ($this->request->is('post')) { + + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + + $this->State->create(); + if ($this->State->save($this->request->data)) { + return $this->flash(__('The state has been saved.'), array('action' => 'index')); + } + } + } + +/** + * edit method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function edit($id = null) { + if (!$this->State->exists($id)) { + throw new NotFoundException(__('Invalid state')); + } + + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + + if ($this->request->is(array('post', 'put'))) { + if ($this->State->save($this->request->data)) { + return $this->flash(__('The state has been saved.'), array('action' => 'index')); + } + } else { + $options = array('conditions' => array('State.' . $this->State->primaryKey => $id)); + $this->request->data = $this->State->find('first', $options); + } + } + +/** + * delete method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function delete($id = null) { + $this->State->id = $id; + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + + if (!$this->State->exists()) { + throw new NotFoundException(__('Invalid state')); + } + $this->request->allowMethod('post', 'delete'); + if ($this->State->delete()) { + return $this->flash(__('The state has been deleted.'), array('action' => 'index')); + } else { + return $this->flash(__('The state could not be deleted. Please, try again.'), array('action' => 'index')); + } + } + + public function change() { + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + + $newState = $this->request->params['pass'][0]; + $blah = $this->packageControl($newState); + + $this->set(array( + 'blah' => $blah, + '_serialize' => array('blah') + )); + } + + public function packageControl( $command ) { + $zm_path_bin = Configure::read('ZM_PATH_BIN'); + $string = $zm_path_bin.'/zmpkg.pl '.escapeshellarg( $command ); + $status = exec( $string ); + + return $status; + } + + +} diff --git a/web/api/app/Controller/ZonePresetsController.php b/web/api/app/Controller/ZonePresetsController.php new file mode 100644 index 000000000..b89a6c75d --- /dev/null +++ b/web/api/app/Controller/ZonePresetsController.php @@ -0,0 +1,99 @@ +ZonePreset->find('all'); + $this->set(array( + 'zonePresets' => $zonePresets, + '_serialize' => array('zonePresets') + )); + } + +/** + * view method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function view($id = null) { + if (!$this->ZonePreset->exists($id)) { + throw new NotFoundException(__('Invalid zone preset')); + } + $options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id)); + $this->set('zonePreset', $this->ZonePreset->find('first', $options)); + } + +/** + * add method + * + * @return void + */ + public function add() { + if ($this->request->is('post')) { + $this->ZonePreset->create(); + if ($this->ZonePreset->save($this->request->data)) { + return $this->flash(__('The zone preset has been saved.'), array('action' => 'index')); + } + } + } + +/** + * edit method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function edit($id = null) { + if (!$this->ZonePreset->exists($id)) { + throw new NotFoundException(__('Invalid zone preset')); + } + if ($this->request->is(array('post', 'put'))) { + if ($this->ZonePreset->save($this->request->data)) { + return $this->flash(__('The zone preset has been saved.'), array('action' => 'index')); + } + } else { + $options = array('conditions' => array('ZonePreset.' . $this->ZonePreset->primaryKey => $id)); + $this->request->data = $this->ZonePreset->find('first', $options); + } + } + +/** + * delete method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function delete($id = null) { + $this->ZonePreset->id = $id; + if (!$this->ZonePreset->exists()) { + throw new NotFoundException(__('Invalid zone preset')); + } + $this->request->allowMethod('post', 'delete'); + if ($this->ZonePreset->delete()) { + return $this->flash(__('The zone preset has been deleted.'), array('action' => 'index')); + } else { + return $this->flash(__('The zone preset could not be deleted. Please, try again.'), array('action' => 'index')); + } + }} diff --git a/web/api/app/Controller/ZonesController.php b/web/api/app/Controller/ZonesController.php index dc30a382a..be80c57fc 100644 --- a/web/api/app/Controller/ZonesController.php +++ b/web/api/app/Controller/ZonesController.php @@ -4,51 +4,26 @@ App::uses('AppController', 'Controller'); * Zones Controller * * @property Zone $Zone - * @property PaginatorComponent $Paginator */ class ZonesController extends AppController { -/** - * Components - * - * @var array - */ - public $components = array('Paginator', 'RequestHandler'); - -/** - * index method - * - * @return void - */ - public function index() { - $this->Zone->recursive = -1; - $zones = $this->Zone->find('all'); - $this->set(array( - 'zones' => $zones, - '_serialize' => array('zones') - )); - } - -/** - * view method - * - * @throws NotFoundException - * @param string $id - * @return void - */ - public function view($id = null) { - $this->Zone->recursive = -1; - if (!$this->Zone->exists($id)) { - throw new NotFoundException(__('Invalid zone')); +// Find all zones which belong to a MonitorId + public function forMonitor($id = null) { + $this->loadModel('Monitor'); + if (!$this->Monitor->exists($id)) { + throw new NotFoundException(__('Invalid monitor')); } - $options = array('conditions' => array('Zone.' . $this->Zone->primaryKey => $id)); - $zone = $this->Zone->find('first', $options); + + $this->Zone->recursive = -1; + + $zones = $this->Zone->find('all', array( + 'conditions' => array('MonitorId' => $id) + )); $this->set(array( - 'zone' => $zone, - '_serialize' => array('zone') + 'zones' => $zones, + '_serialize' => array('zones') )); } - /** * add method * @@ -108,4 +83,38 @@ class ZonesController extends AppController { } else { return $this->flash(__('The zone could not be deleted. Please, try again.'), array('action' => 'index')); } - }} + } + + + + public function createZoneImage( $id = null ) { + $this->loadModel('Monitor'); + $this->Monitor->id = $id; + if (!$this->Monitor->exists()) { + throw new NotFoundException(__('Invalid zone')); + } + + + $this->loadModel('Config'); + $zm_dir_images = $this->Config->find('list', array( + 'conditions' => array('Name' => 'ZM_DIR_IMAGES'), + 'fields' => array('Name', 'Value') + )); + + $zm_dir_images = $zm_dir_images['ZM_DIR_IMAGES']; + $zm_path_web = Configure::read('ZM_PATH_WEB'); + $zm_path_bin = Configure::read('ZM_PATH_BIN'); + $images_path = "$zm_path_web/$zm_dir_images"; + + chdir($images_path); + + $command = escapeshellcmd("$zm_path_bin/zmu -z -m $id"); + system( $command, $status ); + + $this->set(array( + 'status' => $status, + '_serialize' => array('status') + )); + + } +} diff --git a/web/api/app/Model/Config.php b/web/api/app/Model/Config.php index 2ada8258d..d83728c76 100644 --- a/web/api/app/Model/Config.php +++ b/web/api/app/Model/Config.php @@ -18,13 +18,25 @@ class Config extends AppModel { * * @var string */ - public $primaryKey = 'Id'; + public $primaryKey = 'Name'; /** * Display field * * @var string */ - public $displayField = 'Name'; + public $displayField = 'Value'; + + + // Add a find method for returning a hash of the Config table. + // This is used for the Options view. + public $findMethods = array('hash' => true); + protected function _findHash($state, $query, $results = array()) { + if ($state === 'before') { + return $query; + } + $results = Set::combine($results, '{n}.Config.Name', '{n}.Config'); + return $results; + } } diff --git a/web/api/app/Model/Control.php b/web/api/app/Model/Control.php new file mode 100644 index 000000000..d5716cc66 --- /dev/null +++ b/web/api/app/Model/Control.php @@ -0,0 +1,54 @@ + array( + 'numeric' => array( + 'rule' => array('numeric'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + +} diff --git a/web/api/app/Model/Event.php b/web/api/app/Model/Event.php index f5d7f4ec2..8bf6be518 100644 --- a/web/api/app/Model/Event.php +++ b/web/api/app/Model/Event.php @@ -62,7 +62,7 @@ class Event extends AppModel { 'order' => '', 'limit' => '', 'offset' => '', - 'exclusive' => '', + 'exclusive' => 'true', 'finderQuery' => '', 'counterQuery' => '' ) diff --git a/web/api/app/Model/Frame.php b/web/api/app/Model/Frame.php index 48b880304..e1977cf02 100644 --- a/web/api/app/Model/Frame.php +++ b/web/api/app/Model/Frame.php @@ -19,7 +19,7 @@ class Frame extends AppModel { * * @var string */ - public $primaryKey = 'FrameId'; + public $primaryKey = 'Id'; /** * Validation rules @@ -121,4 +121,6 @@ class Frame extends AppModel { 'order' => '' ) ); + + public $recursive = -1; } diff --git a/web/api/app/Model/Host.php b/web/api/app/Model/Host.php new file mode 100644 index 000000000..5c24c7531 --- /dev/null +++ b/web/api/app/Model/Host.php @@ -0,0 +1,9 @@ + + array( + 'numeric' => array( + 'rule' => array('numeric'), + //'message' => 'Your custom message here', + //'allowEmpty' => false, + //'required' => false, + //'last' => false, // Stop validation after this rule + //'on' => 'create', // Limit validation to 'create' or 'update' operations + ), + ), + ); + + //The Associations below have been created with all possible keys, those that are not needed can be removed + +/** + * hasMany associations + * + * @var array + */ + public $hasMany = array( + 'Monitor' => array( + 'className' => 'Monitor', + 'foreignKey' => 'ServerId', + 'dependent' => false, + 'conditions' => '', + 'fields' => '', + 'order' => '', + 'limit' => '', + 'offset' => '', + 'exclusive' => '', + 'finderQuery' => '', + 'counterQuery' => '' + ) + ); +} diff --git a/web/api/app/Model/State.php b/web/api/app/Model/State.php new file mode 100644 index 000000000..651a9cc09 --- /dev/null +++ b/web/api/app/Model/State.php @@ -0,0 +1,30 @@ +Paginator->params(); + echo json_encode($array); +?> diff --git a/web/api/app/View/Logs/add.ctp b/web/api/app/View/Logs/add.ctp new file mode 100644 index 000000000..a667ae1f1 --- /dev/null +++ b/web/api/app/View/Logs/add.ctp @@ -0,0 +1,23 @@ +
+Form->create('Log'); ?> +
+ + Form->input('Component'); + echo $this->Form->input('Pid'); + echo $this->Form->input('Level'); + echo $this->Form->input('Code'); + echo $this->Form->input('Message'); + echo $this->Form->input('File'); + echo $this->Form->input('Line'); + ?> +
+Form->end(__('Submit')); ?> +
+
+

+
    + +
  • Html->link(__('List Logs'), array('action' => 'index')); ?>
  • +
+
diff --git a/web/api/app/View/Logs/edit.ctp b/web/api/app/View/Logs/edit.ctp new file mode 100644 index 000000000..cdd5dab2b --- /dev/null +++ b/web/api/app/View/Logs/edit.ctp @@ -0,0 +1,25 @@ +
+Form->create('Log'); ?> +
+ + Form->input('TimeKey'); + echo $this->Form->input('Component'); + echo $this->Form->input('Pid'); + echo $this->Form->input('Level'); + echo $this->Form->input('Code'); + echo $this->Form->input('Message'); + echo $this->Form->input('File'); + echo $this->Form->input('Line'); + ?> +
+Form->end(__('Submit')); ?> +
+
+

+
    + +
  • Form->postLink(__('Delete'), array('action' => 'delete', $this->Form->value('Log.TimeKey')), null, __('Are you sure you want to delete # %s?', $this->Form->value('Log.TimeKey'))); ?>
  • +
  • Html->link(__('List Logs'), array('action' => 'index')); ?>
  • +
+
diff --git a/web/api/app/View/Logs/index.ctp b/web/api/app/View/Logs/index.ctp new file mode 100644 index 000000000..47cbeb57f --- /dev/null +++ b/web/api/app/View/Logs/index.ctp @@ -0,0 +1,52 @@ +
+

+
'.$SLANG['DateTime'].''.$SLANG['Component'].''.$SLANG['Pid'].''.$SLANG['Level'].''.$SLANG['Message'].''.$SLANG['File'].''.$SLANG['Line'].'
'.translate('DateTime').''.translate('Component').''.translate('Server').''.translate('Pid').''.translate('Level').''.translate('Message').''.translate('File').''.translate('Line').'
%s%s%d%s%s%s%s
%s%s%s%d%s%s%s%s
+ + + + + + + + + + + + + + + + + + + + + + + + +
Paginator->sort('TimeKey'); ?>Paginator->sort('Component'); ?>Paginator->sort('Pid'); ?>Paginator->sort('Level'); ?>Paginator->sort('Code'); ?>Paginator->sort('Message'); ?>Paginator->sort('File'); ?>Paginator->sort('Line'); ?>
         + Html->link(__('View'), array('action' => 'view', $log['Log']['TimeKey'])); ?> + Html->link(__('Edit'), array('action' => 'edit', $log['Log']['TimeKey'])); ?> + Form->postLink(__('Delete'), array('action' => 'delete', $log['Log']['TimeKey']), array(), __('Are you sure you want to delete # %s?', $log['Log']['TimeKey'])); ?> +
+

+ Paginator->counter(array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + )); + ?>

+
+ Paginator->prev('< ' . __('previous'), array(), null, array('class' => 'prev disabled')); + echo $this->Paginator->numbers(array('separator' => '')); + echo $this->Paginator->next(__('next') . ' >', array(), null, array('class' => 'next disabled')); + ?> +
+ +
+

+
    +
  • Html->link(__('New Log'), array('action' => 'add')); ?>
  • +
+
diff --git a/web/api/app/View/Logs/json/index.ctp b/web/api/app/View/Logs/json/index.ctp new file mode 100644 index 000000000..32cbd51f8 --- /dev/null +++ b/web/api/app/View/Logs/json/index.ctp @@ -0,0 +1,5 @@ +Paginator->params(); + echo json_encode($array); +?> diff --git a/web/api/app/View/Logs/view.ctp b/web/api/app/View/Logs/view.ctp new file mode 100644 index 000000000..7f0b8f15a --- /dev/null +++ b/web/api/app/View/Logs/view.ctp @@ -0,0 +1,54 @@ +
+

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

+
    +
  • Html->link(__('Edit Log'), array('action' => 'edit', $log['Log']['TimeKey'])); ?>
  • +
  • Form->postLink(__('Delete Log'), array('action' => 'delete', $log['Log']['TimeKey']), null, __('Are you sure you want to delete # %s?', $log['Log']['TimeKey'])); ?>
  • +
  • Html->link(__('List Logs'), array('action' => 'index')); ?>
  • +
  • Html->link(__('New Log'), array('action' => 'add')); ?>
  • +
+
diff --git a/web/api/app/View/Servers/json/edit.ctp b/web/api/app/View/Servers/json/edit.ctp new file mode 100644 index 000000000..0be859571 --- /dev/null +++ b/web/api/app/View/Servers/json/edit.ctp @@ -0,0 +1,2 @@ +echo json_encode($message); +echo json_encode($server); diff --git a/web/api/app/View/Servers/json/index.ctp b/web/api/app/View/Servers/json/index.ctp new file mode 100644 index 000000000..6e5cbd26d --- /dev/null +++ b/web/api/app/View/Servers/json/index.ctp @@ -0,0 +1 @@ +echo json_encode($servers); diff --git a/web/api/app/View/Servers/json/view.ctp b/web/api/app/View/Servers/json/view.ctp new file mode 100644 index 000000000..c3d0226ab --- /dev/null +++ b/web/api/app/View/Servers/json/view.ctp @@ -0,0 +1 @@ +echo json_encode($server); diff --git a/web/api/app/View/Servers/xml/edit.ctp b/web/api/app/View/Servers/xml/edit.ctp new file mode 100644 index 000000000..09fb8979a --- /dev/null +++ b/web/api/app/View/Servers/xml/edit.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $message)); +echo $xml->asXML(); diff --git a/web/api/app/View/Servers/xml/index.ctp b/web/api/app/View/Servers/xml/index.ctp new file mode 100644 index 000000000..37afc918b --- /dev/null +++ b/web/api/app/View/Servers/xml/index.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $monitors)); +echo $xml->asXML(); diff --git a/web/api/app/View/Servers/xml/view.ctp b/web/api/app/View/Servers/xml/view.ctp new file mode 100644 index 000000000..b33c6e79a --- /dev/null +++ b/web/api/app/View/Servers/xml/view.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $monitor)); +echo $xml->asXML(); diff --git a/web/api/app/View/View/Configs/json/edit.ctp b/web/api/app/View/View/Configs/json/edit.ctp new file mode 100644 index 000000000..75fa758bd --- /dev/null +++ b/web/api/app/View/View/Configs/json/edit.ctp @@ -0,0 +1 @@ +echo json_encode($config); diff --git a/web/api/app/View/View/Configs/json/index.ctp b/web/api/app/View/View/Configs/json/index.ctp new file mode 100644 index 000000000..86edf870a --- /dev/null +++ b/web/api/app/View/View/Configs/json/index.ctp @@ -0,0 +1 @@ +echo json_encode($configs); diff --git a/web/api/app/View/View/Configs/json/view.ctp b/web/api/app/View/View/Configs/json/view.ctp new file mode 100644 index 000000000..75fa758bd --- /dev/null +++ b/web/api/app/View/View/Configs/json/view.ctp @@ -0,0 +1 @@ +echo json_encode($config); diff --git a/web/api/app/View/View/Configs/xml/index.ctp b/web/api/app/View/View/Configs/xml/index.ctp new file mode 100644 index 000000000..b13a76093 --- /dev/null +++ b/web/api/app/View/View/Configs/xml/index.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $configs)); +echo $xml->asXML(); diff --git a/web/api/app/View/View/Configs/xml/view.ctp b/web/api/app/View/View/Configs/xml/view.ctp new file mode 100644 index 000000000..7987d32e7 --- /dev/null +++ b/web/api/app/View/View/Configs/xml/view.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $config)); +echo $xml->asXML(); diff --git a/web/skins/xml/views/none.php b/web/api/app/View/View/Elements/empty similarity index 100% rename from web/skins/xml/views/none.php rename to web/api/app/View/View/Elements/empty diff --git a/web/api/app/View/View/Emails/html/default.ctp b/web/api/app/View/View/Emails/html/default.ctp new file mode 100644 index 000000000..e2bff19c0 --- /dev/null +++ b/web/api/app/View/View/Emails/html/default.ctp @@ -0,0 +1,25 @@ + + ' . $line . "

\n"; +endforeach; +?> \ No newline at end of file diff --git a/web/api/app/View/View/Emails/text/default.ctp b/web/api/app/View/View/Emails/text/default.ctp new file mode 100644 index 000000000..090b5c403 --- /dev/null +++ b/web/api/app/View/View/Emails/text/default.ctp @@ -0,0 +1,19 @@ + + \ No newline at end of file diff --git a/web/api/app/View/View/Errors/error400.ctp b/web/api/app/View/View/Errors/error400.ctp new file mode 100644 index 000000000..4c3850b28 --- /dev/null +++ b/web/api/app/View/View/Errors/error400.ctp @@ -0,0 +1,31 @@ + +

+

+ : + '{$url}'" + ); ?> +

+ 0): + echo $this->element('exception_stack_trace'); +endif; +?> diff --git a/web/api/app/View/View/Errors/error500.ctp b/web/api/app/View/View/Errors/error500.ctp new file mode 100644 index 000000000..518b9ee77 --- /dev/null +++ b/web/api/app/View/View/Errors/error500.ctp @@ -0,0 +1,28 @@ + +

+

+ : + +

+ 0): + echo $this->element('exception_stack_trace'); +endif; +?> diff --git a/web/api/app/View/View/Events/json/index.ctp b/web/api/app/View/View/Events/json/index.ctp new file mode 100644 index 000000000..d54386749 --- /dev/null +++ b/web/api/app/View/View/Events/json/index.ctp @@ -0,0 +1,5 @@ +Paginator->params(); + echo json_encode($array); +?> diff --git a/web/api/app/View/View/Events/json/view.ctp b/web/api/app/View/View/Events/json/view.ctp new file mode 100644 index 000000000..b320feb4d --- /dev/null +++ b/web/api/app/View/View/Events/json/view.ctp @@ -0,0 +1 @@ +echo json_encode($event); diff --git a/web/api/app/View/View/Events/xml/index.ctp b/web/api/app/View/View/Events/xml/index.ctp new file mode 100644 index 000000000..af960238f --- /dev/null +++ b/web/api/app/View/View/Events/xml/index.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $events)); +echo $xml->asXML(); diff --git a/web/api/app/View/View/Events/xml/view.ctp b/web/api/app/View/View/Events/xml/view.ctp new file mode 100644 index 000000000..7f64e422f --- /dev/null +++ b/web/api/app/View/View/Events/xml/view.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $event)); +echo $xml->asXML(); diff --git a/web/api/app/View/View/Helper/AppHelper.php b/web/api/app/View/View/Helper/AppHelper.php new file mode 100644 index 000000000..9097d33f0 --- /dev/null +++ b/web/api/app/View/View/Helper/AppHelper.php @@ -0,0 +1,33 @@ + + + + + <?php echo $title_for_layout; ?> + + + fetch('content'); ?> + +

This email was sent using the CakePHP Framework

+ + \ No newline at end of file diff --git a/web/api/app/View/View/Layouts/Emails/text/default.ctp b/web/api/app/View/View/Layouts/Emails/text/default.ctp new file mode 100644 index 000000000..ee624de45 --- /dev/null +++ b/web/api/app/View/View/Layouts/Emails/text/default.ctp @@ -0,0 +1,21 @@ + +fetch('content'); ?> + +This email was sent using the CakePHP Framework, http://cakephp.org. diff --git a/web/api/app/View/View/Layouts/ajax.ctp b/web/api/app/View/View/Layouts/ajax.ctp new file mode 100644 index 000000000..0f9a4fb62 --- /dev/null +++ b/web/api/app/View/View/Layouts/ajax.ctp @@ -0,0 +1,19 @@ + +fetch('content'); ?> diff --git a/web/api/app/View/View/Layouts/default.ctp b/web/api/app/View/View/Layouts/default.ctp new file mode 100644 index 000000000..38dececbc --- /dev/null +++ b/web/api/app/View/View/Layouts/default.ctp @@ -0,0 +1,65 @@ + + + + + Html->charset(); ?> + + <?php echo $cakeDescription ?>: + <?php echo $title_for_layout; ?> + + Html->meta('icon'); + + echo $this->Html->css('cake.generic'); + + echo $this->fetch('meta'); + echo $this->fetch('css'); + echo $this->fetch('script'); + ?> + + +
+ +
+ + Session->flash(); ?> + + fetch('content'); ?> +
+ +
+ element('sql_dump'); ?> + + diff --git a/web/api/app/View/View/Layouts/error.ctp b/web/api/app/View/View/Layouts/error.ctp new file mode 100644 index 000000000..e9d738178 --- /dev/null +++ b/web/api/app/View/View/Layouts/error.ctp @@ -0,0 +1,61 @@ + + + + + Html->charset(); ?> + + <?php echo $cakeDescription ?>: + <?php echo $title_for_layout; ?> + + Html->meta('icon'); + + echo $this->Html->css('cake.generic'); + + echo $this->fetch('meta'); + echo $this->fetch('css'); + echo $this->fetch('script'); + ?> + + +
+ +
+ + Session->flash(); ?> + + fetch('content'); ?> +
+ +
+ element('sql_dump'); ?> + + diff --git a/web/api/app/View/View/Layouts/flash.ctp b/web/api/app/View/View/Layouts/flash.ctp new file mode 100644 index 000000000..cd79f5008 --- /dev/null +++ b/web/api/app/View/View/Layouts/flash.ctp @@ -0,0 +1,37 @@ + + + + +Html->charset(); ?> +<?php echo $page_title; ?> + + + + + + + +

+ + diff --git a/web/api/app/View/View/Layouts/js/default.ctp b/web/api/app/View/View/Layouts/js/default.ctp new file mode 100644 index 000000000..7239b5dae --- /dev/null +++ b/web/api/app/View/View/Layouts/js/default.ctp @@ -0,0 +1,2 @@ + + diff --git a/web/api/app/View/View/Layouts/rss/default.ctp b/web/api/app/View/View/Layouts/rss/default.ctp new file mode 100644 index 000000000..26d875eda --- /dev/null +++ b/web/api/app/View/View/Layouts/rss/default.ctp @@ -0,0 +1,14 @@ +Rss->document( + $this->Rss->channel( + array(), $channel, $this->fetch('content') + ) +); +?> diff --git a/web/api/app/View/View/Layouts/xml/default.ctp b/web/api/app/View/View/Layouts/xml/default.ctp new file mode 100644 index 000000000..fbd5ee0c3 --- /dev/null +++ b/web/api/app/View/View/Layouts/xml/default.ctp @@ -0,0 +1 @@ +fetch('content'); ?> diff --git a/web/api/app/View/View/Logs/json/index.ctp b/web/api/app/View/View/Logs/json/index.ctp new file mode 100644 index 000000000..32cbd51f8 --- /dev/null +++ b/web/api/app/View/View/Logs/json/index.ctp @@ -0,0 +1,5 @@ +Paginator->params(); + echo json_encode($array); +?> diff --git a/web/api/app/View/View/Monitors/json/edit.ctp b/web/api/app/View/View/Monitors/json/edit.ctp new file mode 100644 index 000000000..77d2dd08b --- /dev/null +++ b/web/api/app/View/View/Monitors/json/edit.ctp @@ -0,0 +1,2 @@ +echo json_encode($message); +echo json_encode($monitor); diff --git a/web/api/app/View/View/Monitors/json/index.ctp b/web/api/app/View/View/Monitors/json/index.ctp new file mode 100644 index 000000000..facf965d4 --- /dev/null +++ b/web/api/app/View/View/Monitors/json/index.ctp @@ -0,0 +1 @@ +echo json_encode($monitors); diff --git a/web/api/app/View/View/Monitors/json/view.ctp b/web/api/app/View/View/Monitors/json/view.ctp new file mode 100644 index 000000000..acfced9d0 --- /dev/null +++ b/web/api/app/View/View/Monitors/json/view.ctp @@ -0,0 +1 @@ +echo json_encode($monitor); diff --git a/web/api/app/View/View/Monitors/xml/edit.ctp b/web/api/app/View/View/Monitors/xml/edit.ctp new file mode 100644 index 000000000..09fb8979a --- /dev/null +++ b/web/api/app/View/View/Monitors/xml/edit.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $message)); +echo $xml->asXML(); diff --git a/web/api/app/View/View/Monitors/xml/index.ctp b/web/api/app/View/View/Monitors/xml/index.ctp new file mode 100644 index 000000000..37afc918b --- /dev/null +++ b/web/api/app/View/View/Monitors/xml/index.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $monitors)); +echo $xml->asXML(); diff --git a/web/api/app/View/View/Monitors/xml/view.ctp b/web/api/app/View/View/Monitors/xml/view.ctp new file mode 100644 index 000000000..b33c6e79a --- /dev/null +++ b/web/api/app/View/View/Monitors/xml/view.ctp @@ -0,0 +1,2 @@ +$xml = Xml::fromArray(array('response' => $monitor)); +echo $xml->asXML(); diff --git a/web/api/app/View/View/Pages/home.ctp b/web/api/app/View/View/Pages/home.ctp new file mode 100644 index 000000000..082cc99b0 --- /dev/null +++ b/web/api/app/View/View/Pages/home.ctp @@ -0,0 +1,233 @@ + +

+

+ +

+ 0): + Debugger::checkSecurityKeys(); +endif; +?> + +

+ + 1) Help me configure it + 2) I don't / can't use URL rewriting +

+ +

+=')): + echo ''; + echo __d('cake_dev', 'Your version of PHP is 5.2.8 or higher.'); + echo ''; + else: + echo ''; + echo __d('cake_dev', 'Your version of PHP is too low. You need PHP 5.2.8 or higher to use CakePHP.'); + echo ''; + endif; +?> +

+

+ '; + echo __d('cake_dev', 'Your tmp directory is writable.'); + echo ''; + else: + echo ''; + echo __d('cake_dev', 'Your tmp directory is NOT writable.'); + echo ''; + endif; + ?> +

+

+ '; + echo __d('cake_dev', 'The %s is being used for core caching. To change the config edit %s', ''. $settings['engine'] . 'Engine', 'APP/Config/core.php'); + echo ''; + else: + echo ''; + echo __d('cake_dev', 'Your cache is NOT working. Please check the settings in %s', 'APP/Config/core.php'); + echo ''; + endif; + ?> +

+

+ '; + echo __d('cake_dev', 'Your database configuration file is present.'); + $filePresent = true; + echo ''; + else: + echo ''; + echo __d('cake_dev', 'Your database configuration file is NOT present.'); + echo '
'; + echo __d('cake_dev', 'Rename %s to %s', 'APP/Config/database.php.default', 'APP/Config/database.php'); + echo '
'; + endif; + ?> +

+getMessage(); + if (method_exists($connectionError, 'getAttributes')): + $attributes = $connectionError->getAttributes(); + if (isset($errorMsg['message'])): + $errorMsg .= '
' . $attributes['message']; + endif; + endif; + } +?> +

+ isConnected()): + echo ''; + echo __d('cake_dev', 'CakePHP is able to connect to the database.'); + echo ''; + else: + echo ''; + echo __d('cake_dev', 'CakePHP is NOT able to connect to the database.'); + echo '

'; + echo $errorMsg; + echo '
'; + endif; + ?> +

+ +'; + echo __d('cake_dev', 'PCRE has not been compiled with Unicode support.'); + echo '
'; + echo __d('cake_dev', 'Recompile PCRE with Unicode support by adding --enable-unicode-properties when configuring'); + echo '

'; + endif; +?> + +

+ '; + echo __d('cake_dev', 'DebugKit plugin is present'); + echo ''; + else: + echo ''; + echo __d('cake_dev', 'DebugKit is not installed. It will help you inspect and debug different aspects of your application.'); + echo '
'; + echo __d('cake_dev', 'You can install it from %s', $this->Html->link('GitHub', 'https://github.com/cakephp/debug_kit')); + echo '
'; + endif; + ?> +

+ +

+

+ +To change its layout, edit: %s.
+You can also add some CSS styles for your pages at: %s.', + 'APP/View/Pages/home.ctp', 'APP/View/Layouts/default.ctp', 'APP/webroot/css'); +?> +

+ +

+

+ Html->link( + sprintf('%s %s', __d('cake_dev', 'New'), __d('cake_dev', 'CakePHP 2.0 Docs')), + 'http://book.cakephp.org/2.0/en/', + array('target' => '_blank', 'escape' => false) + ); + ?> +

+

+ Html->link( + __d('cake_dev', 'The 15 min Blog Tutorial'), + 'http://book.cakephp.org/2.0/en/tutorials-and-examples/blog/blog.html', + array('target' => '_blank', 'escape' => false) + ); + ?> +

+ +

+

+

    +
  • + Html->link('DebugKit', 'https://github.com/cakephp/debug_kit') ?>: + +
  • +
  • + Html->link('Localized', 'https://github.com/cakephp/localized') ?>: + +
  • +
+

+ +

+

+ +

+

+ +

+ + diff --git a/web/api/app/View/View/Scaffolds/empty b/web/api/app/View/View/Scaffolds/empty new file mode 100644 index 000000000..e69de29bb diff --git a/web/api/app/vendor/autoload.php b/web/api/app/vendor/autoload.php new file mode 100644 index 000000000..0a2797817 --- /dev/null +++ b/web/api/app/vendor/autoload.php @@ -0,0 +1,7 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0 class loader + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @author Fabien Potencier + * @author Jordi Boggiano + */ +class ClassLoader +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + + private $classMapAuthoritative = false; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * appending or prepending to the ones previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-0 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + return $this->classMapAuthoritative; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 + if ('\\' == $class[0]) { + $class = substr($class, 1); + } + + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + if ($this->classMapAuthoritative) { + return false; + } + + $file = $this->findFileWithExtension($class, '.php'); + + // Search for Hack files if we are running on HHVM + if ($file === null && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if ($file === null) { + // Remember that this class does not exist. + return $this->classMap[$class] = false; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { + if (0 === strpos($class, $prefix)) { + foreach ($this->prefixDirsPsr4[$prefix] as $dir) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (is_file($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/web/api/app/vendor/composer/autoload_classmap.php b/web/api/app/vendor/composer/autoload_classmap.php new file mode 100644 index 000000000..7a91153b0 --- /dev/null +++ b/web/api/app/vendor/composer/autoload_classmap.php @@ -0,0 +1,9 @@ + array($vendorDir . '/composer/installers/src'), +); diff --git a/web/api/app/vendor/composer/autoload_psr4.php b/web/api/app/vendor/composer/autoload_psr4.php new file mode 100644 index 000000000..b265c64a2 --- /dev/null +++ b/web/api/app/vendor/composer/autoload_psr4.php @@ -0,0 +1,9 @@ + $path) { + $loader->set($namespace, $path); + } + + $map = require __DIR__ . '/autoload_psr4.php'; + foreach ($map as $namespace => $path) { + $loader->setPsr4($namespace, $path); + } + + $classMap = require __DIR__ . '/autoload_classmap.php'; + if ($classMap) { + $loader->addClassMap($classMap); + } + + $loader->register(true); + + return $loader; + } +} + +function composerRequiredfd8518a66bb8898e7b22470609a6c8f($file) +{ + require $file; +} diff --git a/web/api/app/vendor/composer/installed.json b/web/api/app/vendor/composer/installed.json new file mode 100644 index 000000000..39d771174 --- /dev/null +++ b/web/api/app/vendor/composer/installed.json @@ -0,0 +1,167 @@ +[ + { + "name": "composer/installers", + "version": "v1.0.21", + "version_normalized": "1.0.21.0", + "source": { + "type": "git", + "url": "https://github.com/composer/installers.git", + "reference": "d64e23fce42a4063d63262b19b8e7c0f3b5e4c45" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/installers/zipball/d64e23fce42a4063d63262b19b8e7c0f3b5e4c45", + "reference": "d64e23fce42a4063d63262b19b8e7c0f3b5e4c45", + "shasum": "" + }, + "replace": { + "roundcube/plugin-installer": "*", + "shama/baton": "*" + }, + "require-dev": { + "composer/composer": "1.0.*@dev", + "phpunit/phpunit": "4.1.*" + }, + "time": "2015-02-18 17:17:01", + "type": "composer-installer", + "extra": { + "class": "Composer\\Installers\\Installer", + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Composer\\Installers\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "description": "A multi-framework Composer library installer", + "homepage": "http://composer.github.com/installers/", + "keywords": [ + "Craft", + "Dolibarr", + "Hurad", + "MODX Evo", + "OXID", + "SMF", + "Thelia", + "WolfCMS", + "agl", + "aimeos", + "annotatecms", + "bitrix", + "cakephp", + "chef", + "codeigniter", + "concrete5", + "croogo", + "dokuwiki", + "drupal", + "elgg", + "fuelphp", + "grav", + "installer", + "joomla", + "kohana", + "laravel", + "lithium", + "magento", + "mako", + "mediawiki", + "modulework", + "moodle", + "phpbb", + "piwik", + "ppi", + "puppet", + "roundcube", + "shopware", + "silverstripe", + "symfony", + "typo3", + "wordpress", + "zend", + "zikula" + ] + }, + { + "name": "friendsofcake/crud", + "version": "3.0.10", + "version_normalized": "3.0.10.0", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfCake/crud.git", + "reference": "c3976f1478c681b0bbc132ec3a3e82c3984eeed5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfCake/crud/zipball/c3976f1478c681b0bbc132ec3a3e82c3984eeed5", + "reference": "c3976f1478c681b0bbc132ec3a3e82c3984eeed5", + "shasum": "" + }, + "require": { + "composer/installers": "*" + }, + "suggest": { + "cakedc/search": "If you want to use the Search Listener" + }, + "time": "2015-04-18 19:08:17", + "type": "cakephp-plugin", + "extra": { + "installer-name": "Crud" + }, + "installation-source": "dist", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christian Winther", + "homepage": "http://cakephp.nu/", + "role": "Author" + }, + { + "name": "José Lorenzo Rodríguez", + "homepage": "https://github.com/lorenzo", + "role": "Contributor" + }, + { + "name": "Andy Dawson", + "homepage": "https://github.com/ad7six", + "role": "Contributor" + }, + { + "name": "ADmad", + "homepage": "https://github.com/admad", + "role": "Contributor" + } + ], + "description": "CakePHP Application development on steroids - rapid prototyping / scaffolding & production ready code - XML / JSON APIs and more", + "homepage": "https://github.com/FriendsOfCake/crud", + "keywords": [ + "bake", + "cake", + "cakephp", + "create", + "crud", + "delete", + "retrieve", + "scaffold", + "scaffolding", + "update" + ] + } +] diff --git a/web/api/app/vendor/composer/installers/.editorconfig b/web/api/app/vendor/composer/installers/.editorconfig new file mode 100644 index 000000000..153cf3ef5 --- /dev/null +++ b/web/api/app/vendor/composer/installers/.editorconfig @@ -0,0 +1,10 @@ +; top-most EditorConfig file +root = true + +; Unix-style newlines +[*] +end_of_line = LF + +[*.php] +indent_style = space +indent_size = 4 diff --git a/web/api/app/vendor/composer/installers/.gitignore b/web/api/app/vendor/composer/installers/.gitignore new file mode 100644 index 000000000..ff7f293dc --- /dev/null +++ b/web/api/app/vendor/composer/installers/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +.idea/ diff --git a/web/api/app/vendor/composer/installers/.travis.yml b/web/api/app/vendor/composer/installers/.travis.yml new file mode 100644 index 000000000..81ca8e101 --- /dev/null +++ b/web/api/app/vendor/composer/installers/.travis.yml @@ -0,0 +1,14 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - curl -s http://getcomposer.org/installer | php -- --quiet + - php composer.phar install --dev + +script: phpunit diff --git a/web/api/app/vendor/composer/installers/LICENSE b/web/api/app/vendor/composer/installers/LICENSE new file mode 100644 index 000000000..85f97fc79 --- /dev/null +++ b/web/api/app/vendor/composer/installers/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Kyle Robinson Young + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/web/api/app/vendor/composer/installers/README.md b/web/api/app/vendor/composer/installers/README.md new file mode 100644 index 000000000..b33177198 --- /dev/null +++ b/web/api/app/vendor/composer/installers/README.md @@ -0,0 +1,191 @@ +# A Multi-Framework [Composer](http://getcomposer.org) Library Installer + +[![Build Status](http://img.shields.io/travis/composer/installers.svg)](http://travis-ci.org/composer/installers) + +This is for PHP package authors to require in their `composer.json`. It will +install their package to the correct location based on the specified package +type. + +The goal of `installers` is to be a simple package type to install path map. +Users can also customize the install path per package and package authors can +modify the package name upon installing. + +`installers` isn't intended on replacing all custom installers. If your +package requires special installation handling then by all means, create a +custom installer to handle it. + +**Natively Supported Frameworks**: + +The following frameworks natively work with Composer and will be +installed to the default `vendor` directory. `composer/installers` +is not needed to install packages with these frameworks: + +* Aura +* Symfony2 +* Yii +* Yii2 + +**Current Supported Package Types**: + +> Stable types are marked as **bold**, this means that installation paths +> for those type will not be changed. Any adjustment for those types would +> require creation of brand new type that will cover required changes. + +| Framework | Types +| --------- | ----- +| Aimeos | `aimeos-extension` +| Asgard | `asgard-module`
`asgard-theme` +| AGL | `agl-module` +| AnnotateCms | `annotatecms-module`
`annotatecms-component`
`annotatecms-service` +| Bitrix | `bitrix-module`
`bitrix-component`
`bitrix-theme` +| CakePHP 2+ | **`cakephp-plugin`** +| Chef | `chef-cookbook`
`chef-role` +| CCFramework | `ccframework-ship`
`ccframework-theme` +| CodeIgniter | `codeigniter-library`
`codeigniter-third-party`
`codeigniter-module` +| concrete5 | `concrete5-block`
`concrete5-package`
`concrete5-theme`
`concrete5-update` +| Craft | `craft-plugin` +| Croogo | `croogo-plugin`
`croogo-theme` +| DokuWiki | `dokuwiki-plugin`
`dokuwiki-template` +| Dolibarr | `dolibarr-module` +| Drupal | `drupal-module`
`drupal-theme`

`drupal-library`
`drupal-profile`
`drupal-drush` +| Elgg | `elgg-plugin` +| FuelPHP v1.x | `fuel-module`
`fuel-package`
`fuel-theme` +| FuelPHP v2.x | `fuelphp-component` +| Grav | `grav-plugin`
`grav-theme` +| Hurad | `hurad-plugin`
`hurad-theme` +| Joomla | `joomla-component`
`joomla-module`
`joomla-template`
`joomla-plugin`
`joomla-library` +| Kirby | **`kirby-plugin`** +| Kohana | **`kohana-module`** +| Laravel | `laravel-library` +| Lithium | **`lithium-library`
`lithium-source`** +| Magento | `magento-library`
`magento-skin`
`magento-theme` +| Mako | `mako-package` +| MODX Evo | `modxevo-snippet`
`modxevo-plugin`
`modxevo-module`
`modxevo-template`
`modxevo-lib` +| MediaWiki | `mediawiki-extension` +| October | **`october-module`
`october-plugin`
`october-theme`** +| OXID | `oxid-module`
`oxid-theme`
`oxid-out` +| MODULEWork | `modulework-module` +| Moodle | `moodle-*` (Please [check source](https://raw.githubusercontent.com/composer/installers/master/src/Composer/Installers/MoodleInstaller.php) for all supported types) +| Piwik | `piwik-plugin` +| phpBB | `phpbb-extension`
`phpbb-style`
`phpbb-language` +| Pimcore | `pimcore-plugin` +| PPI | **`ppi-module`** +| Puppet | `puppet-module` +| REDAXO | `redaxo-addon` +| Roundcube | `roundcube-plugin` +| shopware | `shopware-backend-plugin`
`shopware-core-plugin`
`shopware-frontend-plugin`
`shopware-theme` +| SilverStripe | `silverstripe-module`
`silverstripe-theme` +| SMF | `smf-module`
`smf-theme` +| symfony1 | **`symfony1-plugin`** +| Tusk | `tusk-task`
`tusk-command`
`tusk-asset` +| TYPO3 Flow | `typo3-flow-package`
`typo3-flow-framework`
`typo3-flow-plugin`
`typo3-flow-site`
`typo3-flow-boilerplate`
`typo3-flow-build` +| TYPO3 CMS | `typo3-cms-extension` +| Wolf CMS | `wolfcms-plugin` +| WordPress | `wordpress-plugin`
`wordpress-theme`

`wordpress-muplugin` +| Zend | `zend-library`
`zend-extra`
`zend-module` +| Zikula | `zikula-module`
`zikula-theme` +| Prestashop | `prestashop-module`
`prestashop-theme` + +## Example `composer.json` File + +This is an example for a CakePHP plugin. The only important parts to set in your +composer.json file are `"type": "cakephp-plugin"` which describes what your +package is and `"require": { "composer/installers": "~1.0" }` which tells composer +to load the custom installers. + +```json +{ + "name": "you/ftp", + "type": "cakephp-plugin", + "require": { + "composer/installers": "~1.0" + } +} +``` + +This would install your package to the `Plugin/Ftp/` folder of a CakePHP app +when a user runs `php composer.phar install`. + +So submit your packages to [packagist.org](http://packagist.org)! + +## Custom Install Paths + +If you are consuming a package that uses the `composer/installers` you can +override the install path with the following extra in your `composer.json`: + +```json +{ + "extra": { + "installer-paths": { + "your/custom/path/{$name}/": ["shama/ftp", "vendor/package"] + } + } +} +``` + +A package type can have a custom installation path with a `type:` prefix. + +``` json +{ + "extra": { + "installer-paths": { + "your/custom/path/{$name}/": ["type:wordpress-plugin"] + } + } +} +``` + +This would use your custom path for each of the listed packages. The available +variables to use in your paths are: `{$name}`, `{$vendor}`, `{$type}`. + +## Custom Install Names + +If you're a package author and need your package to be named differently when +installed consider using the `installer-name` extra. + +For example you have a package named `shama/cakephp-ftp` with the type +`cakephp-plugin`. Installing with `composer/installers` would install to the +path `Plugin/CakephpFtp`. Due to the strict naming conventions, you as a +package author actually need the package to be named and installed to +`Plugin/Ftp`. Using the following config within your **package** `composer.json` +will allow this: + +```json +{ + "name": "shama/cakephp-ftp", + "type": "cakephp-plugin", + "extra": { + "installer-name": "Ftp" + } +} +``` + +Please note the name entered into `installer-name` will be the final and will +not be inflected. + +## Contribute! + +* [Fork and clone](https://help.github.com/articles/fork-a-repo). +* Run the command `php composer.phar install --dev` to install the dev + dependencies. See [Composer](https://github.com/composer/composer#installation--usage). +* Use the command `phpunit` to run the tests. See [PHPUnit](http://phpunit.de). +* Create a branch, commit, push and send us a + [pull request](https://help.github.com/articles/using-pull-requests). + +To ensure a consistent code base, you should make sure the code follows the +[Coding Standards](http://symfony.com/doc/2.0/contributing/code/standards.html) +which we borrowed from Symfony. + +If you would like to help, please take a look at the list of +[issues](https://github.com/composer/installers/issues). + +### Should we allow dynamic package types or paths? No. +What are they? The ability for a package author to determine where a package +will be installed either through setting the path directly in their +`composer.json` or through a dynamic package type: `"type": +"framework-install-here"`. + +It has been proposed many times. Even implemented once early on and then +removed. `installers` won't do this because it would allow a single package +author to wipe out entire folders without the user's consent. That user would +then come here to yell at us. diff --git a/web/api/app/vendor/composer/installers/composer.json b/web/api/app/vendor/composer/installers/composer.json new file mode 100644 index 000000000..6ee931ee9 --- /dev/null +++ b/web/api/app/vendor/composer/installers/composer.json @@ -0,0 +1,77 @@ +{ + "name": "composer/installers", + "type": "composer-installer", + "license": "MIT", + "description": "A multi-framework Composer library installer", + "keywords": [ + "installer", + "Aimeos", + "AGL", + "AnnotateCms", + "Bitrix", + "CakePHP", + "Chef", + "CodeIgniter", + "concrete5", + "Craft", + "Croogo", + "DokuWiki", + "Dolibarr", + "Drupal", + "Elgg", + "FuelPHP", + "Grav", + "Hurad", + "Joomla", + "Kohana", + "Laravel", + "Lithium", + "Magento", + "Mako", + "MODX Evo", + "MediaWiki", + "OXID", + "MODULEWork", + "Moodle", + "Piwik", + "phpBB", + "PPI", + "Puppet", + "Roundcube", + "shopware", + "SilverStripe", + "SMF", + "symfony", + "Thelia", + "TYPO3", + "WolfCMS", + "WordPress", + "Zend", + "Zikula" + ], + "homepage": "http://composer.github.com/installers/", + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "autoload": { + "psr-0": { "Composer\\Installers\\": "src/" } + }, + "extra": { + "class": "Composer\\Installers\\Installer", + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "replace": { + "shama/baton": "*", + "roundcube/plugin-installer": "*" + }, + "require-dev": { + "composer/composer": "1.0.*@dev", + "phpunit/phpunit": "4.1.*" + } +} diff --git a/web/api/app/vendor/composer/installers/phpunit.xml.dist b/web/api/app/vendor/composer/installers/phpunit.xml.dist new file mode 100644 index 000000000..cc5cc9915 --- /dev/null +++ b/web/api/app/vendor/composer/installers/phpunit.xml.dist @@ -0,0 +1,25 @@ + + + + + + tests/Composer/Installers + + + + + + src/Composer/Installers + + + \ No newline at end of file diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php new file mode 100644 index 000000000..01b8a4165 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AglInstaller.php @@ -0,0 +1,21 @@ + 'More/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars($vars) + { + $vars['name'] = preg_replace_callback('/(?:^|_|-)(.?)/', function ($matches) { + return strtoupper($matches[1]); + }, $vars['name']); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php new file mode 100644 index 000000000..79a0e958f --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AimeosInstaller.php @@ -0,0 +1,9 @@ + 'ext/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php new file mode 100644 index 000000000..89d7ad905 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php @@ -0,0 +1,11 @@ + 'addons/modules/{$name}/', + 'component' => 'addons/components/{$name}/', + 'service' => 'addons/services/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php new file mode 100644 index 000000000..995ee2b49 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php @@ -0,0 +1,45 @@ + 'Modules/{$name}/', + 'theme' => 'Themes/{$name}/' + ); + + /** + * Format package name. + * + * For package type asgard-module, cut off a trailing '-plugin' if present. + * + * For package type asgard-theme, cut off a trailing '-theme' if present. + * + */ + public function inflectPackageVars($vars) + { + if ($vars['type'] === 'asgard-module') { + return $this->inflectPluginVars($vars); + } + + if ($vars['type'] === 'asgard-theme') { + return $this->inflectThemeVars($vars); + } + + return $vars; + } + + protected function inflectPluginVars($vars) + { + $vars['name'] = ucfirst(preg_replace('/-module/', '', $vars['name'])); + + return $vars; + } + + protected function inflectThemeVars($vars) + { + $vars['name'] = ucfirst(preg_replace('/-theme$/', '', $vars['name'])); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php new file mode 100644 index 000000000..cc27d3e28 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php @@ -0,0 +1,131 @@ +composer = $composer; + $this->package = $package; + } + + /** + * Return the install path based on package type. + * + * @param PackageInterface $package + * @param string $frameworkType + * @return string + */ + public function getInstallPath(PackageInterface $package, $frameworkType = '') + { + $type = $this->package->getType(); + + $prettyName = $this->package->getPrettyName(); + if (strpos($prettyName, '/') !== false) { + list($vendor, $name) = explode('/', $prettyName); + } else { + $vendor = ''; + $name = $prettyName; + } + + $availableVars = $this->inflectPackageVars(compact('name', 'vendor', 'type')); + + $extra = $package->getExtra(); + if (!empty($extra['installer-name'])) { + $availableVars['name'] = $extra['installer-name']; + } + + if ($this->composer->getPackage()) { + $extra = $this->composer->getPackage()->getExtra(); + if (!empty($extra['installer-paths'])) { + $customPath = $this->mapCustomInstallPaths($extra['installer-paths'], $prettyName, $type); + if ($customPath !== false) { + return $this->templatePath($customPath, $availableVars); + } + } + } + + $packageType = substr($type, strlen($frameworkType) + 1); + $locations = $this->getLocations(); + if (!isset($locations[$packageType])) { + throw new \InvalidArgumentException(sprintf('Package type "%s" is not supported', $type)); + } + + return $this->templatePath($locations[$packageType], $availableVars); + } + + /** + * For an installer to override to modify the vars per installer. + * + * @param array $vars + * @return array + */ + public function inflectPackageVars($vars) + { + return $vars; + } + + /** + * Gets the installer's locations + * + * @return array + */ + public function getLocations() + { + return $this->locations; + } + + /** + * Replace vars in a path + * + * @param string $path + * @param array $vars + * @return string + */ + protected function templatePath($path, array $vars = array()) + { + if (strpos($path, '{') !== false) { + extract($vars); + preg_match_all('@\{\$([A-Za-z0-9_]*)\}@i', $path, $matches); + if (!empty($matches[1])) { + foreach ($matches[1] as $var) { + $path = str_replace('{$' . $var . '}', $$var, $path); + } + } + } + + return $path; + } + + /** + * Search through a passed paths array for a custom install path. + * + * @param array $paths + * @param string $name + * @param string $type + * @return string + */ + protected function mapCustomInstallPaths(array $paths, $name, $type) + { + foreach ($paths as $path => $names) { + if (in_array($name, $names) || in_array('type:' . $type, $names)) { + return $path; + } + } + + return false; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php new file mode 100644 index 000000000..48a8367ab --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php @@ -0,0 +1,11 @@ + 'local/modules/{$name}/', + 'component' => 'local/components/{$name}/', + 'theme' => 'local/templates/{$name}/' + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php new file mode 100644 index 000000000..cbeb60b80 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php @@ -0,0 +1,78 @@ + 'Plugin/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars($vars) + { + if ($this->matchesCakeVersion('>=', '3.0.0')) { + return $vars; + } + + $nameParts = explode('/', $vars['name']); + foreach ($nameParts as &$value) { + $value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value)); + $value = str_replace(array('-', '_'), ' ', $value); + $value = str_replace(' ', '', ucwords($value)); + } + $vars['name'] = implode('/', $nameParts); + + return $vars; + } + + /** + * Change the default plugin location when cakephp >= 3.0 + */ + public function getLocations() + { + if ($this->matchesCakeVersion('>=', '3.0.0')) { + $this->locations['plugin'] = $this->composer->getConfig()->get('vendor-dir') . '/{$vendor}/{$name}/'; + } + return $this->locations; + } + + /** + * Check if CakePHP version matches against a version + * + * @param string $matcher + * @param string $version + * @return bool + */ + protected function matchesCakeVersion($matcher, $version) + { + $repositoryManager = $this->composer->getRepositoryManager(); + if ($repositoryManager) { + $repos = $repositoryManager->getLocalRepository(); + if (!$repos) { + return false; + } + $cake3 = new MultiConstraint(array( + new VersionConstraint($matcher, $version), + new VersionConstraint('!=', '9999999-dev'), + )); + $pool = new Pool('dev'); + $pool->addRepository($repos); + $packages = $pool->whatProvides('cakephp/cakephp'); + foreach ($packages as $package) { + $installed = new VersionConstraint('=', $package->getVersion()); + if ($cake3->matches($installed)) { + return true; + break; + } + } + } + return false; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php new file mode 100644 index 000000000..ab2f9aad8 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php @@ -0,0 +1,11 @@ + 'Chef/{$vendor}/{$name}/', + 'role' => 'Chef/roles/{$name}/', + ); +} + diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php new file mode 100644 index 000000000..c887815c9 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php @@ -0,0 +1,10 @@ + 'CCF/orbit/{$name}/', + 'theme' => 'CCF/app/themes/{$name}/', + ); +} \ No newline at end of file diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php new file mode 100644 index 000000000..3b4a4ece1 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php @@ -0,0 +1,11 @@ + 'application/libraries/{$name}/', + 'third-party' => 'application/third_party/{$name}/', + 'module' => 'application/modules/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php new file mode 100644 index 000000000..4d398a445 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php @@ -0,0 +1,12 @@ + 'blocks/{$name}/', + 'package' => 'packages/{$name}/', + 'theme' => 'themes/{$name}/', + 'update' => 'updates/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php new file mode 100644 index 000000000..dc3be8d1a --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CraftInstaller.php @@ -0,0 +1,9 @@ + 'craft/plugins/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php new file mode 100644 index 000000000..d94219d3a --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php @@ -0,0 +1,21 @@ + 'Plugin/{$name}/', + 'theme' => 'View/Themed/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars($vars) + { + $vars['name'] = strtolower(str_replace(array('-', '_'), ' ', $vars['name'])); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php new file mode 100644 index 000000000..cfd638d5f --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php @@ -0,0 +1,50 @@ + 'lib/plugins/{$name}/', + 'template' => 'lib/tpl/{$name}/', + ); + + /** + * Format package name. + * + * For package type dokuwiki-plugin, cut off a trailing '-plugin', + * or leading dokuwiki_ if present. + * + * For package type dokuwiki-template, cut off a trailing '-template' if present. + * + */ + public function inflectPackageVars($vars) + { + + if ($vars['type'] === 'dokuwiki-plugin') { + return $this->inflectPluginVars($vars); + } + + if ($vars['type'] === 'dokuwiki-template') { + return $this->inflectTemplateVars($vars); + } + + return $vars; + } + + protected function inflectPluginVars($vars) + { + $vars['name'] = preg_replace('/-plugin$/', '', $vars['name']); + $vars['name'] = preg_replace('/^dokuwiki_?-?/', '', $vars['name']); + + return $vars; + } + + protected function inflectTemplateVars($vars) + { + $vars['name'] = preg_replace('/-template$/', '', $vars['name']); + $vars['name'] = preg_replace('/^dokuwiki_?-?/', '', $vars['name']); + + return $vars; + } + +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php new file mode 100644 index 000000000..21f7e8e80 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php @@ -0,0 +1,16 @@ + + */ +class DolibarrInstaller extends BaseInstaller +{ + //TODO: Add support for scripts and themes + protected $locations = array( + 'module' => 'htdocs/custom/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php new file mode 100644 index 000000000..179413145 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php @@ -0,0 +1,14 @@ + 'core/', + 'module' => 'modules/{$name}/', + 'theme' => 'themes/{$name}/', + 'library' => 'libraries/{$name}/', + 'profile' => 'profiles/{$name}/', + 'drush' => 'drush/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php new file mode 100644 index 000000000..c0bb609f4 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php @@ -0,0 +1,9 @@ + 'mod/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php new file mode 100644 index 000000000..6eba2e34f --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php @@ -0,0 +1,11 @@ + 'fuel/app/modules/{$name}/', + 'package' => 'fuel/packages/{$name}/', + 'theme' => 'fuel/app/themes/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php new file mode 100644 index 000000000..29d980b30 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php @@ -0,0 +1,9 @@ + 'components/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php new file mode 100644 index 000000000..dbe63e07e --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/GravInstaller.php @@ -0,0 +1,30 @@ + 'user/plugins/{$name}/', + 'theme' => 'user/themes/{$name}/', + ); + + /** + * Format package name + * + * @param array $vars + * + * @return array + */ + public function inflectPackageVars($vars) + { + $restrictedWords = implode('|', array_keys($this->locations)); + + $vars['name'] = strtolower($vars['name']); + $vars['name'] = preg_replace('/^(?:grav-)?(?:(?:'.$restrictedWords.')-)?(.*?)(?:-(?:'.$restrictedWords.'))?$/ui', + '$1', + $vars['name'] + ); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php new file mode 100644 index 000000000..8fe017f0f --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php @@ -0,0 +1,25 @@ + 'plugins/{$name}/', + 'theme' => 'plugins/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars($vars) + { + $nameParts = explode('/', $vars['name']); + foreach ($nameParts as &$value) { + $value = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $value)); + $value = str_replace(array('-', '_'), ' ', $value); + $value = str_replace(' ', '', ucwords($value)); + } + $vars['name'] = implode('/', $nameParts); + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php new file mode 100644 index 000000000..63ba64ce3 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/Installer.php @@ -0,0 +1,163 @@ + 'AimeosInstaller', + 'asgard' => 'AsgardInstaller', + 'agl' => 'AglInstaller', + 'annotatecms' => 'AnnotateCmsInstaller', + 'bitrix' => 'BitrixInstaller', + 'cakephp' => 'CakePHPInstaller', + 'chef' => 'ChefInstaller', + 'ccframework' => 'ClanCatsFrameworkInstaller', + 'codeigniter' => 'CodeIgniterInstaller', + 'concrete5' => 'Concrete5Installer', + 'craft' => 'CraftInstaller', + 'croogo' => 'CroogoInstaller', + 'dokuwiki' => 'DokuWikiInstaller', + 'dolibarr' => 'DolibarrInstaller', + 'drupal' => 'DrupalInstaller', + 'elgg' => 'ElggInstaller', + 'fuel' => 'FuelInstaller', + 'fuelphp' => 'FuelphpInstaller', + 'grav' => 'GravInstaller', + 'hurad' => 'HuradInstaller', + 'joomla' => 'JoomlaInstaller', + 'kirby' => 'KirbyInstaller', + 'kohana' => 'KohanaInstaller', + 'laravel' => 'LaravelInstaller', + 'lithium' => 'LithiumInstaller', + 'magento' => 'MagentoInstaller', + 'mako' => 'MakoInstaller', + 'mediawiki' => 'MediaWikiInstaller', + 'microweber' => 'MicroweberInstaller', + 'modulework' => 'MODULEWorkInstaller', + 'modxevo' => 'MODXEvoInstaller', + 'moodle' => 'MoodleInstaller', + 'october' => 'OctoberInstaller', + 'oxid' => 'OxidInstaller', + 'phpbb' => 'PhpBBInstaller', + 'pimcore' => 'PimcoreInstaller', + 'piwik' => 'PiwikInstaller', + 'ppi' => 'PPIInstaller', + 'puppet' => 'PuppetInstaller', + 'redaxo' => 'RedaxoInstaller', + 'roundcube' => 'RoundcubeInstaller', + 'shopware' => 'ShopwareInstaller', + 'silverstripe' => 'SilverStripeInstaller', + 'smf' => 'SMFInstaller', + 'symfony1' => 'Symfony1Installer', + 'thelia' => 'TheliaInstaller', + 'tusk' => 'TuskInstaller', + 'typo3-cms' => 'TYPO3CmsInstaller', + 'typo3-flow' => 'TYPO3FlowInstaller', + 'whmcs' => 'WHMCSInstaller', + 'wolfcms' => 'WolfCMSInstaller', + 'wordpress' => 'WordPressInstaller', + 'zend' => 'ZendInstaller', + 'zikula' => 'ZikulaInstaller', + 'prestashop' => 'PrestashopInstaller', + ); + + /** + * {@inheritDoc} + */ + public function getInstallPath(PackageInterface $package) + { + $type = $package->getType(); + $frameworkType = $this->findFrameworkType($type); + + if ($frameworkType === false) { + throw new \InvalidArgumentException( + 'Sorry the package type of this package is not yet supported.' + ); + } + + $class = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType]; + $installer = new $class($package, $this->composer); + + return $installer->getInstallPath($package, $frameworkType); + } + + public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package) + { + if (!$repo->hasPackage($package)) { + throw new \InvalidArgumentException('Package is not installed: '.$package); + } + + $repo->removePackage($package); + + $installPath = $this->getInstallPath($package); + $this->io->write(sprintf('Deleting %s - %s', $installPath, $this->filesystem->removeDirectory($installPath) ? 'deleted' : 'not deleted')); + } + + /** + * {@inheritDoc} + */ + public function supports($packageType) + { + $frameworkType = $this->findFrameworkType($packageType); + + if ($frameworkType === false) { + return false; + } + + $locationPattern = $this->getLocationPattern($frameworkType); + + return preg_match('#' . $frameworkType . '-' . $locationPattern . '#', $packageType, $matches) === 1; + } + + /** + * Finds a supported framework type if it exists and returns it + * + * @param string $type + * @return string + */ + protected function findFrameworkType($type) + { + $frameworkType = false; + + krsort($this->supportedTypes); + + foreach ($this->supportedTypes as $key => $val) { + if ($key === substr($type, 0, strlen($key))) { + $frameworkType = substr($type, 0, strlen($key)); + break; + } + } + + return $frameworkType; + } + + /** + * Get the second part of the regular expression to check for support of a + * package type + * + * @param string $frameworkType + * @return string + */ + protected function getLocationPattern($frameworkType) + { + $pattern = false; + if (!empty($this->supportedTypes[$frameworkType])) { + $frameworkClass = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType]; + /** @var BaseInstaller $framework */ + $framework = new $frameworkClass(null, $this->composer); + $locations = array_keys($framework->getLocations()); + $pattern = $locations ? '(' . implode('|', $locations) . ')' : false; + } + + return $pattern ? : '(\w+)'; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php new file mode 100644 index 000000000..9ee775965 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/JoomlaInstaller.php @@ -0,0 +1,15 @@ + 'components/{$name}/', + 'module' => 'modules/{$name}/', + 'template' => 'templates/{$name}/', + 'plugin' => 'plugins/{$name}/', + 'library' => 'libraries/{$name}/', + ); + + // TODO: Add inflector for mod_ and com_ names +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php new file mode 100644 index 000000000..ae7ba8a4b --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/KirbyInstaller.php @@ -0,0 +1,9 @@ + 'site/plugins/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php new file mode 100644 index 000000000..dcd6d2632 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php @@ -0,0 +1,9 @@ + 'modules/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php new file mode 100644 index 000000000..be4d53a7b --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php @@ -0,0 +1,9 @@ + 'libraries/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php new file mode 100644 index 000000000..47bbd4cab --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php @@ -0,0 +1,10 @@ + 'libraries/{$name}/', + 'source' => 'libraries/_source/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php new file mode 100644 index 000000000..9c2e9fb40 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php @@ -0,0 +1,9 @@ + 'modules/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php new file mode 100644 index 000000000..5a664608d --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php @@ -0,0 +1,16 @@ + 'assets/snippets/{$name}/', + 'plugin' => 'assets/plugins/{$name}/', + 'module' => 'assets/modules/{$name}/', + 'template' => 'assets/templates/{$name}/', + 'lib' => 'assets/lib/{$name}/' + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php new file mode 100644 index 000000000..cf18e9478 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php @@ -0,0 +1,11 @@ + 'app/design/frontend/{$name}/', + 'skin' => 'skin/frontend/default/{$name}/', + 'library' => 'lib/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php new file mode 100644 index 000000000..ca3cfacb4 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php @@ -0,0 +1,9 @@ + 'app/packages/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php new file mode 100644 index 000000000..01008c638 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php @@ -0,0 +1,50 @@ + 'extensions/{$name}/', + 'skin' => 'skins/{$name}/', + ); + + /** + * Format package name. + * + * For package type mediawiki-extension, cut off a trailing '-extension' if present and transform + * to CamelCase keeping existing uppercase chars. + * + * For package type mediawiki-skin, cut off a trailing '-skin' if present. + * + */ + public function inflectPackageVars($vars) + { + + if ($vars['type'] === 'mediawiki-extension') { + return $this->inflectExtensionVars($vars); + } + + if ($vars['type'] === 'mediawiki-skin') { + return $this->inflectSkinVars($vars); + } + + return $vars; + } + + protected function inflectExtensionVars($vars) + { + $vars['name'] = preg_replace('/-extension$/', '', $vars['name']); + $vars['name'] = str_replace('-', ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } + + protected function inflectSkinVars($vars) + { + $vars['name'] = preg_replace('/-skin$/', '', $vars['name']); + + return $vars; + } + +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php new file mode 100644 index 000000000..4bbbec8c0 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php @@ -0,0 +1,111 @@ + 'userfiles/modules/{$name}/', + 'module-skin' => 'userfiles/modules/{$name}/templates/', + 'template' => 'userfiles/templates/{$name}/', + 'element' => 'userfiles/elements/{$name}/', + 'vendor' => 'vendor/{$name}/', + 'components' => 'components/{$name}/' + ); + + /** + * Format package name. + * + * For package type microweber-module, cut off a trailing '-module' if present + * + * For package type microweber-template, cut off a trailing '-template' if present. + * + */ + public function inflectPackageVars($vars) + { + if ($vars['type'] === 'microweber-template') { + return $this->inflectTemplateVars($vars); + } + if ($vars['type'] === 'microweber-templates') { + return $this->inflectTemplatesVars($vars); + } + if ($vars['type'] === 'microweber-core') { + return $this->inflectCoreVars($vars); + } + if ($vars['type'] === 'microweber-adapter') { + return $this->inflectCoreVars($vars); + } + if ($vars['type'] === 'microweber-module') { + return $this->inflectModuleVars($vars); + } + if ($vars['type'] === 'microweber-modules') { + return $this->inflectModulesVars($vars); + } + if ($vars['type'] === 'microweber-skin') { + return $this->inflectSkinVars($vars); + } + if ($vars['type'] === 'microweber-element' or $vars['type'] === 'microweber-elements') { + return $this->inflectElementVars($vars); + } + + return $vars; + } + + protected function inflectTemplateVars($vars) + { + $vars['name'] = preg_replace('/-template$/', '', $vars['name']); + $vars['name'] = preg_replace('/template-$/', '', $vars['name']); + + return $vars; + } + + protected function inflectTemplatesVars($vars) + { + $vars['name'] = preg_replace('/-templates$/', '', $vars['name']); + $vars['name'] = preg_replace('/templates-$/', '', $vars['name']); + + return $vars; + } + + protected function inflectCoreVars($vars) + { + $vars['name'] = preg_replace('/-providers$/', '', $vars['name']); + $vars['name'] = preg_replace('/-provider$/', '', $vars['name']); + $vars['name'] = preg_replace('/-adapter$/', '', $vars['name']); + + return $vars; + } + + protected function inflectModuleVars($vars) + { + $vars['name'] = preg_replace('/-module$/', '', $vars['name']); + $vars['name'] = preg_replace('/module-$/', '', $vars['name']); + + return $vars; + } + + protected function inflectModulesVars($vars) + { + $vars['name'] = preg_replace('/-modules$/', '', $vars['name']); + $vars['name'] = preg_replace('/modules-$/', '', $vars['name']); + + return $vars; + } + + protected function inflectSkinVars($vars) + { + $vars['name'] = preg_replace('/-skin$/', '', $vars['name']); + $vars['name'] = preg_replace('/skin-$/', '', $vars['name']); + + return $vars; + } + + protected function inflectElementVars($vars) + { + $vars['name'] = preg_replace('/-elements$/', '', $vars['name']); + $vars['name'] = preg_replace('/elements-$/', '', $vars['name']); + $vars['name'] = preg_replace('/-element$/', '', $vars['name']); + $vars['name'] = preg_replace('/element-$/', '', $vars['name']); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php new file mode 100644 index 000000000..04be73c2a --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php @@ -0,0 +1,47 @@ + 'mod/{$name}/', + 'admin_report' => 'admin/report/{$name}/', + 'tool' => 'admin/tool/{$name}/', + 'assignment' => 'mod/assignment/type/{$name}/', + 'assignsubmission' => 'mod/assign/submission/{$name}/', + 'assignfeedback' => 'mod/assign/feedback/{$name}/', + 'auth' => 'auth/{$name}/', + 'availability' => 'availability/condition/{$name}/', + 'block' => 'blocks/{$name}/', + 'calendartype' => 'calendar/type/{$name}/', + 'format' => 'course/format/{$name}/', + 'coursereport' => 'course/report/{$name}/', + 'datafield' => 'mod/data/field/{$name}/', + 'datapreset' => 'mod/data/preset/{$name}/', + 'editor' => 'lib/editor/{$name}/', + 'enrol' => 'enrol/{$name}/', + 'filter' => 'filter/{$name}/', + 'gradeexport' => 'grade/export/{$name}/', + 'gradeimport' => 'grade/import/{$name}/', + 'gradereport' => 'grade/report/{$name}/', + 'gradingform' => 'grade/grading/form/{$name}/', + 'local' => 'local/{$name}/', + 'message' => 'message/output/{$name}/', + 'plagiarism' => 'plagiarism/{$name}/', + 'portfolio' => 'portfolio/{$name}/', + 'qbehaviour' => 'question/behaviour/{$name}/', + 'qformat' => 'question/format/{$name}/', + 'qtype' => 'question/type/{$name}/', + 'quizaccess' => 'mod/quiz/accessrule/{$name}/', + 'quiz' => 'mod/quiz/report/{$name}/', + 'report' => 'report/{$name}/', + 'repository' => 'repository/{$name}/', + 'scormreport' => 'mod/scorm/report/{$name}/', + 'theme' => 'theme/{$name}/', + 'profilefield' => 'user/profile/field/{$name}/', + 'webservice' => 'webservice/{$name}/', + 'workshopallocation' => 'mod/workshop/allocation/{$name}/', + 'workshopeval' => 'mod/workshop/eval/{$name}/', + 'workshopform' => 'mod/workshop/form/{$name}/' + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php new file mode 100644 index 000000000..6bf53fd14 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php @@ -0,0 +1,46 @@ + 'modules/{$name}/', + 'plugin' => 'plugins/{$vendor}/{$name}/', + 'theme' => 'themes/{$name}/' + ); + + /** + * Format package name. + * + * For package type october-plugin, cut off a trailing '-plugin' if present. + * + * For package type october-theme, cut off a trailing '-theme' if present. + * + */ + public function inflectPackageVars($vars) + { + if ($vars['type'] === 'october-plugin') { + return $this->inflectPluginVars($vars); + } + + if ($vars['type'] === 'october-theme') { + return $this->inflectThemeVars($vars); + } + + return $vars; + } + + protected function inflectPluginVars($vars) + { + $vars['name'] = preg_replace('/-plugin$/', '', $vars['name']); + + return $vars; + } + + protected function inflectThemeVars($vars) + { + $vars['name'] = preg_replace('/-theme$/', '', $vars['name']); + + return $vars; + } +} \ No newline at end of file diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php new file mode 100644 index 000000000..22fb56aa1 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php @@ -0,0 +1,11 @@ + 'modules/{$name}/', + 'theme' => 'application/views/{$name}/', + 'out' => 'out/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php new file mode 100644 index 000000000..170136f98 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php @@ -0,0 +1,9 @@ + 'modules/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php new file mode 100644 index 000000000..deb2b77a6 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php @@ -0,0 +1,11 @@ + 'ext/{$vendor}/{$name}/', + 'language' => 'language/{$name}/', + 'style' => 'styles/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php new file mode 100644 index 000000000..4781fa6d1 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PimcoreInstaller.php @@ -0,0 +1,21 @@ + 'plugins/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars($vars) + { + $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name'])); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php new file mode 100644 index 000000000..c17f4572b --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php @@ -0,0 +1,32 @@ + 'plugins/{$name}/', + ); + + /** + * Format package name to CamelCase + * @param array $vars + * + * @return array + */ + public function inflectPackageVars($vars) + { + $vars['name'] = strtolower(preg_replace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name'])); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php new file mode 100644 index 000000000..4c8421e36 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php @@ -0,0 +1,10 @@ + 'modules/{$name}/', + 'theme' => 'themes/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php new file mode 100644 index 000000000..77cc3dd87 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php @@ -0,0 +1,11 @@ + 'modules/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php new file mode 100644 index 000000000..09544576b --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php @@ -0,0 +1,10 @@ + 'redaxo/include/addons/{$name}/', + 'bestyle-plugin' => 'redaxo/include/addons/be_style/plugins/{$name}/' + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php new file mode 100644 index 000000000..d8d795be0 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php @@ -0,0 +1,22 @@ + 'plugins/{$name}/', + ); + + /** + * Lowercase name and changes the name to a underscores + * + * @param array $vars + * @return array + */ + public function inflectPackageVars($vars) + { + $vars['name'] = strtolower(str_replace('-', '_', $vars['name'])); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php new file mode 100644 index 000000000..1acd3b14c --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php @@ -0,0 +1,10 @@ + 'Sources/{$name}/', + 'theme' => 'Themes/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php new file mode 100644 index 000000000..673f1fc1f --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php @@ -0,0 +1,58 @@ + 'engine/Shopware/Plugins/Local/Backend/{$name}/', + 'core-plugin' => 'engine/Shopware/Plugins/Local/Core/{$name}/', + 'frontend-plugin' => 'engine/Shopware/Plugins/Local/Frontend/{$name}/', + 'theme' => 'templates/{$name}/' + ); + + /** + * Transforms the names + * @param array $vars + * @return array + */ + public function inflectPackageVars($vars) + { + if ($vars['type'] === 'shopware-theme') { + return $this->correctThemeName($vars); + } else { + return $this->correctPluginName($vars); + } + } + + /** + * Changes the name to a camelcased combination of vendor and name + * @param array $vars + * @return array + */ + private function correctPluginName($vars) + { + $camelCasedName = preg_replace_callback('/(-[a-z])/', function ($matches) { + return strtoupper($matches[0][1]); + }, $vars['name']); + + $vars['name'] = ucfirst($vars['vendor']) . ucfirst($camelCasedName); + + return $vars; + } + + /** + * Changes the name to a underscore separated name + * @param array $vars + * @return array + */ + private function correctThemeName($vars) + { + $vars['name'] = str_replace('-', '_', $vars['name']); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php new file mode 100644 index 000000000..17ca543a2 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php @@ -0,0 +1,36 @@ + '{$name}/', + 'theme' => 'themes/{$name}/', + ); + + /** + * Return the install path based on package type. + * + * Relies on built-in BaseInstaller behaviour with one exception: silverstripe/framework + * must be installed to 'sapphire' and not 'framework' if the version is <3.0.0 + * + * @param PackageInterface $package + * @param string $frameworkType + * @return string + */ + public function getInstallPath(PackageInterface $package, $frameworkType = '') + { + if ( + $package->getName() == 'silverstripe/framework' + && preg_match('/^\d+\.\d+\.\d+/', $package->getVersion()) + && version_compare($package->getVersion(), '2.999.999') < 0 + ) { + return $this->templatePath($this->locations['module'], array('name' => 'sapphire')); + } else { + return parent::getInstallPath($package, $frameworkType); + } + + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php new file mode 100644 index 000000000..1675c4f21 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/Symfony1Installer.php @@ -0,0 +1,26 @@ + + */ +class Symfony1Installer extends BaseInstaller +{ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars($vars) + { + $vars['name'] = preg_replace_callback('/(-[a-z])/', function ($matches) { + return strtoupper($matches[0][1]); + }, $vars['name']); + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php new file mode 100644 index 000000000..8220b40df --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3CmsInstaller.php @@ -0,0 +1,14 @@ + + */ +class TYPO3CmsInstaller extends BaseInstaller +{ + protected $locations = array( + 'extension' => 'typo3conf/ext/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php new file mode 100644 index 000000000..42572f44f --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TYPO3FlowInstaller.php @@ -0,0 +1,38 @@ + 'Packages/Application/{$name}/', + 'framework' => 'Packages/Framework/{$name}/', + 'plugin' => 'Packages/Plugins/{$name}/', + 'site' => 'Packages/Sites/{$name}/', + 'boilerplate' => 'Packages/Boilerplates/{$name}/', + 'build' => 'Build/{$name}/', + ); + + /** + * Modify the package name to be a TYPO3 Flow style key. + * + * @param array $vars + * @return array + */ + public function inflectPackageVars($vars) + { + $autoload = $this->package->getAutoload(); + if (isset($autoload['psr-0']) && is_array($autoload['psr-0'])) { + $namespace = key($autoload['psr-0']); + $vars['name'] = str_replace('\\', '.', $namespace); + } + if (isset($autoload['psr-4']) && is_array($autoload['psr-4'])) { + $namespace = key($autoload['psr-4']); + $vars['name'] = rtrim(str_replace('\\', '.', $namespace), '.'); + } + + return $vars; + } +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php new file mode 100644 index 000000000..158af5261 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php @@ -0,0 +1,12 @@ + 'local/modules/{$name}/', + 'frontoffice-template' => 'templates/frontOffice/{$name}/', + 'backoffice-template' => 'templates/backOffice/{$name}/', + 'email-template' => 'templates/email/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php new file mode 100644 index 000000000..7c0113b85 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php @@ -0,0 +1,14 @@ + + */ + class TuskInstaller extends BaseInstaller + { + protected $locations = array( + 'task' => '.tusk/tasks/{$name}/', + 'command' => '.tusk/commands/{$name}/', + 'asset' => 'assets/tusk/{$name}/', + ); + } diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php new file mode 100644 index 000000000..2cbb4a463 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php @@ -0,0 +1,10 @@ + 'modules/gateways/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php new file mode 100644 index 000000000..cb387881d --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php @@ -0,0 +1,9 @@ + 'wolf/plugins/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php new file mode 100644 index 000000000..b03219c69 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php @@ -0,0 +1,11 @@ + 'wp-content/plugins/{$name}/', + 'theme' => 'wp-content/themes/{$name}/', + 'muplugin' => 'wp-content/mu-plugins/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php new file mode 100644 index 000000000..bde9bc8c8 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php @@ -0,0 +1,11 @@ + 'library/{$name}/', + 'extra' => 'extras/library/{$name}/', + 'module' => 'module/{$name}/', + ); +} diff --git a/web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php new file mode 100644 index 000000000..56cdf5da7 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php @@ -0,0 +1,10 @@ + 'modules/{$vendor}-{$name}/', + 'theme' => 'themes/{$vendor}-{$name}/' + ); +} diff --git a/web/api/app/vendor/composer/installers/src/bootstrap.php b/web/api/app/vendor/composer/installers/src/bootstrap.php new file mode 100644 index 000000000..0de276ee2 --- /dev/null +++ b/web/api/app/vendor/composer/installers/src/bootstrap.php @@ -0,0 +1,13 @@ +installer = new AsgardInstaller( + new Package('NyanCat', '4.2', '4.2'), + new Composer() + ); + } + + /** + * @dataProvider packageNameInflectionProvider + */ + public function testInflectPackageVars($type, $name, $expected) + { + $this->assertEquals( + $this->installer->inflectPackageVars(array('name' => $name, 'type' => $type)), + array('name' => $expected, 'type' => $type) + ); + } + + public function packageNameInflectionProvider() + { + return array( + array( + 'asgard-module', + 'asgard-module', + 'Asgard' + ), + array( + 'asgard-module', + 'blog', + 'Blog' + ), + // tests that exactly one '-theme' is cut off + array( + 'asgard-theme', + 'some-theme-theme', + 'Some-theme', + ), + // tests that names without '-theme' suffix stay valid + array( + 'asgard-theme', + 'someothertheme', + 'Someothertheme', + ), + ); + } +} diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php new file mode 100644 index 000000000..976bd9be8 --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/CakePHPInstallerTest.php @@ -0,0 +1,115 @@ +package = new Package('CamelCased', '1.0', '1.0'); + $this->io = $this->getMock('Composer\IO\PackageInterface'); + $this->composer = new Composer(); + $this->composer->setConfig(new Config(false)); + } + + /** + * testInflectPackageVars + * + * @return void + */ + public function testInflectPackageVars() + { + $installer = new CakePHPInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'CamelCased')); + $this->assertEquals($result, array('name' => 'CamelCased')); + + $installer = new CakePHPInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'with-dash')); + $this->assertEquals($result, array('name' => 'WithDash')); + + $installer = new CakePHPInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'with_underscore')); + $this->assertEquals($result, array('name' => 'WithUnderscore')); + + $installer = new CakePHPInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'cake/acl')); + $this->assertEquals($result, array('name' => 'Cake/Acl')); + + $installer = new CakePHPInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'cake/debug-kit')); + $this->assertEquals($result, array('name' => 'Cake/DebugKit')); + } + + /** + * Test getLocations returning appropriate values based on CakePHP version + * + */ + public function testGetLocations() { + $package = new RootPackage('CamelCased', '1.0', '1.0'); + $composer = $this->composer; + $rm = new RepositoryManager( + $this->getMock('Composer\IO\IOInterface'), + $this->getMock('Composer\Config') + ); + $composer->setRepositoryManager($rm); + $installer = new CakePHPInstaller($package, $composer); + + // 2.0 < cakephp < 3.0 + $this->setCakephpVersion($rm, '2.0.0'); + $result = $installer->getLocations(); + $this->assertContains('Plugin/', $result['plugin']); + + $this->setCakephpVersion($rm, '2.5.9'); + $result = $installer->getLocations(); + $this->assertContains('Plugin/', $result['plugin']); + + $this->setCakephpVersion($rm, '~2.5'); + $result = $installer->getLocations(); + $this->assertContains('Plugin/', $result['plugin']); + + // special handling for 2.x versions when 3.x is still in development + $this->setCakephpVersion($rm, 'dev-master'); + $result = $installer->getLocations(); + $this->assertContains('Plugin/', $result['plugin']); + + $this->setCakephpVersion($rm, '>=2.5'); + $result = $installer->getLocations(); + $this->assertContains('Plugin/', $result['plugin']); + + // cakephp >= 3.0 + $this->setCakephpVersion($rm, '3.0.*-dev'); + $result = $installer->getLocations(); + $this->assertContains('vendor/{$vendor}/{$name}/', $result['plugin']); + + $this->setCakephpVersion($rm, '~8.8'); + $result = $installer->getLocations(); + $this->assertEquals('vendor/{$vendor}/{$name}/', $result['plugin']); + } + + protected function setCakephpVersion($rm, $version) { + $parser = new VersionParser(); + list(, $version) = explode(' ', $parser->parseConstraints($version)); + $installed = new InstalledArrayRepository(); + $package = new Package('cakephp/cakephp', $version, $version); + $installed->addPackage($package); + $rm->setLocalRepository($installed); + } + +} diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php new file mode 100644 index 000000000..9e385e6a8 --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/DokuWikiInstallerTest.php @@ -0,0 +1,89 @@ +installer = new DokuWikiInstaller( + new Package('NyanCat', '4.2', '4.2'), + new Composer() + ); + } + + /** + * @dataProvider packageNameInflectionProvider + */ + public function testInflectPackageVars($type, $name, $expected) + { + $this->assertEquals( + $this->installer->inflectPackageVars(array('name' => $name, 'type'=>$type)), + array('name' => $expected, 'type'=>$type) + ); + } + + public function packageNameInflectionProvider() + { + return array( + array( + 'dokuwiki-plugin', + 'dokuwiki-test-plugin', + 'test', + ), + array( + 'dokuwiki-plugin', + 'test-plugin', + 'test', + ), + array( + 'dokuwiki-plugin', + 'dokuwiki_test', + 'test', + ), + array( + 'dokuwiki-plugin', + 'test', + 'test', + ), + array( + 'dokuwiki-plugin', + 'test-template', + 'test-template', + ), + array( + 'dokuwiki-template', + 'dokuwiki-test-template', + 'test', + ), + array( + 'dokuwiki-template', + 'test-template', + 'test', + ), + array( + 'dokuwiki-template', + 'dokuwiki_test', + 'test', + ), + array( + 'dokuwiki-template', + 'test', + 'test', + ), + array( + 'dokuwiki-template', + 'test-plugin', + 'test-plugin', + ), + ); + } +} diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php new file mode 100644 index 000000000..b757799b4 --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/GravInstallerTest.php @@ -0,0 +1,63 @@ +composer = new Composer(); + } + + public function testInflectPackageVars() + { + $package = $this->getPackage('vendor/name', '0.0.0'); + $installer = new GravInstaller($package, $this->composer); + $packageVars = $this->getPackageVars($package); + + $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => 'test'))); + $this->assertEquals('test', $result['name']); + + foreach ($installer->getLocations() as $name => $location) { + $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "$name-test"))); + $this->assertEquals('test', $result['name']); + $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "test-$name"))); + $this->assertEquals('test', $result['name']); + $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "$name-test-test"))); + $this->assertEquals('test-test', $result['name']); + $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "test-test-$name"))); + $this->assertEquals('test-test', $result['name']); + $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-$name-test"))); + $this->assertEquals('test', $result['name']); + $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-test-$name"))); + $this->assertEquals('test', $result['name']); + $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-$name-test-test"))); + $this->assertEquals('test-test', $result['name']); + $result = $installer->inflectPackageVars(array_merge($packageVars, array('name' => "grav-test-test-$name"))); + $this->assertEquals('test-test', $result['name']); + } + } + + /** + * @param $package \Composer\Package\PackageInterface + */ + public function getPackageVars($package) + { + $type = $package->getType(); + + $prettyName = $package->getPrettyName(); + if (strpos($prettyName, '/') !== false) { + list($vendor, $name) = explode('/', $prettyName); + } else { + $vendor = ''; + $name = $prettyName; + } + + return compact('name', 'vendor', 'type'); + } +} diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php new file mode 100644 index 000000000..a516daf07 --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/InstallerTest.php @@ -0,0 +1,422 @@ +fs = new Filesystem; + + $this->composer = new Composer(); + $this->config = new Config(); + $this->composer->setConfig($this->config); + + $this->vendorDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'baton-test-vendor'; + $this->ensureDirectoryExistsAndClear($this->vendorDir); + + $this->binDir = realpath(sys_get_temp_dir()) . DIRECTORY_SEPARATOR . 'baton-test-bin'; + $this->ensureDirectoryExistsAndClear($this->binDir); + + $this->config->merge(array( + 'config' => array( + 'vendor-dir' => $this->vendorDir, + 'bin-dir' => $this->binDir, + ), + )); + + $this->dm = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->disableOriginalConstructor() + ->getMock(); + $this->composer->setDownloadManager($this->dm); + + $this->repository = $this->getMock('Composer\Repository\InstalledRepositoryInterface'); + $this->io = $this->getMock('Composer\IO\IOInterface'); + } + + /** + * tearDown + * + * @return void + */ + public function tearDown() + { + $this->fs->removeDirectory($this->vendorDir); + $this->fs->removeDirectory($this->binDir); + } + + /** + * testSupports + * + * @return void + * + * @dataProvider dataForTestSupport + */ + public function testSupports($type, $expected) + { + $installer = new Installer($this->io, $this->composer); + $this->assertSame($expected, $installer->supports($type), sprintf('Failed to show support for %s', $type)); + } + + /** + * dataForTestSupport + */ + public function dataForTestSupport() + { + return array( + array('agl-module', true), + array('aimeos-extension', true), + array('annotatecms-module', true), + array('annotatecms-component', true), + array('annotatecms-service', true), + array('bitrix-module', true), + array('bitrix-component', true), + array('bitrix-theme', true), + array('cakephp', false), + array('cakephp-', false), + array('cakephp-app', false), + array('cakephp-plugin', true), + array('chef-cookbook', true), + array('chef-role', true), + array('codeigniter-app', false), + array('codeigniter-library', true), + array('codeigniter-third-party', true), + array('codeigniter-module', true), + array('concrete5-block', true), + array('concrete5-package', true), + array('concrete5-theme', true), + array('concrete5-update', true), + array('craft-plugin', true), + array('croogo-plugin', true), + array('croogo-theme', true), + array('dokuwiki-plugin', true), + array('dokuwiki-template', true), + array('drupal-module', true), + array('dolibarr-module', true), + array('elgg-plugin', true), + array('fuel-module', true), + array('fuel-package', true), + array('fuel-theme', true), + array('fuelphp-component', true), + array('hurad-plugin', true), + array('hurad-theme', true), + array('joomla-library', true), + array('kirby-plugin', true), + array('kohana-module', true), + array('laravel-library', true), + array('lithium-library', true), + array('magento-library', true), + array('mako-package', true), + array('modxevo-snippet', true), + array('modxevo-plugin', true), + array('modxevo-module', true), + array('modxevo-template', true), + array('modxevo-lib', true), + array('mediawiki-extension', true), + array('mediawiki-skin', true), + array('microweber-module', true), + array('modulework-module', true), + array('moodle-mod', true), + array('october-module', true), + array('october-plugin', true), + array('piwik-plugin', true), + array('phpbb-extension', true), + array('pimcore-plugin', true), + array('ppi-module', true), + array('prestashop-module', true), + array('prestashop-theme', true), + array('puppet-module', true), + array('redaxo-addon', true), + array('redaxo-bestyle-plugin', true), + array('roundcube-plugin', true), + array('shopware-backend-plugin', true), + array('shopware-core-plugin', true), + array('shopware-frontend-plugin', true), + array('shopware-theme', true), + array('silverstripe-module', true), + array('silverstripe-theme', true), + array('smf-module', true), + array('smf-theme', true), + array('symfony1-plugin', true), + array('thelia-module', true), + array('thelia-frontoffice-template', true), + array('thelia-backoffice-template', true), + array('thelia-email-template', true), + array('tusk-task', true), + array('tusk-asset', true), + array('typo3-flow-plugin', true), + array('typo3-cms-extension', true), + array('whmcs-gateway', true), + array('wolfcms-plugin', true), + array('wordpress-plugin', true), + array('wordpress-core', false), + array('zend-library', true), + array('zikula-module', true), + array('zikula-theme', true), + ); + } + + /** + * testInstallPath + * + * @dataProvider dataForTestInstallPath + */ + public function testInstallPath($type, $path, $name, $version = '1.0.0') + { + $installer = new Installer($this->io, $this->composer); + $package = new Package($name, $version, $version); + + $package->setType($type); + $result = $installer->getInstallPath($package); + $this->assertEquals($path, $result); + } + + /** + * dataFormTestInstallPath + */ + public function dataForTestInstallPath() + { + return array( + array('agl-module', 'More/MyTestPackage/', 'agl/my_test-package'), + array('aimeos-extension', 'ext/ai-test/', 'author/ai-test'), + array('annotatecms-module', 'addons/modules/my_module/', 'vysinsky/my_module'), + array('annotatecms-component', 'addons/components/my_component/', 'vysinsky/my_component'), + array('annotatecms-service', 'addons/services/my_service/', 'vysinsky/my_service'), + array('bitrix-module', 'local/modules/my_module/', 'author/my_module'), + array('bitrix-component', 'local/components/my_component/', 'author/my_component'), + array('bitrix-theme', 'local/templates/my_theme/', 'author/my_theme'), + array('cakephp-plugin', 'Plugin/Ftp/', 'shama/ftp'), + array('chef-cookbook', 'Chef/mre/my_cookbook/', 'mre/my_cookbook'), + array('chef-role', 'Chef/roles/my_role/', 'mre/my_role'), + array('codeigniter-library', 'application/libraries/my_package/', 'shama/my_package'), + array('codeigniter-module', 'application/modules/my_package/', 'shama/my_package'), + array('concrete5-block', 'blocks/concrete5_block/', 'remo/concrete5_block'), + array('concrete5-package', 'packages/concrete5_package/', 'remo/concrete5_package'), + array('concrete5-theme', 'themes/concrete5_theme/', 'remo/concrete5_theme'), + array('concrete5-update', 'updates/concrete5/', 'concrete5/concrete5'), + array('craft-plugin', 'craft/plugins/my_plugin/', 'mdcpepper/my_plugin'), + array('croogo-plugin', 'Plugin/Sitemaps/', 'fahad19/sitemaps'), + array('croogo-theme', 'View/Themed/Readable/', 'rchavik/readable'), + array('dokuwiki-plugin', 'lib/plugins/someplugin/', 'author/someplugin'), + array('dokuwiki-template', 'lib/tpl/sometemplate/', 'author/sometemplate'), + array('dolibarr-module', 'htdocs/custom/my_module/', 'shama/my_module'), + array('drupal-module', 'modules/my_module/', 'shama/my_module'), + array('drupal-theme', 'themes/my_module/', 'shama/my_module'), + array('drupal-profile', 'profiles/my_module/', 'shama/my_module'), + array('drupal-drush', 'drush/my_module/', 'shama/my_module'), + array('elgg-plugin', 'mod/sample_plugin/', 'test/sample_plugin'), + array('fuel-module', 'fuel/app/modules/module/', 'fuel/module'), + array('fuel-package', 'fuel/packages/orm/', 'fuel/orm'), + array('fuel-theme', 'fuel/app/themes/theme/', 'fuel/theme'), + array('fuelphp-component', 'components/demo/', 'fuelphp/demo'), + array('hurad-plugin', 'plugins/Akismet/', 'atkrad/akismet'), + array('hurad-theme', 'plugins/Hurad2013/', 'atkrad/Hurad2013'), + array('joomla-plugin', 'plugins/my_plugin/', 'shama/my_plugin'), + array('kirby-plugin', 'site/plugins/my_plugin/', 'shama/my_plugin'), + array('kohana-module', 'modules/my_package/', 'shama/my_package'), + array('laravel-library', 'libraries/my_package/', 'shama/my_package'), + array('lithium-library', 'libraries/li3_test/', 'user/li3_test'), + array('magento-library', 'lib/foo/', 'test/foo'), + array('modxevo-snippet', 'assets/snippets/my_snippet/', 'shama/my_snippet'), + array('modxevo-plugin', 'assets/plugins/my_plugin/', 'shama/my_plugin'), + array('modxevo-module', 'assets/modules/my_module/', 'shama/my_module'), + array('modxevo-template', 'assets/templates/my_template/', 'shama/my_template'), + array('modxevo-lib', 'assets/lib/my_lib/', 'shama/my_lib'), + array('mako-package', 'app/packages/my_package/', 'shama/my_package'), + array('mediawiki-extension', 'extensions/APC/', 'author/APC'), + array('mediawiki-extension', 'extensions/APC/', 'author/APC-extension'), + array('mediawiki-extension', 'extensions/UploadWizard/', 'author/upload-wizard'), + array('mediawiki-extension', 'extensions/SyntaxHighlight_GeSHi/', 'author/syntax-highlight_GeSHi'), + array('mediawiki-skin', 'skins/someskin/', 'author/someskin-skin'), + array('mediawiki-skin', 'skins/someskin/', 'author/someskin'), + array('microweber-module', 'userfiles/modules/my-thing/', 'author/my-thing-module'), + array('modulework-module', 'modules/my_package/', 'shama/my_package'), + array('moodle-mod', 'mod/my_package/', 'shama/my_package'), + array('october-module', 'modules/my_plugin/', 'shama/my_plugin'), + array('october-plugin', 'plugins/shama/my_plugin/', 'shama/my_plugin'), + array('october-theme', 'themes/my_theme/', 'shama/my_theme'), + array('piwik-plugin', 'plugins/VisitSummary/', 'shama/visit-summary'), + array('prestashop-module', 'modules/a-module/', 'vendor/a-module'), + array('prestashop-theme', 'themes/a-theme/', 'vendor/a-theme'), + array('phpbb-extension', 'ext/test/foo/', 'test/foo'), + array('phpbb-style', 'styles/foo/', 'test/foo'), + array('phpbb-language', 'language/foo/', 'test/foo'), + array('pimcore-plugin', 'plugins/MyPlugin/', 'ubikz/my_plugin'), + array('ppi-module', 'modules/foo/', 'test/foo'), + array('puppet-module', 'modules/puppet-name/', 'puppet/puppet-name'), + array('redaxo-addon', 'redaxo/include/addons/my_plugin/', 'shama/my_plugin'), + array('redaxo-bestyle-plugin', 'redaxo/include/addons/be_style/plugins/my_plugin/', 'shama/my_plugin'), + array('roundcube-plugin', 'plugins/base/', 'test/base'), + array('roundcube-plugin', 'plugins/replace_dash/', 'test/replace-dash'), + array('shopware-backend-plugin', 'engine/Shopware/Plugins/Local/Backend/ShamaMyBackendPlugin/', 'shama/my-backend-plugin'), + array('shopware-core-plugin', 'engine/Shopware/Plugins/Local/Core/ShamaMyCorePlugin/', 'shama/my-core-plugin'), + array('shopware-frontend-plugin', 'engine/Shopware/Plugins/Local/Frontend/ShamaMyFrontendPlugin/', 'shama/my-frontend-plugin'), + array('shopware-theme', 'templates/my_theme/', 'shama/my-theme'), + array('silverstripe-module', 'my_module/', 'shama/my_module'), + array('silverstripe-module', 'sapphire/', 'silverstripe/framework', '2.4.0'), + array('silverstripe-module', 'framework/', 'silverstripe/framework', '3.0.0'), + array('silverstripe-module', 'framework/', 'silverstripe/framework', '3.0.0-rc1'), + array('silverstripe-module', 'framework/', 'silverstripe/framework', 'my/branch'), + array('silverstripe-theme', 'themes/my_theme/', 'shama/my_theme'), + array('smf-module', 'Sources/my_module/', 'shama/my_module'), + array('smf-theme', 'Themes/my_theme/', 'shama/my_theme'), + array('symfony1-plugin', 'plugins/sfShamaPlugin/', 'shama/sfShamaPlugin'), + array('symfony1-plugin', 'plugins/sfShamaPlugin/', 'shama/sf-shama-plugin'), + array('thelia-module', 'local/modules/my_module/', 'shama/my_module'), + array('thelia-frontoffice-template', 'templates/frontOffice/my_template_fo/', 'shama/my_template_fo'), + array('thelia-backoffice-template', 'templates/backOffice/my_template_bo/', 'shama/my_template_bo'), + array('thelia-email-template', 'templates/email/my_template_email/', 'shama/my_template_email'), + array('tusk-task', '.tusk/tasks/my_task/', 'shama/my_task'), + array('typo3-flow-package', 'Packages/Application/my_package/', 'shama/my_package'), + array('typo3-flow-build', 'Build/my_package/', 'shama/my_package'), + array('typo3-cms-extension', 'typo3conf/ext/my_extension/', 'shama/my_extension'), + array('whmcs-gateway', 'modules/gateways/gateway_name/', 'vendor/gateway_name'), + array('wolfcms-plugin', 'wolf/plugins/my_plugin/', 'shama/my_plugin'), + array('wordpress-plugin', 'wp-content/plugins/my_plugin/', 'shama/my_plugin'), + array('wordpress-muplugin', 'wp-content/mu-plugins/my_plugin/', 'shama/my_plugin'), + array('zend-extra', 'extras/library/zend_test/', 'shama/zend_test'), + array('zikula-module', 'modules/my-test_module/', 'my/test_module'), + array('zikula-theme', 'themes/my-test_theme/', 'my/test_theme'), + ); + } + + /** + * testGetCakePHPInstallPathException + * + * @return void + * + * @expectedException \InvalidArgumentException + */ + public function testGetCakePHPInstallPathException() + { + $installer = new Installer($this->io, $this->composer); + $package = new Package('shama/ftp', '1.0.0', '1.0.0'); + + $package->setType('cakephp-whoops'); + $result = $installer->getInstallPath($package); + } + + /** + * testCustomInstallPath + */ + public function testCustomInstallPath() + { + $installer = new Installer($this->io, $this->composer); + $package = new Package('shama/ftp', '1.0.0', '1.0.0'); + $package->setType('cakephp-plugin'); + $consumerPackage = new RootPackage('foo/bar', '1.0.0', '1.0.0'); + $this->composer->setPackage($consumerPackage); + $consumerPackage->setExtra(array( + 'installer-paths' => array( + 'my/custom/path/{$name}/' => array( + 'shama/ftp', + 'foo/bar', + ), + ), + )); + $result = $installer->getInstallPath($package); + $this->assertEquals('my/custom/path/Ftp/', $result); + } + + /** + * testCustomInstallerName + */ + public function testCustomInstallerName() + { + $installer = new Installer($this->io, $this->composer); + $package = new Package('shama/cakephp-ftp-plugin', '1.0.0', '1.0.0'); + $package->setType('cakephp-plugin'); + $package->setExtra(array( + 'installer-name' => 'FTP', + )); + $result = $installer->getInstallPath($package); + $this->assertEquals('Plugin/FTP/', $result); + } + + /** + * testCustomTypePath + */ + public function testCustomTypePath() + { + $installer = new Installer($this->io, $this->composer); + $package = new Package('slbmeh/my_plugin', '1.0.0', '1.0.0'); + $package->setType('wordpress-plugin'); + $consumerPackage = new RootPackage('foo/bar', '1.0.0', '1.0.0'); + $this->composer->setPackage($consumerPackage); + $consumerPackage->setExtra(array( + 'installer-paths' => array( + 'my/custom/path/{$name}/' => array( + 'type:wordpress-plugin' + ), + ), + )); + $result = $installer->getInstallPath($package); + $this->assertEquals('my/custom/path/my_plugin/', $result); + } + + /** + * testNoVendorName + */ + public function testNoVendorName() + { + $installer = new Installer($this->io, $this->composer); + $package = new Package('sfPhpunitPlugin', '1.0.0', '1.0.0'); + + $package->setType('symfony1-plugin'); + $result = $installer->getInstallPath($package); + $this->assertEquals('plugins/sfPhpunitPlugin/', $result); + } + + /** + * testTypo3Inflection + */ + public function testTypo3Inflection() + { + $installer = new Installer($this->io, $this->composer); + $package = new Package('typo3/fluid', '1.0.0', '1.0.0'); + + $package->setAutoload(array( + 'psr-0' => array( + 'TYPO3\\Fluid' => 'Classes', + ), + )); + + $package->setType('typo3-flow-package'); + $result = $installer->getInstallPath($package); + $this->assertEquals('Packages/Application/TYPO3.Fluid/', $result); + } + + public function testUninstallAndDeletePackageFromLocalRepo() + { + $package = new Package('foo', '1.0.0', '1.0.0'); + + $installer = $this->getMock('Composer\Installers\Installer', array('getInstallPath'), array($this->io, $this->composer)); + $installer->expects($this->once())->method('getInstallPath')->with($package)->will($this->returnValue(sys_get_temp_dir().'/foo')); + + $repo = $this->getMock('Composer\Repository\InstalledRepositoryInterface'); + $repo->expects($this->once())->method('hasPackage')->with($package)->will($this->returnValue(true)); + $repo->expects($this->once())->method('removePackage')->with($package); + + $installer->uninstall($repo, $package); + } +} diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php new file mode 100644 index 000000000..3675e188b --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/MediaWikiInstallerTest.php @@ -0,0 +1,66 @@ +installer = new MediaWikiInstaller( + new Package('NyanCat', '4.2', '4.2'), + new Composer() + ); + } + + /** + * @dataProvider packageNameInflectionProvider + */ + public function testInflectPackageVars($type, $name, $expected) + { + $this->assertEquals( + $this->installer->inflectPackageVars(array('name' => $name, 'type'=>$type)), + array('name' => $expected, 'type'=>$type) + ); + } + + public function packageNameInflectionProvider() + { + return array( + array( + 'mediawiki-extension', + 'sub-page-list', + 'SubPageList', + ), + array( + 'mediawiki-extension', + 'sub-page-list-extension', + 'SubPageList', + ), + array( + 'mediawiki-extension', + 'semantic-mediawiki', + 'SemanticMediawiki', + ), + // tests that exactly one '-skin' is cut off, and that skins do not get ucwords treatment like extensions + array( + 'mediawiki-skin', + 'some-skin-skin', + 'some-skin', + ), + // tests that names without '-skin' suffix stay valid + array( + 'mediawiki-skin', + 'someotherskin', + 'someotherskin', + ), + ); + } +} diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php new file mode 100644 index 000000000..fd427cdc3 --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/OctoberInstallerTest.php @@ -0,0 +1,66 @@ +installer = new OctoberInstaller( + new Package('NyanCat', '4.2', '4.2'), + new Composer() + ); + } + + /** + * @dataProvider packageNameInflectionProvider + */ + public function testInflectPackageVars($type, $name, $expected) + { + $this->assertEquals( + $this->installer->inflectPackageVars(array('name' => $name, 'type' => $type)), + array('name' => $expected, 'type' => $type) + ); + } + + public function packageNameInflectionProvider() + { + return array( + array( + 'october-plugin', + 'subpagelist', + 'subpagelist', + ), + array( + 'october-plugin', + 'subpagelist-plugin', + 'subpagelist', + ), + array( + 'october-plugin', + 'semanticoctober', + 'semanticoctober', + ), + // tests that exactly one '-theme' is cut off + array( + 'october-theme', + 'some-theme-theme', + 'some-theme', + ), + // tests that names without '-theme' suffix stay valid + array( + 'october-theme', + 'someothertheme', + 'someothertheme', + ), + ); + } +} \ No newline at end of file diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php new file mode 100644 index 000000000..ea79374bf --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PimcoreInstallerTest.php @@ -0,0 +1,44 @@ +package = new Package('CamelCased', '1.0', '1.0'); + $this->io = $this->getMock('Composer\IO\PackageInterface'); + $this->composer = new Composer(); + } + + /** + * testInflectPackageVars + * + * @return void + */ + public function testInflectPackageVars() + { + $installer = new PimcoreInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'CamelCased')); + $this->assertEquals($result, array('name' => 'CamelCased')); + + $installer = new PimcoreInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'with-dash')); + $this->assertEquals($result, array('name' => 'WithDash')); + + $installer = new PimcoreInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'with_underscore')); + $this->assertEquals($result, array('name' => 'WithUnderscore')); + } +} diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php new file mode 100644 index 000000000..8d9ff3f82 --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/PiwikInstallerTest.php @@ -0,0 +1,63 @@ +package = new Package('VisitSummary', '1.0', '1.0'); + $this->io = $this->getMock('Composer\IO\PackageInterface'); + $this->composer = new Composer(); + } + + /** + * testInflectPackageVars + * + * @return void + */ + public function testInflectPackageVars() + { + $installer = new PiwikInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'VisitSummary')); + $this->assertEquals($result, array('name' => 'VisitSummary')); + + $installer = new PiwikInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'visit-summary')); + $this->assertEquals($result, array('name' => 'VisitSummary')); + + $installer = new PiwikInstaller($this->package, $this->composer); + $result = $installer->inflectPackageVars(array('name' => 'visit_summary')); + $this->assertEquals($result, array('name' => 'VisitSummary')); + } + +} diff --git a/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php new file mode 100644 index 000000000..6418a03b8 --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/Composer/Installers/Test/TestCase.php @@ -0,0 +1,64 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Installers\Test; + +use Composer\Package\Version\VersionParser; +use Composer\Package\Package; +use Composer\Package\AliasPackage; +use Composer\Package\LinkConstraint\VersionConstraint; +use Composer\Util\Filesystem; + +abstract class TestCase extends \PHPUnit_Framework_TestCase +{ + private static $parser; + + protected static function getVersionParser() + { + if (!self::$parser) { + self::$parser = new VersionParser(); + } + + return self::$parser; + } + + protected function getVersionConstraint($operator, $version) + { + return new VersionConstraint( + $operator, + self::getVersionParser()->normalize($version) + ); + } + + protected function getPackage($name, $version) + { + $normVersion = self::getVersionParser()->normalize($version); + + return new Package($name, $normVersion, $version); + } + + protected function getAliasPackage($package, $version) + { + $normVersion = self::getVersionParser()->normalize($version); + + return new AliasPackage($package, $normVersion, $version); + } + + protected function ensureDirectoryExistsAndClear($directory) + { + $fs = new Filesystem(); + if (is_dir($directory)) { + $fs->removeDirectory($directory); + } + mkdir($directory, 0777, true); + } +} diff --git a/web/api/app/vendor/composer/installers/tests/bootstrap.php b/web/api/app/vendor/composer/installers/tests/bootstrap.php new file mode 100644 index 000000000..30c8fdc67 --- /dev/null +++ b/web/api/app/vendor/composer/installers/tests/bootstrap.php @@ -0,0 +1,4 @@ +add('Composer\Installers\Test', __DIR__); diff --git a/web/api/app/webroot/.htaccess b/web/api/app/webroot/.htaccess index 1f19e4c06..f08afa8b2 100644 --- a/web/api/app/webroot/.htaccess +++ b/web/api/app/webroot/.htaccess @@ -3,4 +3,5 @@ RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [L] + RewriteBase /zm/api diff --git a/web/api/lib/Cake/Cache/Cache.php b/web/api/lib/Cake/Cache/Cache.php index a75f0acfd..a7a01d0a1 100644 --- a/web/api/lib/Cake/Cache/Cache.php +++ b/web/api/lib/Cake/Cache/Cache.php @@ -26,12 +26,12 @@ App::uses('CacheEngine', 'Cache'); * You can configure Cache engines in your application's `bootstrap.php` file. A sample configuration would * be * - * {{{ + * ``` * Cache::config('shared', array( * 'engine' => 'Apc', * 'prefix' => 'my_app_' * )); - * }}} + * ``` * * This would configure an APC cache engine to the 'shared' alias. You could then read and write * to that cache alias by using it for the `$config` parameter in the various Cache methods. In @@ -113,11 +113,11 @@ class Cache { * - `user` Used by Xcache. Username for XCache * - `password` Used by Xcache/Redis. Password for XCache/Redis * - * @see app/Config/core.php for configuration settings * @param string $name Name of the configuration * @param array $settings Optional associative array of settings passed to the engine * @return array array(engine, settings) on success, false on failure * @throws CacheException + * @see app/Config/core.php for configuration settings */ public static function config($name = null, $settings = array()) { if (is_array($name)) { @@ -125,33 +125,33 @@ class Cache { } $current = array(); - if (isset(self::$_config[$name])) { - $current = self::$_config[$name]; + if (isset(static::$_config[$name])) { + $current = static::$_config[$name]; } if (!empty($settings)) { - self::$_config[$name] = $settings + $current; + static::$_config[$name] = $settings + $current; } - if (empty(self::$_config[$name]['engine'])) { + if (empty(static::$_config[$name]['engine'])) { return false; } - if (!empty(self::$_config[$name]['groups'])) { - foreach (self::$_config[$name]['groups'] as $group) { - self::$_groups[$group][] = $name; - sort(self::$_groups[$group]); - self::$_groups[$group] = array_unique(self::$_groups[$group]); + if (!empty(static::$_config[$name]['groups'])) { + foreach (static::$_config[$name]['groups'] as $group) { + static::$_groups[$group][] = $name; + sort(static::$_groups[$group]); + static::$_groups[$group] = array_unique(static::$_groups[$group]); } } - $engine = self::$_config[$name]['engine']; + $engine = static::$_config[$name]['engine']; - if (!isset(self::$_engines[$name])) { - self::_buildEngine($name); - $settings = self::$_config[$name] = self::settings($name); - } elseif ($settings = self::set(self::$_config[$name], null, $name)) { - self::$_config[$name] = $settings; + if (!isset(static::$_engines[$name])) { + static::_buildEngine($name); + $settings = static::$_config[$name] = static::settings($name); + } elseif ($settings = static::set(static::$_config[$name], null, $name)) { + static::$_config[$name] = $settings; } return compact('engine', 'settings'); } @@ -160,11 +160,11 @@ class Cache { * Finds and builds the instance of the required engine class. * * @param string $name Name of the config array that needs an engine instance built - * @return boolean + * @return bool * @throws CacheException */ protected static function _buildEngine($name) { - $config = self::$_config[$name]; + $config = static::$_config[$name]; list($plugin, $class) = pluginSplit($config['engine'], true); $cacheClass = $class . 'Engine'; @@ -176,12 +176,17 @@ class Cache { if (!is_subclass_of($cacheClass, 'CacheEngine')) { throw new CacheException(__d('cake_dev', 'Cache engines must use %s as a base class.', 'CacheEngine')); } - self::$_engines[$name] = new $cacheClass(); - if (!self::$_engines[$name]->init($config)) { - throw new CacheException(__d('cake_dev', 'Cache engine %s is not properly configured.', $name)); + static::$_engines[$name] = new $cacheClass(); + if (!static::$_engines[$name]->init($config)) { + $msg = __d( + 'cake_dev', + 'Cache engine "%s" is not properly configured. Ensure required extensions are installed, and credentials/permissions are correct', + $name + ); + throw new CacheException($msg); } - if (self::$_engines[$name]->settings['probability'] && time() % self::$_engines[$name]->settings['probability'] === 0) { - self::$_engines[$name]->gc(); + if (static::$_engines[$name]->settings['probability'] && time() % static::$_engines[$name]->settings['probability'] === 0) { + static::$_engines[$name]->gc(); } return true; } @@ -192,22 +197,22 @@ class Cache { * @return array Array of configured Cache config names. */ public static function configured() { - return array_keys(self::$_config); + return array_keys(static::$_config); } /** * Drops a cache engine. Deletes the cache configuration information - * If the deleted configuration is the last configuration using an certain engine, + * If the deleted configuration is the last configuration using a certain engine, * the Engine instance is also unset. * * @param string $name A currently configured cache config you wish to remove. - * @return boolean success of the removal, returns false when the config does not exist. + * @return bool success of the removal, returns false when the config does not exist. */ public static function drop($name) { - if (!isset(self::$_config[$name])) { + if (!isset(static::$_config[$name])) { return false; } - unset(self::$_config[$name], self::$_engines[$name]); + unset(static::$_config[$name], static::$_engines[$name]); return true; } @@ -238,29 +243,29 @@ class Cache { if (is_array($settings) && $value !== null) { $config = $value; } - if (!isset(self::$_config[$config]) || !isset(self::$_engines[$config])) { + if (!isset(static::$_config[$config]) || !isset(static::$_engines[$config])) { return false; } if (!empty($settings)) { - self::$_reset = true; + static::$_reset = true; } - if (self::$_reset === true) { + if (static::$_reset === true) { if (empty($settings)) { - self::$_reset = false; - $settings = self::$_config[$config]; + static::$_reset = false; + $settings = static::$_config[$config]; } else { if (is_string($settings) && $value !== null) { $settings = array($settings => $value); } - $settings += self::$_config[$config]; + $settings += static::$_config[$config]; if (isset($settings['duration']) && !is_numeric($settings['duration'])) { $settings['duration'] = strtotime($settings['duration']) - time(); } } - self::$_engines[$config]->settings = $settings; + static::$_engines[$config]->settings = $settings; } - return self::settings($config); + return static::settings($config); } /** @@ -269,11 +274,11 @@ class Cache { * Permanently remove all expired and deleted data * * @param string $config [optional] The config name you wish to have garbage collected. Defaults to 'default' - * @param integer $expires [optional] An expires timestamp. Defaults to NULL - * @return void + * @param int $expires [optional] An expires timestamp. Defaults to NULL + * @return bool */ public static function gc($config = 'default', $expires = null) { - self::$_engines[$config]->gc($expires); + return static::$_engines[$config]->gc($expires); } /** @@ -292,32 +297,32 @@ class Cache { * @param string $key Identifier for the data * @param mixed $value Data to be cached - anything except a resource * @param string $config Optional string configuration name to write to. Defaults to 'default' - * @return boolean True if the data was successfully cached, false on failure + * @return bool True if the data was successfully cached, false on failure */ public static function write($key, $value, $config = 'default') { - $settings = self::settings($config); + $settings = static::settings($config); if (empty($settings)) { return false; } - if (!self::isInitialized($config)) { + if (!static::isInitialized($config)) { return false; } - $key = self::$_engines[$config]->key($key); + $key = static::$_engines[$config]->key($key); if (!$key || is_resource($value)) { return false; } - $success = self::$_engines[$config]->write($settings['prefix'] . $key, $value, $settings['duration']); - self::set(null, $config); + $success = static::$_engines[$config]->write($settings['prefix'] . $key, $value, $settings['duration']); + static::set(null, $config); if ($success === false && $value !== '') { trigger_error( __d('cake_dev', "%s cache was unable to write '%s' to %s cache", $config, $key, - self::$_engines[$config]->settings['engine'] + static::$_engines[$config]->settings['engine'] ), E_USER_WARNING ); @@ -343,46 +348,46 @@ class Cache { * @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it */ public static function read($key, $config = 'default') { - $settings = self::settings($config); + $settings = static::settings($config); if (empty($settings)) { return false; } - if (!self::isInitialized($config)) { + if (!static::isInitialized($config)) { return false; } - $key = self::$_engines[$config]->key($key); + $key = static::$_engines[$config]->key($key); if (!$key) { return false; } - return self::$_engines[$config]->read($settings['prefix'] . $key); + return static::$_engines[$config]->read($settings['prefix'] . $key); } /** * Increment a number under the key and return incremented value. * * @param string $key Identifier for the data - * @param integer $offset How much to add + * @param int $offset How much to add * @param string $config Optional string configuration name. Defaults to 'default' * @return mixed new value, or false if the data doesn't exist, is not integer, * or if there was an error fetching it. */ public static function increment($key, $offset = 1, $config = 'default') { - $settings = self::settings($config); + $settings = static::settings($config); if (empty($settings)) { return false; } - if (!self::isInitialized($config)) { + if (!static::isInitialized($config)) { return false; } - $key = self::$_engines[$config]->key($key); + $key = static::$_engines[$config]->key($key); if (!$key || !is_int($offset) || $offset < 0) { return false; } - $success = self::$_engines[$config]->increment($settings['prefix'] . $key, $offset); - self::set(null, $config); + $success = static::$_engines[$config]->increment($settings['prefix'] . $key, $offset); + static::set(null, $config); return $success; } @@ -390,27 +395,27 @@ class Cache { * Decrement a number under the key and return decremented value. * * @param string $key Identifier for the data - * @param integer $offset How much to subtract + * @param int $offset How much to subtract * @param string $config Optional string configuration name. Defaults to 'default' * @return mixed new value, or false if the data doesn't exist, is not integer, * or if there was an error fetching it */ public static function decrement($key, $offset = 1, $config = 'default') { - $settings = self::settings($config); + $settings = static::settings($config); if (empty($settings)) { return false; } - if (!self::isInitialized($config)) { + if (!static::isInitialized($config)) { return false; } - $key = self::$_engines[$config]->key($key); + $key = static::$_engines[$config]->key($key); if (!$key || !is_int($offset) || $offset < 0) { return false; } - $success = self::$_engines[$config]->decrement($settings['prefix'] . $key, $offset); - self::set(null, $config); + $success = static::$_engines[$config]->decrement($settings['prefix'] . $key, $offset); + static::set(null, $config); return $success; } @@ -429,40 +434,40 @@ class Cache { * * @param string $key Identifier for the data * @param string $config name of the configuration to use. Defaults to 'default' - * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public static function delete($key, $config = 'default') { - $settings = self::settings($config); + $settings = static::settings($config); if (empty($settings)) { return false; } - if (!self::isInitialized($config)) { + if (!static::isInitialized($config)) { return false; } - $key = self::$_engines[$config]->key($key); + $key = static::$_engines[$config]->key($key); if (!$key) { return false; } - $success = self::$_engines[$config]->delete($settings['prefix'] . $key); - self::set(null, $config); + $success = static::$_engines[$config]->delete($settings['prefix'] . $key); + static::set(null, $config); return $success; } /** * Delete all keys from the cache. * - * @param boolean $check if true will check expiration, otherwise delete all + * @param bool $check if true will check expiration, otherwise delete all * @param string $config name of the configuration to use. Defaults to 'default' - * @return boolean True if the cache was successfully cleared, false otherwise + * @return bool True if the cache was successfully cleared, false otherwise */ public static function clear($check = false, $config = 'default') { - if (!self::isInitialized($config)) { + if (!static::isInitialized($config)) { return false; } - $success = self::$_engines[$config]->clear($check); - self::set(null, $config); + $success = static::$_engines[$config]->clear($check); + static::set(null, $config); return $success; } @@ -471,14 +476,14 @@ class Cache { * * @param string $group name of the group to be cleared * @param string $config name of the configuration to use. Defaults to 'default' - * @return boolean True if the cache group was successfully cleared, false otherwise + * @return bool True if the cache group was successfully cleared, false otherwise */ public static function clearGroup($group, $config = 'default') { - if (!self::isInitialized($config)) { + if (!static::isInitialized($config)) { return false; } - $success = self::$_engines[$config]->clearGroup($group); - self::set(null, $config); + $success = static::$_engines[$config]->clearGroup($group); + static::set(null, $config); return $success; } @@ -486,13 +491,13 @@ class Cache { * Check if Cache has initialized a working config for the given name. * * @param string $config name of the configuration to use. Defaults to 'default' - * @return boolean Whether or not the config name has been initialized. + * @return bool Whether or not the config name has been initialized. */ public static function isInitialized($config = 'default') { if (Configure::read('Cache.disable')) { return false; } - return isset(self::$_engines[$config]); + return isset(static::$_engines[$config]); } /** @@ -503,8 +508,8 @@ class Cache { * @see Cache::config() */ public static function settings($name = 'default') { - if (!empty(self::$_engines[$name])) { - return self::$_engines[$name]->settings(); + if (!empty(static::$_engines[$name])) { + return static::$_engines[$name]->settings(); } return array(); } @@ -512,7 +517,7 @@ class Cache { /** * Retrieve group names to config mapping. * - * {{{ + * ``` * Cache::config('daily', array( * 'duration' => '1 day', 'groups' => array('posts') * )); @@ -520,7 +525,7 @@ class Cache { * 'duration' => '1 week', 'groups' => array('posts', 'archive') * )); * $configs = Cache::groupConfigs('posts'); - * }}} + * ``` * * $config will equal to `array('posts' => array('daily', 'weekly'))` * @@ -530,10 +535,10 @@ class Cache { */ public static function groupConfigs($group = null) { if ($group === null) { - return self::$_groups; + return static::$_groups; } - if (isset(self::$_groups[$group])) { - return array($group => self::$_groups[$group]); + if (isset(static::$_groups[$group])) { + return array($group => static::$_groups[$group]); } throw new CacheException(__d('cake_dev', 'Invalid cache group %s', $group)); } @@ -549,27 +554,66 @@ class Cache { * * Using a Closure to provide data, assume $this is a Model: * - * {{{ + * ``` * $model = $this; * $results = Cache::remember('all_articles', function() use ($model) { * return $model->find('all'); * }); - * }}} + * ``` * * @param string $key The cache key to read/store data at. * @param callable $callable The callable that provides data in the case when * the cache key is empty. Can be any callable type supported by your PHP. * @param string $config The cache configuration to use for this operation. * Defaults to default. + * @return mixed The results of the callable or unserialized results. */ public static function remember($key, $callable, $config = 'default') { - $existing = self::read($key, $config); + $existing = static::read($key, $config); if ($existing !== false) { return $existing; } $results = call_user_func($callable); - self::write($key, $results, $config); + static::write($key, $results, $config); return $results; } +/** + * Write data for key into a cache engine if it doesn't exist already. + * + * ### Usage: + * + * Writing to the active cache config: + * + * `Cache::add('cached_data', $data);` + * + * Writing to a specific cache config: + * + * `Cache::add('cached_data', $data, 'long_term');` + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached - anything except a resource. + * @param string $config Optional string configuration name to write to. Defaults to 'default'. + * @return bool True if the data was successfully cached, false on failure. + * Or if the key existed already. + */ + public static function add($key, $value, $config = 'default') { + $settings = self::settings($config); + + if (empty($settings)) { + return false; + } + if (!self::isInitialized($config)) { + return false; + } + $key = self::$_engines[$config]->key($key); + + if (!$key || is_resource($value)) { + return false; + } + + $success = self::$_engines[$config]->add($settings['prefix'] . $key, $value, $settings['duration']); + self::set(null, $config); + return $success; + } } diff --git a/web/api/lib/Cake/Cache/CacheEngine.php b/web/api/lib/Cake/Cache/CacheEngine.php index 5d19bdeaf..6bad9f7ad 100644 --- a/web/api/lib/Cake/Cache/CacheEngine.php +++ b/web/api/lib/Cake/Cache/CacheEngine.php @@ -42,7 +42,7 @@ abstract class CacheEngine { * Called automatically by the cache frontend * * @param array $settings Associative array of parameters for the engine - * @return boolean True if the engine has been successfully initialized, false if not + * @return bool True if the engine has been successfully initialized, false if not */ public function init($settings = array()) { $settings += $this->settings + array( @@ -67,7 +67,7 @@ abstract class CacheEngine { * * Permanently remove all expired and deleted data * - * @param integer $expires [optional] An expires timestamp, invalidating all data before. + * @param int $expires [optional] An expires timestamp, invalidating all data before. * @return void */ public function gc($expires = null) { @@ -78,11 +78,22 @@ abstract class CacheEngine { * * @param string $key Identifier for the data * @param mixed $value Data to be cached - * @param integer $duration How long to cache for. - * @return boolean True if the data was successfully cached, false on failure + * @param int $duration How long to cache for. + * @return bool True if the data was successfully cached, false on failure */ abstract public function write($key, $value, $duration); +/** + * Write value for a key into cache if it doesn't already exist + * + * @param string $key Identifier for the data + * @param mixed $value Data to be cached + * @param int $duration How long to cache for. + * @return bool True if the data was successfully cached, false on failure + */ + public function add($key, $value, $duration) { + } + /** * Read a key from the cache * @@ -95,7 +106,7 @@ abstract class CacheEngine { * Increment a number under the key and return incremented value * * @param string $key Identifier for the data - * @param integer $offset How much to add + * @param int $offset How much to add * @return New incremented value, false otherwise */ abstract public function increment($key, $offset = 1); @@ -104,7 +115,7 @@ abstract class CacheEngine { * Decrement a number under the key and return decremented value * * @param string $key Identifier for the data - * @param integer $offset How much to subtract + * @param int $offset How much to subtract * @return New incremented value, false otherwise */ abstract public function decrement($key, $offset = 1); @@ -113,15 +124,15 @@ abstract class CacheEngine { * Delete a key from the cache * * @param string $key Identifier for the data - * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ abstract public function delete($key); /** * Delete all keys from the cache * - * @param boolean $check if true will check expiration, otherwise delete all - * @return boolean True if the cache was successfully cleared, false otherwise + * @param bool $check if true will check expiration, otherwise delete all + * @return bool True if the cache was successfully cleared, false otherwise */ abstract public function clear($check); @@ -130,8 +141,8 @@ abstract class CacheEngine { * to decide whether actually delete the keys or just simulate it to achieve * the same result. * - * @param string $groups name of the group to be cleared - * @return boolean + * @param string $group name of the group to be cleared + * @return bool */ public function clearGroup($group) { return false; @@ -176,5 +187,4 @@ abstract class CacheEngine { $key = preg_replace('/[\s]+/', '_', strtolower(trim(str_replace(array(DS, '/', '.'), '_', strval($key))))); return $prefix . $key; } - } diff --git a/web/api/lib/Cake/Cache/Engine/ApcEngine.php b/web/api/lib/Cake/Cache/Engine/ApcEngine.php index 8fd0ff1ad..da31651e3 100644 --- a/web/api/lib/Cake/Cache/Engine/ApcEngine.php +++ b/web/api/lib/Cake/Cache/Engine/ApcEngine.php @@ -38,7 +38,7 @@ class ApcEngine extends CacheEngine { * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); * * @param array $settings array of setting for the engine - * @return boolean True if the engine has been successfully initialized, false if not + * @return bool True if the engine has been successfully initialized, false if not * @see CacheEngine::__defaults */ public function init($settings = array()) { @@ -55,8 +55,8 @@ class ApcEngine extends CacheEngine { * * @param string $key Identifier for the data * @param mixed $value Data to be cached - * @param integer $duration How long to cache the data, in seconds - * @return boolean True if the data was successfully cached, false on failure + * @param int $duration How long to cache the data, in seconds + * @return bool True if the data was successfully cached, false on failure */ public function write($key, $value, $duration) { $expires = 0; @@ -75,7 +75,7 @@ class ApcEngine extends CacheEngine { */ public function read($key) { $time = time(); - $cachetime = intval(apc_fetch($key . '_expires')); + $cachetime = (int)apc_fetch($key . '_expires'); if ($cachetime !== 0 && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) { return false; } @@ -86,7 +86,7 @@ class ApcEngine extends CacheEngine { * Increments the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to increment + * @param int $offset How much to increment * @return New incremented value, false otherwise */ public function increment($key, $offset = 1) { @@ -97,7 +97,7 @@ class ApcEngine extends CacheEngine { * Decrements the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to subtract + * @param int $offset How much to subtract * @return New decremented value, false otherwise */ public function decrement($key, $offset = 1) { @@ -108,7 +108,7 @@ class ApcEngine extends CacheEngine { * Delete a key from the cache * * @param string $key Identifier for the data - * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public function delete($key) { return apc_delete($key); @@ -117,18 +117,25 @@ class ApcEngine extends CacheEngine { /** * Delete all keys from the cache. This will clear every cache config using APC. * - * @param boolean $check If true, nothing will be cleared, as entries are removed + * @param bool $check If true, nothing will be cleared, as entries are removed * from APC as they expired. This flag is really only used by FileEngine. - * @return boolean True Returns true. + * @return bool True Returns true. */ public function clear($check) { if ($check) { return true; } - $info = apc_cache_info('user'); - $cacheKeys = $info['cache_list']; - unset($info); - foreach ($cacheKeys as $key) { + if (class_exists('APCIterator', false)) { + $iterator = new APCIterator( + 'user', + '/^' . preg_quote($this->settings['prefix'], '/') . '/', + APC_ITER_NONE + ); + apc_delete($iterator); + return true; + } + $cache = apc_cache_info('user'); + foreach ($cache['cache_list'] as $key) { if (strpos($key['info'], $this->settings['prefix']) === 0) { apc_delete($key['info']); } @@ -173,11 +180,30 @@ class ApcEngine extends CacheEngine { * Increments the group value to simulate deletion of all keys under a group * old values will remain in storage until they expire. * - * @return boolean success + * @param string $group The group to clear. + * @return bool success */ public function clearGroup($group) { apc_inc($this->settings['prefix'] . $group, 1, $success); return $success; } +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + * @link http://php.net/manual/en/function.apc-add.php + */ + public function add($key, $value, $duration) { + $expires = 0; + if ($duration) { + $expires = time() + $duration; + } + apc_add($key . '_expires', $expires, $duration); + return apc_add($key, $value, $duration); + } } diff --git a/web/api/lib/Cake/Cache/Engine/FileEngine.php b/web/api/lib/Cake/Cache/Engine/FileEngine.php index b93d6c1a8..d650e60ee 100644 --- a/web/api/lib/Cake/Cache/Engine/FileEngine.php +++ b/web/api/lib/Cake/Cache/Engine/FileEngine.php @@ -53,7 +53,7 @@ class FileEngine extends CacheEngine { /** * True unless FileEngine::__active(); fails * - * @var boolean + * @var bool */ protected $_init = true; @@ -64,7 +64,7 @@ class FileEngine extends CacheEngine { * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); * * @param array $settings array of setting for the engine - * @return boolean True if the engine has been successfully initialized, false if not + * @return bool True if the engine has been successfully initialized, false if not */ public function init($settings = array()) { $settings += array( @@ -93,8 +93,8 @@ class FileEngine extends CacheEngine { /** * Garbage collection. Permanently remove all expired and deleted data * - * @param integer $expires [optional] An expires timestamp, invalidating all data before. - * @return boolean True if garbage collection was successful, false on failure + * @param int $expires [optional] An expires timestamp, invalidating all data before. + * @return bool True if garbage collection was successful, false on failure */ public function gc($expires = null) { return $this->clear(true); @@ -105,11 +105,11 @@ class FileEngine extends CacheEngine { * * @param string $key Identifier for the data * @param mixed $data Data to be cached - * @param integer $duration How long to cache the data, in seconds - * @return boolean True if the data was successfully cached, false on failure + * @param int $duration How long to cache the data, in seconds + * @return bool True if the data was successfully cached, false on failure */ public function write($key, $data, $duration) { - if ($data === '' || !$this->_init) { + if (!$this->_init) { return false; } @@ -165,7 +165,7 @@ class FileEngine extends CacheEngine { $this->_File->rewind(); $time = time(); - $cachetime = intval($this->_File->current()); + $cachetime = (int)$this->_File->current(); if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) { if ($this->settings['lock']) { @@ -200,7 +200,7 @@ class FileEngine extends CacheEngine { * Delete a key from the cache * * @param string $key Identifier for the data - * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public function delete($key) { if ($this->_setKey($key) === false || !$this->_init) { @@ -217,8 +217,8 @@ class FileEngine extends CacheEngine { /** * Delete all values from the cache * - * @param boolean $check Optional - only delete expired cache items - * @return boolean True if the cache was successfully cleared, false otherwise + * @param bool $check Optional - only delete expired cache items + * @return bool True if the cache was successfully cleared, false otherwise */ public function clear($check) { if (!$this->_init) { @@ -255,8 +255,8 @@ class FileEngine extends CacheEngine { * Used to clear a directory of matching files. * * @param string $path The path to search. - * @param integer $now The current timestamp - * @param integer $threshold Any file not modified after this value will be deleted. + * @param int $now The current timestamp + * @param int $threshold Any file not modified after this value will be deleted. * @return void */ protected function _clearDirectory($path, $now, $threshold) { @@ -271,11 +271,12 @@ class FileEngine extends CacheEngine { if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) { continue; } - $filePath = $path . $entry; - if (!file_exists($filePath) || is_dir($filePath)) { + + try { + $file = new SplFileObject($path . $entry, 'r'); + } catch (Exception $e) { continue; } - $file = new SplFileObject($path . $entry, 'r'); if ($threshold) { $mtime = $file->getMTime(); @@ -303,8 +304,8 @@ class FileEngine extends CacheEngine { /** * Not implemented * - * @param string $key - * @param integer $offset + * @param string $key The key to decrement + * @param int $offset The number to offset * @return void * @throws CacheException */ @@ -315,8 +316,8 @@ class FileEngine extends CacheEngine { /** * Not implemented * - * @param string $key - * @param integer $offset + * @param string $key The key to decrement + * @param int $offset The number to offset * @return void * @throws CacheException */ @@ -329,8 +330,8 @@ class FileEngine extends CacheEngine { * for the cache file the key is referring to. * * @param string $key The key - * @param boolean $createKey Whether the key should be created if it doesn't exists, or not - * @return boolean true if the cache key could be set, false otherwise + * @param bool $createKey Whether the key should be created if it doesn't exists, or not + * @return bool true if the cache key could be set, false otherwise */ protected function _setKey($key, $createKey = false) { $groups = null; @@ -369,7 +370,7 @@ class FileEngine extends CacheEngine { /** * Determine is cache directory is writable * - * @return boolean + * @return bool */ protected function _active() { $dir = new SplFileInfo($this->settings['path']); @@ -405,7 +406,8 @@ class FileEngine extends CacheEngine { /** * Recursively deletes all files under any directory named as $group * - * @return boolean success + * @param string $group The group to clear. + * @return bool success */ public function clearGroup($group) { $this->_File = null; @@ -427,4 +429,21 @@ class FileEngine extends CacheEngine { } return true; } + +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + */ + public function add($key, $value, $duration) { + $cachedValue = $this->read($key); + if ($cachedValue === false) { + return $this->write($key, $value, $duration); + } + return false; + } } diff --git a/web/api/lib/Cake/Cache/Engine/MemcacheEngine.php b/web/api/lib/Cake/Cache/Engine/MemcacheEngine.php index facaedf0e..eba2ec451 100644 --- a/web/api/lib/Cake/Cache/Engine/MemcacheEngine.php +++ b/web/api/lib/Cake/Cache/Engine/MemcacheEngine.php @@ -22,7 +22,7 @@ * more information. * * @package Cake.Cache.Engine - * @deprecated You should use the Memcached adapter instead. + * @deprecated 3.0.0 You should use the Memcached adapter instead. */ class MemcacheEngine extends CacheEngine { @@ -59,7 +59,7 @@ class MemcacheEngine extends CacheEngine { * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); * * @param array $settings array of setting for the engine - * @return boolean True if the engine has been successfully initialized, false if not + * @return bool True if the engine has been successfully initialized, false if not */ public function init($settings = array()) { if (!class_exists('Memcache')) { @@ -104,7 +104,7 @@ class MemcacheEngine extends CacheEngine { * @return array Array containing host, port */ protected function _parseServerString($server) { - if ($server[0] === 'u') { + if (strpos($server, 'unix://') === 0) { return array($server, 0); } if (substr($server, 0, 1) === '[') { @@ -131,8 +131,8 @@ class MemcacheEngine extends CacheEngine { * * @param string $key Identifier for the data * @param mixed $value Data to be cached - * @param integer $duration How long to cache the data, in seconds - * @return boolean True if the data was successfully cached, false on failure + * @param int $duration How long to cache the data, in seconds + * @return bool True if the data was successfully cached, false on failure * @see http://php.net/manual/en/memcache.set.php */ public function write($key, $value, $duration) { @@ -156,7 +156,7 @@ class MemcacheEngine extends CacheEngine { * Increments the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to increment + * @param int $offset How much to increment * @return New incremented value, false otherwise * @throws CacheException when you try to increment with compress = true */ @@ -173,7 +173,7 @@ class MemcacheEngine extends CacheEngine { * Decrements the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to subtract + * @param int $offset How much to subtract * @return New decremented value, false otherwise * @throws CacheException when you try to decrement with compress = true */ @@ -190,7 +190,7 @@ class MemcacheEngine extends CacheEngine { * Delete a key from the cache * * @param string $key Identifier for the data - * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public function delete($key) { return $this->_Memcache->delete($key); @@ -199,8 +199,9 @@ class MemcacheEngine extends CacheEngine { /** * Delete all keys from the cache * - * @param boolean $check - * @return boolean True if the cache was successfully cleared, false otherwise + * @param bool $check If true no deletes will occur and instead CakePHP will rely + * on key TTL values. + * @return bool True if the cache was successfully cleared, false otherwise */ public function clear($check) { if ($check) { @@ -231,8 +232,8 @@ class MemcacheEngine extends CacheEngine { * Connects to a server in connection pool * * @param string $host host ip address or name - * @param integer $port Server port - * @return boolean True if memcache server was connected + * @param int $port Server port + * @return bool True if memcache server was connected */ public function connect($host, $port = 11211) { if ($this->_Memcache->getServerStatus($host, $port) === 0) { @@ -282,9 +283,30 @@ class MemcacheEngine extends CacheEngine { * Increments the group value to simulate deletion of all keys under a group * old values will remain in storage until they expire. * - * @return boolean success + * @param string $group The group to clear. + * @return bool success */ public function clearGroup($group) { return (bool)$this->_Memcache->increment($this->settings['prefix'] . $group); } + +/** + * Write data for key into cache if it doesn't exist already. When using memcached as your cache engine + * remember that the Memcached PECL extension does not support cache expiry times greater + * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + * @link http://php.net/manual/en/memcache.add.php + */ + public function add($key, $value, $duration) { + if ($duration > 30 * DAY) { + $duration = 0; + } + + return $this->_Memcache->add($key, $value, $this->settings['compress'], $duration); + } } diff --git a/web/api/lib/Cake/Cache/Engine/MemcachedEngine.php b/web/api/lib/Cake/Cache/Engine/MemcachedEngine.php old mode 100755 new mode 100644 index 9c50186fe..cc101a2bf --- a/web/api/lib/Cake/Cache/Engine/MemcachedEngine.php +++ b/web/api/lib/Cake/Cache/Engine/MemcachedEngine.php @@ -45,6 +45,8 @@ class MemcachedEngine extends CacheEngine { * - serialize = string, default => php. The serializer engine used to serialize data. * Available engines are php, igbinary and json. Beside php, the memcached extension * must be compiled with the appropriate serializer support. + * - options - Additional options for the memcached client. Should be an array of option => value. + * Use the Memcached::OPT_* constants as keys. * * @var array */ @@ -70,7 +72,7 @@ class MemcachedEngine extends CacheEngine { * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); * * @param array $settings array of setting for the engine - * @return boolean True if the engine has been successfully initialized, false if not + * @return bool True if the engine has been successfully initialized, false if not * @throws CacheException when you try use authentication without Memcached compiled with SASL support */ public function init($settings = array()) { @@ -92,7 +94,8 @@ class MemcachedEngine extends CacheEngine { 'persistent' => false, 'login' => null, 'password' => null, - 'serialize' => 'php' + 'serialize' => 'php', + 'options' => array() ); parent::init($settings); @@ -104,7 +107,11 @@ class MemcachedEngine extends CacheEngine { return true; } - $this->_Memcached = new Memcached($this->settings['persistent'] ? (string)$this->settings['persistent'] : null); + if (!$this->settings['persistent']) { + $this->_Memcached = new Memcached(); + } else { + $this->_Memcached = new Memcached((string)$this->settings['persistent']); + } $this->_setOptions(); if (count($this->_Memcached->getServerList())) { @@ -126,8 +133,14 @@ class MemcachedEngine extends CacheEngine { __d('cake_dev', 'Memcached extension is not build with SASL support') ); } + $this->_Memcached->setOption(Memcached::OPT_BINARY_PROTOCOL, true); $this->_Memcached->setSaslAuthData($this->settings['login'], $this->settings['password']); } + if (is_array($this->settings['options'])) { + foreach ($this->settings['options'] as $opt => $value) { + $this->_Memcached->setOption($opt, $value); + } + } return true; } @@ -136,6 +149,7 @@ class MemcachedEngine extends CacheEngine { * Settings the memcached instance * * @throws CacheException when the Memcached extension is not built with the desired serializer engine + * @return void */ protected function _setOptions() { $this->_Memcached->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true); @@ -171,8 +185,9 @@ class MemcachedEngine extends CacheEngine { * @return array Array containing host, port */ protected function _parseServerString($server) { - if ($server[0] === 'u') { - return array($server, 0); + $socketTransport = 'unix://'; + if (strpos($server, $socketTransport) === 0) { + return array(substr($server, strlen($socketTransport)), 0); } if (substr($server, 0, 1) === '[') { $position = strpos($server, ']:'); @@ -193,13 +208,13 @@ class MemcachedEngine extends CacheEngine { /** * Write data for key into cache. When using memcached as your cache engine - * remember that the Memcached pecl extension does not support cache expiry times greater + * remember that the Memcached PECL extension does not support cache expiry times greater * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring. * * @param string $key Identifier for the data * @param mixed $value Data to be cached - * @param integer $duration How long to cache the data, in seconds - * @return boolean True if the data was successfully cached, false on failure + * @param int $duration How long to cache the data, in seconds + * @return bool True if the data was successfully cached, false on failure * @see http://php.net/manual/en/memcache.set.php */ public function write($key, $value, $duration) { @@ -224,7 +239,7 @@ class MemcachedEngine extends CacheEngine { * Increments the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to increment + * @param int $offset How much to increment * @return New incremented value, false otherwise * @throws CacheException when you try to increment with compress = true */ @@ -236,7 +251,7 @@ class MemcachedEngine extends CacheEngine { * Decrements the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to subtract + * @param int $offset How much to subtract * @return New decremented value, false otherwise * @throws CacheException when you try to decrement with compress = true */ @@ -248,7 +263,7 @@ class MemcachedEngine extends CacheEngine { * Delete a key from the cache * * @param string $key Identifier for the data - * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public function delete($key) { return $this->_Memcached->delete($key); @@ -257,8 +272,10 @@ class MemcachedEngine extends CacheEngine { /** * Delete all keys from the cache * - * @param boolean $check - * @return boolean True if the cache was successfully cleared, false otherwise + * @param bool $check If true no deletes will occur and instead CakePHP will rely + * on key TTL values. + * @return bool True if the cache was successfully cleared, false otherwise. Will + * also return false if you are using a binary protocol. */ public function clear($check) { if ($check) { @@ -266,6 +283,9 @@ class MemcachedEngine extends CacheEngine { } $keys = $this->_Memcached->getAllKeys(); + if ($keys === false) { + return false; + } foreach ($keys as $key) { if (strpos($key, $this->settings['prefix']) === 0) { @@ -314,9 +334,30 @@ class MemcachedEngine extends CacheEngine { * Increments the group value to simulate deletion of all keys under a group * old values will remain in storage until they expire. * - * @return boolean success + * @param string $group The group to clear. + * @return bool success */ public function clearGroup($group) { return (bool)$this->_Memcached->increment($this->settings['prefix'] . $group); } + +/** + * Write data for key into cache if it doesn't exist already. When using memcached as your cache engine + * remember that the Memcached pecl extension does not support cache expiry times greater + * than 30 days in the future. Any duration greater than 30 days will be treated as never expiring. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + * @link http://php.net/manual/en/memcached.add.php + */ + public function add($key, $value, $duration) { + if ($duration > 30 * DAY) { + $duration = 0; + } + + return $this->_Memcached->add($key, $value, $duration); + } } diff --git a/web/api/lib/Cake/Cache/Engine/RedisEngine.php b/web/api/lib/Cake/Cache/Engine/RedisEngine.php index 09415c95d..8dd98fb99 100644 --- a/web/api/lib/Cake/Cache/Engine/RedisEngine.php +++ b/web/api/lib/Cake/Cache/Engine/RedisEngine.php @@ -38,6 +38,7 @@ class RedisEngine extends CacheEngine { * - port = integer port number to the Redis server (default: 6379) * - timeout = float timeout in seconds (default: 0) * - persistent = boolean Connects to the Redis server with a persistent connection (default: true) + * - unix_socket = path to the unix socket file (default: false) * * @var array */ @@ -50,7 +51,7 @@ class RedisEngine extends CacheEngine { * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); * * @param array $settings array of setting for the engine - * @return boolean True if the engine has been successfully initialized, false if not + * @return bool True if the engine has been successfully initialized, false if not */ public function init($settings = array()) { if (!class_exists('Redis')) { @@ -58,13 +59,14 @@ class RedisEngine extends CacheEngine { } parent::init(array_merge(array( 'engine' => 'Redis', - 'prefix' => null, + 'prefix' => Inflector::slug(APP_DIR) . '_', 'server' => '127.0.0.1', 'database' => 0, 'port' => 6379, 'password' => false, 'timeout' => 0, - 'persistent' => true + 'persistent' => true, + 'unix_socket' => false ), $settings) ); @@ -74,28 +76,29 @@ class RedisEngine extends CacheEngine { /** * Connects to a Redis server * - * @return boolean True if Redis server was connected + * @return bool True if Redis server was connected */ protected function _connect() { - $return = false; try { $this->_Redis = new Redis(); - if (empty($this->settings['persistent'])) { + if (!empty($this->settings['unix_socket'])) { + $return = $this->_Redis->connect($this->settings['unix_socket']); + } elseif (empty($this->settings['persistent'])) { $return = $this->_Redis->connect($this->settings['server'], $this->settings['port'], $this->settings['timeout']); } else { $persistentId = $this->settings['port'] . $this->settings['timeout'] . $this->settings['database']; $return = $this->_Redis->pconnect($this->settings['server'], $this->settings['port'], $this->settings['timeout'], $persistentId); } } catch (RedisException $e) { + $return = false; + } + if (!$return) { return false; } - if ($return && $this->settings['password']) { - $return = $this->_Redis->auth($this->settings['password']); + if ($this->settings['password'] && !$this->_Redis->auth($this->settings['password'])) { + return false; } - if ($return) { - $return = $this->_Redis->select($this->settings['database']); - } - return $return; + return $this->_Redis->select($this->settings['database']); } /** @@ -103,13 +106,18 @@ class RedisEngine extends CacheEngine { * * @param string $key Identifier for the data * @param mixed $value Data to be cached - * @param integer $duration How long to cache the data, in seconds - * @return boolean True if the data was successfully cached, false on failure + * @param int $duration How long to cache the data, in seconds + * @return bool True if the data was successfully cached, false on failure */ public function write($key, $value, $duration) { if (!is_int($value)) { $value = serialize($value); } + + if (!$this->_Redis->isConnected()) { + $this->_connect(); + } + if ($duration === 0) { return $this->_Redis->set($key, $value); } @@ -138,7 +146,7 @@ class RedisEngine extends CacheEngine { * Increments the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to increment + * @param int $offset How much to increment * @return New incremented value, false otherwise * @throws CacheException when you try to increment with compress = true */ @@ -150,7 +158,7 @@ class RedisEngine extends CacheEngine { * Decrements the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to subtract + * @param int $offset How much to subtract * @return New decremented value, false otherwise * @throws CacheException when you try to decrement with compress = true */ @@ -162,7 +170,7 @@ class RedisEngine extends CacheEngine { * Delete a key from the cache * * @param string $key Identifier for the data - * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public function delete($key) { return $this->_Redis->delete($key) > 0; @@ -171,8 +179,9 @@ class RedisEngine extends CacheEngine { /** * Delete all keys from the cache * - * @param boolean $check - * @return boolean True if the cache was successfully cleared, false otherwise + * @param bool $check Whether or not expiration keys should be checked. If + * true, no keys will be removed as cache will rely on redis TTL's. + * @return bool True if the cache was successfully cleared, false otherwise */ public function clear($check) { if ($check) { @@ -208,7 +217,8 @@ class RedisEngine extends CacheEngine { * Increments the group value to simulate deletion of all keys under a group * old values will remain in storage until they expire. * - * @return boolean success + * @param string $group The group name to clear. + * @return bool success */ public function clearGroup($group) { return (bool)$this->_Redis->incr($this->settings['prefix'] . $group); @@ -222,4 +232,27 @@ class RedisEngine extends CacheEngine { $this->_Redis->close(); } } + +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + * @link https://github.com/phpredis/phpredis#setnx + */ + public function add($key, $value, $duration) { + if (!is_int($value)) { + $value = serialize($value); + } + + $result = $this->_Redis->setnx($key, $value); + // setnx() doesn't have an expiry option, so overwrite the key with one + if ($result) { + return $this->_Redis->setex($key, $duration, $value); + } + return false; + } } diff --git a/web/api/lib/Cake/Cache/Engine/WincacheEngine.php b/web/api/lib/Cake/Cache/Engine/WincacheEngine.php index bfba803d2..e5c9d7bd7 100644 --- a/web/api/lib/Cake/Cache/Engine/WincacheEngine.php +++ b/web/api/lib/Cake/Cache/Engine/WincacheEngine.php @@ -40,7 +40,7 @@ class WincacheEngine extends CacheEngine { * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); * * @param array $settings array of setting for the engine - * @return boolean True if the engine has been successfully initialized, false if not + * @return bool True if the engine has been successfully initialized, false if not * @see CacheEngine::__defaults */ public function init($settings = array()) { @@ -57,8 +57,8 @@ class WincacheEngine extends CacheEngine { * * @param string $key Identifier for the data * @param mixed $value Data to be cached - * @param integer $duration How long to cache the data, in seconds - * @return boolean True if the data was successfully cached, false on failure + * @param int $duration How long to cache the data, in seconds + * @return bool True if the data was successfully cached, false on failure */ public function write($key, $value, $duration) { $expires = time() + $duration; @@ -80,7 +80,7 @@ class WincacheEngine extends CacheEngine { */ public function read($key) { $time = time(); - $cachetime = intval(wincache_ucache_get($key . '_expires')); + $cachetime = (int)wincache_ucache_get($key . '_expires'); if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) { return false; } @@ -91,7 +91,7 @@ class WincacheEngine extends CacheEngine { * Increments the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to increment + * @param int $offset How much to increment * @return New incremented value, false otherwise */ public function increment($key, $offset = 1) { @@ -102,7 +102,7 @@ class WincacheEngine extends CacheEngine { * Decrements the value of an integer cached key * * @param string $key Identifier for the data - * @param integer $offset How much to subtract + * @param int $offset How much to subtract * @return New decremented value, false otherwise */ public function decrement($key, $offset = 1) { @@ -113,7 +113,7 @@ class WincacheEngine extends CacheEngine { * Delete a key from the cache * * @param string $key Identifier for the data - * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public function delete($key) { return wincache_ucache_delete($key); @@ -123,9 +123,9 @@ class WincacheEngine extends CacheEngine { * Delete all keys from the cache. This will clear every * item in the cache matching the cache config prefix. * - * @param boolean $check If true, nothing will be cleared, as entries will + * @param bool $check If true, nothing will be cleared, as entries will * naturally expire in wincache.. - * @return boolean True Returns true. + * @return bool True Returns true. */ public function clear($check) { if ($check) { @@ -179,7 +179,8 @@ class WincacheEngine extends CacheEngine { * Increments the group value to simulate deletion of all keys under a group * old values will remain in storage until they expire. * - * @return boolean success + * @param string $group The group to clear. + * @return bool success */ public function clearGroup($group) { $success = null; @@ -187,4 +188,20 @@ class WincacheEngine extends CacheEngine { return $success; } +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + */ + public function add($key, $value, $duration) { + $cachedValue = $this->read($key); + if ($cachedValue === false) { + return $this->write($key, $value, $duration); + } + return false; + } } diff --git a/web/api/lib/Cake/Cache/Engine/XcacheEngine.php b/web/api/lib/Cake/Cache/Engine/XcacheEngine.php index 12e19c786..c46a7b9c5 100644 --- a/web/api/lib/Cake/Cache/Engine/XcacheEngine.php +++ b/web/api/lib/Cake/Cache/Engine/XcacheEngine.php @@ -41,10 +41,10 @@ class XcacheEngine extends CacheEngine { * To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array()); * * @param array $settings array of setting for the engine - * @return boolean True if the engine has been successfully initialized, false if not + * @return bool True if the engine has been successfully initialized, false if not */ public function init($settings = array()) { - if (php_sapi_name() !== 'cli') { + if (PHP_SAPI !== 'cli') { parent::init(array_merge(array( 'engine' => 'Xcache', 'prefix' => Inflector::slug(APP_DIR) . '_', @@ -62,8 +62,8 @@ class XcacheEngine extends CacheEngine { * * @param string $key Identifier for the data * @param mixed $value Data to be cached - * @param integer $duration How long to cache the data, in seconds - * @return boolean True if the data was successfully cached, false on failure + * @param int $duration How long to cache the data, in seconds + * @return bool True if the data was successfully cached, false on failure */ public function write($key, $value, $duration) { $expires = time() + $duration; @@ -80,7 +80,7 @@ class XcacheEngine extends CacheEngine { public function read($key) { if (xcache_isset($key)) { $time = time(); - $cachetime = intval(xcache_get($key . '_expires')); + $cachetime = (int)xcache_get($key . '_expires'); if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) { return false; } @@ -94,7 +94,7 @@ class XcacheEngine extends CacheEngine { * If the cache key is not an integer it will be treated as 0 * * @param string $key Identifier for the data - * @param integer $offset How much to increment + * @param int $offset How much to increment * @return New incremented value, false otherwise */ public function increment($key, $offset = 1) { @@ -106,7 +106,7 @@ class XcacheEngine extends CacheEngine { * If the cache key is not an integer it will be treated as 0 * * @param string $key Identifier for the data - * @param integer $offset How much to subtract + * @param int $offset How much to subtract * @return New decremented value, false otherwise */ public function decrement($key, $offset = 1) { @@ -117,7 +117,7 @@ class XcacheEngine extends CacheEngine { * Delete a key from the cache * * @param string $key Identifier for the data - * @return boolean True if the value was successfully deleted, false if it didn't exist or couldn't be removed + * @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed */ public function delete($key) { return xcache_unset($key); @@ -126,8 +126,9 @@ class XcacheEngine extends CacheEngine { /** * Delete all keys from the cache * - * @param boolean $check - * @return boolean True if the cache was successfully cleared, false otherwise + * @param bool $check If true no deletes will occur and instead CakePHP will rely + * on key TTL values. + * @return bool True if the cache was successfully cleared, false otherwise */ public function clear($check) { $this->_auth(); @@ -163,7 +164,8 @@ class XcacheEngine extends CacheEngine { * Increments the group value to simulate deletion of all keys under a group * old values will remain in storage until they expire. * - * @return boolean success + * @param string $group The group to clear. + * @return bool success */ public function clearGroup($group) { return (bool)xcache_inc($this->settings['prefix'] . $group, 1); @@ -176,7 +178,7 @@ class XcacheEngine extends CacheEngine { * This has to be done because xcache_clear_cache() needs to pass Basic Http Auth * (see xcache.admin configuration settings) * - * @param boolean $reverse Revert changes + * @param bool $reverse Revert changes * @return void */ protected function _auth($reverse = false) { @@ -205,4 +207,21 @@ class XcacheEngine extends CacheEngine { } } } + +/** + * Write data for key into cache if it doesn't exist already. + * If it already exists, it fails and returns false. + * + * @param string $key Identifier for the data. + * @param mixed $value Data to be cached. + * @param int $duration How long to cache the data, in seconds. + * @return bool True if the data was successfully cached, false on failure. + */ + public function add($key, $value, $duration) { + $cachedValue = $this->read($key); + if ($cachedValue === false) { + return $this->write($key, $value, $duration); + } + return false; + } } diff --git a/web/api/lib/Cake/Config/routes.php b/web/api/lib/Cake/Config/routes.php index e27ba3a40..7bcc0c06b 100644 --- a/web/api/lib/Cake/Config/routes.php +++ b/web/api/lib/Cake/Config/routes.php @@ -48,8 +48,8 @@ if ($plugins = CakePlugin::loaded()) { $plugins[$key] = Inflector::underscore($value); } $pluginPattern = implode('|', $plugins); - $match = array('plugin' => $pluginPattern); - $shortParams = array('routeClass' => 'PluginShortRoute', 'plugin' => $pluginPattern); + $match = array('plugin' => $pluginPattern, 'defaultRoute' => true); + $shortParams = array('routeClass' => 'PluginShortRoute', 'plugin' => $pluginPattern, 'defaultRoute' => true); foreach ($prefixes as $prefix) { $params = array('prefix' => $prefix, $prefix => true); @@ -66,11 +66,11 @@ if ($plugins = CakePlugin::loaded()) { foreach ($prefixes as $prefix) { $params = array('prefix' => $prefix, $prefix => true); $indexParams = $params + array('action' => 'index'); - Router::connect("/{$prefix}/:controller", $indexParams); - Router::connect("/{$prefix}/:controller/:action/*", $params); + Router::connect("/{$prefix}/:controller", $indexParams, array('defaultRoute' => true)); + Router::connect("/{$prefix}/:controller/:action/*", $params, array('defaultRoute' => true)); } -Router::connect('/:controller', array('action' => 'index')); -Router::connect('/:controller/:action/*'); +Router::connect('/:controller', array('action' => 'index'), array('defaultRoute' => true)); +Router::connect('/:controller/:action/*', array(), array('defaultRoute' => true)); $namedConfig = Router::namedConfig(); if ($namedConfig['rules'] === false) { @@ -79,3 +79,4 @@ if ($namedConfig['rules'] === false) { unset($namedConfig, $params, $indexParams, $prefix, $prefixes, $shortParams, $match, $pluginPattern, $plugins, $key, $value); + diff --git a/web/api/lib/Cake/Configure/ConfigReaderInterface.php b/web/api/lib/Cake/Configure/ConfigReaderInterface.php index e36f05697..ddea4e2d3 100644 --- a/web/api/lib/Cake/Configure/ConfigReaderInterface.php +++ b/web/api/lib/Cake/Configure/ConfigReaderInterface.php @@ -26,7 +26,7 @@ interface ConfigReaderInterface { * These sources can either be static resources like files, or dynamic ones like * a database, or other datasource. * - * @param string $key + * @param string $key Key to read. * @return array An array of data to merge into the runtime configuration */ public function read($key); @@ -36,7 +36,7 @@ interface ConfigReaderInterface { * * @param string $key The identifier to write to. * @param array $data The data to dump. - * @return boolean True on success or false on failure. + * @return bool True on success or false on failure. */ public function dump($key, $data); diff --git a/web/api/lib/Cake/Configure/IniReader.php b/web/api/lib/Cake/Configure/IniReader.php index 886ab1779..ad3b88145 100644 --- a/web/api/lib/Cake/Configure/IniReader.php +++ b/web/api/lib/Cake/Configure/IniReader.php @@ -17,6 +17,7 @@ */ App::uses('Hash', 'Utility'); +App::uses('CakePlugin', 'Core'); /** * Ini file configuration engine. @@ -33,10 +34,10 @@ App::uses('Hash', 'Utility'); * You can nest properties as deeply as needed using `.`'s. In addition to using `.` you * can use standard ini section notation to create nested structures: * - * {{{ + * ``` * [section] * key = value - * }}} + * ``` * * Once loaded into Configure, the above would be accessed using: * @@ -101,7 +102,7 @@ class IniReader implements ConfigReaderInterface { } $file = $this->_getFilePath($key); - if (!is_file($file)) { + if (!is_file(realpath($file))) { throw new ConfigureException(__d('cake_dev', 'Could not load configuration file: %s', $file)); } @@ -152,7 +153,7 @@ class IniReader implements ConfigReaderInterface { * @param string $key The identifier to write to. If the key has a . it will be treated * as a plugin prefix. * @param array $data The data to convert to ini file. - * @return integer Bytes saved. + * @return int Bytes saved. */ public function dump($key, $data) { $result = array(); @@ -181,7 +182,7 @@ class IniReader implements ConfigReaderInterface { /** * Converts a value into the ini equivalent * - * @param mixed $value to export. + * @param mixed $val Value to export. * @return string String value for ini file. */ protected function _value($val) { @@ -218,7 +219,7 @@ class IniReader implements ConfigReaderInterface { } if ($plugin) { - $file = App::pluginPath($plugin) . 'Config' . DS . $key; + $file = CakePlugin::path($plugin) . 'Config' . DS . $key; } else { $file = $this->_path . $key; } diff --git a/web/api/lib/Cake/Configure/PhpReader.php b/web/api/lib/Cake/Configure/PhpReader.php index 175168a39..d15a41b83 100644 --- a/web/api/lib/Cake/Configure/PhpReader.php +++ b/web/api/lib/Cake/Configure/PhpReader.php @@ -15,6 +15,8 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ +App::uses('CakePlugin', 'Core'); + /** * PHP Reader allows Configure to load configuration values from * files containing simple PHP arrays. @@ -49,7 +51,7 @@ class PhpReader implements ConfigReaderInterface { * Read a config file and return its contents. * * Files with `.` in the name will be treated as values in plugins. Instead of reading from - * the initialized path, plugin keys will be located using App::pluginPath(). + * the initialized path, plugin keys will be located using CakePlugin::path(). * * @param string $key The identifier to read from. If the key has a . it will be treated * as a plugin prefix. @@ -63,7 +65,7 @@ class PhpReader implements ConfigReaderInterface { } $file = $this->_getFilePath($key); - if (!is_file($file)) { + if (!is_file(realpath($file))) { throw new ConfigureException(__d('cake_dev', 'Could not load configuration file: %s', $file)); } @@ -81,7 +83,7 @@ class PhpReader implements ConfigReaderInterface { * @param string $key The identifier to write to. If the key has a . it will be treated * as a plugin prefix. * @param array $data Data to dump. - * @return integer Bytes saved. + * @return int Bytes saved. */ public function dump($key, $data) { $contents = '_path . $key; } diff --git a/web/api/lib/Cake/Console/Command/AclShell.php b/web/api/lib/Cake/Console/Command/AclShell.php index ac5b3cdfe..15536ebb6 100644 --- a/web/api/lib/Cake/Console/Command/AclShell.php +++ b/web/api/lib/Cake/Console/Command/AclShell.php @@ -220,7 +220,7 @@ class AclShell extends AppShell { * * @param string $class Class name that is being used. * @param array $node Array of node information. - * @param integer $indent indent level. + * @param int $indent indent level. * @return void */ protected function _outputNode($class, $node, $indent) { @@ -519,7 +519,7 @@ class AclShell extends AppShell { /** * Checks that given node exists * - * @return boolean Success + * @return bool Success */ public function nodeExists() { if (!isset($this->args[0]) || !isset($this->args[1])) { @@ -558,8 +558,8 @@ class AclShell extends AppShell { * or an array of properties to use in AcoNode::node() * * @param string $class Class type you want (Aro/Aco) - * @param string|array $identifier A mixed identifier for finding the node. - * @return integer Integer of NodeId. Will trigger an error if nothing is found. + * @param string|array|null $identifier A mixed identifier for finding the node, otherwise null. + * @return int Integer of NodeId. Will trigger an error if nothing is found. */ protected function _getNodeId($class, $identifier) { $node = $this->Acl->{$class}->node($identifier); @@ -568,7 +568,7 @@ class AclShell extends AppShell { $identifier = var_export($identifier, true); } $this->error(__d('cake_console', 'Could not find node using reference "%s"', $identifier)); - return; + return null; } return Hash::get($node, "0.{$class}.id"); } @@ -579,8 +579,8 @@ class AclShell extends AppShell { * @return array aro, aco, action */ protected function _getParams() { - $aro = is_numeric($this->args[0]) ? intval($this->args[0]) : $this->args[0]; - $aco = is_numeric($this->args[1]) ? intval($this->args[1]) : $this->args[1]; + $aro = is_numeric($this->args[0]) ? (int)$this->args[0] : $this->args[0]; + $aco = is_numeric($this->args[1]) ? (int)$this->args[1] : $this->args[1]; $aroName = $aro; $acoName = $aco; diff --git a/web/api/lib/Cake/Console/Command/CommandListShell.php b/web/api/lib/Cake/Console/Command/CommandListShell.php index 755166f41..ca6620a5b 100644 --- a/web/api/lib/Cake/Console/Command/CommandListShell.php +++ b/web/api/lib/Cake/Console/Command/CommandListShell.php @@ -77,7 +77,7 @@ class CommandListShell extends AppShell { /** * Output text. * - * @param array $shellList + * @param array $shellList The shell list. * @return void */ protected function _asText($shellList) { @@ -95,7 +95,7 @@ class CommandListShell extends AppShell { /** * Output as XML * - * @param array $shellList + * @param array $shellList The shell list. * @return void */ protected function _asXml($shellList) { diff --git a/web/api/lib/Cake/Console/Command/CompletionShell.php b/web/api/lib/Cake/Console/Command/CompletionShell.php index b57c84145..57e86f949 100644 --- a/web/api/lib/Cake/Console/Command/CompletionShell.php +++ b/web/api/lib/Cake/Console/Command/CompletionShell.php @@ -144,7 +144,7 @@ class CompletionShell extends AppShell { /** * Emit results as a string, space delimited * - * @param array $options + * @param array $options The options to output * @return void */ protected function _output($options = array()) { diff --git a/web/api/lib/Cake/Console/Command/ConsoleShell.php b/web/api/lib/Cake/Console/Command/ConsoleShell.php index 917015a9f..ac270e19b 100644 --- a/web/api/lib/Cake/Console/Command/ConsoleShell.php +++ b/web/api/lib/Cake/Console/Command/ConsoleShell.php @@ -19,7 +19,7 @@ App::uses('AppShell', 'Console/Command'); * Provides a very basic 'interactive' console for CakePHP apps. * * @package Cake.Console.Command - * @deprecated Deprecated since version 2.4, will be removed in 3.0 + * @deprecated 3.0.0 Deprecated since version 2.4, will be removed in 3.0 */ class ConsoleShell extends AppShell { @@ -193,7 +193,7 @@ class ConsoleShell extends AppShell { /** * Override main() to handle action * - * @param string $command + * @param string $command The command to run. * @return void */ public function main($command = null) { @@ -218,7 +218,7 @@ class ConsoleShell extends AppShell { /** * Determine the method to process the current command * - * @param string $command + * @param string $command The command to run. * @return string or false */ protected function _method($command) { @@ -256,7 +256,7 @@ class ConsoleShell extends AppShell { /** * Bind an association * - * @param mixed $command + * @param mixed $command The command to run. * @return void */ protected function _bind($command) { @@ -283,7 +283,7 @@ class ConsoleShell extends AppShell { /** * Unbind an association * - * @param mixed $command + * @param mixed $command The command to run. * @return void */ protected function _unbind($command) { @@ -303,7 +303,7 @@ class ConsoleShell extends AppShell { $validCurrentAssociation = false; foreach ($currentAssociations as $model => $currentAssociation) { - if ($model == $modelB && $association == $currentAssociation) { + if ($model === $modelB && $association === $currentAssociation) { $validCurrentAssociation = true; } } @@ -320,7 +320,7 @@ class ConsoleShell extends AppShell { /** * Perform a find * - * @param mixed $command + * @param mixed $command The command to run. * @return void */ protected function _find($command) { @@ -382,7 +382,7 @@ class ConsoleShell extends AppShell { /** * Save a record * - * @param mixed $command + * @param mixed $command The command to run. * @return void */ protected function _save($command) { @@ -406,7 +406,7 @@ class ConsoleShell extends AppShell { /** * Show the columns for a model * - * @param mixed $command + * @param mixed $command The command to run. * @return void */ protected function _columns($command) { @@ -455,7 +455,7 @@ class ConsoleShell extends AppShell { /** * Parse an array URL and show the equivalent URL as a string * - * @param mixed $command + * @param mixed $command The command to run. * @return void */ protected function _routeToString($command) { @@ -471,7 +471,7 @@ class ConsoleShell extends AppShell { /** * Parse a string URL and show as an array * - * @param mixed $command + * @param mixed $command The command to run. * @return void */ protected function _routeToArray($command) { @@ -483,8 +483,8 @@ class ConsoleShell extends AppShell { /** * Tells if the specified model is included in the list of available models * - * @param string $modelToCheck - * @return boolean true if is an available model, false otherwise + * @param string $modelToCheck The model to check. + * @return bool true if is an available model, false otherwise */ protected function _isValidModel($modelToCheck) { return in_array($modelToCheck, $this->models); @@ -494,7 +494,7 @@ class ConsoleShell extends AppShell { * Reloads the routes configuration from app/Config/routes.php, and compiles * all routes found * - * @return boolean True if config reload was a success, otherwise false + * @return bool True if config reload was a success, otherwise false */ protected function _loadRoutes() { Router::reload(); diff --git a/web/api/lib/Cake/Console/Command/SchemaShell.php b/web/api/lib/Cake/Console/Command/SchemaShell.php index 6df8a32ea..a2ddfcc0a 100644 --- a/web/api/lib/Cake/Console/Command/SchemaShell.php +++ b/web/api/lib/Cake/Console/Command/SchemaShell.php @@ -39,7 +39,7 @@ class SchemaShell extends AppShell { /** * is this a dry run? * - * @var boolean + * @var bool */ protected $_dry = null; @@ -66,13 +66,10 @@ class SchemaShell extends AppShell { list($this->params['plugin'], $splitName) = pluginSplit($name); $name = $this->params['name'] = $splitName; } - - $defaultFile = 'schema.php'; - if (empty($this->params['file'])) { - $this->params['file'] = $defaultFile; - } - if ($name && $this->params['file'] === $defaultFile) { + if ($name && empty($this->params['file'])) { $this->params['file'] = Inflector::underscore($name); + } elseif (empty($this->params['file'])) { + $this->params['file'] = 'schema.php'; } if (strpos($this->params['file'], '.php') === false) { $this->params['file'] .= '.php'; @@ -92,7 +89,7 @@ class SchemaShell extends AppShell { $name = $plugin; } } - $name = Inflector::classify($name); + $name = Inflector::camelize($name); $this->Schema = new CakeSchema(compact('name', 'path', 'file', 'connection', 'plugin')); } @@ -125,7 +122,7 @@ class SchemaShell extends AppShell { if ($this->params['force']) { $options['models'] = false; } elseif (!empty($this->params['models'])) { - $options['models'] = String::tokenize($this->params['models']); + $options['models'] = CakeText::tokenize($this->params['models']); } $snapshot = false; @@ -154,14 +151,14 @@ class SchemaShell extends AppShell { Configure::write('Cache.disable', $cacheDisable); if (!empty($this->params['exclude']) && !empty($content)) { - $excluded = String::tokenize($this->params['exclude']); + $excluded = CakeText::tokenize($this->params['exclude']); foreach ($excluded as $table) { unset($content['tables'][$table]); } } if ($snapshot === true) { - $fileName = rtrim($this->params['file'], '.php'); + $fileName = basename($this->params['file'], '.php'); $Folder = new Folder($this->Schema->path); $result = $Folder->read(); @@ -288,17 +285,17 @@ class SchemaShell extends AppShell { 'connection' => $this->params['connection'], ); if (!empty($this->params['snapshot'])) { - $fileName = rtrim($this->Schema->file, '.php'); + $fileName = basename($this->Schema->file, '.php'); $options['file'] = $fileName . '_' . $this->params['snapshot'] . '.php'; } $Schema = $this->Schema->load($options); if (!$Schema) { - $this->err(__d('cake_console', 'The chosen schema could not be loaded. Attempted to load:')); - $this->err(__d('cake_console', 'File: %s', $this->Schema->path . DS . $this->Schema->file)); - $this->err(__d('cake_console', 'Name: %s', $this->Schema->name)); - return $this->_stop(); + $this->err(__d('cake_console', 'Error: The chosen schema could not be loaded. Attempted to load:')); + $this->err(__d('cake_console', '- file: %s', $this->Schema->path . DS . $this->Schema->file)); + $this->err(__d('cake_console', '- name: %s', $this->Schema->name)); + return $this->_stop(2); } $table = null; if (isset($this->args[1])) { @@ -311,8 +308,8 @@ class SchemaShell extends AppShell { * Create database from Schema object * Should be called via the run method * - * @param CakeSchema $Schema - * @param string $table + * @param CakeSchema $Schema The schema instance to create. + * @param string $table The table name. * @return void */ protected function _create(CakeSchema $Schema, $table = null) { @@ -337,8 +334,7 @@ class SchemaShell extends AppShell { $this->out("\n" . __d('cake_console', 'The following table(s) will be dropped.')); $this->out(array_keys($drop)); - if ( - !empty($this->params['yes']) || + if (!empty($this->params['yes']) || $this->in(__d('cake_console', 'Are you sure you want to drop the table(s)?'), array('y', 'n'), 'n') === 'y' ) { $this->out(__d('cake_console', 'Dropping table(s).')); @@ -348,8 +344,7 @@ class SchemaShell extends AppShell { $this->out("\n" . __d('cake_console', 'The following table(s) will be created.')); $this->out(array_keys($create)); - if ( - !empty($this->params['yes']) || + if (!empty($this->params['yes']) || $this->in(__d('cake_console', 'Are you sure you want to create the table(s)?'), array('y', 'n'), 'y') === 'y' ) { $this->out(__d('cake_console', 'Creating table(s).')); @@ -362,8 +357,8 @@ class SchemaShell extends AppShell { * Update database with Schema object * Should be called via the run method * - * @param CakeSchema $Schema - * @param string $table + * @param CakeSchema &$Schema The schema instance + * @param string $table The table name. * @return void */ protected function _update(&$Schema, $table = null) { @@ -402,13 +397,15 @@ class SchemaShell extends AppShell { $this->out("\n" . __d('cake_console', 'The following statements will run.')); $this->out(array_map('trim', $contents)); - if ( - !empty($this->params['yes']) || + if (!empty($this->params['yes']) || $this->in(__d('cake_console', 'Are you sure you want to alter the tables?'), array('y', 'n'), 'n') === 'y' ) { $this->out(); $this->out(__d('cake_console', 'Updating Database...')); $this->_run($contents, 'update', $Schema); + + Configure::write('Cache.disable', false); + Cache::clear(false, '_cake_model_'); } $this->out(__d('cake_console', 'End update.')); @@ -417,9 +414,9 @@ class SchemaShell extends AppShell { /** * Runs sql from _create() or _update() * - * @param array $contents - * @param string $event - * @param CakeSchema $Schema + * @param array $contents The contents to execute. + * @param string $event The event to fire + * @param CakeSchema $Schema The schema instance. * @return void */ protected function _run($contents, $event, CakeSchema $Schema) { @@ -483,7 +480,6 @@ class SchemaShell extends AppShell { ); $file = array( 'help' => __d('cake_console', 'File name to read and write.'), - 'default' => 'schema.php' ); $name = array( 'help' => __d('cake_console', diff --git a/web/api/lib/Cake/Console/Command/ServerShell.php b/web/api/lib/Cake/Console/Command/ServerShell.php index ae46f6ef8..8c7d5e5e3 100644 --- a/web/api/lib/Cake/Console/Command/ServerShell.php +++ b/web/api/lib/Cake/Console/Command/ServerShell.php @@ -34,7 +34,7 @@ class ServerShell extends AppShell { /** * Default ListenPort * - * @var integer + * @var int */ const DEFAULT_PORT = 80; @@ -65,8 +65,8 @@ class ServerShell extends AppShell { * @return void */ public function initialize() { - $this->_host = self::DEFAULT_HOST; - $this->_port = self::DEFAULT_PORT; + $this->_host = static::DEFAULT_HOST; + $this->_port = static::DEFAULT_PORT; $this->_documentRoot = WWW_ROOT; } @@ -91,8 +91,8 @@ class ServerShell extends AppShell { $this->_documentRoot = $this->params['document_root']; } - // for windows - if (substr($this->_documentRoot, -1, 1) == DIRECTORY_SEPARATOR) { + // for Windows + if (substr($this->_documentRoot, -1, 1) === DIRECTORY_SEPARATOR) { $this->_documentRoot = substr($this->_documentRoot, 0, strlen($this->_documentRoot) - 1); } if (preg_match("/^([a-z]:)[\\\]+(.+)$/i", $this->_documentRoot, $m)) { @@ -135,7 +135,7 @@ class ServerShell extends AppShell { escapeshellarg($this->_documentRoot . '/index.php') ); - $port = ($this->_port == self::DEFAULT_PORT) ? '' : ':' . $this->_port; + $port = ($this->_port == static::DEFAULT_PORT) ? '' : ':' . $this->_port; $this->out(__d('cake_console', 'built-in server is running in http://%s%s/', $this->_host, $port)); system($command); } diff --git a/web/api/lib/Cake/Console/Command/Task/BakeTask.php b/web/api/lib/Cake/Console/Command/Task/BakeTask.php index 6b534f365..8298e112a 100644 --- a/web/api/lib/Cake/Console/Command/Task/BakeTask.php +++ b/web/api/lib/Cake/Console/Command/Task/BakeTask.php @@ -41,7 +41,7 @@ class BakeTask extends AppShell { /** * Flag for interactive mode * - * @var boolean + * @var bool */ public $interactive = false; diff --git a/web/api/lib/Cake/Console/Command/Task/CommandTask.php b/web/api/lib/Cake/Console/Command/Task/CommandTask.php index 9c90fc6be..fca006a72 100644 --- a/web/api/lib/Cake/Console/Command/Task/CommandTask.php +++ b/web/api/lib/Cake/Console/Command/Task/CommandTask.php @@ -53,9 +53,9 @@ class CommandTask extends AppShell { /** * Scan the provided paths for shells, and append them into $shellList * - * @param string $type - * @param array $shells - * @param array $shellList + * @param string $type The type of object. + * @param array $shells The shell name. + * @param array &$shellList List of shells. * @return void */ protected function _appendShells($type, $shells, &$shellList) { @@ -90,7 +90,7 @@ class CommandTask extends AppShell { /** * Return a list of subcommands for a given command * - * @param string $commandName + * @param string $commandName The command you want subcommands from. * @return array */ public function subCommands($commandName) { @@ -127,7 +127,7 @@ class CommandTask extends AppShell { /** * Get Shell instance for the given command * - * @param mixed $commandName + * @param mixed $commandName The command you want. * @return mixed */ public function getShell($commandName) { @@ -157,7 +157,7 @@ class CommandTask extends AppShell { /** * Get Shell instance for the given command * - * @param mixed $commandName + * @param mixed $commandName The command to get options for. * @return array */ public function options($commandName) { diff --git a/web/api/lib/Cake/Console/Command/Task/ControllerTask.php b/web/api/lib/Cake/Console/Command/Task/ControllerTask.php index 5a4553466..fc2d91343 100644 --- a/web/api/lib/Cake/Console/Command/Task/ControllerTask.php +++ b/web/api/lib/Cake/Console/Command/Task/ControllerTask.php @@ -188,6 +188,7 @@ class ControllerTask extends BakeTask { if (strtolower($wannaUseSession) === 'y') { array_push($components, 'Session'); } + array_unique($components); } } else { list($wannaBakeCrud, $wannaBakeAdminCrud) = $this->_askAboutMethods(); @@ -224,10 +225,10 @@ class ControllerTask extends BakeTask { /** * Confirm a to be baked controller with the user * - * @param string $controllerName - * @param string $useDynamicScaffold - * @param array $helpers - * @param array $components + * @param string $controllerName The name of the controller. + * @param string $useDynamicScaffold Whether or not to use dynamic scaffolds. + * @param array $helpers The list of helpers to include. + * @param array $components The list of components to include. * @return void */ public function confirmController($controllerName, $useDynamicScaffold, $helpers, $components) { @@ -247,10 +248,10 @@ class ControllerTask extends BakeTask { ); foreach ($properties as $var => $title) { - if (count($$var)) { + if (count(${$var})) { $output = ''; - $length = count($$var); - foreach ($$var as $i => $propElement) { + $length = count(${$var}); + foreach (${$var} as $i => $propElement) { if ($i != $length - 1) { $output .= ucfirst($propElement) . ', '; } else { @@ -285,7 +286,7 @@ class ControllerTask extends BakeTask { * * @param string $controllerName Controller name * @param string $admin Admin route to use - * @param boolean $wannaUseSession Set to true to use sessions, false otherwise + * @param bool $wannaUseSession Set to true to use sessions, false otherwise * @return string Baked actions */ public function bakeActions($controllerName, $admin = null, $wannaUseSession = true) { @@ -383,9 +384,9 @@ class ControllerTask extends BakeTask { * @return array Components the user wants to use. */ public function doComponents() { - $components = array('Paginator'); + $components = array('Paginator', 'Flash'); return array_merge($components, $this->_doPropertyChoices( - __d('cake_console', "Would you like this controller to use other components\nbesides PaginatorComponent?"), + __d('cake_console', "Would you like this controller to use other components\nbesides PaginatorComponent and FlashComponent?"), __d('cake_console', "Please provide a comma separated list of the component names you'd like to use.\nExample: 'Acl, Security, RequestHandler'") )); } @@ -451,14 +452,14 @@ class ControllerTask extends BakeTask { return $this->_stop(); } - if (!$enteredController || intval($enteredController) > count($controllers)) { + if (!$enteredController || (int)$enteredController > count($controllers)) { $this->err(__d('cake_console', "The Controller name you supplied was empty,\nor the number you selected was not an option. Please try again.")); $enteredController = ''; } } - if (intval($enteredController) > 0 && intval($enteredController) <= count($controllers)) { - $controllerName = $controllers[intval($enteredController) - 1]; + if ((int)$enteredController > 0 && (int)$enteredController <= count($controllers)) { + $controllerName = $controllers[(int)$enteredController - 1]; } else { $controllerName = Inflector::camelize($enteredController); } diff --git a/web/api/lib/Cake/Console/Command/Task/DbConfigTask.php b/web/api/lib/Cake/Console/Command/Task/DbConfigTask.php index 75c0c13b6..ee1aa6c99 100644 --- a/web/api/lib/Cake/Console/Command/Task/DbConfigTask.php +++ b/web/api/lib/Cake/Console/Command/Task/DbConfigTask.php @@ -199,8 +199,8 @@ class DbConfigTask extends AppShell { /** * Output verification message and bake if it looks good * - * @param array $config - * @return boolean True if user says it looks good, false otherwise + * @param array $config The config data. + * @return bool True if user says it looks good, false otherwise */ protected function _verify($config) { $config += $this->_defaultConfig; @@ -247,7 +247,7 @@ class DbConfigTask extends AppShell { * Assembles and writes database.php * * @param array $configs Configuration settings to use - * @return boolean Success + * @return bool Success */ public function bake($configs) { if (!is_dir($this->path)) { @@ -296,7 +296,7 @@ class DbConfigTask extends AppShell { foreach ($oldConfigs as $key => $oldConfig) { foreach ($configs as $config) { - if ($oldConfig['name'] == $config['name']) { + if ($oldConfig['name'] === $config['name']) { unset($oldConfigs[$key]); } } diff --git a/web/api/lib/Cake/Console/Command/Task/ExtractTask.php b/web/api/lib/Cake/Console/Command/Task/ExtractTask.php index d60bc0e5c..740528f70 100644 --- a/web/api/lib/Cake/Console/Command/Task/ExtractTask.php +++ b/web/api/lib/Cake/Console/Command/Task/ExtractTask.php @@ -44,7 +44,7 @@ class ExtractTask extends AppShell { /** * Merge all domain and category strings into the default.pot file * - * @var boolean + * @var bool */ protected $_merge = false; @@ -70,7 +70,7 @@ class ExtractTask extends AppShell { protected $_tokens = array(); /** - * Extracted strings indexed by category and domain. + * Extracted strings indexed by category, domain, msgid and context. * * @var array */ @@ -93,21 +93,21 @@ class ExtractTask extends AppShell { /** * Holds whether this call should extract model validation messages * - * @var boolean + * @var bool */ protected $_extractValidation = true; /** * Holds the validation string domain to use for validation messages when extracting * - * @var boolean + * @var bool */ protected $_validationDomain = 'default'; /** * Holds whether this call should extract the CakePHP Lib messages * - * @var boolean + * @var bool */ protected $_extractCore = false; @@ -127,7 +127,7 @@ class ExtractTask extends AppShell { ); $response = $this->in($message, null, $defaultPath); if (strtoupper($response) === 'Q') { - $this->out(__d('cake_console', 'Extract Aborted')); + $this->err(__d('cake_console', 'Extract Aborted')); return $this->_stop(); } elseif (strtoupper($response) === 'D' && count($this->_paths)) { $this->out(); @@ -151,7 +151,7 @@ class ExtractTask extends AppShell { */ public function execute() { if (!empty($this->params['exclude'])) { - $this->_exclude = explode(',', $this->params['exclude']); + $this->_exclude = explode(',', str_replace('/', DS, $this->params['exclude'])); } if (isset($this->params['files']) && !is_array($this->params['files'])) { $this->_files = explode(',', $this->params['files']); @@ -204,7 +204,7 @@ class ExtractTask extends AppShell { while (true) { $response = $this->in($message, null, rtrim($this->_paths[0], DS) . DS . 'Locale'); if (strtoupper($response) === 'Q') { - $this->out(__d('cake_console', 'Extract Aborted')); + $this->err(__d('cake_console', 'Extract Aborted')); return $this->_stop(); } elseif ($this->_isPathUsable($response)) { $this->_output = $response . DS; @@ -242,29 +242,33 @@ class ExtractTask extends AppShell { * * Takes care of duplicate translations * - * @param string $category - * @param string $domain - * @param string $msgid - * @param array $details + * @param string $category The category + * @param string $domain The domain + * @param string $msgid The message string + * @param array $details The file and line references * @return void */ protected function _addTranslation($category, $domain, $msgid, $details = array()) { - if (empty($this->_translations[$category][$domain][$msgid])) { - $this->_translations[$category][$domain][$msgid] = array( - 'msgid_plural' => false + $context = ''; + if (isset($details['msgctxt'])) { + $context = $details['msgctxt']; + } + + if (empty($this->_translations[$category][$domain][$msgid][$context])) { + $this->_translations[$category][$domain][$msgid][$context] = array( + 'msgid_plural' => false, ); } if (isset($details['msgid_plural'])) { - $this->_translations[$category][$domain][$msgid]['msgid_plural'] = $details['msgid_plural']; + $this->_translations[$category][$domain][$msgid][$context]['msgid_plural'] = $details['msgid_plural']; } - if (isset($details['file'])) { $line = 0; if (isset($details['line'])) { $line = $details['line']; } - $this->_translations[$category][$domain][$msgid]['references'][$details['file']][] = $line; + $this->_translations[$category][$domain][$msgid][$context]['references'][$details['file']][] = $line; } } @@ -312,6 +316,10 @@ class ExtractTask extends AppShell { ))->addOption('merge', array( 'help' => __d('cake_console', 'Merge all domain and category strings into the default.po file.'), 'choices' => array('yes', 'no') + ))->addOption('no-location', array( + 'boolean' => true, + 'default' => false, + 'help' => __d('cake_console', 'Do not write lines with locations'), ))->addOption('output', array( 'help' => __d('cake_console', 'Full path to output directory.') ))->addOption('files', array( @@ -355,14 +363,14 @@ class ExtractTask extends AppShell { protected function _extractTokens() { foreach ($this->_files as $file) { $this->_file = $file; - $this->out(__d('cake_console', 'Processing %s...', $file)); + $this->out(__d('cake_console', 'Processing %s...', $file), 1, Shell::VERBOSE); $code = file_get_contents($file); $allTokens = token_get_all($code); $this->_tokens = array(); foreach ($allTokens as $token) { - if (!is_array($token) || ($token[0] != T_WHITESPACE && $token[0] != T_INLINE_HTML)) { + if (!is_array($token) || ($token[0] !== T_WHITESPACE && $token[0] !== T_INLINE_HTML)) { $this->_tokens[] = $token; } } @@ -374,6 +382,15 @@ class ExtractTask extends AppShell { $this->_parse('__dc', array('domain', 'singular', 'category')); $this->_parse('__dn', array('domain', 'singular', 'plural')); $this->_parse('__dcn', array('domain', 'singular', 'plural', 'count', 'category')); + + $this->_parse('__x', array('context', 'singular')); + $this->_parse('__xn', array('context', 'singular', 'plural')); + $this->_parse('__dx', array('domain', 'context', 'singular')); + $this->_parse('__dxc', array('domain', 'context', 'singular', 'category')); + $this->_parse('__dxn', array('domain', 'context', 'singular', 'plural')); + $this->_parse('__dxcn', array('domain', 'context', 'singular', 'plural', 'count', 'category')); + $this->_parse('__xc', array('context', 'singular', 'category')); + } } @@ -398,7 +415,7 @@ class ExtractTask extends AppShell { } list($type, $string, $line) = $countToken; - if (($type == T_STRING) && ($string == $functionName) && ($firstParenthesis === '(')) { + if (($type == T_STRING) && ($string === $functionName) && ($firstParenthesis === '(')) { $position = $count; $depth = 0; @@ -414,11 +431,12 @@ class ExtractTask extends AppShell { $mapCount = count($map); $strings = $this->_getStrings($position, $mapCount); - if ($mapCount == count($strings)) { + if ($mapCount === count($strings)) { extract(array_combine($map, $strings)); $category = isset($category) ? $category : 6; - $category = intval($category); + $category = (int)$category; $categoryName = $categories[$category]; + $domain = isset($domain) ? $domain : 'default'; $details = array( 'file' => $this->_file, @@ -427,8 +445,14 @@ class ExtractTask extends AppShell { if (isset($plural)) { $details['msgid_plural'] = $plural; } - $this->_addTranslation($categoryName, $domain, $singular, $details); - } else { + if (isset($context)) { + $details['msgctxt'] = $context; + } + // Skip LC_TIME files as we use a special file format for them. + if ($categoryName !== 'LC_TIME') { + $this->_addTranslation($categoryName, $domain, $singular, $details); + } + } elseif (!is_array($this->_tokens[$count - 1]) || $this->_tokens[$count - 1][0] != T_FUNCTION) { $this->_markerError($this->_file, $line, $functionName, $count); } } @@ -547,32 +571,46 @@ class ExtractTask extends AppShell { protected function _buildFiles() { $paths = $this->_paths; $paths[] = realpath(APP) . DS; + + usort($paths, function ($a, $b) { + return strlen($b) - strlen($a); + }); + foreach ($this->_translations as $category => $domains) { foreach ($domains as $domain => $translations) { - foreach ($translations as $msgid => $details) { - $plural = $details['msgid_plural']; - $files = $details['references']; - $occurrences = array(); - foreach ($files as $file => $lines) { - $lines = array_unique($lines); - $occurrences[] = $file . ':' . implode(';', $lines); - } - $occurrences = implode("\n#: ", $occurrences); - $header = '#: ' . str_replace(DS, '/', str_replace($paths, '', $occurrences)) . "\n"; + foreach ($translations as $msgid => $contexts) { + foreach ($contexts as $context => $details) { + $plural = $details['msgid_plural']; + $header = ''; + if (empty($this->params['no-location'])) { + $files = $details['references']; + $occurrences = array(); + foreach ($files as $file => $lines) { + $lines = array_unique($lines); + $occurrences[] = $file . ':' . implode(';', $lines); + } + $occurrences = implode("\n#: ", $occurrences); + $header = '#: ' . str_replace(DS, '/', str_replace($paths, '', $occurrences)) . "\n"; + } - if ($plural === false) { - $sentence = "msgid \"{$msgid}\"\n"; - $sentence .= "msgstr \"\"\n\n"; - } else { - $sentence = "msgid \"{$msgid}\"\n"; - $sentence .= "msgid_plural \"{$plural}\"\n"; - $sentence .= "msgstr[0] \"\"\n"; - $sentence .= "msgstr[1] \"\"\n\n"; - } + $sentence = ''; + if ($context) { + $sentence .= "msgctxt \"{$context}\"\n"; + } + if ($plural === false) { + $sentence .= "msgid \"{$msgid}\"\n"; + $sentence .= "msgstr \"\"\n\n"; + } else { + $sentence .= "msgid \"{$msgid}\"\n"; + $sentence .= "msgid_plural \"{$plural}\"\n"; + $sentence .= "msgstr[0] \"\"\n"; + $sentence .= "msgstr[1] \"\"\n\n"; + } - $this->_store($category, $domain, $header, $sentence); - if (($category !== 'LC_MESSAGES' || $domain !== 'default') && $this->_merge) { - $this->_store('LC_MESSAGES', 'default', $header, $sentence); + $this->_store($category, $domain, $header, $sentence); + if (($category !== 'LC_MESSAGES' || $domain !== 'default') && $this->_merge) { + $this->_store('LC_MESSAGES', 'default', $header, $sentence); + } } } } @@ -582,10 +620,10 @@ class ExtractTask extends AppShell { /** * Prepare a file to be stored * - * @param string $category - * @param string $domain - * @param string $header - * @param string $sentence + * @param string $category The category + * @param string $domain The domain + * @param string $header The header content. + * @param string $sentence The sentence to store. * @return void */ protected function _store($category, $domain, $header, $sentence) { @@ -664,7 +702,6 @@ class ExtractTask extends AppShell { $output .= "msgid \"\"\n"; $output .= "msgstr \"\"\n"; $output .= "\"Project-Id-Version: PROJECT VERSION\\n\"\n"; - $output .= "\"POT-Creation-Date: " . date("Y-m-d H:iO") . "\\n\"\n"; $output .= "\"PO-Revision-Date: YYYY-mm-DD HH:MM+ZZZZ\\n\"\n"; $output .= "\"Last-Translator: NAME \\n\"\n"; $output .= "\"Language-Team: LANGUAGE \\n\"\n"; @@ -678,8 +715,8 @@ class ExtractTask extends AppShell { /** * Get the strings from the position forward * - * @param integer $position Actual position on tokens array - * @param integer $target Number of strings to extract + * @param int &$position Actual position on tokens array + * @param int $target Number of strings to extract * @return array Strings extracted */ protected function _getStrings(&$position, $target) { @@ -728,22 +765,22 @@ class ExtractTask extends AppShell { * Indicate an invalid marker on a processed file * * @param string $file File where invalid marker resides - * @param integer $line Line number + * @param int $line Line number * @param string $marker Marker found - * @param integer $count Count + * @param int $count Count * @return void */ protected function _markerError($file, $line, $marker, $count) { - $this->out(__d('cake_console', "Invalid marker content in %s:%s\n* %s(", $file, $line, $marker)); + $this->err(__d('cake_console', "Invalid marker content in %s:%s\n* %s(", $file, $line, $marker)); $count += 2; $tokenCount = count($this->_tokens); $parenthesis = 1; while ((($tokenCount - $count) > 0) && $parenthesis) { if (is_array($this->_tokens[$count])) { - $this->out($this->_tokens[$count][1], false); + $this->err($this->_tokens[$count][1], false); } else { - $this->out($this->_tokens[$count], false); + $this->err($this->_tokens[$count], false); if ($this->_tokens[$count] === '(') { $parenthesis++; } @@ -754,7 +791,7 @@ class ExtractTask extends AppShell { } $count++; } - $this->out("\n", true); + $this->err("\n", true); } /** @@ -774,26 +811,24 @@ class ExtractTask extends AppShell { } $pattern = '/' . implode('|', $exclude) . '/'; } - foreach ($this->_paths as $path) { - $Folder = new Folder($path); + foreach ($this->_paths as $i => $path) { + $this->_paths[$i] = realpath($path) . DS; + $Folder = new Folder($this->_paths[$i]); $files = $Folder->findRecursive('.*\.(php|ctp|thtml|inc|tpl)', true); if (!empty($pattern)) { - foreach ($files as $i => $file) { - if (preg_match($pattern, $file)) { - unset($files[$i]); - } - } + $files = preg_grep($pattern, $files, PREG_GREP_INVERT); $files = array_values($files); } $this->_files = array_merge($this->_files, $files); } + $this->_files = array_unique($this->_files); } /** * Returns whether this execution is meant to extract string only from directories in folder represented by the * APP constant, i.e. this task is extracting strings from same application. * - * @return boolean + * @return bool */ protected function _isExtractingApp() { return $this->_paths === array(APP); @@ -803,7 +838,7 @@ class ExtractTask extends AppShell { * Checks whether or not a given path is usable for writing. * * @param string $path Path to folder - * @return boolean true if it exists and is writable, false otherwise + * @return bool true if it exists and is writable, false otherwise */ protected function _isPathUsable($path) { return is_dir($path) && is_writable($path); diff --git a/web/api/lib/Cake/Console/Command/Task/FixtureTask.php b/web/api/lib/Cake/Console/Command/Task/FixtureTask.php index 0be6d36dd..1f578d083 100644 --- a/web/api/lib/Cake/Console/Command/Task/FixtureTask.php +++ b/web/api/lib/Cake/Console/Command/Task/FixtureTask.php @@ -74,7 +74,7 @@ class FixtureTask extends BakeTask { ))->addOption('count', array( 'help' => __d('cake_console', 'When using generated data, the number of records to include in the fixture(s).'), 'short' => 'n', - 'default' => 10 + 'default' => 1 ))->addOption('connection', array( 'help' => __d('cake_console', 'Which database configuration to use for baking.'), 'short' => 'c', @@ -210,7 +210,7 @@ class FixtureTask extends BakeTask { * @param string $model Name of model to bake. * @param string $useTable Name of table to use. * @param array $importOptions Options for public $import - * @return string Baked fixture content + * @return string|null Baked fixture content, otherwise null. */ public function bake($model, $useTable = false, $importOptions = array()) { App::uses('CakeSchema', 'Model'); @@ -242,8 +242,8 @@ class FixtureTask extends BakeTask { $this->_Schema = new CakeSchema(); $data = $this->_Schema->read(array('models' => false, 'connection' => $this->connection)); if (!isset($data['tables'][$useTable])) { - $this->error('Could not find your selected table ' . $useTable); - return false; + $this->err("Warning: Could not find the '${useTable}' table for ${model}."); + return null; } $tableInfo = $data['tables'][$useTable]; @@ -316,7 +316,7 @@ class FixtureTask extends BakeTask { * Generate String representation of Records * * @param array $tableInfo Table schema array - * @param integer $recordCount + * @param int $recordCount The number of records to generate. * @return array Array of records to use in the fixture. */ protected function _generateRecords($tableInfo, $recordCount = 1) { @@ -340,7 +340,7 @@ class FixtureTask extends BakeTask { isset($fieldInfo['length']) && $fieldInfo['length'] == 36 ); if ($isPrimaryUuid) { - $insert = String::uuid(); + $insert = CakeText::uuid(); } else { $insert = "Lorem ipsum dolor sit amet"; if (!empty($fieldInfo['length'])) { @@ -381,7 +381,7 @@ class FixtureTask extends BakeTask { } /** - * Convert a $records array into a a string. + * Convert a $records array into a string. * * @param array $records Array of records to be converted to string * @return string A string value of the $records array. @@ -414,19 +414,26 @@ class FixtureTask extends BakeTask { * @return array Array of records. */ protected function _getRecordsFromTable($modelName, $useTable = null) { + $modelObject = new Model(array('name' => $modelName, 'table' => $useTable, 'ds' => $this->connection)); if ($this->interactive) { $condition = null; $prompt = __d('cake_console', "Please provide a SQL fragment to use as conditions\nExample: WHERE 1=1"); while (!$condition) { $condition = $this->in($prompt, null, 'WHERE 1=1'); } + + $recordsFound = $modelObject->find('count', array( + 'conditions' => $condition, + 'recursive' => -1, + )); + $prompt = __d('cake_console', "How many records do you want to import?"); - $recordCount = $this->in($prompt, null, 10); + $recordCount = $this->in($prompt, null, ($recordsFound < 10 ) ? $recordsFound : 10); } else { $condition = 'WHERE 1=1'; $recordCount = (isset($this->params['count']) ? $this->params['count'] : 10); } - $modelObject = new Model(array('name' => $modelName, 'table' => $useTable, 'ds' => $this->connection)); + $records = $modelObject->find('all', array( 'conditions' => $condition, 'recursive' => -1, diff --git a/web/api/lib/Cake/Console/Command/Task/ModelTask.php b/web/api/lib/Cake/Console/Command/Task/ModelTask.php index 128674b5b..2dc03a5c3 100644 --- a/web/api/lib/Cake/Console/Command/Task/ModelTask.php +++ b/web/api/lib/Cake/Console/Command/Task/ModelTask.php @@ -161,8 +161,8 @@ class ModelTask extends BakeTask { * * @param array $options Array of options to use for the selections. indexes must start at 0 * @param string $prompt Prompt to use for options list. - * @param integer $default The default option for the given prompt. - * @return integer Result of user choice. + * @param int $default The default option for the given prompt. + * @return int Result of user choice. */ public function inOptions($options, $prompt = null, $default = null) { $valid = false; @@ -176,7 +176,7 @@ class ModelTask extends BakeTask { $prompt = __d('cake_console', 'Make a selection from the choices above'); } $choice = $this->in($prompt, null, $default); - if (intval($choice) > 0 && intval($choice) <= $max) { + if ((int)$choice > 0 && (int)$choice <= $max) { $valid = true; } } @@ -186,7 +186,7 @@ class ModelTask extends BakeTask { /** * Handles interactive baking * - * @return boolean + * @return bool */ protected function _interactive() { $this->hr(); @@ -342,7 +342,7 @@ class ModelTask extends BakeTask { * Handles Generation and user interaction for creating validation. * * @param Model $model Model to have validations generated for. - * @return array $validate Array of user selected validations. + * @return array validate Array of user selected validations. */ public function doValidation($model) { if (!$model instanceof Model) { @@ -383,6 +383,8 @@ class ModelTask extends BakeTask { if (class_exists('Validation')) { $options = get_class_methods('Validation'); } + $deprecatedOptions = array('notEmpty', 'between', 'ssn'); + $options = array_diff($options, $deprecatedOptions); sort($options); $default = 1; foreach ($options as $option) { @@ -401,7 +403,7 @@ class ModelTask extends BakeTask { * * @param string $fieldName Name of field to be validated. * @param array $metaData metadata for field - * @param string $primaryKey + * @param string $primaryKey The primary key field. * @return array Array of validation for the field. */ public function fieldValidation($fieldName, $metaData, $primaryKey = 'id') { @@ -443,9 +445,9 @@ class ModelTask extends BakeTask { } elseif ($metaData['type'] === 'string' && $metaData['length'] == 36) { $guess = $methods['uuid']; } elseif ($metaData['type'] === 'string') { - $guess = $methods['notEmpty']; + $guess = $methods['notBlank']; } elseif ($metaData['type'] === 'text') { - $guess = $methods['notEmpty']; + $guess = $methods['notBlank']; } elseif ($metaData['type'] === 'integer') { $guess = $methods['numeric']; } elseif ($metaData['type'] === 'float') { @@ -460,6 +462,8 @@ class ModelTask extends BakeTask { $guess = $methods['datetime']; } elseif ($metaData['type'] === 'inet') { $guess = $methods['ip']; + } elseif ($metaData['type'] === 'decimal') { + $guess = $methods['decimal']; } } @@ -510,7 +514,7 @@ class ModelTask extends BakeTask { /** * Handles associations * - * @param Model $model + * @param Model $model The model object * @return array Associations */ public function doAssociations($model) { @@ -562,7 +566,7 @@ class ModelTask extends BakeTask { /** * Handles behaviors * - * @param Model $model + * @param Model $model The model object. * @return array Behaviors */ public function doActsAs($model) { @@ -632,13 +636,13 @@ class ModelTask extends BakeTask { } foreach ($tempFieldNames as $fieldName) { $assoc = false; - if ($fieldName != $model->primaryKey && $fieldName == $foreignKey) { + if ($fieldName !== $model->primaryKey && $fieldName === $foreignKey) { $assoc = array( 'alias' => $tempOtherModel->name, 'className' => $tempOtherModel->name, 'foreignKey' => $fieldName ); - } elseif ($otherTable == $model->table && $fieldName === 'parent_id') { + } elseif ($otherTable === $model->table && $fieldName === 'parent_id') { $assoc = array( 'alias' => 'Child' . $model->name, 'className' => $model->name, @@ -728,7 +732,7 @@ class ModelTask extends BakeTask { while (strtolower($wannaDoMoreAssoc) === 'y') { $assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); $this->out(__d('cake_console', 'What is the association type?')); - $assocType = intval($this->inOptions($assocs, __d('cake_console', 'Enter a number'))); + $assocType = (int)$this->inOptions($assocs, __d('cake_console', 'Enter a number')); $this->out(__d('cake_console', "For the following options be very careful to match your setup exactly.\n" . "Any spelling mistakes will cause errors.")); @@ -765,7 +769,7 @@ class ModelTask extends BakeTask { if (!empty($showKeys)) { $this->out(__d('cake_console', 'A helpful List of possible keys')); $foreignKey = $this->inOptions($showKeys, __d('cake_console', 'What is the foreignKey?')); - $foreignKey = $showKeys[intval($foreignKey)]; + $foreignKey = $showKeys[(int)$foreignKey]; } if (!isset($foreignKey)) { $foreignKey = $this->in(__d('cake_console', 'What is the foreignKey? Specify your own.'), null, $suggestedForeignKey); @@ -812,7 +816,7 @@ class ModelTask extends BakeTask { * Assembles and writes a Model file. * * @param string|object $name Model name or object - * @param array|boolean $data if array and $name is not an object assume bake data, otherwise boolean. + * @param array|bool $data if array and $name is not an object assume bake data, otherwise boolean. * @return string */ public function bake($name, $data = array()) { @@ -926,7 +930,7 @@ class ModelTask extends BakeTask { $tableIsGood = $this->in(__d('cake_console', 'Do you want to use this table?'), array('y', 'n'), 'y'); } if (strtolower($tableIsGood) === 'n') { - $useTable = $this->in(__d('cake_console', 'What is the name of the table?')); + $useTable = $this->in(__d('cake_console', 'What is the name of the table (without prefix)?')); } } return $useTable; @@ -985,14 +989,14 @@ class ModelTask extends BakeTask { return $this->_stop(); } - if (!$enteredModel || intval($enteredModel) > count($this->_modelNames)) { + if (!$enteredModel || (int)$enteredModel > count($this->_modelNames)) { $this->err(__d('cake_console', "The model name you supplied was empty,\n" . "or the number you selected was not an option. Please try again.")); $enteredModel = ''; } } - if (intval($enteredModel) > 0 && intval($enteredModel) <= count($this->_modelNames)) { - return $this->_modelNames[intval($enteredModel) - 1]; + if ((int)$enteredModel > 0 && (int)$enteredModel <= count($this->_modelNames)) { + return $this->_modelNames[(int)$enteredModel - 1]; } return $enteredModel; diff --git a/web/api/lib/Cake/Console/Command/Task/PluginTask.php b/web/api/lib/Cake/Console/Command/Task/PluginTask.php index e37e437c3..7cd507942 100644 --- a/web/api/lib/Cake/Console/Command/Task/PluginTask.php +++ b/web/api/lib/Cake/Console/Command/Task/PluginTask.php @@ -1,7 +1,5 @@ path . $plugin); $directories = array( 'Config' . DS . 'Schema', - 'Model' . DS . 'Behavior', - 'Model' . DS . 'Datasource', 'Console' . DS . 'Command' . DS . 'Task', + 'Console' . DS . 'Templates', 'Controller' . DS . 'Component', 'Lib', - 'View' . DS . 'Helper', + 'Locale' . DS . 'eng' . DS . 'LC_MESSAGES', + 'Model' . DS . 'Behavior', + 'Model' . DS . 'Datasource', 'Test' . DS . 'Case' . DS . 'Controller' . DS . 'Component', - 'Test' . DS . 'Case' . DS . 'View' . DS . 'Helper', + 'Test' . DS . 'Case' . DS . 'Lib', 'Test' . DS . 'Case' . DS . 'Model' . DS . 'Behavior', + 'Test' . DS . 'Case' . DS . 'Model' . DS . 'Datasource', + 'Test' . DS . 'Case' . DS . 'View' . DS . 'Helper', 'Test' . DS . 'Fixture', - 'Vendor', - 'webroot' + 'View' . DS . 'Elements', + 'View' . DS . 'Helper', + 'View' . DS . 'Layouts', + 'webroot' . DS . 'css', + 'webroot' . DS . 'js', + 'webroot' . DS . 'img', ); foreach ($directories as $directory) { @@ -184,7 +189,7 @@ class PluginTask extends AppShell { /** * find and change $this->path to the user selection * - * @param array $pathOptions + * @param array $pathOptions The list of paths to look in. * @return void */ public function findPath($pathOptions) { @@ -203,7 +208,7 @@ class PluginTask extends AppShell { } $prompt = __d('cake_console', 'Choose a plugin path from the paths above.'); $choice = $this->in($prompt, null, 1); - if (intval($choice) > 0 && intval($choice) <= $max) { + if ((int)$choice > 0 && (int)$choice <= $max) { $valid = true; } } diff --git a/web/api/lib/Cake/Console/Command/Task/ProjectTask.php b/web/api/lib/Cake/Console/Command/Task/ProjectTask.php index c3225a99f..df1ab19db 100644 --- a/web/api/lib/Cake/Console/Command/Task/ProjectTask.php +++ b/web/api/lib/Cake/Console/Command/Task/ProjectTask.php @@ -18,7 +18,7 @@ App::uses('AppShell', 'Console/Command'); App::uses('File', 'Utility'); App::uses('Folder', 'Utility'); -App::uses('String', 'Utility'); +App::uses('CakeText', 'Utility'); App::uses('Security', 'Utility'); /** @@ -142,7 +142,7 @@ class ProjectTask extends AppShell { /** * Checks PHP's include_path for CakePHP. * - * @return boolean Indicates whether or not CakePHP exists on include_path + * @return bool Indicates whether or not CakePHP exists on include_path */ public function cakeOnIncludePath() { $paths = explode(PATH_SEPARATOR, ini_get('include_path')); @@ -212,7 +212,7 @@ class ProjectTask extends AppShell { } foreach ($Folder->messages() as $message) { - $this->out(String::wrap(' * ' . $message), 1, Shell::VERBOSE); + $this->out(CakeText::wrap(' * ' . $message), 1, Shell::VERBOSE); } return true; @@ -231,14 +231,14 @@ class ProjectTask extends AppShell { * and points app/console/cake.php to the right place * * @param string $path Project path. - * @return boolean success + * @return bool success */ public function consolePath($path) { $File = new File($path . 'Console' . DS . 'cake.php'); $contents = $File->read(); if (preg_match('/(__CAKE_PATH__)/', $contents, $match)) { - $root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " \$ds . '" : "'"; - $replacement = $root . str_replace(DS, "' . \$ds . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'"; + $root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'"; + $replacement = $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'"; $result = str_replace($match[0], $replacement, $contents); if ($File->write($result)) { return true; @@ -252,7 +252,7 @@ class ProjectTask extends AppShell { * Generates and writes 'Security.salt' * * @param string $path Project path - * @return boolean Success + * @return bool Success */ public function securitySalt($path) { $File = new File($path . 'Config' . DS . 'core.php'); @@ -272,7 +272,7 @@ class ProjectTask extends AppShell { * Generates and writes 'Security.cipherSeed' * * @param string $path Project path - * @return boolean Success + * @return bool Success */ public function securityCipherSeed($path) { $File = new File($path . 'Config' . DS . 'core.php'); @@ -293,7 +293,7 @@ class ProjectTask extends AppShell { * Writes cache prefix using app's name * * @param string $dir Path to project - * @return boolean Success + * @return bool Success */ public function cachePrefix($dir) { $app = basename($dir); @@ -310,8 +310,8 @@ class ProjectTask extends AppShell { * Generates and writes CAKE_CORE_INCLUDE_PATH * * @param string $path Project path - * @param boolean $hardCode Whether or not define calls should be hardcoded. - * @return boolean Success + * @param bool $hardCode Whether or not define calls should be hardcoded. + * @return bool Success */ public function corePath($path, $hardCode = true) { if (dirname($path) !== CAKE_CORE_INCLUDE_PATH) { @@ -331,8 +331,8 @@ class ProjectTask extends AppShell { * Replaces the __CAKE_PATH__ placeholder in the template files. * * @param string $filename The filename to operate on. - * @param boolean $hardCode Whether or not the define should be uncommented. - * @return boolean Success + * @param bool $hardCode Whether or not the define should be uncommented. + * @return bool Success */ protected function _replaceCorePath($filename, $hardCode) { $contents = file_get_contents($filename); @@ -340,6 +340,11 @@ class ProjectTask extends AppShell { $root = strpos(CAKE_CORE_INCLUDE_PATH, '/') === 0 ? " DS . '" : "'"; $corePath = $root . str_replace(DS, "' . DS . '", trim(CAKE_CORE_INCLUDE_PATH, DS)) . "'"; + $composer = ROOT . DS . APP_DIR . DS . 'Vendor' . DS . 'cakephp' . DS . 'cakephp' . DS . 'lib'; + if (file_exists($composer)) { + $corePath = " ROOT . DS . APP_DIR . DS . 'Vendor' . DS . 'cakephp' . DS . 'cakephp' . DS . 'lib'"; + } + $result = str_replace('__CAKE_PATH__', $corePath, $contents, $count); if ($hardCode) { $result = str_replace('//define(\'CAKE_CORE', 'define(\'CAKE_CORE', $result); @@ -354,7 +359,7 @@ class ProjectTask extends AppShell { * Enables Configure::read('Routing.prefixes') in /app/Config/core.php * * @param string $name Name to use as admin routing - * @return boolean Success + * @return bool Success */ public function cakeAdmin($name) { $path = (empty($this->configPath)) ? APP . 'Config' . DS : $this->configPath; diff --git a/web/api/lib/Cake/Console/Command/Task/TestTask.php b/web/api/lib/Cake/Console/Command/Task/TestTask.php index f85d57cd0..a147951c2 100644 --- a/web/api/lib/Cake/Console/Command/Task/TestTask.php +++ b/web/api/lib/Cake/Console/Command/Task/TestTask.php @@ -102,8 +102,8 @@ class TestTask extends BakeTask { /** * Handles interactive baking * - * @param string $type - * @return string|boolean + * @param string $type The type of object to bake a test for. + * @return string|bool */ protected function _interactive($type = null) { $this->interactive = true; @@ -129,7 +129,7 @@ class TestTask extends BakeTask { * * @param string $type Type of object to bake test case for ie. Model, Controller * @param string $className the 'cake name' for the class ie. Posts for the PostsController - * @return string|boolean + * @return string|bool */ public function bake($type, $className) { $plugin = null; @@ -242,7 +242,7 @@ class TestTask extends BakeTask { * Currently only model, and controller are supported * * @param string $type The Type of object you are generating tests for eg. controller - * @return boolean + * @return bool */ public function typeCanDetectFixtures($type) { $type = strtolower($type); @@ -254,7 +254,7 @@ class TestTask extends BakeTask { * * @param string $package The package of object you are generating tests for eg. controller * @param string $class the Classname of the class the test is being generated for. - * @return boolean + * @return bool */ public function isLoadableClass($package, $class) { App::uses($class, $package); @@ -302,7 +302,7 @@ class TestTask extends BakeTask { $position = strpos($class, $type); - if ($position !== false && strlen($class) - $position == strlen($type)) { + if ($position !== false && (strlen($class) - $position) === strlen($type)) { return $class; } return $class . $type; @@ -466,7 +466,7 @@ class TestTask extends BakeTask { * Controllers require a mock class. * * @param string $type The type of object tests are being generated for eg. controller. - * @return boolean + * @return bool */ public function hasMockClass($type) { $type = strtolower($type); diff --git a/web/api/lib/Cake/Console/Command/Task/ViewTask.php b/web/api/lib/Cake/Console/Command/Task/ViewTask.php index 8fc387aba..06ecf0013 100644 --- a/web/api/lib/Cake/Console/Command/Task/ViewTask.php +++ b/web/api/lib/Cake/Console/Command/Task/ViewTask.php @@ -89,7 +89,7 @@ class ViewTask extends BakeTask { $this->_interactive(); } if (empty($this->args[0])) { - return; + return null; } if (!isset($this->connection)) { $this->connection = 'default'; @@ -151,7 +151,7 @@ class ViewTask extends BakeTask { unset($methods[$i]); } } - if ($method[0] === '_' || $method == strtolower($this->controllerName . 'Controller')) { + if ($method[0] === '_' || $method === strtolower($this->controllerName . 'Controller')) { unset($methods[$i]); } } @@ -206,7 +206,7 @@ class ViewTask extends BakeTask { $this->Controller->connection = $this->connection; $this->controllerName = $this->Controller->getName(); - $prompt = __d('cake_console', "Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite %s views if it exist.", $this->controllerName); + $prompt = __d('cake_console', "Would you like bake to build your views interactively?\nWarning: Choosing no will overwrite %s views if they exist.", $this->controllerName); $interactive = $this->in($prompt, array('y', 'n'), 'n'); if (strtolower($interactive) === 'n') { @@ -248,7 +248,7 @@ class ViewTask extends BakeTask { * 'singularHumanName', 'pluralHumanName', 'fields', 'foreignKeys', * 'belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany' * - * @return array Returns an variables to be made available to a view template + * @return array Returns a variables to be made available to a view template */ protected function _loadController() { if (!$this->controllerName) { @@ -298,7 +298,7 @@ class ViewTask extends BakeTask { * Bake a view file for each of the supplied actions * * @param array $actions Array of actions to make files for. - * @param array $vars + * @param array $vars The template variables. * @return void */ public function bakeActions($actions, $vars) { @@ -342,7 +342,7 @@ class ViewTask extends BakeTask { * * @param string $action Action to bake * @param string $content Content to write - * @return boolean Success + * @return bool Success */ public function bake($action, $content = '') { if ($content === true) { @@ -454,8 +454,8 @@ class ViewTask extends BakeTask { /** * Returns associations for controllers models. * - * @param Model $model - * @return array $associations + * @param Model $model The Model instance. + * @return array associations */ protected function _associations(Model $model) { $keys = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'); diff --git a/web/api/lib/Cake/Console/Command/TestShell.php b/web/api/lib/Cake/Console/Command/TestShell.php index bd7e9d277..4dfacfdfc 100644 --- a/web/api/lib/Cake/Console/Command/TestShell.php +++ b/web/api/lib/Cake/Console/Command/TestShell.php @@ -179,11 +179,11 @@ class TestShell extends Shell { /** * Parse the CLI options into an array CakeTestDispatcher can use. * - * @return array Array of params for CakeTestDispatcher + * @return array|null Array of params for CakeTestDispatcher or null. */ protected function _parseArgs() { if (empty($this->args)) { - return; + return null; } $params = array( 'core' => false, @@ -222,6 +222,7 @@ class TestShell extends Shell { $options = array(); $params = $this->params; unset($params['help']); + unset($params['quiet']); if (!empty($params['no-colors'])) { unset($params['no-colors'], $params['colors']); @@ -334,9 +335,9 @@ class TestShell extends Shell { /** * Find the test case for the passed file. The file could itself be a test. * - * @param string $file - * @param string $category - * @param boolean $throwOnMissingFile + * @param string $file The file to map. + * @param string $category The test file category. + * @param bool $throwOnMissingFile Whether or not to throw an exception. * @return array array(type, case) * @throws Exception */ @@ -411,7 +412,7 @@ class TestShell extends Shell { /** * For the given file, what category of test is it? returns app, core or the name of the plugin * - * @param string $file + * @param string $file The file to map. * @return string */ protected function _mapFileToCategory($file) { diff --git a/web/api/lib/Cake/Console/Command/UpgradeShell.php b/web/api/lib/Cake/Console/Command/UpgradeShell.php index 27d7561fe..12b3fbc9d 100644 --- a/web/api/lib/Cake/Console/Command/UpgradeShell.php +++ b/web/api/lib/Cake/Console/Command/UpgradeShell.php @@ -18,6 +18,7 @@ App::uses('AppShell', 'Console/Command'); App::uses('Folder', 'Utility'); +App::uses('CakePlugin', 'Core'); /** * A shell class to help developers upgrade applications to CakePHP 2.0 @@ -102,7 +103,7 @@ class UpgradeShell extends AppShell { public function tests() { $this->_paths = array(APP . 'tests' . DS); if (!empty($this->params['plugin'])) { - $this->_paths = array(App::pluginPath($this->params['plugin']) . 'tests' . DS); + $this->_paths = array(CakePlugin::path($this->params['plugin']) . 'tests' . DS); } $patterns = array( array( @@ -128,7 +129,7 @@ class UpgradeShell extends AppShell { $cwd = getcwd(); if (!empty($this->params['plugin'])) { - chdir(App::pluginPath($this->params['plugin'])); + chdir(CakePlugin::path($this->params['plugin'])); } if (is_dir('plugins')) { @@ -215,7 +216,7 @@ class UpgradeShell extends AppShell { $this->_paths = array_diff(App::path('views'), App::core('views')); if (!empty($this->params['plugin'])) { - $this->_paths = array(App::pluginPath($this->params['plugin']) . 'views' . DS); + $this->_paths = array(CakePlugin::path($this->params['plugin']) . 'views' . DS); } $patterns = array(); @@ -229,7 +230,7 @@ class UpgradeShell extends AppShell { CakePlugin::load($plugin); $pluginHelpers = array_merge( $pluginHelpers, - App::objects('helper', App::pluginPath($plugin) . DS . 'views' . DS . 'helpers' . DS, false) + App::objects('helper', CakePlugin::path($plugin) . DS . 'views' . DS . 'helpers' . DS, false) ); } $helpers = array_merge($pluginHelpers, $helpers); @@ -260,7 +261,7 @@ class UpgradeShell extends AppShell { APP ); if (!empty($this->params['plugin'])) { - $this->_paths = array(App::pluginPath($this->params['plugin'])); + $this->_paths = array(CakePlugin::path($this->params['plugin'])); } $patterns = array( @@ -299,7 +300,7 @@ class UpgradeShell extends AppShell { APP ); if (!empty($this->params['plugin'])) { - $this->_paths = array(App::pluginPath($this->params['plugin'])); + $this->_paths = array(CakePlugin::path($this->params['plugin'])); } $patterns = array( array( @@ -354,7 +355,7 @@ class UpgradeShell extends AppShell { $this->_paths = array_merge($views, $controllers, $components); if (!empty($this->params['plugin'])) { - $pluginPath = App::pluginPath($this->params['plugin']); + $pluginPath = CakePlugin::path($this->params['plugin']); $this->_paths = array( $pluginPath . 'controllers' . DS, $pluginPath . 'controllers' . DS . 'components' . DS, @@ -411,7 +412,7 @@ class UpgradeShell extends AppShell { APP ); if (!empty($this->params['plugin'])) { - $this->_paths = array(App::pluginPath($this->params['plugin'])); + $this->_paths = array(CakePlugin::path($this->params['plugin'])); } $patterns = array( array( @@ -433,7 +434,7 @@ class UpgradeShell extends AppShell { APP ); if (!empty($this->params['plugin'])) { - $this->_paths = array(App::pluginPath($this->params['plugin'])); + $this->_paths = array(CakePlugin::path($this->params['plugin'])); } $patterns = array( array( @@ -554,6 +555,7 @@ class UpgradeShell extends AppShell { /** * Replace cakeError with built-in exceptions. * NOTE: this ignores calls where you've passed your own secondary parameters to cakeError(). + * * @return void */ public function exceptions() { @@ -563,7 +565,7 @@ class UpgradeShell extends AppShell { $this->_paths = array_merge($controllers, $components); if (!empty($this->params['plugin'])) { - $pluginPath = App::pluginPath($this->params['plugin']); + $pluginPath = CakePlugin::path($this->params['plugin']); $this->_paths = array( $pluginPath . 'controllers' . DS, $pluginPath . 'controllers' . DS . 'components' . DS, @@ -609,7 +611,7 @@ class UpgradeShell extends AppShell { $new = 'View' . DS . Inflector::camelize($old); $old = 'View' . DS . $old; - if ($new == $old) { + if ($new === $old) { continue; } @@ -661,7 +663,7 @@ class UpgradeShell extends AppShell { * Find all php files in the folder (honoring recursive) and determine where CakePHP expects the file to be * If the file is not exactly where CakePHP expects it - move it. * - * @param string $path + * @param string $path The path to move files in. * @param array $options array(recursive, checkFolder) * @return void */ @@ -763,7 +765,7 @@ class UpgradeShell extends AppShell { /** * Searches the paths and finds files based on extension. * - * @param string $extensions + * @param string $extensions The extensions to include. Defaults to none. * @return void */ protected function _findFiles($extensions = '') { @@ -839,8 +841,9 @@ class UpgradeShell extends AppShell { ); $parser->description( - __d('cake_console', "A shell to help automate upgrading from CakePHP 1.3 to 2.0. \n" . - "Be sure to have a backup of your application before running these commands." + __d('cake_console', "A tool to help automate upgrading an application or plugin " . + "from CakePHP 1.3 to 2.0. Be sure to have a backup of your application before " . + "running these commands." ))->addSubcommand('all', array( 'help' => __d('cake_console', 'Run all upgrade commands.'), 'parser' => $subcommandParser diff --git a/web/api/lib/Cake/Console/ConsoleErrorHandler.php b/web/api/lib/Cake/Console/ConsoleErrorHandler.php index aa5c464f9..7cbb2df15 100644 --- a/web/api/lib/Cake/Console/ConsoleErrorHandler.php +++ b/web/api/lib/Cake/Console/ConsoleErrorHandler.php @@ -40,35 +40,37 @@ class ConsoleErrorHandler { * @return ConsoleOutput */ public static function getStderr() { - if (empty(self::$stderr)) { - self::$stderr = new ConsoleOutput('php://stderr'); + if (empty(static::$stderr)) { + static::$stderr = new ConsoleOutput('php://stderr'); } - return self::$stderr; + return static::$stderr; } /** - * Handle a exception in the console environment. Prints a message to stderr. + * Handle an exception in the console environment. Prints a message to stderr. * - * @param Exception $exception The exception to handle + * @param Exception|ParserError $exception The exception to handle * @return void */ - public function handleException(Exception $exception) { - $stderr = self::getStderr(); + public function handleException($exception) { + $stderr = static::getStderr(); $stderr->write(__d('cake_console', "Error: %s\n%s", $exception->getMessage(), $exception->getTraceAsString() )); - return $this->_stop($exception->getCode() ? $exception->getCode() : 1); + $code = $exception->getCode(); + $code = ($code && is_int($code)) ? $code : 1; + return $this->_stop($code); } /** * Handle errors in the console environment. Writes errors to stderr, * and logs messages if Configure::read('debug') is 0. * - * @param integer $code Error code + * @param int $code Error code * @param string $description Description of the error. * @param string $file The file the error occurred in. - * @param integer $line The line the error occurred on. + * @param int $line The line the error occurred on. * @param array $context The backtrace of the error. * @return void */ @@ -76,7 +78,7 @@ class ConsoleErrorHandler { if (error_reporting() === 0) { return; } - $stderr = self::getStderr(); + $stderr = static::getStderr(); list($name, $log) = ErrorHandler::mapErrorCode($code); $message = __d('cake_console', '%s in [%s, line %s]', $description, $file, $line); $stderr->write(__d('cake_console', "%s Error: %s\n", $name, $message)); @@ -93,7 +95,7 @@ class ConsoleErrorHandler { /** * Wrapper for exit(), used for testing. * - * @param integer $code The exit code. + * @param int $code The exit code. * @return void */ protected function _stop($code = 0) { diff --git a/web/api/lib/Cake/Console/ConsoleInput.php b/web/api/lib/Cake/Console/ConsoleInput.php index e8af58bb0..551635b82 100644 --- a/web/api/lib/Cake/Console/ConsoleInput.php +++ b/web/api/lib/Cake/Console/ConsoleInput.php @@ -37,7 +37,7 @@ class ConsoleInput { * 2. Handle we are attached to must be stdin. * Allows rich editing with arrow keys and history when inputting a string. * - * @var boolean + * @var bool */ protected $_canReadline; @@ -47,7 +47,7 @@ class ConsoleInput { * @param string $handle The location of the stream to use as input. */ public function __construct($handle = 'php://stdin') { - $this->_canReadline = extension_loaded('readline') && $handle == 'php://stdin' ? true : false; + $this->_canReadline = extension_loaded('readline') && $handle === 'php://stdin' ? true : false; $this->_input = fopen($handle, 'r'); } @@ -70,8 +70,8 @@ class ConsoleInput { /** * Checks if data is available on the stream * - * @param integer $timeout An optional time to wait for data - * @return boolean True for data available, false otherwise + * @param int $timeout An optional time to wait for data + * @return bool True for data available, false otherwise */ public function dataAvailable($timeout = 0) { $readFds = array($this->_input); diff --git a/web/api/lib/Cake/Console/ConsoleInputArgument.php b/web/api/lib/Cake/Console/ConsoleInputArgument.php index 7380893bf..08dbc21e9 100644 --- a/web/api/lib/Cake/Console/ConsoleInputArgument.php +++ b/web/api/lib/Cake/Console/ConsoleInputArgument.php @@ -41,7 +41,7 @@ class ConsoleInputArgument { /** * Is this option required? * - * @var boolean + * @var bool */ protected $_required; @@ -57,7 +57,7 @@ class ConsoleInputArgument { * * @param string|array $name The long name of the option, or an array with all the properties. * @param string $help The help text for this option - * @param boolean $required Whether this argument is required. Missing required args will trigger exceptions + * @param bool $required Whether this argument is required. Missing required args will trigger exceptions * @param array $choices Valid choices for this option. */ public function __construct($name, $help = '', $required = false, $choices = array()) { @@ -85,7 +85,7 @@ class ConsoleInputArgument { /** * Generate the help for this argument. * - * @param integer $width The width to make the name of the option. + * @param int $width The width to make the name of the option. * @return string */ public function help($width = 0) { @@ -123,7 +123,7 @@ class ConsoleInputArgument { /** * Check if this argument is a required argument * - * @return boolean + * @return bool */ public function isRequired() { return (bool)$this->_required; @@ -132,8 +132,8 @@ class ConsoleInputArgument { /** * Check that $value is a valid choice for this argument. * - * @param string $value - * @return boolean + * @param string $value The choice to validate. + * @return bool * @throws ConsoleException */ public function validChoice($value) { diff --git a/web/api/lib/Cake/Console/ConsoleInputOption.php b/web/api/lib/Cake/Console/ConsoleInputOption.php index 4d68c9f9e..3a4b7a1c9 100644 --- a/web/api/lib/Cake/Console/ConsoleInputOption.php +++ b/web/api/lib/Cake/Console/ConsoleInputOption.php @@ -48,7 +48,7 @@ class ConsoleInputOption { /** * Is the option a boolean option. Boolean options do not consume a parameter. * - * @var boolean + * @var bool */ protected $_boolean; @@ -72,7 +72,7 @@ class ConsoleInputOption { * @param string|array $name The long name of the option, or an array with all the properties. * @param string $short The short alias for this option * @param string $help The help text for this option - * @param boolean $boolean Whether this option is a boolean option. Boolean options don't consume extra tokens + * @param bool $boolean Whether this option is a boolean option. Boolean options don't consume extra tokens * @param string $default The default value for this option. * @param array $choices Valid choices for this option. * @throws ConsoleException @@ -118,7 +118,7 @@ class ConsoleInputOption { /** * Generate the help for this this option. * - * @param integer $width The width to make the name of the option. + * @param int $width The width to make the name of the option. * @return string */ public function help($width = 0) { @@ -168,7 +168,7 @@ class ConsoleInputOption { /** * Check if this option is a boolean option * - * @return boolean + * @return bool */ public function isBoolean() { return (bool)$this->_boolean; @@ -177,8 +177,8 @@ class ConsoleInputOption { /** * Check that a value is a valid choice for this option. * - * @param string $value - * @return boolean + * @param string $value The choice to validate. + * @return bool * @throws ConsoleException */ public function validChoice($value) { diff --git a/web/api/lib/Cake/Console/ConsoleInputSubcommand.php b/web/api/lib/Cake/Console/ConsoleInputSubcommand.php index 2ddcb955b..65c10db06 100644 --- a/web/api/lib/Cake/Console/ConsoleInputSubcommand.php +++ b/web/api/lib/Cake/Console/ConsoleInputSubcommand.php @@ -81,7 +81,7 @@ class ConsoleInputSubcommand { /** * Generate the help for this this subcommand. * - * @param integer $width The width to make the name of the subcommand. + * @param int $width The width to make the name of the subcommand. * @return string */ public function help($width = 0) { diff --git a/web/api/lib/Cake/Console/ConsoleOptionParser.php b/web/api/lib/Cake/Console/ConsoleOptionParser.php index 589c27949..f54c5460d 100644 --- a/web/api/lib/Cake/Console/ConsoleOptionParser.php +++ b/web/api/lib/Cake/Console/ConsoleOptionParser.php @@ -136,7 +136,7 @@ class ConsoleOptionParser { * Construct an OptionParser so you can define its behavior * * @param string $command The command name this parser is for. The command name is used for generating help. - * @param boolean $defaultOptions Whether you want the verbose and quiet options set. Setting + * @param bool $defaultOptions Whether you want the verbose and quiet options set. Setting * this to false will prevent the addition of `--verbose` & `--quiet` options. */ public function __construct($command = null, $defaultOptions = true) { @@ -165,7 +165,7 @@ class ConsoleOptionParser { * Static factory method for creating new OptionParsers so you can chain methods off of them. * * @param string $command The command name this parser is for. The command name is used for generating help. - * @param boolean $defaultOptions Whether you want the verbose and quiet options set. + * @param bool $defaultOptions Whether you want the verbose and quiet options set. * @return ConsoleOptionParser */ public static function create($command, $defaultOptions = true) { @@ -175,7 +175,7 @@ class ConsoleOptionParser { /** * Build a parser from an array. Uses an array like * - * {{{ + * ``` * $spec = array( * 'description' => 'text', * 'epilog' => 'text', @@ -189,7 +189,7 @@ class ConsoleOptionParser { * // list of subcommands to add. * ) * ); - * }}} + * ``` * * @param array $spec The spec to build the OptionParser with. * @return ConsoleOptionParser @@ -218,7 +218,7 @@ class ConsoleOptionParser { * Get or set the command name for shell/task. * * @param string $text The text to set, or null if you want to read - * @return mixed If reading, the value of the command. If setting $this will be returned + * @return string|self If reading, the value of the command. If setting $this will be returned. */ public function command($text = null) { if ($text !== null) { @@ -233,7 +233,7 @@ class ConsoleOptionParser { * * @param string|array $text The text to set, or null if you want to read. If an array the * text will be imploded with "\n" - * @return mixed If reading, the value of the description. If setting $this will be returned + * @return string|self If reading, the value of the description. If setting $this will be returned. */ public function description($text = null) { if ($text !== null) { @@ -251,7 +251,7 @@ class ConsoleOptionParser { * the options and arguments listing when help is generated. * * @param string|array $text Text when setting or null when reading. If an array the text will be imploded with "\n" - * @return mixed If reading, the value of the epilog. If setting $this will be returned. + * @return string|self If reading, the value of the epilog. If setting $this will be returned. */ public function epilog($text = null) { if ($text !== null) { @@ -284,7 +284,7 @@ class ConsoleOptionParser { * @param ConsoleInputOption|string $name The long name you want to the value to be parsed out as when options are parsed. * Will also accept an instance of ConsoleInputOption * @param array $options An array of parameters that define the behavior of the option - * @return ConsoleOptionParser $this. + * @return self */ public function addOption($name, $options = array()) { if (is_object($name) && $name instanceof ConsoleInputOption) { @@ -324,7 +324,7 @@ class ConsoleOptionParser { * * @param ConsoleInputArgument|string $name The name of the argument. Will also accept an instance of ConsoleInputArgument * @param array $params Parameters for the argument, see above. - * @return ConsoleOptionParser $this. + * @return self */ public function addArgument($name, $params = array()) { if (is_object($name) && $name instanceof ConsoleInputArgument) { @@ -354,7 +354,7 @@ class ConsoleOptionParser { * * @param array $args Array of arguments to add. * @see ConsoleOptionParser::addArgument() - * @return ConsoleOptionParser $this + * @return self */ public function addArguments(array $args) { foreach ($args as $name => $params) { @@ -369,7 +369,7 @@ class ConsoleOptionParser { * * @param array $options Array of options to add. * @see ConsoleOptionParser::addOption() - * @return ConsoleOptionParser $this + * @return self */ public function addOptions(array $options) { foreach ($options as $name => $params) { @@ -391,7 +391,7 @@ class ConsoleOptionParser { * * @param ConsoleInputSubcommand|string $name Name of the subcommand. Will also accept an instance of ConsoleInputSubcommand * @param array $options Array of params, see above. - * @return ConsoleOptionParser $this. + * @return self */ public function addSubcommand($name, $options = array()) { if (is_object($name) && $name instanceof ConsoleInputSubcommand) { @@ -410,11 +410,22 @@ class ConsoleOptionParser { return $this; } +/** + * Remove a subcommand from the option parser. + * + * @param string $name The subcommand name to remove. + * @return self + */ + public function removeSubcommand($name) { + unset($this->_subcommands[$name]); + return $this; + } + /** * Add multiple subcommands at once. * * @param array $commands Array of subcommands. - * @return ConsoleOptionParser $this + * @return self */ public function addSubcommands(array $commands) { foreach ($commands as $name => $params) { @@ -458,7 +469,7 @@ class ConsoleOptionParser { * @param array $argv Array of args (argv) to parse. * @param string $command The subcommand to use. If this parameter is a subcommand, that has a parser, * That parser will be used to parse $argv instead. - * @return Array array($params, $args) + * @return array array($params, $args) * @throws ConsoleException When an invalid parameter is encountered. */ public function parse($argv, $command = null) { @@ -506,12 +517,11 @@ class ConsoleOptionParser { * @param string $subcommand If present and a valid subcommand that has a linked parser. * That subcommands help will be shown instead. * @param string $format Define the output format, can be text or xml - * @param integer $width The width to format user content to. Defaults to 72 + * @param int $width The width to format user content to. Defaults to 72 * @return string Generated help. */ public function help($subcommand = null, $format = 'text', $width = 72) { - if ( - isset($this->_subcommands[$subcommand]) && + if (isset($this->_subcommands[$subcommand]) && $this->_subcommands[$subcommand]->parser() instanceof self ) { $subparser = $this->_subcommands[$subcommand]->parser(); @@ -597,13 +607,14 @@ class ConsoleOptionParser { $params[$name] = $value; return $params; } + return array(); } /** * Check to see if $name has an option (short/long) defined for it. * * @param string $name The name of the option. - * @return boolean + * @return bool */ protected function _optionExists($name) { if (substr($name, 0, 2) === '--') { diff --git a/web/api/lib/Cake/Console/ConsoleOutput.php b/web/api/lib/Cake/Console/ConsoleOutput.php index fa021b565..ec6fb3941 100644 --- a/web/api/lib/Cake/Console/ConsoleOutput.php +++ b/web/api/lib/Cake/Console/ConsoleOutput.php @@ -47,21 +47,21 @@ class ConsoleOutput { /** * Raw output constant - no modification of output text. * - * @var integer + * @var int */ const RAW = 0; /** * Plain output - tags will be stripped. * - * @var integer + * @var int */ const PLAIN = 1; /** * Color output - Convert known tags in to ANSI color escape codes. * - * @var integer + * @var int */ const COLOR = 2; @@ -79,10 +79,18 @@ class ConsoleOutput { */ protected $_output; +/** + * The number of bytes last written to the output stream + * used when overwriting the previous message. + * + * @var int + */ + protected $_lastWritten = 0; + /** * The current output type. Manipulated with ConsoleOutput::outputAs(); * - * @var integer + * @var int */ protected $_outputAs = self::COLOR; @@ -153,16 +161,19 @@ class ConsoleOutput { /** * Construct the output object. * - * Checks for a pretty console environment. Ansicon allows pretty consoles - * on windows, and is supported. + * Checks for a pretty console environment. Ansicon and ConEmu allows + * pretty consoles on Windows, and is supported. * * @param string $stream The identifier of the stream to write output to. */ public function __construct($stream = 'php://stdout') { $this->_output = fopen($stream, 'w'); - if (DS === '\\' && !(bool)env('ANSICON')) { - $this->_outputAs = self::PLAIN; + if ((DS === '\\' && !(bool)env('ANSICON') && env('ConEmuANSI') !== 'ON') || + $stream === 'php://output' || + (function_exists('posix_isatty') && !posix_isatty($this->_output)) + ) { + $this->_outputAs = static::PLAIN; } } @@ -170,15 +181,44 @@ class ConsoleOutput { * Outputs a single or multiple messages to stdout. If no parameters * are passed, outputs just a newline. * - * @param string|array $message A string or a an array of strings to output - * @param integer $newlines Number of newlines to append - * @return integer Returns the number of bytes returned from writing to stdout. + * @param string|array $message A string or an array of strings to output + * @param int $newlines Number of newlines to append + * @return int Returns the number of bytes returned from writing to stdout. */ public function write($message, $newlines = 1) { if (is_array($message)) { - $message = implode(self::LF, $message); + $message = implode(static::LF, $message); + } + return $this->_write($this->styleText($message . str_repeat(static::LF, $newlines))); + } + +/** + * Overwrite some already output text. + * + * Useful for building progress bars, or when you want to replace + * text already output to the screen with new text. + * + * **Warning** You cannot overwrite text that contains newlines. + * + * @param array|string $message The message to output. + * @param int $newlines Number of newlines to append. + * @param int|null $size The number of bytes to overwrite. Defaults to the + * length of the last message output. + * @return void + */ + public function overwrite($message, $newlines = 1, $size = null) { + $size = $size ?: $this->_lastWritten; + // Output backspaces. + $this->write(str_repeat("\x08", $size), 0); + $newBytes = $this->write($message, 0); + // Fill any remaining bytes with spaces. + $fill = $size - $newBytes; + if ($fill > 0) { + $this->write(str_repeat(' ', $fill), 0); + } + if ($newlines) { + $this->write("", $newlines); } - return $this->_write($this->styleText($message . str_repeat(self::LF, $newlines))); } /** @@ -188,11 +228,11 @@ class ConsoleOutput { * @return string String with color codes added. */ public function styleText($text) { - if ($this->_outputAs == self::RAW) { + if ($this->_outputAs == static::RAW) { return $text; } - if ($this->_outputAs == self::PLAIN) { - $tags = implode('|', array_keys(self::$_styles)); + if ($this->_outputAs == static::PLAIN) { + $tags = implode('|', array_keys(static::$_styles)); return preg_replace('##', '', $text); } return preg_replace_callback( @@ -203,7 +243,7 @@ class ConsoleOutput { /** * Replace tags with color codes. * - * @param array $matches. + * @param array $matches An array of matches to replace. * @return string */ protected function _replaceTags($matches) { @@ -213,16 +253,16 @@ class ConsoleOutput { } $styleInfo = array(); - if (!empty($style['text']) && isset(self::$_foregroundColors[$style['text']])) { - $styleInfo[] = self::$_foregroundColors[$style['text']]; + if (!empty($style['text']) && isset(static::$_foregroundColors[$style['text']])) { + $styleInfo[] = static::$_foregroundColors[$style['text']]; } - if (!empty($style['background']) && isset(self::$_backgroundColors[$style['background']])) { - $styleInfo[] = self::$_backgroundColors[$style['background']]; + if (!empty($style['background']) && isset(static::$_backgroundColors[$style['background']])) { + $styleInfo[] = static::$_backgroundColors[$style['background']]; } unset($style['text'], $style['background']); foreach ($style as $option => $value) { if ($value) { - $styleInfo[] = self::$_options[$option]; + $styleInfo[] = static::$_options[$option]; } } return "\033[" . implode($styleInfo, ';') . 'm' . $matches['text'] . "\033[0m"; @@ -232,10 +272,11 @@ class ConsoleOutput { * Writes a message to the output stream. * * @param string $message Message to write. - * @return boolean success + * @return bool success */ protected function _write($message) { - return fwrite($this->_output, $message); + $this->_lastWritten = fwrite($this->_output, $message); + return $this->_lastWritten; } /** @@ -265,23 +306,23 @@ class ConsoleOutput { */ public function styles($style = null, $definition = null) { if ($style === null && $definition === null) { - return self::$_styles; + return static::$_styles; } if (is_string($style) && $definition === null) { - return isset(self::$_styles[$style]) ? self::$_styles[$style] : null; + return isset(static::$_styles[$style]) ? static::$_styles[$style] : null; } if ($definition === false) { - unset(self::$_styles[$style]); + unset(static::$_styles[$style]); return true; } - self::$_styles[$style] = $definition; + static::$_styles[$style] = $definition; return true; } /** * Get/Set the output type to use. The output type how formatting tags are treated. * - * @param integer $type The output type to use. Should be one of the class constants. + * @param int $type The output type to use. Should be one of the class constants. * @return mixed Either null or the value if getting. */ public function outputAs($type = null) { @@ -295,7 +336,9 @@ class ConsoleOutput { * Clean up and close handles */ public function __destruct() { - fclose($this->_output); + if (is_resource($this->_output)) { + fclose($this->_output); + } } } diff --git a/web/api/lib/Cake/Console/HelpFormatter.php b/web/api/lib/Cake/Console/HelpFormatter.php index c3b40c354..3a8fac98f 100644 --- a/web/api/lib/Cake/Console/HelpFormatter.php +++ b/web/api/lib/Cake/Console/HelpFormatter.php @@ -14,7 +14,7 @@ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -App::uses('String', 'Utility'); +App::uses('CakeText', 'Utility'); /** * HelpFormatter formats help for console shells. Can format to either @@ -33,19 +33,19 @@ class HelpFormatter { /** * The maximum number of arguments shown when generating usage. * - * @var integer + * @var int */ protected $_maxArgs = 6; /** * The maximum number of options shown when generating usage. * - * @var integer + * @var int */ protected $_maxOptions = 6; /** - * Build the help formatter for a an OptionParser + * Build the help formatter for an OptionParser * * @param ConsoleOptionParser $parser The option parser help is being generated for. */ @@ -56,7 +56,7 @@ class HelpFormatter { /** * Get the help as formatted text suitable for output on the command line. * - * @param integer $width The width of the help output. + * @param int $width The width of the help output. * @return string */ public function text($width = 72) { @@ -64,7 +64,7 @@ class HelpFormatter { $out = array(); $description = $parser->description(); if (!empty($description)) { - $out[] = String::wrap($description, $width); + $out[] = CakeText::wrap($description, $width); $out[] = ''; } $out[] = __d('cake_console', 'Usage:'); @@ -76,7 +76,7 @@ class HelpFormatter { $out[] = ''; $max = $this->_getMaxLength($subcommands) + 2; foreach ($subcommands as $command) { - $out[] = String::wrap($command->help($max), array( + $out[] = CakeText::wrap($command->help($max), array( 'width' => $width, 'indent' => str_repeat(' ', $max), 'indentAt' => 1 @@ -93,7 +93,7 @@ class HelpFormatter { $out[] = __d('cake_console', 'Options:'); $out[] = ''; foreach ($options as $option) { - $out[] = String::wrap($option->help($max), array( + $out[] = CakeText::wrap($option->help($max), array( 'width' => $width, 'indent' => str_repeat(' ', $max), 'indentAt' => 1 @@ -108,7 +108,7 @@ class HelpFormatter { $out[] = __d('cake_console', 'Arguments:'); $out[] = ''; foreach ($arguments as $argument) { - $out[] = String::wrap($argument->help($max), array( + $out[] = CakeText::wrap($argument->help($max), array( 'width' => $width, 'indent' => str_repeat(' ', $max), 'indentAt' => 1 @@ -118,7 +118,7 @@ class HelpFormatter { } $epilog = $parser->epilog(); if (!empty($epilog)) { - $out[] = String::wrap($epilog, $width); + $out[] = CakeText::wrap($epilog, $width); $out[] = ''; } return implode("\n", $out); @@ -159,8 +159,8 @@ class HelpFormatter { /** * Iterate over a collection and find the longest named thing. * - * @param array $collection - * @return integer + * @param array $collection The collection to find a max length of. + * @return int */ protected function _getMaxLength($collection) { $max = 0; @@ -173,7 +173,7 @@ class HelpFormatter { /** * Get the help as an xml string. * - * @param boolean $string Return the SimpleXml object or a string. Defaults to true. + * @param bool $string Return the SimpleXml object or a string. Defaults to true. * @return string|SimpleXmlElement See $string */ public function xml($string = true) { diff --git a/web/api/lib/Cake/Console/Helper/BaseShellHelper.php b/web/api/lib/Cake/Console/Helper/BaseShellHelper.php new file mode 100644 index 000000000..f06a17aac --- /dev/null +++ b/web/api/lib/Cake/Console/Helper/BaseShellHelper.php @@ -0,0 +1,82 @@ +_consoleOutput = $consoleOutput; + $this->config($config); + } + +/** + * Initialize config & store config values + * + * @param null $config Config values to set + * @return array|void + */ + public function config($config = null) { + if ($config === null) { + return $this->_config; + } + if (!$this->_configInitialized) { + $this->_config = array_merge($this->_defaultConfig, $config); + $this->_configInitialized = true; + } else { + $this->_config = array_merge($this->_config, $config); + } + } + +/** + * This method should output content using `$this->_consoleOutput`. + * + * @param array $args The arguments for the helper. + * @return void + */ + abstract public function output($args); +} \ No newline at end of file diff --git a/web/api/lib/Cake/Console/Helper/ProgressShellHelper.php b/web/api/lib/Cake/Console/Helper/ProgressShellHelper.php new file mode 100644 index 000000000..03b216963 --- /dev/null +++ b/web/api/lib/Cake/Console/Helper/ProgressShellHelper.php @@ -0,0 +1,122 @@ + null); + if (isset($args[0])) { + $args['callback'] = $args[0]; + } + if (!$args['callback'] || !is_callable($args['callback'])) { + throw new RuntimeException('Callback option must be a callable.'); + } + $this->init($args); + $callback = $args['callback']; + while ($this->_progress < $this->_total) { + $callback($this); + $this->draw(); + } + $this->_consoleOutput->write(''); + } + +/** + * Initialize the progress bar for use. + * + * - `total` The total number of items in the progress bar. Defaults + * to 100. + * - `width` The width of the progress bar. Defaults to 80. + * + * @param array $args The initialization data. + * @return void + */ + public function init(array $args = array()) { + $args += array('total' => 100, 'width' => 80); + $this->_progress = 0; + $this->_width = $args['width']; + $this->_total = $args['total']; + } + +/** + * Increment the progress bar. + * + * @param int $num The amount of progress to advance by. + * @return void + */ + public function increment($num = 1) { + $this->_progress = min(max(0, $this->_progress + $num), $this->_total); + } + +/** + * Render the progress bar based on the current state. + * + * @return void + */ + public function draw() { + $numberLen = strlen(' 100%'); + $complete = round($this->_progress / $this->_total, 2); + $barLen = ($this->_width - $numberLen) * ($this->_progress / $this->_total); + $bar = ''; + if ($barLen > 1) { + $bar = str_repeat('=', $barLen - 1) . '>'; + } + $pad = ceil($this->_width - $numberLen - $barLen); + if ($pad > 0) { + $bar .= str_repeat(' ', $pad); + } + $percent = ($complete * 100) . '%'; + $bar .= str_pad($percent, $numberLen, ' ', STR_PAD_LEFT); + $this->_consoleOutput->overwrite($bar, 0); + } +} \ No newline at end of file diff --git a/web/api/lib/Cake/Console/Helper/TableShellHelper.php b/web/api/lib/Cake/Console/Helper/TableShellHelper.php new file mode 100644 index 000000000..7cf6c799a --- /dev/null +++ b/web/api/lib/Cake/Console/Helper/TableShellHelper.php @@ -0,0 +1,124 @@ + true, + 'rowSeparator' => false, + 'headerStyle' => 'info', + ); + +/** + * Calculate the column widths + * + * @param array $rows The rows on which the columns width will be calculated on. + * @return array + */ + protected function _calculateWidths($rows) { + $widths = array(); + foreach ($rows as $line) { + for ($i = 0, $len = count($line); $i < $len; $i++) { + $columnLength = mb_strlen($line[$i]); + if ($columnLength > (isset($widths[$i]) ? $widths[$i] : 0)) { + $widths[$i] = $columnLength; + } + } + } + return $widths; + } + +/** + * Output a row separator. + * + * @param array $widths The widths of each column to output. + * @return void + */ + protected function _rowSeparator($widths) { + $out = ''; + foreach ($widths as $column) { + $out .= '+' . str_repeat('-', $column + 2); + } + $out .= '+'; + $this->_consoleOutput->write($out); + } + +/** + * Output a row. + * + * @param array $row The row to output. + * @param array $widths The widths of each column to output. + * @param array $options Options to be passed. + * @return void + */ + protected function _render($row, $widths, $options = array()) { + $out = ''; + foreach ($row as $i => $column) { + $pad = $widths[$i] - mb_strlen($column); + if (!empty($options['style'])) { + $column = $this->_addStyle($column, $options['style']); + } + $out .= '| ' . $column . str_repeat(' ', $pad) . ' '; + } + $out .= '|'; + $this->_consoleOutput->write($out); + } + +/** + * Output a table. + * + * @param array $rows The data to render out. + * @return void + */ + public function output($rows) { + $config = $this->config(); + $widths = $this->_calculateWidths($rows); + $this->_rowSeparator($widths); + if ($config['headers'] === true) { + $this->_render(array_shift($rows), $widths, array('style' => $config['headerStyle'])); + $this->_rowSeparator($widths); + } + foreach ($rows as $line) { + $this->_render($line, $widths); + if ($config['rowSeparator'] === true) { + $this->_rowSeparator($widths); + } + } + if ($config['rowSeparator'] !== true) { + $this->_rowSeparator($widths); + } + } + +/** + * Add style tags + * + * @param string $text The text to be surrounded + * @param string $style The style to be applied + * @return string + */ + protected function _addStyle($text, $style) { + return '<' . $style . '>' . $text . ''; + } +} \ No newline at end of file diff --git a/web/api/lib/Cake/Console/Shell.php b/web/api/lib/Cake/Console/Shell.php index b6a6cbcf2..c446b9cd3 100644 --- a/web/api/lib/Cake/Console/Shell.php +++ b/web/api/lib/Cake/Console/Shell.php @@ -22,7 +22,6 @@ App::uses('ConsoleInputSubcommand', 'Console'); App::uses('ConsoleOptionParser', 'Console'); App::uses('ClassRegistry', 'Utility'); App::uses('File', 'Utility'); -App::uses('ClassRegistry', 'Utility'); /** * Base class for command-line utilities for automating programmer chores. @@ -31,24 +30,31 @@ App::uses('ClassRegistry', 'Utility'); */ class Shell extends Object { +/** + * Default error code + * + * @var int + */ + const CODE_ERROR = 1; + /** * Output constant making verbose shells. * - * @var integer + * @var int */ const VERBOSE = 2; /** * Output constant for making normal shells. * - * @var integer + * @var int */ const NORMAL = 1; /** * Output constants for making quiet shells. * - * @var integer + * @var int */ const QUIET = 0; @@ -62,7 +68,7 @@ class Shell extends Object { /** * If true, the script will ask for permission to perform actions. * - * @var boolean + * @var bool */ public $interactive = true; @@ -167,6 +173,21 @@ class Shell extends Object { */ public $stdin; +/** + * The number of bytes last written to the output stream + * used when overwriting the previous message. + * + * @var int + */ + protected $_lastWritten = 0; + +/** + * Contains helpers which have been previously instantiated + * + * @var array + */ + protected $_helpers = array(); + /** * Constructs this Shell instance. * @@ -239,7 +260,7 @@ class Shell extends Object { /** * If $uses is an array load each of the models in the array * - * @return boolean + * @return bool */ protected function _loadModels() { if (is_array($this->uses)) { @@ -254,7 +275,7 @@ class Shell extends Object { /** * Lazy loads models using the loadModel() method if declared in $uses * - * @param string $name + * @param string $name The name of the model to look for. * @return void */ public function __isset($name) { @@ -303,7 +324,7 @@ class Shell extends Object { /** * Loads tasks defined in public $tasks * - * @return boolean + * @return bool */ public function loadTasks() { if ($this->tasks === true || empty($this->tasks) || empty($this->Tasks)) { @@ -318,7 +339,7 @@ class Shell extends Object { * Check to see if this shell has a task with the provided name. * * @param string $task The task name to check. - * @return boolean Success + * @return bool Success * @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::hasTask */ public function hasTask($task) { @@ -329,7 +350,7 @@ class Shell extends Object { * Check to see if this shell has a callable method by the given name. * * @param string $name The method name to check. - * @return boolean + * @return bool * @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::hasMethod */ public function hasMethod($name) { @@ -446,7 +467,7 @@ class Shell extends Object { /** * Display the help in the correct format * - * @param string $command + * @param string $command The command to get help for. * @return void */ protected function _displayHelp($command) { @@ -477,7 +498,7 @@ class Shell extends Object { /** * Overload get for lazy building of tasks * - * @param string $name + * @param string $name The property name to access. * @return Shell Object of Task */ public function __get($name) { @@ -492,6 +513,19 @@ class Shell extends Object { return $this->{$name}; } +/** + * Safely access the values in $this->params. + * + * @param string $name The name of the parameter to get. + * @return string|bool|null Value. Will return null if it doesn't exist. + */ + public function param($name) { + if (!isset($this->params[$name])) { + return null; + } + return $this->params[$name]; + } + /** * Prompts the user for input, and returns it. * @@ -553,7 +587,8 @@ class Shell extends Object { $result = $this->stdin->read(); if ($result === false) { - return $this->_stop(1); + $this->_stop(self::CODE_ERROR); + return self::CODE_ERROR; } $result = trim($result); @@ -574,13 +609,13 @@ class Shell extends Object { * - `indent` Indent the text with the string provided. Defaults to null. * * @param string $text Text the text to format. - * @param string|integer|array $options Array of options to use, or an integer to wrap the text to. + * @param string|int|array $options Array of options to use, or an integer to wrap the text to. * @return string Wrapped / indented text - * @see String::wrap() + * @see CakeText::wrap() * @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::wrapText */ public function wrapText($text, $options = array()) { - return String::wrap($text, $options); + return CakeText::wrap($text, $options); } /** @@ -594,10 +629,10 @@ class Shell extends Object { * present in most shells. Using Shell::QUIET for a message means it will always display. * While using Shell::VERBOSE means it will only display when verbose output is toggled. * - * @param string|array $message A string or a an array of strings to output - * @param integer $newlines Number of newlines to append - * @param integer $level The message's output level, see above. - * @return integer|boolean Returns the number of bytes returned from writing to stdout. + * @param string|array $message A string or an array of strings to output + * @param int $newlines Number of newlines to append + * @param int $level The message's output level, see above. + * @return int|bool Returns the number of bytes returned from writing to stdout. * @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::out */ public function out($message = null, $newlines = 1, $level = Shell::NORMAL) { @@ -609,17 +644,49 @@ class Shell extends Object { $currentLevel = Shell::QUIET; } if ($level <= $currentLevel) { - return $this->stdout->write($message, $newlines); + $this->_lastWritten = $this->stdout->write($message, $newlines); + return $this->_lastWritten; } return true; } +/** + * Overwrite some already output text. + * + * Useful for building progress bars, or when you want to replace + * text already output to the screen with new text. + * + * **Warning** You cannot overwrite text that contains newlines. + * + * @param array|string $message The message to output. + * @param int $newlines Number of newlines to append. + * @param int $size The number of bytes to overwrite. Defaults to the length of the last message output. + * @return int|bool Returns the number of bytes returned from writing to stdout. + */ + public function overwrite($message, $newlines = 1, $size = null) { + $size = $size ? $size : $this->_lastWritten; + + // Output backspaces. + $this->out(str_repeat("\x08", $size), 0); + + $newBytes = $this->out($message, 0); + + // Fill any remaining bytes with spaces. + $fill = $size - $newBytes; + if ($fill > 0) { + $this->out(str_repeat(' ', $fill), 0); + } + if ($newlines) { + $this->out($this->nl($newlines), 0); + } + } + /** * Outputs a single or multiple error messages to stderr. If no parameters * are passed outputs just a newline. * - * @param string|array $message A string or a an array of strings to output - * @param integer $newlines Number of newlines to append + * @param string|array $message A string or an array of strings to output + * @param int $newlines Number of newlines to append * @return void * @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::err */ @@ -630,7 +697,7 @@ class Shell extends Object { /** * Returns a single or multiple linefeeds sequences. * - * @param integer $multiplier Number of times the linefeed sequence should be repeated + * @param int $multiplier Number of times the linefeed sequence should be repeated * @return string * @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::nl */ @@ -641,8 +708,8 @@ class Shell extends Object { /** * Outputs a series of minus characters to the standard output, acts as a visual separator. * - * @param integer $newlines Number of newlines to pre- and append - * @param integer $width Width of the line, defaults to 63 + * @param int $newlines Number of newlines to pre- and append + * @param int $width Width of the line, defaults to 63 * @return void * @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::hr */ @@ -667,7 +734,8 @@ class Shell extends Object { if (!empty($message)) { $this->err($message); } - return $this->_stop(1); + $this->_stop(self::CODE_ERROR); + return self::CODE_ERROR; } /** @@ -691,7 +759,7 @@ class Shell extends Object { * * @param string $path Where to put the file. * @param string $contents Content to put in the file. - * @return boolean Success + * @return bool Success * @link http://book.cakephp.org/2.0/en/console-and-shells.html#Shell::createFile */ public function createFile($path, $contents) { @@ -705,7 +773,8 @@ class Shell extends Object { if (strtolower($key) === 'q') { $this->out(__d('cake_console', 'Quitting.'), 2); - return $this->_stop(); + $this->_stop(); + return true; } elseif (strtolower($key) !== 'y') { $this->out(__d('cake_console', 'Skip `%s`', $path), 2); return false; @@ -726,10 +795,32 @@ class Shell extends Object { return false; } +/** + * Load given shell helper class + * + * @param string $name Name of the helper class. Supports plugin syntax. + * @return BaseShellHelper Instance of helper class + * @throws RuntimeException If invalid class name is provided + */ + public function helper($name) { + if (isset($this->_helpers[$name])) { + return $this->_helpers[$name]; + } + list($plugin, $helperClassName) = pluginSplit($name, true); + $helperClassName = Inflector::camelize($name) . "ShellHelper"; + App::uses($helperClassName, $plugin . "Console/Helper"); + if (!class_exists($helperClassName)) { + throw new RuntimeException("Class " . $helperClassName . " not found"); + } + $helper = new $helperClassName($this->stdout); + $this->_helpers[$name] = $helper; + return $helper; + } + /** * Action to create a Unit Test * - * @return boolean Success + * @return bool Success */ protected function _checkUnitTest() { if (class_exists('PHPUnit_Framework_TestCase')) { @@ -819,8 +910,8 @@ class Shell extends Object { /** * creates the singular name for use in views. * - * @param string $name - * @return string $name + * @param string $name The plural underscored value. + * @return string name */ protected function _singularName($name) { return Inflector::variable(Inflector::singularize($name)); @@ -860,7 +951,7 @@ class Shell extends Object { * Find the correct path for a plugin. Scans $pluginPaths for the plugin you want. * * @param string $pluginName Name of the plugin you want ie. DebugKit - * @return string $path path to the correct plugin. + * @return string path path to the correct plugin. */ protected function _pluginPath($pluginName) { if (CakePlugin::loaded($pluginName)) { @@ -874,7 +965,7 @@ class Shell extends Object { * If you don't wish to see in your stdout or stderr everything that is logged * through CakeLog, call this function with first param as false * - * @param boolean $enable whether to enable CakeLog output or not + * @param bool $enable whether to enable CakeLog output or not * @return void */ protected function _useLogger($enable = true) { diff --git a/web/api/lib/Cake/Console/ShellDispatcher.php b/web/api/lib/Cake/Console/ShellDispatcher.php index b284fcbe9..d53851127 100644 --- a/web/api/lib/Cake/Console/ShellDispatcher.php +++ b/web/api/lib/Cake/Console/ShellDispatcher.php @@ -43,7 +43,7 @@ class ShellDispatcher { * a status code of either 0 or 1 according to the result of the dispatch. * * @param array $args the argv from PHP - * @param boolean $bootstrap Should the environment be bootstrapped. + * @param bool $bootstrap Should the environment be bootstrapped. */ public function __construct($args = array(), $bootstrap = true) { set_time_limit(0); @@ -79,9 +79,11 @@ class ShellDispatcher { } if (!defined('CAKE_CORE_INCLUDE_PATH')) { - define('DS', DIRECTORY_SEPARATOR); define('CAKE_CORE_INCLUDE_PATH', dirname(dirname(dirname(__FILE__)))); define('CAKEPHP_SHELL', true); + if (!defined('DS')) { + define('DS', DIRECTORY_SEPARATOR); + } if (!defined('CORE_PATH')) { define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); } @@ -114,7 +116,7 @@ class ShellDispatcher { /** * Initializes the environment and loads the CakePHP core. * - * @return boolean Success. + * @return bool Success. */ protected function _bootstrap() { if (!defined('ROOT')) { @@ -175,12 +177,15 @@ class ShellDispatcher { } set_exception_handler($exception['consoleHandler']); set_error_handler($error['consoleHandler'], Configure::read('Error.level')); + + App::uses('Debugger', 'Utility'); + Debugger::getInstance()->output('txt'); } /** * Dispatches a CLI request * - * @return boolean + * @return bool * @throws MissingShellMethodException */ public function dispatch() { @@ -244,6 +249,11 @@ class ShellDispatcher { App::uses('AppShell', 'Console/Command'); App::uses($class, $plugin . 'Console/Command'); + if (!class_exists($class)) { + $plugin = Inflector::camelize($shell) . '.'; + App::uses($class, $plugin . 'Console/Command'); + } + if (!class_exists($class)) { throw new MissingShellException(array( 'class' => $class @@ -317,12 +327,13 @@ class ShellDispatcher { /** * Parses out the paths from from the argv * - * @param array $args + * @param array $args The argv to parse. * @return void */ protected function _parsePaths($args) { $parsed = array(); $keys = array('-working', '--working', '-app', '--app', '-root', '--root'); + $args = (array)$args; foreach ($keys as $key) { while (($index = array_search($key, $args)) !== false) { $keyname = str_replace('-', '', $key); @@ -357,7 +368,7 @@ class ShellDispatcher { /** * Stop execution of the current script * - * @param integer|string $status see http://php.net/exit for values + * @param int|string $status see http://php.net/exit for values * @return void */ protected function _stop($status = 0) { diff --git a/web/api/lib/Cake/Console/TaskCollection.php b/web/api/lib/Cake/Console/TaskCollection.php index ff1ddee67..3fafddd13 100644 --- a/web/api/lib/Cake/Console/TaskCollection.php +++ b/web/api/lib/Cake/Console/TaskCollection.php @@ -43,7 +43,7 @@ class TaskCollection extends ObjectCollection { /** * Constructor * - * @param Shell $Shell + * @param Shell $Shell The shell this task collection is attached to. */ public function __construct(Shell $Shell) { $this->_Shell = $Shell; @@ -53,13 +53,13 @@ class TaskCollection extends ObjectCollection { * Loads/constructs a task. Will return the instance in the registry if it already exists. * * You can alias your task as an existing task by setting the 'className' key, i.e., - * {{{ + * ``` * public $tasks = array( * 'DbConfig' => array( * 'className' => 'Bakeplus.DbConfigure' * ); * ); - * }}} + * ``` * All calls to the `DbConfig` task would use `DbConfigure` found in the `Bakeplus` plugin instead. * * @param string $task Task name to load diff --git a/web/api/lib/Cake/Console/Templates/default/actions/controller_actions.ctp b/web/api/lib/Cake/Console/Templates/default/actions/controller_actions.ctp index b6b89a993..dfc309215 100644 --- a/web/api/lib/Cake/Console/Templates/default/actions/controller_actions.ctp +++ b/web/api/lib/Cake/Console/Templates/default/actions/controller_actions.ctp @@ -53,10 +53,10 @@ $this->->create(); if ($this->->save($this->request->data)) { - $this->Session->setFlash(__('The has been saved.')); + $this->Flash->success(__('The has been saved.')); return $this->redirect(array('action' => 'index')); } else { - $this->Session->setFlash(__('The could not be saved. Please, try again.')); + $this->Flash->error(__('The could not be saved. Please, try again.')); return $this->flash(__('The has been saved.'), array('action' => 'index')); @@ -94,10 +94,10 @@ if ($this->request->is(array('post', 'put'))) { if ($this->->save($this->request->data)) { - $this->Session->setFlash(__('The has been saved.')); + $this->Flash->success(__('The has been saved.')); return $this->redirect(array('action' => 'index')); } else { - $this->Session->setFlash(__('The could not be saved. Please, try again.')); + $this->Flash->error(__('The could not be saved. Please, try again.')); return $this->flash(__('The has been saved.'), array('action' => 'index')); @@ -138,9 +138,9 @@ $this->request->allowMethod('post', 'delete'); if ($this->->delete()) { - $this->Session->setFlash(__('The has been deleted.')); + $this->Flash->success(__('The has been deleted.')); } else { - $this->Session->setFlash(__('The could not be deleted. Please, try again.')); + $this->Flash->error(__('The could not be deleted. Please, try again.')); } return $this->redirect(array('action' => 'index')); diff --git a/web/api/lib/Cake/Console/Templates/default/classes/controller.ctp b/web/api/lib/Cake/Console/Templates/default/classes/controller.ctp index e290a8675..6beb4b401 100644 --- a/web/api/lib/Cake/Console/Templates/default/classes/controller.ctp +++ b/web/api/lib/Cake/Console/Templates/default/classes/controller.ctp @@ -23,10 +23,10 @@ echo "App::uses('{$plugin}AppController', '{$pluginPath}Controller');\n"; ?> /** * Controller - * Controller extends App echo ");\n\n"; endif; - echo trim($actions); + if (!empty($actions)) { + echo trim($actions) . "\n"; + } endif; ?> } diff --git a/web/api/lib/Cake/Console/Templates/default/classes/fixture.ctp b/web/api/lib/Cake/Console/Templates/default/classes/fixture.ctp index 0fb23e24f..7ac2c3987 100644 --- a/web/api/lib/Cake/Console/Templates/default/classes/fixture.ctp +++ b/web/api/lib/Cake/Console/Templates/default/classes/fixture.ctp @@ -21,8 +21,7 @@ echo " /** - * Fixture - * + * Fixture */ class Fixture extends CakeTestFixture { diff --git a/web/api/lib/Cake/Console/Templates/default/classes/model.ctp b/web/api/lib/Cake/Console/Templates/default/classes/model.ctp index 0014e004e..6c3e64763 100644 --- a/web/api/lib/Cake/Console/Templates/default/classes/model.ctp +++ b/web/api/lib/Cake/Console/Templates/default/classes/model.ctp @@ -106,7 +106,7 @@ foreach ($associations as $assoc): if (!empty($assoc)): ?> - //The Associations below have been created with all possible keys, those that are not needed can be removed + // The Associations below have been created with all possible keys, those that are not needed can be removed ', ''); /** * Test Case - * */ class Test extends ControllerTestCase { diff --git a/web/api/lib/Cake/Console/Templates/default/views/form.ctp b/web/api/lib/Cake/Console/Templates/default/views/form.ctp index fa9c58315..4a21aabe3 100644 --- a/web/api/lib/Cake/Console/Templates/default/views/form.ctp +++ b/web/api/lib/Cake/Console/Templates/default/views/form.ctp @@ -1,7 +1,5 @@ Form->input('{$field}');\n"; @@ -46,7 +44,7 @@
    -
  • Form->postLink(__('Delete'), array('action' => 'delete', \$this->Form->value('{$modelClass}.{$primaryKey}')), null, __('Are you sure you want to delete # %s?', \$this->Form->value('{$modelClass}.{$primaryKey}'))); ?>"; ?>
  • +
  • Form->postLink(__('Delete'), array('action' => 'delete', \$this->Form->value('{$modelClass}.{$primaryKey}')), array('confirm' => __('Are you sure you want to delete # %s?', \$this->Form->value('{$modelClass}.{$primaryKey}')))); ?>"; ?>
  • Html->link(__('List " . $pluralHumanName . "'), array('action' => 'index')); ?>"; ?>
  • index">

    "; ?>

    + + + \n"; echo "\t\n"; @@ -47,17 +48,18 @@ echo "\t\t\n"; echo "\t\n"; echo "\n"; ?> +
    Paginator->sort('{$field}'); ?>"; ?> "; ?>
    \n"; echo "\t\t\tHtml->link(__('View'), array('action' => 'view', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n"; echo "\t\t\tHtml->link(__('Edit'), array('action' => 'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n"; - echo "\t\t\tForm->postLink(__('Delete'), array('action' => 'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), array(), __('Are you sure you want to delete # %s?', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>\n"; + echo "\t\t\tForm->postLink(__('Delete'), array('action' => 'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), array('confirm' => __('Are you sure you want to delete # %s?', \${$singularVar}['{$modelClass}']['{$primaryKey}']))); ?>\n"; echo "\t\t

    Paginator->counter(array( - 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') )); ?>"; ?>

    diff --git a/web/api/lib/Cake/Console/Templates/default/views/view.ctp b/web/api/lib/Cake/Console/Templates/default/views/view.ctp index d5b21acc4..3acfad8be 100644 --- a/web/api/lib/Cake/Console/Templates/default/views/view.ctp +++ b/web/api/lib/Cake/Console/Templates/default/views/view.ctp @@ -1,7 +1,5 @@ Html->link(__('Edit " . $singularHumanName ."'), array('action' => 'edit', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?> \n"; - echo "\t\t
  • Form->postLink(__('Delete " . $singularHumanName . "'), array('action' => 'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), null, __('Are you sure you want to delete # %s?', \${$singularVar}['{$modelClass}']['{$primaryKey}'])); ?>
  • \n"; + echo "\t\t
  • Form->postLink(__('Delete " . $singularHumanName . "'), array('action' => 'delete', \${$singularVar}['{$modelClass}']['{$primaryKey}']), array('confirm' => __('Are you sure you want to delete # %s?', \${$singularVar}['{$modelClass}']['{$primaryKey}']))); ?>
  • \n"; echo "\t\t
  • Html->link(__('List " . $pluralHumanName . "'), array('action' => 'index')); ?>
  • \n"; echo "\t\t
  • Html->link(__('New " . $singularHumanName . "'), array('action' => 'add')); ?>
  • \n"; @@ -119,7 +117,7 @@ echo "\t\n"; echo "\t\t\t\tHtml->link(__('View'), array('controller' => '{$details['controller']}', 'action' => 'view', \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n"; echo "\t\t\t\tHtml->link(__('Edit'), array('controller' => '{$details['controller']}', 'action' => 'edit', \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n"; - echo "\t\t\t\tForm->postLink(__('Delete'), array('controller' => '{$details['controller']}', 'action' => 'delete', \${$otherSingularVar}['{$details['primaryKey']}']), null, __('Are you sure you want to delete # %s?', \${$otherSingularVar}['{$details['primaryKey']}'])); ?>\n"; + echo "\t\t\t\tForm->postLink(__('Delete'), array('controller' => '{$details['controller']}', 'action' => 'delete', \${$otherSingularVar}['{$details['primaryKey']}']), array('confirm' => __('Are you sure you want to delete # %s?', \${$otherSingularVar}['{$details['primaryKey']}']))); ?>\n"; echo "\t\t\t\n"; echo "\t\t\n"; @@ -133,4 +131,6 @@ echo "\t\n";
- + diff --git a/web/api/lib/Cake/Console/Templates/skel/.htaccess b/web/api/lib/Cake/Console/Templates/skel/.htaccess index fc3aac4b2..128e7871b 100644 --- a/web/api/lib/Cake/Console/Templates/skel/.htaccess +++ b/web/api/lib/Cake/Console/Templates/skel/.htaccess @@ -1,5 +1,5 @@ - RewriteEngine on - RewriteRule ^$ webroot/ [L] - RewriteRule (.*) webroot/$1 [L] + RewriteEngine on + RewriteRule ^$ webroot/ [L] + RewriteRule (.*) webroot/$1 [L] \ No newline at end of file diff --git a/web/api/lib/Cake/Console/Templates/skel/Config/Schema/db_acl.php b/web/api/lib/Cake/Console/Templates/skel/Config/Schema/db_acl.php index b532fe370..9a4f54337 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Config/Schema/db_acl.php +++ b/web/api/lib/Cake/Console/Templates/skel/Config/Schema/db_acl.php @@ -9,21 +9,34 @@ * @since CakePHP(tm) v 0.2.9 */ -/* - * +/** * Using the Schema command line utility * cake schema run create DbAcl - * */ class DbAclSchema extends CakeSchema { +/** + * Before event. + * + * @param array $event The event data. + * @return bool success + */ public function before($event = array()) { return true; } +/** + * After event. + * + * @param array $event The event data. + * @return void + */ public function after($event = array()) { } +/** + * ACO - Access Control Object - Something that is wanted + */ public $acos = array( 'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 10, 'key' => 'primary'), 'parent_id' => array('type' => 'integer', 'null' => true, 'default' => null, 'length' => 10), @@ -35,6 +48,9 @@ class DbAclSchema extends CakeSchema { 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) ); +/** + * ARO - Access Request Object - Something that wants something + */ public $aros = array( 'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 10, 'key' => 'primary'), 'parent_id' => array('type' => 'integer', 'null' => true, 'default' => null, 'length' => 10), @@ -46,6 +62,10 @@ class DbAclSchema extends CakeSchema { 'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1)) ); +/** + * Used by the Cake::Model:Permission class. + * Checks if the given $aro has access to action $action in $aco. + */ public $aros_acos = array( 'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 10, 'key' => 'primary'), 'aro_id' => array('type' => 'integer', 'null' => false, 'length' => 10, 'key' => 'index'), diff --git a/web/api/lib/Cake/Console/Templates/skel/Config/Schema/db_acl.sql b/web/api/lib/Cake/Console/Templates/skel/Config/Schema/db_acl.sql index 274780e26..0bf3f7687 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Config/Schema/db_acl.sql +++ b/web/api/lib/Cake/Console/Templates/skel/Config/Schema/db_acl.sql @@ -38,4 +38,15 @@ CREATE TABLE aros ( lft INTEGER(10) DEFAULT NULL, rght INTEGER(10) DEFAULT NULL, PRIMARY KEY (id) -); \ No newline at end of file +); + +/* this indexes will improve acl perfomance */ +CREATE INDEX idx_acos_lft_rght ON `acos` (`lft`, `rght`); + +CREATE INDEX idx_acos_alias ON `acos` (`alias`); + +CREATE INDEX idx_aros_lft_rght ON `aros` (`lft`, `rght`); + +CREATE INDEX idx_aros_alias ON `aros` (`alias`); + +CREATE INDEX idx_aco_id ON `aros_acos` (`aco_id`); diff --git a/web/api/lib/Cake/Console/Templates/skel/Config/Schema/i18n.php b/web/api/lib/Cake/Console/Templates/skel/Config/Schema/i18n.php index d09922e83..cd598b35e 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Config/Schema/i18n.php +++ b/web/api/lib/Cake/Console/Templates/skel/Config/Schema/i18n.php @@ -4,13 +4,21 @@ * * Use it to configure database for i18n * + * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) + * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) + * + * Licensed under The MIT License + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @package app.Config.Schema * @since CakePHP(tm) v 0.2.9 + * @license http://www.opensource.org/licenses/mit-license.php MIT License */ /** - * * Using the Schema command line utility * * Use it to configure database for i18n @@ -19,15 +27,37 @@ */ class I18nSchema extends CakeSchema { +/** + * The name property + * + * @var string + */ public $name = 'i18n'; +/** + * Before callback. + * + * @param array $event Schema object properties + * @return bool Should process continue + */ public function before($event = array()) { return true; } +/** + * After callback. + * + * @param array $event Schema object properties + * @return void + */ public function after($event = array()) { } +/** + * The i18n table definition + * + * @var array + */ public $i18n = array( 'id' => array('type' => 'integer', 'null' => false, 'default' => null, 'length' => 10, 'key' => 'primary'), 'locale' => array('type' => 'string', 'null' => false, 'length' => 6, 'key' => 'index'), diff --git a/web/api/lib/Cake/Console/Templates/skel/Config/Schema/sessions.php b/web/api/lib/Cake/Console/Templates/skel/Config/Schema/sessions.php index 078f5d531..bd7b1efc3 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Config/Schema/sessions.php +++ b/web/api/lib/Cake/Console/Templates/skel/Config/Schema/sessions.php @@ -4,28 +4,57 @@ * * Use it to configure database for Sessions * + * CakePHP(tm) : Rapid Development Framework (http://cakephp.org) + * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) + * + * Licensed under The MIT License + * For full copyright and license information, please see the LICENSE.txt + * Redistributions of files must retain the above copyright notice. + * + * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) * @link http://cakephp.org CakePHP(tm) Project * @package app.Config.Schema * @since CakePHP(tm) v 0.2.9 + * @license http://www.opensource.org/licenses/mit-license.php MIT License */ -/* - * +/** * Using the Schema command line utility * cake schema run create Sessions - * */ class SessionsSchema extends CakeSchema { +/** + * Name property + * + * @var string + */ public $name = 'Sessions'; +/** + * Before callback. + * + * @param array $event Schema object properties + * @return bool Should process continue + */ public function before($event = array()) { return true; } +/** + * After callback. + * + * @param array $event Schema object properties + * @return void + */ public function after($event = array()) { } +/** + * The cake_sessions table definition + * + * @var array + */ public $cake_sessions = array( 'id' => array('type' => 'string', 'null' => false, 'key' => 'primary'), 'data' => array('type' => 'text', 'null' => true, 'default' => null), diff --git a/web/api/lib/Cake/Console/Templates/skel/Config/bootstrap.php b/web/api/lib/Cake/Console/Templates/skel/Config/bootstrap.php index 7fe55949c..0cf81bec3 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Config/bootstrap.php +++ b/web/api/lib/Cake/Console/Templates/skel/Config/bootstrap.php @@ -39,7 +39,6 @@ Cache::config('default', array('engine' => 'File')); * 'Vendor' => array('/path/to/vendors/', '/next/path/to/vendors/'), * 'Plugin' => array('/path/to/plugins/', '/next/path/to/plugins/'), * )); - * */ /** @@ -48,7 +47,6 @@ Cache::config('default', array('engine' => 'File')); * * Inflector::rules('singular', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); * Inflector::rules('plural', array('rules' => array(), 'irregular' => array(), 'uninflected' => array())); - * */ /** @@ -57,8 +55,7 @@ Cache::config('default', array('engine' => 'File')); * advanced ways of loading plugins * * CakePlugin::loadAll(); // Loads all plugins at once - * CakePlugin::load('DebugKit'); //Loads a single plugin named DebugKit - * + * CakePlugin::load('DebugKit'); // Loads a single plugin named DebugKit */ /** diff --git a/web/api/lib/Cake/Console/Templates/skel/Config/core.php b/web/api/lib/Cake/Console/Templates/skel/Config/core.php index 957152d1e..18d645a33 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Config/core.php +++ b/web/api/lib/Cake/Console/Templates/skel/Config/core.php @@ -138,13 +138,11 @@ * Enables: * `admin_index()` and `/admin/controller/index` * `manager_index()` and `/manager/controller/index` - * */ //Configure::write('Routing.prefixes', array('admin')); /** * Turn off all caching application-wide. - * */ //Configure::write('Cache.disable', true); @@ -155,7 +153,6 @@ * public $cacheAction inside your controllers to define caching settings. * You can either set it controller-wide by setting public $cacheAction = true, * or in each action using $this->cacheAction = true. - * */ //Configure::write('Cache.check', true); @@ -204,7 +201,6 @@ * * To use database sessions, run the app/Config/Schema/sessions.php schema using * the cake shell command: cake schema create Sessions - * */ Configure::write('Session', array( 'defaults' => 'php' @@ -261,7 +257,6 @@ //date_default_timezone_set('UTC'); /** - * * Cache Engine Configuration * Default settings provided below * diff --git a/web/api/lib/Cake/Console/Templates/skel/Config/database.php.default b/web/api/lib/Cake/Console/Templates/skel/Config/database.php.default index 00ad15507..cc549a5f6 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Config/database.php.default +++ b/web/api/lib/Cake/Console/Templates/skel/Config/database.php.default @@ -1,7 +1,5 @@ * The origin email. See CakeEmail::from() about the valid values - * */ class EmailConfig { diff --git a/web/api/lib/Cake/Console/Templates/skel/Console/cake.php b/web/api/lib/Cake/Console/Templates/skel/Console/cake.php index e3afb675c..f5b262a2a 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Console/cake.php +++ b/web/api/lib/Cake/Console/Templates/skel/Console/cake.php @@ -16,20 +16,33 @@ * @since CakePHP(tm) v 2.0 */ -$ds = DIRECTORY_SEPARATOR; -$dispatcher = 'Cake' . $ds . 'Console' . $ds . 'ShellDispatcher.php'; +if (!defined('DS')) { + define('DS', DIRECTORY_SEPARATOR); +} + +$dispatcher = 'Cake' . DS . 'Console' . DS . 'ShellDispatcher.php'; if (function_exists('ini_set')) { $root = dirname(dirname(dirname(__FILE__))); + $appDir = basename(dirname(dirname(__FILE__))); + $install = $root . DS . 'lib'; + $composerInstall = $root . DS . $appDir . DS . 'Vendor' . DS . 'cakephp' . DS . 'cakephp' . DS . 'lib'; - // the following line differs from its sibling + // the following lines differ from its sibling // /app/Console/cake.php - ini_set('include_path', $root . PATH_SEPARATOR . __CAKE_PATH__ . PATH_SEPARATOR . ini_get('include_path')); + if (file_exists($composerInstall . DS . $dispatcher)) { + $install = $composerInstall; + } elseif (!file_exists($install . DS . $dispatcher)) { + $install = $root . PATH_SEPARATOR . __CAKE_PATH__; + } + + ini_set('include_path', $install . PATH_SEPARATOR . ini_get('include_path')); + unset($root, $appDir, $install, $composerInstall); } if (!include $dispatcher) { trigger_error('Could not locate CakePHP core files.', E_USER_ERROR); } -unset($paths, $path, $dispatcher, $root, $ds); +unset($dispatcher); return ShellDispatcher::run($argv); diff --git a/web/api/lib/Cake/Console/Templates/skel/Controller/PagesController.php b/web/api/lib/Cake/Console/Templates/skel/Controller/PagesController.php index 87fb589af..eb023a573 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Controller/PagesController.php +++ b/web/api/lib/Cake/Console/Templates/skel/Controller/PagesController.php @@ -31,10 +31,9 @@ class PagesController extends AppController { /** * Displays a view * - * @param mixed What page to display * @return void * @throws NotFoundException When the view file could not be found - * or MissingViewException in debug mode. + * or MissingViewException in debug mode. */ public function display() { $path = func_get_args(); diff --git a/web/api/lib/Cake/Console/Templates/skel/Test/Case/AllTestsTest.php b/web/api/lib/Cake/Console/Templates/skel/Test/Case/AllTestsTest.php index 904bc3b50..9929fa582 100644 --- a/web/api/lib/Cake/Console/Templates/skel/Test/Case/AllTestsTest.php +++ b/web/api/lib/Cake/Console/Templates/skel/Test/Case/AllTestsTest.php @@ -18,6 +18,11 @@ class AllTestsTest extends CakeTestSuite { +/** + * Get the suite object. + * + * @return CakeTestSuite Suite class instance. + */ public static function suite() { $suite = new CakeTestSuite('All application tests'); $suite->addTestDirectoryRecursive(TESTS . 'Case'); diff --git a/web/api/lib/Cake/Console/Templates/skel/View/Elements/Flash/default.ctp b/web/api/lib/Cake/Console/Templates/skel/View/Elements/Flash/default.ctp new file mode 100644 index 000000000..ce0f61355 --- /dev/null +++ b/web/api/lib/Cake/Console/Templates/skel/View/Elements/Flash/default.ctp @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/web/api/lib/Cake/Console/Templates/skel/View/Emails/html/default.ctp b/web/api/lib/Cake/Console/Templates/skel/View/Emails/html/default.ctp index e2bff19c0..a1333dc19 100644 --- a/web/api/lib/Cake/Console/Templates/skel/View/Emails/html/default.ctp +++ b/web/api/lib/Cake/Console/Templates/skel/View/Emails/html/default.ctp @@ -1,7 +1,5 @@ ' . $line . "

\n"; -endforeach; -?> \ No newline at end of file +endforeach; \ No newline at end of file diff --git a/web/api/lib/Cake/Console/Templates/skel/View/Emails/text/default.ctp b/web/api/lib/Cake/Console/Templates/skel/View/Emails/text/default.ctp index 090b5c403..7f8832498 100644 --- a/web/api/lib/Cake/Console/Templates/skel/View/Emails/text/default.ctp +++ b/web/api/lib/Cake/Console/Templates/skel/View/Emails/text/default.ctp @@ -1,7 +1,5 @@ 0): echo $this->element('exception_stack_trace'); endif; -?> diff --git a/web/api/lib/Cake/Console/Templates/skel/View/Errors/error500.ctp b/web/api/lib/Cake/Console/Templates/skel/View/Errors/error500.ctp index 51734d10f..339e140da 100644 --- a/web/api/lib/Cake/Console/Templates/skel/View/Errors/error500.ctp +++ b/web/api/lib/Cake/Console/Templates/skel/View/Errors/error500.ctp @@ -1,7 +1,5 @@ 0): echo $this->element('exception_stack_trace'); endif; -?> diff --git a/web/api/lib/Cake/Console/Templates/skel/View/Layouts/Emails/html/default.ctp b/web/api/lib/Cake/Console/Templates/skel/View/Layouts/Emails/html/default.ctp index 9521a6d01..c98487509 100644 --- a/web/api/lib/Cake/Console/Templates/skel/View/Layouts/Emails/html/default.ctp +++ b/web/api/lib/Cake/Console/Templates/skel/View/Layouts/Emails/html/default.ctp @@ -1,7 +1,5 @@ Html->charset(); ?> -<?php echo $page_title; ?> +<?php echo $pageTitle; ?> - + ', 'charset' => '', 'ul' => '%s', @@ -112,11 +113,11 @@ class HtmlHelper extends AppHelper { protected $_crumbs = array(); /** - * Names of script files that have been included once + * Names of script & css files that have been included once * * @var array */ - protected $_includedScripts = array(); + protected $_includedAssets = array(); /** * Options for the currently opened script block buffer if any. @@ -174,7 +175,7 @@ class HtmlHelper extends AppHelper { * @param string $name Text for link * @param string $link URL for link (if empty it won't be a link) * @param string|array $options Link attributes e.g. array('id' => 'selected') - * @return this HtmlHelper + * @return self * @see HtmlHelper::link() for details on $options that can be used. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#creating-breadcrumb-trails-with-htmlhelper */ @@ -198,7 +199,7 @@ class HtmlHelper extends AppHelper { * - xhtml11: XHTML1.1. * * @param string $type Doctype to use. - * @return string Doctype string + * @return string|null Doctype string * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::docType */ public function docType($type = 'html5') { @@ -279,12 +280,12 @@ class HtmlHelper extends AppHelper { if (isset($options['link'])) { $options['link'] = $this->assetUrl($options['link']); if (isset($options['rel']) && $options['rel'] === 'icon') { - $out = sprintf($this->_tags['metalink'], $options['link'], $this->_parseAttributes($options, array('block', 'link'), ' ', ' ')); + $out = sprintf($this->_tags['metalink'], $options['link'], $this->_parseAttributes($options, array('block', 'link'))); $options['rel'] = 'shortcut icon'; } - $out .= sprintf($this->_tags['metalink'], $options['link'], $this->_parseAttributes($options, array('block', 'link'), ' ', ' ')); + $out .= sprintf($this->_tags['metalink'], $options['link'], $this->_parseAttributes($options, array('block', 'link'))); } else { - $out = sprintf($this->_tags['meta'], $this->_parseAttributes($options, array('block', 'type'), ' ', ' ')); + $out = sprintf($this->_tags['meta'], $this->_parseAttributes($options, array('block', 'type'))); } if (empty($options['block'])) { @@ -326,7 +327,8 @@ class HtmlHelper extends AppHelper { * @param string $title The content to be wrapped by tags. * @param string|array $url Cake-relative URL or array of URL parameters, or external URL (starts with http://) * @param array $options Array of options and HTML attributes. - * @param string $confirmMessage JavaScript confirmation message. + * @param string $confirmMessage JavaScript confirmation message. This + * argument is deprecated as of 2.6. Use `confirm` key in $options instead. * @return string An `` element. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#HtmlHelper::link */ @@ -397,6 +399,9 @@ class HtmlHelper extends AppHelper { * * - `inline` If set to false, the generated tag will be appended to the 'css' block, * and included in the `$scripts_for_layout` layout variable. Defaults to true. + * - `once` Whether or not the css file should be checked for uniqueness. If true css + * files will only be included once, use false to allow the same + * css to be included more than once per request. * - `block` Set the name of the block link/style tag will be appended to. * This overrides the `inline` option. * - `plugin` False value will prevent parsing path as a plugin @@ -423,7 +428,12 @@ class HtmlHelper extends AppHelper { unset($rel); } - $options += array('block' => null, 'inline' => true, 'rel' => 'stylesheet'); + $options += array( + 'block' => null, + 'inline' => true, + 'once' => false, + 'rel' => 'stylesheet' + ); if (!$options['inline'] && empty($options['block'])) { $options['block'] = __FUNCTION__; } @@ -437,9 +447,15 @@ class HtmlHelper extends AppHelper { if (empty($options['block'])) { return $out . "\n"; } - return; + return ''; } + if ($options['once'] && isset($this->_includedAssets[__METHOD__][$path])) { + return ''; + } + unset($options['once']); + $this->_includedAssets[__METHOD__][$path] = true; + if (strpos($path, '//') !== false) { $url = $path; } else { @@ -454,10 +470,10 @@ class HtmlHelper extends AppHelper { } } - if ($options['rel'] == 'import') { + if ($options['rel'] === 'import') { $out = sprintf( $this->_tags['style'], - $this->_parseAttributes($options, array('rel', 'block'), '', ' '), + $this->_parseAttributes($options, array('rel', 'block')), '@import url(' . $url . ');' ); } else { @@ -465,7 +481,7 @@ class HtmlHelper extends AppHelper { $this->_tags['css'], $options['rel'], $url, - $this->_parseAttributes($options, array('rel', 'block'), '', ' ') + $this->_parseAttributes($options, array('rel', 'block')) ); } @@ -481,7 +497,6 @@ class HtmlHelper extends AppHelper { * If the filename is prefixed with "/", the path will be relative to the base path of your * application. Otherwise, the path will be relative to your JavaScript path, usually webroot/js. * - * * ### Usage * * Include one script file: @@ -498,7 +513,7 @@ class HtmlHelper extends AppHelper { * * Add the script file to a custom block: * - * `$this->Html->script('styles.js', null, array('block' => 'bodyScript'));` + * `$this->Html->script('styles.js', array('block' => 'bodyScript'));` * * ### Options * @@ -512,7 +527,7 @@ class HtmlHelper extends AppHelper { * - `fullBase` If true the url will get a full address for the script file. * * @param string|array $url String or array of javascript files to include - * @param array|boolean $options Array of options, and html attributes see above. If boolean sets $options['inline'] = value + * @param array|bool $options Array of options, and html attributes see above. If boolean sets $options['inline'] = value * @return mixed String of `'; + Error ("Invalid recaptcha secret detected"); + + } + } + + } + + } + } + // General scope actions if ( $action == "login" && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == "remote" || isset($_REQUEST['password']) ) ) { @@ -169,46 +246,14 @@ if ( !empty($action) ) if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) { require_once( 'control_functions.php' ); + require_once( 'Monitor.php' ); $mid = validInt($_REQUEST['mid']); if ( $action == "control" ) { - $monitor = dbFetchOne( "select C.*,M.* from Monitors as M inner join Controls as C on (M.ControlId = C.Id) where M.Id = ?", NULL, array($mid) ); + $monitor = new Monitor( $mid ); $ctrlCommand = buildControlCommand( $monitor ); - - if ( $ctrlCommand ) - { - $socket = socket_create( AF_UNIX, SOCK_STREAM, 0 ); - if ( $socket < 0 ) - { - Fatal( "socket_create() failed: ".socket_strerror($socket) ); - } - $sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$monitor['Id'].'.sock'; - if ( @socket_connect( $socket, $sockFile ) ) - { - $options = array(); - foreach ( explode( " ", $ctrlCommand ) as $option ) - { - if ( preg_match( '/--([^=]+)(?:=(.+))?/', $option, $matches ) ) - { - $options[$matches[1]] = $matches[2]?$matches[2]:1; - } - } - $optionString = jsonEncode( $options ); - if ( !socket_write( $socket, $optionString ) ) - { - Fatal( "Can't write to control socket: ".socket_strerror(socket_last_error($socket)) ); - } - socket_close( $socket ); - } - else - { - $ctrlCommand .= " --id=".$monitor['Id']; - - // Can't connect so use script - $ctrlOutput = exec( escapeshellcmd( $ctrlCommand ) ); - } - } + sendControlCommand( $monitor->Id(), $ctrlCommand ); } elseif ( $action == "settings" ) { @@ -293,8 +338,7 @@ if ( !empty($action) ) $monitor = dbFetchOne( "SELECT * FROM Monitors WHERE Id=?", NULL, array($mid) ); $newFunction = validStr($_REQUEST['newFunction']); - $newEnabled = validStr($_REQUEST['newEnabled']); - if ($newEnabled != "1") $newEnabled = "0"; + $newEnabled = isset( $_REQUEST['newEnabled'] ) and $_REQUEST['newEnabled'] != "1" ? "0" : "1"; $oldFunction = $monitor['Function']; $oldEnabled = $monitor['Enabled']; if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) @@ -359,8 +403,21 @@ if ( !empty($action) ) //if ( $cookies ) session_write_close(); if ( daemonCheck() ) { - zmaControl( $mid, "restart" ); - } + if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) + { + zmaControl( $monitor, "stop" ); + zmcControl( $monitor, "restart" ); + zmaControl( $monitor, "start" ); + } + else + { + zmaControl( $mid, "restart" ); + } + } + if ( $_REQUEST['newZone']['Type'] == 'Privacy' && $monitor['Controllable'] ) { + require_once( 'control_functions.php' ); + sendControlCommand( $mid, 'quit' ); + } $refreshParent = true; } $view = 'none'; @@ -408,6 +465,7 @@ if ( !empty($action) ) $deletedZid = 0; foreach( $_REQUEST['markZids'] as $markZid ) { + $zone = dbFetchOne( "select * from Zones where Id=?", NULL, array($markZid) ); dbQuery( "delete from Zones WHERE MonitorId=? AND Id=?", array( $mid, $markZid) ); $deletedZid = 1; } @@ -416,7 +474,16 @@ if ( !empty($action) ) //if ( $cookies ) //session_write_close(); if ( daemonCheck() ) - zmaControl( $mid, "restart" ); + if ( $zone['Type'] == 'Privacy' ) + { + zmaControl( $mid, "stop" ); + zmcControl( $mid, "restart" ); + zmaControl( $mid, "start" ); + } + else + { + zmaControl( $mid, "restart" ); + } $refreshParent = true; } } @@ -455,7 +522,9 @@ if ( !empty($action) ) 'Controllable' => 'toggle', 'TrackMotion' => 'toggle', 'Enabled' => 'toggle', - 'DoNativeMotDet' => 'toggle' + 'DoNativeMotDet' => 'toggle', + 'Exif' => 'toggle', + 'RTSPDescribe' => 'toggle', ); $columns = getTableColumns( 'Monitors' ); @@ -569,6 +638,10 @@ if ( !empty($action) ) zmcControl( $monitor, "restart" ); zmaControl( $monitor, "start" ); } + if ( $monitor['Controllable'] ) { + require_once( 'control_functions.php' ); + sendControlCommand( $mid, 'quit' ); + } //daemonControl( 'restart', 'zmwatch.pl' ); $refreshParent = true; } @@ -603,9 +676,10 @@ if ( !empty($action) ) // well time out before completing, in which case zmaudit will still tidy up if ( !ZM_OPT_FAST_DELETE ) { - $markEids = dbFetchAll( "select Id from Events where MonitorId=?", 'Id', array($markMid) ); + // Slight hack, we maybe should load *, but we happen to know that the deleteEvent function uses Id and StartTime. + $markEids = dbFetchAll( "SELECT Id,StartTime FROM Events WHERE MonitorId=?", NULL, array($markMid) ); foreach( $markEids as $markEid ) - deleteEvent( $markEid ); + deleteEvent( $markEid, $markMid ); deletePath( ZM_DIR_EVENTS."/".basename($monitor['Name']) ); deletePath( ZM_DIR_EVENTS."/".$monitor['Id'] ); // I'm trusting the Id. @@ -653,20 +727,77 @@ if ( !empty($action) ) } } - // System view actions - if ( $action == "setgroup" ) { - if ( !empty($_REQUEST['gid']) ) { - setcookie( "zmGroup", validInt($_REQUEST['gid']), time()+3600*24*30*12*10 ); - } else { - setcookie( "zmGroup", "", time()-3600*24*2 ); - } - $refreshParent = true; - } + // Group view actions + if ( canView( 'Groups' ) && $action == "setgroup" ) { + if ( !empty($_REQUEST['gid']) ) { + setcookie( "zmGroup", validInt($_REQUEST['gid']), time()+3600*24*30*12*10 ); + } else { + setcookie( "zmGroup", "", time()-3600*24*2 ); + } + $refreshParent = true; + } + + // Group edit actions + if ( canEdit( 'Groups' ) ) { + if ( $action == "group" ) { + # Should probably verfy that each monitor id is a valid monitor, that we have access to. HOwever at the moment, you have to have System permissions to do this + $monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? NULL : implode(',', $_POST['newGroup']['MonitorIds']); + if ( !empty($_POST['gid']) ) { + dbQuery( "UPDATE Groups SET Name=?, MonitorIds=? WHERE Id=?", array($_POST['newGroup']['Name'], $monitors, $_POST['gid']) ); + } else { + dbQuery( "INSERT INTO Groups SET Name=?, MonitorIds=?", array( $_POST['newGroup']['Name'], $monitors ) ); + } + $view = 'none'; + } + if ( !empty($_REQUEST['gid']) && $action == "delete" ) { + dbQuery( "delete from Groups where Id = ?", array($_REQUEST['gid']) ); + if ( isset($_COOKIE['zmGroup']) ) + { + if ( $_REQUEST['gid'] == $_COOKIE['zmGroup'] ) + { + unset( $_COOKIE['zmGroup'] ); + setcookie( "zmGroup", "", time()-3600*24*2 ); + $refreshParent = true; + } + } + } + $refreshParent = true; + } // System edit actions if ( canEdit( 'System' ) ) { - if ( $action == "version" && isset($_REQUEST['option']) ) + if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'server' ) ) { + + if ( $action == "Save" ) { + if ( !empty($_REQUEST['id']) ) + $dbServer = dbFetchOne( "SELECT * FROM Servers WHERE Id=?", NULL, array($_REQUEST['id']) ); + else + $dbServer = array(); + + $types = array(); + $changes = getFormChanges( $dbServer, $_REQUEST['newServer'], $types ); + + if ( count( $changes ) ) { + if ( !empty($_REQUEST['id']) ) { + dbQuery( "UPDATE Servers SET ".implode( ", ", $changes )." WHERE Id = ?", array($_REQUEST['id']) ); + } else { + dbQuery( "INSERT INTO Servers set ".implode( ", ", $changes ) ); + } + $refreshParent = true; + } + $view = 'none'; + } else if ( $action == 'delete' ) { + if ( !empty($_REQUEST['markIds']) ) { + foreach( $_REQUEST['markIds'] as $Id ) + dbQuery( "DELETE FROM Servers WHERE Id=?", array($Id) ); + } + $refreshParent = true; + } else { + Error( "Unknown action $action in saving Server" ); + } + + } else if ( $action == "version" && isset($_REQUEST['option']) ) { $option = $_REQUEST['option']; switch( $option ) @@ -790,7 +921,6 @@ if ( !empty($action) ) case "highband" : case "medband" : case "lowband" : - case "phoneband" : break; } } @@ -852,16 +982,6 @@ if ( !empty($action) ) dbQuery( "replace into States set Name=?, Definition=?", array( $_REQUEST['runState'],$definition) ); } } - elseif ( $action == "group" ) - { - if ( !empty($_POST['gid']) ) { - dbQuery( "UPDATE Groups SET Name=?, MonitorIds=? WHERE Id=?", array($_POST['newGroup']['Name'], $_POST['newGroup']['MonitorIds'], $_POST['gid']) ); - } else { - dbQuery( "INSERT INTO Groups SET Name=?, MonitorIds=?", array( $_POST['newGroup']['Name'], empty($_POST['newGroup']['MonitorIds']) ? array() : $_POST['newGroup']['MonitorIds'] ) ); - } - $refreshParent = true; - $view = 'none'; - } elseif ( $action == "delete" ) { if ( isset($_REQUEST['runState']) ) @@ -874,19 +994,6 @@ if ( !empty($action) ) if ( $markUid == $user['Id'] ) userLogout(); } - if ( !empty($_REQUEST['gid']) ) - { - dbQuery( "delete from Groups where Id = ?", array($_REQUEST['gid']) ); - if ( isset($_COOKIE['zmGroup']) ) - { - if ( $_REQUEST['gid'] == $_COOKIE['zmGroup'] ) - { - unset( $_COOKIE['zmGroup'] ); - setcookie( "zmGroup", "", time()-3600*24*2 ); - $refreshParent = true; - } - } - } } } else diff --git a/web/includes/config.php.in b/web/includes/config.php.in index be956d75c..ba96d9a54 100644 --- a/web/includes/config.php.in +++ b/web/includes/config.php.in @@ -61,6 +61,11 @@ define( "ZM_HAS_V4L2", "@ZM_HAS_V4L2@" ); // V4L2 support enabled define( "ZM_HAS_V4L1", "@ZM_HAS_V4L1@" ); // V4L1 support enabled define( "ZM_HAS_V4L", "@ZM_HAS_V4L@" ); // V4L support enabled +// +// If ONVIF support has been built in +// +define( "ZM_HAS_ONVIF", "@ZM_HAS_ONVIF@" ); // ONVIF support enabled + // // If PCRE dev libraries are installed // @@ -99,6 +104,7 @@ define( "CMD_PREV", 12 ); define( "CMD_NEXT", 13 ); define( "CMD_SEEK", 14 ); define( "CMD_VARPLAY", 15 ); +define( "CMD_QUIT", 17 ); define( "CMD_QUERY", 99 ); // @@ -126,6 +132,7 @@ $GLOBALS['defaultUser'] = array( "Events" => 'Edit', "Control" => 'Edit', "Monitors" => 'Edit', + "Groups" => 'Edit', "Devices" => 'Edit', "System" => 'Edit', "MaxBandwidth" => "", @@ -161,4 +168,25 @@ function loadConfig( $defineConsts=true ) //print_r( $configCats ); } +require_once( 'logger.php' ); +// For Human-readability, user ZM_SERVER in zm.conf, and convert it here to a ZM_SERVER_ID +if ( ! defined('ZM_SERVER_ID') ) { + if ( defined('ZM_SERVER_NAME') and ZM_SERVER_NAME ) { + $server_id = dbFetchOne('SELECT Id FROM Servers WHERE Name=?', 'Id', array(ZM_SERVER_NAME)); + if ( ! $server_id ) { + Error("Invalid Multi-Server configration detected. ZM_SERVER_NAME set to " . ZM_SERVER_NAME . " in zm.conf, but no corresponding entry found in Servers table."); + } else { + define( 'ZM_SERVER_ID', $server_id ); + } + } else if ( defined('ZM_SERVER_HOST') and ZM_SERVER_HOST ) { + $server_id = dbFetchOne('SELECT Id FROM Servers WHERE Name=?', 'Id', array(ZM_SERVER_HOST)); + if ( ! $server_id ) { + Error("Invalid Multi-Server configration detected. ZM_SERVER_HOST set to " . ZM_SERVER_HOST . " in zm.conf, but no corresponding entry found in Servers table."); + } else { + define( 'ZM_SERVER_ID', $server_id ); + } + } +} + + ?> diff --git a/web/includes/control_functions.php b/web/includes/control_functions.php index 2ad782926..dfbc8cb3e 100644 --- a/web/includes/control_functions.php +++ b/web/includes/control_functions.php @@ -20,9 +20,9 @@ function buildControlCommand( $monitor ) case 'focus' : { $factor = $_REQUEST['yge']/100; - if ( $monitor['HasFocusSpeed'] ) + if ( $monitor->HasFocusSpeed() ) { - $speed = intval(round($monitor['MinFocusSpeed']+(($monitor['MaxFocusSpeed']-$monitor['MinFocusSpeed'])*$factor))); + $speed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -30,15 +30,15 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinFocusStep']+(($monitor['MaxFocusStep']-$monitor['MinFocusStep'])*$factor))); + $step = intval(round($monitor->MinFocusStep()+(($monitor->MaxFocusStep()-$monitor->MinFocusStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } case 'Con' : { - if ( $monitor['AutoStopTimeout'] ) + if ( $monitor->AutoStopTimeout() ) { - $slowSpeed = intval(round($monitor['MinFocusSpeed']+(($monitor['MaxFocusSpeed']-$monitor['MinFocusSpeed'])*$slow))); + $slowSpeed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= " --autostop"; @@ -52,9 +52,9 @@ function buildControlCommand( $monitor ) case 'zoom' : { $factor = $_REQUEST['yge']/100; - if ( $monitor['HasZoomSpeed'] ) + if ( $monitor->HasZoomSpeed() ) { - $speed = intval(round($monitor['MinZoomSpeed']+(($monitor['MaxZoomSpeed']-$monitor['MinZoomSpeed'])*$factor))); + $speed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -62,15 +62,15 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinZoomStep']+(($monitor['MaxZoomStep']-$monitor['MinZoomStep'])*$factor))); + $step = intval(round($monitor->MinZoomStep()+(($monitor->MaxZoomStep()-$monitor->MinZoomStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } case 'Con' : { - if ( $monitor['AutoStopTimeout'] ) + if ( $monitor->AutoStopTimeout() ) { - $slowSpeed = intval(round($monitor['MinZoomSpeed']+(($monitor['MaxZoomSpeed']-$monitor['MinZoomSpeed'])*$slow))); + $slowSpeed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= " --autostop"; @@ -84,9 +84,9 @@ function buildControlCommand( $monitor ) case 'iris' : { $factor = $_REQUEST['yge']/100; - if ( $monitor['HasIrisSpeed'] ) + if ( $monitor->HasIrisSpeed() ) { - $speed = intval(round($monitor['MinIrisSpeed']+(($monitor['MaxIrisSpeed']-$monitor['MinIrisSpeed'])*$factor))); + $speed = intval(round($monitor->MinIrisSpeed()+(($monitor->MaxIrisSpeed()-$monitor->MinIrisSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -94,7 +94,7 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinIrisStep']+(($monitor['MaxIrisStep']-$monitor['MinIrisStep'])*$factor))); + $step = intval(round($monitor->MinIrisStep()+(($monitor->MaxIrisStep()-$monitor->MinIrisStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } @@ -104,9 +104,9 @@ function buildControlCommand( $monitor ) case 'white' : { $factor = $_REQUEST['yge']/100; - if ( $monitor['HasWhiteSpeed'] ) + if ( $monitor->HasWhiteSpeed() ) { - $speed = intval(round($monitor['MinWhiteSpeed']+(($monitor['MaxWhiteSpeed']-$monitor['MinWhiteSpeed'])*$factor))); + $speed = intval(round($monitor->MinWhiteSpeed()+(($monitor->MaxWhiteSpeed()-$monitor->MinWhiteSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -114,7 +114,7 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinWhiteStep']+(($monitor['MaxWhiteStep']-$monitor['MinWhiteStep'])*$factor))); + $step = intval(round($monitor->MinWhiteStep()+(($monitor->MaxWhiteStep()-$monitor->MinWhiteStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } @@ -124,9 +124,9 @@ function buildControlCommand( $monitor ) case 'gain' : { $factor = $_REQUEST['yge']/100; - if ( $monitor['HasGainSpeed'] ) + if ( $monitor->HasGainSpeed() ) { - $speed = intval(round($monitor['MinGainSpeed']+(($monitor['MaxGainSpeed']-$monitor['MinGainSpeed'])*$factor))); + $speed = intval(round($monitor->MinGainSpeed()+(($monitor->MaxGainSpeed()-$monitor->MinGainSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -134,7 +134,7 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinGainStep']+(($monitor['MaxGainStep']-$monitor['MinGainStep'])*$factor))); + $step = intval(round($monitor->MinGainStep()+(($monitor->MaxGainStep()-$monitor->MinGainStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } @@ -146,7 +146,7 @@ function buildControlCommand( $monitor ) $xFactor = empty($_REQUEST['xge'])?0:$_REQUEST['xge']/100; $yFactor = empty($_REQUEST['yge'])?0:$_REQUEST['yge']/100; - if ( $monitor['Orientation'] != '0' ) + if ( $monitor->Orientation() != '0' ) { $conversions = array( '90' => array( @@ -200,48 +200,48 @@ function buildControlCommand( $monitor ) 'DownRight' => 'UpRight', ), ); - $new_dirn = $conversions[$monitor['Orientation']][$dirn]; + $new_dirn = $conversions[$monitor->Orientation()][$dirn]; $_REQUEST['control'] = preg_replace( "/_$dirn\$/", "_$new_dirn", $_REQUEST['control'] ); $dirn = $new_dirn; } - if ( $monitor['HasPanSpeed'] && $xFactor ) + if ( $monitor->HasPanSpeed() && $xFactor ) { - if ( $monitor['HasTurboPan'] ) + if ( $monitor->HasTurboPan() ) { if ( $xFactor >= $turbo ) { - $panSpeed = $monitor['TurboPanSpeed']; + $panSpeed = $monitor->TurboPanSpeed(); } else { $xFactor = $xFactor/$turbo; - $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); + $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); } } else { - $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); + $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); } $ctrlCommand .= " --panspeed=".$panSpeed; } - if ( $monitor['HasTiltSpeed'] && $yFactor ) + if ( $monitor->HasTiltSpeed() && $yFactor ) { - if ( $monitor['HasTurboTilt'] ) + if ( $monitor->HasTurboTilt() ) { if ( $yFactor >= $turbo ) { - $tiltSpeed = $monitor['TurboTiltSpeed']; + $tiltSpeed = $monitor->TurboTiltSpeed(); } else { $yFactor = $yFactor/$turbo; - $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); + $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); } } else { - $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); + $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); } $ctrlCommand .= " --tiltspeed=".$tiltSpeed; } @@ -252,22 +252,22 @@ function buildControlCommand( $monitor ) { if ( preg_match( '/(Left|Right)$/', $dirn ) ) { - $panStep = intval(round($monitor['MinPanStep']+(($monitor['MaxPanStep']-$monitor['MinPanStep'])*$xFactor))); + $panStep = intval(round($monitor->MinPanStep()+(($monitor->MaxPanStep()-$monitor->MinPanStep())*$xFactor))); $ctrlCommand .= " --panstep=".$panStep; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { - $tiltStep = intval(round($monitor['MinTiltStep']+(($monitor['MaxTiltStep']-$monitor['MinTiltStep'])*$yFactor))); + $tiltStep = intval(round($monitor->MinTiltStep()+(($monitor->MaxTiltStep()-$monitor->MinTiltStep())*$yFactor))); $ctrlCommand .= " --tiltstep=".$tiltStep; } break; } case 'Con' : { - if ( $monitor['AutoStopTimeout'] ) + if ( $monitor->AutoStopTimeout() ) { - $slowPanSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$slow))); - $slowTiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$slow))); + $slowPanSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$slow))); + $slowTiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$slow))); if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) { $ctrlCommand .= " --autostop"; @@ -286,22 +286,22 @@ function buildControlCommand( $monitor ) { $x = deScale( $_REQUEST['x'], $_REQUEST['scale'] ); $y = deScale( $_REQUEST['y'], $_REQUEST['scale'] ); - switch ( $monitor['Orientation'] ) + switch ( $monitor->Orientation() ) { case '0' : case '180' : case 'hori' : case 'vert' : - $width = $monitor['Width']; - $height = $monitor['Height']; + $width = $monitor->Width(); + $height = $monitor->Height(); break; case '90' : case '270' : - $width = $monitor['Height']; - $height = $monitor['Width']; + $width = $monitor->Height(); + $height = $monitor->Width(); break; } - switch ( $monitor['Orientation'] ) + switch ( $monitor->Orientation() ) { case '90' : $tempY = $y; @@ -332,12 +332,12 @@ function buildControlCommand( $monitor ) $x = deScale( $_REQUEST['x'], $_REQUEST['scale'] ); $y = deScale( $_REQUEST['y'], $_REQUEST['scale'] ); - $halfWidth = $monitor['Width'] / 2; - $halfHeight = $monitor['Height'] / 2; + $halfWidth = $monitor->Width() / 2; + $halfHeight = $monitor->Height() / 2; $xFactor = ($x - $halfWidth)/$halfWidth; $yFactor = ($y - $halfHeight)/$halfHeight; - switch ( $monitor['Orientation'] ) + switch ( $monitor->Orientation() ) { case '90' : $tempYFactor = $y; @@ -396,52 +396,52 @@ function buildControlCommand( $monitor ) $xFactor = abs($xFactor); $yFactor = abs($yFactor); - if ( $monitor['HasPanSpeed'] && $xFactor ) + if ( $monitor->HasPanSpeed() && $xFactor ) { - if ( $monitor['HasTurboPan'] ) + if ( $monitor->HasTurboPan() ) { if ( $xFactor >= $turbo ) { - $panSpeed = $monitor['TurboPanSpeed']; + $panSpeed = $monitor->TurboPanSpeed(); } else { $xFactor = $xFactor/$turbo; - $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); + $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); } } else { - $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); + $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); } } - if ( $monitor['HasTiltSpeed'] && $yFactor ) + if ( $monitor->HasTiltSpeed() && $yFactor ) { - if ( $monitor['HasTurboTilt'] ) + if ( $monitor->HasTurboTilt() ) { if ( $yFactor >= $turbo ) { - $tiltSpeed = $monitor['TurboTiltSpeed']; + $tiltSpeed = $monitor->TurboTiltSpeed(); } else { $yFactor = $yFactor/$turbo; - $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); + $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); } } else { - $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); + $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); } } if ( preg_match( '/(Left|Right)$/', $dirn ) ) { - $panStep = intval(round($monitor['MinPanStep']+(($monitor['MaxPanStep']-$monitor['MinPanStep'])*$xFactor))); + $panStep = intval(round($monitor->MinPanStep()+(($monitor->MaxPanStep()-$monitor->MinPanStep())*$xFactor))); $ctrlCommand .= " --panstep=".$panStep." --panspeed=".$panSpeed; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { - $tiltStep = intval(round($monitor['MinTiltStep']+(($monitor['MaxTiltStep']-$monitor['MinTiltStep'])*$yFactor))); + $tiltStep = intval(round($monitor->MinTiltStep()+(($monitor->MaxTiltStep()-$monitor->MinTiltStep())*$yFactor))); $ctrlCommand .= " --tiltstep=".$tiltStep." --tiltspeed=".$tiltSpeed; } } @@ -451,12 +451,12 @@ function buildControlCommand( $monitor ) $x = deScale( $_REQUEST['x'], $_REQUEST['scale'] ); $y = deScale( $_REQUEST['y'], $_REQUEST['scale'] ); - $halfWidth = $monitor['Width'] / 2; - $halfHeight = $monitor['Height'] / 2; + $halfWidth = $monitor->Width() / 2; + $halfHeight = $monitor->Height() / 2; $xFactor = ($x - $halfWidth)/$halfWidth; $yFactor = ($y - $halfHeight)/$halfHeight; - switch ( $monitor['Orientation'] ) + switch ( $monitor->Orientation() ) { case '90' : $tempYFactor = $y; @@ -515,42 +515,42 @@ function buildControlCommand( $monitor ) $xFactor = abs($xFactor); $yFactor = abs($yFactor); - if ( $monitor['HasPanSpeed'] && $xFactor ) + if ( $monitor->HasPanSpeed() && $xFactor ) { - if ( $monitor['HasTurboPan'] ) + if ( $monitor->HasTurboPan() ) { if ( $xFactor >= $turbo ) { - $panSpeed = $monitor['TurboPanSpeed']; + $panSpeed = $monitor->TurboPanSpeed(); } else { $xFactor = $xFactor/$turbo; - $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); + $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); } } else { - $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); + $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); } } - if ( $monitor['HasTiltSpeed'] && $yFactor ) + if ( $monitor->HasTiltSpeed() && $yFactor ) { - if ( $monitor['HasTurboTilt'] ) + if ( $monitor->HasTurboTilt() ) { if ( $yFactor >= $turbo ) { - $tiltSpeed = $monitor['TurboTiltSpeed']; + $tiltSpeed = $monitor->TurboTiltSpeed(); } else { $yFactor = $yFactor/$turbo; - $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); + $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); } } else { - $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); + $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); } } if ( preg_match( '/(Left|Right)$/', $dirn ) ) @@ -561,10 +561,10 @@ function buildControlCommand( $monitor ) { $ctrlCommand .= " --tiltspeed=".$tiltSpeed; } - if ( $monitor['AutoStopTimeout'] ) + if ( $monitor->AutoStopTimeout() ) { - $slowPanSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$slow))); - $slowTiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$slow))); + $slowPanSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$slow))); + $slowTiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$slow))); if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) { $ctrlCommand .= " --autostop"; @@ -603,9 +603,9 @@ function buildControlCommand( $monitor ) break; } } - if ( $monitor['HasFocusSpeed'] ) + if ( $monitor->HasFocusSpeed() ) { - $speed = intval(round($monitor['MinFocusSpeed']+(($monitor['MaxFocusSpeed']-$monitor['MinFocusSpeed'])*$factor))); + $speed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -613,15 +613,15 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinFocusStep']+(($monitor['MaxFocusStep']-$monitor['MinFocusStep'])*$factor))); + $step = intval(round($monitor->MinFocusStep()+(($monitor->MaxFocusStep()-$monitor->MinFocusStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } case 'Con' : { - if ( $monitor['AutoStopTimeout'] ) + if ( $monitor->AutoStopTimeout() ) { - $slowSpeed = intval(round($monitor['MinFocusSpeed']+(($monitor['MaxFocusSpeed']-$monitor['MinFocusSpeed'])*$slow))); + $slowSpeed = intval(round($monitor->MinFocusSpeed()+(($monitor->MaxFocusSpeed()-$monitor->MinFocusSpeed())*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= " --autostop"; @@ -647,9 +647,9 @@ function buildControlCommand( $monitor ) break; } } - if ( $monitor['HasZoomSpeed'] ) + if ( $monitor->HasZoomSpeed() ) { - $speed = intval(round($monitor['MinZoomSpeed']+(($monitor['MaxZoomSpeed']-$monitor['MinZoomSpeed'])*$factor))); + $speed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -657,15 +657,15 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinZoomStep']+(($monitor['MaxZoomStep']-$monitor['MinZoomStep'])*$factor))); + $step = intval(round($monitor->MinZoomStep()+(($monitor->MaxZoomStep()-$monitor->MinZoomStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } case 'Con' : { - if ( $monitor['AutoStopTimeout'] ) + if ( $monitor->AutoStopTimeout() ) { - $slowSpeed = intval(round($monitor['MinZoomSpeed']+(($monitor['MaxZoomSpeed']-$monitor['MinZoomSpeed'])*$slow))); + $slowSpeed = intval(round($monitor->MinZoomSpeed()+(($monitor->MaxZoomSpeed()-$monitor->MinZoomSpeed())*$slow))); if ( $speed < $slowSpeed ) { $ctrlCommand .= " --autostop"; @@ -691,9 +691,9 @@ function buildControlCommand( $monitor ) break; } } - if ( $monitor['HasIrisSpeed'] ) + if ( $monitor->HasIrisSpeed() ) { - $speed = intval(round($monitor['MinIrisSpeed']+(($monitor['MaxIrisSpeed']-$monitor['MinIrisSpeed'])*$factor))); + $speed = intval(round($monitor->MinIrisSpeed()+(($monitor->MaxIrisSpeed()-$monitor->MinIrisSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -701,7 +701,7 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinIrisStep']+(($monitor['MaxIrisStep']-$monitor['MinIrisStep'])*$factor))); + $step = intval(round($monitor->MinIrisStep()+(($monitor->MaxIrisStep()-$monitor->MinIrisStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } @@ -723,9 +723,9 @@ function buildControlCommand( $monitor ) break; } } - if ( $monitor['HasWhiteSpeed'] ) + if ( $monitor->HasWhiteSpeed() ) { - $speed = intval(round($monitor['MinWhiteSpeed']+(($monitor['MaxWhiteSpeed']-$monitor['MinWhiteSpeed'])*$factor))); + $speed = intval(round($monitor->MinWhiteSpeed()+(($monitor->MaxWhiteSpeed()-$monitor->MinWhiteSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -733,7 +733,7 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinWhiteStep']+(($monitor['MaxWhiteStep']-$monitor['MinWhiteStep'])*$factor))); + $step = intval(round($monitor->MinWhiteStep()+(($monitor->MaxWhiteStep()-$monitor->MinWhiteStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } @@ -755,9 +755,9 @@ function buildControlCommand( $monitor ) break; } } - if ( $monitor['HasGainSpeed'] ) + if ( $monitor->HasGainSpeed() ) { - $speed = intval(round($monitor['MinGainSpeed']+(($monitor['MaxGainSpeed']-$monitor['MinGainSpeed'])*$factor))); + $speed = intval(round($monitor->MinGainSpeed()+(($monitor->MaxGainSpeed()-$monitor->MinGainSpeed())*$factor))); $ctrlCommand .= " --speed=".$speed; } switch( $mode ) @@ -765,7 +765,7 @@ function buildControlCommand( $monitor ) case 'Abs' : case 'Rel' : { - $step = intval(round($monitor['MinGainStep']+(($monitor['MaxGainStep']-$monitor['MinGainStep'])*$factor))); + $step = intval(round($monitor->MinGainStep()+(($monitor->MaxGainStep()-$monitor->MinGainStep())*$factor))); $ctrlCommand .= " --step=".$step; break; } @@ -794,7 +794,7 @@ function buildControlCommand( $monitor ) $xFactor = ($x+1)/$short_x; } - if ( $monitor['Orientation'] != '0' ) + if ( $monitor->Orientation() != '0' ) { $conversions = array( '90' => array( @@ -848,48 +848,48 @@ function buildControlCommand( $monitor ) 'DownRight' => 'UpRight', ), ); - $new_dirn = $conversions[$monitor['Orientation']][$dirn]; + $new_dirn = $conversions[$monitor->Orientation()][$dirn]; $_REQUEST['control'] = preg_replace( "/_$dirn\$/", "_$new_dirn", $_REQUEST['control'] ); $dirn = $new_dirn; } - if ( $monitor['HasPanSpeed'] && $xFactor ) + if ( $monitor->HasPanSpeed() && $xFactor ) { - if ( $monitor['HasTurboPan'] ) + if ( $monitor->HasTurboPan() ) { if ( $xFactor >= $turbo ) { - $panSpeed = $monitor['TurboPanSpeed']; + $panSpeed = $monitor->TurboPanSpeed(); } else { $xFactor = $xFactor/$turbo; - $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); + $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); } } else { - $panSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$xFactor))); + $panSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$xFactor))); } $ctrlCommand .= " --panspeed=".$panSpeed; } - if ( $monitor['HasTiltSpeed'] && $yFactor ) + if ( $monitor->HasTiltSpeed() && $yFactor ) { - if ( $monitor['HasTurboTilt'] ) + if ( $monitor->HasTurboTilt() ) { if ( $yFactor >= $turbo ) { - $tiltSpeed = $monitor['TurboTiltSpeed']; + $tiltSpeed = $monitor->TurboTiltSpeed(); } else { $yFactor = $yFactor/$turbo; - $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); + $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); } } else { - $tiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$yFactor))); + $tiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$yFactor))); } $ctrlCommand .= " --tiltspeed=".$tiltSpeed; } @@ -900,22 +900,22 @@ function buildControlCommand( $monitor ) { if ( preg_match( '/(Left|Right)$/', $dirn ) ) { - $panStep = intval(round($monitor['MinPanStep']+(($monitor['MaxPanStep']-$monitor['MinPanStep'])*$xFactor))); + $panStep = intval(round($monitor->MinPanStep()+(($monitor->MaxPanStep()-$monitor->MinPanStep())*$xFactor))); $ctrlCommand .= " --panstep=".$panStep; } if ( preg_match( '/^(Up|Down)/', $dirn ) ) { - $tiltStep = intval(round($monitor['MinTiltStep']+(($monitor['MaxTiltStep']-$monitor['MinTiltStep'])*$yFactor))); + $tiltStep = intval(round($monitor->MinTiltStep()+(($monitor->MaxTiltStep()-$monitor->MinTiltStep())*$yFactor))); $ctrlCommand .= " --tiltstep=".$tiltStep; } break; } case 'Con' : { - if ( $monitor['AutoStopTimeout'] ) + if ( $monitor->AutoStopTimeout() ) { - $slowPanSpeed = intval(round($monitor['MinPanSpeed']+(($monitor['MaxPanSpeed']-$monitor['MinPanSpeed'])*$slow))); - $slowTiltSpeed = intval(round($monitor['MinTiltSpeed']+(($monitor['MaxTiltSpeed']-$monitor['MinTiltSpeed'])*$slow))); + $slowPanSpeed = intval(round($monitor->MinPanSpeed()+(($monitor->MaxPanSpeed()-$monitor->MinPanSpeed())*$slow))); + $slowTiltSpeed = intval(round($monitor->MinTiltSpeed()+(($monitor->MaxTiltSpeed()-$monitor->MinTiltSpeed())*$slow))); if ( (!isset($panSpeed) || ($panSpeed < $slowPanSpeed)) && (!isset($tiltSpeed) || ($tiltSpeed < $slowTiltSpeed)) ) { $ctrlCommand .= " --autostop"; @@ -943,12 +943,12 @@ function buildControlCommand( $monitor ) if ( canEdit( 'Control' ) ) { $preset = validInt($_REQUEST['preset']); $newLabel = validJsStr($_REQUEST['newLabel']); - $row = dbFetchOne( 'SELECT * FROM ControlPresets WHERE MonitorId = ? AND Preset = ?', NULL, array( $monitor['Id'], $preset ) ); + $row = dbFetchOne( 'SELECT * FROM ControlPresets WHERE MonitorId = ? AND Preset = ?', NULL, array( $monitor->Id(), $preset ) ); if ( $newLabel != $row['Label'] ) { if ( $newLabel ) { - dbQuery( 'REPLACE INTO ControlPresets ( MonitorId, Preset, Label ) VALUES ( ?, ?, ? )', array( $monitor['Id'], $preset, $newLabel ) ); + dbQuery( 'REPLACE INTO ControlPresets ( MonitorId, Preset, Label ) VALUES ( ?, ?, ? )', array( $monitor->Id(), $preset, $newLabel ) ); } else { - dbQuery( 'DELETE FROM ControlPresets WHERE MonitorId = ? AND Preset = ?', array( $monitor['Id'], $preset ) ); + dbQuery( 'DELETE FROM ControlPresets WHERE MonitorId = ? AND Preset = ?', array( $monitor->Id(), $preset ) ); } } $ctrlCommand .= " --preset=".$preset; @@ -963,3 +963,30 @@ function buildControlCommand( $monitor ) $ctrlCommand .= " --command=".$_REQUEST['control']; return( $ctrlCommand ); } + +function sendControlCommand($mid,$command) { + // Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command. + $socket = socket_create( AF_UNIX, SOCK_STREAM, 0 ); + if ( $socket < 0 ) { + Fatal( "socket_create() failed: ".socket_strerror($socket) ); + } + $sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$mid.'.sock'; + if ( @socket_connect( $socket, $sockFile ) ) { + $options = array(); + foreach ( explode( " ", $command ) as $option ) { + if ( preg_match( '/--([^=]+)(?:=(.+))?/', $option, $matches ) ) { + $options[$matches[1]] = $matches[2]?$matches[2]:1; + } + } + $optionString = jsonEncode( $options ); + if ( !socket_write( $socket, $optionString ) ) { + Fatal( "Can't write to control socket: ".socket_strerror(socket_last_error($socket)) ); + } + socket_close( $socket ); + } else if ( $command != 'quit' ) { + $command .= ' --id='.$mid; + + // Can't connect so use script + $ctrlOutput = exec( escapeshellcmd( $command ) ); + } +} // end function sendControlCommand( $mid, $command ) diff --git a/web/includes/database.php b/web/includes/database.php index b849405d9..809145032 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -32,6 +32,7 @@ function dbConnect() try { $dbConn = new PDO( ZM_DB_TYPE . ':host=' . ZM_DB_HOST . ';dbname='.ZM_DB_NAME, ZM_DB_USER, ZM_DB_PASS ); + $dbConn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch(PDOException $ex ) { echo "Unable to connect to ZM db." . $ex->getMessage(); $dbConn = null; @@ -80,7 +81,7 @@ function dbLog( $sql, $update=false ) function dbError( $sql ) { - Fatal( "SQL-ERR '".mysql_error()."', statement was '".$sql."'" ); + Fatal( "SQL-ERR '".$dbConn->errorInfo()."', statement was '".$sql."'" ); } function dbEscape( $string ) @@ -111,7 +112,7 @@ function dbQuery( $sql, $params=NULL ) { $result = $dbConn->query( $sql ); } } catch(PDOException $e) { - Fatal( "SQL-ERR '".$e.getMessage()."', statement was '".$sql."'" ); + Fatal( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."'" ); } return( $result ); } diff --git a/web/includes/functions.php b/web/includes/functions.php index e41c82030..2b73160db 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -19,2426 +19,2092 @@ // // Compatibility functions -if ( version_compare( phpversion(), "4.3.0", "<") ) -{ - function ob_get_clean() - { - $buffer = ob_get_contents(); - ob_end_clean(); - return( $buffer ); - } +if ( version_compare( phpversion(), "4.3.0", "<") ) { + function ob_get_clean() { + $buffer = ob_get_contents(); + ob_end_clean(); + return( $buffer ); + } } -function userLogin( $username, $password="", $passwordHashed=false ) -{ - global $user, $cookies; +function userLogin( $username, $password="", $passwordHashed=false ) { + global $user, $cookies; - $sql = "select * from Users where Enabled = 1"; - $sql_values = NULL; - if ( ZM_AUTH_TYPE == "builtin" ) - { - if ( $passwordHashed ) { - $sql .= " AND Username=? AND Password=?"; - } else { - $sql .= " AND Username=? AND Password=password(?)"; - } - $sql_values = array( $username, $password ); + $sql = "select * from Users where Enabled = 1"; + $sql_values = NULL; + if ( ZM_AUTH_TYPE == "builtin" ) { + if ( $passwordHashed ) { + $sql .= " AND Username=? AND Password=?"; } else { - $sql .= " AND Username = ?"; - $sql_values = array( $username ); + $sql .= " AND Username=? AND Password=password(?)"; } - $_SESSION['username'] = $username; - if ( ZM_AUTH_RELAY == "plain" ) - { - // Need to save this in session - $_SESSION['password'] = $password; + $sql_values = array( $username, $password ); + } else { + $sql .= " AND Username = ?"; + $sql_values = array( $username ); + } + $_SESSION['username'] = $username; + if ( ZM_AUTH_RELAY == "plain" ) { + // Need to save this in session + $_SESSION['password'] = $password; + } + $_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking + if ( $dbUser = dbFetchOne( $sql, NULL, $sql_values ) ) { + Info( "Login successful for user \"$username\"" ); + $_SESSION['user'] = $user = $dbUser; + if ( ZM_AUTH_TYPE == "builtin" ) { + $_SESSION['passwordHash'] = $user['Password']; } - $_SESSION['remoteAddr'] = $_SERVER['REMOTE_ADDR']; // To help prevent session hijacking - if ( $dbUser = dbFetchOne( $sql, NULL, $sql_values ) ) - { - $_SESSION['user'] = $user = $dbUser; - if ( ZM_AUTH_TYPE == "builtin" ) - { - $_SESSION['passwordHash'] = $user['Password']; - } - } - else - { - unset( $user ); - } - if ( $cookies ) - session_write_close(); -} - -function userLogout() -{ - global $user; - - unset( $_SESSION['user'] ); + } else { + Warning( "Login denied for user \"$username\"" ); unset( $user ); - - session_destroy(); + } + if ( $cookies ) + session_write_close(); } -function noCacheHeaders() -{ - header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past - header("Last-Modified: ".gmdate( "D, d M Y H:i:s" )." GMT"); // always modified - header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 - header("Cache-Control: post-check=0, pre-check=0", false); - header("Pragma: no-cache"); // HTTP/1.0 +function userLogout() { + global $user; + $username = $user['Username']; + + Info( "User \"$username\" logged out" ); + + unset( $_SESSION['user'] ); + unset( $user ); + + session_destroy(); } -function getAuthUser( $auth ) -{ - if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == "hashed" && !empty($auth) ) - { +function noCacheHeaders() { + header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past + header("Last-Modified: ".gmdate( "D, d M Y H:i:s" )." GMT"); // always modified + header("Cache-Control: no-store, no-cache, must-revalidate"); // HTTP/1.1 + header("Cache-Control: post-check=0, pre-check=0", false); + header("Pragma: no-cache"); // HTTP/1.0 +} + +function CORSHeaders() { + if ( isset( $_SERVER['HTTP_ORIGIN'] ) ) { + +# The following is left for future reference/use. + $valid = false; + $servers = dbFetchAll( 'SELECT * FROM Servers' ); + if ( sizeof($servers) <= 1 ) { +# Only need CORSHeaders in the event that there are multiple servers in use. + return; + } + foreach( dbFetchAll( 'SELECT * FROM Servers' ) as $row ) { + $Server = new Server( $row ); + if ( $_SERVER['HTTP_ORIGIN'] == $Server->Url() ) { + $valid = true; + header("Access-Control-Allow-Origin: " . $Server->Url() ); + header("Access-Control-Allow-Headers: x-requested-with,x-request"); + } + } + if ( ! $valid ) { + Warning( $_SERVER['HTTP_ORIGIN'] . " is not found in servers list." ); + } + } +} + +function getAuthUser( $auth ) { + if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == "hashed" && !empty($auth) ) { + $remoteAddr = ""; + if ( ZM_AUTH_HASH_IPS ) { + $remoteAddr = $_SERVER['REMOTE_ADDR']; + if ( !$remoteAddr ) { + Error( "Can't determine remote address for authentication, using empty string" ); $remoteAddr = ""; - if ( ZM_AUTH_HASH_IPS ) - { - $remoteAddr = $_SERVER['REMOTE_ADDR']; - if ( !$remoteAddr ) - { - Error( "Can't determine remote address for authentication, using empty string" ); - $remoteAddr = ""; - } - } - - $sql = "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1"; - foreach ( dbFetchAll( $sql ) as $user ) - { - $now = time(); - for ( $i = 0; $i < 2; $i++, $now -= (60*60) ) // Try for last two hours - { - $time = localtime( $now ); - $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5]; - $authHash = md5( $authKey ); - - if ( $auth == $authHash ) - { - return( $user ); - } - } - } + } } - Error( "Unable to authenticate user from auth hash '$auth'" ); - return( false ); + + $sql = "select Username, Password, Enabled, Stream+0, Events+0, Control+0, Monitors+0, System+0, MonitorIds from Users where Enabled = 1"; + foreach ( dbFetchAll( $sql ) as $user ) { + $now = time(); + for ( $i = 0; $i < 2; $i++, $now -= (60*60) ) { // Try for last two hours + $time = localtime( $now ); + $authKey = ZM_AUTH_HASH_SECRET.$user['Username'].$user['Password'].$remoteAddr.$time[2].$time[3].$time[4].$time[5]; + $authHash = md5( $authKey ); + + if ( $auth == $authHash ) { + return( $user ); + } + } + } + } + Error( "Unable to authenticate user from auth hash '$auth'" ); + return( false ); } -function generateAuthHash( $useRemoteAddr ) -{ - if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == "hashed" ) - { - $time = localtime(); - if ( $useRemoteAddr ) - { - $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$_SESSION['remoteAddr'].$time[2].$time[3].$time[4].$time[5]; - } - else - { - $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$time[2].$time[3].$time[4].$time[5]; - } - $auth = md5( $authKey ); +function generateAuthHash( $useRemoteAddr ) { + if ( ZM_OPT_USE_AUTH && ZM_AUTH_RELAY == "hashed" ) { + $time = localtime(); + if ( $useRemoteAddr ) { + $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$_SESSION['remoteAddr'].$time[2].$time[3].$time[4].$time[5]; + } else { + $authKey = ZM_AUTH_HASH_SECRET.$_SESSION['username'].$_SESSION['passwordHash'].$time[2].$time[3].$time[4].$time[5]; } - else - { - $auth = ""; - } - return( $auth ); + $auth = md5( $authKey ); + } else { + $auth = ""; + } + return( $auth ); } -function getStreamSrc( $args, $querySep='&' ) -{ - $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; +function getStreamSrc( $args, $querySep='&' ) { + $streamSrc = ZM_BASE_URL.ZM_PATH_ZMS; - if ( ZM_OPT_USE_AUTH ) - { - if ( ZM_AUTH_RELAY == "hashed" ) - { - $args[] = "auth=".generateAuthHash( ZM_AUTH_HASH_IPS ); - } - elseif ( ZM_AUTH_RELAY == "plain" ) - { - $args[] = "user=".$_SESSION['username']; - $args[] = "pass=".$_SESSION['password']; - } - elseif ( ZM_AUTH_RELAY == "none" ) - { - $args[] = "user=".$_SESSION['username']; - } - } - if ( !in_array( "mode=single", $args ) && !empty($GLOBALS['connkey']) ) - { - $args[] = "connkey=".$GLOBALS['connkey']; - } - if ( ZM_RAND_STREAM ) - { - $args[] = "rand=".time(); + if ( ZM_OPT_USE_AUTH ) { + if ( ZM_AUTH_RELAY == "hashed" ) { + $args[] = "auth=".generateAuthHash( ZM_AUTH_HASH_IPS ); + } elseif ( ZM_AUTH_RELAY == "plain" ) { + $args[] = "user=".$_SESSION['username']; + $args[] = "pass=".$_SESSION['password']; + } elseif ( ZM_AUTH_RELAY == "none" ) { + $args[] = "user=".$_SESSION['username']; } + } + if ( !in_array( "mode=single", $args ) && !empty($GLOBALS['connkey']) ) { + $args[] = "connkey=".$GLOBALS['connkey']; + } + if ( ZM_RAND_STREAM ) { + $args[] = "rand=".time(); + } - if ( count($args) ) - { - $streamSrc .= "?".join( $querySep, $args ); - } + if ( count($args) ) { + $streamSrc .= "?".join( $querySep, $args ); + } - return( $streamSrc ); + return( $streamSrc ); } -function getMimeType( $file ) -{ - if ( function_exists('mime_content_type') ) - { - return( mime_content_type( $file ) ); - } - elseif ( function_exists('finfo_file') ) - { - $finfo = finfo_open( FILEINFO_MIME ); - $mimeType = finfo_file( $finfo, $file ); - finfo_close($finfo); - return( $mimeType ); - } - return( trim( exec( 'file -bi '.escapeshellarg( $file ).' 2>/dev/null' ) ) ); +function getMimeType( $file ) { + if ( function_exists('mime_content_type') ) { + return( mime_content_type( $file ) ); + } elseif ( function_exists('finfo_file') ) { + $finfo = finfo_open( FILEINFO_MIME ); + $mimeType = finfo_file( $finfo, $file ); + finfo_close($finfo); + return( $mimeType ); + } + return( trim( exec( 'file -bi '.escapeshellarg( $file ).' 2>/dev/null' ) ) ); } -function outputVideoStream( $id, $src, $width, $height, $format, $title="" ) -{ - if ( file_exists( $src ) ) - $mimeType = getMimeType( $src ); - else - { - switch( $format ) +function outputVideoStream( $id, $src, $width, $height, $format, $title="" ) { + echo getVideoStreamHTML( $id, $src, $width, $height, $format, $title ); +} + +function getVideoStreamHTML( $id, $src, $width, $height, $format, $title="" ) { + $html = ''; + $width = validInt($width); + $height = validInt($height); + $title = validHtmlStr($title); + + if ( file_exists( $src ) ) { + $mimeType = getMimeType( $src ); + } else { + switch( $format ) { + case 'asf' : + $mimeType = "video/x-ms-asf"; + break; + case 'avi' : + case 'wmv' : + $mimeType = "video/x-msvideo"; + break; + case 'mov' : + $mimeType = "video/quicktime"; + break; + case 'mpg' : + case 'mpeg' : + $mimeType = "video/mpeg"; + break; + case 'swf' : + $mimeType = "application/x-shockwave-flash"; + break; + case '3gp' : + $mimeType = "video/3gpp"; + break; + default : + $mimeType = "video/$format"; + break; + } + } + if ( !$mimeType || ($mimeType == 'application/octet-stream') ) + $mimeType = 'video/'.$format; + if ( ZM_WEB_USE_OBJECT_TAGS ) { + switch( $mimeType ) { + case "video/x-ms-asf" : + case "video/x-msvideo" : + case "video/mp4" : { - case 'asf' : - $mimeType = "video/x-ms-asf"; - break; - case 'avi' : - case 'wmv' : - $mimeType = "video/x-msvideo"; - break; - case 'mov' : - $mimeType = "video/quicktime"; - break; - case 'mpg' : - case 'mpeg' : - $mimeType = "video/mpeg"; - break; - case 'swf' : - $mimeType = "application/x-shockwave-flash"; - break; - case '3gp' : - $mimeType = "video/3gpp"; - break; - default : - $mimeType = "video/$format"; - break; + if ( isWindows() ) { + return ' + + + + + + '; + } } - } - if ( !$mimeType || ($mimeType == 'application/octet-stream') ) - $mimeType = 'video/'.$format; - $objectTag = false; - if ( ZM_WEB_USE_OBJECT_TAGS ) - { - switch( $mimeType ) + case "video/quicktime" : { - case "video/x-ms-asf" : - case "video/x-msvideo" : - case "video/mp4" : - { - if ( isWindows() ) - { -?> - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + '; } - } - if ( !$objectTag ) - { -?> - -src="" -name="" -width="" -height="" -autostart="1" -autoplay="1" -showcontrols="0" -controller="0"> - - + + + + + + '; + } + } # end switch + } # end if use object tags + return ' + '; } -function outputImageStream( $id, $src, $width, $height, $title="" ) -{ - if ( canStreamIframe() ) { -?> - - - - - - - - - - - - - - <?= ZM_WEB_TITLE_PREFIX ?> - <?= validHtmlStr($title) ?> - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web/skins/flat/includes/timeline_functions.php b/web/skins/flat/includes/timeline_functions.php deleted file mode 100644 index cf92f8689..000000000 --- a/web/skins/flat/includes/timeline_functions.php +++ /dev/null @@ -1,525 +0,0 @@ -"; - if ( $scaleRange >= $minLines ) - { - $scale['range'] = $scaleRange; - break; - } - } - if ( !isset($scale['range']) ) - { - $scale['range'] = (int)($range/($scale['factor']*$align)); - } - $scale['divisor'] = 1; - while ( ($scale['range']/$scale['divisor']) > $maxLines ) - { - $scale['divisor']++; - } - $scale['lines'] = (int)($scale['range']/$scale['divisor']); - return( $scale ); -} - -function getYScale( $range, $minLines, $maxLines ) -{ - $scale['range'] = $range; - $scale['divisor'] = 1; - while ( $scale['range']/$scale['divisor'] > $maxLines ) - { - $scale['divisor']++; - } - $scale['lines'] = (int)(($scale['range']-1)/$scale['divisor'])+1; - - return( $scale ); -} - -function getSlotFrame( $slot ) -{ - $slotFrame = isset($slot['frame'])?$slot['frame']['FrameId']:1; - if ( false && $slotFrame ) - { - $slotFrame -= $monitor['PreEventCount']; - if ( $slotFrame < 1 ) - $slotFrame = 1; - } - return( $slotFrame ); -} - -function parseFilterToTree( $filter ) -{ - if ( count($filter['terms']) > 0 ) - { - $postfixExpr = array(); - $postfixStack = array(); - - $priorities = array( - '<' => 1, - '<=' => 1, - '>' => 1, - '>=' => 1, - '=' => 2, - '!=' => 2, - '=~' => 2, - '!~' => 2, - '=[]' => 2, - '![]' => 2, - 'and' => 3, - 'or' => 4, - ); - - for ( $i = 0; $i <= count($filter['terms']); $i++ ) - { - if ( !empty($filter['terms'][$i]['cnj']) ) - { - while( true ) - { - if ( !count($postfixStack) ) - { - $postfixStack[] = array( 'type'=>"cnj", 'value'=>$filter['terms'][$i]['cnj'], 'sqlValue'=>$filter['terms'][$i]['cnj']); - break; - } - elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) - { - $postfixStack[] = array( 'type'=>"cnj", 'value'=>$filter['terms'][$i]['cnj'], 'sqlValue'=>$filter['terms'][$i]['cnj']); - break; - } - elseif ( $priorities[$filter['terms'][$i]['cnj']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) - { - $postfixStack[] = array( 'type'=>"cnj", 'value'=>$filter['terms'][$i]['cnj'], 'sqlValue'=>$filter['terms'][$i]['cnj']); - break; - } - else - { - $postfixExpr[] = array_pop( $postfixStack ); - } - } - } - if ( !empty($filter['terms'][$i]['obr']) ) - { - for ( $j = 0; $j < $filter['terms'][$i]['obr']; $j++ ) - { - $postfixStack[] = array( 'type'=>"obr", 'value'=>$filter['terms'][$i]['obr']); - } - } - if ( !empty($filter['terms'][$i]['attr']) ) - { - $dtAttr = false; - switch ( $filter['terms'][$i]['attr']) - { - case 'MonitorName': - $sqlValue = 'M.'.preg_replace( '/^Monitor/', '', $filter['terms'][$i]['attr']); - break; - case 'Name': - $sqlValue = "E.Name"; - break; - case 'Cause': - $sqlValue = "E.Cause"; - break; - case 'DateTime': - $sqlValue = "E.StartTime"; - $dtAttr = true; - break; - case 'Date': - $sqlValue = "to_days( E.StartTime )"; - $dtAttr = true; - break; - case 'Time': - $sqlValue = "extract( hour_second from E.StartTime )"; - break; - case 'Weekday': - $sqlValue = "weekday( E.StartTime )"; - break; - case 'Id': - case 'Name': - case 'MonitorId': - case 'Length': - case 'Frames': - case 'AlarmFrames': - case 'TotScore': - case 'AvgScore': - case 'MaxScore': - case 'Archived': - $sqlValue = "E.".$filter['terms'][$i]['attr']; - break; - case 'DiskPercent': - $sqlValue = getDiskPercent(); - break; - case 'DiskBlocks': - $sqlValue = getDiskBlocks(); - break; - default : - $sqlValue = $filter['terms'][$i]['attr']; - break; - } - if ( $dtAttr ) - { - $postfixExpr[] = array( 'type'=>"attr", 'value'=>$filter['terms'][$i]['attr'], 'sqlValue'=>$sqlValue, 'dtAttr'=>true ); - } - else - { - $postfixExpr[] = array( 'type'=>"attr", 'value'=>$filter['terms'][$i]['attr'], 'sqlValue'=>$sqlValue ); - } - } - if ( isset($filter['terms'][$i]['op']) ) - { - if ( empty($filter['terms'][$i]['op']) ) - { - $filter['terms'][$i]['op' ]= '='; - } - switch ( $filter['terms'][$i]['op' ]) - { - case '=' : - case '!=' : - case '>=' : - case '>' : - case '<' : - case '<=' : - $sqlValue = $filter['terms'][$i]['op']; - break; - case '=~' : - $sqlValue = "regexp"; - break; - case '!~' : - $sqlValue = "not regexp"; - break; - case '=[]' : - $sqlValue = 'in ('; - break; - case '![]' : - $sqlValue = 'not in ('; - break; - } - while( true ) - { - if ( !count($postfixStack) ) - { - $postfixStack[] = array( 'type'=>"op", 'value'=>$filter['terms'][$i]['op'], 'sqlValue'=>$sqlValue ); - break; - } - elseif ( $postfixStack[count($postfixStack)-1]['type'] == 'obr' ) - { - $postfixStack[] = array( 'type'=>"op", 'value'=>$filter['terms'][$i]['op'], 'sqlValue'=>$sqlValue ); - break; - } - elseif ( $priorities[$filter['terms'][$i]['op']] < $priorities[$postfixStack[count($postfixStack)-1]['value']] ) - { - $postfixStack[] = array( 'type'=>"op", 'value'=>$filter['terms'][$i]['op'], 'sqlValue'=>$sqlValue ); - break; - } - else - { - $postfixExpr[] = array_pop( $postfixStack ); - } - } - } - if ( isset($filter['terms'][$i]['val']) ) - { - $valueList = array(); - foreach ( preg_split( '/["\'\s]*?,["\'\s]*?/', preg_replace( '/^["\']+?(.+)["\']+?$/', '$1', $filter['terms'][$i]['val' ]) ) as $value ) - { - switch ( $filter['terms'][$i]['attr']) - { - case 'MonitorName': - case 'Name': - case 'Cause': - case 'Notes': - $value = "'$value'"; - break; - case 'DateTime': - $value = "'".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."'"; - break; - case 'Date': - $value = "to_days( '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; - break; - case 'Time': - $value = "extract( hour_second from '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; - break; - case 'Weekday': - $value = "weekday( '".strftime( STRF_FMT_DATETIME_DB, strtotime( $value ) )."' )"; - break; - } - $valueList[] = $value; - } - $postfixExpr[] = array( 'type'=>"val", 'value'=>$filter['terms'][$i]['val'], 'sqlValue'=>join( ',', $valueList ) ); - } - if ( !empty($filter['terms'][$i]['cbr']) ) - { - for ( $j = 0; $j < $filter['terms'][$i]['cbr']; $j++ ) - { - while ( count($postfixStack) ) - { - $element = array_pop( $postfixStack ); - if ( $element['type'] == "obr" ) - { - $postfixExpr[count($postfixExpr)-1]['bracket'] = true; - break; - } - $postfixExpr[] = $element; - } - } - } - } - while ( count($postfixStack) ) - { - $postfixExpr[] = array_pop( $postfixStack ); - } - - $exprStack = array(); - //foreach ( $postfixExpr as $element ) - //{ - //echo $element['value']." "; - //} - //echo "
"; - foreach ( $postfixExpr as $element ) - { - if ( $element['type'] == 'attr' || $element['type'] == 'val' ) - { - $node = array( 'data'=>$element, 'count'=>0 ); - $exprStack[] = $node; - } - elseif ( $element['type'] == 'op' || $element['type'] == 'cnj' ) - { - $right = array_pop( $exprStack ); - $left = array_pop( $exprStack ); - $node = array( 'data'=>$element, 'count'=>2+$left['count']+$right['count'], 'right'=>$right, 'left'=>$left ); - $exprStack[] = $node; - } - else - { - Fatal( "Unexpected element type '".$element['type']."', value '".$element['value']."'" ); - } - } - if ( count($exprStack) != 1 ) - { - Fatal( "Expression stack has ".count($exprStack)." elements" ); - } - $exprTree = array_pop( $exprStack ); - return( $exprTree ); - } - return( false ); -} - -function _parseTreeToInfix( $node ) -{ - $expression = ''; - if ( isset($node) ) - { - if ( isset($node['left']) ) - { - if ( !empty($node['data']['bracket']) ) - $expression .= '( '; - $expression .= _parseTreeToInfix( $node['left'] ); - } - $expression .= $node['data']['value']." "; - if ( isset($node['right']) ) - { - $expression .= _parseTreeToInfix( $node['right'] ); - if ( !empty($node['data']['bracket']) ) - $expression .= ') '; - } - } - return( $expression ); -} - -function parseTreeToInfix( $tree ) -{ - return( _parseTreeToInfix( $tree ) ); -} - -function _parseTreeToSQL( $node, $cbr=false ) -{ - $expression = ''; - if ( $node ) - { - if ( isset($node['left']) ) - { - if ( !empty($node['data']['bracket']) ) - $expression .= '( '; - $expression .= _parseTreeToSQL( $node['left'] ); - } - $inExpr = $node['data']['type'] == 'op' && ($node['data']['value'] == '=[]' || $node['data']['value'] == '![]'); - $expression .= $node['data']['sqlValue']; - if ( !$inExpr ) - $expression .= ' '; - if ( $cbr ) - $expression .= ') '; - if ( isset($node['right']) ) - { - $expression .= _parseTreeToSQL( $node['right'], $inExpr ); - if ( !empty($node['data']['bracket']) ) - $expression .= ') '; - } - } - return( $expression ); -} - -function parseTreeToSQL( $tree ) -{ - return( _parseTreeToSQL( $tree ) ); -} - -function _parseTreeToFilter( $node, &$terms, &$level ) -{ - $elements = array(); - if ( $node ) - { - if ( isset($node['left']) ) - { - if ( !empty($node['data']['bracket']) ) - $terms[$level]['obr'] = 1; - _parseTreeToFilter( $node['left'], $terms, $level ); - } - if ( $node['data']['type'] == 'cnj' ) - { - $level++; - } - $terms[$level][$node['data']['type']] = $node['data']['value']; - if ( isset($node['right']) ) - { - _parseTreeToFilter( $node['right'], $terms, $level ); - if ( !empty($node['data']['bracket']) ) - $terms[$level]['cbr'] = 1; - } - } -} - -function parseTreeToFilter( $tree ) -{ - $terms = array(); - if ( isset($tree) ) - { - $level = 0; - _parseTreeToFilter( $tree, $terms, $level ); - } - return( array( 'terms' => $terms ) ); -} - -function parseTreeToQuery( $tree ) -{ - $filter = parseTreeToFilter( $tree ); - parseFilter( $filter, false, '&' ); - return( $filter['query'] ); -} - -function _drawTree( $node, $level ) -{ - if ( isset($node['left']) ) - { - _drawTree( $node['left'], $level+1 ); - } - echo str_repeat( ".", $level*2 ).$node['data']['value']."
"; - if ( isset($node['right']) ) - { - _drawTree( $node['right'], $level+1 ); - } -} - -function drawTree( $tree ) -{ - _drawTree( $tree, 0 ); -} - -function _extractDatetimeRange( &$node, &$minTime, &$maxTime, &$expandable, $subOr ) -{ - $pruned = $leftPruned = $rightPruned = false; - if ( $node ) - { - if ( isset($node['left']) && isset($node['right']) ) - { - if ( $node['data']['type'] == 'cnj' && $node['data']['value'] == 'or' ) - { - $subOr = true; - } - elseif ( !empty($node['left']['data']['dtAttr']) ) - { - if ( $subOr ) - { - $expandable = false; - } - elseif ( $node['data']['type'] == 'op' ) - { - if ( $node['data']['value'] == '>' || $node['data']['value'] == '>=' ) - { - if ( !$minTime || $minTime > $node['right']['data']['sqlValue'] ) - { - $minTime = $node['right']['data']['value']; - return( true ); - } - } - if ( $node['data']['value'] == '<' || $node['data']['value'] == '<=' ) - { - if ( !$maxTime || $maxTime < $node['right']['data']['sqlValue'] ) - { - $maxTime = $node['right']['data']['value']; - return( true ); - } - } - } - else - { - Fatal( "Unexpected node type '".$node['data']['type']."'" ); - } - return( false ); - } - - $leftPruned = _extractDatetimeRange( $node['left'], $minTime, $maxTime, $expandable, $subOr ); - $rightPruned = _extractDatetimeRange( $node['right'], $minTime, $maxTime, $expandable, $subOr ); - - if ( $leftPruned && $rightPruned ) - { - $pruned = true; - } - elseif ( $leftPruned ) - { - $node = $node['right']; - } - elseif ( $rightPruned ) - { - $node = $node['left']; - } - } - } - return( $pruned ); -} - -function extractDatetimeRange( &$tree, &$minTime, &$maxTime, &$expandable ) -{ - $minTime = ""; - $maxTime = ""; - $expandable = true; - - _extractDateTimeRange( $tree, $minTime, $maxTime, $expandable, false ); -} - -function appendDatetimeRange( &$tree, $minTime, $maxTime=false ) -{ - $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'DateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); - $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$minTime, 'sqlValue'=>$minTime ), 'count'=>0 ); - $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'>=', 'sqlValue'=>'>=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); - if ( isset($tree) ) - { - $cnjNode = array( 'data'=>array( 'type'=>'cnj', 'value'=>'and', 'sqlValue'=>'and' ), 'count'=>2+$tree['count']+$opNode['count'], 'left'=>$tree, 'right'=>$opNode ); - $tree = $cnjNode; - } - else - { - $tree = $opNode; - } - - if ( $maxTime ) - { - $attrNode = array( 'data'=>array( 'type'=>'attr', 'value'=>'DateTime', 'sqlValue'=>'E.StartTime', 'dtAttr'=>true ), 'count'=>0 ); - $valNode = array( 'data'=>array( 'type'=>'val', 'value'=>$maxTime, 'sqlValue'=>$maxTime ), 'count'=>0 ); - $opNode = array( 'data'=>array( 'type'=>'op', 'value'=>'<=', 'sqlValue'=>'<=' ), 'count'=>2, 'left'=>$attrNode, 'right'=>$valNode ); - $cnjNode = array( 'data'=>array( 'type'=>'cnj', 'value'=>'and', 'sqlValue'=>'and' ), 'count'=>2+$tree['count']+$opNode['count'], 'left'=>$tree, 'right'=>$opNode ); - $tree = $cnjNode; - } -} - -?> diff --git a/web/skins/flat/js/Makefile.am b/web/skins/flat/js/Makefile.am deleted file mode 100644 index a5b95f5e6..000000000 --- a/web/skins/flat/js/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/flat/js - -dist_web_DATA = \ - skin.js \ - skin.js.php diff --git a/web/skins/flat/js/jquery-1.4.2.min.js b/web/skins/flat/js/jquery-1.4.2.min.js deleted file mode 100644 index 7c2430802..000000000 --- a/web/skins/flat/js/jquery-1.4.2.min.js +++ /dev/null @@ -1,154 +0,0 @@ -/*! - * jQuery JavaScript Library v1.4.2 - * http://jquery.com/ - * - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Sat Feb 13 22:33:48 2010 -0500 - */ -(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, -Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& -(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, -a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== -"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, -function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; -var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, -parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= -false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= -s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, -applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; -else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, -a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== -w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, -cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= -c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); -a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, -function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); -k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), -C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= -e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& -f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; -if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", -e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, -"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, -d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, -e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); -t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| -g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, -CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, -g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, -text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, -setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= -h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== -"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, -h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& -q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; -if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); -(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: -function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= -{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== -"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", -d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? -a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== -1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= -c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, -wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, -prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, -this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); -return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, -""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); -return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", -""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= -c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? -c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= -function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= -Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, -"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= -a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= -a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== -"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, -serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), -function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, -global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& -e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? -"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== -false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= -false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", -c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| -d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); -g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== -1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== -"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; -if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== -"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| -c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; -this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= -this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, -e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; -a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); -c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, -d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- -f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": -"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in -e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/web/skins/flat/js/skin.js b/web/skins/flat/js/skin.js deleted file mode 100644 index cc1476a9a..000000000 --- a/web/skins/flat/js/skin.js +++ /dev/null @@ -1,310 +0,0 @@ -// -// ZoneMinder base static javascript file, $Date$, $Revision$ -// Copyright (C) 2001-2008 Philip Coombes -// -// 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 file should only contain static JavaScript and no php. -// Use skin.js.php for JavaScript that need pre-processing -// - -// Javascript window sizes -var popupSizes = { - 'bandwidth': { 'width': 280, 'height': 160 }, - 'console': { 'width': 750, 'height': 312 }, - 'control': { 'width': 380, 'height': 480 }, - 'controlcaps': { 'width': 780, 'height': 320 }, - 'controlcap': { 'width': 400, 'height': 400 }, - 'cycle': { 'addWidth': 16, 'minWidth': 384, 'addHeight': 48 }, - 'device': { 'width': 260, 'height': 150 }, - 'devices': { 'width': 400, 'height': 240 }, - 'donate': { 'width': 500, 'height': 280 }, - 'event': { 'addWidth': 108, 'minWidth': 496, 'addHeight': 200, minHeight: 540 }, - 'eventdetail': { 'width': 560, 'height': 290 }, - 'events': { 'width': 1020, 'height': 480 }, - 'export': { 'width': 400, 'height': 340 }, - 'filter': { 'width': 740, 'height': 390 }, - 'filtersave': { 'width': 560, 'height': 160 }, - 'frame': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 100 }, - 'frames': { 'width': 500, 'height': 300 }, - 'function': { 'width': 248, 'height': 130 }, - 'group': { 'width': 360, 'height': 180 }, - 'groups': { 'width': 460, 'height': 220 }, - 'image': { 'addWidth': 48, 'addHeight': 80 }, - 'log': { 'width': 980, 'height': 720 }, - 'login': { 'width': 720, 'height': 480 }, - 'logout': { 'width': 240, 'height': 100 }, - 'monitor': { 'width': 500, 'height': 660 }, - 'monitorpreset':{ 'width': 400, 'height': 250 }, - 'monitorprobe': { 'width': 500, 'height': 240 }, - 'monitorselect':{ 'width': 160, 'height': 200 }, - 'montage': { 'width': -1, 'height': -1 }, - 'optionhelp': { 'width': 400, 'height': 320 }, - 'options': { 'width': 1370, 'height': 620 }, - 'preset': { 'width': 300, 'height': 120 }, - 'settings': { 'width': 200, 'height': 225 }, - 'state': { 'width': 400, 'height': 154 }, - 'stats': { 'width': 740, 'height': 200 }, - 'timeline': { 'width': 760, 'height': 540 }, - 'user': { 'width': 340, 'height': 680 }, - 'version': { 'width': 440, 'height': 160 }, - 'video': { 'width': 420, 'height': 360 }, - 'videoview': { 'addWidth': 48, 'addHeight': 80 }, - 'watch': { 'addWidth': 96, 'minWidth': 420, 'addHeight': 384 }, - 'zone': { 'addWidth': 450, 'addHeight': 200, 'minHeight': 450 }, - 'zones': { 'addWidth': 72, 'addHeight': 232 } -}; - -var popupOptions = "resizable,scrollbars,status=no"; - -function checkSize() { - if (window.outerHeight) { - var w = window.outerWidth; - var prevW = w; - var h = window.outerHeight; - var prevH = h; - if (h > screen.availHeight) - h = screen.availHeight; - if (w > screen.availWidth) - w = screen.availWidth; - if (w != prevW || h != prevH) - window.resizeTo(w,h); - } -} - -// Deprecated -function newWindow( url, name, width, height ) -{ - var windowId = window.open( url, name, popupOptions+",width="+width+",height="+height ); -} - -function getPopupSize( tag, width, height ) -{ - var popupSize = Object.clone( popupSizes[tag] ); - if ( !popupSize ) - { - Error( "Can't find window size for tag '"+tag+"'" ); - return( { 'width': 0, 'height': 0 } ); - } - if ( popupSize.width && popupSize.height ) - { - if ( width || height ) - Warning( "Ignoring passed dimensions "+width+"x"+height+" when getting popup size for tag '"+tag+"'" ); - return( popupSize ); - } - if ( popupSize.addWidth ) - { - popupSize.width = popupSize.addWidth; - if ( !width ) - Error( "Got addWidth but no passed width when getting popup size for tag '"+tag+"'" ); - else - popupSize.width += parseInt(width); - } - else if ( width ) - { - popupSize.width = width; - Error( "Got passed width but no addWidth when getting popup size for tag '"+tag+"'" ); - } - if ( popupSize.minWidth && popupSize.width < popupSize.minWidth ) - { - Warning( "Adjusting to minimum width when getting popup size for tag '"+tag+"'" ); - popupSize.width = popupSize.minWidth; - } - if ( popupSize.addHeight ) - { - popupSize.height = popupSize.addHeight; - if ( !height ) - Error( "Got addHeight but no passed height when getting popup size for tag '"+tag+"'" ); - else - popupSize.height += parseInt(height); - } - else if ( height ) - { - popupSize.height = height; - Error( "Got passed height but no addHeight when getting popup size for tag '"+tag+"'" ); - } - if ( popupSize.minHeight && popupSize.height < popupSize.minHeight ) - { - Warning( "Adjusting to minimum height when getting popup size for tag '"+tag+"'" ); - popupSize.height = popupSize.minHeight; - } - Debug( popupSize ); - return( popupSize ); -} - -function zmWindow() -{ - var zmWin = window.open( 'http://www.zoneminder.com', 'ZoneMinder' ); - zmWin.focus(); -} - -function createPopup( url, name, tag, width, height ) -{ - var popupSize = getPopupSize( tag, width, height ); - var popupDimensions = ""; - if ( popupSize.width > 0 ) - popupDimensions += ",width="+popupSize.width; - if ( popupSize.height > 0 ) - popupDimensions += ",height="+popupSize.height; - var popup = window.open( url, name, popupOptions+popupDimensions ); - popup.focus(); -} - -function createEventPopup( eventId, eventFilter, width, height ) -{ - var url = '?view=event&eid='+eventId; - if ( eventFilter ) - url += eventFilter; - var name = 'zmEvent'; - var popupSize = getPopupSize( 'event', width, height ); - var popup = window.open( url, name, popupOptions+",width="+popupSize.width+",height="+popupSize.height ); - popup.focus(); -} - -function createFramesPopup( eventId, width, height ) -{ - var url = '?view=frames&eid='+eventId; - var name = 'zmFrames'; - var popupSize = getPopupSize( 'frames', width, height ); - var popup = window.open( url, name, popupOptions+",width="+popupSize.width+",height="+popupSize.height ); - popup.focus(); -} - -function createFramePopup( eventId, frameId, width, height ) -{ - var url = '?view=frame&eid='+eventId+'&fid='+frameId; - var name = 'zmFrame'; - var popupSize = getPopupSize( 'frame', width, height ); - var popup = window.open( url, name, popupOptions+",width="+popupSize.width+",height="+popupSize.height ); - popup.focus(); -} - -function windowToFront() -{ - top.window.focus(); -} - -function closeWindow() -{ - top.window.close(); -} - -function refreshWindow() -{ - window.location.reload( true ); -} - -function refreshParentWindow() -{ - if ( window.opener ) - window.opener.location.reload( true ); -} - -//Shows a message if there is an error in the streamObj or the stream doesn't exist. Returns true if error, false otherwise. -function checkStreamForErrors( funcName, streamObj ) -{ - if ( !streamObj ) - { - Error( funcName+": stream object was null" ); - return true; - } - if ( streamObj.result == "Error" ) - { - Error( funcName+" stream error: "+streamObj.message ); - return true; - } - return false; -} - -function secsToTime( seconds ) -{ - var timeString = "--"; - if ( seconds < 60 ) - timeString = seconds.toString(); - else if ( seconds < 60*60 ) - { - var timeMins = parseInt(seconds/60); - var timeSecs = seconds%60; - if ( timeSecs < 10 ) - timeSecs = '0'+timeSecs.toString().substr( 0, 4 ); - else - timeSecs = timeSecs.toString().substr( 0, 5 ); - timeString = timeMins+":"+timeSecs; - } - else - { - var timeHours = parseInt(seconds/3600); - var timeMins = (seconds%3600)/60; - var timeSecs = seconds%60; - if ( timeMins < 10 ) - timeMins = '0'+timeMins.toString().substr( 0, 4 ); - else - timeMins = timeMins.toString().substr( 0, 5 ); - if ( timeSecs < 10 ) - timeSecs = '0'+timeSecs.toString().substr( 0, 4 ); - else - timeSecs = timeSecs.toString().substr( 0, 5 ); - timeString = timeHours+":"+timeMins+":"+timeSecs; - } - return( timeString ); -} - -function submitTab( tab ) -{ - var form = $('contentForm'); - form.action.value = ""; - form.tab.value = tab; - form.submit(); -} - -function configureDeleteButton( element ) -{ - var form = element.form; - var checked = element.checked; - if ( !checked ) - { - for ( var i = 0; i < form.elements.length; i++ ) - { - if ( form.elements[i].name == element.name ) - { - if ( form.elements[i].checked ) - { - checked = true; - break; - } - } - } - } - form.deleteBtn.disabled = !checked; -} - -function confirmDelete( message ) -{ - return( confirm( message?message:'Are you sure you wish to delete?' ) ); -} - -if ( refreshParent ) -{ - refreshParentWindow(); -} - -if ( focusWindow ) -{ - windowToFront(); -} - -window.addEvent( 'domready', checkSize); diff --git a/web/skins/flat/js/skin.js.php b/web/skins/flat/js/skin.js.php deleted file mode 100644 index 16b8f055e..000000000 --- a/web/skins/flat/js/skin.js.php +++ /dev/null @@ -1,40 +0,0 @@ - -var AJAX_TIMEOUT = ; - -var currentView = ''; -var thisUrl = ""; -var skinPath = ""; - -var canEditSystem = ; -var canViewSystem = ; - -var refreshParent = ; - -var focusWindow = ; - -var imagePrefix = ""; diff --git a/web/skins/flat/lang/Makefile.am b/web/skins/flat/lang/Makefile.am deleted file mode 100644 index 272014a19..000000000 --- a/web/skins/flat/lang/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/flat/lang - -dist_web_DATA = # No files here diff --git a/web/skins/flat/skin.php b/web/skins/flat/skin.php deleted file mode 100644 index 31d72a79b..000000000 --- a/web/skins/flat/skin.php +++ /dev/null @@ -1,67 +0,0 @@ - diff --git a/web/skins/flat/views/Makefile.am b/web/skins/flat/views/Makefile.am deleted file mode 100644 index 84b6b9811..000000000 --- a/web/skins/flat/views/Makefile.am +++ /dev/null @@ -1,55 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -SUBDIRS = \ - css \ - js - -webdir = @WEB_PREFIX@/skins/flat/views - -dist_web_DATA = \ - bandwidth.php \ - blank.php \ - console.php \ - controlcap.php \ - controlcaps.php \ - control.php \ - controlpreset.php \ - cycle.php \ - device.php \ - devices.php \ - donate.php \ - error.php \ - eventdetail.php \ - event.php \ - events.php \ - export.php \ - filter.php \ - filtersave.php \ - frame.php \ - frames.php \ - function.php \ - group.php \ - groups.php \ - log.php \ - login.php \ - logout.php \ - Makefile.am \ - monitor.php \ - monitorpreset.php \ - monitorprobe.php \ - montage.php \ - none.php \ - optionhelp.php \ - options.php \ - postlogin.php \ - settings.php \ - state.php \ - stats.php \ - status.php \ - timeline.php \ - user.php \ - version.php \ - video.php \ - watch.php \ - zone.php \ - zones.php diff --git a/web/skins/flat/views/bandwidth.php b/web/skins/flat/views/bandwidth.php deleted file mode 100644 index 5c09ed446..000000000 --- a/web/skins/flat/views/bandwidth.php +++ /dev/null @@ -1,58 +0,0 @@ - - -
- -
-
- - -

-

-
- -
-
-
-
- - diff --git a/web/skins/flat/views/blank.php b/web/skins/flat/views/blank.php deleted file mode 100644 index ac5c9caa7..000000000 --- a/web/skins/flat/views/blank.php +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - diff --git a/web/skins/flat/views/console.php b/web/skins/flat/views/console.php deleted file mode 100644 index ffe6f8821..000000000 --- a/web/skins/flat/views/console.php +++ /dev/null @@ -1,352 +0,0 @@ - $SLANG['Events'], - "filter" => array( - "terms" => array( - ) - ), - ), - array( - "title" => $SLANG['Hour'], - "filter" => array( - "terms" => array( - array( "attr" => "DateTime", "op" => ">=", "val" => "-1 hour" ), - ) - ), - ), - array( - "title" => $SLANG['Day'], - "filter" => array( - "terms" => array( - array( "attr" => "DateTime", "op" => ">=", "val" => "-1 day" ), - ) - ), - ), - array( - "title" => $SLANG['Week'], - "filter" => array( - "terms" => array( - array( "attr" => "DateTime", "op" => ">=", "val" => "-7 day" ), - ) - ), - ), - array( - "title" => $SLANG['Month'], - "filter" => array( - "terms" => array( - array( "attr" => "DateTime", "op" => ">=", "val" => "-1 month" ), - ) - ), - ), - array( - "title" => $SLANG['Archived'], - "filter" => array( - "terms" => array( - array( "attr" => "Archived", "op" => "=", "val" => "1" ), - ) - ), - ), -); - -$running = daemonCheck(); -$status = $running?$SLANG['Running']:$SLANG['Stopped']; - -$group = NULL; -if ( ! empty($_COOKIE['zmGroup']) ) { - if ( $group = dbFetchOne( 'select * from Groups where Id = ?', NULL, array($_COOKIE['zmGroup'])) ) - $groupIds = array_flip(explode( ',', $group['MonitorIds'] )); -} - -noCacheHeaders(); - -$maxWidth = 0; -$maxHeight = 0; -$cycleCount = 0; -$minSequence = 0; -$maxSequence = 1; -$seqIdList = array(); -$monitors = dbFetchAll( "select * from Monitors order by Sequence asc" ); -$displayMonitors = array(); -for ( $i = 0; $i < count($monitors); $i++ ) -{ - if ( !visibleMonitor( $monitors[$i]['Id'] ) ) - { - continue; - } - if ( $group && !empty($groupIds) && !array_key_exists( $monitors[$i]['Id'], $groupIds ) ) - { - continue; - } - $monitors[$i]['Show'] = true; - if ( empty($minSequence) || ($monitors[$i]['Sequence'] < $minSequence) ) - { - $minSequence = $monitors[$i]['Sequence']; - } - if ( $monitors[$i]['Sequence'] > $maxSequence ) - { - $maxSequence = $monitors[$i]['Sequence']; - } - $monitors[$i]['zmc'] = zmcStatus( $monitors[$i] ); - $monitors[$i]['zma'] = zmaStatus( $monitors[$i] ); - $monitors[$i]['ZoneCount'] = dbFetchOne( 'select count(Id) as ZoneCount from Zones where MonitorId = ?', 'ZoneCount', array($monitors[$i]['Id']) ); - $counts = array(); - for ( $j = 0; $j < count($eventCounts); $j++ ) - { - $filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['terms']), array( "cnj" => "and", "attr" => "MonitorId", "op" => "=", "val" => $monitors[$i]['Id'] ) ); - parseFilter( $filter ); - $counts[] = "count(if(1".$filter['sql'].",1,NULL)) as EventCount$j"; - $monitors[$i]['eventCounts'][$j]['filter'] = $filter; - } - $sql = "select ".join($counts,", ")." from Events as E where MonitorId = ?"; - $counts = dbFetchOne( $sql, NULL, array($monitors[$i]['Id']) ); - if ( $monitors[$i]['Function'] != 'None' ) - { - $cycleCount++; - $scaleWidth = reScale( $monitors[$i]['Width'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); - $scaleHeight = reScale( $monitors[$i]['Height'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); - if ( $maxWidth < $scaleWidth ) $maxWidth = $scaleWidth; - if ( $maxHeight < $scaleHeight ) $maxHeight = $scaleHeight; - } - if ( $counts ) $monitors[$i] = array_merge( $monitors[$i], $counts ); - $seqIdList[] = $monitors[$i]['Id']; - $displayMonitors[] = $monitors[$i]; -} -$lastId = 0; -$seqIdUpList = array(); -foreach ( $seqIdList as $seqId ) -{ - if ( !empty($lastId) ) - $seqIdUpList[$seqId] = $lastId; - else - $seqIdUpList[$seqId] = $seqId; - $lastId = $seqId; -} -$lastId = 0; -$seqIdDownList = array(); -foreach ( array_reverse($seqIdList) as $seqId ) -{ - if ( !empty($lastId) ) - $seqIdDownList[$seqId] = $lastId; - else - $seqIdDownList[$seqId] = $seqId; - $lastId = $seqId; -} - -$cycleWidth = $maxWidth; -$cycleHeight = $maxHeight; - -$eventsView = ZM_WEB_EVENTS_VIEW; -$eventsWindow = 'zm'.ucfirst(ZM_WEB_EVENTS_VIEW); - -$eventCount = 0; -for ( $i = 0; $i < count($eventCounts); $i++ ) -{ - $eventCounts[$i]['total'] = 0; -} -$zoneCount = 0; -foreach( $displayMonitors as $monitor ) -{ - for ( $i = 0; $i < count($eventCounts); $i++ ) - { - $eventCounts[$i]['total'] += $monitor['EventCount'.$i]; - } - $zoneCount += $monitor['ZoneCount']; -} - -$seqUpFile = getSkinFile( 'graphics/seq-u.gif' ); -$seqDownFile = getSkinFile( 'graphics/seq-d.gif' ); - -$versionClass = (ZM_DYN_DB_VERSION&&(ZM_DYN_DB_VERSION!=ZM_VERSION))?'errorText':''; - -xhtmlHeaders( __FILE__, $SLANG['Console'] ); -?> - -
-
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
'.$SLANG['Fn'.$monitor['Function']].( empty($monitor['Enabled']) ? ', disabled' : '' ) .'', canEdit( 'Monitors' ) ) ?>'.$monitor['Device'].' ('.$monitor['Channel'].')', canEdit( 'Monitors' ) ) ?>'.preg_replace( '/^.*@/', '', $monitor['Host'] ).'', canEdit( 'Monitors' ) ) ?>'.preg_replace( '/^.*\//', '', $monitor['Path'] ).'', canEdit( 'Monitors' ) ) ?>'.preg_replace( '/^.*\//', '', $monitor['Path'] ).'', canEdit( 'Monitors' ) ) ?>'.$shortpath.'', canEdit( 'Monitors' ) ) ?>'.preg_replace( '/^.*\//', '', $monitor['Path'] ).'', canEdit( 'Monitors' ) ) ?> ', $monitor['Sequence']>$minSequence ) ?>', $monitor['Sequence']<$maxSequence ) ?> disabled="disabled"/>
-
-
-
- - diff --git a/web/skins/flat/views/control.php b/web/skins/flat/views/control.php deleted file mode 100644 index 28845243d..000000000 --- a/web/skins/flat/views/control.php +++ /dev/null @@ -1,79 +0,0 @@ - - -
- -
-
- -
-
-
- - diff --git a/web/skins/flat/views/controlcap.php b/web/skins/flat/views/controlcap.php deleted file mode 100644 index 6529bea12..000000000 --- a/web/skins/flat/views/controlcap.php +++ /dev/null @@ -1,512 +0,0 @@ - $SLANG['New'], - 'Type' => "Local", - 'Protocol' => "", - 'CanWake' => "", - 'CanSleep' => "", - 'CanReset' => "", - 'CanMove' => "", - 'CanMoveDiag' => "", - 'CanMoveMap' => "", - 'CanMoveAbs' => "", - 'CanMoveRel' => "", - 'CanMoveCon' => "", - 'CanPan' => "", - 'MinPanRange' => "", - 'MaxPanRange' => "", - 'MinPanStep' => "", - 'MaxPanStep' => "", - 'HasPanSpeed' => "", - 'MinPanSpeed' => "", - 'MaxPanSpeed' => "", - 'HasTurboPan' => "", - 'TurboPanSpeed' => "", - 'CanTilt' => "", - 'MinTiltRange' => "", - 'MaxTiltRange' => "", - 'MinTiltStep' => "", - 'MaxTiltStep' => "", - 'HasTiltSpeed' => "", - 'MinTiltSpeed' => "", - 'MaxTiltSpeed' => "", - 'HasTurboTilt' => "", - 'TurboTiltSpeed' => "", - 'CanZoom' => "", - 'CanZoomAbs' => "", - 'CanZoomRel' => "", - 'CanZoomCon' => "", - 'MinZoomRange' => "", - 'MaxZoomRange' => "", - 'MinZoomStep' => "", - 'MaxZoomStep' => "", - 'HasZoomSpeed' => "", - 'MinZoomSpeed' => "", - 'MaxZoomSpeed' => "", - 'CanFocus' => "", - 'CanAutoFocus' => "", - 'CanFocusAbs' => "", - 'CanFocusRel' => "", - 'CanFocusCon' => "", - 'MinFocusRange' => "", - 'MaxFocusRange' => "", - 'MinFocusStep' => "", - 'MaxFocusStep' => "", - 'HasFocusSpeed' => "", - 'MinFocusSpeed' => "", - 'MaxFocusSpeed' => "", - 'CanIris' => "", - 'CanAutoIris' => "", - 'CanIrisAbs' => "", - 'CanIrisRel' => "", - 'CanIrisCon' => "", - 'MinIrisRange' => "", - 'MaxIrisRange' => "", - 'MinIrisStep' => "", - 'MaxIrisStep' => "", - 'HasIrisSpeed' => "", - 'MinIrisSpeed' => "", - 'MaxIrisSpeed' => "", - 'CanGain' => "", - 'CanAutoGain' => "", - 'CanGainAbs' => "", - 'CanGainRel' => "", - 'CanGainCon' => "", - 'MinGainRange' => "", - 'MaxGainRange' => "", - 'MinGainStep' => "", - 'MaxGainStep' => "", - 'HasGainSpeed' => "", - 'MinGainSpeed' => "", - 'MaxGainSpeed' => "", - 'CanWhite' => "", - 'CanAutoWhite' => "", - 'CanWhiteAbs' => "", - 'CanWhiteRel' => "", - 'CanWhiteCon' => "", - 'MinWhiteRange' => "", - 'MaxWhiteRange' => "", - 'MinWhiteStep' => "", - 'MaxWhiteStep' => "", - 'HasWhiteSpeed' => "", - 'MinWhiteSpeed' => "", - 'MaxWhiteSpeed' => "", - 'HasPresets' => "", - 'NumPresets' => "", - 'HasHomePreset' => "", - 'CanSetPresets' => "", - ); - } - $newControl = $control; -} - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['ControlCap']." - ".$newControl['Name'] ); -?> - -
- -
-
    -$value ) -{ - if ( $tab == $name ) - { -?> -
  • - -
  • - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -$SLANG['Local'], 'Remote'=>$SLANG['Remote'], 'Ffmpeg'=>$SLANG['Ffmpeg'], 'Libvlc'=>$SLANG['Libvlc'], 'cURL'=>"cURL"); -?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked">
checked="checked"/>
checked="checked"/>
checked="checked">
checked="checked"/>
checked="checked"/>
checked="checked">
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked">
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
checked="checked"/>
-
- disabled="disabled"/> -
-
-
-
- - diff --git a/web/skins/flat/views/controlcaps.php b/web/skins/flat/views/controlcaps.php deleted file mode 100644 index 4007caa79..000000000 --- a/web/skins/flat/views/controlcaps.php +++ /dev/null @@ -1,89 +0,0 @@ - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
disabled="disabled"/>
-
- disabled="disabled"/> -
-
-
-
- - diff --git a/web/skins/flat/views/controlpreset.php b/web/skins/flat/views/controlpreset.php deleted file mode 100644 index d89eb0567..000000000 --- a/web/skins/flat/views/controlpreset.php +++ /dev/null @@ -1,70 +0,0 @@ - - -
- -
-
- - - - - -

-

-
- -
-
-
-
- - diff --git a/web/skins/flat/views/css/Makefile.am b/web/skins/flat/views/css/Makefile.am deleted file mode 100644 index 3e0ef100b..000000000 --- a/web/skins/flat/views/css/Makefile.am +++ /dev/null @@ -1,31 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/flat/views/css - -dist_web_DATA = \ - console.css \ - controlcaps.css \ - control.css \ - devices.css \ - event.css \ - events.css \ - export.css \ - filter.css \ - frame.css \ - frames.css \ - groups.css \ - log.css \ - monitor.css \ - montage_2wide.css \ - montage_3wide50enlarge.css \ - montage_3wide.css \ - montage_4wide.css \ - montage.css \ - montage_freeform.css \ - options.css \ - stats.css \ - timeline.css \ - timeline.css.php \ - video.css \ - watch.css \ - zone.css diff --git a/web/skins/flat/views/css/control.css b/web/skins/flat/views/css/control.css deleted file mode 100644 index 0096ffcd7..000000000 --- a/web/skins/flat/views/css/control.css +++ /dev/null @@ -1 +0,0 @@ -@import url(../../css/control.css); diff --git a/web/skins/flat/views/css/timeline.css.php b/web/skins/flat/views/css/timeline.css.php deleted file mode 100644 index 3bc75fbcf..000000000 --- a/web/skins/flat/views/css/timeline.css.php +++ /dev/null @@ -1,77 +0,0 @@ -.chartSize { - width: px; - height: px; -} - -.graphSize { - width: px; - height: px; -} - -.graphHeight { - height: px; -} - -.graphWidth { - width: px; -} - -.imageSize { - width: px; - height: px; -} - -.imageHeight { - height: px; -} - -.activitySize { - width: px; - height: px; -} - -.eventsSize { - width: px; - height: px; -} - -.eventsHeight { - height: px; -} - -#chartPanel .eventsPos { - top: px; -} - -#chartPanel .activityPos { - top: px; -} - -#chartPanel .eventsPos { - top: px; -} - -.monitorColour { - background-color: ; -} - diff --git a/web/skins/flat/views/cycle.php b/web/skins/flat/views/cycle.php deleted file mode 100644 index 95757c54c..000000000 --- a/web/skins/flat/views/cycle.php +++ /dev/null @@ -1,128 +0,0 @@ - - -
- -
-
- -
-
-
- - diff --git a/web/skins/flat/views/device.php b/web/skins/flat/views/device.php deleted file mode 100644 index a5d59096f..000000000 --- a/web/skins/flat/views/device.php +++ /dev/null @@ -1,67 +0,0 @@ - "", - "Name" => "New Device", - "KeyString" => "" - ); -} - -xhtmlHeaders( __FILE__, $SLANG['Device']." - ".$newDevice['Name'] ); -?> - -
- -
-
- - - - - - - - - - - - - - -
-
- disabled="disabled"/> -
-
-
-
- - diff --git a/web/skins/flat/views/devices.php b/web/skins/flat/views/devices.php deleted file mode 100644 index 15100a4cd..000000000 --- a/web/skins/flat/views/devices.php +++ /dev/null @@ -1,86 +0,0 @@ - - -
- -
-
- - - - - - - - - - - - - - - -
'.validHtmlStr($device['Name']).' ('.validHtmlStr($device['KeyString']).')', canEdit( 'Devices' ) ) ?> onclick="switchDeviceOn( this, '' )"/> onclick="switchDeviceOff( this, '' )"/> disabled="disabled"/>
-
- /> - - -
-
-
-
- - diff --git a/web/skins/flat/views/donate.php b/web/skins/flat/views/donate.php deleted file mode 100644 index 34ee4efa3..000000000 --- a/web/skins/flat/views/donate.php +++ /dev/null @@ -1,65 +0,0 @@ - $SLANG['DonateYes'], - "hour" => $SLANG['DonateRemindHour'], - "day" => $SLANG['DonateRemindDay'], - "week" => $SLANG['DonateRemindWeek'], - "month" => $SLANG['DonateRemindMonth'], - "never" => $SLANG['DonateRemindNever'], - "already" => $SLANG['DonateAlready'], -); - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['Donate'] ); -?> - -
- -
-
- - -

- -

-

- -

-
- - -
-
-
-
- - diff --git a/web/skins/flat/views/error.php b/web/skins/flat/views/error.php deleted file mode 100644 index c18ae307e..000000000 --- a/web/skins/flat/views/error.php +++ /dev/null @@ -1,43 +0,0 @@ - - -
- -
-

- -

-

- -

-

- -

-
-
- - diff --git a/web/skins/flat/views/event.php b/web/skins/flat/views/event.php deleted file mode 100644 index 3d06f4c9e..000000000 --- a/web/skins/flat/views/event.php +++ /dev/null @@ -1,224 +0,0 @@ - $SLANG['ReplaySingle'], - 'all' => $SLANG['ReplayAll'], - 'gapless' => $SLANG['ReplayGapless'], -); - -if ( isset( $_REQUEST['streamMode'] ) ) - $streamMode = validHtmlStr($_REQUEST['streamMode']); -else - $streamMode = canStream()?'stream':'stills'; - -if ( isset( $_REQUEST['replayMode'] ) ) - $replayMode = validHtmlStr($_REQUEST['replayMode']); -if ( isset( $_COOKIE['replayMode']) && preg_match('#^[a-z]+$#', $_COOKIE['replayMode']) ) - $replayMode = validHtmlStr($_COOKIE['replayMode']); -else { - $keys = array_keys( $replayModes ); - $replayMode = array_shift( $keys ); -} - -parseSort(); -parseFilter( $_REQUEST['filter'] ); -$filterQuery = $_REQUEST['filter']['query']; - -$panelSections = 40; -$panelSectionWidth = (int)ceil(reScale($event['Width'],$scale)/$panelSections); -$panelWidth = ($panelSections*$panelSectionWidth-1); - -$connkey = generateConnKey(); - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['Event'] ); -?> - -
-
-
- - - - - - - - - -
s">/">//
-
- - -
-
- -
-

- - - - - - - - - -

-
- Mode:   - Rate: x - Progress: s - Zoom: x -
- -
- -
-
- - diff --git a/web/skins/flat/views/eventdetail.php b/web/skins/flat/views/eventdetail.php deleted file mode 100644 index e9aa9a5a5..000000000 --- a/web/skins/flat/views/eventdetail.php +++ /dev/null @@ -1,131 +0,0 @@ - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - -
-
- disabled="disabled"/> -
-
-
-
- - diff --git a/web/skins/flat/views/events.php b/web/skins/flat/views/events.php deleted file mode 100644 index 46c3407fa..000000000 --- a/web/skins/flat/views/events.php +++ /dev/null @@ -1,263 +0,0 @@ - $limit ) -{ - $nEvents = $limit; -} -$pages = (int)ceil($nEvents/ZM_WEB_EVENTS_PER_PAGE); -if ( $pages > 1 ) { - if ( !empty($page) ) { - if ( $page < 0 ) - $page = 1; - if ( $page > $pages ) - $page = $pages; - } -} -if ( !empty($page) ) { - $limitStart = (($page-1)*ZM_WEB_EVENTS_PER_PAGE); - if ( empty( $limit ) ) - { - $limitAmount = ZM_WEB_EVENTS_PER_PAGE; - } - else - { - $limitLeft = $limit - $limitStart; - $limitAmount = ($limitLeft>ZM_WEB_EVENTS_PER_PAGE)?ZM_WEB_EVENTS_PER_PAGE:$limitLeft; - } - $eventsSql .= " limit $limitStart, $limitAmount"; -} elseif ( !empty( $limit ) ) { - $eventsSql .= " limit 0, ".$limit; -} - -$maxWidth = 0; -$maxHeight = 0; -$archived = false; -$unarchived = false; -$events = array(); -foreach ( dbFetchAll( $eventsSql ) as $event ) -{ - $events[] = $event; - $scale = max( reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE ), SCALE_BASE ); - $eventWidth = reScale( $event['Width'], $scale ); - $eventHeight = reScale( $event['Height'], $scale ); - if ( $maxWidth < $eventWidth ) $maxWidth = $eventWidth; - if ( $maxHeight < $eventHeight ) $maxHeight = $eventHeight; - if ( $event['Archived'] ) - $archived = true; - else - $unarchived = true; -} - -$maxShortcuts = 5; -$pagination = getPagination( $pages, $page, $maxShortcuts, $filterQuery.$sortQuery.'&limit='.$limit ); - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['Events'] ); - -?> - -
- -
-
- - - - - - - - -

- -

- - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
disabled="disabled"/>
' ) ?>  disabled="disabled"/>
- -

- -
- - - - - - -
- -
-
-
- - diff --git a/web/skins/flat/views/export.php b/web/skins/flat/views/export.php deleted file mode 100644 index 5ca464f88..000000000 --- a/web/skins/flat/views/export.php +++ /dev/null @@ -1,131 +0,0 @@ - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
checked="checked" onclick="configureExportButton( this )"/>
checked="checked" onclick="configureExportButton( this )"/>
checked="checked" onclick="configureExportButton( this )"/>
checked="checked" onclick="configureExportButton( this )"/>
checked="checked" onclick="configureExportButton( this )"/>
- checked="checked" onclick="configureExportButton( this )"/> - checked="checked" onclick="configureExportButton( this )"/> -
- -
-
- -

- - - - - -
- - diff --git a/web/skins/flat/views/filter.php b/web/skins/flat/views/filter.php deleted file mode 100644 index 73b74be69..000000000 --- a/web/skins/flat/views/filter.php +++ /dev/null @@ -1,331 +0,0 @@ -$SLANG['ChooseFilter'] ); -foreach ( dbFetchAll( "select * from Filters order by Name" ) as $row ) -{ - $filterNames[$row['Name']] = $row['Name']; - if ( $row['Background'] ) - $filterNames[$row['Name']] .= "*"; - if ( !empty($_REQUEST['reload']) && isset($_REQUEST['filterName']) && $_REQUEST['filterName'] == $row['Name'] ) - $dbFilter = $row; -} - -$backgroundStr = ""; -if ( isset($dbFilter) ) -{ - if ( $dbFilter['Background'] ) - $backgroundStr = '['.strtolower($SLANG['Background']).']'; - $_REQUEST['filter'] = jsonDecode( $dbFilter['Query'] ); - $_REQUEST['sort_field'] = isset($_REQUEST['filter']['sort_field'])?$_REQUEST['filter']['sort_field']:"DateTime"; - $_REQUEST['sort_asc'] = isset($_REQUEST['filter']['sort_asc'])?$_REQUEST['filter']['sort_asc']:"1"; - $_REQUEST['limit'] = isset($_REQUEST['filter']['limit'])?$_REQUEST['filter']['limit']:""; - unset( $_REQUEST['filter']['sort_field'] ); - unset( $_REQUEST['filter']['sort_asc'] ); - unset( $_REQUEST['filter']['limit'] ); -} - -$conjunctionTypes = array( - 'and' => $SLANG['ConjAnd'], - 'or' => $SLANG['ConjOr'] -); -$obracketTypes = array(); -$cbracketTypes = array(); -if ( isset($_REQUEST['filter']['terms']) ) -{ - for ( $i = 0; $i <= count($_REQUEST['filter']['terms'])-2; $i++ ) - { - $obracketTypes[$i] = str_repeat( "(", $i ); - $cbracketTypes[$i] = str_repeat( ")", $i ); - } -} - -$attrTypes = array( - 'MonitorId' => $SLANG['AttrMonitorId'], - 'MonitorName' => $SLANG['AttrMonitorName'], - 'Id' => $SLANG['AttrId'], - 'Name' => $SLANG['AttrName'], - 'Cause' => $SLANG['AttrCause'], - 'Notes' => $SLANG['AttrNotes'], - 'DateTime' => $SLANG['AttrDateTime'], - 'Date' => $SLANG['AttrDate'], - 'Time' => $SLANG['AttrTime'], - 'Weekday' => $SLANG['AttrWeekday'], - 'Length' => $SLANG['AttrDuration'], - 'Frames' => $SLANG['AttrFrames'], - 'AlarmFrames' => $SLANG['AttrAlarmFrames'], - 'TotScore' => $SLANG['AttrTotalScore'], - 'AvgScore' => $SLANG['AttrAvgScore'], - 'MaxScore' => $SLANG['AttrMaxScore'], - 'Archived' => $SLANG['AttrArchiveStatus'], - 'DiskPercent' => $SLANG['AttrDiskPercent'], - 'DiskBlocks' => $SLANG['AttrDiskBlocks'], - 'SystemLoad' => $SLANG['AttrSystemLoad'], -); -$opTypes = array( - '=' => $SLANG['OpEq'], - '!=' => $SLANG['OpNe'], - '>=' => $SLANG['OpGtEq'], - '>' => $SLANG['OpGt'], - '<' => $SLANG['OpLt'], - '<=' => $SLANG['OpLtEq'], - '=~' => $SLANG['OpMatches'], - '!~' => $SLANG['OpNotMatches'], - '=[]' => $SLANG['OpIn'], - '![]' => $SLANG['OpNotIn'], -); -$archiveTypes = array( - '0' => $SLANG['ArchUnarchived'], - '1' => $SLANG['ArchArchived'] -); -$weekdays = array(); -for ( $i = 0; $i < 7; $i++ ) -{ - $weekdays[$i] = strftime( "%A", mktime( 12, 0, 0, 1, $i+1, 2001 ) ); -} -$sort_fields = array( - 'Id' => $SLANG['AttrId'], - 'Name' => $SLANG['AttrName'], - 'Cause' => $SLANG['AttrCause'], - 'Notes' => $SLANG['AttrNotes'], - 'MonitorName' => $SLANG['AttrMonitorName'], - 'DateTime' => $SLANG['AttrDateTime'], - 'Length' => $SLANG['AttrDuration'], - 'Frames' => $SLANG['AttrFrames'], - 'AlarmFrames' => $SLANG['AttrAlarmFrames'], - 'TotScore' => $SLANG['AttrTotalScore'], - 'AvgScore' => $SLANG['AttrAvgScore'], - 'MaxScore' => $SLANG['AttrMaxScore'], -); -$sort_dirns = array( - '1' => $SLANG['SortAsc'], - '0' => $SLANG['SortDesc'] -); -if ( empty($_REQUEST['sort_field']) ) -{ - $_REQUEST['sort_field'] = ZM_WEB_EVENT_SORT_FIELD; - $_REQUEST['sort_asc'] = (ZM_WEB_EVENT_SORT_ORDER == "asc"); -} - -$hasCal = file_exists( 'tools/jscalendar/calendar.js' ); - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['EventFilter'] ); -?> - -
- -
-
- - - - - - - - -
-
1 ) { echo buildSelect( $selectName, $filterNames, "submitToFilter( this, 1 );" ); } else { ?>
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  2 ) { echo buildSelect( "filter[terms][$i][obr]", $obracketTypes ); } else { ?>  2 ) { echo buildSelect( "filter[terms][$i][cbr]", $cbracketTypes ); } else { ?>  1 ) { ?>
-
- - - - - - - -
"/>
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
checked="checked" onclick="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>
checked="checked"/>" size="32" maxlength="255" onchange="updateButtons( this )"/>
checked="checked" onclick="updateButtons( this )"/>
-
-
- - - - - - - -
-
-
-
- - diff --git a/web/skins/flat/views/filtersave.php b/web/skins/flat/views/filtersave.php deleted file mode 100644 index ba040d72f..000000000 --- a/web/skins/flat/views/filtersave.php +++ /dev/null @@ -1,86 +0,0 @@ - - -
- -
-
- - - - - - - - - - - - - - - -

- -

- -

- -

- -

- checked="checked"/> -

-
- disabled="disabled"/> -
-
-
-
- - diff --git a/web/skins/flat/views/frame.php b/web/skins/flat/views/frame.php deleted file mode 100644 index c9c871fca..000000000 --- a/web/skins/flat/views/frame.php +++ /dev/null @@ -1,100 +0,0 @@ -$fid, 'Type'=>'Normal', 'Score'=>0 ); -} else { - $frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId = ? AND Score = ?', NULL, array( $eid, $event['MaxScore'] ) ); -} - -$maxFid = $event['Frames']; - -$firstFid = 1; -$prevFid = $frame['FrameId']-1; -$nextFid = $frame['FrameId']+1; -$lastFid = $maxFid; - -$alarmFrame = $frame['Type']=='Alarm'; - -if ( isset( $_REQUEST['scale'] ) ) - $scale = validInt($_REQUEST['scale']); -else - $scale = max( reScale( SCALE_BASE, $event['DefaultScale'], ZM_WEB_DEFAULT_SCALE ), SCALE_BASE ); - -$imageData = getImageSrc( $event, $frame, $scale, (isset($_REQUEST['show']) && $_REQUEST['show']=="capt") ); - -$imagePath = $imageData['thumbPath']; -$eventPath = $imageData['eventPath']; -$dImagePath = sprintf( "%s/%0".ZM_EVENT_IMAGE_DIGITS."d-diag-d.jpg", $eventPath, $frame['FrameId'] ); -$rImagePath = sprintf( "%s/%0".ZM_EVENT_IMAGE_DIGITS."d-diag-r.jpg", $eventPath, $frame['FrameId'] ); - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['Frame']." - ".$event['Id']." - ".$frame['FrameId'] ); -?> - -
- -
-

"><?= $frame['EventId']." class=""/>

-

- 1 ) { ?> - - 1 ) { ?> - - - - - - -

- -

-

" width="" height="" class=""/>

- -

-

- -
-
- - diff --git a/web/skins/flat/views/frames.php b/web/skins/flat/views/frames.php deleted file mode 100644 index ec306a3f6..000000000 --- a/web/skins/flat/views/frames.php +++ /dev/null @@ -1,103 +0,0 @@ - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
- - diff --git a/web/skins/flat/views/function.php b/web/skins/flat/views/function.php deleted file mode 100644 index 445450f30..000000000 --- a/web/skins/flat/views/function.php +++ /dev/null @@ -1,64 +0,0 @@ - - -
- -
-
- - - -

- - checked="checked"/> -

-
- - -
-
-
-
- - diff --git a/web/skins/flat/views/group.php b/web/skins/flat/views/group.php deleted file mode 100644 index bb81774d6..000000000 --- a/web/skins/flat/views/group.php +++ /dev/null @@ -1,88 +0,0 @@ - "", - "Name" => "New Group", - "MonitorIds" => "" - ); -} - -xhtmlHeaders( __FILE__, $SLANG['Group']." - ".$newGroup['Name'] ); -?> - -
- -
-
- - - - - - - - - - - - - - -
- -
-
- disabled="disabled"/> - -
-
-
-
- - diff --git a/web/skins/flat/views/groups.php b/web/skins/flat/views/groups.php deleted file mode 100644 index 70dc79f9c..000000000 --- a/web/skins/flat/views/groups.php +++ /dev/null @@ -1,84 +0,0 @@ - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
onclick="configureButtons( this );"/>
onclick="configureButtons( this );"/>
-
- - /> - /> - /> - -
-
-
-
- - diff --git a/web/skins/flat/views/js/Makefile.am b/web/skins/flat/views/js/Makefile.am deleted file mode 100644 index 2b2c1a53d..000000000 --- a/web/skins/flat/views/js/Makefile.am +++ /dev/null @@ -1,49 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/flat/views/js - -dist_web_DATA = \ - console.js \ - console.js.php \ - control.js \ - controlpreset.js \ - controlpreset.js.php \ - cycle.js \ - cycle.js.php \ - devices.js \ - donate.js \ - donate.js.php \ - event.js \ - event.js.php \ - events.js \ - events.js.php \ - export.js \ - export.js.php \ - filter.js \ - filter.js.php \ - group.js \ - groups.js \ - log.js \ - login.js \ - Makefile.am \ - monitor.js \ - monitor.js.php \ - monitorpreset.js \ - monitorprobe.js \ - montage.js \ - montage.js.php \ - options.js.php \ - postlogin.js \ - state.js \ - state.js.php \ - timeline.js \ - timeline.js.php \ - user.js \ - version.js \ - version.js.php \ - video.js \ - video.js.php \ - watch.js \ - watch.js.php \ - zone.js \ - zone.js.php diff --git a/web/skins/flat/views/js/console.js b/web/skins/flat/views/js/console.js deleted file mode 100644 index 5182a5fd5..000000000 --- a/web/skins/flat/views/js/console.js +++ /dev/null @@ -1,65 +0,0 @@ -function setButtonStates( element ) -{ - var form = element.form; - var checked = 0; - for ( var i = 0; i < form.elements.length; i++ ) - { - if ( form.elements[i].type == "checkbox" ) - { - if ( form.elements[i].checked ) - { - if ( checked++ > 1 ) - break; - } - } - } - $(element).getParent( 'tr' ).toggleClass( 'highlight' ); - form.editBtn.disabled = (checked!=1); - form.deleteBtn.disabled = (checked==0); -} - -function editMonitor( element ) -{ - var form = element.form; - for ( var i = 0; i < form.elements.length; i++ ) - { - if ( form.elements[i].type == "checkbox" ) - { - if ( form.elements[i].checked ) - { - var monitorId = form.elements[i].value; - createPopup( '?view=monitor&mid='+monitorId, 'zmMonitor'+monitorId, 'monitor' ); - form.elements[i].checked = false; - setButtonStates( form.elements[i] ); - //$(form.elements[i]).getParent( 'tr' ).removeClass( 'highlight' ); - break; - } - } - } -} - -function deleteMonitor( element ) -{ - if ( confirm( 'Warning, deleting a monitor also deletes all events and database entries associated with it.\nAre you sure you wish to delete?' ) ) - { - var form = element.form; - form.elements['action'].value = 'delete'; - form.submit(); - } -} - -function reloadWindow() -{ - window.location.replace( thisUrl ); -} - -function initPage() -{ - reloadWindow.periodical( consoleRefreshTimeout ); - if ( showVersionPopup ) - createPopup( '?view=version', 'zmVersion', 'version' ); - if ( showDonatePopup ) - createPopup( '?view=donate', 'zmDonate', 'donate' ); -} - -window.addEvent( 'domready', initPage ); diff --git a/web/skins/flat/views/js/console.js.php b/web/skins/flat/views/js/console.js.php deleted file mode 100644 index 90f66a619..000000000 --- a/web/skins/flat/views/js/console.js.php +++ /dev/null @@ -1,28 +0,0 @@ -var consoleRefreshTimeout = ; - - 0 ) - { - if ( ZM_DYN_DONATE_REMINDER_TIME < time() ) - { - $showDonatePopup = true; - } - } - else - { - $nextReminder = time() + 30*24*60*60; - dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" ); - } - } -} -?> -var showVersionPopup = ; -var showDonatePopup = ; diff --git a/web/skins/flat/views/js/control.js b/web/skins/flat/views/js/control.js deleted file mode 100644 index 819f088d8..000000000 --- a/web/skins/flat/views/js/control.js +++ /dev/null @@ -1,49 +0,0 @@ -var controlParms = "view=request&request=control"; -var controlReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, onSuccess: getControlResponse } ); - -function getControlResponse( respObj, respText ) -{ - if ( !respObj ) - return; - //console.log( respText ); - if ( respObj.result != 'Ok' ) - { - alert( "Control response was status = "+respObj.status+"\nmessage = "+respObj.message ); - } -} - -function controlCmd( control, event, xtell, ytell ) -{ - var locParms = "&id="+$('mid').get('value'); - if ( event && (xtell || ytell) ) - { - var xEvent = new Event( event ); - var target = xEvent.target; - var coords = $(target).getCoordinates(); - - var l = coords.left; - var t = coords.top; - var x = xEvent.page.x - l; - var y = xEvent.page.y - t; - - if ( xtell ) - { - var xge = parseInt( (x*100)/coords.width ); - if ( xtell == -1 ) - xge = 100 - xge; - else if ( xtell == 2 ) - xge = 2*(50 - xge); - locParms += "&xge="+xge; - } - if ( ytell ) - { - var yge = parseInt( (y*100)/coords.height ); - if ( ytell == -1 ) - yge = 100 - yge; - else if ( ytell == 2 ) - yge = 2*(50 - yge); - locParms += "&yge="+yge; - } - } - controlReq.send( controlParms+"&control="+control+locParms ); -} diff --git a/web/skins/flat/views/js/controlpreset.js b/web/skins/flat/views/js/controlpreset.js deleted file mode 100644 index ec2629cae..000000000 --- a/web/skins/flat/views/js/controlpreset.js +++ /dev/null @@ -1,14 +0,0 @@ -function updateLabel() -{ - var presetIndex = $('contentForm').preset.getValue(); - if ( labels[presetIndex] ) - { - $('contentForm').newLabel.value = labels[presetIndex]; - } - else - { - $('contentForm').newLabel.value = ""; - } -} - -window.addEvent( 'domready', updateLabel ); diff --git a/web/skins/flat/views/js/controlpreset.js.php b/web/skins/flat/views/js/controlpreset.js.php deleted file mode 100644 index 0aac01bf6..000000000 --- a/web/skins/flat/views/js/controlpreset.js.php +++ /dev/null @@ -1,9 +0,0 @@ -var labels = new Array(); -$label ) -{ -?> -labels[] = ""; - diff --git a/web/skins/flat/views/js/cycle.js b/web/skins/flat/views/js/cycle.js deleted file mode 100644 index e799a422e..000000000 --- a/web/skins/flat/views/js/cycle.js +++ /dev/null @@ -1,11 +0,0 @@ -function nextCycleView() -{ - window.location.replace( '?view=cycle&group='+currGroup+'&mid='+nextMid+'&mode='+mode, cycleRefreshTimeout ); -} - -function initCycle() -{ - nextCycleView.periodical( cycleRefreshTimeout ); -} - -window.addEvent( 'domready', initCycle ); diff --git a/web/skins/flat/views/js/cycle.js.php b/web/skins/flat/views/js/cycle.js.php deleted file mode 100644 index c9dabaec4..000000000 --- a/web/skins/flat/views/js/cycle.js.php +++ /dev/null @@ -1,5 +0,0 @@ -var currGroup = ""; -var nextMid = ""; -var mode = ""; - -var cycleRefreshTimeout = ; diff --git a/web/skins/flat/views/js/devices.js b/web/skins/flat/views/js/devices.js deleted file mode 100644 index 8191518fc..000000000 --- a/web/skins/flat/views/js/devices.js +++ /dev/null @@ -1,47 +0,0 @@ -function switchDeviceOn( element, key ) -{ - var form = element.form; - form.view.value = currentView; - form.action.value = 'device'; - form.command.value = 'on'; - form.key.value = key; - form.submit(); -} - -function switchDeviceOff( element, key ) -{ - var form = element.form; - form.view.value = currentView; - form.action.value = 'device'; - form.command.value = 'off'; - form.key.value = key; - form.submit(); -} - -function deleteDevice( element ) -{ - var form = element.form; - form.view.value = currentView; - form.action.value = 'delete'; - form.submit(); -} - -function configureButtons( element, name ) -{ - var form = element.form; - var checked = false; - for (var i = 0; i < form.elements.length; i++) - { - if ( form.elements[i].name.indexOf(name) == 0) - { - if ( form.elements[i].checked ) - { - checked = true; - break; - } - } - } - form.deleteBtn.disabled = !checked; -} - -window.focus(); diff --git a/web/skins/flat/views/js/donate.js b/web/skins/flat/views/js/donate.js deleted file mode 100644 index 87d99198b..000000000 --- a/web/skins/flat/views/js/donate.js +++ /dev/null @@ -1,14 +0,0 @@ -function submitForm( element ) -{ - var form = element.form; - if ( form.option.selectedIndex == 0 ) - form.view.value = currentView; - else - form.view.value = 'none'; - form.submit(); -} - -if ( action == "donate" && option == "go" ) -{ - zmWindow(); -} diff --git a/web/skins/flat/views/js/donate.js.php b/web/skins/flat/views/js/donate.js.php deleted file mode 100644 index 9d24e392f..000000000 --- a/web/skins/flat/views/js/donate.js.php +++ /dev/null @@ -1,2 +0,0 @@ -var action = ''; -var option = ''; diff --git a/web/skins/flat/views/js/event.js b/web/skins/flat/views/js/event.js deleted file mode 100644 index a1b2a2e55..000000000 --- a/web/skins/flat/views/js/event.js +++ /dev/null @@ -1,754 +0,0 @@ -function setButtonState( element, butClass ) -{ - element.className = butClass; - element.disabled = (butClass != 'inactive'); -} - -function changeScale() -{ - var scale = $('scale').get('value'); - var baseWidth = eventData.Width; - var baseHeight = eventData.Height; - var newWidth = ( baseWidth * scale ) / SCALE_BASE; - var newHeight = ( baseHeight * scale ) / SCALE_BASE; - - streamScale( scale ); - - /*Stream could be an applet so can't use moo tools*/ - var streamImg = document.getElementById('evtStream'); - streamImg.style.width = newWidth + "px"; - streamImg.style.height = newHeight + "px"; -} - -function changeReplayMode() -{ - var replayMode = $('replayMode').get('value'); - - Cookie.write( 'replayMode', replayMode, { duration: 10*365 }) - - refreshWindow(); -} - -var streamParms = "view=request&request=stream&connkey="+connKey; -var streamCmdTimer = null; - -var streamStatus = null; -var lastEventId = 0; - -function getCmdResponse( respObj, respText ) -{ - if ( checkStreamForErrors( "getCmdResponse" ,respObj ) ) - return; - - if ( streamCmdTimer ) - streamCmdTimer = clearTimeout( streamCmdTimer ); - - streamStatus = respObj.status; - - var eventId = streamStatus.event; - if ( eventId != lastEventId ) - { - eventQuery( eventId ); - lastEventId = eventId; - } - if ( streamStatus.paused == true ) - { - $('modeValue').set( 'text', "Paused" ); - $('rate').addClass( 'hidden' ); - streamPause( false ); - } - else - { - $('modeValue').set( 'text', "Replay" ); - $('rateValue').set( 'text', streamStatus.rate ); - $('rate').removeClass( 'hidden' ); - streamPlay( false ); - } - $('progressValue').set( 'text', secsToTime( parseInt(streamStatus.progress) ) ); - $('zoomValue').set( 'text', streamStatus.zoom ); - if ( streamStatus.zoom == "1.0" ) - setButtonState( $('zoomOutBtn'), 'unavail' ); - else - setButtonState( $('zoomOutBtn'), 'inactive' ); - - updateProgressBar(); - - streamCmdTimer = streamQuery.delay( streamTimeout ); -} - -var streamReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse } ); - -function streamPause( action ) -{ - setButtonState( $('pauseBtn'), 'active' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'unavail' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'unavail' ); - if ( action ) - streamReq.send( streamParms+"&command="+CMD_PAUSE ); -} - -function streamPlay( action ) -{ - setButtonState( $('pauseBtn'), 'inactive' ); - if (streamStatus) - setButtonState( $('playBtn'), streamStatus.rate==1?'active':'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'unavail' ); - setButtonState( $('slowRevBtn'), 'unavail' ); - setButtonState( $('fastRevBtn'), 'inactive' ); - if ( action ) - { - streamReq.send( streamParms+"&command="+CMD_PLAY ); - } -} - -function streamFastFwd( action ) -{ - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'unavail' ); - setButtonState( $('slowRevBtn'), 'unavail' ); - setButtonState( $('fastRevBtn'), 'inactive' ); - if ( action ) - streamReq.send( streamParms+"&command="+CMD_FASTFWD ); -} - -function streamSlowFwd( action ) -{ - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'unavail' ); - setButtonState( $('slowFwdBtn'), 'active' ); - setButtonState( $('slowRevBtn'), 'inactive' ); - setButtonState( $('fastRevBtn'), 'unavail' ); - if ( action ) - streamReq.send( streamParms+"&command="+CMD_SLOWFWD ); - setButtonState( $('pauseBtn'), 'active' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); -} - -function streamSlowRev( action ) -{ - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'unavail' ); - setButtonState( $('slowFwdBtn'), 'inactive' ); - setButtonState( $('slowRevBtn'), 'active' ); - setButtonState( $('fastRevBtn'), 'unavail' ); - if ( action ) - streamReq.send( streamParms+"&command="+CMD_SLOWREV ); - setButtonState( $('pauseBtn'), 'active' ); - setButtonState( $('slowRevBtn'), 'inactive' ); -} - -function streamFastRev( action ) -{ - setButtonState( $('pauseBtn'), 'inactive' ); - setButtonState( $('playBtn'), 'inactive' ); - setButtonState( $('fastFwdBtn'), 'inactive' ); - setButtonState( $('slowFwdBtn'), 'unavail' ); - setButtonState( $('slowRevBtn'), 'unavail' ); - setButtonState( $('fastRevBtn'), 'inactive' ); - if ( action ) - streamReq.send( streamParms+"&command="+CMD_FASTREV ); -} - -function streamPrev( action ) -{ - streamPlay( false ); - if ( action ) - streamReq.send( streamParms+"&command="+CMD_PREV ); -} - -function streamNext( action ) -{ - streamPlay( false ); - if ( action ) - streamReq.send( streamParms+"&command="+CMD_NEXT ); -} - -function streamZoomIn( x, y ) -{ - streamReq.send( streamParms+"&command="+CMD_ZOOMIN+"&x="+x+"&y="+y ); -} - -function streamZoomOut() -{ - streamReq.send( streamParms+"&command="+CMD_ZOOMOUT ); -} - -function streamScale( scale ) -{ - streamReq.send( streamParms+"&command="+CMD_SCALE+"&scale="+scale ); -} - -function streamPan( x, y ) -{ - streamReq.send( streamParms+"&command="+CMD_PAN+"&x="+x+"&y="+y ); -} - -function streamSeek( offset ) -{ - streamReq.send( streamParms+"&command="+CMD_SEEK+"&offset="+offset ); -} - -function streamQuery() -{ - streamReq.send( streamParms+"&command="+CMD_QUERY ); -} - -var slider = null; -var scroll = null; - -function getEventResponse( respObj, respText ) -{ - if ( checkStreamForErrors( "getEventResponse", respObj ) ) - return; - - eventData = respObj.event; - if ( !$('eventStills').hasClass( 'hidden' ) && currEventId != eventData.Id ) - resetEventStills(); - currEventId = eventData.Id; - - $('dataId').set( 'text', eventData.Id ); - if ( eventData.Notes ) - { - $('dataCause').setProperty( 'title', eventData.Notes ); - } - else - { - $('dataCause').setProperty( 'title', causeString ); - } - $('dataCause').set( 'text', eventData.Cause ); - $('dataTime').set( 'text', eventData.StartTime ); - $('dataDuration').set( 'text', eventData.Length ); - $('dataFrames').set( 'text', eventData.Frames+"/"+eventData.AlarmFrames ); - $('dataScore').set( 'text', eventData.TotScore+"/"+eventData.AvgScore+"/"+eventData.MaxScore ); - $('eventName').setProperty( 'value', eventData.Name ); - - if ( canEditEvents ) - { - if ( parseInt(eventData.Archived) ) - { - $('archiveEvent').addClass( 'hidden' ); - $('unarchiveEvent').removeClass( 'hidden' ); - } - else - { - $('archiveEvent').removeClass( 'hidden' ); - $('unarchiveEvent').addClass( 'hidden' ); - } - } - //var eventImg = $('eventImage'); - //eventImg.setStyles( { 'width': eventData.width, 'height': eventData.height } ); - drawProgressBar(); - nearEventsQuery( eventData.Id ); -} - -var eventReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getEventResponse } ); - -function eventQuery( eventId ) -{ - var eventParms = "view=request&request=status&entity=event&id="+eventId; - eventReq.send( eventParms ); -} - -var prevEventId = 0; -var nextEventId = 0; - -function getNearEventsResponse( respObj, respText ) -{ - if ( checkStreamForErrors( "getNearEventsResponse", respObj ) ) - return; - prevEventId = respObj.nearevents.PrevEventId; - nextEventId = respObj.nearevents.NextEventId; - - $('prevEventBtn').disabled = !prevEventId; - $('nextEventBtn').disabled = !nextEventId; -} - -var nearEventsReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getNearEventsResponse } ); - -function nearEventsQuery( eventId ) -{ - var parms = "view=request&request=status&entity=nearevents&id="+eventId; - nearEventsReq.send( parms ); -} - -var frameBatch = 40; - -function loadEventThumb( event, frame, loadImage ) -{ - var thumbImg = $('eventThumb'+frame.FrameId); - if ( !thumbImg ) - { - console.error( "No holder found for frame "+frame.FrameId ); - return; - } - var img = new Asset.image( imagePrefix+frame.Image.imagePath, - { - 'onload': ( function( loadImage ) - { - thumbImg.setProperty( 'src', img.getProperty( 'src' ) ); - thumbImg.removeClass( 'placeholder' ); - thumbImg.setProperty( 'class', frame.Type=='Alarm'?'alarm':'normal' ); - thumbImg.setProperty( 'title', frame.FrameId+' / '+((frame.Type=='Alarm')?frame.Score:0) ); - thumbImg.removeEvents( 'click' ); - thumbImg.addEvent( 'click', function() { locateImage( frame.FrameId, true ); } ); - if ( loadImage ) - loadEventImage( event, frame ); - } ).pass( loadImage ) - } - ); -} - -function updateStillsSizes( noDelay ) -{ - var containerDim = $('eventThumbs').getSize(); - - var containerWidth = containerDim.x; - var containerHeight = containerDim.y; - var popupWidth = parseInt($('eventImage').getStyle( 'width' )); - var popupHeight = parseInt($('eventImage').getStyle( 'height' )); - - var left = (containerWidth - popupWidth)/2; - if ( left < 0 ) left = 0; - var top = (containerHeight - popupHeight)/2; - if ( top < 0 ) top = 0; - if ( popupHeight == 0 && !noDelay ) // image not yet loaded lets give it another second - { - updateStillsSizes.pass( true ).delay( 50 ); - return; - } - $('eventImagePanel').setStyles( { - 'left': left, - 'top': top - } ); -} - -function loadEventImage( event, frame ) -{ - console.debug( "Loading "+event.Id+"/"+frame.FrameId ); - var eventImg = $('eventImage'); - var thumbImg = $('eventThumb'+frame.FrameId); - if ( eventImg.getProperty( 'src' ) != thumbImg.getProperty( 'src' ) ) - { - var eventImagePanel = $('eventImagePanel'); - - if ( eventImagePanel.getStyle( 'display' ) != 'none' ) - { - var lastThumbImg = $('eventThumb'+eventImg.getProperty( 'alt' )); - lastThumbImg.removeClass('selected'); - lastThumbImg.setOpacity( 1.0 ); - } - - eventImg.setProperties( { - 'class': frame.Type=='Alarm'?'alarm':'normal', - 'src': thumbImg.getProperty( 'src' ), - 'title': thumbImg.getProperty( 'title' ), - 'alt': thumbImg.getProperty( 'alt' ), - 'width': event.Width, - 'height': event.Height - } ); - $('eventImageBar').setStyle( 'width', event.Width ); - if ( frame.Type=='Alarm' ) - $('eventImageStats').removeClass( 'hidden' ); - else - $('eventImageStats').addClass( 'hidden' ); - thumbImg.addClass( 'selected' ); - thumbImg.setOpacity( 0.5 ); - - if ( eventImagePanel.getStyle( 'display' ) == 'none' ) - { - eventImagePanel.setOpacity( 0 ); - updateStillsSizes(); - eventImagePanel.setStyle( 'display', 'block' ); - new Fx.Tween( eventImagePanel, { duration: 500, transition: Fx.Transitions.Sine } ).start( 'opacity', 0, 1 ); - } - - $('eventImageNo').set( 'text', frame.FrameId ); - $('prevImageBtn').disabled = (frame.FrameId==1); - $('nextImageBtn').disabled = (frame.FrameId==event.Frames); - } -} - -function hideEventImageComplete() -{ - var eventImg = $('eventImage'); - var thumbImg = $('eventThumb'+$('eventImage').getProperty( 'alt' )); - thumbImg.removeClass('selected'); - thumbImg.setOpacity( 1.0 ); - $('prevImageBtn').disabled = true; - $('nextImageBtn').disabled = true; - $('eventImagePanel').setStyle( 'display', 'none' ); - $('eventImageStats').addClass( 'hidden' ); -} - -function hideEventImage() -{ - if ( $('eventImagePanel').getStyle( 'display' ) != 'none' ) - new Fx.Tween( $('eventImagePanel'), { duration: 500, transition: Fx.Transitions.Sine, onComplete: hideEventImageComplete } ).start( 'opacity', 1, 0 ); -} - -function resetEventStills() -{ - hideEventImage(); - $('eventThumbs').empty(); - if ( true || !slider ) - { - slider = new Slider( $('thumbsSlider'), $('thumbsKnob'), { - /*steps: eventData.Frames,*/ - onChange: function( step ) - { - if ( !step ) - step = 0; - var fid = parseInt((step * eventData.Frames)/this.options.steps); - if ( fid < 1 ) - fid = 1; - else if ( fid > eventData.Frames ) - fid = eventData.Frames; - checkFrames( eventData.Id, fid ); - scroll.toElement( 'eventThumb'+fid ); - } - } ).set( 0 ); - } - if ( $('eventThumbs').getStyle( 'height' ).match( /^\d+/ ) < (parseInt(eventData.Height)+80) ) - $('eventThumbs').setStyle( 'height', (parseInt(eventData.Height)+80)+'px' ); -} - -function getFrameResponse( respObj, respText ) -{ - if ( checkStreamForErrors( "getFrameResponse", respObj ) ) - return; - - var frame = respObj.frameimage; - - if ( !eventData ) - { - console.error( "No event "+frame.EventId+" found" ); - return; - } - - if ( !eventData['frames'] ) - eventData['frames'] = new Object(); - - eventData['frames'][frame.FrameId] = frame; - - loadEventThumb( eventData, frame, respObj.loopback=="true" ); -} - -var frameReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getFrameResponse } ); - -function frameQuery( eventId, frameId, loadImage ) -{ - var parms = "view=request&request=status&entity=frameimage&id[0]="+eventId+"&id[1]="+frameId+"&loopback="+loadImage; - frameReq.send( parms ); -} - -var currFrameId = null; - -function checkFrames( eventId, frameId, loadImage ) -{ - if ( !eventData ) - { - console.error( "No event "+eventId+" found" ); - return; - } - - if ( !eventData['frames'] ) - eventData['frames'] = new Object(); - - currFrameId = frameId; - - var loFid = frameId - frameBatch/2; - if ( loFid < 1 ) - loFid = 1; - var hiFid = loFid + (frameBatch-1); - if ( hiFid > eventData.Frames ) - hiFid = eventData.Frames; - - for ( var fid = loFid; fid <= hiFid; fid++ ) - { - if ( !$('eventThumb'+fid) ) - { - var img = new Element( 'img', { 'id': 'eventThumb'+fid, 'src': 'graphics/transparent.gif', 'alt': fid, 'class': 'placeholder' } ); - img.addEvent( 'click', function () { eventData['frames'][fid] = null; checkFrames( eventId, fid ) } ); - frameQuery( eventId, fid, loadImage && (fid == frameId) ); - var imgs = $('eventThumbs').getElements( 'img' ); - var injected = false; - if ( fid < imgs.length ) - { - img.inject( imgs[fid-1], 'before' ); - injected = true; - } - else - { - injected = imgs.some( - function( thumbImg, index ) - { - if ( parseInt(img.getProperty( 'alt' )) < parseInt(thumbImg.getProperty( 'alt' )) ) - { - img.inject( thumbImg, 'before' ); - return( true ); - } - return( false ); - } - ); - } - if ( !injected ) - { - img.inject( $('eventThumbs') ); - } - var scale = parseInt(img.getStyle('height')); - img.setStyles( { - 'width': parseInt((eventData.Width*scale)/100), - 'height': parseInt((eventData.Height*scale)/100) - } ); - } - else if ( eventData['frames'][fid] ) - { - if ( loadImage && (fid == frameId) ) - { - loadEventImage( eventData, eventData['frames'][fid], loadImage ); - } - } - } - $('prevThumbsBtn').disabled = (frameId==1); - $('nextThumbsBtn').disabled = (frameId==eventData.Frames); -} - -function locateImage( frameId, loadImage ) -{ - if ( slider ) - slider.fireEvent( 'tick', slider.toPosition( parseInt((frameId-1)*slider.options.steps/eventData.Frames) )); - checkFrames( eventData.Id, frameId, loadImage ); - scroll.toElement( 'eventThumb'+frameId ); -} - -function prevImage() -{ - if ( currFrameId > 1 ) - locateImage( parseInt(currFrameId)-1, true ); -} - -function nextImage() -{ - if ( currFrameId < eventData.Frames ) - locateImage( parseInt(currFrameId)+1, true ); -} - -function prevThumbs() -{ - if ( currFrameId > 1 ) - locateImage( parseInt(currFrameId)>10?(parseInt(currFrameId)-10):1, $('eventImagePanel').getStyle('display')!="none" ); -} - -function nextThumbs() -{ - if ( currFrameId < eventData.Frames ) - locateImage( parseInt(currFrameId)<(eventData.Frames-10)?(parseInt(currFrameId)+10):eventData.Frames, $('eventImagePanel').getStyle('display')!="none" ); -} - -function prevEvent() -{ - if ( prevEventId ) - { - eventQuery( prevEventId ); - streamPrev( true ); - } -} - -function nextEvent() -{ - if ( nextEventId ) - { - eventQuery( nextEventId ); - streamNext( true ); - } -} - -function getActResponse( respObj, respText ) -{ - if ( checkStreamForErrors( "getActResponse", respObj ) ) - return; - - if ( respObj.refreshParent ) - refreshParentWindow(); - - if ( respObj.refreshEvent ) - eventQuery( eventData.Id ); -} - -var actReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getActResponse } ); - -function actQuery( action, parms ) -{ - var actParms = "view=request&request=event&id="+eventData.Id+"&action="+action; - if ( parms != null ) - actParms += "&"+Object.toQueryString( parms ); - actReq.send( actParms ); -} - -function deleteEvent() -{ - actQuery( 'delete' ); - streamNext( true ); -} - -function renameEvent() -{ - var newName = $('eventName').get('value'); - actQuery( 'rename', { eventName: newName } ); -} - -function editEvent() -{ - createPopup( '?view=eventdetail&eid='+eventData.Id, 'zmEventDetail', 'eventdetail' ); -} - -function exportEvent() -{ - createPopup( '?view=export&eid='+eventData.Id, 'zmExport', 'export' ); -} - -function archiveEvent() -{ - actQuery( 'archive' ); -} - -function unarchiveEvent() -{ - actQuery( 'unarchive' ); -} - -function showEventFrames() -{ - createPopup( '?view=frames&eid='+eventData.Id, 'zmFrames', 'frames' ); -} - -function showStream() -{ - $('eventStills').addClass( 'hidden' ); - $('eventStream').removeClass( 'hidden' ); - $('streamEvent').addClass( 'hidden' ); - $('stillsEvent').removeClass( 'hidden' ); - - //$(window).removeEvent( 'resize', updateStillsSizes ); -} - -function showStills() -{ - $('eventStream').addClass( 'hidden' ); - $('eventStills').removeClass( 'hidden' ); - $('stillsEvent').addClass( 'hidden' ); - $('streamEvent').removeClass( 'hidden' ); - streamPause( true ); - if ( !scroll ) - { - scroll = new Fx.Scroll( 'eventThumbs', { - wait: false, - duration: 500, - offset: { 'x': 0, 'y': 0 }, - transition: Fx.Transitions.Quad.easeInOut - } - ); - } - resetEventStills(); - $(window).addEvent( 'resize', updateStillsSizes ); -} - -function showFrameStats() -{ - var fid = $('eventImageNo').get('text'); - createPopup( '?view=stats&eid='+eventData.Id+'&fid='+fid, 'zmStats', 'stats', eventData.Width, eventData.Height ); -} - -function videoEvent() -{ - createPopup( '?view=video&eid='+eventData.Id, 'zmVideo', 'video', eventData.Width, eventData.Height ); -} - -function drawProgressBar() -{ - var barWidth = 0; - $('progressBar').addClass( 'invisible' ); - var cells = $('progressBar').getElements( 'div' ); - var cellWidth = parseInt( eventData.Width/$$(cells).length ); - $$(cells).forEach( - function( cell, index ) - { - if ( index == 0 ) - $(cell).setStyles( { 'left': barWidth, 'width': cellWidth, 'borderLeft': 0 } ); - else - $(cell).setStyles( { 'left': barWidth, 'width': cellWidth } ); - var offset = parseInt((index*eventData.Length)/$$(cells).length); - $(cell).setProperty( 'title', '+'+secsToTime(offset)+'s' ); - $(cell).removeEvent( 'click' ); - $(cell).addEvent( 'click', function(){ streamSeek( offset ); } ); - barWidth += $(cell).getCoordinates().width; - } - ); - $('progressBar').setStyle( 'width', barWidth ); - $('progressBar').removeClass( 'invisible' ); -} - -function updateProgressBar() -{ - if ( eventData && streamStatus ) - { - var cells = $('progressBar').getElements( 'div' ); - var completeIndex = parseInt((($$(cells).length+1)*streamStatus.progress)/eventData.Length); - $$(cells).forEach( - function( cell, index ) - { - if ( index < completeIndex ) - { - if ( !$(cell).hasClass( 'complete' ) ) - { - $(cell).addClass( 'complete' ); - } - } - else - { - if ( $(cell).hasClass( 'complete' ) ) - { - $(cell).removeClass( 'complete' ); - } - } - } - ); - } -} - -function handleClick( event ) -{ - var target = event.target; - var x = event.page.x - $(target).getLeft(); - var y = event.page.y - $(target).getTop(); - - if ( event.shift ) - streamPan( x, y ); - else - streamZoomIn( x, y ); -} - -function initPage() -{ - streamCmdTimer = streamQuery.delay( 250 ); - eventQuery.pass( eventData.Id ).delay( 500 ); - - if ( canStreamNative ) - { - var streamImg = $('imageFeed').getElement('img'); - if ( !streamImg ) - streamImg = $('imageFeed').getElement('object'); - $(streamImg).addEvent( 'click', function( event ) { handleClick( event ); } ); - } -} - -// Kick everything off -window.addEvent( 'domready', initPage ); diff --git a/web/skins/flat/views/js/event.js.php b/web/skins/flat/views/js/event.js.php deleted file mode 100644 index 9ae458651..000000000 --- a/web/skins/flat/views/js/event.js.php +++ /dev/null @@ -1,48 +0,0 @@ -// -// Import constants -// -var CMD_NONE = ; -var CMD_PAUSE = ; -var CMD_PLAY = ; -var CMD_STOP = ; -var CMD_FASTFWD = ; -var CMD_SLOWFWD = ; -var CMD_SLOWREV = ; -var CMD_FASTREV = ; -var CMD_ZOOMIN = ; -var CMD_ZOOMOUT = ; -var CMD_PAN = ; -var CMD_SCALE = ; -var CMD_PREV = ; -var CMD_NEXT = ; -var CMD_SEEK = ; -var CMD_QUERY = ; - -var SCALE_BASE = ; - -// -// PHP variables to JS -// -var connKey = ''; - -var eventData = { - Id: , - Width: , - Height: , - Length: -}; - -var filterQuery = ''; -var sortQuery = ''; - -var scale = ; -var canEditEvents = ; -var streamTimeout = ; - -var canStreamNative = ; - -// -// Strings -// -var deleteString = ""; -var causeString = ""; diff --git a/web/skins/flat/views/js/events.js b/web/skins/flat/views/js/events.js deleted file mode 100644 index 6aa129932..000000000 --- a/web/skins/flat/views/js/events.js +++ /dev/null @@ -1,150 +0,0 @@ -function closeWindows() -{ - window.close(); - // This is a hack. The only way to close an existing window is to try and open it! - var filterWindow = window.open( thisUrl+'?view=none', 'zmFilter', 'width=1,height=1' ); - filterWindow.close(); -} - -function toggleCheckbox( element, name ) -{ - var form = element.form; - var checked = element.checked; - for (var i = 0; i < form.elements.length; i++) - if (form.elements[i].name.indexOf(name) == 0) - form.elements[i].checked = checked; - form.viewBtn.disabled = !checked; - form.editBtn.disabled = !checked; - form.archiveBtn.disabled = unarchivedEvents?!checked:true; - form.unarchiveBtn.disabled = archivedEvents?!checked:true; - form.exportBtn.disabled = !checked; - form.deleteBtn.disabled = !checked; -} - -function configureButton( element, name ) -{ - var form = element.form; - var checked = element.checked; - if ( !checked ) - { - for (var i = 0; i < form.elements.length; i++) - { - if ( form.elements[i].name.indexOf(name) == 0) - { - if ( form.elements[i].checked ) - { - checked = true; - break; - } - } - } - } - if ( !element.checked ) - form.toggleCheck.checked = false; - form.viewBtn.disabled = !checked; - form.editBtn.disabled = !checked; - form.archiveBtn.disabled = (!checked)||(!unarchivedEvents); - form.unarchiveBtn.disabled = (!checked)||(!archivedEvents); - form.exportBtn.disabled = !checked; - form.deleteBtn.disabled = !checked; -} - -function deleteEvents( element, name ) -{ - var form = element.form; - var count = 0; - for (var i = 0; i < form.elements.length; i++) - { - if (form.elements[i].name.indexOf(name) == 0) - { - if ( form.elements[i].checked ) - { - count++; - break; - } - } - } - if ( count > 0 ) - { - if ( confirm( confirmDeleteEventsString ) ) - { - form.action.value = 'delete'; - form.submit(); - } - } -} - -function editEvents( element, name ) -{ - var form = element.form; - var eids = new Array(); - for (var i = 0; i < form.elements.length; i++) - { - if (form.elements[i].name.indexOf(name) == 0) - { - if ( form.elements[i].checked ) - { - eids[eids.length] = 'eids[]='+form.elements[i].value; - } - } - } - createPopup( '?view=eventdetail&'+eids.join( '&' ), 'zmEventDetail', 'eventdetail' ); -} - -function exportEvents( element, name ) -{ - var form = element.form; - var eids = new Array(); - for (var i = 0; i < form.elements.length; i++) - { - if (form.elements[i].name.indexOf(name) == 0) - { - if ( form.elements[i].checked ) - { - eids[eids.length] = 'eids[]='+form.elements[i].value; - } - } - } - createPopup( '?view=export&'+eids.join( '&' ), 'zmExport', 'export' ); -} - -function viewEvents( element, name ) -{ - var form = element.form; - var events = new Array(); - for (var i = 0; i < form.elements.length; i++) - { - if ( form.elements[i].name.indexOf(name) == 0) - { - if ( form.elements[i].checked ) - { - events[events.length] = form.elements[i].value; - } - } - } - if ( events.length > 0 ) - { - createPopup( '?view=event&eid='+events[0]+'&filter[terms][0][attr]=Id&&filter[terms][0][op]=%3D%5B%5D&&filter[terms][0][val]='+events.join('%2C')+sortQuery+'&page=1&play=1', 'zmEvent', 'event', maxWidth, maxHeight ); - } -} - -function archiveEvents( element, name ) -{ - var form = element.form; - form.action.value = 'archive'; - form.submit(); -} - -function unarchiveEvents( element, name ) -{ - var form = element.form; - form.action.value = 'unarchive'; - form.submit(); -} - -if ( openFilterWindow ) -{ - //opener.location.reload(true); - createPopup( '?view=filter&page='+thisPage+filterQuery, 'zmFilter', 'filter' ); - location.replace( '?view='+currentView+'&page='+thisPage+filterQuery ); -} diff --git a/web/skins/flat/views/js/events.js.php b/web/skins/flat/views/js/events.js.php deleted file mode 100644 index 497d0c96e..000000000 --- a/web/skins/flat/views/js/events.js.php +++ /dev/null @@ -1,13 +0,0 @@ -//var openFilterWindow = ; -var openFilterWindow = false; - -var archivedEvents = ; -var unarchivedEvents = ; - -var filterQuery = ''; -var sortQuery = ''; - -var maxWidth = ; -var maxHeight = ; - -var confirmDeleteEventsString = ""; diff --git a/web/skins/flat/views/js/export.js b/web/skins/flat/views/js/export.js deleted file mode 100644 index 482b41d72..000000000 --- a/web/skins/flat/views/js/export.js +++ /dev/null @@ -1,58 +0,0 @@ -function configureExportButton( element ) -{ - var form = element.form; - - var checkCount = 0; - var radioCount = 0; - for ( var i = 0; i < form.elements.length; i++ ) - if ( form.elements[i].type == "checkbox" && form.elements[i].checked ) - checkCount++; - else if ( form.elements[i].type == "radio" && form.elements[i].checked ) - radioCount++; - form.elements['exportButton'].disabled = (checkCount == 0 || radioCount == 0); -} - -function startDownload( exportFile ) -{ - window.location.replace( exportFile ); -} - -var exportTimer = null; - -function exportProgress() -{ - var tickerText = $('exportProgressTicker').get('text'); - if ( tickerText.length < 1 || tickerText.length > 4 ) - $('exportProgressTicker').set( 'text', '.' ); - else - $('exportProgressTicker').appendText( '.' ); -} - -function exportResponse( respObj, respText ) -{ - window.location.replace( thisUrl+'?view='+currentView+'&'+eidParm+'&exportFile='+respObj.exportFile+'&generated='+((respObj.result=='Ok')?1:0) ); -} - -function exportEvent( form ) -{ - var parms = 'view=request&request=event&action=export'; - parms += '&'+$(form).toQueryString(); - var query = new Request.JSON( { url: thisUrl, method: 'post', data: parms, onSuccess: exportResponse } ); - query.send(); - $('exportProgress').removeClass( 'hidden' ); - $('exportProgress').setProperty( 'class', 'warnText' ); - $('exportProgressText').set( 'text', exportProgressString ); - exportProgress(); - exportTimer = exportProgress.periodical( 500 ); -} - -function initPage() -{ - configureExportButton( $('exportButton') ); - if ( exportReady ) - { - startDownload.pass( exportFile ).delay( 1500 ); - } -} - -window.addEvent( 'domready', initPage ); diff --git a/web/skins/flat/views/js/export.js.php b/web/skins/flat/views/js/export.js.php deleted file mode 100644 index e51089c14..000000000 --- a/web/skins/flat/views/js/export.js.php +++ /dev/null @@ -1,22 +0,0 @@ - -var eidParm = ''; - -var eidParm = 'eid='; - - -var exportReady = ; -var exportFile = ''; - -var exportProgressString = ''; diff --git a/web/skins/flat/views/js/filter.js b/web/skins/flat/views/js/filter.js deleted file mode 100644 index 300862640..000000000 --- a/web/skins/flat/views/js/filter.js +++ /dev/null @@ -1,120 +0,0 @@ -function updateButtons( element ) -{ - var form = element.form; - - if ( element.type == 'checkbox' && element.checked ) - form.elements['executeButton'].disabled = false; - else - { - var canExecute = false; - if ( form.elements['autoArchive'].checked ) - canExecute = true; - else if ( typeof ZM_OPT_FFMPEG !== "undefined" && form.elements['autoVideo'].checked ) - canExecute = true; - else if ( typeof ZM_OPT_UPLOAD !== "undefined" && form.elements['autoUpload'].checked ) - canExecute = true; - else if ( typeof ZM_OPT_EMAIL !== "undefined" && form.elements['autoEmail'].checked ) - canExecute = true; - else if ( typeof ZM_OPT_MESSAGE !== "undefined" && form.elements['autoMessage'].checked ) - canExecute = true; - else if ( form.elements['autoExecute'].checked && form.elements['autoExecuteCmd'].value != '' ) - canExecute = true; - else if ( form.elements['autoDelete'].checked ) - canExecute = true; - form.elements['executeButton'].disabled = !canExecute; - } -} - -function clearValue( element, line ) -{ - var form = element.form; - var val = form.elements['filter[terms]['+line+'][val]']; - val.value = ''; -} - -function submitToFilter( element, reload ) -{ - var form = element.form; - form.target = window.name; - form.view.value = 'filter'; - form.reload.value = reload; - form.submit(); -} - -function submitToEvents( element ) -{ - var form = element.form; - if ( validateForm( form ) ) - { - form.target = 'zmEvents'; - form.view.value = 'events'; - form.action.value = ''; - form.execute.value = 0; - form.submit(); - } -} - -function executeFilter( element ) -{ - var form = element.form; - if ( validateForm( form ) ) - { - form.target = 'zmEvents'; - form.view.value = 'events'; - form.action.value = 'filter'; - form.execute.value = 1; - form.submit(); - } -} - -function saveFilter( element ) -{ - var form = element.form; - - var popupName = 'zmEventsFilterSave'; - createPopup( thisUrl, popupName, 'filtersave' ); - - form.target = popupName; - form.view.value = 'filtersave'; - form.submit(); -} - -function deleteFilter( element, name ) -{ - if ( confirm( deleteSavedFilterString+" '"+name+"'" ) ) - { - var form = element.form; - form.action.value = 'delete'; - form.fid.value = name; - submitToFilter( element, 1 ); - } -} - -function addTerm( element, line ) -{ - var form = element.form; - form.target = window.name; - form.view.value = currentView; - form.action.value = 'filter'; - form.subaction.value = 'addterm'; - form.line.value = line; - form.submit(); -} - -function delTerm( element, line ) -{ - var form = element.form; - form.target = window.name; - form.view.value = currentView; - form.action.value = 'filter'; - form.subaction.value = 'delterm'; - form.line.value = line; - form.submit(); -} - -function init() -{ - updateButtons( $('executeButton') ); -} - -window.addEvent( 'domready', init ); diff --git a/web/skins/flat/views/js/filter.js.php b/web/skins/flat/views/js/filter.js.php deleted file mode 100644 index 7f6bbdfd5..000000000 --- a/web/skins/flat/views/js/filter.js.php +++ /dev/null @@ -1,56 +0,0 @@ -var deleteSavedFilterString = ""; - -function validateForm( form ) -{ - 2 ) -{ -?> - var bracket_count = 0; - - var obr = form.elements['filter[terms][][obr]']; - var cbr = form.elements['filter[terms][][cbr]']; - bracket_count += parseInt(obr.options[obr.selectedIndex].value); - bracket_count -= parseInt(cbr.options[cbr.selectedIndex].value); - - if ( bracket_count ) - { - alert( "" ); - return( false ); - } - - - var val = form.elements['filter[terms][][val]']; - if ( val.value == '' ) - { - alert( "" ); - return( false ); - } - - return( true ); -} - - - - - - - - - - - - - - diff --git a/web/skins/flat/views/optionhelp.php b/web/skins/flat/views/optionhelp.php deleted file mode 100644 index e02720b24..000000000 --- a/web/skins/flat/views/optionhelp.php +++ /dev/null @@ -1,44 +0,0 @@ -", $optionHelpText ); - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['OptionHelp'] ); -?> - -
- -
-

-

-
-
- - diff --git a/web/skins/flat/views/options.php b/web/skins/flat/views/options.php deleted file mode 100644 index 34f6fc6f1..000000000 --- a/web/skins/flat/views/options.php +++ /dev/null @@ -1,323 +0,0 @@ - - -
- -
-
    -$value ) -{ - if ( $tab == $name ) - { -?> -
  • - -
  • - -
-
-window.opener.location.reload();window.location.href=\"{$_SERVER['PHP_SELF']}?view={$view}&tab={$tab}\""; - } - -?> -
- - - - - - - - -
ZM_SKIN -
-
- /> - -
-
- - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
disabled="disabled"/>
-
- disabled="disabled"/> -
-
- -
- - - - - - - - - - - - -$value ) - { - $shortName = preg_replace( '/^ZM_/', '', $name ); - $optionPromptText = !empty($OLANG[$shortName])?$OLANG[$shortName]['Prompt']:$value['Prompt']; -?> - - - - - - - - - - - - - - - - - - - - - -
 () checked="checked"/> - 3 ) - { -?> - - - checked="checked"/>  - - />/>/>/>
-
- /> -
-
- -
-
- - diff --git a/web/skins/flat/views/plugin.php b/web/skins/flat/views/plugin.php deleted file mode 100644 index ee8819dbd..000000000 --- a/web/skins/flat/views/plugin.php +++ /dev/null @@ -1,167 +0,0 @@ - 0 ) { - $newZone = dbFetchOne( 'SELECT * FROM Zones WHERE MonitorId = ? AND Id = ?', NULL, array( $mid, $zid) ); -} else { - $view = "error"; - return; -} -$monitor = dbFetchMonitor ( $mid ); -$plugin = $_REQUEST['pl']; - -$plugin_path = dirname(ZM_PLUGINS_CONFIG_PATH)."/".$plugin; - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['Plugin'] ); - - -$pluginOptions=array( - 'Enabled'=>array( - 'Type'=>'select', - 'Name'=>'Enabled', - 'Choices'=>'yes,no', - 'Value'=>'no' - ) - ); - -$optionNames=array(); -if(file_exists($plugin_path."/config.php")) -{ - include_once($plugin_path."/config.php"); -} - -$sql='SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?'; -foreach( dbFetchAll( $sql, NULL, array( $mid, $zid, $plugin ) ) as $popt ) -{ - if(array_key_exists($popt['Name'], $pluginOptions) - && $popt['Type']==$pluginOptions[$popt['Name']]['Type'] - && $popt['Choices']==$pluginOptions[$popt['Name']]['Choices'] - ) - { - $pluginOptions[$popt['Name']]=$popt; - array_push($optionNames, $popt['Name']); - } else { - dbQuery('DELETE FROM PluginsConfig WHERE Id=?', array( $popt['Id'] ) ); - } -} -foreach($pluginOptions as $name => $values) -{ - if(!in_array($name, $optionNames)) - { - $popt=$pluginOptions[$name]; - $sql="INSERT INTO PluginsConfig VALUES ('',?,?,?,?,?,?,?)"; - dbQuery($sql, array( $popt['Name'], $popt['Value'], $popt['Type'], $popt['Choices'], $mid, $zid, $plugin ) ); - } -} - -$PLANG=array(); -if(file_exists($plugin_path."/lang/".$user['Language'].".php")) { - include_once($plugin_path."/lang/".$user['Language'].".php"); -} - -function pLang($name) -{ - global $PLANG; - if(array_key_exists($name, $PLANG)) - return $PLANG[$name]; - else - return $name; -} - - -?> - -
- -
-
- - - - - - -
- - - $popt) -{ - ?> - - - - - - - - -
-
- disabled="disabled"/> -
-
-
-
- - diff --git a/web/skins/flat/views/postlogin.php b/web/skins/flat/views/postlogin.php deleted file mode 100644 index 2ce1ff88f..000000000 --- a/web/skins/flat/views/postlogin.php +++ /dev/null @@ -1,33 +0,0 @@ - - -
- -
-

-
-
- - diff --git a/web/skins/flat/views/settings.php b/web/skins/flat/views/settings.php deleted file mode 100644 index dcc0316f5..000000000 --- a/web/skins/flat/views/settings.php +++ /dev/null @@ -1,78 +0,0 @@ - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - -
disabled="disabled"/>
disabled="disabled"/>
disabled="disabled"/>
disabled="disabled"/>
-
- disabled="disabled"/> -
-
-
-
- - diff --git a/web/skins/flat/views/state.php b/web/skins/flat/views/state.php deleted file mode 100644 index 4e8365d51..000000000 --- a/web/skins/flat/views/state.php +++ /dev/null @@ -1,106 +0,0 @@ - - -
- -
-
- - - - -

- -

- - - - - - - -
-
- - - - -
- - - - -

-

- -
-
-
- - diff --git a/web/skins/flat/views/stats.php b/web/skins/flat/views/stats.php deleted file mode 100644 index 07b5e7088..000000000 --- a/web/skins/flat/views/stats.php +++ /dev/null @@ -1,111 +0,0 @@ - - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - 1 ) -{ -?> - - - - - - - - - - - - - -
-
-
-
- - diff --git a/web/skins/flat/views/status.php b/web/skins/flat/views/status.php deleted file mode 100644 index 113c23d88..000000000 --- a/web/skins/flat/views/status.php +++ /dev/null @@ -1,85 +0,0 @@ - - -
- -
- - - - - - - - - - - - - - - - - - - -
-
-
- - diff --git a/web/skins/flat/views/timeline.php b/web/skins/flat/views/timeline.php deleted file mode 100644 index 22c61d638..000000000 --- a/web/skins/flat/views/timeline.php +++ /dev/null @@ -1,974 +0,0 @@ -700, - "height"=>460, - "image" => array( - "width"=>264, - "height"=>220, - "topOffset"=>20, - ), - "imageText" => array( - "width"=>400, - "height"=>30, - "topOffset"=>20, - ), - "graph" => array( - "width"=>600, - "height"=>160, - "topOffset"=>30, - ), - "title" => array( - "topOffset"=>50 - ), - "key" => array( - "topOffset"=>50 - ), - "axes" => array( - "x" => array( - "height" => 20, - ), - "y" => array( - "width" => 30, - ), - ), - "grid" => array( - "x" => array( - "major" => array( - "max" => 12, - "min" => 4, - ), - "minor" => array( - "max" => 48, - "min" => 12, - ), - ), - "y" => array( - "major" => array( - "max" => 8, - "min" => 1, - ), - "minor" => array( - "max" => 0, - "min" => 0, - ), - ), - ), -); - -$monitors = array(); -$monitorsSql = "select * from Monitors order by Sequence asc"; -//srand( 97981 ); -foreach( dbFetchAll( $monitorsSql ) as $row ) -{ - //if ( empty($row['WebColour']) ) - //{ - //$row['WebColour'] = sprintf( "#%02x%02x%02x", rand( 0, 255 ), rand( 0, 255), rand( 0, 255 ) ); - //} - $monitors[$row['Id']] = $row; -} - -$rangeSql = "select min(E.StartTime) as MinTime, max(E.EndTime) as MaxTime from Events as E inner join Monitors as M on (E.MonitorId = M.Id) where not isnull(E.StartTime) and not isnull(E.EndTime)"; -$eventsSql = "select E.Id,E.Name,E.StartTime,E.EndTime,E.Length,E.Frames,E.MaxScore,E.Cause,E.Notes,E.Archived,E.MonitorId from Events as E inner join Monitors as M on (E.MonitorId = M.Id) where not isnull(StartTime)"; - -if ( !empty($user['MonitorIds']) ) -{ - $monFilterSql = " and M.Id in (".join( ",", preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).")"; - - $rangeSql .= $monFilterSql; - $eventsSql .= $monFilterSql; -} - -if ( isset($_REQUEST['filter']) ) - $tree = parseFilterToTree( $_REQUEST['filter'] ); -else - $tree = false; - -if ( isset($_REQUEST['range']) ) - $range = validHtmlStr($_REQUEST['range']); -if ( isset($_REQUEST['minTime']) ) - $minTime = validHtmlStr($_REQUEST['minTime']); -if ( isset($_REQUEST['midTime']) ) - $midTime = validHtmlStr($_REQUEST['midTime']); -if ( isset($_REQUEST['maxTime']) ) - $maxTime = validHtmlStr($_REQUEST['maxTime']); - -if ( isset($range) ) -{ - $halfRange = (int)($range/2); - if ( isset($midTime) ) - { - $midTimeT = strtotime($midTime); - $minTimeT = $midTimeT-$halfRange; - $maxTimeT = $midTimeT+$halfRange; - if ( !($range%1) ) - { - $maxTimeT--; - } - $minTime = strftime( STRF_FMT_DATETIME_DB, $minTimeT ); - $maxTime = strftime( STRF_FMT_DATETIME_DB, $maxTimeT ); - } - elseif ( isset($minTime) ) - { - $minTimeT = strtotime($minTime); - $maxTimeT = $minTimeT + $range; - $midTimeT = $minTimeT + $halfRange; - $midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); - $maxTime = strftime( STRF_FMT_DATETIME_DB, $maxTimeT ); - } - elseif ( isset($maxTime) ) - { - $maxTimeT = strtotime($maxTime); - $minTimeT = $maxTimeT - $range; - $midTimeT = $minTimeT + $halfRange; - $minTime = strftime( STRF_FMT_DATETIME_DB, $minTimeT ); - $midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); - } -} -elseif ( isset($minTime) && isset($maxTime) ) -{ - $minTimeT = strtotime($minTime); - $maxTimeT = strtotime($maxTime); - $range = ($maxTimeT - $minTimeT) + 1; - $halfRange = (int)($range/2); - $midTimeT = $minTimeT + $halfRange; - $midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); -} - -if ( isset($minTime) && isset($maxTime) ) -{ - $tempMinTime = $tempMaxTime = $tempExpandable = false; - extractDatetimeRange( $tree, $tempMinTime, $tempMaxTime, $tempExpandable ); - $filterSql = parseTreeToSQL( $tree ); - - if ( $filterSql ) - { - $filterSql = " and $filterSql"; - $eventsSql .= $filterSql; - } -} -else -{ - $filterSql = parseTreeToSQL( $tree ); - $tempMinTime = $tempMaxTime = $tempExpandable = false; - extractDatetimeRange( $tree, $tempMinTime, $tempMaxTime, $tempExpandable ); - - if ( $filterSql ) - { - $filterSql = " and $filterSql"; - $rangeSql .= $filterSql; - $eventsSql .= $filterSql; - } - - if ( !isset($minTime) || !isset($maxTime) ) - { - // Dynamically determine range - $row = dbFetchOne( $rangeSql ); - - if ( !isset($minTime) ) - $minTime = $row['MinTime']; - if ( !isset($maxTime) ) - $maxTime = $row['MaxTime']; - } - - if ( empty($minTime) ) - $minTime = $tempMinTime; - if ( empty($maxTime) ) - $maxTime = $tempMaxTime; - if ( empty($maxTime) ) - $maxTime = "now"; - - $minTimeT = strtotime($minTime); - $maxTimeT = strtotime($maxTime); - $range = ($maxTimeT - $minTimeT) + 1; - $halfRange = (int)($range/2); - $midTimeT = $minTimeT + $halfRange; - $midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); -} - -//echo "MnT: $tempMinTime, MxT: $tempMaxTime, ExP: $tempExpandable
"; -if ( $tree ) -{ - appendDatetimeRange( $tree, $minTime, $maxTime ); - - $filterQuery = parseTreeToQuery( $tree ); -} -else -{ - $filterQuery = false; -} - -$scales = array( - array( "name"=>"year", "factor"=>60*60*24*365, "align"=>1, "zoomout"=>2, "label"=>STRF_TL_AXIS_LABEL_YEAR ), - array( "name"=>"month", "factor"=>60*60*24*30, "align"=>1, "zoomout"=>12, "label"=>STRF_TL_AXIS_LABEL_MONTH ), - array( "name"=>"week", "factor"=>60*60*24*7, "align"=>1, "zoomout"=>4.25, "label"=>STRF_TL_AXIS_LABEL_WEEK, "labelCheck"=>"%W" ), - array( "name"=>"day", "factor"=>60*60*24, "align"=>1, "zoomout"=>7, "label"=>STRF_TL_AXIS_LABEL_DAY ), - array( "name"=>"hour4", "factor"=>60*60, "align"=>4, "zoomout"=>6, "label"=>STRF_TL_AXIS_LABEL_4HOUR, "labelCheck"=>"%H" ), - array( "name"=>"hour", "factor"=>60*60, "align"=>1, "zoomout"=>4, "label"=>STRF_TL_AXIS_LABEL_HOUR, "labelCheck"=>"%H" ), - array( "name"=>"minute10", "factor"=>60, "align"=>10, "zoomout"=>6, "label"=>STRF_TL_AXIS_LABEL_10MINUTE, "labelCheck"=>"%M" ), - array( "name"=>"minute", "factor"=>60, "align"=>1, "zoomout"=>10, "label"=>STRF_TL_AXIS_LABEL_MINUTE, "labelCheck"=>"%M" ), - array( "name"=>"second10", "factor"=>1, "align"=>10, "zoomout"=>6, "label"=>STRF_TL_AXIS_LABEL_10SECOND ), - array( "name"=>"second", "factor"=>1, "align"=>1, "zoomout"=>10, "label"=>STRF_TL_AXIS_LABEL_SECOND ), -); - -$majXScale = getDateScale( $scales, $range, $chart['grid']['x']['major']['min'], $chart['grid']['x']['major']['max'] ); - -// Adjust the range etc for scale -$minTimeT -= $minTimeT%($majXScale['factor']*$majXScale['align']); -$minTime = strftime( STRF_FMT_DATETIME_DB, $minTimeT ); -$maxTimeT += (($majXScale['factor']*$majXScale['align'])-$maxTimeT%($majXScale['factor']*$majXScale['align']))-1; -if ( $maxTimeT > time() ) - $maxTimeT = time(); -$maxTime = strftime( STRF_FMT_DATETIME_DB, $maxTimeT ); -$range = ($maxTimeT - $minTimeT) + 1; -$halfRange = (int)($range/2); -$midTimeT = $minTimeT + $halfRange; -$midTime = strftime( STRF_FMT_DATETIME_DB, $midTimeT ); - -//echo "R:$range
"; -//echo "MnT:$minTime
"; -//echo "MnTt:$minTimeT
"; -//echo "MdT:$midTime
"; -//echo "MdTt:$midTimeT
"; -//echo "MxT:$maxTime
"; -//echo "MxTt:$maxTimeT
"; - -if ( isset($minTime) && isset($maxTime) ) -{ - $eventsSql .= " and E.EndTime >= '$minTime' and E.StartTime <= '$maxTime'"; -} - -$eventsSql .= " order by Id asc"; -//echo "ESQL: $eventsSql
"; - -$chart['data'] = array( - "x" => array( - "lo" => strtotime( $minTime ), - "hi" => strtotime( $maxTime ), - ), - "y" => array( - "lo" => 0, - "hi" => 0, - ) -); - -$chart['data']['x']['range'] = ($chart['data']['x']['hi'] - $chart['data']['x']['lo']) + 1; -$chart['data']['x']['density'] = $chart['data']['x']['range']/$chart['graph']['width']; - -$monEventSlots = array(); -$monFrameSlots = array(); -$monitorIds = array(); -foreach( dbFetchAll( $eventsSql ) as $event ) -{ - if ( !isset($monitorIds[$event['MonitorId']]) ) - $monitorIds[$event['MonitorId']] = true; - - if ( !isset($monEventSlots[$event['MonitorId']]) ) - $monEventSlots[$event['MonitorId']] = array(); - if ( !isset($monFrameSlots[$event['MonitorId']]) ) - $monFrameSlots[$event['MonitorId']] = array(); - - $currEventSlots = &$monEventSlots[$event['MonitorId']]; - $currFrameSlots = &$monFrameSlots[$event['MonitorId']]; - - $startTimeT = strtotime($event['StartTime']); - $startIndex = $rawStartIndex = (int)(($startTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); - if ( $startIndex < 0 ) - $startIndex = 0; - - if ( isset($event['EndTime']) ) - $endTimeT = strtotime($event['EndTime']); - else - $endTimeT = time(); - $endIndex = $rawEndIndex = (int)(($endTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); - - if ( $endIndex >= $chart['graph']['width'] ) - $endIndex = $chart['graph']['width'] - 1; - - for ( $i = $startIndex; $i <= $endIndex; $i++ ) - { - if ( !isset($currEventSlots[$i]) ) - { - if ( $rawStartIndex == $rawEndIndex ) - { - $offset = 1; - } - else - { - $offset = 1 + ($event['Frames']?((int)(($event['Frames']-1)*(($i-$rawStartIndex)/($rawEndIndex-$rawStartIndex)))):0); - } - $currEventSlots[$i] = array( "count"=>0, "width"=>1, "offset"=>$offset, "event"=>$event ); - } - else - { - $currEventSlots[$i]['count']++; - } - } - if ( $event['MaxScore'] > 0 ) - { - if ( $startIndex == $endIndex ) - { - $framesSql = "select FrameId,Score from Frames where EventId = ? and Score > 0 order by Score desc limit 1"; - $frame = dbFetchOne( $framesSql, NULL, array($event['Id']) ); - - $i = $startIndex; - if ( !isset($currFrameSlots[$i]) ) - { - $currFrameSlots[$i] = array( "count"=>1, "value"=>$event['MaxScore'], "event"=>$event, "frame"=>$frame ); - } - else - { - $currFrameSlots[$i]['count']++; - if ( $event['MaxScore'] > $currFrameSlots[$i]['value'] ) - { - $currFrameSlots[$i]['value'] = $event['MaxScore']; - $currFrameSlots[$i]['event'] = $event; - $currFrameSlots[$i]['frame'] = $frame; - } - } - if ( $event['MaxScore'] > $chart['data']['y']['hi'] ) - { - $chart['data']['y']['hi'] = $event['MaxScore']; - } - } - else - { - $framesSql = "select FrameId,Delta,unix_timestamp(TimeStamp) as TimeT,Score from Frames where EventId = ? and Score > 0"; - $result = dbQuery( $framesSql, array( $event['Id'] ) ); - while( $frame = dbFetchNext( $result ) ) - { - if ( $frame['Score'] == 0 ) - continue; - $frameTimeT = $frame['TimeT']; - $frameTimeT = $startTimeT + $frame['Delta']; - $frameIndex = (int)(($frameTimeT - $chart['data']['x']['lo']) / $chart['data']['x']['density']); - if ( $frameIndex < 0 ) - continue; - if ( $frameIndex >= $chart['graph']['width'] ) - continue; - - if ( !isset($currFrameSlots[$frameIndex]) ) - { - $currFrameSlots[$frameIndex] = array( "count"=>1, "value"=>$frame['Score'], "event"=>$event, "frame"=>$frame ); - } - else - { - $currFrameSlots[$frameIndex]['count']++; - if ( $frame['Score'] > $currFrameSlots[$frameIndex]['value'] ) - { - $currFrameSlots[$frameIndex]['value'] = $frame['Score']; - $currFrameSlots[$frameIndex]['event'] = $event; - $currFrameSlots[$frameIndex]['frame'] = $frame; - } - } - if ( $frame['Score'] > $chart['data']['y']['hi'] ) - { - $chart['data']['y']['hi'] = $frame['Score']; - } - } - } - } -} - -ksort( $monitorIds, SORT_NUMERIC ); -ksort( $monEventSlots, SORT_NUMERIC ); -ksort( $monFrameSlots, SORT_NUMERIC ); - -// No longer needed? -if ( false ) -{ - // Add on missing frames - foreach( array_keys($monFrameSlots) as $monitorId ) - { - unset( $currFrameSlots ); - $currFrameSlots = &$monFrameSlots[$monitorId]; - for ( $i = 0; $i < $chart['graph']['width']; $i++ ) - { - if ( isset($currFrameSlots[$i]) ) - { - if ( !isset($currFrameSlots[$i]['frame']) ) - { - $framesSql = "select FrameId,Score from Frames where EventId = ? and Score > 0 order by FrameId limit 1"; - $currFrameSlots[$i]['frame'] = dbFetchOne( $framesSql, NULL, array( $currFrameSlots[$i]['event']['Id'] ) ); - } - } - } - } -} - -$chart['data']['y']['range'] = ($chart['data']['y']['hi'] - $chart['data']['y']['lo']) + 1; -$chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['height']; - -$majYScale = getYScale( $chart['data']['y']['range'], $chart['grid']['y']['major']['min'], $chart['grid']['y']['major']['max'] ); - -$maxWidth = 0; -$maxHeight = 0; - -foreach ( array_keys($monitorIds) as $monitorId ) -{ - if ( $maxWidth < $monitors[$monitorId]['Width'] ) - $maxWidth = $monitors[$monitorId]['Width']; - if ( $maxHeight < $monitors[$monitorId]['Height'] ) - $maxHeight = $monitors[$monitorId]['Height']; -} - -//print_r( $monEventSlots ); -// Optimise boxes -foreach( array_keys($monEventSlots) as $monitorId ) -{ - unset( $currEventSlots ); - $currEventSlots = &$monEventSlots[$monitorId]; - for ( $i = 0; $i < $chart['graph']['width']; $i++ ) - { - if ( isset($currEventSlots[$i]) ) - { - if ( isset($currSlot) ) - { - if ( $currSlot['event']['Id'] == $currEventSlots[$i]['event']['Id'] ) - { - if ( $currSlot['width'] < $maxEventWidth ) - { - // Merge slots for the same long event - $currSlot['width']++; - unset( $currEventSlots[$i] ); - continue; - } - elseif ( $currSlot['offset'] < $currEventSlots[$i]['offset'] ) - { - // Split very long events - $currEventSlots[$i]['frame'] = array( 'FrameId'=>$currEventSlots[$i]['offset'] ); - } - } - elseif ( $currSlot['width'] < $minEventWidth ) - { - // Merge multiple small events - $currSlot['width']++; - unset( $currEventSlots[$i] ); - continue; - } - } - $currSlot = &$currEventSlots[$i]; - } - else - { - unset( $currSlot ); - } - } - if ( isset( $currSlot ) ) - unset( $currSlot ); -} -//print_r( $monEventSlots ); - -// Stack events -$frameSlots = array(); -$frameMonitorIds = array_keys($monFrameSlots); -for ( $i = 0; $i < $chart['graph']['width']; $i++ ) -{ - foreach ( $frameMonitorIds as $frameMonitorId ) - { - unset( $currFrameSlots ); - $currFrameSlots = &$monFrameSlots[$frameMonitorId]; - if ( isset($currFrameSlots[$i]) ) - { - if ( !isset($frameSlots[$i]) ) - { - $frameSlots[$i] = array(); - $frameSlots[$i][] = &$currFrameSlots[$i]; - } - else - { - $slotCount = count($frameSlots[$i]); - for ( $j = 0; $j < $slotCount; $j++ ) - { - if ( $currFrameSlots[$i]['value'] > $frameSlots[$i][$j]['value'] ) - { - for ( $k = $slotCount; $k > $j; $k-- ) - { - $frameSlots[$i][$k] = $frameSlots[$i][$k-1]; - } - $frameSlots[$i][$j] = &$currFrameSlots[$i]; - break 2; - } - } - $frameSlots[$i][] = &$currFrameSlots[$i]; - } - } - } -} - -//print_r( $monEventSlots ); -//print_r( $monFrameSlots ); -//print_r( $chart ); - -$graphHeight = $chart['graph']['height']; - -if ( $mode == "overlay" ) -{ - $minEventBarHeight = 10; - $maxEventBarHeight = 40; - - if ( count($monitorIds) ) - { - $chart['graph']['eventBarHeight'] = $minEventBarHeight; - while ( ($chart['graph']['eventsHeight'] = (($chart['graph']['eventBarHeight'] * count($monitorIds)) + (count($monitorIds)-1))) < $maxEventBarHeight ) - { - $chart['graph']['eventBarHeight']++; - } - } - else - { - $chart['graph']['eventBarHeight'] = $maxEventBarHeight; - $chart['graph']['eventsHeight'] = $maxEventBarHeight; - } - $chart['graph']['activityHeight'] = ($graphHeight - $chart['graph']['eventsHeight']); - $chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityHeight']; - - $chart['eventBars'] = array(); - $top = $chart['graph']['activityHeight']; - foreach ( array_keys($monitorIds) as $monitorId ) - { - $chart['eventBars'][$monitorId] = array( 'top' => $top ); - $top += $chart['graph']['eventBarHeight']+1; - } -} -elseif ( $mode == "split" ) -{ - $minActivityBarHeight = 30; - $minEventBarHeight = 10; - $maxEventBarHeight = 40; - - if ( count($monitorIds) ) - { - $chart['graph']['eventBarHeight'] = $minEventBarHeight; - $chart['graph']['activityBarHeight'] = $minActivityBarHeight; - while ( ((($chart['graph']['eventBarHeight']+$chart['graph']['activityBarHeight']) * count($monitorIds)) + ((2*count($monitorIds))-1)) < $graphHeight ) - { - $chart['graph']['activityBarHeight']++; - if ( $chart['graph']['eventBarHeight'] < $maxEventBarHeight ) - { - $chart['graph']['eventBarHeight']++; - } - } - } - else - { - $chart['graph']['eventBarHeight'] = $maxEventBarHeight; - $chart['graph']['activityBarHeight'] = $graphHeight - $chart['graph']['eventBarHeight']; - } - $chart['data']['y']['density'] = $chart['data']['y']['range']/$chart['graph']['activityBarHeight']; - -?> - $top ); - $chart['eventBars'][$monitorId] = array( 'top' => $top+$chart['graph']['activityBarHeight']+1 ); - $top += $chart['graph']['activityBarHeight']+1+$chart['graph']['eventBarHeight']+1; - } -} - -preg_match( '/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $minTime, $startMatches ); -preg_match( '/^(\d+)-(\d+)-(\d+) (\d+):(\d+)/', $maxTime, $endMatches ); - -if ( $startMatches[1] != $endMatches[1] ) -{ - // Different years - $title = strftime( STRF_TL_AXIS_RANGE_YEAR1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_YEAR2, $chart['data']['x']['hi'] ); -} -elseif ( $startMatches[2] != $endMatches[2] ) -{ - // Different months - $title = strftime( STRF_TL_AXIS_RANGE_MONTH1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_MONTH2, $chart['data']['x']['hi'] ); -} -elseif ( $startMatches[3] != $endMatches[3] ) -{ - // Different dates - $title = strftime( STRF_TL_AXIS_RANGE_DAY1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_DAY2, $chart['data']['x']['hi'] ); -} -else -{ - // Different times - $title = strftime( STRF_TL_AXIS_RANGE_TIME1, $chart['data']['x']['lo'] )." - ".strftime( STRF_TL_AXIS_RANGE_TIME2, $chart['data']['x']['hi'] ); -} - -function drawXGrid( $chart, $scale, $labelClass, $tickClass, $gridClass, $zoomClass=false ) -{ - global $SLANG; - - ob_start(); - $labelCount = 0; - $lastTick = 0; - unset( $lastLabel ); - $labelCheck = isset($scale['labelCheck'])?$scale['labelCheck']:$scale['label']; -?> -
- 1 ) - { - $label = (int)(strftime( $labelCheck, $timeOffset )/$scale['align']); - } - else - { - $label = strftime( $labelCheck, $timeOffset ); - } - if ( !isset($lastLabel) || ($lastLabel != $label) ) - { - $labelCount++; - } - if ( $labelCount >= $scale['divisor'] ) - { - $labelCount = 0; - if ( isset($lastLabel) ) - { - if ( $labelClass ) - { -?> -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- -
- - -
- -
-
-
-
<?= $SLANG['ViewEvent'] ?>
-
-
-
-
-

-

-

-

-
-
-
-
- -
-
-
-
- -
-$slots ) - { - foreach ( $slots as $slot ) - { - $slotHeight = (int)($slot['value']/$chart['data']['y']['density']); - - if ( $slotHeight <= 0 ) - continue; - - if ( $mouseover ) - { - $behaviours = array( - 'onclick="'.getSlotShowEventBehaviour( $slot ).'"', - 'onmouseover="'.getSlotPreviewEventBehaviour( $slot ).'"' - ); - } - else - { - $behaviours = array( - 'onclick="'.getSlotPreviewEventBehaviour( $slot ).'"' - ); - } -?> -
>
- -
- -
-$slot ) - { - $slotHeight = (int)($slot['value']/$chart['data']['y']['density']); - - if ( $slotHeight <= 0 ) - continue; - - if ( $mouseover ) - { - $behaviours = array( - 'onclick="'.getSlotShowEventBehaviour( $slot ).'"', - 'onmouseover="'.getSlotPreviewEventBehaviour( $slot ).'"' - ); - } - else - { - $behaviours = array( - 'onclick="'.getSlotPreviewEventBehaviour( $slot ).'"' - ); - } - ?> -
>
- -
- -
- -
>
- -
- -
-
-
-
- - <?= $monitors[$monitorId]['Name'] ?> - -
-
-
-
-
- - diff --git a/web/skins/flat/views/user.php b/web/skins/flat/views/user.php deleted file mode 100644 index da42a2f25..000000000 --- a/web/skins/flat/views/user.php +++ /dev/null @@ -1,158 +0,0 @@ -$SLANG['No'], 1=>$SLANG['Yes'] ); -$nv = array( 'None'=>$SLANG['None'], 'View'=>$SLANG['View'] ); -$nve = array( 'None'=>$SLANG['None'], 'View'=>$SLANG['View'], 'Edit'=>$SLANG['Edit'] ); -$bandwidths = array_merge( array( ""=>"" ), $bwArray ); -$langs = array_merge( array( ""=>"" ), getLanguages() ); - -$sql = "select Id,Name from Monitors order by Sequence asc"; -$monitors = array(); -foreach( dbFetchAll( $sql ) as $monitor ) -{ - $monitors[] = $monitor; -} - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['User']." - ".$newUser['Username'] ); -?> - -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
-
-
-
- - diff --git a/web/skins/flat/views/version.php b/web/skins/flat/views/version.php deleted file mode 100644 index 2feacbbbe..000000000 --- a/web/skins/flat/views/version.php +++ /dev/null @@ -1,93 +0,0 @@ - $SLANG['GoToZoneMinder'] -); - -if ( verNum( ZM_DYN_CURR_VERSION ) != verNum( ZM_DYN_LAST_VERSION ) ) -{ - $options = array_merge( $options, array( - "ignore" => $SLANG['VersionIgnore'], - "hour" => $SLANG['VersionRemindHour'], - "day" => $SLANG['VersionRemindDay'], - "week" => $SLANG['VersionRemindWeek'], - "never" => $SLANG['VersionRemindNever'] - ) ); -} - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['Version'] ); -?> - -
- -
- -

-

-
- -
- -

-

-

-
- -
- -
- - -

-

-

-
- - -
-
- -
-
- - diff --git a/web/skins/flat/views/video.php b/web/skins/flat/views/video.php deleted file mode 100644 index 9ff0e6a5e..000000000 --- a/web/skins/flat/views/video.php +++ /dev/null @@ -1,241 +0,0 @@ - - -
- -
- -

-
- -
- - - - - - - - - - - - - - - - - - - - -
checked="checked"/>
- disabled="disabled"/> -
- -

- - - -

- -

- - - - - - - - - - - - - 0 ) - { - preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches ); - if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) - { - $rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); - $rateText = isset($rates[$rate])?$rates[$rate]:($rate."x"); - } - elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) - { - $rateText = $temp_matches[1]."fps"; - } - if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) - { - $scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); - $scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x"); - } - elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) - { - $scaleText = $temp_matches[1]; - } - $width = $scale?reScale( $event['Width'], $scale ):$event['Width']; - $height = $scale?reScale( $event['Height'], $scale ):$event['Height']; -?> - - - - - - - - - -
 /  / 
- -
-
- - diff --git a/web/skins/flat/views/watch.php b/web/skins/flat/views/watch.php deleted file mode 100644 index baad13323..000000000 --- a/web/skins/flat/views/watch.php +++ /dev/null @@ -1,231 +0,0 @@ - - -
-
- -
- -
-
- -
- -
- -
 -  fps
-
- - - -
- -
- - - - - -
-
- - diff --git a/web/skins/flat/views/zone.php b/web/skins/flat/views/zone.php deleted file mode 100644 index 980c0e430..000000000 --- a/web/skins/flat/views/zone.php +++ /dev/null @@ -1,278 +0,0 @@ - 0 ) - { - $zone = dbFetchOne( 'SELECT * FROM Zones WHERE MonitorId = ? AND Id=?', NULL, array( $monitor['Id'], $zid ) ); - } - else - { - $zone = array( - 'Name' => $SLANG['New'], - 'Id' => 0, - 'MonitorId' => $monitor['Id'], - 'NumCoords' => 4, - 'Coords' => sprintf( "%d,%d %d,%d, %d,%d %d,%d", $minX, $minY, $maxX, $minY, $maxX, $maxY, $minX, $maxY ), - 'Area' => $monitor['Width'] * $monitor['Height'], - 'AlarmRGB' => 0xff0000, - 'CheckMethod' => 'Blobs', - 'MinPixelThreshold' => '', - 'MaxPixelThreshold' => '', - 'MinAlarmPixels' => '', - 'MaxAlarmPixels' => '', - 'FilterX' => '', - 'FilterY' => '', - 'MinFilterPixels' => '', - 'MaxFilterPixels' => '', - 'MinBlobPixels' => '', - 'MaxBlobPixels' => '', - 'MinBlobs' => '', - 'MaxBlobs' => '', - 'OverloadFrames' => '', - 'ExtendAlarmFrames' => '', - ); - } - $zone['Points'] = coordsToPoints( $zone['Coords'] ); - - $newZone = $zone; -} - -//if ( !$points ) -//{ - //$points = $zone['Points']; -//} - -ksort( $newZone['Points'], SORT_NUMERIC ); - -$newZone['Coords'] = pointsToCoords( $newZone['Points'] ); -$newZone['Area'] = getPolyArea( $newZone['Points'] ); -$selfIntersecting = isSelfIntersecting( $newZone['Points'] ); - -$wd = getcwd(); -chdir( ZM_DIR_IMAGES ); -$command = getZmuCommand( " -m ".$mid." -z" ); -$command .= '"'.$zid.' '.$hicolor.' '.$newZone['Coords'].'"'; -$status = exec( escapeshellcmd( $command ) ); -chdir( $wd ); - -$zoneImage = ZM_DIR_IMAGES.'/Zones'.$monitor['Id'].'.jpg?'.time(); - -$focusWindow = true; - -xhtmlHeaders(__FILE__, $SLANG['Zone'] ); -?> - -
- -
-
- - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"applyPreset()", "onblur"=>"this.selectedIndex=0" ) ) ?>
 /  / 
-
-
-
-
- Zone Image -
-
- - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - -
disabled="disabled"/> disabled="disabled"/>+ 3 ) { ?>  X
-
 
- disabled="disabled"/> -
-
-
-
- - diff --git a/web/skins/flat/views/zones.php b/web/skins/flat/views/zones.php deleted file mode 100644 index 76d249b61..000000000 --- a/web/skins/flat/views/zones.php +++ /dev/null @@ -1,105 +0,0 @@ - - -
- -
- - - <?= htmlspecialchars($zone['Name']) ?> - - - - zones -
- - - - - - - - - - - - - - - - - - - - - - -
 /  disabled="disabled"/>
-
- disabled="disabled"/> - -
-
-
-
- - diff --git a/web/skins/mobile/Makefile.am b/web/skins/mobile/Makefile.am deleted file mode 100644 index 35051fade..000000000 --- a/web/skins/mobile/Makefile.am +++ /dev/null @@ -1,14 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/mobile - -SUBDIRS = \ - ajax \ - css \ - graphics \ - includes \ - lang \ - views - -dist_web_DATA = \ - skin.php diff --git a/web/skins/mobile/ajax/Makefile.am b/web/skins/mobile/ajax/Makefile.am deleted file mode 100644 index 0558929c0..000000000 --- a/web/skins/mobile/ajax/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/mobile/ajax - -dist_web_DATA = # No files here diff --git a/web/skins/mobile/css/Makefile.am b/web/skins/mobile/css/Makefile.am deleted file mode 100644 index 6353e163e..000000000 --- a/web/skins/mobile/css/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/mobile/css - -dist_web_DATA = \ - skin.css diff --git a/web/skins/mobile/css/skin.css b/web/skins/mobile/css/skin.css deleted file mode 100644 index b996392a3..000000000 --- a/web/skins/mobile/css/skin.css +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Primary look and feel styles - */ - -body { - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: 100%; - color: #333333; - font-weight: normal; - text-align: center; -} - -h1 { - font-family: inherit; - font-size: 120%; - color: #000066; - font-weight: bold; -} - -h2 { - font-family: inherit; - font-size: 110%; - color: #000066; - font-weight: bold; -} - -h3 { - font-family: inherit; - font-size: 100%; - color: #016A9D; - font-weight: bold; -} - -p { - font-family: inherit; - font-size: 100%; - color: #333333; - font-weight: normal; -} - -th { - font-weight: bold; - color: #016A9D; -} - -a:link { - color: #7F7FB2; - text-decoration: none; -} - -a:visited { - color: #7F7FB2; - text-decoration: none; -} - -a:hover { - color: #666699; - text-decoration: underline; -} - -label { - margin-right: 4px; -} - -input,textarea,select { - border: 1px #7F7FB2 solid; - font-family: inherit; - font-size: 100%; - color: #333333; -} - -input[type=text], input[type=password], textarea { - padding: 1px; -} - -input.noborder { - border: 0; -} - -input[disabled] { - color: #888888; -} - -img.normal { - border: white solid 1px; -} - -img.alarm { - border: red solid 1px; -} - -hr { - height: 1px; - width: 100%; - border: 0; - color: #7f7fb2; - background-color: #7f7fb2; -} - -/* - * Major league table for multiple inputs or presentation - */ - -#content table.major { - margin: 4px auto; - width: 100%; - border-collapse: collapse; -} - -#content table.major tr.highlight { - background-color: #eeeeee; -} - -#content table.major thead tr th { - padding-top: 6px; - padding-bottom: 6px; - vertical-align: middle; -} - -#content table.major tfoot td { - padding-top: 6px; - padding-bottom: 6px; - vertical-align: middle; -} - -#content table.major th, #content table.major td { - border: 1px solid #7f7fb2; - padding: 3px; - text-align: left; -} - -#content table.major th { - vertical-align: bottom; -} - -#content table.major td { - vertical-align: middle; -} - -#content table.major th[scope=row] { - padding: 4px 3px 3px; - vertical-align: top; - text-align: right; -} - -#content table.major .colMark, #content table.major .colSelect { - text-align: center; -} -/* - * Lesser table for very simple forms - */ - -#content table.minor { - width: 200px; - margin: 0 auto; -} - -#content table.minor td { - padding: 4px; -} - -#content table.minor .colLeft { - width: 50%; - text-align: right; -} - -#content table.minor .colRight { - width: 50%; - text-align: left; -} - -#content table.minor input[type=submit] -{ - margin-top: 4px; - padding: 0 2px; - font-size: 120%; -} - -/* - * Behavior classes - */ -.error { - /*font-family: Verdana, Arial, Helvetica, sans-serif;*/ - font-size: 100%; - color: #DC143C; - font-weight: bold; -} - -.warn { - /*font-family: Verdana, Arial, Helvetica, sans-serif;*/ - font-size: 100%; - color: #FF8C00; - font-weight: bold; -} - -.info { - /*font-family: Verdana, Arial, Helvetica, sans-serif;*/ - font-size: 100%; - color: #688E23; - font-weight: bold; -} - -.errorText { - color: #DC143C; -} - -.warnText { - color: #FF8C00; -} - -.infoText { - color: #688E23; -} - -.disabledText { - font-style: italic; -} - -/* - * Generic useful classes, especially with mootools - */ - -.hidden { - display: none; -} - -.invisible { - visibility: hidden; -} - -.nowrap { - white-space: nowrap; -} - -div.clear { - clear: both; -} - -/* - * Primary layout styles - */ - -#page { - width: 100%; -} - -#header { - width: 98%; - line-height: 24px; - margin: 4px auto 0; - clear: both; -} - -#header h2 { - left: 0; -} - -#headerControl { -} - -#headerButtons { - float: right; -} - -#headerButtons a { - margin-left: 8px; -} - -#content { - width: 98%; - margin: 4px auto; - line-height: 130%; - text-align: center; - clear: both; -} - -#contentTable { - width: 100%; -} - -#content p { - margin-top: 4px; -} - -#content p.textblock { - text-align: justify; - padding: 4px; -} - -#content > input[type=submit], #content > input[type=button] { - margin-top: 4px; -} - -#content table input[type=submit], #content table input[type=button] { - margin-top: 0; -} - -#contentButtons { - margin: 4px auto 0; -} - -#contentButtons input, #contentButtons a { - margin: 0 4px; -} - -#footer { - width: 98%; - margin: 4px auto 0; - clear: both; -} - diff --git a/web/skins/mobile/graphics/Makefile.am b/web/skins/mobile/graphics/Makefile.am deleted file mode 100644 index dfd43bef7..000000000 --- a/web/skins/mobile/graphics/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/mobile/graphics - -dist_web_DATA = # No files here diff --git a/web/skins/mobile/includes/Makefile.am b/web/skins/mobile/includes/Makefile.am deleted file mode 100644 index c88ad435a..000000000 --- a/web/skins/mobile/includes/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/mobile/includes - -dist_web_DATA = \ - init.php \ - config.php \ - functions.php \ - control_functions.php diff --git a/web/skins/mobile/includes/config.php b/web/skins/mobile/includes/config.php deleted file mode 100644 index 956c36bcc..000000000 --- a/web/skins/mobile/includes/config.php +++ /dev/null @@ -1,63 +0,0 @@ - "50x", - "2000" => "20x", - "500" => "5x", - "200" => "2x", - "100" => $SLANG['Real'], - "50" => "1/2x", -); - -$scales = array( - "400" => "4x", - "300" => "3x", - "200" => "2x", - "150" => "1.5x", - "100" => $SLANG['Actual'], - "75" => "3/4x", - "50" => "1/2x", - "33" => "1/3x", - "25" => "1/4x", -); - -switch ( $_COOKIE['zmBandwidth'] ) -{ - case "phone" : // Very incomplete at present - { - define( "ZM_WEB_CAN_STREAM", ZM_WEB_P_CAN_STREAM ); // Override the automatic detection of browser streaming capability - define( "ZM_WEB_STREAM_METHOD", ZM_WEB_P_STREAM_METHOD ); // Which method should be used to send video streams to your brow - define( "ZM_WEB_DEFAULT_SCALE", ZM_WEB_P_DEFAULT_SCALE ); // What the default scaling factor applied to 'live' or 'event' views is (%) - define( "ZM_WEB_DEFAULT_RATE", ZM_WEB_P_DEFAULT_RATE ); // What the default replay rate factor applied to 'event' views is (%) - define( "ZM_WEB_VIDEO_BITRATE", ZM_WEB_P_VIDEO_BITRATE ); // What the bitrate of any streamed video should be - define( "ZM_WEB_VIDEO_MAXFPS", ZM_WEB_P_VIDEO_MAXFPS ); // What the maximum frame rate of any streamed video should be - define( "ZM_WEB_SCALE_THUMBS", ZM_WEB_P_SCALE_THUMBS ); // Image scaling for thumbnails, bandwidth versus cpu in rescaling - define( "ZM_WEB_AJAX_TIMEOUT", ZM_WEB_P_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset - - break; - } -} - -?> diff --git a/web/skins/mobile/includes/control_functions.php b/web/skins/mobile/includes/control_functions.php deleted file mode 100644 index 3a5314998..000000000 --- a/web/skins/mobile/includes/control_functions.php +++ /dev/null @@ -1,186 +0,0 @@ - -
-
-
- -
- - " name="preset" value=""/> - -
-
-
-
- diff --git a/web/skins/mobile/includes/functions.php b/web/skins/mobile/includes/functions.php deleted file mode 100644 index 40c1b055a..000000000 --- a/web/skins/mobile/includes/functions.php +++ /dev/null @@ -1,66 +0,0 @@ -'."\n" ); -?> - - - - <?= ZM_WEB_TITLE_PREFIX ?> - <?= $title ?> - - - - - - - - - - - diff --git a/web/skins/mobile/includes/init.php b/web/skins/mobile/includes/init.php deleted file mode 100644 index ae92bcdee..000000000 --- a/web/skins/mobile/includes/init.php +++ /dev/null @@ -1,29 +0,0 @@ - diff --git a/web/skins/mobile/lang/Makefile.am b/web/skins/mobile/lang/Makefile.am deleted file mode 100644 index 5084203fb..000000000 --- a/web/skins/mobile/lang/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/mobile/lang - -dist_web_DATA = # No files here diff --git a/web/skins/mobile/skin.php b/web/skins/mobile/skin.php deleted file mode 100644 index 51ea63e01..000000000 --- a/web/skins/mobile/skin.php +++ /dev/null @@ -1,97 +0,0 @@ -GetDeviceCapabilitiesFromAgent($_SERVER['HTTP_USER_AGENT']); - - //print_r( $wurfl->wurfl_agent ); - if ( $wurfl->wurfl_agent ) - { - if ( $wurfl->getDeviceCapability( 'html_wi_oma_xhtmlmp_1_0' ) ) - { - $device['width'] = $wurfl->getDeviceCapability( 'resolution_width' ); - $device['height'] = $wurfl->getDeviceCapability( 'resolution_height' ); - } - } -} -else -{ - // This is an example of using fixed device strings to just match your phone etc - $devices = array( - array( 'name'=>"Motorola V600", 'ua_match'=>"MOT-V600", 'skin'=>"mobile", 'cookies'=>false, 'width'=>176, 'height'=>220 ), - ); - - foreach ( $devices as $tempDevice ) - { - if ( preg_match( '/'.$tempDevice['ua_match'].'/', $_SERVER['HTTP_USER_AGENT'] ) ) - { - $skin = $tempDevice['skin']; - $cookies = $tempDevice['cookies']; - break; - } - } -} - -foreach ( getSkinIncludes( 'includes/config.php' ) as $includeFile ) - require_once $includeFile; - -foreach ( getSkinIncludes( 'includes/functions.php' ) as $includeFile ) - require_once $includeFile; - -if ( empty($view) ) - $view = isset($user)?'console':'login'; - -if ( !isset($user) && ZM_OPT_USE_AUTH && ZM_AUTH_TYPE == "remote" && !empty( $_SERVER['REMOTE_USER']) ) -{ - $view = "postlogin"; - $action = "login"; - $_REQUEST['username'] = $_SERVER['REMOTE_USER']; -} - -// If there are additional actions -foreach ( getSkinIncludes( 'includes/actions.php' ) as $includeFile ) - require_once $includeFile; - -?> diff --git a/web/skins/mobile/views/Makefile.am b/web/skins/mobile/views/Makefile.am deleted file mode 100644 index c7d175ca3..000000000 --- a/web/skins/mobile/views/Makefile.am +++ /dev/null @@ -1,22 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -SUBDIRS = \ - css - -webdir = @WEB_PREFIX@/skins/mobile/views - -dist_web_DATA = \ - console.php \ - devices.php \ - error.php \ - eventdetails.php \ - event.php \ - events.php \ - filter.php \ - frame.php \ - function.php \ - login.php \ - montage.php \ - state.php \ - video.php \ - watch.php diff --git a/web/skins/mobile/views/console.php b/web/skins/mobile/views/console.php deleted file mode 100644 index 9422c5d62..000000000 --- a/web/skins/mobile/views/console.php +++ /dev/null @@ -1,178 +0,0 @@ - array( - "terms" => array( - array( "attr" => "Archived", "op" => "=", "val" => "0" ), - array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-1 hour" ), - ) - ), - ), - // Today - array( - "filter" => array( - "terms" => array( - array( "attr" => "Archived", "op" => "=", "val" => "0" ), - array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "today" ), - ) - ), - ), -); - -$running = daemonCheck(); -$status = $running?$SLANG['Running']:$SLANG['Stopped']; - -if ( $group = dbFetchOne( "select * from Groups where Name = 'Mobile'" ) ) - $groupIds = array_flip(explode( ',', $group['MonitorIds'] )); - -$maxWidth = 0; -$maxHeight = 0; -$cycleCount = 0; -$monitors = dbFetchAll( "select * from Monitors order by Sequence asc" ); -for ( $i = 0; $i < count($monitors); $i++ ) -{ - if ( !visibleMonitor( $monitors[$i]['Id'] ) ) - { - continue; - } - if ( $group && !empty($groupIds) && !array_key_exists( $monitors[$i]['Id'], $groupIds ) ) - { - continue; - } - $monitors[$i]['Show'] = true; - $monitors[$i]['zmc'] = zmcStatus( $monitors[$i] ); - $monitors[$i]['zma'] = zmaStatus( $monitors[$i] ); - $counts = array(); - for ( $j = 0; $j < count($eventCounts); $j++ ) - { - $filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['terms']), array( "cnj" => "and", "attr" => "MonitorId", "op" => "=", "val" => $monitors[$i]['Id'] ) ); - parseFilter( $filter, false, '&' ); - $counts[] = "count(if(1".$filter['sql'].",1,NULL)) as EventCount$j"; - $monitors[$i]['eventCounts'][$j]['filter'] = $filter; - } - $sql = "select ".join($counts,", ")." from Events as E where MonitorId = '".$monitors[$i]['Id']."'"; - $counts = dbFetchOne( $sql ); - if ( $monitors[$i]['Function'] != 'None' ) - { - $cycleCount++; - if ( $maxWidth < $monitors[$i]['Width'] ) $maxWidth = $monitors[$i]['Width']; - if ( $maxHeight < $monitors[$i]['Height'] ) $maxHeight = $monitors[$i]['Height']; - } - $monitors[$i] = array_merge( $monitors[$i], $counts ); -} - -xhtmlHeaders( __FILE__, $SLANG['Console'] ); -?> - -
- -
- - - - - - - - - - - - - - - - - 1 ) { -?> - - - - - - - -
".substr( $monitor['Function'], 0, 4 )."", canEdit( 'Monitors' ) ) ?>
  
-
-
- - diff --git a/web/skins/mobile/views/css/Makefile.am b/web/skins/mobile/views/css/Makefile.am deleted file mode 100644 index 86e0ff0a3..000000000 --- a/web/skins/mobile/views/css/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/mobile/views/css - -dist_web_DATA = \ - console.css diff --git a/web/skins/mobile/views/css/console.css b/web/skins/mobile/views/css/console.css deleted file mode 100644 index 49c711039..000000000 --- a/web/skins/mobile/views/css/console.css +++ /dev/null @@ -1,16 +0,0 @@ -#systemTime { - float: left; -} - -#systemState { - margin: 0 auto; - text-align: center; -} - -#systemStats { - float: right; -} - -td.colEvents { - text-align: right; -} diff --git a/web/skins/mobile/views/devices.php b/web/skins/mobile/views/devices.php deleted file mode 100644 index 1270e518d..000000000 --- a/web/skins/mobile/views/devices.php +++ /dev/null @@ -1,73 +0,0 @@ - - -
- -
- - - - - - - - -
-

-
-
- - diff --git a/web/skins/mobile/views/error.php b/web/skins/mobile/views/error.php deleted file mode 100644 index 0adf4f9f3..000000000 --- a/web/skins/mobile/views/error.php +++ /dev/null @@ -1,34 +0,0 @@ - - -
- -
-

-

-
-
- - diff --git a/web/skins/mobile/views/event.php b/web/skins/mobile/views/event.php deleted file mode 100644 index 182bd7775..000000000 --- a/web/skins/mobile/views/event.php +++ /dev/null @@ -1,147 +0,0 @@ -= ?".$_REQUEST['filter']['sql'].$midSql." order by $sortColumn asc"; -} -$result = dbQuery( $sql, array( $event[$_REQUEST['sort_field']] ) ); -while ( $row = dbFetchNext( $result ) ) -{ - if ( $row['Id'] == $_REQUEST['eid'] ) - { - $prevEvent = dbFetchNext( $result ); - break; - } -} - -$sql = "select E.* from Events as E inner join Monitors as M on E.MonitorId = M.Id where $sortColumn ".($sortOrder=='asc'?'>=':'<=').' ?'.$_REQUEST['filter']['sql'].$midSql." order by $sortColumn $sortOrder"; -$result = dbQuery( $sql, array($event[$_REQUEST['sort_field']]) ); -while ( $row = dbFetchNext( $result ) ) -{ - if ( $row['Id'] == $_REQUEST['eid'] ) - { - $nextEvent = dbFetchNext( $result ); - break; - } -} - -$framesPerPage = 15; -$framesPerLine = 3; -$maxShortcuts = 3; - -$paged = $event['Frames'] > $framesPerPage; - -if ( $paged && !empty($_REQUEST['page']) ) -{ - $loFrameId = (($_REQUEST['page']-1)*$framesPerPage)+1; - $hiFrameId = min( $_REQUEST['page']*$framesPerPage, $event['Frames'] ); -} -else -{ - $loFrameId = 1; - $hiFrameId = $event['Frames']; -} - -$sql = 'SELECT * FROM Frames WHERE EventID = ?'; -if ( $paged && !empty($_REQUEST['page']) ) - $sql .= " and FrameId between $loFrameId and $hiFrameId"; -$sql .= " order by FrameId"; -$frames = dbFetchAll( $sql, NULL, array( $_REQUEST['eid'] ) ); - -$scale = getDeviceScale( $event['Width'], $event['Height'], $framesPerLine+0.3 ); - -$pages = (int)ceil($event['Frames']/$framesPerPage); -if ( !empty($_REQUEST['fid']) ) - $_REQUEST['page'] = ($_REQUEST['fid']/$framesPerPage)+1; - -$pagination = getPagination( $pages, $_REQUEST['page'], $maxShortcuts, '&eid='.$_REQUEST['eid'].$filterQuery.$sortQuery, '&' ); - -xhtmlHeaders( __FILE__, $SLANG['Event'].' - '.$event['Name'] ); -?> - -
- -
- -

- -
- - <?= $frame['Type'] ?>/<?= $frame['Type']=='Alarm'?$frame['Score']:0 ?> - -
-
-
- - diff --git a/web/skins/mobile/views/eventdetails.php b/web/skins/mobile/views/eventdetails.php deleted file mode 100644 index 65d2862ff..000000000 --- a/web/skins/mobile/views/eventdetails.php +++ /dev/null @@ -1,86 +0,0 @@ - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
s
()
//
-
- 1 - <?= $frame['FrameId'] ?> -
-
- - -
-
-
- - diff --git a/web/skins/mobile/views/events.php b/web/skins/mobile/views/events.php deleted file mode 100644 index 4a897aec1..000000000 --- a/web/skins/mobile/views/events.php +++ /dev/null @@ -1,157 +0,0 @@ -$deviceLines)?$deviceLines:$limitLeft; - } - $eventsSql .= " limit $limitStart, $limitAmount"; -} -elseif ( !empty( $_REQUEST['limit'] ) ) -{ - $eventsSql .= " limit 0, ".$_REQUEST['limit']; -} - -$nEvents = dbFetchOne( $countSql, 'EventCount' ); -if ( !empty($limit) && $nEvents > $_REQUEST['limit'] ) -{ - $nEvents = $_REQUEST['limit']; -} -$pages = (int)ceil($nEvents/$deviceLines); - -$maxShortcuts = 3; -$pagination = getPagination( $pages, $_REQUEST['page'], $maxShortcuts, $filterQuery.$sortQuery.'&limit='.$_REQUEST['limit'], '&' ); - -xhtmlHeaders( __FILE__, $SLANG['Events'] ); -?> - -
- -
- -

- - - - - - - - - - - - - - - - - - - -
-

-
-
- - diff --git a/web/skins/mobile/views/filter.php b/web/skins/mobile/views/filter.php deleted file mode 100644 index d01249109..000000000 --- a/web/skins/mobile/views/filter.php +++ /dev/null @@ -1,71 +0,0 @@ - - -
- -
-
- - 0 ) -{ -?> -
- -
-
- -
- -

- -
-
-
- - diff --git a/web/skins/mobile/views/frame.php b/web/skins/mobile/views/frame.php deleted file mode 100644 index 73f5e5a30..000000000 --- a/web/skins/mobile/views/frame.php +++ /dev/null @@ -1,73 +0,0 @@ - - -
- -
- "> -
- 1 ) { ?> - << - 1 ) { ?> - < - - > - - >> - -
-
-
- - diff --git a/web/skins/mobile/views/function.php b/web/skins/mobile/views/function.php deleted file mode 100644 index 8825129ff..000000000 --- a/web/skins/mobile/views/function.php +++ /dev/null @@ -1,66 +0,0 @@ - - -
- -
-
- -
- -
-
- checked="checked"/> -
-
- -
-
-
-
- - diff --git a/web/skins/mobile/views/login.php b/web/skins/mobile/views/login.php deleted file mode 100644 index 7882ce9be..000000000 --- a/web/skins/mobile/views/login.php +++ /dev/null @@ -1,54 +0,0 @@ - - -
- -
-
- - - - - - - - - - -
" size="12"/>
-
- -
-
-
-
- - diff --git a/web/skins/mobile/views/montage.php b/web/skins/mobile/views/montage.php deleted file mode 100644 index a491804e6..000000000 --- a/web/skins/mobile/views/montage.php +++ /dev/null @@ -1,75 +0,0 @@ - - -
- -
-
- - <?= $monitor['Name'] ?> - -
-
-
-
- - diff --git a/web/skins/mobile/views/state.php b/web/skins/mobile/views/state.php deleted file mode 100644 index caa7f8f74..000000000 --- a/web/skins/mobile/views/state.php +++ /dev/null @@ -1,77 +0,0 @@ - - -
- -
-
- - -
- -
-
-
-
- - diff --git a/web/skins/mobile/views/video.php b/web/skins/mobile/views/video.php deleted file mode 100644 index 02fc128b9..000000000 --- a/web/skins/mobile/views/video.php +++ /dev/null @@ -1,226 +0,0 @@ -= 352 && $deviceHeight >= 288 ) - $videoSize = "352x288"; -elseif ( $deviceWidth >= 176 && $deviceHeight >= 144 ) - $videoSize = "176x144"; -else - $videoSize = "128x96"; - -$eventWidth = $event['Width']; -$eventHeight = $event['Height']; - -if ( !isset( $rate ) ) - $_REQUEST['rate'] = reScale( RATE_BASE, $event['DefaultRate'], ZM_WEB_DEFAULT_RATE ); - -$eventPath = ZM_DIR_EVENTS.'/'.getEventPath( $event ); - -$videoFormats = array(); -$ffmpegFormats = preg_split( '/\s+/', ZM_FFMPEG_FORMATS ); -foreach ( $ffmpegFormats as $ffmpegFormat ) -{ - preg_match( '/^([^*]+)(\**)$/', $ffmpegFormat, $matches ); - $videoFormats[$matches[1]] = $matches[1]; - if ( $matches[2] == '*' ) - $defaultVideoFormat = $matches[1]; - elseif ( $matches[2] == '**' ) - $defaultPhoneFormat = $matches[1]; -} -if ( !isset($_REQUEST['videoFormat']) ) -{ - if ( isset($defaultPhoneFormat) ) - $_REQUEST['videoFormat'] = $defaultPhoneFormat; - elseif ( isset($defaultVideoFormat) ) - $_REQUEST['videoFormat'] = $defaultVideoFormat; - else - $videoFormat = $ffmpegFormats[0]; -} - -if ( !empty($_REQUEST['generate']) ) -{ - $videoFile = createVideo( $event, $_REQUEST['videoFormat'], $_REQUEST['rate'], $videoSize, !empty($_REQUEST['overwrite']) ); -} - -$videoFiles = array(); -if ( $dir = opendir( $eventPath ) ) -{ - while ( ($file = readdir( $dir )) !== false ) - { - $file = $eventPath.'/'.$file; - if ( is_file( $file ) ) - { - if ( preg_match( '/-S([\da-z]+)\.(?:'.join( '|', $videoFormats ).')$/', $file, $matches ) ) - { - if ( $matches[1] == $videoSize ) - { - $videoFiles[] = $file; - } - } - } - } - closedir( $dir ); -} - -if ( isset($_REQUEST['download']) ) -{ - header( "Content-type: ".getMimeType($videoFiles[$_REQUEST['download']])); - header( "Content-length: ".filesize($videoFiles[$_REQUEST['download']])); - header( "Content-disposition: attachment; filename=".preg_replace( "/^.*\//", "", $videoFiles[$_REQUEST['download']] )."; size=".filesize($videoFiles[$_REQUEST['download']]) ); - readfile( $videoFiles[$_REQUEST['download']] ); - exit; -} - -xhtmlHeaders( __FILE__, $SLANG['Video'].' - '.$event['Name'] ); -?> - -
-
-
- - - - - - - - - - - - - - -
checked="checked"/>
-
-
- -

- -

- - -

- - - - - - - - - 0 ) - { - $index = 0; - foreach ( $videoFiles as $file ) - { - preg_match( '/^(.+)-((?:r[_\d]+)|(?:F[_\d]+))-((?:s[_\d]+)|(?:S[0-9a-z]+))\.([^.]+)$/', $file, $matches ); - if ( preg_match( '/^r(.+)$/', $matches[2], $temp_matches ) ) - { - $rate = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); - $rateText = isset($rates[$rate])?$rates[$rate]:($rate."x"); - } - elseif ( preg_match( '/^F(.+)$/', $matches[2], $temp_matches ) ) - { - $rateText = $temp_matches[1]."fps"; - } - if ( preg_match( '/^s(.+)$/', $matches[3], $temp_matches ) ) - { - $scale = (int)(100 * preg_replace( '/_/', '.', $temp_matches[1] ) ); - $scaleText = isset($scales[$scale])?$scales[$scale]:($scale."x"); - } - elseif ( preg_match( '/^S(.+)$/', $matches[3], $temp_matches ) ) - { - $scaleText = $temp_matches[1]; - } -?> - - - - - - - - -
 / 
- -

- -
-
- - diff --git a/web/skins/mobile/views/watch.php b/web/skins/mobile/views/watch.php deleted file mode 100644 index 33250c510..000000000 --- a/web/skins/mobile/views/watch.php +++ /dev/null @@ -1,154 +0,0 @@ - - -
-
-

 -  fps

-

- - - -

- -
- -
- -
- - - -
- -
-
- - diff --git a/web/skins/xml/Makefile.am b/web/skins/xml/Makefile.am deleted file mode 100644 index 2c30d19a3..000000000 --- a/web/skins/xml/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/xml - -SUBDIRS = \ - includes \ - views - -dist_web_DATA = \ - skin.php diff --git a/web/skins/xml/includes/Makefile.am b/web/skins/xml/includes/Makefile.am deleted file mode 100644 index c6128f8a2..000000000 --- a/web/skins/xml/includes/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/skins/xml/includes - -dist_web_DATA = \ - init.php \ - config.php \ - functions.php diff --git a/web/skins/xml/includes/config.php b/web/skins/xml/includes/config.php deleted file mode 100644 index 538938df2..000000000 --- a/web/skins/xml/includes/config.php +++ /dev/null @@ -1,155 +0,0 @@ - "100x", - "5000" => "50x", - "2500" => "25x", - "1000" => "10x", - "400" => "4x", - "200" => "2x", - "100" => $SLANG['Real'], - "50" => "1/2x", - "25" => "1/4x", -); - -$scales = array( - "400" => "4x", - "300" => "3x", - "200" => "2x", - "150" => "1.5x", - "100" => $SLANG['Actual'], - "75" => "3/4x", - "50" => "1/2x", - "33" => "1/3x", - "25" => "1/4x", -); - -$bwArray = array( - "high" => $SLANG['High'], - "medium" => $SLANG['Medium'], - "low" => $SLANG['Low'] -); - -/* Check if ZM_WEB_L_CAN_STREAM and ZM_WEB_L_STREAM_METHOD are defined */ -if (!defined("ZM_WEB_L_CAN_STREAM")) { - define ("ZM_WEB_L_CAN_STREAM", 1); - define ("ZM_WEB_M_CAN_STREAM", 1); - define ("ZM_WEB_H_CAN_STREAM", 1); -} -if (!defined("ZM_WEB_L_STREAM_METHOD")) { - define ("ZM_WEB_L_STREAM_METHOD", "jpeg"); - define ("ZM_WEB_M_STREAM_METHOD", "jpeg"); - define ("ZM_WEB_H_STREAM_METHOD", "jpeg"); -} - -switch ( $_COOKIE['zmBandwidth'] ) -{ - case "high" : - { - define( "ZM_WEB_REFRESH_MAIN", ZM_WEB_H_REFRESH_MAIN ); // How often (in seconds) the main console window refreshes - define( "ZM_WEB_REFRESH_CYCLE", ZM_WEB_H_REFRESH_CYCLE ); // How often the cycle watch windows swaps to the next monitor - define( "ZM_WEB_REFRESH_IMAGE", ZM_WEB_H_REFRESH_IMAGE ); // How often the watched image is refreshed (if not streaming) - define( "ZM_WEB_REFRESH_STATUS", ZM_WEB_H_REFRESH_STATUS ); // How often the little status frame refreshes itself in the watch window - define( "ZM_WEB_REFRESH_EVENTS", ZM_WEB_H_REFRESH_EVENTS ); // How often the event listing is refreshed in the watch window, only for recent events - define( "ZM_WEB_CAN_STREAM", ZM_WEB_H_CAN_STREAM ); // Override the automatic detection of browser streaming capability - define( "ZM_WEB_STREAM_METHOD", ZM_WEB_H_STREAM_METHOD ); // Which method should be used to send video streams to your browser - define( "ZM_WEB_DEFAULT_SCALE", ZM_WEB_H_DEFAULT_SCALE ); // What the default scaling factor applied to 'live' or 'event' views is (%) - define( "ZM_WEB_DEFAULT_RATE", ZM_WEB_H_DEFAULT_RATE ); // What the default replay rate factor applied to 'event' views is (%) - define( "ZM_WEB_VIDEO_BITRATE", ZM_WEB_H_VIDEO_BITRATE ); // What the bitrate of any streamed video should be - define( "ZM_WEB_VIDEO_MAXFPS", ZM_WEB_H_VIDEO_MAXFPS ); // What the maximum frame rate of any streamed video should be - define( "ZM_WEB_SCALE_THUMBS", ZM_WEB_H_SCALE_THUMBS ); // Image scaling for thumbnails, bandwidth versus cpu in rescaling - define( "ZM_WEB_EVENTS_VIEW", ZM_WEB_H_EVENTS_VIEW ); // What the default view of multiple events should be. - define( "ZM_WEB_SHOW_PROGRESS", ZM_WEB_H_SHOW_PROGRESS ); // Whether to show the progress of replay in event view. - define( "ZM_WEB_AJAX_TIMEOUT", ZM_WEB_H_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset - break; - } - case "medium" : - { - define( "ZM_WEB_REFRESH_MAIN", ZM_WEB_M_REFRESH_MAIN ); // How often (in seconds) the main console window refreshes - define( "ZM_WEB_REFRESH_CYCLE", ZM_WEB_M_REFRESH_CYCLE ); // How often the cycle watch windows swaps to the next monitor - define( "ZM_WEB_REFRESH_IMAGE", ZM_WEB_M_REFRESH_IMAGE ); // How often the watched image is refreshed (if not streaming) - define( "ZM_WEB_REFRESH_STATUS", ZM_WEB_M_REFRESH_STATUS ); // How often the little status frame refreshes itself in the watch window - define( "ZM_WEB_REFRESH_EVENTS", ZM_WEB_M_REFRESH_EVENTS ); // How often the event listing is refreshed in the watch window, only for recent events - define( "ZM_WEB_CAN_STREAM", ZM_WEB_M_CAN_STREAM ); // Override the automatic detection of browser streaming capability - define( "ZM_WEB_STREAM_METHOD", ZM_WEB_M_STREAM_METHOD ); // Which method should be used to send video streams to your browser - define( "ZM_WEB_DEFAULT_SCALE", ZM_WEB_M_DEFAULT_SCALE ); // What the default scaling factor applied to 'live' or 'event' views is (%) - define( "ZM_WEB_DEFAULT_RATE", ZM_WEB_M_DEFAULT_RATE ); // What the default replay rate factor applied to 'event' views is (%) - define( "ZM_WEB_VIDEO_BITRATE", ZM_WEB_M_VIDEO_BITRATE ); // What the bitrate of any streamed video should be - define( "ZM_WEB_VIDEO_MAXFPS", ZM_WEB_M_VIDEO_MAXFPS ); // What the maximum frame rate of any streamed video should be - define( "ZM_WEB_SCALE_THUMBS", ZM_WEB_M_SCALE_THUMBS ); // Image scaling for thumbnails, bandwidth versus cpu in rescaling - define( "ZM_WEB_EVENTS_VIEW", ZM_WEB_M_EVENTS_VIEW ); // What the default view of multiple events should be. - define( "ZM_WEB_SHOW_PROGRESS", ZM_WEB_M_SHOW_PROGRESS ); // Whether to show the progress of replay in event view. - define( "ZM_WEB_AJAX_TIMEOUT", ZM_WEB_M_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset - break; - } - case "low" : - { - define( "ZM_WEB_REFRESH_MAIN", ZM_WEB_L_REFRESH_MAIN ); // How often (in seconds) the main console window refreshes - define( "ZM_WEB_REFRESH_CYCLE", ZM_WEB_L_REFRESH_CYCLE ); // How often the cycle watch windows swaps to the next monitor - define( "ZM_WEB_REFRESH_IMAGE", ZM_WEB_L_REFRESH_IMAGE ); // How often the watched image is refreshed (if not streaming) - define( "ZM_WEB_REFRESH_STATUS", ZM_WEB_L_REFRESH_STATUS ); // How often the little status frame refreshes itself in the watch window - define( "ZM_WEB_REFRESH_EVENTS", ZM_WEB_L_REFRESH_EVENTS ); // How often the event listing is refreshed in the watch window, only for recent events - define( "ZM_WEB_CAN_STREAM", ZM_WEB_L_CAN_STREAM ); // Override the automatic detection of browser streaming capability - define( "ZM_WEB_STREAM_METHOD", ZM_WEB_L_STREAM_METHOD ); // Which method should be used to send video streams to your browser - define( "ZM_WEB_DEFAULT_SCALE", ZM_WEB_L_DEFAULT_SCALE ); // What the default scaling factor applied to 'live' or 'event' views is (%) - define( "ZM_WEB_DEFAULT_RATE", ZM_WEB_L_DEFAULT_RATE ); // What the default replay rate factor applied to 'event' views is (%) - define( "ZM_WEB_VIDEO_BITRATE", ZM_WEB_L_VIDEO_BITRATE ); // What the bitrate of any streamed video should be - define( "ZM_WEB_VIDEO_MAXFPS", ZM_WEB_L_VIDEO_MAXFPS ); // What the maximum frame rate of any streamed video should be - define( "ZM_WEB_SCALE_THUMBS", ZM_WEB_L_SCALE_THUMBS ); // Image scaling for thumbnails, bandwidth versus cpu in rescaling - define( "ZM_WEB_EVENTS_VIEW", ZM_WEB_L_EVENTS_VIEW ); // What the default view of multiple events should be. - define( "ZM_WEB_SHOW_PROGRESS", ZM_WEB_L_SHOW_PROGRESS ); // Whether to show the progress of replay in event view. - define( "ZM_WEB_AJAX_TIMEOUT", ZM_WEB_L_AJAX_TIMEOUT ); // Timeout to use for Ajax requests, no timeout used if unset - break; - } -} - -?> diff --git a/web/skins/xml/includes/functions.php b/web/skins/xml/includes/functions.php deleted file mode 100644 index b84e65464..000000000 --- a/web/skins/xml/includes/functions.php +++ /dev/null @@ -1,447 +0,0 @@ - $maj) return 1; - if ((getClientVerMaj() == $maj) && (getClientVerMin() >= $min)) return 1; - return 0; -} -function logXmlErr($str) -{ - logXml($str, 1); -} -function logXml($str, $err = 0) -{ - if (!defined("ZM_EYEZM_DEBUG")) { - /* Check session variable */ - if (isset($_SESSION['xml_debug'])) define("ZM_EYEZM_DEBUG", $_SESSION['xml_debug']); - else define ("ZM_EYEZM_DEBUG", "0"); - } - if (!defined("ZM_EYEZM_LOG_TO_FILE")) { - /* Check session variable */ - if (isset($_SESSION['xml_log_to_file'])) define("ZM_EYEZM_LOG_TO_FILE", $_SESSION['xml_log_to_file']); - else define ("ZM_EYEZM_LOG_TO_FILE", "1"); - } - if (!defined("ZM_EYEZM_LOG_FILE")) { - /* Check session variable */ - if (isset($_SESSION['xml_log_file'])) define("ZM_EYEZM_LOG_FILE", $_SESSION['xml_log_file']); - else define ("ZM_EYEZM_LOG_FILE", "/tmp/zm_xml.log"); - } - /* Only log if debug is enabled */ - if (ZM_EYEZM_DEBUG == 0) return; - /* Logging is enabled, set log string */ - $logstr = "XML_LOG (".($err?"ERROR":"NOTICE")."): ".$str.(ZM_EYEZM_LOG_TO_FILE?"\n":""); - if (ZM_EYEZM_LOG_TO_FILE) { - error_log("[".date("r")."] ".$logstr, 3, ZM_EYEZM_LOG_FILE); - } else { - error_log($logstr); - } -} -/* Returns defval if varname is not set, otherwise return varname */ -function getset($varname, $defval) -{ - if (isset($_GET[$varname])) return $_GET[$varname]; - return $defval; -} -function xml_header() -{ - header ("content-type: text/xml"); - echo ""; -} -function xml_tag_val($tag, $val) -{ - echo "<".$tag.">".$val.""; -} -function xml_tag_sec($tag, $open) -{ - if ($open) $tok = "<"; - else $tok = ""; -} -function xhtmlHeaders( $file, $title ) -{ -?> - - - - - - - /dev/null")) > 0) { - /* More than one match */ - return TRUE; - } else { - /* Check -formats tag also if we fail -codecs */ - if (preg_match("/\b".$codec."\b/", shell_exec(getFfmpegPath()." -formats 2> /dev/null")) > 0) return TRUE; - return FALSE; - } -} -function exeExists($exepath) -{ - $path = trim($exepath); - return (file_exists($path) && is_readable($path) && ($path != "")); -} -/* Returns whether ffmpeg exists or not */ -function ffmpegExists() -{ - return exeExists(getFfmpegPath()); -} -/* Returns with PHP-GD exists */ -function gdExists() -{ - if (extension_loaded('gd') && function_exists('gd_info')) { - return TRUE; - } - return FALSE; -} - -function getFfmpeg264FoutParms($br, $fout) -{ - $ffparms = "-analyzeduration 0 -acodec copy"; - $ffparms .= " -vcodec libx264 -b ".$br; - $ffparms .= " -flags +loop -cmp +chroma -partitions +parti4x4+partp8x8+partb8x8"; - $ffparms .= " -subq 5 -trellis 1 -refs 1 -coder 0 -me_range 16 -keyint_min 25"; - $ffparms .= " -sc_threshold 40 -i_qfactor 0.71 -bt 16k"; - $ffparms .= " -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6"; - $ffparms .= " -qmin 10 -qmax 51 -qdiff 4 -level 30"; - $ffparms .= " -g 30 -analyzeduration 0 -async 2 ".$fout." 2> /dev/null"; - return $ffparms; -} -/** Return FFMPEG parameters for H264 streaming */ -function getFfmpeg264Str($width, $height, $br, $fin, $fout) -{ - $ffparms = getFfmpeg264FoutParms($br, $fout); - $ffstr = getFfmpegPath()." -t ".ZM_EYEZM_H264_MAX_DURATION." -analyzeduration 0 -i "; - $ffstr .= $fin." -f mpegts ".$ffparms; - return $ffstr; -} -/** Returns true when monitor exists */ -function isMonitor($monitor) -{ - $query = "select Id from Monitors where Id = ?"; - $res = dbFetchOne($query, NULL, array($monitor)); - if ($res) return TRUE; - logXml("Monitor ID ".$monitor." does not exist"); - return FALSE; -} -/** Returns the width and height of a monitor */ -function getMonitorDims($monitor) -{ - $query = "select Width,Height from Monitors where Id = ?"; - $res = dbFetchOne($query, NULL, array( $monitor ) ); - return $res; -} -/** Returns the temp directory for H264 encoding */ -function getTempDir() -{ - /* Assume that the directory structure is /skins/xml/views */ - return dirname(__FILE__)."/../../../temp"; -} -/** Returns the name of the m3u8 playlist based on monitor */ -function m3u8fname($monitor) { - return "stream_".$monitor.".m3u8"; -} - -/** Erases the M3u8 and TS file names for a given monitor */ -function eraseH264Files($monitor) { - /** NOTE: This command executes an 'rm' command, so $monitor parameter - * should be properly validated before executing */ - /* Remove wdir/.m3u8 and wdir/sample_*.ts */ - shell_exec("rm -f ".getTempDir()."/".m3u8fname($monitor)." ".getTempDir()."/sample_".$monitor."*.ts"); -} -function kill264proc($monitor) { - /** NOTE: This command executes an 'kill' command, so $monitor parameter - * should be properly validated before executing */ - $pid = trim(shell_exec("pgrep -f -x \"zmstreamer -m ".$monitor."\"")); - if ($pid == "") { - logXml("No PID found for ZMStreamer to kill"); - } else { - shell_exec("kill -9 ".$pid); - logXml("Killed process ".$pid." for Monitor ".$monitor); - } -} -/** Return the command-line shell function to setup H264 stream */ -function stream264fn ($mid, $width, $height, $br) { - $cdir = "./temp"; - $zmstrm = "zmstreamer -m ".$mid." 2> /dev/null"; - $ffstr = getFfmpeg264Str($width, $height, $br, "-", "-"); - $seg = "segmenter - ".ZM_EYEZM_SEG_DURATION." ".$cdir."/sample_".$mid." ".$cdir."/".m3u8fname($mid)." ../ 2> /dev/null"; - $url = $zmstrm . " | ".$ffstr." | " . $seg; - return "nohup ".$url." & echo $!"; -} - -/** Generate the web-page presented to the viewer when using H264 */ -function h264vidHtml($width, $height, $monitor, $br, $thumbsrc) { - function printTermLink() { - $str = "H264 Streaming Launching...
Tap to re-load if stream fails"; - $str2 = "document.getElementById(\"loaddiv\").innerHTML = \"".$str."\";"; - echo $str2; - - } - $ajaxUrl = "?view=actions&action=spawn264&&monitor=".$monitor."&br=".$br; - /* Call these two directly to bypass server blocking issues */ - $ajax2Url = "./skins/xml/views/actions.php?action=chk264&monitor=".$monitor; - $ajax2Url .= "&timeout=".ZM_EYEZM_H264_TIMEOUT; - $ajax3Url = "./skins/xml/views/actions.php?action=kill264&monitor=".$monitor; -?> - - - - - - - -
-
-Initializing H264 Stream ()...
-This may take a few seconds -
-
- -
- -
- - - diff --git a/web/skins/xml/includes/init.php b/web/skins/xml/includes/init.php deleted file mode 100644 index acb6c3546..000000000 --- a/web/skins/xml/includes/init.php +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/web/skins/xml/skin.php b/web/skins/xml/skin.php deleted file mode 100644 index c88718c4e..000000000 --- a/web/skins/xml/skin.php +++ /dev/null @@ -1,47 +0,0 @@ - diff --git a/web/skins/xml/views/Makefile.am b/web/skins/xml/views/Makefile.am deleted file mode 100644 index ab9147ae4..000000000 --- a/web/skins/xml/views/Makefile.am +++ /dev/null @@ -1,11 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -SUBDIRS = - -webdir = @WEB_PREFIX@/skins/xml/views - -dist_web_DATA = \ - console.php \ - actions.php \ - none.php \ - notfound.png diff --git a/web/skins/xml/views/actions.php b/web/skins/xml/views/actions.php deleted file mode 100644 index 73e206af6..000000000 --- a/web/skins/xml/views/actions.php +++ /dev/null @@ -1,392 +0,0 @@ - */ - if (!canEdit('Events')) { - logXmlErr("User ".$user['Username']. " doesn't have edit Events perms"); - exit; - } - if (!isset($_REQUEST['eid'])) { - logXmlErr("EID not set for action delete-event"); - exit; - } - $eid = validInteger($_REQUEST['eid']); - $url = "./index.php?view=request&request=event&id=".$eid."&action=delete"; - header("Location: ".$url); - exit; - - } else if (!strcmp($action, "spawn264")) { - /* ACTION: Spawn 264 streaming process. - * Parms: [br|width|height] */ - if (!canView('Stream')) { - logXmlErr("User ".$user['Username']. " doesn't have view Stream perms"); - exit; - } - if (!isset($_GET['monitor'])) { - logXmlErr("Not all parameters specified for spawn264"); - exit; - } - $monitor = validInteger($_REQUEST['monitor']); - if (!isMonitor($monitor)) exit; - $dims = getMonitorDims($monitor); - $width = validInteger(getset('width', $dims['Width'])); - $height = validInteger(getset('height', $dims['Height'])); - $br = validString(getset('br', ZM_EYEZM_H264_DEFAULT_BR)); - /* Check that we can stream first */ - if (!canStream264()) { - /* canStream264 will print out error */ - exit; - } - $streamUrl = stream264fn($monitor, $width, $height, $br); - logXml("Using H264 Pipe Function: ".$streamUrl); - $pid = shell_exec($streamUrl); - logXml("Streaming Process for monitor ".$monitor." ended, cleaning up files"); - eraseH264Files($monitor); - exit; - - } else if (!strcmp($action, "kill264")) { - /* ACTION: Kill existing H264 stream process and cleanup files. - * Parms: . - * NOTE: This will be called directly by path, so include files - * may not be available */ - session_start(); - require_once(dirname(__FILE__)."/../includes/functions.php"); - if (!isset($_GET['monitor'])) { - logXmlErr("Not all parameters specified for kill264"); - exit; - } - $monitor = validInteger($_GET['monitor']); - kill264proc($monitor); - logXml("Killed Segmenter process for monitor ".$monitor); - exit; - - } else if (!strcmp($action, "chk264")) { - /* ACTION: Simply stalls while checking for 264 file. - * Parms: - * NOTE: This will be called directly by path, so include files - * may not be available */ - session_start(); - require_once(dirname(__FILE__)."/../includes/functions.php"); - if (!isset($_GET['monitor']) || !isset($_GET['timeout'])) { - logXmlErr("Monitor not specified for chk264"); - exit; - } - $monitor = validInteger($_GET['monitor']); - $path = getTempDir()."/".m3u8fname($monitor); - /* Wait for the second sample to become available */ - $tsfile = getTempDir()."/sample_".$monitor."-2.ts"; - /* Setup timeout */ - $startTime = time(); - $timeout = validInteger($_GET['timeout']); - while (!file_exists($path) || !file_exists($tsfile)) { - if (time() > $startTime + $timeout) { - logXmlErr("Timed out waiting for stream to start, exiting..."); - kill264proc($monitor); - exit; - } - usleep(10000); - } - logXml("File exists, stream created after ".(time()-$startTime)." sec"); - exit; - - } else if (!strcmp($action, "feed")) { - /* ACTION: View a feed. Parms: [height|width|fps|scale|vcodec|br] */ - if (!canView('Stream')) { - logXmlErr("User ".$user['Username']. " doesn't have view Stream perms"); - exit; - } - /* Check that required variables are set */ - if (!isset($_REQUEST['monitor'])) { - logXmlErr("Not all parameters set for action view-feed"); - exit; - } - $monitor = validInteger($_REQUEST['monitor']); - if (!isMonitor($monitor)) exit; - $dims = getMonitorDims($monitor); - $width = validInteger(getset('width', $dims['Width'])); - $height = validInteger(getset('height', $dims['Height'])); - $fps = validInteger(getset('fps', ZM_WEB_VIDEO_MAXFPS)); - $scale = validInteger(getset('scale', 100)); - $vcodec = validString(getset('vcodec', ZM_EYEZM_FEED_VCODEC)); - /* Select which codec we want */ - if (!strcmp($vcodec, "h264")) { - /* Validate that we can in fact stream H264 */ - if (!canStream264()) { - /* canStream264 will print out error if - * there is one */ - echo "Server cannot stream H264. Check eyeZm log for details"; - exit; - } - if (!requireVer("1", "2")) { - echo "H264 Streaming requires eyeZm v1.2 or above"; - logXmlErr("H264 Streaming requires eyeZm v1.2 or above"); - exit; - } - $br = validString(getset('br', ZM_EYEZM_H264_DEFAULT_BR)); - /* H264 processing */ - noCacheHeaders(); - /* Kill any existing processes and files */ - kill264proc($monitor); - eraseH264Files($monitor); - logXml("Streaming H264 on Monitor ".$monitor.", ".$width."x".$height." @".$br); - /* Get thumbnail source */ - $thumbsrc = - getStreamSrc( array( - "mode=single", - "monitor=".$monitor, - "scale=".$scale, - "maxfps=".$fps, - "buffer=1000" - ) ); - logXml("Using thumbnail image from ".$thumbsrc); - /* Generate H264 Web-page */ - echo "\n"; - h264vidHtml($width, $height, $monitor, $br, $thumbsrc); - } else if (!strcmp($vcodec, "mjpeg")) { - /* MJPEG streaming */ - /* If $fps=0, get a single-shot */ - if (!$fps) { - /* single-shot */ - $streamSrc = - getStreamSrc( array( - "mode=single", - "monitor=".$monitor, - "scale=".$scale, - "maxfps=0", - "buffer=1000" - ) ); - } else { - $streamSrc = - getStreamSrc( array( - "mode=jpeg", - "monitor=".$monitor, - "scale=".$scale, - "maxfps=".$fps, - "buffer=1000" - ) ); - } - noCacheHeaders(); - xhtmlHeaders( __FILE__, "Stream" ); - logXml("Streaming MJPEG on Monitor ".$monitor.", ".$width."x".$height." @".$fps."fps"); - echo "\n"; - echo "\n"; - echo "
\n"; - logXml("Using stream source: ".$streamSrc); - outputImageStream("liveStream", $streamSrc, $width, $height, "stream"); - echo "
"; - } else { - logXmlErr("Unsupported codec ".$vcodec." selected for streaming"); - echo("Unsupported codec ".$vcodec." selected for streaming"); - } - exit; - - } else if (!strcmp($action, "vevent")) { - /* ACTION: View an event. Parms: [fps|vcodec|br] */ - if (!canView('Events')) { - logXmlErr("User ".$user['Username']. " doesn't have view Events perms"); - exit; - } - if (!isset($_GET['eid'])) { - logXmlErr("Not all parameters set for Action View-event"); - exit; - } - /* Grab event from the database */ - $eid = validInteger($_GET['eid']); - $eventsSql = "select E.Id, E.MonitorId, E.Name, E.StartTime, E.Length, E.Frames from Events as E where E.Id = ?"; - $event = dbFetchOne($eventsSql, NULL, array( $eid ) ); - /* Check if exists */ - if (!$event) { - logxmlErr("Requested event ID ".$eid." does not exist"); - exit; - } - /* Calculate FPS */ - $fps = validInteger(getset('fps',ceil($event['Frames'] / $event['Length']))); - $vcodec = validString(getset('vcodec', ZM_EYEZM_EVENT_VCODEC)); - $baseURL = ZM_PATH_WEB."/".getEventPathSafe($event); - /* Here we validate the codec. - * Check that FFMPEG exists and supports codecs */ - if (!strcmp($vcodec, "mpeg4")) { - if (!ffmpegSupportsCodec("mpeg4")) { - logXmlErr("FFMPEG not installed, accessible in path/ZM_PATH_FFMPEG, or doesn't support mpeg4"); - exit; - } - /* Can generate, we are good to go */ - $fname = "capture.mov"; - $ffparms = "-vcodec mpeg4 -r ".ZM_EYEZM_EVENT_FPS." ".$baseURL."/".$fname." 2> /dev/null"; - - } else if (!strcmp($vcodec, "h264")) { - if (!ffmpegSupportsCodec("libx264")) { - logXmlErr("FFMPEG not installed, accessible in path/ZM_PATH_FFMPEG, or doesn't support H264"); - exit; - } - if (!requireVer("1","2")) { - logXmlErr("H264 Event viewing requires eyeZm v1.2 or greater"); - exit; - } - /* Good to go */ - $fname = "capture.mp4"; - $ffparms = getFfmpeg264FoutParms( - validString(getset('br',ZM_EYEZM_H264_DEFAULT_EVBR)), - $baseURL."/".$fname); - - } else { - logXmlErr("Unknown codec ".$vcodec." selected for event viewing"); - exit; - } - logXml("Selected ".$vcodec." for viewing event ".$event['Id']); - $fnameOut = $baseURL."/".$fname; - $shellCmd = getFfmpegPath()." -y -r ".$fps." -i ".$baseURL."/%0".ZM_EVENT_IMAGE_DIGITS."d-capture.jpg"; - $shellCmd .= " ".$ffparms; - logXml("Encoding event with command: ".$shellCmd); - $shellOutput = shell_exec($shellCmd); - /* Check that file exists */ - if (!file_exists(trim($fnameOut))) { - logXmlErr("Generate Event ".$event['Id']." file ".$fnameOut." does not exist"); - exit; - } - $url = "./".getEventPathSafe($event)."/".$fname; - logXml("Loading Event URL ".$url); - header("Location: ".$url); - exit; - - } else if (!strcmp($action, "vframe")) { - /* ACTION: View a frame given by an event and frame-id. Parms: [alarm | analyze | qty | scale] - * If 'alarm' is set, the returned frame will be the -th alarm frame. If 'analyze' is set, - * the returned frame will be the %03d-analyse frame instead of %03d-capture, if ZM_CREATE_ANALYSIS_IMAGES - * is set. Otherwise it just returns the captured frame. - * If qty is set, it will apply a quality factor from 0-100, and if width is set, it will scale the jpeg accordingly - */ - if (!isset($_GET['eid']) || !isset($_GET['frame'])) { - logXmlErr("Not all parameters set for action view-frame"); - exit; - } - $eid = validInteger($_GET['eid']); - $frame = validInteger($_GET['frame']); - $eventsSql = "select E.Id, E.MonitorId, E.Name, E.StartTime, E.Length, E.Frames from Events as E where E.Id = ?"; - $event = dbFetchOne($eventsSql, NULL, array( $eid ) ); - $qty = validInteger(getset('qty', 100)); - if ($qty > 100) $qty = 100; - $scale = validInteger(getset('scale', 100)); - if (!$event) { - logxmlErr("Requested event ID ".$eid." does not exist"); - exit; - } - /* Figure out the frame number. If 'alarm' is not set, this is just equal to the parameter. - * If 'alarm' is set, need to query DB and grab the -th item */ - if (isset($_GET['alarm'])) { - $frameSql = "select * from Frames as F where F.EventId=? and (F.Type = 'Alarm') order by F.FrameId"; - $i=0; - foreach (dbFetchAll($frameSql, NULL, array($eid) ) as $dbframe) { - if ($i == $frame) { - $frame = $dbframe['FrameId']; - break; - } - $i++; - } - } - if (isset($_GET['analyze']) && ZM_CREATE_ANALYSIS_IMAGES) { - $suffix = "analyse"; - } else { - $suffix = "capture"; - } - /* A frame index of 0 is invalid, so if we see this, just use frame 1 */ - if (!$frame) $frame = 1; - /* Suffix based on 'analyze' */ - $fname = sprintf("%0".ZM_EVENT_IMAGE_DIGITS."d-%s.jpg", $frame, $suffix); - $url = "./".getEventPathSafe($event)."/".$fname; - if (!file_exists($url)) { - logXmlErr("Invalid frame image requested: ".$url); - $url = "./skins/xml/views/notfound.png"; - } - /* Check if the image needs any processing - check for GD if requested */ - if (($scale != 100) || ($qty < 100)) { - if (!gdExists()) { - logXmlErr("Lib GD is not loaded, but required for image scaling functions"); - $url = "./skins/xml/views/notfound.png"; - } else if (!$img = imagecreatefromjpeg($url)) { - logXmlErr("Could not load JPEG from ".$url); - $url = "./skins/xml/views/notfound.png"; - } else { - /* GD exists and we read the file ok */ - header('Content-type: image/jpeg'); - /* Check if resizing is needed */ - if ($scale != 100) { - list($width_orig, $height_orig) = getimagesize($url); - $width_new = $width_orig * ($scale/100); - $height_new = $height_orig * ($scale/100); - $img_new = imagecreatetruecolor($width_new, $height_new); - imagecopyresampled($img_new, $img, 0, 0, 0, 0, $width_new, $height_new, $width_orig, $height_orig); - imagejpeg($img_new, NULL, $qty); - } else { - imagejpeg($img, NULL, $qty); - } - exit; - } - } - header("Location: ".$url); - exit; - - } else if (!strcmp($action, "state")) { - /* ACTION: Change the state of the system. Parms: */ - if (!canEdit('System')) { - logXmlErr("User ".$user['Username']. " doesn't have edit System perms"); - exit; - } - if (!isset($_GET['state'])) { - logXmlErr("Server state not specified for action"); - exit; - } - $url = "./index.php?view=none&action=state&runState=".validString($_GET['state']); - header("Location: ".$url); - exit; - - } else if (!strcmp($action, "func")) { - /* ACTION: Change state of the monitor. Parms: */ - if (!canEdit('Monitors')) { - logXmlErr("User ".$user['Username']. " doesn't have monitors Edit perms"); - exit; - } - if (!isset($_GET['mid']) || !isset($_GET['func']) || !isset($_GET['en'])) { - logXmlErr("Not all parameters specified for action Monitor state"); - exit; - } - $mid = validInteger($_GET['mid']); - if (!isMonitor($mid)) exit; - $url = "./index.php?view=none&action=function&mid=".$mid."&newFunction=".validString($_GET['func'])."&newEnabled=".validString($_GET['en']); - header("Location: ".$url); - exit; - - } else if (!strcmp($action, "vlog")) { - /* ACTION: View log file. Must have debug and log to file enabled, and sufficient perms - * Parms: [lines] */ - if (!canEdit('System')) { - logXmlErr("Insufficient permissions to view log file"); - echo "Insufficient permissions to view log file"; - exit; - } - if (!ZM_EYEZM_DEBUG || !ZM_EYEZM_LOG_TO_FILE) { - echo "eyeZm Debug (EYEZM_DEBUG) or log-to-file (EYEZM_LOG_TO_FILE) not enabled. Please enable first"; - exit; - } - if (!file_exists(ZM_EYEZM_LOG_FILE)) { - echo "Log file ".ZM_EYEZM_LOG_FILE." doesn't exist"; - exit; - } - $lines = validInteger(getset('lines',ZM_EYEZM_LOG_LINES)); - logXml("Returning last ".$lines." lines of eyeZm Log from ".ZM_EYEZM_LOG_FILE); - echo shell_exec("tail -n ".$lines." ".ZM_EYEZM_LOG_FILE); - echo "\n\n--- Showing last ".$lines." lines ---\n"; - echo "--- End of Log ---\n\n"; - } -} -?> diff --git a/web/skins/xml/views/console.php b/web/skins/xml/views/console.php deleted file mode 100644 index 86cf78768..000000000 --- a/web/skins/xml/views/console.php +++ /dev/null @@ -1,259 +0,0 @@ - $SLANG['Events'], - "filter" => array( - "terms" => array( - ) - ), - ), - array( - "title" => $SLANG['Hour'], - "filter" => array( - "terms" => array( - array( "attr" => "Archived", "op" => "=", "val" => "0" ), - array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-1 hour" ), - ) - ), - ), - array( - "title" => $SLANG['Day'], - "filter" => array( - "terms" => array( - array( "attr" => "Archived", "op" => "=", "val" => "0" ), - array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-1 day" ), - ) - ), - ), - array( - "title" => $SLANG['Week'], - "filter" => array( - "terms" => array( - array( "attr" => "Archived", "op" => "=", "val" => "0" ), - array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-7 day" ), - ) - ), - ), - array( - "title" => $SLANG['Month'], - "filter" => array( - "terms" => array( - array( "attr" => "Archived", "op" => "=", "val" => "0" ), - array( "cnj" => "and", "attr" => "DateTime", "op" => ">=", "val" => "-1 month" ), - ) - ), - ), - array( - "title" => $SLANG['Archived'], - "filter" => array( - "terms" => array( - array( "attr" => "Archived", "op" => "=", "val" => "1" ), - ) - ), - ), -); - -$running = daemonCheck(); -$status = $running?$SLANG['Running']:$SLANG['Stopped']; - -if ( $group = dbFetchOne( 'SELECT * FROM Groups WHERE Id = ?', NULL, array(empty($_COOKIE['zmGroup'])?0:$_COOKIE['zmGroup']) ) ) - $groupIds = array_flip(split( ',', $group['MonitorIds'] )); - -$maxWidth = 0; -$maxHeight = 0; -$cycleCount = 0; -$minSequence = 0; -$maxSequence = 1; -$seqIdList = array(); -$monitors = dbFetchAll( "select * from Monitors order by Sequence asc" ); -$displayMonitors = array(); -for ( $i = 0; $i < count($monitors); $i++ ) -{ - if ( !visibleMonitor( $monitors[$i]['Id'] ) ) - { - continue; - } - if ( $group && !empty($groupIds) && !array_key_exists( $monitors[$i]['Id'], $groupIds ) ) - { - continue; - } - $monitors[$i]['Show'] = true; - if ( empty($minSequence) || ($monitors[$i]['Sequence'] < $minSequence) ) - { - $minSequence = $monitors[$i]['Sequence']; - } - if ( $monitors[$i]['Sequence'] > $maxSequence ) - { - $maxSequence = $monitors[$i]['Sequence']; - } - if (isset($_GET['nostatus'])) { - $monitors[$i]['zmc'] = 1; - $monitors[$i]['zma'] = 1; - } else { - $monitors[$i]['zmc'] = zmcStatus( $monitors[$i] ); - $monitors[$i]['zma'] = zmaStatus( $monitors[$i] ); - } - $monitors[$i]['ZoneCount'] = dbFetchOne( 'select count(Id) as ZoneCount from Zones where MonitorId = ?', 'ZoneCount', array($monitors[$i]['Id']) ); - $counts = array(); - for ( $j = 0; $j < count($eventCounts); $j++ ) - { - $filter = addFilterTerm( $eventCounts[$j]['filter'], count($eventCounts[$j]['filter']['terms']), array( "cnj" => "and", "attr" => "MonitorId", "op" => "=", "val" => $monitors[$i]['Id'] ) ); - parseFilter( $filter ); - $counts[] = "count(if(1".$filter['sql'].",1,NULL)) as EventCount$j"; - $monitors[$i]['eventCounts'][$j]['filter'] = $filter; - } - $sql = 'SELECT '.join($counts,", ").' from Events as E where MonitorId = ?'; - $counts = dbFetchOne( $sql, NULL, array( $monitors[$i]['Id'] ) ); - if ( $monitors[$i]['Function'] != 'None' ) - { - $cycleCount++; - $scaleWidth = reScale( $monitors[$i]['Width'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); - $scaleHeight = reScale( $monitors[$i]['Height'], $monitors[$i]['DefaultScale'], ZM_WEB_DEFAULT_SCALE ); - if ( $maxWidth < $scaleWidth ) $maxWidth = $scaleWidth; - if ( $maxHeight < $scaleHeight ) $maxHeight = $scaleHeight; - } - $monitors[$i] = array_merge( $monitors[$i], $counts ); - $seqIdList[] = $monitors[$i]['Id']; - $displayMonitors[] = $monitors[$i]; -} -$states = dbFetchAll("select * from States"); -/* XML Dump Starts here */ -xml_header(); -/* Print out the general section */ -xml_tag_sec("ZM_XML", 1); -xml_tag_sec("GENERAL", 1); -xml_tag_val("RUNNING", $running); -xml_tag_val("PROTOVER", ZM_EYEZM_PROTOCOL_VERSION); -xml_tag_val("FEATURESET", ZM_EYEZM_FEATURE_SET); -xml_tag_val("VERSION", ZM_VERSION); -xml_tag_val("CANSTR264", canStream264(1)); -xml_tag_val("GD", gdExists()); -xml_tag_val("FVCODEC", ZM_EYEZM_FEED_VCODEC); -xml_tag_val("FVTMT", ZM_EYEZM_H264_TIMEOUT); -xml_tag_val("USER", $user['Username']); -xml_tag_val("UID", $user['Id']); -/* Permissions block */ -xml_tag_sec("PERMS", 1); -xml_tag_val("STREAM", $user['Stream']); -xml_tag_val("EVENTS", $user['Events']); -xml_tag_val("CONTROL", $user['Control']); -xml_tag_val("MONITORS", $user['Monitors']); -xml_tag_val("DEVICES", $user['Devices']); -xml_tag_val("SYSTEM", $user['System']); -xml_tag_sec("PERMS", 0); -/* End permissions block */ -if (canEdit('System')) { - if ($running) { - xml_tag_val("STATE", "stop"); - xml_tag_val("STATE", "restart"); - } else { - xml_tag_val("STATE", "start"); - } - foreach ($states as $state) { - xml_tag_val("STATE", $state['Name']); - } -} -/* End general section */ -xml_tag_sec("GENERAL", 0); -/* Print out the monitors section */ -xml_tag_sec("MONITOR_LIST", 1); -foreach( $displayMonitors as $monitor ) -{ - if (!canView('Monitors')) continue; - xml_tag_sec("MONITOR", 1); - xml_tag_val("ID", $monitor['Id']); - xml_tag_val("NAME", $monitor['Name']); - xml_tag_val("FUNCTION", $monitor['Function']); - xml_tag_val("NUMEVENTS", $monitor['EventCount0']); - xml_tag_val("ENABLED", $monitor['Enabled']); - xml_tag_val("ZMC", $monitor['zmc']); - xml_tag_val("ZMA", $monitor['zma']); - xml_tag_val("STATE", ($monitor['zmc']!=1)?"ERROR":( - ($monitor['zma']==1)?"OK":"WARN")); - xml_tag_val("WIDTH", $monitor['Width']); - xml_tag_val("HEIGHT", $monitor['Height']); - - /* Form the data-base query for this monitor */ - $pageOffset = 0; - $offset = 0; - if (isset($_GET['numEvents'])) { - $numEvents = validInteger($_GET['numEvents']); - $eventsSql = "select E.Id,E.MonitorId,M.Name As MonitorName,E.Cause,E.Name,E.StartTime,E.Length,E.Frames,E.AlarmFrames,E.TotScore,E.AvgScore,E.MaxScore,E.Archived from Monitors as M inner join Events as E on (M.Id = E.MonitorId) and ( E.MonitorId = ? ) order by E.StartTime desc"; - $eventsSql .= " limit ".$numEvents; - /* If there is an pageOff tag for this monitor, then retrieve the offset. Otherwise, don't specify offset */ - if (isset($_GET['pageOff'.$monitor['Id']])) { - /* If pageOffset is greater than we actually have, - * we need to adjust it */ - $pageOffset = validInteger($_GET['pageOff'.$monitor['Id']]); - if ($pageOffset >= ceil($monitor['EventCount0']/$numEvents)) { - $pageOffset = 0; - } - $offset = $pageOffset * $numEvents; - } - $eventsSql .= " offset ".$offset; - } else { - unset($eventsSql); - } - xml_tag_val("PAGEOFF", $pageOffset); - xml_tag_sec("EVENTS",1); - if (canView('Events') && isset($eventsSql)) { - foreach ( dbFetchAll( $eventsSql, NULL, array($monitor['Id']) ) as $event ) - { - xml_tag_sec("EVENT",1); - xml_tag_val("ID",$event['Id']); - xml_tag_val("NAME",$event['Name']); - xml_tag_val("TIME", strftime( STRF_FMT_DATETIME_SHORTER, strtotime($event['StartTime']))); - xml_tag_val("DURATION", $event['Length']); - xml_tag_val("FRAMES", $event['Frames']); - xml_tag_val("FPS", ($event['Length'] > 0)?ceil($event['Frames']/$event['Length']):0); - xml_tag_val("TOTSCORE", $event['TotScore']); - xml_tag_val("AVGSCORE", $event['AvgScore']); - xml_tag_val("MAXSCORE", $event['MaxScore']); - /* Grab the max frame-id from Frames table. If AlarmFrames = 0, don't try - * to grab any frames, and just signal the max frame index as index 0 */ - $fridx = 1; - $alarmFrames = 1; - if ($event['AlarmFrames']) { - $framesSql = "SELECT FrameId FROM Frames WHERE (Type = 'Alarm') and (EventId = ?) ORDER BY Score DESC LIMIT 1"; - $fr = dbFetchOne($framesSql, NULL, array( $event['Id'] ) ); - $fridx = $fr['FrameId']; - $alarmFrames = $event['AlarmFrames']; - } - xml_tag_val("ALARMFRAMES", $alarmFrames); - xml_tag_val("MAXFRAMEID", $fridx); - xml_tag_sec("EVENT",0); - } - } - xml_tag_sec("EVENTS",0); - xml_tag_sec("MONITOR", 0); -} -xml_tag_sec("MONITOR_LIST", 0); -xml_tag_sec("ZM_XML", 0); -?> diff --git a/web/skins/xml/views/notfound.png b/web/skins/xml/views/notfound.png deleted file mode 100644 index 219706897..000000000 Binary files a/web/skins/xml/views/notfound.png and /dev/null differ diff --git a/web/tools/Makefile.am b/web/tools/Makefile.am deleted file mode 100644 index 29c91bc02..000000000 --- a/web/tools/Makefile.am +++ /dev/null @@ -1,4 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -SUBDIRS = \ - mootools diff --git a/web/tools/mootools/Makefile.am b/web/tools/mootools/Makefile.am deleted file mode 100644 index 958647796..000000000 --- a/web/tools/mootools/Makefile.am +++ /dev/null @@ -1,18 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/tools/mootools - -dist_web_DATA = \ - mootools-core-1.3.2-nc.js \ - mootools-core-1.3.2-yc.js \ - mootools-more-1.3.2.1-nc.js \ - mootools-more-1.3.2.1-yc.js - -# Yes, you are correct. This is a HACK! -install-data-hook: - ( cd $(DESTDIR)$(webdir); rm -f mootools-core.js mootools-more.js ) - ( cd $(DESTDIR)$(webdir); ln -s mootools-core-1.3.2-yc.js mootools-core.js ) - ( cd $(DESTDIR)$(webdir); ln -s mootools-more-1.3.2.1-yc.js mootools-more.js ) - -uninstall-hook: - @-( cd $(DESTDIR)$(webdir); rm -f mootools-* ) diff --git a/web/tools/mootools/mootools-core-1.3.2-yc.js b/web/tools/mootools/mootools-core-1.3.2-yc.js deleted file mode 100644 index 71a263b81..000000000 --- a/web/tools/mootools/mootools-core-1.3.2-yc.js +++ /dev/null @@ -1,450 +0,0 @@ -/* ---- -MooTools: the javascript framework - -web build: - - http://mootools.net/core/7c56cfef9dddcf170a5d68e3fb61cfd7 - -packager build: - - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff - -copyrights: - - [MooTools](http://mootools.net) - -licenses: - - [MIT License](http://mootools.net/license.txt) -... -*/ -(function(){this.MooTools={version:"1.3.2",build:"c9f1ff10e9e7facb65e9481049ed1b450959d587"};var o=this.typeOf=function(i){if(i==null){return"null";}if(i.$family){return i.$family(); -}if(i.nodeName){if(i.nodeType==1){return"element";}if(i.nodeType==3){return(/\S/).test(i.nodeValue)?"textnode":"whitespace";}}else{if(typeof i.length=="number"){if(i.callee){return"arguments"; -}if("item" in i){return"collection";}}}return typeof i;};var j=this.instanceOf=function(t,i){if(t==null){return false;}var s=t.$constructor||t.constructor; -while(s){if(s===i){return true;}s=s.parent;}return t instanceof i;};var f=this.Function;var p=true;for(var k in {toString:1}){p=null;}if(p){p=["hasOwnProperty","valueOf","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","constructor"]; -}f.prototype.overloadSetter=function(s){var i=this;return function(u,t){if(u==null){return this;}if(s||typeof u!="string"){for(var v in u){i.call(this,v,u[v]); -}if(p){for(var w=p.length;w--;){v=p[w];if(u.hasOwnProperty(v)){i.call(this,v,u[v]);}}}}else{i.call(this,u,t);}return this;};};f.prototype.overloadGetter=function(s){var i=this; -return function(u){var v,t;if(s||typeof u!="string"){v=u;}else{if(arguments.length>1){v=arguments;}}if(v){t={};for(var w=0;w-1:this.indexOf(a)>-1;},trim:function(){return this.replace(/^\s+|\s+$/g,"");},clean:function(){return this.replace(/\s+/g," ").trim(); -},camelCase:function(){return this.replace(/-\D/g,function(a){return a.charAt(1).toUpperCase();});},hyphenate:function(){return this.replace(/[A-Z]/g,function(a){return("-"+a.charAt(0).toLowerCase()); -});},capitalize:function(){return this.replace(/\b[a-z]/g,function(a){return a.toUpperCase();});},escapeRegExp:function(){return this.replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1"); -},toInt:function(a){return parseInt(this,a||10);},toFloat:function(){return parseFloat(this);},hexToRgb:function(b){var a=this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); -return(a)?a.slice(1).hexToRgb(b):null;},rgbToHex:function(b){var a=this.match(/\d{1,3}/g);return(a)?a.rgbToHex(b):null;},substitute:function(a,b){return this.replace(b||(/\\?\{([^{}]+)\}/g),function(d,c){if(d.charAt(0)=="\\"){return d.slice(1); -}return(a[c]!=null)?a[c]:"";});}});Number.implement({limit:function(b,a){return Math.min(a,Math.max(b,this));},round:function(a){a=Math.pow(10,a||0).toFixed(a<0?-a:0); -return Math.round(this*a)/a;},times:function(b,c){for(var a=0;a1)?Array.slice(arguments,1):null;return function(){if(!b&&!arguments.length){return a.call(c);}if(b&&arguments.length){return a.apply(c,b.concat(Array.from(arguments))); -}return a.apply(c,b||arguments);};},pass:function(b,c){var a=this;if(b!=null){b=Array.from(b);}return function(){return a.apply(c,b||arguments);};},delay:function(b,c,a){return setTimeout(this.pass((a==null?[]:a),c),b); -},periodical:function(c,b,a){return setInterval(this.pass((a==null?[]:a),b),c);}});(function(){var a=Object.prototype.hasOwnProperty;Object.extend({subset:function(d,g){var f={}; -for(var e=0,b=g.length;e]*>([\s\S]*?)<\/script>/gi,function(r,s){e+=s+"\n"; -return"";});if(p===true){o.exec(e);}else{if(typeOf(p)=="function"){p(e,q);}}return q;});o.extend({Document:this.Document,Window:this.Window,Element:this.Element,Event:this.Event}); -this.Window=this.$constructor=new Type("Window",function(){});this.$family=Function.from("window").hide();Window.mirror(function(e,p){i[e]=p;});this.Document=k.$constructor=new Type("Document",function(){}); -k.$family=Function.from("document").hide();Document.mirror(function(e,p){k[e]=p;});k.html=k.documentElement;if(!k.head){k.head=k.getElementsByTagName("head")[0]; -}if(k.execCommand){try{k.execCommand("BackgroundImageCache",false,true);}catch(g){}}if(this.attachEvent&&!this.addEventListener){var d=function(){this.detachEvent("onunload",d); -k.head=k.html=k.window=null;};this.attachEvent("onunload",d);}var m=Array.from;try{m(k.html.childNodes);}catch(g){Array.from=function(p){if(typeof p!="string"&&Type.isEnumerable(p)&&typeOf(p)!="array"){var e=p.length,q=new Array(e); -while(e--){q[e]=p[e];}return q;}return m(p);};var l=Array.prototype,n=l.slice;["pop","push","reverse","shift","sort","splice","unshift","concat","join","slice"].each(function(e){var p=l[e]; -Array[e]=function(q){return p.apply(Array.from(q),n.call(arguments,1));};});}})();var Event=new Type("Event",function(a,i){if(!i){i=window;}var o=i.document; -a=a||i.event;if(a.$extended){return a;}this.$extended=true;var n=a.type,k=a.target||a.srcElement,m={},c={},q=null,h,l,b,p;while(k&&k.nodeType==3){k=k.parentNode; -}if(n.indexOf("key")!=-1){b=a.which||a.keyCode;p=Object.keyOf(Event.Keys,b);if(n=="keydown"){var d=b-111;if(d>0&&d<13){p="f"+d;}}if(!p){p=String.fromCharCode(b).toLowerCase(); -}}else{if((/click|mouse|menu/i).test(n)){o=(!o.compatMode||o.compatMode=="CSS1Compat")?o.html:o.body;m={x:(a.pageX!=null)?a.pageX:a.clientX+o.scrollLeft,y:(a.pageY!=null)?a.pageY:a.clientY+o.scrollTop}; -c={x:(a.pageX!=null)?a.pageX-i.pageXOffset:a.clientX,y:(a.pageY!=null)?a.pageY-i.pageYOffset:a.clientY};if((/DOMMouseScroll|mousewheel/).test(n)){l=(a.wheelDelta)?a.wheelDelta/120:-(a.detail||0)/3; -}h=(a.which==3)||(a.button==2);if((/over|out/).test(n)){q=a.relatedTarget||a[(n=="mouseover"?"from":"to")+"Element"];var j=function(){while(q&&q.nodeType==3){q=q.parentNode; -}return true;};var g=(Browser.firefox2)?j.attempt():j();q=(g)?q:null;}}else{if((/gesture|touch/i).test(n)){this.rotation=a.rotation;this.scale=a.scale; -this.targetTouches=a.targetTouches;this.changedTouches=a.changedTouches;var f=this.touches=a.touches;if(f&&f[0]){var e=f[0];m={x:e.pageX,y:e.pageY};c={x:e.clientX,y:e.clientY}; -}}}}return Object.append(this,{event:a,type:n,page:m,client:c,rightClick:h,wheel:l,relatedTarget:document.id(q),target:document.id(k),code:b,key:p,shift:a.shiftKey,control:a.ctrlKey,alt:a.altKey,meta:a.metaKey}); -});Event.Keys={enter:13,up:38,down:40,left:37,right:39,esc:27,space:32,backspace:8,tab:9,"delete":46};Event.implement({stop:function(){return this.stopPropagation().preventDefault(); -},stopPropagation:function(){if(this.event.stopPropagation){this.event.stopPropagation();}else{this.event.cancelBubble=true;}return this;},preventDefault:function(){if(this.event.preventDefault){this.event.preventDefault(); -}else{this.event.returnValue=false;}return this;}});(function(){var a=this.Class=new Type("Class",function(h){if(instanceOf(h,Function)){h={initialize:h}; -}var g=function(){e(this);if(g.$prototyping){return this;}this.$caller=null;var i=(this.initialize)?this.initialize.apply(this,arguments):this;this.$caller=this.caller=null; -return i;}.extend(this).implement(h);g.$constructor=a;g.prototype.$constructor=g;g.prototype.parent=c;return g;});var c=function(){if(!this.$caller){throw new Error('The method "parent" cannot be called.'); -}var g=this.$caller.$name,h=this.$caller.$owner.parent,i=(h)?h.prototype[g]:null;if(!i){throw new Error('The method "'+g+'" has no parent.');}return i.apply(this,arguments); -};var e=function(g){for(var h in g){var j=g[h];switch(typeOf(j)){case"object":var i=function(){};i.prototype=j;g[h]=e(new i);break;case"array":g[h]=j.clone(); -break;}}return g;};var b=function(g,h,j){if(j.$origin){j=j.$origin;}var i=function(){if(j.$protected&&this.$caller==null){throw new Error('The method "'+h+'" cannot be called.'); -}var l=this.caller,m=this.$caller;this.caller=m;this.$caller=i;var k=j.apply(this,arguments);this.$caller=m;this.caller=l;return k;}.extend({$owner:g,$origin:j,$name:h}); -return i;};var f=function(h,i,g){if(a.Mutators.hasOwnProperty(h)){i=a.Mutators[h].call(this,i);if(i==null){return this;}}if(typeOf(i)=="function"){if(i.$hidden){return this; -}this.prototype[h]=(g)?i:b(this,h,i);}else{Object.merge(this.prototype,h,i);}return this;};var d=function(g){g.$prototyping=true;var h=new g;delete g.$prototyping; -return h;};a.implement("implement",f.overloadSetter());a.Mutators={Extends:function(g){this.parent=g;this.prototype=d(g);},Implements:function(g){Array.from(g).each(function(j){var h=new j; -for(var i in h){f.call(this,i,h[i],true);}},this);}};})();(function(){this.Chain=new Class({$chain:[],chain:function(){this.$chain.append(Array.flatten(arguments)); -return this;},callChain:function(){return(this.$chain.length)?this.$chain.shift().apply(this,arguments):false;},clearChain:function(){this.$chain.empty(); -return this;}});var a=function(b){return b.replace(/^on([A-Z])/,function(c,d){return d.toLowerCase();});};this.Events=new Class({$events:{},addEvent:function(d,c,b){d=a(d); -this.$events[d]=(this.$events[d]||[]).include(c);if(b){c.internal=true;}return this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]);}return this; -},fireEvent:function(e,c,b){e=a(e);var d=this.$events[e];if(!d){return this;}c=Array.from(c);d.each(function(f){if(b){f.delay(b,this,c);}else{f.apply(this,c); -}},this);return this;},removeEvent:function(e,d){e=a(e);var c=this.$events[e];if(c&&!d.internal){var b=c.indexOf(d);if(b!=-1){delete c[b];}}return this; -},removeEvents:function(d){var e;if(typeOf(d)=="object"){for(e in d){this.removeEvent(e,d[e]);}return this;}if(d){d=a(d);}for(e in this.$events){if(d&&d!=e){continue; -}var c=this.$events[e];for(var b=c.length;b--;){if(b in c){this.removeEvent(e,c[b]);}}}return this;}});this.Options=new Class({setOptions:function(){var b=this.options=Object.merge.apply(null,[{},this.options].append(arguments)); -if(this.addEvent){for(var c in b){if(typeOf(b[c])!="function"||!(/^on[A-Z]/).test(c)){continue;}this.addEvent(c,b[c]);delete b[c];}}return this;}});})(); -(function(){var k,n,l,g,a={},c={},m=/\\/g;var e=function(q,p){if(q==null){return null;}if(q.Slick===true){return q;}q=(""+q).replace(/^\s+|\s+$/g,"");g=!!p; -var o=(g)?c:a;if(o[q]){return o[q];}k={Slick:true,expressions:[],raw:q,reverse:function(){return e(this.raw,true);}};n=-1;while(q!=(q=q.replace(j,b))){}k.length=k.expressions.length; -return o[k.raw]=(g)?h(k):k;};var i=function(o){if(o==="!"){return" ";}else{if(o===" "){return"!";}else{if((/^!/).test(o)){return o.replace(/^!/,"");}else{return"!"+o; -}}}};var h=function(u){var r=u.expressions;for(var p=0;p+)\\s*|(\\s+)|(+|\\*)|\\#(+)|\\.(+)|\\[\\s*(+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)".replace(//,"["+f(">+~`!@$%^&={}\\;/g,"(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])").replace(//g,"(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])")); -function b(x,s,D,z,r,C,q,B,A,y,u,F,G,v,p,w){if(s||n===-1){k.expressions[++n]=[];l=-1;if(s){return"";}}if(D||z||l===-1){D=D||" ";var t=k.expressions[n]; -if(g&&t[l]){t[l].reverseCombinator=i(D);}t[++l]={combinator:D,tag:"*"};}var o=k.expressions[n][l];if(r){o.tag=r.replace(m,"");}else{if(C){o.id=C.replace(m,""); -}else{if(q){q=q.replace(m,"");if(!o.classList){o.classList=[];}if(!o.classes){o.classes=[];}o.classList.push(q);o.classes.push({value:q,regexp:new RegExp("(^|\\s)"+f(q)+"(\\s|$)")}); -}else{if(G){w=w||p;w=w?w.replace(m,""):null;if(!o.pseudos){o.pseudos=[];}o.pseudos.push({key:G.replace(m,""),value:w,type:F.length==1?"class":"element"}); -}else{if(B){B=B.replace(m,"");u=(u||"").replace(m,"");var E,H;switch(A){case"^=":H=new RegExp("^"+f(u));break;case"$=":H=new RegExp(f(u)+"$");break;case"~=":H=new RegExp("(^|\\s)"+f(u)+"(\\s|$)"); -break;case"|=":H=new RegExp("^"+f(u)+"(-|$)");break;case"=":E=function(I){return u==I;};break;case"*=":E=function(I){return I&&I.indexOf(u)>-1;};break; -case"!=":E=function(I){return u!=I;};break;default:E=function(I){return !!I;};}if(u==""&&(/^[*$^]=$/).test(A)){E=function(){return false;};}if(!E){E=function(I){return I&&H.test(I); -};}if(!o.attributes){o.attributes=[];}o.attributes.push({key:B,operator:A,value:u,test:E});}}}}}return"";}var d=(this.Slick||{});d.parse=function(o){return e(o); -};d.escapeRegExp=f;if(!this.Slick){this.Slick=d;}}).apply((typeof exports!="undefined")?exports:this);(function(){var j={},l={},b=Object.prototype.toString; -j.isNativeCode=function(c){return(/\{\s*\[native code\]\s*\}/).test(""+c);};j.isXML=function(c){return(!!c.xmlVersion)||(!!c.xml)||(b.call(c)=="[object XMLDocument]")||(c.nodeType==9&&c.documentElement.nodeName!="HTML"); -};j.setDocument=function(w){var t=w.nodeType;if(t==9){}else{if(t){w=w.ownerDocument;}else{if(w.navigator){w=w.document;}else{return;}}}if(this.document===w){return; -}this.document=w;var y=w.documentElement,u=this.getUIDXML(y),o=l[u],A;if(o){for(A in o){this[A]=o[A];}return;}o=l[u]={};o.root=y;o.isXMLDocument=this.isXML(w); -o.brokenStarGEBTN=o.starSelectsClosedQSA=o.idGetsName=o.brokenMixedCaseQSA=o.brokenGEBCN=o.brokenCheckedQSA=o.brokenEmptyAttributeQSA=o.isHTMLDocument=o.nativeMatchesSelector=false; -var m,n,x,q,r;var s,c="slick_uniqueid";var z=w.createElement("div");var p=w.body||w.getElementsByTagName("body")[0]||y;p.appendChild(z);try{z.innerHTML=''; -o.isHTMLDocument=!!w.getElementById(c);}catch(v){}if(o.isHTMLDocument){z.style.display="none";z.appendChild(w.createComment(""));n=(z.getElementsByTagName("*").length>1); -try{z.innerHTML="foo";s=z.getElementsByTagName("*");m=(s&&!!s.length&&s[0].nodeName.charAt(0)=="/");}catch(v){}o.brokenStarGEBTN=n||m;try{z.innerHTML=''; -o.idGetsName=w.getElementById(c)===z.firstChild;}catch(v){}if(z.getElementsByClassName){try{z.innerHTML='';z.getElementsByClassName("b").length; -z.firstChild.className="b";q=(z.getElementsByClassName("b").length!=2);}catch(v){}try{z.innerHTML='';x=(z.getElementsByClassName("a").length!=2); -}catch(v){}o.brokenGEBCN=q||x;}if(z.querySelectorAll){try{z.innerHTML="foo";s=z.querySelectorAll("*");o.starSelectsClosedQSA=(s&&!!s.length&&s[0].nodeName.charAt(0)=="/"); -}catch(v){}try{z.innerHTML='';o.brokenMixedCaseQSA=!z.querySelectorAll(".MiX").length;}catch(v){}try{z.innerHTML=''; -o.brokenCheckedQSA=(z.querySelectorAll(":checked").length==0);}catch(v){}try{z.innerHTML='';o.brokenEmptyAttributeQSA=(z.querySelectorAll('[class*=""]').length!=0); -}catch(v){}}try{z.innerHTML='
';r=(z.firstChild.getAttribute("action")!="s");}catch(v){}o.nativeMatchesSelector=y.matchesSelector||y.mozMatchesSelector||y.webkitMatchesSelector; -if(o.nativeMatchesSelector){try{o.nativeMatchesSelector.call(y,":slick");o.nativeMatchesSelector=null;}catch(v){}}}try{y.slick_expando=1;delete y.slick_expando; -o.getUID=this.getUIDHTML;}catch(v){o.getUID=this.getUIDXML;}p.removeChild(z);z=s=p=null;o.getAttribute=(o.isHTMLDocument&&r)?function(D,B){var E=this.attributeGetters[B]; -if(E){return E.call(D);}var C=D.getAttributeNode(B);return(C)?C.nodeValue:null;}:function(C,B){var D=this.attributeGetters[B];return(D)?D.call(C):C.getAttribute(B); -};o.hasAttribute=(y&&this.isNativeCode(y.hasAttribute))?function(C,B){return C.hasAttribute(B);}:function(C,B){C=C.getAttributeNode(B);return !!(C&&(C.specified||C.nodeValue)); -};o.contains=(y&&this.isNativeCode(y.contains))?function(B,C){return B.contains(C);}:(y&&y.compareDocumentPosition)?function(B,C){return B===C||!!(B.compareDocumentPosition(C)&16); -}:function(B,C){if(C){do{if(C===B){return true;}}while((C=C.parentNode));}return false;};o.documentSorter=(y.compareDocumentPosition)?function(C,B){if(!C.compareDocumentPosition||!B.compareDocumentPosition){return 0; -}return C.compareDocumentPosition(B)&4?-1:C===B?0:1;}:("sourceIndex" in y)?function(C,B){if(!C.sourceIndex||!B.sourceIndex){return 0;}return C.sourceIndex-B.sourceIndex; -}:(w.createRange)?function(E,C){if(!E.ownerDocument||!C.ownerDocument){return 0;}var D=E.ownerDocument.createRange(),B=C.ownerDocument.createRange();D.setStart(E,0); -D.setEnd(E,0);B.setStart(C,0);B.setEnd(C,0);return D.compareBoundaryPoints(Range.START_TO_END,B);}:null;y=null;for(A in o){this[A]=o[A];}};var e=/^([#.]?)((?:[\w-]+|\*))$/,g=/\[.+[*$^]=(?:""|'')?\]/,f={}; -j.search=function(U,z,H,s){var p=this.found=(s)?null:(H||[]);if(!U){return p;}else{if(U.navigator){U=U.document;}else{if(!U.nodeType){return p;}}}var F,O,V=this.uniques={},I=!!(H&&H.length),y=(U.nodeType==9); -if(this.document!==(y?U:U.ownerDocument)){this.setDocument(U);}if(I){for(O=p.length;O--;){V[this.getUID(p[O])]=true;}}if(typeof z=="string"){var r=z.match(e); -simpleSelectors:if(r){var u=r[1],v=r[2],A,E;if(!u){if(v=="*"&&this.brokenStarGEBTN){break simpleSelectors;}E=U.getElementsByTagName(v);if(s){return E[0]||null; -}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{if(u=="#"){if(!this.isHTMLDocument||!y){break simpleSelectors;}A=U.getElementById(v); -if(!A){return p;}if(this.idGetsName&&A.getAttributeNode("id").nodeValue!=v){break simpleSelectors;}if(s){return A||null;}if(!(I&&V[this.getUID(A)])){p.push(A); -}}else{if(u=="."){if(!this.isHTMLDocument||((!U.getElementsByClassName||this.brokenGEBCN)&&U.querySelectorAll)){break simpleSelectors;}if(U.getElementsByClassName&&!this.brokenGEBCN){E=U.getElementsByClassName(v); -if(s){return E[0]||null;}for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}else{var T=new RegExp("(^|\\s)"+d.escapeRegExp(v)+"(\\s|$)");E=U.getElementsByTagName("*"); -for(O=0;A=E[O++];){className=A.className;if(!(className&&T.test(className))){continue;}if(s){return A;}if(!(I&&V[this.getUID(A)])){p.push(A);}}}}}}if(I){this.sort(p); -}return(s)?null:p;}querySelector:if(U.querySelectorAll){if(!this.isHTMLDocument||f[z]||this.brokenMixedCaseQSA||(this.brokenCheckedQSA&&z.indexOf(":checked")>-1)||(this.brokenEmptyAttributeQSA&&g.test(z))||(!y&&z.indexOf(",")>-1)||d.disableQSA){break querySelector; -}var S=z,x=U;if(!y){var C=x.getAttribute("id"),t="slickid__";x.setAttribute("id",t);S="#"+t+" "+S;U=x.parentNode;}try{if(s){return U.querySelector(S)||null; -}else{E=U.querySelectorAll(S);}}catch(Q){f[z]=1;break querySelector;}finally{if(!y){if(C){x.setAttribute("id",C);}else{x.removeAttribute("id");}U=x;}}if(this.starSelectsClosedQSA){for(O=0; -A=E[O++];){if(A.nodeName>"@"&&!(I&&V[this.getUID(A)])){p.push(A);}}}else{for(O=0;A=E[O++];){if(!(I&&V[this.getUID(A)])){p.push(A);}}}if(I){this.sort(p); -}return p;}F=this.Slick.parse(z);if(!F.length){return p;}}else{if(z==null){return p;}else{if(z.Slick){F=z;}else{if(this.contains(U.documentElement||U,z)){(p)?p.push(z):p=z; -return p;}else{return p;}}}}this.posNTH={};this.posNTHLast={};this.posNTHType={};this.posNTHTypeLast={};this.push=(!I&&(s||(F.length==1&&F.expressions[0].length==1)))?this.pushArray:this.pushUID; -if(p==null){p=[];}var M,L,K;var B,J,D,c,q,G,W;var N,P,o,w,R=F.expressions;search:for(O=0;(P=R[O]);O++){for(M=0;(o=P[M]);M++){B="combinator:"+o.combinator; -if(!this[B]){continue search;}J=(this.isXMLDocument)?o.tag:o.tag.toUpperCase();D=o.id;c=o.classList;q=o.classes;G=o.attributes;W=o.pseudos;w=(M===(P.length-1)); -this.bitUniques={};if(w){this.uniques=V;this.found=p;}else{this.uniques={};this.found=[];}if(M===0){this[B](U,J,D,q,G,W,c);if(s&&w&&p.length){break search; -}}else{if(s&&w){for(L=0,K=N.length;L1)){this.sort(p);}return(s)?(p[0]||null):p;};j.uidx=1;j.uidk="slick-uniqueid";j.getUIDXML=function(m){var c=m.getAttribute(this.uidk); -if(!c){c=this.uidx++;m.setAttribute(this.uidk,c);}return c;};j.getUIDHTML=function(c){return c.uniqueNumber||(c.uniqueNumber=this.uidx++);};j.sort=function(c){if(!this.documentSorter){return c; -}c.sort(this.documentSorter);return c;};j.cacheNTH={};j.matchNTH=/^([+-]?\d*)?([a-z]+)?([+-]\d+)?$/;j.parseNTHArgument=function(p){var n=p.match(this.matchNTH); -if(!n){return false;}var o=n[2]||false;var m=n[1]||1;if(m=="-"){m=-1;}var c=+n[3]||0;n=(o=="n")?{a:m,b:c}:(o=="odd")?{a:2,b:1}:(o=="even")?{a:2,b:0}:{a:0,b:m}; -return(this.cacheNTH[p]=n);};j.createNTHPseudo=function(o,m,c,n){return function(r,p){var t=this.getUID(r);if(!this[c][t]){var z=r.parentNode;if(!z){return false; -}var q=z[o],s=1;if(n){var y=r.nodeName;do{if(q.nodeName!=y){continue;}this[c][this.getUID(q)]=s++;}while((q=q[m]));}else{do{if(q.nodeType!=1){continue; -}this[c][this.getUID(q)]=s++;}while((q=q[m]));}}p=p||"n";var u=this.cacheNTH[p]||this.parseNTHArgument(p);if(!u){return false;}var x=u.a,w=u.b,v=this[c][t]; -if(x==0){return w==v;}if(x>0){if(v":function(o,c,q,n,m,p){if((o=o.firstChild)){do{if(o.nodeType==1){this.push(o,c,q,n,m,p); -}}while((o=o.nextSibling));}},"+":function(o,c,q,n,m,p){while((o=o.nextSibling)){if(o.nodeType==1){this.push(o,c,q,n,m,p);break;}}},"^":function(o,c,q,n,m,p){o=o.firstChild; -if(o){if(o.nodeType==1){this.push(o,c,q,n,m,p);}else{this["combinator:+"](o,c,q,n,m,p);}}},"~":function(p,c,r,o,m,q){while((p=p.nextSibling)){if(p.nodeType!=1){continue; -}var n=this.getUID(p);if(this.bitUniques[n]){break;}this.bitUniques[n]=true;this.push(p,c,r,o,m,q);}},"++":function(o,c,q,n,m,p){this["combinator:+"](o,c,q,n,m,p); -this["combinator:!+"](o,c,q,n,m,p);},"~~":function(o,c,q,n,m,p){this["combinator:~"](o,c,q,n,m,p);this["combinator:!~"](o,c,q,n,m,p);},"!":function(o,c,q,n,m,p){while((o=o.parentNode)){if(o!==this.document){this.push(o,c,q,n,m,p); -}}},"!>":function(o,c,q,n,m,p){o=o.parentNode;if(o!==this.document){this.push(o,c,q,n,m,p);}},"!+":function(o,c,q,n,m,p){while((o=o.previousSibling)){if(o.nodeType==1){this.push(o,c,q,n,m,p); -break;}}},"!^":function(o,c,q,n,m,p){o=o.lastChild;if(o){if(o.nodeType==1){this.push(o,c,q,n,m,p);}else{this["combinator:!+"](o,c,q,n,m,p);}}},"!~":function(p,c,r,o,m,q){while((p=p.previousSibling)){if(p.nodeType!=1){continue; -}var n=this.getUID(p);if(this.bitUniques[n]){break;}this.bitUniques[n]=true;this.push(p,c,r,o,m,q);}}};for(var h in i){j["combinator:"+h]=i[h];}var k={empty:function(c){var m=c.firstChild; -return !(m&&m.nodeType==1)&&!(c.innerText||c.textContent||"").length;},not:function(c,m){return !this.matchNode(c,m);},contains:function(c,m){return(c.innerText||c.textContent||"").indexOf(m)>-1; -},"first-child":function(c){while((c=c.previousSibling)){if(c.nodeType==1){return false;}}return true;},"last-child":function(c){while((c=c.nextSibling)){if(c.nodeType==1){return false; -}}return true;},"only-child":function(n){var m=n;while((m=m.previousSibling)){if(m.nodeType==1){return false;}}var c=n;while((c=c.nextSibling)){if(c.nodeType==1){return false; -}}return true;},"nth-child":j.createNTHPseudo("firstChild","nextSibling","posNTH"),"nth-last-child":j.createNTHPseudo("lastChild","previousSibling","posNTHLast"),"nth-of-type":j.createNTHPseudo("firstChild","nextSibling","posNTHType",true),"nth-last-of-type":j.createNTHPseudo("lastChild","previousSibling","posNTHTypeLast",true),index:function(m,c){return this["pseudo:nth-child"](m,""+c+1); -},even:function(c){return this["pseudo:nth-child"](c,"2n");},odd:function(c){return this["pseudo:nth-child"](c,"2n+1");},"first-of-type":function(c){var m=c.nodeName; -while((c=c.previousSibling)){if(c.nodeName==m){return false;}}return true;},"last-of-type":function(c){var m=c.nodeName;while((c=c.nextSibling)){if(c.nodeName==m){return false; -}}return true;},"only-of-type":function(n){var m=n,o=n.nodeName;while((m=m.previousSibling)){if(m.nodeName==o){return false;}}var c=n;while((c=c.nextSibling)){if(c.nodeName==o){return false; -}}return true;},enabled:function(c){return !c.disabled;},disabled:function(c){return c.disabled;},checked:function(c){return c.checked||c.selected;},focus:function(c){return this.isHTMLDocument&&this.document.activeElement===c&&(c.href||c.type||this.hasAttribute(c,"tabindex")); -},root:function(c){return(c===this.root);},selected:function(c){return c.selected;}};for(var a in k){j["pseudo:"+a]=k[a];}j.attributeGetters={"class":function(){return this.getAttribute("class")||this.className; -},"for":function(){return("htmlFor" in this)?this.htmlFor:this.getAttribute("for");},href:function(){return("href" in this)?this.getAttribute("href",2):this.getAttribute("href"); -},style:function(){return(this.style)?this.style.cssText:this.getAttribute("style");},tabindex:function(){var c=this.getAttributeNode("tabindex");return(c&&c.specified)?c.nodeValue:null; -},type:function(){return this.getAttribute("type");}};var d=j.Slick=(this.Slick||{});d.version="1.1.5";d.search=function(m,n,c){return j.search(m,n,c); -};d.find=function(c,m){return j.search(c,m,null,true);};d.contains=function(c,m){j.setDocument(c);return j.contains(c,m);};d.getAttribute=function(m,c){return j.getAttribute(m,c); -};d.match=function(m,c){if(!(m&&c)){return false;}if(!c||c===m){return true;}j.setDocument(m);return j.matchNode(m,c);};d.defineAttributeGetter=function(c,m){j.attributeGetters[c]=m; -return this;};d.lookupAttributeGetter=function(c){return j.attributeGetters[c];};d.definePseudo=function(c,m){j["pseudo:"+c]=function(o,n){return m.call(o,n); -};return this;};d.lookupPseudo=function(c){var m=j["pseudo:"+c];if(m){return function(n){return m.call(this,n);};}return null;};d.override=function(m,c){j.override(m,c); -return this;};d.isXML=j.isXML;d.uidOf=function(c){return j.getUIDHTML(c);};if(!this.Slick){this.Slick=d;}}).apply((typeof exports!="undefined")?exports:this); -var Element=function(b,g){var h=Element.Constructors[b];if(h){return h(g);}if(typeof b!="string"){return document.id(b).set(g);}if(!g){g={};}if(!(/^[\w-]+$/).test(b)){var e=Slick.parse(b).expressions[0][0]; -b=(e.tag=="*")?"div":e.tag;if(e.id&&g.id==null){g.id=e.id;}var d=e.attributes;if(d){for(var f=0,c=d.length;f=this.length){delete this[e--];}return this; -}.protect());}Elements.implement(Array.prototype);Array.mirror(Elements);var f;try{var a=document.createElement("");f=(a.name=="x");}catch(c){}var d=function(e){return(""+e).replace(/&/g,"&").replace(/"/g,"""); -};Document.implement({newElement:function(e,h){if(h&&h.checked!=null){h.defaultChecked=h.checked;}if(f&&h){e="<"+e;if(h.name){e+=' name="'+d(h.name)+'"'; -}if(h.type){e+=' type="'+d(h.type)+'"';}e+=">";delete h.name;delete h.type;}return this.id(this.createElement(e)).set(h);}});})();Document.implement({newTextNode:function(a){return this.createTextNode(a); -},getDocument:function(){return this;},getWindow:function(){return this.window;},id:(function(){var a={string:function(d,c,b){d=Slick.find(b,"#"+d.replace(/(\W)/g,"\\$1")); -return(d)?a.element(d,c):null;},element:function(b,c){$uid(b);if(!c&&!b.$family&&!(/^(?:object|embed)$/i).test(b.tagName)){Object.append(b,Element.Prototype); -}return b;},object:function(c,d,b){if(c.toElement){return a.element(c.toElement(b),d);}return null;}};a.textnode=a.whitespace=a.window=a.document=function(b){return b; -};return function(c,e,d){if(c&&c.$family&&c.uid){return c;}var b=typeOf(c);return(a[b])?a[b](c,e,d||document):null;};})()});if(window.$==null){Window.implement("$",function(a,b){return document.id(a,b,this.document); -});}Window.implement({getDocument:function(){return this.document;},getWindow:function(){return this;}});[Document,Element].invoke("implement",{getElements:function(a){return Slick.search(this,a,new Elements); -},getElement:function(a){return document.id(Slick.find(this,a));}});if(window.$$==null){Window.implement("$$",function(a){if(arguments.length==1){if(typeof a=="string"){return Slick.search(this.document,a,new Elements); -}else{if(Type.isEnumerable(a)){return new Elements(a);}}}return new Elements(arguments);});}(function(){var k={},i={};var n={input:"checked",option:"selected",textarea:"value"}; -var e=function(p){return(i[p]||(i[p]={}));};var j=function(q){var p=q.uid;if(q.removeEvents){q.removeEvents();}if(q.clearAttributes){q.clearAttributes(); -}if(p!=null){delete k[p];delete i[p];}return q;};var o=["defaultValue","accessKey","cellPadding","cellSpacing","colSpan","frameBorder","maxLength","readOnly","rowSpan","tabIndex","useMap"]; -var d=["compact","nowrap","ismap","declare","noshade","checked","disabled","readOnly","multiple","selected","noresize","defer","defaultChecked"];var g={html:"innerHTML","class":"className","for":"htmlFor",text:(function(){var p=document.createElement("div"); -return(p.textContent==null)?"innerText":"textContent";})()};var m=["type"];var h=["value","defaultValue"];var l=/^(?:href|src|usemap)$/i;d=d.associate(d); -o=o.associate(o.map(String.toLowerCase));m=m.associate(m);Object.append(g,h.associate(h));var c={before:function(q,p){var r=p.parentNode;if(r){r.insertBefore(q,p); -}},after:function(q,p){var r=p.parentNode;if(r){r.insertBefore(q,p.nextSibling);}},bottom:function(q,p){p.appendChild(q);},top:function(q,p){p.insertBefore(q,p.firstChild); -}};c.inside=c.bottom;var b=function(s,r){if(!s){return r;}s=Object.clone(Slick.parse(s));var q=s.expressions;for(var p=q.length;p--;){q[p][0].combinator=r; -}return s;};Element.implement({set:function(r,q){var p=Element.Properties[r];(p&&p.set)?p.set.call(this,q):this.setProperty(r,q);}.overloadSetter(),get:function(q){var p=Element.Properties[q]; -return(p&&p.get)?p.get.apply(this):this.getProperty(q);}.overloadGetter(),erase:function(q){var p=Element.Properties[q];(p&&p.erase)?p.erase.apply(this):this.removeProperty(q); -return this;},setProperty:function(q,r){q=o[q]||q;if(r==null){return this.removeProperty(q);}var p=g[q];(p)?this[p]=r:(d[q])?this[q]=!!r:this.setAttribute(q,""+r); -return this;},setProperties:function(p){for(var q in p){this.setProperty(q,p[q]);}return this;},getProperty:function(q){q=o[q]||q;var p=g[q]||m[q];return(p)?this[p]:(d[q])?!!this[q]:(l.test(q)?this.getAttribute(q,2):(p=this.getAttributeNode(q))?p.nodeValue:null)||null; -},getProperties:function(){var p=Array.from(arguments);return p.map(this.getProperty,this).associate(p);},removeProperty:function(q){q=o[q]||q;var p=g[q]; -(p)?this[p]="":(d[q])?this[q]=false:this.removeAttribute(q);return this;},removeProperties:function(){Array.each(arguments,this.removeProperty,this);return this; -},hasClass:function(p){return this.className.clean().contains(p," ");},addClass:function(p){if(!this.hasClass(p)){this.className=(this.className+" "+p).clean(); -}return this;},removeClass:function(p){this.className=this.className.replace(new RegExp("(^|\\s)"+p+"(?:\\s|$)"),"$1");return this;},toggleClass:function(p,q){if(q==null){q=!this.hasClass(p); -}return(q)?this.addClass(p):this.removeClass(p);},adopt:function(){var s=this,p,u=Array.flatten(arguments),t=u.length;if(t>1){s=p=document.createDocumentFragment(); -}for(var r=0;r"))[0]);},getLast:function(p){return document.id(Slick.search(this,b(p,">")).getLast()); -},getParent:function(p){return document.id(Slick.find(this,b(p,"!")));},getParents:function(p){return Slick.search(this,b(p,"!"),new Elements);},getSiblings:function(p){return Slick.search(this,b(p,"~~"),new Elements); -},getChildren:function(p){return Slick.search(this,b(p,">"),new Elements);},getWindow:function(){return this.ownerDocument.window;},getDocument:function(){return this.ownerDocument; -},getElementById:function(p){return document.id(Slick.find(this,"#"+(""+p).replace(/(\W)/g,"\\$1")));},getSelected:function(){this.selectedIndex;return new Elements(Array.from(this.options).filter(function(p){return p.selected; -}));},toQueryString:function(){var p=[];this.getElements("input, select, textarea").each(function(r){var q=r.type;if(!r.name||r.disabled||q=="submit"||q=="reset"||q=="file"||q=="image"){return; -}var s=(r.get("tag")=="select")?r.getSelected().map(function(t){return document.id(t).get("value");}):((q=="radio"||q=="checkbox")&&!r.checked)?null:r.get("value"); -Array.from(s).each(function(t){if(typeof t!="undefined"){p.push(encodeURIComponent(r.name)+"="+encodeURIComponent(t));}});});return p.join("&");},destroy:function(){var p=j(this).getElementsByTagName("*"); -Array.each(p,j);Element.dispose(this);return null;},empty:function(){Array.from(this.childNodes).each(Element.dispose);return this;},dispose:function(){return(this.parentNode)?this.parentNode.removeChild(this):this; -},match:function(p){return !p||Slick.match(this,p);}});var a=function(t,s,q){if(!q){t.setAttributeNode(document.createAttribute("id"));}if(t.clearAttributes){t.clearAttributes(); -t.mergeAttributes(s);t.removeAttribute("uid");if(t.options){var u=t.options,p=s.options;for(var r=u.length;r--;){u[r].selected=p[r].selected;}}}var v=n[s.tagName.toLowerCase()]; -if(v&&s[v]){t[v]=s[v];}};Element.implement("clone",function(r,p){r=r!==false;var w=this.cloneNode(r),q;if(r){var s=w.getElementsByTagName("*"),u=this.getElementsByTagName("*"); -for(q=s.length;q--;){a(s[q],u[q],p);}}a(w,this,p);if(Browser.ie){var t=w.getElementsByTagName("object"),v=this.getElementsByTagName("object");for(q=t.length; -q--;){t[q].outerHTML=v[q].outerHTML;}}return document.id(w);});var f={contains:function(p){return Slick.contains(this,p);}};if(!document.contains){Document.implement(f); -}if(!document.createElement("div").contains){Element.implement(f);}[Element,Window,Document].invoke("implement",{addListener:function(s,r){if(s=="unload"){var p=r,q=this; -r=function(){q.removeListener("unload",r);p();};}else{k[$uid(this)]=this;}if(this.addEventListener){this.addEventListener(s,r,!!arguments[2]);}else{this.attachEvent("on"+s,r); -}return this;},removeListener:function(q,p){if(this.removeEventListener){this.removeEventListener(q,p,!!arguments[2]);}else{this.detachEvent("on"+q,p); -}return this;},retrieve:function(q,p){var s=e($uid(this)),r=s[q];if(p!=null&&r==null){r=s[q]=p;}return r!=null?r:null;},store:function(q,p){var r=e($uid(this)); -r[q]=p;return this;},eliminate:function(p){var q=e($uid(this));delete q[p];return this;}});if(window.attachEvent&&!window.addEventListener){window.addListener("unload",function(){Object.each(k,j); -if(window.CollectGarbage){CollectGarbage();}});}})();Element.Properties={};Element.Properties.style={set:function(a){this.style.cssText=a;},get:function(){return this.style.cssText; -},erase:function(){this.style.cssText="";}};Element.Properties.tag={get:function(){return this.tagName.toLowerCase();}};(function(a){if(a!=null){Element.Properties.maxlength=Element.Properties.maxLength={get:function(){var b=this.getAttribute("maxLength"); -return b==a?null:b;}};}})(document.createElement("input").getAttribute("maxLength"));Element.Properties.html=(function(){var c=Function.attempt(function(){var e=document.createElement("table"); -e.innerHTML="";});var d=document.createElement("div");var a={table:[1,"","
"],select:[1,""],tbody:[2,"","
"],tr:[3,"","
"]}; -a.thead=a.tfoot=a.tbody;var b={set:function(){var f=Array.flatten(arguments).join("");var g=(!c&&a[this.get("tag")]);if(g){var h=d;h.innerHTML=g[1]+f+g[2]; -for(var e=g[0];e--;){h=h.firstChild;}this.empty().adopt(h.childNodes);}else{this.innerHTML=f;}}};b.erase=b.set;return b;})();(function(){var c=document.html; -Element.Properties.styles={set:function(f){this.setStyles(f);}};var e=(c.style.opacity!=null);var d=/alpha\(opacity=([\d.]+)\)/i;var b=function(g,f){if(!g.currentStyle||!g.currentStyle.hasLayout){g.style.zoom=1; -}if(e){g.style.opacity=f;}else{f=(f*100).limit(0,100).round();f=(f==100)?"":"alpha(opacity="+f+")";var h=g.style.filter||g.getComputedStyle("filter")||""; -g.style.filter=d.test(h)?h.replace(d,f):h+f;}};Element.Properties.opacity={set:function(g){var f=this.style.visibility;if(g==0&&f!="hidden"){this.style.visibility="hidden"; -}else{if(g!=0&&f!="visible"){this.style.visibility="visible";}}b(this,g);},get:(e)?function(){var f=this.style.opacity||this.getComputedStyle("opacity"); -return(f=="")?1:f;}:function(){var f,g=(this.style.filter||this.getComputedStyle("filter"));if(g){f=g.match(d);}return(f==null||g==null)?1:(f[1]/100);}}; -var a=(c.style.cssFloat==null)?"styleFloat":"cssFloat";Element.implement({getComputedStyle:function(h){if(this.currentStyle){return this.currentStyle[h.camelCase()]; -}var g=Element.getDocument(this).defaultView,f=g?g.getComputedStyle(this,null):null;return(f)?f.getPropertyValue((h==a)?"float":h.hyphenate()):null;},setOpacity:function(f){b(this,f); -return this;},getOpacity:function(){return this.get("opacity");},setStyle:function(g,f){switch(g){case"opacity":return this.set("opacity",parseFloat(f)); -case"float":g=a;}g=g.camelCase();if(typeOf(f)!="string"){var h=(Element.Styles[g]||"@").split(" ");f=Array.from(f).map(function(k,j){if(!h[j]){return""; -}return(typeOf(k)=="number")?h[j].replace("@",Math.round(k)):k;}).join(" ");}else{if(f==String(Number(f))){f=Math.round(f);}}this.style[g]=f;return this; -},getStyle:function(l){switch(l){case"opacity":return this.get("opacity");case"float":l=a;}l=l.camelCase();var f=this.style[l];if(!f||l=="zIndex"){f=[]; -for(var k in Element.ShortStyles){if(l!=k){continue;}for(var j in Element.ShortStyles[k]){f.push(this.getStyle(j));}return f.join(" ");}f=this.getComputedStyle(l); -}if(f){f=String(f);var h=f.match(/rgba?\([\d\s,]+\)/);if(h){f=f.replace(h[0],h[0].rgbToHex());}}if(Browser.opera||(Browser.ie&&isNaN(parseFloat(f)))){if((/^(height|width)$/).test(l)){var g=(l=="width")?["left","right"]:["top","bottom"],i=0; -g.each(function(m){i+=this.getStyle("border-"+m+"-width").toInt()+this.getStyle("padding-"+m).toInt();},this);return this["offset"+l.capitalize()]-i+"px"; -}if(Browser.opera&&String(f).indexOf("px")!=-1){return f;}if((/^border(.+)Width|margin|padding/).test(l)){return"0px";}}return f;},setStyles:function(g){for(var f in g){this.setStyle(f,g[f]); -}return this;},getStyles:function(){var f={};Array.flatten(arguments).each(function(g){f[g]=this.getStyle(g);},this);return f;}});Element.Styles={left:"@px",top:"@px",bottom:"@px",right:"@px",width:"@px",height:"@px",maxWidth:"@px",maxHeight:"@px",minWidth:"@px",minHeight:"@px",backgroundColor:"rgb(@, @, @)",backgroundPosition:"@px @px",color:"rgb(@, @, @)",fontSize:"@px",letterSpacing:"@px",lineHeight:"@px",clip:"rect(@px @px @px @px)",margin:"@px @px @px @px",padding:"@px @px @px @px",border:"@px @ rgb(@, @, @) @px @ rgb(@, @, @) @px @ rgb(@, @, @)",borderWidth:"@px @px @px @px",borderStyle:"@ @ @ @",borderColor:"rgb(@, @, @) rgb(@, @, @) rgb(@, @, @) rgb(@, @, @)",zIndex:"@",zoom:"@",fontWeight:"@",textIndent:"@px",opacity:"@"}; -Element.ShortStyles={margin:{},padding:{},border:{},borderWidth:{},borderStyle:{},borderColor:{}};["Top","Right","Bottom","Left"].each(function(l){var k=Element.ShortStyles; -var g=Element.Styles;["margin","padding"].each(function(m){var n=m+l;k[m][n]=g[n]="@px";});var j="border"+l;k.border[j]=g[j]="@px @ rgb(@, @, @)";var i=j+"Width",f=j+"Style",h=j+"Color"; -k[j]={};k.borderWidth[i]=k[j][i]=g[i]="@px";k.borderStyle[f]=k[j][f]=g[f]="@";k.borderColor[h]=k[j][h]=g[h]="rgb(@, @, @)";});})();(function(){Element.Properties.events={set:function(b){this.addEvents(b); -}};[Element,Window,Document].invoke("implement",{addEvent:function(f,h){var i=this.retrieve("events",{});if(!i[f]){i[f]={keys:[],values:[]};}if(i[f].keys.contains(h)){return this; -}i[f].keys.push(h);var g=f,b=Element.Events[f],d=h,j=this;if(b){if(b.onAdd){b.onAdd.call(this,h);}if(b.condition){d=function(k){if(b.condition.call(this,k)){return h.call(this,k); -}return true;};}g=b.base||g;}var e=function(){return h.call(j);};var c=Element.NativeEvents[g];if(c){if(c==2){e=function(k){k=new Event(k,j.getWindow()); -if(d.call(j,k)===false){k.stop();}};}this.addListener(g,e,arguments[2]);}i[f].values.push(e);return this;},removeEvent:function(e,d){var c=this.retrieve("events"); -if(!c||!c[e]){return this;}var h=c[e];var b=h.keys.indexOf(d);if(b==-1){return this;}var g=h.values[b];delete h.keys[b];delete h.values[b];var f=Element.Events[e]; -if(f){if(f.onRemove){f.onRemove.call(this,d);}e=f.base||e;}return(Element.NativeEvents[e])?this.removeListener(e,g,arguments[2]):this;},addEvents:function(b){for(var c in b){this.addEvent(c,b[c]); -}return this;},removeEvents:function(b){var d;if(typeOf(b)=="object"){for(d in b){this.removeEvent(d,b[d]);}return this;}var c=this.retrieve("events"); -if(!c){return this;}if(!b){for(d in c){this.removeEvents(d);}this.eliminate("events");}else{if(c[b]){c[b].keys.each(function(e){this.removeEvent(b,e);},this); -delete c[b];}}return this;},fireEvent:function(e,c,b){var d=this.retrieve("events");if(!d||!d[e]){return this;}c=Array.from(c);d[e].keys.each(function(f){if(b){f.delay(b,this,c); -}else{f.apply(this,c);}},this);return this;},cloneEvents:function(e,d){e=document.id(e);var c=e.retrieve("events");if(!c){return this;}if(!d){for(var b in c){this.cloneEvents(e,b); -}}else{if(c[d]){c[d].keys.each(function(f){this.addEvent(d,f);},this);}}return this;}});Element.NativeEvents={click:2,dblclick:2,mouseup:2,mousedown:2,contextmenu:2,mousewheel:2,DOMMouseScroll:2,mouseover:2,mouseout:2,mousemove:2,selectstart:2,selectend:2,keydown:2,keypress:2,keyup:2,orientationchange:2,touchstart:2,touchmove:2,touchend:2,touchcancel:2,gesturestart:2,gesturechange:2,gestureend:2,focus:2,blur:2,change:2,reset:2,select:2,submit:2,load:2,unload:1,beforeunload:2,resize:1,move:1,DOMContentLoaded:1,readystatechange:1,error:1,abort:1,scroll:1}; -var a=function(b){var c=b.relatedTarget;if(c==null){return true;}if(!c){return false;}return(c!=this&&c.prefix!="xul"&&typeOf(this)!="document"&&!this.contains(c)); -};Element.Events={mouseenter:{base:"mouseover",condition:a},mouseleave:{base:"mouseout",condition:a},mousewheel:{base:(Browser.firefox)?"DOMMouseScroll":"mousewheel"}}; -})();(function(){var h=document.createElement("div"),e=document.createElement("div");h.style.height="0";h.appendChild(e);var d=(e.offsetParent===h);h=e=null; -var l=function(m){return k(m,"position")!="static"||a(m);};var i=function(m){return l(m)||(/^(?:table|td|th)$/i).test(m.tagName);};Element.implement({scrollTo:function(m,n){if(a(this)){this.getWindow().scrollTo(m,n); -}else{this.scrollLeft=m;this.scrollTop=n;}return this;},getSize:function(){if(a(this)){return this.getWindow().getSize();}return{x:this.offsetWidth,y:this.offsetHeight}; -},getScrollSize:function(){if(a(this)){return this.getWindow().getScrollSize();}return{x:this.scrollWidth,y:this.scrollHeight};},getScroll:function(){if(a(this)){return this.getWindow().getScroll(); -}return{x:this.scrollLeft,y:this.scrollTop};},getScrolls:function(){var n=this.parentNode,m={x:0,y:0};while(n&&!a(n)){m.x+=n.scrollLeft;m.y+=n.scrollTop; -n=n.parentNode;}return m;},getOffsetParent:d?function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}var n=(k(m,"position")=="static")?i:l; -while((m=m.parentNode)){if(n(m)){return m;}}return null;}:function(){var m=this;if(a(m)||k(m,"position")=="fixed"){return null;}try{return m.offsetParent; -}catch(n){}return null;},getOffsets:function(){if(this.getBoundingClientRect&&!Browser.Platform.ios){var r=this.getBoundingClientRect(),o=document.id(this.getDocument().documentElement),q=o.getScroll(),t=this.getScrolls(),s=(k(this,"position")=="fixed"); -return{x:r.left.toInt()+t.x+((s)?0:q.x)-o.clientLeft,y:r.top.toInt()+t.y+((s)?0:q.y)-o.clientTop};}var n=this,m={x:0,y:0};if(a(this)){return m;}while(n&&!a(n)){m.x+=n.offsetLeft; -m.y+=n.offsetTop;if(Browser.firefox){if(!c(n)){m.x+=b(n);m.y+=g(n);}var p=n.parentNode;if(p&&k(p,"overflow")!="visible"){m.x+=b(p);m.y+=g(p);}}else{if(n!=this&&Browser.safari){m.x+=b(n); -m.y+=g(n);}}n=n.offsetParent;}if(Browser.firefox&&!c(this)){m.x-=b(this);m.y-=g(this);}return m;},getPosition:function(p){if(a(this)){return{x:0,y:0};}var q=this.getOffsets(),n=this.getScrolls(); -var m={x:q.x-n.x,y:q.y-n.y};if(p&&(p=document.id(p))){var o=p.getPosition();return{x:m.x-o.x-b(p),y:m.y-o.y-g(p)};}return m;},getCoordinates:function(o){if(a(this)){return this.getWindow().getCoordinates(); -}var m=this.getPosition(o),n=this.getSize();var p={left:m.x,top:m.y,width:n.x,height:n.y};p.right=p.left+p.width;p.bottom=p.top+p.height;return p;},computePosition:function(m){return{left:m.x-j(this,"margin-left"),top:m.y-j(this,"margin-top")}; -},setPosition:function(m){return this.setStyles(this.computePosition(m));}});[Document,Window].invoke("implement",{getSize:function(){var m=f(this);return{x:m.clientWidth,y:m.clientHeight}; -},getScroll:function(){var n=this.getWindow(),m=f(this);return{x:n.pageXOffset||m.scrollLeft,y:n.pageYOffset||m.scrollTop};},getScrollSize:function(){var o=f(this),n=this.getSize(),m=this.getDocument().body; -return{x:Math.max(o.scrollWidth,m.scrollWidth,n.x),y:Math.max(o.scrollHeight,m.scrollHeight,n.y)};},getPosition:function(){return{x:0,y:0};},getCoordinates:function(){var m=this.getSize(); -return{top:0,left:0,bottom:m.y,right:m.x,height:m.y,width:m.x};}});var k=Element.getComputedStyle;function j(m,n){return k(m,n).toInt()||0;}function c(m){return k(m,"-moz-box-sizing")=="border-box"; -}function g(m){return j(m,"border-top-width");}function b(m){return j(m,"border-left-width");}function a(m){return(/^(?:body|html)$/i).test(m.tagName); -}function f(m){var n=m.getDocument();return(!n.compatMode||n.compatMode=="CSS1Compat")?n.html:n.body;}})();Element.alias({position:"setPosition"});[Window,Document,Element].invoke("implement",{getHeight:function(){return this.getSize().y; -},getWidth:function(){return this.getSize().x;},getScrollTop:function(){return this.getScroll().y;},getScrollLeft:function(){return this.getScroll().x; -},getScrollHeight:function(){return this.getScrollSize().y;},getScrollWidth:function(){return this.getScrollSize().x;},getTop:function(){return this.getPosition().y; -},getLeft:function(){return this.getPosition().x;}});(function(){var f=this.Fx=new Class({Implements:[Chain,Events,Options],options:{fps:60,unit:false,duration:500,frames:null,frameSkip:true,link:"ignore"},initialize:function(g){this.subject=this.subject||this; -this.setOptions(g);},getTransition:function(){return function(g){return -(Math.cos(Math.PI*g)-1)/2;};},step:function(g){if(this.options.frameSkip){var h=(this.time!=null)?(g-this.time):0,i=h/this.frameInterval; -this.time=g;this.frame+=i;}else{this.frame++;}if(this.frame=(7-4*d)/11){e=c*c-Math.pow((11-6*d-11*f)/4,2);break;}}return e; -},Elastic:function(b,a){return Math.pow(2,10*--b)*Math.cos(20*b*Math.PI*(a&&a[0]||1)/3);}});["Quad","Cubic","Quart","Quint"].each(function(b,a){Fx.Transitions[b]=new Fx.Transition(function(c){return Math.pow(c,a+2); -});});(function(){var d=function(){},a=("onprogress" in new Browser.Request);var c=this.Request=new Class({Implements:[Chain,Events,Options],options:{url:"",data:"",headers:{"X-Requested-With":"XMLHttpRequest",Accept:"text/javascript, text/html, application/xml, text/xml, */*"},async:true,format:false,method:"post",link:"ignore",isSuccess:null,emulation:true,urlEncoded:true,encoding:"utf-8",evalScripts:false,evalResponse:false,timeout:0,noCache:false},initialize:function(e){this.xhr=new Browser.Request(); -this.setOptions(e);this.headers=this.options.headers;},onStateChange:function(){var e=this.xhr;if(e.readyState!=4||!this.running){return;}this.running=false; -this.status=0;Function.attempt(function(){var f=e.status;this.status=(f==1223)?204:f;}.bind(this));e.onreadystatechange=d;if(a){e.onprogress=e.onloadstart=d; -}clearTimeout(this.timer);this.response={text:this.xhr.responseText||"",xml:this.xhr.responseXML};if(this.options.isSuccess.call(this,this.status)){this.success(this.response.text,this.response.xml); -}else{this.failure();}},isSuccess:function(){var e=this.status;return(e>=200&&e<300);},isRunning:function(){return !!this.running;},processScripts:function(e){if(this.options.evalResponse||(/(ecma|java)script/).test(this.getHeader("Content-type"))){return Browser.exec(e); -}return e.stripScripts(this.options.evalScripts);},success:function(f,e){this.onSuccess(this.processScripts(f),e);},onSuccess:function(){this.fireEvent("complete",arguments).fireEvent("success",arguments).callChain(); -},failure:function(){this.onFailure();},onFailure:function(){this.fireEvent("complete").fireEvent("failure",this.xhr);},loadstart:function(e){this.fireEvent("loadstart",[e,this.xhr]); -},progress:function(e){this.fireEvent("progress",[e,this.xhr]);},timeout:function(){this.fireEvent("timeout",this.xhr);},setHeader:function(e,f){this.headers[e]=f; -return this;},getHeader:function(e){return Function.attempt(function(){return this.xhr.getResponseHeader(e);}.bind(this));},check:function(){if(!this.running){return true; -}switch(this.options.link){case"cancel":this.cancel();return true;case"chain":this.chain(this.caller.pass(arguments,this));return false;}return false;},send:function(o){if(!this.check(o)){return this; -}this.options.isSuccess=this.options.isSuccess||this.isSuccess;this.running=true;var l=typeOf(o);if(l=="string"||l=="element"){o={data:o};}var h=this.options; -o=Object.append({data:h.data,url:h.url,method:h.method},o);var j=o.data,f=String(o.url),e=o.method.toLowerCase();switch(typeOf(j)){case"element":j=document.id(j).toQueryString(); -break;case"object":case"hash":j=Object.toQueryString(j);}if(this.options.format){var m="format="+this.options.format;j=(j)?m+"&"+j:m;}if(this.options.emulation&&!["get","post"].contains(e)){var k="_method="+e; -j=(j)?k+"&"+j:k;e="post";}if(this.options.urlEncoded&&["post","put"].contains(e)){var g=(this.options.encoding)?"; charset="+this.options.encoding:"";this.headers["Content-type"]="application/x-www-form-urlencoded"+g; -}if(!f){f=document.location.pathname;}var i=f.lastIndexOf("/");if(i>-1&&(i=f.indexOf("#"))>-1){f=f.substr(0,i);}if(this.options.noCache){f+=(f.contains("?")?"&":"?")+String.uniqueID(); -}if(j&&e=="get"){f+=(f.contains("?")?"&":"?")+j;j=null;}var n=this.xhr;if(a){n.onloadstart=this.loadstart.bind(this);n.onprogress=this.progress.bind(this); -}n.open(e.toUpperCase(),f,this.options.async,this.options.user,this.options.password);if(this.options.user&&"withCredentials" in n){n.withCredentials=true; -}n.onreadystatechange=this.onStateChange.bind(this);Object.each(this.headers,function(q,p){try{n.setRequestHeader(p,q);}catch(r){this.fireEvent("exception",[p,q]); -}},this);this.fireEvent("request");n.send(j);if(!this.options.async){this.onStateChange();}if(this.options.timeout){this.timer=this.timeout.delay(this.options.timeout,this); -}return this;},cancel:function(){if(!this.running){return this;}this.running=false;var e=this.xhr;e.abort();clearTimeout(this.timer);e.onreadystatechange=d; -if(a){e.onprogress=e.onloadstart=d;}this.xhr=new Browser.Request();this.fireEvent("cancel");return this;}});var b={};["get","post","put","delete","GET","POST","PUT","DELETE"].each(function(e){b[e]=function(g){var f={method:e}; -if(g!=null){f.data=g;}return this.send(f);};});c.implement(b);Element.Properties.send={set:function(e){var f=this.get("send").cancel();f.setOptions(e); -return this;},get:function(){var e=this.retrieve("send");if(!e){e=new c({data:this,link:"cancel",method:this.get("method")||"post",url:this.get("action")}); -this.store("send",e);}return e;}};Element.implement({send:function(e){var f=this.get("send");f.send({data:this,url:e||f.options.url});return this;}});})(); -Request.HTML=new Class({Extends:Request,options:{update:false,append:false,evalScripts:true,filter:false,headers:{Accept:"text/html, application/xml, text/xml, */*"}},success:function(e){var d=this.options,b=this.response; -b.html=e.stripScripts(function(f){b.javascript=f;});var c=b.html.match(/]*>([\s\S]*?)<\/body>/i);if(c){b.html=c[1];}var a=new Element("div").set("html",b.html); -b.tree=a.childNodes;b.elements=a.getElements("*");if(d.filter){b.tree=b.elements.filter(d.filter);}if(d.update){document.id(d.update).empty().set("html",b.html); -}else{if(d.append){document.id(d.append).adopt(a.getChildren());}}if(d.evalScripts){Browser.exec(b.javascript);}this.onSuccess(b.tree,b.elements,b.html,b.javascript); -}});Element.Properties.load={set:function(a){var b=this.get("load").cancel();b.setOptions(a);return this;},get:function(){var a=this.retrieve("load");if(!a){a=new Request.HTML({data:this,link:"cancel",update:this,method:"get"}); -this.store("load",a);}return a;}};Element.implement({load:function(){this.get("load").send(Array.link(arguments,{data:Type.isObject,url:Type.isString})); -return this;}});if(typeof JSON=="undefined"){this.JSON={};}(function(){var special={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"}; -var escape=function(chr){return special[chr]||"\\u"+("0000"+chr.charCodeAt(0).toString(16)).slice(-4);};JSON.validate=function(string){string=string.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""); -return(/^[\],:{}\s]*$/).test(string);};JSON.encode=JSON.stringify?function(obj){return JSON.stringify(obj);}:function(obj){if(obj&&obj.toJSON){obj=obj.toJSON(); -}switch(typeOf(obj)){case"string":return'"'+obj.replace(/[\x00-\x1f\\"]/g,escape)+'"';case"array":return"["+obj.map(JSON.encode).clean()+"]";case"object":case"hash":var string=[]; -Object.each(obj,function(value,key){var json=JSON.encode(value);if(json){string.push(JSON.encode(key)+":"+json);}});return"{"+string+"}";case"number":case"boolean":return""+obj; -case"null":return"null";}return null;};JSON.decode=function(string,secure){if(!string||typeOf(string)!="string"){return null;}if(secure||JSON.secure){if(JSON.parse){return JSON.parse(string); -}if(!JSON.validate(string)){throw new Error("JSON could not decode the input; security is enabled and the value is not secure.");}}return eval("("+string+")"); -};})();Request.JSON=new Class({Extends:Request,options:{secure:true},initialize:function(a){this.parent(a);Object.append(this.headers,{Accept:"application/json","X-Request":"JSON"}); -},success:function(c){var b;try{b=this.response.json=JSON.decode(c,this.options.secure);}catch(a){this.fireEvent("error",[c,a]);return;}if(b==null){this.onFailure(); -}else{this.onSuccess(b,c);}}});var Cookie=new Class({Implements:Options,options:{path:"/",domain:false,duration:false,secure:false,document:document,encode:true},initialize:function(b,a){this.key=b; -this.setOptions(a);},write:function(b){if(this.options.encode){b=encodeURIComponent(b);}if(this.options.domain){b+="; domain="+this.options.domain;}if(this.options.path){b+="; path="+this.options.path; -}if(this.options.duration){var a=new Date();a.setTime(a.getTime()+this.options.duration*24*60*60*1000);b+="; expires="+a.toGMTString();}if(this.options.secure){b+="; secure"; -}this.options.document.cookie=this.key+"="+b;return this;},read:function(){var a=this.options.document.cookie.match("(?:^|;)\\s*"+this.key.escapeRegExp()+"=([^;]*)"); -return(a)?decodeURIComponent(a[1]):null;},dispose:function(){new Cookie(this.key,Object.merge({},this.options,{duration:-1})).write("");return this;}}); -Cookie.write=function(b,c,a){return new Cookie(b,a).write(c);};Cookie.read=function(a){return new Cookie(a).read();};Cookie.dispose=function(b,a){return new Cookie(b,a).dispose(); -};(function(i,k){var l,f,e=[],c,b,d=k.createElement("div");var g=function(){clearTimeout(b);if(l){return;}Browser.loaded=l=true;k.removeListener("DOMContentLoaded",g).removeListener("readystatechange",a); -k.fireEvent("domready");i.fireEvent("domready");};var a=function(){for(var m=e.length;m--;){if(e[m]()){g();return true;}}return false;};var j=function(){clearTimeout(b); -if(!a()){b=setTimeout(j,10);}};k.addListener("DOMContentLoaded",g);var h=function(){try{d.doScroll();return true;}catch(m){}return false;};if(d.doScroll&&!h()){e.push(h); -c=true;}if(k.readyState){e.push(function(){var m=k.readyState;return(m=="loaded"||m=="complete");});}if("onreadystatechange" in k){k.addListener("readystatechange",a); -}else{c=true;}if(c){j();}Element.Events.domready={onAdd:function(m){if(l){m.call(this);}}};Element.Events.load={base:"load",onAdd:function(m){if(f&&this==i){m.call(this); -}},condition:function(){if(this==i){g();delete Element.Events.load;}return true;}};i.addEvent("load",function(){f=true;});})(window,document);(function(){var Swiff=this.Swiff=new Class({Implements:Options,options:{id:null,height:1,width:1,container:null,properties:{},params:{quality:"high",allowScriptAccess:"always",wMode:"window",swLiveConnect:true},callBacks:{},vars:{}},toElement:function(){return this.object; -},initialize:function(path,options){this.instance="Swiff_"+String.uniqueID();this.setOptions(options);options=this.options;var id=this.id=options.id||this.instance; -var container=document.id(options.container);Swiff.CallBacks[this.instance]={};var params=options.params,vars=options.vars,callBacks=options.callBacks; -var properties=Object.append({height:options.height,width:options.width},options.properties);var self=this;for(var callBack in callBacks){Swiff.CallBacks[this.instance][callBack]=(function(option){return function(){return option.apply(self.object,arguments); -};})(callBacks[callBack]);vars[callBack]="Swiff.CallBacks."+this.instance+"."+callBack;}params.flashVars=Object.toQueryString(vars);if(Browser.ie){properties.classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"; -params.movie=path;}else{properties.type="application/x-shockwave-flash";}properties.data=path;var build='';}}build+="";this.object=((container)?container.empty():new Element("div")).set("html",build).firstChild; -},replaces:function(element){element=document.id(element,true);element.parentNode.replaceChild(this.toElement(),element);return this;},inject:function(element){document.id(element,true).appendChild(this.toElement()); -return this;},remote:function(){return Swiff.remote.apply(Swiff,[this.toElement()].append(arguments));}});Swiff.CallBacks={};Swiff.remote=function(obj,fn){var rs=obj.CallFunction(''+__flash__argumentsToXML(arguments,2)+""); -return eval(rs);};})(); \ No newline at end of file diff --git a/web/tools/mootools/mootools-core-1.3.2-nc.js b/web/tools/mootools/mootools-core-1.4.5-compat.js similarity index 76% rename from web/tools/mootools/mootools-core-1.3.2-nc.js rename to web/tools/mootools/mootools-core-1.4.5-compat.js index cba97ef7f..574c9411a 100644 --- a/web/tools/mootools/mootools-core-1.3.2-nc.js +++ b/web/tools/mootools/mootools-core-1.4.5-compat.js @@ -3,10 +3,13 @@ MooTools: the javascript framework web build: - - http://mootools.net/core/7c56cfef9dddcf170a5d68e3fb61cfd7 + - http://mootools.net/core/76bf47062d6c1983d66ce47ad66aa0e0 packager build: - - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff + - packager build Core/Core Core/Array Core/String Core/Number Core/Function Core/Object Core/Event Core/Browser Core/Class Core/Class.Extras Core/Slick.Parser Core/Slick.Finder Core/Element Core/Element.Style Core/Element.Event Core/Element.Delegation Core/Element.Dimensions Core/Fx Core/Fx.CSS Core/Fx.Tween Core/Fx.Morph Core/Fx.Transitions Core/Request Core/Request.HTML Core/Request.JSON Core/Cookie Core/JSON Core/DOMReady Core/Swiff + +... +*/ /* --- @@ -17,7 +20,7 @@ description: The heart of MooTools. license: MIT-style license. -copyright: Copyright (c) 2006-2010 [Valerio Proietti](http://mad4milk.net/). +copyright: Copyright (c) 2006-2012 [Valerio Proietti](http://mad4milk.net/). authors: The MooTools production team (http://mootools.net/developers/) @@ -33,15 +36,15 @@ provides: [Core, MooTools, Type, typeOf, instanceOf, Native] (function(){ this.MooTools = { - version: '1.3.2', - build: 'c9f1ff10e9e7facb65e9481049ed1b450959d587' + version: '1.4.5', + build: 'ab8ea8824dc3b24b6666867a2c4ed58ebb762cf0' }; // typeOf, instanceOf var typeOf = this.typeOf = function(item){ if (item == null) return 'null'; - if (item.$family) return item.$family(); + if (item.$family != null) return item.$family(); if (item.nodeName){ if (item.nodeType == 1) return 'element'; @@ -61,6 +64,9 @@ var instanceOf = this.instanceOf = function(item, object){ if (constructor === object) return true; constructor = constructor.parent; } + /**/ + if (!item.hasOwnProperty) return false; + /**/ return item instanceof object; }; @@ -93,8 +99,9 @@ Function.prototype.overloadGetter = function(usePlural){ var self = this; return function(a){ var args, result; - if (usePlural || typeof a != 'string') args = a; + if (typeof a != 'string') args = a; else if (arguments.length > 1) args = arguments; + else if (usePlural) args = [a]; if (args){ result = {}; for (var i = 0; i < args.length; i++) result[args[i]] = self.call(this, args[i]); @@ -167,7 +174,9 @@ var Type = this.Type = function(name, object){ object.prototype.$family = (function(){ return lower; }).hide(); - + //<1.2compat> + object.type = typeCheck; + // } } @@ -203,7 +212,7 @@ var implement = function(name, method){ if (typeOf(hook) == 'type') implement.call(hook, name, method); else hook.call(this, name, method); } - + var previous = this.prototype[name]; if (previous == null || !previous.$protected) this.prototype[name] = method; @@ -251,21 +260,25 @@ var force = function(name, object, methods){ proto = prototype[key]; if (generic) generic.protect(); - - if (isType && proto){ - delete prototype[key]; - prototype[key] = proto.protect(); - } + if (isType && proto) object.implement(key, proto.protect()); } - if (isType) object.implement(prototype); + if (isType){ + var methodsEnumerable = prototype.propertyIsEnumerable(methods[0]); + object.forEachMethod = function(fn){ + if (!methodsEnumerable) for (var i = 0, l = methods.length; i < l; i++){ + fn.call(prototype, prototype[methods[i]], methods[i]); + } + for (var key in prototype) fn.call(prototype, prototype[key], key) + }; + } return force; }; force('String', String, [ 'charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'match', 'quote', 'replace', 'search', - 'slice', 'split', 'substr', 'substring', 'toLowerCase', 'toUpperCase' + 'slice', 'split', 'substr', 'substring', 'trim', 'toLowerCase', 'toUpperCase' ])('Array', Array, [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice', 'indexOf', 'lastIndexOf', 'filter', 'forEach', 'every', 'map', 'some', 'reduce', 'reduceRight' @@ -396,7 +409,125 @@ String.extend('uniqueID', function(){ return (UID++).toString(36); }); +//<1.2compat> +var Hash = this.Hash = new Type('Hash', function(object){ + if (typeOf(object) == 'hash') object = Object.clone(object.getClean()); + for (var key in object) this[key] = object[key]; + return this; +}); + +Hash.implement({ + + forEach: function(fn, bind){ + Object.forEach(this, fn, bind); + }, + + getClean: function(){ + var clean = {}; + for (var key in this){ + if (this.hasOwnProperty(key)) clean[key] = this[key]; + } + return clean; + }, + + getLength: function(){ + var length = 0; + for (var key in this){ + if (this.hasOwnProperty(key)) length++; + } + return length; + } + +}); + +Hash.alias('each', 'forEach'); + +Object.type = Type.isObject; + +var Native = this.Native = function(properties){ + return new Type(properties.name, properties.initialize); +}; + +Native.type = Type.type; + +Native.implement = function(objects, methods){ + for (var i = 0; i < objects.length; i++) objects[i].implement(methods); + return Native; +}; + +var arrayType = Array.type; +Array.type = function(item){ + return instanceOf(item, Array) || arrayType(item); +}; + +this.$A = function(item){ + return Array.from(item).slice(); +}; + +this.$arguments = function(i){ + return function(){ + return arguments[i]; + }; +}; + +this.$chk = function(obj){ + return !!(obj || obj === 0); +}; + +this.$clear = function(timer){ + clearTimeout(timer); + clearInterval(timer); + return null; +}; + +this.$defined = function(obj){ + return (obj != null); +}; + +this.$each = function(iterable, fn, bind){ + var type = typeOf(iterable); + ((type == 'arguments' || type == 'collection' || type == 'array' || type == 'elements') ? Array : Object).each(iterable, fn, bind); +}; + +this.$empty = function(){}; + +this.$extend = function(original, extended){ + return Object.append(original, extended); +}; + +this.$H = function(object){ + return new Hash(object); +}; + +this.$merge = function(){ + var args = Array.slice(arguments); + args.unshift({}); + return Object.merge.apply(null, args); +}; + +this.$lambda = Function.from; +this.$mixin = Object.merge; +this.$random = Number.random; +this.$splat = Array.from; +this.$time = Date.now; + +this.$type = function(object){ + var type = typeOf(object); + if (type == 'elements') return 'array'; + return (type == 'null') ? false : type; +}; + +this.$unlink = function(object){ + switch (typeOf(object)){ + case 'object': return Object.clone(object); + case 'array': return Array.clone(object); + case 'hash': return new Hash(object); + default: return object; + } +}; + +// })(); @@ -421,7 +552,7 @@ Array.implement({ /**/ every: function(fn, bind){ - for (var i = 0, l = this.length; i < l; i++){ + for (var i = 0, l = this.length >>> 0; i < l; i++){ if ((i in this) && !fn.call(bind, this[i], i, this)) return false; } return true; @@ -429,30 +560,31 @@ Array.implement({ filter: function(fn, bind){ var results = []; - for (var i = 0, l = this.length; i < l; i++){ - if ((i in this) && fn.call(bind, this[i], i, this)) results.push(this[i]); + for (var value, i = 0, l = this.length >>> 0; i < l; i++) if (i in this){ + value = this[i]; + if (fn.call(bind, value, i, this)) results.push(value); } return results; }, indexOf: function(item, from){ - var len = this.length; - for (var i = (from < 0) ? Math.max(0, len + from) : from || 0; i < len; i++){ + var length = this.length >>> 0; + for (var i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){ if (this[i] === item) return i; } return -1; }, map: function(fn, bind){ - var results = []; - for (var i = 0, l = this.length; i < l; i++){ + var length = this.length >>> 0, results = Array(length); + for (var i = 0; i < length; i++){ if (i in this) results[i] = fn.call(bind, this[i], i, this); } return results; }, some: function(fn, bind){ - for (var i = 0, l = this.length; i < l; i++){ + for (var i = 0, l = this.length >>> 0; i < l; i++){ if ((i in this) && fn.call(bind, this[i], i, this)) return true; } return false; @@ -570,7 +702,15 @@ Array.implement({ }); +//<1.2compat> +Array.alias('extend', 'append'); + +var $pick = function(){ + return Array.from(arguments).pick(); +}; + +// /* @@ -596,37 +736,37 @@ String.implement({ }, contains: function(string, separator){ - return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : this.indexOf(string) > -1; + return (separator) ? (separator + this + separator).indexOf(separator + string + separator) > -1 : String(this).indexOf(string) > -1; }, trim: function(){ - return this.replace(/^\s+|\s+$/g, ''); + return String(this).replace(/^\s+|\s+$/g, ''); }, clean: function(){ - return this.replace(/\s+/g, ' ').trim(); + return String(this).replace(/\s+/g, ' ').trim(); }, camelCase: function(){ - return this.replace(/-\D/g, function(match){ + return String(this).replace(/-\D/g, function(match){ return match.charAt(1).toUpperCase(); }); }, hyphenate: function(){ - return this.replace(/[A-Z]/g, function(match){ + return String(this).replace(/[A-Z]/g, function(match){ return ('-' + match.charAt(0).toLowerCase()); }); }, capitalize: function(){ - return this.replace(/\b[a-z]/g, function(match){ + return String(this).replace(/\b[a-z]/g, function(match){ return match.toUpperCase(); }); }, escapeRegExp: function(){ - return this.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); + return String(this).replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1'); }, toInt: function(base){ @@ -638,17 +778,17 @@ String.implement({ }, hexToRgb: function(array){ - var hex = this.match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); + var hex = String(this).match(/^#?(\w{1,2})(\w{1,2})(\w{1,2})$/); return (hex) ? hex.slice(1).hexToRgb(array) : null; }, rgbToHex: function(array){ - var rgb = this.match(/\d{1,3}/g); + var rgb = String(this).match(/\d{1,3}/g); return (rgb) ? rgb.rgbToHex(array) : null; }, substitute: function(object, regexp){ - return this.replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ + return String(this).replace(regexp || (/\\?\{([^{}]+)\}/g), function(match, name){ if (match.charAt(0) == '\\') return match.slice(1); return (object[name] != null) ? object[name] : ''; }); @@ -746,22 +886,30 @@ Function.implement({ try { return this.apply(bind, Array.from(args)); } catch (e){} - + return null; }, - /**/ - bind: function(bind){ + /**/ + bind: function(that){ var self = this, - args = (arguments.length > 1) ? Array.slice(arguments, 1) : null; - - return function(){ - if (!args && !arguments.length) return self.call(bind); - if (args && arguments.length) return self.apply(bind, args.concat(Array.from(arguments))); - return self.apply(bind, args || arguments); + args = arguments.length > 1 ? Array.slice(arguments, 1) : null, + F = function(){}; + + var bound = function(){ + var context = that, length = arguments.length; + if (this instanceof bound){ + F.prototype = self.prototype; + context = new F; + } + var result = (!args && !length) + ? self.call(context) + : self.apply(context, args && length ? args.concat(Array.slice(arguments)) : args || arguments); + return context == that ? result : context; }; + return bound; }, - /**/ + /**/ pass: function(args, bind){ var self = this; @@ -781,7 +929,56 @@ Function.implement({ }); +//<1.2compat> +delete Function.prototype.bind; + +Function.implement({ + + create: function(options){ + var self = this; + options = options || {}; + return function(event){ + var args = options.arguments; + args = (args != null) ? Array.from(args) : Array.slice(arguments, (options.event) ? 1 : 0); + if (options.event) args = [event || window.event].extend(args); + var returns = function(){ + return self.apply(options.bind || null, args); + }; + if (options.delay) return setTimeout(returns, options.delay); + if (options.periodical) return setInterval(returns, options.periodical); + if (options.attempt) return Function.attempt(returns); + return returns(); + }; + }, + + bind: function(bind, args){ + var self = this; + if (args != null) args = Array.from(args); + return function(){ + return self.apply(bind, args || arguments); + }; + }, + + bindWithEvent: function(bind, args){ + var self = this; + if (args != null) args = Array.from(args); + return function(event){ + return self.apply(bind, (args == null) ? arguments : [event].concat(args)); + }; + }, + + run: function(args, bind){ + return this.apply(bind, Array.from(args)); + } + +}); + +if (Object.create == Function.prototype.create) Object.create = null; + +var $try = Function.attempt; + +// /* @@ -904,7 +1101,95 @@ Object.extend({ })(); +//<1.2compat> +Hash.implement({ + + has: Object.prototype.hasOwnProperty, + + keyOf: function(value){ + return Object.keyOf(this, value); + }, + + hasValue: function(value){ + return Object.contains(this, value); + }, + + extend: function(properties){ + Hash.each(properties || {}, function(value, key){ + Hash.set(this, key, value); + }, this); + return this; + }, + + combine: function(properties){ + Hash.each(properties || {}, function(value, key){ + Hash.include(this, key, value); + }, this); + return this; + }, + + erase: function(key){ + if (this.hasOwnProperty(key)) delete this[key]; + return this; + }, + + get: function(key){ + return (this.hasOwnProperty(key)) ? this[key] : null; + }, + + set: function(key, value){ + if (!this[key] || this.hasOwnProperty(key)) this[key] = value; + return this; + }, + + empty: function(){ + Hash.each(this, function(value, key){ + delete this[key]; + }, this); + return this; + }, + + include: function(key, value){ + if (this[key] == null) this[key] = value; + return this; + }, + + map: function(fn, bind){ + return new Hash(Object.map(this, fn, bind)); + }, + + filter: function(fn, bind){ + return new Hash(Object.filter(this, fn, bind)); + }, + + every: function(fn, bind){ + return Object.every(this, fn, bind); + }, + + some: function(fn, bind){ + return Object.some(this, fn, bind); + }, + + getKeys: function(){ + return Object.keys(this); + }, + + getValues: function(){ + return Object.values(this); + }, + + toQueryString: function(base){ + return Object.toQueryString(this, base); + } + +}); + +Hash.extend = Object.append; + +Hash.alias({indexOf: 'keyOf', contains: 'hasValue'}); + +// /* @@ -928,17 +1213,6 @@ provides: [Browser, Window, Document] var document = this.document; var window = document.window = this; -var UID = 1; - -this.$uid = (window.ActiveXObject) ? function(item){ - return (item.uid || (item.uid = [UID++]))[0]; -} : function(item){ - return item.uid || (item.uid = UID++); -}; - -$uid(window); -$uid(document); - var ua = navigator.userAgent.toLowerCase(), platform = navigator.platform.toLowerCase(), UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0], @@ -1108,7 +1382,67 @@ try { } /**/ +//<1.2compat> +if (Browser.Platform.ios) Browser.Platform.ipod = true; + +Browser.Engine = {}; + +var setEngine = function(name, version){ + Browser.Engine.name = name; + Browser.Engine[name + version] = true; + Browser.Engine.version = version; +}; + +if (Browser.ie){ + Browser.Engine.trident = true; + + switch (Browser.version){ + case 6: setEngine('trident', 4); break; + case 7: setEngine('trident', 5); break; + case 8: setEngine('trident', 6); + } +} + +if (Browser.firefox){ + Browser.Engine.gecko = true; + + if (Browser.version >= 3) setEngine('gecko', 19); + else setEngine('gecko', 18); +} + +if (Browser.safari || Browser.chrome){ + Browser.Engine.webkit = true; + + switch (Browser.version){ + case 2: setEngine('webkit', 419); break; + case 3: setEngine('webkit', 420); break; + case 4: setEngine('webkit', 525); + } +} + +if (Browser.opera){ + Browser.Engine.presto = true; + + if (Browser.version >= 9.6) setEngine('presto', 960); + else if (Browser.version >= 9.5) setEngine('presto', 950); + else setEngine('presto', 925); +} + +if (Browser.name == 'unknown'){ + switch ((ua.match(/(?:webkit|khtml|gecko)/) || [])[0]){ + case 'webkit': + case 'khtml': + Browser.Engine.webkit = true; + break; + case 'gecko': + Browser.Engine.gecko = true; + } +} + +this.$exec = Browser.exec; + +// })(); @@ -1118,7 +1452,7 @@ try { name: Event -description: Contains the Event Class, to make the event object cross-browser. +description: Contains the Event Type, to make the event object cross-browser. license: MIT-style license. @@ -1129,52 +1463,54 @@ provides: Event ... */ -var Event = new Type('Event', function(event, win){ +(function() { + +var _keys = {}; + +var DOMEvent = this.DOMEvent = new Type('DOMEvent', function(event, win){ if (!win) win = window; - var doc = win.document; event = event || win.event; if (event.$extended) return event; + this.event = event; this.$extended = true; - var type = event.type, - target = event.target || event.srcElement, - page = {}, - client = {}, - related = null, - rightClick, wheel, code, key; + this.shift = event.shiftKey; + this.control = event.ctrlKey; + this.alt = event.altKey; + this.meta = event.metaKey; + var type = this.type = event.type; + var target = event.target || event.srcElement; while (target && target.nodeType == 3) target = target.parentNode; + this.target = document.id(target); - if (type.indexOf('key') != -1){ - code = event.which || event.keyCode; - key = Object.keyOf(Event.Keys, code); + if (type.indexOf('key') == 0){ + var code = this.code = (event.which || event.keyCode); + this.key = _keys[code]/*<1.3compat>*/ || Object.keyOf(Event.Keys, code)/**/; if (type == 'keydown'){ - var fKey = code - 111; - if (fKey > 0 && fKey < 13) key = 'f' + fKey; + if (code > 111 && code < 124) this.key = 'f' + (code - 111); + else if (code > 95 && code < 106) this.key = code - 96; } - if (!key) key = String.fromCharCode(code).toLowerCase(); - } else if ((/click|mouse|menu/i).test(type)){ + if (this.key == null) this.key = String.fromCharCode(code).toLowerCase(); + } else if (type == 'click' || type == 'dblclick' || type == 'contextmenu' || type == 'DOMMouseScroll' || type.indexOf('mouse') == 0){ + var doc = win.document; doc = (!doc.compatMode || doc.compatMode == 'CSS1Compat') ? doc.html : doc.body; - page = { + this.page = { x: (event.pageX != null) ? event.pageX : event.clientX + doc.scrollLeft, y: (event.pageY != null) ? event.pageY : event.clientY + doc.scrollTop }; - client = { + this.client = { x: (event.pageX != null) ? event.pageX - win.pageXOffset : event.clientX, y: (event.pageY != null) ? event.pageY - win.pageYOffset : event.clientY }; - if ((/DOMMouseScroll|mousewheel/).test(type)){ - wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3; + if (type == 'DOMMouseScroll' || type == 'mousewheel') + this.wheel = (event.wheelDelta) ? event.wheelDelta / 120 : -(event.detail || 0) / 3; + + this.rightClick = (event.which == 3 || event.button == 2); + if (type == 'mouseover' || type == 'mouseout'){ + var related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element']; + while (related && related.nodeType == 3) related = related.parentNode; + this.relatedTarget = document.id(related); } - rightClick = (event.which == 3) || (event.button == 2); - if ((/over|out/).test(type)){ - related = event.relatedTarget || event[(type == 'mouseover' ? 'from' : 'to') + 'Element']; - var testRelated = function(){ - while (related && related.nodeType == 3) related = related.parentNode; - return true; - }; - var hasRelated = (Browser.firefox2) ? testRelated.attempt() : testRelated(); - related = (hasRelated) ? related : null; - } - } else if ((/gesture|touch/i).test(type)){ + } else if (type.indexOf('touch') == 0 || type.indexOf('gesture') == 0){ this.rotation = event.rotation; this.scale = event.scale; this.targetTouches = event.targetTouches; @@ -1182,53 +1518,19 @@ var Event = new Type('Event', function(event, win){ var touches = this.touches = event.touches; if (touches && touches[0]){ var touch = touches[0]; - page = {x: touch.pageX, y: touch.pageY}; - client = {x: touch.clientX, y: touch.clientY}; + this.page = {x: touch.pageX, y: touch.pageY}; + this.client = {x: touch.clientX, y: touch.clientY}; } } - return Object.append(this, { - event: event, - type: type, - - page: page, - client: client, - rightClick: rightClick, - - wheel: wheel, - - relatedTarget: document.id(related), - target: document.id(target), - - code: code, - key: key, - - shift: event.shiftKey, - control: event.ctrlKey, - alt: event.altKey, - meta: event.metaKey - }); + if (!this.client) this.client = {}; + if (!this.page) this.page = {}; }); -Event.Keys = { - 'enter': 13, - 'up': 38, - 'down': 40, - 'left': 37, - 'right': 39, - 'esc': 27, - 'space': 32, - 'backspace': 8, - 'tab': 9, - 'delete': 46 -}; - - - -Event.implement({ +DOMEvent.implement({ stop: function(){ - return this.stopPropagation().preventDefault(); + return this.preventDefault().stopPropagation(); }, stopPropagation: function(){ @@ -1245,6 +1547,32 @@ Event.implement({ }); +DOMEvent.defineKey = function(code, key){ + _keys[code] = key; + return this; +}; + +DOMEvent.defineKeys = DOMEvent.defineKey.overloadSetter(true); + +DOMEvent.defineKeys({ + '38': 'up', '40': 'down', '37': 'left', '39': 'right', + '27': 'esc', '32': 'space', '8': 'backspace', '9': 'tab', + '46': 'delete', '13': 'enter' +}); + +})(); + +/*<1.3compat>*/ +var Event = DOMEvent; +Event.Keys = {}; +/**/ + +/*<1.2compat>*/ + +Event.Keys = new Hash(Event.Keys); + +/**/ + /* --- @@ -1414,7 +1742,9 @@ this.Events = new Class({ addEvent: function(type, fn, internal){ type = removeOn(type); - + /*<1.2compat>*/ + if (fn == $empty) return this; + /**/ this.$events[type] = (this.$events[type] || []).include(fn); if (internal) fn.internal = true; @@ -1437,7 +1767,7 @@ this.Events = new Class({ }, this); return this; }, - + removeEvent: function(type, fn){ type = removeOn(type); var events = this.$events[type]; @@ -1792,7 +2122,7 @@ local.setDocument = function(document){ var selected, id = 'slick_uniqueid'; var testNode = document.createElement('div'); - + var testRoot = document.body || document.getElementsByTagName('body')[0] || root; testRoot.appendChild(testNode); @@ -1843,7 +2173,7 @@ local.setDocument = function(document){ features.brokenGEBCN = cachedGetElementsByClassName || brokenSecondClassNameGEBCN; } - + if (testNode.querySelectorAll){ // IE 8 returns closed nodes (EG:"") for querySelectorAll('*') for some documents try { @@ -1923,8 +2253,14 @@ local.setDocument = function(document){ // contains // FIXME: Add specs: local.contains should be different for xml and html documents? - features.contains = (root && this.isNativeCode(root.contains)) ? function(context, node){ + var nativeRootContains = root && this.isNativeCode(root.contains), + nativeDocumentContains = document && this.isNativeCode(document.contains); + + features.contains = (nativeRootContains && nativeDocumentContains) ? function(context, node){ return context.contains(node); + } : (nativeRootContains && !nativeDocumentContains) ? function(context, node){ + // IE8 does not have .contains on document. + return context === node || ((context === document) ? document.documentElement : context).contains(node); } : (root && root.compareDocumentPosition) ? function(context, node){ return context === node || !!(context.compareDocumentPosition(node) & 16); } : function(context, node){ @@ -1969,7 +2305,7 @@ var reSimpleSelector = /^([#.]?)((?:[\w-]+|\*))$/, local.search = function(context, expression, append, first){ var found = this.found = (first) ? null : (append || []); - + if (!context) return found; else if (context.navigator) context = context.document; // Convert the node from a window to a document else if (!context.nodeType) return found; @@ -2275,12 +2611,12 @@ local.matchNode = function(node, selector){ return this.nativeMatchesSelector.call(node, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]')); } catch(matchError) {} } - + var parsed = this.Slick.parse(selector); if (!parsed) return true; // simple (single) selectors - var expressions = parsed.expressions, reversedExpressions, simpleExpCounter = 0, i; + var expressions = parsed.expressions, simpleExpCounter = 0, i; for (i = 0; (currentExpression = expressions[i]); i++){ if (currentExpression.length == 1){ var exp = currentExpression[0]; @@ -2319,7 +2655,7 @@ local.matchSelector = function(node, tag, id, classes, attributes, pseudos){ var i, part, cls; if (classes) for (i = classes.length; i--;){ - cls = node.getAttribute('class') || node.className; + cls = this.getAttribute(node, 'class'); if (!(cls && classes[i].regexp.test(cls))) return false; } if (attributes) for (i = attributes.length; i--;){ @@ -2354,7 +2690,7 @@ var combinators = { this.push(item, tag, null, classes, attributes, pseudos); break; } - } + } return; } if (!item){ @@ -2505,7 +2841,7 @@ var pseudos = { 'nth-last-of-type': local.createNTHPseudo('lastChild', 'previousSibling', 'posNTHTypeLast', true), 'index': function(node, index){ - return this['pseudo:nth-child'](node, '' + index + 1); + return this['pseudo:nth-child'](node, '' + (index + 1)); }, 'even': function(node){ @@ -2563,7 +2899,7 @@ var pseudos = { 'root': function(node){ return (node === this.root); }, - + 'selected': function(node){ return node.selected; } @@ -2575,11 +2911,7 @@ for (var p in pseudos) local['pseudo:' + p] = pseudos[p]; // attributes methods -local.attributeGetters = { - - 'class': function(){ - return this.getAttribute('class') || this.className; - }, +var attributeGetters = local.attributeGetters = { 'for': function(){ return ('htmlFor' in this) ? this.htmlFor : this.getAttribute('for'); @@ -2592,7 +2924,7 @@ local.attributeGetters = { 'style': function(){ return (this.style) ? this.style.cssText : this.getAttribute('style'); }, - + 'tabindex': function(){ var attributeNode = this.getAttributeNode('tabindex'); return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null; @@ -2600,15 +2932,22 @@ local.attributeGetters = { 'type': function(){ return this.getAttribute('type'); + }, + + 'maxlength': function(){ + var attributeNode = this.getAttributeNode('maxLength'); + return (attributeNode && attributeNode.specified) ? attributeNode.nodeValue : null; } }; +attributeGetters.MAXLENGTH = attributeGetters.maxLength = attributeGetters.maxlength; + // Slick var Slick = local.Slick = (this.Slick || {}); -Slick.version = '1.1.5'; +Slick.version = '1.1.7'; // Slick finder @@ -2630,9 +2969,15 @@ Slick.contains = function(container, node){ // Slick attribute getter Slick.getAttribute = function(node, name){ + local.setDocument(node); return local.getAttribute(node, name); }; +Slick.hasAttribute = function(node, name){ + local.setDocument(node); + return local.hasAttribute(node, name); +}; + // Slick matcher Slick.match = function(node, selector){ @@ -2697,7 +3042,7 @@ description: One of the most important items in MooTools. Contains the dollar fu license: MIT-style license. -requires: [Window, Document, Array, String, Function, Number, Slick.Parser, Slick.Finder] +requires: [Window, Document, Array, String, Function, Object, Number, Slick.Parser, Slick.Finder] provides: [Element, Elements, $, $$, Iframe, Selectors] @@ -2717,8 +3062,8 @@ var Element = function(tag, props){ if (parsed.id && props.id == null) props.id = parsed.id; var attributes = parsed.attributes; - if (attributes) for (var i = 0, l = attributes.length; i < l; i++){ - var attr = attributes[i]; + if (attributes) for (var attr, i = 0, l = attributes.length; i < l; i++){ + attr = attributes[i]; if (props[attr.key] != null) continue; if (attr.value != null && attr.operator == '=') props[attr.key] = attr.value; @@ -2731,7 +3076,16 @@ var Element = function(tag, props){ return document.newElement(tag, props); }; -if (Browser.Element) Element.prototype = Browser.Element.prototype; + +if (Browser.Element){ + Element.prototype = Browser.Element.prototype; + // IE8 and IE9 require the wrapping. + Element.prototype._fireEvent = (function(fireEvent){ + return function(type, event){ + return fireEvent.call(this, type, event); + }; + })(Element.prototype.fireEvent); +} new Type('Element', Element).mirror(function(name){ if (Array.prototype[name]) return; @@ -2752,7 +3106,10 @@ new Type('Element', Element).mirror(function(name){ if (!Browser.Element){ Element.parent = Object; - Element.Prototype = {'$family': Function.from('element').hide()}; + Element.Prototype = { + '$constructor': Element, + '$family': Function.from('element').hide() + }; Element.mirror(function(name, method){ Element.Prototype[name] = method; @@ -2761,7 +3118,11 @@ if (!Browser.Element){ Element.Constructors = {}; +//<1.2compat> +Element.Constructors = new Hash; + +// var IFrame = new Type('IFrame', function(){ var params = Array.link(arguments, { @@ -2852,7 +3213,11 @@ new Type('Elements', Elements).implement({ }); +//<1.2compat> +Elements.alias('extend', 'append'); + +// (function(){ @@ -2862,21 +3227,22 @@ var splice = Array.prototype.splice, object = {'0': 0, '1': 1, length: 2}; splice.call(object, 1, 1); if (object[1] == 1) Elements.implement('splice', function(){ var length = this.length; - splice.apply(this, arguments); + var result = splice.apply(this, arguments); while (length >= this.length) delete this[length--]; - return this; + return result; }.protect()); -Elements.implement(Array.prototype); +Array.forEachMethod(function(method, name){ + Elements.implement(name, method); +}); Array.mirror(Elements); /**/ var createElementAcceptsHTML; try { - var x = document.createElement(''); - createElementAcceptsHTML = (x.name == 'x'); -} catch(e){} + createElementAcceptsHTML = (document.createElement('').name == 'x'); +} catch (e){} var escapeQuotes = function(html){ return ('' + html).replace(/&/g, '&').replace(/"/g, '"'); @@ -2904,6 +3270,11 @@ Document.implement({ })(); +(function(){ + +Slick.uidOf(window); +Slick.uidOf(document); + Document.implement({ newTextNode: function(text){ @@ -2928,8 +3299,13 @@ Document.implement({ }, element: function(el, nocash){ - $uid(el); + Slick.uidOf(el); if (!nocash && !el.$family && !(/^(?:object|embed)$/i).test(el.tagName)){ + var fireEvent = el.fireEvent; + // wrapping needed in IE7, or else crash + el._fireEvent = function(type, event){ + return fireEvent(type, event); + }; Object.append(el, Element.Prototype); } return el; @@ -2947,7 +3323,7 @@ Document.implement({ }; return function(el, nocash, doc){ - if (el && el.$family && el.uid) return el; + if (el && el.$family && el.uniqueNumber) return el; var type = typeOf(el); return (types[type]) ? types[type](el, nocash, doc || document) : null; }; @@ -2984,7 +3360,131 @@ Window.implement({ }); +var contains = {contains: function(element){ + return Slick.contains(this, element); +}}; +if (!document.contains) Document.implement(contains); +if (!document.createElement('div').contains) Element.implement(contains); + +//<1.2compat> + +Element.implement('hasChild', function(element){ + return this !== element && this.contains(element); +}); + +(function(search, find, match){ + + this.Selectors = {}; + var pseudos = this.Selectors.Pseudo = new Hash(); + + var addSlickPseudos = function(){ + for (var name in pseudos) if (pseudos.hasOwnProperty(name)){ + Slick.definePseudo(name, pseudos[name]); + delete pseudos[name]; + } + }; + + Slick.search = function(context, expression, append){ + addSlickPseudos(); + return search.call(this, context, expression, append); + }; + + Slick.find = function(context, expression){ + addSlickPseudos(); + return find.call(this, context, expression); + }; + + Slick.match = function(node, selector){ + addSlickPseudos(); + return match.call(this, node, selector); + }; + +})(Slick.search, Slick.find, Slick.match); + +// + +// tree walking + +var injectCombinator = function(expression, combinator){ + if (!expression) return combinator; + + expression = Object.clone(Slick.parse(expression)); + + var expressions = expression.expressions; + for (var i = expressions.length; i--;) + expressions[i][0].combinator = combinator; + + return expression; +}; + +Object.forEach({ + getNext: '~', + getPrevious: '!~', + getParent: '!' +}, function(combinator, method){ + Element.implement(method, function(expression){ + return this.getElement(injectCombinator(expression, combinator)); + }); +}); + +Object.forEach({ + getAllNext: '~', + getAllPrevious: '!~', + getSiblings: '~~', + getChildren: '>', + getParents: '!' +}, function(combinator, method){ + Element.implement(method, function(expression){ + return this.getElements(injectCombinator(expression, combinator)); + }); +}); + +Element.implement({ + + getFirst: function(expression){ + return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]); + }, + + getLast: function(expression){ + return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast()); + }, + + getWindow: function(){ + return this.ownerDocument.window; + }, + + getDocument: function(){ + return this.ownerDocument; + }, + + getElementById: function(id){ + return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1'))); + }, + + match: function(expression){ + return !expression || Slick.match(this, expression); + } + +}); + +//<1.2compat> + +if (window.$$ == null) Window.implement('$$', function(selector){ + var elements = new Elements; + if (arguments.length == 1 && typeof selector == 'string') return Slick.search(this.document, selector, elements); + var args = Array.flatten(arguments); + for (var i = 0, l = args.length; i < l; i++){ + var item = args[i]; + switch (typeOf(item)){ + case 'element': elements.push(item); break; + case 'string': Slick.search(this.document, item, elements); + } + } + return elements; +}); + +// if (window.$$ == null) Window.implement('$$', function(selector){ if (arguments.length == 1){ @@ -2994,50 +3494,7 @@ if (window.$$ == null) Window.implement('$$', function(selector){ return new Elements(arguments); }); -(function(){ - -var collected = {}, storage = {}; -var formProps = {input: 'checked', option: 'selected', textarea: 'value'}; - -var get = function(uid){ - return (storage[uid] || (storage[uid] = {})); -}; - -var clean = function(item){ - var uid = item.uid; - if (item.removeEvents) item.removeEvents(); - if (item.clearAttributes) item.clearAttributes(); - if (uid != null){ - delete collected[uid]; - delete storage[uid]; - } - return item; -}; - -var camels = ['defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', 'frameBorder', 'maxLength', 'readOnly', - 'rowSpan', 'tabIndex', 'useMap' -]; -var bools = ['compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', 'disabled', 'readOnly', 'multiple', 'selected', - 'noresize', 'defer', 'defaultChecked' -]; - var attributes = { - 'html': 'innerHTML', - 'class': 'className', - 'for': 'htmlFor', - 'text': (function(){ - var temp = document.createElement('div'); - return (temp.textContent == null) ? 'innerText' : 'textContent'; - })() -}; -var readOnly = ['type']; -var expandos = ['value', 'defaultValue']; -var uriAttrs = /^(?:href|src|usemap)$/i; - -bools = bools.associate(bools); -camels = camels.associate(camels.map(String.toLowerCase)); -readOnly = readOnly.associate(readOnly); - -Object.append(attributes, expandos.associate(expandos)); +// Inserters var inserters = { @@ -3063,22 +3520,199 @@ var inserters = { inserters.inside = inserters.bottom; +//<1.2compat> +Object.each(inserters, function(inserter, where){ -var injectCombinator = function(expression, combinator){ - if (!expression) return combinator; + where = where.capitalize(); - expression = Object.clone(Slick.parse(expression)); + var methods = {}; - var expressions = expression.expressions; - for (var i = expressions.length; i--;) - expressions[i][0].combinator = combinator; + methods['inject' + where] = function(el){ + inserter(this, document.id(el, true)); + return this; + }; - return expression; + methods['grab' + where] = function(el){ + inserter(document.id(el, true), this); + return this; + }; + + Element.implement(methods); + +}); + +// + +// getProperty / setProperty + +var propertyGetters = {}, propertySetters = {}; + +// properties + +var properties = {}; +Array.forEach([ + 'type', 'value', 'defaultValue', 'accessKey', 'cellPadding', 'cellSpacing', 'colSpan', + 'frameBorder', 'rowSpan', 'tabIndex', 'useMap' +], function(property){ + properties[property.toLowerCase()] = property; +}); + +properties.html = 'innerHTML'; +properties.text = (document.createElement('div').textContent == null) ? 'innerText': 'textContent'; + +Object.forEach(properties, function(real, key){ + propertySetters[key] = function(node, value){ + node[real] = value; + }; + propertyGetters[key] = function(node){ + return node[real]; + }; +}); + +// Booleans + +var bools = [ + 'compact', 'nowrap', 'ismap', 'declare', 'noshade', 'checked', + 'disabled', 'readOnly', 'multiple', 'selected', 'noresize', + 'defer', 'defaultChecked', 'autofocus', 'controls', 'autoplay', + 'loop' +]; + +var booleans = {}; +Array.forEach(bools, function(bool){ + var lower = bool.toLowerCase(); + booleans[lower] = bool; + propertySetters[lower] = function(node, value){ + node[bool] = !!value; + }; + propertyGetters[lower] = function(node){ + return !!node[bool]; + }; +}); + +// Special cases + +Object.append(propertySetters, { + + 'class': function(node, value){ + ('className' in node) ? node.className = (value || '') : node.setAttribute('class', value); + }, + + 'for': function(node, value){ + ('htmlFor' in node) ? node.htmlFor = value : node.setAttribute('for', value); + }, + + 'style': function(node, value){ + (node.style) ? node.style.cssText = value : node.setAttribute('style', value); + }, + + 'value': function(node, value){ + node.value = (value != null) ? value : ''; + } + +}); + +propertyGetters['class'] = function(node){ + return ('className' in node) ? node.className || null : node.getAttribute('class'); }; +/* */ +var el = document.createElement('button'); +// IE sets type as readonly and throws +try { el.type = 'button'; } catch(e){} +if (el.type != 'button') propertySetters.type = function(node, value){ + node.setAttribute('type', value); +}; +el = null; +/* */ + +/**/ +var input = document.createElement('input'); +input.value = 't'; +input.type = 'submit'; +if (input.value != 't') propertySetters.type = function(node, type){ + var value = node.value; + node.type = type; + node.value = value; +}; +input = null; +/**/ + +/* getProperty, setProperty */ + +/* */ +var pollutesGetAttribute = (function(div){ + div.random = 'attribute'; + return (div.getAttribute('random') == 'attribute'); +})(document.createElement('div')); + +/* */ + Element.implement({ + setProperty: function(name, value){ + var setter = propertySetters[name.toLowerCase()]; + if (setter){ + setter(this, value); + } else { + /* */ + if (pollutesGetAttribute) var attributeWhiteList = this.retrieve('$attributeWhiteList', {}); + /* */ + + if (value == null){ + this.removeAttribute(name); + /* */ + if (pollutesGetAttribute) delete attributeWhiteList[name]; + /* */ + } else { + this.setAttribute(name, '' + value); + /* */ + if (pollutesGetAttribute) attributeWhiteList[name] = true; + /* */ + } + } + return this; + }, + + setProperties: function(attributes){ + for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]); + return this; + }, + + getProperty: function(name){ + var getter = propertyGetters[name.toLowerCase()]; + if (getter) return getter(this); + /* */ + if (pollutesGetAttribute){ + var attr = this.getAttributeNode(name), attributeWhiteList = this.retrieve('$attributeWhiteList', {}); + if (!attr) return null; + if (attr.expando && !attributeWhiteList[name]){ + var outer = this.outerHTML; + // segment by the opening tag and find mention of attribute name + if (outer.substr(0, outer.search(/\/?['"]?>(?![^<]*<['"])/)).indexOf(name) < 0) return null; + attributeWhiteList[name] = true; + } + } + /* */ + var result = Slick.getAttribute(this, name); + return (!result && !Slick.hasAttribute(this, name)) ? null : result; + }, + + getProperties: function(){ + var args = Array.from(arguments); + return args.map(this.getProperty, this).associate(args); + }, + + removeProperty: function(name){ + return this.setProperty(name, null); + }, + + removeProperties: function(){ + Array.each(arguments, this.removeProperty, this); + return this; + }, + set: function(prop, value){ var property = Element.Properties[prop]; (property && property.set) ? property.set.call(this, value) : this.setProperty(prop, value); @@ -3095,47 +3729,6 @@ Element.implement({ return this; }, - setProperty: function(attribute, value){ - attribute = camels[attribute] || attribute; - if (value == null) return this.removeProperty(attribute); - var key = attributes[attribute]; - (key) ? this[key] = value : - (bools[attribute]) ? this[attribute] = !!value : this.setAttribute(attribute, '' + value); - return this; - }, - - setProperties: function(attributes){ - for (var attribute in attributes) this.setProperty(attribute, attributes[attribute]); - return this; - }, - - getProperty: function(attribute){ - attribute = camels[attribute] || attribute; - var key = attributes[attribute] || readOnly[attribute]; - return (key) ? this[key] : - (bools[attribute]) ? !!this[attribute] : - (uriAttrs.test(attribute) ? this.getAttribute(attribute, 2) : - (key = this.getAttributeNode(attribute)) ? key.nodeValue : null) || null; - }, - - getProperties: function(){ - var args = Array.from(arguments); - return args.map(this.getProperty, this).associate(args); - }, - - removeProperty: function(attribute){ - attribute = camels[attribute] || attribute; - var key = attributes[attribute]; - (key) ? this[key] = '' : - (bools[attribute]) ? this[attribute] = false : this.removeAttribute(attribute); - return this; - }, - - removeProperties: function(){ - Array.each(arguments, this.removeProperty, this); - return this; - }, - hasClass: function(className){ return this.className.clean().contains(className, ' '); }, @@ -3194,58 +3787,6 @@ Element.implement({ return this.replaces(el).grab(el, where); }, - getPrevious: function(expression){ - return document.id(Slick.find(this, injectCombinator(expression, '!~'))); - }, - - getAllPrevious: function(expression){ - return Slick.search(this, injectCombinator(expression, '!~'), new Elements); - }, - - getNext: function(expression){ - return document.id(Slick.find(this, injectCombinator(expression, '~'))); - }, - - getAllNext: function(expression){ - return Slick.search(this, injectCombinator(expression, '~'), new Elements); - }, - - getFirst: function(expression){ - return document.id(Slick.search(this, injectCombinator(expression, '>'))[0]); - }, - - getLast: function(expression){ - return document.id(Slick.search(this, injectCombinator(expression, '>')).getLast()); - }, - - getParent: function(expression){ - return document.id(Slick.find(this, injectCombinator(expression, '!'))); - }, - - getParents: function(expression){ - return Slick.search(this, injectCombinator(expression, '!'), new Elements); - }, - - getSiblings: function(expression){ - return Slick.search(this, injectCombinator(expression, '~~'), new Elements); - }, - - getChildren: function(expression){ - return Slick.search(this, injectCombinator(expression, '>'), new Elements); - }, - - getWindow: function(){ - return this.ownerDocument.window; - }, - - getDocument: function(){ - return this.ownerDocument; - }, - - getElementById: function(id){ - return document.id(Slick.find(this, '#' + ('' + id).replace(/(\W)/g, '\\$1'))); - }, - getSelected: function(){ this.selectedIndex; // Safari 3.2.1 return new Elements(Array.from(this.options).filter(function(option){ @@ -3269,7 +3810,30 @@ Element.implement({ }); }); return queryString.join('&'); - }, + } + +}); + +var collected = {}, storage = {}; + +var get = function(uid){ + return (storage[uid] || (storage[uid] = {})); +}; + +var clean = function(item){ + var uid = item.uniqueNumber; + if (item.removeEvents) item.removeEvents(); + if (item.clearAttributes) item.clearAttributes(); + if (uid != null){ + delete collected[uid]; + delete storage[uid]; + } + return item; +}; + +var formProps = {input: 'checked', option: 'selected', textarea: 'value'}; + +Element.implement({ destroy: function(){ var children = clean(this).getElementsByTagName('*'); @@ -3287,55 +3851,44 @@ Element.implement({ return (this.parentNode) ? this.parentNode.removeChild(this) : this; }, - match: function(expression){ - return !expression || Slick.match(this, expression); - } + clone: function(contents, keepid){ + contents = contents !== false; + var clone = this.cloneNode(contents), ce = [clone], te = [this], i; -}); - -var cleanClone = function(node, element, keepid){ - if (!keepid) node.setAttributeNode(document.createAttribute('id')); - if (node.clearAttributes){ - node.clearAttributes(); - node.mergeAttributes(element); - node.removeAttribute('uid'); - if (node.options){ - var no = node.options, eo = element.options; - for (var i = no.length; i--;) no[i].selected = eo[i].selected; + if (contents){ + ce.append(Array.from(clone.getElementsByTagName('*'))); + te.append(Array.from(this.getElementsByTagName('*'))); } + + for (i = ce.length; i--;){ + var node = ce[i], element = te[i]; + if (!keepid) node.removeAttribute('id'); + /**/ + if (node.clearAttributes){ + node.clearAttributes(); + node.mergeAttributes(element); + node.removeAttribute('uniqueNumber'); + if (node.options){ + var no = node.options, eo = element.options; + for (var j = no.length; j--;) no[j].selected = eo[j].selected; + } + } + /**/ + var prop = formProps[element.tagName.toLowerCase()]; + if (prop && element[prop]) node[prop] = element[prop]; + } + + /**/ + if (Browser.ie){ + var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object'); + for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML; + } + /**/ + return document.id(clone); } - var prop = formProps[element.tagName.toLowerCase()]; - if (prop && element[prop]) node[prop] = element[prop]; -}; - -Element.implement('clone', function(contents, keepid){ - contents = contents !== false; - var clone = this.cloneNode(contents), i; - - if (contents){ - var ce = clone.getElementsByTagName('*'), te = this.getElementsByTagName('*'); - for (i = ce.length; i--;) cleanClone(ce[i], te[i], keepid); - } - - cleanClone(clone, this, keepid); - - if (Browser.ie){ - var co = clone.getElementsByTagName('object'), to = this.getElementsByTagName('object'); - for (i = co.length; i--;) co[i].outerHTML = to[i].outerHTML; - } - return document.id(clone); }); -var contains = {contains: function(element){ - return Slick.contains(this, element); -}}; - -if (!document.contains) Document.implement(contains); -if (!document.createElement('div').contains) Element.implement(contains); - - - [Element, Window, Document].invoke('implement', { addListener: function(type, fn){ @@ -3346,7 +3899,7 @@ if (!document.createElement('div').contains) Element.implement(contains); old(); }; } else { - collected[$uid(this)] = this; + collected[Slick.uidOf(this)] = this; } if (this.addEventListener) this.addEventListener(type, fn, !!arguments[2]); else this.attachEvent('on' + type, fn); @@ -3360,19 +3913,19 @@ if (!document.createElement('div').contains) Element.implement(contains); }, retrieve: function(property, dflt){ - var storage = get($uid(this)), prop = storage[property]; + var storage = get(Slick.uidOf(this)), prop = storage[property]; if (dflt != null && prop == null) prop = storage[property] = dflt; return prop != null ? prop : null; }, store: function(property, value){ - var storage = get($uid(this)); + var storage = get(Slick.uidOf(this)); storage[property] = value; return this; }, eliminate: function(property){ - var storage = get($uid(this)); + var storage = get(Slick.uidOf(this)); delete storage[property]; return this; } @@ -3386,11 +3939,13 @@ if (window.attachEvent && !window.addEventListener) window.addListener('unload', }); /**/ -})(); - Element.Properties = {}; +//<1.2compat> +Element.Properties = new Hash; + +// Element.Properties.style = { @@ -3416,55 +3971,126 @@ Element.Properties.tag = { }; +Element.Properties.html = { + + set: function(html){ + if (html == null) html = ''; + else if (typeOf(html) == 'array') html = html.join(''); + this.innerHTML = html; + }, + + erase: function(){ + this.innerHTML = ''; + } + +}; + /**/ -(function(maxLength){ - if (maxLength != null) Element.Properties.maxlength = Element.Properties.maxLength = { - get: function(){ - var maxlength = this.getAttribute('maxLength'); - return maxlength == maxLength ? null : maxlength; - } - }; -})(document.createElement('input').getAttribute('maxLength')); +// technique by jdbarlett - http://jdbartlett.com/innershiv/ +var div = document.createElement('div'); +div.innerHTML = ''; +var supportsHTML5Elements = (div.childNodes.length == 1); +if (!supportsHTML5Elements){ + var tags = 'abbr article aside audio canvas datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video'.split(' '), + fragment = document.createDocumentFragment(), l = tags.length; + while (l--) fragment.createElement(tags[l]); +} +div = null; /**/ -/**/ -Element.Properties.html = (function(){ +/**/ +var supportsTableInnerHTML = Function.attempt(function(){ + var table = document.createElement('table'); + table.innerHTML = ''; + return true; +}); - var tableTest = Function.attempt(function(){ - var table = document.createElement('table'); - table.innerHTML = ''; - }); +/**/ +var tr = document.createElement('tr'), html = ''; +tr.innerHTML = html; +var supportsTRInnerHTML = (tr.innerHTML == html); +tr = null; +/**/ - var wrapper = document.createElement('div'); +if (!supportsTableInnerHTML || !supportsTRInnerHTML || !supportsHTML5Elements){ - var translations = { - table: [1, '', '
'], - select: [1, ''], - tbody: [2, '', '
'], - tr: [3, '', '
'] - }; - translations.thead = translations.tfoot = translations.tbody; + Element.Properties.html.set = (function(set){ - var html = { - set: function(){ - var html = Array.flatten(arguments).join(''); - var wrap = (!tableTest && translations[this.get('tag')]); - if (wrap){ - var first = wrapper; - first.innerHTML = wrap[1] + html + wrap[2]; - for (var i = wrap[0]; i--;) first = first.firstChild; - this.empty().adopt(first.childNodes); - } else { - this.innerHTML = html; - } + var translations = { + table: [1, '', '
'], + select: [1, ''], + tbody: [2, '', '
'], + tr: [3, '', '
'] + }; + + translations.thead = translations.tfoot = translations.tbody; + + return function(html){ + var wrap = translations[this.get('tag')]; + if (!wrap && !supportsHTML5Elements) wrap = [0, '', '']; + if (!wrap) return set.call(this, html); + + var level = wrap[0], wrapper = document.createElement('div'), target = wrapper; + if (!supportsHTML5Elements) fragment.appendChild(wrapper); + wrapper.innerHTML = [wrap[1], html, wrap[2]].flatten().join(''); + while (level--) target = target.firstChild; + this.empty().adopt(target.childNodes); + if (!supportsHTML5Elements) fragment.removeChild(wrapper); + wrapper = null; + }; + + })(Element.Properties.html.set); +} +/*
*/ + +/**/ +var testForm = document.createElement('form'); +testForm.innerHTML = ''; + +if (testForm.firstChild.value != 's') Element.Properties.value = { + + set: function(value){ + var tag = this.get('tag'); + if (tag != 'select') return this.setProperty('value', value); + var options = this.getElements('option'); + for (var i = 0; i < options.length; i++){ + var option = options[i], + attr = option.getAttributeNode('value'), + optionValue = (attr && attr.specified) ? option.value : option.get('text'); + if (optionValue == value) return option.selected = true; } - }; + }, - html.erase = html.set; + get: function(){ + var option = this, tag = option.get('tag'); + + if (tag != 'select' && tag != 'option') return this.getProperty('value'); + + if (tag == 'select' && !(option = option.getSelected()[0])) return ''; + + var attr = option.getAttributeNode('value'); + return (attr && attr.specified) ? option.value : option.get('text'); + } + +}; +testForm = null; +/**/ + +/**/ +if (document.createElement('div').getAttributeNode('id')) Element.Properties.id = { + set: function(id){ + this.id = this.getAttributeNode('id').value = id; + }, + get: function(){ + return this.id || null; + }, + erase: function(){ + this.id = this.getAttributeNode('id').value = ''; + } +}; +/**/ - return html; })(); -/**/ /* @@ -3487,45 +4113,53 @@ provides: Element.Style var html = document.html; +// +// Check for oldIE, which does not remove styles when they're set to null +var el = document.createElement('div'); +el.style.color = 'red'; +el.style.color = null; +var doesNotRemoveStyles = el.style.color == 'red'; +el = null; +// + Element.Properties.styles = {set: function(styles){ this.setStyles(styles); }}; -var hasOpacity = (html.style.opacity != null); -var reAlpha = /alpha\(opacity=([\d.]+)\)/i; +var hasOpacity = (html.style.opacity != null), + hasFilter = (html.style.filter != null), + reAlpha = /alpha\(opacity=([\d.]+)\)/i; -var setOpacity = function(element, opacity){ - if (!element.currentStyle || !element.currentStyle.hasLayout) element.style.zoom = 1; - if (hasOpacity){ - element.style.opacity = opacity; - } else { - opacity = (opacity * 100).limit(0, 100).round(); - opacity = (opacity == 100) ? '' : 'alpha(opacity=' + opacity + ')'; - var filter = element.style.filter || element.getComputedStyle('filter') || ''; - element.style.filter = reAlpha.test(filter) ? filter.replace(reAlpha, opacity) : filter + opacity; - } +var setVisibility = function(element, opacity){ + element.store('$opacity', opacity); + element.style.visibility = opacity > 0 || opacity == null ? 'visible' : 'hidden'; }; -Element.Properties.opacity = { +var setOpacity = (hasOpacity ? function(element, opacity){ + element.style.opacity = opacity; +} : (hasFilter ? function(element, opacity){ + var style = element.style; + if (!element.currentStyle || !element.currentStyle.hasLayout) style.zoom = 1; + if (opacity == null || opacity == 1) opacity = ''; + else opacity = 'alpha(opacity=' + (opacity * 100).limit(0, 100).round() + ')'; + var filter = style.filter || element.getComputedStyle('filter') || ''; + style.filter = reAlpha.test(filter) ? filter.replace(reAlpha, opacity) : filter + opacity; + if (!style.filter) style.removeAttribute('filter'); +} : setVisibility)); - set: function(opacity){ - var visibility = this.style.visibility; - if (opacity == 0 && visibility != 'hidden') this.style.visibility = 'hidden'; - else if (opacity != 0 && visibility != 'visible') this.style.visibility = 'visible'; - - setOpacity(this, opacity); - }, - - get: (hasOpacity) ? function(){ - var opacity = this.style.opacity || this.getComputedStyle('opacity'); - return (opacity == '') ? 1 : opacity; - } : function(){ - var opacity, filter = (this.style.filter || this.getComputedStyle('filter')); - if (filter) opacity = filter.match(reAlpha); - return (opacity == null || filter == null) ? 1 : (opacity[1] / 100); - } - -}; +var getOpacity = (hasOpacity ? function(element){ + var opacity = element.style.opacity || element.getComputedStyle('opacity'); + return (opacity == '') ? 1 : opacity.toFloat(); +} : (hasFilter ? function(element){ + var filter = (element.style.filter || element.getComputedStyle('filter')), + opacity; + if (filter) opacity = filter.match(reAlpha); + return (opacity == null || filter == null) ? 1 : (opacity[1] / 100); +} : function(element){ + var opacity = element.retrieve('$opacity'); + if (opacity == null) opacity = (element.style.visibility == 'hidden' ? 0 : 1); + return opacity; +})); var floatName = (html.style.cssFloat == null) ? 'styleFloat' : 'cssFloat'; @@ -3538,21 +4172,13 @@ Element.implement({ return (computed) ? computed.getPropertyValue((property == floatName) ? 'float' : property.hyphenate()) : null; }, - setOpacity: function(value){ - setOpacity(this, value); - return this; - }, - - getOpacity: function(){ - return this.get('opacity'); - }, - setStyle: function(property, value){ - switch (property){ - case 'opacity': return this.set('opacity', parseFloat(value)); - case 'float': property = floatName; + if (property == 'opacity'){ + if (value != null) value = parseFloat(value); + setOpacity(this, value); + return this; } - property = property.camelCase(); + property = (property == 'float' ? floatName : property).camelCase(); if (typeOf(value) != 'string'){ var map = (Element.Styles[property] || '@').split(' '); value = Array.from(value).map(function(val, i){ @@ -3563,15 +4189,17 @@ Element.implement({ value = Math.round(value); } this.style[property] = value; + // + if ((value == '' || value == null) && doesNotRemoveStyles && this.style.removeAttribute){ + this.style.removeAttribute(property); + } + // return this; }, getStyle: function(property){ - switch (property){ - case 'opacity': return this.get('opacity'); - case 'float': property = floatName; - } - property = property.camelCase(); + if (property == 'opacity') return getOpacity(this); + property = (property == 'float' ? floatName : property).camelCase(); var result = this.style[property]; if (!result || property == 'zIndex'){ result = []; @@ -3587,16 +4215,17 @@ Element.implement({ var color = result.match(/rgba?\([\d\s,]+\)/); if (color) result = result.replace(color[0], color[0].rgbToHex()); } - if (Browser.opera || (Browser.ie && isNaN(parseFloat(result)))){ - if ((/^(height|width)$/).test(property)){ + if (Browser.opera || Browser.ie){ + if ((/^(height|width)$/).test(property) && !(/px$/.test(result))){ var values = (property == 'width') ? ['left', 'right'] : ['top', 'bottom'], size = 0; values.each(function(value){ size += this.getStyle('border-' + value + '-width').toInt() + this.getStyle('padding-' + value).toInt(); }, this); return this['offset' + property.capitalize()] - size + 'px'; } - if (Browser.opera && String(result).indexOf('px') != -1) return result; - if ((/^border(.+)Width|margin|padding/).test(property)) return '0px'; + if (Browser.ie && (/^border(.+)Width|margin|padding/).test(property) && isNaN(parseFloat(result))){ + return '0px'; + } } return result; }, @@ -3626,7 +4255,41 @@ Element.Styles = { zIndex: '@', 'zoom': '@', fontWeight: '@', textIndent: '@px', opacity: '@' }; +//<1.3compat> +Element.implement({ + + setOpacity: function(value){ + setOpacity(this, value); + return this; + }, + + getOpacity: function(){ + return getOpacity(this); + } + +}); + +Element.Properties.opacity = { + + set: function(opacity){ + setOpacity(this, opacity); + setVisibility(this, opacity); + }, + + get: function(){ + return getOpacity(this); + } + +}; + +// + +//<1.2compat> + +Element.Styles = new Hash(Element.Styles); + +// Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, borderStyle: {}, borderColor: {}}; @@ -3654,7 +4317,7 @@ Element.ShortStyles = {margin: {}, padding: {}, border: {}, borderWidth: {}, bor name: Element.Event -description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events. +description: Contains Element methods for dealing with events. This file also includes mouseenter and mouseleave custom Element Events, if necessary. license: MIT-style license. @@ -3683,14 +4346,14 @@ Element.Properties.events = {set: function(events){ condition = fn, self = this; if (custom){ - if (custom.onAdd) custom.onAdd.call(this, fn); + if (custom.onAdd) custom.onAdd.call(this, fn, type); if (custom.condition){ condition = function(event){ - if (custom.condition.call(this, event)) return fn.call(this, event); + if (custom.condition.call(this, event, type)) return fn.call(this, event); return true; }; } - realType = custom.base || realType; + if (custom.base) realType = Function.from(custom.base).call(this, type); } var defn = function(){ return fn.call(self); @@ -3699,7 +4362,7 @@ Element.Properties.events = {set: function(events){ if (nativeEvent){ if (nativeEvent == 2){ defn = function(event){ - event = new Event(event, self.getWindow()); + event = new DOMEvent(event, self.getWindow()); if (condition.call(self, event) === false) event.stop(); }; } @@ -3720,8 +4383,8 @@ Element.Properties.events = {set: function(events){ delete list.values[index]; var custom = Element.Events[type]; if (custom){ - if (custom.onRemove) custom.onRemove.call(this, fn); - type = custom.base || type; + if (custom.onRemove) custom.onRemove.call(this, fn, type); + if (custom.base) type = Function.from(custom.base).call(this, type); } return (Element.NativeEvents[type]) ? this.removeListener(type, value, arguments[2]) : this; }, @@ -3787,37 +4450,257 @@ Element.NativeEvents = { orientationchange: 2, // mobile touchstart: 2, touchmove: 2, touchend: 2, touchcancel: 2, // touch gesturestart: 2, gesturechange: 2, gestureend: 2, // gesture - focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, //form elements + focus: 2, blur: 2, change: 2, reset: 2, select: 2, submit: 2, paste: 2, input: 2, //form elements load: 2, unload: 1, beforeunload: 2, resize: 1, move: 1, DOMContentLoaded: 1, readystatechange: 1, //window error: 1, abort: 1, scroll: 1 //misc }; -var check = function(event){ - var related = event.relatedTarget; - if (related == null) return true; - if (!related) return false; - return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related)); -}; +Element.Events = {mousewheel: { + base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel' +}}; -Element.Events = { +if ('onmouseenter' in document.documentElement){ + Element.NativeEvents.mouseenter = Element.NativeEvents.mouseleave = 2; +} else { + var check = function(event){ + var related = event.relatedTarget; + if (related == null) return true; + if (!related) return false; + return (related != this && related.prefix != 'xul' && typeOf(this) != 'document' && !this.contains(related)); + }; - mouseenter: { + Element.Events.mouseenter = { base: 'mouseover', condition: check - }, + }; - mouseleave: { + Element.Events.mouseleave = { base: 'mouseout', condition: check + }; +} + +/**/ +if (!window.addEventListener){ + Element.NativeEvents.propertychange = 2; + Element.Events.change = { + base: function(){ + var type = this.type; + return (this.get('tag') == 'input' && (type == 'radio' || type == 'checkbox')) ? 'propertychange' : 'change' + }, + condition: function(event){ + return this.type != 'radio' || (event.event.propertyName == 'checked' && this.checked); + } + } +} +/**/ + +//<1.2compat> + +Element.Events = new Hash(Element.Events); + +// + +})(); + + +/* +--- + +name: Element.Delegation + +description: Extends the Element native object to include the delegate method for more efficient event management. + +license: MIT-style license. + +requires: [Element.Event] + +provides: [Element.Delegation] + +... +*/ + +(function(){ + +var eventListenerSupport = !!window.addEventListener; + +Element.NativeEvents.focusin = Element.NativeEvents.focusout = 2; + +var bubbleUp = function(self, match, fn, event, target){ + while (target && target != self){ + if (match(target, event)) return fn.call(target, event, target); + target = document.id(target.parentNode); + } +}; + +var map = { + mouseenter: { + base: 'mouseover' + }, + mouseleave: { + base: 'mouseout' + }, + focus: { + base: 'focus' + (eventListenerSupport ? '' : 'in'), + capture: true + }, + blur: { + base: eventListenerSupport ? 'blur' : 'focusout', + capture: true + } +}; + +/**/ +var _key = '$delegation:'; +var formObserver = function(type){ + + return { + + base: 'focusin', + + remove: function(self, uid){ + var list = self.retrieve(_key + type + 'listeners', {})[uid]; + if (list && list.forms) for (var i = list.forms.length; i--;){ + list.forms[i].removeEvent(type, list.fns[i]); + } + }, + + listen: function(self, match, fn, event, target, uid){ + var form = (target.get('tag') == 'form') ? target : event.target.getParent('form'); + if (!form) return; + + var listeners = self.retrieve(_key + type + 'listeners', {}), + listener = listeners[uid] || {forms: [], fns: []}, + forms = listener.forms, fns = listener.fns; + + if (forms.indexOf(form) != -1) return; + forms.push(form); + + var _fn = function(event){ + bubbleUp(self, match, fn, event, target); + }; + form.addEvent(type, _fn); + fns.push(_fn); + + listeners[uid] = listener; + self.store(_key + type + 'listeners', listeners); + } + }; +}; + +var inputObserver = function(type){ + return { + base: 'focusin', + listen: function(self, match, fn, event, target){ + var events = {blur: function(){ + this.removeEvents(events); + }}; + events[type] = function(event){ + bubbleUp(self, match, fn, event, target); + }; + event.target.addEvents(events); + } + }; +}; + +if (!eventListenerSupport) Object.append(map, { + submit: formObserver('submit'), + reset: formObserver('reset'), + change: inputObserver('change'), + select: inputObserver('select') +}); +/**/ + +var proto = Element.prototype, + addEvent = proto.addEvent, + removeEvent = proto.removeEvent; + +var relay = function(old, method){ + return function(type, fn, useCapture){ + if (type.indexOf(':relay') == -1) return old.call(this, type, fn, useCapture); + var parsed = Slick.parse(type).expressions[0][0]; + if (parsed.pseudos[0].key != 'relay') return old.call(this, type, fn, useCapture); + var newType = parsed.tag; + parsed.pseudos.slice(1).each(function(pseudo){ + newType += ':' + pseudo.key + (pseudo.value ? '(' + pseudo.value + ')' : ''); + }); + old.call(this, type, fn); + return method.call(this, newType, parsed.pseudos[0].value, fn); + }; +}; + +var delegation = { + + addEvent: function(type, match, fn){ + var storage = this.retrieve('$delegates', {}), stored = storage[type]; + if (stored) for (var _uid in stored){ + if (stored[_uid].fn == fn && stored[_uid].match == match) return this; + } + + var _type = type, _match = match, _fn = fn, _map = map[type] || {}; + type = _map.base || _type; + + match = function(target){ + return Slick.match(target, _match); + }; + + var elementEvent = Element.Events[_type]; + if (elementEvent && elementEvent.condition){ + var __match = match, condition = elementEvent.condition; + match = function(target, event){ + return __match(target, event) && condition.call(target, event, type); + }; + } + + var self = this, uid = String.uniqueID(); + var delegator = _map.listen ? function(event, target){ + if (!target && event && event.target) target = event.target; + if (target) _map.listen(self, match, fn, event, target, uid); + } : function(event, target){ + if (!target && event && event.target) target = event.target; + if (target) bubbleUp(self, match, fn, event, target); + }; + + if (!stored) stored = {}; + stored[uid] = { + match: _match, + fn: _fn, + delegator: delegator + }; + storage[_type] = stored; + return addEvent.call(this, type, delegator, _map.capture); }, - mousewheel: { - base: (Browser.firefox) ? 'DOMMouseScroll' : 'mousewheel' + removeEvent: function(type, match, fn, _uid){ + var storage = this.retrieve('$delegates', {}), stored = storage[type]; + if (!stored) return this; + + if (_uid){ + var _type = type, delegator = stored[_uid].delegator, _map = map[type] || {}; + type = _map.base || _type; + if (_map.remove) _map.remove(this, _uid); + delete stored[_uid]; + storage[_type] = stored; + return removeEvent.call(this, type, delegator); + } + + var __uid, s; + if (fn) for (__uid in stored){ + s = stored[__uid]; + if (s.match == match && s.fn == fn) return delegation.removeEvent.call(this, type, match, fn, __uid); + } else for (__uid in stored){ + s = stored[__uid]; + if (s.match == match) delegation.removeEvent.call(this, type, match, s.fn, __uid); + } + return this; } }; - +[Element, Window, Document].invoke('implement', { + addEvent: relay(addEvent, delegation.addEvent), + removeEvent: relay(removeEvent, delegation.removeEvent) +}); })(); @@ -3961,14 +4844,13 @@ Element.implement({ }, getPosition: function(relative){ - if (isBody(this)) return {x: 0, y: 0}; var offset = this.getOffsets(), scroll = this.getScrolls(); var position = { x: offset.x - scroll.x, y: offset.y - scroll.y }; - + if (relative && (relative = document.id(relative))){ var relativePosition = relative.getPosition(); return {x: position.x - relativePosition.x - leftBorder(relative), y: position.y - relativePosition.y - topBorder(relative)}; @@ -4162,7 +5044,7 @@ var Fx = this.Fx = new Class({ } else { this.frame++; } - + if (this.frame < this.frames){ var delta = this.transition(this.frame / this.frames); this.set(this.compute(this.from, this.to, delta)); @@ -4205,7 +5087,7 @@ var Fx = this.Fx = new Class({ pushInstance.call(this, fps); return this; }, - + stop: function(){ if (this.isRunning()){ this.time = null; @@ -4219,7 +5101,7 @@ var Fx = this.Fx = new Class({ } return this; }, - + cancel: function(){ if (this.isRunning()){ this.time = null; @@ -4229,7 +5111,7 @@ var Fx = this.Fx = new Class({ } return this; }, - + pause: function(){ if (this.isRunning()){ this.time = null; @@ -4237,12 +5119,12 @@ var Fx = this.Fx = new Class({ } return this; }, - + resume: function(){ if ((this.frame < this.frames) && !this.isRunning()) pushInstance.call(this, this.options.fps); return this; }, - + isRunning: function(){ var list = instances[this.options.fps]; return list && list.contains(this); @@ -4312,12 +5194,31 @@ Fx.CSS = new Class({ prepare: function(element, property, values){ values = Array.from(values); - if (values[1] == null){ - values[1] = values[0]; - values[0] = element.getStyle(property); + var from = values[0], to = values[1]; + if (to == null){ + to = from; + from = element.getStyle(property); + var unit = this.options.unit; + // adapted from: https://github.com/ryanmorr/fx/blob/master/fx.js#L299 + if (unit && from.slice(-unit.length) != unit && parseFloat(from) != 0){ + element.setStyle(property, to + unit); + var value = element.getComputedStyle(property); + // IE and Opera support pixelLeft or pixelWidth + if (!(/px$/.test(value))){ + value = element.style[('pixel-' + property).camelCase()]; + if (value == null){ + // adapted from Dean Edwards' http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + var left = element.style.left; + element.style.left = to + unit; + value = element.style.pixelLeft; + element.style.left = left; + } + } + from = (to || 1) / (parseFloat(value) || 1) * (parseFloat(from) || 0); + element.setStyle(property, from + unit); + } } - var parsed = values.map(this.parse); - return {from: parsed[0], to: parsed[1]}; + return {from: this.parse(from), to: this.parse(to)}; }, //parses a value into an array @@ -4432,7 +5333,11 @@ Fx.CSS.Parsers = { }; +//<1.2compat> +Fx.CSS.Parsers = new Hash(Fx.CSS.Parsers); + +// /* @@ -4500,27 +5405,35 @@ Element.Properties.tween = { Element.implement({ tween: function(property, from, to){ - this.get('tween').start(arguments); + this.get('tween').start(property, from, to); return this; }, fade: function(how){ - var fade = this.get('tween'), o = 'opacity', toggle; - how = [how, 'toggle'].pick(); - switch (how){ - case 'in': fade.start(o, 1); break; - case 'out': fade.start(o, 0); break; - case 'show': fade.set(o, 1); break; - case 'hide': fade.set(o, 0); break; + var fade = this.get('tween'), method, args = ['opacity'].append(arguments), toggle; + if (args[1] == null) args[1] = 'toggle'; + switch (args[1]){ + case 'in': method = 'start'; args[1] = 1; break; + case 'out': method = 'start'; args[1] = 0; break; + case 'show': method = 'set'; args[1] = 1; break; + case 'hide': method = 'set'; args[1] = 0; break; case 'toggle': - var flag = this.retrieve('fade:flag', this.get('opacity') == 1); - fade.start(o, (flag) ? 0 : 1); + var flag = this.retrieve('fade:flag', this.getStyle('opacity') == 1); + method = 'start'; + args[1] = flag ? 0 : 1; this.store('fade:flag', !flag); toggle = true; break; - default: fade.start(o, arguments); + default: method = 'start'; } if (!toggle) this.eliminate('fade:flag'); + fade[method].apply(fade, args); + var to = args[args.length - 1]; + if (method == 'set' || to != 0) this.setStyle('visibility', to == 0 ? 'hidden' : 'visible'); + else fade.chain(function(){ + this.element.setStyle('visibility', 'hidden'); + this.callChain(); + }); return this; }, @@ -4677,7 +5590,11 @@ Fx.Transitions = { }; +//<1.2compat> +Fx.Transitions = new Hash(Fx.Transitions); + +// Fx.Transitions.extend = function(transitions){ for (var transition in transitions) Fx.Transitions[transition] = new Fx.Transition(transitions[transition]); @@ -4805,7 +5722,7 @@ var Request = this.Request = new Class({ xhr.onreadystatechange = empty; if (progressSupport) xhr.onprogress = xhr.onloadstart = empty; clearTimeout(this.timer); - + this.response = {text: this.xhr.responseText || '', xml: this.xhr.responseXML}; if (this.options.isSuccess.call(this, this.status)) this.success(this.response.text, this.response.xml); @@ -4842,15 +5759,15 @@ var Request = this.Request = new Class({ onFailure: function(){ this.fireEvent('complete').fireEvent('failure', this.xhr); }, - + loadstart: function(event){ this.fireEvent('loadstart', [event, this.xhr]); }, - + progress: function(event){ this.fireEvent('progress', [event, this.xhr]); }, - + timeout: function(){ this.fireEvent('timeout', this.xhr); }, @@ -4874,7 +5791,7 @@ var Request = this.Request = new Class({ } return false; }, - + send: function(options){ if (!this.check(options)) return this; @@ -4910,7 +5827,7 @@ var Request = this.Request = new Class({ } if (!url) url = document.location.pathname; - + var trimPosition = url.lastIndexOf('/'); if (trimPosition > -1 && (trimPosition = url.indexOf('#')) > -1) url = url.substr(0, trimPosition); @@ -4930,7 +5847,7 @@ var Request = this.Request = new Class({ xhr.open(method.toUpperCase(), url, this.options.async, this.options.user, this.options.password); if (this.options.user && 'withCredentials' in xhr) xhr.withCredentials = true; - + xhr.onreadystatechange = this.onStateChange.bind(this); Object.each(this.headers, function(value, key){ @@ -4944,7 +5861,7 @@ var Request = this.Request = new Class({ this.fireEvent('request'); xhr.send(data); if (!this.options.async) this.onStateChange(); - if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this); + else if (this.options.timeout) this.timer = this.timeout.delay(this.options.timeout, this); return this; }, @@ -5009,6 +5926,7 @@ Element.implement({ })(); + /* --- @@ -5051,11 +5969,18 @@ Request.HTML = new Class({ var temp = new Element('div').set('html', response.html); response.tree = temp.childNodes; - response.elements = temp.getElements('*'); + response.elements = temp.getElements(options.filter || '*'); - if (options.filter) response.tree = response.elements.filter(options.filter); - if (options.update) document.id(options.update).empty().set('html', response.html); - else if (options.append) document.id(options.append).adopt(temp.getChildren()); + if (options.filter) response.tree = response.elements; + if (options.update){ + var update = document.id(options.update).empty(); + if (options.filter) update.adopt(response.elements); + else update.set('html', response.html); + } else if (options.append){ + var append = document.id(options.append); + if (options.filter) response.elements.reverse().inject(append); + else append.adopt(temp.getChildren()); + } if (options.evalScripts) Browser.exec(response.javascript); this.onSuccess(response.tree, response.elements, response.html, response.javascript); @@ -5101,7 +6026,7 @@ description: JSON encoder and decoder. license: MIT-style license. -See Also: +SeeAlso: requires: [Array, String, Number, Function] @@ -5112,7 +6037,14 @@ provides: JSON if (typeof JSON == 'undefined') this.JSON = {}; +//<1.2compat> +JSON = new Hash({ + stringify: JSON.stringify, + parse: JSON.parse +}); + +// (function(){ @@ -5322,7 +6254,7 @@ var domready = function(){ if (ready) return; Browser.loaded = ready = true; document.removeListener('DOMContentLoaded', domready).removeListener('readystatechange', check); - + document.fireEvent('domready'); window.fireEvent('domready'); }; @@ -5351,7 +6283,7 @@ var doScrollWorks = function(){ return true; } catch (e){} return false; -} +}; // If doScroll works already, it can't be used to determine domready // e.g. in an iframe if (testElement.doScroll && !doScrollWorks()){ diff --git a/web/tools/mootools/mootools-more-1.3.2.1-yc.js b/web/tools/mootools/mootools-more-1.3.2.1-yc.js deleted file mode 100644 index 807e27ecf..000000000 --- a/web/tools/mootools/mootools-more-1.3.2.1-yc.js +++ /dev/null @@ -1,742 +0,0 @@ -// MooTools: the javascript framework. -// Load this file's selection again by visiting: http://mootools.net/more/09f3e47813269cd5026cbf8c1f828e72 -// Or build this file again with packager using: packager build More/Chain.Wait More/Array.Extras More/Date More/Date.Extras More/Number.Format More/String.Extras More/String.QueryString More/URI More/URI.Relative More/Hash More/Hash.Extras More/Element.Forms More/Elements.From More/Element.Event.Pseudos.Keys More/Element.Pin More/Element.Position More/Element.Shortcuts More/Form.Request More/Form.Request.Append More/Form.Validator.Inline More/Form.Validator.Extras More/OverText More/Fx.Accordion More/Fx.Move More/Fx.Reveal More/Fx.Slide More/Fx.SmoothScroll More/Fx.Sort More/Drag.Move More/Slider More/Sortables More/Request.JSONP More/Request.Queue More/Request.Periodical More/Assets More/Color More/Group More/Hash.Cookie More/Table More/HtmlTable.Zebra More/HtmlTable.Sort More/HtmlTable.Select More/Keyboard.Extras More/Mask More/Scroller More/Tips More/Locale.en-GB.Date -/* ---- -copyrights: - - [MooTools](http://mootools.net) - -licenses: - - [MIT License](http://mootools.net/license.txt) -... -*/ -MooTools.More={version:"1.3.2.1",build:"e586bcd2496e9b22acfde32e12f84d49ce09e59d"};(function(){var a={wait:function(b){return this.chain(function(){this.callChain.delay(b==null?500:b,this); -return this;}.bind(this));}};Chain.implement(a);if(this.Fx){Fx.implement(a);}if(this.Element&&Element.implement&&this.Fx){Element.implement({chains:function(b){Array.from(b||["tween","morph","reveal"]).each(function(c){c=this.get(c); -if(!c){return;}c.setOptions({link:"chain"});},this);return this;},pauseFx:function(c,b){this.chains(b).get(b||"tween").wait(c);return this;}});}})();(function(a){Array.implement({min:function(){return Math.min.apply(null,this); -},max:function(){return Math.max.apply(null,this);},average:function(){return this.length?this.sum()/this.length:0;},sum:function(){var b=0,c=this.length; -if(c){while(c--){b+=this[c];}}return b;},unique:function(){return[].combine(this);},shuffle:function(){for(var c=this.length;c&&--c;){var b=this[c],d=Math.floor(Math.random()*(c+1)); -this[c]=this[d];this[d]=b;}return this;},reduce:function(d,e){for(var c=0,b=this.length;c3&&a<21)?"th":["th","st","nd","rd","th"][Math.min(a%10,4)]; -},lessThanMinuteAgo:"less than a minute ago",minuteAgo:"about a minute ago",minutesAgo:"{delta} minutes ago",hourAgo:"about an hour ago",hoursAgo:"about {delta} hours ago",dayAgo:"1 day ago",daysAgo:"{delta} days ago",weekAgo:"1 week ago",weeksAgo:"{delta} weeks ago",monthAgo:"1 month ago",monthsAgo:"{delta} months ago",yearAgo:"1 year ago",yearsAgo:"{delta} years ago",lessThanMinuteUntil:"less than a minute from now",minuteUntil:"about a minute from now",minutesUntil:"{delta} minutes from now",hourUntil:"about an hour from now",hoursUntil:"about {delta} hours from now",dayUntil:"1 day from now",daysUntil:"{delta} days from now",weekUntil:"1 week from now",weeksUntil:"{delta} weeks from now",monthUntil:"1 month from now",monthsUntil:"{delta} months from now",yearUntil:"1 year from now",yearsUntil:"{delta} years from now"}); -(function(){var a=this.Date;var f=a.Methods={ms:"Milliseconds",year:"FullYear",min:"Minutes",mo:"Month",sec:"Seconds",hr:"Hours"};["Date","Day","FullYear","Hours","Milliseconds","Minutes","Month","Seconds","Time","TimezoneOffset","Week","Timezone","GMTOffset","DayOfYear","LastMonth","LastDayOfMonth","UTCDate","UTCDay","UTCFullYear","AMPM","Ordinal","UTCHours","UTCMilliseconds","UTCMinutes","UTCMonth","UTCSeconds","UTCMilliseconds"].each(function(t){a.Methods[t.toLowerCase()]=t; -});var p=function(v,u,t){if(u==1){return v;}return v28){return 1;}if(z==0&&t<-2){y=new a(y).decrement("day",v); -v=0;}x=new a(y.get("year"),0,1).get("day")||7;if(x>4){u=-7;}}else{x=new a(y.get("year"),0,1).get("day");}u+=y.get("dayofyear");u+=6-v;u+=(7+x-w)%7;return(u/7); -},getOrdinal:function(t){return a.getMsg("ordinal",t||this.get("date"));},getTimezone:function(){return this.toString().replace(/^.*? ([A-Z]{3}).[0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3"); -},getGMTOffset:function(){var t=this.get("timezoneOffset");return((t>0)?"-":"+")+p((t.abs()/60).floor(),2)+p(t%60,2);},setAMPM:function(t){t=t.toUpperCase(); -var u=this.get("hr");if(u>11&&t=="AM"){return this.decrement("hour",12);}else{if(u<12&&t=="PM"){return this.increment("hour",12);}}return this;},getAMPM:function(){return(this.get("hr")<12)?"AM":"PM"; -},parse:function(t){this.set("time",a.parse(t));return this;},isValid:function(t){return !isNaN((t||this).valueOf());},format:function(u){if(!this.isValid()){return"invalid date"; -}if(!u){u="%x %X";}var t=u.toLowerCase();if(s[t]){return s[t](this);}u=g[t]||u;var v=this;return u.replace(/%([a-z%])/gi,function(x,w){switch(w){case"a":return a.getMsg("days_abbr")[v.get("day")]; -case"A":return a.getMsg("days")[v.get("day")];case"b":return a.getMsg("months_abbr")[v.get("month")];case"B":return a.getMsg("months")[v.get("month")]; -case"c":return v.format("%a %b %d %H:%M:%S %Y");case"d":return p(v.get("date"),2);case"e":return p(v.get("date"),2," ");case"H":return p(v.get("hr"),2); -case"I":return p((v.get("hr")%12)||12,2);case"j":return p(v.get("dayofyear"),3);case"k":return p(v.get("hr"),2," ");case"l":return p((v.get("hr")%12)||12,2," "); -case"L":return p(v.get("ms"),3);case"m":return p((v.get("mo")+1),2);case"M":return p(v.get("min"),2);case"o":return v.get("ordinal");case"p":return a.getMsg(v.get("ampm")); -case"s":return Math.round(v/1000);case"S":return p(v.get("seconds"),2);case"T":return v.format("%H:%M:%S");case"U":return p(v.get("week"),2);case"w":return v.get("day"); -case"x":return v.format(a.getMsg("shortDate"));case"X":return v.format(a.getMsg("shortTime"));case"y":return v.get("year").toString().substr(2);case"Y":return v.get("year"); -case"z":return v.get("GMTOffset");case"Z":return v.get("Timezone");}return w;});},toISOString:function(){return this.format("iso8601");}}).alias({toJSON:"toISOString",compare:"diff",strftime:"format"}); -var g={db:"%Y-%m-%d %H:%M:%S",compact:"%Y%m%dT%H%M%S","short":"%d %b %H:%M","long":"%B %d, %Y %H:%M"};var k=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],h=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; -var s={rfc822:function(t){return k[t.get("day")]+t.format(", %d ")+h[t.get("month")]+t.format(" %Y %H:%M:%S %Z");},rfc2822:function(t){return k[t.get("day")]+t.format(", %d ")+h[t.get("month")]+t.format(" %Y %H:%M:%S %z"); -},iso8601:function(t){return(t.getUTCFullYear()+"-"+p(t.getUTCMonth()+1,2)+"-"+p(t.getUTCDate(),2)+"T"+p(t.getUTCHours(),2)+":"+p(t.getUTCMinutes(),2)+":"+p(t.getUTCSeconds(),2)+"."+p(t.getUTCMilliseconds(),3)+"Z"); -}};var c=[],n=a.parse;var r=function(w,y,v){var u=-1,x=a.getMsg(w+"s");switch(typeOf(y)){case"object":u=x[y.get(w)];break;case"number":u=x[y];if(!u){throw new Error("Invalid "+w+" index: "+y); -}break;case"string":var t=x.filter(function(z){return this.test(z);},new RegExp("^"+y,"i"));if(!t.length){throw new Error("Invalid "+w+" string");}if(t.length>1){throw new Error("Ambiguous "+w); -}u=t[0];}return(v)?x.indexOf(u):u;};var i=1900,o=70;a.extend({getMsg:function(u,t){return Locale.get("Date."+u,t);},units:{ms:Function.from(1),second:Function.from(1000),minute:Function.from(60000),hour:Function.from(3600000),day:Function.from(86400000),week:Function.from(608400000),month:function(u,t){var v=new a; -return a.daysInMonth(u!=null?u:v.get("mo"),t!=null?t:v.get("year"))*86400000;},year:function(t){t=t||new a().get("year");return a.isLeapYear(t)?31622400000:31536000000; -}},daysInMonth:function(u,t){return[31,a.isLeapYear(t)?29:28,31,30,31,30,31,31,30,31,30,31][u];},isLeapYear:function(t){return((t%4===0)&&(t%100!==0))||(t%400===0); -},parse:function(w){var v=typeOf(w);if(v=="number"){return new a(w);}if(v!="string"){return w;}w=w.clean();if(!w.length){return null;}var u;c.some(function(x){var t=x.re.exec(w); -return(t)?(u=x.handler(t)):false;});if(!(u&&u.isValid())){u=new a(n(w));if(!(u&&u.isValid())){u=new a(w.toInt());}}return u;},parseDay:function(t,u){return r("day",t,u); -},parseMonth:function(u,t){return r("month",u,t);},parseUTC:function(u){var t=new a(u);var v=a.UTC(t.get("year"),t.get("mo"),t.get("date"),t.get("hr"),t.get("min"),t.get("sec"),t.get("ms")); -return new a(v);},orderIndex:function(t){return a.getMsg("dateOrder").indexOf(t)+1;},defineFormat:function(t,u){g[t]=u;return this;},defineFormats:function(t){for(var u in t){a.defineFormat(u,t[u]); -}return this;},defineParser:function(t){c.push((t.re&&t.handler)?t:l(t));return this;},defineParsers:function(){Array.flatten(arguments).each(a.defineParser); -return this;},define2DigitYearStart:function(t){o=t%100;i=t-o;return this;}});var d=function(t){return new RegExp("(?:"+a.getMsg(t).map(function(u){return u.substr(0,3); -}).join("|")+")[a-z]*");};var m=function(t){switch(t){case"T":return"%H:%M:%S";case"x":return((a.orderIndex("month")==1)?"%m[-./]%d":"%d[-./]%m")+"([-./]%y)?"; -case"X":return"%H([.:]%M)?([.:]%S([.:]%s)?)? ?%p? ?%z?";}return null;};var j={d:/[0-2]?[0-9]|3[01]/,H:/[01]?[0-9]|2[0-3]/,I:/0?[1-9]|1[0-2]/,M:/[0-5]?\d/,s:/\d+/,o:/[a-z]*/,p:/[ap]\.?m\.?/,y:/\d{2}|\d{4}/,Y:/\d{4}/,z:/Z|[+-]\d{2}(?::?\d{2})?/}; -j.m=j.I;j.S=j.M;var e;var b=function(t){e=t;j.a=j.A=d("days");j.b=j.B=d("months");c.each(function(v,u){if(v.format){c[u]=l(v.format);}});};var l=function(v){if(!e){return{format:v}; -}var t=[];var u=(v.source||v).replace(/%([a-z])/gi,function(x,w){return m(w)||x;}).replace(/\((?!\?)/g,"(?:").replace(/ (?!\?|\*)/g,",? ").replace(/%([a-z%])/gi,function(x,w){var y=j[w]; -if(!y){return w;}t.push(w);return"("+y.source+")";}).replace(/\[a-z\]/gi,"[a-z\\u00c0-\\uffff;&]");return{format:v,re:new RegExp("^"+u+"$","i"),handler:function(z){z=z.slice(1).associate(t); -var w=new a().clearTime(),y=z.y||z.Y;if(y!=null){q.call(w,"y",y);}if("d" in z){q.call(w,"d",1);}if("m" in z||z.b||z.B){q.call(w,"m",1);}for(var x in z){q.call(w,x,z[x]); -}return w;}};};var q=function(t,u){if(!u){return this;}switch(t){case"a":case"A":return this.set("day",a.parseDay(u,true));case"b":case"B":return this.set("mo",a.parseMonth(u,true)); -case"d":return this.set("date",u);case"H":case"I":return this.set("hr",u);case"m":return this.set("mo",u-1);case"M":return this.set("min",u);case"p":return this.set("ampm",u.replace(/\./g,"")); -case"S":return this.set("sec",u);case"s":return this.set("ms",("0."+u)*1000);case"w":return this.set("day",u);case"Y":return this.set("year",u);case"y":u=+u; -if(u<100){u+=i+(u0.75*a){e=c;}break;}f/=a;e=c+"s";}f=f.round();return Date.getMsg(e+d,f).substitute({delta:f});}}).defineParsers({re:/^(?:tod|tom|yes)/i,handler:function(a){var b=new Date().clearTime(); -switch(a[0]){case"tom":return b.increment();case"yes":return b.decrement();default:return b;}}},{re:/^(next|last) ([a-z]+)$/i,handler:function(e){var f=new Date().clearTime(); -var b=f.getDay();var c=Date.parseDay(e[2],true);var a=c-b;if(c<=b){a+=7;}if(e[1]=="last"){a-=7;}return f.set("date",f.getDate()+a);}}).alias("timeAgoInWords","timeDiffInWords"); -Locale.define("en-US","Number",{decimal:".",group:",",currency:{prefix:"$ "}});Number.implement({format:function(q){var n=this;q=q?Object.clone(q):{};var a=function(i){if(q[i]!=null){return q[i]; -}return Locale.get("Number."+i);};var f=n<0,h=a("decimal"),k=a("precision"),o=a("group"),c=a("decimals");if(f){var e=a("negative")||{};if(e.prefix==null&&e.suffix==null){e.prefix="-"; -}["prefix","suffix"].each(function(i){if(e[i]){q[i]=a(i)+e[i];}});n=-n;}var l=a("prefix"),p=a("suffix");if(c!==""&&c>=0&&c<=20){n=n.toFixed(c);}if(k>=1&&k<=21){n=(+n).toPrecision(k); -}n+="";var m;if(a("scientific")===false&&n.indexOf("e")>-1){var j=n.split("e"),b=+j[1];n=j[0].replace(".","");if(b<0){b=-b-1;m=j[0].indexOf(".");if(m>-1){b-=m-1; -}while(b--){n="0"+n;}n="0."+n;}else{m=j[0].lastIndexOf(".");if(m>-1){b-=j[0].length-m-1;}while(b--){n+="0";}}}if(h!="."){n=n.replace(".",h);}if(o){m=n.lastIndexOf(h); -m=(m>-1)?m:n.length;var d=n.substring(m),g=m;while(g--){if((m-g-1)%3==0&&g!=(m-1)){d=o+d;}d=n.charAt(g)+d;}n=d;}if(l){n=l+n;}if(p){n+=p;}return n;},formatCurrency:function(){var a=Locale.get("Number.currency")||{}; -if(a.scientific==null){a.scientific=false;}if(a.decimals==null){a.decimals=2;}return this.format(a);},formatPercentage:function(){var a=Locale.get("Number.percentage")||{}; -if(a.suffix==null){a.suffix="%";}if(a.decimals==null){a.decimals=2;}return this.format(a);}});(function(){var c={a:/[àáâãäåăą]/g,A:/[ÀÁÂÃÄÅĂĄ]/g,c:/[ćčç]/g,C:/[ĆČÇ]/g,d:/[ďđ]/g,D:/[ĎÐ]/g,e:/[èéêëěę]/g,E:/[ÈÉÊËĚĘ]/g,g:/[ğ]/g,G:/[Ğ]/g,i:/[ìíîï]/g,I:/[ÌÍÎÏ]/g,l:/[ĺľł]/g,L:/[ĹĽŁ]/g,n:/[ñňń]/g,N:/[ÑŇŃ]/g,o:/[òóôõöøő]/g,O:/[ÒÓÔÕÖØ]/g,r:/[řŕ]/g,R:/[ŘŔ]/g,s:/[ššş]/g,S:/[ŠŞŚ]/g,t:/[ťţ]/g,T:/[ŤŢ]/g,ue:/[ü]/g,UE:/[Ü]/g,u:/[ùúûůµ]/g,U:/[ÙÚÛŮ]/g,y:/[ÿý]/g,Y:/[ŸÝ]/g,z:/[žźż]/g,Z:/[ŽŹŻ]/g,th:/[þ]/g,TH:/[Þ]/g,dh:/[ð]/g,DH:/[Ð]/g,ss:/[ß]/g,oe:/[œ]/g,OE:/[Œ]/g,ae:/[æ]/g,AE:/[Æ]/g},b={" ":/[\xa0\u2002\u2003\u2009]/g,"*":/[\xb7]/g,"'":/[\u2018\u2019]/g,'"':/[\u201c\u201d]/g,"...":/[\u2026]/g,"-":/[\u2013]/g,"»":/[\uFFFD]/g}; -var a=function(f,h){var e=f,g;for(g in h){e=e.replace(h[g],g);}return e;};var d=function(e,g){e=e||"";var h=g?"<"+e+"(?!\\w)[^>]*>([\\s\\S]*?)":"]+)?>",f=new RegExp(h,"gi"); -return f;};String.implement({standardize:function(){return a(this,c);},repeat:function(e){return new Array(e+1).join(this);},pad:function(e,h,g){if(this.length>=e){return this; -}var f=(h==null?" ":""+h).repeat(e-this.length).substr(0,e-this.length);if(!g||g=="right"){return this+f;}if(g=="left"){return f+this;}return f.substr(0,(f.length/2).floor())+this+f.substr(0,(f.length/2).ceil()); -},getTags:function(e,f){return this.match(d(e,f))||[];},stripTags:function(e,f){return this.replace(d(e,f),"");},tidy:function(){return a(this,b);},truncate:function(e,f,i){var h=this; -if(f==null&&arguments.length==1){f="…";}if(h.length>e){h=h.substring(0,e);if(i){var g=h.lastIndexOf(i);if(g!=-1){h=h.substr(0,g);}}if(f){h+=f;}}return h; -}});})();String.implement({parseQueryString:function(d,a){if(d==null){d=true;}if(a==null){a=true;}var c=this.split(/[&;]/),b={};if(!c.length){return b; -}c.each(function(i){var e=i.indexOf("=")+1,g=e?i.substr(e):"",f=e?i.substr(0,e-1).match(/([^\]\[]+|(\B)(?=\]))/g):[i],h=b;if(!f){return;}if(a){g=decodeURIComponent(g); -}f.each(function(k,j){if(d){k=decodeURIComponent(k);}var l=h[k];if(j0){c.pop(); -}else{if(f!="."){c.push(f);}}});return c.join("/")+"/";},combine:function(c){return c.value||c.scheme+"://"+(c.user?c.user+(c.password?":"+c.password:"")+"@":"")+(c.host||"")+(c.port&&c.port!=this.schemes[c.scheme]?":"+c.port:"")+(c.directory||"/")+(c.file||"")+(c.query?"?"+c.query:"")+(c.fragment?"#"+c.fragment:""); -},set:function(d,f,e){if(d=="value"){var c=f.match(a.regs.scheme);if(c){c=c[1];}if(c&&this.schemes[c.toLowerCase()]==null){this.parsed={scheme:c,value:f}; -}else{this.parsed=this.parse(f,(e||this).parsed)||(c?{scheme:c,value:f}:{value:f});}}else{if(d=="data"){this.setData(f);}else{this.parsed[d]=f;}}return this; -},get:function(c,d){switch(c){case"value":return this.combine(this.parsed,d?d.parsed:false);case"data":return this.getData();}return this.parsed[c]||""; -},go:function(){document.location.href=this.toString();},toURI:function(){return this;},getData:function(e,d){var c=this.get(d||"query");if(!(c||c===0)){return e?null:{}; -}var f=c.parseQueryString();return e?f[e]:f;},setData:function(c,f,d){if(typeof c=="string"){var e=this.getData();e[arguments[0]]=arguments[1];c=e;}else{if(f){c=Object.merge(this.getData(),c); -}}return this.set(d||"query",Object.toQueryString(c));},clearData:function(c){return this.set(c||"query","");},toString:b,valueOf:b});a.regs={endSlash:/\/$/,scheme:/^(\w+):/,directoryDot:/\.\/|\.$/}; -a.base=new a(Array.from(document.getElements("base[href]",true)).getLast(),{base:document.location});String.implement({toURI:function(c){return new a(this,c); -}});})();Class.refactor=function(b,a){Object.each(a,function(e,d){var c=b.prototype[d];c=(c&&c.$origin)||c||function(){};b.implement(d,(typeof e=="function")?function(){var f=this.previous; -this.previous=c;var g=e.apply(this,arguments);this.previous=f;return g;}:e);});return b;};URI=Class.refactor(URI,{combine:function(f,e){if(!e||f.scheme!=e.scheme||f.host!=e.host||f.port!=e.port){return this.previous.apply(this,arguments); -}var a=f.file+(f.query?"?"+f.query:"")+(f.fragment?"#"+f.fragment:"");if(!e.directory){return(f.directory||(f.file?"":"./"))+a;}var d=e.directory.split("/"),c=f.directory.split("/"),g="",h; -var b=0;for(h=0;h=0||g.parentPositioned||d.allowNegative)?c.x:0).toInt(); -c.top=((c.y>=0||g.parentPositioned||d.allowNegative)?c.y:0).toInt();a.toMinMax(c,d);if(d.relFixedPosition||f.getStyle("position")=="fixed"){a.toRelFixedPosition(f,c); -}if(d.ignoreScroll){a.toIgnoreScroll(f,c);}if(d.ignoreMargins){a.toIgnoreMargins(c,d);}c.left=Math.ceil(c.left);c.top=Math.ceil(c.top);delete c.x;delete c.y; -return c;},setPositionCoordinates:function(k,g,d){var f=k.offset.y,h=k.offset.x,e=(d==document.body)?window.getScroll():d.getPosition(),j=e.y,c=e.x,i=window.getSize(); -switch(k.position.x){case"left":g.x=c+h;break;case"right":g.x=c+h+d.offsetWidth;break;default:g.x=c+((d==document.body?i.x:d.offsetWidth)/2)+h;break;}switch(k.position.y){case"top":g.y=j+f; -break;case"bottom":g.y=j+f+d.offsetHeight;break;default:g.y=j+((d==document.body?i.y:d.offsetHeight)/2)+f;break;}},toMinMax:function(c,d){var f={left:"x",top:"y"},e; -["minimum","maximum"].each(function(g){["left","top"].each(function(h){e=d[g]?d[g][f[h]]:null;if(e!=null&&((g=="minimum")?c[h]e)){c[h]=e;}});}); -},toRelFixedPosition:function(e,c){var d=window.getScroll();c.top+=d.y;c.left+=d.x;},toIgnoreScroll:function(e,d){var c=e.getScroll();d.top-=c.y;d.left-=c.x; -},toIgnoreMargins:function(c,d){c.left+=d.edge.x=="right"?d.dimensions["margin-right"]:(d.edge.x!="center"?-d.dimensions["margin-left"]:-d.dimensions["margin-left"]+((d.dimensions["margin-right"]+d.dimensions["margin-left"])/2)); -c.top+=d.edge.y=="bottom"?d.dimensions["margin-bottom"]:(d.edge.y!="center"?-d.dimensions["margin-top"]:-d.dimensions["margin-top"]+((d.dimensions["margin-bottom"]+d.dimensions["margin-top"])/2)); -},toEdge:function(c,d){var e={},g=d.dimensions,f=d.edge;switch(f.x){case"left":e.x=0;break;case"right":e.x=-g.x-g.computedRight-g.computedLeft;break;default:e.x=-(Math.round(g.totalWidth/2)); -break;}switch(f.y){case"top":e.y=0;break;case"bottom":e.y=-g.y-g.computedTop-g.computedBottom;break;default:e.y=-(Math.round(g.totalHeight/2));break;}c.x+=e.x; -c.y+=e.y;},getCoordinateFromValue:function(c){if(typeOf(c)!="string"){return c;}c=c.toLowerCase();return{x:c.test("left")?"left":(c.test("right")?"right":"center"),y:c.test(/upper|top/)?"top":(c.test("bottom")?"bottom":"center")}; -}};Element.implement({position:function(d){if(d&&(d.x!=null||d.y!=null)){return(b?b.apply(this,arguments):this);}var c=this.setStyle("position","absolute").calculatePosition(d); -return(d&&d.returnPos)?c:this.setStyles(c);},calculatePosition:function(c){return a.getPosition(this,c);}});})(Element.prototype.position);Element.implement({isDisplayed:function(){return this.getStyle("display")!="none"; -},isVisible:function(){var a=this.offsetWidth,b=this.offsetHeight;return(a==0&&b==0)?false:(a>0&&b>0)?true:this.style.display!="none";},toggle:function(){return this[this.isDisplayed()?"hide":"show"](); -},hide:function(){var b;try{b=this.getStyle("display");}catch(a){}if(b=="none"){return this;}return this.store("element:_originalDisplay",b||"").setStyle("display","none"); -},show:function(a){if(!a&&this.isDisplayed()){return this;}a=a||this.retrieve("element:_originalDisplay")||"block";return this.setStyle("display",(a=="none")?"block":a); -},swapClass:function(a,b){return this.removeClass(a).addClass(b);}});Document.implement({clearSelection:function(){if(window.getSelection){var a=window.getSelection(); -if(a&&a.removeAllRanges){a.removeAllRanges();}}else{if(document.selection&&document.selection.empty){try{document.selection.empty();}catch(b){}}}}});Class.Mutators.Binds=function(a){if(!this.prototype.initialize){this.implement("initialize",function(){}); -}return Array.from(a).concat(this.prototype.Binds||[]);};Class.Mutators.initialize=function(a){return function(){Array.from(this.Binds).each(function(b){var c=this[b]; -if(c){this[b]=c.bind(this);}},this);return a.apply(this,arguments);};};Class.Occlude=new Class({occlude:function(c,b){b=document.id(b||this.element);var a=b.retrieve(c||this.property); -if(a&&!this.occluded){return(this.occluded=a);}this.occluded=false;b.store(c||this.property,this);return this.occluded;}});var IframeShim=new Class({Implements:[Options,Events,Class.Occlude],options:{className:"iframeShim",src:'javascript:false;document.write("");',display:false,zIndex:null,margin:0,offset:{x:0,y:0},browsers:(Browser.ie6||(Browser.firefox&&Browser.version<3&&Browser.Platform.mac))},property:"IframeShim",initialize:function(b,a){this.element=document.id(b); -if(this.occlude()){return this.occluded;}this.setOptions(a);this.makeShim();return this;},makeShim:function(){if(this.options.browsers){var c=this.element.getStyle("zIndex").toInt(); -if(!c){c=1;var b=this.element.getStyle("position");if(b=="static"||!b){this.element.setStyle("position","relative");}this.element.setStyle("zIndex",c); -}c=((this.options.zIndex!=null||this.options.zIndex===0)&&c>this.options.zIndex)?this.options.zIndex:c-1;if(c<0){c=1;}this.shim=new Element("iframe",{src:this.options.src,scrolling:"no",frameborder:0,styles:{zIndex:c,position:"absolute",border:"none",filter:"progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)"},"class":this.options.className}).store("IframeShim",this); -var a=(function(){this.shim.inject(this.element,"after");this[this.options.display?"show":"hide"]();this.fireEvent("inject");}).bind(this);if(!IframeShim.ready){window.addEvent("load",a); -}else{a();}}else{this.position=this.hide=this.show=this.dispose=Function.from(this);}},position:function(){if(!IframeShim.ready||!this.shim){return this; -}var a=this.element.measure(function(){return this.getSize();});if(this.options.margin!=undefined){a.x=a.x-(this.options.margin*2);a.y=a.y-(this.options.margin*2); -this.options.offset.x+=this.options.margin;this.options.offset.y+=this.options.margin;}this.shim.set({width:a.x,height:a.y}).position({relativeTo:this.element,offset:this.options.offset}); -return this;},hide:function(){if(this.shim){this.shim.setStyle("display","none");}return this;},show:function(){if(this.shim){this.shim.setStyle("display","block"); -}return this.position();},dispose:function(){if(this.shim){this.shim.dispose();}return this;},destroy:function(){if(this.shim){this.shim.destroy();}return this; -}});window.addEvent("load",function(){IframeShim.ready=true;});var Mask=new Class({Implements:[Options,Events],Binds:["position"],options:{style:{},"class":"mask",maskMargins:false,useIframeShim:true,iframeShimOptions:{}},initialize:function(b,a){this.target=document.id(b)||document.id(document.body); -this.target.store("mask",this);this.setOptions(a);this.render();this.inject();},render:function(){this.element=new Element("div",{"class":this.options["class"],id:this.options.id||"mask-"+String.uniqueID(),styles:Object.merge({},this.options.style,{display:"none"}),events:{click:function(a){this.fireEvent("click",a); -if(this.options.hideOnClick){this.hide();}}.bind(this)}});this.hidden=true;},toElement:function(){return this.element;},inject:function(b,a){a=a||(this.options.inject?this.options.inject.where:"")||this.target==document.body?"inside":"after"; -b=b||(this.options.inject&&this.options.inject.target)||this.target;this.element.inject(b,a);if(this.options.useIframeShim){this.shim=new IframeShim(this.element,this.options.iframeShimOptions); -this.addEvents({show:this.shim.show.bind(this.shim),hide:this.shim.hide.bind(this.shim),destroy:this.shim.destroy.bind(this.shim)});}},position:function(){this.resize(this.options.width,this.options.height); -this.element.position({relativeTo:this.target,position:"topLeft",ignoreMargins:!this.options.maskMargins,ignoreScroll:this.target==document.body});return this; -},resize:function(a,e){var b={styles:["padding","border"]};if(this.options.maskMargins){b.styles.push("margin");}var d=this.target.getComputedSize(b);if(this.target==document.body){this.element.setStyles({width:0,height:0}); -var c=window.getScrollSize();if(d.totalHeight=0&&a.options[a.selectedIndex].value!=""); -}else{return((a.get("value")==null)||(a.get("value").length==0));}}});Form.Validator.addAllThese([["required",{errorMsg:function(){return Form.Validator.getMsg("required"); -},test:function(a){return !Form.Validator.getValidator("IsEmpty").test(a);}}],["minLength",{errorMsg:function(a,b){if(typeOf(b.minLength)!="null"){return Form.Validator.getMsg("minLength").substitute({minLength:b.minLength,length:a.get("value").length}); -}else{return"";}},test:function(a,b){if(typeOf(b.minLength)!="null"){return(a.get("value").length>=(b.minLength||0));}else{return true;}}}],["maxLength",{errorMsg:function(a,b){if(typeOf(b.maxLength)!="null"){return Form.Validator.getMsg("maxLength").substitute({maxLength:b.maxLength,length:a.get("value").length}); -}else{return"";}},test:function(a,b){return a.get("value").length<=(b.maxLength||10000);}}],["validate-integer",{errorMsg:Form.Validator.getMsg.pass("integer"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^(-?[1-9]\d*|0)$/).test(a.get("value")); -}}],["validate-numeric",{errorMsg:Form.Validator.getMsg.pass("numeric"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^-?(?:0$0(?=\d*\.)|[1-9]|0)\d*(\.\d+)?$/).test(a.get("value")); -}}],["validate-digits",{errorMsg:Form.Validator.getMsg.pass("digits"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^[\d() .:\-\+#]+$/.test(a.get("value"))); -}}],["validate-alpha",{errorMsg:Form.Validator.getMsg.pass("alpha"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^[a-zA-Z]+$/).test(a.get("value")); -}}],["validate-alphanum",{errorMsg:Form.Validator.getMsg.pass("alphanum"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||!(/\W/).test(a.get("value")); -}}],["validate-date",{errorMsg:function(a,b){if(Date.parse){var c=b.dateFormat||"%x";return Form.Validator.getMsg("dateSuchAs").substitute({date:new Date().format(c)}); -}else{return Form.Validator.getMsg("dateInFormatMDY");}},test:function(e,g){if(Form.Validator.getValidator("IsEmpty").test(e)){return true;}var a=Locale.getCurrent().sets.Date,b=new RegExp([a.days,a.days_abbr,a.months,a.months_abbr].flatten().join("|"),"i"),i=e.get("value"),f=i.match(/[a-z]+/gi); -if(f&&!f.every(b.exec,b)){return false;}var c=Date.parse(i),h=g.dateFormat||"%x",d=c.format(h);if(d!="invalid date"){e.set("value",d);}return c.isValid(); -}}],["validate-email",{errorMsg:Form.Validator.getMsg.pass("email"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]\.?){0,63}[a-z0-9!#$%&'*+\/=?^_`{|}~-]@(?:(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\])$/i).test(a.get("value")); -}}],["validate-url",{errorMsg:Form.Validator.getMsg.pass("url"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^(https?|ftp|rmtp|mms):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i).test(a.get("value")); -}}],["validate-currency-dollar",{errorMsg:Form.Validator.getMsg.pass("currencyDollar"),test:function(a){return Form.Validator.getValidator("IsEmpty").test(a)||(/^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(a.get("value")); -}}],["validate-one-required",{errorMsg:Form.Validator.getMsg.pass("oneRequired"),test:function(a,b){var c=document.id(b["validate-one-required"])||a.getParent(b["validate-one-required"]); -return c.getElements("input").some(function(d){if(["checkbox","radio"].contains(d.get("type"))){return d.get("checked");}return d.get("value");});}}]]); -Element.Properties.validator={set:function(a){this.get("validator").setOptions(a);},get:function(){var a=this.retrieve("validator");if(!a){a=new Form.Validator(this); -this.store("validator",a);}return a;}};Element.implement({validate:function(a){if(a){this.set("validator",a);}return this.get("validator").validate();}}); -Form.Validator.Inline=new Class({Extends:Form.Validator,options:{showError:function(a){if(a.reveal){a.reveal();}else{a.setStyle("display","block");}},hideError:function(a){if(a.dissolve){a.dissolve(); -}else{a.setStyle("display","none");}},scrollToErrorsOnSubmit:true,scrollToErrorsOnBlur:false,scrollToErrorsOnChange:false,scrollFxOptions:{transition:"quad:out",offset:{y:-20}}},initialize:function(b,a){this.parent(b,a); -this.addEvent("onElementValidate",function(g,f,e,h){var d=this.getValidator(e);if(!g&&d.getError(f)){if(h){f.addClass("warning");}var c=this.makeAdvice(e,f,d.getError(f),h); -this.insertAdvice(c,f);this.showAdvice(e,f);}else{this.hideAdvice(e,f);}});},makeAdvice:function(d,f,c,g){var e=(g)?this.warningPrefix:this.errorPrefix; -e+=(this.options.useTitles)?f.title||c:c;var a=(g)?"warning-advice":"validation-advice";var b=this.getAdvice(d,f);if(b){b=b.set("html",e);}else{b=new Element("div",{html:e,styles:{display:"none"},id:"advice-"+d.split(":")[0]+"-"+this.getFieldId(f)}).addClass(a); -}f.store("$moo:advice-"+d,b);return b;},getFieldId:function(a){return a.id?a.id:a.id="input_"+a.name;},showAdvice:function(b,c){var a=this.getAdvice(b,c); -if(a&&!c.retrieve("$moo:"+this.getPropName(b))&&(a.getStyle("display")=="none"||a.getStyle("visiblity")=="hidden"||a.getStyle("opacity")==0)){c.store("$moo:"+this.getPropName(b),true); -this.options.showError(a);this.fireEvent("showAdvice",[c,a,b]);}},hideAdvice:function(b,c){var a=this.getAdvice(b,c);if(a&&c.retrieve("$moo:"+this.getPropName(b))){c.store("$moo:"+this.getPropName(b),false); -this.options.hideError(a);this.fireEvent("hideAdvice",[c,a,b]);}},getPropName:function(a){return"advice"+a;},resetField:function(a){a=document.id(a);if(!a){return this; -}this.parent(a);a.get("validators").each(function(b){this.hideAdvice(b,a);},this);return this;},getAllAdviceMessages:function(d,c){var b=[];if(d.hasClass("ignoreValidation")&&!c){return b; -}var a=d.get("validators").some(function(g){var e=g.test("^warn-")||d.hasClass("warnOnly");if(e){g=g.replace(/^warn-/,"");}var f=this.getValidator(g);if(!f){return; -}b.push({message:f.getError(d),warnOnly:e,passed:f.test(),validator:f});},this);return b;},getAdvice:function(a,b){return b.retrieve("$moo:advice-"+a); -},insertAdvice:function(a,c){var b=c.get("validatorProps");if(!b.msgPos||!document.id(b.msgPos)){if(c.type&&c.type.toLowerCase()=="radio"){c.getParent().adopt(a); -}else{a.inject(document.id(c),"after");}}else{document.id(b.msgPos).grab(a);}},validateField:function(g,f,b){var a=this.parent(g,f);if(((this.options.scrollToErrorsOnSubmit&&b==null)||b)&&!a){var c=document.id(this).getElement(".validation-failed"); -var d=document.id(this).getParent();while(d!=document.body&&d.getScrollSize().y==d.getSize().y){d=d.getParent();}var e=d.retrieve("$moo:fvScroller");if(!e&&window.Fx&&Fx.Scroll){e=new Fx.Scroll(d,this.options.scrollFxOptions); -d.store("$moo:fvScroller",e);}if(c){if(e){e.toElement(c);}else{d.scrollTo(d.getScroll().x,c.getPosition(d).y-20);}}}return a;},watchFields:function(a){a.each(function(b){if(this.options.evaluateFieldsOnBlur){b.addEvent("blur",this.validationMonitor.pass([b,false,this.options.scrollToErrorsOnBlur],this)); -}if(this.options.evaluateFieldsOnChange){b.addEvent("change",this.validationMonitor.pass([b,true,this.options.scrollToErrorsOnChange],this));}},this);}}); -Form.Validator.addAllThese([["validate-enforce-oncheck",{test:function(a,b){var c=a.getParent("form").retrieve("validator");if(!c){return true;}(b.toEnforce||document.id(b.enforceChildrenOf).getElements("input, select, textarea")).map(function(d){if(a.checked){c.enforceField(d); -}else{c.ignoreField(d);c.resetField(d);}});return true;}}],["validate-ignore-oncheck",{test:function(a,b){var c=a.getParent("form").retrieve("validator"); -if(!c){return true;}(b.toIgnore||document.id(b.ignoreChildrenOf).getElements("input, select, textarea")).each(function(d){if(a.checked){c.ignoreField(d); -c.resetField(d);}else{c.enforceField(d);}});return true;}}],["validate-nospace",{errorMsg:function(){return Form.Validator.getMsg("noSpace");},test:function(a,b){return !a.get("value").test(/\s/); -}}],["validate-toggle-oncheck",{test:function(b,c){var d=b.getParent("form").retrieve("validator");if(!d){return true;}var a=c.toToggle||document.id(c.toToggleChildrenOf).getElements("input, select, textarea"); -if(!b.checked){a.each(function(e){d.ignoreField(e);d.resetField(e);});}else{a.each(function(e){d.enforceField(e);});}return true;}}],["validate-reqchk-bynode",{errorMsg:function(){return Form.Validator.getMsg("reqChkByNode"); -},test:function(a,b){return(document.id(b.nodeId).getElements(b.selector||"input[type=checkbox], input[type=radio]")).some(function(c){return c.checked; -});}}],["validate-required-check",{errorMsg:function(a,b){return b.useTitle?a.get("title"):Form.Validator.getMsg("requiredChk");},test:function(a,b){return !!a.checked; -}}],["validate-reqchk-byname",{errorMsg:function(a,b){return Form.Validator.getMsg("reqChkByName").substitute({label:b.label||a.get("type")});},test:function(b,d){var c=d.groupName||b.get("name"); -var a=$$(document.getElementsByName(c)).some(function(g,f){return g.checked;});var e=b.getParent("form").retrieve("validator");if(a&&e){e.resetField(b); -}return a;}}],["validate-match",{errorMsg:function(a,b){return Form.Validator.getMsg("match").substitute({matchName:b.matchName||document.id(b.matchInput).get("name")}); -},test:function(b,c){var d=b.get("value");var a=document.id(c.matchInput)&&document.id(c.matchInput).get("value");return d&&a?d==a:true;}}],["validate-after-date",{errorMsg:function(a,b){return Form.Validator.getMsg("afterDate").substitute({label:b.afterLabel||(b.afterElement?Form.Validator.getMsg("startDate"):Form.Validator.getMsg("currentDate"))}); -},test:function(b,c){var d=document.id(c.afterElement)?Date.parse(document.id(c.afterElement).get("value")):new Date();var a=Date.parse(b.get("value")); -return a&&d?a>=d:true;}}],["validate-before-date",{errorMsg:function(a,b){return Form.Validator.getMsg("beforeDate").substitute({label:b.beforeLabel||(b.beforeElement?Form.Validator.getMsg("endDate"):Form.Validator.getMsg("currentDate"))}); -},test:function(b,c){var d=Date.parse(b.get("value"));var a=document.id(c.beforeElement)?Date.parse(document.id(c.beforeElement).get("value")):new Date(); -return a&&d?a>=d:true;}}],["validate-custom-required",{errorMsg:function(){return Form.Validator.getMsg("required");},test:function(a,b){return a.get("value")!=b.emptyValue; -}}],["validate-same-month",{errorMsg:function(a,b){var c=document.id(b.sameMonthAs)&&document.id(b.sameMonthAs).get("value");var d=a.get("value");if(d!=""){return Form.Validator.getMsg(c?"sameMonth":"startMonth"); -}},test:function(a,b){var d=Date.parse(a.get("value"));var c=Date.parse(document.id(b.sameMonthAs)&&document.id(b.sameMonthAs).get("value"));return d&&c?d.format("%B")==c.format("%B"):true; -}}],["validate-cc-num",{errorMsg:function(a){var b=a.get("value").replace(/[^0-9]/g,"");return Form.Validator.getMsg("creditcard").substitute({length:b.length}); -},test:function(c){if(Form.Validator.getValidator("IsEmpty").test(c)){return true;}var g=c.get("value");g=g.replace(/[^0-9]/g,"");var a=false;if(g.test(/^4[0-9]{12}([0-9]{3})?$/)){a="Visa"; -}else{if(g.test(/^5[1-5]([0-9]{14})$/)){a="Master Card";}else{if(g.test(/^3[47][0-9]{13}$/)){a="American Express";}else{if(g.test(/^6011[0-9]{12}$/)){a="Discover"; -}}}}if(a){var d=0;var e=0;for(var b=g.length-1;b>=0;--b){e=g.charAt(b).toInt();if(e==0){continue;}if((g.length-b)%2==0){e+=e;}if(e>9){e=e.toString().charAt(0).toInt()+e.toString().charAt(1).toInt(); -}d+=e;}if((d%10)==0){return true;}}var f="";while(g!=""){f+=" "+g.substr(0,4);g=g.substr(4);}c.getParent("form").retrieve("validator").ignoreField(c);c.set("value",f.clean()); -c.getParent("form").retrieve("validator").enforceField(c);return false;}}]]);var OverText=new Class({Implements:[Options,Events,Class.Occlude],Binds:["reposition","assert","focus","hide"],options:{element:"label",labelClass:"overTxtLabel",positionOptions:{position:"upperLeft",edge:"upperLeft",offset:{x:4,y:2}},poll:false,pollInterval:250,wrap:false},property:"OverText",initialize:function(b,a){b=this.element=document.id(b); -if(this.occlude()){return this.occluded;}this.setOptions(a);this.attach(b);OverText.instances.push(this);if(this.options.poll){this.poll();}},toElement:function(){return this.element; -},attach:function(){var b=this.element,a=this.options,c=a.textOverride||b.get("alt")||b.get("title");if(!c){return this;}var d=this.text=new Element(a.element,{"class":a.labelClass,styles:{lineHeight:"normal",position:"absolute",cursor:"text"},html:c,events:{click:this.hide.pass(a.element=="label",this)}}).inject(b,"after"); -if(a.element=="label"){if(!b.get("id")){b.set("id","input_"+String.uniqueID());}d.set("for",b.get("id"));}if(a.wrap){this.textHolder=new Element("div.overTxtWrapper",{styles:{lineHeight:"normal",position:"relative"}}).grab(d).inject(b,"before"); -}return this.enable();},destroy:function(){this.element.eliminate(this.property);this.disable();if(this.text){this.text.destroy();}if(this.textHolder){this.textHolder.destroy(); -}return this;},disable:function(){this.element.removeEvents({focus:this.focus,blur:this.assert,change:this.assert});window.removeEvent("resize",this.reposition); -this.hide(true,true);return this;},enable:function(){this.element.addEvents({focus:this.focus,blur:this.assert,change:this.assert});window.addEvent("resize",this.reposition); -this.assert(true);this.reposition();return this;},wrap:function(){if(this.options.element=="label"){if(!this.element.get("id")){this.element.set("id","input_"+String.uniqueID()); -}this.text.set("for",this.element.get("id"));}},startPolling:function(){this.pollingPaused=false;return this.poll();},poll:function(a){if(this.poller&&!a){return this; -}if(a){clearInterval(this.poller);}else{this.poller=(function(){if(!this.pollingPaused){this.assert(true);}}).periodical(this.options.pollInterval,this); -}return this;},stopPolling:function(){this.pollingPaused=true;return this.poll(true);},focus:function(){if(this.text&&(!this.text.isDisplayed()||this.element.get("disabled"))){return this; -}return this.hide();},hide:function(c,a){if(this.text&&(this.text.isDisplayed()&&(!this.element.get("disabled")||a))){this.text.hide();this.fireEvent("textHide",[this.text,this.element]); -this.pollingPaused=true;if(!c){try{this.element.fireEvent("focus");this.element.focus();}catch(b){}}}return this;},show:function(){if(this.text&&!this.text.isDisplayed()){this.text.show(); -this.reposition();this.fireEvent("textShow",[this.text,this.element]);this.pollingPaused=false;}return this;},test:function(){return !this.element.get("value"); -},assert:function(a){return this[this.test()?"show":"hide"](a);},reposition:function(){this.assert(true);if(!this.element.isVisible()){return this.stopPolling().hide(); -}if(this.text&&this.test()){this.text.position(Object.merge(this.options.positionOptions,{relativeTo:this.element}));}return this;}});OverText.instances=[]; -Object.append(OverText,{each:function(a){return OverText.instances.each(function(c,b){if(c.element&&c.text){a.call(OverText,c,b);}});},update:function(){return OverText.each(function(a){return a.reposition(); -});},hideAll:function(){return OverText.each(function(a){return a.hide(true,true);});},showAll:function(){return OverText.each(function(a){return a.show(); -});}});Fx.Elements=new Class({Extends:Fx.CSS,initialize:function(b,a){this.elements=this.subject=$$(b);this.parent(a);},compute:function(g,h,j){var c={}; -for(var d in g){var a=g[d],e=h[d],f=c[d]={};for(var b in a){f[b]=this.parent(a[b],e[b],j);}}return c;},set:function(b){for(var c in b){if(!this.elements[c]){continue; -}var a=b[c];for(var d in a){this.render(this.elements[c],d,a[d],this.options.unit);}}return this;},start:function(c){if(!this.check(c)){return this;}var h={},j={}; -for(var d in c){if(!this.elements[d]){continue;}var f=c[d],a=h[d]={},g=j[d]={};for(var b in f){var e=this.prepare(this.elements[d],b,f[b]);a[b]=e.from; -g[b]=e.to;}}return this.parent(h,j);}});Fx.Accordion=new Class({Extends:Fx.Elements,options:{fixedHeight:false,fixedWidth:false,display:0,show:false,height:true,width:false,opacity:true,alwaysHide:false,trigger:"click",initialDisplayFx:true,resetHeight:true},initialize:function(){var g=function(h){return h!=null; -};var f=Array.link(arguments,{container:Type.isElement,options:Type.isObject,togglers:g,elements:g});this.parent(f.elements,f.options);var b=this.options,e=this.togglers=$$(f.togglers); -this.previous=-1;this.internalChain=new Chain();if(b.alwaysHide){this.options.link="chain";}if(b.show||this.options.show===0){b.display=false;this.previous=b.show; -}if(b.start){b.display=false;b.show=false;}var d=this.effects={};if(b.opacity){d.opacity="fullOpacity";}if(b.width){d.width=b.fixedWidth?"fullWidth":"offsetWidth"; -}if(b.height){d.height=b.fixedHeight?"fullHeight":"scrollHeight";}for(var c=0,a=e.length;c=0?a-1:0)).chain(d);}else{d();}return this;},detach:function(b){var a=function(c){c.removeEvent(this.options.trigger,c.retrieve("accordion:display")); -}.bind(this);if(!b){this.togglers.each(a);}else{a(b);}return this;},display:function(b,c){if(!this.check(b,c)){return this;}var h={},g=this.elements,a=this.options,f=this.effects; -if(c==null){c=true;}if(typeOf(b)=="element"){b=g.indexOf(b);}if(b==this.previous&&!a.alwaysHide){return this;}if(a.resetHeight){var e=g[this.previous]; -if(e&&!this.selfHidden){for(var d in f){e.setStyle(d,e[f[d]]);}}}if((this.timer&&a.link=="chain")||(b===this.previous&&!a.alwaysHide)){return this;}this.previous=b; -this.selfHidden=false;g.each(function(l,k){h[k]={};var j;if(k!=b){j=true;}else{if(a.alwaysHide&&((l.offsetHeight>0&&a.height)||l.offsetWidth>0&&a.width)){j=true; -this.selfHidden=true;}}this.fireEvent(j?"background":"active",[this.togglers[k],l]);for(var m in f){h[k][m]=j?0:l[f[m]];}if(!c&&!j&&a.resetHeight){h[k].height="auto"; -}},this);this.internalChain.clearChain();this.internalChain.chain(function(){if(a.resetHeight&&!this.selfHidden){var i=g[b];if(i){i.setStyle("height","auto"); -}}}.bind(this));return c?this.start(h):this.set(h).internalChain.callChain();}});Fx.Move=new Class({Extends:Fx.Morph,options:{relativeTo:document.body,position:"center",edge:false,offset:{x:0,y:0}},start:function(a){var b=this.element,c=b.getStyles("top","left"); -if(c.top=="auto"||c.left=="auto"){b.setPosition(b.getPosition(b.getOffsetParent()));}return this.parent(b.position(Object.merge({},this.options,a,{returnPos:true}))); -}});Element.Properties.move={set:function(a){this.get("move").cancel().setOptions(a);return this;},get:function(){var a=this.retrieve("move");if(!a){a=new Fx.Move(this,{link:"cancel"}); -this.store("move",a);}return a;}};Element.implement({move:function(a){this.get("move").start(a);return this;}});Fx.Slide=new Class({Extends:Fx,options:{mode:"vertical",wrapper:false,hideOverflow:true,resetHeight:false},initialize:function(b,a){b=this.element=this.subject=document.id(b); -this.parent(a);a=this.options;var d=b.retrieve("wrapper"),c=b.getStyles("margin","position","overflow");if(a.hideOverflow){c=Object.append(c,{overflow:"hidden"}); -}if(a.wrapper){d=document.id(a.wrapper).setStyles(c);}if(!d){d=new Element("div",{styles:c}).wraps(b);}b.store("wrapper",d).setStyle("margin",0);if(b.getStyle("overflow")=="visible"){b.setStyle("overflow","hidden"); -}this.now=[];this.open=true;this.wrapper=d;this.addEvent("complete",function(){this.open=(d["offset"+this.layout.capitalize()]!=0);if(this.open&&this.options.resetHeight){d.setStyle("height",""); -}},true);},vertical:function(){this.margin="margin-top";this.layout="height";this.offset=this.element.offsetHeight;},horizontal:function(){this.margin="margin-left"; -this.layout="width";this.offset=this.element.offsetWidth;},set:function(a){this.element.setStyle(this.margin,a[0]);this.wrapper.setStyle(this.layout,a[1]); -return this;},compute:function(c,b,a){return[0,1].map(function(d){return Fx.compute(c[d],b[d],a);});},start:function(b,e){if(!this.check(b,e)){return this; -}this[e||this.options.mode]();var d=this.element.getStyle(this.margin).toInt(),c=this.wrapper.getStyle(this.layout).toInt(),a=[[d,c],[0,this.offset]],g=[[d,c],[-this.offset,0]],f; -switch(b){case"in":f=a;break;case"out":f=g;break;case"toggle":f=(c==0)?a:g;}return this.parent(f[0],f[1]);},slideIn:function(a){return this.start("in",a); -},slideOut:function(a){return this.start("out",a);},hide:function(a){this[a||this.options.mode]();this.open=false;return this.set([-this.offset,0]);},show:function(a){this[a||this.options.mode](); -this.open=true;return this.set([0,this.offset]);},toggle:function(a){return this.start("toggle",a);}});Element.Properties.slide={set:function(a){this.get("slide").cancel().setOptions(a); -return this;},get:function(){var a=this.retrieve("slide");if(!a){a=new Fx.Slide(this,{link:"cancel"});this.store("slide",a);}return a;}};Element.implement({slide:function(d,e){d=d||"toggle"; -var b=this.get("slide"),a;switch(d){case"hide":b.hide(e);break;case"show":b.show(e);break;case"toggle":var c=this.retrieve("slide:flag",b.open);b[c?"slideOut":"slideIn"](e); -this.store("slide:flag",!c);a=true;break;default:b.start(d,e);}if(!a){this.eliminate("slide:flag");}return this;}});(function(){Fx.Scroll=new Class({Extends:Fx,options:{offset:{x:0,y:0},wheelStops:true},initialize:function(c,b){this.element=this.subject=document.id(c); -this.parent(b);if(typeOf(this.element)!="element"){this.element=document.id(this.element.getDocument().body);}if(this.options.wheelStops){var d=this.element,e=this.cancel.pass(false,this); -this.addEvent("start",function(){d.addEvent("mousewheel",e);},true);this.addEvent("complete",function(){d.removeEvent("mousewheel",e);},true);}},set:function(){var b=Array.flatten(arguments); -if(Browser.firefox){b=[Math.round(b[0]),Math.round(b[1])];}this.element.scrollTo(b[0],b[1]);return this;},compute:function(d,c,b){return[0,1].map(function(e){return Fx.compute(d[e],c[e],b); -});},start:function(c,d){if(!this.check(c,d)){return this;}var b=this.element.getScroll();return this.parent([b.x,b.y],[c,d]);},calculateScroll:function(g,f){var d=this.element,b=d.getScrollSize(),h=d.getScroll(),j=d.getSize(),c=this.options.offset,i={x:g,y:f}; -for(var e in i){if(!i[e]&&i[e]!==0){i[e]=h[e];}if(typeOf(i[e])!="number"){i[e]=b[e]-j[e];}i[e]+=c[e];}return[i.x,i.y];},toTop:function(){return this.start.apply(this,this.calculateScroll(false,0)); -},toLeft:function(){return this.start.apply(this,this.calculateScroll(0,false));},toRight:function(){return this.start.apply(this,this.calculateScroll("right",false)); -},toBottom:function(){return this.start.apply(this,this.calculateScroll(false,"bottom"));},toElement:function(d,e){e=e?Array.from(e):["x","y"];var c=a(this.element)?{x:0,y:0}:this.element.getScroll(); -var b=Object.map(document.id(d).getPosition(this.element),function(g,f){return e.contains(f)?g+c[f]:false;});return this.start.apply(this,this.calculateScroll(b.x,b.y)); -},toElementEdge:function(d,g,e){g=g?Array.from(g):["x","y"];d=document.id(d);var i={},f=d.getPosition(this.element),j=d.getSize(),h=this.element.getScroll(),b=this.element.getSize(),c={x:f.x+j.x,y:f.y+j.y}; -["x","y"].each(function(k){if(g.contains(k)){if(c[k]>h[k]+b[k]){i[k]=c[k]-b[k];}if(f[k]this.elements.length){e.splice(this.elements.length-1,e.length-this.elements.length); -}}var b=0;i=a=0;e.each(function(k){var j={};if(d){j.top=i-f[k].top-b;i+=f[k].height;}else{j.left=a-f[k].left;a+=f[k].width;}b=b+f[k].margin;c[k]=j;},this); -var g={};Array.clone(e).sort().each(function(j){g[j]=c[j];});this.start(g);this.currentOrder=e;return this;},rearrangeDOM:function(a){a=a||this.currentOrder; -var b=this.elements[0].getParent();var c=[];this.elements.setStyle("opacity",0);a.each(function(d){c.push(this.elements[d].inject(b).setStyles({top:0,left:0})); -},this);this.elements.setStyle("opacity",1);this.elements=$$(c);this.setDefaultOrder();return this;},getDefaultOrder:function(){return this.elements.map(function(b,a){return a; -});},getCurrentOrder:function(){return this.currentOrder;},forward:function(){return this.sort(this.getDefaultOrder());},backward:function(){return this.sort(this.getDefaultOrder().reverse()); -},reverse:function(){return this.sort(this.currentOrder.reverse());},sortByElements:function(a){return this.sort(a.map(function(b){return this.elements.indexOf(b); -},this));},swap:function(c,b){if(typeOf(c)=="element"){c=this.elements.indexOf(c);}if(typeOf(b)=="element"){b=this.elements.indexOf(b);}var a=Array.clone(this.currentOrder); -a[this.currentOrder.indexOf(c)]=b;a[this.currentOrder.indexOf(b)]=c;return this.sort(a);}});var Drag=new Class({Implements:[Events,Options],options:{snap:6,unit:"px",grid:false,style:true,limit:false,handle:false,invert:false,preventDefault:false,stopPropagation:false,modifiers:{x:"left",y:"top"}},initialize:function(){var b=Array.link(arguments,{options:Type.isObject,element:function(c){return c!=null; -}});this.element=document.id(b.element);this.document=this.element.getDocument();this.setOptions(b.options||{});var a=typeOf(this.options.handle);this.handles=((a=="array"||a=="collection")?$$(this.options.handle):document.id(this.options.handle))||this.element; -this.mouse={now:{},pos:{}};this.value={start:{},now:{}};this.selection=(Browser.ie)?"selectstart":"mousedown";if(Browser.ie&&!Drag.ondragstartFixed){document.ondragstart=Function.from(false); -Drag.ondragstartFixed=true;}this.bound={start:this.start.bind(this),check:this.check.bind(this),drag:this.drag.bind(this),stop:this.stop.bind(this),cancel:this.cancel.bind(this),eventStop:Function.from(false)}; -this.attach();},attach:function(){this.handles.addEvent("mousedown",this.bound.start);return this;},detach:function(){this.handles.removeEvent("mousedown",this.bound.start); -return this;},start:function(a){var j=this.options;if(a.rightClick){return;}if(j.preventDefault){a.preventDefault();}if(j.stopPropagation){a.stopPropagation(); -}this.mouse.start=a.page;this.fireEvent("beforeStart",this.element);var c=j.limit;this.limit={x:[],y:[]};var e,g;for(e in j.modifiers){if(!j.modifiers[e]){continue; -}var b=this.element.getStyle(j.modifiers[e]);if(b&&!b.match(/px$/)){if(!g){g=this.element.getCoordinates(this.element.getOffsetParent());}b=g[j.modifiers[e]]; -}if(j.style){this.value.now[e]=(b||0).toInt();}else{this.value.now[e]=this.element[j.modifiers[e]];}if(j.invert){this.value.now[e]*=-1;}this.mouse.pos[e]=a.page[e]-this.value.now[e]; -if(c&&c[e]){var d=2;while(d--){var f=c[e][d];if(f||f===0){this.limit[e][d]=(typeof f=="function")?f():f;}}}}if(typeOf(this.options.grid)=="number"){this.options.grid={x:this.options.grid,y:this.options.grid}; -}var h={mousemove:this.bound.check,mouseup:this.bound.cancel};h[this.selection]=this.bound.eventStop;this.document.addEvents(h);},check:function(a){if(this.options.preventDefault){a.preventDefault(); -}var b=Math.round(Math.sqrt(Math.pow(a.page.x-this.mouse.start.x,2)+Math.pow(a.page.y-this.mouse.start.y,2)));if(b>this.options.snap){this.cancel();this.document.addEvents({mousemove:this.bound.drag,mouseup:this.bound.stop}); -this.fireEvent("start",[this.element,a]).fireEvent("snap",this.element);}},drag:function(b){var a=this.options;if(a.preventDefault){b.preventDefault(); -}this.mouse.now=b.page;for(var c in a.modifiers){if(!a.modifiers[c]){continue;}this.value.now[c]=this.mouse.now[c]-this.mouse.pos[c];if(a.invert){this.value.now[c]*=-1; -}if(a.limit&&this.limit[c]){if((this.limit[c][1]||this.limit[c][1]===0)&&(this.value.now[c]>this.limit[c][1])){this.value.now[c]=this.limit[c][1];}else{if((this.limit[c][0]||this.limit[c][0]===0)&&(this.value.now[c]d.left&&b.xd.top);},this).getLast();if(this.overed!=a){if(this.overed){this.fireEvent("leave",[this.element,this.overed]); -}if(a){this.fireEvent("enter",[this.element,a]);}this.overed=a;}},drag:function(a){this.parent(a);if(this.options.checkDroppables&&this.droppables.length){this.checkDroppables(); -}},stop:function(a){this.checkDroppables();this.fireEvent("drop",[this.element,this.overed,a]);this.overed=null;return this.parent(a);}});Element.implement({makeDraggable:function(a){var b=new Drag.Move(this,a); -this.store("dragger",b);return b;}});var Slider=new Class({Implements:[Events,Options],Binds:["clickedElement","draggedKnob","scrolledElement"],options:{onTick:function(a){this.setKnobPosition(a); -},initialStep:0,snap:false,offset:0,range:false,wheel:false,steps:100,mode:"horizontal"},initialize:function(f,a,e){this.setOptions(e);e=this.options;this.element=document.id(f); -a=this.knob=document.id(a);this.previousChange=this.previousEnd=this.step=-1;var b={},d={x:false,y:false};switch(e.mode){case"vertical":this.axis="y";this.property="top"; -this.offset="offsetHeight";break;case"horizontal":this.axis="x";this.property="left";this.offset="offsetWidth";}this.setSliderDimensions();this.setRange(e.range); -if(a.getStyle("position")=="static"){a.setStyle("position","relative");}a.setStyle(this.property,-e.offset);d[this.axis]=this.property;b[this.axis]=[-e.offset,this.full-e.offset]; -var c={snap:0,limit:b,modifiers:d,onDrag:this.draggedKnob,onStart:this.draggedKnob,onBeforeStart:(function(){this.isDragging=true;}).bind(this),onCancel:function(){this.isDragging=false; -}.bind(this),onComplete:function(){this.isDragging=false;this.draggedKnob();this.end();}.bind(this)};if(e.snap){this.setSnap(c);}this.drag=new Drag(a,c); -this.attach();if(e.initialStep!=null){this.set(e.initialStep);}},attach:function(){this.element.addEvent("mousedown",this.clickedElement);if(this.options.wheel){this.element.addEvent("mousewheel",this.scrolledElement); -}this.drag.attach();return this;},detach:function(){this.element.removeEvent("mousedown",this.clickedElement).removeEvent("mousewheel",this.scrolledElement); -this.drag.detach();return this;},autosize:function(){this.setSliderDimensions().setKnobPosition(this.toPosition(this.step));this.drag.options.limit[this.axis]=[-this.options.offset,this.full-this.options.offset]; -if(this.options.snap){this.setSnap();}return this;},setSnap:function(a){if(!a){a=this.drag.options;}a.grid=Math.ceil(this.stepWidth);a.limit[this.axis][1]=this.full; -return this;},setKnobPosition:function(a){if(this.options.snap){a=this.toPosition(this.step);}this.knob.setStyle(this.property,a);return this;},setSliderDimensions:function(){this.full=this.element.measure(function(){this.half=this.knob[this.offset]/2; -return this.element[this.offset]-this.knob[this.offset]+(this.options.offset*2);}.bind(this));return this;},set:function(a){if(!((this.range>0)^(a0)^(a>this.max))){a=this.max;}this.step=Math.round(a);return this.checkStep().fireEvent("tick",this.toPosition(this.step)).end();},setRange:function(a,b){this.min=Array.pick([a[0],0]); -this.max=Array.pick([a[1],this.options.steps]);this.range=this.max-this.min;this.steps=this.options.steps||this.full;this.stepSize=Math.abs(this.range)/this.steps; -this.stepWidth=this.stepSize*this.full/Math.abs(this.range);if(a){this.set(Array.pick([b,this.step]).floor(this.min).max(this.max));}return this;},clickedElement:function(c){if(this.isDragging||c.target==this.knob){return; -}var b=this.range<0?-1:1,a=c.page[this.axis]-this.element.getPosition()[this.axis]-this.half;a=a.limit(-this.options.offset,this.full-this.options.offset); -this.step=Math.round(this.min+b*this.toStep(a));this.checkStep().fireEvent("tick",a).end();},scrolledElement:function(a){var b=(this.options.mode=="horizontal")?(a.wheel<0):(a.wheel>0); -this.set(this.step+(b?-1:1)*this.stepSize);a.stop();},draggedKnob:function(){var b=this.range<0?-1:1,a=this.drag.value.now[this.axis];a=a.limit(-this.options.offset,this.full-this.options.offset); -this.step=Math.round(this.min+b*this.toStep(a));this.checkStep();},checkStep:function(){var a=this.step;if(this.previousChange!=a){this.previousChange=a; -this.fireEvent("change",a);}return this;},end:function(){var a=this.step;if(this.previousEnd!==a){this.previousEnd=a;this.fireEvent("complete",a+"");}return this; -},toStep:function(a){var b=(a+this.options.offset)*this.stepSize/this.full*this.steps;return this.options.steps?Math.round(b-=b%this.stepSize):b;},toPosition:function(a){return(this.full*Math.abs(this.min-a))/(this.steps*this.stepSize)-this.options.offset; -}});var Sortables=new Class({Implements:[Events,Options],options:{opacity:1,clone:false,revert:false,handle:false,dragOptions:{}},initialize:function(a,b){this.setOptions(b); -this.elements=[];this.lists=[];this.idle=true;this.addLists($$(document.id(a)||a));if(!this.options.clone){this.options.revert=false;}if(this.options.revert){this.effect=new Fx.Morph(null,Object.merge({duration:250,link:"cancel"},this.options.revert)); -}},attach:function(){this.addLists(this.lists);return this;},detach:function(){this.lists=this.removeLists(this.lists);return this;},addItems:function(){Array.flatten(arguments).each(function(a){this.elements.push(a); -var b=a.retrieve("sortables:start",function(c){this.start.call(this,c,a);}.bind(this));(this.options.handle?a.getElement(this.options.handle)||a:a).addEvent("mousedown",b); -},this);return this;},addLists:function(){Array.flatten(arguments).each(function(a){this.lists.include(a);this.addItems(a.getChildren());},this);return this; -},removeItems:function(){return $$(Array.flatten(arguments).map(function(a){this.elements.erase(a);var b=a.retrieve("sortables:start");(this.options.handle?a.getElement(this.options.handle)||a:a).removeEvent("mousedown",b); -return a;},this));},removeLists:function(){return $$(Array.flatten(arguments).map(function(a){this.lists.erase(a);this.removeItems(a.getChildren());return a; -},this));},getClone:function(b,a){if(!this.options.clone){return new Element(a.tagName).inject(document.body);}if(typeOf(this.options.clone)=="function"){return this.options.clone.call(this,b,a,this.list); -}var c=a.clone(true).setStyles({margin:0,position:"absolute",visibility:"hidden",width:a.getStyle("width")}).addEvent("mousedown",function(d){a.fireEvent("mousedown",d); -});if(c.get("html").test("radio")){c.getElements("input[type=radio]").each(function(d,e){d.set("name","clone_"+e);if(d.get("checked")){a.getElements("input[type=radio]")[e].set("checked",true); -}});}return c.inject(this.list).setPosition(a.getPosition(a.getOffsetParent()));},getDroppables:function(){var a=this.list.getChildren().erase(this.clone).erase(this.element); -if(!this.options.constrain){a.append(this.lists).erase(this.list);}return a;},insert:function(c,b){var a="inside";if(this.lists.contains(b)){this.list=b; -this.drag.droppables=this.getDroppables();}else{a=this.element.getAllPrevious().contains(b)?"before":"after";}this.element.inject(b,a);this.fireEvent("sort",[this.element,this.clone]); -},start:function(b,a){if(!this.idle||b.rightClick||["button","input","a"].contains(b.target.get("tag"))){return;}this.idle=false;this.element=a;this.opacity=a.get("opacity"); -this.list=a.getParent();this.clone=this.getClone(b,a);this.drag=new Drag.Move(this.clone,Object.merge({droppables:this.getDroppables()},this.options.dragOptions)).addEvents({onSnap:function(){b.stop(); -this.clone.setStyle("visibility","visible");this.element.set("opacity",this.options.opacity||0);this.fireEvent("start",[this.element,this.clone]);}.bind(this),onEnter:this.insert.bind(this),onCancel:this.end.bind(this),onComplete:this.end.bind(this)}); -this.clone.inject(this.element,"before");this.drag.start(b);},end:function(){this.drag.detach();this.element.set("opacity",this.opacity);if(this.effect){var b=this.element.getStyles("width","height"),d=this.clone,c=d.computePosition(this.element.getPosition(this.clone.getOffsetParent())); -var a=function(){this.removeEvent("cancel",a);d.destroy();};this.effect.element=d;this.effect.start({top:c.top,left:c.left,width:b.width,height:b.height,opacity:0.25}).addEvent("cancel",a).chain(a); -}else{this.clone.destroy();}this.reset();},reset:function(){this.idle=true;this.fireEvent("complete",this.element);},serialize:function(){var c=Array.link(arguments,{modifier:Type.isFunction,index:function(d){return d!=null; -}});var b=this.lists.map(function(d){return d.getChildren().map(c.modifier||function(e){return e.get("id");},this);},this);var a=c.index;if(this.lists.length==1){a=0; -}return(a||a===0)&&a>=0&&a2083){this.fireEvent("error",f);}Request.JSONP.request_map["request_"+b]=function(){this.success(arguments,b);}.bind(this);var a=this.getScript(f).inject(c.injectScript); -this.fireEvent("request",[f,a]);if(c.timeout){this.timeout.delay(c.timeout,this);}return this;},getScript:function(a){if(!this.script){this.script=new Element("script",{type:"text/javascript",async:true,src:a}); -}return this.script;},success:function(b,a){if(!this.running){return;}this.clear().fireEvent("complete",b).fireEvent("success",b).callChain();},cancel:function(){if(this.running){this.clear().fireEvent("cancel"); -}return this;},isRunning:function(){return !!this.running;},clear:function(){this.running=false;if(this.script){this.script.destroy();this.script=null; -}return this;},timeout:function(){if(this.running){this.running=false;this.fireEvent("timeout",[this.script.get("src"),this.script]).fireEvent("failure").cancel(); -}return this;}});Request.JSONP.counter=0;Request.JSONP.request_map={};Request.Queue=new Class({Implements:[Options,Events],Binds:["attach","request","complete","cancel","success","failure","exception"],options:{stopOnFailure:true,autoAdvance:true,concurrent:1,requests:{}},initialize:function(a){var b; -if(a){b=a.requests;delete a.requests;}this.setOptions(a);this.requests={};this.queue=[];this.reqBinders={};if(b){this.addRequests(b);}},addRequest:function(a,b){this.requests[a]=b; -this.attach(a,b);return this;},addRequests:function(a){Object.each(a,function(c,b){this.addRequest(b,c);},this);return this;},getName:function(a){return Object.keyOf(this.requests,a); -},attach:function(a,b){if(b._groupSend){return this;}["request","complete","cancel","success","failure","exception"].each(function(c){if(!this.reqBinders[a]){this.reqBinders[a]={}; -}this.reqBinders[a][c]=function(){this["on"+c.capitalize()].apply(this,[a,b].append(arguments));}.bind(this);b.addEvent(c,this.reqBinders[a][c]);},this); -b._groupSend=b.send;b.send=function(c){this.send(a,c);return b;}.bind(this);return this;},removeRequest:function(b){var a=typeOf(b)=="object"?this.getName(b):b; -if(!a&&typeOf(a)!="string"){return this;}b=this.requests[a];if(!b){return this;}["request","complete","cancel","success","failure","exception"].each(function(c){b.removeEvent(c,this.reqBinders[a][c]); -},this);b.send=b._groupSend;delete b._groupSend;return this;},getRunning:function(){return Object.filter(this.requests,function(a){return a.running;}); -},isRunning:function(){return !!(Object.keys(this.getRunning()).length);},send:function(b,a){var c=function(){this.requests[b]._groupSend(a);this.queue.erase(c); -}.bind(this);c.name=b;if(Object.keys(this.getRunning()).length>=this.options.concurrent||(this.error&&this.options.stopOnFailure)){this.queue.push(c);}else{c(); -}return this;},hasNext:function(a){return(!a)?!!this.queue.length:!!this.queue.filter(function(b){return b.name==a;}).length;},resume:function(){this.error=false; -(this.options.concurrent-Object.keys(this.getRunning()).length).times(this.runNext,this);return this;},runNext:function(a){if(!this.queue.length){return this; -}if(!a){this.queue[0]();}else{var b;this.queue.each(function(c){if(!b&&c.name==a){b=true;c();}});}return this;},runAll:function(){this.queue.each(function(a){a(); -});return this;},clear:function(a){if(!a){this.queue.empty();}else{this.queue=this.queue.map(function(b){if(b.name!=a){return b;}else{return false;}}).filter(function(b){return b; -});}return this;},cancel:function(a){this.requests[a].cancel();return this;},onRequest:function(){this.fireEvent("request",arguments);},onComplete:function(){this.fireEvent("complete",arguments); -if(!this.queue.length){this.fireEvent("end");}},onCancel:function(){if(this.options.autoAdvance&&!this.error){this.runNext();}this.fireEvent("cancel",arguments); -},onSuccess:function(){if(this.options.autoAdvance&&!this.error){this.runNext();}this.fireEvent("success",arguments);},onFailure:function(){this.error=true; -if(!this.options.stopOnFailure&&this.options.autoAdvance){this.runNext();}this.fireEvent("failure",arguments);},onException:function(){this.error=true; -if(!this.options.stopOnFailure&&this.options.autoAdvance){this.runNext();}this.fireEvent("exception",arguments);}});Request.implement({options:{initialDelay:5000,delay:5000,limit:60000},startTimer:function(b){var a=function(){if(!this.running){this.send({data:b}); -}};this.lastDelay=this.options.initialDelay;this.timer=a.delay(this.lastDelay,this);this.completeCheck=function(c){clearTimeout(this.timer);this.lastDelay=(c)?this.options.delay:(this.lastDelay+this.options.delay).min(this.options.limit); -this.timer=a.delay(this.lastDelay,this);};return this.addEvent("complete",this.completeCheck);},stopTimer:function(){clearTimeout(this.timer);return this.removeEvent("complete",this.completeCheck); -}});var Asset={javascript:function(f,c){if(!c){c={};}var a=new Element("script",{src:f,type:"text/javascript"}),g=c.document||document,b=0,d=c.onload||c.onLoad; -var e=d?function(){if(++b==1){d.call(this);}}:function(){};delete c.onload;delete c.onLoad;delete c.document;return a.addEvents({load:e,readystatechange:function(){if(["loaded","complete"].contains(this.readyState)){e.call(this); -}}}).set(c).inject(g.head);},css:function(d,a){if(!a){a={};}var b=new Element("link",{rel:"stylesheet",media:"screen",type:"text/css",href:d});var c=a.onload||a.onLoad,e=a.document||document; -delete a.onload;delete a.onLoad;delete a.document;if(c){b.addEvent("load",c);}return b.set(a).inject(e.head);},image:function(c,b){if(!b){b={};}var d=new Image(),a=document.id(d)||new Element("img"); -["load","abort","error"].each(function(e){var g="on"+e,f="on"+e.capitalize(),h=b[g]||b[f]||function(){};delete b[f];delete b[g];d[g]=function(){if(!d){return; -}if(!a.parentNode){a.width=d.width;a.height=d.height;}d=d.onload=d.onabort=d.onerror=null;h.delay(1,a,a);a.fireEvent(e,a,1);};});d.src=a.src=c;if(d&&d.complete){d.onload.delay(1); -}return a.set(b);},images:function(c,b){c=Array.from(c);var d=function(){},a=0;b=Object.merge({onComplete:d,onProgress:d,onError:d,properties:{}},b);return new Elements(c.map(function(f,e){return Asset.image(f,Object.append(b.properties,{onload:function(){a++; -b.onProgress.call(this,a,e,f);if(a==c.length){b.onComplete();}},onerror:function(){a++;b.onError.call(this,a,e,f);if(a==c.length){b.onComplete();}}})); -}));}};(function(){var a=this.Color=new Type("Color",function(c,d){if(arguments.length>=3){d="rgb";c=Array.slice(arguments,0,3);}else{if(typeof c=="string"){if(c.match(/rgb/)){c=c.rgbToHex().hexToRgb(true); -}else{if(c.match(/hsb/)){c=c.hsbToRgb();}else{c=c.hexToRgb(true);}}}}d=d||"rgb";switch(d){case"hsb":var b=c;c=c.hsbToRgb();c.hsb=b;break;case"hex":c=c.hexToRgb(true); -break;}c.rgb=c.slice(0,3);c.hsb=c.hsb||c.rgbToHsb();c.hex=c.rgbToHex();return Object.append(c,this);});a.implement({mix:function(){var b=Array.slice(arguments); -var d=(typeOf(b.getLast())=="number")?b.pop():50;var c=this.slice();b.each(function(e){e=new a(e);for(var f=0;f<3;f++){c[f]=Math.round((c[f]/100*(100-d))+(e[f]/100*d)); -}});return new a(c,"rgb");},invert:function(){return new a(this.map(function(b){return 255-b;}));},setHue:function(b){return new a([b,this.hsb[1],this.hsb[2]],"hsb"); -},setSaturation:function(b){return new a([this.hsb[0],b,this.hsb[2]],"hsb");},setBrightness:function(b){return new a([this.hsb[0],this.hsb[1],b],"hsb"); -}});this.$RGB=function(e,d,c){return new a([e,d,c],"rgb");};this.$HSB=function(e,d,c){return new a([e,d,c],"hsb");};this.$HEX=function(b){return new a(b,"hex"); -};Array.implement({rgbToHsb:function(){var c=this[0],d=this[1],k=this[2],h=0;var j=Math.max(c,d,k),f=Math.min(c,d,k);var l=j-f;var i=j/255,g=(j!=0)?l/j:0; -if(g!=0){var e=(j-c)/l;var b=(j-d)/l;var m=(j-k)/l;if(c==j){h=m-b;}else{if(d==j){h=2+e-m;}else{h=4+b-e;}}h/=6;if(h<0){h++;}}return[Math.round(h*360),Math.round(g*100),Math.round(i*100)]; -},hsbToRgb:function(){var d=Math.round(this[2]/100*255);if(this[1]==0){return[d,d,d];}else{var b=this[0]%360;var g=b%60;var h=Math.round((this[2]*(100-this[1]))/10000*255); -var e=Math.round((this[2]*(6000-this[1]*g))/600000*255);var c=Math.round((this[2]*(6000-this[1]*(60-g)))/600000*255);switch(Math.floor(b/60)){case 0:return[d,c,h]; -case 1:return[e,d,h];case 2:return[h,d,c];case 3:return[h,e,d];case 4:return[c,h,d];case 5:return[d,h,e];}}return false;}});String.implement({rgbToHsb:function(){var b=this.match(/\d{1,3}/g); -return(b)?b.rgbToHsb():null;},hsbToRgb:function(){var b=this.match(/\d{1,3}/g);return(b)?b.hsbToRgb():null;}});})();(function(){this.Group=new Class({initialize:function(){this.instances=Array.flatten(arguments); -this.events={};this.checker={};},addEvent:function(b,a){this.checker[b]=this.checker[b]||{};this.events[b]=this.events[b]||[];if(this.events[b].contains(a)){return false; -}else{this.events[b].push(a);}this.instances.each(function(c,d){c.addEvent(b,this.check.pass([b,c,d],this));},this);return this;},check:function(c,a,b){this.checker[c][b]=true; -var d=this.instances.every(function(f,e){return this.checker[c][e]||false;},this);if(!d){return;}this.checker[c]={};this.events[c].each(function(e){e.call(this,this.instances,a); -},this);}});})();Hash.Cookie=new Class({Extends:Cookie,options:{autoSave:true},initialize:function(b,a){this.parent(b,a);this.load();},save:function(){var a=JSON.encode(this.hash); -if(!a||a.length>4096){return false;}if(a=="{}"){this.dispose();}else{this.write(a);}return true;},load:function(){this.hash=new Hash(JSON.decode(this.read(),true)); -return this;}});Hash.each(Hash.prototype,function(b,a){if(typeof b=="function"){Hash.Cookie.implement(a,function(){var c=b.apply(this.hash,arguments);if(this.options.autoSave){this.save(); -}return c;});}});(function(){var a=this.Table=function(){this.length=0;var c=[],b=[];this.set=function(e,g){var d=c.indexOf(e);if(d==-1){var f=c.length; -c[f]=e;b[f]=g;this.length++;}else{b[d]=g;}return this;};this.get=function(e){var d=c.indexOf(e);return(d==-1)?null:b[d];};this.erase=function(e){var d=c.indexOf(e); -if(d!=-1){this.length--;c.splice(d,1);return b.splice(d,1)[0];}return null;};this.each=this.forEach=function(f,g){for(var e=0,d=this.length;e1?$$(a):a.length?document.id(a[0]):false;},setHeaders:function(a){this.set("headers",a); -return this;},setFooters:function(a){this.set("footers",a);return this;},push:function(f,c,e,a,b){if(typeOf(f)=="element"&&f.get("tag")=="tr"){f.inject(e||this.body,b); -return{tr:f,tds:f.getChildren("td")};}var d=f.map(function(i){var j=new Element(a||"td",i?i.properties:{}),h=(i?i.content:"")||i,g=typeOf(h);if(["element","array","collection","elements"].contains(g)){j.adopt(h); -}else{j.set("html",h);}return j;});return{tr:new Element("tr",c).inject(e||this.body,b).adopt(d),tds:d};}});["adopt","inject","wraps","grab","replaces","dispose"].each(function(a){HtmlTable.implement(a,function(){this.element[a].apply(this.element,arguments); -return this;});});HtmlTable=Class.refactor(HtmlTable,{options:{classZebra:"table-tr-odd",zebra:true},initialize:function(){this.previous.apply(this,arguments); -if(this.occluded){return this.occluded;}if(this.options.zebra){this.updateZebras();}},updateZebras:function(){Array.each(this.body.rows,this.zebra,this); -},setRowStyle:function(b,a){if(this.previous){this.previous(b,a);}this.zebra(b,a);},zebra:function(b,a){return b[((a%2)?"remove":"add")+"Class"](this.options.classZebra); -},push:function(){var a=this.previous.apply(this,arguments);if(this.options.zebra){this.updateZebras();}return a;}});HtmlTable=Class.refactor(HtmlTable,{options:{sortIndex:0,sortReverse:false,parsers:[],defaultParser:"string",classSortable:"table-sortable",classHeadSort:"table-th-sort",classHeadSortRev:"table-th-sort-rev",classNoSort:"table-th-nosort",classGroupHead:"table-tr-group-head",classGroup:"table-tr-group",classCellSort:"table-td-sort",classSortSpan:"table-th-sort-span",sortable:false,thSelector:"th"},initialize:function(){this.previous.apply(this,arguments); -if(this.occluded){return this.occluded;}this.sorted={index:null,dir:1};this.bound={headClick:this.headClick.bind(this)};this.sortSpans=new Elements();if(this.options.sortable){this.enableSort(); -if(this.options.sortIndex!=null){this.sort(this.options.sortIndex,this.options.sortReverse);}}},attachSorts:function(a){this.detachSorts();if(a!==false){this.element.addEvent("click:relay("+this.options.thSelector+")",this.bound.headClick); -}},detachSorts:function(){this.element.removeEvents("click:relay("+this.options.thSelector+")");},setHeaders:function(){this.previous.apply(this,arguments); -if(this.sortEnabled){this.setParsers();}},setParsers:function(){this.parsers=this.detectParsers();},detectParsers:function(){return this.head&&this.head.getElements(this.options.thSelector).flatten().map(this.detectParser,this); -},detectParser:function(a,b){if(a.hasClass(this.options.classNoSort)||a.retrieve("htmltable-parser")){return a.retrieve("htmltable-parser");}var c=new Element("div"); -c.adopt(a.childNodes).inject(a);var f=new Element("span",{"class":this.options.classSortSpan}).inject(c,"top");this.sortSpans.push(f);var g=this.options.parsers[b],e=this.body.rows,d; -switch(typeOf(g)){case"function":g={convert:g};d=true;break;case"string":g=g;d=true;break;}if(!d){HtmlTable.ParserPriority.some(function(k){var o=HtmlTable.Parsers[k],m=o.match; -if(!m){return false;}for(var n=0,l=e.length;nc){b[f].position--;}}}},setRowStyle:function(b,a){this.previous(b,a);b.cells[this.sorted.index].addClass(this.options.classCellSort); -},setGroupSort:function(b,c,a){if(b==a.value){c.removeClass(this.options.classGroupHead).addClass(this.options.classGroup);}else{c.removeClass(this.options.classGroup).addClass(this.options.classGroupHead); -}return a.value;},getParser:function(){var a=this.parsers[this.sorted.index];return typeOf(a)=="string"?HtmlTable.Parsers[a]:a;},sort:function(c,b,e){if(!this.head){return; -}if(!e){this.clearSort();this.setSortedState(c,b);this.setHeadSort(true);}var f=this.getParser();if(!f){return;}var a;if(!Browser.ie){a=this.body.getParent(); -this.body.dispose();}var d=this.parseData(f).sort(function(h,g){if(h.value===g.value){return 0;}return h.value>g.value?1:-1;});if(this.sorted.reverse==(f==HtmlTable.Parsers["input-checked"])){d.reverse(true); -}this.setRowSort(d,e);if(a){a.grab(this.body);}this.fireEvent("stateChanged");return this.fireEvent("sort",[this.body,this.sorted.index]);},parseData:function(a){return Array.map(this.body.rows,function(d,b){var c=a.convert.call(document.id(d.cells[this.sorted.index])); -return{position:b,value:c};},this);},clearSort:function(){this.setHeadSort(false);this.body.getElements("td").removeClass(this.options.classCellSort);},reSort:function(){if(this.sortEnabled){this.sort.call(this,this.sorted.index,this.sorted.reverse); -}return this;},enableSort:function(){this.element.addClass(this.options.classSortable);this.attachSorts(true);this.setParsers();this.sortEnabled=true;return this; -},disableSort:function(){this.element.removeClass(this.options.classSortable);this.attachSorts(false);this.sortSpans.each(function(a){a.destroy();});this.sortSpans.empty(); -this.sortEnabled=false;return this;}});HtmlTable.ParserPriority=["date","input-checked","input-value","float","number"];HtmlTable.Parsers={date:{match:/^\d{2}[-\/ ]\d{2}[-\/ ]\d{2,4}$/,convert:function(){var a=Date.parse(this.get("text").stripTags()); -return(typeOf(a)=="date")?a.format("db"):"";},type:"date"},"input-checked":{match:/ type="(radio|checkbox)" /,convert:function(){return this.getElement("input").checked; -}},"input-value":{match:/=b.length){a=null;}return a;},attachSelects:function(d){d=d!=null?d:true; -var g=d?"addEvents":"removeEvents";this.element[g]({mouseleave:this.bound.mouseleave,click:this.bound.activateKeyboard});this.body[g]({"click:relay(tr)":this.bound.clickRow,"contextmenu:relay(tr)":this.bound.clickRow}); -if(this.options.useKeyboard||this.keyboard){if(!this.keyboard){this.keyboard=new Keyboard();}if(!this.selectKeysDefined){this.selectKeysDefined=true;var f,e; -var c=function(i){var h=function(j){clearTimeout(f);j.preventDefault();var k=this.body.rows[this.getRowByOffset(i)];if(j.shift&&k&&this.isSelected(k)){this.deselectRow(this.focused); -this.focused=k;}else{if(k&&(!this.options.allowMultiSelect||!j.shift)){this.selectNone();}this.shiftFocus(i,j);}if(e){f=h.delay(100,this,j);}else{f=(function(){e=true; -h(j);}).delay(400);}}.bind(this);return h;}.bind(this);var b=function(){clearTimeout(f);e=false;};this.keyboard.addEvents({"keydown:shift+up":c(-1),"keydown:shift+down":c(1),"keyup:shift+up":b,"keyup:shift+down":b,"keyup:up":b,"keyup:down":b}); -var a="";if(this.options.allowMultiSelect&&this.options.shiftForMultiSelect&&this.options.useKeyboard){a=" (Shift multi-selects).";}this.keyboard.addShortcuts({"Select Previous Row":{keys:"up",shortcut:"up arrow",handler:c(-1),description:"Select the previous row in the table."+a},"Select Next Row":{keys:"down",shortcut:"down arrow",handler:c(1),description:"Select the next row in the table."+a}}); -}this.keyboard[d?"activate":"deactivate"]();}this.updateSelects();},mouseleave:function(){if(this.hovered){this.leaveRow(this.hovered);}}});var Scroller=new Class({Implements:[Events,Options],options:{area:20,velocity:1,onChange:function(a,b){this.element.scrollTo(a,b); -},fps:50},initialize:function(b,a){this.setOptions(a);this.element=document.id(b);this.docBody=document.id(this.element.getDocument().body);this.listener=(typeOf(this.element)!="element")?this.docBody:this.element; -this.timer=null;this.bound={attach:this.attach.bind(this),detach:this.detach.bind(this),getCoords:this.getCoords.bind(this)};},start:function(){this.listener.addEvents({mouseover:this.bound.attach,mouseleave:this.bound.detach}); -return this;},stop:function(){this.listener.removeEvents({mouseover:this.bound.attach,mouseleave:this.bound.detach});this.detach();this.timer=clearInterval(this.timer); -return this;},attach:function(){this.listener.addEvent("mousemove",this.bound.getCoords);},detach:function(){this.listener.removeEvent("mousemove",this.bound.getCoords); -this.timer=clearInterval(this.timer);},getCoords:function(a){this.page=(this.listener.get("tag")=="body")?a.client:a.page;if(!this.timer){this.timer=this.scroll.periodical(Math.round(1000/this.options.fps),this); -}},scroll:function(){var c=this.element.getSize(),a=this.element.getScroll(),h=this.element!=this.docBody?this.element.getOffsets():{x:0,y:0},d=this.element.getScrollSize(),g={x:0,y:0},e=this.options.area.top||this.options.area,b=this.options.area.bottom||this.options.area; -for(var f in this.page){if(this.page[f]<(e+h[f])&&a[f]!=0){g[f]=(this.page[f]-e-h[f])*this.options.velocity;}else{if(this.page[f]+b>(c[f]+h[f])&&a[f]+c[f]!=d[f]){g[f]=(this.page[f]-c[f]+b-h[f])*this.options.velocity; -}}g[f]=g[f].round();}if(g.y||g.x){this.fireEvent("change",[a.x+g.x,a.y+g.y]);}}});(function(){var a=function(c,b){return(c)?(typeOf(c)=="function"?c(b):b.get(c)):""; -};this.Tips=new Class({Implements:[Events,Options],options:{onShow:function(){this.tip.setStyle("display","block");},onHide:function(){this.tip.setStyle("display","none"); -},title:"title",text:function(b){return b.get("rel")||b.get("href");},showDelay:100,hideDelay:100,className:"tip-wrap",offset:{x:16,y:16},windowPadding:{x:0,y:0},fixed:false},initialize:function(){var b=Array.link(arguments,{options:Type.isObject,elements:function(c){return c!=null; -}});this.setOptions(b.options);if(b.elements){this.attach(b.elements);}this.container=new Element("div",{"class":"tip"});},toElement:function(){if(this.tip){return this.tip; -}this.tip=new Element("div",{"class":this.options.className,styles:{position:"absolute",top:0,left:0}}).adopt(new Element("div",{"class":"tip-top"}),this.container,new Element("div",{"class":"tip-bottom"})); -return this.tip;},attach:function(b){$$(b).each(function(d){var f=a(this.options.title,d),e=a(this.options.text,d);d.set("title","").store("tip:native",f).retrieve("tip:title",f); -d.retrieve("tip:text",e);this.fireEvent("attach",[d]);var c=["enter","leave"];if(!this.options.fixed){c.push("move");}c.each(function(h){var g=d.retrieve("tip:"+h); -if(!g){g=function(i){this["element"+h.capitalize()].apply(this,[i,d]);}.bind(this);}d.store("tip:"+h,g).addEvent("mouse"+h,g);},this);},this);return this; -},detach:function(b){$$(b).each(function(d){["enter","leave","move"].each(function(e){d.removeEvent("mouse"+e,d.retrieve("tip:"+e)).eliminate("tip:"+e); -});this.fireEvent("detach",[d]);if(this.options.title=="title"){var c=d.retrieve("tip:native");if(c){d.set("title",c);}}},this);return this;},elementEnter:function(c,b){clearTimeout(this.timer); -this.timer=(function(){this.container.empty();["title","text"].each(function(e){var d=b.retrieve("tip:"+e);var f=this["_"+e+"Element"]=new Element("div",{"class":"tip-"+e}).inject(this.container); -if(d){this.fill(f,d);}},this);this.show(b);this.position((this.options.fixed)?{page:b.getPosition()}:c);}).delay(this.options.showDelay,this);},elementLeave:function(c,b){clearTimeout(this.timer); -this.timer=this.hide.delay(this.options.hideDelay,this,b);this.fireForParent(c,b);},setTitle:function(b){if(this._titleElement){this._titleElement.empty(); -this.fill(this._titleElement,b);}return this;},setText:function(b){if(this._textElement){this._textElement.empty();this.fill(this._textElement,b);}return this; -},fireForParent:function(c,b){b=b.getParent();if(!b||b==document.body){return;}if(b.retrieve("tip:enter")){b.fireEvent("mouseenter",c);}else{this.fireForParent(c,b); -}},elementMove:function(c,b){this.position(c);},position:function(f){if(!this.tip){document.id(this);}var c=window.getSize(),b=window.getScroll(),g={x:this.tip.offsetWidth,y:this.tip.offsetHeight},d={x:"left",y:"top"},e={y:false,x2:false,y2:false,x:false},h={}; -for(var i in d){h[d[i]]=f.page[i]+this.options.offset[i];if(h[d[i]]<0){e[i]=true;}if((h[d[i]]+g[i]-b[i])>c[i]-this.options.windowPadding[i]){h[d[i]]=f.page[i]-this.options.offset[i]-g[i]; -e[i+"2"]=true;}}this.fireEvent("bound",e);this.tip.setStyles(h);},fill:function(b,c){if(typeof c=="string"){b.set("html",c);}else{b.adopt(c);}},show:function(b){if(!this.tip){document.id(this); -}if(!this.tip.getParent()){this.tip.inject(document.body);}this.fireEvent("show",[this.tip,b]);},hide:function(b){if(!this.tip){document.id(this);}this.fireEvent("hide",[this.tip,b]); -}});})();Locale.define("en-GB","Date",{dateOrder:["date","month","year"],shortDate:"%d/%m/%Y",shortTime:"%H:%M"}).inherit("en-US","Date"); \ No newline at end of file diff --git a/web/tools/mootools/mootools-more-1.3.2.1-nc.js b/web/tools/mootools/mootools-more-1.5.1.js similarity index 57% rename from web/tools/mootools/mootools-more-1.3.2.1-nc.js rename to web/tools/mootools/mootools-more-1.5.1.js index 4bf490e79..15a277029 100644 --- a/web/tools/mootools/mootools-more-1.3.2.1-nc.js +++ b/web/tools/mootools/mootools-more-1.5.1.js @@ -1,6 +1,7 @@ -// MooTools: the javascript framework. -// Load this file's selection again by visiting: http://mootools.net/more/09f3e47813269cd5026cbf8c1f828e72 -// Or build this file again with packager using: packager build More/Chain.Wait More/Array.Extras More/Date More/Date.Extras More/Number.Format More/String.Extras More/String.QueryString More/URI More/URI.Relative More/Hash More/Hash.Extras More/Element.Forms More/Elements.From More/Element.Event.Pseudos.Keys More/Element.Pin More/Element.Position More/Element.Shortcuts More/Form.Request More/Form.Request.Append More/Form.Validator.Inline More/Form.Validator.Extras More/OverText More/Fx.Accordion More/Fx.Move More/Fx.Reveal More/Fx.Slide More/Fx.SmoothScroll More/Fx.Sort More/Drag.Move More/Slider More/Sortables More/Request.JSONP More/Request.Queue More/Request.Periodical More/Assets More/Color More/Group More/Hash.Cookie More/Table More/HtmlTable.Zebra More/HtmlTable.Sort More/HtmlTable.Select More/Keyboard.Extras More/Mask More/Scroller More/Tips More/Locale.en-GB.Date +/* MooTools: the javascript framework. license: MIT-style license. copyright: Copyright (c) 2006-2015 [Valerio Proietti](http://mad4milk.net/).*/ +/* +Web Build: http://mootools.net/more/builder/a3048f4bfdf603b22a69c141dbd0fca9 +*/ /* --- @@ -31,11 +32,10 @@ provides: [MooTools.More] */ MooTools.More = { - 'version': '1.3.2.1', - 'build': 'e586bcd2496e9b22acfde32e12f84d49ce09e59d' + version: '1.5.1', + build: '2dd695ba957196ae4b0275a690765d6636a61ccd' }; - /* --- @@ -54,7 +54,7 @@ requires: - Core/Chain - Core/Element - Core/Fx - - /MooTools.More + - MooTools.More provides: [Chain.Wait] @@ -100,87 +100,3495 @@ provides: [Chain.Wait] })(); - /* --- -script: Array.Extras.js +script: Class.Binds.js -name: Array.Extras +name: Class.Binds -description: Extends the Array native object to include useful methods to work with arrays. +description: Automagically binds specified methods in a class to the instance of the class. license: MIT-style license authors: - - Christoph Pojer - - Sebastian Markbåge + - Aaron Newton requires: - - Core/Array + - Core/Class - MooTools.More -provides: [Array.Extras] +provides: [Class.Binds] ... */ -(function(nil){ +Class.Mutators.Binds = function(binds){ + if (!this.prototype.initialize) this.implement('initialize', function(){}); + return Array.from(binds).concat(this.prototype.Binds || []); +}; -Array.implement({ +Class.Mutators.initialize = function(initialize){ + return function(){ + Array.from(this.Binds).each(function(name){ + var original = this[name]; + if (original) this[name] = original.bind(this); + }, this); + return initialize.apply(this, arguments); + }; +}; - min: function(){ - return Math.min.apply(null, this); - }, +/* +--- - max: function(){ - return Math.max.apply(null, this); - }, +script: Class.Occlude.js - average: function(){ - return this.length ? this.sum() / this.length : 0; - }, +name: Class.Occlude - sum: function(){ - var result = 0, l = this.length; - if (l){ - while (l--) result += this[l]; +description: Prevents a class from being applied to a DOM element twice. + +license: MIT-style license. + +authors: + - Aaron Newton + +requires: + - Core/Class + - Core/Element + - MooTools.More + +provides: [Class.Occlude] + +... +*/ + +Class.Occlude = new Class({ + + occlude: function(property, element){ + element = document.id(element || this.element); + var instance = element.retrieve(property || this.property); + if (instance && !this.occluded) + return (this.occluded = instance); + + this.occluded = false; + element.store(property || this.property, this); + return this.occluded; + } + +}); + +/* +--- + +script: Class.Refactor.js + +name: Class.Refactor + +description: Extends a class onto itself with new property, preserving any items attached to the class's namespace. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Class + - MooTools.More + +# Some modules declare themselves dependent on Class.Refactor +provides: [Class.refactor, Class.Refactor] + +... +*/ + +Class.refactor = function(original, refactors){ + + Object.each(refactors, function(item, name){ + var origin = original.prototype[name]; + origin = (origin && origin.$origin) || origin || function(){}; + original.implement(name, (typeof item == 'function') ? function(){ + var old = this.previous; + this.previous = origin; + var value = item.apply(this, arguments); + this.previous = old; + return value; + } : item); + }); + + return original; + +}; + +/* +--- + +name: Events.Pseudos + +description: Adds the functionality to add pseudo events + +license: MIT-style license + +authors: + - Arian Stolwijk + +requires: [Core/Class.Extras, Core/Slick.Parser, MooTools.More] + +provides: [Events.Pseudos] + +... +*/ + +(function(){ + +Events.Pseudos = function(pseudos, addEvent, removeEvent){ + + var storeKey = '_monitorEvents:'; + + var storageOf = function(object){ + return { + store: object.store ? function(key, value){ + object.store(storeKey + key, value); + } : function(key, value){ + (object._monitorEvents || (object._monitorEvents = {}))[key] = value; + }, + retrieve: object.retrieve ? function(key, dflt){ + return object.retrieve(storeKey + key, dflt); + } : function(key, dflt){ + if (!object._monitorEvents) return dflt; + return object._monitorEvents[key] || dflt; + } + }; + }; + + var splitType = function(type){ + if (type.indexOf(':') == -1 || !pseudos) return null; + + var parsed = Slick.parse(type).expressions[0][0], + parsedPseudos = parsed.pseudos, + l = parsedPseudos.length, + splits = []; + + while (l--){ + var pseudo = parsedPseudos[l].key, + listener = pseudos[pseudo]; + if (listener != null) splits.push({ + event: parsed.tag, + value: parsedPseudos[l].value, + pseudo: pseudo, + original: type, + listener: listener + }); } - return result; - }, + return splits.length ? splits : null; + }; - unique: function(){ - return [].combine(this); - }, + return { - shuffle: function(){ - for (var i = this.length; i && --i;){ - var temp = this[i], r = Math.floor(Math.random() * ( i + 1 )); - this[i] = this[r]; - this[r] = temp; + addEvent: function(type, fn, internal){ + var split = splitType(type); + if (!split) return addEvent.call(this, type, fn, internal); + + var storage = storageOf(this), + events = storage.retrieve(type, []), + eventType = split[0].event, + args = Array.slice(arguments, 2), + stack = fn, + self = this; + + split.each(function(item){ + var listener = item.listener, + stackFn = stack; + if (listener == false) eventType += ':' + item.pseudo + '(' + item.value + ')'; + else stack = function(){ + listener.call(self, item, stackFn, arguments, stack); + }; + }); + + events.include({type: eventType, event: fn, monitor: stack}); + storage.store(type, events); + + if (type != eventType) addEvent.apply(this, [type, fn].concat(args)); + return addEvent.apply(this, [eventType, stack].concat(args)); + }, + + removeEvent: function(type, fn){ + var split = splitType(type); + if (!split) return removeEvent.call(this, type, fn); + + var storage = storageOf(this), + events = storage.retrieve(type); + if (!events) return this; + + var args = Array.slice(arguments, 2); + + removeEvent.apply(this, [type, fn].concat(args)); + events.each(function(monitor, i){ + if (!fn || monitor.event == fn) removeEvent.apply(this, [monitor.type, monitor.monitor].concat(args)); + delete events[i]; + }, this); + + storage.store(type, events); + return this; } + + }; + +}; + +var pseudos = { + + once: function(split, fn, args, monitor){ + fn.apply(this, args); + this.removeEvent(split.event, monitor) + .removeEvent(split.original, fn); + }, + + throttle: function(split, fn, args){ + if (!fn._throttled){ + fn.apply(this, args); + fn._throttled = setTimeout(function(){ + fn._throttled = false; + }, split.value || 250); + } + }, + + pause: function(split, fn, args){ + clearTimeout(fn._pause); + fn._pause = fn.delay(split.value || 250, this, args); + } + +}; + +Events.definePseudo = function(key, listener){ + pseudos[key] = listener; + return this; +}; + +Events.lookupPseudo = function(key){ + return pseudos[key]; +}; + +var proto = Events.prototype; +Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); + +['Request', 'Fx'].each(function(klass){ + if (this[klass]) this[klass].implement(Events.prototype); +}); + +})(); + +/* +--- + +script: Drag.js + +name: Drag + +description: The base Drag Class. Can be used to drag and resize Elements using mouse events. + +license: MIT-style license + +authors: + - Valerio Proietti + - Tom Occhinno + - Jan Kassens + +requires: + - Core/Events + - Core/Options + - Core/Element.Event + - Core/Element.Style + - Core/Element.Dimensions + - MooTools.More + +provides: [Drag] +... + +*/ + +var Drag = new Class({ + + Implements: [Events, Options], + + options: {/* + onBeforeStart: function(thisElement){}, + onStart: function(thisElement, event){}, + onSnap: function(thisElement){}, + onDrag: function(thisElement, event){}, + onCancel: function(thisElement){}, + onComplete: function(thisElement, event){},*/ + snap: 6, + unit: 'px', + grid: false, + style: true, + limit: false, + handle: false, + invert: false, + preventDefault: false, + stopPropagation: false, + compensateScroll: false, + modifiers: {x: 'left', y: 'top'} + }, + + initialize: function(){ + var params = Array.link(arguments, { + 'options': Type.isObject, + 'element': function(obj){ + return obj != null; + } + }); + + this.element = document.id(params.element); + this.document = this.element.getDocument(); + this.setOptions(params.options || {}); + var htype = typeOf(this.options.handle); + this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element; + this.mouse = {'now': {}, 'pos': {}}; + this.value = {'start': {}, 'now': {}}; + this.offsetParent = (function(el){ + var offsetParent = el.getOffsetParent(); + var isBody = !offsetParent || (/^(?:body|html)$/i).test(offsetParent.tagName); + return isBody ? window : document.id(offsetParent); + })(this.element); + this.selection = 'selectstart' in document ? 'selectstart' : 'mousedown'; + + this.compensateScroll = {start: {}, diff: {}, last: {}}; + + if ('ondragstart' in document && !('FileReader' in window) && !Drag.ondragstartFixed){ + document.ondragstart = Function.from(false); + Drag.ondragstartFixed = true; + } + + this.bound = { + start: this.start.bind(this), + check: this.check.bind(this), + drag: this.drag.bind(this), + stop: this.stop.bind(this), + cancel: this.cancel.bind(this), + eventStop: Function.from(false), + scrollListener: this.scrollListener.bind(this) + }; + this.attach(); + }, + + attach: function(){ + this.handles.addEvent('mousedown', this.bound.start); + if (this.options.compensateScroll) this.offsetParent.addEvent('scroll', this.bound.scrollListener); return this; }, - reduce: function(fn, value){ - for (var i = 0, l = this.length; i < l; i++){ - if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this); - } - return value; + detach: function(){ + this.handles.removeEvent('mousedown', this.bound.start); + if (this.options.compensateScroll) this.offsetParent.removeEvent('scroll', this.bound.scrollListener); + return this; }, - reduceRight: function(fn, value){ - var i = this.length; - while (i--){ - if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this); + scrollListener: function(){ + + if (!this.mouse.start) return; + var newScrollValue = this.offsetParent.getScroll(); + + if (this.element.getStyle('position') == 'absolute'){ + var scrollDiff = this.sumValues(newScrollValue, this.compensateScroll.last, -1); + this.mouse.now = this.sumValues(this.mouse.now, scrollDiff, 1); + } else { + this.compensateScroll.diff = this.sumValues(newScrollValue, this.compensateScroll.start, -1); } - return value; + if (this.offsetParent != window) this.compensateScroll.diff = this.sumValues(this.compensateScroll.start, newScrollValue, -1); + this.compensateScroll.last = newScrollValue; + this.render(this.options); + }, + + sumValues: function(alpha, beta, op){ + var sum = {}, options = this.options; + for (z in options.modifiers){ + if (!options.modifiers[z]) continue; + sum[z] = alpha[z] + beta[z] * op; + } + return sum; + }, + + start: function(event){ + var options = this.options; + + if (event.rightClick) return; + + if (options.preventDefault) event.preventDefault(); + if (options.stopPropagation) event.stopPropagation(); + this.compensateScroll.start = this.compensateScroll.last = this.offsetParent.getScroll(); + this.compensateScroll.diff = {x: 0, y: 0}; + this.mouse.start = event.page; + this.fireEvent('beforeStart', this.element); + + var limit = options.limit; + this.limit = {x: [], y: []}; + + var z, coordinates, offsetParent = this.offsetParent == window ? null : this.offsetParent; + for (z in options.modifiers){ + if (!options.modifiers[z]) continue; + + var style = this.element.getStyle(options.modifiers[z]); + + // Some browsers (IE and Opera) don't always return pixels. + if (style && !style.match(/px$/)){ + if (!coordinates) coordinates = this.element.getCoordinates(offsetParent); + style = coordinates[options.modifiers[z]]; + } + + if (options.style) this.value.now[z] = (style || 0).toInt(); + else this.value.now[z] = this.element[options.modifiers[z]]; + + if (options.invert) this.value.now[z] *= -1; + + this.mouse.pos[z] = event.page[z] - this.value.now[z]; + + if (limit && limit[z]){ + var i = 2; + while (i--){ + var limitZI = limit[z][i]; + if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI; + } + } + } + + if (typeOf(this.options.grid) == 'number') this.options.grid = { + x: this.options.grid, + y: this.options.grid + }; + + var events = { + mousemove: this.bound.check, + mouseup: this.bound.cancel + }; + events[this.selection] = this.bound.eventStop; + this.document.addEvents(events); + }, + + check: function(event){ + if (this.options.preventDefault) event.preventDefault(); + var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2))); + if (distance > this.options.snap){ + this.cancel(); + this.document.addEvents({ + mousemove: this.bound.drag, + mouseup: this.bound.stop + }); + this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element); + } + }, + + drag: function(event){ + var options = this.options; + if (options.preventDefault) event.preventDefault(); + this.mouse.now = this.sumValues(event.page, this.compensateScroll.diff, -1); + + this.render(options); + this.fireEvent('drag', [this.element, event]); + }, + + render: function(options){ + for (var z in options.modifiers){ + if (!options.modifiers[z]) continue; + this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z]; + + if (options.invert) this.value.now[z] *= -1; + if (options.limit && this.limit[z]){ + if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){ + this.value.now[z] = this.limit[z][1]; + } else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){ + this.value.now[z] = this.limit[z][0]; + } + } + if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]); + if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit); + else this.element[options.modifiers[z]] = this.value.now[z]; + } + }, + + cancel: function(event){ + this.document.removeEvents({ + mousemove: this.bound.check, + mouseup: this.bound.cancel + }); + if (event){ + this.document.removeEvent(this.selection, this.bound.eventStop); + this.fireEvent('cancel', this.element); + } + }, + + stop: function(event){ + var events = { + mousemove: this.bound.drag, + mouseup: this.bound.stop + }; + events[this.selection] = this.bound.eventStop; + this.document.removeEvents(events); + this.mouse.start = null; + if (event) this.fireEvent('complete', [this.element, event]); + } + +}); + +Element.implement({ + + makeResizable: function(options){ + var drag = new Drag(this, Object.merge({ + modifiers: { + x: 'width', + y: 'height' + } + }, options)); + + this.store('resizer', drag); + return drag.addEvent('drag', function(){ + this.fireEvent('resize', drag); + }.bind(this)); + } + +}); + +/* +--- + +script: Drag.Move.js + +name: Drag.Move + +description: A Drag extension that provides support for the constraining of draggables to containers and droppables. + +license: MIT-style license + +authors: + - Valerio Proietti + - Tom Occhinno + - Jan Kassens + - Aaron Newton + - Scott Kyle + +requires: + - Core/Element.Dimensions + - Drag + +provides: [Drag.Move] + +... +*/ + +Drag.Move = new Class({ + + Extends: Drag, + + options: {/* + onEnter: function(thisElement, overed){}, + onLeave: function(thisElement, overed){}, + onDrop: function(thisElement, overed, event){},*/ + droppables: [], + container: false, + precalculate: false, + includeMargins: true, + checkDroppables: true + }, + + initialize: function(element, options){ + this.parent(element, options); + element = this.element; + + this.droppables = $$(this.options.droppables); + this.setContainer(this.options.container); + + if (this.options.style){ + if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){ + var parent = element.getOffsetParent(), + styles = element.getStyles('left', 'top'); + if (parent && (styles.left == 'auto' || styles.top == 'auto')){ + element.setPosition(element.getPosition(parent)); + } + } + + if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute'); + } + + this.addEvent('start', this.checkDroppables, true); + this.overed = null; + }, + + setContainer: function(container) { + this.container = document.id(container); + if (this.container && typeOf(this.container) != 'element'){ + this.container = document.id(this.container.getDocument().body); + } + }, + + start: function(event){ + if (this.container) this.options.limit = this.calculateLimit(); + + if (this.options.precalculate){ + this.positions = this.droppables.map(function(el){ + return el.getCoordinates(); + }); + } + + this.parent(event); + }, + + calculateLimit: function(){ + var element = this.element, + container = this.container, + + offsetParent = document.id(element.getOffsetParent()) || document.body, + containerCoordinates = container.getCoordinates(offsetParent), + elementMargin = {}, + elementBorder = {}, + containerMargin = {}, + containerBorder = {}, + offsetParentPadding = {}, + offsetScroll = offsetParent.getScroll(); + + ['top', 'right', 'bottom', 'left'].each(function(pad){ + elementMargin[pad] = element.getStyle('margin-' + pad).toInt(); + elementBorder[pad] = element.getStyle('border-' + pad).toInt(); + containerMargin[pad] = container.getStyle('margin-' + pad).toInt(); + containerBorder[pad] = container.getStyle('border-' + pad).toInt(); + offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt(); + }, this); + + var width = element.offsetWidth + elementMargin.left + elementMargin.right, + height = element.offsetHeight + elementMargin.top + elementMargin.bottom, + left = 0 + offsetScroll.x, + top = 0 + offsetScroll.y, + right = containerCoordinates.right - containerBorder.right - width + offsetScroll.x, + bottom = containerCoordinates.bottom - containerBorder.bottom - height + offsetScroll.y; + + if (this.options.includeMargins){ + left += elementMargin.left; + top += elementMargin.top; + } else { + right += elementMargin.right; + bottom += elementMargin.bottom; + } + + if (element.getStyle('position') == 'relative'){ + var coords = element.getCoordinates(offsetParent); + coords.left -= element.getStyle('left').toInt(); + coords.top -= element.getStyle('top').toInt(); + + left -= coords.left; + top -= coords.top; + if (container.getStyle('position') != 'relative'){ + left += containerBorder.left; + top += containerBorder.top; + } + right += elementMargin.left - coords.left; + bottom += elementMargin.top - coords.top; + + if (container != offsetParent){ + left += containerMargin.left + offsetParentPadding.left; + if (!offsetParentPadding.left && left < 0) left = 0; + top += offsetParent == document.body ? 0 : containerMargin.top + offsetParentPadding.top; + if (!offsetParentPadding.top && top < 0) top = 0; + } + } else { + left -= elementMargin.left; + top -= elementMargin.top; + if (container != offsetParent){ + left += containerCoordinates.left + containerBorder.left; + top += containerCoordinates.top + containerBorder.top; + } + } + + return { + x: [left, right], + y: [top, bottom] + }; + }, + + getDroppableCoordinates: function(element){ + var position = element.getCoordinates(); + if (element.getStyle('position') == 'fixed'){ + var scroll = window.getScroll(); + position.left += scroll.x; + position.right += scroll.x; + position.top += scroll.y; + position.bottom += scroll.y; + } + return position; + }, + + checkDroppables: function(){ + var overed = this.droppables.filter(function(el, i){ + el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el); + var now = this.mouse.now; + return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top); + }, this).getLast(); + + if (this.overed != overed){ + if (this.overed) this.fireEvent('leave', [this.element, this.overed]); + if (overed) this.fireEvent('enter', [this.element, overed]); + this.overed = overed; + } + }, + + drag: function(event){ + this.parent(event); + if (this.options.checkDroppables && this.droppables.length) this.checkDroppables(); + }, + + stop: function(event){ + this.checkDroppables(); + this.fireEvent('drop', [this.element, this.overed, event]); + this.overed = null; + return this.parent(event); + } + +}); + +Element.implement({ + + makeDraggable: function(options){ + var drag = new Drag.Move(this, options); + this.store('dragger', drag); + return drag; + } + +}); + +/* +--- + +script: Element.Measure.js + +name: Element.Measure + +description: Extends the Element native object to include methods useful in measuring dimensions. + +credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz" + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Element.Style + - Core/Element.Dimensions + - MooTools.More + +provides: [Element.Measure] + +... +*/ + +(function(){ + +var getStylesList = function(styles, planes){ + var list = []; + Object.each(planes, function(directions){ + Object.each(directions, function(edge){ + styles.each(function(style){ + list.push(style + '-' + edge + (style == 'border' ? '-width' : '')); + }); + }); + }); + return list; +}; + +var calculateEdgeSize = function(edge, styles){ + var total = 0; + Object.each(styles, function(value, style){ + if (style.test(edge)) total = total + value.toInt(); + }); + return total; +}; + +var isVisible = function(el){ + return !!(!el || el.offsetHeight || el.offsetWidth); +}; + + +Element.implement({ + + measure: function(fn){ + if (isVisible(this)) return fn.call(this); + var parent = this.getParent(), + toMeasure = []; + while (!isVisible(parent) && parent != document.body){ + toMeasure.push(parent.expose()); + parent = parent.getParent(); + } + var restore = this.expose(), + result = fn.call(this); + restore(); + toMeasure.each(function(restore){ + restore(); + }); + return result; + }, + + expose: function(){ + if (this.getStyle('display') != 'none') return function(){}; + var before = this.style.cssText; + this.setStyles({ + display: 'block', + position: 'absolute', + visibility: 'hidden' + }); + return function(){ + this.style.cssText = before; + }.bind(this); + }, + + getDimensions: function(options){ + options = Object.merge({computeSize: false}, options); + var dim = {x: 0, y: 0}; + + var getSize = function(el, options){ + return (options.computeSize) ? el.getComputedSize(options) : el.getSize(); + }; + + var parent = this.getParent('body'); + + if (parent && this.getStyle('display') == 'none'){ + dim = this.measure(function(){ + return getSize(this, options); + }); + } else if (parent){ + try { //safari sometimes crashes here, so catch it + dim = getSize(this, options); + }catch(e){} + } + + return Object.append(dim, (dim.x || dim.x === 0) ? { + width: dim.x, + height: dim.y + } : { + x: dim.width, + y: dim.height + } + ); + }, + + getComputedSize: function(options){ + + + options = Object.merge({ + styles: ['padding','border'], + planes: { + height: ['top','bottom'], + width: ['left','right'] + }, + mode: 'both' + }, options); + + var styles = {}, + size = {width: 0, height: 0}, + dimensions; + + if (options.mode == 'vertical'){ + delete size.width; + delete options.planes.width; + } else if (options.mode == 'horizontal'){ + delete size.height; + delete options.planes.height; + } + + getStylesList(options.styles, options.planes).each(function(style){ + styles[style] = this.getStyle(style).toInt(); + }, this); + + Object.each(options.planes, function(edges, plane){ + + var capitalized = plane.capitalize(), + style = this.getStyle(plane); + + if (style == 'auto' && !dimensions) dimensions = this.getDimensions(); + + style = styles[plane] = (style == 'auto') ? dimensions[plane] : style.toInt(); + size['total' + capitalized] = style; + + edges.each(function(edge){ + var edgesize = calculateEdgeSize(edge, styles); + size['computed' + edge.capitalize()] = edgesize; + size['total' + capitalized] += edgesize; + }); + + }, this); + + return Object.append(size, styles); } }); })(); +/* +--- + +script: Slider.js + +name: Slider + +description: Class for creating horizontal and vertical slider controls. + +license: MIT-style license + +authors: + - Valerio Proietti + +requires: + - Core/Element.Dimensions + - Core/Number + - Class.Binds + - Drag + - Element.Measure + +provides: [Slider] + +... +*/ + +var Slider = new Class({ + + Implements: [Events, Options], + + Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'], + + options: {/* + onTick: function(intPosition){}, + onMove: function(){}, + onChange: function(intStep){}, + onComplete: function(strStep){},*/ + onTick: function(position){ + this.setKnobPosition(position); + }, + initialStep: 0, + snap: false, + offset: 0, + range: false, + wheel: false, + steps: 100, + mode: 'horizontal' + }, + + initialize: function(element, knob, options){ + this.setOptions(options); + options = this.options; + this.element = document.id(element); + knob = this.knob = document.id(knob); + this.previousChange = this.previousEnd = this.step = options.initialStep ? options.initialStep : options.range ? options.range[0] : 0; + + var limit = {}, + modifiers = {x: false, y: false}; + + switch (options.mode){ + case 'vertical': + this.axis = 'y'; + this.property = 'top'; + this.offset = 'offsetHeight'; + break; + case 'horizontal': + this.axis = 'x'; + this.property = 'left'; + this.offset = 'offsetWidth'; + } + + this.setSliderDimensions(); + this.setRange(options.range, null, true); + + if (knob.getStyle('position') == 'static') knob.setStyle('position', 'relative'); + knob.setStyle(this.property, -options.offset); + modifiers[this.axis] = this.property; + limit[this.axis] = [-options.offset, this.full - options.offset]; + + var dragOptions = { + snap: 0, + limit: limit, + modifiers: modifiers, + onDrag: this.draggedKnob, + onStart: this.draggedKnob, + onBeforeStart: (function(){ + this.isDragging = true; + }).bind(this), + onCancel: function(){ + this.isDragging = false; + }.bind(this), + onComplete: function(){ + this.isDragging = false; + this.draggedKnob(); + this.end(); + }.bind(this) + }; + if (options.snap) this.setSnap(dragOptions); + + this.drag = new Drag(knob, dragOptions); + if (options.initialStep != null) this.set(options.initialStep, true); + this.attach(); + }, + + attach: function(){ + this.element.addEvent('mousedown', this.clickedElement); + if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement); + this.drag.attach(); + return this; + }, + + detach: function(){ + this.element.removeEvent('mousedown', this.clickedElement) + .removeEvent('mousewheel', this.scrolledElement); + this.drag.detach(); + return this; + }, + + autosize: function(){ + this.setSliderDimensions() + .setKnobPosition(this.toPosition(this.step)); + this.drag.options.limit[this.axis] = [-this.options.offset, this.full - this.options.offset]; + if (this.options.snap) this.setSnap(); + return this; + }, + + setSnap: function(options){ + if (!options) options = this.drag.options; + options.grid = Math.ceil(this.stepWidth); + options.limit[this.axis][1] = this.element[this.offset]; + return this; + }, + + setKnobPosition: function(position){ + if (this.options.snap) position = this.toPosition(this.step); + this.knob.setStyle(this.property, position); + return this; + }, + + setSliderDimensions: function(){ + this.full = this.element.measure(function(){ + this.half = this.knob[this.offset] / 2; + return this.element[this.offset] - this.knob[this.offset] + (this.options.offset * 2); + }.bind(this)); + return this; + }, + + set: function(step, silently){ + if (!((this.range > 0) ^ (step < this.min))) step = this.min; + if (!((this.range > 0) ^ (step > this.max))) step = this.max; + + this.step = (step).round(this.modulus.decimalLength); + if (silently) this.checkStep().setKnobPosition(this.toPosition(this.step)); + else this.checkStep().fireEvent('tick', this.toPosition(this.step)).fireEvent('move').end(); + return this; + }, + + setRange: function(range, pos, silently){ + this.min = Array.pick([range[0], 0]); + this.max = Array.pick([range[1], this.options.steps]); + this.range = this.max - this.min; + this.steps = this.options.steps || this.full; + var stepSize = this.stepSize = Math.abs(this.range) / this.steps; + this.stepWidth = this.stepSize * this.full / Math.abs(this.range); + this.setModulus(); + + if (range) this.set(Array.pick([pos, this.step]).limit(this.min,this.max), silently); + return this; + }, + + setModulus: function(){ + var decimals = ((this.stepSize + '').split('.')[1] || []).length, + modulus = 1 + ''; + while (decimals--) modulus += '0'; + this.modulus = {multiplier: (modulus).toInt(10), decimalLength: modulus.length - 1}; + }, + + clickedElement: function(event){ + if (this.isDragging || event.target == this.knob) return; + + var dir = this.range < 0 ? -1 : 1, + position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half; + + position = position.limit(-this.options.offset, this.full - this.options.offset); + + this.step = (this.min + dir * this.toStep(position)).round(this.modulus.decimalLength); + + this.checkStep() + .fireEvent('tick', position) + .fireEvent('move') + .end(); + }, + + scrolledElement: function(event){ + var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0); + this.set(this.step + (mode ? -1 : 1) * this.stepSize); + event.stop(); + }, + + draggedKnob: function(){ + var dir = this.range < 0 ? -1 : 1, + position = this.drag.value.now[this.axis]; + + position = position.limit(-this.options.offset, this.full -this.options.offset); + + this.step = (this.min + dir * this.toStep(position)).round(this.modulus.decimalLength); + this.checkStep(); + this.fireEvent('move'); + }, + + checkStep: function(){ + var step = this.step; + if (this.previousChange != step){ + this.previousChange = step; + this.fireEvent('change', step); + } + return this; + }, + + end: function(){ + var step = this.step; + if (this.previousEnd !== step){ + this.previousEnd = step; + this.fireEvent('complete', step + ''); + } + return this; + }, + + toStep: function(position){ + var step = (position + this.options.offset) * this.stepSize / this.full * this.steps; + return this.options.steps ? (step - (step * this.modulus.multiplier) % (this.stepSize * this.modulus.multiplier) / this.modulus.multiplier).round(this.modulus.decimalLength) : step; + }, + + toPosition: function(step){ + return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset || 0; + } + +}); + +/* +--- + +script: Sortables.js + +name: Sortables + +description: Class for creating a drag and drop sorting interface for lists of items. + +license: MIT-style license + +authors: + - Tom Occhino + +requires: + - Core/Fx.Morph + - Drag.Move + +provides: [Sortables] + +... +*/ + +var Sortables = new Class({ + + Implements: [Events, Options], + + options: {/* + onSort: function(element, clone){}, + onStart: function(element, clone){}, + onComplete: function(element){},*/ + opacity: 1, + clone: false, + revert: false, + handle: false, + dragOptions: {}, + unDraggableTags: ['button', 'input', 'a', 'textarea', 'select', 'option'] + }, + + initialize: function(lists, options){ + this.setOptions(options); + + this.elements = []; + this.lists = []; + this.idle = true; + + this.addLists($$(document.id(lists) || lists)); + + if (!this.options.clone) this.options.revert = false; + if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({ + duration: 250, + link: 'cancel' + }, this.options.revert)); + }, + + attach: function(){ + this.addLists(this.lists); + return this; + }, + + detach: function(){ + this.lists = this.removeLists(this.lists); + return this; + }, + + addItems: function(){ + Array.flatten(arguments).each(function(element){ + this.elements.push(element); + var start = element.retrieve('sortables:start', function(event){ + this.start.call(this, event, element); + }.bind(this)); + (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start); + }, this); + return this; + }, + + addLists: function(){ + Array.flatten(arguments).each(function(list){ + this.lists.include(list); + this.addItems(list.getChildren()); + }, this); + return this; + }, + + removeItems: function(){ + return $$(Array.flatten(arguments).map(function(element){ + this.elements.erase(element); + var start = element.retrieve('sortables:start'); + (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start); + + return element; + }, this)); + }, + + removeLists: function(){ + return $$(Array.flatten(arguments).map(function(list){ + this.lists.erase(list); + this.removeItems(list.getChildren()); + + return list; + }, this)); + }, + + getDroppableCoordinates: function (element){ + var offsetParent = element.getOffsetParent(); + var position = element.getPosition(offsetParent); + var scroll = { + w: window.getScroll(), + offsetParent: offsetParent.getScroll() + }; + position.x += scroll.offsetParent.x; + position.y += scroll.offsetParent.y; + + if (offsetParent.getStyle('position') == 'fixed'){ + position.x -= scroll.w.x; + position.y -= scroll.w.y; + } + + return position; + }, + + getClone: function(event, element){ + if (!this.options.clone) return new Element(element.tagName).inject(document.body); + if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list); + var clone = element.clone(true).setStyles({ + margin: 0, + position: 'absolute', + visibility: 'hidden', + width: element.getStyle('width') + }).addEvent('mousedown', function(event){ + element.fireEvent('mousedown', event); + }); + //prevent the duplicated radio inputs from unchecking the real one + if (clone.get('html').test('radio')){ + clone.getElements('input[type=radio]').each(function(input, i){ + input.set('name', 'clone_' + i); + if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true); + }); + } + + return clone.inject(this.list).setPosition(this.getDroppableCoordinates(this.element)); + }, + + getDroppables: function(){ + var droppables = this.list.getChildren().erase(this.clone).erase(this.element); + if (!this.options.constrain) droppables.append(this.lists).erase(this.list); + return droppables; + }, + + insert: function(dragging, element){ + var where = 'inside'; + if (this.lists.contains(element)){ + this.list = element; + this.drag.droppables = this.getDroppables(); + } else { + where = this.element.getAllPrevious().contains(element) ? 'before' : 'after'; + } + this.element.inject(element, where); + this.fireEvent('sort', [this.element, this.clone]); + }, + + start: function(event, element){ + if ( + !this.idle || + event.rightClick || + (!this.options.handle && this.options.unDraggableTags.contains(event.target.get('tag'))) + ) return; + + this.idle = false; + this.element = element; + this.opacity = element.getStyle('opacity'); + this.list = element.getParent(); + this.clone = this.getClone(event, element); + + this.drag = new Drag.Move(this.clone, Object.merge({ + + droppables: this.getDroppables() + }, this.options.dragOptions)).addEvents({ + onSnap: function(){ + event.stop(); + this.clone.setStyle('visibility', 'visible'); + this.element.setStyle('opacity', this.options.opacity || 0); + this.fireEvent('start', [this.element, this.clone]); + }.bind(this), + onEnter: this.insert.bind(this), + onCancel: this.end.bind(this), + onComplete: this.end.bind(this) + }); + + this.clone.inject(this.element, 'before'); + this.drag.start(event); + }, + + end: function(){ + this.drag.detach(); + this.element.setStyle('opacity', this.opacity); + var self = this; + if (this.effect){ + var dim = this.element.getStyles('width', 'height'), + clone = this.clone, + pos = clone.computePosition(this.getDroppableCoordinates(clone)); + + var destroy = function(){ + this.removeEvent('cancel', destroy); + clone.destroy(); + self.reset(); + }; + + this.effect.element = clone; + this.effect.start({ + top: pos.top, + left: pos.left, + width: dim.width, + height: dim.height, + opacity: 0.25 + }).addEvent('cancel', destroy).chain(destroy); + } else { + this.clone.destroy(); + self.reset(); + } + + }, + + reset: function(){ + this.idle = true; + this.fireEvent('complete', this.element); + }, + + serialize: function(){ + var params = Array.link(arguments, { + modifier: Type.isFunction, + index: function(obj){ + return obj != null; + } + }); + var serial = this.lists.map(function(list){ + return list.getChildren().map(params.modifier || function(element){ + return element.get('id'); + }, this); + }, this); + + var index = params.index; + if (this.lists.length == 1) index = 0; + return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial; + } + +}); + +/* +--- + +name: Element.Event.Pseudos + +description: Adds the functionality to add pseudo events for Elements + +license: MIT-style license + +authors: + - Arian Stolwijk + +requires: [Core/Element.Event, Core/Element.Delegation, Events.Pseudos] + +provides: [Element.Event.Pseudos, Element.Delegation.Pseudo] + +... +*/ + +(function(){ + +var pseudos = {relay: false}, + copyFromEvents = ['once', 'throttle', 'pause'], + count = copyFromEvents.length; + +while (count--) pseudos[copyFromEvents[count]] = Events.lookupPseudo(copyFromEvents[count]); + +DOMEvent.definePseudo = function(key, listener){ + pseudos[key] = listener; + return this; +}; + +var proto = Element.prototype; +[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); + +})(); + +/* +--- + +name: Element.Event.Pseudos.Keys + +description: Adds functionality fire events if certain keycombinations are pressed + +license: MIT-style license + +authors: + - Arian Stolwijk + +requires: [Element.Event.Pseudos] + +provides: [Element.Event.Pseudos.Keys] + +... +*/ + +(function(){ + +var keysStoreKey = '$moo:keys-pressed', + keysKeyupStoreKey = '$moo:keys-keyup'; + + +DOMEvent.definePseudo('keys', function(split, fn, args){ + + var event = args[0], + keys = [], + pressed = this.retrieve(keysStoreKey, []), + value = split.value; + + if (value != '+') keys.append(value.replace('++', function(){ + keys.push('+'); // shift++ and shift+++a + return ''; + }).split('+')); + else keys = ['+']; + + pressed.include(event.key); + + if (keys.every(function(key){ + return pressed.contains(key); + })) fn.apply(this, args); + + this.store(keysStoreKey, pressed); + + if (!this.retrieve(keysKeyupStoreKey)){ + var keyup = function(event){ + (function(){ + pressed = this.retrieve(keysStoreKey, []).erase(event.key); + this.store(keysStoreKey, pressed); + }).delay(0, this); // Fix for IE + }; + this.store(keysKeyupStoreKey, keyup).addEvent('keyup', keyup); + } + +}); + +DOMEvent.defineKeys({ + '16': 'shift', + '17': 'control', + '18': 'alt', + '20': 'capslock', + '33': 'pageup', + '34': 'pagedown', + '35': 'end', + '36': 'home', + '144': 'numlock', + '145': 'scrolllock', + '186': ';', + '187': '=', + '188': ',', + '190': '.', + '191': '/', + '192': '`', + '219': '[', + '220': '\\', + '221': ']', + '222': "'", + '107': '+', + '109': '-', // subtract + '189': '-' // dash +}) + +})(); + +/* +--- + +script: String.Extras.js + +name: String.Extras + +description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc). + +license: MIT-style license + +authors: + - Aaron Newton + - Guillermo Rauch + - Christopher Pitt + +requires: + - Core/String + - Core/Array + - MooTools.More + +provides: [String.Extras] + +... +*/ + +(function(){ + +var special = { + 'a': /[àáâãäåăą]/g, + 'A': /[ÀÁÂÃÄÅĂĄ]/g, + 'c': /[ćčç]/g, + 'C': /[ĆČÇ]/g, + 'd': /[ďđ]/g, + 'D': /[ĎÐ]/g, + 'e': /[èéêëěę]/g, + 'E': /[ÈÉÊËĚĘ]/g, + 'g': /[ğ]/g, + 'G': /[Ğ]/g, + 'i': /[ìíîï]/g, + 'I': /[ÌÍÎÏ]/g, + 'l': /[ĺľł]/g, + 'L': /[ĹĽŁ]/g, + 'n': /[ñňń]/g, + 'N': /[ÑŇŃ]/g, + 'o': /[òóôõöøő]/g, + 'O': /[ÒÓÔÕÖØ]/g, + 'r': /[řŕ]/g, + 'R': /[ŘŔ]/g, + 's': /[ššş]/g, + 'S': /[ŠŞŚ]/g, + 't': /[ťţ]/g, + 'T': /[ŤŢ]/g, + 'u': /[ùúûůüµ]/g, + 'U': /[ÙÚÛŮÜ]/g, + 'y': /[ÿý]/g, + 'Y': /[ŸÝ]/g, + 'z': /[žźż]/g, + 'Z': /[ŽŹŻ]/g, + 'th': /[þ]/g, + 'TH': /[Þ]/g, + 'dh': /[ð]/g, + 'DH': /[Ð]/g, + 'ss': /[ß]/g, + 'oe': /[œ]/g, + 'OE': /[Œ]/g, + 'ae': /[æ]/g, + 'AE': /[Æ]/g +}, + +tidy = { + ' ': /[\xa0\u2002\u2003\u2009]/g, + '*': /[\xb7]/g, + '\'': /[\u2018\u2019]/g, + '"': /[\u201c\u201d]/g, + '...': /[\u2026]/g, + '-': /[\u2013]/g, +// '--': /[\u2014]/g, + '»': /[\uFFFD]/g +}, + +conversions = { + ms: 1, + s: 1000, + m: 6e4, + h: 36e5 +}, + +findUnits = /(\d*.?\d+)([msh]+)/; + +var walk = function(string, replacements){ + var result = string, key; + for (key in replacements) result = result.replace(replacements[key], key); + return result; +}; + +var getRegexForTag = function(tag, contents){ + tag = tag || ''; + var regstr = contents ? "<" + tag + "(?!\\w)[^>]*>([\\s\\S]*?)<\/" + tag + "(?!\\w)>" : "<\/?" + tag + "([^>]+)?>", + reg = new RegExp(regstr, "gi"); + return reg; +}; + +String.implement({ + + standardize: function(){ + return walk(this, special); + }, + + repeat: function(times){ + return new Array(times + 1).join(this); + }, + + pad: function(length, str, direction){ + if (this.length >= length) return this; + + var pad = (str == null ? ' ' : '' + str) + .repeat(length - this.length) + .substr(0, length - this.length); + + if (!direction || direction == 'right') return this + pad; + if (direction == 'left') return pad + this; + + return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil()); + }, + + getTags: function(tag, contents){ + return this.match(getRegexForTag(tag, contents)) || []; + }, + + stripTags: function(tag, contents){ + return this.replace(getRegexForTag(tag, contents), ''); + }, + + tidy: function(){ + return walk(this, tidy); + }, + + truncate: function(max, trail, atChar){ + var string = this; + if (trail == null && arguments.length == 1) trail = '…'; + if (string.length > max){ + string = string.substring(0, max); + if (atChar){ + var index = string.lastIndexOf(atChar); + if (index != -1) string = string.substr(0, index); + } + if (trail) string += trail; + } + return string; + }, + + ms: function(){ + // "Borrowed" from https://gist.github.com/1503944 + var units = findUnits.exec(this); + if (units == null) return Number(this); + return Number(units[1]) * conversions[units[2]]; + } + +}); + +})(); + +/* +--- + +script: Element.Forms.js + +name: Element.Forms + +description: Extends the Element native object to include methods useful in managing inputs. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Element + - String.Extras + - MooTools.More + +provides: [Element.Forms] + +... +*/ + +Element.implement({ + + tidy: function(){ + this.set('value', this.get('value').tidy()); + }, + + getTextInRange: function(start, end){ + return this.get('value').substring(start, end); + }, + + getSelectedText: function(){ + if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd()); + return document.selection.createRange().text; + }, + + getSelectedRange: function(){ + if (this.selectionStart != null){ + return { + start: this.selectionStart, + end: this.selectionEnd + }; + } + + var pos = { + start: 0, + end: 0 + }; + var range = this.getDocument().selection.createRange(); + if (!range || range.parentElement() != this) return pos; + var duplicate = range.duplicate(); + + if (this.type == 'text'){ + pos.start = 0 - duplicate.moveStart('character', -100000); + pos.end = pos.start + range.text.length; + } else { + var value = this.get('value'); + var offset = value.length; + duplicate.moveToElementText(this); + duplicate.setEndPoint('StartToEnd', range); + if (duplicate.text.length) offset -= value.match(/[\n\r]*$/)[0].length; + pos.end = offset - duplicate.text.length; + duplicate.setEndPoint('StartToStart', range); + pos.start = offset - duplicate.text.length; + } + return pos; + }, + + getSelectionStart: function(){ + return this.getSelectedRange().start; + }, + + getSelectionEnd: function(){ + return this.getSelectedRange().end; + }, + + setCaretPosition: function(pos){ + if (pos == 'end') pos = this.get('value').length; + this.selectRange(pos, pos); + return this; + }, + + getCaretPosition: function(){ + return this.getSelectedRange().start; + }, + + selectRange: function(start, end){ + if (this.setSelectionRange){ + this.focus(); + this.setSelectionRange(start, end); + } else { + var value = this.get('value'); + var diff = value.substr(start, end - start).replace(/\r/g, '').length; + start = value.substr(0, start).replace(/\r/g, '').length; + var range = this.createTextRange(); + range.collapse(true); + range.moveEnd('character', start + diff); + range.moveStart('character', start); + range.select(); + } + return this; + }, + + insertAtCursor: function(value, select){ + var pos = this.getSelectedRange(); + var text = this.get('value'); + this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length)); + if (select !== false) this.selectRange(pos.start, pos.start + value.length); + else this.setCaretPosition(pos.start + value.length); + return this; + }, + + insertAroundCursor: function(options, select){ + options = Object.append({ + before: '', + defaultMiddle: '', + after: '' + }, options); + + var value = this.getSelectedText() || options.defaultMiddle; + var pos = this.getSelectedRange(); + var text = this.get('value'); + + if (pos.start == pos.end){ + this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length)); + this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length); + } else { + var current = text.substring(pos.start, pos.end); + this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length)); + var selStart = pos.start + options.before.length; + if (select !== false) this.selectRange(selStart, selStart + current.length); + else this.setCaretPosition(selStart + text.length); + } + return this; + } + +}); + +/* +--- + +script: Element.Pin.js + +name: Element.Pin + +description: Extends the Element native object to include the pin method useful for fixed positioning for elements. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Element.Event + - Core/Element.Dimensions + - Core/Element.Style + - MooTools.More + +provides: [Element.Pin] + +... +*/ + +(function(){ + var supportsPositionFixed = false, + supportTested = false; + + var testPositionFixed = function(){ + var test = new Element('div').setStyles({ + position: 'fixed', + top: 0, + right: 0 + }).inject(document.body); + supportsPositionFixed = (test.offsetTop === 0); + test.dispose(); + supportTested = true; + }; + + Element.implement({ + + pin: function(enable, forceScroll){ + if (!supportTested) testPositionFixed(); + if (this.getStyle('display') == 'none') return this; + + var pinnedPosition, + scroll = window.getScroll(), + parent, + scrollFixer; + + if (enable !== false){ + pinnedPosition = this.getPosition(); + if (!this.retrieve('pin:_pinned')) { + var currentPosition = { + top: pinnedPosition.y - scroll.y, + left: pinnedPosition.x - scroll.x, + margin: '0px', + padding: '0px' + }; + + if (supportsPositionFixed && !forceScroll){ + this.setStyle('position', 'fixed').setStyles(currentPosition); + } else { + + parent = this.getOffsetParent(); + var position = this.getPosition(parent), + styles = this.getStyles('left', 'top'); + + if (parent && styles.left == 'auto' || styles.top == 'auto') this.setPosition(position); + if (this.getStyle('position') == 'static') this.setStyle('position', 'absolute'); + + position = { + x: styles.left.toInt() - scroll.x, + y: styles.top.toInt() - scroll.y + }; + + scrollFixer = function(){ + if (!this.retrieve('pin:_pinned')) return; + var scroll = window.getScroll(); + this.setStyles({ + left: position.x + scroll.x, + top: position.y + scroll.y + }); + }.bind(this); + + this.store('pin:_scrollFixer', scrollFixer); + window.addEvent('scroll', scrollFixer); + } + this.store('pin:_pinned', true); + } + + } else { + if (!this.retrieve('pin:_pinned')) return this; + + parent = this.getParent(); + var offsetParent = (parent.getComputedStyle('position') != 'static' ? parent : parent.getOffsetParent()); + + pinnedPosition = this.getPosition(); + + this.store('pin:_pinned', false); + scrollFixer = this.retrieve('pin:_scrollFixer'); + if (!scrollFixer){ + this.setStyles({ + position: 'absolute', + top: pinnedPosition.y + scroll.y, + left: pinnedPosition.x + scroll.x + }); + } else { + this.store('pin:_scrollFixer', null); + window.removeEvent('scroll', scrollFixer); + } + this.removeClass('isPinned'); + } + return this; + }, + + unpin: function(){ + return this.pin(false); + }, + + togglePin: function(){ + return this.pin(!this.retrieve('pin:_pinned')); + } + + }); + + + +})(); + +/* +--- + +script: Element.Position.js + +name: Element.Position + +description: Extends the Element native object to include methods useful positioning elements relative to others. + +license: MIT-style license + +authors: + - Aaron Newton + - Jacob Thornton + +requires: + - Core/Options + - Core/Element.Dimensions + - Element.Measure + +provides: [Element.Position] + +... +*/ + +(function(original){ + +var local = Element.Position = { + + options: {/* + edge: false, + returnPos: false, + minimum: {x: 0, y: 0}, + maximum: {x: 0, y: 0}, + relFixedPosition: false, + ignoreMargins: false, + ignoreScroll: false, + allowNegative: false,*/ + relativeTo: document.body, + position: { + x: 'center', //left, center, right + y: 'center' //top, center, bottom + }, + offset: {x: 0, y: 0} + }, + + getOptions: function(element, options){ + options = Object.merge({}, local.options, options); + local.setPositionOption(options); + local.setEdgeOption(options); + local.setOffsetOption(element, options); + local.setDimensionsOption(element, options); + return options; + }, + + setPositionOption: function(options){ + options.position = local.getCoordinateFromValue(options.position); + }, + + setEdgeOption: function(options){ + var edgeOption = local.getCoordinateFromValue(options.edge); + options.edge = edgeOption ? edgeOption : + (options.position.x == 'center' && options.position.y == 'center') ? {x: 'center', y: 'center'} : + {x: 'left', y: 'top'}; + }, + + setOffsetOption: function(element, options){ + var parentOffset = {x: 0, y: 0}; + var parentScroll = {x: 0, y: 0}; + var offsetParent = element.measure(function(){ + return document.id(this.getOffsetParent()); + }); + + if (!offsetParent || offsetParent == element.getDocument().body) return; + + parentScroll = offsetParent.getScroll(); + parentOffset = offsetParent.measure(function(){ + var position = this.getPosition(); + if (this.getStyle('position') == 'fixed'){ + var scroll = window.getScroll(); + position.x += scroll.x; + position.y += scroll.y; + } + return position; + }); + + options.offset = { + parentPositioned: offsetParent != document.id(options.relativeTo), + x: options.offset.x - parentOffset.x + parentScroll.x, + y: options.offset.y - parentOffset.y + parentScroll.y + }; + }, + + setDimensionsOption: function(element, options){ + options.dimensions = element.getDimensions({ + computeSize: true, + styles: ['padding', 'border', 'margin'] + }); + }, + + getPosition: function(element, options){ + var position = {}; + options = local.getOptions(element, options); + var relativeTo = document.id(options.relativeTo) || document.body; + + local.setPositionCoordinates(options, position, relativeTo); + if (options.edge) local.toEdge(position, options); + + var offset = options.offset; + position.left = ((position.x >= 0 || offset.parentPositioned || options.allowNegative) ? position.x : 0).toInt(); + position.top = ((position.y >= 0 || offset.parentPositioned || options.allowNegative) ? position.y : 0).toInt(); + + local.toMinMax(position, options); + + if (options.relFixedPosition || relativeTo.getStyle('position') == 'fixed') local.toRelFixedPosition(relativeTo, position); + if (options.ignoreScroll) local.toIgnoreScroll(relativeTo, position); + if (options.ignoreMargins) local.toIgnoreMargins(position, options); + + position.left = Math.ceil(position.left); + position.top = Math.ceil(position.top); + delete position.x; + delete position.y; + + return position; + }, + + setPositionCoordinates: function(options, position, relativeTo){ + var offsetY = options.offset.y, + offsetX = options.offset.x, + calc = (relativeTo == document.body) ? window.getScroll() : relativeTo.getPosition(), + top = calc.y, + left = calc.x, + winSize = window.getSize(); + + switch(options.position.x){ + case 'left': position.x = left + offsetX; break; + case 'right': position.x = left + offsetX + relativeTo.offsetWidth; break; + default: position.x = left + ((relativeTo == document.body ? winSize.x : relativeTo.offsetWidth) / 2) + offsetX; break; + } + + switch(options.position.y){ + case 'top': position.y = top + offsetY; break; + case 'bottom': position.y = top + offsetY + relativeTo.offsetHeight; break; + default: position.y = top + ((relativeTo == document.body ? winSize.y : relativeTo.offsetHeight) / 2) + offsetY; break; + } + }, + + toMinMax: function(position, options){ + var xy = {left: 'x', top: 'y'}, value; + ['minimum', 'maximum'].each(function(minmax){ + ['left', 'top'].each(function(lr){ + value = options[minmax] ? options[minmax][xy[lr]] : null; + if (value != null && ((minmax == 'minimum') ? position[lr] < value : position[lr] > value)) position[lr] = value; + }); + }); + }, + + toRelFixedPosition: function(relativeTo, position){ + var winScroll = window.getScroll(); + position.top += winScroll.y; + position.left += winScroll.x; + }, + + toIgnoreScroll: function(relativeTo, position){ + var relScroll = relativeTo.getScroll(); + position.top -= relScroll.y; + position.left -= relScroll.x; + }, + + toIgnoreMargins: function(position, options){ + position.left += options.edge.x == 'right' + ? options.dimensions['margin-right'] + : (options.edge.x != 'center' + ? -options.dimensions['margin-left'] + : -options.dimensions['margin-left'] + ((options.dimensions['margin-right'] + options.dimensions['margin-left']) / 2)); + + position.top += options.edge.y == 'bottom' + ? options.dimensions['margin-bottom'] + : (options.edge.y != 'center' + ? -options.dimensions['margin-top'] + : -options.dimensions['margin-top'] + ((options.dimensions['margin-bottom'] + options.dimensions['margin-top']) / 2)); + }, + + toEdge: function(position, options){ + var edgeOffset = {}, + dimensions = options.dimensions, + edge = options.edge; + + switch(edge.x){ + case 'left': edgeOffset.x = 0; break; + case 'right': edgeOffset.x = -dimensions.x - dimensions.computedRight - dimensions.computedLeft; break; + // center + default: edgeOffset.x = -(Math.round(dimensions.totalWidth / 2)); break; + } + + switch(edge.y){ + case 'top': edgeOffset.y = 0; break; + case 'bottom': edgeOffset.y = -dimensions.y - dimensions.computedTop - dimensions.computedBottom; break; + // center + default: edgeOffset.y = -(Math.round(dimensions.totalHeight / 2)); break; + } + + position.x += edgeOffset.x; + position.y += edgeOffset.y; + }, + + getCoordinateFromValue: function(option){ + if (typeOf(option) != 'string') return option; + option = option.toLowerCase(); + + return { + x: option.test('left') ? 'left' + : (option.test('right') ? 'right' : 'center'), + y: option.test(/upper|top/) ? 'top' + : (option.test('bottom') ? 'bottom' : 'center') + }; + } + +}; + +Element.implement({ + + position: function(options){ + if (options && (options.x != null || options.y != null)){ + return (original ? original.apply(this, arguments) : this); + } + var position = this.setStyle('position', 'absolute').calculatePosition(options); + return (options && options.returnPos) ? position : this.setStyles(position); + }, + + calculatePosition: function(options){ + return local.getPosition(this, options); + } + +}); + +})(Element.prototype.position); + +/* +--- + +script: Element.Shortcuts.js + +name: Element.Shortcuts + +description: Extends the Element native object to include some shortcut methods. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Element.Style + - MooTools.More + +provides: [Element.Shortcuts] + +... +*/ + +Element.implement({ + + isDisplayed: function(){ + return this.getStyle('display') != 'none'; + }, + + isVisible: function(){ + var w = this.offsetWidth, + h = this.offsetHeight; + return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none'; + }, + + toggle: function(){ + return this[this.isDisplayed() ? 'hide' : 'show'](); + }, + + hide: function(){ + var d; + try { + //IE fails here if the element is not in the dom + d = this.getStyle('display'); + } catch(e){} + if (d == 'none') return this; + return this.store('element:_originalDisplay', d || '').setStyle('display', 'none'); + }, + + show: function(display){ + if (!display && this.isDisplayed()) return this; + display = display || this.retrieve('element:_originalDisplay') || 'block'; + return this.setStyle('display', (display == 'none') ? 'block' : display); + }, + + swapClass: function(remove, add){ + return this.removeClass(remove).addClass(add); + } + +}); + +Document.implement({ + + clearSelection: function(){ + if (window.getSelection){ + var selection = window.getSelection(); + if (selection && selection.removeAllRanges) selection.removeAllRanges(); + } else if (document.selection && document.selection.empty){ + try { + //IE fails here if selected element is not in dom + document.selection.empty(); + } catch(e){} + } + } + +}); + +/* +--- + +script: Elements.From.js + +name: Elements.From + +description: Returns a collection of elements from a string of html. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/String + - Core/Element + - MooTools.More + +provides: [Elements.from, Elements.From] + +... +*/ + +Elements.from = function(text, excludeScripts){ + if (excludeScripts || excludeScripts == null) text = text.stripScripts(); + + var container, match = text.match(/^\s*(?:\s*)*<(t[dhr]|tbody|tfoot|thead)/i); + + if (match){ + container = new Element('table'); + var tag = match[1].toLowerCase(); + if (['td', 'th', 'tr'].contains(tag)){ + container = new Element('tbody').inject(container); + if (tag != 'tr') container = new Element('tr').inject(container); + } + } + + return (container || new Element('div')).set('html', text).getChildren(); +}; + +/* +--- + +script: IframeShim.js + +name: IframeShim + +description: Defines IframeShim, a class for obscuring select lists and flash objects in IE. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Element.Event + - Core/Element.Style + - Core/Options + - Core/Events + - Element.Position + - Class.Occlude + +provides: [IframeShim] + +... +*/ + +(function(){ + +var browsers = false; + + +this.IframeShim = new Class({ + + Implements: [Options, Events, Class.Occlude], + + options: { + className: 'iframeShim', + src: 'javascript:false;document.write("");', + display: false, + zIndex: null, + margin: 0, + offset: {x: 0, y: 0}, + browsers: browsers + }, + + property: 'IframeShim', + + initialize: function(element, options){ + this.element = document.id(element); + if (this.occlude()) return this.occluded; + this.setOptions(options); + this.makeShim(); + return this; + }, + + makeShim: function(){ + if (this.options.browsers){ + var zIndex = this.element.getStyle('zIndex').toInt(); + + if (!zIndex){ + zIndex = 1; + var pos = this.element.getStyle('position'); + if (pos == 'static' || !pos) this.element.setStyle('position', 'relative'); + this.element.setStyle('zIndex', zIndex); + } + zIndex = ((this.options.zIndex != null || this.options.zIndex === 0) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1; + if (zIndex < 0) zIndex = 1; + this.shim = new Element('iframe', { + src: this.options.src, + scrolling: 'no', + frameborder: 0, + styles: { + zIndex: zIndex, + position: 'absolute', + border: 'none', + filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)' + }, + 'class': this.options.className + }).store('IframeShim', this); + var inject = (function(){ + this.shim.inject(this.element, 'after'); + this[this.options.display ? 'show' : 'hide'](); + this.fireEvent('inject'); + }).bind(this); + if (!IframeShim.ready) window.addEvent('load', inject); + else inject(); + } else { + this.position = this.hide = this.show = this.dispose = Function.from(this); + } + }, + + position: function(){ + if (!IframeShim.ready || !this.shim) return this; + var size = this.element.measure(function(){ + return this.getSize(); + }); + if (this.options.margin != undefined){ + size.x = size.x - (this.options.margin * 2); + size.y = size.y - (this.options.margin * 2); + this.options.offset.x += this.options.margin; + this.options.offset.y += this.options.margin; + } + this.shim.set({width: size.x, height: size.y}).position({ + relativeTo: this.element, + offset: this.options.offset + }); + return this; + }, + + hide: function(){ + if (this.shim) this.shim.setStyle('display', 'none'); + return this; + }, + + show: function(){ + if (this.shim) this.shim.setStyle('display', 'block'); + return this.position(); + }, + + dispose: function(){ + if (this.shim) this.shim.dispose(); + return this; + }, + + destroy: function(){ + if (this.shim) this.shim.destroy(); + return this; + } + +}); + +})(); + +window.addEvent('load', function(){ + IframeShim.ready = true; +}); + +/* +--- + +script: Mask.js + +name: Mask + +description: Creates a mask element to cover another. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Options + - Core/Events + - Core/Element.Event + - Class.Binds + - Element.Position + - IframeShim + +provides: [Mask] + +... +*/ + +var Mask = new Class({ + + Implements: [Options, Events], + + Binds: ['position'], + + options: {/* + onShow: function(){}, + onHide: function(){}, + onDestroy: function(){}, + onClick: function(event){}, + inject: { + where: 'after', + target: null, + }, + hideOnClick: false, + id: null, + destroyOnHide: false,*/ + style: {}, + 'class': 'mask', + maskMargins: false, + useIframeShim: true, + iframeShimOptions: {} + }, + + initialize: function(target, options){ + this.target = document.id(target) || document.id(document.body); + this.target.store('mask', this); + this.setOptions(options); + this.render(); + this.inject(); + }, + + render: function(){ + this.element = new Element('div', { + 'class': this.options['class'], + id: this.options.id || 'mask-' + String.uniqueID(), + styles: Object.merge({}, this.options.style, { + display: 'none' + }), + events: { + click: function(event){ + this.fireEvent('click', event); + if (this.options.hideOnClick) this.hide(); + }.bind(this) + } + }); + + this.hidden = true; + }, + + toElement: function(){ + return this.element; + }, + + inject: function(target, where){ + where = where || (this.options.inject ? this.options.inject.where : '') || (this.target == document.body ? 'inside' : 'after'); + target = target || (this.options.inject && this.options.inject.target) || this.target; + + this.element.inject(target, where); + + if (this.options.useIframeShim){ + this.shim = new IframeShim(this.element, this.options.iframeShimOptions); + + this.addEvents({ + show: this.shim.show.bind(this.shim), + hide: this.shim.hide.bind(this.shim), + destroy: this.shim.destroy.bind(this.shim) + }); + } + }, + + position: function(){ + this.resize(this.options.width, this.options.height); + + this.element.position({ + relativeTo: this.target, + position: 'topLeft', + ignoreMargins: !this.options.maskMargins, + ignoreScroll: this.target == document.body + }); + + return this; + }, + + resize: function(x, y){ + var opt = { + styles: ['padding', 'border'] + }; + if (this.options.maskMargins) opt.styles.push('margin'); + + var dim = this.target.getComputedSize(opt); + if (this.target == document.body){ + this.element.setStyles({width: 0, height: 0}); + var win = window.getScrollSize(); + if (dim.totalHeight < win.y) dim.totalHeight = win.y; + if (dim.totalWidth < win.x) dim.totalWidth = win.x; + } + this.element.setStyles({ + width: Array.pick([x, dim.totalWidth, dim.x]), + height: Array.pick([y, dim.totalHeight, dim.y]) + }); + + return this; + }, + + show: function(){ + if (!this.hidden) return this; + + window.addEvent('resize', this.position); + this.position(); + this.showMask.apply(this, arguments); + + return this; + }, + + showMask: function(){ + this.element.setStyle('display', 'block'); + this.hidden = false; + this.fireEvent('show'); + }, + + hide: function(){ + if (this.hidden) return this; + + window.removeEvent('resize', this.position); + this.hideMask.apply(this, arguments); + if (this.options.destroyOnHide) return this.destroy(); + + return this; + }, + + hideMask: function(){ + this.element.setStyle('display', 'none'); + this.hidden = true; + this.fireEvent('hide'); + }, + + toggle: function(){ + this[this.hidden ? 'show' : 'hide'](); + }, + + destroy: function(){ + this.hide(); + this.element.destroy(); + this.fireEvent('destroy'); + this.target.eliminate('mask'); + } + +}); + +Element.Properties.mask = { + + set: function(options){ + var mask = this.retrieve('mask'); + if (mask) mask.destroy(); + return this.eliminate('mask').store('mask:options', options); + }, + + get: function(){ + var mask = this.retrieve('mask'); + if (!mask){ + mask = new Mask(this, this.retrieve('mask:options')); + this.store('mask', mask); + } + return mask; + } + +}; + +Element.implement({ + + mask: function(options){ + if (options) this.set('mask', options); + this.get('mask').show(); + return this; + }, + + unmask: function(){ + this.get('mask').hide(); + return this; + } + +}); + +/* +--- + +script: Spinner.js + +name: Spinner + +description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Fx.Tween + - Core/Request + - Class.refactor + - Mask + +provides: [Spinner] + +... +*/ + +var Spinner = new Class({ + + Extends: Mask, + + Implements: Chain, + + options: {/* + message: false,*/ + 'class': 'spinner', + containerPosition: {}, + content: { + 'class': 'spinner-content' + }, + messageContainer: { + 'class': 'spinner-msg' + }, + img: { + 'class': 'spinner-img' + }, + fxOptions: { + link: 'chain' + } + }, + + initialize: function(target, options){ + this.target = document.id(target) || document.id(document.body); + this.target.store('spinner', this); + this.setOptions(options); + this.render(); + this.inject(); + + // Add this to events for when noFx is true; parent methods handle hide/show. + var deactivate = function(){ this.active = false; }.bind(this); + this.addEvents({ + hide: deactivate, + show: deactivate + }); + }, + + render: function(){ + this.parent(); + + this.element.set('id', this.options.id || 'spinner-' + String.uniqueID()); + + this.content = document.id(this.options.content) || new Element('div', this.options.content); + this.content.inject(this.element); + + if (this.options.message){ + this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message); + this.msg.inject(this.content); + } + + if (this.options.img){ + this.img = document.id(this.options.img) || new Element('div', this.options.img); + this.img.inject(this.content); + } + + this.element.set('tween', this.options.fxOptions); + }, + + show: function(noFx){ + if (this.active) return this.chain(this.show.bind(this)); + if (!this.hidden){ + this.callChain.delay(20, this); + return this; + } + + this.target.set('aria-busy', 'true'); + this.active = true; + + return this.parent(noFx); + }, + + showMask: function(noFx){ + var pos = function(){ + this.content.position(Object.merge({ + relativeTo: this.element + }, this.options.containerPosition)); + }.bind(this); + + if (noFx){ + this.parent(); + pos(); + } else { + if (!this.options.style.opacity) this.options.style.opacity = this.element.getStyle('opacity').toFloat(); + this.element.setStyles({ + display: 'block', + opacity: 0 + }).tween('opacity', this.options.style.opacity); + pos(); + this.hidden = false; + this.fireEvent('show'); + this.callChain(); + } + }, + + hide: function(noFx){ + if (this.active) return this.chain(this.hide.bind(this)); + if (this.hidden){ + this.callChain.delay(20, this); + return this; + } + + this.target.set('aria-busy', 'false'); + this.active = true; + + return this.parent(noFx); + }, + + hideMask: function(noFx){ + if (noFx) return this.parent(); + this.element.tween('opacity', 0).get('tween').chain(function(){ + this.element.setStyle('display', 'none'); + this.hidden = true; + this.fireEvent('hide'); + this.callChain(); + }.bind(this)); + }, + + destroy: function(){ + this.content.destroy(); + this.parent(); + this.target.eliminate('spinner'); + } + +}); + +Request = Class.refactor(Request, { + + options: { + useSpinner: false, + spinnerOptions: {}, + spinnerTarget: false + }, + + initialize: function(options){ + this._send = this.send; + this.send = function(options){ + var spinner = this.getSpinner(); + if (spinner) spinner.chain(this._send.pass(options, this)).show(); + else this._send(options); + return this; + }; + this.previous(options); + }, + + getSpinner: function(){ + if (!this.spinner){ + var update = document.id(this.options.spinnerTarget) || document.id(this.options.update); + if (this.options.useSpinner && update){ + update.set('spinner', this.options.spinnerOptions); + var spinner = this.spinner = update.get('spinner'); + ['complete', 'exception', 'cancel'].each(function(event){ + this.addEvent(event, spinner.hide.bind(spinner)); + }, this); + } + } + return this.spinner; + } + +}); + +Element.Properties.spinner = { + + set: function(options){ + var spinner = this.retrieve('spinner'); + if (spinner) spinner.destroy(); + return this.eliminate('spinner').store('spinner:options', options); + }, + + get: function(){ + var spinner = this.retrieve('spinner'); + if (!spinner){ + spinner = new Spinner(this, this.retrieve('spinner:options')); + this.store('spinner', spinner); + } + return spinner; + } + +}; + +Element.implement({ + + spin: function(options){ + if (options) this.set('spinner', options); + this.get('spinner').show(); + return this; + }, + + unspin: function(){ + this.get('spinner').hide(); + return this; + } + +}); + +/* +--- + +script: String.QueryString.js + +name: String.QueryString + +description: Methods for dealing with URI query strings. + +license: MIT-style license + +authors: + - Sebastian Markbåge + - Aaron Newton + - Lennart Pilon + - Valerio Proietti + +requires: + - Core/Array + - Core/String + - MooTools.More + +provides: [String.QueryString] + +... +*/ + +String.implement({ + + parseQueryString: function(decodeKeys, decodeValues){ + if (decodeKeys == null) decodeKeys = true; + if (decodeValues == null) decodeValues = true; + + var vars = this.split(/[&;]/), + object = {}; + if (!vars.length) return object; + + vars.each(function(val){ + var index = val.indexOf('=') + 1, + value = index ? val.substr(index) : '', + keys = index ? val.substr(0, index - 1).match(/([^\]\[]+|(\B)(?=\]))/g) : [val], + obj = object; + if (!keys) return; + if (decodeValues) value = decodeURIComponent(value); + keys.each(function(key, i){ + if (decodeKeys) key = decodeURIComponent(key); + var current = obj[key]; + + if (i < keys.length - 1) obj = obj[key] = current || {}; + else if (typeOf(current) == 'array') current.push(value); + else obj[key] = current != null ? [current, value] : value; + }); + }); + + return object; + }, + + cleanQueryString: function(method){ + return this.split('&').filter(function(val){ + var index = val.indexOf('='), + key = index < 0 ? '' : val.substr(0, index), + value = val.substr(index + 1); + + return method ? method.call(null, key, value) : (value || value === 0); + }).join('&'); + } + +}); + +/* +--- + +script: Form.Request.js + +name: Form.Request + +description: Handles the basic functionality of submitting a form and updating a dom element with the result. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Request.HTML + - Class.Binds + - Class.Occlude + - Spinner + - String.QueryString + - Element.Delegation.Pseudo + +provides: [Form.Request] + +... +*/ + +if (!window.Form) window.Form = {}; + +(function(){ + + Form.Request = new Class({ + + Binds: ['onSubmit', 'onFormValidate'], + + Implements: [Options, Events, Class.Occlude], + + options: {/* + onFailure: function(){}, + onSuccess: function(){}, // aliased to onComplete, + onSend: function(){}*/ + requestOptions: { + evalScripts: true, + useSpinner: true, + emulation: false, + link: 'ignore' + }, + sendButtonClicked: true, + extraData: {}, + resetForm: true + }, + + property: 'form.request', + + initialize: function(form, target, options){ + this.element = document.id(form); + if (this.occlude()) return this.occluded; + this.setOptions(options) + .setTarget(target) + .attach(); + }, + + setTarget: function(target){ + this.target = document.id(target); + if (!this.request){ + this.makeRequest(); + } else { + this.request.setOptions({ + update: this.target + }); + } + return this; + }, + + toElement: function(){ + return this.element; + }, + + makeRequest: function(){ + var self = this; + this.request = new Request.HTML(Object.merge({ + update: this.target, + emulation: false, + spinnerTarget: this.element, + method: this.element.get('method') || 'post' + }, this.options.requestOptions)).addEvents({ + success: function(tree, elements, html, javascript){ + ['complete', 'success'].each(function(evt){ + self.fireEvent(evt, [self.target, tree, elements, html, javascript]); + }); + }, + failure: function(){ + self.fireEvent('complete', arguments).fireEvent('failure', arguments); + }, + exception: function(){ + self.fireEvent('failure', arguments); + } + }); + return this.attachReset(); + }, + + attachReset: function(){ + if (!this.options.resetForm) return this; + this.request.addEvent('success', function(){ + Function.attempt(function(){ + this.element.reset(); + }.bind(this)); + if (window.OverText) OverText.update(); + }.bind(this)); + return this; + }, + + attach: function(attach){ + var method = (attach != false) ? 'addEvent' : 'removeEvent'; + this.element[method]('click:relay(button, input[type=submit])', this.saveClickedButton.bind(this)); + + var fv = this.element.retrieve('validator'); + if (fv) fv[method]('onFormValidate', this.onFormValidate); + else this.element[method]('submit', this.onSubmit); + + return this; + }, + + detach: function(){ + return this.attach(false); + }, + + //public method + enable: function(){ + return this.attach(); + }, + + //public method + disable: function(){ + return this.detach(); + }, + + onFormValidate: function(valid, form, event){ + //if there's no event, then this wasn't a submit event + if (!event) return; + var fv = this.element.retrieve('validator'); + if (valid || (fv && !fv.options.stopOnFailure)){ + event.stop(); + this.send(); + } + }, + + onSubmit: function(event){ + var fv = this.element.retrieve('validator'); + if (fv){ + //form validator was created after Form.Request + this.element.removeEvent('submit', this.onSubmit); + fv.addEvent('onFormValidate', this.onFormValidate); + fv.validate(event); + return; + } + if (event) event.stop(); + this.send(); + }, + + saveClickedButton: function(event, target){ + var targetName = target.get('name'); + if (!targetName || !this.options.sendButtonClicked) return; + this.options.extraData[targetName] = target.get('value') || true; + this.clickedCleaner = function(){ + delete this.options.extraData[targetName]; + this.clickedCleaner = function(){}; + }.bind(this); + }, + + clickedCleaner: function(){}, + + send: function(){ + var str = this.element.toQueryString().trim(), + data = Object.toQueryString(this.options.extraData); + + if (str) str += "&" + data; + else str = data; + + this.fireEvent('send', [this.element, str.parseQueryString()]); + this.request.send({ + data: str, + url: this.options.requestOptions.url || this.element.get('action') + }); + this.clickedCleaner(); + return this; + } + + }); + + Element.implement('formUpdate', function(update, options){ + var fq = this.retrieve('form.request'); + if (!fq){ + fq = new Form.Request(this, update, options); + } else { + if (update) fq.setTarget(update); + if (options) fq.setOptions(options).makeRequest(); + } + fq.send(); + return this; + }); + +})(); + +/* +--- + +script: Fx.Reveal.js + +name: Fx.Reveal + +description: Defines Fx.Reveal, a class that shows and hides elements with a transition. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Fx.Morph + - Element.Shortcuts + - Element.Measure + +provides: [Fx.Reveal] + +... +*/ + +(function(){ + + +var hideTheseOf = function(object){ + var hideThese = object.options.hideInputs; + if (window.OverText){ + var otClasses = [null]; + OverText.each(function(ot){ + otClasses.include('.' + ot.options.labelClass); + }); + if (otClasses) hideThese += otClasses.join(', '); + } + return (hideThese) ? object.element.getElements(hideThese) : null; +}; + + +Fx.Reveal = new Class({ + + Extends: Fx.Morph, + + options: {/* + onShow: function(thisElement){}, + onHide: function(thisElement){}, + onComplete: function(thisElement){}, + heightOverride: null, + widthOverride: null,*/ + link: 'cancel', + styles: ['padding', 'border', 'margin'], + transitionOpacity: 'opacity' in document.documentElement, + mode: 'vertical', + display: function(){ + return this.element.get('tag') != 'tr' ? 'block' : 'table-row'; + }, + opacity: 1, + hideInputs: !('opacity' in document.documentElement) ? 'select, input, textarea, object, embed' : null + }, + + dissolve: function(){ + if (!this.hiding && !this.showing){ + if (this.element.getStyle('display') != 'none'){ + this.hiding = true; + this.showing = false; + this.hidden = true; + this.cssText = this.element.style.cssText; + + var startStyles = this.element.getComputedSize({ + styles: this.options.styles, + mode: this.options.mode + }); + if (this.options.transitionOpacity) startStyles.opacity = this.options.opacity; + + var zero = {}; + Object.each(startStyles, function(style, name){ + zero[name] = [style, 0]; + }); + + this.element.setStyles({ + display: Function.from(this.options.display).call(this), + overflow: 'hidden' + }); + + var hideThese = hideTheseOf(this); + if (hideThese) hideThese.setStyle('visibility', 'hidden'); + + this.$chain.unshift(function(){ + if (this.hidden){ + this.hiding = false; + this.element.style.cssText = this.cssText; + this.element.setStyle('display', 'none'); + if (hideThese) hideThese.setStyle('visibility', 'visible'); + } + this.fireEvent('hide', this.element); + this.callChain(); + }.bind(this)); + + this.start(zero); + } else { + this.callChain.delay(10, this); + this.fireEvent('complete', this.element); + this.fireEvent('hide', this.element); + } + } else if (this.options.link == 'chain'){ + this.chain(this.dissolve.bind(this)); + } else if (this.options.link == 'cancel' && !this.hiding){ + this.cancel(); + this.dissolve(); + } + return this; + }, + + reveal: function(){ + if (!this.showing && !this.hiding){ + if (this.element.getStyle('display') == 'none'){ + this.hiding = false; + this.showing = true; + this.hidden = false; + this.cssText = this.element.style.cssText; + + var startStyles; + this.element.measure(function(){ + startStyles = this.element.getComputedSize({ + styles: this.options.styles, + mode: this.options.mode + }); + }.bind(this)); + if (this.options.heightOverride != null) startStyles.height = this.options.heightOverride.toInt(); + if (this.options.widthOverride != null) startStyles.width = this.options.widthOverride.toInt(); + if (this.options.transitionOpacity){ + this.element.setStyle('opacity', 0); + startStyles.opacity = this.options.opacity; + } + + var zero = { + height: 0, + display: Function.from(this.options.display).call(this) + }; + Object.each(startStyles, function(style, name){ + zero[name] = 0; + }); + zero.overflow = 'hidden'; + + this.element.setStyles(zero); + + var hideThese = hideTheseOf(this); + if (hideThese) hideThese.setStyle('visibility', 'hidden'); + + this.$chain.unshift(function(){ + this.element.style.cssText = this.cssText; + this.element.setStyle('display', Function.from(this.options.display).call(this)); + if (!this.hidden) this.showing = false; + if (hideThese) hideThese.setStyle('visibility', 'visible'); + this.callChain(); + this.fireEvent('show', this.element); + }.bind(this)); + + this.start(startStyles); + } else { + this.callChain(); + this.fireEvent('complete', this.element); + this.fireEvent('show', this.element); + } + } else if (this.options.link == 'chain'){ + this.chain(this.reveal.bind(this)); + } else if (this.options.link == 'cancel' && !this.showing){ + this.cancel(); + this.reveal(); + } + return this; + }, + + toggle: function(){ + if (this.element.getStyle('display') == 'none'){ + this.reveal(); + } else { + this.dissolve(); + } + return this; + }, + + cancel: function(){ + this.parent.apply(this, arguments); + if (this.cssText != null) this.element.style.cssText = this.cssText; + this.hiding = false; + this.showing = false; + return this; + } + +}); + +Element.Properties.reveal = { + + set: function(options){ + this.get('reveal').cancel().setOptions(options); + return this; + }, + + get: function(){ + var reveal = this.retrieve('reveal'); + if (!reveal){ + reveal = new Fx.Reveal(this); + this.store('reveal', reveal); + } + return reveal; + } + +}; + +Element.Properties.dissolve = Element.Properties.reveal; + +Element.implement({ + + reveal: function(options){ + this.get('reveal').setOptions(options).reveal(); + return this; + }, + + dissolve: function(options){ + this.get('reveal').setOptions(options).dissolve(); + return this; + }, + + nix: function(options){ + var params = Array.link(arguments, {destroy: Type.isBoolean, options: Type.isObject}); + this.get('reveal').setOptions(options).dissolve().chain(function(){ + this[params.destroy ? 'destroy' : 'dispose'](); + }.bind(this)); + return this; + }, + + wink: function(){ + var params = Array.link(arguments, {duration: Type.isNumber, options: Type.isObject}); + var reveal = this.get('reveal').setOptions(params.options); + reveal.reveal().chain(function(){ + (function(){ + reveal.dissolve(); + }).delay(params.duration || 2000); + }); + } + +}); + +})(); + +/* +--- + +script: Form.Request.Append.js + +name: Form.Request.Append + +description: Handles the basic functionality of submitting a form and updating a dom element with the result. The result is appended to the DOM element instead of replacing its contents. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Form.Request + - Fx.Reveal + - Elements.from + +provides: [Form.Request.Append] + +... +*/ + +Form.Request.Append = new Class({ + + Extends: Form.Request, + + options: { + //onBeforeEffect: function(){}, + useReveal: true, + revealOptions: {}, + inject: 'bottom' + }, + + makeRequest: function(){ + this.request = new Request.HTML(Object.merge({ + url: this.element.get('action'), + method: this.element.get('method') || 'post', + spinnerTarget: this.element + }, this.options.requestOptions, { + evalScripts: false + }) + ).addEvents({ + success: function(tree, elements, html, javascript){ + var container; + var kids = Elements.from(html); + if (kids.length == 1){ + container = kids[0]; + } else { + container = new Element('div', { + styles: { + display: 'none' + } + }).adopt(kids); + } + container.inject(this.target, this.options.inject); + if (this.options.requestOptions.evalScripts) Browser.exec(javascript); + this.fireEvent('beforeEffect', container); + var finish = function(){ + this.fireEvent('success', [container, this.target, tree, elements, html, javascript]); + }.bind(this); + if (this.options.useReveal){ + container.set('reveal', this.options.revealOptions).get('reveal').chain(finish); + container.reveal(); + } else { + finish(); + } + }.bind(this), + failure: function(xhr){ + this.fireEvent('failure', xhr); + }.bind(this) + }); + this.attachReset(); + } + +}); /* --- @@ -198,7 +3606,7 @@ authors: requires: - Core/Object - - /MooTools.More + - MooTools.More provides: [Object.Extras] @@ -249,7 +3657,6 @@ Object.extend({ })(); - /* --- @@ -267,8 +3674,8 @@ authors: requires: - Core/Events - - /Object.Extras - - /MooTools.More + - Object.Extras + - MooTools.More provides: [Locale, Lang] @@ -417,7 +3824,6 @@ Locale.Set = new Class({ })(); - /* --- @@ -431,7 +3837,7 @@ authors: - Aaron Newton requires: - - /Locale + - Locale provides: [Locale.en-US.Date] @@ -489,7 +3895,6 @@ Locale.define('en-US', 'Date', { }); - /* --- @@ -697,19 +4102,19 @@ Date.implement({ }, isValid: function(date){ - return !isNaN((date || this).valueOf()); + if (!date) date = this; + return typeOf(date) == 'date' && !isNaN(date.valueOf()); }, - format: function(f){ + format: function(format){ if (!this.isValid()) return 'invalid date'; - if (!f) f = '%x %X'; - var formatLower = f.toLowerCase(); - if (formatters[formatLower]) return formatters[formatLower](this); // it's a formatter! - f = formats[formatLower] || f; // replace short-hand with actual format + if (!format) format = '%x %X'; + if (typeof format == 'string') format = formats[format.toLowerCase()] || format; + if (typeof format == 'function') return format(this); var d = this; - return f.replace(/%([a-z%])/gi, + return format.replace(/%([a-z%])/gi, function($0, $1){ switch ($1){ case 'a': return Date.getMsg('days_abbr')[d.get('day')]; @@ -756,18 +4161,15 @@ Date.implement({ strftime: 'format' }); -var formats = { - db: '%Y-%m-%d %H:%M:%S', - compact: '%Y%m%dT%H%M%S', - 'short': '%d %b %H:%M', - 'long': '%B %d, %Y %H:%M' -}; - // The day and month abbreviations are standardized, so we cannot use simply %a and %b because they will get localized var rfcDayAbbr = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], rfcMonthAbbr = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; -var formatters = { +var formats = { + db: '%Y-%m-%d %H:%M:%S', + compact: '%Y%m%dT%H%M%S', + 'short': '%d %b %H:%M', + 'long': '%B %d, %Y %H:%M', rfc822: function(date){ return rfcDayAbbr[date.get('day')] + date.format(', %d ') + rfcMonthAbbr[date.get('month')] + date.format(' %Y %H:%M:%S %Z'); }, @@ -787,7 +4189,6 @@ var formatters = { } }; - var parsePatterns = [], nativeParse = Date.parse; @@ -899,11 +4300,6 @@ Date.extend({ return this; }, - defineFormats: function(formats){ - for (var name in formats) Date.defineFormat(name, formats[name]); - return this; - }, - defineParser: function(pattern){ @@ -922,6 +4318,8 @@ Date.extend({ return this; } +}).extend({ + defineFormats: Date.defineFormat.overloadSetter() }); var regexOf = function(type){ @@ -1056,3384 +4454,6 @@ Locale.addEvent('change', function(language){ })(); - -/* ---- - -script: Date.Extras.js - -name: Date.Extras - -description: Extends the Date native object to include extra methods (on top of those in Date.js). - -license: MIT-style license - -authors: - - Aaron Newton - - Scott Kyle - -requires: - - /Date - -provides: [Date.Extras] - -... -*/ - -Date.implement({ - - timeDiffInWords: function(to){ - return Date.distanceOfTimeInWords(this, to || new Date); - }, - - timeDiff: function(to, separator){ - if (to == null) to = new Date; - var delta = ((to - this) / 1000).floor().abs(); - - var vals = [], - durations = [60, 60, 24, 365, 0], - names = ['s', 'm', 'h', 'd', 'y'], - value, duration; - - for (var item = 0; item < durations.length; item++){ - if (item && !delta) break; - value = delta; - if ((duration = durations[item])){ - value = (delta % duration); - delta = (delta / duration).floor(); - } - vals.unshift(value + (names[item] || '')); - } - - return vals.join(separator || ':'); - } - -}).extend({ - - distanceOfTimeInWords: function(from, to){ - return Date.getTimePhrase(((to - from) / 1000).toInt()); - }, - - getTimePhrase: function(delta){ - var suffix = (delta < 0) ? 'Until' : 'Ago'; - if (delta < 0) delta *= -1; - - var units = { - minute: 60, - hour: 60, - day: 24, - week: 7, - month: 52 / 12, - year: 12, - eon: Infinity - }; - - var msg = 'lessThanMinute'; - - for (var unit in units){ - var interval = units[unit]; - if (delta < 1.5 * interval){ - if (delta > 0.75 * interval) msg = unit; - break; - } - delta /= interval; - msg = unit + 's'; - } - - delta = delta.round(); - return Date.getMsg(msg + suffix, delta).substitute({delta: delta}); - } - -}).defineParsers( - - { - // "today", "tomorrow", "yesterday" - re: /^(?:tod|tom|yes)/i, - handler: function(bits){ - var d = new Date().clearTime(); - switch (bits[0]){ - case 'tom': return d.increment(); - case 'yes': return d.decrement(); - default: return d; - } - } - }, - - { - // "next Wednesday", "last Thursday" - re: /^(next|last) ([a-z]+)$/i, - handler: function(bits){ - var d = new Date().clearTime(); - var day = d.getDay(); - var newDay = Date.parseDay(bits[2], true); - var addDays = newDay - day; - if (newDay <= day) addDays += 7; - if (bits[1] == 'last') addDays -= 7; - return d.set('date', d.getDate() + addDays); - } - } - -).alias('timeAgoInWords', 'timeDiffInWords'); - - -/* ---- - -name: Locale.en-US.Number - -description: Number messages for US English. - -license: MIT-style license - -authors: - - Arian Stolwijk - -requires: - - /Locale - -provides: [Locale.en-US.Number] - -... -*/ - -Locale.define('en-US', 'Number', { - - decimal: '.', - group: ',', - -/* Commented properties are the defaults for Number.format - decimals: 0, - precision: 0, - scientific: null, - - prefix: null, - suffic: null, - - // Negative/Currency/percentage will mixin Number - negative: { - prefix: '-' - },*/ - - currency: { -// decimals: 2, - prefix: '$ ' - }/*, - - percentage: { - decimals: 2, - suffix: '%' - }*/ - -}); - - - - -/* ---- -name: Number.Format -description: Extends the Number Type object to include a number formatting method. -license: MIT-style license -authors: [Arian Stolwijk] -requires: [Core/Number, Locale.en-US.Number] -# Number.Extras is for compatibility -provides: [Number.Format, Number.Extras] -... -*/ - - -Number.implement({ - - format: function(options){ - // Thanks dojo and YUI for some inspiration - var value = this; - options = options ? Object.clone(options) : {}; - var getOption = function(key){ - if (options[key] != null) return options[key]; - return Locale.get('Number.' + key); - }; - - var negative = value < 0, - decimal = getOption('decimal'), - precision = getOption('precision'), - group = getOption('group'), - decimals = getOption('decimals'); - - if (negative){ - var negativeLocale = getOption('negative') || {}; - if (negativeLocale.prefix == null && negativeLocale.suffix == null) negativeLocale.prefix = '-'; - ['prefix', 'suffix'].each(function(key){ - if (negativeLocale[key]) options[key] = getOption(key) + negativeLocale[key]; - }); - - value = -value; - } - - var prefix = getOption('prefix'), - suffix = getOption('suffix'); - - if (decimals !== '' && decimals >= 0 && decimals <= 20) value = value.toFixed(decimals); - if (precision >= 1 && precision <= 21) value = (+value).toPrecision(precision); - - value += ''; - var index; - if (getOption('scientific') === false && value.indexOf('e') > -1){ - var match = value.split('e'), - zeros = +match[1]; - value = match[0].replace('.', ''); - - if (zeros < 0){ - zeros = -zeros - 1; - index = match[0].indexOf('.'); - if (index > -1) zeros -= index - 1; - while (zeros--) value = '0' + value; - value = '0.' + value; - } else { - index = match[0].lastIndexOf('.'); - if (index > -1) zeros -= match[0].length - index - 1; - while (zeros--) value += '0'; - } - } - - if (decimal != '.') value = value.replace('.', decimal); - - if (group){ - index = value.lastIndexOf(decimal); - index = (index > -1) ? index : value.length; - var newOutput = value.substring(index), - i = index; - - while (i--){ - if ((index - i - 1) % 3 == 0 && i != (index - 1)) newOutput = group + newOutput; - newOutput = value.charAt(i) + newOutput; - } - - value = newOutput; - } - - if (prefix) value = prefix + value; - if (suffix) value += suffix; - - return value; - }, - - formatCurrency: function(){ - var locale = Locale.get('Number.currency') || {}; - if (locale.scientific == null) locale.scientific = false; - if (locale.decimals == null) locale.decimals = 2; - - return this.format(locale); - }, - - formatPercentage: function(){ - var locale = Locale.get('Number.percentage') || {}; - if (locale.suffix == null) locale.suffix = '%'; - if (locale.decimals == null) locale.decimals = 2; - - return this.format(locale); - } - -}); - - -/* ---- - -script: String.Extras.js - -name: String.Extras - -description: Extends the String native object to include methods useful in managing various kinds of strings (query strings, urls, html, etc). - -license: MIT-style license - -authors: - - Aaron Newton - - Guillermo Rauch - - Christopher Pitt - -requires: - - Core/String - - Core/Array - - MooTools.More - -provides: [String.Extras] - -... -*/ - -(function(){ - -var special = { - 'a': /[àáâãäåăą]/g, - 'A': /[ÀÁÂÃÄÅĂĄ]/g, - 'c': /[ćčç]/g, - 'C': /[ĆČÇ]/g, - 'd': /[ďđ]/g, - 'D': /[ĎÐ]/g, - 'e': /[èéêëěę]/g, - 'E': /[ÈÉÊËĚĘ]/g, - 'g': /[ğ]/g, - 'G': /[Ğ]/g, - 'i': /[ìíîï]/g, - 'I': /[ÌÍÎÏ]/g, - 'l': /[ĺľł]/g, - 'L': /[ĹĽŁ]/g, - 'n': /[ñňń]/g, - 'N': /[ÑŇŃ]/g, - 'o': /[òóôõöøő]/g, - 'O': /[ÒÓÔÕÖØ]/g, - 'r': /[řŕ]/g, - 'R': /[ŘŔ]/g, - 's': /[ššş]/g, - 'S': /[ŠŞŚ]/g, - 't': /[ťţ]/g, - 'T': /[ŤŢ]/g, - 'ue': /[ü]/g, - 'UE': /[Ü]/g, - 'u': /[ùúûůµ]/g, - 'U': /[ÙÚÛŮ]/g, - 'y': /[ÿý]/g, - 'Y': /[ŸÝ]/g, - 'z': /[žźż]/g, - 'Z': /[ŽŹŻ]/g, - 'th': /[þ]/g, - 'TH': /[Þ]/g, - 'dh': /[ð]/g, - 'DH': /[Ð]/g, - 'ss': /[ß]/g, - 'oe': /[œ]/g, - 'OE': /[Œ]/g, - 'ae': /[æ]/g, - 'AE': /[Æ]/g -}, - -tidy = { - ' ': /[\xa0\u2002\u2003\u2009]/g, - '*': /[\xb7]/g, - '\'': /[\u2018\u2019]/g, - '"': /[\u201c\u201d]/g, - '...': /[\u2026]/g, - '-': /[\u2013]/g, -// '--': /[\u2014]/g, - '»': /[\uFFFD]/g -}; - -var walk = function(string, replacements){ - var result = string, key; - for (key in replacements) result = result.replace(replacements[key], key); - return result; -}; - -var getRegexForTag = function(tag, contents){ - tag = tag || ''; - var regstr = contents ? "<" + tag + "(?!\\w)[^>]*>([\\s\\S]*?)<\/" + tag + "(?!\\w)>" : "<\/?" + tag + "([^>]+)?>", - reg = new RegExp(regstr, "gi"); - return reg; -}; - -String.implement({ - - standardize: function(){ - return walk(this, special); - }, - - repeat: function(times){ - return new Array(times + 1).join(this); - }, - - pad: function(length, str, direction){ - if (this.length >= length) return this; - - var pad = (str == null ? ' ' : '' + str) - .repeat(length - this.length) - .substr(0, length - this.length); - - if (!direction || direction == 'right') return this + pad; - if (direction == 'left') return pad + this; - - return pad.substr(0, (pad.length / 2).floor()) + this + pad.substr(0, (pad.length / 2).ceil()); - }, - - getTags: function(tag, contents){ - return this.match(getRegexForTag(tag, contents)) || []; - }, - - stripTags: function(tag, contents){ - return this.replace(getRegexForTag(tag, contents), ''); - }, - - tidy: function(){ - return walk(this, tidy); - }, - - truncate: function(max, trail, atChar){ - var string = this; - if (trail == null && arguments.length == 1) trail = '…'; - if (string.length > max){ - string = string.substring(0, max); - if (atChar){ - var index = string.lastIndexOf(atChar); - if (index != -1) string = string.substr(0, index); - } - if (trail) string += trail; - } - return string; - } - -}); - -})(); - - -/* ---- - -script: String.QueryString.js - -name: String.QueryString - -description: Methods for dealing with URI query strings. - -license: MIT-style license - -authors: - - Sebastian Markbåge - - Aaron Newton - - Lennart Pilon - - Valerio Proietti - -requires: - - Core/Array - - Core/String - - /MooTools.More - -provides: [String.QueryString] - -... -*/ - -String.implement({ - - parseQueryString: function(decodeKeys, decodeValues){ - if (decodeKeys == null) decodeKeys = true; - if (decodeValues == null) decodeValues = true; - - var vars = this.split(/[&;]/), - object = {}; - if (!vars.length) return object; - - vars.each(function(val){ - var index = val.indexOf('=') + 1, - value = index ? val.substr(index) : '', - keys = index ? val.substr(0, index - 1).match(/([^\]\[]+|(\B)(?=\]))/g) : [val], - obj = object; - if (!keys) return; - if (decodeValues) value = decodeURIComponent(value); - keys.each(function(key, i){ - if (decodeKeys) key = decodeURIComponent(key); - var current = obj[key]; - - if (i < keys.length - 1) obj = obj[key] = current || {}; - else if (typeOf(current) == 'array') current.push(value); - else obj[key] = current != null ? [current, value] : value; - }); - }); - - return object; - }, - - cleanQueryString: function(method){ - return this.split('&').filter(function(val){ - var index = val.indexOf('='), - key = index < 0 ? '' : val.substr(0, index), - value = val.substr(index + 1); - - return method ? method.call(null, key, value) : (value || value === 0); - }).join('&'); - } - -}); - - -/* ---- - -script: URI.js - -name: URI - -description: Provides methods useful in managing the window location and uris. - -license: MIT-style license - -authors: - - Sebastian Markbåge - - Aaron Newton - -requires: - - Core/Object - - Core/Class - - Core/Class.Extras - - Core/Element - - /String.QueryString - -provides: [URI] - -... -*/ - -(function(){ - -var toString = function(){ - return this.get('value'); -}; - -var URI = this.URI = new Class({ - - Implements: Options, - - options: { - /*base: false*/ - }, - - regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/, - parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'], - schemes: {http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0}, - - initialize: function(uri, options){ - this.setOptions(options); - var base = this.options.base || URI.base; - if (!uri) uri = base; - - if (uri && uri.parsed) this.parsed = Object.clone(uri.parsed); - else this.set('value', uri.href || uri.toString(), base ? new URI(base) : false); - }, - - parse: function(value, base){ - var bits = value.match(this.regex); - if (!bits) return false; - bits.shift(); - return this.merge(bits.associate(this.parts), base); - }, - - merge: function(bits, base){ - if ((!bits || !bits.scheme) && (!base || !base.scheme)) return false; - if (base){ - this.parts.every(function(part){ - if (bits[part]) return false; - bits[part] = base[part] || ''; - return true; - }); - } - bits.port = bits.port || this.schemes[bits.scheme.toLowerCase()]; - bits.directory = bits.directory ? this.parseDirectory(bits.directory, base ? base.directory : '') : '/'; - return bits; - }, - - parseDirectory: function(directory, baseDirectory){ - directory = (directory.substr(0, 1) == '/' ? '' : (baseDirectory || '/')) + directory; - if (!directory.test(URI.regs.directoryDot)) return directory; - var result = []; - directory.replace(URI.regs.endSlash, '').split('/').each(function(dir){ - if (dir == '..' && result.length > 0) result.pop(); - else if (dir != '.') result.push(dir); - }); - return result.join('/') + '/'; - }, - - combine: function(bits){ - return bits.value || bits.scheme + '://' + - (bits.user ? bits.user + (bits.password ? ':' + bits.password : '') + '@' : '') + - (bits.host || '') + (bits.port && bits.port != this.schemes[bits.scheme] ? ':' + bits.port : '') + - (bits.directory || '/') + (bits.file || '') + - (bits.query ? '?' + bits.query : '') + - (bits.fragment ? '#' + bits.fragment : ''); - }, - - set: function(part, value, base){ - if (part == 'value'){ - var scheme = value.match(URI.regs.scheme); - if (scheme) scheme = scheme[1]; - if (scheme && this.schemes[scheme.toLowerCase()] == null) this.parsed = { scheme: scheme, value: value }; - else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value }); - } else if (part == 'data'){ - this.setData(value); - } else { - this.parsed[part] = value; - } - return this; - }, - - get: function(part, base){ - switch (part){ - case 'value': return this.combine(this.parsed, base ? base.parsed : false); - case 'data' : return this.getData(); - } - return this.parsed[part] || ''; - }, - - go: function(){ - document.location.href = this.toString(); - }, - - toURI: function(){ - return this; - }, - - getData: function(key, part){ - var qs = this.get(part || 'query'); - if (!(qs || qs === 0)) return key ? null : {}; - var obj = qs.parseQueryString(); - return key ? obj[key] : obj; - }, - - setData: function(values, merge, part){ - if (typeof values == 'string'){ - var data = this.getData(); - data[arguments[0]] = arguments[1]; - values = data; - } else if (merge){ - values = Object.merge(this.getData(), values); - } - return this.set(part || 'query', Object.toQueryString(values)); - }, - - clearData: function(part){ - return this.set(part || 'query', ''); - }, - - toString: toString, - valueOf: toString - -}); - -URI.regs = { - endSlash: /\/$/, - scheme: /^(\w+):/, - directoryDot: /\.\/|\.$/ -}; - -URI.base = new URI(Array.from(document.getElements('base[href]', true)).getLast(), {base: document.location}); - -String.implement({ - - toURI: function(options){ - return new URI(this, options); - } - -}); - -})(); - - -/* ---- - -script: Class.Refactor.js - -name: Class.Refactor - -description: Extends a class onto itself with new property, preserving any items attached to the class's namespace. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Class - - /MooTools.More - -# Some modules declare themselves dependent on Class.Refactor -provides: [Class.refactor, Class.Refactor] - -... -*/ - -Class.refactor = function(original, refactors){ - - Object.each(refactors, function(item, name){ - var origin = original.prototype[name]; - origin = (origin && origin.$origin) || origin || function(){}; - original.implement(name, (typeof item == 'function') ? function(){ - var old = this.previous; - this.previous = origin; - var value = item.apply(this, arguments); - this.previous = old; - return value; - } : item); - }); - - return original; - -}; - - -/* ---- - -script: URI.Relative.js - -name: URI.Relative - -description: Extends the URI class to add methods for computing relative and absolute urls. - -license: MIT-style license - -authors: - - Sebastian Markbåge - - -requires: - - /Class.refactor - - /URI - -provides: [URI.Relative] - -... -*/ - -URI = Class.refactor(URI, { - - combine: function(bits, base){ - if (!base || bits.scheme != base.scheme || bits.host != base.host || bits.port != base.port) - return this.previous.apply(this, arguments); - var end = bits.file + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : ''); - - if (!base.directory) return (bits.directory || (bits.file ? '' : './')) + end; - - var baseDir = base.directory.split('/'), - relDir = bits.directory.split('/'), - path = '', - offset; - - var i = 0; - for (offset = 0; offset < baseDir.length && offset < relDir.length && baseDir[offset] == relDir[offset]; offset++); - for (i = 0; i < baseDir.length - offset - 1; i++) path += '../'; - for (i = offset; i < relDir.length - 1; i++) path += relDir[i] + '/'; - - return (path || (bits.file ? '' : './')) + end; - }, - - toAbsolute: function(base){ - base = new URI(base); - if (base) base.set('directory', '').set('file', ''); - return this.toRelative(base); - }, - - toRelative: function(base){ - return this.get('value', new URI(base)); - } - -}); - - -/* ---- - -name: Hash - -description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects. - -license: MIT-style license. - -requires: - - Core/Object - - /MooTools.More - -provides: [Hash] - -... -*/ - -(function(){ - -if (this.Hash) return; - -var Hash = this.Hash = new Type('Hash', function(object){ - if (typeOf(object) == 'hash') object = Object.clone(object.getClean()); - for (var key in object) this[key] = object[key]; - return this; -}); - -this.$H = function(object){ - return new Hash(object); -}; - -Hash.implement({ - - forEach: function(fn, bind){ - Object.forEach(this, fn, bind); - }, - - getClean: function(){ - var clean = {}; - for (var key in this){ - if (this.hasOwnProperty(key)) clean[key] = this[key]; - } - return clean; - }, - - getLength: function(){ - var length = 0; - for (var key in this){ - if (this.hasOwnProperty(key)) length++; - } - return length; - } - -}); - -Hash.alias('each', 'forEach'); - -Hash.implement({ - - has: Object.prototype.hasOwnProperty, - - keyOf: function(value){ - return Object.keyOf(this, value); - }, - - hasValue: function(value){ - return Object.contains(this, value); - }, - - extend: function(properties){ - Hash.each(properties || {}, function(value, key){ - Hash.set(this, key, value); - }, this); - return this; - }, - - combine: function(properties){ - Hash.each(properties || {}, function(value, key){ - Hash.include(this, key, value); - }, this); - return this; - }, - - erase: function(key){ - if (this.hasOwnProperty(key)) delete this[key]; - return this; - }, - - get: function(key){ - return (this.hasOwnProperty(key)) ? this[key] : null; - }, - - set: function(key, value){ - if (!this[key] || this.hasOwnProperty(key)) this[key] = value; - return this; - }, - - empty: function(){ - Hash.each(this, function(value, key){ - delete this[key]; - }, this); - return this; - }, - - include: function(key, value){ - if (this[key] == undefined) this[key] = value; - return this; - }, - - map: function(fn, bind){ - return new Hash(Object.map(this, fn, bind)); - }, - - filter: function(fn, bind){ - return new Hash(Object.filter(this, fn, bind)); - }, - - every: function(fn, bind){ - return Object.every(this, fn, bind); - }, - - some: function(fn, bind){ - return Object.some(this, fn, bind); - }, - - getKeys: function(){ - return Object.keys(this); - }, - - getValues: function(){ - return Object.values(this); - }, - - toQueryString: function(base){ - return Object.toQueryString(this, base); - } - -}); - -Hash.alias({indexOf: 'keyOf', contains: 'hasValue'}); - - -})(); - - - -/* ---- - -script: Hash.Extras.js - -name: Hash.Extras - -description: Extends the Hash Type to include getFromPath which allows a path notation to child elements. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - /Hash - - /Object.Extras - -provides: [Hash.Extras] - -... -*/ - -Hash.implement({ - - getFromPath: function(notation){ - return Object.getFromPath(this, notation); - }, - - cleanValues: function(method){ - return new Hash(Object.cleanValues(this, method)); - }, - - run: function(){ - Object.run(arguments); - } - -}); - - -/* ---- - -script: Element.Forms.js - -name: Element.Forms - -description: Extends the Element native object to include methods useful in managing inputs. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Element - - /String.Extras - - /MooTools.More - -provides: [Element.Forms] - -... -*/ - -Element.implement({ - - tidy: function(){ - this.set('value', this.get('value').tidy()); - }, - - getTextInRange: function(start, end){ - return this.get('value').substring(start, end); - }, - - getSelectedText: function(){ - if (this.setSelectionRange) return this.getTextInRange(this.getSelectionStart(), this.getSelectionEnd()); - return document.selection.createRange().text; - }, - - getSelectedRange: function(){ - if (this.selectionStart != null){ - return { - start: this.selectionStart, - end: this.selectionEnd - }; - } - - var pos = { - start: 0, - end: 0 - }; - var range = this.getDocument().selection.createRange(); - if (!range || range.parentElement() != this) return pos; - var duplicate = range.duplicate(); - - if (this.type == 'text'){ - pos.start = 0 - duplicate.moveStart('character', -100000); - pos.end = pos.start + range.text.length; - } else { - var value = this.get('value'); - var offset = value.length; - duplicate.moveToElementText(this); - duplicate.setEndPoint('StartToEnd', range); - if (duplicate.text.length) offset -= value.match(/[\n\r]*$/)[0].length; - pos.end = offset - duplicate.text.length; - duplicate.setEndPoint('StartToStart', range); - pos.start = offset - duplicate.text.length; - } - return pos; - }, - - getSelectionStart: function(){ - return this.getSelectedRange().start; - }, - - getSelectionEnd: function(){ - return this.getSelectedRange().end; - }, - - setCaretPosition: function(pos){ - if (pos == 'end') pos = this.get('value').length; - this.selectRange(pos, pos); - return this; - }, - - getCaretPosition: function(){ - return this.getSelectedRange().start; - }, - - selectRange: function(start, end){ - if (this.setSelectionRange){ - this.focus(); - this.setSelectionRange(start, end); - } else { - var value = this.get('value'); - var diff = value.substr(start, end - start).replace(/\r/g, '').length; - start = value.substr(0, start).replace(/\r/g, '').length; - var range = this.createTextRange(); - range.collapse(true); - range.moveEnd('character', start + diff); - range.moveStart('character', start); - range.select(); - } - return this; - }, - - insertAtCursor: function(value, select){ - var pos = this.getSelectedRange(); - var text = this.get('value'); - this.set('value', text.substring(0, pos.start) + value + text.substring(pos.end, text.length)); - if (select !== false) this.selectRange(pos.start, pos.start + value.length); - else this.setCaretPosition(pos.start + value.length); - return this; - }, - - insertAroundCursor: function(options, select){ - options = Object.append({ - before: '', - defaultMiddle: '', - after: '' - }, options); - - var value = this.getSelectedText() || options.defaultMiddle; - var pos = this.getSelectedRange(); - var text = this.get('value'); - - if (pos.start == pos.end){ - this.set('value', text.substring(0, pos.start) + options.before + value + options.after + text.substring(pos.end, text.length)); - this.selectRange(pos.start + options.before.length, pos.end + options.before.length + value.length); - } else { - var current = text.substring(pos.start, pos.end); - this.set('value', text.substring(0, pos.start) + options.before + current + options.after + text.substring(pos.end, text.length)); - var selStart = pos.start + options.before.length; - if (select !== false) this.selectRange(selStart, selStart + current.length); - else this.setCaretPosition(selStart + text.length); - } - return this; - } - -}); - - -/* ---- - -script: Elements.From.js - -name: Elements.From - -description: Returns a collection of elements from a string of html. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/String - - Core/Element - - /MooTools.More - -provides: [Elements.from, Elements.From] - -... -*/ - -Elements.from = function(text, excludeScripts){ - if (excludeScripts || excludeScripts == null) text = text.stripScripts(); - - var container, match = text.match(/^\s*<(t[dhr]|tbody|tfoot|thead)/i); - - if (match){ - container = new Element('table'); - var tag = match[1].toLowerCase(); - if (['td', 'th', 'tr'].contains(tag)){ - container = new Element('tbody').inject(container); - if (tag != 'tr') container = new Element('tr').inject(container); - } - } - - return (container || new Element('div')).set('html', text).getChildren(); -}; - - -/* ---- - -name: Events.Pseudos - -description: Adds the functionality to add pseudo events - -license: MIT-style license - -authors: - - Arian Stolwijk - -requires: [Core/Class.Extras, Core/Slick.Parser, More/MooTools.More] - -provides: [Events.Pseudos] - -... -*/ - -Events.Pseudos = function(pseudos, addEvent, removeEvent){ - - var storeKey = 'monitorEvents:'; - - var storageOf = function(object){ - return { - store: object.store ? function(key, value){ - object.store(storeKey + key, value); - } : function(key, value){ - (object.$monitorEvents || (object.$monitorEvents = {}))[key] = value; - }, - retrieve: object.retrieve ? function(key, dflt){ - return object.retrieve(storeKey + key, dflt); - } : function(key, dflt){ - if (!object.$monitorEvents) return dflt; - return object.$monitorEvents[key] || dflt; - } - }; - }; - - var splitType = function(type){ - if (type.indexOf(':') == -1 || !pseudos) return null; - - var parsed = Slick.parse(type).expressions[0][0], - parsedPseudos = parsed.pseudos, - l = parsedPseudos.length, - splits = []; - - while (l--) if (pseudos[parsedPseudos[l].key]){ - splits.push({ - event: parsed.tag, - value: parsedPseudos[l].value, - pseudo: parsedPseudos[l].key, - original: type - }); - } - - return splits.length ? splits : null; - }; - - var mergePseudoOptions = function(split){ - return Object.merge.apply(this, split.map(function(item){ - return pseudos[item.pseudo].options || {}; - })); - }; - - return { - - addEvent: function(type, fn, internal){ - var split = splitType(type); - if (!split) return addEvent.call(this, type, fn, internal); - - var storage = storageOf(this), - events = storage.retrieve(type, []), - eventType = split[0].event, - options = mergePseudoOptions(split), - stack = fn, - eventOptions = options[eventType] || {}, - args = Array.slice(arguments, 2), - self = this, - monitor; - - if (eventOptions.args) args.append(Array.from(eventOptions.args)); - if (eventOptions.base) eventType = eventOptions.base; - if (eventOptions.onAdd) eventOptions.onAdd(this); - - split.each(function(item){ - var stackFn = stack; - stack = function(){ - (eventOptions.listener || pseudos[item.pseudo].listener).call(self, item, stackFn, arguments, monitor, options); - }; - }); - monitor = stack.bind(this); - - events.include({event: fn, monitor: monitor}); - storage.store(type, events); - - addEvent.apply(this, [type, fn].concat(args)); - return addEvent.apply(this, [eventType, monitor].concat(args)); - }, - - removeEvent: function(type, fn){ - var split = splitType(type); - if (!split) return removeEvent.call(this, type, fn); - - var storage = storageOf(this), - events = storage.retrieve(type); - if (!events) return this; - - var eventType = split[0].event, - options = mergePseudoOptions(split), - eventOptions = options[eventType] || {}, - args = Array.slice(arguments, 2); - - if (eventOptions.args) args.append(Array.from(eventOptions.args)); - if (eventOptions.base) eventType = eventOptions.base; - if (eventOptions.onRemove) eventOptions.onRemove(this); - - removeEvent.apply(this, [type, fn].concat(args)); - events.each(function(monitor, i){ - if (!fn || monitor.event == fn) removeEvent.apply(this, [eventType, monitor.monitor].concat(args)); - delete events[i]; - }, this); - - storage.store(type, events); - return this; - } - - }; - -}; - -(function(){ - -var pseudos = { - - once: { - listener: function(split, fn, args, monitor){ - fn.apply(this, args); - this.removeEvent(split.event, monitor) - .removeEvent(split.original, fn); - } - }, - - throttle: { - listener: function(split, fn, args){ - if (!fn._throttled){ - fn.apply(this, args); - fn._throttled = setTimeout(function(){ - fn._throttled = false; - }, split.value || 250); - } - } - }, - - pause: { - listener: function(split, fn, args){ - clearTimeout(fn._pause); - fn._pause = fn.delay(split.value || 250, this, args); - } - } - -}; - -Events.definePseudo = function(key, listener){ - pseudos[key] = Type.isFunction(listener) ? {listener: listener} : listener; - return this; -}; - -Events.lookupPseudo = function(key){ - return pseudos[key]; -}; - -var proto = Events.prototype; -Events.implement(Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); - -['Request', 'Fx'].each(function(klass){ - if (this[klass]) this[klass].implement(Events.prototype); -}); - -})(); - - -/* ---- - -name: Element.Event.Pseudos - -description: Adds the functionality to add pseudo events for Elements - -license: MIT-style license - -authors: - - Arian Stolwijk - -requires: [Core/Element.Event, Events.Pseudos] - -provides: [Element.Event.Pseudos] - -... -*/ - -(function(){ - -var pseudos = {}, - copyFromEvents = ['once', 'throttle', 'pause'], - count = copyFromEvents.length; - -while (count--) pseudos[copyFromEvents[count]] = Events.lookupPseudo(copyFromEvents[count]); - -Event.definePseudo = function(key, listener){ - pseudos[key] = Type.isFunction(listener) ? {listener: listener} : listener; - return this; -}; - -var proto = Element.prototype; -[Element, Window, Document].invoke('implement', Events.Pseudos(pseudos, proto.addEvent, proto.removeEvent)); - -})(); - - -/* ---- - -name: Element.Event.Pseudos.Keys - -description: Adds functionality fire events if certain keycombinations are pressed - -license: MIT-style license - -authors: - - Arian Stolwijk - -requires: [Element.Event.Pseudos] - -provides: [Element.Event.Pseudos.Keys] - -... -*/ - -(function(){ - -var keysStoreKey = '$moo:keys-pressed', - keysKeyupStoreKey = '$moo:keys-keyup'; - - -Event.definePseudo('keys', function(split, fn, args){ - - var event = args[0], - keys = [], - pressed = this.retrieve(keysStoreKey, []); - - keys.append(split.value.replace('++', function(){ - keys.push('+'); // shift++ and shift+++a - return ''; - }).split('+')); - - pressed.include(event.key); - - if (keys.every(function(key){ - return pressed.contains(key); - })) fn.apply(this, args); - - this.store(keysStoreKey, pressed); - - if (!this.retrieve(keysKeyupStoreKey)){ - var keyup = function(event){ - (function(){ - pressed = this.retrieve(keysStoreKey, []).erase(event.key); - this.store(keysStoreKey, pressed); - }).delay(0, this); // Fix for IE - }; - this.store(keysKeyupStoreKey, keyup).addEvent('keyup', keyup); - } - -}); - -Object.append(Event.Keys, { - 'shift': 16, - 'control': 17, - 'alt': 18, - 'capslock': 20, - 'pageup': 33, - 'pagedown': 34, - 'end': 35, - 'home': 36, - 'numlock': 144, - 'scrolllock': 145, - ';': 186, - '=': 187, - ',': 188, - '-': Browser.firefox ? 109 : 189, - '.': 190, - '/': 191, - '`': 192, - '[': 219, - '\\': 220, - ']': 221, - "'": 222, - '+': 107 -}); - -})(); - - -/* ---- - -script: Element.Pin.js - -name: Element.Pin - -description: Extends the Element native object to include the pin method useful for fixed positioning for elements. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Element.Event - - Core/Element.Dimensions - - Core/Element.Style - - /MooTools.More - -provides: [Element.Pin] - -... -*/ - -(function(){ - var supportsPositionFixed = false, - supportTested = false; - - var testPositionFixed = function(){ - var test = new Element('div').setStyles({ - position: 'fixed', - top: 0, - right: 0 - }).inject(document.body); - supportsPositionFixed = (test.offsetTop === 0); - test.dispose(); - supportTested = true; - }; - - Element.implement({ - - pin: function(enable, forceScroll){ - if (!supportTested) testPositionFixed(); - if (this.getStyle('display') == 'none') return this; - - var pinnedPosition, - scroll = window.getScroll(), - parent, - scrollFixer; - - if (enable !== false){ - pinnedPosition = this.getPosition(supportsPositionFixed ? document.body : this.getOffsetParent()); - if (!this.retrieve('pin:_pinned')){ - var currentPosition = { - top: pinnedPosition.y - scroll.y, - left: pinnedPosition.x - scroll.x - }; - - if (supportsPositionFixed && !forceScroll){ - this.setStyle('position', 'fixed').setStyles(currentPosition); - } else { - - parent = this.getOffsetParent(); - var position = this.getPosition(parent), - styles = this.getStyles('left', 'top'); - - if (parent && styles.left == 'auto' || styles.top == 'auto') this.setPosition(position); - if (this.getStyle('position') == 'static') this.setStyle('position', 'absolute'); - - position = { - x: styles.left.toInt() - scroll.x, - y: styles.top.toInt() - scroll.y - }; - - scrollFixer = function(){ - if (!this.retrieve('pin:_pinned')) return; - var scroll = window.getScroll(); - this.setStyles({ - left: position.x + scroll.x, - top: position.y + scroll.y - }); - }.bind(this); - - this.store('pin:_scrollFixer', scrollFixer); - window.addEvent('scroll', scrollFixer); - } - this.store('pin:_pinned', true); - } - - } else { - if (!this.retrieve('pin:_pinned')) return this; - - parent = this.getParent(); - var offsetParent = (parent.getComputedStyle('position') != 'static' ? parent : parent.getOffsetParent()); - - pinnedPosition = this.getPosition(offsetParent); - - this.store('pin:_pinned', false); - scrollFixer = this.retrieve('pin:_scrollFixer'); - if (!scrollFixer){ - this.setStyles({ - position: 'absolute', - top: pinnedPosition.y + scroll.y, - left: pinnedPosition.x + scroll.x - }); - } else { - this.store('pin:_scrollFixer', null); - window.removeEvent('scroll', scrollFixer); - } - this.removeClass('isPinned'); - } - return this; - }, - - unpin: function(){ - return this.pin(false); - }, - - togglePin: function(){ - return this.pin(!this.retrieve('pin:_pinned')); - } - - }); - - - -})(); - - -/* ---- - -script: Element.Measure.js - -name: Element.Measure - -description: Extends the Element native object to include methods useful in measuring dimensions. - -credits: "Element.measure / .expose methods by Daniel Steigerwald License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz" - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Element.Style - - Core/Element.Dimensions - - /MooTools.More - -provides: [Element.Measure] - -... -*/ - -(function(){ - -var getStylesList = function(styles, planes){ - var list = []; - Object.each(planes, function(directions){ - Object.each(directions, function(edge){ - styles.each(function(style){ - list.push(style + '-' + edge + (style == 'border' ? '-width' : '')); - }); - }); - }); - return list; -}; - -var calculateEdgeSize = function(edge, styles){ - var total = 0; - Object.each(styles, function(value, style){ - if (style.test(edge)) total = total + value.toInt(); - }); - return total; -}; - -var isVisible = function(el){ - return !!(!el || el.offsetHeight || el.offsetWidth); -}; - - -Element.implement({ - - measure: function(fn){ - if (isVisible(this)) return fn.call(this); - var parent = this.getParent(), - toMeasure = []; - while (!isVisible(parent) && parent != document.body){ - toMeasure.push(parent.expose()); - parent = parent.getParent(); - } - var restore = this.expose(), - result = fn.call(this); - restore(); - toMeasure.each(function(restore){ - restore(); - }); - return result; - }, - - expose: function(){ - if (this.getStyle('display') != 'none') return function(){}; - var before = this.style.cssText; - this.setStyles({ - display: 'block', - position: 'absolute', - visibility: 'hidden' - }); - return function(){ - this.style.cssText = before; - }.bind(this); - }, - - getDimensions: function(options){ - options = Object.merge({computeSize: false}, options); - var dim = {x: 0, y: 0}; - - var getSize = function(el, options){ - return (options.computeSize) ? el.getComputedSize(options) : el.getSize(); - }; - - var parent = this.getParent('body'); - - if (parent && this.getStyle('display') == 'none'){ - dim = this.measure(function(){ - return getSize(this, options); - }); - } else if (parent){ - try { //safari sometimes crashes here, so catch it - dim = getSize(this, options); - }catch(e){} - } - - return Object.append(dim, (dim.x || dim.x === 0) ? { - width: dim.x, - height: dim.y - } : { - x: dim.width, - y: dim.height - } - ); - }, - - getComputedSize: function(options){ - - - options = Object.merge({ - styles: ['padding','border'], - planes: { - height: ['top','bottom'], - width: ['left','right'] - }, - mode: 'both' - }, options); - - var styles = {}, - size = {width: 0, height: 0}, - dimensions; - - if (options.mode == 'vertical'){ - delete size.width; - delete options.planes.width; - } else if (options.mode == 'horizontal'){ - delete size.height; - delete options.planes.height; - } - - getStylesList(options.styles, options.planes).each(function(style){ - styles[style] = this.getStyle(style).toInt(); - }, this); - - Object.each(options.planes, function(edges, plane){ - - var capitalized = plane.capitalize(), - style = this.getStyle(plane); - - if (style == 'auto' && !dimensions) dimensions = this.getDimensions(); - - style = styles[plane] = (style == 'auto') ? dimensions[plane] : style.toInt(); - size['total' + capitalized] = style; - - edges.each(function(edge){ - var edgesize = calculateEdgeSize(edge, styles); - size['computed' + edge.capitalize()] = edgesize; - size['total' + capitalized] += edgesize; - }); - - }, this); - - return Object.append(size, styles); - } - -}); - -})(); - - -/* ---- - -script: Element.Position.js - -name: Element.Position - -description: Extends the Element native object to include methods useful positioning elements relative to others. - -license: MIT-style license - -authors: - - Aaron Newton - - Jacob Thornton - -requires: - - Core/Options - - Core/Element.Dimensions - - Element.Measure - -provides: [Element.Position] - -... -*/ - -(function(original){ - -var local = Element.Position = { - - options: {/* - edge: false, - returnPos: false, - minimum: {x: 0, y: 0}, - maximum: {x: 0, y: 0}, - relFixedPosition: false, - ignoreMargins: false, - ignoreScroll: false, - allowNegative: false,*/ - relativeTo: document.body, - position: { - x: 'center', //left, center, right - y: 'center' //top, center, bottom - }, - offset: {x: 0, y: 0} - }, - - getOptions: function(element, options){ - options = Object.merge({}, local.options, options); - local.setPositionOption(options); - local.setEdgeOption(options); - local.setOffsetOption(element, options); - local.setDimensionsOption(element, options); - return options; - }, - - setPositionOption: function(options){ - options.position = local.getCoordinateFromValue(options.position); - }, - - setEdgeOption: function(options){ - var edgeOption = local.getCoordinateFromValue(options.edge); - options.edge = edgeOption ? edgeOption : - (options.position.x == 'center' && options.position.y == 'center') ? {x: 'center', y: 'center'} : - {x: 'left', y: 'top'}; - }, - - setOffsetOption: function(element, options){ - var parentOffset = {x: 0, y: 0}, - offsetParent = element.measure(function(){ - return document.id(this.getOffsetParent()); - }), - parentScroll = offsetParent.getScroll(); - - if (!offsetParent || offsetParent == element.getDocument().body) return; - parentOffset = offsetParent.measure(function(){ - var position = this.getPosition(); - if (this.getStyle('position') == 'fixed'){ - var scroll = window.getScroll(); - position.x += scroll.x; - position.y += scroll.y; - } - return position; - }); - - options.offset = { - parentPositioned: offsetParent != document.id(options.relativeTo), - x: options.offset.x - parentOffset.x + parentScroll.x, - y: options.offset.y - parentOffset.y + parentScroll.y - }; - }, - - setDimensionsOption: function(element, options){ - options.dimensions = element.getDimensions({ - computeSize: true, - styles: ['padding', 'border', 'margin'] - }); - }, - - getPosition: function(element, options){ - var position = {}; - options = local.getOptions(element, options); - var relativeTo = document.id(options.relativeTo) || document.body; - - local.setPositionCoordinates(options, position, relativeTo); - if (options.edge) local.toEdge(position, options); - - var offset = options.offset; - position.left = ((position.x >= 0 || offset.parentPositioned || options.allowNegative) ? position.x : 0).toInt(); - position.top = ((position.y >= 0 || offset.parentPositioned || options.allowNegative) ? position.y : 0).toInt(); - - local.toMinMax(position, options); - - if (options.relFixedPosition || relativeTo.getStyle('position') == 'fixed') local.toRelFixedPosition(relativeTo, position); - if (options.ignoreScroll) local.toIgnoreScroll(relativeTo, position); - if (options.ignoreMargins) local.toIgnoreMargins(position, options); - - position.left = Math.ceil(position.left); - position.top = Math.ceil(position.top); - delete position.x; - delete position.y; - - return position; - }, - - setPositionCoordinates: function(options, position, relativeTo){ - var offsetY = options.offset.y, - offsetX = options.offset.x, - calc = (relativeTo == document.body) ? window.getScroll() : relativeTo.getPosition(), - top = calc.y, - left = calc.x, - winSize = window.getSize(); - - switch(options.position.x){ - case 'left': position.x = left + offsetX; break; - case 'right': position.x = left + offsetX + relativeTo.offsetWidth; break; - default: position.x = left + ((relativeTo == document.body ? winSize.x : relativeTo.offsetWidth) / 2) + offsetX; break; - } - - switch(options.position.y){ - case 'top': position.y = top + offsetY; break; - case 'bottom': position.y = top + offsetY + relativeTo.offsetHeight; break; - default: position.y = top + ((relativeTo == document.body ? winSize.y : relativeTo.offsetHeight) / 2) + offsetY; break; - } - }, - - toMinMax: function(position, options){ - var xy = {left: 'x', top: 'y'}, value; - ['minimum', 'maximum'].each(function(minmax){ - ['left', 'top'].each(function(lr){ - value = options[minmax] ? options[minmax][xy[lr]] : null; - if (value != null && ((minmax == 'minimum') ? position[lr] < value : position[lr] > value)) position[lr] = value; - }); - }); - }, - - toRelFixedPosition: function(relativeTo, position){ - var winScroll = window.getScroll(); - position.top += winScroll.y; - position.left += winScroll.x; - }, - - toIgnoreScroll: function(relativeTo, position){ - var relScroll = relativeTo.getScroll(); - position.top -= relScroll.y; - position.left -= relScroll.x; - }, - - toIgnoreMargins: function(position, options){ - position.left += options.edge.x == 'right' - ? options.dimensions['margin-right'] - : (options.edge.x != 'center' - ? -options.dimensions['margin-left'] - : -options.dimensions['margin-left'] + ((options.dimensions['margin-right'] + options.dimensions['margin-left']) / 2)); - - position.top += options.edge.y == 'bottom' - ? options.dimensions['margin-bottom'] - : (options.edge.y != 'center' - ? -options.dimensions['margin-top'] - : -options.dimensions['margin-top'] + ((options.dimensions['margin-bottom'] + options.dimensions['margin-top']) / 2)); - }, - - toEdge: function(position, options){ - var edgeOffset = {}, - dimensions = options.dimensions, - edge = options.edge; - - switch(edge.x){ - case 'left': edgeOffset.x = 0; break; - case 'right': edgeOffset.x = -dimensions.x - dimensions.computedRight - dimensions.computedLeft; break; - // center - default: edgeOffset.x = -(Math.round(dimensions.totalWidth / 2)); break; - } - - switch(edge.y){ - case 'top': edgeOffset.y = 0; break; - case 'bottom': edgeOffset.y = -dimensions.y - dimensions.computedTop - dimensions.computedBottom; break; - // center - default: edgeOffset.y = -(Math.round(dimensions.totalHeight / 2)); break; - } - - position.x += edgeOffset.x; - position.y += edgeOffset.y; - }, - - getCoordinateFromValue: function(option){ - if (typeOf(option) != 'string') return option; - option = option.toLowerCase(); - - return { - x: option.test('left') ? 'left' - : (option.test('right') ? 'right' : 'center'), - y: option.test(/upper|top/) ? 'top' - : (option.test('bottom') ? 'bottom' : 'center') - }; - } - -}; - -Element.implement({ - - position: function(options){ - if (options && (options.x != null || options.y != null)) { - return (original ? original.apply(this, arguments) : this); - } - var position = this.setStyle('position', 'absolute').calculatePosition(options); - return (options && options.returnPos) ? position : this.setStyles(position); - }, - - calculatePosition: function(options){ - return local.getPosition(this, options); - } - -}); - -})(Element.prototype.position); - - -/* ---- - -script: Element.Shortcuts.js - -name: Element.Shortcuts - -description: Extends the Element native object to include some shortcut methods. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Element.Style - - /MooTools.More - -provides: [Element.Shortcuts] - -... -*/ - -Element.implement({ - - isDisplayed: function(){ - return this.getStyle('display') != 'none'; - }, - - isVisible: function(){ - var w = this.offsetWidth, - h = this.offsetHeight; - return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none'; - }, - - toggle: function(){ - return this[this.isDisplayed() ? 'hide' : 'show'](); - }, - - hide: function(){ - var d; - try { - //IE fails here if the element is not in the dom - d = this.getStyle('display'); - } catch(e){} - if (d == 'none') return this; - return this.store('element:_originalDisplay', d || '').setStyle('display', 'none'); - }, - - show: function(display){ - if (!display && this.isDisplayed()) return this; - display = display || this.retrieve('element:_originalDisplay') || 'block'; - return this.setStyle('display', (display == 'none') ? 'block' : display); - }, - - swapClass: function(remove, add){ - return this.removeClass(remove).addClass(add); - } - -}); - -Document.implement({ - - clearSelection: function(){ - if (window.getSelection){ - var selection = window.getSelection(); - if (selection && selection.removeAllRanges) selection.removeAllRanges(); - } else if (document.selection && document.selection.empty){ - try { - //IE fails here if selected element is not in dom - document.selection.empty(); - } catch(e){} - } - } - -}); - - -/* ---- - -script: Class.Binds.js - -name: Class.Binds - -description: Automagically binds specified methods in a class to the instance of the class. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Class - - /MooTools.More - -provides: [Class.Binds] - -... -*/ - -Class.Mutators.Binds = function(binds){ - if (!this.prototype.initialize) this.implement('initialize', function(){}); - return Array.from(binds).concat(this.prototype.Binds || []); -}; - -Class.Mutators.initialize = function(initialize){ - return function(){ - Array.from(this.Binds).each(function(name){ - var original = this[name]; - if (original) this[name] = original.bind(this); - }, this); - return initialize.apply(this, arguments); - }; -}; - - -/* ---- - -script: Class.Occlude.js - -name: Class.Occlude - -description: Prevents a class from being applied to a DOM element twice. - -license: MIT-style license. - -authors: - - Aaron Newton - -requires: - - Core/Class - - Core/Element - - /MooTools.More - -provides: [Class.Occlude] - -... -*/ - -Class.Occlude = new Class({ - - occlude: function(property, element){ - element = document.id(element || this.element); - var instance = element.retrieve(property || this.property); - if (instance && !this.occluded) - return (this.occluded = instance); - - this.occluded = false; - element.store(property || this.property, this); - return this.occluded; - } - -}); - - -/* ---- - -script: IframeShim.js - -name: IframeShim - -description: Defines IframeShim, a class for obscuring select lists and flash objects in IE. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Element.Event - - Core/Element.Style - - Core/Options - - Core/Events - - /Element.Position - - /Class.Occlude - -provides: [IframeShim] - -... -*/ - -var IframeShim = new Class({ - - Implements: [Options, Events, Class.Occlude], - - options: { - className: 'iframeShim', - src: 'javascript:false;document.write("");', - display: false, - zIndex: null, - margin: 0, - offset: {x: 0, y: 0}, - browsers: (Browser.ie6 || (Browser.firefox && Browser.version < 3 && Browser.Platform.mac)) - }, - - property: 'IframeShim', - - initialize: function(element, options){ - this.element = document.id(element); - if (this.occlude()) return this.occluded; - this.setOptions(options); - this.makeShim(); - return this; - }, - - makeShim: function(){ - if (this.options.browsers){ - var zIndex = this.element.getStyle('zIndex').toInt(); - - if (!zIndex){ - zIndex = 1; - var pos = this.element.getStyle('position'); - if (pos == 'static' || !pos) this.element.setStyle('position', 'relative'); - this.element.setStyle('zIndex', zIndex); - } - zIndex = ((this.options.zIndex != null || this.options.zIndex === 0) && zIndex > this.options.zIndex) ? this.options.zIndex : zIndex - 1; - if (zIndex < 0) zIndex = 1; - this.shim = new Element('iframe', { - src: this.options.src, - scrolling: 'no', - frameborder: 0, - styles: { - zIndex: zIndex, - position: 'absolute', - border: 'none', - filter: 'progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0)' - }, - 'class': this.options.className - }).store('IframeShim', this); - var inject = (function(){ - this.shim.inject(this.element, 'after'); - this[this.options.display ? 'show' : 'hide'](); - this.fireEvent('inject'); - }).bind(this); - if (!IframeShim.ready) window.addEvent('load', inject); - else inject(); - } else { - this.position = this.hide = this.show = this.dispose = Function.from(this); - } - }, - - position: function(){ - if (!IframeShim.ready || !this.shim) return this; - var size = this.element.measure(function(){ - return this.getSize(); - }); - if (this.options.margin != undefined){ - size.x = size.x - (this.options.margin * 2); - size.y = size.y - (this.options.margin * 2); - this.options.offset.x += this.options.margin; - this.options.offset.y += this.options.margin; - } - this.shim.set({width: size.x, height: size.y}).position({ - relativeTo: this.element, - offset: this.options.offset - }); - return this; - }, - - hide: function(){ - if (this.shim) this.shim.setStyle('display', 'none'); - return this; - }, - - show: function(){ - if (this.shim) this.shim.setStyle('display', 'block'); - return this.position(); - }, - - dispose: function(){ - if (this.shim) this.shim.dispose(); - return this; - }, - - destroy: function(){ - if (this.shim) this.shim.destroy(); - return this; - } - -}); - -window.addEvent('load', function(){ - IframeShim.ready = true; -}); - - -/* ---- - -script: Mask.js - -name: Mask - -description: Creates a mask element to cover another. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Options - - Core/Events - - Core/Element.Event - - /Class.Binds - - /Element.Position - - /IframeShim - -provides: [Mask] - -... -*/ - -var Mask = new Class({ - - Implements: [Options, Events], - - Binds: ['position'], - - options: {/* - onShow: function(){}, - onHide: function(){}, - onDestroy: function(){}, - onClick: function(event){}, - inject: { - where: 'after', - target: null, - }, - hideOnClick: false, - id: null, - destroyOnHide: false,*/ - style: {}, - 'class': 'mask', - maskMargins: false, - useIframeShim: true, - iframeShimOptions: {} - }, - - initialize: function(target, options){ - this.target = document.id(target) || document.id(document.body); - this.target.store('mask', this); - this.setOptions(options); - this.render(); - this.inject(); - }, - - render: function(){ - this.element = new Element('div', { - 'class': this.options['class'], - id: this.options.id || 'mask-' + String.uniqueID(), - styles: Object.merge({}, this.options.style, { - display: 'none' - }), - events: { - click: function(event){ - this.fireEvent('click', event); - if (this.options.hideOnClick) this.hide(); - }.bind(this) - } - }); - - this.hidden = true; - }, - - toElement: function(){ - return this.element; - }, - - inject: function(target, where){ - where = where || (this.options.inject ? this.options.inject.where : '') || this.target == document.body ? 'inside' : 'after'; - target = target || (this.options.inject && this.options.inject.target) || this.target; - - this.element.inject(target, where); - - if (this.options.useIframeShim){ - this.shim = new IframeShim(this.element, this.options.iframeShimOptions); - - this.addEvents({ - show: this.shim.show.bind(this.shim), - hide: this.shim.hide.bind(this.shim), - destroy: this.shim.destroy.bind(this.shim) - }); - } - }, - - position: function(){ - this.resize(this.options.width, this.options.height); - - this.element.position({ - relativeTo: this.target, - position: 'topLeft', - ignoreMargins: !this.options.maskMargins, - ignoreScroll: this.target == document.body - }); - - return this; - }, - - resize: function(x, y){ - var opt = { - styles: ['padding', 'border'] - }; - if (this.options.maskMargins) opt.styles.push('margin'); - - var dim = this.target.getComputedSize(opt); - if (this.target == document.body){ - this.element.setStyles({width: 0, height: 0}); - var win = window.getScrollSize(); - if (dim.totalHeight < win.y) dim.totalHeight = win.y; - if (dim.totalWidth < win.x) dim.totalWidth = win.x; - } - this.element.setStyles({ - width: Array.pick([x, dim.totalWidth, dim.x]), - height: Array.pick([y, dim.totalHeight, dim.y]) - }); - - return this; - }, - - show: function(){ - if (!this.hidden) return this; - - window.addEvent('resize', this.position); - this.position(); - this.showMask.apply(this, arguments); - - return this; - }, - - showMask: function(){ - this.element.setStyle('display', 'block'); - this.hidden = false; - this.fireEvent('show'); - }, - - hide: function(){ - if (this.hidden) return this; - - window.removeEvent('resize', this.position); - this.hideMask.apply(this, arguments); - if (this.options.destroyOnHide) return this.destroy(); - - return this; - }, - - hideMask: function(){ - this.element.setStyle('display', 'none'); - this.hidden = true; - this.fireEvent('hide'); - }, - - toggle: function(){ - this[this.hidden ? 'show' : 'hide'](); - }, - - destroy: function(){ - this.hide(); - this.element.destroy(); - this.fireEvent('destroy'); - this.target.eliminate('mask'); - } - -}); - -Element.Properties.mask = { - - set: function(options){ - var mask = this.retrieve('mask'); - if (mask) mask.destroy(); - return this.eliminate('mask').store('mask:options', options); - }, - - get: function(){ - var mask = this.retrieve('mask'); - if (!mask){ - mask = new Mask(this, this.retrieve('mask:options')); - this.store('mask', mask); - } - return mask; - } - -}; - -Element.implement({ - - mask: function(options){ - if (options) this.set('mask', options); - this.get('mask').show(); - return this; - }, - - unmask: function(){ - this.get('mask').hide(); - return this; - } - -}); - - -/* ---- - -script: Spinner.js - -name: Spinner - -description: Adds a semi-transparent overlay over a dom element with a spinnin ajax icon. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Fx.Tween - - Core/Request - - /Class.refactor - - /Mask - -provides: [Spinner] - -... -*/ - -var Spinner = new Class({ - - Extends: Mask, - - Implements: Chain, - - options: {/* - message: false,*/ - 'class': 'spinner', - containerPosition: {}, - content: { - 'class': 'spinner-content' - }, - messageContainer: { - 'class': 'spinner-msg' - }, - img: { - 'class': 'spinner-img' - }, - fxOptions: { - link: 'chain' - } - }, - - initialize: function(target, options){ - this.target = document.id(target) || document.id(document.body); - this.target.store('spinner', this); - this.setOptions(options); - this.render(); - this.inject(); - - // Add this to events for when noFx is true; parent methods handle hide/show. - var deactivate = function(){ this.active = false; }.bind(this); - this.addEvents({ - hide: deactivate, - show: deactivate - }); - }, - - render: function(){ - this.parent(); - - this.element.set('id', this.options.id || 'spinner-' + String.uniqueID()); - - this.content = document.id(this.options.content) || new Element('div', this.options.content); - this.content.inject(this.element); - - if (this.options.message){ - this.msg = document.id(this.options.message) || new Element('p', this.options.messageContainer).appendText(this.options.message); - this.msg.inject(this.content); - } - - if (this.options.img){ - this.img = document.id(this.options.img) || new Element('div', this.options.img); - this.img.inject(this.content); - } - - this.element.set('tween', this.options.fxOptions); - }, - - show: function(noFx){ - if (this.active) return this.chain(this.show.bind(this)); - if (!this.hidden){ - this.callChain.delay(20, this); - return this; - } - - this.active = true; - - return this.parent(noFx); - }, - - showMask: function(noFx){ - var pos = function(){ - this.content.position(Object.merge({ - relativeTo: this.element - }, this.options.containerPosition)); - }.bind(this); - - if (noFx){ - this.parent(); - pos(); - } else { - if (!this.options.style.opacity) this.options.style.opacity = this.element.getStyle('opacity').toFloat(); - this.element.setStyles({ - display: 'block', - opacity: 0 - }).tween('opacity', this.options.style.opacity); - pos(); - this.hidden = false; - this.fireEvent('show'); - this.callChain(); - } - }, - - hide: function(noFx){ - if (this.active) return this.chain(this.hide.bind(this)); - if (this.hidden){ - this.callChain.delay(20, this); - return this; - } - this.active = true; - return this.parent(noFx); - }, - - hideMask: function(noFx){ - if (noFx) return this.parent(); - this.element.tween('opacity', 0).get('tween').chain(function(){ - this.element.setStyle('display', 'none'); - this.hidden = true; - this.fireEvent('hide'); - this.callChain(); - }.bind(this)); - }, - - destroy: function(){ - this.content.destroy(); - this.parent(); - this.target.eliminate('spinner'); - } - -}); - -Request = Class.refactor(Request, { - - options: { - useSpinner: false, - spinnerOptions: {}, - spinnerTarget: false - }, - - initialize: function(options){ - this._send = this.send; - this.send = function(options){ - var spinner = this.getSpinner(); - if (spinner) spinner.chain(this._send.pass(options, this)).show(); - else this._send(options); - return this; - }; - this.previous(options); - }, - - getSpinner: function(){ - if (!this.spinner){ - var update = document.id(this.options.spinnerTarget) || document.id(this.options.update); - if (this.options.useSpinner && update){ - update.set('spinner', this.options.spinnerOptions); - var spinner = this.spinner = update.get('spinner'); - ['complete', 'exception', 'cancel'].each(function(event){ - this.addEvent(event, spinner.hide.bind(spinner)); - }, this); - } - } - return this.spinner; - } - -}); - -Element.Properties.spinner = { - - set: function(options){ - var spinner = this.retrieve('spinner'); - if (spinner) spinner.destroy(); - return this.eliminate('spinner').store('spinner:options', options); - }, - - get: function(){ - var spinner = this.retrieve('spinner'); - if (!spinner){ - spinner = new Spinner(this, this.retrieve('spinner:options')); - this.store('spinner', spinner); - } - return spinner; - } - -}; - -Element.implement({ - - spin: function(options){ - if (options) this.set('spinner', options); - this.get('spinner').show(); - return this; - }, - - unspin: function(){ - this.get('spinner').hide(); - return this; - } - -}); - - -/* ---- - -script: Element.Delegation.js - -name: Element.Delegation - -description: Extends the Element native object to include the delegate method for more efficient event management. - -credits: - - "Event checking based on the work of Daniel Steigerwald. License: MIT-style license. Copyright: Copyright (c) 2008 Daniel Steigerwald, daniel.steigerwald.cz" - -license: MIT-style license - -authors: - - Aaron Newton - - Daniel Steigerwald - -requires: [/MooTools.More, Element.Event.Pseudos] - -provides: [Element.Delegation] - -... -*/ - -(function(){ - -var eventListenerSupport = !(window.attachEvent && !window.addEventListener), - nativeEvents = Element.NativeEvents; - -nativeEvents.focusin = 2; -nativeEvents.focusout = 2; - -var check = function(split, target, event){ - var elementEvent = Element.Events[split.event], condition; - if (elementEvent) condition = elementEvent.condition; - return Slick.match(target, split.value) && (!condition || condition.call(target, event)); -}; - -var bubbleUp = function(split, event, fn){ - for (var target = event.target; target && target != this; target = document.id(target.parentNode)){ - if (target && check(split, target, event)) return fn.call(target, event, target); - } -}; - -var formObserver = function(eventName){ - - var $delegationKey = '$delegation:'; - - return { - base: 'focusin', - - onRemove: function(element){ - element.retrieve($delegationKey + 'forms', []).each(function(el){ - el.retrieve($delegationKey + 'listeners', []).each(function(listener){ - el.removeEvent(eventName, listener); - }); - el.eliminate($delegationKey + eventName + 'listeners') - .eliminate($delegationKey + eventName + 'originalFn'); - }); - }, - - listener: function(split, fn, args, monitor, options){ - var event = args[0], - forms = this.retrieve($delegationKey + 'forms', []), - target = event.target, - form = (target.get('tag') == 'form') ? target : event.target.getParent('form'); - - if (!form) return; - - var formEvents = form.retrieve($delegationKey + 'originalFn', []), - formListeners = form.retrieve($delegationKey + 'listeners', []), - self = this; - - forms.include(form); - this.store($delegationKey + 'forms', forms); - - if (!formEvents.contains(fn)){ - var formListener = function(event){ - bubbleUp.call(self, split, event, fn); - }; - form.addEvent(eventName, formListener); - - formEvents.push(fn); - formListeners.push(formListener); - - form.store($delegationKey + eventName + 'originalFn', formEvents) - .store($delegationKey + eventName + 'listeners', formListeners); - } - } - }; -}; - -var inputObserver = function(eventName){ - return { - base: 'focusin', - listener: function(split, fn, args){ - var events = {blur: function(){ - this.removeEvents(events); - }}, self = this; - events[eventName] = function(event){ - bubbleUp.call(self, split, event, fn); - }; - args[0].target.addEvents(events); - } - }; -}; - -var eventOptions = { - mouseenter: { - base: 'mouseover' - }, - mouseleave: { - base: 'mouseout' - }, - focus: { - base: 'focus' + (eventListenerSupport ? '' : 'in'), - args: [true] - }, - blur: { - base: eventListenerSupport ? 'blur' : 'focusout', - args: [true] - } -}; - -if (!eventListenerSupport) Object.append(eventOptions, { - submit: formObserver('submit'), - reset: formObserver('reset'), - change: inputObserver('change'), - select: inputObserver('select') -}); - -Event.definePseudo('relay', { - listener: function(split, fn, args){ - bubbleUp.call(this, split, args[0], fn); - }, - options: eventOptions -}); - -})(); - - -/* ---- - -script: Form.Request.js - -name: Form.Request - -description: Handles the basic functionality of submitting a form and updating a dom element with the result. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Request.HTML - - /Class.Binds - - /Class.Occlude - - /Spinner - - /String.QueryString - - /Element.Delegation - -provides: [Form.Request] - -... -*/ - -if (!window.Form) window.Form = {}; - -(function(){ - - Form.Request = new Class({ - - Binds: ['onSubmit', 'onFormValidate'], - - Implements: [Options, Events, Class.Occlude], - - options: {/* - onFailure: function(){}, - onSuccess: function(){}, // aliased to onComplete, - onSend: function(){}*/ - requestOptions: { - evalScripts: true, - useSpinner: true, - emulation: false, - link: 'ignore' - }, - sendButtonClicked: true, - extraData: {}, - resetForm: true - }, - - property: 'form.request', - - initialize: function(form, target, options){ - this.element = document.id(form); - if (this.occlude()) return this.occluded; - this.setOptions(options) - .setTarget(target) - .attach(); - }, - - setTarget: function(target){ - this.target = document.id(target); - if (!this.request){ - this.makeRequest(); - } else { - this.request.setOptions({ - update: this.target - }); - } - return this; - }, - - toElement: function(){ - return this.element; - }, - - makeRequest: function(){ - var self = this; - this.request = new Request.HTML(Object.merge({ - update: this.target, - emulation: false, - spinnerTarget: this.element, - method: this.element.get('method') || 'post' - }, this.options.requestOptions)).addEvents({ - success: function(tree, elements, html, javascript){ - ['complete', 'success'].each(function(evt){ - self.fireEvent(evt, [self.target, tree, elements, html, javascript]); - }); - }, - failure: function(){ - self.fireEvent('complete', arguments).fireEvent('failure', arguments); - }, - exception: function(){ - self.fireEvent('failure', arguments); - } - }); - return this.attachReset(); - }, - - attachReset: function(){ - if (!this.options.resetForm) return this; - this.request.addEvent('success', function(){ - Function.attempt(function(){ - this.element.reset(); - }.bind(this)); - if (window.OverText) OverText.update(); - }.bind(this)); - return this; - }, - - attach: function(attach){ - var method = (attach != false) ? 'addEvent' : 'removeEvent'; - this.element[method]('click:relay(button, input[type=submit])', this.saveClickedButton.bind(this)); - - var fv = this.element.retrieve('validator'); - if (fv) fv[method]('onFormValidate', this.onFormValidate); - else this.element[method]('submit', this.onSubmit); - - return this; - }, - - detach: function(){ - return this.attach(false); - }, - - //public method - enable: function(){ - return this.attach(); - }, - - //public method - disable: function(){ - return this.detach(); - }, - - onFormValidate: function(valid, form, event){ - //if there's no event, then this wasn't a submit event - if (!event) return; - var fv = this.element.retrieve('validator'); - if (valid || (fv && !fv.options.stopOnFailure)){ - event.stop(); - this.send(); - } - }, - - onSubmit: function(event){ - var fv = this.element.retrieve('validator'); - if (fv){ - //form validator was created after Form.Request - this.element.removeEvent('submit', this.onSubmit); - fv.addEvent('onFormValidate', this.onFormValidate); - this.element.validate(); - return; - } - if (event) event.stop(); - this.send(); - }, - - saveClickedButton: function(event, target){ - var targetName = target.get('name'); - if (!targetName || !this.options.sendButtonClicked) return; - this.options.extraData[targetName] = target.get('value') || true; - this.clickedCleaner = function(){ - delete this.options.extraData[targetName]; - this.clickedCleaner = function(){}; - }.bind(this); - }, - - clickedCleaner: function(){}, - - send: function(){ - var str = this.element.toQueryString().trim(), - data = Object.toQueryString(this.options.extraData); - - if (str) str += "&" + data; - else str = data; - - this.fireEvent('send', [this.element, str.parseQueryString()]); - this.request.send({ - data: str, - url: this.options.requestOptions.url || this.element.get('action') - }); - this.clickedCleaner(); - return this; - } - - }); - - Element.implement('formUpdate', function(update, options){ - var fq = this.retrieve('form.request'); - if (!fq) { - fq = new Form.Request(this, update, options); - } else { - if (update) fq.setTarget(update); - if (options) fq.setOptions(options).makeRequest(); - } - fq.send(); - return this; - }); - -})(); - - -/* ---- - -script: Fx.Reveal.js - -name: Fx.Reveal - -description: Defines Fx.Reveal, a class that shows and hides elements with a transition. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Fx.Morph - - /Element.Shortcuts - - /Element.Measure - -provides: [Fx.Reveal] - -... -*/ - -(function(){ - - -var hideTheseOf = function(object){ - var hideThese = object.options.hideInputs; - if (window.OverText){ - var otClasses = [null]; - OverText.each(function(ot){ - otClasses.include('.' + ot.options.labelClass); - }); - if (otClasses) hideThese += otClasses.join(', '); - } - return (hideThese) ? object.element.getElements(hideThese) : null; -}; - - -Fx.Reveal = new Class({ - - Extends: Fx.Morph, - - options: {/* - onShow: function(thisElement){}, - onHide: function(thisElement){}, - onComplete: function(thisElement){}, - heightOverride: null, - widthOverride: null,*/ - link: 'cancel', - styles: ['padding', 'border', 'margin'], - transitionOpacity: !Browser.ie6, - mode: 'vertical', - display: function(){ - return this.element.get('tag') != 'tr' ? 'block' : 'table-row'; - }, - opacity: 1, - hideInputs: Browser.ie ? 'select, input, textarea, object, embed' : null - }, - - dissolve: function(){ - if (!this.hiding && !this.showing){ - if (this.element.getStyle('display') != 'none'){ - this.hiding = true; - this.showing = false; - this.hidden = true; - this.cssText = this.element.style.cssText; - - var startStyles = this.element.getComputedSize({ - styles: this.options.styles, - mode: this.options.mode - }); - if (this.options.transitionOpacity) startStyles.opacity = this.options.opacity; - - var zero = {}; - Object.each(startStyles, function(style, name){ - zero[name] = [style, 0]; - }); - - this.element.setStyles({ - display: Function.from(this.options.display).call(this), - overflow: 'hidden' - }); - - var hideThese = hideTheseOf(this); - if (hideThese) hideThese.setStyle('visibility', 'hidden'); - - this.$chain.unshift(function(){ - if (this.hidden){ - this.hiding = false; - this.element.style.cssText = this.cssText; - this.element.setStyle('display', 'none'); - if (hideThese) hideThese.setStyle('visibility', 'visible'); - } - this.fireEvent('hide', this.element); - this.callChain(); - }.bind(this)); - - this.start(zero); - } else { - this.callChain.delay(10, this); - this.fireEvent('complete', this.element); - this.fireEvent('hide', this.element); - } - } else if (this.options.link == 'chain'){ - this.chain(this.dissolve.bind(this)); - } else if (this.options.link == 'cancel' && !this.hiding){ - this.cancel(); - this.dissolve(); - } - return this; - }, - - reveal: function(){ - if (!this.showing && !this.hiding){ - if (this.element.getStyle('display') == 'none'){ - this.hiding = false; - this.showing = true; - this.hidden = false; - this.cssText = this.element.style.cssText; - - var startStyles; - this.element.measure(function(){ - startStyles = this.element.getComputedSize({ - styles: this.options.styles, - mode: this.options.mode - }); - }.bind(this)); - if (this.options.heightOverride != null) startStyles.height = this.options.heightOverride.toInt(); - if (this.options.widthOverride != null) startStyles.width = this.options.widthOverride.toInt(); - if (this.options.transitionOpacity){ - this.element.setStyle('opacity', 0); - startStyles.opacity = this.options.opacity; - } - - var zero = { - height: 0, - display: Function.from(this.options.display).call(this) - }; - Object.each(startStyles, function(style, name){ - zero[name] = 0; - }); - zero.overflow = 'hidden'; - - this.element.setStyles(zero); - - var hideThese = hideTheseOf(this); - if (hideThese) hideThese.setStyle('visibility', 'hidden'); - - this.$chain.unshift(function(){ - this.element.style.cssText = this.cssText; - this.element.setStyle('display', Function.from(this.options.display).call(this)); - if (!this.hidden) this.showing = false; - if (hideThese) hideThese.setStyle('visibility', 'visible'); - this.callChain(); - this.fireEvent('show', this.element); - }.bind(this)); - - this.start(startStyles); - } else { - this.callChain(); - this.fireEvent('complete', this.element); - this.fireEvent('show', this.element); - } - } else if (this.options.link == 'chain'){ - this.chain(this.reveal.bind(this)); - } else if (this.options.link == 'cancel' && !this.showing){ - this.cancel(); - this.reveal(); - } - return this; - }, - - toggle: function(){ - if (this.element.getStyle('display') == 'none'){ - this.reveal(); - } else { - this.dissolve(); - } - return this; - }, - - cancel: function(){ - this.parent.apply(this, arguments); - if (this.cssText != null) this.element.style.cssText = this.cssText; - this.hiding = false; - this.showing = false; - return this; - } - -}); - -Element.Properties.reveal = { - - set: function(options){ - this.get('reveal').cancel().setOptions(options); - return this; - }, - - get: function(){ - var reveal = this.retrieve('reveal'); - if (!reveal){ - reveal = new Fx.Reveal(this); - this.store('reveal', reveal); - } - return reveal; - } - -}; - -Element.Properties.dissolve = Element.Properties.reveal; - -Element.implement({ - - reveal: function(options){ - this.get('reveal').setOptions(options).reveal(); - return this; - }, - - dissolve: function(options){ - this.get('reveal').setOptions(options).dissolve(); - return this; - }, - - nix: function(options){ - var params = Array.link(arguments, {destroy: Type.isBoolean, options: Type.isObject}); - this.get('reveal').setOptions(options).dissolve().chain(function(){ - this[params.destroy ? 'destroy' : 'dispose'](); - }.bind(this)); - return this; - }, - - wink: function(){ - var params = Array.link(arguments, {duration: Type.isNumber, options: Type.isObject}); - var reveal = this.get('reveal').setOptions(params.options); - reveal.reveal().chain(function(){ - (function(){ - reveal.dissolve(); - }).delay(params.duration || 2000); - }); - } - -}); - -})(); - - -/* ---- - -script: Form.Request.Append.js - -name: Form.Request.Append - -description: Handles the basic functionality of submitting a form and updating a dom element with the result. The result is appended to the DOM element instead of replacing its contents. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - /Form.Request - - /Fx.Reveal - - /Elements.from - -provides: [Form.Request.Append] - -... -*/ - -Form.Request.Append = new Class({ - - Extends: Form.Request, - - options: { - //onBeforeEffect: function(){}, - useReveal: true, - revealOptions: {}, - inject: 'bottom' - }, - - makeRequest: function(){ - this.request = new Request.HTML(Object.merge({ - url: this.element.get('action'), - method: this.element.get('method') || 'post', - spinnerTarget: this.element - }, this.options.requestOptions, { - evalScripts: false - }) - ).addEvents({ - success: function(tree, elements, html, javascript){ - var container; - var kids = Elements.from(html); - if (kids.length == 1){ - container = kids[0]; - } else { - container = new Element('div', { - styles: { - display: 'none' - } - }).adopt(kids); - } - container.inject(this.target, this.options.inject); - if (this.options.requestOptions.evalScripts) Browser.exec(javascript); - this.fireEvent('beforeEffect', container); - var finish = function(){ - this.fireEvent('success', [container, this.target, tree, elements, html, javascript]); - }.bind(this); - if (this.options.useReveal){ - container.set('reveal', this.options.revealOptions).get('reveal').chain(finish); - container.reveal(); - } else { - finish(); - } - }.bind(this), - failure: function(xhr){ - this.fireEvent('failure', xhr); - }.bind(this) - }); - this.attachReset(); - } - -}); - - /* --- @@ -4447,7 +4467,7 @@ authors: - Aaron Newton requires: - - /Locale + - Locale provides: [Locale.en-US.Form.Validator] @@ -4457,6 +4477,7 @@ provides: [Locale.en-US.Form.Validator] Locale.define('en-US', 'FormValidator', { required: 'This field is required.', + length: 'Please enter {length} characters (you entered {elLength} characters)', minLength: 'Please enter at least {minLength} characters (you entered {length} characters).', maxLength: 'Please enter no more than {maxLength} characters (you entered {length} characters).', integer: 'Please enter an integer in this field. Numbers with decimals (e.g. 1.25) are not permitted.', @@ -4481,7 +4502,7 @@ Locale.define('en-US', 'FormValidator', { match: 'This field needs to match the {matchName} field', startDate: 'the start date', endDate: 'the end date', - currendDate: 'the current date', + currentDate: 'the current date', afterDate: 'The date should be the same or after {label}.', beforeDate: 'The date should be the same or before {label}.', startMonth: 'Please select a start month', @@ -4490,7 +4511,6 @@ Locale.define('en-US', 'FormValidator', { }); - /* --- @@ -4508,16 +4528,17 @@ authors: requires: - Core/Options - Core/Events + - Core/Element.Delegation - Core/Slick.Finder - Core/Element.Event - Core/Element.Style - Core/JSON - - /Locale - - /Class.Binds - - /Date - - /Element.Forms - - /Locale.en-US.Form.Validator - - /Element.Shortcuts + - Locale + - Class.Binds + - Date + - Element.Forms + - Locale.en-US.Form.Validator + - Element.Shortcuts provides: [Form.Validator, InputValidator, FormValidator.BaseValidators] @@ -4577,7 +4598,7 @@ Element.Properties.validatorProps = { if (this.retrieve('$moo:validatorProps')) return this.retrieve('$moo:validatorProps'); if (this.getProperty('data-validator-properties') || this.getProperty('validatorProps')){ try { - this.store('$moo:validatorProps', JSON.decode(this.getProperty('validatorProps') || this.getProperty('data-validator-properties'))); + this.store('$moo:validatorProps', JSON.decode(this.getProperty('validatorProps') || this.getProperty('data-validator-properties'), false)); }catch(e){ return {}; } @@ -4593,7 +4614,7 @@ Element.Properties.validatorProps = { var split = cls.split(':'); if (split[1]){ try { - props[split[0]] = JSON.decode(split[1]); + props[split[0]] = JSON.decode(split[1], false); } catch(e){} } }); @@ -4609,8 +4630,6 @@ Form.Validator = new Class({ Implements: [Options, Events], - Binds: ['onSubmit'], - options: {/* onFormValidate: function(isValid, form, event){}, onElementValidate: function(isValid, field, className, warn){}, @@ -4636,11 +4655,15 @@ Form.Validator = new Class({ initialize: function(form, options){ this.setOptions(options); this.element = document.id(form); - this.element.store('validator', this); this.warningPrefix = Function.from(this.options.warningPrefix)(); this.errorPrefix = Function.from(this.options.errorPrefix)(); - if (this.options.evaluateOnSubmit) this.element.addEvent('submit', this.onSubmit); - if (this.options.evaluateFieldsOnBlur || this.options.evaluateFieldsOnChange) this.watchFields(this.getFields()); + this._bound = { + onSubmit: this.onSubmit.bind(this), + blurOrChange: function(event, field){ + this.validationMonitor(field, true); + }.bind(this) + }; + this.enable(); }, toElement: function(){ @@ -4651,13 +4674,24 @@ Form.Validator = new Class({ return (this.fields = this.element.getElements(this.options.fieldSelectors)); }, - watchFields: function(fields){ - fields.each(function(el){ - if (this.options.evaluateFieldsOnBlur) - el.addEvent('blur', this.validationMonitor.pass([el, false], this)); - if (this.options.evaluateFieldsOnChange) - el.addEvent('change', this.validationMonitor.pass([el, true], this)); - }, this); + enable: function(){ + this.element.store('validator', this); + if (this.options.evaluateOnSubmit) this.element.addEvent('submit', this._bound.onSubmit); + if (this.options.evaluateFieldsOnBlur){ + this.element.addEvent('blur:relay(input,select,textarea)', this._bound.blurOrChange); + } + if (this.options.evaluateFieldsOnChange){ + this.element.addEvent('change:relay(input,select,textarea)', this._bound.blurOrChange); + } + }, + + disable: function(){ + this.element.eliminate('validator'); + this.element.removeEvents({ + submit: this._bound.onSubmit, + 'blur:relay(input,select,textarea)': this._bound.blurOrChange, + 'change:relay(input,select,textarea)': this._bound.blurOrChange + }); }, validationMonitor: function(){ @@ -4736,8 +4770,8 @@ Form.Validator = new Class({ var validator = this.getValidator(className); if (warn != null) warn = false; if (this.hasValidator(field, 'warnOnly')) warn = true; - var isValid = this.hasValidator(field, 'ignoreValidation') || (validator ? validator.test(field) : true); - if (validator && field.isVisible()) this.fireEvent('elementValidate', [isValid, field, className, warn]); + var isValid = field.hasClass('ignoreValidation') || (validator ? validator.test(field) : true); + if (validator) this.fireEvent('elementValidate', [isValid, field, className, warn]); if (warn) return true; return isValid; }, @@ -4846,10 +4880,22 @@ Form.Validator.addAllThese([ } }], + ['length', { + errorMsg: function(element, props){ + if (typeOf(props.length) != 'null') + return Form.Validator.getMsg('length').substitute({length: props.length, elLength: element.get('value').length}); + else return ''; + }, + test: function(element, props){ + if (typeOf(props.length) != 'null') return (element.get('value').length == props.length || element.get('value').length == 0); + else return true; + } + }], + ['minLength', { errorMsg: function(element, props){ if (typeOf(props.minLength) != 'null') - return Form.Validator.getMsg('minLength').substitute({minLength:props.minLength,length:element.get('value').length }); + return Form.Validator.getMsg('minLength').substitute({minLength: props.minLength, length: element.get('value').length}); else return ''; }, test: function(element, props){ @@ -4862,7 +4908,7 @@ Form.Validator.addAllThese([ errorMsg: function(element, props){ //props is {maxLength:10} if (typeOf(props.maxLength) != 'null') - return Form.Validator.getMsg('maxLength').substitute({maxLength:props.maxLength,length:element.get('value').length }); + return Form.Validator.getMsg('maxLength').substitute({maxLength: props.maxLength, length: element.get('value').length}); else return ''; }, test: function(element, props){ @@ -4917,19 +4963,20 @@ Form.Validator.addAllThese([ }, test: function(element, props){ if (Form.Validator.getValidator('IsEmpty').test(element)) return true; - var dateLocale = Locale.getCurrent().sets.Date, - dateNouns = new RegExp([dateLocale.days, dateLocale.days_abbr, dateLocale.months, dateLocale.months_abbr].flatten().join('|'), 'i'), + var dateLocale = Locale.get('Date'), + dateNouns = new RegExp([dateLocale.days, dateLocale.days_abbr, dateLocale.months, dateLocale.months_abbr, dateLocale.AM, dateLocale.PM].flatten().join('|'), 'i'), value = element.get('value'), wordsInValue = value.match(/[a-z]+/gi); - if (wordsInValue && !wordsInValue.every(dateNouns.exec, dateNouns)) return false; + if (wordsInValue && !wordsInValue.every(dateNouns.exec, dateNouns)) return false; - var date = Date.parse(value), - format = props.dateFormat || '%x', - formatted = date.format(format); + var date = Date.parse(value); + if (!date) return false; - if (formatted != 'invalid date') element.set('value', formatted); - return date.isValid(); + var format = props.dateFormat || '%x', + formatted = date.format(format); + if (formatted != 'invalid date') element.set('value', formatted); + return date.isValid(); } }], @@ -5012,199 +5059,6 @@ Element.implement({ - -/* ---- - -script: Form.Validator.Inline.js - -name: Form.Validator.Inline - -description: Extends Form.Validator to add inline messages. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - /Form.Validator - -provides: [Form.Validator.Inline] - -... -*/ - -Form.Validator.Inline = new Class({ - - Extends: Form.Validator, - - options: { - showError: function(errorElement){ - if (errorElement.reveal) errorElement.reveal(); - else errorElement.setStyle('display', 'block'); - }, - hideError: function(errorElement){ - if (errorElement.dissolve) errorElement.dissolve(); - else errorElement.setStyle('display', 'none'); - }, - scrollToErrorsOnSubmit: true, - scrollToErrorsOnBlur: false, - scrollToErrorsOnChange: false, - scrollFxOptions: { - transition: 'quad:out', - offset: { - y: -20 - } - } - }, - - initialize: function(form, options){ - this.parent(form, options); - this.addEvent('onElementValidate', function(isValid, field, className, warn){ - var validator = this.getValidator(className); - if (!isValid && validator.getError(field)){ - if (warn) field.addClass('warning'); - var advice = this.makeAdvice(className, field, validator.getError(field), warn); - this.insertAdvice(advice, field); - this.showAdvice(className, field); - } else { - this.hideAdvice(className, field); - } - }); - }, - - makeAdvice: function(className, field, error, warn){ - var errorMsg = (warn) ? this.warningPrefix : this.errorPrefix; - errorMsg += (this.options.useTitles) ? field.title || error:error; - var cssClass = (warn) ? 'warning-advice' : 'validation-advice'; - var advice = this.getAdvice(className, field); - if (advice){ - advice = advice.set('html', errorMsg); - } else { - advice = new Element('div', { - html: errorMsg, - styles: { display: 'none' }, - id: 'advice-' + className.split(':')[0] + '-' + this.getFieldId(field) - }).addClass(cssClass); - } - field.store('$moo:advice-' + className, advice); - return advice; - }, - - getFieldId : function(field){ - return field.id ? field.id : field.id = 'input_' + field.name; - }, - - showAdvice: function(className, field){ - var advice = this.getAdvice(className, field); - if ( - advice && - !field.retrieve('$moo:' + this.getPropName(className)) && - ( - advice.getStyle('display') == 'none' || - advice.getStyle('visiblity') == 'hidden' || - advice.getStyle('opacity') == 0 - ) - ){ - field.store('$moo:' + this.getPropName(className), true); - this.options.showError(advice); - this.fireEvent('showAdvice', [field, advice, className]); - } - }, - - hideAdvice: function(className, field){ - var advice = this.getAdvice(className, field); - if (advice && field.retrieve('$moo:' + this.getPropName(className))){ - field.store('$moo:' + this.getPropName(className), false); - this.options.hideError(advice); - this.fireEvent('hideAdvice', [field, advice, className]); - } - }, - - getPropName: function(className){ - return 'advice' + className; - }, - - resetField: function(field){ - field = document.id(field); - if (!field) return this; - this.parent(field); - field.get('validators').each(function(className){ - this.hideAdvice(className, field); - }, this); - return this; - }, - - getAllAdviceMessages: function(field, force){ - var advice = []; - if (field.hasClass('ignoreValidation') && !force) return advice; - var validators = field.get('validators').some(function(cn){ - var warner = cn.test('^warn-') || field.hasClass('warnOnly'); - if (warner) cn = cn.replace(/^warn-/, ''); - var validator = this.getValidator(cn); - if (!validator) return; - advice.push({ - message: validator.getError(field), - warnOnly: warner, - passed: validator.test(), - validator: validator - }); - }, this); - return advice; - }, - - getAdvice: function(className, field){ - return field.retrieve('$moo:advice-' + className); - }, - - insertAdvice: function(advice, field){ - //Check for error position prop - var props = field.get('validatorProps'); - //Build advice - if (!props.msgPos || !document.id(props.msgPos)){ - if (field.type && field.type.toLowerCase() == 'radio') field.getParent().adopt(advice); - else advice.inject(document.id(field), 'after'); - } else { - document.id(props.msgPos).grab(advice); - } - }, - - validateField: function(field, force, scroll){ - var result = this.parent(field, force); - if (((this.options.scrollToErrorsOnSubmit && scroll == null) || scroll) && !result){ - var failed = document.id(this).getElement('.validation-failed'); - var par = document.id(this).getParent(); - while (par != document.body && par.getScrollSize().y == par.getSize().y){ - par = par.getParent(); - } - var fx = par.retrieve('$moo:fvScroller'); - if (!fx && window.Fx && Fx.Scroll){ - fx = new Fx.Scroll(par, this.options.scrollFxOptions); - par.store('$moo:fvScroller', fx); - } - if (failed){ - if (fx) fx.toElement(failed); - else par.scrollTo(par.getScroll().x, failed.getPosition(par).y - 20); - } - } - return result; - }, - - watchFields: function(fields){ - fields.each(function(el){ - if (this.options.evaluateFieldsOnBlur){ - el.addEvent('blur', this.validationMonitor.pass([el, false, this.options.scrollToErrorsOnBlur], this)); - } - if (this.options.evaluateFieldsOnChange){ - el.addEvent('change', this.validationMonitor.pass([el, true, this.options.scrollToErrorsOnChange], this)); - } - }, this); - } - -}); - - /* --- @@ -5220,7 +5074,7 @@ authors: - Aaron Newton requires: - - /Form.Validator + - Form.Validator provides: [Form.Validator.Extras] @@ -5401,7 +5255,8 @@ Form.Validator.addAllThese([ if (ccNum.test(/^4[0-9]{12}([0-9]{3})?$/)) valid_type = 'Visa'; else if (ccNum.test(/^5[1-5]([0-9]{14})$/)) valid_type = 'Master Card'; else if (ccNum.test(/^3[47][0-9]{13}$/)) valid_type = 'American Express'; - else if (ccNum.test(/^6011[0-9]{12}$/)) valid_type = 'Discover'; + else if (ccNum.test(/^6(?:011|5[0-9]{2})[0-9]{12}$/)) valid_type = 'Discover'; + else if (ccNum.test(/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/)) valid_type = 'Diners Club'; if (valid_type){ var sum = 0; @@ -5437,6 +5292,196 @@ Form.Validator.addAllThese([ ]); +/* +--- + +script: Form.Validator.Inline.js + +name: Form.Validator.Inline + +description: Extends Form.Validator to add inline messages. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Form.Validator + +provides: [Form.Validator.Inline] + +... +*/ + +Form.Validator.Inline = new Class({ + + Extends: Form.Validator, + + options: { + showError: function(errorElement){ + if (errorElement.reveal) errorElement.reveal(); + else errorElement.setStyle('display', 'block'); + }, + hideError: function(errorElement){ + if (errorElement.dissolve) errorElement.dissolve(); + else errorElement.setStyle('display', 'none'); + }, + scrollToErrorsOnSubmit: true, + scrollToErrorsOnBlur: false, + scrollToErrorsOnChange: false, + scrollFxOptions: { + transition: 'quad:out', + offset: { + y: -20 + } + } + }, + + initialize: function(form, options){ + this.parent(form, options); + this.addEvent('onElementValidate', function(isValid, field, className, warn){ + var validator = this.getValidator(className); + if (!isValid && validator.getError(field)){ + if (warn) field.addClass('warning'); + var advice = this.makeAdvice(className, field, validator.getError(field), warn); + this.insertAdvice(advice, field); + this.showAdvice(className, field); + } else { + this.hideAdvice(className, field); + } + }); + }, + + makeAdvice: function(className, field, error, warn){ + var errorMsg = (warn) ? this.warningPrefix : this.errorPrefix; + errorMsg += (this.options.useTitles) ? field.title || error:error; + var cssClass = (warn) ? 'warning-advice' : 'validation-advice'; + var advice = this.getAdvice(className, field); + if (advice){ + advice = advice.set('html', errorMsg); + } else { + advice = new Element('div', { + html: errorMsg, + styles: { display: 'none' }, + id: 'advice-' + className.split(':')[0] + '-' + this.getFieldId(field) + }).addClass(cssClass); + } + field.store('$moo:advice-' + className, advice); + return advice; + }, + + getFieldId : function(field){ + return field.id ? field.id : field.id = 'input_' + field.name; + }, + + showAdvice: function(className, field){ + var advice = this.getAdvice(className, field); + if ( + advice && + !field.retrieve('$moo:' + this.getPropName(className)) && + ( + advice.getStyle('display') == 'none' || + advice.getStyle('visibility') == 'hidden' || + advice.getStyle('opacity') == 0 + ) + ){ + field.store('$moo:' + this.getPropName(className), true); + this.options.showError(advice); + this.fireEvent('showAdvice', [field, advice, className]); + } + }, + + hideAdvice: function(className, field){ + var advice = this.getAdvice(className, field); + if (advice && field.retrieve('$moo:' + this.getPropName(className))){ + field.store('$moo:' + this.getPropName(className), false); + this.options.hideError(advice); + this.fireEvent('hideAdvice', [field, advice, className]); + } + }, + + getPropName: function(className){ + return 'advice' + className; + }, + + resetField: function(field){ + field = document.id(field); + if (!field) return this; + this.parent(field); + field.get('validators').each(function(className){ + this.hideAdvice(className, field); + }, this); + return this; + }, + + getAllAdviceMessages: function(field, force){ + var advice = []; + if (field.hasClass('ignoreValidation') && !force) return advice; + var validators = field.get('validators').some(function(cn){ + var warner = cn.test('^warn-') || field.hasClass('warnOnly'); + if (warner) cn = cn.replace(/^warn-/, ''); + var validator = this.getValidator(cn); + if (!validator) return; + advice.push({ + message: validator.getError(field), + warnOnly: warner, + passed: validator.test(), + validator: validator + }); + }, this); + return advice; + }, + + getAdvice: function(className, field){ + return field.retrieve('$moo:advice-' + className); + }, + + insertAdvice: function(advice, field){ + //Check for error position prop + var props = field.get('validatorProps'); + //Build advice + if (!props.msgPos || !document.id(props.msgPos)){ + if (field.type && field.type.toLowerCase() == 'radio') field.getParent().adopt(advice); + else advice.inject(document.id(field), 'after'); + } else { + document.id(props.msgPos).grab(advice); + } + }, + + validateField: function(field, force, scroll){ + var result = this.parent(field, force); + if (((this.options.scrollToErrorsOnSubmit && scroll == null) || scroll) && !result){ + var failed = document.id(this).getElement('.validation-failed'); + var par = document.id(this).getParent(); + while (par != document.body && par.getScrollSize().y == par.getSize().y){ + par = par.getParent(); + } + var fx = par.retrieve('$moo:fvScroller'); + if (!fx && window.Fx && Fx.Scroll){ + fx = new Fx.Scroll(par, this.options.scrollFxOptions); + par.store('$moo:fvScroller', fx); + } + if (failed){ + if (fx) fx.toElement(failed); + else par.scrollTo(par.getScroll().x, failed.getPosition(par).y - 20); + } + } + return result; + }, + + watchFields: function(fields){ + fields.each(function(el){ + if (this.options.evaluateFieldsOnBlur){ + el.addEvent('blur', this.validationMonitor.pass([el, false, this.options.scrollToErrorsOnBlur], this)); + } + if (this.options.evaluateFieldsOnChange){ + el.addEvent('change', this.validationMonitor.pass([el, true, this.options.scrollToErrorsOnChange], this)); + } + }, this); + } + +}); /* --- @@ -5573,7 +5618,6 @@ var OverText = new Class({ change: this.assert }); window.addEvent('resize', this.reposition); - this.assert(true); this.reposition(); return this; }, @@ -5632,7 +5676,7 @@ var OverText = new Class({ }, show: function(){ - if (this.text && !this.text.isDisplayed()){ + if (document.id(this.text) && !this.text.isDisplayed()){ this.text.show(); this.reposition(); this.fireEvent('textShow', [this.text, this.element]); @@ -5697,7 +5741,6 @@ Object.append(OverText, { }); - /* --- @@ -5714,7 +5757,7 @@ authors: requires: - Core/Fx.CSS - - /MooTools.More + - MooTools.More provides: [Fx.Elements] @@ -5773,7 +5816,6 @@ Fx.Elements = new Class({ }); - /* --- @@ -5790,7 +5832,7 @@ authors: requires: - Core/Element.Event - - /Fx.Elements + - Fx.Elements provides: [Fx.Accordion] @@ -5940,18 +5982,19 @@ Fx.Accordion = new Class({ if (useFx == null) useFx = true; if (typeOf(index) == 'element') index = elements.indexOf(index); - if (index == this.previous && !options.alwaysHide) return this; + if (index == this.current && !options.alwaysHide) return this; if (options.resetHeight){ - var prev = elements[this.previous]; + var prev = elements[this.current]; if (prev && !this.selfHidden){ for (var fx in effects) prev.setStyle(fx, prev[effects[fx]]); } } - if ((this.timer && options.link == 'chain') || (index === this.previous && !options.alwaysHide)) return this; + if ((this.timer && options.link == 'chain') || (index === this.current && !options.alwaysHide)) return this; - this.previous = index; + if (this.current != null) this.previous = this.current; + this.current = index; this.selfHidden = false; elements.each(function(el, i){ @@ -5983,7 +6026,6 @@ Fx.Accordion = new Class({ - /* --- @@ -6000,7 +6042,7 @@ authors: requires: - Core/Fx.Morph - - /Element.Position + - Element.Position provides: [Fx.Move] @@ -6056,6 +6098,176 @@ Element.implement({ }); +/* +--- + +script: Fx.Scroll.js + +name: Fx.Scroll + +description: Effect to smoothly scroll any element, including the window. + +license: MIT-style license + +authors: + - Valerio Proietti + +requires: + - Core/Fx + - Core/Element.Event + - Core/Element.Dimensions + - MooTools.More + +provides: [Fx.Scroll] + +... +*/ + +(function(){ + +Fx.Scroll = new Class({ + + Extends: Fx, + + options: { + offset: {x: 0, y: 0}, + wheelStops: true + }, + + initialize: function(element, options){ + this.element = this.subject = document.id(element); + this.parent(options); + + if (typeOf(this.element) != 'element') this.element = document.id(this.element.getDocument().body); + + if (this.options.wheelStops){ + var stopper = this.element, + cancel = this.cancel.pass(false, this); + this.addEvent('start', function(){ + stopper.addEvent('mousewheel', cancel); + }, true); + this.addEvent('complete', function(){ + stopper.removeEvent('mousewheel', cancel); + }, true); + } + }, + + set: function(){ + var now = Array.flatten(arguments); + this.element.scrollTo(now[0], now[1]); + return this; + }, + + compute: function(from, to, delta){ + return [0, 1].map(function(i){ + return Fx.compute(from[i], to[i], delta); + }); + }, + + start: function(x, y){ + if (!this.check(x, y)) return this; + var scroll = this.element.getScroll(); + return this.parent([scroll.x, scroll.y], [x, y]); + }, + + calculateScroll: function(x, y){ + var element = this.element, + scrollSize = element.getScrollSize(), + scroll = element.getScroll(), + size = element.getSize(), + offset = this.options.offset, + values = {x: x, y: y}; + + for (var z in values){ + if (!values[z] && values[z] !== 0) values[z] = scroll[z]; + if (typeOf(values[z]) != 'number') values[z] = scrollSize[z] - size[z]; + values[z] += offset[z]; + } + + return [values.x, values.y]; + }, + + toTop: function(){ + return this.start.apply(this, this.calculateScroll(false, 0)); + }, + + toLeft: function(){ + return this.start.apply(this, this.calculateScroll(0, false)); + }, + + toRight: function(){ + return this.start.apply(this, this.calculateScroll('right', false)); + }, + + toBottom: function(){ + return this.start.apply(this, this.calculateScroll(false, 'bottom')); + }, + + toElement: function(el, axes){ + axes = axes ? Array.from(axes) : ['x', 'y']; + var scroll = isBody(this.element) ? {x: 0, y: 0} : this.element.getScroll(); + var position = Object.map(document.id(el).getPosition(this.element), function(value, axis){ + return axes.contains(axis) ? value + scroll[axis] : false; + }); + return this.start.apply(this, this.calculateScroll(position.x, position.y)); + }, + + toElementEdge: function(el, axes, offset){ + axes = axes ? Array.from(axes) : ['x', 'y']; + el = document.id(el); + var to = {}, + position = el.getPosition(this.element), + size = el.getSize(), + scroll = this.element.getScroll(), + containerSize = this.element.getSize(), + edge = { + x: position.x + size.x, + y: position.y + size.y + }; + + ['x', 'y'].each(function(axis){ + if (axes.contains(axis)){ + if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis]; + if (position[axis] < scroll[axis]) to[axis] = position[axis]; + } + if (to[axis] == null) to[axis] = scroll[axis]; + if (offset && offset[axis]) to[axis] = to[axis] + offset[axis]; + }, this); + + if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); + return this; + }, + + toElementCenter: function(el, axes, offset){ + axes = axes ? Array.from(axes) : ['x', 'y']; + el = document.id(el); + var to = {}, + position = el.getPosition(this.element), + size = el.getSize(), + scroll = this.element.getScroll(), + containerSize = this.element.getSize(); + + ['x', 'y'].each(function(axis){ + if (axes.contains(axis)){ + to[axis] = position[axis] - (containerSize[axis] - size[axis]) / 2; + } + if (to[axis] == null) to[axis] = scroll[axis]; + if (offset && offset[axis]) to[axis] = to[axis] + offset[axis]; + }, this); + + if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); + return this; + } + +}); + + + +function isBody(element){ + return (/^(?:body|html)$/i).test(element.tagName); +} + +})(); /* --- @@ -6074,7 +6286,7 @@ authors: requires: - Core/Fx - Core/Element.Style - - /MooTools.More + - MooTools.More provides: [Fx.Slide] @@ -6228,180 +6440,6 @@ Element.implement({ }); - -/* ---- - -script: Fx.Scroll.js - -name: Fx.Scroll - -description: Effect to smoothly scroll any element, including the window. - -license: MIT-style license - -authors: - - Valerio Proietti - -requires: - - Core/Fx - - Core/Element.Event - - Core/Element.Dimensions - - /MooTools.More - -provides: [Fx.Scroll] - -... -*/ - -(function(){ - -Fx.Scroll = new Class({ - - Extends: Fx, - - options: { - offset: {x: 0, y: 0}, - wheelStops: true - }, - - initialize: function(element, options){ - this.element = this.subject = document.id(element); - this.parent(options); - - if (typeOf(this.element) != 'element') this.element = document.id(this.element.getDocument().body); - - if (this.options.wheelStops){ - var stopper = this.element, - cancel = this.cancel.pass(false, this); - this.addEvent('start', function(){ - stopper.addEvent('mousewheel', cancel); - }, true); - this.addEvent('complete', function(){ - stopper.removeEvent('mousewheel', cancel); - }, true); - } - }, - - set: function(){ - var now = Array.flatten(arguments); - if (Browser.firefox) now = [Math.round(now[0]), Math.round(now[1])]; // not needed anymore in newer firefox versions - this.element.scrollTo(now[0], now[1]); - return this; - }, - - compute: function(from, to, delta){ - return [0, 1].map(function(i){ - return Fx.compute(from[i], to[i], delta); - }); - }, - - start: function(x, y){ - if (!this.check(x, y)) return this; - var scroll = this.element.getScroll(); - return this.parent([scroll.x, scroll.y], [x, y]); - }, - - calculateScroll: function(x, y){ - var element = this.element, - scrollSize = element.getScrollSize(), - scroll = element.getScroll(), - size = element.getSize(), - offset = this.options.offset, - values = {x: x, y: y}; - - for (var z in values){ - if (!values[z] && values[z] !== 0) values[z] = scroll[z]; - if (typeOf(values[z]) != 'number') values[z] = scrollSize[z] - size[z]; - values[z] += offset[z]; - } - - return [values.x, values.y]; - }, - - toTop: function(){ - return this.start.apply(this, this.calculateScroll(false, 0)); - }, - - toLeft: function(){ - return this.start.apply(this, this.calculateScroll(0, false)); - }, - - toRight: function(){ - return this.start.apply(this, this.calculateScroll('right', false)); - }, - - toBottom: function(){ - return this.start.apply(this, this.calculateScroll(false, 'bottom')); - }, - - toElement: function(el, axes){ - axes = axes ? Array.from(axes) : ['x', 'y']; - var scroll = isBody(this.element) ? {x: 0, y: 0} : this.element.getScroll(); - var position = Object.map(document.id(el).getPosition(this.element), function(value, axis){ - return axes.contains(axis) ? value + scroll[axis] : false; - }); - return this.start.apply(this, this.calculateScroll(position.x, position.y)); - }, - - toElementEdge: function(el, axes, offset){ - axes = axes ? Array.from(axes) : ['x', 'y']; - el = document.id(el); - var to = {}, - position = el.getPosition(this.element), - size = el.getSize(), - scroll = this.element.getScroll(), - containerSize = this.element.getSize(), - edge = { - x: position.x + size.x, - y: position.y + size.y - }; - - ['x', 'y'].each(function(axis){ - if (axes.contains(axis)){ - if (edge[axis] > scroll[axis] + containerSize[axis]) to[axis] = edge[axis] - containerSize[axis]; - if (position[axis] < scroll[axis]) to[axis] = position[axis]; - } - if (to[axis] == null) to[axis] = scroll[axis]; - if (offset && offset[axis]) to[axis] = to[axis] + offset[axis]; - }, this); - - if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); - return this; - }, - - toElementCenter: function(el, axes, offset){ - axes = axes ? Array.from(axes) : ['x', 'y']; - el = document.id(el); - var to = {}, - position = el.getPosition(this.element), - size = el.getSize(), - scroll = this.element.getScroll(), - containerSize = this.element.getSize(); - - ['x', 'y'].each(function(axis){ - if (axes.contains(axis)){ - to[axis] = position[axis] - (containerSize[axis] - size[axis]) / 2; - } - if (to[axis] == null) to[axis] = scroll[axis]; - if (offset && offset[axis]) to[axis] = to[axis] + offset[axis]; - }, this); - - if (to.x != scroll.x || to.y != scroll.y) this.start(to.x, to.y); - return this; - } - -}); - - - -function isBody(element){ - return (/^(?:body|html)$/i).test(element.tagName); -} - -})(); - - /* --- @@ -6418,7 +6456,7 @@ authors: requires: - Core/Slick.Finder - - /Fx.Scroll + - Fx.Scroll provides: [Fx.SmoothScroll] @@ -6473,7 +6511,6 @@ Fx.SmoothScroll = new Class({ } }); - /* --- @@ -6490,8 +6527,8 @@ authors: requires: - Core/Element.Dimensions - - /Fx.Elements - - /Element.Measure + - Fx.Elements + - Element.Measure provides: [Fx.Sort] @@ -6647,895 +6684,5928 @@ Fx.Sort = new Class({ }); - /* --- -script: Drag.js +script: Keyboard.js -name: Drag +name: Keyboard -description: The base Drag Class. Can be used to drag and resize Elements using mouse events. +description: KeyboardEvents used to intercept events on a class for keyboard and format modifiers in a specific order so as to make alt+shift+c the same as shift+alt+c. license: MIT-style license authors: - - Valerio Proietti - - Tom Occhinno - - Jan Kassens + - Perrin Westrich + - Aaron Newton + - Scott Kyle requires: - Core/Events - Core/Options - Core/Element.Event - - Core/Element.Style - - Core/Element.Dimensions - - /MooTools.More + - Element.Event.Pseudos.Keys + +provides: [Keyboard] -provides: [Drag] ... - */ -var Drag = new Class({ +(function(){ + + var Keyboard = this.Keyboard = new Class({ + + Extends: Events, + + Implements: [Options], + + options: {/* + onActivate: function(){}, + onDeactivate: function(){},*/ + defaultEventType: 'keydown', + active: false, + manager: null, + events: {}, + nonParsedEvents: ['activate', 'deactivate', 'onactivate', 'ondeactivate', 'changed', 'onchanged'] + }, + + initialize: function(options){ + if (options && options.manager){ + this._manager = options.manager; + delete options.manager; + } + this.setOptions(options); + this._setup(); + }, + + addEvent: function(type, fn, internal){ + return this.parent(Keyboard.parse(type, this.options.defaultEventType, this.options.nonParsedEvents), fn, internal); + }, + + removeEvent: function(type, fn){ + return this.parent(Keyboard.parse(type, this.options.defaultEventType, this.options.nonParsedEvents), fn); + }, + + toggleActive: function(){ + return this[this.isActive() ? 'deactivate' : 'activate'](); + }, + + activate: function(instance){ + if (instance){ + if (instance.isActive()) return this; + //if we're stealing focus, store the last keyboard to have it so the relinquish command works + if (this._activeKB && instance != this._activeKB){ + this.previous = this._activeKB; + this.previous.fireEvent('deactivate'); + } + //if we're enabling a child, assign it so that events are now passed to it + this._activeKB = instance.fireEvent('activate'); + Keyboard.manager.fireEvent('changed'); + } else if (this._manager){ + //else we're enabling ourselves, we must ask our parent to do it for us + this._manager.activate(this); + } + return this; + }, + + isActive: function(){ + return this._manager ? (this._manager._activeKB == this) : (Keyboard.manager == this); + }, + + deactivate: function(instance){ + if (instance){ + if (instance === this._activeKB){ + this._activeKB = null; + instance.fireEvent('deactivate'); + Keyboard.manager.fireEvent('changed'); + } + } else if (this._manager){ + this._manager.deactivate(this); + } + return this; + }, + + relinquish: function(){ + if (this.isActive() && this._manager && this._manager.previous) this._manager.activate(this._manager.previous); + else this.deactivate(); + return this; + }, + + //management logic + manage: function(instance){ + if (instance._manager) instance._manager.drop(instance); + this._instances.push(instance); + instance._manager = this; + if (!this._activeKB) this.activate(instance); + return this; + }, + + drop: function(instance){ + instance.relinquish(); + this._instances.erase(instance); + if (this._activeKB == instance){ + if (this.previous && this._instances.contains(this.previous)) this.activate(this.previous); + else this._activeKB = this._instances[0]; + } + return this; + }, + + trace: function(){ + Keyboard.trace(this); + }, + + each: function(fn){ + Keyboard.each(this, fn); + }, + + /* + PRIVATE METHODS + */ + + _instances: [], + + _disable: function(instance){ + if (this._activeKB == instance) this._activeKB = null; + }, + + _setup: function(){ + this.addEvents(this.options.events); + //if this is the root manager, nothing manages it + if (Keyboard.manager && !this._manager) Keyboard.manager.manage(this); + if (this.options.active) this.activate(); + else this.relinquish(); + }, + + _handle: function(event, type){ + //Keyboard.stop(event) prevents key propagation + if (event.preventKeyboardPropagation) return; + + var bubbles = !!this._manager; + if (bubbles && this._activeKB){ + this._activeKB._handle(event, type); + if (event.preventKeyboardPropagation) return; + } + this.fireEvent(type, event); + + if (!bubbles && this._activeKB) this._activeKB._handle(event, type); + } + + }); + + var parsed = {}; + var modifiers = ['shift', 'control', 'alt', 'meta']; + var regex = /^(?:shift|control|ctrl|alt|meta)$/; + + Keyboard.parse = function(type, eventType, ignore){ + if (ignore && ignore.contains(type.toLowerCase())) return type; + + type = type.toLowerCase().replace(/^(keyup|keydown):/, function($0, $1){ + eventType = $1; + return ''; + }); + + if (!parsed[type]){ + if (type != '+'){ + var key, mods = {}; + type.split('+').each(function(part){ + if (regex.test(part)) mods[part] = true; + else key = part; + }); + + mods.control = mods.control || mods.ctrl; // allow both control and ctrl + + var keys = []; + modifiers.each(function(mod){ + if (mods[mod]) keys.push(mod); + }); + + if (key) keys.push(key); + parsed[type] = keys.join('+'); + } else { + parsed[type] = type; + } + } + + return eventType + ':keys(' + parsed[type] + ')'; + }; + + Keyboard.each = function(keyboard, fn){ + var current = keyboard || Keyboard.manager; + while (current){ + fn(current); + current = current._activeKB; + } + }; + + Keyboard.stop = function(event){ + event.preventKeyboardPropagation = true; + }; + + Keyboard.manager = new Keyboard({ + active: true + }); + + Keyboard.trace = function(keyboard){ + keyboard = keyboard || Keyboard.manager; + var hasConsole = window.console && console.log; + if (hasConsole) console.log('the following items have focus: '); + Keyboard.each(keyboard, function(current){ + if (hasConsole) console.log(document.id(current.widget) || current.wiget || current); + }); + }; + + var handler = function(event){ + var keys = []; + modifiers.each(function(mod){ + if (event[mod]) keys.push(mod); + }); + + if (!regex.test(event.key)) keys.push(event.key); + Keyboard.manager._handle(event, event.type + ':keys(' + keys.join('+') + ')'); + }; + + document.addEvents({ + 'keyup': handler, + 'keydown': handler + }); + +})(); + +/* +--- + +script: Keyboard.Extras.js + +name: Keyboard.Extras + +description: Enhances Keyboard by adding the ability to name and describe keyboard shortcuts, and the ability to grab shortcuts by name and bind the shortcut to different keys. + +license: MIT-style license + +authors: + - Perrin Westrich + +requires: + - Keyboard + - MooTools.More + +provides: [Keyboard.Extras] + +... +*/ +Keyboard.prototype.options.nonParsedEvents.combine(['rebound', 'onrebound']); + +Keyboard.implement({ + + /* + shortcut should be in the format of: + { + 'keys': 'shift+s', // the default to add as an event. + 'description': 'blah blah blah', // a brief description of the functionality. + 'handler': function(){} // the event handler to run when keys are pressed. + } + */ + addShortcut: function(name, shortcut){ + this._shortcuts = this._shortcuts || []; + this._shortcutIndex = this._shortcutIndex || {}; + + shortcut.getKeyboard = Function.from(this); + shortcut.name = name; + this._shortcutIndex[name] = shortcut; + this._shortcuts.push(shortcut); + if (shortcut.keys) this.addEvent(shortcut.keys, shortcut.handler); + return this; + }, + + addShortcuts: function(obj){ + for (var name in obj) this.addShortcut(name, obj[name]); + return this; + }, + + removeShortcut: function(name){ + var shortcut = this.getShortcut(name); + if (shortcut && shortcut.keys){ + this.removeEvent(shortcut.keys, shortcut.handler); + delete this._shortcutIndex[name]; + this._shortcuts.erase(shortcut); + } + return this; + }, + + removeShortcuts: function(names){ + names.each(this.removeShortcut, this); + return this; + }, + + getShortcuts: function(){ + return this._shortcuts || []; + }, + + getShortcut: function(name){ + return (this._shortcutIndex || {})[name]; + } + +}); + +Keyboard.rebind = function(newKeys, shortcuts){ + Array.from(shortcuts).each(function(shortcut){ + shortcut.getKeyboard().removeEvent(shortcut.keys, shortcut.handler); + shortcut.getKeyboard().addEvent(newKeys, shortcut.handler); + shortcut.keys = newKeys; + shortcut.getKeyboard().fireEvent('rebound'); + }); +}; + + +Keyboard.getActiveShortcuts = function(keyboard){ + var activeKBS = [], activeSCS = []; + Keyboard.each(keyboard, [].push.bind(activeKBS)); + activeKBS.each(function(kb){ activeSCS.extend(kb.getShortcuts()); }); + return activeSCS; +}; + +Keyboard.getShortcut = function(name, keyboard, opts){ + opts = opts || {}; + var shortcuts = opts.many ? [] : null, + set = opts.many ? function(kb){ + var shortcut = kb.getShortcut(name); + if (shortcut) shortcuts.push(shortcut); + } : function(kb){ + if (!shortcuts) shortcuts = kb.getShortcut(name); + }; + Keyboard.each(keyboard, set); + return shortcuts; +}; + +Keyboard.getShortcuts = function(name, keyboard){ + return Keyboard.getShortcut(name, keyboard, { many: true }); +}; + +/* +--- + +script: HtmlTable.js + +name: HtmlTable + +description: Builds table elements with methods to add rows. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Core/Options + - Core/Events + - Class.Occlude + +provides: [HtmlTable] + +... +*/ + +var HtmlTable = new Class({ + + Implements: [Options, Events, Class.Occlude], + + options: { + properties: { + cellpadding: 0, + cellspacing: 0, + border: 0 + }, + rows: [], + headers: [], + footers: [] + }, + + property: 'HtmlTable', + + initialize: function(){ + var params = Array.link(arguments, {options: Type.isObject, table: Type.isElement, id: Type.isString}); + this.setOptions(params.options); + if (!params.table && params.id) params.table = document.id(params.id); + this.element = params.table || new Element('table', this.options.properties); + if (this.occlude()) return this.occluded; + this.build(); + }, + + build: function(){ + this.element.store('HtmlTable', this); + + this.body = document.id(this.element.tBodies[0]) || new Element('tbody').inject(this.element); + $$(this.body.rows); + + if (this.options.headers.length) this.setHeaders(this.options.headers); + else this.thead = document.id(this.element.tHead); + + if (this.thead) this.head = this.getHead(); + if (this.options.footers.length) this.setFooters(this.options.footers); + + this.tfoot = document.id(this.element.tFoot); + if (this.tfoot) this.foot = document.id(this.tfoot.rows[0]); + + this.options.rows.each(function(row){ + this.push(row); + }, this); + }, + + toElement: function(){ + return this.element; + }, + + empty: function(){ + this.body.empty(); + return this; + }, + + set: function(what, items){ + var target = (what == 'headers') ? 'tHead' : 'tFoot', + lower = target.toLowerCase(); + + this[lower] = (document.id(this.element[target]) || new Element(lower).inject(this.element, 'top')).empty(); + var data = this.push(items, {}, this[lower], what == 'headers' ? 'th' : 'td'); + + if (what == 'headers') this.head = this.getHead(); + else this.foot = this.getHead(); + + return data; + }, + + getHead: function(){ + var rows = this.thead.rows; + return rows.length > 1 ? $$(rows) : rows.length ? document.id(rows[0]) : false; + }, + + setHeaders: function(headers){ + this.set('headers', headers); + return this; + }, + + setFooters: function(footers){ + this.set('footers', footers); + return this; + }, + + update: function(tr, row, tag){ + var tds = tr.getChildren(tag || 'td'), last = tds.length - 1; + + row.each(function(data, index){ + var td = tds[index] || new Element(tag || 'td').inject(tr), + content = ((data && Object.prototype.hasOwnProperty.call(data, 'content')) ? data.content : '') || data, + type = typeOf(content); + + if (data && Object.prototype.hasOwnProperty.call(data, 'properties')) td.set(data.properties); + if (/(element(s?)|array|collection)/.test(type)) td.empty().adopt(content); + else td.set('html', content); + + if (index > last) tds.push(td); + else tds[index] = td; + }); + + return { + tr: tr, + tds: tds + }; + }, + + push: function(row, rowProperties, target, tag, where){ + if (typeOf(row) == 'element' && row.get('tag') == 'tr'){ + row.inject(target || this.body, where); + return { + tr: row, + tds: row.getChildren('td') + }; + } + return this.update(new Element('tr', rowProperties).inject(target || this.body, where), row, tag); + }, + + pushMany: function(rows, rowProperties, target, tag, where){ + return rows.map(function(row){ + return this.push(row, rowProperties, target, tag, where); + }, this); + } + +}); + + +['adopt', 'inject', 'wraps', 'grab', 'replaces', 'dispose'].each(function(method){ + HtmlTable.implement(method, function(){ + this.element[method].apply(this.element, arguments); + return this; + }); +}); + + + +/* +--- + +script: HtmlTable.Select.js + +name: HtmlTable.Select + +description: Builds a stripy, sortable table with methods to add rows. Rows can be selected with the mouse or keyboard navigation. + +license: MIT-style license + +authors: + - Harald Kirschner + - Aaron Newton + +requires: + - Keyboard + - Keyboard.Extras + - HtmlTable + - Class.refactor + - Element.Delegation.Pseudo + - Element.Shortcuts + +provides: [HtmlTable.Select] + +... +*/ + +HtmlTable = Class.refactor(HtmlTable, { + + options: { + /*onRowFocus: function(){}, + onRowUnfocus: function(){},*/ + useKeyboard: true, + classRowSelected: 'table-tr-selected', + classRowHovered: 'table-tr-hovered', + classSelectable: 'table-selectable', + shiftForMultiSelect: true, + allowMultiSelect: true, + selectable: false, + selectHiddenRows: false + }, + + initialize: function(){ + this.previous.apply(this, arguments); + if (this.occluded) return this.occluded; + + this.selectedRows = new Elements(); + + if (!this.bound) this.bound = {}; + this.bound.mouseleave = this.mouseleave.bind(this); + this.bound.clickRow = this.clickRow.bind(this); + this.bound.activateKeyboard = function(){ + if (this.keyboard && this.selectEnabled) this.keyboard.activate(); + }.bind(this); + + if (this.options.selectable) this.enableSelect(); + }, + + empty: function(){ + if (this.body.rows.length) this.selectNone(); + return this.previous(); + }, + + enableSelect: function(){ + this.selectEnabled = true; + this.attachSelects(); + this.element.addClass(this.options.classSelectable); + return this; + }, + + disableSelect: function(){ + this.selectEnabled = false; + this.attachSelects(false); + this.element.removeClass(this.options.classSelectable); + return this; + }, + + push: function(){ + var ret = this.previous.apply(this, arguments); + this.updateSelects(); + return ret; + }, + + toggleRow: function(row){ + return this[(this.isSelected(row) ? 'de' : '') + 'selectRow'](row); + }, + + selectRow: function(row, _nocheck){ + //private variable _nocheck: boolean whether or not to confirm the row is in the table body + //added here for optimization when selecting ranges + if (this.isSelected(row) || (!_nocheck && !this.body.getChildren().contains(row))) return; + if (!this.options.allowMultiSelect) this.selectNone(); + + if (!this.isSelected(row)){ + this.selectedRows.push(row); + row.addClass(this.options.classRowSelected); + this.fireEvent('rowFocus', [row, this.selectedRows]); + this.fireEvent('stateChanged'); + } + + this.focused = row; + document.clearSelection(); + + return this; + }, + + isSelected: function(row){ + return this.selectedRows.contains(row); + }, + + getSelected: function(){ + return this.selectedRows; + }, + + serialize: function(){ + var previousSerialization = this.previous.apply(this, arguments) || {}; + if (this.options.selectable){ + previousSerialization.selectedRows = this.selectedRows.map(function(row){ + return Array.indexOf(this.body.rows, row); + }.bind(this)); + } + return previousSerialization; + }, + + restore: function(tableState){ + if(this.options.selectable && tableState.selectedRows){ + tableState.selectedRows.each(function(index){ + this.selectRow(this.body.rows[index]); + }.bind(this)); + } + this.previous.apply(this, arguments); + }, + + deselectRow: function(row, _nocheck){ + if (!this.isSelected(row) || (!_nocheck && !this.body.getChildren().contains(row))) return; + + this.selectedRows = new Elements(Array.from(this.selectedRows).erase(row)); + row.removeClass(this.options.classRowSelected); + this.fireEvent('rowUnfocus', [row, this.selectedRows]); + this.fireEvent('stateChanged'); + return this; + }, + + selectAll: function(selectNone){ + if (!selectNone && !this.options.allowMultiSelect) return; + this.selectRange(0, this.body.rows.length, selectNone); + return this; + }, + + selectNone: function(){ + return this.selectAll(true); + }, + + selectRange: function(startRow, endRow, _deselect){ + if (!this.options.allowMultiSelect && !_deselect) return; + var method = _deselect ? 'deselectRow' : 'selectRow', + rows = Array.clone(this.body.rows); + + if (typeOf(startRow) == 'element') startRow = rows.indexOf(startRow); + if (typeOf(endRow) == 'element') endRow = rows.indexOf(endRow); + endRow = endRow < rows.length - 1 ? endRow : rows.length - 1; + + if (endRow < startRow){ + var tmp = startRow; + startRow = endRow; + endRow = tmp; + } + + for (var i = startRow; i <= endRow; i++){ + if (this.options.selectHiddenRows || rows[i].isDisplayed()) this[method](rows[i], true); + } + + return this; + }, + + deselectRange: function(startRow, endRow){ + this.selectRange(startRow, endRow, true); + }, + +/* + Private methods: +*/ + + enterRow: function(row){ + if (this.hovered) this.hovered = this.leaveRow(this.hovered); + this.hovered = row.addClass(this.options.classRowHovered); + }, + + leaveRow: function(row){ + row.removeClass(this.options.classRowHovered); + }, + + updateSelects: function(){ + Array.each(this.body.rows, function(row){ + var binders = row.retrieve('binders'); + if (!binders && !this.selectEnabled) return; + if (!binders){ + binders = { + mouseenter: this.enterRow.pass([row], this), + mouseleave: this.leaveRow.pass([row], this) + }; + row.store('binders', binders); + } + if (this.selectEnabled) row.addEvents(binders); + else row.removeEvents(binders); + }, this); + }, + + shiftFocus: function(offset, event){ + if (!this.focused) return this.selectRow(this.body.rows[0], event); + var to = this.getRowByOffset(offset, this.options.selectHiddenRows); + if (to === null || this.focused == this.body.rows[to]) return this; + this.toggleRow(this.body.rows[to], event); + }, + + clickRow: function(event, row){ + var selecting = (event.shift || event.meta || event.control) && this.options.shiftForMultiSelect; + if (!selecting && !(event.rightClick && this.isSelected(row) && this.options.allowMultiSelect)) this.selectNone(); + + if (event.rightClick) this.selectRow(row); + else this.toggleRow(row); + + if (event.shift){ + this.selectRange(this.rangeStart || this.body.rows[0], row, this.rangeStart ? !this.isSelected(row) : true); + this.focused = row; + } + this.rangeStart = row; + }, + + getRowByOffset: function(offset, includeHiddenRows){ + if (!this.focused) return 0; + var index = Array.indexOf(this.body.rows, this.focused); + if ((index == 0 && offset < 0) || (index == this.body.rows.length -1 && offset > 0)) return null; + if (includeHiddenRows){ + index += offset; + } else { + var limit = 0, + count = 0; + if (offset > 0){ + while (count < offset && index < this.body.rows.length -1){ + if (this.body.rows[++index].isDisplayed()) count++; + } + } else { + while (count > offset && index > 0){ + if (this.body.rows[--index].isDisplayed()) count--; + } + } + } + return index; + }, + + attachSelects: function(attach){ + attach = attach != null ? attach : true; + + var method = attach ? 'addEvents' : 'removeEvents'; + this.element[method]({ + mouseleave: this.bound.mouseleave, + click: this.bound.activateKeyboard + }); + + this.body[method]({ + 'click:relay(tr)': this.bound.clickRow, + 'contextmenu:relay(tr)': this.bound.clickRow + }); + + if (this.options.useKeyboard || this.keyboard){ + if (!this.keyboard) this.keyboard = new Keyboard(); + if (!this.selectKeysDefined){ + this.selectKeysDefined = true; + var timer, held; + + var move = function(offset){ + var mover = function(e){ + clearTimeout(timer); + e.preventDefault(); + var to = this.body.rows[this.getRowByOffset(offset, this.options.selectHiddenRows)]; + if (e.shift && to && this.isSelected(to)){ + this.deselectRow(this.focused); + this.focused = to; + } else { + if (to && (!this.options.allowMultiSelect || !e.shift)){ + this.selectNone(); + } + this.shiftFocus(offset, e); + } + + if (held){ + timer = mover.delay(100, this, e); + } else { + timer = (function(){ + held = true; + mover(e); + }).delay(400); + } + }.bind(this); + return mover; + }.bind(this); + + var clear = function(){ + clearTimeout(timer); + held = false; + }; + + this.keyboard.addEvents({ + 'keydown:shift+up': move(-1), + 'keydown:shift+down': move(1), + 'keyup:shift+up': clear, + 'keyup:shift+down': clear, + 'keyup:up': clear, + 'keyup:down': clear + }); + + var shiftHint = ''; + if (this.options.allowMultiSelect && this.options.shiftForMultiSelect && this.options.useKeyboard){ + shiftHint = " (Shift multi-selects)."; + } + + this.keyboard.addShortcuts({ + 'Select Previous Row': { + keys: 'up', + shortcut: 'up arrow', + handler: move(-1), + description: 'Select the previous row in the table.' + shiftHint + }, + 'Select Next Row': { + keys: 'down', + shortcut: 'down arrow', + handler: move(1), + description: 'Select the next row in the table.' + shiftHint + } + }); + + } + this.keyboard[attach ? 'activate' : 'deactivate'](); + } + this.updateSelects(); + }, + + mouseleave: function(){ + if (this.hovered) this.leaveRow(this.hovered); + } + +}); + +/* +--- + +script: HtmlTable.Sort.js + +name: HtmlTable.Sort + +description: Builds a stripy, sortable table with methods to add rows. + +license: MIT-style license + +authors: + - Harald Kirschner + - Aaron Newton + - Jacob Thornton + +requires: + - Core/Hash + - HtmlTable + - Class.refactor + - Element.Delegation.Pseudo + - String.Extras + - Date + +provides: [HtmlTable.Sort] + +... +*/ +(function(){ + +var readOnlyNess = document.createElement('table'); +try { + readOnlyNess.innerHTML = ''; + readOnlyNess = readOnlyNess.childNodes.length === 0; +} catch (e){ + readOnlyNess = true; +} + +HtmlTable = Class.refactor(HtmlTable, { + + options: {/* + onSort: function(){}, */ + sortIndex: 0, + sortReverse: false, + parsers: [], + defaultParser: 'string', + classSortable: 'table-sortable', + classHeadSort: 'table-th-sort', + classHeadSortRev: 'table-th-sort-rev', + classNoSort: 'table-th-nosort', + classGroupHead: 'table-tr-group-head', + classGroup: 'table-tr-group', + classCellSort: 'table-td-sort', + classSortSpan: 'table-th-sort-span', + sortable: false, + thSelector: 'th' + }, + + initialize: function (){ + this.previous.apply(this, arguments); + if (this.occluded) return this.occluded; + this.sorted = {index: null, dir: 1}; + if (!this.bound) this.bound = {}; + this.bound.headClick = this.headClick.bind(this); + this.sortSpans = new Elements(); + if (this.options.sortable){ + this.enableSort(); + if (this.options.sortIndex != null) this.sort(this.options.sortIndex, this.options.sortReverse); + } + }, + + attachSorts: function(attach){ + this.detachSorts(); + if (attach !== false) this.element.addEvent('click:relay(' + this.options.thSelector + ')', this.bound.headClick); + }, + + detachSorts: function(){ + this.element.removeEvents('click:relay(' + this.options.thSelector + ')'); + }, + + setHeaders: function(){ + this.previous.apply(this, arguments); + if (this.sortable) this.setParsers(); + }, + + setParsers: function(){ + this.parsers = this.detectParsers(); + }, + + detectParsers: function(){ + return this.head && this.head.getElements(this.options.thSelector).flatten().map(this.detectParser, this); + }, + + detectParser: function(cell, index){ + if (cell.hasClass(this.options.classNoSort) || cell.retrieve('htmltable-parser')) return cell.retrieve('htmltable-parser'); + var thDiv = new Element('div'); + thDiv.adopt(cell.childNodes).inject(cell); + var sortSpan = new Element('span', {'class': this.options.classSortSpan}).inject(thDiv, 'top'); + this.sortSpans.push(sortSpan); + var parser = this.options.parsers[index], + rows = this.body.rows, + cancel; + switch (typeOf(parser)){ + case 'function': parser = {convert: parser}; cancel = true; break; + case 'string': parser = parser; cancel = true; break; + } + if (!cancel){ + HtmlTable.ParserPriority.some(function(parserName){ + var current = HtmlTable.Parsers[parserName], + match = current.match; + if (!match) return false; + for (var i = 0, j = rows.length; i < j; i++){ + var cell = document.id(rows[i].cells[index]), + text = cell ? cell.get('html').clean() : ''; + if (text && match.test(text)){ + parser = current; + return true; + } + } + }); + } + if (!parser) parser = this.options.defaultParser; + cell.store('htmltable-parser', parser); + return parser; + }, + + headClick: function(event, el){ + if (!this.head || el.hasClass(this.options.classNoSort)) return; + return this.sort(Array.indexOf(this.head.getElements(this.options.thSelector).flatten(), el) % this.body.rows[0].cells.length); + }, + + serialize: function(){ + var previousSerialization = this.previous.apply(this, arguments) || {}; + if (this.options.sortable){ + previousSerialization.sortIndex = this.sorted.index; + previousSerialization.sortReverse = this.sorted.reverse; + } + return previousSerialization; + }, + + restore: function(tableState){ + if(this.options.sortable && tableState.sortIndex){ + this.sort(tableState.sortIndex, tableState.sortReverse); + } + this.previous.apply(this, arguments); + }, + + setSortedState: function(index, reverse){ + if (reverse != null) this.sorted.reverse = reverse; + else if (this.sorted.index == index) this.sorted.reverse = !this.sorted.reverse; + else this.sorted.reverse = this.sorted.index == null; + + if (index != null) this.sorted.index = index; + }, + + setHeadSort: function(sorted){ + var head = $$(!this.head.length ? this.head.cells[this.sorted.index] : this.head.map(function(row){ + return row.getElements(this.options.thSelector)[this.sorted.index]; + }, this).clean()); + if (!head.length) return; + if (sorted){ + head.addClass(this.options.classHeadSort); + if (this.sorted.reverse) head.addClass(this.options.classHeadSortRev); + else head.removeClass(this.options.classHeadSortRev); + } else { + head.removeClass(this.options.classHeadSort).removeClass(this.options.classHeadSortRev); + } + }, + + setRowSort: function(data, pre){ + var count = data.length, + body = this.body, + group, + rowIndex; + + while (count){ + var item = data[--count], + position = item.position, + row = body.rows[position]; + + if (row.disabled) continue; + if (!pre){ + group = this.setGroupSort(group, row, item); + this.setRowStyle(row, count); + } + body.appendChild(row); + + for (rowIndex = 0; rowIndex < count; rowIndex++){ + if (data[rowIndex].position > position) data[rowIndex].position--; + } + } + }, + + setRowStyle: function(row, i){ + this.previous(row, i); + row.cells[this.sorted.index].addClass(this.options.classCellSort); + }, + + setGroupSort: function(group, row, item){ + if (group == item.value) row.removeClass(this.options.classGroupHead).addClass(this.options.classGroup); + else row.removeClass(this.options.classGroup).addClass(this.options.classGroupHead); + return item.value; + }, + + getParser: function(){ + var parser = this.parsers[this.sorted.index]; + return typeOf(parser) == 'string' ? HtmlTable.Parsers[parser] : parser; + }, + + sort: function(index, reverse, pre, sortFunction){ + if (!this.head) return; + + if (!pre){ + this.clearSort(); + this.setSortedState(index, reverse); + this.setHeadSort(true); + } + + var parser = this.getParser(); + if (!parser) return; + + var rel; + if (!readOnlyNess){ + rel = this.body.getParent(); + this.body.dispose(); + } + + var data = this.parseData(parser).sort(sortFunction ? sortFunction : function(a, b){ + if (a.value === b.value) return 0; + return a.value > b.value ? 1 : -1; + }); + + if (this.sorted.reverse == (parser == HtmlTable.Parsers['input-checked'])) data.reverse(true); + this.setRowSort(data, pre); + + if (rel) rel.grab(this.body); + this.fireEvent('stateChanged'); + return this.fireEvent('sort', [this.body, this.sorted.index]); + }, + + parseData: function(parser){ + return Array.map(this.body.rows, function(row, i){ + var value = parser.convert.call(document.id(row.cells[this.sorted.index])); + return { + position: i, + value: value + }; + }, this); + }, + + clearSort: function(){ + this.setHeadSort(false); + this.body.getElements('td').removeClass(this.options.classCellSort); + }, + + reSort: function(){ + if (this.sortable) this.sort.call(this, this.sorted.index, this.sorted.reverse); + return this; + }, + + enableSort: function(){ + this.element.addClass(this.options.classSortable); + this.attachSorts(true); + this.setParsers(); + this.sortable = true; + return this; + }, + + disableSort: function(){ + this.element.removeClass(this.options.classSortable); + this.attachSorts(false); + this.sortSpans.each(function(span){ + span.destroy(); + }); + this.sortSpans.empty(); + this.sortable = false; + return this; + } + +}); + +HtmlTable.ParserPriority = ['date', 'input-checked', 'input-value', 'float', 'number']; + +HtmlTable.Parsers = { + + 'date': { + match: /^\d{2}[-\/ ]\d{2}[-\/ ]\d{2,4}$/, + convert: function(){ + var d = Date.parse(this.get('text').stripTags()); + return (typeOf(d) == 'date') ? d.format('db') : ''; + }, + type: 'date' + }, + 'input-checked': { + match: / type="(radio|checkbox)"/, + convert: function(){ + return this.getElement('input').checked; + } + }, + 'input-value': { + match: / (size[z] + pos[z]) && scroll[z] + size[z] != scrollSize[z]){ + change[z] = (this.page[z] - size[z] + bottom - pos[z]) * this.options.velocity; + } + change[z] = change[z].round(); + } + if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]); + } + +}); + +/* +--- + +script: Tips.js + +name: Tips + +description: Class for creating nice tips that follow the mouse cursor when hovering an element. + +license: MIT-style license + +authors: + - Valerio Proietti + - Christoph Pojer + - Luis Merino + +requires: + - Core/Options + - Core/Events + - Core/Element.Event + - Core/Element.Style + - Core/Element.Dimensions + - MooTools.More + +provides: [Tips] + +... +*/ + +(function(){ + +var read = function(option, element){ + return (option) ? (typeOf(option) == 'function' ? option(element) : element.get(option)) : ''; +}; + +this.Tips = new Class({ Implements: [Events, Options], options: {/* - onBeforeStart: function(thisElement){}, - onStart: function(thisElement, event){}, - onSnap: function(thisElement){}, - onDrag: function(thisElement, event){}, - onCancel: function(thisElement){}, - onComplete: function(thisElement, event){},*/ - snap: 6, - unit: 'px', - grid: false, - style: true, - limit: false, - handle: false, - invert: false, - preventDefault: false, - stopPropagation: false, - modifiers: {x: 'left', y: 'top'} + id: null, + onAttach: function(element){}, + onDetach: function(element){}, + onBound: function(coords){},*/ + onShow: function(){ + this.tip.setStyle('display', 'block'); + }, + onHide: function(){ + this.tip.setStyle('display', 'none'); + }, + title: 'title', + text: function(element){ + return element.get('rel') || element.get('href'); + }, + showDelay: 100, + hideDelay: 100, + className: 'tip-wrap', + offset: {x: 16, y: 16}, + windowPadding: {x:0, y:0}, + fixed: false, + waiAria: true }, initialize: function(){ var params = Array.link(arguments, { - 'options': Type.isObject, - 'element': function(obj){ + options: Type.isObject, + elements: function(obj){ return obj != null; } }); + this.setOptions(params.options); + if (params.elements) this.attach(params.elements); + this.container = new Element('div', {'class': 'tip'}); - this.element = document.id(params.element); - this.document = this.element.getDocument(); - this.setOptions(params.options || {}); - var htype = typeOf(this.options.handle); - this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element; - this.mouse = {'now': {}, 'pos': {}}; - this.value = {'start': {}, 'now': {}}; - - this.selection = (Browser.ie) ? 'selectstart' : 'mousedown'; - - - if (Browser.ie && !Drag.ondragstartFixed){ - document.ondragstart = Function.from(false); - Drag.ondragstartFixed = true; + if (this.options.id){ + this.container.set('id', this.options.id); + if (this.options.waiAria) this.attachWaiAria(); } - - this.bound = { - start: this.start.bind(this), - check: this.check.bind(this), - drag: this.drag.bind(this), - stop: this.stop.bind(this), - cancel: this.cancel.bind(this), - eventStop: Function.from(false) - }; - this.attach(); }, - attach: function(){ - this.handles.addEvent('mousedown', this.bound.start); - return this; - }, + toElement: function(){ + if (this.tip) return this.tip; - detach: function(){ - this.handles.removeEvent('mousedown', this.bound.start); - return this; - }, - - start: function(event){ - var options = this.options; - - if (event.rightClick) return; - - if (options.preventDefault) event.preventDefault(); - if (options.stopPropagation) event.stopPropagation(); - this.mouse.start = event.page; - - this.fireEvent('beforeStart', this.element); - - var limit = options.limit; - this.limit = {x: [], y: []}; - - var z, coordinates; - for (z in options.modifiers){ - if (!options.modifiers[z]) continue; - - var style = this.element.getStyle(options.modifiers[z]); - - // Some browsers (IE and Opera) don't always return pixels. - if (style && !style.match(/px$/)){ - if (!coordinates) coordinates = this.element.getCoordinates(this.element.getOffsetParent()); - style = coordinates[options.modifiers[z]]; + this.tip = new Element('div', { + 'class': this.options.className, + styles: { + position: 'absolute', + top: 0, + left: 0 } + }).adopt( + new Element('div', {'class': 'tip-top'}), + this.container, + new Element('div', {'class': 'tip-bottom'}) + ); - if (options.style) this.value.now[z] = (style || 0).toInt(); - else this.value.now[z] = this.element[options.modifiers[z]]; + return this.tip; + }, - if (options.invert) this.value.now[z] *= -1; + attachWaiAria: function(){ + var id = this.options.id; + this.container.set('role', 'tooltip'); - this.mouse.pos[z] = event.page[z] - this.value.now[z]; - - if (limit && limit[z]){ - var i = 2; - while (i--){ - var limitZI = limit[z][i]; - if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI; + if (!this.waiAria){ + this.waiAria = { + show: function(element){ + if (id) element.set('aria-describedby', id); + this.container.set('aria-hidden', 'false'); + }, + hide: function(element){ + if (id) element.erase('aria-describedby'); + this.container.set('aria-hidden', 'true'); } - } - } - - if (typeOf(this.options.grid) == 'number') this.options.grid = { - x: this.options.grid, - y: this.options.grid - }; - - var events = { - mousemove: this.bound.check, - mouseup: this.bound.cancel - }; - events[this.selection] = this.bound.eventStop; - this.document.addEvents(events); - }, - - check: function(event){ - if (this.options.preventDefault) event.preventDefault(); - var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2))); - if (distance > this.options.snap){ - this.cancel(); - this.document.addEvents({ - mousemove: this.bound.drag, - mouseup: this.bound.stop - }); - this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element); - } - }, - - drag: function(event){ - var options = this.options; - - if (options.preventDefault) event.preventDefault(); - this.mouse.now = event.page; - - for (var z in options.modifiers){ - if (!options.modifiers[z]) continue; - this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z]; - - if (options.invert) this.value.now[z] *= -1; - - if (options.limit && this.limit[z]){ - if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){ - this.value.now[z] = this.limit[z][1]; - } else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){ - this.value.now[z] = this.limit[z][0]; - } - } - - if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]); - - if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit); - else this.element[options.modifiers[z]] = this.value.now[z]; - } - - this.fireEvent('drag', [this.element, event]); - }, - - cancel: function(event){ - this.document.removeEvents({ - mousemove: this.bound.check, - mouseup: this.bound.cancel - }); - if (event){ - this.document.removeEvent(this.selection, this.bound.eventStop); - this.fireEvent('cancel', this.element); - } - }, - - stop: function(event){ - var events = { - mousemove: this.bound.drag, - mouseup: this.bound.stop - }; - events[this.selection] = this.bound.eventStop; - this.document.removeEvents(events); - if (event) this.fireEvent('complete', [this.element, event]); - } - -}); - -Element.implement({ - - makeResizable: function(options){ - var drag = new Drag(this, Object.merge({ - modifiers: { - x: 'width', - y: 'height' - } - }, options)); - - this.store('resizer', drag); - return drag.addEvent('drag', function(){ - this.fireEvent('resize', drag); - }.bind(this)); - } - -}); - - -/* ---- - -script: Drag.Move.js - -name: Drag.Move - -description: A Drag extension that provides support for the constraining of draggables to containers and droppables. - -license: MIT-style license - -authors: - - Valerio Proietti - - Tom Occhinno - - Jan Kassens - - Aaron Newton - - Scott Kyle - -requires: - - Core/Element.Dimensions - - /Drag - -provides: [Drag.Move] - -... -*/ - -Drag.Move = new Class({ - - Extends: Drag, - - options: {/* - onEnter: function(thisElement, overed){}, - onLeave: function(thisElement, overed){}, - onDrop: function(thisElement, overed, event){},*/ - droppables: [], - container: false, - precalculate: false, - includeMargins: true, - checkDroppables: true - }, - - initialize: function(element, options){ - this.parent(element, options); - element = this.element; - - this.droppables = $$(this.options.droppables); - this.container = document.id(this.options.container); - - if (this.container && typeOf(this.container) != 'element') - this.container = document.id(this.container.getDocument().body); - - if (this.options.style){ - if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){ - var parent = element.getOffsetParent(), - styles = element.getStyles('left', 'top'); - if (parent && (styles.left == 'auto' || styles.top == 'auto')){ - element.setPosition(element.getPosition(parent)); - } - } - - if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute'); - } - - this.addEvent('start', this.checkDroppables, true); - this.overed = null; - }, - - start: function(event){ - if (this.container) this.options.limit = this.calculateLimit(); - - if (this.options.precalculate){ - this.positions = this.droppables.map(function(el){ - return el.getCoordinates(); - }); - } - - this.parent(event); - }, - - calculateLimit: function(){ - var element = this.element, - container = this.container, - - offsetParent = document.id(element.getOffsetParent()) || document.body, - containerCoordinates = container.getCoordinates(offsetParent), - elementMargin = {}, - elementBorder = {}, - containerMargin = {}, - containerBorder = {}, - offsetParentPadding = {}; - - ['top', 'right', 'bottom', 'left'].each(function(pad){ - elementMargin[pad] = element.getStyle('margin-' + pad).toInt(); - elementBorder[pad] = element.getStyle('border-' + pad).toInt(); - containerMargin[pad] = container.getStyle('margin-' + pad).toInt(); - containerBorder[pad] = container.getStyle('border-' + pad).toInt(); - offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt(); - }, this); - - var width = element.offsetWidth + elementMargin.left + elementMargin.right, - height = element.offsetHeight + elementMargin.top + elementMargin.bottom, - left = 0, - top = 0, - right = containerCoordinates.right - containerBorder.right - width, - bottom = containerCoordinates.bottom - containerBorder.bottom - height; - - if (this.options.includeMargins){ - left += elementMargin.left; - top += elementMargin.top; - } else { - right += elementMargin.right; - bottom += elementMargin.bottom; - } - - if (element.getStyle('position') == 'relative'){ - var coords = element.getCoordinates(offsetParent); - coords.left -= element.getStyle('left').toInt(); - coords.top -= element.getStyle('top').toInt(); - - left -= coords.left; - top -= coords.top; - if (container.getStyle('position') != 'relative'){ - left += containerBorder.left; - top += containerBorder.top; - } - right += elementMargin.left - coords.left; - bottom += elementMargin.top - coords.top; - - if (container != offsetParent){ - left += containerMargin.left + offsetParentPadding.left; - top += ((Browser.ie6 || Browser.ie7) ? 0 : containerMargin.top) + offsetParentPadding.top; - } - } else { - left -= elementMargin.left; - top -= elementMargin.top; - if (container != offsetParent){ - left += containerCoordinates.left + containerBorder.left; - top += containerCoordinates.top + containerBorder.top; - } - } - - return { - x: [left, right], - y: [top, bottom] - }; - }, - - getDroppableCoordinates: function(element){ - var position = element.getCoordinates(); - if (element.getStyle('position') == 'fixed'){ - var scroll = window.getScroll(); - position.left += scroll.x; - position.right += scroll.x; - position.top += scroll.y; - position.bottom += scroll.y; - } - return position; - }, - - checkDroppables: function(){ - var overed = this.droppables.filter(function(el, i){ - el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el); - var now = this.mouse.now; - return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top); - }, this).getLast(); - - if (this.overed != overed){ - if (this.overed) this.fireEvent('leave', [this.element, this.overed]); - if (overed) this.fireEvent('enter', [this.element, overed]); - this.overed = overed; - } - }, - - drag: function(event){ - this.parent(event); - if (this.options.checkDroppables && this.droppables.length) this.checkDroppables(); - }, - - stop: function(event){ - this.checkDroppables(); - this.fireEvent('drop', [this.element, this.overed, event]); - this.overed = null; - return this.parent(event); - } - -}); - -Element.implement({ - - makeDraggable: function(options){ - var drag = new Drag.Move(this, options); - this.store('dragger', drag); - return drag; - } - -}); - - -/* ---- - -script: Slider.js - -name: Slider - -description: Class for creating horizontal and vertical slider controls. - -license: MIT-style license - -authors: - - Valerio Proietti - -requires: - - Core/Element.Dimensions - - /Class.Binds - - /Drag - - /Element.Measure - -provides: [Slider] - -... -*/ - -var Slider = new Class({ - - Implements: [Events, Options], - - Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'], - - options: {/* - onTick: function(intPosition){}, - onChange: function(intStep){}, - onComplete: function(strStep){},*/ - onTick: function(position){ - this.setKnobPosition(position); - }, - initialStep: 0, - snap: false, - offset: 0, - range: false, - wheel: false, - steps: 100, - mode: 'horizontal' - }, - - initialize: function(element, knob, options){ - this.setOptions(options); - options = this.options; - this.element = document.id(element); - knob = this.knob = document.id(knob); - this.previousChange = this.previousEnd = this.step = -1; - - var limit = {}, - modifiers = {x: false, y: false}; - - switch (options.mode){ - case 'vertical': - this.axis = 'y'; - this.property = 'top'; - this.offset = 'offsetHeight'; - break; - case 'horizontal': - this.axis = 'x'; - this.property = 'left'; - this.offset = 'offsetWidth'; - } - - this.setSliderDimensions(); - this.setRange(options.range); - - if (knob.getStyle('position') == 'static') knob.setStyle('position', 'relative'); - knob.setStyle(this.property, -options.offset); - modifiers[this.axis] = this.property; - limit[this.axis] = [-options.offset, this.full - options.offset]; - - var dragOptions = { - snap: 0, - limit: limit, - modifiers: modifiers, - onDrag: this.draggedKnob, - onStart: this.draggedKnob, - onBeforeStart: (function(){ - this.isDragging = true; - }).bind(this), - onCancel: function(){ - this.isDragging = false; - }.bind(this), - onComplete: function(){ - this.isDragging = false; - this.draggedKnob(); - this.end(); - }.bind(this) - }; - if (options.snap) this.setSnap(dragOptions); - - this.drag = new Drag(knob, dragOptions); - this.attach(); - if (options.initialStep != null) this.set(options.initialStep); - }, - - attach: function(){ - this.element.addEvent('mousedown', this.clickedElement); - if (this.options.wheel) this.element.addEvent('mousewheel', this.scrolledElement); - this.drag.attach(); - return this; - }, - - detach: function(){ - this.element.removeEvent('mousedown', this.clickedElement) - .removeEvent('mousewheel', this.scrolledElement); - this.drag.detach(); - return this; - }, - - autosize: function(){ - this.setSliderDimensions() - .setKnobPosition(this.toPosition(this.step)); - this.drag.options.limit[this.axis] = [-this.options.offset, this.full - this.options.offset]; - if (this.options.snap) this.setSnap(); - return this; - }, - - setSnap: function(options){ - if (!options) options = this.drag.options; - options.grid = Math.ceil(this.stepWidth); - options.limit[this.axis][1] = this.full; - return this; - }, - - setKnobPosition: function(position){ - if (this.options.snap) position = this.toPosition(this.step); - this.knob.setStyle(this.property, position); - return this; - }, - - setSliderDimensions: function(){ - this.full = this.element.measure(function(){ - this.half = this.knob[this.offset] / 2; - return this.element[this.offset] - this.knob[this.offset] + (this.options.offset * 2); - }.bind(this)); - return this; - }, - - set: function(step){ - if (!((this.range > 0) ^ (step < this.min))) step = this.min; - if (!((this.range > 0) ^ (step > this.max))) step = this.max; - - this.step = Math.round(step); - return this.checkStep() - .fireEvent('tick', this.toPosition(this.step)) - .end(); - }, - - setRange: function(range, pos){ - this.min = Array.pick([range[0], 0]); - this.max = Array.pick([range[1], this.options.steps]); - this.range = this.max - this.min; - this.steps = this.options.steps || this.full; - this.stepSize = Math.abs(this.range) / this.steps; - this.stepWidth = this.stepSize * this.full / Math.abs(this.range); - if (range) this.set(Array.pick([pos, this.step]).floor(this.min).max(this.max)); - return this; - }, - - clickedElement: function(event){ - if (this.isDragging || event.target == this.knob) return; - - var dir = this.range < 0 ? -1 : 1, - position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half; - - position = position.limit(-this.options.offset, this.full - this.options.offset); - - this.step = Math.round(this.min + dir * this.toStep(position)); - - this.checkStep() - .fireEvent('tick', position) - .end(); - }, - - scrolledElement: function(event){ - var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0); - this.set(this.step + (mode ? -1 : 1) * this.stepSize); - event.stop(); - }, - - draggedKnob: function(){ - var dir = this.range < 0 ? -1 : 1, - position = this.drag.value.now[this.axis]; - - position = position.limit(-this.options.offset, this.full -this.options.offset); - - this.step = Math.round(this.min + dir * this.toStep(position)); - this.checkStep(); - }, - - checkStep: function(){ - var step = this.step; - if (this.previousChange != step){ - this.previousChange = step; - this.fireEvent('change', step); - } - return this; - }, - - end: function(){ - var step = this.step; - if (this.previousEnd !== step){ - this.previousEnd = step; - this.fireEvent('complete', step + ''); - } - return this; - }, - - toStep: function(position){ - var step = (position + this.options.offset) * this.stepSize / this.full * this.steps; - return this.options.steps ? Math.round(step -= step % this.stepSize) : step; - }, - - toPosition: function(step){ - return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset; - } - -}); - - -/* ---- - -script: Sortables.js - -name: Sortables - -description: Class for creating a drag and drop sorting interface for lists of items. - -license: MIT-style license - -authors: - - Tom Occhino - -requires: - - Core/Fx.Morph - - /Drag.Move - -provides: [Sortables] - -... -*/ - -var Sortables = new Class({ - - Implements: [Events, Options], - - options: {/* - onSort: function(element, clone){}, - onStart: function(element, clone){}, - onComplete: function(element){},*/ - opacity: 1, - clone: false, - revert: false, - handle: false, - dragOptions: {} - }, - - initialize: function(lists, options){ - this.setOptions(options); - - this.elements = []; - this.lists = []; - this.idle = true; - - this.addLists($$(document.id(lists) || lists)); - - if (!this.options.clone) this.options.revert = false; - if (this.options.revert) this.effect = new Fx.Morph(null, Object.merge({ - duration: 250, - link: 'cancel' - }, this.options.revert)); - }, - - attach: function(){ - this.addLists(this.lists); - return this; - }, - - detach: function(){ - this.lists = this.removeLists(this.lists); - return this; - }, - - addItems: function(){ - Array.flatten(arguments).each(function(element){ - this.elements.push(element); - var start = element.retrieve('sortables:start', function(event){ - this.start.call(this, event, element); - }.bind(this)); - (this.options.handle ? element.getElement(this.options.handle) || element : element).addEvent('mousedown', start); - }, this); - return this; - }, - - addLists: function(){ - Array.flatten(arguments).each(function(list){ - this.lists.include(list); - this.addItems(list.getChildren()); - }, this); - return this; - }, - - removeItems: function(){ - return $$(Array.flatten(arguments).map(function(element){ - this.elements.erase(element); - var start = element.retrieve('sortables:start'); - (this.options.handle ? element.getElement(this.options.handle) || element : element).removeEvent('mousedown', start); - - return element; - }, this)); - }, - - removeLists: function(){ - return $$(Array.flatten(arguments).map(function(list){ - this.lists.erase(list); - this.removeItems(list.getChildren()); - - return list; - }, this)); - }, - - getClone: function(event, element){ - if (!this.options.clone) return new Element(element.tagName).inject(document.body); - if (typeOf(this.options.clone) == 'function') return this.options.clone.call(this, event, element, this.list); - var clone = element.clone(true).setStyles({ - margin: 0, - position: 'absolute', - visibility: 'hidden', - width: element.getStyle('width') - }).addEvent('mousedown', function(event){ - element.fireEvent('mousedown', event); - }); - //prevent the duplicated radio inputs from unchecking the real one - if (clone.get('html').test('radio')){ - clone.getElements('input[type=radio]').each(function(input, i){ - input.set('name', 'clone_' + i); - if (input.get('checked')) element.getElements('input[type=radio]')[i].set('checked', true); - }); - } - - return clone.inject(this.list).setPosition(element.getPosition(element.getOffsetParent())); - }, - - getDroppables: function(){ - var droppables = this.list.getChildren().erase(this.clone).erase(this.element); - if (!this.options.constrain) droppables.append(this.lists).erase(this.list); - return droppables; - }, - - insert: function(dragging, element){ - var where = 'inside'; - if (this.lists.contains(element)){ - this.list = element; - this.drag.droppables = this.getDroppables(); - } else { - where = this.element.getAllPrevious().contains(element) ? 'before' : 'after'; - } - this.element.inject(element, where); - this.fireEvent('sort', [this.element, this.clone]); - }, - - start: function(event, element){ - if ( - !this.idle || - event.rightClick || - ['button', 'input', 'a'].contains(event.target.get('tag')) - ) return; - - this.idle = false; - this.element = element; - this.opacity = element.get('opacity'); - this.list = element.getParent(); - this.clone = this.getClone(event, element); - - this.drag = new Drag.Move(this.clone, Object.merge({ - - droppables: this.getDroppables() - }, this.options.dragOptions)).addEvents({ - onSnap: function(){ - event.stop(); - this.clone.setStyle('visibility', 'visible'); - this.element.set('opacity', this.options.opacity || 0); - this.fireEvent('start', [this.element, this.clone]); - }.bind(this), - onEnter: this.insert.bind(this), - onCancel: this.end.bind(this), - onComplete: this.end.bind(this) - }); - - this.clone.inject(this.element, 'before'); - this.drag.start(event); - }, - - end: function(){ - this.drag.detach(); - this.element.set('opacity', this.opacity); - if (this.effect){ - var dim = this.element.getStyles('width', 'height'), - clone = this.clone, - pos = clone.computePosition(this.element.getPosition(this.clone.getOffsetParent())); - - var destroy = function(){ - this.removeEvent('cancel', destroy); - clone.destroy(); }; - - this.effect.element = clone; - this.effect.start({ - top: pos.top, - left: pos.left, - width: dim.width, - height: dim.height, - opacity: 0.25 - }).addEvent('cancel', destroy).chain(destroy); - } else { - this.clone.destroy(); } - this.reset(); + this.addEvents(this.waiAria); }, - reset: function(){ - this.idle = true; - this.fireEvent('complete', this.element); + detachWaiAria: function(){ + if (this.waiAria){ + this.container.erase('role'); + this.container.erase('aria-hidden'); + this.removeEvents(this.waiAria); + } }, - serialize: function(){ - var params = Array.link(arguments, { - modifier: Type.isFunction, - index: function(obj){ - return obj != null; - } - }); - var serial = this.lists.map(function(list){ - return list.getChildren().map(params.modifier || function(element){ - return element.get('id'); + attach: function(elements){ + $$(elements).each(function(element){ + var title = read(this.options.title, element), + text = read(this.options.text, element); + + element.set('title', '').store('tip:native', title).retrieve('tip:title', title); + element.retrieve('tip:text', text); + this.fireEvent('attach', [element]); + + var events = ['enter', 'leave']; + if (!this.options.fixed) events.push('move'); + + events.each(function(value){ + var event = element.retrieve('tip:' + value); + if (!event) event = function(event){ + this['element' + value.capitalize()].apply(this, [event, element]); + }.bind(this); + + element.store('tip:' + value, event).addEvent('mouse' + value, event); }, this); }, this); - var index = params.index; - if (this.lists.length == 1) index = 0; - return (index || index === 0) && index >= 0 && index < this.lists.length ? serial[index] : serial; + return this; + }, + + detach: function(elements){ + $$(elements).each(function(element){ + ['enter', 'leave', 'move'].each(function(value){ + element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value); + }); + + this.fireEvent('detach', [element]); + + if (this.options.title == 'title'){ // This is necessary to check if we can revert the title + var original = element.retrieve('tip:native'); + if (original) element.set('title', original); + } + }, this); + + return this; + }, + + elementEnter: function(event, element){ + clearTimeout(this.timer); + this.timer = (function(){ + this.container.empty(); + + ['title', 'text'].each(function(value){ + var content = element.retrieve('tip:' + value); + var div = this['_' + value + 'Element'] = new Element('div', { + 'class': 'tip-' + value + }).inject(this.container); + if (content) this.fill(div, content); + }, this); + this.show(element); + this.position((this.options.fixed) ? {page: element.getPosition()} : event); + }).delay(this.options.showDelay, this); + }, + + elementLeave: function(event, element){ + clearTimeout(this.timer); + this.timer = this.hide.delay(this.options.hideDelay, this, element); + this.fireForParent(event, element); + }, + + setTitle: function(title){ + if (this._titleElement){ + this._titleElement.empty(); + this.fill(this._titleElement, title); + } + return this; + }, + + setText: function(text){ + if (this._textElement){ + this._textElement.empty(); + this.fill(this._textElement, text); + } + return this; + }, + + fireForParent: function(event, element){ + element = element.getParent(); + if (!element || element == document.body) return; + if (element.retrieve('tip:enter')) element.fireEvent('mouseenter', event); + else this.fireForParent(event, element); + }, + + elementMove: function(event, element){ + this.position(event); + }, + + position: function(event){ + if (!this.tip) document.id(this); + + var size = window.getSize(), scroll = window.getScroll(), + tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight}, + props = {x: 'left', y: 'top'}, + bounds = {y: false, x2: false, y2: false, x: false}, + obj = {}; + + for (var z in props){ + obj[props[z]] = event.page[z] + this.options.offset[z]; + if (obj[props[z]] < 0) bounds[z] = true; + if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowPadding[z]){ + obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z]; + bounds[z+'2'] = true; + } + } + + this.fireEvent('bound', bounds); + this.tip.setStyles(obj); + }, + + fill: function(element, contents){ + if (typeof contents == 'string') element.set('html', contents); + else element.adopt(contents); + }, + + show: function(element){ + if (!this.tip) document.id(this); + if (!this.tip.getParent()) this.tip.inject(document.body); + this.fireEvent('show', [this.tip, element]); + }, + + hide: function(element){ + if (!this.tip) document.id(this); + this.fireEvent('hide', [this.tip, element]); } }); +})(); + +/* +--- + +name: Locale.EU.Number + +description: Number messages for Europe. + +license: MIT-style license + +authors: + - Arian Stolwijk + +requires: + - Locale + +provides: [Locale.EU.Number] + +... +*/ + +Locale.define('EU', 'Number', { + + decimal: ',', + group: '.', + + currency: { + prefix: '€ ' + } + +}); + +/* +--- + +script: Locale.Set.From.js + +name: Locale.Set.From + +description: Provides an alternative way to create Locale.Set objects. + +license: MIT-style license + +authors: + - Tim Wienk + +requires: + - Core/JSON + - Locale + +provides: Locale.Set.From + +... +*/ + +(function(){ + +var parsers = { + 'json': JSON.decode +}; + +Locale.Set.defineParser = function(name, fn){ + parsers[name] = fn; +}; + +Locale.Set.from = function(set, type){ + if (instanceOf(set, Locale.Set)) return set; + + if (!type && typeOf(set) == 'string') type = 'json'; + if (parsers[type]) set = parsers[type](set); + + var locale = new Locale.Set; + + locale.sets = set.sets || {}; + + if (set.inherits){ + locale.inherits.locales = Array.from(set.inherits.locales); + locale.inherits.sets = set.inherits.sets || {}; + } + + return locale; +}; + +})(); + +/* +--- + +name: Locale.ZA.Number + +description: Number messages for ZA. + +license: MIT-style license + +authors: + - Werner Mollentze + +requires: + - Locale + +provides: [Locale.ZA.Number] + +... +*/ + +Locale.define('ZA', 'Number', { + + decimal: '.', + group: ',', + + currency: { + prefix: 'R ' + } + +}); + + + +/* +--- + +name: Locale.af-ZA.Date + +description: Date messages for ZA Afrikaans. + +license: MIT-style license + +authors: + - Werner Mollentze + +requires: + - Locale + +provides: [Locale.af-ZA.Date] + +... +*/ + +Locale.define('af-ZA', 'Date', { + + months: ['Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember'], + months_abbr: ['Jan', 'Feb', 'Mrt', 'Apr', 'Mei', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'], + days: ['Sondag', 'Maandag', 'Dinsdag', 'Woensdag', 'Donderdag', 'Vrydag', 'Saterdag'], + days_abbr: ['Son', 'Maa', 'Din', 'Woe', 'Don', 'Vry', 'Sat'], + + // Culture's date order: MM/DD/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d-%m-%Y', + shortTime: '%H:%M', + AM: 'VM', + PM: 'NM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: function(dayOfMonth){ + return ((dayOfMonth > 1 && dayOfMonth < 20 && dayOfMonth != 8) || (dayOfMonth > 100 && dayOfMonth.toString().substr(-2, 1) == '1')) ? 'de' : 'ste'; + }, + + lessThanMinuteAgo: 'minder as \'n minuut gelede', + minuteAgo: 'ongeveer \'n minuut gelede', + minutesAgo: '{delta} minute gelede', + hourAgo: 'omtret \'n uur gelede', + hoursAgo: 'ongeveer {delta} ure gelede', + dayAgo: '1 dag gelede', + daysAgo: '{delta} dae gelede', + weekAgo: '1 week gelede', + weeksAgo: '{delta} weke gelede', + monthAgo: '1 maand gelede', + monthsAgo: '{delta} maande gelede', + yearAgo: '1 jaar gelede', + yearsAgo: '{delta} jare gelede', + + lessThanMinuteUntil: 'oor minder as \'n minuut', + minuteUntil: 'oor ongeveer \'n minuut', + minutesUntil: 'oor {delta} minute', + hourUntil: 'oor ongeveer \'n uur', + hoursUntil: 'oor {delta} uur', + dayUntil: 'oor ongeveer \'n dag', + daysUntil: 'oor {delta} dae', + weekUntil: 'oor \'n week', + weeksUntil: 'oor {delta} weke', + monthUntil: 'oor \'n maand', + monthsUntil: 'oor {delta} maande', + yearUntil: 'oor \'n jaar', + yearsUntil: 'oor {delta} jaar' + +}); + +/* +--- + +name: Locale.af-ZA.Form.Validator + +description: Form Validator messages for Afrikaans. + +license: MIT-style license + +authors: + - Werner Mollentze + +requires: + - Locale + +provides: [Locale.af-ZA.Form.Validator] + +... +*/ + +Locale.define('af-ZA', 'FormValidator', { + + required: 'Hierdie veld word vereis.', + length: 'Voer asseblief {length} karakters in (u het {elLength} karakters ingevoer)', + minLength: 'Voer asseblief ten minste {minLength} karakters in (u het {length} karakters ingevoer).', + maxLength: 'Moet asseblief nie meer as {maxLength} karakters invoer nie (u het {length} karakters ingevoer).', + integer: 'Voer asseblief \'n heelgetal in hierdie veld in. Getalle met desimale (bv. 1.25) word nie toegelaat nie.', + numeric: 'Voer asseblief slegs numeriese waardes in hierdie veld in (bv. "1" of "1.1" of "-1" of "-1.1").', + digits: 'Gebruik asseblief slegs nommers en punktuasie in hierdie veld. (by voorbeeld, \'n telefoon nommer wat koppeltekens en punte bevat is toelaatbaar).', + alpha: 'Gebruik asseblief slegs letters (a-z) binne-in hierdie veld. Geen spasies of ander karakters word toegelaat nie.', + alphanum: 'Gebruik asseblief slegs letters (a-z) en nommers (0-9) binne-in hierdie veld. Geen spasies of ander karakters word toegelaat nie.', + dateSuchAs: 'Voer asseblief \'n geldige datum soos {date} in', + dateInFormatMDY: 'Voer asseblief \'n geldige datum soos MM/DD/YYYY in (bv. "12/31/1999")', + email: 'Voer asseblief \'n geldige e-pos adres in. Byvoorbeeld "fred@domain.com".', + url: 'Voer asseblief \'n geldige bronadres (URL) soos http://www.example.com in.', + currencyDollar: 'Voer asseblief \'n geldige $ bedrag in. Byvoorbeeld $100.00 .', + oneRequired: 'Voer asseblief iets in vir ten minste een van hierdie velde.', + errorPrefix: 'Fout: ', + warningPrefix: 'Waarskuwing: ', + + // Form.Validator.Extras + noSpace: 'Daar mag geen spasies in hierdie toevoer wees nie.', + reqChkByNode: 'Geen items is gekies nie.', + requiredChk: 'Hierdie veld word vereis.', + reqChkByName: 'Kies asseblief \'n {label}.', + match: 'Hierdie veld moet by die {matchName} veld pas', + startDate: 'die begin datum', + endDate: 'die eind datum', + currentDate: 'die huidige datum', + afterDate: 'Die datum moet dieselfde of na {label} wees.', + beforeDate: 'Die datum moet dieselfde of voor {label} wees.', + startMonth: 'Kies asseblief \'n begin maand', + sameMonth: 'Hierdie twee datums moet in dieselfde maand wees - u moet een of beide verander.', + creditcard: 'Die ingevoerde kredietkaart nommer is ongeldig. Bevestig asseblief die nommer en probeer weer. {length} syfers is ingevoer.' + +}); + +/* +--- + +name: Locale.af-ZA.Number + +description: Number messages for ZA Afrikaans. + +license: MIT-style license + +authors: + - Werner Mollentze + +requires: + - Locale + - Locale.ZA.Number + +provides: [Locale.af-ZA.Number] + +... +*/ + +Locale.define('af-ZA').inherit('ZA', 'Number'); + +/* +--- + +name: Locale.ar.Date + +description: Date messages for Arabic. + +license: MIT-style license + +authors: + - Chafik Barbar + +requires: + - Locale + +provides: [Locale.ar.Date] + +... +*/ + +Locale.define('ar', 'Date', { + + // Culture's date order: DD/MM/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%H:%M' + +}); + +/* +--- + +name: Locale.ar.Form.Validator + +description: Form Validator messages for Arabic. + +license: MIT-style license + +authors: + - Chafik Barbar + +requires: + - Locale + +provides: [Locale.ar.Form.Validator] + +... +*/ + +Locale.define('ar', 'FormValidator', { + + required: 'هذا الحقل مطلوب.', + minLength: 'رجاءً إدخال {minLength} أحرف على الأقل (تم إدخال {length} أحرف).', + maxLength: 'الرجاء عدم إدخال أكثر من {maxLength} أحرف (تم إدخال {length} أحرف).', + integer: 'الرجاء إدخال عدد صحيح في هذا الحقل. أي رقم ذو كسر عشري أو مئوي (مثال 1.25 ) غير مسموح.', + numeric: 'الرجاء إدخال قيم رقمية في هذا الحقل (مثال "1" أو "1.1" أو "-1" أو "-1.1").', + digits: 'الرجاء أستخدام قيم رقمية وعلامات ترقيمية فقط في هذا الحقل (مثال, رقم هاتف مع نقطة أو شحطة)', + alpha: 'الرجاء أستخدام أحرف فقط (ا-ي) في هذا الحقل. أي فراغات أو علامات غير مسموحة.', + alphanum: 'الرجاء أستخدام أحرف فقط (ا-ي) أو أرقام (0-9) فقط في هذا الحقل. أي فراغات أو علامات غير مسموحة.', + dateSuchAs: 'الرجاء إدخال تاريخ صحيح كالتالي {date}', + dateInFormatMDY: 'الرجاء إدخال تاريخ صحيح (مثال, 31-12-1999)', + email: 'الرجاء إدخال بريد إلكتروني صحيح.', + url: 'الرجاء إدخال عنوان إلكتروني صحيح مثل http://www.example.com', + currencyDollar: 'الرجاء إدخال قيمة $ صحيحة. مثال, 100.00$', + oneRequired: 'الرجاء إدخال قيمة في أحد هذه الحقول على الأقل.', + errorPrefix: 'خطأ: ', + warningPrefix: 'تحذير: ' + +}); + +/* +--- + +name: Locale.ca-CA.Date + +description: Date messages for Catalan. + +license: MIT-style license + +authors: + - Ãlfons Sanchez + +requires: + - Locale + +provides: [Locale.ca-CA.Date] + +... +*/ + +Locale.define('ca-CA', 'Date', { + + months: ['Gener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juli', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Desembre'], + months_abbr: ['gen.', 'febr.', 'març', 'abr.', 'maig', 'juny', 'jul.', 'ag.', 'set.', 'oct.', 'nov.', 'des.'], + days: ['Diumenge', 'Dilluns', 'Dimarts', 'Dimecres', 'Dijous', 'Divendres', 'Dissabte'], + days_abbr: ['dg', 'dl', 'dt', 'dc', 'dj', 'dv', 'ds'], + + // Culture's date order: DD/MM/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 0, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: 'fa menys d`un minut', + minuteAgo: 'fa un minut', + minutesAgo: 'fa {delta} minuts', + hourAgo: 'fa un hora', + hoursAgo: 'fa unes {delta} hores', + dayAgo: 'fa un dia', + daysAgo: 'fa {delta} dies', + + lessThanMinuteUntil: 'menys d`un minut des d`ara', + minuteUntil: 'un minut des d`ara', + minutesUntil: '{delta} minuts des d`ara', + hourUntil: 'un hora des d`ara', + hoursUntil: 'unes {delta} hores des d`ara', + dayUntil: '1 dia des d`ara', + daysUntil: '{delta} dies des d`ara' + +}); + +/* +--- + +name: Locale.ca-CA.Form.Validator + +description: Form Validator messages for Catalan. + +license: MIT-style license + +authors: + - Miquel Hudin + - Ãlfons Sanchez + +requires: + - Locale + +provides: [Locale.ca-CA.Form.Validator] + +... +*/ + +Locale.define('ca-CA', 'FormValidator', { + + required: 'Aquest camp es obligatori.', + minLength: 'Per favor introdueix al menys {minLength} caracters (has introduit {length} caracters).', + maxLength: 'Per favor introdueix no mes de {maxLength} caracters (has introduit {length} caracters).', + integer: 'Per favor introdueix un nombre enter en aquest camp. Nombres amb decimals (p.e. 1,25) no estan permesos.', + numeric: 'Per favor introdueix sols valors numerics en aquest camp (p.e. "1" o "1,1" o "-1" o "-1,1").', + digits: 'Per favor usa sols numeros i puntuacio en aquest camp (per exemple, un nombre de telefon amb guions i punts no esta permes).', + alpha: 'Per favor utilitza lletres nomes (a-z) en aquest camp. No s´admiteixen espais ni altres caracters.', + alphanum: 'Per favor, utilitza nomes lletres (a-z) o numeros (0-9) en aquest camp. No s´admiteixen espais ni altres caracters.', + dateSuchAs: 'Per favor introdueix una data valida com {date}', + dateInFormatMDY: 'Per favor introdueix una data valida com DD/MM/YYYY (p.e. "31/12/1999")', + email: 'Per favor, introdueix una adreça de correu electronic valida. Per exemple, "fred@domain.com".', + url: 'Per favor introdueix una URL valida com http://www.example.com.', + currencyDollar: 'Per favor introdueix una quantitat valida de €. Per exemple €100,00 .', + oneRequired: 'Per favor introdueix alguna cosa per al menys una d´aquestes entrades.', + errorPrefix: 'Error: ', + warningPrefix: 'Avis: ', + + // Form.Validator.Extras + noSpace: 'No poden haver espais en aquesta entrada.', + reqChkByNode: 'No hi han elements seleccionats.', + requiredChk: 'Aquest camp es obligatori.', + reqChkByName: 'Per favor selecciona una {label}.', + match: 'Aquest camp necessita coincidir amb el camp {matchName}', + startDate: 'la data de inici', + endDate: 'la data de fi', + currentDate: 'la data actual', + afterDate: 'La data deu ser igual o posterior a {label}.', + beforeDate: 'La data deu ser igual o anterior a {label}.', + startMonth: 'Per favor selecciona un mes d´orige', + sameMonth: 'Aquestes dos dates deuen estar dins del mateix mes - deus canviar una o altra.' + +}); + +/* +--- + +name: Locale.cs-CZ.Date + +description: Date messages for Czech. + +license: MIT-style license + +authors: + - Jan Černý chemiX + - Christopher Zukowski + +requires: + - Locale + +provides: [Locale.cs-CZ.Date] + +... +*/ +(function(){ + +// Czech language pluralization rules, see http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html +// one -> n is 1; 1 +// few -> n in 2..4; 2-4 +// other -> everything else 0, 5-999, 1.31, 2.31, 5.31... +var pluralize = function (n, one, few, other){ + if (n == 1) return one; + else if (n == 2 || n == 3 || n == 4) return few; + else return other; +}; + +Locale.define('cs-CZ', 'Date', { + + months: ['Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Červenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec'], + months_abbr: ['ledna', 'února', 'března', 'dubna', 'května', 'června', 'července', 'srpna', 'září', 'října', 'listopadu', 'prosince'], + days: ['Neděle', 'Pondělí', 'Úterý', 'Středa', 'Čtvrtek', 'Pátek', 'Sobota'], + days_abbr: ['ne', 'po', 'út', 'st', 'čt', 'pá', 'so'], + + // Culture's date order: DD.MM.YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d.%m.%Y', + shortTime: '%H:%M', + AM: 'dop.', + PM: 'odp.', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '.', + + lessThanMinuteAgo: 'před chvílí', + minuteAgo: 'přibližně před minutou', + minutesAgo: function(delta){ return 'před {delta} ' + pluralize(delta, 'minutou', 'minutami', 'minutami'); }, + hourAgo: 'přibližně před hodinou', + hoursAgo: function(delta){ return 'před {delta} ' + pluralize(delta, 'hodinou', 'hodinami', 'hodinami'); }, + dayAgo: 'před dnem', + daysAgo: function(delta){ return 'před {delta} ' + pluralize(delta, 'dnem', 'dny', 'dny'); }, + weekAgo: 'před týdnem', + weeksAgo: function(delta){ return 'před {delta} ' + pluralize(delta, 'týdnem', 'týdny', 'týdny'); }, + monthAgo: 'před měsícem', + monthsAgo: function(delta){ return 'před {delta} ' + pluralize(delta, 'měsícem', 'měsíci', 'měsíci'); }, + yearAgo: 'před rokem', + yearsAgo: function(delta){ return 'před {delta} ' + pluralize(delta, 'rokem', 'lety', 'lety'); }, + + lessThanMinuteUntil: 'za chvíli', + minuteUntil: 'přibližně za minutu', + minutesUntil: function(delta){ return 'za {delta} ' + pluralize(delta, 'minutu', 'minuty', 'minut'); }, + hourUntil: 'přibližně za hodinu', + hoursUntil: function(delta){ return 'za {delta} ' + pluralize(delta, 'hodinu', 'hodiny', 'hodin'); }, + dayUntil: 'za den', + daysUntil: function(delta){ return 'za {delta} ' + pluralize(delta, 'den', 'dny', 'dnů'); }, + weekUntil: 'za týden', + weeksUntil: function(delta){ return 'za {delta} ' + pluralize(delta, 'týden', 'týdny', 'týdnů'); }, + monthUntil: 'za měsíc', + monthsUntil: function(delta){ return 'za {delta} ' + pluralize(delta, 'měsíc', 'měsíce', 'měsíců'); }, + yearUntil: 'za rok', + yearsUntil: function(delta){ return 'za {delta} ' + pluralize(delta, 'rok', 'roky', 'let'); } +}); + +})(); + +/* +--- + +name: Locale.cs-CZ.Form.Validator + +description: Form Validator messages for Czech. + +license: MIT-style license + +authors: + - Jan Černý chemiX + +requires: + - Locale + +provides: [Locale.cs-CZ.Form.Validator] + +... +*/ + +Locale.define('cs-CZ', 'FormValidator', { + + required: 'Tato položka je povinná.', + minLength: 'Zadejte prosím alespoň {minLength} znaků (napsáno {length} znaků).', + maxLength: 'Zadejte prosím méně než {maxLength} znaků (nápsáno {length} znaků).', + integer: 'Zadejte prosím celé číslo. Desetinná čísla (např. 1.25) nejsou povolena.', + numeric: 'Zadejte jen číselné hodnoty (tj. "1" nebo "1.1" nebo "-1" nebo "-1.1").', + digits: 'Zadejte prosím pouze čísla a interpunkční znaménka(například telefonní číslo s pomlčkami nebo tečkami je povoleno).', + alpha: 'Zadejte prosím pouze písmena (a-z). Mezery nebo jiné znaky nejsou povoleny.', + alphanum: 'Zadejte prosím pouze písmena (a-z) nebo číslice (0-9). Mezery nebo jiné znaky nejsou povoleny.', + dateSuchAs: 'Zadejte prosím platné datum jako {date}', + dateInFormatMDY: 'Zadejte prosím platné datum jako MM / DD / RRRR (tj. "12/31/1999")', + email: 'Zadejte prosím platnou e-mailovou adresu. Například "fred@domain.com".', + url: 'Zadejte prosím platnou URL adresu jako http://www.example.com.', + currencyDollar: 'Zadejte prosím platnou částku. Například $100.00.', + oneRequired: 'Zadejte prosím alespoň jednu hodnotu pro tyto položky.', + errorPrefix: 'Chyba: ', + warningPrefix: 'Upozornění: ', + + // Form.Validator.Extras + noSpace: 'V této položce nejsou povoleny mezery', + reqChkByNode: 'Nejsou vybrány žádné položky.', + requiredChk: 'Tato položka je vyžadována.', + reqChkByName: 'Prosím vyberte {label}.', + match: 'Tato položka se musí shodovat s položkou {matchName}', + startDate: 'datum zahájení', + endDate: 'datum ukončení', + currentDate: 'aktuální datum', + afterDate: 'Datum by mělo být stejné nebo větší než {label}.', + beforeDate: 'Datum by mělo být stejné nebo menší než {label}.', + startMonth: 'Vyberte počáteční měsíc.', + sameMonth: 'Tyto dva datumy musí být ve stejném měsíci - změňte jeden z nich.', + creditcard: 'Zadané číslo kreditní karty je neplatné. Prosím opravte ho. Bylo zadáno {length} čísel.' + +}); + +/* +--- + +name: Locale.da-DK.Date + +description: Date messages for Danish. + +license: MIT-style license + +authors: + - Martin Overgaard + - Henrik Hansen + +requires: + - Locale + +provides: [Locale.da-DK.Date] + +... +*/ + +Locale.define('da-DK', 'Date', { + + months: ['Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'], + months_abbr: ['jan.', 'feb.', 'mar.', 'apr.', 'maj.', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'dec.'], + days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'], + days_abbr: ['søn', 'man', 'tir', 'ons', 'tor', 'fre', 'lør'], + + // Culture's date order: DD-MM-YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d-%m-%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '.', + + lessThanMinuteAgo: 'mindre end et minut siden', + minuteAgo: 'omkring et minut siden', + minutesAgo: '{delta} minutter siden', + hourAgo: 'omkring en time siden', + hoursAgo: 'omkring {delta} timer siden', + dayAgo: '1 dag siden', + daysAgo: '{delta} dage siden', + weekAgo: '1 uge siden', + weeksAgo: '{delta} uger siden', + monthAgo: '1 måned siden', + monthsAgo: '{delta} måneder siden', + yearAgo: '1 år siden', + yearsAgo: '{delta} år siden', + + lessThanMinuteUntil: 'mindre end et minut fra nu', + minuteUntil: 'omkring et minut fra nu', + minutesUntil: '{delta} minutter fra nu', + hourUntil: 'omkring en time fra nu', + hoursUntil: 'omkring {delta} timer fra nu', + dayUntil: '1 dag fra nu', + daysUntil: '{delta} dage fra nu', + weekUntil: '1 uge fra nu', + weeksUntil: '{delta} uger fra nu', + monthUntil: '1 måned fra nu', + monthsUntil: '{delta} måneder fra nu', + yearUntil: '1 år fra nu', + yearsUntil: '{delta} år fra nu' + +}); + +/* +--- + +name: Locale.da-DK.Form.Validator + +description: Form Validator messages for Danish. + +license: MIT-style license + +authors: + - Martin Overgaard + +requires: + - Locale + +provides: [Locale.da-DK.Form.Validator] + +... +*/ + +Locale.define('da-DK', 'FormValidator', { + + required: 'Feltet skal udfyldes.', + minLength: 'Skriv mindst {minLength} tegn (du skrev {length} tegn).', + maxLength: 'Skriv maksimalt {maxLength} tegn (du skrev {length} tegn).', + integer: 'Skriv et tal i dette felt. Decimal tal (f.eks. 1.25) er ikke tilladt.', + numeric: 'Skriv kun tal i dette felt (i.e. "1" eller "1.1" eller "-1" eller "-1.1").', + digits: 'Skriv kun tal og tegnsætning i dette felt (eksempel, et telefon nummer med bindestreg eller punktum er tilladt).', + alpha: 'Skriv kun bogstaver (a-z) i dette felt. Mellemrum og andre tegn er ikke tilladt.', + alphanum: 'Skriv kun bogstaver (a-z) eller tal (0-9) i dette felt. Mellemrum og andre tegn er ikke tilladt.', + dateSuchAs: 'Skriv en gyldig dato som {date}', + dateInFormatMDY: 'Skriv dato i formatet DD-MM-YYYY (f.eks. "31-12-1999")', + email: 'Skriv en gyldig e-mail adresse. F.eks "fred@domain.com".', + url: 'Skriv en gyldig URL adresse. F.eks "http://www.example.com".', + currencyDollar: 'Skriv et gldigt beløb. F.eks Kr.100.00 .', + oneRequired: 'Et eller flere af felterne i denne formular skal udfyldes.', + errorPrefix: 'Fejl: ', + warningPrefix: 'Advarsel: ', + + // Form.Validator.Extras + noSpace: 'Der må ikke benyttes mellemrum i dette felt.', + reqChkByNode: 'Foretag et valg.', + requiredChk: 'Dette felt skal udfyldes.', + reqChkByName: 'Vælg en {label}.', + match: 'Dette felt skal matche {matchName} feltet', + startDate: 'start dato', + endDate: 'slut dato', + currentDate: 'dags dato', + afterDate: 'Datoen skal være større end eller lig med {label}.', + beforeDate: 'Datoen skal være mindre end eller lig med {label}.', + startMonth: 'Vælg en start måned', + sameMonth: 'De valgte datoer skal være i samme måned - skift en af dem.' + +}); + +/* +--- + +name: Locale.de-DE.Date + +description: Date messages for German. + +license: MIT-style license + +authors: + - Christoph Pojer + - Frank Rossi + - Ulrich Petri + - Fabian Beiner + +requires: + - Locale + +provides: [Locale.de-DE.Date] + +... +*/ + +Locale.define('de-DE', 'Date', { + + months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], + months_abbr: ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'], + days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], + days_abbr: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'], + + // Culture's date order: DD.MM.YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d.%m.%Y', + shortTime: '%H:%M', + AM: 'vormittags', + PM: 'nachmittags', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '.', + + lessThanMinuteAgo: 'vor weniger als einer Minute', + minuteAgo: 'vor einer Minute', + minutesAgo: 'vor {delta} Minuten', + hourAgo: 'vor einer Stunde', + hoursAgo: 'vor {delta} Stunden', + dayAgo: 'vor einem Tag', + daysAgo: 'vor {delta} Tagen', + weekAgo: 'vor einer Woche', + weeksAgo: 'vor {delta} Wochen', + monthAgo: 'vor einem Monat', + monthsAgo: 'vor {delta} Monaten', + yearAgo: 'vor einem Jahr', + yearsAgo: 'vor {delta} Jahren', + + lessThanMinuteUntil: 'in weniger als einer Minute', + minuteUntil: 'in einer Minute', + minutesUntil: 'in {delta} Minuten', + hourUntil: 'in ca. einer Stunde', + hoursUntil: 'in ca. {delta} Stunden', + dayUntil: 'in einem Tag', + daysUntil: 'in {delta} Tagen', + weekUntil: 'in einer Woche', + weeksUntil: 'in {delta} Wochen', + monthUntil: 'in einem Monat', + monthsUntil: 'in {delta} Monaten', + yearUntil: 'in einem Jahr', + yearsUntil: 'in {delta} Jahren' + +}); + +/* +--- + +name: Locale.de-CH.Date + +description: Date messages for German (Switzerland). + +license: MIT-style license + +authors: + - Michael van der Weg + +requires: + - Locale + - Locale.de-DE.Date + +provides: [Locale.de-CH.Date] + +... +*/ + +Locale.define('de-CH').inherit('de-DE', 'Date'); + +/* +--- + +name: Locale.de-CH.Form.Validator + +description: Form Validator messages for German (Switzerland). + +license: MIT-style license + +authors: + - Michael van der Weg + +requires: + - Locale + +provides: [Locale.de-CH.Form.Validator] + +... +*/ + +Locale.define('de-CH', 'FormValidator', { + + required: 'Dieses Feld ist obligatorisch.', + minLength: 'Geben Sie bitte mindestens {minLength} Zeichen ein (Sie haben {length} Zeichen eingegeben).', + maxLength: 'Bitte geben Sie nicht mehr als {maxLength} Zeichen ein (Sie haben {length} Zeichen eingegeben).', + integer: 'Geben Sie bitte eine ganze Zahl ein. Dezimalzahlen (z.B. 1.25) sind nicht erlaubt.', + numeric: 'Geben Sie bitte nur Zahlenwerte in dieses Eingabefeld ein (z.B. "1", "1.1", "-1" oder "-1.1").', + digits: 'Benutzen Sie bitte nur Zahlen und Satzzeichen in diesem Eingabefeld (erlaubt ist z.B. eine Telefonnummer mit Bindestrichen und Punkten).', + alpha: 'Benutzen Sie bitte nur Buchstaben (a-z) in diesem Feld. Leerzeichen und andere Zeichen sind nicht erlaubt.', + alphanum: 'Benutzen Sie bitte nur Buchstaben (a-z) und Zahlen (0-9) in diesem Eingabefeld. Leerzeichen und andere Zeichen sind nicht erlaubt.', + dateSuchAs: 'Geben Sie bitte ein gültiges Datum ein. Wie zum Beispiel {date}', + dateInFormatMDY: 'Geben Sie bitte ein gültiges Datum ein. Wie zum Beispiel TT.MM.JJJJ (z.B. "31.12.1999")', + email: 'Geben Sie bitte eine gültige E-Mail Adresse ein. Wie zum Beispiel "maria@bernasconi.ch".', + url: 'Geben Sie bitte eine gültige URL ein. Wie zum Beispiel http://www.example.com.', + currencyDollar: 'Geben Sie bitte einen gültigen Betrag in Schweizer Franken ein. Wie zum Beispiel 100.00 CHF .', + oneRequired: 'Machen Sie für mindestens eines der Eingabefelder einen Eintrag.', + errorPrefix: 'Fehler: ', + warningPrefix: 'Warnung: ', + + // Form.Validator.Extras + noSpace: 'In diesem Eingabefeld darf kein Leerzeichen sein.', + reqChkByNode: 'Es wurden keine Elemente gewählt.', + requiredChk: 'Dieses Feld ist obligatorisch.', + reqChkByName: 'Bitte wählen Sie ein {label}.', + match: 'Dieses Eingabefeld muss mit dem Feld {matchName} übereinstimmen.', + startDate: 'Das Anfangsdatum', + endDate: 'Das Enddatum', + currentDate: 'Das aktuelle Datum', + afterDate: 'Das Datum sollte zur gleichen Zeit oder später sein {label}.', + beforeDate: 'Das Datum sollte zur gleichen Zeit oder früher sein {label}.', + startMonth: 'Wählen Sie bitte einen Anfangsmonat', + sameMonth: 'Diese zwei Datumsangaben müssen im selben Monat sein - Sie müssen eine von beiden verändern.', + creditcard: 'Die eingegebene Kreditkartennummer ist ungültig. Bitte überprüfen Sie diese und versuchen Sie es erneut. {length} Zahlen eingegeben.' + +}); + +/* +--- + +name: Locale.de-DE.Form.Validator + +description: Form Validator messages for German. + +license: MIT-style license + +authors: + - Frank Rossi + - Ulrich Petri + - Fabian Beiner + +requires: + - Locale + +provides: [Locale.de-DE.Form.Validator] + +... +*/ + +Locale.define('de-DE', 'FormValidator', { + + required: 'Dieses Eingabefeld muss ausgefüllt werden.', + minLength: 'Geben Sie bitte mindestens {minLength} Zeichen ein (Sie haben nur {length} Zeichen eingegeben).', + maxLength: 'Geben Sie bitte nicht mehr als {maxLength} Zeichen ein (Sie haben {length} Zeichen eingegeben).', + integer: 'Geben Sie in diesem Eingabefeld bitte eine ganze Zahl ein. Dezimalzahlen (z.B. "1.25") sind nicht erlaubt.', + numeric: 'Geben Sie in diesem Eingabefeld bitte nur Zahlenwerte (z.B. "1", "1.1", "-1" oder "-1.1") ein.', + digits: 'Geben Sie in diesem Eingabefeld bitte nur Zahlen und Satzzeichen ein (z.B. eine Telefonnummer mit Bindestrichen und Punkten ist erlaubt).', + alpha: 'Geben Sie in diesem Eingabefeld bitte nur Buchstaben (a-z) ein. Leerzeichen und andere Zeichen sind nicht erlaubt.', + alphanum: 'Geben Sie in diesem Eingabefeld bitte nur Buchstaben (a-z) und Zahlen (0-9) ein. Leerzeichen oder andere Zeichen sind nicht erlaubt.', + dateSuchAs: 'Geben Sie bitte ein gültiges Datum ein (z.B. "{date}").', + dateInFormatMDY: 'Geben Sie bitte ein gültiges Datum im Format TT.MM.JJJJ ein (z.B. "31.12.1999").', + email: 'Geben Sie bitte eine gültige E-Mail-Adresse ein (z.B. "max@mustermann.de").', + url: 'Geben Sie bitte eine gültige URL ein (z.B. "http://www.example.com").', + currencyDollar: 'Geben Sie bitte einen gültigen Betrag in EURO ein (z.B. 100.00€).', + oneRequired: 'Bitte füllen Sie mindestens ein Eingabefeld aus.', + errorPrefix: 'Fehler: ', + warningPrefix: 'Warnung: ', + + // Form.Validator.Extras + noSpace: 'Es darf kein Leerzeichen in diesem Eingabefeld sein.', + reqChkByNode: 'Es wurden keine Elemente gewählt.', + requiredChk: 'Dieses Feld muss ausgefüllt werden.', + reqChkByName: 'Bitte wählen Sie ein {label}.', + match: 'Dieses Eingabefeld muss mit dem {matchName} Eingabefeld übereinstimmen.', + startDate: 'Das Anfangsdatum', + endDate: 'Das Enddatum', + currentDate: 'Das aktuelle Datum', + afterDate: 'Das Datum sollte zur gleichen Zeit oder später sein als {label}.', + beforeDate: 'Das Datum sollte zur gleichen Zeit oder früher sein als {label}.', + startMonth: 'Wählen Sie bitte einen Anfangsmonat', + sameMonth: 'Diese zwei Datumsangaben müssen im selben Monat sein - Sie müssen eines von beiden verändern.', + creditcard: 'Die eingegebene Kreditkartennummer ist ungültig. Bitte überprüfen Sie diese und versuchen Sie es erneut. {length} Zahlen eingegeben.' + +}); + +/* +--- + +name: Locale.de-DE.Number + +description: Number messages for German. + +license: MIT-style license + +authors: + - Christoph Pojer + +requires: + - Locale + - Locale.EU.Number + +provides: [Locale.de-DE.Number] + +... +*/ + +Locale.define('de-DE').inherit('EU', 'Number'); + +/* +--- + +name: Locale.el-GR.Date + +description: Date messages for Greek language. + +license: MIT-style license + +authors: + - Periklis Argiriadis + +requires: + - Locale + +provides: [Locale.el-GR.Date] + +... +*/ + +Locale.define('el-GR', 'Date', { + + months: ['Ιανουάριος', 'Φεβρουάριος', 'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος', 'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος', 'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος'], + months_abbr: ['Ιαν', 'Φεβ', 'Μαρ', 'Απρ', 'Μάι', 'Ιουν', 'Ιουλ', 'Αυγ', 'Σεπ', 'Οκτ', 'Νοε', 'Δεκ'], + days: ['Κυριακή', 'Δευτέρα', 'Τρίτη', 'Τετάρτη', 'Πέμπτη', 'Παρασκευή', 'Σάββατο'], + days_abbr: ['Κυρ', 'Δευ', 'Τρι', 'Τετ', 'Πεμ', 'Παρ', 'Σαβ'], + + // Culture's date order: DD/MM/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%I:%M%p', + AM: 'πμ', + PM: 'μμ', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: function(dayOfMonth){ + // 1st, 2nd, 3rd, etc. + return (dayOfMonth > 3 && dayOfMonth < 21) ? 'ος' : ['ος'][Math.min(dayOfMonth % 10, 4)]; + }, + + lessThanMinuteAgo: 'λιγότερο από ένα λεπτό πριν', + minuteAgo: 'περίπου ένα λεπτό πριν', + minutesAgo: '{delta} λεπτά πριν', + hourAgo: 'περίπου μια ώρα πριν', + hoursAgo: 'περίπου {delta} ώρες πριν', + dayAgo: '1 ημέρα πριν', + daysAgo: '{delta} ημέρες πριν', + weekAgo: '1 εβδομάδα πριν', + weeksAgo: '{delta} εβδομάδες πριν', + monthAgo: '1 μήνα πριν', + monthsAgo: '{delta} μήνες πριν', + yearAgo: '1 χρόνο πριν', + yearsAgo: '{delta} χρόνια πριν', + + lessThanMinuteUntil: 'λιγότερο από λεπτό από τώρα', + minuteUntil: 'περίπου ένα λεπτό από τώρα', + minutesUntil: '{delta} λεπτά από τώρα', + hourUntil: 'περίπου μια ώρα από τώρα', + hoursUntil: 'περίπου {delta} ώρες από τώρα', + dayUntil: '1 ημέρα από τώρα', + daysUntil: '{delta} ημέρες από τώρα', + weekUntil: '1 εβδομάδα από τώρα', + weeksUntil: '{delta} εβδομάδες από τώρα', + monthUntil: '1 μήνας από τώρα', + monthsUntil: '{delta} μήνες από τώρα', + yearUntil: '1 χρόνος από τώρα', + yearsUntil: '{delta} χρόνια από τώρα' + +}); + +/* +--- + +name: Locale.el-GR.Form.Validator + +description: Form Validator messages for Greek language. + +license: MIT-style license + +authors: + - Dimitris Tsironis + +requires: + - Locale + +provides: [Locale.el-GR.Form.Validator] + +... +*/ + +Locale.define('el-GR', 'FormValidator', { + + required: 'Αυτό το πεδίο είναι απαραίτητο.', + length: 'Παρακαλούμε, εισάγετε {length} χαρακτήρες (έχετε ήδη εισάγει {elLength} χαρακτήρες).', + minLength: 'Παρακαλούμε, εισάγετε τουλάχιστον {minLength} χαρακτήρες (έχετε ήδη εισάγε {length} χαρακτήρες).', + maxlength: 'Παρακαλούμε, εισάγετε εώς {maxlength} χαρακτήρες (έχετε ήδη εισάγε {length} χαρακτήρες).', + integer: 'Παρακαλούμε, εισάγετε έναν ακέραιο αριθμό σε αυτό το πεδίο. Οι αριθμοί με δεκαδικά ψηφία (π.χ. 1.25) δεν επιτρέπονται.', + numeric: 'Παρακαλούμε, εισάγετε μόνο αριθμητικές τιμές σε αυτό το πεδίο (π.χ." 1 " ή " 1.1 " ή " -1 " ή " -1.1 " ).', + digits: 'Παρακαλούμε, χρησιμοποιήστε μόνο αριθμούς και σημεία στίξης σε αυτόν τον τομέα (π.χ. επιτρέπεται αριθμός τηλεφώνου με παύλες ή τελείες).', + alpha: 'Παρακαλούμε, χρησιμοποιήστε μόνο γράμματα (a-z) σε αυτό το πεδίο. Δεν επιτρέπονται κενά ή άλλοι χαρακτήρες.', + alphanum: 'Παρακαλούμε, χρησιμοποιήστε μόνο γράμματα (a-z) ή αριθμούς (0-9) σε αυτόν τον τομέα. Δεν επιτρέπονται κενά ή άλλοι χαρακτήρες.', + dateSuchAs: 'Παρακαλούμε, εισάγετε μια έγκυρη ημερομηνία, όπως {date}', + dateInFormatMDY: 'Παρακαλώ εισάγετε μια έγκυρη ημερομηνία, όπως ΜΜ/ΗΗ/ΕΕΕΕ (π.χ. "12/31/1999").', + email: 'Παρακαλούμε, εισάγετε μια έγκυρη διεύθυνση ηλεκτρονικού ταχυδρομείου (π.χ. "fred@domain.com").', + url: 'Παρακαλούμε, εισάγετε μια έγκυρη URL διεύθυνση, όπως http://www.example.com', + currencyDollar: 'Παρακαλούμε, εισάγετε ένα έγκυρο ποσό σε δολλάρια (π.χ. $100.00).', + oneRequired: 'Παρακαλούμε, εισάγετε κάτι για τουλάχιστον ένα από αυτά τα πεδία.', + errorPrefix: 'Σφάλμα: ', + warningPrefix: 'Προσοχή: ', + + // Form.Validator.Extras + noSpace: 'Δεν επιτρέπονται τα κενά σε αυτό το πεδίο.', + reqChkByNode: 'Δεν έχει επιλεγεί κάποιο αντικείμενο', + requiredChk: 'Αυτό το πεδίο είναι απαραίτητο.', + reqChkByName: 'Παρακαλούμε, επιλέξτε μια ετικέτα {label}.', + match: 'Αυτό το πεδίο πρέπει να ταιριάζει με το πεδίο {matchName}.', + startDate: 'η ημερομηνία έναρξης', + endDate: 'η ημερομηνία λήξης', + currentDate: 'η τρέχουσα ημερομηνία', + afterDate: 'Η ημερομηνία πρέπει να είναι η ίδια ή μετά από την {label}.', + beforeDate: 'Η ημερομηνία πρέπει να είναι η ίδια ή πριν από την {label}.', + startMonth: 'Παρακαλώ επιλέξτε ένα μήνα αρχής.', + sameMonth: 'Αυτές οι δύο ημερομηνίες πρέπει να έχουν τον ίδιο μήνα - θα πρέπει να αλλάξετε ή το ένα ή το άλλο', + creditcard: 'Ο αριθμός της πιστωτικής κάρτας δεν είναι έγκυρος. Παρακαλούμε ελέγξτε τον αριθμό και δοκιμάστε ξανά. {length} μήκος ψηφίων.' + +}); + +/* +--- + +name: Locale.en-GB.Date + +description: Date messages for British English. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Locale + - Locale.en-US.Date + +provides: [Locale.en-GB.Date] + +... +*/ + +Locale.define('en-GB', 'Date', { + + // Culture's date order: DD/MM/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%H:%M' + +}).inherit('en-US', 'Date'); + +/* +--- + +name: Locale.en-US.Number + +description: Number messages for US English. + +license: MIT-style license + +authors: + - Arian Stolwijk + +requires: + - Locale + +provides: [Locale.en-US.Number] + +... +*/ + +Locale.define('en-US', 'Number', { + + decimal: '.', + group: ',', + +/* Commented properties are the defaults for Number.format + decimals: 0, + precision: 0, + scientific: null, + + prefix: null, + suffic: null, + + // Negative/Currency/percentage will mixin Number + negative: { + prefix: '-' + },*/ + + currency: { +// decimals: 2, + prefix: '$ ' + }/*, + + percentage: { + decimals: 2, + suffix: '%' + }*/ + +}); + + + +/* +--- + +name: Locale.es-ES.Date + +description: Date messages for Spanish. + +license: MIT-style license + +authors: + - Ãlfons Sanchez + +requires: + - Locale + +provides: [Locale.es-ES.Date] + +... +*/ + +Locale.define('es-ES', 'Date', { + + months: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'], + months_abbr: ['ene', 'feb', 'mar', 'abr', 'may', 'jun', 'jul', 'ago', 'sep', 'oct', 'nov', 'dic'], + days: ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado'], + days_abbr: ['dom', 'lun', 'mar', 'mié', 'juv', 'vie', 'sáb'], + + // Culture's date order: DD/MM/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: 'hace menos de un minuto', + minuteAgo: 'hace un minuto', + minutesAgo: 'hace {delta} minutos', + hourAgo: 'hace una hora', + hoursAgo: 'hace unas {delta} horas', + dayAgo: 'hace un día', + daysAgo: 'hace {delta} días', + weekAgo: 'hace una semana', + weeksAgo: 'hace unas {delta} semanas', + monthAgo: 'hace un mes', + monthsAgo: 'hace {delta} meses', + yearAgo: 'hace un año', + yearsAgo: 'hace {delta} años', + + lessThanMinuteUntil: 'menos de un minuto desde ahora', + minuteUntil: 'un minuto desde ahora', + minutesUntil: '{delta} minutos desde ahora', + hourUntil: 'una hora desde ahora', + hoursUntil: 'unas {delta} horas desde ahora', + dayUntil: 'un día desde ahora', + daysUntil: '{delta} días desde ahora', + weekUntil: 'una semana desde ahora', + weeksUntil: 'unas {delta} semanas desde ahora', + monthUntil: 'un mes desde ahora', + monthsUntil: '{delta} meses desde ahora', + yearUntil: 'un año desde ahora', + yearsUntil: '{delta} años desde ahora' + +}); + +/* +--- + +name: Locale.es-AR.Date + +description: Date messages for Spanish (Argentina). + +license: MIT-style license + +authors: + - Ãlfons Sanchez + - Diego Massanti + +requires: + - Locale + - Locale.es-ES.Date + +provides: [Locale.es-AR.Date] + +... +*/ + +Locale.define('es-AR').inherit('es-ES', 'Date'); + +/* +--- + +name: Locale.es-AR.Form.Validator + +description: Form Validator messages for Spanish (Argentina). + +license: MIT-style license + +authors: + - Diego Massanti + +requires: + - Locale + +provides: [Locale.es-AR.Form.Validator] + +... +*/ + +Locale.define('es-AR', 'FormValidator', { + + required: 'Este campo es obligatorio.', + minLength: 'Por favor ingrese al menos {minLength} caracteres (ha ingresado {length} caracteres).', + maxLength: 'Por favor no ingrese más de {maxLength} caracteres (ha ingresado {length} caracteres).', + integer: 'Por favor ingrese un número entero en este campo. Números con decimales (p.e. 1,25) no se permiten.', + numeric: 'Por favor ingrese solo valores numéricos en este campo (p.e. "1" o "1,1" o "-1" o "-1,1").', + digits: 'Por favor use sólo números y puntuación en este campo (por ejemplo, un número de teléfono con guiones y/o puntos no está permitido).', + alpha: 'Por favor use sólo letras (a-z) en este campo. No se permiten espacios ni otros caracteres.', + alphanum: 'Por favor, usa sólo letras (a-z) o números (0-9) en este campo. No se permiten espacios u otros caracteres.', + dateSuchAs: 'Por favor ingrese una fecha válida como {date}', + dateInFormatMDY: 'Por favor ingrese una fecha válida, utulizando el formato DD/MM/YYYY (p.e. "31/12/1999")', + email: 'Por favor, ingrese una dirección de e-mail válida. Por ejemplo, "fred@dominio.com".', + url: 'Por favor ingrese una URL válida como http://www.example.com.', + currencyDollar: 'Por favor ingrese una cantidad válida de pesos. Por ejemplo $100,00 .', + oneRequired: 'Por favor ingrese algo para por lo menos una de estas entradas.', + errorPrefix: 'Error: ', + warningPrefix: 'Advertencia: ', + + // Form.Validator.Extras + noSpace: 'No se permiten espacios en este campo.', + reqChkByNode: 'No hay elementos seleccionados.', + requiredChk: 'Este campo es obligatorio.', + reqChkByName: 'Por favor selecciona una {label}.', + match: 'Este campo necesita coincidir con el campo {matchName}', + startDate: 'la fecha de inicio', + endDate: 'la fecha de fin', + currentDate: 'la fecha actual', + afterDate: 'La fecha debe ser igual o posterior a {label}.', + beforeDate: 'La fecha debe ser igual o anterior a {label}.', + startMonth: 'Por favor selecciona un mes de origen', + sameMonth: 'Estas dos fechas deben estar en el mismo mes - debes cambiar una u otra.' + +}); + +/* +--- + +name: Locale.es-ES.Form.Validator + +description: Form Validator messages for Spanish. + +license: MIT-style license + +authors: + - Ãlfons Sanchez + +requires: + - Locale + +provides: [Locale.es-ES.Form.Validator] + +... +*/ + +Locale.define('es-ES', 'FormValidator', { + + required: 'Este campo es obligatorio.', + minLength: 'Por favor introduce al menos {minLength} caracteres (has introducido {length} caracteres).', + maxLength: 'Por favor introduce no más de {maxLength} caracteres (has introducido {length} caracteres).', + integer: 'Por favor introduce un número entero en este campo. Números con decimales (p.e. 1,25) no se permiten.', + numeric: 'Por favor introduce solo valores numéricos en este campo (p.e. "1" o "1,1" o "-1" o "-1,1").', + digits: 'Por favor usa solo números y puntuación en este campo (por ejemplo, un número de teléfono con guiones y puntos no esta permitido).', + alpha: 'Por favor usa letras solo (a-z) en este campo. No se admiten espacios ni otros caracteres.', + alphanum: 'Por favor, usa solo letras (a-z) o números (0-9) en este campo. No se admiten espacios ni otros caracteres.', + dateSuchAs: 'Por favor introduce una fecha válida como {date}', + dateInFormatMDY: 'Por favor introduce una fecha válida como DD/MM/YYYY (p.e. "31/12/1999")', + email: 'Por favor, introduce una dirección de email válida. Por ejemplo, "fred@domain.com".', + url: 'Por favor introduce una URL válida como http://www.example.com.', + currencyDollar: 'Por favor introduce una cantidad válida de €. Por ejemplo €100,00 .', + oneRequired: 'Por favor introduce algo para por lo menos una de estas entradas.', + errorPrefix: 'Error: ', + warningPrefix: 'Aviso: ', + + // Form.Validator.Extras + noSpace: 'No pueden haber espacios en esta entrada.', + reqChkByNode: 'No hay elementos seleccionados.', + requiredChk: 'Este campo es obligatorio.', + reqChkByName: 'Por favor selecciona una {label}.', + match: 'Este campo necesita coincidir con el campo {matchName}', + startDate: 'la fecha de inicio', + endDate: 'la fecha de fin', + currentDate: 'la fecha actual', + afterDate: 'La fecha debe ser igual o posterior a {label}.', + beforeDate: 'La fecha debe ser igual o anterior a {label}.', + startMonth: 'Por favor selecciona un mes de origen', + sameMonth: 'Estas dos fechas deben estar en el mismo mes - debes cambiar una u otra.' + +}); + +/* +--- + +name: Locale.es-VE.Date + +description: Date messages for Spanish (Venezuela). + +license: MIT-style license + +authors: + - Daniel Barreto + +requires: + - Locale + - Locale.es-ES.Date + +provides: [Locale.es-VE.Date] + +... +*/ + +Locale.define('es-VE').inherit('es-ES', 'Date'); + +/* +--- + +name: Locale.es-VE.Form.Validator + +description: Form Validator messages for Spanish (Venezuela). + +license: MIT-style license + +authors: + - Daniel Barreto + +requires: + - Locale + - Locale.es-ES.Form.Validator + +provides: [Locale.es-VE.Form.Validator] + +... +*/ + +Locale.define('es-VE', 'FormValidator', { + + digits: 'Por favor usa solo números y puntuación en este campo. Por ejemplo, un número de teléfono con guiones y puntos no esta permitido.', + alpha: 'Por favor usa solo letras (a-z) en este campo. No se admiten espacios ni otros caracteres.', + currencyDollar: 'Por favor introduce una cantidad válida de Bs. Por ejemplo Bs. 100,00 .', + oneRequired: 'Por favor introduce un valor para por lo menos una de estas entradas.', + + // Form.Validator.Extras + startDate: 'La fecha de inicio', + endDate: 'La fecha de fin', + currentDate: 'La fecha actual' + +}).inherit('es-ES', 'FormValidator'); + +/* +--- + +name: Locale.es-VE.Number + +description: Number messages for Spanish (Venezuela). + +license: MIT-style license + +authors: + - Daniel Barreto + +requires: + - Locale + +provides: [Locale.es-VE.Number] + +... +*/ + +Locale.define('es-VE', 'Number', { + + decimal: ',', + group: '.', +/* + decimals: 0, + precision: 0, +*/ + // Negative/Currency/percentage will mixin Number + negative: { + prefix: '-' + }, + + currency: { + decimals: 2, + prefix: 'Bs. ' + }, + + percentage: { + decimals: 2, + suffix: '%' + } + +}); + +/* +--- + +name: Locale.et-EE.Date + +description: Date messages for Estonian. + +license: MIT-style license + +authors: + - Kevin Valdek + +requires: + - Locale + +provides: [Locale.et-EE.Date] + +... +*/ + +Locale.define('et-EE', 'Date', { + + months: ['jaanuar', 'veebruar', 'märts', 'aprill', 'mai', 'juuni', 'juuli', 'august', 'september', 'oktoober', 'november', 'detsember'], + months_abbr: ['jaan', 'veebr', 'märts', 'apr', 'mai', 'juuni', 'juuli', 'aug', 'sept', 'okt', 'nov', 'dets'], + days: ['pühapäev', 'esmaspäev', 'teisipäev', 'kolmapäev', 'neljapäev', 'reede', 'laupäev'], + days_abbr: ['pühap', 'esmasp', 'teisip', 'kolmap', 'neljap', 'reede', 'laup'], + + // Culture's date order: MM.DD.YYYY + dateOrder: ['month', 'date', 'year'], + shortDate: '%m.%d.%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: 'vähem kui minut aega tagasi', + minuteAgo: 'umbes minut aega tagasi', + minutesAgo: '{delta} minutit tagasi', + hourAgo: 'umbes tund aega tagasi', + hoursAgo: 'umbes {delta} tundi tagasi', + dayAgo: '1 päev tagasi', + daysAgo: '{delta} päeva tagasi', + weekAgo: '1 nädal tagasi', + weeksAgo: '{delta} nädalat tagasi', + monthAgo: '1 kuu tagasi', + monthsAgo: '{delta} kuud tagasi', + yearAgo: '1 aasta tagasi', + yearsAgo: '{delta} aastat tagasi', + + lessThanMinuteUntil: 'vähem kui minuti aja pärast', + minuteUntil: 'umbes minuti aja pärast', + minutesUntil: '{delta} minuti pärast', + hourUntil: 'umbes tunni aja pärast', + hoursUntil: 'umbes {delta} tunni pärast', + dayUntil: '1 päeva pärast', + daysUntil: '{delta} päeva pärast', + weekUntil: '1 nädala pärast', + weeksUntil: '{delta} nädala pärast', + monthUntil: '1 kuu pärast', + monthsUntil: '{delta} kuu pärast', + yearUntil: '1 aasta pärast', + yearsUntil: '{delta} aasta pärast' + +}); + +/* +--- + +name: Locale.et-EE.Form.Validator + +description: Form Validator messages for Estonian. + +license: MIT-style license + +authors: + - Kevin Valdek + +requires: + - Locale + +provides: [Locale.et-EE.Form.Validator] + +... +*/ + +Locale.define('et-EE', 'FormValidator', { + + required: 'Väli peab olema täidetud.', + minLength: 'Palun sisestage vähemalt {minLength} tähte (te sisestasite {length} tähte).', + maxLength: 'Palun ärge sisestage rohkem kui {maxLength} tähte (te sisestasite {length} tähte).', + integer: 'Palun sisestage väljale täisarv. Kümnendarvud (näiteks 1.25) ei ole lubatud.', + numeric: 'Palun sisestage ainult numbreid väljale (näiteks "1", "1.1", "-1" või "-1.1").', + digits: 'Palun kasutage ainult numbreid ja kirjavahemärke (telefoninumbri sisestamisel on lubatud kasutada kriipse ja punkte).', + alpha: 'Palun kasutage ainult tähti (a-z). Tühikud ja teised sümbolid on keelatud.', + alphanum: 'Palun kasutage ainult tähti (a-z) või numbreid (0-9). Tühikud ja teised sümbolid on keelatud.', + dateSuchAs: 'Palun sisestage kehtiv kuupäev kujul {date}', + dateInFormatMDY: 'Palun sisestage kehtiv kuupäev kujul MM.DD.YYYY (näiteks: "12.31.1999").', + email: 'Palun sisestage kehtiv e-maili aadress (näiteks: "fred@domain.com").', + url: 'Palun sisestage kehtiv URL (näiteks: http://www.example.com).', + currencyDollar: 'Palun sisestage kehtiv $ summa (näiteks: $100.00).', + oneRequired: 'Palun sisestage midagi vähemalt ühele antud väljadest.', + errorPrefix: 'Viga: ', + warningPrefix: 'Hoiatus: ', + + // Form.Validator.Extras + noSpace: 'Väli ei tohi sisaldada tühikuid.', + reqChkByNode: 'Ükski väljadest pole valitud.', + requiredChk: 'Välja täitmine on vajalik.', + reqChkByName: 'Palun valige üks {label}.', + match: 'Väli peab sobima {matchName} väljaga', + startDate: 'algkuupäev', + endDate: 'lõppkuupäev', + currentDate: 'praegune kuupäev', + afterDate: 'Kuupäev peab olema võrdne või pärast {label}.', + beforeDate: 'Kuupäev peab olema võrdne või enne {label}.', + startMonth: 'Palun valige algkuupäev.', + sameMonth: 'Antud kaks kuupäeva peavad olema samas kuus - peate muutma ühte kuupäeva.' + +}); + +/* +--- + +name: Locale.fa.Date + +description: Date messages for Persian. + +license: MIT-style license + +authors: + - Amir Hossein Hodjaty Pour + +requires: + - Locale + +provides: [Locale.fa.Date] + +... +*/ + +Locale.define('fa', 'Date', { + + months: ['ژانویه', 'فوریه', 'مارس', 'آپریل', 'مه', 'ژوئن', 'ژوئیه', 'آگوست', 'سپتامبر', 'اکتبر', 'نوامبر', 'دسامبر'], + months_abbr: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], + days: ['یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'], + days_abbr: ['ي', 'د', 'س', 'چ', 'پ', 'ج', 'ش'], + + // Culture's date order: MM/DD/YYYY + dateOrder: ['month', 'date', 'year'], + shortDate: '%m/%d/%Y', + shortTime: '%I:%M%p', + AM: 'ق.ظ', + PM: 'ب.ظ', + + // Date.Extras + ordinal: 'ام', + + lessThanMinuteAgo: 'کمتر از یک دقیقه پیش', + minuteAgo: 'حدود یک دقیقه پیش', + minutesAgo: '{delta} دقیقه پیش', + hourAgo: 'حدود یک ساعت پیش', + hoursAgo: 'حدود {delta} ساعت پیش', + dayAgo: '1 روز پیش', + daysAgo: '{delta} روز پیش', + weekAgo: '1 هفته پیش', + weeksAgo: '{delta} هفته پیش', + monthAgo: '1 ماه پیش', + monthsAgo: '{delta} ماه پیش', + yearAgo: '1 سال پیش', + yearsAgo: '{delta} سال پیش', + + lessThanMinuteUntil: 'کمتر از یک دقیقه از حالا', + minuteUntil: 'حدود یک دقیقه از حالا', + minutesUntil: '{delta} دقیقه از حالا', + hourUntil: 'حدود یک ساعت از حالا', + hoursUntil: 'حدود {delta} ساعت از حالا', + dayUntil: '1 روز از حالا', + daysUntil: '{delta} روز از حالا', + weekUntil: '1 هفته از حالا', + weeksUntil: '{delta} هفته از حالا', + monthUntil: '1 ماه از حالا', + monthsUntil: '{delta} ماه از حالا', + yearUntil: '1 سال از حالا', + yearsUntil: '{delta} سال از حالا' + +}); + +/* +--- + +name: Locale.fa.Form.Validator + +description: Form Validator messages for Persian. + +license: MIT-style license + +authors: + - Amir Hossein Hodjaty Pour + +requires: + - Locale + +provides: [Locale.fa.Form.Validator] + +... +*/ + +Locale.define('fa', 'FormValidator', { + + required: 'این فیلد الزامی است.', + minLength: 'شما باید حداقل {minLength} حرف وارد کنید ({length} حرف وارد کرده اید).', + maxLength: 'لطفا حداکثر {maxLength} حرف وارد کنید (شما {length} حرف وارد کرده اید).', + integer: 'لطفا از عدد صحیح استفاده کنید. اعداد اعشاری (مانند 1.25) مجاز نیستند.', + numeric: 'لطفا فقط داده عددی وارد کنید (مانند "1" یا "1.1" یا "1-" یا "1.1-").', + digits: 'لطفا فقط از اعداد و علامتها در این فیلد استفاده کنید (برای مثال شماره تلفن با خط تیره و نقطه قابل قبول است).', + alpha: 'لطفا فقط از حروف الفباء برای این بخش استفاده کنید. کاراکترهای دیگر و فاصله مجاز نیستند.', + alphanum: 'لطفا فقط از حروف الفباء و اعداد در این بخش استفاده کنید. کاراکترهای دیگر و فاصله مجاز نیستند.', + dateSuchAs: 'لطفا یک تاریخ معتبر مانند {date} وارد کنید.', + dateInFormatMDY: 'لطفا یک تاریخ معتبر به شکل MM/DD/YYYY وارد کنید (مانند "12/31/1999").', + email: 'لطفا یک آدرس ایمیل معتبر وارد کنید. برای مثال "fred@domain.com".', + url: 'لطفا یک URL معتبر مانند http://www.example.com وارد کنید.', + currencyDollar: 'لطفا یک محدوده معتبر برای این بخش وارد کنید مانند 100.00$ .', + oneRequired: 'لطفا حداقل یکی از فیلدها را پر کنید.', + errorPrefix: 'خطا: ', + warningPrefix: 'هشدار: ', + + // Form.Validator.Extras + noSpace: 'استفاده از فاصله در این بخش مجاز نیست.', + reqChkByNode: 'موردی انتخاب نشده است.', + requiredChk: 'این فیلد الزامی است.', + reqChkByName: 'لطفا یک {label} را انتخاب کنید.', + match: 'این فیلد باید با فیلد {matchName} مطابقت داشته باشد.', + startDate: 'تاریخ شروع', + endDate: 'تاریخ پایان', + currentDate: 'تاریخ کنونی', + afterDate: 'تاریخ میبایست برابر یا بعد از {label} باشد', + beforeDate: 'تاریخ میبایست برابر یا قبل از {label} باشد', + startMonth: 'لطفا ماه شروع را انتخاب کنید', + sameMonth: 'این دو تاریخ باید در یک ماه باشند - شما باید یکی یا هر دو را تغییر دهید.', + creditcard: 'شماره کارت اعتباری که وارد کرده اید معتبر نیست. لطفا شماره را بررسی کنید و مجددا تلاش کنید. {length} رقم وارد شده است.' + +}); + +/* +--- + +name: Locale.fi-FI.Date + +description: Date messages for Finnish. + +license: MIT-style license + +authors: + - ksel + +requires: + - Locale + +provides: [Locale.fi-FI.Date] + +... +*/ + +Locale.define('fi-FI', 'Date', { + + // NOTE: months and days are not capitalized in finnish + months: ['tammikuu', 'helmikuu', 'maaliskuu', 'huhtikuu', 'toukokuu', 'kesäkuu', 'heinäkuu', 'elokuu', 'syyskuu', 'lokakuu', 'marraskuu', 'joulukuu'], + + // these abbreviations are really not much used in finnish because they obviously won't abbreviate very much. ;) + // NOTE: sometimes one can see forms such as "tammi", "helmi", etc. but that is not proper finnish. + months_abbr: ['tammik.', 'helmik.', 'maalisk.', 'huhtik.', 'toukok.', 'kesäk.', 'heinäk.', 'elok.', 'syysk.', 'lokak.', 'marrask.', 'jouluk.'], + + days: ['sunnuntai', 'maanantai', 'tiistai', 'keskiviikko', 'torstai', 'perjantai', 'lauantai'], + days_abbr: ['su', 'ma', 'ti', 'ke', 'to', 'pe', 'la'], + + // Culture's date order: DD/MM/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d.%m.%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '.', + + lessThanMinuteAgo: 'vajaa minuutti sitten', + minuteAgo: 'noin minuutti sitten', + minutesAgo: '{delta} minuuttia sitten', + hourAgo: 'noin tunti sitten', + hoursAgo: 'noin {delta} tuntia sitten', + dayAgo: 'päivä sitten', + daysAgo: '{delta} päivää sitten', + weekAgo: 'viikko sitten', + weeksAgo: '{delta} viikkoa sitten', + monthAgo: 'kuukausi sitten', + monthsAgo: '{delta} kuukautta sitten', + yearAgo: 'vuosi sitten', + yearsAgo: '{delta} vuotta sitten', + + lessThanMinuteUntil: 'vajaan minuutin kuluttua', + minuteUntil: 'noin minuutin kuluttua', + minutesUntil: '{delta} minuutin kuluttua', + hourUntil: 'noin tunnin kuluttua', + hoursUntil: 'noin {delta} tunnin kuluttua', + dayUntil: 'päivän kuluttua', + daysUntil: '{delta} päivän kuluttua', + weekUntil: 'viikon kuluttua', + weeksUntil: '{delta} viikon kuluttua', + monthUntil: 'kuukauden kuluttua', + monthsUntil: '{delta} kuukauden kuluttua', + yearUntil: 'vuoden kuluttua', + yearsUntil: '{delta} vuoden kuluttua' + +}); + +/* +--- + +name: Locale.fi-FI.Form.Validator + +description: Form Validator messages for Finnish. + +license: MIT-style license + +authors: + - ksel + +requires: + - Locale + +provides: [Locale.fi-FI.Form.Validator] + +... +*/ + +Locale.define('fi-FI', 'FormValidator', { + + required: 'Tämä kenttä on pakollinen.', + minLength: 'Ole hyvä ja anna vähintään {minLength} merkkiä (annoit {length} merkkiä).', + maxLength: 'Älä anna enempää kuin {maxLength} merkkiä (annoit {length} merkkiä).', + integer: 'Ole hyvä ja anna kokonaisluku. Luvut, joissa on desimaaleja (esim. 1.25) eivät ole sallittuja.', + numeric: 'Anna tähän kenttään lukuarvo (kuten "1" tai "1.1" tai "-1" tai "-1.1").', + digits: 'Käytä pelkästään numeroita ja välimerkkejä tässä kentässä (syötteet, kuten esim. puhelinnumero, jossa on väliviivoja, pilkkuja tai pisteitä, kelpaa).', + alpha: 'Anna tähän kenttään vain kirjaimia (a-z). Välilyönnit tai muut merkit eivät ole sallittuja.', + alphanum: 'Anna tähän kenttään vain kirjaimia (a-z) tai numeroita (0-9). Välilyönnit tai muut merkit eivät ole sallittuja.', + dateSuchAs: 'Ole hyvä ja anna kelvollinen päivmäärä, kuten esimerkiksi {date}', + dateInFormatMDY: 'Ole hyvä ja anna kelvollinen päivämäärä muodossa pp/kk/vvvv (kuten "12/31/1999")', + email: 'Ole hyvä ja anna kelvollinen sähköpostiosoite (kuten esimerkiksi "matti@meikalainen.com").', + url: 'Ole hyvä ja anna kelvollinen URL, kuten esimerkiksi http://www.example.com.', + currencyDollar: 'Ole hyvä ja anna kelvollinen eurosumma (kuten esimerkiksi 100,00 EUR) .', + oneRequired: 'Ole hyvä ja syötä jotakin ainakin johonkin näistä kentistä.', + errorPrefix: 'Virhe: ', + warningPrefix: 'Varoitus: ', + + // Form.Validator.Extras + noSpace: 'Tässä syötteessä ei voi olla välilyöntejä', + reqChkByNode: 'Ei valintoja.', + requiredChk: 'Tämä kenttä on pakollinen.', + reqChkByName: 'Ole hyvä ja valitse {label}.', + match: 'Tämän kentän tulee vastata kenttää {matchName}', + startDate: 'alkupäivämäärä', + endDate: 'loppupäivämäärä', + currentDate: 'nykyinen päivämäärä', + afterDate: 'Päivämäärän tulisi olla sama tai myöhäisempi ajankohta kuin {label}.', + beforeDate: 'Päivämäärän tulisi olla sama tai aikaisempi ajankohta kuin {label}.', + startMonth: 'Ole hyvä ja valitse aloituskuukausi', + sameMonth: 'Näiden kahden päivämäärän tulee olla saman kuun sisällä -- sinun pitää muuttaa jompaa kumpaa.', + creditcard: 'Annettu luottokortin numero ei kelpaa. Ole hyvä ja tarkista numero sekä yritä uudelleen. {length} numeroa syötetty.' + +}); + +/* +--- + +name: Locale.fi-FI.Number + +description: Finnish number messages + +license: MIT-style license + +authors: + - ksel + +requires: + - Locale + - Locale.EU.Number + +provides: [Locale.fi-FI.Number] + +... +*/ + +Locale.define('fi-FI', 'Number', { + + group: ' ' // grouped by space + +}).inherit('EU', 'Number'); + +/* +--- + +name: Locale.fr-FR.Date + +description: Date messages for French. + +license: MIT-style license + +authors: + - Nicolas Sorosac + - Antoine Abt + +requires: + - Locale + +provides: [Locale.fr-FR.Date] + +... +*/ + +Locale.define('fr-FR', 'Date', { + + months: ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'], + months_abbr: ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juil.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'], + days: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'], + days_abbr: ['dim.', 'lun.', 'mar.', 'mer.', 'jeu.', 'ven.', 'sam.'], + + // Culture's date order: DD/MM/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: function(dayOfMonth){ + return (dayOfMonth > 1) ? '' : 'er'; + }, + + lessThanMinuteAgo: "il y a moins d'une minute", + minuteAgo: 'il y a une minute', + minutesAgo: 'il y a {delta} minutes', + hourAgo: 'il y a une heure', + hoursAgo: 'il y a {delta} heures', + dayAgo: 'il y a un jour', + daysAgo: 'il y a {delta} jours', + weekAgo: 'il y a une semaine', + weeksAgo: 'il y a {delta} semaines', + monthAgo: 'il y a 1 mois', + monthsAgo: 'il y a {delta} mois', + yearthAgo: 'il y a 1 an', + yearsAgo: 'il y a {delta} ans', + + lessThanMinuteUntil: "dans moins d'une minute", + minuteUntil: 'dans une minute', + minutesUntil: 'dans {delta} minutes', + hourUntil: 'dans une heure', + hoursUntil: 'dans {delta} heures', + dayUntil: 'dans un jour', + daysUntil: 'dans {delta} jours', + weekUntil: 'dans 1 semaine', + weeksUntil: 'dans {delta} semaines', + monthUntil: 'dans 1 mois', + monthsUntil: 'dans {delta} mois', + yearUntil: 'dans 1 an', + yearsUntil: 'dans {delta} ans' + +}); + +/* +--- + +name: Locale.fr-FR.Form.Validator + +description: Form Validator messages for French. + +license: MIT-style license + +authors: + - Miquel Hudin + - Nicolas Sorosac + +requires: + - Locale + +provides: [Locale.fr-FR.Form.Validator] + +... +*/ + +Locale.define('fr-FR', 'FormValidator', { + + required: 'Ce champ est obligatoire.', + length: 'Veuillez saisir {length} caractère(s) (vous avez saisi {elLength} caractère(s)', + minLength: 'Veuillez saisir un minimum de {minLength} caractère(s) (vous avez saisi {length} caractère(s)).', + maxLength: 'Veuillez saisir un maximum de {maxLength} caractère(s) (vous avez saisi {length} caractère(s)).', + integer: 'Veuillez saisir un nombre entier dans ce champ. Les nombres décimaux (ex : "1,25") ne sont pas autorisés.', + numeric: 'Veuillez saisir uniquement des chiffres dans ce champ (ex : "1" ou "1,1" ou "-1" ou "-1,1").', + digits: "Veuillez saisir uniquement des chiffres et des signes de ponctuation dans ce champ (ex : un numéro de téléphone avec des traits d'union est autorisé).", + alpha: 'Veuillez saisir uniquement des lettres (a-z) dans ce champ. Les espaces ou autres caractères ne sont pas autorisés.', + alphanum: 'Veuillez saisir uniquement des lettres (a-z) ou des chiffres (0-9) dans ce champ. Les espaces ou autres caractères ne sont pas autorisés.', + dateSuchAs: 'Veuillez saisir une date correcte comme {date}', + dateInFormatMDY: 'Veuillez saisir une date correcte, au format JJ/MM/AAAA (ex : "31/11/1999").', + email: 'Veuillez saisir une adresse de courrier électronique. Par exemple "fred@domaine.com".', + url: 'Veuillez saisir une URL, comme http://www.exemple.com.', + currencyDollar: 'Veuillez saisir une quantité correcte. Par exemple 100,00€.', + oneRequired: 'Veuillez sélectionner au moins une de ces options.', + errorPrefix: 'Erreur : ', + warningPrefix: 'Attention : ', + + // Form.Validator.Extras + noSpace: "Ce champ n'accepte pas les espaces.", + reqChkByNode: "Aucun élément n'est sélectionné.", + requiredChk: 'Ce champ est obligatoire.', + reqChkByName: 'Veuillez sélectionner un(e) {label}.', + match: 'Ce champ doit correspondre avec le champ {matchName}.', + startDate: 'date de début', + endDate: 'date de fin', + currentDate: 'date actuelle', + afterDate: 'La date doit être identique ou postérieure à {label}.', + beforeDate: 'La date doit être identique ou antérieure à {label}.', + startMonth: 'Veuillez sélectionner un mois de début.', + sameMonth: 'Ces deux dates doivent être dans le même mois - vous devez en modifier une.', + creditcard: 'Le numéro de carte de crédit est invalide. Merci de vérifier le numéro et de réessayer. Vous avez entré {length} chiffre(s).' + +}); + +/* +--- + +name: Locale.fr-FR.Number + +description: Number messages for French. + +license: MIT-style license + +authors: + - Arian Stolwijk + - sv1l + +requires: + - Locale + - Locale.EU.Number + +provides: [Locale.fr-FR.Number] + +... +*/ + +Locale.define('fr-FR', 'Number', { + + group: ' ' // In fr-FR localization, group character is a blank space + +}).inherit('EU', 'Number'); + +/* +--- + +name: Locale.he-IL.Date + +description: Date messages for Hebrew. + +license: MIT-style license + +authors: + - Elad Ossadon + +requires: + - Locale + +provides: [Locale.he-IL.Date] + +... +*/ + +Locale.define('he-IL', 'Date', { + + months: ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'], + months_abbr: ['ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'], + days: ['ראשון', 'שני', 'שלישי', 'רביעי', 'חמישי', 'שישי', 'שבת'], + days_abbr: ['ראשון', 'שני', 'שלישי', 'רביעי', 'חמישי', 'שישי', 'שבת'], + + // Culture's date order: MM/DD/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 0, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: 'לפני פחות מדקה', + minuteAgo: 'לפני כדקה', + minutesAgo: 'לפני {delta} דקות', + hourAgo: 'לפני כשעה', + hoursAgo: 'לפני {delta} שעות', + dayAgo: 'לפני יום', + daysAgo: 'לפני {delta} ימים', + weekAgo: 'לפני שבוע', + weeksAgo: 'לפני {delta} שבועות', + monthAgo: 'לפני חודש', + monthsAgo: 'לפני {delta} חודשים', + yearAgo: 'לפני שנה', + yearsAgo: 'לפני {delta} שנים', + + lessThanMinuteUntil: 'בעוד פחות מדקה', + minuteUntil: 'בעוד כדקה', + minutesUntil: 'בעוד {delta} דקות', + hourUntil: 'בעוד כשעה', + hoursUntil: 'בעוד {delta} שעות', + dayUntil: 'בעוד יום', + daysUntil: 'בעוד {delta} ימים', + weekUntil: 'בעוד שבוע', + weeksUntil: 'בעוד {delta} שבועות', + monthUntil: 'בעוד חודש', + monthsUntil: 'בעוד {delta} חודשים', + yearUntil: 'בעוד שנה', + yearsUntil: 'בעוד {delta} שנים' + +}); + +/* +--- + +name: Locale.he-IL.Form.Validator + +description: Form Validator messages for Hebrew. + +license: MIT-style license + +authors: + - Elad Ossadon + +requires: + - Locale + +provides: [Locale.he-IL.Form.Validator] + +... +*/ + +Locale.define('he-IL', 'FormValidator', { + + required: 'נא למלא שדה זה.', + minLength: 'נא להזין לפחות {minLength} תווים (הזנת {length} תווים).', + maxLength: 'נא להזין עד {maxLength} תווים (הזנת {length} תווים).', + integer: 'נא להזין מספר שלם לשדה זה. מספרים עשרוניים (כמו 1.25) אינם חוקיים.', + numeric: 'נא להזין ערך מספרי בלבד בשדה זה (כמו "1", "1.1", "-1" או "-1.1").', + digits: 'נא להזין רק ספרות וסימני הפרדה בשדה זה (למשל, מספר טלפון עם מקפים או נקודות הוא חוקי).', + alpha: 'נא להזין רק אותיות באנגלית (a-z) בשדה זה. רווחים או תווים אחרים אינם חוקיים.', + alphanum: 'נא להזין רק אותריות באנגלית (a-z) או ספרות (0-9) בשדה זה. אווחרים או תווים אחרים אינם חוקיים.', + dateSuchAs: 'נא להזין תאריך חוקי, כמו {date}', + dateInFormatMDY: 'נא להזין תאריך חוקי בפורמט MM/DD/YYYY (כמו "12/31/1999")', + email: 'נא להזין כתובת אימייל חוקית. לדוגמה: "fred@domain.com".', + url: 'נא להזין כתובת אתר חוקית, כמו http://www.example.com.', + currencyDollar: 'נא להזין סכום דולרי חוקי. לדוגמה $100.00.', + oneRequired: 'נא לבחור לפחות בשדה אחד.', + errorPrefix: 'שגיאה: ', + warningPrefix: 'אזהרה: ', + + // Form.Validator.Extras + noSpace: 'אין להזין רווחים בשדה זה.', + reqChkByNode: 'נא לבחור אחת מהאפשרויות.', + requiredChk: 'שדה זה נדרש.', + reqChkByName: 'נא לבחור {label}.', + match: 'שדה זה צריך להתאים לשדה {matchName}', + startDate: 'תאריך ההתחלה', + endDate: 'תאריך הסיום', + currentDate: 'התאריך הנוכחי', + afterDate: 'התאריך צריך להיות זהה או אחרי {label}.', + beforeDate: 'התאריך צריך להיות זהה או לפני {label}.', + startMonth: 'נא לבחור חודש התחלה', + sameMonth: 'שני תאריכים אלה צריכים להיות באותו חודש - נא לשנות אחד התאריכים.', + creditcard: 'מספר כרטיס האשראי שהוזן אינו חוקי. נא לבדוק שנית. הוזנו {length} ספרות.' + +}); + +/* +--- + +name: Locale.he-IL.Number + +description: Number messages for Hebrew. + +license: MIT-style license + +authors: + - Elad Ossadon + +requires: + - Locale + +provides: [Locale.he-IL.Number] + +... +*/ + +Locale.define('he-IL', 'Number', { + + decimal: '.', + group: ',', + + currency: { + suffix: ' ₪' + } + +}); + +/* +--- + +name: Locale.hu-HU.Date + +description: Date messages for Hungarian. + +license: MIT-style license + +authors: + - Zsolt Szegheő + +requires: + - Locale + +provides: [Locale.hu-HU.Date] + +... +*/ + +Locale.define('hu-HU', 'Date', { + + months: ['Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December'], + months_abbr: ['jan.', 'febr.', 'márc.', 'ápr.', 'máj.', 'jún.', 'júl.', 'aug.', 'szept.', 'okt.', 'nov.', 'dec.'], + days: ['Vasárnap', 'Hétfő', 'Kedd', 'Szerda', 'Csütörtök', 'Péntek', 'Szombat'], + days_abbr: ['V', 'H', 'K', 'Sze', 'Cs', 'P', 'Szo'], + + // Culture's date order: YYYY.MM.DD. + dateOrder: ['year', 'month', 'date'], + shortDate: '%Y.%m.%d.', + shortTime: '%I:%M', + AM: 'de.', + PM: 'du.', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '.', + + lessThanMinuteAgo: 'alig egy perce', + minuteAgo: 'egy perce', + minutesAgo: '{delta} perce', + hourAgo: 'egy órája', + hoursAgo: '{delta} órája', + dayAgo: '1 napja', + daysAgo: '{delta} napja', + weekAgo: '1 hete', + weeksAgo: '{delta} hete', + monthAgo: '1 hónapja', + monthsAgo: '{delta} hónapja', + yearAgo: '1 éve', + yearsAgo: '{delta} éve', + + lessThanMinuteUntil: 'alig egy perc múlva', + minuteUntil: 'egy perc múlva', + minutesUntil: '{delta} perc múlva', + hourUntil: 'egy óra múlva', + hoursUntil: '{delta} óra múlva', + dayUntil: '1 nap múlva', + daysUntil: '{delta} nap múlva', + weekUntil: '1 hét múlva', + weeksUntil: '{delta} hét múlva', + monthUntil: '1 hónap múlva', + monthsUntil: '{delta} hónap múlva', + yearUntil: '1 év múlva', + yearsUntil: '{delta} év múlva' + +}); + +/* +--- + +name: Locale.hu-HU.Form.Validator + +description: Form Validator messages for Hungarian. + +license: MIT-style license + +authors: + - Zsolt Szegheő + +requires: + - Locale + +provides: [Locale.hu-HU.Form.Validator] + +... +*/ + +Locale.define('hu-HU', 'FormValidator', { + + required: 'A mező kitöltése kötelező.', + minLength: 'Legalább {minLength} karakter megadása szükséges (megadva {length} karakter).', + maxLength: 'Legfeljebb {maxLength} karakter megadása lehetséges (megadva {length} karakter).', + integer: 'Egész szám megadása szükséges. A tizedesjegyek (pl. 1.25) nem engedélyezettek.', + numeric: 'Szám megadása szükséges (pl. "1" vagy "1.1" vagy "-1" vagy "-1.1").', + digits: 'Csak számok és írásjelek megadása lehetséges (pl. telefonszám kötőjelek és/vagy perjelekkel).', + alpha: 'Csak betűk (a-z) megadása lehetséges. Szóköz és egyéb karakterek nem engedélyezettek.', + alphanum: 'Csak betűk (a-z) vagy számok (0-9) megadása lehetséges. Szóköz és egyéb karakterek nem engedélyezettek.', + dateSuchAs: 'Valós dátum megadása szükséges (pl. {date}).', + dateInFormatMDY: 'Valós dátum megadása szükséges ÉÉÉÉ.HH.NN. formában. (pl. "1999.12.31.")', + email: 'Valós e-mail cím megadása szükséges (pl. "fred@domain.hu").', + url: 'Valós URL megadása szükséges (pl. http://www.example.com).', + currencyDollar: 'Valós pénzösszeg megadása szükséges (pl. 100.00 Ft.).', + oneRequired: 'Az alábbi mezők legalább egyikének kitöltése kötelező.', + errorPrefix: 'Hiba: ', + warningPrefix: 'Figyelem: ', + + // Form.Validator.Extras + noSpace: 'A mező nem tartalmazhat szóközöket.', + reqChkByNode: 'Nincs egyetlen kijelölt elem sem.', + requiredChk: 'A mező kitöltése kötelező.', + reqChkByName: 'Egy {label} kiválasztása szükséges.', + match: 'A mezőnek egyeznie kell a(z) {matchName} mezővel.', + startDate: 'a kezdet dátuma', + endDate: 'a vég dátuma', + currentDate: 'jelenlegi dátum', + afterDate: 'A dátum nem lehet kisebb, mint {label}.', + beforeDate: 'A dátum nem lehet nagyobb, mint {label}.', + startMonth: 'Kezdeti hónap megadása szükséges.', + sameMonth: 'A két dátumnak ugyanazon hónapban kell lennie.', + creditcard: 'A megadott bankkártyaszám nem valódi (megadva {length} számjegy).' + +}); + +/* +--- + +name: Locale.it-IT.Date + +description: Date messages for Italian. + +license: MIT-style license. + +authors: + - Andrea Novero + - Valerio Proietti + +requires: + - Locale + +provides: [Locale.it-IT.Date] + +... +*/ + +Locale.define('it-IT', 'Date', { + + months: ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre'], + months_abbr: ['gen', 'feb', 'mar', 'apr', 'mag', 'giu', 'lug', 'ago', 'set', 'ott', 'nov', 'dic'], + days: ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'], + days_abbr: ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab'], + + // Culture's date order: DD/MM/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%H.%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: 'º', + + lessThanMinuteAgo: 'meno di un minuto fa', + minuteAgo: 'circa un minuto fa', + minutesAgo: 'circa {delta} minuti fa', + hourAgo: "circa un'ora fa", + hoursAgo: 'circa {delta} ore fa', + dayAgo: 'circa 1 giorno fa', + daysAgo: 'circa {delta} giorni fa', + weekAgo: 'una settimana fa', + weeksAgo: '{delta} settimane fa', + monthAgo: 'un mese fa', + monthsAgo: '{delta} mesi fa', + yearAgo: 'un anno fa', + yearsAgo: '{delta} anni fa', + + lessThanMinuteUntil: 'tra meno di un minuto', + minuteUntil: 'tra circa un minuto', + minutesUntil: 'tra circa {delta} minuti', + hourUntil: "tra circa un'ora", + hoursUntil: 'tra circa {delta} ore', + dayUntil: 'tra circa un giorno', + daysUntil: 'tra circa {delta} giorni', + weekUntil: 'tra una settimana', + weeksUntil: 'tra {delta} settimane', + monthUntil: 'tra un mese', + monthsUntil: 'tra {delta} mesi', + yearUntil: 'tra un anno', + yearsUntil: 'tra {delta} anni' + +}); + +/* +--- + +name: Locale.it-IT.Form.Validator + +description: Form Validator messages for Italian. + +license: MIT-style license + +authors: + - Leonardo Laureti + - Andrea Novero + +requires: + - Locale + +provides: [Locale.it-IT.Form.Validator] + +... +*/ + +Locale.define('it-IT', 'FormValidator', { + + required: 'Il campo è obbligatorio.', + minLength: 'Inserire almeno {minLength} caratteri (ne sono stati inseriti {length}).', + maxLength: 'Inserire al massimo {maxLength} caratteri (ne sono stati inseriti {length}).', + integer: 'Inserire un numero intero. Non sono consentiti decimali (es.: 1.25).', + numeric: 'Inserire solo valori numerici (es.: "1" oppure "1.1" oppure "-1" oppure "-1.1").', + digits: 'Inserire solo numeri e caratteri di punteggiatura. Per esempio è consentito un numero telefonico con trattini o punti.', + alpha: 'Inserire solo lettere (a-z). Non sono consentiti spazi o altri caratteri.', + alphanum: 'Inserire solo lettere (a-z) o numeri (0-9). Non sono consentiti spazi o altri caratteri.', + dateSuchAs: 'Inserire una data valida del tipo {date}', + dateInFormatMDY: 'Inserire una data valida nel formato MM/GG/AAAA (es.: "12/31/1999")', + email: 'Inserire un indirizzo email valido. Per esempio "nome@dominio.com".', + url: 'Inserire un indirizzo valido. Per esempio "http://www.example.com".', + currencyDollar: 'Inserire un importo valido. Per esempio "$100.00".', + oneRequired: 'Completare almeno uno dei campi richiesti.', + errorPrefix: 'Errore: ', + warningPrefix: 'Attenzione: ', + + // Form.Validator.Extras + noSpace: 'Non sono consentiti spazi.', + reqChkByNode: 'Nessuna voce selezionata.', + requiredChk: 'Il campo è obbligatorio.', + reqChkByName: 'Selezionare un(a) {label}.', + match: 'Il valore deve corrispondere al campo {matchName}', + startDate: "data d'inizio", + endDate: 'data di fine', + currentDate: 'data attuale', + afterDate: 'La data deve corrispondere o essere successiva al {label}.', + beforeDate: 'La data deve corrispondere o essere precedente al {label}.', + startMonth: "Selezionare un mese d'inizio", + sameMonth: 'Le due date devono essere dello stesso mese - occorre modificarne una.' + +}); + +/* +--- + +name: Locale.ja-JP.Date + +description: Date messages for Japanese. + +license: MIT-style license + +authors: + - Noritaka Horio + +requires: + - Locale + +provides: [Locale.ja-JP.Date] + +... +*/ + +Locale.define('ja-JP', 'Date', { + + months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + months_abbr: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], + days: ['日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'], + days_abbr: ['日', '月', '火', '水', '木', '金', '土'], + + // Culture's date order: YYYY/MM/DD + dateOrder: ['year', 'month', 'date'], + shortDate: '%Y/%m/%d', + shortTime: '%H:%M', + AM: '午前', + PM: '午後', + firstDayOfWeek: 0, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: '1分以内前', + minuteAgo: '約1分前', + minutesAgo: '約{delta}分前', + hourAgo: '約1時間前', + hoursAgo: '約{delta}時間前', + dayAgo: '1日前', + daysAgo: '{delta}日前', + weekAgo: '1週間前', + weeksAgo: '{delta}週間前', + monthAgo: '1ヶ月前', + monthsAgo: '{delta}ヶ月前', + yearAgo: '1年前', + yearsAgo: '{delta}年前', + + lessThanMinuteUntil: '今から約1分以内', + minuteUntil: '今から約1分', + minutesUntil: '今から約{delta}分', + hourUntil: '今から約1時間', + hoursUntil: '今から約{delta}時間', + dayUntil: '今から1日間', + daysUntil: '今から{delta}日間', + weekUntil: '今から1週間', + weeksUntil: '今から{delta}週間', + monthUntil: '今から1ヶ月', + monthsUntil: '今から{delta}ヶ月', + yearUntil: '今から1年', + yearsUntil: '今から{delta}年' + +}); + +/* +--- + +name: Locale.ja-JP.Form.Validator + +description: Form Validator messages for Japanese. + +license: MIT-style license + +authors: + - Noritaka Horio + +requires: + - Locale + +provides: [Locale.ja-JP.Form.Validator] + +... +*/ + +Locale.define("ja-JP", "FormValidator", { + + required: '入力は必須です。', + minLength: '入力文字数は{minLength}以上にしてください。({length}文字)', + maxLength: '入力文字数は{maxLength}以下にしてください。({length}文字)', + integer: '整数を入力してください。', + numeric: '入力できるのは数値だけです。(例: "1", "1.1", "-1", "-1.1"....)', + digits: '入力できるのは数値と句読記号です。 (例: -や+を含む電話番号など).', + alpha: '入力できるのは半角英字だけです。それ以外の文字は入力できません。', + alphanum: '入力できるのは半角英数字だけです。それ以外の文字は入力できません。', + dateSuchAs: '有効な日付を入力してください。{date}', + dateInFormatMDY: '日付の書式に誤りがあります。YYYY/MM/DD (i.e. "1999/12/31")', + email: 'メールアドレスに誤りがあります。', + url: 'URLアドレスに誤りがあります。', + currencyDollar: '金額に誤りがあります。', + oneRequired: 'ひとつ以上入力してください。', + errorPrefix: 'エラー: ', + warningPrefix: '警告: ', + + // FormValidator.Extras + noSpace: 'スペースは入力できません。', + reqChkByNode: '選択されていません。', + requiredChk: 'この項目は必須です。', + reqChkByName: '{label}を選択してください。', + match: '{matchName}が入力されている場合必須です。', + startDate: '開始日', + endDate: '終了日', + currentDate: '今日', + afterDate: '{label}以降の日付にしてください。', + beforeDate: '{label}以前の日付にしてください。', + startMonth: '開始月を選択してください。', + sameMonth: '日付が同一です。どちらかを変更してください。' + +}); + +/* +--- + +name: Locale.ja-JP.Number + +description: Number messages for Japanese. + +license: MIT-style license + +authors: + - Noritaka Horio + +requires: + - Locale + +provides: [Locale.ja-JP.Number] + +... +*/ + +Locale.define('ja-JP', 'Number', { + + decimal: '.', + group: ',', + + currency: { + decimals: 0, + prefix: '\\' + } + +}); + +/* +--- + +name: Locale.nl-NL.Date + +description: Date messages for Dutch. + +license: MIT-style license + +authors: + - Lennart Pilon + - Tim Wienk + +requires: + - Locale + +provides: [Locale.nl-NL.Date] + +... +*/ + +Locale.define('nl-NL', 'Date', { + + months: ['januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december'], + months_abbr: ['jan', 'feb', 'mrt', 'apr', 'mei', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + days: ['zondag', 'maandag', 'dinsdag', 'woensdag', 'donderdag', 'vrijdag', 'zaterdag'], + days_abbr: ['zo', 'ma', 'di', 'wo', 'do', 'vr', 'za'], + + // Culture's date order: DD-MM-YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d-%m-%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: 'e', + + lessThanMinuteAgo: 'minder dan een minuut geleden', + minuteAgo: 'ongeveer een minuut geleden', + minutesAgo: '{delta} minuten geleden', + hourAgo: 'ongeveer een uur geleden', + hoursAgo: 'ongeveer {delta} uur geleden', + dayAgo: 'een dag geleden', + daysAgo: '{delta} dagen geleden', + weekAgo: 'een week geleden', + weeksAgo: '{delta} weken geleden', + monthAgo: 'een maand geleden', + monthsAgo: '{delta} maanden geleden', + yearAgo: 'een jaar geleden', + yearsAgo: '{delta} jaar geleden', + + lessThanMinuteUntil: 'over minder dan een minuut', + minuteUntil: 'over ongeveer een minuut', + minutesUntil: 'over {delta} minuten', + hourUntil: 'over ongeveer een uur', + hoursUntil: 'over {delta} uur', + dayUntil: 'over ongeveer een dag', + daysUntil: 'over {delta} dagen', + weekUntil: 'over een week', + weeksUntil: 'over {delta} weken', + monthUntil: 'over een maand', + monthsUntil: 'over {delta} maanden', + yearUntil: 'over een jaar', + yearsUntil: 'over {delta} jaar' + +}); + +/* +--- + +name: Locale.nl-NL.Form.Validator + +description: Form Validator messages for Dutch. + +license: MIT-style license + +authors: + - Lennart Pilon + - Arian Stolwijk + - Tim Wienk + +requires: + - Locale + +provides: [Locale.nl-NL.Form.Validator] + +... +*/ + +Locale.define('nl-NL', 'FormValidator', { + + required: 'Dit veld is verplicht.', + length: 'Vul precies {length} karakters in (je hebt {elLength} karakters ingevoerd).', + minLength: 'Vul minimaal {minLength} karakters in (je hebt {length} karakters ingevoerd).', + maxLength: 'Vul niet meer dan {maxLength} karakters in (je hebt {length} karakters ingevoerd).', + integer: 'Vul een getal in. Getallen met decimalen (bijvoorbeeld 1.25) zijn niet toegestaan.', + numeric: 'Vul alleen numerieke waarden in (bijvoorbeeld "1" of "1.1" of "-1" of "-1.1").', + digits: 'Vul alleen nummers en leestekens in (bijvoorbeeld een telefoonnummer met streepjes is toegestaan).', + alpha: 'Vul alleen letters in (a-z). Spaties en andere karakters zijn niet toegestaan.', + alphanum: 'Vul alleen letters (a-z) of nummers (0-9) in. Spaties en andere karakters zijn niet toegestaan.', + dateSuchAs: 'Vul een geldige datum in, zoals {date}', + dateInFormatMDY: 'Vul een geldige datum, in het formaat MM/DD/YYYY (bijvoorbeeld "12/31/1999")', + email: 'Vul een geldig e-mailadres in. Bijvoorbeeld "fred@domein.nl".', + url: 'Vul een geldige URL in, zoals http://www.example.com.', + currencyDollar: 'Vul een geldig $ bedrag in. Bijvoorbeeld $100.00 .', + oneRequired: 'Vul iets in bij in ieder geval een van deze velden.', + warningPrefix: 'Waarschuwing: ', + errorPrefix: 'Fout: ', + + // Form.Validator.Extras + noSpace: 'Spaties zijn niet toegestaan in dit veld.', + reqChkByNode: 'Er zijn geen items geselecteerd.', + requiredChk: 'Dit veld is verplicht.', + reqChkByName: 'Selecteer een {label}.', + match: 'Dit veld moet overeen komen met het {matchName} veld', + startDate: 'de begin datum', + endDate: 'de eind datum', + currentDate: 'de huidige datum', + afterDate: 'De datum moet hetzelfde of na {label} zijn.', + beforeDate: 'De datum moet hetzelfde of voor {label} zijn.', + startMonth: 'Selecteer een begin maand', + sameMonth: 'Deze twee data moeten in dezelfde maand zijn - u moet een van beide aanpassen.', + creditcard: 'Het ingevulde creditcardnummer is niet geldig. Controleer het nummer en probeer opnieuw. {length} getallen ingevuld.' + +}); + +/* +--- + +name: Locale.nl-NL.Number + +description: Number messages for Dutch. + +license: MIT-style license + +authors: + - Arian Stolwijk + +requires: + - Locale + - Locale.EU.Number + +provides: [Locale.nl-NL.Number] + +... +*/ + +Locale.define('nl-NL').inherit('EU', 'Number'); + + + + +/* +--- + +name: Locale.no-NO.Date + +description: Date messages for Norwegian. + +license: MIT-style license + +authors: + - Espen 'Rexxars' Hovlandsdal + - Ole Tøsse Kolvik +requires: + - Locale + +provides: [Locale.no-NO.Date] + +... +*/ + +Locale.define('no-NO', 'Date', { + months: ['Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember'], + months_abbr: ['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Des'], + days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'], + days_abbr: ['Søn', 'Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør'], + + // Culture's date order: DD.MM.YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d.%m.%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + lessThanMinuteAgo: 'mindre enn et minutt siden', + minuteAgo: 'omtrent et minutt siden', + minutesAgo: '{delta} minutter siden', + hourAgo: 'omtrent en time siden', + hoursAgo: 'omtrent {delta} timer siden', + dayAgo: '{delta} dag siden', + daysAgo: '{delta} dager siden', + weekAgo: 'en uke siden', + weeksAgo: '{delta} uker siden', + monthAgo: 'en måned siden', + monthsAgo: '{delta} måneder siden', + yearAgo: 'ett år siden', + yearsAgo: '{delta} år siden', + + lessThanMinuteUntil: 'mindre enn et minutt til', + minuteUntil: 'omtrent et minutt til', + minutesUntil: '{delta} minutter til', + hourUntil: 'omtrent en time til', + hoursUntil: 'omtrent {delta} timer til', + dayUntil: 'en dag til', + daysUntil: '{delta} dager til', + weekUntil: 'en uke til', + weeksUntil: '{delta} uker til', + monthUntil: 'en måned til', + monthsUntil: '{delta} måneder til', + yearUntil: 'et år til', + yearsUntil: '{delta} år til' +}); + +/* +--- + +name: Locale.no-NO.Form.Validator + +description: Form Validator messages for Norwegian. + +license: MIT-style license + +authors: + - Espen 'Rexxars' Hovlandsdal + +requires: + - Locale + +provides: [Locale.no-NO.Form.Validator] + +... +*/ + +Locale.define('no-NO', 'FormValidator', { + + required: 'Dette feltet er pÃ¥krevd.', + minLength: 'Vennligst skriv inn minst {minLength} tegn (du skrev {length} tegn).', + maxLength: 'Vennligst skriv inn maksimalt {maxLength} tegn (du skrev {length} tegn).', + integer: 'Vennligst skriv inn et tall i dette feltet. Tall med desimaler (for eksempel 1,25) er ikke tillat.', + numeric: 'Vennligst skriv inn kun numeriske verdier i dette feltet (for eksempel "1", "1.1", "-1" eller "-1.1").', + digits: 'Vennligst bruk kun nummer og skilletegn i dette feltet.', + alpha: 'Vennligst bruk kun bokstaver (a-z) i dette feltet. Ingen mellomrom eller andre tegn er tillat.', + alphanum: 'Vennligst bruk kun bokstaver (a-z) eller nummer (0-9) i dette feltet. Ingen mellomrom eller andre tegn er tillat.', + dateSuchAs: 'Vennligst skriv inn en gyldig dato, som {date}', + dateInFormatMDY: 'Vennligst skriv inn en gyldig dato, i formatet MM/DD/YYYY (for eksempel "12/31/1999")', + email: 'Vennligst skriv inn en gyldig epost-adresse. For eksempel "espen@domene.no".', + url: 'Vennligst skriv inn en gyldig URL, for eksempel http://www.example.com.', + currencyDollar: 'Vennligst fyll ut et gyldig $ beløp. For eksempel $100.00 .', + oneRequired: 'Vennligst fyll ut noe i minst ett av disse feltene.', + errorPrefix: 'Feil: ', + warningPrefix: 'Advarsel: ' + +}); + +/* +--- + +name: Locale.pl-PL.Date + +description: Date messages for Polish. + +license: MIT-style license + +authors: + - Oskar Krawczyk + +requires: + - Locale + +provides: [Locale.pl-PL.Date] + +... +*/ + +Locale.define('pl-PL', 'Date', { + + months: ['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień'], + months_abbr: ['sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip', 'sie', 'wrz', 'paź', 'lis', 'gru'], + days: ['Niedziela', 'Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota'], + days_abbr: ['niedz.', 'pon.', 'wt.', 'śr.', 'czw.', 'pt.', 'sob.'], + + // Culture's date order: YYYY-MM-DD + dateOrder: ['year', 'month', 'date'], + shortDate: '%Y-%m-%d', + shortTime: '%H:%M', + AM: 'nad ranem', + PM: 'po południu', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: function(dayOfMonth){ + return (dayOfMonth > 3 && dayOfMonth < 21) ? 'ty' : ['ty', 'szy', 'gi', 'ci', 'ty'][Math.min(dayOfMonth % 10, 4)]; + }, + + lessThanMinuteAgo: 'mniej niż minute temu', + minuteAgo: 'około minutę temu', + minutesAgo: '{delta} minut temu', + hourAgo: 'około godzinę temu', + hoursAgo: 'około {delta} godzin temu', + dayAgo: 'Wczoraj', + daysAgo: '{delta} dni temu', + + lessThanMinuteUntil: 'za niecałą minutę', + minuteUntil: 'za około minutę', + minutesUntil: 'za {delta} minut', + hourUntil: 'za około godzinę', + hoursUntil: 'za około {delta} godzin', + dayUntil: 'za 1 dzień', + daysUntil: 'za {delta} dni' + +}); + +/* +--- + +name: Locale.pl-PL.Form.Validator + +description: Form Validator messages for Polish. + +license: MIT-style license + +authors: + - Oskar Krawczyk + +requires: + - Locale + +provides: [Locale.pl-PL.Form.Validator] + +... +*/ + +Locale.define('pl-PL', 'FormValidator', { + + required: 'To pole jest wymagane.', + minLength: 'Wymagane jest przynajmniej {minLength} znaków (wpisanych zostało tylko {length}).', + maxLength: 'Dozwolone jest nie więcej niż {maxLength} znaków (wpisanych zostało {length})', + integer: 'To pole wymaga liczb całych. Liczby dziesiętne (np. 1.25) są niedozwolone.', + numeric: 'Prosimy używać tylko numerycznych wartości w tym polu (np. "1", "1.1", "-1" lub "-1.1").', + digits: 'Prosimy używać liczb oraz zankow punktuacyjnych w typ polu (dla przykładu, przy numerze telefonu myślniki i kropki są dozwolone).', + alpha: 'Prosimy używać tylko liter (a-z) w tym polu. Spacje oraz inne znaki są niedozwolone.', + alphanum: 'Prosimy używać tylko liter (a-z) lub liczb (0-9) w tym polu. Spacje oraz inne znaki są niedozwolone.', + dateSuchAs: 'Prosimy podać prawidłową datę w formacie: {date}', + dateInFormatMDY: 'Prosimy podać poprawną date w formacie DD.MM.RRRR (i.e. "12.01.2009")', + email: 'Prosimy podać prawidłowy adres e-mail, np. "jan@domena.pl".', + url: 'Prosimy podać prawidłowy adres URL, np. http://www.example.com.', + currencyDollar: 'Prosimy podać prawidłową sumę w PLN. Dla przykładu: 100.00 PLN.', + oneRequired: 'Prosimy wypełnić chociaż jedno z pól.', + errorPrefix: 'Błąd: ', + warningPrefix: 'Uwaga: ', + + // Form.Validator.Extras + noSpace: 'W tym polu nie mogą znajdować się spacje.', + reqChkByNode: 'Brak zaznaczonych elementów.', + requiredChk: 'To pole jest wymagane.', + reqChkByName: 'Prosimy wybrać z {label}.', + match: 'To pole musi być takie samo jak {matchName}', + startDate: 'data początkowa', + endDate: 'data końcowa', + currentDate: 'aktualna data', + afterDate: 'Podana data poinna być taka sama lub po {label}.', + beforeDate: 'Podana data poinna być taka sama lub przed {label}.', + startMonth: 'Prosimy wybrać początkowy miesiąc.', + sameMonth: 'Te dwie daty muszą być w zakresie tego samego miesiąca - wymagana jest zmiana któregoś z pól.' + +}); + +/* +--- + +name: Locale.pt-PT.Date + +description: Date messages for Portuguese. + +license: MIT-style license + +authors: + - Fabio Miranda Costa + +requires: + - Locale + +provides: [Locale.pt-PT.Date] + +... +*/ + +Locale.define('pt-PT', 'Date', { + + months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro'], + months_abbr: ['Jan', 'Fev', 'Mar', 'Abr', 'Mai', 'Jun', 'Jul', 'Ago', 'Set', 'Out', 'Nov', 'Dez'], + days: ['Domingo', 'Segunda-feira', 'Terça-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'Sábado'], + days_abbr: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'], + + // Culture's date order: DD-MM-YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d-%m-%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: 'º', + + lessThanMinuteAgo: 'há menos de um minuto', + minuteAgo: 'há cerca de um minuto', + minutesAgo: 'há {delta} minutos', + hourAgo: 'há cerca de uma hora', + hoursAgo: 'há cerca de {delta} horas', + dayAgo: 'há um dia', + daysAgo: 'há {delta} dias', + weekAgo: 'há uma semana', + weeksAgo: 'há {delta} semanas', + monthAgo: 'há um mês', + monthsAgo: 'há {delta} meses', + yearAgo: 'há um ano', + yearsAgo: 'há {delta} anos', + + lessThanMinuteUntil: 'em menos de um minuto', + minuteUntil: 'em um minuto', + minutesUntil: 'em {delta} minutos', + hourUntil: 'em uma hora', + hoursUntil: 'em {delta} horas', + dayUntil: 'em um dia', + daysUntil: 'em {delta} dias', + weekUntil: 'em uma semana', + weeksUntil: 'em {delta} semanas', + monthUntil: 'em um mês', + monthsUntil: 'em {delta} meses', + yearUntil: 'em um ano', + yearsUntil: 'em {delta} anos' + +}); + +/* +--- + +name: Locale.pt-BR.Date + +description: Date messages for Portuguese (Brazil). + +license: MIT-style license + +authors: + - Fabio Miranda Costa + +requires: + - Locale + - Locale.pt-PT.Date + +provides: [Locale.pt-BR.Date] + +... +*/ + +Locale.define('pt-BR', 'Date', { + + // Culture's date order: DD/MM/YYYY + shortDate: '%d/%m/%Y' + +}).inherit('pt-PT', 'Date'); + +/* +--- + +name: Locale.pt-BR.Form.Validator + +description: Form Validator messages for Portuguese (Brazil). + +license: MIT-style license + +authors: + - Fábio Miranda Costa + +requires: + - Locale + +provides: [Locale.pt-BR.Form.Validator] + +... +*/ + +Locale.define('pt-BR', 'FormValidator', { + + required: 'Este campo é obrigatório.', + minLength: 'Digite pelo menos {minLength} caracteres (tamanho atual: {length}).', + maxLength: 'Não digite mais de {maxLength} caracteres (tamanho atual: {length}).', + integer: 'Por favor digite apenas um número inteiro neste campo. Não são permitidos números decimais (por exemplo, 1,25).', + numeric: 'Por favor digite apenas valores numéricos neste campo (por exemplo, "1" ou "1.1" ou "-1" ou "-1,1").', + digits: 'Por favor use apenas números e pontuação neste campo (por exemplo, um número de telefone com traços ou pontos é permitido).', + alpha: 'Por favor use somente letras (a-z). Espaço e outros caracteres não são permitidos.', + alphanum: 'Use somente letras (a-z) ou números (0-9) neste campo. Espaço e outros caracteres não são permitidos.', + dateSuchAs: 'Digite uma data válida, como {date}', + dateInFormatMDY: 'Digite uma data válida, como DD/MM/YYYY (por exemplo, "31/12/1999")', + email: 'Digite um endereço de email válido. Por exemplo "nome@dominio.com".', + url: 'Digite uma URL válida. Exemplo: http://www.example.com.', + currencyDollar: 'Digite um valor em dinheiro válido. Exemplo: R$100,00 .', + oneRequired: 'Digite algo para pelo menos um desses campos.', + errorPrefix: 'Erro: ', + warningPrefix: 'Aviso: ', + + // Form.Validator.Extras + noSpace: 'Não é possível digitar espaços neste campo.', + reqChkByNode: 'Não foi selecionado nenhum item.', + requiredChk: 'Este campo é obrigatório.', + reqChkByName: 'Por favor digite um {label}.', + match: 'Este campo deve ser igual ao campo {matchName}.', + startDate: 'a data inicial', + endDate: 'a data final', + currentDate: 'a data atual', + afterDate: 'A data deve ser igual ou posterior a {label}.', + beforeDate: 'A data deve ser igual ou anterior a {label}.', + startMonth: 'Por favor selecione uma data inicial.', + sameMonth: 'Estas duas datas devem ter o mesmo mês - você deve modificar uma das duas.', + creditcard: 'O número do cartão de crédito informado é inválido. Por favor verifique o valor e tente novamente. {length} números informados.' + +}); + +/* +--- + +name: Locale.pt-BR.Number + +description: Number messages for PT Brazilian. + +license: MIT-style license + +authors: + - Arian Stolwijk + - Danillo César + +requires: + - Locale + +provides: [Locale.pt-BR.Number] + +... +*/ + +Locale.define('pt-BR', 'Number', { + + decimal: ',', + group: '.', + + currency: { + prefix: 'R$ ' + } + +}); + + + +/* +--- + +name: Locale.pt-PT.Form.Validator + +description: Form Validator messages for Portuguese. + +license: MIT-style license + +authors: + - Miquel Hudin + +requires: + - Locale + +provides: [Locale.pt-PT.Form.Validator] + +... +*/ + +Locale.define('pt-PT', 'FormValidator', { + + required: 'Este campo é necessário.', + minLength: 'Digite pelo menos{minLength} caracteres (comprimento {length} caracteres).', + maxLength: 'Não insira mais de {maxLength} caracteres (comprimento {length} caracteres).', + integer: 'Digite um número inteiro neste domínio. Com números decimais (por exemplo, 1,25), não são permitidas.', + numeric: 'Digite apenas valores numéricos neste domínio (p.ex., "1" ou "1.1" ou "-1" ou "-1,1").', + digits: 'Por favor, use números e pontuação apenas neste campo (p.ex., um número de telefone com traços ou pontos é permitida).', + alpha: 'Por favor use somente letras (a-z), com nesta área. Não utilize espaços nem outros caracteres são permitidos.', + alphanum: 'Use somente letras (a-z) ou números (0-9) neste campo. Não utilize espaços nem outros caracteres são permitidos.', + dateSuchAs: 'Digite uma data válida, como {date}', + dateInFormatMDY: 'Digite uma data válida, como DD/MM/YYYY (p.ex. "31/12/1999")', + email: 'Digite um endereço de email válido. Por exemplo "fred@domain.com".', + url: 'Digite uma URL válida, como http://www.example.com.', + currencyDollar: 'Digite um valor válido $. Por exemplo $ 100,00. ', + oneRequired: 'Digite algo para pelo menos um desses insumos.', + errorPrefix: 'Erro: ', + warningPrefix: 'Aviso: ' + +}); + +/* +--- + +name: Locale.ru-RU-unicode.Date + +description: Date messages for Russian (utf-8). + +license: MIT-style license + +authors: + - Evstigneev Pavel + - Kuryanovich Egor + +requires: + - Locale + +provides: [Locale.ru-RU.Date] + +... +*/ + +(function(){ + +// Russian language pluralization rules, taken from CLDR project, http://unicode.org/cldr/ +// one -> n mod 10 is 1 and n mod 100 is not 11; +// few -> n mod 10 in 2..4 and n mod 100 not in 12..14; +// many -> n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14; +// other -> everything else (example 3.14) +var pluralize = function (n, one, few, many, other){ + var modulo10 = n % 10, + modulo100 = n % 100; + + if (modulo10 == 1 && modulo100 != 11){ + return one; + } else if ((modulo10 == 2 || modulo10 == 3 || modulo10 == 4) && !(modulo100 == 12 || modulo100 == 13 || modulo100 == 14)){ + return few; + } else if (modulo10 == 0 || (modulo10 == 5 || modulo10 == 6 || modulo10 == 7 || modulo10 == 8 || modulo10 == 9) || (modulo100 == 11 || modulo100 == 12 || modulo100 == 13 || modulo100 == 14)){ + return many; + } else { + return other; + } +}; + +Locale.define('ru-RU', 'Date', { + + months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'], + months_abbr: ['янв', 'февр', 'март', 'апр', 'май','июнь','июль','авг','сент','окт','нояб','дек'], + days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'], + days_abbr: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'], + + // Culture's date order: DD.MM.YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d.%m.%Y', + shortTime: '%H:%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: 'меньше минуты назад', + minuteAgo: 'минуту назад', + minutesAgo: function(delta){ return '{delta} ' + pluralize(delta, 'минуту', 'минуты', 'минут') + ' назад'; }, + hourAgo: 'час назад', + hoursAgo: function(delta){ return '{delta} ' + pluralize(delta, 'час', 'часа', 'часов') + ' назад'; }, + dayAgo: 'вчера', + daysAgo: function(delta){ return '{delta} ' + pluralize(delta, 'день', 'дня', 'дней') + ' назад'; }, + weekAgo: 'неделю назад', + weeksAgo: function(delta){ return '{delta} ' + pluralize(delta, 'неделя', 'недели', 'недель') + ' назад'; }, + monthAgo: 'месяц назад', + monthsAgo: function(delta){ return '{delta} ' + pluralize(delta, 'месяц', 'месяца', 'месяцев') + ' назад'; }, + yearAgo: 'год назад', + yearsAgo: function(delta){ return '{delta} ' + pluralize(delta, 'год', 'года', 'лет') + ' назад'; }, + + lessThanMinuteUntil: 'меньше чем через минуту', + minuteUntil: 'через минуту', + minutesUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'минуту', 'минуты', 'минут') + ''; }, + hourUntil: 'через час', + hoursUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'час', 'часа', 'часов') + ''; }, + dayUntil: 'завтра', + daysUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'день', 'дня', 'дней') + ''; }, + weekUntil: 'через неделю', + weeksUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'неделю', 'недели', 'недель') + ''; }, + monthUntil: 'через месяц', + monthsUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'месяц', 'месяца', 'месяцев') + ''; }, + yearUntil: 'через', + yearsUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'год', 'года', 'лет') + ''; } + +}); + + + +})(); + +/* +--- + +name: Locale.ru-RU-unicode.Form.Validator + +description: Form Validator messages for Russian (utf-8). + +license: MIT-style license + +authors: + - Chernodarov Egor + +requires: + - Locale + +provides: [Locale.ru-RU.Form.Validator] + +... +*/ + +Locale.define('ru-RU', 'FormValidator', { + + required: 'Это поле обязательно к заполнению.', + minLength: 'Пожалуйста, введите хотя бы {minLength} символов (Вы ввели {length}).', + maxLength: 'Пожалуйста, введите не больше {maxLength} символов (Вы ввели {length}).', + integer: 'Пожалуйста, введите в это поле число. Дробные числа (например 1.25) тут не разрешены.', + numeric: 'Пожалуйста, введите в это поле число (например "1" или "1.1", или "-1", или "-1.1").', + digits: 'В этом поле Вы можете использовать только цифры и знаки пунктуации (например, телефонный номер со знаками дефиса или с точками).', + alpha: 'В этом поле можно использовать только латинские буквы (a-z). Пробелы и другие символы запрещены.', + alphanum: 'В этом поле можно использовать только латинские буквы (a-z) и цифры (0-9). Пробелы и другие символы запрещены.', + dateSuchAs: 'Пожалуйста, введите корректную дату {date}', + dateInFormatMDY: 'Пожалуйста, введите дату в формате ММ/ДД/ГГГГ (например "12/31/1999")', + email: 'Пожалуйста, введите корректный емейл-адрес. Для примера "fred@domain.com".', + url: 'Пожалуйста, введите правильную ссылку вида http://www.example.com.', + currencyDollar: 'Пожалуйста, введите сумму в долларах. Например: $100.00 .', + oneRequired: 'Пожалуйста, выберите хоть что-нибудь в одном из этих полей.', + errorPrefix: 'Ошибка: ', + warningPrefix: 'Внимание: ' + +}); + + + +/* +--- + +name: Locale.sk-SK.Date + +description: Date messages for Slovak. + +license: MIT-style license + +authors: + - Ivan Masár + +requires: + - Locale + +provides: [Locale.sk-SK.Date] + +... +*/ +(function(){ + +// Slovak language pluralization rules, see http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html +// one -> n is 1; 1 +// few -> n in 2..4; 2-4 +// other -> everything else 0, 5-999, 1.31, 2.31, 5.31... +var pluralize = function (n, one, few, other){ + if (n == 1) return one; + else if (n == 2 || n == 3 || n == 4) return few; + else return other; +}; + +Locale.define('sk-SK', 'Date', { + + months: ['Január', 'Február', 'Marec', 'Apríl', 'Máj', 'Jún', 'Júl', 'August', 'September', 'Október', 'November', 'December'], + months_abbr: ['januára', 'februára', 'marca', 'apríla', 'mája', 'júna', 'júla', 'augusta', 'septembra', 'októbra', 'novembra', 'decembra'], + days: ['Nedele', 'Pondelí', 'Úterý', 'Streda', 'Čtvrtek', 'Pátek', 'Sobota'], + days_abbr: ['ne', 'po', 'ut', 'st', 'št', 'pi', 'so'], + + // Culture's date order: DD.MM.YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d.%m.%Y', + shortTime: '%H:%M', + AM: 'dop.', + PM: 'pop.', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '.', + + lessThanMinuteAgo: 'pred chvíľou', + minuteAgo: 'približne pred minútou', + minutesAgo: function(delta){ return 'pred {delta} ' + pluralize(delta, 'minútou', 'minútami', 'minútami'); }, + hourAgo: 'približne pred hodinou', + hoursAgo: function(delta){ return 'pred {delta} ' + pluralize(delta, 'hodinou', 'hodinami', 'hodinami'); }, + dayAgo: 'pred dňom', + daysAgo: function(delta){ return 'pred {delta} ' + pluralize(delta, 'dňom', 'dňami', 'dňami'); }, + weekAgo: 'pred týždňom', + weeksAgo: function(delta){ return 'pred {delta} ' + pluralize(delta, 'týždňom', 'týždňami', 'týždňami'); }, + monthAgo: 'pred mesiacom', + monthsAgo: function(delta){ return 'pred {delta} ' + pluralize(delta, 'mesiacom', 'mesiacmi', 'mesiacmi'); }, + yearAgo: 'pred rokom', + yearsAgo: function(delta){ return 'pred {delta} ' + pluralize(delta, 'rokom', 'rokmi', 'rokmi'); }, + + lessThanMinuteUntil: 'o chvíľu', + minuteUntil: 'približne o minútu', + minutesUntil: function(delta){ return 'o {delta} ' + pluralize(delta, 'minútu', 'minúty', 'minúty'); }, + hourUntil: 'približne o hodinu', + hoursUntil: function(delta){ return 'o {delta} ' + pluralize(delta, 'hodinu', 'hodiny', 'hodín'); }, + dayUntil: 'o deň', + daysUntil: function(delta){ return 'o {delta} ' + pluralize(delta, 'deň', 'dni', 'dní'); }, + weekUntil: 'o týždeň', + weeksUntil: function(delta){ return 'o {delta} ' + pluralize(delta, 'týždeň', 'týždne', 'týždňov'); }, + monthUntil: 'o mesiac', + monthsUntil: function(delta){ return 'o {delta} ' + pluralize(delta, 'mesiac', 'mesiace', 'mesiacov'); }, + yearUntil: 'o rok', + yearsUntil: function(delta){ return 'o {delta} ' + pluralize(delta, 'rok', 'roky', 'rokov'); } +}); + +})(); + +/* +--- + +name: Locale.sk-SK.Form.Validator + +description: Form Validator messages for Czech. + +license: MIT-style license + +authors: + - Ivan Masár + +requires: + - Locale + +provides: [Locale.sk-SK.Form.Validator] + +... +*/ + +Locale.define('sk-SK', 'FormValidator', { + + required: 'Táto položka je povinná.', + minLength: 'Zadajte prosím aspoň {minLength} znakov (momentálne {length} znakov).', + maxLength: 'Zadajte prosím menej ako {maxLength} znakov (momentálne {length} znakov).', + integer: 'Zadajte prosím celé číslo. Desetinné čísla (napr. 1.25) nie sú povolené.', + numeric: 'Zadajte len číselné hodnoty (t.j. „1“ alebo „1.1“ alebo „-1“ alebo „-1.1“).', + digits: 'Zadajte prosím len čísla a interpunkčné znamienka (napríklad telefónne číslo s pomlčkami albo bodkami je povolené).', + alpha: 'Zadajte prosím len písmená (a-z). Medzery alebo iné znaky nie sú povolené.', + alphanum: 'Zadajte prosím len písmená (a-z) alebo číslice (0-9). Medzery alebo iné znaky nie sú povolené.', + dateSuchAs: 'Zadajte prosím platný dátum v tvare {date}', + dateInFormatMDY: 'Zadajte prosím platný datum v tvare MM / DD / RRRR (t.j. „12/31/1999“)', + email: 'Zadajte prosím platnú emailovú adresu. Napríklad „fred@domain.com“.', + url: 'Zadajte prosím platnoú adresu URL v tvare http://www.example.com.', + currencyDollar: 'Zadajte prosím platnú čiastku. Napríklad $100.00.', + oneRequired: 'Zadajte prosím aspoň jednu hodnotu z týchto položiek.', + errorPrefix: 'Chyba: ', + warningPrefix: 'Upozornenie: ', + + // Form.Validator.Extras + noSpace: 'V tejto položle nie sú povolené medzery', + reqChkByNode: 'Nie sú vybrané žiadne položky.', + requiredChk: 'Táto položka je povinná.', + reqChkByName: 'Prosím vyberte {label}.', + match: 'Táto položka sa musí zhodovať s položkou {matchName}', + startDate: 'dátum začiatku', + endDate: 'dátum ukončenia', + currendDate: 'aktuálny dátum', + afterDate: 'Dátum by mal býť rovnaký alebo väčší ako {label}.', + beforeDate: 'Dátum by mal byť rovnaký alebo menší ako {label}.', + startMonth: 'Vyberte počiatočný mesiac.', + sameMonth: 'Tieto dva dátumy musia býť v rovnakom mesiaci - zmeňte jeden z nich.', + creditcard: 'Zadané číslo kreditnej karty je neplatné. Prosím, opravte ho. Bolo zadaných {length} číslic.' + +}); + +/* +--- + +name: Locale.si-SI.Date + +description: Date messages for Slovenian. + +license: MIT-style license + +authors: + - Radovan Lozej + +requires: + - Locale + +provides: [Locale.si-SI.Date] + +... +*/ + +(function(){ + +var pluralize = function(n, one, two, three, other){ + return (n >= 1 && n <= 3) ? arguments[n] : other; +}; + +Locale.define('sl-SI', 'Date', { + + months: ['januar', 'februar', 'marec', 'april', 'maj', 'junij', 'julij', 'avgust', 'september', 'oktober', 'november', 'december'], + months_abbr: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'avg', 'sep', 'okt', 'nov', 'dec'], + days: ['nedelja', 'ponedeljek', 'torek', 'sreda', 'četrtek', 'petek', 'sobota'], + days_abbr: ['ned', 'pon', 'tor', 'sre', 'čet', 'pet', 'sob'], + + // Culture's date order: DD.MM.YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d.%m.%Y', + shortTime: '%H.%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '.', + + lessThanMinuteAgo: 'manj kot minuto nazaj', + minuteAgo: 'minuto nazaj', + minutesAgo: function(delta){ return '{delta} ' + pluralize(delta, 'minuto', 'minuti', 'minute', 'minut') + ' nazaj'; }, + hourAgo: 'uro nazaj', + hoursAgo: function(delta){ return '{delta} ' + pluralize(delta, 'uro', 'uri', 'ure', 'ur') + ' nazaj'; }, + dayAgo: 'dan nazaj', + daysAgo: function(delta){ return '{delta} ' + pluralize(delta, 'dan', 'dneva', 'dni', 'dni') + ' nazaj'; }, + weekAgo: 'teden nazaj', + weeksAgo: function(delta){ return '{delta} ' + pluralize(delta, 'teden', 'tedna', 'tedne', 'tednov') + ' nazaj'; }, + monthAgo: 'mesec nazaj', + monthsAgo: function(delta){ return '{delta} ' + pluralize(delta, 'mesec', 'meseca', 'mesece', 'mesecov') + ' nazaj'; }, + yearthAgo: 'leto nazaj', + yearsAgo: function(delta){ return '{delta} ' + pluralize(delta, 'leto', 'leti', 'leta', 'let') + ' nazaj'; }, + + lessThanMinuteUntil: 'še manj kot minuto', + minuteUntil: 'še minuta', + minutesUntil: function(delta){ return 'še {delta} ' + pluralize(delta, 'minuta', 'minuti', 'minute', 'minut'); }, + hourUntil: 'še ura', + hoursUntil: function(delta){ return 'še {delta} ' + pluralize(delta, 'ura', 'uri', 'ure', 'ur'); }, + dayUntil: 'še dan', + daysUntil: function(delta){ return 'še {delta} ' + pluralize(delta, 'dan', 'dneva', 'dnevi', 'dni'); }, + weekUntil: 'še tedn', + weeksUntil: function(delta){ return 'še {delta} ' + pluralize(delta, 'teden', 'tedna', 'tedni', 'tednov'); }, + monthUntil: 'še mesec', + monthsUntil: function(delta){ return 'še {delta} ' + pluralize(delta, 'mesec', 'meseca', 'meseci', 'mesecov'); }, + yearUntil: 'še leto', + yearsUntil: function(delta){ return 'še {delta} ' + pluralize(delta, 'leto', 'leti', 'leta', 'let'); } + +}); + +})(); + +/* +--- + +name: Locale.si-SI.Form.Validator + +description: Form Validator messages for Slovenian. + +license: MIT-style license + +authors: + - Radovan Lozej + +requires: + - Locale + +provides: [Locale.si-SI.Form.Validator] + +... +*/ + +Locale.define('sl-SI', 'FormValidator', { + + required: 'To polje je obvezno', + minLength: 'Prosim, vnesite vsaj {minLength} znakov (vnesli ste {length} znakov).', + maxLength: 'Prosim, ne vnesite več kot {maxLength} znakov (vnesli ste {length} znakov).', + integer: 'Prosim, vnesite celo število. Decimalna števila (kot 1,25) niso dovoljena.', + numeric: 'Prosim, vnesite samo numerične vrednosti (kot "1" ali "1.1" ali "-1" ali "-1.1").', + digits: 'Prosim, uporabite številke in ločila le na tem polju (na primer, dovoljena je telefonska številka z pomišlaji ali pikami).', + alpha: 'Prosim, uporabite le črke v tem plju. Presledki in drugi znaki niso dovoljeni.', + alphanum: 'Prosim, uporabite samo črke ali številke v tem polju. Presledki in drugi znaki niso dovoljeni.', + dateSuchAs: 'Prosim, vnesite pravilen datum kot {date}', + dateInFormatMDY: 'Prosim, vnesite pravilen datum kot MM.DD.YYYY (primer "12.31.1999")', + email: 'Prosim, vnesite pravilen email naslov. Na primer "fred@domain.com".', + url: 'Prosim, vnesite pravilen URL kot http://www.example.com.', + currencyDollar: 'Prosim, vnesit epravilno vrednost €. Primer 100,00€ .', + oneRequired: 'Prosimo, vnesite nekaj za vsaj eno izmed teh polj.', + errorPrefix: 'Napaka: ', + warningPrefix: 'Opozorilo: ', + + // Form.Validator.Extras + noSpace: 'To vnosno polje ne dopušča presledkov.', + reqChkByNode: 'Nič niste izbrali.', + requiredChk: 'To polje je obvezno', + reqChkByName: 'Prosim, izberite {label}.', + match: 'To polje se mora ujemati z poljem {matchName}', + startDate: 'datum začetka', + endDate: 'datum konca', + currentDate: 'trenuten datum', + afterDate: 'Datum bi moral biti isti ali po {label}.', + beforeDate: 'Datum bi moral biti isti ali pred {label}.', + startMonth: 'Prosim, vnesite začetni datum', + sameMonth: 'Ta dva datuma morata biti v istem mesecu - premeniti morate eno ali drugo.', + creditcard: 'Številka kreditne kartice ni pravilna. Preverite številko ali poskusite še enkrat. Vnešenih {length} znakov.' + +}); + +/* +--- + +name: Locale.sv-SE.Date + +description: Date messages for Swedish. + +license: MIT-style license + +authors: + - Martin Lundgren + +requires: + - Locale + +provides: [Locale.sv-SE.Date] + +... +*/ + +Locale.define('sv-SE', 'Date', { + + months: ['januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december'], + months_abbr: ['jan', 'feb', 'mar', 'apr', 'maj', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'dec'], + days: ['söndag', 'måndag', 'tisdag', 'onsdag', 'torsdag', 'fredag', 'lördag'], + days_abbr: ['sön', 'mån', 'tis', 'ons', 'tor', 'fre', 'lör'], + + // Culture's date order: YYYY-MM-DD + dateOrder: ['year', 'month', 'date'], + shortDate: '%Y-%m-%d', + shortTime: '%H:%M', + AM: '', + PM: '', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: 'mindre än en minut sedan', + minuteAgo: 'ungefär en minut sedan', + minutesAgo: '{delta} minuter sedan', + hourAgo: 'ungefär en timme sedan', + hoursAgo: 'ungefär {delta} timmar sedan', + dayAgo: '1 dag sedan', + daysAgo: '{delta} dagar sedan', + + lessThanMinuteUntil: 'mindre än en minut sedan', + minuteUntil: 'ungefär en minut sedan', + minutesUntil: '{delta} minuter sedan', + hourUntil: 'ungefär en timme sedan', + hoursUntil: 'ungefär {delta} timmar sedan', + dayUntil: '1 dag sedan', + daysUntil: '{delta} dagar sedan' + +}); + +/* +--- + +name: Locale.sv-SE.Form.Validator + +description: Form Validator messages for Swedish. + +license: MIT-style license + +authors: + - Martin Lundgren + +requires: + - Locale + +provides: [Locale.sv-SE.Form.Validator] + +... +*/ + +Locale.define('sv-SE', 'FormValidator', { + + required: 'Fältet är obligatoriskt.', + minLength: 'Ange minst {minLength} tecken (du angav {length} tecken).', + maxLength: 'Ange högst {maxLength} tecken (du angav {length} tecken). ', + integer: 'Ange ett heltal i fältet. Tal med decimaler (t.ex. 1,25) är inte tillåtna.', + numeric: 'Ange endast numeriska värden i detta fält (t.ex. "1" eller "1.1" eller "-1" eller "-1,1").', + digits: 'Använd endast siffror och skiljetecken i detta fält (till exempel ett telefonnummer med bindestreck tillåtet).', + alpha: 'Använd endast bokstäver (a-ö) i detta fält. Inga mellanslag eller andra tecken är tillåtna.', + alphanum: 'Använd endast bokstäver (a-ö) och siffror (0-9) i detta fält. Inga mellanslag eller andra tecken är tillåtna.', + dateSuchAs: 'Ange ett giltigt datum som t.ex. {date}', + dateInFormatMDY: 'Ange ett giltigt datum som t.ex. YYYY-MM-DD (i.e. "1999-12-31")', + email: 'Ange en giltig e-postadress. Till exempel "erik@domain.com".', + url: 'Ange en giltig webbadress som http://www.example.com.', + currencyDollar: 'Ange en giltig belopp. Exempelvis 100,00.', + oneRequired: 'Vänligen ange minst ett av dessa alternativ.', + errorPrefix: 'Fel: ', + warningPrefix: 'Varning: ', + + // Form.Validator.Extras + noSpace: 'Det får inte finnas några mellanslag i detta fält.', + reqChkByNode: 'Inga objekt är valda.', + requiredChk: 'Detta är ett obligatoriskt fält.', + reqChkByName: 'Välj en {label}.', + match: 'Detta fält måste matcha {matchName}', + startDate: 'startdatumet', + endDate: 'slutdatum', + currentDate: 'dagens datum', + afterDate: 'Datumet bör vara samma eller senare än {label}.', + beforeDate: 'Datumet bör vara samma eller tidigare än {label}.', + startMonth: 'Välj en start månad', + sameMonth: 'Dessa två datum måste vara i samma månad - du måste ändra det ena eller det andra.' + +}); + +/* +--- + +name: Locale.sv-SE.Number + +description: Number messages for Swedish. + +license: MIT-style license + +authors: + - Arian Stolwijk + - Martin Lundgren + +requires: + - Locale + - Locale.EU.Number + +provides: [Locale.sv-SE.Number] + +... +*/ + +Locale.define('sv-SE', 'Number', { + + currency: { + prefix: 'SEK ' + } + +}).inherit('EU', 'Number'); + +/* +--- + +name: Locale.tr-TR.Date + +description: Date messages for Turkish. + +license: MIT-style license + +authors: + - Faruk Can Bilir + +requires: + - Locale + +provides: [Locale.tr-TR.Date] + +... +*/ + +Locale.define('tr-TR', 'Date', { + + months: ['Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık'], + months_abbr: ['Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz', 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara'], + days: ['Pazar', 'Pazartesi', 'Salı', 'Çarşamba', 'Perşembe', 'Cuma', 'Cumartesi'], + days_abbr: ['Pa', 'Pzt', 'Sa', 'Ça', 'Pe', 'Cu', 'Cmt'], + + // Culture's date order: MM/DD/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%H.%M', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: 'bir dakikadan önce', + minuteAgo: 'yaklaşık bir dakika önce', + minutesAgo: '{delta} dakika önce', + hourAgo: 'bir saat kadar önce', + hoursAgo: '{delta} saat kadar önce', + dayAgo: 'bir gün önce', + daysAgo: '{delta} gün önce', + weekAgo: 'bir hafta önce', + weeksAgo: '{delta} hafta önce', + monthAgo: 'bir ay önce', + monthsAgo: '{delta} ay önce', + yearAgo: 'bir yıl önce', + yearsAgo: '{delta} yıl önce', + + lessThanMinuteUntil: 'bir dakikadan az sonra', + minuteUntil: 'bir dakika kadar sonra', + minutesUntil: '{delta} dakika sonra', + hourUntil: 'bir saat kadar sonra', + hoursUntil: '{delta} saat kadar sonra', + dayUntil: 'bir gün sonra', + daysUntil: '{delta} gün sonra', + weekUntil: 'bir hafta sonra', + weeksUntil: '{delta} hafta sonra', + monthUntil: 'bir ay sonra', + monthsUntil: '{delta} ay sonra', + yearUntil: 'bir yıl sonra', + yearsUntil: '{delta} yıl sonra' + +}); + +/* +--- + +name: Locale.tr-TR.Form.Validator + +description: Form Validator messages for Turkish. + +license: MIT-style license + +authors: + - Faruk Can Bilir + +requires: + - Locale + +provides: [Locale.tr-TR.Form.Validator] + +... +*/ + +Locale.define('tr-TR', 'FormValidator', { + + required: 'Bu alan zorunlu.', + minLength: 'Lütfen en az {minLength} karakter girin (siz {length} karakter girdiniz).', + maxLength: 'Lütfen en fazla {maxLength} karakter girin (siz {length} karakter girdiniz).', + integer: 'Lütfen bu alana sadece tamsayı girin. Ondalıklı sayılar (ör: 1.25) kullanılamaz.', + numeric: 'Lütfen bu alana sadece sayısal değer girin (ör: "1", "1.1", "-1" ya da "-1.1").', + digits: 'Lütfen bu alana sadece sayısal değer ve noktalama işareti girin (örneğin, nokta ve tire içeren bir telefon numarası kullanılabilir).', + alpha: 'Lütfen bu alanda yalnızca harf kullanın. Boşluk ve diğer karakterler kullanılamaz.', + alphanum: 'Lütfen bu alanda sadece harf ve rakam kullanın. Boşluk ve diğer karakterler kullanılamaz.', + dateSuchAs: 'Lütfen geçerli bir tarih girin (Ör: {date})', + dateInFormatMDY: 'Lütfen geçerli bir tarih girin (GG/AA/YYYY, ör: "31/12/1999")', + email: 'Lütfen geçerli bir email adresi girin. Ör: "kemal@etikan.com".', + url: 'Lütfen geçerli bir URL girin. Ör: http://www.example.com.', + currencyDollar: 'Lütfen geçerli bir TL miktarı girin. Ör: 100,00 TL .', + oneRequired: 'Lütfen en az bir tanesini doldurun.', + errorPrefix: 'Hata: ', + warningPrefix: 'Uyarı: ', + + // Form.Validator.Extras + noSpace: 'Bu alanda boşluk kullanılamaz.', + reqChkByNode: 'Hiçbir öğe seçilmemiş.', + requiredChk: 'Bu alan zorunlu.', + reqChkByName: 'Lütfen bir {label} girin.', + match: 'Bu alan, {matchName} alanıyla uyuşmalı', + startDate: 'başlangıç tarihi', + endDate: 'bitiş tarihi', + currentDate: 'bugünün tarihi', + afterDate: 'Tarih, {label} tarihiyle aynı gün ya da ondan sonra olmalıdır.', + beforeDate: 'Tarih, {label} tarihiyle aynı gün ya da ondan önce olmalıdır.', + startMonth: 'Lütfen bir başlangıç ayı seçin', + sameMonth: 'Bu iki tarih aynı ayda olmalı - bir tanesini değiştirmeniz gerekiyor.', + creditcard: 'Girdiğiniz kredi kartı numarası geçersiz. Lütfen kontrol edip tekrar deneyin. {length} hane girildi.' + +}); + +/* +--- + +name: Locale.tr-TR.Number + +description: Number messages for Turkish. + +license: MIT-style license + +authors: + - Faruk Can Bilir + +requires: + - Locale + - Locale.EU.Number + +provides: [Locale.tr-TR.Number] + +... +*/ + +Locale.define('tr-TR', 'Number', { + + currency: { + decimals: 0, + suffix: ' TL' + } + +}).inherit('EU', 'Number'); + +/* +--- + +name: Locale.uk-UA.Date + +description: Date messages for Ukrainian (utf-8). + +license: MIT-style license + +authors: + - Slik + +requires: + - Locale + +provides: [Locale.uk-UA.Date] + +... +*/ + +(function(){ + +var pluralize = function(n, one, few, many, other){ + var d = (n / 10).toInt(), + z = n % 10, + s = (n / 100).toInt(); + + if (d == 1 && n > 10) return many; + if (z == 1) return one; + if (z > 0 && z < 5) return few; + return many; +}; + +Locale.define('uk-UA', 'Date', { + + months: ['Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'], + months_abbr: ['Січ', 'Лют', 'Бер', 'Квіт', 'Трав', 'Черв', 'Лип', 'Серп', 'Вер', 'Жовт', 'Лист', 'Груд' ], + days: ['Неділя', 'Понеділок', 'Вівторок', 'Середа', 'Четвер', "П'ятниця", 'Субота'], + days_abbr: ['Нд', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'], + + // Culture's date order: DD/MM/YYYY + dateOrder: ['date', 'month', 'year'], + shortDate: '%d/%m/%Y', + shortTime: '%H:%M', + AM: 'до полудня', + PM: 'по полудню', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: 'меньше хвилини тому', + minuteAgo: 'хвилину тому', + minutesAgo: function(delta){ return '{delta} ' + pluralize(delta, 'хвилину', 'хвилини', 'хвилин') + ' тому'; }, + hourAgo: 'годину тому', + hoursAgo: function(delta){ return '{delta} ' + pluralize(delta, 'годину', 'години', 'годин') + ' тому'; }, + dayAgo: 'вчора', + daysAgo: function(delta){ return '{delta} ' + pluralize(delta, 'день', 'дня', 'днів') + ' тому'; }, + weekAgo: 'тиждень тому', + weeksAgo: function(delta){ return '{delta} ' + pluralize(delta, 'тиждень', 'тижні', 'тижнів') + ' тому'; }, + monthAgo: 'місяць тому', + monthsAgo: function(delta){ return '{delta} ' + pluralize(delta, 'місяць', 'місяці', 'місяців') + ' тому'; }, + yearAgo: 'рік тому', + yearsAgo: function(delta){ return '{delta} ' + pluralize(delta, 'рік', 'роки', 'років') + ' тому'; }, + + lessThanMinuteUntil: 'за мить', + minuteUntil: 'через хвилину', + minutesUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'хвилину', 'хвилини', 'хвилин'); }, + hourUntil: 'через годину', + hoursUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'годину', 'години', 'годин'); }, + dayUntil: 'завтра', + daysUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'день', 'дня', 'днів'); }, + weekUntil: 'через тиждень', + weeksUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'тиждень', 'тижні', 'тижнів'); }, + monthUntil: 'через місяць', + monthesUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'місяць', 'місяці', 'місяців'); }, + yearUntil: 'через рік', + yearsUntil: function(delta){ return 'через {delta} ' + pluralize(delta, 'рік', 'роки', 'років'); } + +}); + +})(); + +/* +--- + +name: Locale.uk-UA.Form.Validator + +description: Form Validator messages for Ukrainian (utf-8). + +license: MIT-style license + +authors: + - Slik + +requires: + - Locale + +provides: [Locale.uk-UA.Form.Validator] + +... +*/ + +Locale.define('uk-UA', 'FormValidator', { + + required: 'Це поле повинне бути заповненим.', + minLength: 'Введіть хоча б {minLength} символів (Ви ввели {length}).', + maxLength: 'Кількість символів не може бути більше {maxLength} (Ви ввели {length}).', + integer: 'Введіть в це поле число. Дробові числа (наприклад 1.25) не дозволені.', + numeric: 'Введіть в це поле число (наприклад "1" або "1.1", або "-1", або "-1.1").', + digits: 'В цьому полі ви можете використовувати лише цифри і знаки пунктіації (наприклад, телефонний номер з знаками дефізу або з крапками).', + alpha: 'В цьому полі можна використовувати лише латинські літери (a-z). Пробіли і інші символи заборонені.', + alphanum: 'В цьому полі можна використовувати лише латинські літери (a-z) і цифри (0-9). Пробіли і інші символи заборонені.', + dateSuchAs: 'Введіть коректну дату {date}.', + dateInFormatMDY: 'Введіть дату в форматі ММ/ДД/РРРР (наприклад "12/31/2009").', + email: 'Введіть коректну адресу електронної пошти (наприклад "name@domain.com").', + url: 'Введіть коректне інтернет-посилання (наприклад http://www.example.com).', + currencyDollar: 'Введіть суму в доларах (наприклад "$100.00").', + oneRequired: 'Заповніть одне з полів.', + errorPrefix: 'Помилка: ', + warningPrefix: 'Увага: ', + + noSpace: 'Пробіли заборонені.', + reqChkByNode: 'Не відмічено жодного варіанту.', + requiredChk: 'Це поле повинне бути віміченим.', + reqChkByName: 'Будь ласка, відмітьте {label}.', + match: 'Це поле повинно відповідати {matchName}', + startDate: 'початкова дата', + endDate: 'кінцева дата', + currentDate: 'сьогоднішня дата', + afterDate: 'Ця дата повинна бути такою ж, або пізнішою за {label}.', + beforeDate: 'Ця дата повинна бути такою ж, або ранішою за {label}.', + startMonth: 'Будь ласка, виберіть початковий місяць', + sameMonth: 'Ці дати повинні відноситись одного і того ж місяця. Будь ласка, змініть одну з них.', + creditcard: 'Номер кредитної карти введений неправильно. Будь ласка, перевірте його. Введено {length} символів.' + +}); + +/* +--- + +name: Locale.zh-CH.Date + +description: Date messages for Chinese (simplified and traditional). + +license: MIT-style license + +authors: + - YMind Chan + +requires: + - Locale + +provides: [Locale.zh-CH.Date] + +... +*/ + +// Simplified Chinese +Locale.define('zh-CHS', 'Date', { + + months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + months_abbr: ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'], + days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + days_abbr: ['日', '一', '二', '三', '四', '五', '六'], + + // Culture's date order: YYYY-MM-DD + dateOrder: ['year', 'month', 'date'], + shortDate: '%Y-%m-%d', + shortTime: '%I:%M%p', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: '不到1分钟前', + minuteAgo: '大约1分钟前', + minutesAgo: '{delta}分钟之前', + hourAgo: '大约1小时前', + hoursAgo: '大约{delta}小时前', + dayAgo: '1天前', + daysAgo: '{delta}天前', + weekAgo: '1星期前', + weeksAgo: '{delta}星期前', + monthAgo: '1个月前', + monthsAgo: '{delta}个月前', + yearAgo: '1年前', + yearsAgo: '{delta}年前', + + lessThanMinuteUntil: '从现在开始不到1分钟', + minuteUntil: '从现在开始約1分钟', + minutesUntil: '从现在开始约{delta}分钟', + hourUntil: '从现在开始1小时', + hoursUntil: '从现在开始约{delta}小时', + dayUntil: '从现在开始1天', + daysUntil: '从现在开始{delta}天', + weekUntil: '从现在开始1星期', + weeksUntil: '从现在开始{delta}星期', + monthUntil: '从现在开始一个月', + monthsUntil: '从现在开始{delta}个月', + yearUntil: '从现在开始1年', + yearsUntil: '从现在开始{delta}年' + +}); + +// Traditional Chinese +Locale.define('zh-CHT', 'Date', { + + months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], + months_abbr: ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'], + days: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + days_abbr: ['日', '一', '二', '三', '四', '五', '六'], + + // Culture's date order: YYYY-MM-DD + dateOrder: ['year', 'month', 'date'], + shortDate: '%Y-%m-%d', + shortTime: '%I:%M%p', + AM: 'AM', + PM: 'PM', + firstDayOfWeek: 1, + + // Date.Extras + ordinal: '', + + lessThanMinuteAgo: '不到1分鐘前', + minuteAgo: '大約1分鐘前', + minutesAgo: '{delta}分鐘之前', + hourAgo: '大約1小時前', + hoursAgo: '大約{delta}小時前', + dayAgo: '1天前', + daysAgo: '{delta}天前', + weekAgo: '1星期前', + weeksAgo: '{delta}星期前', + monthAgo: '1个月前', + monthsAgo: '{delta}个月前', + yearAgo: '1年前', + yearsAgo: '{delta}年前', + + lessThanMinuteUntil: '從現在開始不到1分鐘', + minuteUntil: '從現在開始約1分鐘', + minutesUntil: '從現在開始約{delta}分鐘', + hourUntil: '從現在開始1小時', + hoursUntil: '從現在開始約{delta}小時', + dayUntil: '從現在開始1天', + daysUntil: '從現在開始{delta}天', + weekUntil: '從現在開始1星期', + weeksUntil: '從現在開始{delta}星期', + monthUntil: '從現在開始一個月', + monthsUntil: '從現在開始{delta}個月', + yearUntil: '從現在開始1年', + yearsUntil: '從現在開始{delta}年' + +}); + +/* +--- + +name: Locale.zh-CH.Form.Validator + +description: Form Validator messages for Chinese (simplified and traditional). + +license: MIT-style license + +authors: + - YMind Chan + +requires: + - Locale + - Form.Validator + +provides: [Form.zh-CH.Form.Validator, Form.Validator.CurrencyYuanValidator] + +... +*/ + +// Simplified Chinese +Locale.define('zh-CHS', 'FormValidator', { + + required: '此项必填。', + minLength: '请至少输入 {minLength} 个字符 (已输入 {length} 个)。', + maxLength: '最多只能输入 {maxLength} 个字符 (已输入 {length} 个)。', + integer: '请输入一个整数,不能包含小数点。例如:"1", "200"。', + numeric: '请输入一个数字,例如:"1", "1.1", "-1", "-1.1"。', + digits: '请输入由数字和标点符号组成的内容。例如电话号码。', + alpha: '请输入 A-Z 的 26 个字母,不能包含空格或任何其他字符。', + alphanum: '请输入 A-Z 的 26 个字母或 0-9 的 10 个数字,不能包含空格或任何其他字符。', + dateSuchAs: '请输入合法的日期格式,如:{date}。', + dateInFormatMDY: '请输入合法的日期格式,例如:YYYY-MM-DD ("2010-12-31")。', + email: '请输入合法的电子信箱地址,例如:"fred@domain.com"。', + url: '请输入合法的 Url 地址,例如:http://www.example.com。', + currencyDollar: '请输入合法的货币符号,例如:¥100.0', + oneRequired: '请至少选择一项。', + errorPrefix: '错误:', + warningPrefix: '警告:', + + // Form.Validator.Extras + noSpace: '不能包含空格。', + reqChkByNode: '未选择任何内容。', + requiredChk: '此项必填。', + reqChkByName: '请选择 {label}.', + match: '必须与{matchName}相匹配', + startDate: '起始日期', + endDate: '结束日期', + currentDate: '当前日期', + afterDate: '日期必须等于或晚于 {label}.', + beforeDate: '日期必须早于或等于 {label}.', + startMonth: '请选择起始月份', + sameMonth: '您必须修改两个日期中的一个,以确保它们在同一月份。', + creditcard: '您输入的信用卡号码不正确。当前已输入{length}个字符。' + +}); + +// Traditional Chinese +Locale.define('zh-CHT', 'FormValidator', { + + required: '此項必填。 ', + minLength: '請至少輸入{minLength} 個字符(已輸入{length} 個)。 ', + maxLength: '最多只能輸入{maxLength} 個字符(已輸入{length} 個)。 ', + integer: '請輸入一個整數,不能包含小數點。例如:"1", "200"。 ', + numeric: '請輸入一個數字,例如:"1", "1.1", "-1", "-1.1"。 ', + digits: '請輸入由數字和標點符號組成的內容。例如電話號碼。 ', + alpha: '請輸入AZ 的26 個字母,不能包含空格或任何其他字符。 ', + alphanum: '請輸入AZ 的26 個字母或0-9 的10 個數字,不能包含空格或任何其他字符。 ', + dateSuchAs: '請輸入合法的日期格式,如:{date}。 ', + dateInFormatMDY: '請輸入合法的日期格式,例如:YYYY-MM-DD ("2010-12-31")。 ', + email: '請輸入合法的電子信箱地址,例如:"fred@domain.com"。 ', + url: '請輸入合法的Url 地址,例如:http://www.example.com。 ', + currencyDollar: '請輸入合法的貨幣符號,例如:¥100.0', + oneRequired: '請至少選擇一項。 ', + errorPrefix: '錯誤:', + warningPrefix: '警告:', + + // Form.Validator.Extras + noSpace: '不能包含空格。 ', + reqChkByNode: '未選擇任何內容。 ', + requiredChk: '此項必填。 ', + reqChkByName: '請選擇 {label}.', + match: '必須與{matchName}相匹配', + startDate: '起始日期', + endDate: '結束日期', + currentDate: '當前日期', + afterDate: '日期必須等於或晚於{label}.', + beforeDate: '日期必須早於或等於{label}.', + startMonth: '請選擇起始月份', + sameMonth: '您必須修改兩個日期中的一個,以確保它們在同一月份。 ', + creditcard: '您輸入的信用卡號碼不正確。當前已輸入{length}個字符。 ' + +}); + +Form.Validator.add('validate-currency-yuan', { + + errorMsg: function(){ + return Form.Validator.getMsg('currencyYuan'); + }, + + test: function(element){ + // [¥]1[##][,###]+[.##] + // [¥]1###+[.##] + // [¥]0.## + // [¥].## + return Form.Validator.getValidator('IsEmpty').test(element) || (/^¥?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/).test(element.get('value')); + } + +}); + +/* +--- + +name: Locale.zh-CH.Number + +description: Number messages for for Chinese (simplified and traditional). + +license: MIT-style license + +authors: + - YMind Chan + +requires: + - Locale + - Locale.en-US.Number + +provides: [Locale.zh-CH.Number] + +... +*/ + +// Simplified Chinese +Locale.define('zh-CHS', 'Number', { + + currency: { + prefix: '¥ ' + } + +}).inherit('en-US', 'Number'); + +// Traditional Chinese +Locale.define('zh-CHT').inherit('zh-CHS', 'Number'); /* --- @@ -7680,6 +12750,57 @@ Request.JSONP = new Class({ Request.JSONP.counter = 0; Request.JSONP.request_map = {}; +/* +--- + +script: Request.Periodical.js + +name: Request.Periodical + +description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load + +license: MIT-style license + +authors: + - Christoph Pojer + +requires: + - Core/Request + - MooTools.More + +provides: [Request.Periodical] + +... +*/ + +Request.implement({ + + options: { + initialDelay: 5000, + delay: 5000, + limit: 60000 + }, + + startTimer: function(data){ + var fn = function(){ + if (!this.running) this.send({data: data}); + }; + this.lastDelay = this.options.initialDelay; + this.timer = fn.delay(this.lastDelay, this); + this.completeCheck = function(response){ + clearTimeout(this.timer); + this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit); + this.timer = fn.delay(this.lastDelay, this); + }; + return this.addEvent('complete', this.completeCheck); + }, + + stopTimer: function(){ + clearTimeout(this.timer); + return this.removeEvent('complete', this.completeCheck); + } + +}); /* --- @@ -7698,7 +12819,7 @@ authors: requires: - Core/Element - Core/Request - - /Class.Binds + - Class.Binds provides: [Request.Queue] @@ -7894,59 +13015,732 @@ Request.Queue = new Class({ }); - /* --- -script: Request.Periodical.js +script: Array.Extras.js -name: Request.Periodical +name: Array.Extras -description: Requests the same URL to pull data from a server but increases the intervals if no data is returned to reduce the load +description: Extends the Array native object to include useful methods to work with arrays. license: MIT-style license authors: - Christoph Pojer + - Sebastian Markbåge requires: - - Core/Request - - /MooTools.More + - Core/Array + - MooTools.More -provides: [Request.Periodical] +provides: [Array.Extras] ... */ -Request.implement({ +(function(nil){ - options: { - initialDelay: 5000, - delay: 5000, - limit: 60000 +Array.implement({ + + min: function(){ + return Math.min.apply(null, this); }, - startTimer: function(data){ - var fn = function(){ - if (!this.running) this.send({data: data}); - }; - this.lastDelay = this.options.initialDelay; - this.timer = fn.delay(this.lastDelay, this); - this.completeCheck = function(response){ - clearTimeout(this.timer); - this.lastDelay = (response) ? this.options.delay : (this.lastDelay + this.options.delay).min(this.options.limit); - this.timer = fn.delay(this.lastDelay, this); - }; - return this.addEvent('complete', this.completeCheck); + max: function(){ + return Math.max.apply(null, this); }, - stopTimer: function(){ - clearTimeout(this.timer); - return this.removeEvent('complete', this.completeCheck); + average: function(){ + return this.length ? this.sum() / this.length : 0; + }, + + sum: function(){ + var result = 0, l = this.length; + if (l){ + while (l--){ + if (this[l] != null) result += parseFloat(this[l]); + } + } + return result; + }, + + unique: function(){ + return [].combine(this); + }, + + shuffle: function(){ + for (var i = this.length; i && --i;){ + var temp = this[i], r = Math.floor(Math.random() * ( i + 1 )); + this[i] = this[r]; + this[r] = temp; + } + return this; + }, + + reduce: function(fn, value){ + for (var i = 0, l = this.length; i < l; i++){ + if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this); + } + return value; + }, + + reduceRight: function(fn, value){ + var i = this.length; + while (i--){ + if (i in this) value = value === nil ? this[i] : fn.call(null, value, this[i], i, this); + } + return value; + }, + + pluck: function(prop){ + return this.map(function(item){ + return item[prop]; + }); } }); +})(); + +/* +--- + +script: Date.Extras.js + +name: Date.Extras + +description: Extends the Date native object to include extra methods (on top of those in Date.js). + +license: MIT-style license + +authors: + - Aaron Newton + - Scott Kyle + +requires: + - Date + +provides: [Date.Extras] + +... +*/ + +Date.implement({ + + timeDiffInWords: function(to){ + return Date.distanceOfTimeInWords(this, to || new Date); + }, + + timeDiff: function(to, separator){ + if (to == null) to = new Date; + var delta = ((to - this) / 1000).floor().abs(); + + var vals = [], + durations = [60, 60, 24, 365, 0], + names = ['s', 'm', 'h', 'd', 'y'], + value, duration; + + for (var item = 0; item < durations.length; item++){ + if (item && !delta) break; + value = delta; + if ((duration = durations[item])){ + value = (delta % duration); + delta = (delta / duration).floor(); + } + vals.unshift(value + (names[item] || '')); + } + + return vals.join(separator || ':'); + } + +}).extend({ + + distanceOfTimeInWords: function(from, to){ + return Date.getTimePhrase(((to - from) / 1000).toInt()); + }, + + getTimePhrase: function(delta){ + var suffix = (delta < 0) ? 'Until' : 'Ago'; + if (delta < 0) delta *= -1; + + var units = { + minute: 60, + hour: 60, + day: 24, + week: 7, + month: 52 / 12, + year: 12, + eon: Infinity + }; + + var msg = 'lessThanMinute'; + + for (var unit in units){ + var interval = units[unit]; + if (delta < 1.5 * interval){ + if (delta > 0.75 * interval) msg = unit; + break; + } + delta /= interval; + msg = unit + 's'; + } + + delta = delta.round(); + return Date.getMsg(msg + suffix, delta).substitute({delta: delta}); + } + +}).defineParsers( + + { + // "today", "tomorrow", "yesterday" + re: /^(?:tod|tom|yes)/i, + handler: function(bits){ + var d = new Date().clearTime(); + switch (bits[0]){ + case 'tom': return d.increment(); + case 'yes': return d.decrement(); + default: return d; + } + } + }, + + { + // "next Wednesday", "last Thursday" + re: /^(next|last) ([a-z]+)$/i, + handler: function(bits){ + var d = new Date().clearTime(); + var day = d.getDay(); + var newDay = Date.parseDay(bits[2], true); + var addDays = newDay - day; + if (newDay <= day) addDays += 7; + if (bits[1] == 'last') addDays -= 7; + return d.set('date', d.getDate() + addDays); + } + } + +).alias('timeAgoInWords', 'timeDiffInWords'); + +/* +--- + +name: Hash + +description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects. + +license: MIT-style license. + +requires: + - Core/Object + - MooTools.More + +provides: [Hash] + +... +*/ + +(function(){ + +if (this.Hash) return; + +var Hash = this.Hash = new Type('Hash', function(object){ + if (typeOf(object) == 'hash') object = Object.clone(object.getClean()); + for (var key in object) this[key] = object[key]; + return this; +}); + +this.$H = function(object){ + return new Hash(object); +}; + +Hash.implement({ + + forEach: function(fn, bind){ + Object.forEach(this, fn, bind); + }, + + getClean: function(){ + var clean = {}; + for (var key in this){ + if (this.hasOwnProperty(key)) clean[key] = this[key]; + } + return clean; + }, + + getLength: function(){ + var length = 0; + for (var key in this){ + if (this.hasOwnProperty(key)) length++; + } + return length; + } + +}); + +Hash.alias('each', 'forEach'); + +Hash.implement({ + + has: Object.prototype.hasOwnProperty, + + keyOf: function(value){ + return Object.keyOf(this, value); + }, + + hasValue: function(value){ + return Object.contains(this, value); + }, + + extend: function(properties){ + Hash.each(properties || {}, function(value, key){ + Hash.set(this, key, value); + }, this); + return this; + }, + + combine: function(properties){ + Hash.each(properties || {}, function(value, key){ + Hash.include(this, key, value); + }, this); + return this; + }, + + erase: function(key){ + if (this.hasOwnProperty(key)) delete this[key]; + return this; + }, + + get: function(key){ + return (this.hasOwnProperty(key)) ? this[key] : null; + }, + + set: function(key, value){ + if (!this[key] || this.hasOwnProperty(key)) this[key] = value; + return this; + }, + + empty: function(){ + Hash.each(this, function(value, key){ + delete this[key]; + }, this); + return this; + }, + + include: function(key, value){ + if (this[key] == undefined) this[key] = value; + return this; + }, + + map: function(fn, bind){ + return new Hash(Object.map(this, fn, bind)); + }, + + filter: function(fn, bind){ + return new Hash(Object.filter(this, fn, bind)); + }, + + every: function(fn, bind){ + return Object.every(this, fn, bind); + }, + + some: function(fn, bind){ + return Object.some(this, fn, bind); + }, + + getKeys: function(){ + return Object.keys(this); + }, + + getValues: function(){ + return Object.values(this); + }, + + toQueryString: function(base){ + return Object.toQueryString(this, base); + } + +}); + +Hash.alias({indexOf: 'keyOf', contains: 'hasValue'}); + + +})(); + + +/* +--- + +script: Hash.Extras.js + +name: Hash.Extras + +description: Extends the Hash Type to include getFromPath which allows a path notation to child elements. + +license: MIT-style license + +authors: + - Aaron Newton + +requires: + - Hash + - Object.Extras + +provides: [Hash.Extras] + +... +*/ + +Hash.implement({ + + getFromPath: function(notation){ + return Object.getFromPath(this, notation); + }, + + cleanValues: function(method){ + return new Hash(Object.cleanValues(this, method)); + }, + + run: function(){ + Object.run(arguments); + } + +}); + +/* +--- +name: Number.Format +description: Extends the Number Type object to include a number formatting method. +license: MIT-style license +authors: [Arian Stolwijk] +requires: [Core/Number, Locale.en-US.Number] +# Number.Extras is for compatibility +provides: [Number.Format, Number.Extras] +... +*/ + + +Number.implement({ + + format: function(options){ + // Thanks dojo and YUI for some inspiration + var value = this; + options = options ? Object.clone(options) : {}; + var getOption = function(key){ + if (options[key] != null) return options[key]; + return Locale.get('Number.' + key); + }; + + var negative = value < 0, + decimal = getOption('decimal'), + precision = getOption('precision'), + group = getOption('group'), + decimals = getOption('decimals'); + + if (negative){ + var negativeLocale = getOption('negative') || {}; + if (negativeLocale.prefix == null && negativeLocale.suffix == null) negativeLocale.prefix = '-'; + ['prefix', 'suffix'].each(function(key){ + if (negativeLocale[key]) options[key] = getOption(key) + negativeLocale[key]; + }); + + value = -value; + } + + var prefix = getOption('prefix'), + suffix = getOption('suffix'); + + if (decimals !== '' && decimals >= 0 && decimals <= 20) value = value.toFixed(decimals); + if (precision >= 1 && precision <= 21) value = (+value).toPrecision(precision); + + value += ''; + var index; + if (getOption('scientific') === false && value.indexOf('e') > -1){ + var match = value.split('e'), + zeros = +match[1]; + value = match[0].replace('.', ''); + + if (zeros < 0){ + zeros = -zeros - 1; + index = match[0].indexOf('.'); + if (index > -1) zeros -= index - 1; + while (zeros--) value = '0' + value; + value = '0.' + value; + } else { + index = match[0].lastIndexOf('.'); + if (index > -1) zeros -= match[0].length - index - 1; + while (zeros--) value += '0'; + } + } + + if (decimal != '.') value = value.replace('.', decimal); + + if (group){ + index = value.lastIndexOf(decimal); + index = (index > -1) ? index : value.length; + var newOutput = value.substring(index), + i = index; + + while (i--){ + if ((index - i - 1) % 3 == 0 && i != (index - 1)) newOutput = group + newOutput; + newOutput = value.charAt(i) + newOutput; + } + + value = newOutput; + } + + if (prefix) value = prefix + value; + if (suffix) value += suffix; + + return value; + }, + + formatCurrency: function(decimals){ + var locale = Locale.get('Number.currency') || {}; + if (locale.scientific == null) locale.scientific = false; + locale.decimals = decimals != null ? decimals + : (locale.decimals == null ? 2 : locale.decimals); + + return this.format(locale); + }, + + formatPercentage: function(decimals){ + var locale = Locale.get('Number.percentage') || {}; + if (locale.suffix == null) locale.suffix = '%'; + locale.decimals = decimals != null ? decimals + : (locale.decimals == null ? 2 : locale.decimals); + + return this.format(locale); + } + +}); + +/* +--- + +script: URI.js + +name: URI + +description: Provides methods useful in managing the window location and uris. + +license: MIT-style license + +authors: + - Sebastian Markbåge + - Aaron Newton + +requires: + - Core/Object + - Core/Class + - Core/Class.Extras + - Core/Element + - String.QueryString + +provides: [URI] + +... +*/ + +(function(){ + +var toString = function(){ + return this.get('value'); +}; + +var URI = this.URI = new Class({ + + Implements: Options, + + options: { + /*base: false*/ + }, + + regex: /^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)?(\.\.?$|(?:[^?#\/]*\/)*)([^?#]*)(?:\?([^#]*))?(?:#(.*))?/, + parts: ['scheme', 'user', 'password', 'host', 'port', 'directory', 'file', 'query', 'fragment'], + schemes: {http: 80, https: 443, ftp: 21, rtsp: 554, mms: 1755, file: 0}, + + initialize: function(uri, options){ + this.setOptions(options); + var base = this.options.base || URI.base; + if (!uri) uri = base; + + if (uri && uri.parsed) this.parsed = Object.clone(uri.parsed); + else this.set('value', uri.href || uri.toString(), base ? new URI(base) : false); + }, + + parse: function(value, base){ + var bits = value.match(this.regex); + if (!bits) return false; + bits.shift(); + return this.merge(bits.associate(this.parts), base); + }, + + merge: function(bits, base){ + if ((!bits || !bits.scheme) && (!base || !base.scheme)) return false; + if (base){ + this.parts.every(function(part){ + if (bits[part]) return false; + bits[part] = base[part] || ''; + return true; + }); + } + bits.port = bits.port || this.schemes[bits.scheme.toLowerCase()]; + bits.directory = bits.directory ? this.parseDirectory(bits.directory, base ? base.directory : '') : '/'; + return bits; + }, + + parseDirectory: function(directory, baseDirectory){ + directory = (directory.substr(0, 1) == '/' ? '' : (baseDirectory || '/')) + directory; + if (!directory.test(URI.regs.directoryDot)) return directory; + var result = []; + directory.replace(URI.regs.endSlash, '').split('/').each(function(dir){ + if (dir == '..' && result.length > 0) result.pop(); + else if (dir != '.') result.push(dir); + }); + return result.join('/') + '/'; + }, + + combine: function(bits){ + return bits.value || bits.scheme + '://' + + (bits.user ? bits.user + (bits.password ? ':' + bits.password : '') + '@' : '') + + (bits.host || '') + (bits.port && bits.port != this.schemes[bits.scheme] ? ':' + bits.port : '') + + (bits.directory || '/') + (bits.file || '') + + (bits.query ? '?' + bits.query : '') + + (bits.fragment ? '#' + bits.fragment : ''); + }, + + set: function(part, value, base){ + if (part == 'value'){ + var scheme = value.match(URI.regs.scheme); + if (scheme) scheme = scheme[1]; + if (scheme && this.schemes[scheme.toLowerCase()] == null) this.parsed = { scheme: scheme, value: value }; + else this.parsed = this.parse(value, (base || this).parsed) || (scheme ? { scheme: scheme, value: value } : { value: value }); + } else if (part == 'data'){ + this.setData(value); + } else { + this.parsed[part] = value; + } + return this; + }, + + get: function(part, base){ + switch (part){ + case 'value': return this.combine(this.parsed, base ? base.parsed : false); + case 'data' : return this.getData(); + } + return this.parsed[part] || ''; + }, + + go: function(){ + document.location.href = this.toString(); + }, + + toURI: function(){ + return this; + }, + + getData: function(key, part){ + var qs = this.get(part || 'query'); + if (!(qs || qs === 0)) return key ? null : {}; + var obj = qs.parseQueryString(); + return key ? obj[key] : obj; + }, + + setData: function(values, merge, part){ + if (typeof values == 'string'){ + var data = this.getData(); + data[arguments[0]] = arguments[1]; + values = data; + } else if (merge){ + values = Object.merge(this.getData(null, part), values); + } + return this.set(part || 'query', Object.toQueryString(values)); + }, + + clearData: function(part){ + return this.set(part || 'query', ''); + }, + + toString: toString, + valueOf: toString + +}); + +URI.regs = { + endSlash: /\/$/, + scheme: /^(\w+):/, + directoryDot: /\.\/|\.$/ +}; + +URI.base = new URI(Array.from(document.getElements('base[href]', true)).getLast(), {base: document.location}); + +String.implement({ + + toURI: function(options){ + return new URI(this, options); + } + +}); + +})(); + +/* +--- + +script: URI.Relative.js + +name: URI.Relative + +description: Extends the URI class to add methods for computing relative and absolute urls. + +license: MIT-style license + +authors: + - Sebastian Markbåge + + +requires: + - Class.refactor + - URI + +provides: [URI.Relative] + +... +*/ + +URI = Class.refactor(URI, { + + combine: function(bits, base){ + if (!base || bits.scheme != base.scheme || bits.host != base.host || bits.port != base.port) + return this.previous.apply(this, arguments); + var end = bits.file + (bits.query ? '?' + bits.query : '') + (bits.fragment ? '#' + bits.fragment : ''); + + if (!base.directory) return (bits.directory || (bits.file ? '' : './')) + end; + + var baseDir = base.directory.split('/'), + relDir = bits.directory.split('/'), + path = '', + offset; + + var i = 0; + for (offset = 0; offset < baseDir.length && offset < relDir.length && baseDir[offset] == relDir[offset]; offset++); + for (i = 0; i < baseDir.length - offset - 1; i++) path += '../'; + for (i = offset; i < relDir.length - 1; i++) path += relDir[i] + '/'; + + return (path || (bits.file ? '' : './')) + end; + }, + + toAbsolute: function(base){ + base = new URI(base); + if (base) base.set('directory', '').set('file', ''); + return this.toRelative(base); + }, + + toRelative: function(base){ + return this.get('value', new URI(base)); + } + +}); /* --- @@ -7964,7 +13758,7 @@ authors: requires: - Core/Element.Event - - /MooTools.More + - MooTools.More provides: [Assets] @@ -7978,44 +13772,62 @@ var Asset = { var script = new Element('script', {src: source, type: 'text/javascript'}), doc = properties.document || document, - loaded = 0, - loadEvent = properties.onload || properties.onLoad; - - var load = loadEvent ? function(){ // make sure we only call the event once - if (++loaded == 1) loadEvent.call(this); - } : function(){}; + load = properties.onload || properties.onLoad; delete properties.onload; delete properties.onLoad; delete properties.document; - return script.addEvents({ - load: load, - readystatechange: function(){ - if (['loaded', 'complete'].contains(this.readyState)) load.call(this); + if (load){ + if (!script.addEventListener){ + script.addEvent('readystatechange', function(){ + if (['loaded', 'complete'].contains(this.readyState)) load.call(this); + }); + } else { + script.addEvent('load', load); } - }).set(properties).inject(doc.head); + } + + return script.set(properties).inject(doc.head); }, css: function(source, properties){ if (!properties) properties = {}; - var link = new Element('link', { - rel: 'stylesheet', - media: 'screen', - type: 'text/css', - href: source + var load = properties.onload || properties.onLoad, + doc = properties.document || document, + timeout = properties.timeout || 3000; + + ['onload', 'onLoad', 'document'].each(function(prop){ + delete properties[prop]; }); - var load = properties.onload || properties.onLoad, - doc = properties.document || document; + var link = new Element('link', { + type: 'text/css', + rel: 'stylesheet', + media: 'screen', + href: source + }).setProperties(properties).inject(doc.head); - delete properties.onload; - delete properties.onLoad; - delete properties.document; - - if (load) link.addEvent('load', load); - return link.set(properties).inject(doc.head); + if (load){ + // based on article at http://www.yearofmoo.com/2011/03/cross-browser-stylesheet-preloading.html + var loaded = false, retries = 0; + var check = function(){ + var stylesheets = document.styleSheets; + for (var i = 0; i < stylesheets.length; i++){ + var file = stylesheets[i]; + var owner = file.ownerNode ? file.ownerNode : file.owningElement; + if (owner && owner == link){ + loaded = true; + return load.call(link); + } + } + retries++; + if (!loaded && retries < timeout / 50) return setTimeout(check, 50); + } + setTimeout(check, 0); + } + return link; }, image: function(source, properties){ @@ -8080,7 +13892,6 @@ var Asset = { }; - /* --- @@ -8244,7 +14055,6 @@ String.implement({ })(); - /* --- @@ -8261,7 +14071,7 @@ authors: requires: - Core/Events - - /MooTools.More + - MooTools.More provides: [Group] @@ -8274,39 +14084,32 @@ this.Group = new Class({ initialize: function(){ this.instances = Array.flatten(arguments); - this.events = {}; - this.checker = {}; }, addEvent: function(type, fn){ - this.checker[type] = this.checker[type] || {}; - this.events[type] = this.events[type] || []; - if (this.events[type].contains(fn)) return false; - else this.events[type].push(fn); - this.instances.each(function(instance, i){ - instance.addEvent(type, this.check.pass([type, instance, i], this)); - }, this); - return this; - }, + var instances = this.instances, + len = instances.length, + togo = len, + args = new Array(len), + self = this; - check: function(type, instance, i){ - this.checker[type][i] = true; - var every = this.instances.every(function(current, j){ - return this.checker[type][j] || false; - }, this); - if (!every) return; - this.checker[type] = {}; - this.events[type].each(function(event){ - event.call(this, this.instances, instance); - }, this); + instances.each(function(instance, i){ + instance.addEvent(type, function(){ + if (!args[i]) togo--; + args[i] = arguments; + if (!togo){ + fn.call(self, instances, instance, args); + togo = len; + args = new Array(len); + } + }); + }); } }); })(); - - /* --- @@ -8325,8 +14128,8 @@ authors: requires: - Core/Cookie - Core/JSON - - /MooTools.More - - /Hash + - MooTools.More + - Hash provides: [Hash.Cookie] @@ -8369,6 +14172,119 @@ Hash.each(Hash.prototype, function(method, name){ }); }); +/* +--- + +name: Swiff + +description: Wrapper for embedding SWF movies. Supports External Interface Communication. + +license: MIT-style license. + +credits: + - Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject. + +requires: [Core/Options, Core/Object, Core/Element] + +provides: Swiff + +... +*/ + +(function(){ + +var Swiff = this.Swiff = new Class({ + + Implements: Options, + + options: { + id: null, + height: 1, + width: 1, + container: null, + properties: {}, + params: { + quality: 'high', + allowScriptAccess: 'always', + wMode: 'window', + swLiveConnect: true + }, + callBacks: {}, + vars: {} + }, + + toElement: function(){ + return this.object; + }, + + initialize: function(path, options){ + this.instance = 'Swiff_' + String.uniqueID(); + + this.setOptions(options); + options = this.options; + var id = this.id = options.id || this.instance; + var container = document.id(options.container); + + Swiff.CallBacks[this.instance] = {}; + + var params = options.params, vars = options.vars, callBacks = options.callBacks; + var properties = Object.append({height: options.height, width: options.width}, options.properties); + + var self = this; + + for (var callBack in callBacks){ + Swiff.CallBacks[this.instance][callBack] = (function(option){ + return function(){ + return option.apply(self.object, arguments); + }; + })(callBacks[callBack]); + vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack; + } + + params.flashVars = Object.toQueryString(vars); + if ('ActiveXObject' in window){ + properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000'; + params.movie = path; + } else { + properties.type = 'application/x-shockwave-flash'; + } + properties.data = path; + + var build = ''; + } + build += ''; + this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild; + }, + + replaces: function(element){ + element = document.id(element, true); + element.parentNode.replaceChild(this.toElement(), element); + return this; + }, + + inject: function(element){ + document.id(element, true).appendChild(this.toElement()); + return this; + }, + + remote: function(){ + return Swiff.remote.apply(Swiff, [this.toElement()].append(arguments)); + } + +}); + +Swiff.CallBacks = {}; + +Swiff.remote = function(obj, fn){ + var rs = obj.CallFunction('' + __flash__argumentsToXML(arguments, 2) + ''); + return eval(rs); +}; + +})(); /* --- @@ -8427,1616 +14343,3 @@ var Table = this.Table = function(){ if (this.Type) new Type('Table', Table); })(); - - -/* ---- - -script: HtmlTable.js - -name: HtmlTable - -description: Builds table elements with methods to add rows. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - Core/Options - - Core/Events - - /Class.Occlude - -provides: [HtmlTable] - -... -*/ - -var HtmlTable = new Class({ - - Implements: [Options, Events, Class.Occlude], - - options: { - properties: { - cellpadding: 0, - cellspacing: 0, - border: 0 - }, - rows: [], - headers: [], - footers: [] - }, - - property: 'HtmlTable', - - initialize: function(){ - var params = Array.link(arguments, {options: Type.isObject, table: Type.isElement, id: Type.isString}); - this.setOptions(params.options); - if (!params.table && params.id) params.table = document.id(params.id); - this.element = params.table || new Element('table', this.options.properties); - if (this.occlude()) return this.occluded; - this.build(); - }, - - build: function(){ - this.element.store('HtmlTable', this); - - this.body = document.id(this.element.tBodies[0]) || new Element('tbody').inject(this.element); - $$(this.body.rows); - - if (this.options.headers.length) this.setHeaders(this.options.headers); - else this.thead = document.id(this.element.tHead); - - if (this.thead) this.head = this.getHead(); - if (this.options.footers.length) this.setFooters(this.options.footers); - - this.tfoot = document.id(this.element.tFoot); - if (this.tfoot) this.foot = document.id(this.tfoot.rows[0]); - - this.options.rows.each(function(row){ - this.push(row); - }, this); - }, - - toElement: function(){ - return this.element; - }, - - empty: function(){ - this.body.empty(); - return this; - }, - - set: function(what, items){ - var target = (what == 'headers') ? 'tHead' : 'tFoot', - lower = target.toLowerCase(); - - this[lower] = (document.id(this.element[target]) || new Element(lower).inject(this.element, 'top')).empty(); - var data = this.push(items, {}, this[lower], what == 'headers' ? 'th' : 'td'); - - if (what == 'headers') this.head = this.getHead(); - else this.foot = this.getHead(); - - return data; - }, - - getHead: function(){ - var rows = this.thead.rows; - return rows.length > 1 ? $$(rows) : rows.length ? document.id(rows[0]) : false; - }, - - setHeaders: function(headers){ - this.set('headers', headers); - return this; - }, - - setFooters: function(footers){ - this.set('footers', footers); - return this; - }, - - push: function(row, rowProperties, target, tag, where){ - if (typeOf(row) == 'element' && row.get('tag') == 'tr'){ - row.inject(target || this.body, where); - return { - tr: row, - tds: row.getChildren('td') - }; - } - - var tds = row.map(function(data){ - var td = new Element(tag || 'td', data ? data.properties : {}), - content = (data ? data.content : '') || data, - type = typeOf(content); - - if (['element', 'array', 'collection', 'elements'].contains(type)) td.adopt(content); - else td.set('html', content); - - return td; - }); - - return { - tr: new Element('tr', rowProperties).inject(target || this.body, where).adopt(tds), - tds: tds - }; - } - -}); - - -['adopt', 'inject', 'wraps', 'grab', 'replaces', 'dispose'].each(function(method){ - HtmlTable.implement(method, function(){ - this.element[method].apply(this.element, arguments); - return this; - }); -}); - - - - -/* ---- - -script: HtmlTable.Zebra.js - -name: HtmlTable.Zebra - -description: Builds a stripy table with methods to add rows. - -license: MIT-style license - -authors: - - Harald Kirschner - - Aaron Newton - -requires: - - /HtmlTable - - /Class.refactor - -provides: [HtmlTable.Zebra] - -... -*/ - -HtmlTable = Class.refactor(HtmlTable, { - - options: { - classZebra: 'table-tr-odd', - zebra: true - }, - - initialize: function(){ - this.previous.apply(this, arguments); - if (this.occluded) return this.occluded; - if (this.options.zebra) this.updateZebras(); - }, - - updateZebras: function(){ - Array.each(this.body.rows, this.zebra, this); - }, - - setRowStyle: function(row, i){ - if (this.previous) this.previous(row, i); - this.zebra(row, i); - }, - - zebra: function(row, i){ - return row[((i % 2) ? 'remove' : 'add')+'Class'](this.options.classZebra); - }, - - push: function(){ - var pushed = this.previous.apply(this, arguments); - if (this.options.zebra) this.updateZebras(); - return pushed; - } - -}); - - -/* ---- - -script: HtmlTable.Sort.js - -name: HtmlTable.Sort - -description: Builds a stripy, sortable table with methods to add rows. - -license: MIT-style license - -authors: - - Harald Kirschner - - Aaron Newton - - Jacob Thornton - -requires: - - Core/Hash - - /HtmlTable - - /Class.refactor - - /Element.Delegation - - /String.Extras - - /Date - -provides: [HtmlTable.Sort] - -... -*/ - -HtmlTable = Class.refactor(HtmlTable, { - - options: {/* - onSort: function(){}, */ - sortIndex: 0, - sortReverse: false, - parsers: [], - defaultParser: 'string', - classSortable: 'table-sortable', - classHeadSort: 'table-th-sort', - classHeadSortRev: 'table-th-sort-rev', - classNoSort: 'table-th-nosort', - classGroupHead: 'table-tr-group-head', - classGroup: 'table-tr-group', - classCellSort: 'table-td-sort', - classSortSpan: 'table-th-sort-span', - sortable: false, - thSelector: 'th' - }, - - initialize: function (){ - this.previous.apply(this, arguments); - if (this.occluded) return this.occluded; - this.sorted = {index: null, dir: 1}; - this.bound = { - headClick: this.headClick.bind(this) - }; - this.sortSpans = new Elements(); - if (this.options.sortable){ - this.enableSort(); - if (this.options.sortIndex != null) this.sort(this.options.sortIndex, this.options.sortReverse); - } - }, - - attachSorts: function(attach){ - this.detachSorts(); - if (attach !== false) this.element.addEvent('click:relay(' + this.options.thSelector + ')', this.bound.headClick); - }, - - detachSorts: function(){ - this.element.removeEvents('click:relay(' + this.options.thSelector + ')'); - }, - - setHeaders: function(){ - this.previous.apply(this, arguments); - if (this.sortEnabled) this.setParsers(); - }, - - setParsers: function(){ - this.parsers = this.detectParsers(); - }, - - detectParsers: function(){ - return this.head && this.head.getElements(this.options.thSelector).flatten().map(this.detectParser, this); - }, - - detectParser: function(cell, index){ - if (cell.hasClass(this.options.classNoSort) || cell.retrieve('htmltable-parser')) return cell.retrieve('htmltable-parser'); - var thDiv = new Element('div'); - thDiv.adopt(cell.childNodes).inject(cell); - var sortSpan = new Element('span', {'class': this.options.classSortSpan}).inject(thDiv, 'top'); - this.sortSpans.push(sortSpan); - var parser = this.options.parsers[index], - rows = this.body.rows, - cancel; - switch (typeOf(parser)){ - case 'function': parser = {convert: parser}; cancel = true; break; - case 'string': parser = parser; cancel = true; break; - } - if (!cancel){ - HtmlTable.ParserPriority.some(function(parserName){ - var current = HtmlTable.Parsers[parserName], - match = current.match; - if (!match) return false; - for (var i = 0, j = rows.length; i < j; i++){ - var cell = document.id(rows[i].cells[index]), - text = cell ? cell.get('html').clean() : ''; - if (text && match.test(text)){ - parser = current; - return true; - } - } - }); - } - if (!parser) parser = this.options.defaultParser; - cell.store('htmltable-parser', parser); - return parser; - }, - - headClick: function(event, el){ - if (!this.head || el.hasClass(this.options.classNoSort)) return; - return this.sort(Array.indexOf(this.head.getElements(this.options.thSelector).flatten(), el) % this.body.rows[0].cells.length); - }, - - serialize: function() { - var previousSerialization = this.previous.apply(this, arguments) || {}; - if (this.options.sortable) { - previousSerialization.sortIndex = this.sorted.index; - previousSerialization.sortReverse = this.sorted.reverse; - } - return previousSerialization; - }, - - restore: function(tableState) { - if(this.options.sortable && tableState.sortIndex) { - this.sort(tableState.sortIndex, tableState.sortReverse); - } - this.previous.apply(this, arguments); - }, - - setSortedState: function(index, reverse){ - if (reverse != null) this.sorted.reverse = reverse; - else if (this.sorted.index == index) this.sorted.reverse = !this.sorted.reverse; - else this.sorted.reverse = this.sorted.index == null; - - if (index != null) this.sorted.index = index; - }, - - setHeadSort: function(sorted){ - var head = $$(!this.head.length ? this.head.cells[this.sorted.index] : this.head.map(function(row){ - return row.getElements(this.options.thSelector)[this.sorted.index]; - }, this).clean()); - if (!head.length) return; - if (sorted){ - head.addClass(this.options.classHeadSort); - if (this.sorted.reverse) head.addClass(this.options.classHeadSortRev); - else head.removeClass(this.options.classHeadSortRev); - } else { - head.removeClass(this.options.classHeadSort).removeClass(this.options.classHeadSortRev); - } - }, - - setRowSort: function(data, pre){ - var count = data.length, - body = this.body, - group, - rowIndex; - - while (count){ - var item = data[--count], - position = item.position, - row = body.rows[position]; - - if (row.disabled) continue; - if (!pre){ - group = this.setGroupSort(group, row, item); - this.setRowStyle(row, count); - } - body.appendChild(row); - - for (rowIndex = 0; rowIndex < count; rowIndex++){ - if (data[rowIndex].position > position) data[rowIndex].position--; - } - } - }, - - setRowStyle: function(row, i){ - this.previous(row, i); - row.cells[this.sorted.index].addClass(this.options.classCellSort); - }, - - setGroupSort: function(group, row, item){ - if (group == item.value) row.removeClass(this.options.classGroupHead).addClass(this.options.classGroup); - else row.removeClass(this.options.classGroup).addClass(this.options.classGroupHead); - return item.value; - }, - - getParser: function(){ - var parser = this.parsers[this.sorted.index]; - return typeOf(parser) == 'string' ? HtmlTable.Parsers[parser] : parser; - }, - - sort: function(index, reverse, pre){ - if (!this.head) return; - - if (!pre){ - this.clearSort(); - this.setSortedState(index, reverse); - this.setHeadSort(true); - } - - var parser = this.getParser(); - if (!parser) return; - - var rel; - if (!Browser.ie){ - rel = this.body.getParent(); - this.body.dispose(); - } - - var data = this.parseData(parser).sort(function(a, b){ - if (a.value === b.value) return 0; - return a.value > b.value ? 1 : -1; - }); - - if (this.sorted.reverse == (parser == HtmlTable.Parsers['input-checked'])) data.reverse(true); - this.setRowSort(data, pre); - - if (rel) rel.grab(this.body); - this.fireEvent('stateChanged'); - return this.fireEvent('sort', [this.body, this.sorted.index]); - }, - - parseData: function(parser){ - return Array.map(this.body.rows, function(row, i){ - var value = parser.convert.call(document.id(row.cells[this.sorted.index])); - return { - position: i, - value: value - }; - }, this); - }, - - clearSort: function(){ - this.setHeadSort(false); - this.body.getElements('td').removeClass(this.options.classCellSort); - }, - - reSort: function(){ - if (this.sortEnabled) this.sort.call(this, this.sorted.index, this.sorted.reverse); - return this; - }, - - enableSort: function(){ - this.element.addClass(this.options.classSortable); - this.attachSorts(true); - this.setParsers(); - this.sortEnabled = true; - return this; - }, - - disableSort: function(){ - this.element.removeClass(this.options.classSortable); - this.attachSorts(false); - this.sortSpans.each(function(span){ - span.destroy(); - }); - this.sortSpans.empty(); - this.sortEnabled = false; - return this; - } - -}); - -HtmlTable.ParserPriority = ['date', 'input-checked', 'input-value', 'float', 'number']; - -HtmlTable.Parsers = { - - 'date': { - match: /^\d{2}[-\/ ]\d{2}[-\/ ]\d{2,4}$/, - convert: function(){ - var d = Date.parse(this.get('text').stripTags()); - return (typeOf(d) == 'date') ? d.format('db') : ''; - }, - type: 'date' - }, - 'input-checked': { - match: / type="(radio|checkbox)" /, - convert: function(){ - return this.getElement('input').checked; - } - }, - 'input-value': { - match: /= rows.length) index = null; - - return index; - }, - - attachSelects: function(attach){ - attach = attach != null ? attach : true; - - var method = attach ? 'addEvents' : 'removeEvents'; - this.element[method]({ - mouseleave: this.bound.mouseleave, - click: this.bound.activateKeyboard - }); - - this.body[method]({ - 'click:relay(tr)': this.bound.clickRow, - 'contextmenu:relay(tr)': this.bound.clickRow - }); - - if (this.options.useKeyboard || this.keyboard){ - if (!this.keyboard) this.keyboard = new Keyboard(); - if (!this.selectKeysDefined) { - this.selectKeysDefined = true; - var timer, held; - - var move = function(offset){ - var mover = function(e){ - clearTimeout(timer); - e.preventDefault(); - - var to = this.body.rows[this.getRowByOffset(offset)]; - if (e.shift && to && this.isSelected(to)){ - this.deselectRow(this.focused); - this.focused = to; - } else { - if (to && (!this.options.allowMultiSelect || !e.shift)){ - this.selectNone(); - } - this.shiftFocus(offset, e); - } - - if (held){ - timer = mover.delay(100, this, e); - } else { - timer = (function(){ - held = true; - mover(e); - }).delay(400); - } - }.bind(this); - return mover; - }.bind(this); - - var clear = function(){ - clearTimeout(timer); - held = false; - }; - - this.keyboard.addEvents({ - 'keydown:shift+up': move(-1), - 'keydown:shift+down': move(1), - 'keyup:shift+up': clear, - 'keyup:shift+down': clear, - 'keyup:up': clear, - 'keyup:down': clear - }); - - var shiftHint = ''; - if (this.options.allowMultiSelect && this.options.shiftForMultiSelect && this.options.useKeyboard){ - shiftHint = " (Shift multi-selects)."; - } - - this.keyboard.addShortcuts({ - 'Select Previous Row': { - keys: 'up', - shortcut: 'up arrow', - handler: move(-1), - description: 'Select the previous row in the table.' + shiftHint - }, - 'Select Next Row': { - keys: 'down', - shortcut: 'down arrow', - handler: move(1), - description: 'Select the next row in the table.' + shiftHint - } - }); - - } - this.keyboard[attach ? 'activate' : 'deactivate'](); - } - this.updateSelects(); - }, - - mouseleave: function(){ - if (this.hovered) this.leaveRow(this.hovered); - } - -}); - - -/* ---- - -script: Scroller.js - -name: Scroller - -description: Class which scrolls the contents of any Element (including the window) when the mouse reaches the Element's boundaries. - -license: MIT-style license - -authors: - - Valerio Proietti - -requires: - - Core/Events - - Core/Options - - Core/Element.Event - - Core/Element.Dimensions - - MooTools.More - -provides: [Scroller] - -... -*/ - -var Scroller = new Class({ - - Implements: [Events, Options], - - options: { - area: 20, - velocity: 1, - onChange: function(x, y){ - this.element.scrollTo(x, y); - }, - fps: 50 - }, - - initialize: function(element, options){ - this.setOptions(options); - this.element = document.id(element); - this.docBody = document.id(this.element.getDocument().body); - this.listener = (typeOf(this.element) != 'element') ? this.docBody : this.element; - this.timer = null; - this.bound = { - attach: this.attach.bind(this), - detach: this.detach.bind(this), - getCoords: this.getCoords.bind(this) - }; - }, - - start: function(){ - this.listener.addEvents({ - mouseover: this.bound.attach, - mouseleave: this.bound.detach - }); - return this; - }, - - stop: function(){ - this.listener.removeEvents({ - mouseover: this.bound.attach, - mouseleave: this.bound.detach - }); - this.detach(); - this.timer = clearInterval(this.timer); - return this; - }, - - attach: function(){ - this.listener.addEvent('mousemove', this.bound.getCoords); - }, - - detach: function(){ - this.listener.removeEvent('mousemove', this.bound.getCoords); - this.timer = clearInterval(this.timer); - }, - - getCoords: function(event){ - this.page = (this.listener.get('tag') == 'body') ? event.client : event.page; - if (!this.timer) this.timer = this.scroll.periodical(Math.round(1000 / this.options.fps), this); - }, - - scroll: function(){ - var size = this.element.getSize(), - scroll = this.element.getScroll(), - pos = this.element != this.docBody ? this.element.getOffsets() : {x: 0, y:0}, - scrollSize = this.element.getScrollSize(), - change = {x: 0, y: 0}, - top = this.options.area.top || this.options.area, - bottom = this.options.area.bottom || this.options.area; - for (var z in this.page){ - if (this.page[z] < (top + pos[z]) && scroll[z] != 0){ - change[z] = (this.page[z] - top - pos[z]) * this.options.velocity; - } else if (this.page[z] + bottom > (size[z] + pos[z]) && scroll[z] + size[z] != scrollSize[z]){ - change[z] = (this.page[z] - size[z] + bottom - pos[z]) * this.options.velocity; - } - change[z] = change[z].round(); - } - if (change.y || change.x) this.fireEvent('change', [scroll.x + change.x, scroll.y + change.y]); - } - -}); - - -/* ---- - -script: Tips.js - -name: Tips - -description: Class for creating nice tips that follow the mouse cursor when hovering an element. - -license: MIT-style license - -authors: - - Valerio Proietti - - Christoph Pojer - - Luis Merino - -requires: - - Core/Options - - Core/Events - - Core/Element.Event - - Core/Element.Style - - Core/Element.Dimensions - - /MooTools.More - -provides: [Tips] - -... -*/ - -(function(){ - -var read = function(option, element){ - return (option) ? (typeOf(option) == 'function' ? option(element) : element.get(option)) : ''; -}; - -this.Tips = new Class({ - - Implements: [Events, Options], - - options: {/* - onAttach: function(element){}, - onDetach: function(element){}, - onBound: function(coords){},*/ - onShow: function(){ - this.tip.setStyle('display', 'block'); - }, - onHide: function(){ - this.tip.setStyle('display', 'none'); - }, - title: 'title', - text: function(element){ - return element.get('rel') || element.get('href'); - }, - showDelay: 100, - hideDelay: 100, - className: 'tip-wrap', - offset: {x: 16, y: 16}, - windowPadding: {x:0, y:0}, - fixed: false - }, - - initialize: function(){ - var params = Array.link(arguments, { - options: Type.isObject, - elements: function(obj){ - return obj != null; - } - }); - this.setOptions(params.options); - if (params.elements) this.attach(params.elements); - this.container = new Element('div', {'class': 'tip'}); - }, - - toElement: function(){ - if (this.tip) return this.tip; - - this.tip = new Element('div', { - 'class': this.options.className, - styles: { - position: 'absolute', - top: 0, - left: 0 - } - }).adopt( - new Element('div', {'class': 'tip-top'}), - this.container, - new Element('div', {'class': 'tip-bottom'}) - ); - - return this.tip; - }, - - attach: function(elements){ - $$(elements).each(function(element){ - var title = read(this.options.title, element), - text = read(this.options.text, element); - - element.set('title', '').store('tip:native', title).retrieve('tip:title', title); - element.retrieve('tip:text', text); - this.fireEvent('attach', [element]); - - var events = ['enter', 'leave']; - if (!this.options.fixed) events.push('move'); - - events.each(function(value){ - var event = element.retrieve('tip:' + value); - if (!event) event = function(event){ - this['element' + value.capitalize()].apply(this, [event, element]); - }.bind(this); - - element.store('tip:' + value, event).addEvent('mouse' + value, event); - }, this); - }, this); - - return this; - }, - - detach: function(elements){ - $$(elements).each(function(element){ - ['enter', 'leave', 'move'].each(function(value){ - element.removeEvent('mouse' + value, element.retrieve('tip:' + value)).eliminate('tip:' + value); - }); - - this.fireEvent('detach', [element]); - - if (this.options.title == 'title'){ // This is necessary to check if we can revert the title - var original = element.retrieve('tip:native'); - if (original) element.set('title', original); - } - }, this); - - return this; - }, - - elementEnter: function(event, element){ - clearTimeout(this.timer); - this.timer = (function(){ - this.container.empty(); - - ['title', 'text'].each(function(value){ - var content = element.retrieve('tip:' + value); - var div = this['_' + value + 'Element'] = new Element('div', { - 'class': 'tip-' + value - }).inject(this.container); - if (content) this.fill(div, content); - }, this); - this.show(element); - this.position((this.options.fixed) ? {page: element.getPosition()} : event); - }).delay(this.options.showDelay, this); - }, - - elementLeave: function(event, element){ - clearTimeout(this.timer); - this.timer = this.hide.delay(this.options.hideDelay, this, element); - this.fireForParent(event, element); - }, - - setTitle: function(title){ - if (this._titleElement){ - this._titleElement.empty(); - this.fill(this._titleElement, title); - } - return this; - }, - - setText: function(text){ - if (this._textElement){ - this._textElement.empty(); - this.fill(this._textElement, text); - } - return this; - }, - - fireForParent: function(event, element){ - element = element.getParent(); - if (!element || element == document.body) return; - if (element.retrieve('tip:enter')) element.fireEvent('mouseenter', event); - else this.fireForParent(event, element); - }, - - elementMove: function(event, element){ - this.position(event); - }, - - position: function(event){ - if (!this.tip) document.id(this); - - var size = window.getSize(), scroll = window.getScroll(), - tip = {x: this.tip.offsetWidth, y: this.tip.offsetHeight}, - props = {x: 'left', y: 'top'}, - bounds = {y: false, x2: false, y2: false, x: false}, - obj = {}; - - for (var z in props){ - obj[props[z]] = event.page[z] + this.options.offset[z]; - if (obj[props[z]] < 0) bounds[z] = true; - if ((obj[props[z]] + tip[z] - scroll[z]) > size[z] - this.options.windowPadding[z]){ - obj[props[z]] = event.page[z] - this.options.offset[z] - tip[z]; - bounds[z+'2'] = true; - } - } - - this.fireEvent('bound', bounds); - this.tip.setStyles(obj); - }, - - fill: function(element, contents){ - if (typeof contents == 'string') element.set('html', contents); - else element.adopt(contents); - }, - - show: function(element){ - if (!this.tip) document.id(this); - if (!this.tip.getParent()) this.tip.inject(document.body); - this.fireEvent('show', [this.tip, element]); - }, - - hide: function(element){ - if (!this.tip) document.id(this); - this.fireEvent('hide', [this.tip, element]); - } - -}); - -})(); - - -/* ---- - -name: Locale.en-GB.Date - -description: Date messages for British English. - -license: MIT-style license - -authors: - - Aaron Newton - -requires: - - /Locale - - /Locale.en-US.Date - -provides: [Locale.en-GB.Date] - -... -*/ - -Locale.define('en-GB', 'Date', { - - // Culture's date order: DD/MM/YYYY - dateOrder: ['date', 'month', 'year'], - shortDate: '%d/%m/%Y', - shortTime: '%H:%M' - -}).inherit('en-US', 'Date'); - diff --git a/web/views/Makefile.am b/web/views/Makefile.am deleted file mode 100644 index 6ecec9d8b..000000000 --- a/web/views/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -AUTOMAKE_OPTIONS = gnu - -webdir = @WEB_PREFIX@/views - -dist_web_DATA = file.php \ - image.php diff --git a/web/views/image.php b/web/views/image.php index b56f34730..9bc242244 100644 --- a/web/views/image.php +++ b/web/views/image.php @@ -18,22 +18,69 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // +// Calling sequence: ... /zm/index.php?view=image&path=/monid/path/image.jpg&scale=nnn&width=wwww&height=hhhhh +// +// Path is physical path to the image starting at the monitor id +// +// Scale is optional and between 1 and 400 (percent), +// Omitted or 100 = no scaling done, image passed through directly +// Scaling will increase response time slightly +// +// width and height are each optional, ideally supply both, but if only one is supplied the other is calculated +// These are in pixels +// +// If both scale and either width or height are specified, scale is ignored +// + if ( !canView( 'Events' ) ) { $view = "error"; return; } +require_once('includes/Event.php'); +require_once('includes/Frame.php'); header( 'Content-type: image/jpeg' ); +// Compatibility for PHP 5.4 +if (!function_exists('imagescale')) +{ + function imagescale($image, $new_width, $new_height = -1, $mode = 0) + { + $mode; // Not supported + + $new_height = ($new_height == -1) ? imagesy($image) : $new_height; + $imageNew = imagecreatetruecolor($new_width, $new_height); + imagecopyresampled($imageNew, $image, 0, 0, 0, 0, (int)$new_width, (int)$new_height, imagesx($image), imagesy($image)); + + return $imageNew; + } +} + $errorText = false; if ( empty($_REQUEST['path']) ) { + if ( ! empty($_REQUEST['fid']) ) { + if ( ! empty($_REQUEST['eid'] ) ) { + $Event = new Event( $_REQUEST['eid'] ); + $Frame = Frame::find_one( array( 'EventId' => $_REQUEST['eid'], 'FrameId' => $_REQUEST['fid'] ) ); + if ( ! $Frame ) { + Fatal("No Frame found for event(".$_REQUEST['eid'].") and frame id(".$_REQUEST['fid'].")"); + } + $path = $Event->Path().'/'.sprintf("%'.0".ZM_EVENT_IMAGE_DIGITS.'d',$_REQUEST['fid']).'-capture.jpg'; + } else { +# If we are only specifying fid, then the fid must be the primary key into the frames table. But when the event is specified, then it is the frame # + $Frame = new Frame( $_REQUEST['fid'] ); + $Event = new Event( $Frame->EventId() ); + $path = $Event->Path().'/'.sprintf("%'.0".ZM_EVENT_IMAGE_DIGITS.'d',$Frame->FrameId()).'-capture.jpg'; + } + } else { $errorText = "No image path"; + } } else { - $path = $_REQUEST['path']; + $path = ZM_DIR_EVENTS . '/' . $_REQUEST['path']; if ( !empty($user['MonitorIds']) ) { $imageOk = false; @@ -51,52 +98,67 @@ else } } -if ( true ) -{ - // Simple version - if ( $errorText ) - Error( $errorText ); - else - readfile( ZM_DIR_EVENTS.'/'.$path ); -} +$scale=0; +if( !empty($_REQUEST['scale']) ) + if (is_numeric($_REQUEST['scale'])) + { + $x = $_REQUEST['scale']; + if($x >= 1 and $x <= 400) + $scale=$x; + } + +$width=0; +if( !empty($_REQUEST['width']) ) + if (is_numeric($_REQUEST['width'])) + { + $x = $_REQUEST['width']; + if($x >= 10 and $x <= 8000) + $width=$x; + } +$height=0; +if( !empty($_REQUEST['height']) ) + if (is_numeric($_REQUEST['height'])) + { + $x = $_REQUEST['height']; + if($x >= 10 and $x <= 8000) + $height=$x; + } + + +if ( $errorText ) + Error( $errorText ); else -{ - // Not so simple version - if ( !function_exists( "imagecreatefromjpeg" ) ) - Warning( "The imagecreatefromjpeg function is not present, php-gd not installed?" ); - - if ( !$errorText ) + if( ($scale==0 || $scale==100) && $width==0 && $height==0 ) + readfile( $path ); + else { - if ( !($image = imagecreatefromjpeg( ZM_DIR_EVENTS.'/'.$path )) ) + $i = imagecreatefromjpeg ( $path ); + $oldWidth=imagesx($i); + $oldHeight=imagesy($i); + if($width==0 && $height==0) // scale has to be set to get here with both zero { - $errorText = "Can't load image"; - $error = error_get_last(); - Error( $error['message'] ); + $width = $oldWidth * $scale / 100.0; + $height= $oldHeight * $scale / 100.0; + } + elseif ($width==0 && $height!=0) + { + $width = ($height * $oldWidth) / $oldHeight; + } + elseif ($width!=0 && $height==0) + { + $height = ($width * $oldHeight) / $oldWidth; + } + if($width==$oldWidth && $height==$oldHeight) // See if we really need to scale + { + imagejpeg($i); + imagedestroy($i); + } + else // we do need to scale + { + $iScale = imagescale($i, $width, $height); + imagejpeg($iScale); + imagedestroy($i); + imagedestroy($iScale); } } - - if ( $errorText ) - { - if ( !($image = imagecreatetruecolor( 160, 120 )) ) - { - $error = error_get_last(); - Error( $error['message'] ); - } - if ( !($textColor = imagecolorallocate( $image, 255, 0, 0 )) ) - { - $error = error_get_last(); - Error( $error['message'] ); - } - if ( !imagestring( $image, 1, 20, 60, $errorText, $textColor ) ) - { - $error = error_get_last(); - Error( $error['message'] ); - } - Fatal( $errorText." - ".$path ); - } - - imagejpeg( $image ); - - imagedestroy( $image ); -} ?> diff --git a/zm.conf.in b/zm.conf.in index 37d654441..decca3b19 100644 --- a/zm.conf.in +++ b/zm.conf.in @@ -19,7 +19,7 @@ ZM_PATH_BIN=@BINDIR@ ZM_PATH_LIB=@LIBDIR@ # Path to ZoneMinder configuration (this file only at present) -ZM_PATH_CONF=@SYSCONFDIR@ +ZM_PATH_CONF=@ZM_CONFIG_DIR@ # Path to ZoneMinder web files ZM_PATH_WEB=@WEB_PREFIX@ @@ -46,5 +46,9 @@ ZM_DB_USER=@ZM_DB_USER@ # ZoneMinder database password ZM_DB_PASS=@ZM_DB_PASS@ -# Host of this machine +# Do NOT set ZM_SERVER_HOST if you are not using Multi-Server +# You have been warned +# +# The name specified here must have a corresponding entry +# in the Servers tab under Options ZM_SERVER_HOST= diff --git a/zoneminder-config.cmake b/zoneminder-config.cmake index 90759c1b6..6dd6fc48a 100644 --- a/zoneminder-config.cmake +++ b/zoneminder-config.cmake @@ -4,7 +4,10 @@ /* This file is used by cmake to create config.h for ZM */ /* General system checks */ +#cmakedefine BSD 1 +#cmakedefine SOLARIS 1 #cmakedefine HAVE_LINUX_VIDEODEV_H 1 +#cmakedefine HAVE_LIBV4L1_VIDEODEV_H 1 #cmakedefine HAVE_LINUX_VIDEODEV2_H 1 #cmakedefine HAVE_EXECINFO_H 1 #cmakedefine HAVE_UCONTEXT_H 1