diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 4e33ce4e0..f52716c2d 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,23 +1,43 @@ -You should only file an issue if you found a bug. Feature and enhancement requests, general discussions and support questions should occur in one of the following areas: +**THIS FORUM IS FOR BUG REPORTS ONLY** + +Do not post feature or enhancement requests, general discussions or support questions here. + +Feature and enhancement requests, general discussions, and support questions should occur in one of the following areas: - The [ZoneMinder-Chat Slack channel](https://zoneminder-chat.herokuapp.com/) - The [ZoneMinder Forum](https://forums.zoneminder.com/) -**Do not post feature or enhancement requests, general discussions or support questions here.** - Docker related issues should be posted here: https://github.com/ZoneMinder/zmdockerfiles -Make sure you are running the latest version of ZoneMinder before reporting an issue. +In order to submit a bug report, please populate the fields below. This is required. -**ZoneMinder Version (`zmaudit.pl -v`):** +**Describe Your Environment** +- Version of ZoneMinder [release version, development version, or commit] +- How you installed ZoneMinder [e.g. PPA, RPMFusion, from-source, etc] +- Full name and version of OS -**Are you using a development snapshot / git checkout? If so, what is the latest commit? (`git rev-parse HEAD`):** +**If the issue concerns a camera** +- Make and Model +- frame rate +- resolution +- ZoneMinder Source Type: -**Linux Distribution and Version (`cat /etc/os-release` or `cat /etc/redhat-release`):** +**Describe the bug** +A clear and concise description of what the bug is. -**If the issue concerns a camera, provide the make, model, frame rate, resolution and ZoneMinder Source Type:** +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error -**Relevant log lines:** +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Debug Logs** ``` -log lines here + + + ``` diff --git a/.github/config.yml b/.github/config.yml new file mode 100644 index 000000000..543a2c160 --- /dev/null +++ b/.github/config.yml @@ -0,0 +1,21 @@ +# Configuration for welcome - https://github.com/behaviorbot/welcome + +# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome + +# Comment to be posted to on first time issues +newIssueWelcomeComment: > + Thanks for opening your first issue here! Just a reminder, this forum is for Bug Reports only. Be sure to follow the issue template! + +# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome + +# Comment to be posted to on PRs from first time contributors in your repository +#newPRWelcomeComment: > +# Thanks for opening this pull request! Please check out our contributing guidelines. + +# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge + +# Comment to be posted to on pull requests merged by a first time user +#firstPRMergeComment: > +# Congrats on merging your first pull request! We here at behaviorbot are proud of you! + +# It is recommend to include as many gifs and emojis as possible diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 000000000..7e40c036f --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,13 @@ +# Configuration for probot-no-response - https://github.com/probot/no-response + +# Number of days of inactivity before an Issue is closed for lack of response +daysUntilClose: 7 +# Label requiring a response +responseRequiredLabel: more-information-needed +# Comment to post when closing an Issue for lack of response. Set to `false` to disable +closeComment: > + This issue has been automatically closed because there has been no response + to our request for more information from the original author. With only the + information that is currently in the issue, we don't have enough information + to take action. Please reach out if you have or find the answers we need so + that we can investigate further. diff --git a/.travis.yml b/.travis.yml index 1ae0988d5..d46e0fc85 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,7 @@ env: - OS=el DIST=7 - OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack - OS=fedora DIST=28 DOCKER_REPO=knnniggett/packpack + - OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack - OS=ubuntu DIST=trusty - OS=ubuntu DIST=xenial - OS=ubuntu DIST=trusty ARCH=i386 diff --git a/README.md b/README.md index 8f607c0c0..b8e3f34fd 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde 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 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. @@ -53,9 +53,9 @@ Lastly, if you desire to build a development snapshot from the master branch, it 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. +Many of the ZoneMinder configuration 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: +For example, let's say I have created a new ZoneMinder package that contains the cambozola javascript file. However, by default cambozola support is turned off. To fix that, add this to the packaging script: ```bash ./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes ``` diff --git a/db/zm_create.sql.in b/db/zm_create.sql.in index 3448a962f..b2824b075 100644 --- a/db/zm_create.sql.in +++ b/db/zm_create.sql.in @@ -556,7 +556,9 @@ INSERT INTO States (Name,Definition,IsActive) VALUES ('default','','1'); DROP TABLE IF EXISTS `Servers`; CREATE TABLE `Servers` ( `Id` int(10) unsigned NOT NULL auto_increment, + `Protocol` TEXT, `Hostname` TEXT, + `Port` INTEGER UNSIGNED, `PathPrefix` TEXT, `Name` varchar(64) NOT NULL default '', `State_Id` int(10) unsigned, @@ -782,6 +784,9 @@ INSERT INTO `Controls` VALUES (NULL,'D-LINK DCS-3415','Remote','DCS3415',0,0,0,1 INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,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,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0); INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,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,'PSIA','Remote','PSIA',0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0); +INSERT INTO `Controls` VALUES (NULL,'Dahua','Remote','Dahua',0,0,0,1,0,0,1,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,1,8,0,0,1,0,0,0,0,1,1,8,0,0,0,0); + -- -- Add some monitor preset values -- @@ -807,6 +812,7 @@ INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, unicast','Remote','rtsp INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, multicast','Remote','rtsp',0,255,'rtsp','rtpMulti','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP','Remote','rtsp',0,255,'rtsp','rtpRtsp','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); INSERT into MonitorPresets VALUES (NULL,'Axis IP, mpeg4, RTP/RTSP/HTTP','Remote',NULL,NULL,NULL,'rtsp','rtpRtspHttp','',554,'/mpeg4/media.amp','/trackID=',NULL,NULL,3,NULL,0,NULL,NULL,NULL,100,100); +INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, mpjpeg','Remote','http',0,0,'http','simple','',80,'/nphMotionJpeg?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,NULL,0,NULL,NULL,NULL,100,100); INSERT INTO MonitorPresets VALUES (NULL,'Panasonic IP, 320x240, jpeg, max 5 FPS','Remote','http',0,0,'http','simple','',80,'/SnapshotJPEG?Resolution=320x240&Quality=Standard',NULL,320,240,3,5.0,0,NULL,NULL,NULL,100,100); diff --git a/db/zm_update-1.31.43.sql b/db/zm_update-1.31.43.sql index 05a92288e..c97405903 100644 --- a/db/zm_update-1.31.43.sql +++ b/db/zm_update-1.31.43.sql @@ -5,7 +5,7 @@ -- Add Refresh column to Monitors table -- -ALTER TABLE `zm`.`Monitors` +ALTER TABLE `Monitors` CHANGE COLUMN `Type` `Type` ENUM('Local', 'Remote', 'File', 'Ffmpeg', 'Libvlc', 'cURL', 'WebSite') NOT NULL DEFAULT 'Local' ; SET @s = (SELECT IF( diff --git a/db/zm_update-1.31.47.sql b/db/zm_update-1.31.47.sql index 60cad9eb1..714afd133 100644 --- a/db/zm_update-1.31.47.sql +++ b/db/zm_update-1.31.47.sql @@ -1,15 +1 @@ --- --- Add Prefix column to Storage --- - -SET @s = (SELECT IF( - (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() - AND table_name = 'Servers' - AND column_name = 'PathPrefix' - ) > 0, -"SELECT 'Column PathPrefix already exists in Servers'", -"ALTER TABLE Servers ADD `PathPrefix` TEXT AFTER `Hostname`" -)); - -PREPARE stmt FROM @s; -EXECUTE stmt; +ALTER TABLE Frames MODIFY COLUMN EventId bigint unsigned NOT NULL; diff --git a/db/zm_update-1.32.0.sql b/db/zm_update-1.32.0.sql new file mode 100644 index 000000000..f0ff1719b --- /dev/null +++ b/db/zm_update-1.32.0.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.31.47 database to 1.32.0 +-- +-- No changes required +-- diff --git a/db/zm_update-1.32.1.sql b/db/zm_update-1.32.1.sql new file mode 100644 index 000000000..ed9a1879b --- /dev/null +++ b/db/zm_update-1.32.1.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.32.0 database to 1.32.1 +-- +-- No changes required +-- diff --git a/db/zm_update-1.32.2.sql b/db/zm_update-1.32.2.sql new file mode 100644 index 000000000..0bb156dad --- /dev/null +++ b/db/zm_update-1.32.2.sql @@ -0,0 +1,5 @@ +-- +-- This updates a 1.32.1 database to 1.32.2 +-- +-- No changes required +-- diff --git a/db/zm_update-1.32.3.sql b/db/zm_update-1.32.3.sql new file mode 100644 index 000000000..68d617824 --- /dev/null +++ b/db/zm_update-1.32.3.sql @@ -0,0 +1,57 @@ +-- +-- This updates a 1.32.2 database to 1.32.3 +-- + +-- +-- Add some additional monitor preset values +-- + +INSERT INTO MonitorPresets VALUES (NULL,'D-link DCS-930L, 640x480, mjpeg','Remote','http',0,0,'http','simple','',80,'/mjpeg.cgi',NULL,640,480,3,NULL,0,NULL,NULL,NULL,100,100); + +-- +-- Add Protocol column to Storage +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'Protocol' + ) > 0, +"SELECT 'Column Protocol already exists in Servers'", +"ALTER TABLE Servers ADD `Protocol` TEXT AFTER `Id`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Add Prefix column to Storage +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'PathPrefix' + ) > 0, +"SELECT 'Column PathPrefix already exists in Servers'", +"ALTER TABLE Servers ADD `PathPrefix` TEXT AFTER `Hostname`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; + +-- +-- Add Port column to Storage +-- + +SET @s = (SELECT IF( + (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE() + AND table_name = 'Servers' + AND column_name = 'Port' + ) > 0, +"SELECT 'Column Port already exists in Servers'", +"ALTER TABLE Servers ADD `Port` INTEGER UNSIGNED AFTER `Hostname`" +)); + +PREPARE stmt FROM @s; +EXECUTE stmt; diff --git a/distros/debian/control b/distros/debian/control index 29be56e25..4c23ab367 100644 --- a/distros/debian/control +++ b/distros/debian/control @@ -37,11 +37,13 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} , 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 + , libmodule-load-perl, libsys-mmap-perl, libjson-any-perl, libjson-maybexs-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 + ,libnumber-bytes-human-perl + ,libfile-slurp-perl , libpcre3 , ffmpeg | libav-tools, libavdevice53 | libavdevice55 | libavdevice57 , rsyslog | system-log-daemon diff --git a/distros/redhat/CMakeLists.txt b/distros/redhat/CMakeLists.txt index 683a475b1..716faea4c 100644 --- a/distros/redhat/CMakeLists.txt +++ b/distros/redhat/CMakeLists.txt @@ -1,4 +1,9 @@ -# CMakeLists.txt for the Redhat/CentOS Target Distro. +# CMakeLists.txt for the Redhat Target Distros. + +# +# General strategy is to configure and install all files specific to Apache and Nginx +# Then let the rpm specfile sort them into the appropriate sub-package +# # Display a message to show the RHEL build options are being processed. if(ZM_TARGET_DISTRO MATCHES "^el") @@ -9,28 +14,38 @@ else(ZM_TARGET_DISTRO MATCHES "^el") message([WARNING] "Unknown Build Option Detected" ...) endif(ZM_TARGET_DISTRO MATCHES "^el") -if((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx")) - message([FATAL_ERROR] "Experimental Nginx support is currently only supported on Fedora" ...) -endif((NOT ZM_TARGET_DISTRO MATCHES "^fc") AND (ZM_WEB_USER STREQUAL "nginx")) +# +# CONFIGURE STAGE +# -# Configure the zoneminder service files -configure_file(systemd/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY) -if(ZM_WEB_USER STREQUAL "nginx") - configure_file(nginx/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) - configure_file(nginx/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY) - configure_file(nginx/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY) - configure_file(nginx/zoneminder.php-fpm.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf @ONLY) - configure_file(nginx/README.Fedora ${CMAKE_CURRENT_SOURCE_DIR}/readme/README.Fedora COPYONLY) -else(ZM_WEB_USER STREQUAL "nginx") - configure_file(systemd/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) - configure_file(apache/zoneminder.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.conf @ONLY) - configure_file(systemd/zoneminder.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.tmpfiles @ONLY) -endif(ZM_WEB_USER STREQUAL "nginx") - -# Create several empty folders +# Configure the common zoneminder files +configure_file(common/zoneminder.logrotate.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.logrotate @ONLY) +configure_file(common/zoneminder.service.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.service @ONLY) file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp) -# Install the empty folders +# Configure the Apache zoneminder files +configure_file(httpd/zm-httpd.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zm-httpd.conf @ONLY) +configure_file(httpd/zoneminder.httpd.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.httpd.conf @ONLY) +configure_file(httpd/zoneminder.httpd.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.httpd.tmpfiles.conf @ONLY) +configure_file(httpd/com.zoneminder.systemctl.rules.httpd.in ${CMAKE_CURRENT_SOURCE_DIR}/com.zoneminder.systemctl.rules.httpd @ONLY) + +# Configure the Nginx zoneminder files +configure_file(nginx/zm-nginx.conf ${CMAKE_CURRENT_SOURCE_DIR}/zm-nginx.conf COPYONLY) +configure_file(nginx/zoneminder.nginx.conf.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.nginx.conf @ONLY) +configure_file(nginx/zoneminder.nginx.tmpfiles.in ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.nginx.tmpfiles.conf @ONLY) +configure_file(nginx/zm-web-user.conf ${CMAKE_CURRENT_SOURCE_DIR}/zm-web-user.conf COPYONLY) +configure_file(nginx/zoneminder.php-fpm.conf ${CMAKE_CURRENT_SOURCE_DIR}/zoneminder.php-fpm.conf COPYONLY) +configure_file(nginx/com.zoneminder.systemctl.rules.nginx ${CMAKE_CURRENT_SOURCE_DIR}/com.zoneminder.systemctl.rules.nginx COPYONLY) + +# +# INSTALLATION STAGE +# + + +# Install the common zoneminder files +install(FILES zoneminder.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(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) @@ -38,6 +53,22 @@ install(DIRECTORY zoneminder DESTINATION /var/cache DIRECTORY_PERMISSIONS OWNER_ install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) +# Install the Apache zoneminder files +install(FILES zm-httpd.conf DESTINATION /usr/lib/systemd/system/zoneminder.service.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.httpd.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.httpd.tmpfiles.conf DESTINATION /usr/lib/tmpfiles.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES com.zoneminder.systemctl.rules.httpd DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + +# Install the Nginx zoneminder files +install(FILES zm-nginx.conf DESTINATION /usr/lib/systemd/system/zoneminder.service.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.nginx.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.nginx.tmpfiles.conf DESTINATION /usr/lib/tmpfiles.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES com.zoneminder.systemctl.rules.nginx DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zm-web-user.conf DESTINATION /etc/zm/conf.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) + +# Miscellaneous + # Symlink the cake php temp folder to the ZoneMinder temp folder install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminder/temp \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/zoneminder/www/api/app/tmp\")") @@ -45,16 +76,5 @@ install(CODE "execute_process(COMMAND ln -sf ../../../../../../var/lib/zoneminde 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 -install(FILES misc/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 zoneminder service files -install(FILES zoneminder.logrotate DESTINATION /etc/logrotate.d RENAME zoneminder PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) -install(FILES zoneminder.conf DESTINATION /etc/zm/www PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) - -if(ZM_WEB_USER STREQUAL "nginx") - install(FILES zoneminder.php-fpm.conf DESTINATION /etc/php-fpm.d PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ RENAME zoneminder.conf) -endif(ZM_WEB_USER STREQUAL "nginx") - -install(FILES zoneminder.service DESTINATION /usr/lib/systemd/system PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) -install(FILES zoneminder.tmpfiles DESTINATION /usr/lib/tmpfiles.d RENAME zoneminder.conf PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) +install(FILES common/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) diff --git a/distros/redhat/misc/redalert.wav b/distros/redhat/common/redalert.wav similarity index 100% rename from distros/redhat/misc/redalert.wav rename to distros/redhat/common/redalert.wav diff --git a/distros/redhat/common/zoneminder.logrotate.in b/distros/redhat/common/zoneminder.logrotate.in new file mode 100644 index 000000000..210a84c07 --- /dev/null +++ b/distros/redhat/common/zoneminder.logrotate.in @@ -0,0 +1,12 @@ +@ZM_LOGDIR@/*.log { + missingok + notifempty + sharedscripts + delaycompress + compress + postrotate + @BINDIR@/zmpkg.pl logrot > /dev/null 2>/dev/null || true + endscript + daily + rotate 7 +} diff --git a/distros/redhat/systemd/zoneminder.service.in b/distros/redhat/common/zoneminder.service.in similarity index 76% rename from distros/redhat/systemd/zoneminder.service.in rename to distros/redhat/common/zoneminder.service.in index 68918ab9e..8551a60e2 100644 --- a/distros/redhat/systemd/zoneminder.service.in +++ b/distros/redhat/common/zoneminder.service.in @@ -1,13 +1,12 @@ # ZoneMinder systemd unit file for RedHat distros and clones +# See drop-in folder for additional config directives [Unit] Description=ZoneMinder CCTV recording and security system -After=network.target mariadb.service httpd.service -Requires=mariadb.service httpd.service +After=network.target mariadb.service +Requires=mariadb.service [Service] -User=@WEB_USER@ -Group=@WEB_GROUP@ Type=forking ExecStart=@BINDIR@/zmpkg.pl start ExecReload=@BINDIR@/zmpkg.pl restart diff --git a/distros/redhat/httpd/com.zoneminder.systemctl.rules.httpd.in b/distros/redhat/httpd/com.zoneminder.systemctl.rules.httpd.in new file mode 100644 index 000000000..d101dad69 --- /dev/null +++ b/distros/redhat/httpd/com.zoneminder.systemctl.rules.httpd.in @@ -0,0 +1,7 @@ +polkit.addRule(function(action, subject) { + if (action.id == "com.zoneminder.policykit.pkexec.run-zmsystemctl" && + subject.user != "@WEB_USER@") { + return polkit.Result.NO; + } + +}); diff --git a/distros/redhat/httpd/zm-httpd.conf.in b/distros/redhat/httpd/zm-httpd.conf.in new file mode 100644 index 000000000..1a00d8d9b --- /dev/null +++ b/distros/redhat/httpd/zm-httpd.conf.in @@ -0,0 +1,8 @@ +# Additional config directives for ZoneMinder with Apache web server + +[Unit] +After=httpd.service + +[Service] +User=@WEB_USER@ +Group=@WEB_GROUP@ diff --git a/distros/redhat/apache/zoneminder.conf.in b/distros/redhat/httpd/zoneminder.httpd.conf.in similarity index 100% rename from distros/redhat/apache/zoneminder.conf.in rename to distros/redhat/httpd/zoneminder.httpd.conf.in diff --git a/distros/redhat/systemd/zoneminder.tmpfiles.in b/distros/redhat/httpd/zoneminder.httpd.tmpfiles.in similarity index 99% rename from distros/redhat/systemd/zoneminder.tmpfiles.in rename to distros/redhat/httpd/zoneminder.httpd.tmpfiles.in index 21e6119fe..f24babdc6 100644 --- a/distros/redhat/systemd/zoneminder.tmpfiles.in +++ b/distros/redhat/httpd/zoneminder.httpd.tmpfiles.in @@ -3,3 +3,4 @@ D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@ D @ZM_CACHEDIR@ 0755 @WEB_USER@ @WEB_GROUP@ d @ZM_DIR_EVENTS@ 0755 @WEB_USER@ @WEB_GROUP@ D @ZM_DIR_IMAGES@ 0755 @WEB_USER@ @WEB_GROUP@ + diff --git a/distros/redhat/misc/local_zoneminder.te b/distros/redhat/misc/local_zoneminder.te deleted file mode 100644 index c49505785..000000000 --- a/distros/redhat/misc/local_zoneminder.te +++ /dev/null @@ -1,125 +0,0 @@ -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; - type ionixnetmon_port_t; - type glance_port_t; - type mmcc_port_t; - type postfix_master_t; - type commplex_port_t; - type syslogd_port_t; - type dcc_port_t; - type sip_port_t; - type amqp_port_t; - type condor_port_t; - type afs_fs_port_t; - type nodejs_debug_port_t; - type httpd_var_lib_t; - type websm_port_t; - type afs_pt_port_t; - type postfix_qmgr_t; - 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 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; -} - -#============= httpd_t ============== -allow httpd_t auditd_t:dir { search getattr }; -allow httpd_t auditd_t:file { read getattr open }; -allow httpd_t crond_t:dir { search getattr }; -allow httpd_t crond_t:file { read getattr open }; -allow httpd_t dhcpc_t:dir { search getattr }; -allow httpd_t dhcpc_t:file { read getattr open }; -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 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, 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 { 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 getattr }; -allow httpd_t mysqld_safe_t:file { read getattr open }; -allow httpd_t mysqld_t:dir { search getattr }; -allow httpd_t mysqld_t:file { read getattr open }; -allow httpd_t sshd_t:dir { search getattr }; -allow httpd_t sshd_t:file { read getattr open }; -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 getattr }; -allow httpd_t udev_t:file { read getattr open }; -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; -allow httpd_t afs_pt_port_t:udp_socket name_bind; -allow httpd_t amqp_port_t:udp_socket name_bind; -allow httpd_t aol_port_t:udp_socket name_bind; -allow httpd_t commplex_port_t:udp_socket name_bind; -allow httpd_t condor_port_t:udp_socket name_bind; -allow httpd_t dcc_port_t:udp_socket name_bind; -allow httpd_t git_port_t:udp_socket name_bind; -allow httpd_t glance_port_t:udp_socket name_bind; -allow httpd_t httpd_var_lib_t:lnk_file create; -allow httpd_t ionixnetmon_port_t:udp_socket name_bind; -allow httpd_t ipp_port_t:udp_socket name_bind; -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 getattr }; -allow httpd_t postfix_master_t:file { read getattr open }; -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 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/nginx/README.Fedora b/distros/redhat/nginx/README.Fedora deleted file mode 100644 index 0a5168231..000000000 --- a/distros/redhat/nginx/README.Fedora +++ /dev/null @@ -1,165 +0,0 @@ -What's New -========== - -1. This is an *experimental* build of zoneminder which uses the - nginx web server. - -2. 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. - -3. 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. - -4. 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. - -5. This package ships with the new ZoneMinder API enabled. - -New installs -============ - -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 dnf install mariadb-server - sudo systemctl enable mariadb - sudo systemctl start mariadb.service - mysql_secure_installation - -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 - - 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 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 - - 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. This package comes preconfigured for HTTPS using the default self signed - certificate on your system. We recommend you keep this configuration. - - If this does not meet your needs, then read README.https to - learn about alternatives. - -7. Edit /etc/sysconfig/fcgiwrap and set DAEMON_PROCS to the maximum number of - simulatneous streams the server should support. Generally, a good minimum - value for this equals the total number of cameras you expect to view at the - same time. - -8. Now start the web server: - - sudo systemctl enable nginx - sudo systemctl start nginx - -9. Now start zoneminder: - - sudo systemctl enable zoneminder - sudo systemctl start zoneminder - -10.The Fedora repos have a ZoneMinder package available, but it does not - support ffmpeg or libvlc, which many modern IP cameras require. Most users - will want to prevent the ZoneMinder package in the Fedora repos from - overwriting the ZoneMinder package in zmrepo, during a future dnf update. To - prevent that from happening you must edit /etc/yum.repos.d/fedora.repo - and /etc/yum.repos.d/fedora-updates.repo. Add the line "exclude=zoneminder*" - without the quotes under the [fedora] and [fedora-updates] blocks, - respectively. - -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 restart nginx and php-fpm then start and zoneminder: - - sudo systemctl restart nginx - sudo systemctl restart php-fpm - sudo systemctl start zoneminder - diff --git a/distros/redhat/nginx/com.zoneminder.systemctl.rules.nginx b/distros/redhat/nginx/com.zoneminder.systemctl.rules.nginx new file mode 100644 index 000000000..8eaa78d5c --- /dev/null +++ b/distros/redhat/nginx/com.zoneminder.systemctl.rules.nginx @@ -0,0 +1,7 @@ +polkit.addRule(function(action, subject) { + if (action.id == "com.zoneminder.policykit.pkexec.run-zmsystemctl" && + subject.user != "nginx") { + return polkit.Result.NO; + } + +}); diff --git a/distros/redhat/nginx/zm-nginx.conf b/distros/redhat/nginx/zm-nginx.conf new file mode 100644 index 000000000..5f0319b17 --- /dev/null +++ b/distros/redhat/nginx/zm-nginx.conf @@ -0,0 +1,9 @@ +# Additional config directives for ZoneMinder with Nginx web server + +[Unit] +After=nginx.service php-fpm.service fcgiwrap.service +Requires=php-fpm.service fcgiwrap@nginx.service + +[Service] +User=nginx +Group=nginx diff --git a/distros/redhat/nginx/zm-web-user.conf b/distros/redhat/nginx/zm-web-user.conf new file mode 100644 index 000000000..3146679fd --- /dev/null +++ b/distros/redhat/nginx/zm-web-user.conf @@ -0,0 +1,3 @@ +ZM_WEB_USER=nginx +ZM_WEB_GROUP=nginx + diff --git a/distros/redhat/nginx/zoneminder.conf.in b/distros/redhat/nginx/zoneminder.nginx.conf.in similarity index 91% rename from distros/redhat/nginx/zoneminder.conf.in rename to distros/redhat/nginx/zoneminder.nginx.conf.in index b8ffd816a..fb8141c74 100644 --- a/distros/redhat/nginx/zoneminder.conf.in +++ b/distros/redhat/nginx/zoneminder.nginx.conf.in @@ -22,6 +22,10 @@ location /cgi-bin-zm { fastcgi_pass unix:/run/fcgiwrap.sock; } +location /zm/cache { + alias "@ZM_CACHEDIR@"; +} + location /zm { gzip off; alias "@ZM_WEBDIR@"; @@ -43,7 +47,7 @@ location /zm { location /zm/api/ { alias "@ZM_WEBDIR@"; - rewrite ^/zm/api(.+)$ /zm/api/index.php?p=$1 last; + rewrite ^/zm/api(.+)$ /zm/api/app/webroot/index.php?p=$1 last; } } diff --git a/distros/redhat/nginx/zoneminder.nginx.tmpfiles.in b/distros/redhat/nginx/zoneminder.nginx.tmpfiles.in new file mode 100644 index 000000000..1c581fcb9 --- /dev/null +++ b/distros/redhat/nginx/zoneminder.nginx.tmpfiles.in @@ -0,0 +1,6 @@ +D @ZM_TMPDIR@ 0755 nginx nginx +D @ZM_SOCKDIR@ 0755 nginx nginx +D @ZM_CACHEDIR@ 0755 nginx nginx +d @ZM_DIR_EVENTS@ 0755 nginx nginx +D @ZM_DIR_IMAGES@ 0755 nginx nginx + diff --git a/distros/redhat/nginx/zoneminder.php-fpm.conf b/distros/redhat/nginx/zoneminder.php-fpm.conf new file mode 100644 index 000000000..cd60a5120 --- /dev/null +++ b/distros/redhat/nginx/zoneminder.php-fpm.conf @@ -0,0 +1,14 @@ +; This config file is needed when using ZoneMinder with web servers other +; than Apache. You can ignore this file if you are using Apache web server. +; Change the user and group of the default pool to the web server account +[www] + +user = nginx +group = nginx + +; These parameters are typically a tradoff between performance and memory +; consumption. See the contents of www.conf for details. + +pm = ondemand +pm.max_children = 50 +pm.process_idle_timeout = 10s diff --git a/distros/redhat/nginx/zoneminder.php-fpm.conf.in b/distros/redhat/nginx/zoneminder.php-fpm.conf.in deleted file mode 100644 index 26e8c62cf..000000000 --- a/distros/redhat/nginx/zoneminder.php-fpm.conf.in +++ /dev/null @@ -1,10 +0,0 @@ -# Change the user and group of the default pool to the web server account -[www] - -user = @WEB_USER@ -group = @WEB_GROUP@ - -# Uncomment these on machines with little memory -#pm = ondemand -#pm.max_children = 10 -#pm.process_idle_timeout = 10s diff --git a/distros/redhat/nginx/zoneminder.service.in b/distros/redhat/nginx/zoneminder.service.in deleted file mode 100644 index 7e2e36585..000000000 --- a/distros/redhat/nginx/zoneminder.service.in +++ /dev/null @@ -1,22 +0,0 @@ -# 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 nginx.service php-fpm.service fcgiwrap.service -Requires=mariadb.service nginx.service php-fpm.service fcgiwrap.service - -[Service] -User=@WEB_USER@ -Group=@WEB_GROUP@ -Type=forking -ExecStart=@BINDIR@/zmpkg.pl start -ExecReload=@BINDIR@/zmpkg.pl restart -ExecStop=@BINDIR@/zmpkg.pl stop -PIDFile=@ZM_RUNDIR@/zm.pid -Environment=TZ=/etc/localtime -RuntimeDirectory=zoneminder -RuntimeDirectoryMode=0755 - -[Install] -WantedBy=multi-user.target diff --git a/distros/redhat/nginx/zoneminder.tmpfiles.in b/distros/redhat/nginx/zoneminder.tmpfiles.in deleted file mode 100644 index 8040a7877..000000000 --- a/distros/redhat/nginx/zoneminder.tmpfiles.in +++ /dev/null @@ -1,5 +0,0 @@ -D @ZM_TMPDIR@ 0755 @WEB_USER@ @WEB_GROUP@ -D @ZM_SOCKDIR@ 0755 @WEB_USER@ @WEB_GROUP@ -D /var/lib/php/session 770 root @WEB_GROUP@ -D /var/lib/php/wsdlcache 770 root @WEB_GROUP@ - diff --git a/distros/redhat/readme/README b/distros/redhat/readme/README new file mode 100644 index 000000000..065628321 --- /dev/null +++ b/distros/redhat/readme/README @@ -0,0 +1,37 @@ +What's New +========== + +1. See the ZoneMinder release notes for a list of new features: + https://github.com/ZoneMinder/zoneminder/releases + +2. The contents of the ZoneMinder Apache config file have changed. In + addition, this ZoneMinder package now requires you to manually symlink the + ZoneMinder Apache config file. See new install step 6 and upgrade step 3 + below for details. + +3. This package has been split into sub-packages to allow compatibility with + other web servers. Here is a breakdown of the available packages: + + zoneminder - Meta-package installs zoneminder-common and zoneminder-httpd + This exists soley for backwards compatibility. + zoneminder-common - Common files that do not differ based on the web server + zoneminder-httpd - Files needed for compatibility with the Apache web server + zoneminder-nginx - Files needed for compatibility with the Nginx web server + + You can switch between different subpackages with dnf/yum. Be advised that, + if you modified any of the default config files supplied by the package, + rpm may not update the config file to the proper version. This is by design. + To avoid this issue, use drop-in files instead. + +4. If you have installed ZoneMinder from the FedBerry repositories, this build + of ZoneMinder has support for Raspberry Pi hardware acceleration when using + ffmpeg. Unforunately, there is a problem with the same hardware acceleration + when using libvlc. Consequently, libvlc support in this build of ZoneMinder + has been disabled until the problem is resolved. See the following bug + report for details: https://trac.videolan.org/vlc/ticket/18594 + +5. Continue on to the next README that corresponds to the chosen webserver: + + README.httpd - Follow these steps when using Apache + README.nginx - Follow these steps when using Nginx + diff --git a/distros/redhat/readme/README.Fedora b/distros/redhat/readme/README.httpd similarity index 87% rename from distros/redhat/readme/README.Fedora rename to distros/redhat/readme/README.httpd index 3366c31a6..5301850df 100644 --- a/distros/redhat/readme/README.Fedora +++ b/distros/redhat/readme/README.httpd @@ -1,17 +1,8 @@ -What's New -========== - -1. See the ZoneMinder release notes for a list of new features: - https://github.com/ZoneMinder/zoneminder/releases - -2. The contents of the ZoneMinder Apache config file have changed. In - addition, this ZoneMinder package now requires you to manually symlink the - ZoneMinder Apache config file. See new install step 6 and upgrade step 3 - below for details. - New installs ============ +NOTE: EL7 users should replace "dnf" with "yum" in the instructions below. + 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: @@ -72,19 +63,19 @@ New installs 6. Configure the web server This package uses the HTTPS protocol by default to access the web portal, - using rhe default self signed certificate on your system. Requests using + using the default self signed certificate on your system. Requests using HTTP will auto-redirect to HTTPS. Inspect the web server configuration file and verify it meets your needs: - /etc/zm/www/zoneminder.conf + /etc/zm/www/zoneminder.httpd.conf If you are running other web enabled services then you may need to edit this file to suite. See README.https to learn about other alternatives. When in doubt, proceed with the default: - sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/ + sudo ln -sf /etc/zm/www/zoneminder.httpd.conf /etc/httpd/conf.d/ sudo dnf install mod_ssl 7. Now start the web server: @@ -129,7 +120,7 @@ New installs Upgrades ======== -1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom +1. Conf.d folder support has been added to ZoneMinder. Any custom changes previously made to zm.conf must now be made in one or more custom config files, created under the conf.d folder. Do this now. See /etc/zm/conf.d/README for details. Once you recreate any custom config changes @@ -146,10 +137,17 @@ Upgrades See step 2 of the Installation section to add missing permissions. 3. Verify the ZoneMinder Apache configuration file in the folder - /etc/zm/www. You will have a file called "zoneminder.conf" and there - may also be a file called "zoneminder.conf.rpmnew". If the rpmnew file + /etc/zm/www. You will have a file called "zoneminder.httpd.conf" and there + may also be one or more files with "rpmnew" extenstion. 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. + Verify the SSL Requirements meet your needs. Read README.https if necessary. + + The contents of this file must be merged into your Apache configuration. + See step 6 of the installation section if you have not already done this + during a previous upgrade. + + IMPORTANT: Failure to complete this step properly will result in a mostly + empty or significantly corrupted web console post-upgrade. 4. Upgrade the database before starting ZoneMinder. diff --git a/distros/redhat/readme/README.https b/distros/redhat/readme/README.https index 4b02aaa0d..620588bf9 100644 --- a/distros/redhat/readme/README.https +++ b/distros/redhat/readme/README.https @@ -20,7 +20,8 @@ experience. to do this: https://wiki.centos.org/HowTos/Https . Additionally, Googling "centos certificate" reveals many articles on the subject. -3. 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. +3. When using Apache, you can turn off HTTPS entirely by simply commenting + out the SSLRequireSSL directives found in + /etc/zm/www/zoneminder.apache.conf. You should also comment out the + HTTP -> HTTPS Rewrite rule. diff --git a/distros/redhat/readme/README.Redhat7 b/distros/redhat/readme/README.nginx similarity index 78% rename from distros/redhat/readme/README.Redhat7 rename to distros/redhat/readme/README.nginx index bb2dcdaa0..b55d26428 100644 --- a/distros/redhat/readme/README.Redhat7 +++ b/distros/redhat/readme/README.nginx @@ -1,28 +1,17 @@ -What's New -========== - -1. See the ZoneMinder release notes for a list of new features: - https://github.com/ZoneMinder/zoneminder/releases - -2. The contents of the ZoneMinder Apache config file have changed. In - addition, this ZoneMinder package now requires you to manually symlink the - ZoneMinder Apache config file. See new install step 6 and upgrade step 3 - below for details. - 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 yum install mariadb-server + sudo dnf install mariadb-server sudo systemctl enable mariadb sudo systemctl start mariadb.service 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: +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 \ @@ -42,8 +31,8 @@ New installs Once the file has been saved, set proper file & ownership permissions on it: - sudo chown root:apache *.conf - sudo chmod 640 *.conf + sudo chown root:nginx *.conf + sudo chmod 640 *.conf 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 @@ -56,7 +45,7 @@ New installs 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 + 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. @@ -72,7 +61,7 @@ New installs 6. Configure the web server This package uses the HTTPS protocol by default to access the web portal, - using rhe default self signed certificate on your system. Requests using + using the default self signed certificate on your system. Requests using HTTP will auto-redirect to HTTPS. Inspect the web server configuration file and verify it meets your needs: @@ -84,20 +73,24 @@ New installs When in doubt, proceed with the default: - sudo ln -s /etc/zm/www/zoneminder.conf /etc/httpd/conf.d/ - sudo yum install mod_ssl + sudo ln -sf /etc/zm/www/zoneminder.nginx.conf /etc/nginx/default.d/ -7. Now start the web server: +7. Edit /etc/sysconfig/fcgiwrap and set DAEMON_PROCS to the maximum number of + simulatneous streams the server should support. Generally, a good minimum + value for this equals the total number of cameras you expect to view at the + same time. - sudo systemctl enable httpd - sudo systemctl start httpd +8. Now start the web server: -8. Now start zoneminder: + sudo systemctl enable nginx + sudo systemctl start nginx + +9. Now start zoneminder: sudo systemctl enable zoneminder sudo systemctl start zoneminder -9. Optionally configure the firewall +10. Optionally configure the firewall All Redhat distros ship with the firewall enabled. That means you will not be able to access the ZoneMinder web console from a remote machine until @@ -117,7 +110,7 @@ New installs security requirements and how you use the system. It is up to you to verify these commands are sufficient. -10. Access the ZoneMinder web console +11. Access the ZoneMinder web console You may now access the ZoneMinder web console from your web browser using an appropriate url. Here are some examples: @@ -129,7 +122,7 @@ New installs Upgrades ======== -1. Conf.d folder support has been added to ZoneMinder 1.31.0. Any custom +1. Conf.d folder support has been added to ZoneMinder. Any custom changes previously made to zm.conf must now be made in one or more custom config files, created under the conf.d folder. Do this now. See /etc/zm/conf.d/README for details. Once you recreate any custom config changes @@ -144,13 +137,17 @@ Upgrades 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 + +3. Verify the ZoneMinder Nginx configuration file in the folder /etc/zm/www. 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. + The contents of this file must be merged into your Nginx configuration. + See step 6 of the installation section if you have not already done this + during a previous upgrade. + 4. Upgrade the database before starting ZoneMinder. Most upgrades can be performed by executing the following command: @@ -163,9 +160,9 @@ Upgrades sudo zmupdate.pl --user=root --pass= --version= -5. Now restart the web server then start zoneminder: +5. Now restart nginx and php-fpm then start zoneminder: - sudo systemctl restart httpd + sudo systemctl restart nginx + sudo systemctl restart php-fpm sudo systemctl start zoneminder - diff --git a/distros/redhat/systemd/zoneminder.logrotate.in b/distros/redhat/systemd/zoneminder.logrotate.in deleted file mode 100644 index b4919eb5e..000000000 --- a/distros/redhat/systemd/zoneminder.logrotate.in +++ /dev/null @@ -1,8 +0,0 @@ -@ZM_LOGDIR@/*.log { - missingok - notifempty - sharedscripts - postrotate - @BINDIR@/zmpkg.pl logrot 2> /dev/null > /dev/null || : - endscript -} diff --git a/distros/redhat/zoneminder.spec b/distros/redhat/zoneminder.spec index 042abb40e..3ff68b080 100644 --- a/distros/redhat/zoneminder.spec +++ b/distros/redhat/zoneminder.spec @@ -1,3 +1,4 @@ +# Leaving this to allow one to build zoneminder-http subpackage using arbitrary user account %global zmuid_final apache %global zmgid_final apache @@ -7,10 +8,6 @@ # CakePHP-Enum-Behavior is configured as a git submodule %global ceb_version 1.0-zm -%if "%{zmuid_final}" == "nginx" -%global with_nginx 1 -%endif - %global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt %global sslkey %{_sysconfdir}/pki/tls/private/localhost.key @@ -22,12 +19,12 @@ %global with_apcu_bc 1 %endif -%global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora} +# The default for everything but el7 these days %global _hardened_build 1 Name: zoneminder -Version: 1.31.45 -Release: 1%{?dist} +Version: 1.32.2 +Release: 2%{?dist} Summary: A camera monitoring and analysis tool Group: System Environment/Daemons # Mootools is inder the MIT license: http://mootools.net/ @@ -75,6 +72,7 @@ BuildRequires: vlc-devel BuildRequires: libcurl-devel BuildRequires: libv4l-devel BuildRequires: desktop-file-utils +BuildRequires: gzip # ZoneMinder looks for and records the location of the ffmpeg binary during build BuildRequires: ffmpeg @@ -84,11 +82,25 @@ BuildRequires: ffmpeg-devel BuildRequires: libmp4v2-devel BuildRequires: x264-devel -%{?with_nginx:Requires: nginx} -%{?with_nginx:Requires: fcgiwrap} -%{?with_nginx:Requires: php-fpm} -%{!?with_nginx:Requires: httpd} -%{!?with_nginx:Requires: php} +# Allow existing user base to seamlessly transition to sub-packages +Requires: %{name}-common%{?_isa} = %{version}-%{release} +Requires: %{name}-httpd%{?_isa} = %{version}-%{release} + +%description +ZoneMinder is a set of applications which is intended to provide a complete +solution allowing you to capture, analyze, 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. + +This is a meta package for backwards compatibility with the existing +ZoneMinder user base. + +%package common +Summary: Common files for ZoneMinder, not tied to a specific web server + Requires: php-mysqli Requires: php-common Requires: php-gd @@ -113,16 +125,12 @@ Requires: perl(Net::FTP) Requires: perl(LWP::Protocol::https) Requires: ca-certificates Requires: zip - -Requires(post): systemd -Requires(post): systemd-sysv -Requires(preun): systemd -Requires(postun): systemd +%{systemd_requires} Requires(post): %{_bindir}/gpasswd -Requires(post): %{_bindir}/less +Requires(post): %{_bindir}/chown -%description +%description common ZoneMinder is a set of applications which is intended to provide a complete solution allowing you to capture, analyze, record and monitor any cameras you have attached to a Linux based machine. It is designed to run on kernels which @@ -131,15 +139,57 @@ 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. +This is a meta-package that exists solely to allow the existing user base to +seamlessly transition to sub-packages. + +%package httpd +Summary: ZoneMinder configuration for Apache web server +Requires: %{name}-common%{?_isa} = %{version}-%{release} +Requires: httpd +Requires: php + +Conflicts: %{name}-nginx + +%description httpd +ZoneMinder is a set of applications which is intended to provide a complete +solution allowing you to capture, analyze, 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. + +This sub-package contains configuration specific to Apache web server + +%package nginx +Summary: ZoneMinder configuration for Nginx web server +Requires: %{name}-common%{?_isa} = %{version}-%{release} +Requires: nginx +Requires: php-fpm +Requires: fcgiwrap + +Conflicts: %{name}-httpd + +%description nginx +ZoneMinder is a set of applications which is intended to provide a complete +solution allowing you to capture, analyze, 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. + +This sub-package contains support for ZoneMinder with the Nginx web server + %prep -%autosetup -p 1 -a 1 -n ZoneMinder-%{version} -%{__rm} -rf ./web/api/app/Plugin/Crud -%{__mv} -f crud-%{crud_version} ./web/api/app/Plugin/Crud +%autosetup -p 1 -a 1 +rm -rf ./web/api/app/Plugin/Crud +mv -f crud-%{crud_version} ./web/api/app/Plugin/Crud # The all powerful autosetup macro does not work after the second source tarball -%{__gzip} -dc %{_sourcedir}/cakephp-enum-behavior-%{ceb_version}.tar.gz | tar -xvvf - -%{__rm} -rf ./web/api/app/Plugin/CakePHP-Enum-Behavior -%{__mv} -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Behavior +gzip -dc %{_sourcedir}/cakephp-enum-behavior-%{ceb_version}.tar.gz | tar -xvvf - +rm -rf ./web/api/app/Plugin/CakePHP-Enum-Behavior +mv -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Behavior # Change the following default values ./utils/zmeditconfigdata.sh ZM_OPT_CAMBOZOLA yes @@ -152,7 +202,7 @@ too much degradation of performance. %build %cmake \ -DZM_WEB_USER="%{zmuid_final}" \ - -DZM_WEB_GROUP="%{zmuid_final}" \ + -DZM_WEB_GROUP="%{zmgid_final}" \ -DZM_TARGET_DISTRO="%{zmtargetdistro}" \ . @@ -174,10 +224,13 @@ find %{buildroot} \( -name .htaccess -or -name .editorconfig -or -name .packlist find %{buildroot}%{_datadir}/zoneminder/www/api \( -name cake -or -name cake.php \) -type f -exec sed -i 's\^#!/usr/bin/env bash$\#!%{_buildshell}\' {} \; -exec %{__chmod} 755 {} \; # Use the system cacert file rather then the one bundled with CakePHP -%{__rm} -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem -%{__ln_s} ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem +rm -f %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem +ln -s ../../../../../../../..%{_sysconfdir}/pki/tls/certs/ca-bundle.crt %{buildroot}%{_datadir}/zoneminder/www/api/lib/Cake/Config/cacert.pem -%post +# Handle the polkit file differently for web server agnostic support (see post) +rm -f %{buildroot}%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules + +%post common # Initial installation if [ $1 -eq 1 ] ; then %systemd_post %{name}.service @@ -185,28 +238,50 @@ fi # 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 - # zmupdate.pl --nointeractive fi +# Warn the end user to read the README file +echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, you must read the README file\nto finish the installation or upgrade!" +echo -e "\nThe README file is located here: %{_pkgdocdir}-common/README\n" + +%post httpd +# For the case of changing from nginx <-> httpd, files in these folders must change ownership if they exist +%{_bindir}/chown -R %{zmuid_final}:%{zmgid_final} %{_sharedstatedir}/php/session/* >/dev/null 2>&1 || : +%{_bindir}/chown -R %{zmuid_final}:%{zmgid_final} %{_localstatedir}/log/zoneminder/* >/dev/null 2>&1 || : +%{_bindir}/chown -R %{zmuid_final}:%{zmgid_final} %{_sharedstatedir}/zoneminder/events/* >/dev/null 2>&1 || : + +ln -sf %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.httpd %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules +# backwards compatibility +ln -sf %{_sysconfdir}/zm/www/zoneminder.httpd.conf %{_sysconfdir}/zm/www/zoneminder.conf + # Allow zoneminder access to local video sources, serial ports, and x10 %{_bindir}/gpasswd -a %{zmuid_final} video >/dev/null 2>&1 || : %{_bindir}/gpasswd -a %{zmuid_final} dialout >/dev/null 2>&1 || : -# Warn the end user to read the README file -echo -e "\nVERY IMPORTANT: Before starting ZoneMinder, read README.%{readme_suffix} to finish the\ninstallation or upgrade!\n" -echo -e "\nThe README file is located here: %{_docdir}/%{name}\n" +%post nginx + +# Php package owns the session folder and sets group ownership to apache account +# We could override the folder permission, but adding nginx to the apache group works better +%{_bindir}/gpasswd -a nginx apache >/dev/null 2>&1 || : + +# For the case of changing from httpd <-> nginx, files in these folders must change ownership if they exist +%{_bindir}/chown -R nginx:nginx %{_sharedstatedir}/php/session/* >/dev/null 2>&1 || : +%{_bindir}/chown -R nginx:nginx %{_localstatedir}/log/zoneminder/* >/dev/null 2>&1 || : +%{_bindir}/chown -R nginx:nginx %{_sharedstatedir}/zoneminder/events/* >/dev/null 2>&1 || : + +ln -sf %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.nginx %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules +# backwards compatibility +ln -sf %{_sysconfdir}/zm/www/zoneminder.nginx.conf %{_sysconfdir}/zm/www/zoneminder.conf + +# Allow zoneminder access to local video sources, serial ports, and x10 +%{_bindir}/gpasswd -a nginx video >/dev/null 2>&1 || : +%{_bindir}/gpasswd -a nginx dialout >/dev/null 2>&1 || : -%if 0%{?with_nginx} # Nginx does not create an SSL certificate like the apache package does so lets do that here if [ -f %{sslkey} -o -f %{sslcert} ]; then exit 0 @@ -232,7 +307,6 @@ SomeOrganizationalUnit ${FQDN} root@${FQDN} EOF -%endif %preun %systemd_preun %{name}.service @@ -240,19 +314,12 @@ EOF %postun %systemd_postun_with_restart %{name}.service -%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 -%{_bindir}/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 +# nothing + +%files common %license COPYING -%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https +%doc AUTHORS README.md distros/redhat/readme/README distros/redhat/readme/README.httpd distros/redhat/readme/README.nginx distros/redhat/readme/README.https # We want these two folders to have "normal" read permission # compared to the folder contents @@ -262,21 +329,11 @@ EOF # Config folder contents contain sensitive info # and should not be readable by normal users %{_sysconfdir}/zm/conf.d/README -%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf -%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/*.conf -%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf -%config(noreplace) %attr(644,root,root) /etc/zm/www/zoneminder.conf %config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder -%if 0%{?with_nginx} -%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.conf -%endif - -%{_tmpfilesdir}/zoneminder.conf %{_unitdir}/zoneminder.service %{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy -%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules %{_bindir}/zmsystemctl.pl %{_bindir}/zma @@ -307,8 +364,19 @@ EOF %{_libexecdir}/zoneminder/ %{_datadir}/zoneminder/ -%{_datadir}/applications/*%{name}.desktop +%{_datadir}/applications/*zoneminder.desktop +%files httpd +%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf +%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/0*.conf +%ghost %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/conf.d/zmcustom.conf +%config(noreplace) %{_sysconfdir}/zm/www/zoneminder.httpd.conf +%ghost %{_sysconfdir}/zm/www/zoneminder.conf +%config(noreplace) %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.httpd +%ghost %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules + +%{_unitdir}/zoneminder.service.d/zm-httpd.conf +%{_tmpfilesdir}/zoneminder.httpd.tmpfiles.conf %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/events %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/images @@ -318,14 +386,72 @@ EOF %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/cache/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder %dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload -%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder + +%files nginx +%config(noreplace) %attr(640,root,nginx) %{_sysconfdir}/zm/zm.conf +%config(noreplace) %attr(640,root,nginx) %{_sysconfdir}/zm/conf.d/*.conf +%ghost %attr(640,root,nginx) %{_sysconfdir}/zm/conf.d/zmcustom.conf +%config(noreplace) %{_sysconfdir}/zm/www/zoneminder.nginx.conf +%ghost %{_sysconfdir}/zm/www/zoneminder.conf +%config(noreplace) %{_sysconfdir}/zm/www/com.zoneminder.systemctl.rules.nginx +%ghost %{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules + +%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.php-fpm.conf + + +%{_unitdir}/zoneminder.service.d/zm-nginx.conf +%{_tmpfilesdir}/zoneminder.nginx.tmpfiles.conf +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/events +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/images +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/sock +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/swap +%dir %attr(755,nginx,nginx) %{_sharedstatedir}/zoneminder/temp +%dir %attr(755,nginx,nginx) %{_localstatedir}/cache/zoneminder +%dir %attr(755,nginx,nginx) %{_localstatedir}/log/zoneminder +%dir %attr(755,nginx,nginx) %{_localstatedir}/spool/zoneminder-upload %changelog -* Sun Apr 22 2018 Andrew Bauer - 1.31.42-1 -- Remove support for sysvinit a.k.a. el6 -- use desktop-file-install for new zoneminder.desktop file -- add new web cache folder -- 1.31.42 development snapshot +* Wed Nov 14 2018 Andrew Bauer - 1.32.2-2 +- Break into sub-packages + +* Sat Oct 13 2018 Andrew Bauer - 1.32.2-1 +- 1.32.2 release +- Bug fix release + +* Thu Oct 04 2018 Sérgio Basto - 1.32.1-2 +- Mass rebuild for x264 and/or x265 + +* Tue Oct 2 2018 Andrew Bauer - 1.32.1-1 +- 1.32.1 release +- Bug fix release + +* Wed Sep 12 2018 Andrew Bauer - 1.32.0-1 +- 1.32.0 release +- remove el6 (sys v init) support +- Make README name consistent across all supported distros +- remove jscalendar +- add requires php-json, zip +- support zm/conf.d folder +- support zm cache (busting) folder + +* Sun Aug 19 2018 Leigh Scott - 1.30.4-9 +- Rebuilt for Fedora 29 Mass Rebuild binutils issue + +* Fri Jul 27 2018 RPM Fusion Release Engineering - 1.30.4-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Thu Mar 08 2018 RPM Fusion Release Engineering - 1.30.4-7 +- Rebuilt for new ffmpeg snapshot + +* Thu Mar 01 2018 RPM Fusion Release Engineering - 1.30.4-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Thu Jan 18 2018 Leigh Scott - 1.30.4-5 +- Rebuilt for ffmpeg-3.5 git + +* Thu Aug 31 2017 RPM Fusion Release Engineering - 1.30.4-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild * Tue May 09 2017 Andrew Bauer - 1.30.4-1 - modify autosetup macro parameters diff --git a/distros/ubuntu1204/control b/distros/ubuntu1204/control index febcb9435..f1756c5e8 100644 --- a/distros/ubuntu1204/control +++ b/distros/ubuntu1204/control @@ -50,6 +50,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libdevice-serialport-perl ,libimage-info-perl ,libjson-any-perl + ,libjson-maybexs-perl ,libsys-mmap-perl [!hurd-any] ,liburi-encode-perl ,libwww-perl diff --git a/distros/ubuntu1204/rules b/distros/ubuntu1204/rules index aed63c110..f971e9cc3 100755 --- a/distros/ubuntu1204/rules +++ b/distros/ubuntu1204/rules @@ -28,7 +28,7 @@ override_dh_auto_configure: -DZM_CACHEDIR="/var/cache/zoneminder/cache" \ -DZM_DIR_EVENTS="/var/cache/zoneminder/events" \ -DZM_DIR_IMAGES="/var/cache/zoneminder/images" \ - -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" \ + -DZM_PATH_ZMS="/zm/cgi-bin/nph-zms" override_dh_clean: dh_clean $(MANPAGES1) diff --git a/distros/ubuntu1410/control b/distros/ubuntu1410/control index 4f979f8c2..5a873f133 100644 --- a/distros/ubuntu1410/control +++ b/distros/ubuntu1410/control @@ -32,7 +32,7 @@ Package: libzoneminder-perl Section: perl Architecture: all Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl, - libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, + libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, libjson-maybexs-perl, libsys-mmap-perl, liburi-encode-perl, libwww-perl Description: Perl libraries for ZoneMinder ZoneMinder is a video camera security and surveillance solution. diff --git a/distros/ubuntu1504_cmake_split_packages/control b/distros/ubuntu1504_cmake_split_packages/control index 5f313897f..b24d67cf2 100644 --- a/distros/ubuntu1504_cmake_split_packages/control +++ b/distros/ubuntu1504_cmake_split_packages/control @@ -45,7 +45,7 @@ Package: libzoneminder-perl Section: perl Architecture: all Depends: ${misc:Depends}, ${perl:Depends}, libdbi-perl, - libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, + libdevice-serialport-perl, libimage-info-perl, libjson-any-perl, libjson-maybexs-perl, libsys-mmap-perl, liburi-encode-perl, libwww-perl Description: Perl libraries for ZoneMinder ZoneMinder is a video camera security and surveillance solution. diff --git a/distros/ubuntu1604/control b/distros/ubuntu1604/control index 3054a2d55..d4fe74e79 100644 --- a/distros/ubuntu1604/control +++ b/distros/ubuntu1604/control @@ -53,6 +53,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends} ,libdevice-serialport-perl ,libimage-info-perl ,libjson-any-perl + ,libjson-maybexs-perl ,libsys-mmap-perl [!hurd-any] ,liburi-encode-perl ,libwww-perl diff --git a/distros/ubuntu1604/zoneminder.postinst b/distros/ubuntu1604/zoneminder.postinst index 17272c2df..ffde50283 100644 --- a/distros/ubuntu1604/zoneminder.postinst +++ b/distros/ubuntu1604/zoneminder.postinst @@ -22,7 +22,7 @@ if [ "$1" = "configure" ]; then if [ "$ZM_DB_HOST" = "localhost" ]; then - if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ]; then + if [ -e "/lib/systemd/system/mysql.service" ] || [ -e "/lib/systemd/system/mariadb.service" ] || [ -e "/etc/init.d/mysql" ]; then # Ensure zoneminder is stopped deb-systemd-invoke stop zoneminder.service || exit $? @@ -68,6 +68,7 @@ if [ "$1" = "configure" ]; then # Add any new PTZ control configurations to the database (will not overwrite) zmcamtool.pl --import >/dev/null 2>&1 + echo "Done Updating; starting ZoneMinder." else echo 'NOTE: MySQL/MariaDB not running; please start mysql and run dpkg-reconfigure zoneminder when it is running.' fi @@ -78,7 +79,6 @@ if [ "$1" = "configure" ]; then else echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)." fi - echo "Done Updating; starting ZoneMinder." deb-systemd-invoke restart zoneminder.service fi diff --git a/docs/api.rst b/docs/api.rst index a5bdd35f3..2f90b7fdf 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -5,7 +5,6 @@ This document will provide an overview of ZoneMinder's API. This is work in prog Overview ^^^^^^^^ - In an effort to further 'open up' ZoneMinder, an API was needed. This will allow quick integration with and development of ZoneMinder. @@ -13,6 +12,85 @@ 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. +Streaming Interface +^^^^^^^^^^^^^^^^^^^ +Developers working on their application often ask if there is an "API" to receive live streams, or recorded event streams. +It is possible to stream both live and recorded streams. This isn't strictly an "API" per-se (that is, it is not integrated +into the Cake PHP based API layer discussed here) and also why we've used the term "Interface" instead of an "API". + +Live Streams +~~~~~~~~~~~~~~ +What you need to know is that if you want to display "live streams", ZoneMinder sends you streaming JPEG images (MJPEG) +which can easily be rendered in a browser using an ``img src`` tag. + +For example: + +:: + + + +will display a live feed from monitor id 1, scaled down by 50% in quality and resized to 640x480px. + +* This assumes ``/zm/cgi-bin`` is your CGI_BIN path. Change it to what is correct in your system +* The "auth" token you see above is required if you use ZoneMinder authentication. To understand how to get the auth token, please read the "Login, Logout & API security" section below. +* The "connkey" parameter is essentially a random number which uniquely identifies a stream. If you don't specify a connkey, ZM will generate its own. It is recommended to generate a connkey because you can then use it to "control" the stream (pause/resume etc.) +* Instead of dealing with the "auth" token, you can also use ``&user=username&pass=password`` where "username" and "password" are your ZoneMinder username and password respectively. Note that this is not recommended because you are transmitting them in a URL and even if you use HTTPS, they may show up in web server logs. + + +PTZ on live streams +------------------- +PTZ commands are pretty cryptic in ZoneMinder. This is not meant to be an exhaustive guide, but just something to whet your appetite: + + +Lets assume you have a monitor, with ID=6. Let's further assume you want to pan it left. + +You'd need to send a: +``POST`` command to ``https://yourserver/zm/index.php`` with the following data payload in the command (NOT in the URL) + +``view=request&request=control&id=6&control=moveConLeft&xge=30&yge=30`` + +Obviously, if you are using authentication, you need to be logged in for this to work. + +Like I said, at this stage, this is only meant to get you started. Explore the ZoneMinder code and use "Inspect source" as you use PTZ commands in the ZoneMinder source code. +`control_functions.php `__ is a great place to start. + + +Pre-recorded (past event) streams +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Similar to live playback, if you have chosen to store events in JPEG mode, you can play it back using: + +:: + + + + +* This assumes ``/zm/cgi-bin`` is your CGI_BIN path. Change it to what is correct in your system +* This will playback event 293820, starting from frame 1 as an MJPEG stream +* Like before, you can add more parameters like ``scale`` etc. +* auth and connkey have the same meaning as before, and yes, you can replace auth by ``&user=usename&pass=password`` as before and the same security concerns cited above apply. + +If instead, you have chosen to use the MP4 (Video) storage mode for events, you can directly play back the saved video file: + +:: + + + +* This will play back the video recording for event 294690 + +What other parameters are supported? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The best way to answer this question is to play with ZoneMinder console. Open a browser, play back live or recorded feed, and do an "Inspect Source" to see what parameters +are generated. Change and observe. + + +Enabling API +^^^^^^^^^^^^ +A default ZoneMinder installs with APIs enabled. You can explictly enable/disable the APIs +via the Options->System menu by enabling/disabling ``OPT_USE_API``. Note that if you intend +to use APIs with 3rd party apps, such as zmNinja or others that use APIs, you should also +enable ``AUTH_HASH_LOGINS``. + Login, Logout & API Security ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The APIs tie into ZoneMinder's existing security model. This means if you have @@ -29,13 +107,13 @@ This means if you plan to use cuRL to experiment with these APIs, you first need :: - curl -XPOST -d "user=XXXX&pass=YYYY" -c cookies.txt http://yourzmip/zm/api/login.json + curl -XPOST -d "user=XXXX&pass=YYYY" -c cookies.txt http://yourzmip/zm/api/host/login.json Staring ZM 1.32.0, you also have a `logout` API that basically clears your session. It looks like this: :: - curl -b cookies.txt http://yourzmip/zm/api/logout.json + curl -b cookies.txt http://yourzmip/zm/api/host/logout.json **Login process for older versions of ZoneMinder** @@ -125,6 +203,22 @@ Return a list of all monitors curl http://server/zm/api/monitors.json +It is worthwhile to note that starting ZM 1.32.3 and beyond, this API also returns a ``Monitor_Status`` object per monitor. It looks like this: + +:: + + "Monitor_Status": { + "MonitorId": "2", + "Status": "Connected", + "CaptureFPS": "1.67", + "AnalysisFPS": "1.67", + "CaptureBandwidth": "52095" + } + + +If you don't see this in your API, you are running an older version of ZM. This gives you a very convenient way to check monitor status without calling the ``daemonCheck`` API described later. + + Retrieve monitor 1 ^^^^^^^^^^^^^^^^^^^ @@ -141,6 +235,13 @@ This API changes monitor 1 to Modect and Enabled :: curl -XPOST http://server/zm/api/monitors/1.json -d "Monitor[Function]=Modect&Monitor[Enabled]=1" + +Get Daemon Status of Monitor 1 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + curl http://server/zm/api/monitors/daemonStatus/id:1/daemon:zmc.json Add a monitor ^^^^^^^^^^^^^^ @@ -377,11 +478,13 @@ Create a Zone &Zone[MaxBlobs]=\ &Zone[OverloadFrames]=0" -PTZ Control APIs -^^^^^^^^^^^^^^^^ +PTZ Control Meta-Data 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 +Note that these APIs only retrieve control data related to PTZ. They don't actually move the camera. See the "PTZ on live streams" section to move the camera. + This returns all the control definitions: :: @@ -399,7 +502,97 @@ ZM APIs have various APIs that help you in determining host (aka ZM) daemon stat :: - 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) + + # Note that ZM 1.32.3 onwards has the same information in Monitors.json which is more reliable and works for multi-server too. + curl -XGET http://server/zm/api/host/daemonCheck.json # 1 = ZM running 0=not running + + # The API below uses "du" to calculate disk space. We no longer recommend you use it if you have many events. Use the Storage APIs instead, described later + 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) + + +Storage and Server APIs +^^^^^^^^^^^^^^^^^^^^^^^ + +ZoneMinder introduced many new options that allowed you to configure multiserver/multistorage configurations. While a part of this was available in previous versions, a lot of rework was done as part of ZM 1.31 and 1.32. As part of that work, a lot of new and useful APIs were added. Some of these are part of ZM 1.32 and others will be part of ZM 1.32.3 (of course, if you build from master, you can access them right away, or wait till a stable release is out. + + + +This returns storage data for my single server install. If you are using multi-storage, you'll see many such "Storage" entries, one for each storage defined: + +:: + + curl http://server/zm/api/storage.json + +Returns: + +:: + + { + "storage": [ + { + "Storage": { + "Id": "0", + "Path": "\/var\/cache\/zoneminder\/events", + "Name": "Default", + "Type": "local", + "Url": null, + "DiskSpace": "364705447651", + "Scheme": "Medium", + "ServerId": null, + "DoDelete": true + } + } + ] + } + + + +"DiskSpace" is the disk used in bytes. While this doesn't return disk space data as rich as ``/host/getDiskPercent``, it is much more efficient. + +Similarly, + +:: + + curl http://server/zm/api/servers.json + +Returns: + +:: + + { + "servers": [ + { + "Server": { + "Id": "1", + "Name": "server1", + "Hostname": "server1.mydomain.com", + "State_Id": null, + "Status": "Running", + "CpuLoad": "0.9", + "TotalMem": "6186237952", + "FreeMem": "156102656", + "TotalSwap": "536866816", + "FreeSwap": "525697024", + "zmstats": false, + "zmaudit": false, + "zmtrigger": false + } + } + ] + } + +This only works if you have a multiserver setup in place. If you don't it will return an empty array. + + +Further Reading +^^^^^^^^^^^^^^^^ +As described earlier, treat this document as an "introduction" to the important parts of the API and streaming interfaces. +There are several details that haven't yet been documented. Till they are, here are some resources: + +* zmNinja, the open source mobile app for ZoneMinder is 100% based on ZM APIs. Explore its `source code `__ to see how things work. +* Launch up ZM console in a browser, and do an "Inspect source". See how images are being rendered. Go to the networks tab of the inspect source console and look at network requests that are made when you pause/play/forward streams. +* If you still can't find an answer, post your question in the `forums `__ (not the github repo). + + diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst index e7c821838..6c686d7ed 100644 --- a/docs/installationguide/ubuntu.rst +++ b/docs/installationguide/ubuntu.rst @@ -35,7 +35,7 @@ guide you with a quick search. `releases page `_ for the latest release. - Alternatively, the ZoneMinder project team maintains a ppa, which is updated immediately + Alternatively, the ZoneMinder project team maintains a `PPA `_, which is updated immediately following a new release of ZoneMinder. To use this repository instead of the official Ubuntu repository, enter the following from the command line: @@ -43,6 +43,15 @@ guide you with a quick search. add-apt-repository ppa:iconnor/zoneminder + Please note that as of 1.32.0 We are creating a new PPA for each major version, as a means to prevent automatic upgrades from one major version to another. So instead of the above ppa line use the following: + + :: + + add-apt-repository ppa:iconnor/zoneminder-1.32 + + If you are on Trusty or Xenial, you may want to add both, as there are some packages for dependencies included in the old ppa. + + Update repo and upgrade. :: @@ -51,6 +60,7 @@ Update repo and upgrade. apt-get upgrade apt-get dist-upgrade + **Step 3:** Configure MySQL .. sidebar :: Note @@ -62,8 +72,10 @@ Update repo and upgrade. | /etc/alternatives/my.cnf -> /etc/mysql/mysql.cnf | /etc/mysql/mysql.cnf is a basic file -Certain new defaults in MySQL 5.7 are currently causing some issues with ZoneMinder, -the workaround is to modify the sql_mode setting of MySQL. +Certain new defaults in MySQL 5.7 cause some issues with ZoneMinder < 1.32.0, +the workaround is to modify the sql_mode setting of MySQL. Please note that these +changes are NOT required for ZoneMinder 1.32.0 and some people have reported them +causing problems in 1.32.0. To better manage the MySQL server it is recommended to copy the sample config file and replace the default my.cnf symbolic link. @@ -104,10 +116,12 @@ Restart MySQL **Step 5:** Configure the ZoneMinder Database +This step should not be required on ZoneMinder 1.32.0. + :: 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';" + mysql -uroot -p -e "grant lock tables,alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on zm.* to 'zmuser'@localhost identified by 'zmpass';" **Step 6:** Set permissions @@ -124,9 +138,16 @@ Set /etc/zm/zm.conf to root:www-data 740 and www-data access to content :: - a2enconf zoneminder - a2enmod cgi + a2enmod cgi a2enmod rewrite + a2enconf zoneminder + +You may also want to enable to following modules to improve caching performance + +:: + + a2enmod expires + a2enmod headers **Step 8:** Enable and start Zoneminder diff --git a/docs/userguide/definemonitor.rst b/docs/userguide/definemonitor.rst index 8f85feb98..bcd195570 100644 --- a/docs/userguide/definemonitor.rst +++ b/docs/userguide/definemonitor.rst @@ -150,7 +150,7 @@ Orientation WebSite ^^^^^^^ -This Source Type allows one to configure an arbitrary website as a non-reocrdable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue. +This Source Type allows one to configure an arbitrary website as a non-recordable, fully interactive, monitor in ZoneMinder. Note that sites with self-signed certificates will not display until the end user first manually navigates to the site and accpets the unsigned certificate. Also note that some sites will set an X-Frame option in the header, which discourages their site from being displayed within a frame. ZoneMinder will detect this condition and present a warning in the log. When this occurs, the end user can choose to install a browser plugin or extension to workaround this issue. Website URL Enter the full http or https url to the desired website. @@ -164,6 +164,29 @@ Height (pixels) Web Site Refresh If the website in question has static content, optionally enter a time period in seconds for ZoneMinder to refresh the content. +Storage Tab +----------- + +The storage section allows for each monitor to configure if and how video and audio are recorded. + +Save JPEGs + Records video in individual JPEG frames. Storing JPEG frames requires more storage space than h264 but it allows to view an event anytime while it is being recorded. + + * Disabled – video is not recorded as JPEG frames. If this setting is selected, then "Video Writer" should be enabled otherwise there is no video recording at all. + * Frames only – video is recorded in individual JPEG frames. + * Analysis images only (if available) – video is recorded in invidual JPEG frames with an overlay of the motion detection analysis information. Note that this overlay remains permanently visible in the frames. + * Frames + Analysis images (if available) – video is recorded twice, once as normal individual JPEG frames and once in invidual JPEG frames with analysis information overlaid. + +Video Writer + Records video in real video format. It provides much better compression results than saving JPEGs, thus longer video history can be stored. + + * Disabled – video is not recorded in video format. If this setting is selected, then "Save JPEGs" should be enabled otherwise there is no video recording at all. + * X264 Encode – the video or picture frames received from the camera are transcoded into h264 and stored as a video. This option is useful if the camera cannot natively stream h264. + * H264 Camera Passthrough – this option assumes that the camera is already sending an h264 stream. Video will be recorded as is, without any post-processing in zoneminder. Video characteristics such as bitrate, encoding mode, etc. should be set directly in the camera. + +Recording Audio + Check the box labeled "Whether to store the audio stream when saving an event." in order to save audio (if available) when events are recorded. + Timestamp Tab ------------- diff --git a/docs/userguide/introduction.rst b/docs/userguide/introduction.rst index 8da67a5e9..2d9985eb1 100644 --- a/docs/userguide/introduction.rst +++ b/docs/userguide/introduction.rst @@ -1,15 +1,14 @@ Introduction ============ -Welcome to ZoneMinder, the all-in-one Linux GPL'd security camera solution. +Welcome to ZoneMinder, the all-in-one security camera solution for Linux with GPL License. -Most commercial "security systems" are designed as a monitoring system that also records. Recording quality can vary from bad to unusable, locating the relevant video can range from challenging to impractical, and exporting can often only be done with the manual present. ZoneMinder was designed primarily to record, and allow easy searches and exporting. Recordings are of the best possible quality, easy to filter and find, and simple to export using any system with a web browser. It also monitors. +Commercial "security systems" are often designed as a monitoring system with little attention to recording quality. In such a system, locating and exporting relevant video can be challenging and often requires extensive human intervention. ZoneMinder was designed to provide the best possible record quality while allowing easy searching, filtering and exporting of security footage. -ZoneMinder is designed around a series of independent components that only function when necessary limiting any wasted resource and maximising the efficiency of your machine. A fairly ancient Pentium II PC should be able to track one camera per device at up to 25 frames per second with this dropping by half approximately for each additional camera on the same device. Additional cameras on other devices do not interact so can maintain this frame rate. Even monitoring several cameras still will not overload the CPU as frame processing is designed to synchronise with capture and not stall it. +ZoneMinder is designed around a series of independent components that only function when necessary, limiting any wasted resource and maximising the efficiency of your machine. An outdated Pentium II PC can have multiple recording devices connected to it, and it is able to track one camera per device at up to 25 frames per second, which drops by approximately half for each additional camera on the same device. Additional cameras on devices that do not interact with other devices can maintain the 25 frame rate per second. Monitoring several cameras will not overload the CPU as frame processing is designed to synchronise with capture. -As well as being fast ZoneMinder is designed to be friendly and even more than that, actually useful. As well as the fast video interface core it also comes with a user friendly and comprehensive PHP based web interface allowing you to control and monitor your cameras from home, at work, on the road, or even a web enabled cell phone. It supports variable web capabilities based on available bandwidth. The web interface also allows you to view events that your cameras have captured and archive them or review them time and again, or delete the ones you no longer wish to keep. The web pages directly interact with the core daemons ensuring full co-operation at all times. ZoneMinder can even be installed as a system service ensuring it is right there if your computer has to reboot for any reason. +A fast video interface core, a user-friendly and comprehensive PHP based web interface allows ZoneMinder to be efficient, friendly and most importantly useful. You can control and monitor your cameras from home, at work, on the road, or a web-enabled cell phone. It supports variable web capabilities based on available bandwidth. The web interface also allows you to view events that your cameras have captured, which can be archived, reviewed or deleted. The web application directly interacts with the core daemons ensuring full co-operation at all times. ZoneMinder can also be installed as a system service to reboot a system remotely. -The core of ZoneMinder is the capture and analysis of images and there is a highly configurable set of parameters that allow you to ensure that you can eliminate false positives whilst ensuring that anything you don't want to miss will be captured and saved. ZoneMinder allows you to define a set of 'zones' for each camera of varying sensitivity and functionality. This allows you to eliminate regions that you don't wish to track or define areas that will alarm if various thresholds are exceeded in conjunction with other zones. - -ZoneMinder is free, but if you do find it useful then please feel free to visit http://www.zoneminder.com/donate.html and help to fund future improvements to ZoneMinder. +The core of ZoneMinder is the capture and analysis of images and a highly configurable set of parameters that eliminate false positives whilst ensuring minimum loss of footage. For example, you can define a set of 'zones' for each camera of varying sensitivity and functionality. This eliminates zones that you don't wish to track or define areas that will alarm if various thresholds are exceeded in conjunction with other zones. +ZoneMinder is free under GPL License, but if you do find it useful, then please feel free to visit http://www.zoneminder.com/donate.html and help us fund our future improvements. diff --git a/docs/userguide/options/options_system.rst b/docs/userguide/options/options_system.rst index 53e560fe5..4f108442c 100644 --- a/docs/userguide/options/options_system.rst +++ b/docs/userguide/options/options_system.rst @@ -15,7 +15,7 @@ AUTH_HASH_SECRET - When ZoneMinder is running in hashed authenticated mode it is 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. +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 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. 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. @@ -38,6 +38,7 @@ OPT_CONTROL - ZoneMinder includes limited support for controllable cameras. A nu 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/misc/apache-cors.conf b/misc/apache-cors.conf new file mode 100644 index 000000000..10455d5a6 --- /dev/null +++ b/misc/apache-cors.conf @@ -0,0 +1,21 @@ +# This configuration is only needed for compatibility with zmninja + +# If not using VirtualHosts, copy or symlink this file into the Apache config folder +# If using VirtualHosts, then this config must be placed inside the appropriate +# directive. + +# Make sure you have enabled/loaded header manipulation modules +# For example, in Debian based distros the command is "sudo a2enmod headers" + +# zmNinja header permissions. Tweak to your needs + +Header always set Access-Control-Allow-Credentials true +#zmNinja's WKWebView will set the origin header as localhost:8080 +Header always set Access-Control-Allow-Origin "http://localhost:8080" +Header always set Access-Control-Request-Methods "Authorization" +Header always set Access-Control-Methods "OPTIONS,GET,POST,DELETE,PUT" +Header always set Access-Control-Allow-Headers "X-Requested-With, Content-Type, Authorization, Origin, Accept, client-security-token" +Header always set Access-Control-Expose-Headers "Content-Security-Policy, Location" +Header always set Access-Control-Max-Age "1000" +RewriteCond %{REQUEST_METHOD} OPTIONS +RewriteRule ^(.*)$ $1 [R=200,L] diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Base.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Base.pm.in index d80add206..2607aadf4 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Base.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Base.pm.in @@ -1,27 +1,3 @@ -# ========================================================================== -# -# ZoneMinder Base 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# ========================================================================== -# -# This module contains the common definitions and functions used by the rest -# of the ZoneMinder scripts -# package ZoneMinder::Base; use 5.006; @@ -82,11 +58,18 @@ Philip Coombes, Ephilip.coombes@zoneminder.comE =head1 COPYRIGHT AND LICENSE -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 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. +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 Street, Fifth Floor, Boston, MA 02110-1301 USA. =cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in index d48747703..92085b07b 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Config.pm.in @@ -309,6 +309,8 @@ 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 None by default. diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index c6136717a..eac13cb75 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -409,8 +409,8 @@ our @options = ( that is used to get notifications for alarms detected by ZoneMinder in real time. zmNinja requires this server for push notifications to mobile phones. This option only enables the server if its already installed. - Please visit https://github.com/pliablepixels/zmeventserver for installation - instructions. + Please visit the [zmeventserver project site](https://github.com/pliablepixels/zmeventserver) + for installation instructions. `, type => $types{boolean}, category => 'system', @@ -442,7 +442,7 @@ our @options = ( description => 'Your recaptcha site-key', help => q`You need to generate your keys from the Google reCaptcha website. - Please refer to https://www.google.com/recaptcha/ + Please refer to the [recaptcha project site](https://www.google.com/recaptcha/) for more details. `, requires => [ @@ -457,7 +457,7 @@ our @options = ( description => 'Your recaptcha secret-key', help => q`You need to generate your keys from the Google reCaptcha website. - Please refer to https://www.google.com/recaptcha/ + Please refer to the [recaptcha project site](https://www.google.com/recaptcha/) for more details. `, requires => [ @@ -674,9 +674,9 @@ our @options = ( 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. + from the [cambozola project site](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', @@ -690,9 +690,9 @@ our @options = ( 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 + from the [cambozola project site](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. `, @@ -2721,7 +2721,8 @@ our @options = ( 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. + the web console under Options. For more details on what information + we collect, please refer to our [privacy](?view=privacy) statement. `, type => $types{boolean}, category => 'system', @@ -3882,6 +3883,15 @@ our @options = ( readonly => 1, category => 'dynamic', }, + { + name => 'ZM_SHOW_PRIVACY', + default => 'yes', + description => 'Present the privacy statment', + help => '', + type => $types{boolean}, + readonly => 1, + category => 'dynamic', + }, { name => 'ZM_SSMTP_MAIL', default => 'no', @@ -3898,7 +3908,7 @@ our @options = ( 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 + Please visit the ZoneMinder [SSMTP Wiki page](http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder) for setup and configuration help. `, type => $types{boolean}, diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm new file mode 100644 index 000000000..6cb55d3a6 --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Dahua.pm @@ -0,0 +1,362 @@ +package ZoneMinder::Control::Dahua; + +use 5.8.0; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +our $REALM = ''; +our $USERNAME = ''; +our $PASSWORD = ''; +our $ADDRESS = ''; +our $PROTOCOL = 'http://'; + +use Time::HiRes qw(usleep); + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); +use ZoneMinder::Database qw(zmDbConnect); + +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(); + + # The Dahua camera firmware API supports the concept of having multiple + # channels on a single IP controller. + # As most cameras only have a single channel, and there is no similar + # information model in Zoneminder, I'm hardcoding the first and default + # channel "0", here. + $self->{dahua_channel_number} = "0"; + + if ( ( $self->{Monitor}->{ControlAddress} =~ /^(?https?:\/\/)?(?[^:@]+)?:?(?[^\/@]+)?@?(?
.*)$/ ) ) { + $PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL}; + $USERNAME = $+{USERNAME} if $+{USERNAME}; + $PASSWORD = $+{PASSWORD} if $+{PASSWORD}; + $ADDRESS = $+{ADDRESS} if $+{ADDRESS}; + } else { + Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress}); + $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} = 'closed'; +# 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 $get_config_url = $PROTOCOL . $ADDRESS . "/cgi-bin/configManager.cgi?action=getConfig&name=Ptz"; + my $req = HTTP::Request->new(GET=>$get_config_url); + my $res = $self->{ua}->request($req); + + if ($res->is_success) { + $self->{state} = 'open'; + return; + } + + if ( $res->status_line() eq '401 Unauthorized' ) { + my $headers = $res->headers(); + foreach my $k (keys %$headers) { + Debug("Initial Header $k => $$headers{$k}"); + } + + if ($$headers{'www-authenticate'}) { + my ($auth, $tokens) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/; + if ($tokens =~ /\w+="([^"]+)"/i) { + if ($REALM ne $1) { + $REALM = $1; + Debug("Changing REALM to '" . $REALM . "'"); + $self->{ua}->credentials($ADDRESS, $REALM, $USERNAME, $PASSWORD); + my $req = HTTP::Request->new(GET=>$get_config_url); + $res = $self->{ua}->request($req); + if ($res->is_success()) { + $self->{state} = 'open'; + return; + } + Debug('Authentication still failed after updating REALM' . $res->status_line); + $headers = $res->headers(); + foreach my $k ( keys %$headers ) { + Debug("Initial Header $k => $$headers{$k}"); + } # end foreach + } else { + Error('Authentication failed, not a REALM problem'); + } + } else { + Error('Failed to match realm in tokens'); + } # end if + } else { + Error('No WWW-Authenticate Header'); + } # end if headers + } # end if $res->status_line() eq '401 Unauthorized' +} + +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 sendGetRequest { + my $self = shift; + my $url_path = shift; + + my $result = undef; + + my $url = $PROTOCOL . $ADDRESS . $url_path; + my $req = HTTP::Request->new(GET=>$url); + + my $res = $self->{ua}->request($req); + + if ($res->is_success) { + $result = !undef; + } else { + if ($res->status_line() eq '401 Unauthorized') { + Debug("Error check failed, trying again: USERNAME: $USERNAME realm: $REALM password: " . $PASSWORD); + Debug("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()); + } + } + return($result); +} + +sub sendPtzCommand +{ + my $self = shift; + my $action = shift; + my $command_code = shift; + my $arg1 = shift; + my $arg2 = shift; + my $arg3 = shift; + + my $channel = $self->{dahua_channel_number}; + + my $url_path = "/cgi-bin/ptz.cgi?"; + $url_path .= "action=" . $action . "&"; + $url_path .= "channel=" . $channel . "&"; + $url_path .= "code=" . $command_code . "&"; + $url_path .= "arg1=" . $arg1 . "&"; + $url_path .= "arg2=" . $arg2 . "&"; + $url_path .= "arg3=" . $arg3; + $self->sendGetRequest($url_path); +} +sub sendMomentaryPtzCommand +{ + my $self = shift; + my $command_code = shift; + my $arg1 = shift; + my $arg2 = shift; + my $arg3 = shift; + my $duration_ms = shift; + + $self->sendPtzCommand("start", $command_code, $arg1, $arg2, $arg3); + my $duration_ns = $duration_ms * 1000; + usleep($duration_ns); + $self->sendPtzCommand("stop", $command_code, $arg1, $arg2, $arg3); +} + +sub moveRelUpLeft +{ + my $self = shift; + Debug("Move Up Left"); + $self->sendMomentaryPtzCommand("LeftUp", 4, 4, 0, 500); +} + +sub moveRelUp +{ + my $self = shift; + Debug("Move Up"); + $self->sendMomentaryPtzCommand("Up", 0, 4, 0, 500); +} + +sub moveRelUpRight +{ + my $self = shift; + Debug("Move Up Right"); + $self->sendMomentaryPtzCommand("RightUp", 0, 4, 0, 500); +} + +sub moveRelLeft +{ + my $self = shift; + Debug("Move Left"); + $self->sendMomentaryPtzCommand("Left", 0, 4, 0, 500); +} + +sub moveRelRight +{ + my $self = shift; + Debug("Move Right"); + $self->sendMomentaryPtzCommand("Right", 0, 4, 0, 500); +} + +sub moveRelDownLeft +{ + my $self = shift; + Debug("Move Down Left"); + $self->sendMomentaryPtzCommand("LeftDown", 4, 4, 0, 500); +} + +sub moveRelDown +{ + my $self = shift; + Debug("Move Down"); + $self->sendMomentaryPtzCommand("Down", 0, 4, 0, 500); +} + +sub moveRelDownRight +{ + my $self = shift; + Debug("Move Down Right"); + $self->sendMomentaryPtzCommand("RightDown", 4, 4, 0, 500); +} + +sub zoomRelTele +{ + my $self = shift; + Debug("Zoom Relative Tele"); + $self->sendMomentaryPtzCommand("ZoomTele", 0, 0, 0, 500); +} + +sub zoomRelWide +{ + my $self = shift; + Debug("Zoom Relative Wide"); + $self->sendMomentaryPtzCommand("ZoomWide", 0, 0, 0, 500); +} + + +sub presetClear +{ + my $self = shift; + my $params = shift; + my $preset_id = $self->getParam($params, 'preset'); + $self->sendPtzCommand("start", "ClearPreset", 0, $preset_id, 0); +} + + +sub presetSet +{ + my $self = shift; + my $params = shift; + + my $preset_id = $self->getParam($params, 'preset'); + + my $dbh = zmDbConnect(1); + my $sql = 'SELECT * FROM ControlPresets WHERE MonitorId = ? AND Preset = ?'; + my $sth = $dbh->prepare($sql) + or Fatal("Can't prepare sql '$sql': " . $dbh->errstr()); + my $res = $sth->execute($self->{Monitor}->{Id}, $preset_id) + or Fatal("Can't execute sql '$sql': " . $sth->errstr()); + my $control_preset_row = $sth->fetchrow_hashref(); + my $new_label_name = $control_preset_row->{'Label'}; + + $self->sendPtzCommand("start", "SetPreset", 0, $preset_id, 0); + $self->sendPtzCommand("start", "SetPresetName", $preset_id, $new_label_name, 0); +} + +sub presetGoto +{ + my $self = shift; + my $params = shift; + my $preset_id = $self->getParam($params, 'preset'); + + $self->sendPtzCommand("start", "GotoPreset", 0, $preset_id, 0); +} + +1; +__END__ + +=head1 NAME + +ZoneMinder::Control::Dahua - Perl module for Dahua cameras + +=head1 SYNOPSIS + +use ZoneMinder::Control::Dahua; +place this in /usr/share/perl5/ZoneMinder/Control + +=head1 DESCRIPTION + +This module is an implementation of the Dahua IP camera HTTP control API. + +=head2 EXPORT + +None by default. + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2018 ZoneMinder LLC + +This library 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 library 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCAMIOS.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCAMIOS.pm index 8465ee472..ec3bbfc70 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCAMIOS.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/IPCAMIOS.pm @@ -16,7 +16,7 @@ # # 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. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # ========================================================================== # diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/PSIA.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/PSIA.pm new file mode 100644 index 000000000..fe067fe1f --- /dev/null +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/PSIA.pm @@ -0,0 +1,365 @@ +package ZoneMinder::Control::PSIA; + +use 5.006; +use strict; +use warnings; + +require ZoneMinder::Base; +require ZoneMinder::Control; + +our @ISA = qw(ZoneMinder::Control); + +our $REALM = 'TV-IP450PI'; +our $USERNAME = 'admin'; +our $PASSWORD = ''; +our $ADDRESS = ''; +our $PROTOCOL = 'http://'; + +use ZoneMinder::Logger qw(:all); +use ZoneMinder::Config qw(:all); +use ZoneMinder::Database qw(zmDbConnect); + +sub open +{ + my $self = shift; + $self->loadMonitor(); + + if ( ( $self->{Monitor}->{ControlAddress} =~ /^(?https?:\/\/)?(?[^:@]+)?:?(?[^\/@]+)?@?(?
.*)$/ ) ) { + $PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL}; + $USERNAME = $+{USERNAME} if $+{USERNAME}; + $PASSWORD = $+{PASSWORD} if $+{PASSWORD}; + $ADDRESS = $+{ADDRESS} if $+{ADDRESS}; + } else { + Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress}); + $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} = 'closed'; + 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=>$PROTOCOL . $ADDRESS . "/PSIA/PTZ/channels"); + my $res = $self->{ua}->request($req); + + if ($res->is_success) { + $self->{state} = 'open'; + return; + } elsif (! $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 WWW-Authenticate header"); + } # end if www-authenticate header + } # end if $res->status_line() eq '401 Unauthorized' + } # end elsif ! $res->is_success +} + +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 sendGetRequest { + my $self = shift; + my $url_path = shift; + + my $result = undef; + + my $url = $PROTOCOL . $ADDRESS . $url_path; + my $req = HTTP::Request->new(GET=>$url); + + 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: " . $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()); + } + } + return($result); +} +sub sendPutRequest { + my $self = shift; + my $url_path = shift; + my $content = shift; + + my $result = undef; + + my $url = $PROTOCOL . $ADDRESS . $url_path; + my $req = HTTP::Request->new(PUT=>$url); + if(defined($content)) { + $req->content_type("application/x-www-form-urlencoded; charset=UTF-8"); + $req->content('' . "\n" . $content); + } + + 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: " . $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); +} +sub sendDeleteRequest { + my $self = shift; + my $url_path = shift; + + my $result = undef; + + my $url = $PROTOCOL . $ADDRESS . $url_path; + my $req = HTTP::Request->new(DELETE=>$url); + 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: " . $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); +} + +sub move +{ + my $self = shift; + my $panPercentage = shift; + my $tiltPercentage = shift; + my $zoomPercentage = shift; + + my $cmd = "set_relative_pos&posX=$panSteps&posY=$tiltSteps"; + my $ptzdata = ''; + $ptzdata .= '' . $panPercentage . ''; + $ptzdata .= '' . $tiltPercentage . ''; + $ptzdata .= '' . $zoomPercentage . ''; + $ptzdata .= '500'; + $ptzdata .= ''; + $self->sendPutRequest("/PSIA/PTZ/channels/1/momentary", $ptzdata); +} + +sub moveRelUpLeft +{ + my $self = shift; + Debug( "Move Up Left" ); + $self->move(-50, 50, 0); +} + +sub moveRelUp +{ + my $self = shift; + Debug( "Move Up" ); + $self->move(0, 50, 0); +} + +sub moveRelUpRight +{ + my $self = shift; + Debug( "Move Up Right" ); + $self->move(50, 50, 0); +} + +sub moveRelLeft +{ + my $self = shift; + Debug( "Move Left" ); + $self->move(-50, 0, 0); +} + +sub moveRelRight +{ + my $self = shift; + Debug( "Move Right" ); + $self->move(50, 0, 0); +} + +sub moveRelDownLeft +{ + my $self = shift; + Debug( "Move Down Left" ); + $self->move(-50, -50, 0); +} + +sub moveRelDown +{ + my $self = shift; + Debug( "Move Down" ); + $self->move(0, -50, 0); +} + +sub moveRelDownRight +{ + my $self = shift; + Debug( "Move Down Right" ); + $self->move(50, -50, 0); +} + +sub zoomRelTele +{ + my $self = shift; + Debug("Zoom Relative Tele"); + $self->move(0, 0, 50); +} + +sub zoomRelWide +{ + my $self = shift; + Debug("Zoom Relative Wide"); + $self->move(0, 0, -50); +} + + +sub presetClear +{ + my $self = shift; + my $params = shift; + my $preset_id = $self->getParam($params, 'preset'); + my $url_path = "/PSIA/PTZ/channels/1/presets/" . $preset_id; + $self->sendDeleteRequest($url_path); +} + + +sub presetSet +{ + my $self = shift; + my $params = shift; + + my $preset_id = $self->getParam($params, 'preset'); + + my $dbh = zmDbConnect(1); + my $sql = 'SELECT * FROM ControlPresets WHERE MonitorId = ? AND Preset = ?'; + my $sth = $dbh->prepare($sql) + or Fatal("Can't prepare sql '$sql': " . $dbh->errstr()); + my $res = $sth->execute($self->{Monitor}->{Id}, $preset_id) + or Fatal("Can't execute sql '$sql': " . $sth->errstr()); + my $control_preset_row = $sth->fetchrow_hashref(); + my $new_label_name = $control_preset_row->{'Label'}; + + my $url_path = "/PSIA/PTZ/channels/1/presets/" . $preset_id; + my $ptz_preset_data = ''; + $ptz_preset_data .= '' . $preset_id . ''; + $ptz_preset_data .= '' . $new_label_name . ''; + $ptz_preset_data .= ''; + $self->sendPutRequest($url_path, $ptz_preset_data); +} + +sub presetGoto +{ + my $self = shift; + my $params = shift; + my $preset_id = $self->getParam($params, 'preset'); + + my $url_path = '/PSIA/PTZ/channels/1/presets/' . $preset_id . '/goto'; + + $self->sendPutRequest($url_path); +} + + +1; +__END__ + +=head1 NAME + +ZoneMinder::Control::PSIA - Perl module for cameras implementing the PSIA +(Physical Security Interoperability Alliance), IP Media Devices API +specification + +=head1 SYNOPSIS + +use ZoneMinder::Control::PSIA; +place this in /usr/share/perl5/ZoneMinder/Control + +=head1 DESCRIPTION + +This has so far been tested with: +- Trendnet TV-IP450PI + +=head2 EXPORT + +None by default. + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2018 ZoneMinder LLC + +This library 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 library 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Reolink.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Reolink.pm index 370d463c3..7a2f72fc6 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Reolink.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Reolink.pm @@ -16,7 +16,7 @@ # # 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. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # ========================================================================== # diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Control/Trendnet.pm b/scripts/ZoneMinder/lib/ZoneMinder/Control/Trendnet.pm index 30534c516..4ee710297 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Control/Trendnet.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Control/Trendnet.pm @@ -30,10 +30,10 @@ sub open { $self->loadMonitor(); if ( ( $self->{Monitor}->{ControlAddress} =~ /^(?https?:\/\/)?(?[^:@]+)?:?(?[^\/@]+)?@?(?
.*)$/ ) ) { - $PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL}; - $USERNAME = $+{USERNAME} if $+{USERNAME}; - $PASSWORD = $+{PASSWORD} if $+{PASSWORD}; - $ADDRESS = $+{ADDRESS} if $+{ADDRESS}; + $PROTOCOL = $+{PROTOCOL} if $+{PROTOCOL}; + $USERNAME = $+{USERNAME} if $+{USERNAME}; + $PASSWORD = $+{PASSWORD} if $+{PASSWORD}; + $ADDRESS = $+{ADDRESS} if $+{ADDRESS}; } else { Error('Failed to parse auth from address ' . $self->{Monitor}->{ControlAddress}); $ADDRESS = $self->{Monitor}->{ControlAddress}; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm index 12e3c7065..dcc797bd4 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Database.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Database.pm @@ -41,17 +41,18 @@ our @ISA = qw(Exporter ZoneMinder::Base); # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( - 'functions' => [ qw( + functions => [ qw( zmDbConnect zmDbDisconnect zmDbGetMonitors zmDbGetMonitor zmDbGetMonitorAndControl + zmDbDo ) ] ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; -our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); +our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } ); our @EXPORT = qw(); @@ -66,8 +67,6 @@ our $VERSION = $ZoneMinder::Base::VERSION; use ZoneMinder::Logger qw(:all); use ZoneMinder::Config qw(:all); -use Carp; - our $dbh = undef; sub zmDbConnect { @@ -93,7 +92,7 @@ sub zmDbConnect { my $sslOptions = ''; if ( $Config{ZM_DB_SSL_CA_CERT} ) { - $sslOptions = ';'.join(';', + $sslOptions = join(';','', 'mysql_ssl=1', 'mysql_ssl_ca_file='.$Config{ZM_DB_SSL_CA_CERT}, 'mysql_ssl_client_key='.$Config{ZM_DB_SSL_CLIENT_KEY}, @@ -102,8 +101,9 @@ sub zmDbConnect { } eval { - $dbh = DBI->connect( 'DBI:mysql:database='.$Config{ZM_DB_NAME} - .$socket . $sslOptions . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '') + $dbh = DBI->connect( + 'DBI:mysql:database='.$Config{ZM_DB_NAME} + .$socket . $sslOptions . ($options?join(';', '', map { $_.'='.$$options{$_} } keys %{$options} ) : '') , $Config{ZM_DB_USER} , $Config{ZM_DB_PASS} ); @@ -125,7 +125,7 @@ sub zmDbConnect { sub zmDbDisconnect { if ( defined( $dbh ) ) { - $dbh->disconnect(); + $dbh->disconnect() or Error('Error disconnecting db? ' . $dbh->errstr()); $dbh = undef; } } @@ -141,7 +141,7 @@ sub zmDbGetMonitors { zmDbConnect(); my $function = shift || DB_MON_ALL; - my $sql = "select * from Monitors"; + my $sql = 'SELECT * FROM Monitors'; if ( $function ) { if ( $function == DB_MON_CAPT ) { @@ -156,26 +156,38 @@ sub zmDbGetMonitors { $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 $sth = $dbh->prepare_cached( $sql ); + if ( ! $sth ) { + Error("Can't prepare '$sql': ".$dbh->errstr()); + return undef; + } + my $res = $sth->execute(); + if ( ! $res ) { + Error("Can't execute '$sql': ".$sth->errstr()); + return undef; + } my @monitors; while( my $monitor = $sth->fetchrow_hashref() ) { push( @monitors, $monitor ); } $sth->finish(); - return( \@monitors ); + return \@monitors; } sub zmSQLExecute { my $sql = shift; - - 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 $sth = $dbh->prepare_cached( $sql ); + if ( ! $sth ) { + Error("Can't prepare '$sql': ".$dbh->errstr()); + return undef; + } + my $res = $sth->execute( @_ ); + if ( ! $res ) { + Error("Can't execute '$sql': ".$sth->errstr()); + return undef; + } return 1; } @@ -185,17 +197,22 @@ sub zmDbGetMonitor { my $id = shift; if ( !defined($id) ) { - croak("Undefined id in zmDbgetMonitor"); + Error('Undefined id in zmDbgetMonitor'); return undef ; } 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 $sth = $dbh->prepare_cached($sql); + if ( !$sth ) { + Error("Can't prepare '$sql': ".$dbh->errstr()); + return undef; + } + my $res = $sth->execute($id); + if ( $res ) { + Error("Can't execute '$sql': ".$sth->errstr()); + return undef; + } my $monitor = $sth->fetchrow_hashref(); - return $monitor; } @@ -204,25 +221,28 @@ sub zmDbGetMonitorAndControl { my $id = shift; - return( undef ) if ( !defined($id) ); + return undef if !defined($id); - my $sql = "SELECT C.*,M.*,C.Protocol + my $sql = 'SELECT C.*,M.*,C.Protocol FROM Monitors as M INNER JOIN Controls as C on (M.ControlId = C.Id) - WHERE M.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 $sth = $dbh->prepare_cached($sql); + if ( !$sth ) { + Error("Can't prepare '$sql': ".$dbh->errstr()); + return undef; + } + my $res = $sth->execute( $id ); + if ( !$res ) { + Error("Can't execute '$sql': ".$sth->errstr()); + return undef; + } my $monitor = $sth->fetchrow_hashref(); - - return( $monitor ); + return $monitor; } sub start_transaction { - #my ( $caller, undef, $line ) = caller; -#$openprint::log->debug("Called start_transaction from $caller : $line"); my $d = shift; $d = $dbh if ! $d; my $ac = $d->{AutoCommit}; @@ -231,68 +251,54 @@ sub start_transaction { } # end sub start_transaction sub end_transaction { - #my ( $caller, undef, $line ) = caller; -#$openprint::log->debug("Called end_transaction from $caller : $line"); - my ( $d, $ac ) = @_; -if ( ! defined $ac ) { - Error("Undefined ac"); -} + my ( $d, $ac ) = @_; + if ( ! defined $ac ) { + Error("Undefined ac"); + } $d = $dbh if ! $d; if ( $ac ) { - #$log->debug("Committing"); $d->commit(); } # end if $d->{AutoCommit} = $ac; } # end sub end_transaction + +# Basic execution of $dbh->do but with some pretty logging of the sql on error. +# Returns 1 on success, 0 on error +sub zmDbDo { + my $sql = shift; + if ( ! $dbh->do($sql, undef, @_) ) { + $sql =~ s/\?/'%s'/; + Error(sprintf("Failed $sql :", @_).$dbh->errstr()); + return 0; + } + return 1; +} + 1; __END__ -# Below is stub documentation for your module. You'd better edit it! =head1 NAME -ZoneMinder::Database - Perl extension for blah blah blah +ZoneMinder::Database - Perl module containing database functions used in ZM =head1 SYNOPSIS use ZoneMinder::Database; -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. +zmDbConnect +zmDbDisconnect +zmDbGetMonitors +zmDbGetMonitor +zmDbGetMonitorAndControl +zmDbDo =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/Event.pm b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm index fc09e3f3f..5916f8cdb 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Event.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Event.pm @@ -35,7 +35,6 @@ require Date::Manip; require File::Find; require File::Path; require File::Copy; -require File::Slurp; require File::Basename; require Number::Bytes::Human; @@ -71,6 +70,7 @@ $serial = $primary_key = 'Id'; Frames AlarmFrames DefaultVideo + SaveJPEGs TotScore AvgScore MaxScore @@ -84,6 +84,7 @@ $serial = $primary_key = 'Id'; StateId Orientation DiskSpace + Scheme ); use POSIX; @@ -169,6 +170,8 @@ sub Path { sub Scheme { my $self = shift; + $$self{Scheme} = shift if @_; + if ( ! $$self{Scheme} ) { if ( $$self{RelativePath} ) { if ( $$self{RelativePath} =~ /^\d+\/\d{4}\-\d{2}\-\d{2}\/\d+$/ ) { @@ -239,7 +242,7 @@ sub LinkPath { '.'.$$event{Id} ); } elsif ( $$event{Path} ) { - if ( ( $$event{Path} =~ /^(\d+\/\d{4}\/\d{2}\/\d{2})/ ) ) { + if ( ( $event->RelativePath() =~ /^(\d+\/\d{4}\/\d{2}\/\d{2})/ ) ) { $$event{LinkPath} = $1.'/.'.$$event{Id}; } else { Error("Unable to get LinkPath from Path for $$event{Id} $$event{Path}"); @@ -255,6 +258,33 @@ sub LinkPath { return $$event{LinkPath}; } # end sub LinkPath +sub createPath { + makePath($_[0]->Path()); +} + +sub createLinkPath { + my $LinkPath = $_[0]->LinkPath(); + my $EventPath = $_[0]->EventPath(); + if ( $LinkPath ) { + if ( !symlink($EventPath, $LinkPath) ) { + Error("Failed symlinking $EventPath to $LinkPath"); + } + } +} + +sub idPath { + return sprintf('%s/.%d', $_[0]->Path(), $_[0]->{Id}); +} + +sub createIdFile { + my $event = shift; + my $idFile = $event->idPath(); + open( my $ID_FP, '>', $idFile ) + or Error("Can't open $idFile: $!"); + close($ID_FP); + setFileOwner($idFile); +} + sub GenerateVideo { my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_; @@ -290,11 +320,11 @@ sub GenerateVideo { my $file_size = 'S'.$size; push( @file_parts, $file_size ); } - my $video_file = "$video_name-".$file_parts[0]."-".$file_parts[1].".$format"; + my $video_file = join('-', $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" ); + Info("Creating video file $video_file for event $self->{Id}"); - my $frame_rate = sprintf( "%.2f", $self->{Frames}/$self->{FullLength} ); + my $frame_rate = sprintf('%.2f', $self->{Frames}/$self->{FullLength}); if ( $rate ) { if ( $rate != 1.0 ) { $frame_rate *= $rate; @@ -325,19 +355,19 @@ sub GenerateVideo { .$Config{ZM_FFMPEG_OUTPUT_OPTIONS} ." '$video_file' > ffmpeg.log 2>&1" ; - Debug( $command."\n" ); + Debug($command); my $output = qx($command); my $status = $? >> 8; if ( $status ) { - Error( "Unable to generate video, check $event_path/ffmpeg.log for details"); + Error("Unable to generate video, check $event_path/ffmpeg.log for details"); return; } - Info( "Finished $video_file\n" ); + Info("Finished $video_file"); return $event_path.'/'.$video_file; } else { - Info( "Video file $video_file already exists for event $self->{Id}\n" ); + Info("Video file $video_file already exists for event $self->{Id}"); return $event_path.'/'.$video_file; } return; @@ -345,57 +375,49 @@ sub GenerateVideo { sub delete { my $event = $_[0]; - if ( ! ( $event->{Id} and $event->{MonitorId} and $event->{StartTime} ) ) { - my ( $caller, undef, $line ) = caller; - Warning("Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime} from $caller:$line\n"); - return; - } - if ( ! -e $event->Storage()->Path() ) { - Warning("Not deleting event because storage path doesn't exist"); - return; - } - Info("Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}\n"); - $ZoneMinder::Database::dbh->ping(); - $ZoneMinder::Database::dbh->begin_work(); - #$event->lock_and_load(); + my $in_zmaudit = ( $0 =~ 'zmaudit.pl$'); - { - my $sql = 'DELETE FROM Frames WHERE EventId=?'; - my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql) - or Error( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() ); - my $res = $sth->execute($event->{Id}) - or Error( "Can't execute '$sql': ".$sth->errstr() ); - $sth->finish(); + if ( ! $in_zmaudit ) { + if ( ! ( $event->{Id} and $event->{MonitorId} and $event->{StartTime} ) ) { + # zmfilter shouldn't delete anything in an odd situation. zmaudit will though. + my ( $caller, undef, $line ) = caller; + Warning("$0 Can't Delete event $event->{Id} from Monitor $event->{MonitorId} StartTime:". + (defined($event->{StartTime})?$event->{StartTime}:'undef')." from $caller:$line"); + return; + } + if ( !($event->Storage()->Path() and -e $event->Storage()->Path()) ) { + Warning('Not deleting event because storage path doesn\'t exist'); + return; + } + } + + if ( $$event{Id} ) { + # Need to have an event Id if we are to delete from the db. + Info("Deleting event $event->{Id} from Monitor $event->{MonitorId} StartTime:$event->{StartTime}"); + $ZoneMinder::Database::dbh->ping(); + + $ZoneMinder::Database::dbh->begin_work(); + #$event->lock_and_load(); + + ZoneMinder::Database::zmDbDo('DELETE FROM Frames WHERE EventId=?', $$event{Id}); + if ( $ZoneMinder::Database::dbh->errstr() ) { + $ZoneMinder::Database::dbh->commit(); + return; + } + ZoneMinder::Database::zmDbDo('DELETE FROM Stats WHERE EventId=?', $$event{Id}); if ( $ZoneMinder::Database::dbh->errstr() ) { $ZoneMinder::Database::dbh->commit(); return; } - $sql = 'DELETE FROM Stats WHERE EventId=?'; - $sth = $ZoneMinder::Database::dbh->prepare_cached($sql) - or Error("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr()); - $res = $sth->execute($event->{Id}) - or Error("Can't execute '$sql': ".$sth->errstr()); - $sth->finish(); - if ( $ZoneMinder::Database::dbh->errstr() ) { - $ZoneMinder::Database::dbh->commit(); - return; - } + # Do it individually to avoid locking up the table for new events + ZoneMinder::Database::zmDbDo('DELETE FROM Events WHERE Id=?', $$event{Id}); + $ZoneMinder::Database::dbh->commit(); } -# Do it individually to avoid locking up the table for new events - { - my $sql = 'DELETE FROM Events WHERE Id=?'; - my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql) - or Error("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr()); - my $res = $sth->execute($event->{Id}) - or Error("Can't execute '$sql': ".$sth->errstr()); - $sth->finish(); - } - $ZoneMinder::Database::dbh->commit(); - if ( (! $Config{ZM_OPT_FAST_DELETE}) and $event->Storage()->DoDelete() ) { - $event->delete_files( ); + if ( ( $in_zmaudit or (!$Config{ZM_OPT_FAST_DELETE})) and $event->Storage()->DoDelete() ) { + $event->delete_files(); } else { Debug('Not deleting event files from '.$event->Path().' for speed.'); } @@ -404,11 +426,11 @@ sub delete { sub delete_files { my $event = shift; - my $Storage = @_ ? $_[0] : new ZoneMinder::Storage( $$event{StorageId} ); + my $Storage = @_ ? $_[0] : new ZoneMinder::Storage($$event{StorageId}); my $storage_path = $Storage->Path(); if ( ! $storage_path ) { - Error("Empty storage path when deleting files for event $$event{Id} with storage id $$event{StorageId} "); + Error("Empty storage path when deleting files for event $$event{Id} with storage id $$event{StorageId}"); return; } @@ -417,13 +439,13 @@ sub delete_files { return; } my $event_path = $event->RelativePath(); - Debug("Deleting files for Event $$event{Id} from $storage_path/$event_path."); + Debug("Deleting files for Event $$event{Id} from $storage_path/$event_path, scheme is $$event{Scheme}."); if ( $event_path ) { ( $storage_path ) = ( $storage_path =~ /^(.*)$/ ); # De-taint ( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint my $deleted = 0; - if ( $$Storage{Type} eq 's3fs' ) { + if ( $$Storage{Type} and ( $$Storage{Type} eq 's3fs' ) ) { my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$Storage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ ); eval { require Net::Amazon::S3; @@ -440,23 +462,23 @@ sub delete_files { if ( $bucket->delete_key($event_path) ) { $deleted = 1; } else { - Error("Failed to delete from S3:".$s3->err . ": " . $s3->errstr); + Error('Failed to delete from S3:'.$s3->err . ': ' . $s3->errstr); } }; Error($@) if $@; - } - if ( ! $deleted ) { + } + if ( !$deleted ) { my $command = "/bin/rm -rf $storage_path/$event_path"; - ZoneMinder::General::executeShellCommand( $command ); + ZoneMinder::General::executeShellCommand($command); } } if ( $event->Scheme() eq 'Deep' ) { my $link_path = $event->LinkPath(); - Debug("Deleting files for Event $$event{Id} from $storage_path/$link_path."); + Debug("Deleting link for Event $$event{Id} from $storage_path/$link_path."); if ( $link_path ) { ( $link_path ) = ( $link_path =~ /^(.*)$/ ); # De-taint - unlink($storage_path.'/'.$link_path) or Error( "Unable to unlink '$storage_path/$link_path': $!" ); + unlink($storage_path.'/'.$link_path) or Error("Unable to unlink '$storage_path/$link_path': $!"); } } } # end sub delete_files @@ -467,7 +489,7 @@ sub Storage { } if ( ! $_[0]{Storage} ) { $_[0]{Storage} = new ZoneMinder::Storage($_[0]{StorageId}); - } + } return $_[0]{Storage}; } @@ -476,7 +498,7 @@ sub check_for_in_filesystem { if ( $path ) { if ( -e $path ) { my @files = glob "$path/*"; - Debug("Checking for files for event $_[0]{Id} at $path using glob $path/* found " . scalar @files . " files"); + Debug("Checking for files for event $_[0]{Id} at $path using glob $path/* found " . scalar @files . ' files'); return 1 if @files; } else { Warning("Path not found for Event $_[0]{Id} at $path"); @@ -488,7 +510,7 @@ sub check_for_in_filesystem { sub age { if ( ! $_[0]{age} ) { - if ( -e $_[0]->Path() ) { + if ( -e $_[0]->Path() ) { # $^T is the time the program began running. -M is program start time - file modification time in days $_[0]{age} = (time() - ($^T - ((-M $_[0]->Path() ) * 24*60*60))); } else { @@ -547,7 +569,7 @@ sub MoveTo { if ( $$OldStorage{Id} != $$self{StorageId} ) { $ZoneMinder::Database::dbh->commit(); - return "Old Storage path changed, Event has moved somewhere else."; + return 'Old Storage path changed, Event has moved somewhere else.'; } $$self{Storage} = $NewStorage; @@ -564,6 +586,7 @@ sub MoveTo { my ( $aws_id, $aws_secret, $aws_host, $aws_bucket ) = ( $$NewStorage{Url} =~ /^\s*([^:]+):([^@]+)@([^\/]*)\/(.+)\s*$/ ); eval { require Net::Amazon::S3; + require File::Slurp; my $s3 = Net::Amazon::S3->new( { aws_access_key_id => $aws_id, aws_secret_access_key => $aws_secret, @@ -590,11 +613,11 @@ Debug("Files to move @files"); Debug("Moving file $file to $NewPath"); my $size = -s $file; if ( ! $size ) { - Info("Not moving file with 0 size"); + Info('Not moving file with 0 size'); } my $file_contents = File::Slurp::read_file($file); if ( ! $file_contents ) { - die "Loaded empty file, but it had a size. Giving up"; + die 'Loaded empty file, but it had a size. Giving up'; } my $filename = $event_path.'/'.File::Basename::basename($file); @@ -602,7 +625,7 @@ Debug("Files to move @files"); die "Unable to add key for $filename"; } my $duration = time - $starttime; - Debug("PUT to S3 " . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . Number::Bytes::Human::format_bytes($duration?$size/$duration:$size) . '/sec'); + Debug('PUT to S3 ' . Number::Bytes::Human::format_bytes($size) . " in $duration seconds = " . Number::Bytes::Human::format_bytes($duration?$size/$duration:$size) . '/sec'); } # end foreach file. $moved = 1; @@ -656,7 +679,7 @@ Debug("Files to move @files"); } # Succeeded in copying all files, so we may now update the Event. - $$self{StorageId} = $$NewStorage{Id}; + $$self{StorageId} = $$NewStorage{Id}; $$self{Storage} = $NewStorage; $error .= $self->save(); if ( $error ) { diff --git a/scripts/ZoneMinder/lib/ZoneMinder/General.pm b/scripts/ZoneMinder/lib/ZoneMinder/General.pm index c91511869..d16c248d5 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/General.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/General.pm @@ -1,27 +1,3 @@ -# ========================================================================== -# -# ZoneMinder General Utility 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -# -# ========================================================================== -# -# This module contains the common definitions and functions used by the rest -# of the ZoneMinder scripts -# package ZoneMinder::General; use 5.006; @@ -42,15 +18,13 @@ our @ISA = qw(Exporter ZoneMinder::Base); # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK # will save memory. our %EXPORT_TAGS = ( - 'functions' => [ qw( + functions => [ qw( executeShellCommand getCmdFormat runCommand setFileOwner - getEventPath createEventPath createEvent - deleteEventFiles makePath jsonEncode jsonDecode @@ -58,7 +32,7 @@ our %EXPORT_TAGS = ( ); push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS; -our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); +our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } ); our @EXPORT = qw(); @@ -82,74 +56,74 @@ sub executeShellCommand { my $output = qx( $command ); my $status = $? >> 8; if ( $status || logDebugging() ) { - Debug( "Command: $command\n" ); + Debug("Command: $command"); chomp( $output ); - Debug( "Output: $output\n" ); + Debug("Output: $output"); } - return( $status ); + return $status; } sub getCmdFormat { - Debug( "Testing valid shell syntax\n" ); + Debug("Testing valid shell syntax"); my ( $name ) = getpwuid( $> ); if ( $name eq $Config{ZM_WEB_USER} ) { - Debug( "Running as '$name', su commands not needed\n" ); - return( "" ); + Debug("Running as '$name', su commands not needed"); + return ''; } - my $null_command = "true"; + my $null_command = 'true'; - my $prefix = "sudo -u ".$Config{ZM_WEB_USER}." "; - my $suffix = ""; + my $prefix = 'sudo -u '.$Config{ZM_WEB_USER}.' '; + my $suffix = ''; my $command = $prefix.$null_command.$suffix; - Debug( "Testing \"$command\"\n" ); + Debug("Testing \"$command\""); my $output = qx($command 2>&1); my $status = $? >> 8; $output //= $!; if ( !$status ) { - Debug( "Test ok, using format \"$prefix$suffix\"\n" ); + Debug("Test ok, using format \"$prefix$suffix\""); return( $prefix, $suffix ); } else { chomp( $output ); - Debug( "Test failed, '$output'\n" ); + Debug("Test failed, '$output'"); - $prefix = "su ".$Config{ZM_WEB_USER}." --shell=/bin/sh --command='"; - $suffix = "'"; + $prefix = 'su '.$Config{ZM_WEB_USER}.q` --shell=/bin/sh --command='`; + $suffix = q`'`; $command = $prefix.$null_command.$suffix; - Debug( "Testing \"$command\"\n" ); + Debug("Testing \"$command\""); my $output = qx($command 2>&1); my $status = $? >> 8; $output //= $!; if ( !$status ) { - Debug( "Test ok, using format \"$prefix$suffix\"\n" ); + Debug("Test ok, using format \"$prefix$suffix\""); return( $prefix, $suffix ); } else { - chomp( $output ); - Debug( "Test failed, '$output'\n" ); + chomp($output); + Debug("Test failed, '$output'"); $prefix = "su ".$Config{ZM_WEB_USER}." -c '"; $suffix = "'"; $command = $prefix.$null_command.$suffix; - Debug( "Testing \"$command\"\n" ); + Debug("Testing \"$command\""); $output = qx($command 2>&1); $status = $? >> 8; $output //= $!; if ( !$status ) { - Debug( "Test ok, using format \"$prefix$suffix\"\n" ); + Debug("Test ok, using format \"$prefix$suffix\""); return( $prefix, $suffix ); } else { - chomp( $output ); - Debug( "Test failed, '$output'\n" ); + chomp($output); + Debug("Test failed, '$output'"); } } } - Error( "Unable to find valid 'su' syntax\n" ); - exit( -1 ); -} + Error("Unable to find valid 'su' syntax"); + exit -1; +} # end sub getCmdFormat our $testedShellSyntax = 0; our ( $cmdPrefix, $cmdSuffix ); @@ -163,88 +137,32 @@ sub runCommand { } my $command = shift; - $command = $Config{ZM_PATH_BIN}."/".$command; + $command = $Config{ZM_PATH_BIN}.'/'.$command; if ( $cmdPrefix ) { $command = $cmdPrefix.$command.$cmdSuffix; } - Debug( "Command: $command\n" ); + Debug("Command: $command"); my $output = qx($command); my $status = $? >> 8; - chomp( $output ); + chomp($output); if ( $status || logDebugging() ) { if ( $status ) { - Error( "Unable to run \"$command\", output is \"$output\", status is $status\n" ); + Error("Unable to run \"$command\", output is \"$output\", status is $status"); } else { - Debug( "Output: $output\n" ); + Debug("Output: $output"); } } - return( $output ); -} - -sub getEventPath { - my $event = shift; - - my $Storage = new ZoneMinder::Storage( $$event{StorageId} ); - my $event_path = join( '/', - $Storage->Path(), - $event->{MonitorId}, - ( $Config{ZM_USE_DEEP_STORAGE} ? strftime( "%y/%m/%d/%H/%M/%S", localtime($event->{Time}) ) : $event->{Id} ), - ); - - return( $event_path ); -} + return $output; +} # end sub runCommand sub createEventPath { -# -# WARNING assumes running from events directory -# my $event = shift; - my $Storage = new ZoneMinder::Storage( $$event{Id} ); - my $eventPath = $Storage->Path() . '/'.$event->{MonitorId}; + my $eventPath = $event->Path(); + $event->createPath(); + $event->createIdFile(); + $event->createLinkPath(); - if ( $Config{ZM_USE_DEEP_STORAGE} ) { - my @startTime = localtime( $event->{StartTime} ); - - my @datetimeParts = (); - $datetimeParts[0] = sprintf( "%02d", $startTime[5]-100 ); - $datetimeParts[1] = sprintf( "%02d", $startTime[4]+1 ); - $datetimeParts[2] = sprintf( "%02d", $startTime[3] ); - $datetimeParts[3] = sprintf( "%02d", $startTime[2] ); - $datetimeParts[4] = sprintf( "%02d", $startTime[1] ); - $datetimeParts[5] = sprintf( "%02d", $startTime[0] ); - - my $datePath = join('/',@datetimeParts[0..2]); - my $timePath = join('/',@datetimeParts[3..5]); - - makePath( $datePath, $eventPath ); - $eventPath .= '/'.$datePath; - -# Create event id symlink - my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); - symlink( $timePath, $idFile ) - or Fatal( "Can't symlink $idFile -> $eventPath: $!" ); - - makePath( $timePath, $eventPath ); - $eventPath .= '/'.$timePath; - setFileOwner( $idFile ); # Must come after directory has been created - -# Create empty id tag file - $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); - open( my $ID_FP, ">", $idFile ) - or Fatal( "Can't open $idFile: $!" ); - close( $ID_FP ); - setFileOwner( $idFile ); - } else { - makePath( $event->{Id}, $eventPath ); - $eventPath .= '/'.$event->{Id}; - - my $idFile = sprintf( "%s/.%d", $eventPath, $event->{Id} ); - open( my $ID_FP, ">", $idFile ) - or Fatal( "Can't open $idFile: $!" ); - close( $ID_FP ); - setFileOwner( $idFile ); - } - return( $eventPath ); + return $eventPath; } use Data::Dumper; @@ -268,7 +186,7 @@ sub _checkProcessOwner { $_setFileOwner = 0; } } - return( $_setFileOwner ); + return $_setFileOwner; } sub setFileOwner { @@ -277,7 +195,7 @@ sub setFileOwner { if ( _checkProcessOwner() ) { chown( $_ownerUid, $_ownerGid, $file ) or Fatal( "Can't change ownership of file '$file' to '" - .$Config{ZM_WEB_USER}.":".$Config{ZM_WEB_GROUP}."': $!" + .$Config{ZM_WEB_USER}.':'.$Config{ZM_WEB_GROUP}."': $!" ); } } @@ -292,13 +210,13 @@ sub _checkForImageInfo { }; $_hasImageInfo = $@?0:1; } - return( $_hasImageInfo ); + return $_hasImageInfo; } sub createEvent { my $event = shift; - Debug( "Creating event" ); + Debug('Creating event'); #print( Dumper( $event )."\n" ); _checkForImageInfo(); @@ -481,53 +399,6 @@ sub updateEvent { or Fatal( "Can't execute sql '$sql': ".$sth->errstr() ); } -sub deleteEventFiles { -# -# WARNING assumes running from events directory -# - my $event_id = shift; - my $monitor_id = shift; - $monitor_id = '*' if ( !defined($monitor_id) ); - - if ( $Config{ZM_USE_DEEP_STORAGE} ) { - my $link_path = $monitor_id."/*/*/*/.".$event_id; -#Debug( "LP1:$link_path" ); - my @links = glob($link_path); -#Debug( "L:".$links[0].": $!" ); - if ( @links ) { - ( $link_path ) = ( $links[0] =~ /^(.*)$/ ); # De-taint -#Debug( "LP2:$link_path" ); - - ( my $day_path = $link_path ) =~ s/\.\d+//; -#Debug( "DP:$day_path" ); - my $event_path = $day_path.readlink( $link_path ); - ( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint -#Debug( "EP:$event_path" ); - my $command = "/bin/rm -rf ".$event_path; -#Debug( "C:$command" ); - executeShellCommand( $command ); - - unlink( $link_path ) or Error( "Unable to unlink '$link_path': $!" ); - my @path_parts = split( /\//, $event_path ); - for ( my $i = int(@path_parts)-2; $i >= 1; $i-- ) { - my $delete_path = join( '/', @path_parts[0..$i] ); -#Debug( "DP$i:$delete_path" ); - my @has_files = glob( $delete_path."/*" ); -#Debug( "HF1:".$has_files[0] ) if ( @has_files ); - last if ( @has_files ); - @has_files = glob( $delete_path."/.[0-9]*" ); -#Debug( "HF2:".$has_files[0] ) if ( @has_files ); - last if ( @has_files ); - my $command = "/bin/rm -rf ".$delete_path; - executeShellCommand( $command ); - } - } - } else { - my $command = "/bin/rm -rf $monitor_id/$event_id"; - executeShellCommand( $command ); - } -} - sub makePath { my $path = shift; my $root = shift; @@ -654,7 +525,7 @@ sub jsonDecode { $out =~ s/=>null/=>undef/g; $out =~ s/`/'/g; $out =~ s/qx/qq/g; - ( $out ) = $out =~ m/^({.+})$/; # Detaint and check it's a valid object syntax + ( $out ) = $out =~ m/^(\{.+\})$/; # Detaint and check it's a valid object syntax my $result = eval $out; Fatal( $@ ) if ( $@ ); return( $result ); @@ -666,38 +537,33 @@ __END__ =head1 NAME -ZoneMinder::Database - Perl extension for blah blah blah +ZoneMinder::General - Utility Functions for ZoneMinder =head1 SYNOPSIS -use ZoneMinder::Database; +use ZoneMinder::General; 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. +This module contains the common definitions and functions used by the rest +of the ZoneMinder scripts =head2 EXPORT -None by default. + functions => [ qw( + executeShellCommand + getCmdFormat + runCommand + setFileOwner + createEventPath + createEvent + makePath + jsonEncode + jsonDecode + ) ] - -=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 @@ -706,9 +572,18 @@ Philip Coombes, Ephilip.coombes@zoneminder.comE 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. +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 Street, Fifth Floor, Boston, MA 02110-1301 USA. =cut diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm index bac118a13..dcd6fa3d8 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Logger.pm @@ -440,12 +440,12 @@ sub databaseLevel { if ( defined($databaseLevel) ) { $databaseLevel = $this->limit($databaseLevel); if ( $this->{databaseLevel} != $databaseLevel ) { - if ( $databaseLevel > NOLOG and $this->{databaseLevel} <= NOLOG ) { - if ( !$this->{dbh} ) { - $this->{dbh} = ZoneMinder::Database::zmDbConnect(); + if ( ( $databaseLevel > NOLOG ) and ( $this->{databaseLevel} <= NOLOG ) ) { + if ( ! ( $ZoneMinder::Database::dbh or ZoneMinder::Database::zmDbConnect() ) ) { + Warning("Failed connecting to db. Not using database logging."); + $this->{databaseLevel} = NOLOG; + return NOLOG; } - } elsif ( $databaseLevel <= NOLOG && $this->{databaseLevel} > NOLOG ) { - undef($this->{dbh}); } $this->{databaseLevel} = $databaseLevel; } @@ -558,34 +558,39 @@ sub logPrint { } if ( $level <= $this->{databaseLevel} ) { - if ( ! ( $this->{dbh} and $this->{dbh}->ping() ) ) { + if ( ! ( $ZoneMinder::Database::dbh and $ZoneMinder::Database::dbh->ping() ) ) { $this->{sth} = undef; - if ( ! ( $this->{dbh} = ZoneMinder::Database::zmDbConnect() ) ) { + # Turn this off because zDbConnect will do logging calls. + my $oldlevel = $this->{databaseLevel}; + $this->{databaseLevel} = NOLOG; + if ( ! ZoneMinder::Database::zmDbConnect() ) { #print(STDERR "Can't log to database: "); - $this->{databaseLevel} = NOLOG; return; } + $this->{databaseLevel} = $oldlevel; } - my $sql = 'INSERT INTO Logs ( TimeKey, Component, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, NULL )'; - $this->{sth} = $this->{dbh}->prepare_cached($sql) if ! $this->{sth}; + my $sql = 'INSERT INTO Logs ( TimeKey, Component, ServerId, Pid, Level, Code, Message, File, Line ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, NULL )'; + $this->{sth} = $ZoneMinder::Database::dbh->prepare_cached($sql) if ! $this->{sth}; if ( !$this->{sth} ) { $this->{databaseLevel} = NOLOG; - Error("Can't prepare log entry '$sql': ".$this->{dbh}->errstr()); + Error("Can't prepare log entry '$sql': ".$ZoneMinder::Database::dbh->errstr()); return; } - my $res = $this->{sth}->execute($seconds+($microseconds/1000000.0) - , $this->{id} - , $$ - , $level - , $codes{$level} - , $string - , $this->{fileName} + my $res = $this->{sth}->execute( + $seconds+($microseconds/1000000.0), + $this->{id}, + ($Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : undef), + $$, + $level, + $codes{$level}, + $string, + $this->{fileName}, ); if ( !$res ) { $this->{databaseLevel} = NOLOG; - Error("Can't execute log entry '$sql': ".$this->{dbh}->errstr()); + Error("Can't execute log entry '$sql': ".$ZoneMinder::Database::dbh->errstr()); } } # end if doing db logging } # end if level < effectivelevel @@ -699,6 +704,8 @@ sub Fatal( @ ) { if ( $SIG{TERM} and ( $SIG{TERM} ne 'DEFAULT' ) ) { $SIG{TERM}(); } + # I think if we don't disconnect we will leave sockets around in TIME_WAIT + ZoneMinder::Database::zmDbDisconnect(); exit(-1); } diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in index 2529c8786..7c5292720 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/Memory.pm.in @@ -161,7 +161,6 @@ our $mem_data = { format => { type=>'uint8', seq=>$mem_seq++ }, imagesize => { type=>'uint32', seq=>$mem_seq++ }, epadding1 => { type=>'uint32', seq=>$mem_seq++ }, - epadding2 => { type=>'uint32', seq=>$mem_seq++ }, startup_time => { type=>'time_t64', seq=>$mem_seq++ }, last_write_time => { type=>'time_t64', seq=>$mem_seq++ }, last_read_time => { type=>'time_t64', seq=>$mem_seq++ }, diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm index 8c28028a0..f779255b2 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Object.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Object.pm @@ -456,7 +456,7 @@ sub transform { sub to_string { my $type = ref($_[0]); my $fields = eval '\%'.$type.'::fields'; - return $type . ': '. join(' ' , map { $_[0]{$_} ? "$_ => $_[0]{$_}" : () } keys %$fields ); + return $type . ': '. join(' ' , map { $_[0]{$_} ? "$_ => $_[0]{$_}" : () } sort { $a cmp $b } keys %$fields ); } 1; diff --git a/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm index b6924c4e9..1f7c1b9fe 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm +++ b/scripts/ZoneMinder/lib/ZoneMinder/Storage.pm @@ -15,7 +15,7 @@ # # 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. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # ========================================================================== # @@ -53,64 +53,65 @@ $primary_key = 'Id'; #__PACKAGE__->primary_key('Id'); sub find { - shift if $_[0] eq 'ZoneMinder::Storage'; - my %sql_filters = @_; + shift if $_[0] eq 'ZoneMinder::Storage'; + my %sql_filters = @_; - my $sql = 'SELECT * FROM Storage'; - my @sql_filters; - my @sql_values; + my $sql = 'SELECT * FROM Storage'; + my @sql_filters; + my @sql_values; - if ( exists $sql_filters{Id} ) { - push @sql_filters , ' Id=? '; - push @sql_values, $sql_filters{Id}; - } - if ( exists $sql_filters{Name} ) { - push @sql_filters , ' Name = ? '; - push @sql_values, $sql_filters{Name}; - } - if ( exists $sql_filters{ServerId} ) { - push @sql_filters, ' Id IN ( SELECT StorageId FROM Monitors WHERE ServerId=? )'; - push @sql_values, $sql_filters{ServerId}; - } + if ( exists $sql_filters{Id} ) { + push @sql_filters , ' Id=? '; + push @sql_values, $sql_filters{Id}; + } + if ( exists $sql_filters{Name} ) { + push @sql_filters , ' Name = ? '; + push @sql_values, $sql_filters{Name}; + } + if ( exists $sql_filters{ServerId} ) { + push @sql_filters, ' ServerId = ?'; + push @sql_values, $sql_filters{ServerId}; + } - $sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters; - $sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit}; + $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 $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; + my @results; - while( my $db_filter = $sth->fetchrow_hashref() ) { - my $filter = new ZoneMinder::Storage( $$db_filter{Id}, $db_filter ); - push @results, $filter; - } # end while - return @results; + while( my $db_filter = $sth->fetchrow_hashref() ) { + my $filter = new ZoneMinder::Storage( $$db_filter{Id}, $db_filter ); + push @results, $filter; + } # end while + Debug("SQL: $sql returned " . @results . ' results'); + return @results; } sub find_one { - my @results = find(@_); - return $results[0] if @results; + my @results = find(@_); + return $results[0] if @results; } sub Path { - if ( @_ > 1 ) { - $_[0]{Path} = $_[1]; - } - if ( ! ( $_[0]{Id} or $_[0]{Path} ) ) { - $_[0]{Path} = ($Config{ZM_DIR_EVENTS}=~/^\//) ? $Config{ZM_DIR_EVENTS} : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}) - } - return $_[0]{Path}; + if ( @_ > 1 ) { + $_[0]{Path} = $_[1]; + } + if ( ! ( $_[0]{Id} or $_[0]{Path} ) ) { + $_[0]{Path} = ($Config{ZM_DIR_EVENTS}=~/^\//) ? $Config{ZM_DIR_EVENTS} : ($Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS}) + } + return $_[0]{Path}; } # end sub Path sub Name { - if ( @_ > 1 ) { - $_[0]{Name} = $_[1]; - } - return $_[0]{Name}; + if ( @_ > 1 ) { + $_[0]{Name} = $_[1]; + } + return $_[0]{Name}; } # end sub Path sub DoDelete { diff --git a/scripts/zmaudit.pl.in b/scripts/zmaudit.pl.in index 28a505801..7a5017fbd 100644 --- a/scripts/zmaudit.pl.in +++ b/scripts/zmaudit.pl.in @@ -67,6 +67,7 @@ my $level = 1; my $monitor_id = 0; my $version; my $force = 0; +my $server_id = undef; my $storage_id = undef; logInit(); @@ -78,6 +79,7 @@ GetOptions( level =>\$level, 'monitor_id=i' =>\$monitor_id, report =>\$report, + 'server_id=i' =>\$server_id, 'storage_id=i' =>\$storage_id, version =>\$version ) or pod2usage(-exitstatus => -1); @@ -181,13 +183,15 @@ MAIN: while( $loop ) { Term(); } Info("Auditing Storage Area $Storage_Areas[0]{Id} $Storage_Areas[0]{Name} at $Storage_Areas[0]{Path}"); - } elsif ( $Config{ZM_SERVER_ID} ) { - @Storage_Areas = ZoneMinder::Storage->find( ServerId => $Config{ZM_SERVER_ID} ); + } elsif ( $server_id ) { + @Storage_Areas = ZoneMinder::Storage->find( ServerId => $server_id ); if ( ! @Storage_Areas ) { - Error("No Storage Area found with ServerId =" . $Config{ZM_SERVER_ID}); + Error("No Storage Area found with ServerId =" . $server_id); Term(); } - Info("Auditing All Storage Areas on Server " . $Storage_Areas[0]->Server()->Name()); + foreach my $Storage ( @Storage_Areas ) { + Info('Auditing ' . $Storage->Name() . ' at ' . $Storage->Path() . ' on ' . $Storage->Server()->Name() ); + } } else { @Storage_Areas = ZoneMinder::Storage->find(); Info("Auditing All Storage Areas"); @@ -260,33 +264,39 @@ MAIN: while( $loop ) { Error("Can't open directory '$$Storage{Path}/$day_dir': $!"); next; } + my %event_ids_by_path; + my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR ); Debug("Have " . @event_links . ' event links'); closedir(DIR); + my $count = 0; foreach my $event_link ( @event_links ) { - if ( $event_link =~ /[^\d\.]/ ) { + # Event links start with a period and consist of the digits of the event id. Anything else is not an event link + my ($event_id) = $event_link =~ /^\.(\d+)$/; + if ( !$event_id ) { Warning("Non-event link found $event_link in $day_dir, skipping"); next; } Debug("Checking link $event_link"); - ( my $event = $event_link ) =~ s/^.*\.//; #Event path is hour/minute/sec my $event_path = readlink($event_link); if ( !($event_path and -e $event_path) ) { - aud_print("Event link $day_dir/$event_link does not point to valid target"); + aud_print("Event link $day_dir/$event_link does not point to valid target at $event_path"); if ( confirm() ) { ( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint unlink($event_link); $cleaned = 1; } } else { + $event_ids_by_path{$event_path} = $event_id; + Debug("Checking link $event_link points to $event_path "); - my $Event = $fs_events->{$event} = new ZoneMinder::Event(); - $$Event{Id} = $event; - $$Event{Path} = join('/', $Storage->Path(), $day_dir,$event_path); - $$Event{RelativePath} = join('/', $day_dir,$event_path); + my $Event = $fs_events->{$event_id} = new ZoneMinder::Event(); + $$Event{Id} = $event_id; + $$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_path); + $$Event{RelativePath} = join('/', $day_dir, $event_path); $$Event{Scheme} = 'Deep'; $Event->MonitorId( $monitor_dir ); $Event->StorageId( $Storage->Id() ); @@ -303,15 +313,34 @@ MAIN: while( $loop ) { my $event_id = undef; - my @mp4_files = glob("$event_dir/[0-9]+\-video.mp4"); + if ( ! opendir(DIR, $event_dir) ) { + Error("Can't open directory '$$Storage{Path}/$day_dir': $!"); + next; + } + my @contents = readdir( DIR ); + Debug("Have " . @contents . " files in $day_dir/$event_dir"); + closedir(DIR); + + my @mp4_files = grep( /^\d+\-video.mp4$/, @contents); foreach my $mp4_file ( @mp4_files ) { my ( $id ) = $mp4_file =~ /^([0-9]+)\-video\.mp4$/; if ( $id ) { $event_id = $id; + Debug("Got event id from mp4 file $mp4_file => $event_id"); last; } } - if ( $event_id ) { + + if ( ! $event_id ) { + # Look for .id file + my @hidden_files = grep( /^\.\d+$/, @contents); + Debug("Have " . @hidden_files . ' hidden files'); + if ( @hidden_files ) { + ( $event_id ) = $hidden_files[0] =~ /^.(\d+)$/; + } + } + + if ( $event_id and ! $fs_events->{$event_id} ) { my $Event = $fs_events->{$event_id} = new ZoneMinder::Event(); $$Event{Id} = $event_id; $$Event{Path} = join('/', $Storage->Path(), $day_dir, $event_dir); @@ -320,7 +349,26 @@ MAIN: while( $loop ) { $Event->MonitorId( $monitor_dir ); $Event->StorageId( $Storage->Id() ); $Event->DiskSpace( undef ); + if ( ! $event_ids_by_path{$event_dir} ) { + Warning("No event link found at ".$Event->LinkPath() ." for " . $Event->to_string()); + } } else { + if ( $event_ids_by_path{$event_dir} ) { + Debug("Have an event link, leaving dir alone."); + next; + } + my ( undef, $year, $month, $day ) = split('/', $day_dir); + $year += 2000; + my ( $hour, $minute, $second ) = split('/', $event_dir); + my $StartTime =sprintf('%.4d-%.2d-%.2d %.2d:%.2d:%.2d', $year, $month, $day, $hour, $minute, $second); + my $Event = ZoneMinder::Event->find_one( + MonitorId=>$monitor_dir, + StartTime=>$StartTime, + ); + if ( $Event ) { + Debug("Found event matching starttime on monitor $monitor_dir at $StartTime: " . $Event->to_string()); + next; + } aud_print("Deleting event directories with no event id information at $day_dir/$event_dir"); if ( confirm() ) { my $command = "rm -rf $event_dir"; @@ -336,7 +384,7 @@ MAIN: while( $loop ) { Debug("Checking for Medium Scheme Events under $$Storage{Path}/$monitor_dir"); { my @event_dirs = glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*"); - Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . " entries." ); + Debug(qq`glob("$monitor_dir/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/*") returned ` . scalar @event_dirs . ' entries.' ); foreach my $event_dir ( @event_dirs ) { if ( ! -d $event_dir ) { Debug( "$event_dir is not a dir. Skipping" ); @@ -355,11 +403,12 @@ MAIN: while( $loop ) { $$Event{RelativePath} = $event_dir; $Event->MonitorId( $monitor_dir ); $Event->StorageId( $Storage->Id() ); + $Event->StartTime( POSIX::strftime('%Y-%m-%d %H:%M:%S', gmtime(time_of_youngest_file($$Event{Path})) ) ); } # end foreach event } if ( ! $$Storage{Scheme} ) { - Debug("Storage Scheme not set on $$Storage{Name}"); + Error("Storage Scheme not set on $$Storage{Name}"); if ( ! chdir( $monitor_dir ) ) { Error( "Can't chdir directory '$$Storage{Path}/$monitor_dir': $!" ); next; @@ -380,9 +429,9 @@ MAIN: while( $loop ) { } # end foreach event chdir( $Storage->Path() ); } # if USE_DEEP_STORAGE - Debug( 'Got '.int(keys(%$fs_events))." filesystem events for monitor $monitor_dir\n" ); + Debug( 'Got '.int(keys(%$fs_events))." filesystem events for monitor $monitor_dir" ); - #delete_empty_directories( $monitor_dir ); + delete_empty_subdirs($$Storage{Path}.'/'.$monitor_dir); } # end foreach monitor if ( $cleaned ) { @@ -398,7 +447,7 @@ MAIN: while( $loop ) { next; } my @event_ids = keys %$fs_events; - Debug("Have " .scalar @event_ids . " events for monitor $monitor_id"); + Debug('Have ' .scalar @event_ids . " events for monitor $monitor_id"); foreach my $fs_event_id ( sort { $a <=> $b } keys %$fs_events ) { @@ -442,7 +491,11 @@ MAIN: while( $loop ) { } } } # end foreach Storage Area - redo MAIN if ( $cleaned ); + + if ( $cleaned ) { + Debug("Events were deleted, starting again."); + redo MAIN; + } $cleaned = 0; my $deleteMonitorSql = 'DELETE LOW_PRIORITY FROM Monitors WHERE Id = ?'; @@ -460,64 +513,88 @@ MAIN: while( $loop ) { # Foreach database monitor and it's list of events. while ( my ( $db_monitor, $db_events ) = each(%$db_monitors) ) { + Debug("Checking db events for monitor $db_monitor"); + if ( ! $db_events ) { + Debug("Skipping db events for $db_monitor because there are none"); + next; + } # If we found the monitor in the file system - if ( my $fs_events = $fs_monitors->{$db_monitor} ) { - next if ! $db_events; + my $fs_events = $fs_monitors->{$db_monitor}; - while ( my ( $db_event, $age ) = each( %$db_events ) ) { - if ( ! defined( $fs_events->{$db_event} ) ) { - my $Event = ZoneMinder::Event->find_one( Id=>$db_event ); - if ( ! $Event ) { - Debug("Event $db_event is no longer in db. Filter probably deleted it while we were auditing."); - next; + while ( my ( $db_event, $age ) = each( %$db_events ) ) { + if ( ! ($fs_events and defined( $fs_events->{$db_event} ) ) ) { + Debug("Don't have an fs event for $db_event"); + my $Event = ZoneMinder::Event->find_one( Id=>$db_event ); + if ( ! $Event ) { + Debug("Event $db_event is no longer in db. Filter probably deleted it while we were auditing."); + next; + } + Debug("Event $db_event is not in fs. Should have been at ".$Event->Path()); + if ( $Event->Archived() ) { + Warning("Event $$Event{Id} is Archived. Taking no further action on it."); + next; + } + if ( ! $Event->StartTime() ) { + Info("Event $$Event{Id} has no start time."); + if ( confirm() ) { + $Event->delete(); + $cleaned = 1; } - Debug("Event $db_event is not in fs. Should have been at ".$Event->Path()); - if ( ! $Event->StartTime() ) { - Info("Event $$Event{Id} has no start time. deleting it."); + next; + } + if ( ! $Event->EndTime() ) { + if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { + Info("Event $$Event{Id} has no end time and is $age seconds old. deleting it."); if ( confirm() ) { $Event->delete(); $cleaned = 1; } next; - } - if ( ! $Event->EndTime() ) { - if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { - Info("Event $$Event{Id} has no end time and is $age seconds old. deleting it."); - if ( confirm() ) { - $Event->delete(); - $cleaned = 1; - } - next; - } } - if ( $Event->check_for_in_filesystem() ) { - Debug("Database event $$Event{Id} apparently exists at " . $Event->Path() ); - } else { - if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { - aud_print( "Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem, deleting' ); - if ( confirm() ) { - $Event->delete(); - $cleaned = 1; - } - } else { - aud_print( "Database event '".$Event->Path()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}.\n" ); + } + if ( $Event->check_for_in_filesystem() ) { + Debug("Database event $$Event{Id} apparently exists at " . $Event->Path() ); + } else { + if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) { + aud_print( "Database event '$db_monitor/$db_event' does not exist at " . $Event->Path().' in filesystem, deleting' ); + if ( confirm() ) { + $Event->delete(); + $cleaned = 1; } - } # end if exists in filesystem - } # end if ! in fs_events - } # foreach db_event - #} else { - #my $Monitor = new ZoneMinder::Monitor( $db_monitor ); - #my $Storage = $Monitor->Storage(); - #aud_print( "Database monitor '$db_monitor' does not exist in filesystem, should have been at ".$Storage->Path().'/'.$Monitor->Id()."\n" ); -#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() ); -#$cleaned = 1; -#} - } + } else { + aud_print( "Database event '".$Event->Path()." monitor:$db_monitor event:$db_event' does not exist in filesystem but too young to delete age: $age > MIN $Config{ZM_AUDIT_MIN_AGE}.\n" ); + } + } # end if exists in filesystem + } else { + Debug("Found fs event for $db_event, $age at " . $$fs_events{$db_event}->Path()); + my $Event = new ZoneMinder::Event( $db_event ); + if ( ! $Event->check_for_in_filesystem() ) { + Warning("Not found at " . $Event->Path() . ' was found at ' . $$fs_events{$db_event}->Path() ); + Warning($Event->to_string()); + Warning($$fs_events{$db_event}->to_string()); + if ( $$fs_events{$db_event}->Scheme() ne $Event->Scheme() ) { + Info("Updating scheme on event $$Event{Id} from $$Event{Scheme} to $$fs_events{$db_event}{Scheme}"); + $Event->Scheme($$fs_events{$db_event}->Scheme()); + } + if ( $$fs_events{$db_event}->StorageId() != $Event->StorageId() ) { + Info("Updating storage area on event $$Event{Id} from $$Event{StorageId} to $$fs_events{$db_event}{StorageId}"); + $Event->StorageId($$fs_events{$db_event}->StorageId()); + } + if ( $$fs_events{$db_event}->StartTime() ne $Event->StartTime() ) { + Info("Updating StartTime on event $$Event{Id} from $$Event{StartTime} to $$fs_events{$db_event}{StartTime}"); + if ( $$Event{Scheme} eq 'Deep' ) { + $Event->StartTime($$fs_events{$db_event}->StartTime()); + } else { + $Event->StartTime($$fs_events{$db_event}->StartTime()); + } + $Event->save(); + } + + $Event->save(); + } + } # end if ! in fs_events + } # foreach db_event } # end foreach db_monitor if ( $cleaned ) { Debug("Have done some cleaning, restarting."); @@ -877,30 +954,71 @@ sub deleteSwapImage { } } -sub delete_empty_directories { +# Deletes empty sub directories of the given path. +# Does not delete the path if empty. Is not meant to be recursive. +sub delete_empty_subdirs { my $DIR; - Debug("delete_empty_directories $_[0]"); - if ( ! opendir( $DIR, $_[0] ) ) { - Error( "delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" ); + if ( !opendir($DIR, $_[0]) ) { + Error("delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" ); return; } - my @contents = map { $_ eq '.' or $_ eq '..' ? () : $_ } readdir( $DIR ); + my @contents = map { ( $_ eq '.' or $_ eq '..' ) ? () : $_ } readdir( $DIR ); + Debug("delete_empty_subdirectories $_[0] has " . @contents .' entries:' . ( @contents < 2 ? join(',',@contents) : '' )); + my @dirs = map { -d $_[0].'/'.$_ ? $_ : () } @contents; + Debug("Have " . @dirs . " dirs"); + foreach ( @dirs ) { + delete_empty_directories( $_[0].'/'.$_ ); + } + closedir($DIR); +} + +sub delete_empty_directories { + my $DIR; + if ( !opendir($DIR, $_[0]) ) { + Error("delete_empty_directories: Can't open directory '".getcwd()."/$_[0]': $!" ); + return; + } + my @contents = map { ( $_ eq '.' or $_ eq '..' ) ? () : $_ } readdir( $DIR ); + #Debug("delete_empty_directories $_[0] has " . @contents .' entries:' . ( @contents <= 2 ? join(',',@contents) : '' )); my @dirs = map { -d $_[0].'/'.$_ ? $_ : () } @contents; if ( @dirs ) { + Debug("Have " . @dirs . " dirs"); foreach ( @dirs ) { delete_empty_directories( $_[0].'/'.$_ ); } #Reload, since we may now be empty rewinddir $DIR; - @contents = map { $_ eq '.' or $_ eq '..' ? () : $_ } readdir( $DIR ); + @contents = map { ($_ eq '.' or $_ eq '..') ? () : $_ } readdir( $DIR ); } + closedir($DIR); if ( ! @contents ) { ( my $dir ) = ( $_[0] =~ /^(.*)$/ ); - unlink $dir; + Debug("Unlinking $dir because it's empty"); + if ( ! rmdir $dir ) { + Error("Unable to unlink $dir: $!"); + } } - closedir( $DIR ); } # end sub delete_empty_directories +sub time_of_youngest_file { + my $dir = shift; + + if ( ! opendir(DIR, $dir) ) { + Error("Can't open directory '$dir': $!"); + return; + } + my $youngest = (stat($dir))[9]; + Debug("stat of $dir is $youngest"); + foreach my $file ( readdir( DIR ) ) { + next if $file =~ /^\./; + $_ = (stat($dir))[9]; + $youngest = $_ if $_ and ( $_ < $youngest ); + #Debug("stat of $dir is $_ < $youngest"); + } + Debug("stat of $dir is $youngest"); + return $youngest; +} # end sub time_of_youngest_file + 1; __END__ diff --git a/scripts/zmdc.pl.in b/scripts/zmdc.pl.in index 6e6663b18..4b32b3a65 100644 --- a/scripts/zmdc.pl.in +++ b/scripts/zmdc.pl.in @@ -272,6 +272,8 @@ sub run { ."\n" ); + # We don't want to leave killall zombies, so ignore SIGCHLD + $SIG{CHLD} = 'IGNORE'; # Tell any existing processes to die, wait 1 second between TERM and KILL killAll(1); @@ -436,7 +438,7 @@ sub start { $dbh = zmDbConnect(1); # This logReinit is required. Not sure why. - #logReinit(); + logReinit(); $process->{pid} = $cpid; $process->{started} = time(); @@ -480,7 +482,7 @@ sub start { logTerm(); zmDbDisconnect(); - my $fd = 0; + my $fd = 3; # leave stdin,stdout,stderr open. Closing them causes problems with libx264 while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) { POSIX::close($fd++); } diff --git a/scripts/zmfilter.pl.in b/scripts/zmfilter.pl.in index cfea3bfe4..3f7e720c3 100644 --- a/scripts/zmfilter.pl.in +++ b/scripts/zmfilter.pl.in @@ -69,6 +69,7 @@ use Time::HiRes qw/gettimeofday/; use Getopt::Long; use autouse 'Pod::Usage'=>qw(pod2usage); use autouse 'Data::Dumper'=>qw(Dumper); +use File::Path qw(make_path); my $daemon = 0; my $filter_name = ''; @@ -162,7 +163,7 @@ my $delay = $Config{ZM_FILTER_EXECUTE_INTERVAL}; my $event_id = 0; if ( ! EVENT_PATH ) { - Error("No event path defined. Config was $Config{ZM_DIR_EVENTS}\n"); + Error("No event path defined. Config was $Config{ZM_DIR_EVENTS}"); die; } @@ -173,11 +174,11 @@ chdir( EVENT_PATH ); my $dbh = zmDbConnect(); if ( $filter_name ) { - Info("Scanning for events using filter '$filter_name'\n"); + Info("Scanning for events using filter '$filter_name'"); } elsif ( $filter_id ) { - Info("Scanning for events using filter id '$filter_id'\n"); + Info("Scanning for events using filter id '$filter_id'"); } else { - Info("Scanning for events using all filters\n"); + Info("Scanning for events using all filters"); } if ( ! ( $filter_name or $filter_id ) ) { @@ -191,7 +192,7 @@ my $last_action = 0; while( !$zm_terminate ) { my $now = time; if ( ($now - $last_action) > $Config{ZM_FILTER_RELOAD_DELAY} ) { - Debug("Reloading filters\n"); + Debug("Reloading filters"); $last_action = $now; @filters = getFilters({ Name=>$filter_name, Id=>$filter_id }); } @@ -211,7 +212,7 @@ while( !$zm_terminate ) { last if (!$daemon and ($filter_name or $filter_id)) or $zm_terminate; - Debug("Sleeping for $delay seconds\n"); + Debug("Sleeping for $delay seconds"); sleep($delay); } @@ -292,48 +293,48 @@ sub checkFilter { last if $zm_terminate; my $Event = new ZoneMinder::Event($$event{Id}, $event); - Debug("Checking event $event->{Id}"); + Debug("Checking event $Event->{Id}"); my $delete_ok = !undef; $dbh->ping(); if ( $filter->{AutoArchive} ) { - Info("Archiving event $event->{Id}"); + Info("Archiving event $Event->{Id}"); # 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("Unable to prepare '$sql': ".$dbh->errstr()); - my $res = $sth->execute( $event->{Id} ) + my $res = $sth->execute( $Event->{Id} ) or Error("Unable to execute '$sql': ".$dbh->errstr()); } if ( $Config{ZM_OPT_FFMPEG} && $filter->{AutoVideo} ) { - if ( !$event->{Videoed} ) { - $delete_ok = undef if !generateVideo($filter, $event); + if ( !$Event->{Videoed} ) { + $delete_ok = undef if !generateVideo($filter, $Event); } } if ( $Config{ZM_OPT_EMAIL} && $filter->{AutoEmail} ) { - if ( !$event->{Emailed} ) { + if ( !$Event->{Emailed} ) { $delete_ok = undef if !sendEmail($filter, $Event); } } if ( $Config{ZM_OPT_MESSAGE} && $filter->{AutoMessage} ) { - if ( !$event->{Messaged} ) { + if ( !$Event->{Messaged} ) { $delete_ok = undef if !sendMessage($filter, $Event); } } if ( $Config{ZM_OPT_UPLOAD} && $filter->{AutoUpload} ) { - if ( !$event->{Uploaded} ) { - $delete_ok = undef if !uploadArchFile($filter, $event); + if ( !$Event->{Uploaded} ) { + $delete_ok = undef if !uploadArchFile($filter, $Event); } } if ( $filter->{AutoExecute} ) { - if ( !$event->{Executed} ) { - $delete_ok = undef if !executeCommand($filter, $event); + if ( !$Event->{Executed} ) { + $delete_ok = undef if !executeCommand($filter, $Event); } } if ( $filter->{AutoDelete} ) { if ( $delete_ok ) { $Event->delete(); } else { - Error("Unable toto delete event $event->{Id} as previous operations failed"); + Error("Unable to delete event $Event->{Id} as previous operations failed"); } } # end if AutoDelete @@ -364,11 +365,11 @@ sub checkFilter { sub generateVideo { my $filter = shift; - my $event = shift; + my $Event = shift; my $phone = shift; - my $rate = $event->{DefaultRate}/100; - my $scale = $event->{DefaultScale}/100; + my $rate = $Event->{DefaultRate}/100; + my $scale = $Event->{DefaultScale}/100; my $format; my @ffmpeg_formats = split(/\s+/, $Config{ZM_FFMPEG_FORMATS}); @@ -393,7 +394,7 @@ sub generateVideo { my $command = join('', $Config{ZM_PATH_BIN}, '/zmvideo.pl -e ', - $event->{Id}, + $Event->{Id}, ' -r ', $rate, ' -s ', @@ -405,10 +406,10 @@ sub generateVideo { chomp($output); my $status = $? >> 8; if ( $status || logDebugging() ) { - Debug("Output: $output\n"); + Debug("Output: $output"); } if ( $status ) { - Error("Video generation '$command' failed with status: $status\n"); + Error("Video generation '$command' failed with status: $status"); if ( wantarray() ) { return( undef, undef ); } @@ -417,8 +418,8 @@ sub generateVideo { my $sql = 'UPDATE Events SET Videoed = 1 WHERE Id = ?'; my $sth = $dbh->prepare_cached($sql) or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); - my $res = $sth->execute($event->{Id}) - or Fatal("Unable toexecute '$sql': ".$dbh->errstr()); + my $res = $sth->execute($Event->{Id}) + or Fatal("Unable to execute '$sql': ".$dbh->errstr()); if ( wantarray() ) { return( $format, $output ); } @@ -453,7 +454,7 @@ sub generateImage { chomp( $output ); my $status = $? >> 8; if ( $status || logDebugging() ) { - Debug("Output: $output\n"); + Debug("Output: $output"); } if ( $status ) { Error("Failed $command status $status"); @@ -467,22 +468,30 @@ sub generateImage { sub uploadArchFile { my $filter = shift; - my $event = shift; + my $Event = shift; if ( ! $Config{ZM_UPLOAD_HOST} ) { Error('Cannot upload archive as no upload host defined'); return( 0 ); } - my $archFile = $event->{MonitorName}.'-'.$event->{Id}; - my $archImagePath = getEventPath($event) + # Try to make the path to the upload folder if it doesn't already exist + make_path( $Config{ZM_UPLOAD_LOC_DIR} ); + + # Complain if the upload folder is still not writable + if ( ! -w $Config{ZM_UPLOAD_LOC_DIR} ) { + Error("Upload folder either does not exist or is not writable: $Config{ZM_UPLOAD_LOC_DIR}"); + return(0); + } + + my $archFile = $Event->{MonitorName}.'-'.$Event->{Id}; + my $archImagePath = $Event->Path() .'/' .( ( $Config{ZM_UPLOAD_ARCH_ANALYSE} ) - ? '{*analyse,*capture}' - : '*capture' + ? '{*analyse.jpg,*capture.jpg,snapshot.jpg,*.mp4}' + : '{*capture.jpg,snapshot.jpg,*.mp4}' ) - .'.jpg' ; my @archImageFiles = glob($archImagePath); my $archLocPath; @@ -492,11 +501,11 @@ sub uploadArchFile { $archFile .= '.zip'; $archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile; my $zip = Archive::Zip->new(); - Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n"); + Info("Creating upload file '$archLocPath', ".int(@archImageFiles).' files'); my $status = &AZ_OK; foreach my $imageFile ( @archImageFiles ) { - Debug("Adding $imageFile\n"); + Debug("Adding $imageFile"); my $member = $zip->addFile($imageFile); if ( !$member ) { Error("Unable toto add image file $imageFile to zip archive $archLocPath"); @@ -524,7 +533,7 @@ sub uploadArchFile { $archFile .= '.tar'; } $archLocPath = $Config{ZM_UPLOAD_LOC_DIR}.'/'.$archFile; - Info("Creating upload file '$archLocPath', ".int(@archImageFiles)." files\n"); + Info("Creating upload file '$archLocPath', ".int(@archImageFiles).' files'); if ( $archError = !Archive::Tar->create_archive( $archLocPath, @@ -540,7 +549,7 @@ sub uploadArchFile { return( 0 ); } else { if ( $Config{ZM_UPLOAD_PROTOCOL} eq 'ftp' ) { - Info('Uploading to '.$Config{ZM_UPLOAD_HOST}." using FTP"); + Info('Uploading to '.$Config{ZM_UPLOAD_HOST}.' using FTP'); my $ftp = Net::FTP->new( $Config{ZM_UPLOAD_HOST}, Timeout=>$Config{ZM_UPLOAD_TIMEOUT}, @@ -548,26 +557,27 @@ sub uploadArchFile { Debug=>$Config{ZM_UPLOAD_DEBUG} ); if ( !$ftp ) { - Error("Unable tocreate FTP connection: $@"); + Error("Unable to create FTP connection: $@"); return 0; } $ftp->login($Config{ZM_UPLOAD_USER}, $Config{ZM_UPLOAD_PASS}) - or Error("FTP - Unable tologin"); + or Error("FTP - Unable to login"); $ftp->binary() - or Error("FTP - Unable togo binary"); + or Error("FTP - Unable to go binary"); $ftp->cwd($Config{ZM_UPLOAD_REM_DIR}) - or Error("FTP - Unable tocwd") + or Error("FTP - Unable to cwd") if ( $Config{ZM_UPLOAD_REM_DIR} ); $ftp->put( $archLocPath ) - or Error("FTP - Unable toupload '$archLocPath'"); + or Error("FTP - Unable to upload '$archLocPath'"); $ftp->quit() - or Error("FTP - Unable toquit"); + or Error("FTP - Unable to quit"); } else { my $host = $Config{ZM_UPLOAD_HOST}; $host .= ':'.$Config{ZM_UPLOAD_PORT} if $Config{ZM_UPLOAD_PORT}; - Info('Uploading to '.$host." using SFTP\n"); + Info('Uploading to '.$host.' using SFTP'); my %sftpOptions = ( - host=>$Config{ZM_UPLOAD_HOST}, user=>$Config{ZM_UPLOAD_USER} + host=>$Config{ZM_UPLOAD_HOST}, + user=>$Config{ZM_UPLOAD_USER}, ($Config{ZM_UPLOAD_PASS} ? (password=>$Config{ZM_UPLOAD_PASS}) : ()), ($Config{ZM_UPLOAD_PORT} ? (port=>$Config{ZM_UPLOAD_PORT}) : ()), ($Config{ZM_UPLOAD_TIMEOUT} ? (timeout=>$Config{ZM_UPLOAD_TIMEOUT}) : ()), @@ -580,7 +590,7 @@ sub uploadArchFile { $sftpOptions{more} = [@more_ssh_args]; my $sftp = Net::SFTP::Foreign->new($Config{ZM_UPLOAD_HOST}, %sftpOptions); if ( $sftp->error ) { - Error("Unable tocreate SFTP connection: ".$sftp->error); + Error("Unable to create SFTP connection: ".$sftp->error); return 0; } $sftp->setcwd($Config{ZM_UPLOAD_REM_DIR}) @@ -592,9 +602,9 @@ sub uploadArchFile { unlink($archLocPath); my $sql = 'UPDATE Events SET Uploaded = 1 WHERE Id = ?'; my $sth = $dbh->prepare_cached($sql) - or Fatal("Unable toprepare '$sql': ".$dbh->errstr()); - my $res = $sth->execute($event->{Id}) - or Fatal("Unable toexecute '$sql': ".$dbh->errstr()); + or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); + my $res = $sth->execute($Event->{Id}) + or Fatal("Unable to execute '$sql': ".$dbh->errstr()); } return 1; } # end sub uploadArchFile @@ -622,9 +632,9 @@ sub substituteTags { WHERE EventId = ? AND Type = 'Alarm' ORDER BY FrameId`; my $sth = $dbh->prepare_cached($sql) - or Fatal("Unable toprepare '$sql': ".$dbh->errstr()); + or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); my $res = $sth->execute($Event->{Id}) - or Fatal( "Unable toexecute '$sql': ".$dbh->errstr()); + or Fatal("Unable to execute '$sql': ".$dbh->errstr()); my $rows = 0; while( my $frame = $sth->fetchrow_hashref() ) { if ( !$first_alarm_frame ) { @@ -713,13 +723,17 @@ sub substituteTags { } } # end if $first_alarm_frame - if ( $attachments_ref && $Config{ZM_OPT_FFMPEG} ) { + if ( $attachments_ref ) { if ( $text =~ s/%EV%//g ) { - my ( $format, $path ) = generateVideo($filter, $Event); - if ( !$format ) { - return undef; + if ( $$Event{DefaultVideo} ) { + push @$attachments_ref, { type=>'video/mp4', path=>join('/',$Event->Path(), $Event->DefaultVideo()) }; + } elsif ( $Config{ZM_OPT_FFMPEG} ) { + my ( $format, $path ) = generateVideo($filter, $Event); + if ( !$format ) { + return undef; + } + push( @$attachments_ref, { type=>"video/$format", path=>$path } ); } - push( @$attachments_ref, { type=>"video/$format", path=>$path } ); } if ( $text =~ s/%EVM%//g ) { my ( $format, $path ) = generateVideo($filter, $Event, 1); @@ -792,7 +806,7 @@ sub sendEmail { $ssmtp_location = qx('which ssmtp'); } if ( !$ssmtp_location ) { - Debug('Unable tofind ssmtp, trying MIME::Lite->send'); + Debug('Unable to find ssmtp, trying MIME::Lite->send'); MIME::Lite->send('smtp', $Config{ZM_EMAIL_HOST}, Timeout=>60); $mail->send(); } else { @@ -824,16 +838,16 @@ sub sendEmail { } }; if ( $@ ) { - Error("Unable tosend email: $@"); + Error("Unable to send email: $@"); return 0; } else { Info('Notification email sent'); } my $sql = 'UPDATE Events SET Emailed = 1 WHERE Id = ?'; my $sth = $dbh->prepare_cached($sql) - or Fatal("Unable toprepare '$sql': ".$dbh->errstr()); + or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); my $res = $sth->execute($Event->{Id}) - or Fatal("Unable toexecute '$sql': ".$dbh->errstr()); + or Fatal("Unable to execute '$sql': ".$dbh->errstr()); return 1; } @@ -894,7 +908,7 @@ sub sendMessage { $ssmtp_location = qx('which ssmtp'); } if ( !$ssmtp_location ) { - Debug('Unable tofind ssmtp, trying MIME::Lite->send'); + Debug('Unable to find ssmtp, trying MIME::Lite->send'); MIME::Lite->send(smtp=>$Config{ZM_EMAIL_HOST}, Timeout=>60); $mail->send(); } else { @@ -929,29 +943,29 @@ sub sendMessage { } }; if ( $@ ) { - Error("Unable tosend email: $@"); + Error("Unable to send email: $@"); return 0; } else { Info('Notification message sent'); } my $sql = 'UPDATE Events SET Messaged = 1 WHERE Id = ?'; my $sth = $dbh->prepare_cached($sql) - or Fatal("Unable toprepare '$sql': ".$dbh->errstr()); + or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); my $res = $sth->execute($Event->{Id}) - or Fatal("Unable toexecute '$sql': ".$dbh->errstr()); + or Fatal("Unable to execute '$sql': ".$dbh->errstr()); return 1; } # end sub sendMessage sub executeCommand { my $filter = shift; - my $event = shift; + my $Event = shift; - my $event_path = getEventPath($event); + my $event_path = $Event->Path(); my $command = $filter->{AutoExecuteCmd}; $command .= " $event_path"; - $command = substituteTags($command, $filter, $event); + $command = substituteTags($command, $filter, $Event); Info("Executing '$command'"); my $output = qx($command); @@ -967,7 +981,7 @@ sub executeCommand { my $sql = 'UPDATE Events SET Executed = 1 WHERE Id = ?'; my $sth = $dbh->prepare_cached($sql) or Fatal("Unable to prepare '$sql': ".$dbh->errstr()); - my $res = $sth->execute( $event->{Id} ) + my $res = $sth->execute( $Event->{Id} ) or Fatal("Unable to execute '$sql': ".$dbh->errstr()); } return( 1 ); diff --git a/scripts/zmpkg.pl.in b/scripts/zmpkg.pl.in index d11e04d5a..2d6593619 100644 --- a/scripts/zmpkg.pl.in +++ b/scripts/zmpkg.pl.in @@ -44,7 +44,6 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; my $store_state=''; # PP - will remember state name passed logInit(); -Info("Aftere LogInit"); my $command = $ARGV[0]||''; if ( $command eq 'version' ) { @@ -316,17 +315,19 @@ sub isActiveSanityCheck { 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 = q`"INSERT INTO States (Name,Definition,IsActive) VALUES ('default','','1');`; + if ( $sth->rows ) { + $sql = q`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 = q`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'; diff --git a/scripts/zmstats.pl.in b/scripts/zmstats.pl.in index 888a18351..410459158 100644 --- a/scripts/zmstats.pl.in +++ b/scripts/zmstats.pl.in @@ -1,4 +1,63 @@ #!/usr/bin/perl -wT +use strict; +use bytes; + +# ========================================================================== +# +# These are the elements you can edit to suit your installation +# +# ========================================================================== + +use constant START_DELAY => 30; # To give everything else time to start + +# ========================================================================== +# +# Don't change anything below here +# +# ========================================================================== + +@EXTRA_PERL_LIB@ +use ZoneMinder; +use POSIX; +use DBI; +use autouse 'Data::Dumper'=>qw(Dumper); + +$| = 1; + +$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(); +logSetSignal(); + +Info("Stats Daemon starting in ".START_DELAY." seconds"); +sleep(START_DELAY); + +my $dbh = zmDbConnect(); + +while( 1 ) { + while ( ! ( $dbh and $dbh->ping() ) ) { + Info("Reconnecting to db"); + if ( ! ( $dbh = zmDbConnect() ) ) { + #What we do here is not that important, so just skip this interval + sleep($Config{ZM_STATS_UPDATE_INTERVAL}); + } + } + + $dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)') or Error($dbh->errstr()); + $dbh->do('DELETE FROM Events_Day WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 day)') or Error($dbh->errstr()); + $dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)') or Error($dbh->errstr()); + $dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)') or Error($dbh->errstr()); + + sleep($Config{ZM_STATS_UPDATE_INTERVAL}); +} # end while (1) + +Info("Stats Daemon exiting"); +exit(); +1; +__END__ + # # ========================================================================== # @@ -23,7 +82,7 @@ =head1 NAME -zmwatch.pl - ZoneMinder Stats Updating Script +zmstats.pl - ZoneMinder Stats Updating Script =head1 SYNOPSIS @@ -34,64 +93,3 @@ zmstats.pl This does background updating various stats in the db like event counts, diskspace, etc. =cut -use strict; -use bytes; - -# ========================================================================== -# -# These are the elements you can edit to suit your installation -# -# ========================================================================== - -use constant START_DELAY => 30; # To give everything else time to start - -# ========================================================================== -# -# Don't change anything below here -# -# ========================================================================== - -@EXTRA_PERL_LIB@ -use ZoneMinder; -use ZoneMinder::Storage; -use POSIX; -use DBI; -use autouse 'Data::Dumper'=>qw(Dumper); - -$| = 1; - -$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(); -logSetSignal(); - -Info( "Stats Daemon starting in ".START_DELAY." seconds\n" ); -sleep( START_DELAY ); - -my $dbh = zmDbConnect(); - -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 ) { - while ( ! ( $dbh and $dbh->ping() ) ) { - Info("Reconnecting to db"); - $dbh = zmDbConnect(); - } - - $dbh->do('DELETE FROM Events_Hour WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 hour)') or Error($dbh->errstr()); - $dbh->do('DELETE FROM Events_Day WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 day)') or Error($dbh->errstr()); - $dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)') or Error($dbh->errstr()); - $dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)') or Error($dbh->errstr()); - - sleep( $Config{ZM_STATS_UPDATE_INTERVAL} ); -} # end while (1) - -Info( "Stats Daemon exiting\n" ); -exit(); -1; -__END__ diff --git a/scripts/zmtrigger.pl.in b/scripts/zmtrigger.pl.in index 53492e6a8..b16e1cdd4 100644 --- a/scripts/zmtrigger.pl.in +++ b/scripts/zmtrigger.pl.in @@ -88,13 +88,13 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; logInit(); logSetSignal(); -Info( "Trigger daemon starting\n" ); +Info( "Trigger daemon starting" ); my $dbh = zmDbConnect(); my $base_rin = ''; foreach my $connection ( @connections ) { - Info( "Opening connection '$connection->{name}'\n" ); + Info( "Opening connection '$connection->{name}'" ); $connection->open(); } @@ -109,7 +109,7 @@ foreach my $connection ( @in_select_connections ) { my %spawned_connections; my %monitors; my $monitor_reload_time = 0; -my $needsReload = 0; +my @needsReload; loadMonitors(); $! = undef; @@ -127,14 +127,14 @@ while( 1 ) { my $nfound = select( my $rout = $rin, undef, my $eout = $ein, $timeout ); if ( $nfound > 0 ) { - Debug( "Got input from $nfound connections\n" ); + Debug( "Got input from $nfound connections" ); 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(); @@ -143,7 +143,7 @@ while( 1 ) { .$new_connection->fileno() .'), ' .int(keys(%spawned_connections)) - ." spawned connections\n" + ." spawned connections" ); } else { my $messages = $connection->getMessages(); @@ -162,7 +162,7 @@ while( 1 ) { .$connection->name() .' (' .$connection->fileno() - .")\n" + .")" ); my $messages = $connection->getMessages(); if ( defined($messages) ) { @@ -175,7 +175,7 @@ while( 1 ) { .$connection->fileno() .'), ' .int(keys(%spawned_connections)) - ." spawned connections\n" + ." spawned connections" ); $connection->close(); } @@ -206,7 +206,7 @@ while( 1 ) { if ( ! zmMemVerify($monitor) ) { # Our attempt to verify the memory handle failed. We should reload the monitors. # Don't need to zmMemInvalidate because the monitor reload will do it. - $needsReload = 1; + push @needsReload, $monitor; next; } @@ -217,8 +217,8 @@ while( 1 ) { ] ); -#print( "$monitor->{Id}: S:$state, LE:$last_event\n" ); -#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}\n" ); +#print( "$monitor->{Id}: S:$state, LE:$last_event" ); +#print( "$monitor->{Id}: mS:$monitor->{LastState}, mLE:$monitor->{LastEvent}" ); if ( $state == STATE_ALARM || $state == STATE_ALERT ) { # In alarm state if ( !defined($monitor->{LastEvent}) @@ -261,14 +261,14 @@ while( 1 ) { } if ( my @action_times = keys(%actions) ) { - Debug( "Checking for timed actions\n" ); + Debug( "Checking for timed actions" ); my $now = time(); foreach my $action_time ( sort( grep { $_ < $now } @action_times ) ) { - Info( "Found actions expiring at $action_time\n" ); + Info( "Found actions expiring at $action_time" ); foreach my $action ( @{$actions{$action_time}} ) { my $connection = $action->{connection}; my $message = $action->{message}; - Info( "Found action '$message'\n" ); + Info( "Found action '$message'" ); handleMessage( $connection, $message ); } delete( $actions{$action_time} ); @@ -293,23 +293,41 @@ while( 1 ) { } } -# If necessary reload monitors - if ( $needsReload || ((time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL )) { + # Reload all monitors from the dB every MONITOR_RELOAD_INTERVAL + if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ) { foreach my $monitor ( values(%monitors) ) { -# Free up any used memory handle - zmMemInvalidate( $monitor ); + zmMemInvalidate( $monitor ); # Free up any used memory handle } loadMonitors(); - $needsReload = 0; + @needsReload = (); # We just reloaded all monitors so no need reload a specific monitor + # If we have NOT just reloaded all monitors, reload a specific monitor if its shared mem changed + } elsif ( @needsReload ) { + foreach my $monitor ( @needsReload ) { + loadMonitor($monitor); + } + @needsReload = (); } + # zmDbConnect will ping and reconnect if neccessary $dbh = zmDbConnect(); } # end while ( 1 ) -Info( "Trigger daemon exiting\n" ); +Info( "Trigger daemon exiting" ); exit; +sub loadMonitor { + my $monitor = shift; + + Debug( "Loading monitor $monitor" ); + zmMemInvalidate( $monitor ); + + if ( zmMemVerify( $monitor ) ) { # This will re-init shared memory + $monitor->{LastState} = zmGetMonitorState( $monitor ); + $monitor->{LastEvent} = zmGetLastEvent( $monitor ); + } +} + sub loadMonitors { - Debug( "Loading monitors\n" ); + Debug( "Loading monitors" ); $monitor_reload_time = time(); my %new_monitors = (); @@ -323,14 +341,10 @@ sub loadMonitors { 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() ) { -# Check shared memory ok - if ( !zmMemVerify( $monitor ) ) { - zmMemInvalidate( $monitor ); - next; + if ( zmMemVerify( $monitor ) ) { # This will re-init shared memory + $monitor->{LastState} = zmGetMonitorState( $monitor ); + $monitor->{LastEvent} = zmGetLastEvent( $monitor ); } - - $monitor->{LastState} = zmGetMonitorState( $monitor ); - $monitor->{LastEvent} = zmGetLastEvent( $monitor ); $new_monitors{$monitor->{Id}} = $monitor; } # end while fetchrow %monitors = %new_monitors; @@ -348,14 +362,14 @@ sub handleMessage { my $monitor = $monitors{$id}; if ( !$monitor ) { - Warning( "Can't find monitor '$id' for message '$message'\n" ); + Warning( "Can't find monitor '$id' for message '$message'" ); return; } - Debug( "Found monitor for id '$id'\n" ); + Debug( "Found monitor for id '$id'" ); next if ( !zmMemVerify( $monitor ) ); - Debug( "Handling action '$action'\n" ); + Debug( "Handling action '$action'" ); if ( $action =~ /^(enable|disable)(?:\+(\d+))?$/ ) { my $state = $1; my $delay = $2; @@ -366,7 +380,7 @@ sub handleMessage { } # Force a reload $monitor_reload_time = 0; - Info( "Set monitor to $state\n" ); + Info( "Set monitor to $state" ); if ( $delay ) { my $action_text = $id.'|'.( ($state eq 'enable') ? 'disable' @@ -383,7 +397,7 @@ sub handleMessage { if ( $trigger eq 'on' ) { zmTriggerEventOn( $monitor, $score, $cause, $text ); zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); - Info( "Trigger '$trigger' '$cause'\n" ); + Info( "Trigger '$trigger' '$cause'" ); if ( $delay ) { my $action_text = $id.'|cancel'; handleDelay($delay, $connection, $action_text); @@ -396,7 +410,7 @@ sub handleMessage { my $last_event = zmGetLastEvent( $monitor ); zmTriggerEventOff( $monitor ); zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); - Info( "Trigger '$trigger'\n" ); + Info( "Trigger '$trigger'" ); # Wait til it's finished while( zmInAlarm( $monitor ) && ($last_event == zmGetLastEvent( $monitor )) @@ -410,12 +424,12 @@ sub handleMessage { } elsif( $action eq 'cancel' ) { zmTriggerEventCancel( $monitor ); zmTriggerShowtext( $monitor, $showtext ) if defined($showtext); - Info( "Cancelled event\n" ); + Info( "Cancelled event" ); } elsif( $action eq 'show' ) { zmTriggerShowtext( $monitor, $showtext ); - Info( "Updated show text to '$showtext'\n" ); + Info( "Updated show text to '$showtext'" ); } else { - Error( "Unrecognised action '$action' in message '$message'\n" ); + Error( "Unrecognised action '$action' in message '$message'" ); } } # end sub handleMessage @@ -430,7 +444,7 @@ sub handleDelay { $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" ); + Debug( "Added timed event '$action_text', expires at $action_time (+$delay secs)" ); } 1; __END__ diff --git a/scripts/zmupdate.pl.in b/scripts/zmupdate.pl.in index f428ea1f9..45d45851a 100644 --- a/scripts/zmupdate.pl.in +++ b/scripts/zmupdate.pl.in @@ -922,6 +922,11 @@ if ( $version ) { zmDbDisconnect(); die( "Can't find upgrade from version '$version'" ); } + # Re-enable the privacy popup after each upgrade + my $sql = "update Config set Value = 1 where Name = 'ZM_SHOW_PRIVACY'"; + 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( "\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n" ); } zmDbDisconnect(); diff --git a/scripts/zmwatch.pl.in b/scripts/zmwatch.pl.in index b98b9d22a..e48b8d9d3 100644 --- a/scripts/zmwatch.pl.in +++ b/scripts/zmwatch.pl.in @@ -78,6 +78,11 @@ my $sth = $dbh->prepare_cached($sql) or Fatal("Can't prepare '$sql': ".$dbh->errstr()); while( 1 ) { + while ( ! ( $dbh and $dbh->ping() ) ) { + if ( ! ( $dbh = zmDbConnect() ) ) { + sleep($Config{ZM_WATCH_CHECK_INTERVAL}); + } + } my $res = $sth->execute( $Config{ZM_SERVER_ID} ? $Config{ZM_SERVER_ID} : () ) or Fatal('Can\'t execute: '.$sth->errstr()); @@ -98,7 +103,7 @@ while( 1 ) { Debug("Monitor $$monitor{Id} LastWriteTime is $capture_time."); if ( !$capture_time ) { my $startup_time = zmGetStartupTime($monitor); - if ( $now - $startup_time > $Config{ZM_WATCH_MAX_DELAY} ) { + if ( ( $now - $startup_time ) > $Config{ZM_WATCH_MAX_DELAY} ) { Info( "Restarting capture daemon for $$monitor{Name}, no image since startup. ". "Startup time was $startup_time - now $now > $Config{ZM_WATCH_MAX_DELAY}" @@ -111,11 +116,12 @@ while( 1 ) { } } if ( ! $restart ) { - my $max_image_delay = ( $monitor->{MaxFPS} - &&($monitor->{MaxFPS}>0) - &&($monitor->{MaxFPS}<1) - ) ? (3/$monitor->{MaxFPS}) - : $Config{ZM_WATCH_MAX_DELAY} + my $max_image_delay = ( + $monitor->{MaxFPS} + &&($monitor->{MaxFPS}>0) + &&($monitor->{MaxFPS}<1) + ) ? (3/$monitor->{MaxFPS}) + : $Config{ZM_WATCH_MAX_DELAY} ; my $image_delay = $now-$capture_time; Debug("Monitor $monitor->{Id} last captured $image_delay seconds ago, max is $max_image_delay"); diff --git a/src/zm_camera.h b/src/zm_camera.h index 83b5f922b..cd7a024f4 100644 --- a/src/zm_camera.h +++ b/src/zm_camera.h @@ -33,8 +33,7 @@ class Camera; // Abstract base class for cameras. This is intended just to express // common attributes // -class Camera -{ +class Camera { protected: typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType; @@ -53,49 +52,48 @@ protected: int contrast; bool capture; bool record_audio; - unsigned int bytes; - + unsigned int bytes; public: Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ); virtual ~Camera(); - unsigned int getId() const { return( monitor_id ); } + unsigned int getId() const { return monitor_id; } Monitor *getMonitor(); void setMonitor( Monitor *p_monitor ); - 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 ); } + 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; } unsigned int Bytes() const { return bytes; }; - 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 ); } + bool CanCapture() const { return capture; } bool SupportsNativeVideo() const { return (type == FFMPEG_SRC); //return (type == FFMPEG_SRC )||(type == REMOTE_SRC); } - virtual int PrimeCapture() { return( 0 ); } - virtual int PreCapture()=0; - virtual int Capture( Image &image )=0; - virtual int PostCapture()=0; - virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) = 0; - virtual int Close()=0; + virtual int PrimeCapture() { return 0; } + virtual int PreCapture() = 0; + virtual int Capture(Image &image) = 0; + virtual int PostCapture() = 0; + virtual int CaptureAndRecord(Image &image, timeval recording, char* event_directory) = 0; + virtual int Close() = 0; }; #endif // ZM_CAMERA_H diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 713b9728d..fbf5cf848 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -120,9 +120,15 @@ Event::Event( char id_file[PATH_MAX]; + char *path_ptr = path; + path_ptr += snprintf(path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id()); + // Try to make the Monitor Dir. Normally this would exist, but in odd cases might not. + if ( mkdir(path, 0755) ) { + if ( errno != EEXIST ) + Error("Can't mkdir %s: %s", path, strerror(errno)); + } + if ( storage->Scheme() == Storage::DEEP ) { - char *path_ptr = path; - path_ptr += snprintf(path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id()); int dt_parts[6]; dt_parts[0] = stime->tm_year-100; @@ -141,9 +147,8 @@ Event::Event( errno = 0; if ( mkdir(path, 0755) ) { // FIXME This should not be fatal. Should probably move to a different storage area. - if ( errno != EEXIST ) { + if ( errno != EEXIST ) Error("Can't mkdir %s: %s", path, strerror(errno)); - } } if ( i == 2 ) strncpy(date_path, path, sizeof(date_path)); @@ -155,28 +160,24 @@ Event::Event( if ( symlink(time_path, id_file) < 0 ) Error("Can't symlink %s -> %s: %s", id_file, path, strerror(errno)); } else if ( storage->Scheme() == Storage::MEDIUM ) { - char *path_ptr = path; path_ptr += snprintf( - path_ptr, sizeof(path), "%s/%d/%04d-%02d-%02d", - storage->Path(), monitor->Id(), stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday + path_ptr, sizeof(path), "/%04d-%02d-%02d", + stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday ); if ( mkdir(path, 0755) ) { - // FIXME This should not be fatal. Should probably move to a different storage area. if ( errno != EEXIST ) Error("Can't mkdir %s: %s", path, strerror(errno)); } path_ptr += snprintf(path_ptr, sizeof(path), "/%" PRIu64, id); if ( mkdir(path, 0755) ) { - // FIXME This should not be fatal. Should probably move to a different storage area. if ( errno != EEXIST ) Error("Can't mkdir %s: %s", path, strerror(errno)); } } else { - snprintf(path, sizeof(path), "%s/%d/%" PRIu64, storage->Path(), monitor->Id(), id); + snprintf(path, sizeof(path), "/%" PRIu64, id); if ( mkdir(path, 0755) ) { - if ( errno != EEXIST ) { + if ( errno != EEXIST ) Error("Can't mkdir %s: %s", path, strerror(errno)); - } } // Create empty id tag file @@ -540,7 +541,7 @@ Debug(3, "Writing video"); if ( score < 0 ) score = 0; - bool db_frame = ( frame_type != BULK ) || ((frames%config.bulk_frame_interval)==0) || !frames; + bool db_frame = ( frame_type != BULK ) || (!frames) || ((frames%config.bulk_frame_interval)==0) ; if ( db_frame ) { Debug( 1, "Adding frame %d of type \"%s\" to DB", frames, Event::frame_type_names[frame_type] ); diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 2578d91d4..9f7eae4ec 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -279,8 +279,8 @@ void EventStream::processCommand(const CmdMsg *msg) { } // 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) ) { - Debug(1, "Was in single_mode, and last frame, so jumping to 1st frame"); + if ( (mode == MODE_SINGLE || mode == MODE_NONE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) { + Debug(1, "Was in single or no replay mode, and at last frame, so jumping to 1st frame"); curr_frame_id = 1; } else { Debug(1, "mode is %s, current frame is %d, frame count is %d", (mode == MODE_SINGLE ? "single" : "not single" ), curr_frame_id, event_data->frame_count ); @@ -501,7 +501,7 @@ void EventStream::checkEventLoaded() { } if ( reload_event ) { - if ( forceEventChange || mode != MODE_SINGLE ) { + if ( forceEventChange || ( mode != MODE_SINGLE && mode != MODE_NONE ) ) { //Info( "SQL:%s", sql ); if ( mysql_query( &dbconn, sql ) ) { Error( "Can't run query: %s", mysql_error( &dbconn ) ); @@ -755,6 +755,12 @@ void EventStream::runStream() { // commands may set send_frame to true while(checkCommandQueue()); + // Update modified time of the socket .lock file so that we can tell which ones are stale. + if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) { + touch(sock_path_lock); + last_comm_update = now; + } + if ( step != 0 ) curr_frame_id += step; @@ -812,7 +818,7 @@ void EventStream::runStream() { step = 0; send_frame = true; } else if ( !send_frame ) { - // We are paused, and doing nothing + // We are paused, not stepping and doing nothing double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent; if ( actual_delta_time > MAX_STREAM_DELAY ) { // Send keepalive @@ -828,9 +834,11 @@ void EventStream::runStream() { curr_stream_time = frame_data->timestamp; if ( !paused ) { - curr_frame_id += replay_rate>0?1:-1; - if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) + curr_frame_id += (replay_rate>0) ? 1 : -1; + if ( (mode == MODE_SINGLE) && ((unsigned int)curr_frame_id == event_data->frame_count) ) { + Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start"); curr_frame_id = 1; + } if ( send_frame && type != STREAM_MPEG ) { Debug( 3, "dUs: %d", delta_us ); if ( delta_us ) diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 067661937..90200df65 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -42,7 +42,7 @@ extern "C" { class EventStream : public StreamBase { public: - typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode; + typedef enum { MODE_NONE, MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode; protected: struct FrameData { diff --git a/src/zm_ffmpeg.cpp b/src/zm_ffmpeg.cpp index ac841f27b..79a1d8b26 100644 --- a/src/zm_ffmpeg.cpp +++ b/src/zm_ffmpeg.cpp @@ -225,8 +225,9 @@ static void zm_log_fps(double d, const char *postfix) { Debug(1, "%3.2f %s", d, postfix); } else if (v % (100 * 1000)) { Debug(1, "%1.0f %s", d, postfix); - } else + } else { Debug(1, "%1.0fk %s", d / 1000, postfix); + } } #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) @@ -355,9 +356,8 @@ int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt) { #if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100) #else unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ) { - dst->size = (src->size + FF_INPUT_BUFFER_PADDING_SIZE)/sizeof(uint64_t) + 1; - dst->data = reinterpret_cast(new uint64_t[dst->size]); - memcpy(dst->data, src->data, src->size ); + av_new_packet(dst,src->size); + memcpy(dst->data, src->data, src->size); dst->flags = src->flags; return 0; } diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index 7e3cf9651..a3a88519c 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -303,8 +303,8 @@ void zm_dump_codecpar ( const AVCodecParameters *par ); #define zm_av_packet_unref( packet ) av_packet_unref( packet ) #define zm_av_packet_ref( dst, src ) av_packet_ref( dst, src ) #else + unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ); #define zm_av_packet_unref( packet ) av_free_packet( packet ) -unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ); #endif #if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0) #define zm_avcodec_decode_video( context, rawFrame, frameComplete, packet ) avcodec_decode_video2( context, rawFrame, frameComplete, packet ) diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 2622f5dfa..83d837812 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -331,6 +331,8 @@ int FfmpegCamera::OpenFfmpeg() { ret = av_dict_set(&opts, "rtsp_transport", "tcp", 0); } else if ( method == "rtpRtspHttp" ) { ret = av_dict_set(&opts, "rtsp_transport", "http", 0); + } else if ( method == "rtpUni" ) { + ret = av_dict_set(&opts, "rtsp_transport", "udp", 0); } else { Warning("Unknown method (%s)", method.c_str() ); } @@ -606,7 +608,8 @@ int FfmpegCamera::OpenFfmpeg() { return -1; } - mConvertContext = sws_getContext(mVideoCodecContext->width, + mConvertContext = sws_getContext( + mVideoCodecContext->width, mVideoCodecContext->height, mVideoCodecContext->pix_fmt, width, height, @@ -722,7 +725,8 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event uint32_t video_writer_event_id = monitor->GetVideoWriterEventId(); if ( last_event_id != video_writer_event_id ) { - Debug(2, "Have change of event. last_event(%d), our current (%d)", last_event_id, video_writer_event_id ); + Debug(2, "Have change of event. last_event(%d), our current (%d)", + last_event_id, video_writer_event_id); if ( videoStore ) { Info("Re-starting video storage module"); @@ -731,7 +735,7 @@ int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event // Also don't know how much it matters for audio. if ( packet.stream_index == mVideoStreamId ) { //Write the packet to our video store - int ret = videoStore->writeVideoFramePacket( &packet ); + int ret = videoStore->writeVideoFramePacket(&packet); if ( ret < 0 ) { //Less than zero and we skipped a frame Warning("Error writing last packet to videostore."); } diff --git a/src/zm_ffmpeg_camera.h b/src/zm_ffmpeg_camera.h index d41518bb7..21eeb328f 100644 --- a/src/zm_ffmpeg_camera.h +++ b/src/zm_ffmpeg_camera.h @@ -59,7 +59,7 @@ class FfmpegCamera : public Camera { bool hwaccel; #if HAVE_AVUTIL_HWCONTEXT_H AVFrame *hwFrame; - DecodeContext decode; + DecodeContext decode; #endif // Need to keep track of these because apparently the stream can start with values for pts/dts and then subsequent packets start at zero. diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 9ea89a0d2..491ae8470 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -649,11 +649,9 @@ void Image::Assign( const Image &image ) { (*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 ); +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 */ @@ -1096,8 +1094,7 @@ bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int jpeg_read_header( cinfo, TRUE ); - if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) - { + if ( cinfo->num_components != 1 && cinfo->num_components != 3 ) { Error( "Unexpected colours when reading jpeg image: %d", colours ); jpeg_abort_decompress( cinfo ); return( false ); @@ -1112,8 +1109,7 @@ bool Image::DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int new_width = cinfo->image_width; new_height = cinfo->image_height; - if ( width != new_width || height != new_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); } @@ -1175,7 +1171,7 @@ cinfo->out_color_space = JCS_RGB; } break; } - } + } // end switch if(WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == NULL) { Error("Failed requesting writeable buffer for reading JPEG image."); @@ -1187,21 +1183,18 @@ cinfo->out_color_space = JCS_RGB; 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 ) - { + 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 ); - 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 ) - { +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 ) ); @@ -1211,8 +1204,7 @@ bool Image::EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_over struct jpeg_compress_struct *cinfo = encodejpg_ccinfo[quality]; - if ( !cinfo ) - { + if ( !cinfo ) { cinfo = encodejpg_ccinfo[quality] = new jpeg_compress_struct; cinfo->err = jpeg_std_error( &jpg_err.pub ); jpg_err.pub.error_exit = zm_jpeg_error_exit; @@ -1278,7 +1270,7 @@ cinfo->out_color_space = JCS_RGB; } break; } - } + } // end switch jpeg_set_defaults( cinfo ); jpeg_set_quality( cinfo, quality, FALSE ); @@ -1288,44 +1280,38 @@ cinfo->out_color_space = JCS_RGB; 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 ) - { + 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 ); - return( true ); + return true; } #if HAVE_ZLIB_H -bool Image::Unzip( const Bytef *inbuffer, unsigned long inbuffer_size ) -{ +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 ( 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 ); + if ( zip_size != (unsigned int)size ) { + Error("Unzip failed, size mismatch, expected %d bytes, got %ld", size, zip_size); + return false; } - return( true ); + return true; } -bool Image::Zip( Bytef *outbuffer, unsigned long *outbuffer_size, int compression_level ) const -{ +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 ); + if ( result != Z_OK ) { + Error("Zip failed, result = %d", result); + return false; } - return( true ); + return true; } #endif // HAVE_ZLIB_H @@ -1339,11 +1325,11 @@ bool Image::Crop( unsigned int lo_x, unsigned int lo_y, unsigned int hi_x, unsig } if ( hi_x > (width-1) || ( 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 ); + return false; } if ( new_width == width && new_height == height ) { - return( true ); + return true; } unsigned int new_size = new_width*new_height*colours; @@ -1358,24 +1344,22 @@ bool Image::Crop( unsigned int lo_x, unsigned int lo_y, unsigned int hi_x, unsig 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() ) ); +bool Image::Crop( const Box &limits ) { + 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 ); +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 ) { + if ( colours == image.colours && subpixelorder != image.subpixelorder ) { Warning("Attempt to overlay images of same format but with different subpixel order."); } @@ -1385,10 +1369,8 @@ void Image::Overlay( const Image &image ) const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; - while( pdest < max_ptr ) - { - if ( *psrc ) - { + while( pdest < max_ptr ) { + if ( *psrc ) { *pdest = *psrc; } pdest++; @@ -1403,10 +1385,8 @@ void Image::Overlay( const Image &image ) 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) ) - { + 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); @@ -1423,11 +1403,10 @@ void Image::Overlay( const Image &image ) const Rgb* prsrc = (Rgb*)image.buffer; Rgb* prdest = (Rgb*)buffer; - if(subpixelorder == ZM_SUBPIX_ORDER_RGBA || subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + 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) ) - { + if ( RED_PTR_RGBA(prsrc) || GREEN_PTR_RGBA(prsrc) || BLUE_PTR_RGBA(prsrc) ) { *prdest = *prsrc; } prdest++; @@ -1436,8 +1415,7 @@ void Image::Overlay( const Image &image ) } 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) ) - { + if ( RED_PTR_ABGR(prsrc) || GREEN_PTR_ABGR(prsrc) || BLUE_PTR_ABGR(prsrc) ) { *prdest = *prsrc; } prdest++; @@ -1451,10 +1429,8 @@ void Image::Overlay( const Image &image ) const uint8_t* psrc = image.buffer; uint8_t* pdest = buffer; - while( pdest < max_ptr ) - { - if ( *psrc ) - { + while( pdest < max_ptr ) { + if ( *psrc ) { RED_PTR_RGBA(pdest) = GREEN_PTR_RGBA(pdest) = BLUE_PTR_RGBA(pdest) = *psrc; } pdest += 3; @@ -1467,10 +1443,8 @@ void Image::Overlay( const Image &image ) 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) ) - { + 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); @@ -1489,11 +1463,10 @@ void Image::Overlay( const Image &image ) Rgb* prdest = (Rgb*)buffer; const uint8_t* psrc = image.buffer; - if(subpixelorder == ZM_SUBPIX_ORDER_RGBA || subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + 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 ) - { + while ( prdest < max_ptr ) { + if ( *psrc ) { RED_PTR_RGBA(prdest) = GREEN_PTR_RGBA(prdest) = BLUE_PTR_RGBA(prdest) = *psrc; } prdest++; @@ -1501,9 +1474,8 @@ void Image::Overlay( const Image &image ) } } else { /* ABGR\ARGB subpixel order - Alpha byte is first */ - while (prdest < max_ptr) { - if ( *psrc ) - { + while ( prdest < max_ptr ) { + if ( *psrc ) { RED_PTR_ABGR(prdest) = GREEN_PTR_ABGR(prdest) = BLUE_PTR_ABGR(prdest) = *psrc; } prdest++; @@ -1521,11 +1493,10 @@ void Image::Overlay( const Image &image ) Rgb* prdest = (Rgb*)buffer; const Rgb* prsrc = (Rgb*)image.buffer; - if(image.subpixelorder == ZM_SUBPIX_ORDER_RGBA || image.subpixelorder == ZM_SUBPIX_ORDER_BGRA) { + 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) ) - { + while ( prdest < max_ptr ) { + if ( RED_PTR_RGBA(prsrc) || GREEN_PTR_RGBA(prsrc) || BLUE_PTR_RGBA(prsrc) ) { *prdest = *prsrc; } prdest++; @@ -1533,9 +1504,8 @@ void Image::Overlay( const Image &image ) } } 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) ) - { + while ( prdest < max_ptr ) { + if ( RED_PTR_ABGR(prsrc) || GREEN_PTR_ABGR(prsrc) || BLUE_PTR_ABGR(prsrc) ) { *prdest = *prsrc; } prdest++; @@ -1547,61 +1517,49 @@ void Image::Overlay( const Image &image ) } /* 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 ); +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 < (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 ) - { + if ( colours == ZM_COLOUR_GRAY8 ) { const uint8_t *psrc = image.buffer; - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { + 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++ ) - { + for ( unsigned int x = lo_x; x <= hi_x; x++ ) { *pdest++ = *psrc++; } } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { + } else if ( colours == ZM_COLOUR_RGB24 ) { const uint8_t *psrc = image.buffer; - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { + 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++ ) - { + for ( unsigned int x = lo_x; x <= hi_x; x++ ) { *pdest++ = *psrc++; *pdest++ = *psrc++; *pdest++ = *psrc++; } } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { + } else if ( colours == ZM_COLOUR_RGB32 ) { const Rgb *psrc = (Rgb*)(image.buffer); - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { + 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++ ) - { + for ( unsigned int x = lo_x; x <= hi_x; x++ ) { *pdest++ = *psrc++; } } @@ -1611,8 +1569,7 @@ void Image::Overlay( const Image &image, unsigned int x, unsigned int y ) } -void Image::Blend( const Image &image, int transparency ) -{ +void Image::Blend( const Image &image, int transparency ) { #ifdef ZM_IMAGE_PROFILING struct timespec start,end,diff; unsigned long long executetime; @@ -1620,12 +1577,16 @@ void Image::Blend( const Image &image, int transparency ) #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 ( !( + 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) + if ( transparency <= 0 ) return; new_buffer = AllocBuffer(size); @@ -1646,7 +1607,7 @@ void Image::Blend( const Image &image, int transparency ) 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[] ) { @@ -1657,7 +1618,8 @@ Image *Image::Merge( unsigned int n_images, Image *images[] ) { 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 ); + 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 ); } } @@ -1685,7 +1647,8 @@ Image *Image::Merge( unsigned int n_images, Image *images[], double weight ) { 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 ); + 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 ); } } @@ -1705,8 +1668,7 @@ Image *Image::Merge( unsigned int n_images, Image *images[], double weight ) { return result; } -Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb threshold, const Rgb ref_colour ) -{ +Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb threshold, const Rgb ref_colour ) { if ( n_images == 1 ) return new Image(*images[0]); unsigned int width = images[0]->width; @@ -1714,7 +1676,8 @@ Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb thres 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 ); + 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 ); } } @@ -1740,26 +1703,25 @@ Image *Image::Highlight( unsigned int n_images, Image *images[], const Rgb thres pdest += 3; } } - return( result ); + return result; } /* 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 -{ +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; #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); + 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) { + if ( pdiff == NULL ) { Panic("Failed requesting writeable buffer for storing the delta image"); } @@ -1849,34 +1811,24 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour unsigned char *ptr = &buffer[0]; unsigned int i = 0; - for ( unsigned int y = 0; y < height; y++ ) - { - if ( colours == ZM_COLOUR_GRAY8 ) - { - for ( unsigned int x = 0; x < width; x++, ptr++ ) - { + for ( unsigned int y = 0; y < height; y++ ) { + if ( colours == ZM_COLOUR_GRAY8 ) { + 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] ) - { + } 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 ) - { + } 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; @@ -1886,8 +1838,7 @@ void Image::MaskPrivacy( const unsigned char *p_bitmask, const Rgb pixel_colour Panic("MaskPrivacy called with unexpected colours: %d", colours); return; } - - } + } // end foreach y } /* RGB32 compatible: complete */ @@ -1919,8 +1870,7 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int if (size == 2) zm_text_bitmask = 0x8000; - while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) - { + while ( (index < text_len) && (line_len = strcspn( line, "\n" )) ) { unsigned int line_width = line_len * ZM_CHAR_WIDTH * size; @@ -1950,62 +1900,46 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int if ( hi_line_y > height ) hi_line_y = height; - if ( colours == ZM_COLOUR_GRAY8 ) - { + 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 < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += width ) - { + for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_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++ ) - { + 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] * ZM_CHAR_HEIGHT * size) + r]; else f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; - for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) - { - if ( f & (zm_text_bitmask >> i) ) - { + for ( unsigned int i = 0; i < (ZM_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 ) - { + } else if ( !bg_trans ) { *temp_ptr = bg_bw_col; } } } } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { + } 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 < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += wc ) - { + for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_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++ ) - { + 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] * ZM_CHAR_HEIGHT * size) + r]; else f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; - for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr += colours ) - { - if ( f & (zm_text_bitmask >> i) ) - { - if ( !fg_trans ) - { + for ( unsigned int i = 0; i < (ZM_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 ) - { + } 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; @@ -2013,33 +1947,24 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int } } } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { + } 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 < (ZM_CHAR_HEIGHT * size); y++, r++, ptr += wc ) - { + for ( unsigned int y = lo_line_y, r = 0; y < hi_line_y && r < (ZM_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++ ) - { + 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] * ZM_CHAR_HEIGHT * size) + r]; else f = fontdata[(line[c] * ZM_CHAR_HEIGHT) + r]; - for ( unsigned int i = 0; i < (ZM_CHAR_WIDTH * size) && x < hi_line_x; i++, x++, temp_ptr++ ) - { - if ( f & (zm_text_bitmask >> i) ) - { - if ( !fg_trans ) - { + for ( unsigned int i = 0; i < (ZM_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 ) - { + } else if ( !bg_trans ) { *temp_ptr = bg_rgb_col; } } @@ -2052,8 +1977,7 @@ void Image::Annotate( const char *p_text, const Coord &coord, const unsigned int } index += line_len; - while ( text[index] == '\n' ) - { + while ( text[index] == '\n' ) { index++; } line = text+index; @@ -2074,8 +1998,7 @@ void Image::Timestamp( const char *label, const time_t when, const Coord &coord, } /* RGB32 compatible: complete */ -void Image::Colourise(const unsigned int p_reqcolours, const unsigned int p_reqsubpixelorder) -{ +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) { @@ -2092,9 +2015,9 @@ void Image::Colourise(const unsigned int p_reqcolours, const unsigned int p_reqs Rgb subpixel; Rgb newpixel; - if ( p_reqsubpixelorder == ZM_SUBPIX_ORDER_ABGR || p_reqsubpixelorder == ZM_SUBPIX_ORDER_ARGB) { + 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().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++ ) - { + 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++) - { + 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++ ) - { + } 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) - { + 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++ ) - { + } 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++) - { + for ( unsigned int x = lo_x; x <= (unsigned int)hi_x; x++, p++) { /* Fast, copies the entire pixel in a single pass */ *p = colour; } @@ -2247,15 +2153,13 @@ void Image::Fill( Rgb colour, const Box *limits ) } /* RGB32 compatible: complete */ -void Image::Fill( Rgb colour, int density, const Box *limits ) -{ +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) + 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 ); + 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 */ @@ -2265,25 +2169,18 @@ void Image::Fill( Rgb colour, int density, const Box *limits ) 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++ ) - { + 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++) - { + 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++ ) - { + } 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) - { + 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); @@ -2291,38 +2188,30 @@ void Image::Fill( Rgb colour, int density, const Box *limits ) } } } - } - else if ( colours == ZM_COLOUR_RGB32 ) /* RGB32 */ - { - for ( unsigned int y = lo_y; y <= hi_y; y++ ) - { + } 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++) - { + 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 ); +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++ ) - { + 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 ); @@ -2337,8 +2226,7 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) double grad; //Debug( 9, "dx: %.2lf, dy: %.2lf", dx, dy ); - if ( fabs(dx) <= fabs(dy) ) - { + if ( fabs(dx) <= fabs(dy) ) { //Debug( 9, "dx <= dy" ); if ( y1 != y2 ) grad = dx/dy; @@ -2348,35 +2236,25 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) double x; int y, yinc = (y1 dy" ); if ( x1 != x2 ) grad = dy/dx; @@ -2387,42 +2265,31 @@ void Image::Outline( Rgb colour, const Polygon &polygon ) double y; int x, xinc = (x1= Logger::DEBUG9 ) - { - for ( int i = 0; i < n_global_edges; i++ ) - { + 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 ); } } @@ -2470,60 +2334,46 @@ void Image::Fill( Rgb colour, int density, const Polygon &polygon ) 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" ); + 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) ) - { + 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 - { + } 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++ ) - { + 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) ) - { + if ( !(y%density) ) { //Debug( 9, "%d", y ); - for ( int i = 0; i < n_active_edges; ) - { + 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 ) { + 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) ) - { + 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 ) { + } 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) ) - { + 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); @@ -2531,10 +2381,8 @@ void Image::Fill( Rgb colour, int density, const Polygon &polygon ) } } 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) ) - { + for ( int x = lo_x; x <= hi_x; x++, p++) { + if ( !(x%density) ) { /* Fast, copies the entire pixel in a single pass */ *p = colour; } @@ -2543,43 +2391,35 @@ void Image::Fill( Rgb colour, int density, const Polygon &polygon ) } } y++; - for ( int i = n_active_edges-1; i >= 0; i-- ) - { - if ( y >= active_edges[i].max_y ) // Or >= as per sheets - { + 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) ) - { + 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 - { + } 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 ) -{ +void Image::Fill( Rgb colour, const Polygon &polygon ) { Fill( colour, 1, polygon ); } /* RGB32 compatible: complete */ -void Image::Rotate( int angle ) -{ +void Image::Rotate( int angle ) { angle %= 360; - if ( !angle ) - { + if ( !angle ) { return; } - if ( angle%90 ) - { + if ( angle%90 ) { return; } @@ -2710,39 +2550,28 @@ void Image::Flip( bool leftright ) { 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++ ) - { + 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 ) - { + } 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++ ) - { + 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++ ) - { + } 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); @@ -2751,15 +2580,12 @@ void Image::Flip( bool leftright ) { s_ptr += line_bytes2; } } - } - else - { + } else { // Vertical flip, top to bottom unsigned char *s_ptr = buffer+(height*line_bytes); unsigned char *d_ptr = flip_buffer; - while( s_ptr > buffer ) - { + while( s_ptr > buffer ) { s_ptr -= line_bytes; memcpy( d_ptr, s_ptr, line_bytes ); d_ptr += line_bytes; @@ -2770,15 +2596,12 @@ void Image::Flip( bool leftright ) { } -void Image::Scale( unsigned int factor ) -{ - if ( !factor ) - { +void Image::Scale( unsigned int factor ) { + if ( !factor ) { Error( "Bogus scale factor %d found", factor ); return; } - if ( factor == ZM_SCALE_BASE ) - { + if ( factor == ZM_SCALE_BASE ) { return; } @@ -2789,8 +2612,7 @@ void Image::Scale( unsigned int factor ) uint8_t* scale_buffer = AllocBuffer(scale_buffer_size); - if ( factor > ZM_SCALE_BASE ) - { + if ( factor > ZM_SCALE_BASE ) { unsigned char *pd = scale_buffer; unsigned int wc = width*colours; unsigned int nwc = new_width*colours; @@ -2798,20 +2620,16 @@ void Image::Scale( unsigned int factor ) unsigned int last_h_index = 0; unsigned int last_w_index = 0; unsigned int h_index; - for ( unsigned int y = 0; y < height; y++ ) - { + 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++ ) - { + 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++ ) - { + for (unsigned int f = last_w_index; f < w_index; f++ ) { + for ( unsigned int c = 0; c < colours; c++ ) { *pd++ = *(ps+c); } } @@ -2820,8 +2638,7 @@ void Image::Scale( unsigned int factor ) } h_count += factor; h_index = h_count/ZM_SCALE_BASE; - for ( unsigned int f = last_h_index+1; f < h_index; f++ ) - { + for ( unsigned int f = last_h_index+1; f < h_index; f++ ) { memcpy( pd, pd-nwc, nwc ); pd += nwc; } @@ -2829,9 +2646,7 @@ void Image::Scale( unsigned int factor ) } new_width = last_w_index; new_height = last_h_index; - } - else - { + } else { unsigned char *pd = scale_buffer; unsigned int wc = width*colours; unsigned int xstart = factor/2; @@ -2840,31 +2655,24 @@ void Image::Scale( unsigned int factor ) 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++ ) - { + 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 ) - { + 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++ ) - { + 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++ ) - { + if ( w_index > last_w_index ) { + for ( unsigned int c = 0; c < colours; c++ ) { *pd++ = *ps++; } - } - else - { + } else { ps += colours; } last_w_index = w_index; @@ -2880,29 +2688,23 @@ void Image::Scale( unsigned int factor ) } -void Image::Deinterlace_Discard() -{ +void Image::Deinterlace_Discard() { /* Simple deinterlacing. Copy the even lines into the odd lines */ - if ( colours == ZM_COLOUR_GRAY8 ) - { + if ( colours == ZM_COLOUR_GRAY8 ) { const uint8_t *psrc; uint8_t *pdest; - for (unsigned int y = 0; y < (unsigned int)height; y += 2) - { + 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 ) - { + } else if ( colours == ZM_COLOUR_RGB24 ) { const uint8_t *psrc; uint8_t *pdest; - for (unsigned int y = 0; y < (unsigned int)height; y += 2) - { + 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++) { @@ -2911,13 +2713,10 @@ void Image::Deinterlace_Discard() *pdest++ = *psrc++; } } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { + } else if ( colours == ZM_COLOUR_RGB32 ) { const Rgb *psrc; Rgb *pdest; - for (unsigned int y = 0; y < (unsigned int)height; y += 2) - { + 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++) { @@ -2930,17 +2729,14 @@ void Image::Deinterlace_Discard() } -void Image::Deinterlace_Linear() -{ +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) - { + 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); @@ -2954,11 +2750,8 @@ void Image::Deinterlace_Linear() 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) - { + } 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); @@ -2976,11 +2769,8 @@ void Image::Deinterlace_Linear() *pcurrent++ = *pabove++; *pcurrent++ = *pabove++; } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - for (unsigned int y = 1; y < (unsigned int)(height-1); y += 2) - { + } 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); @@ -3006,16 +2796,13 @@ void Image::Deinterlace_Linear() } -void Image::Deinterlace_Blend() -{ +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) - { + 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++) { @@ -3023,11 +2810,8 @@ void Image::Deinterlace_Blend() *pcurrent++ = *pabove++; } } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { + } 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++) { @@ -3039,11 +2823,8 @@ void Image::Deinterlace_Blend() *pcurrent++ = *pabove++; } } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { + } 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++) { @@ -3063,8 +2844,7 @@ void Image::Deinterlace_Blend() } -void Image::Deinterlace_Blend_CustomRatio(int divider) -{ +void Image::Deinterlace_Blend_CustomRatio(int divider) { /* Simple deinterlacing. Blend the fields together at a custom ratio. */ /* 1 = 50% blending */ /* 2 = 25% blending */ @@ -3078,10 +2858,8 @@ void Image::Deinterlace_Blend_CustomRatio(int divider) Error("Deinterlace called with invalid blend ratio"); } - if ( colours == ZM_COLOUR_GRAY8 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { + 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++) { @@ -3091,11 +2869,8 @@ void Image::Deinterlace_Blend_CustomRatio(int divider) *pabove++ = subpix2; } } - } - else if ( colours == ZM_COLOUR_RGB24 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { + } 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++) { @@ -3113,11 +2888,8 @@ void Image::Deinterlace_Blend_CustomRatio(int divider) *pabove++ = subpix2; } } - } - else if ( colours == ZM_COLOUR_RGB32 ) - { - for (unsigned int y = 1; y < (unsigned int)height; y += 2) - { + } 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++) { @@ -3205,25 +2977,25 @@ void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, u static uint32_t clearmask = 0; static double current_blendpercent = 0.0; - if(current_blendpercent != blendpercent) { + if ( current_blendpercent != blendpercent ) { /* Attempt to match the blending percent to one of the possible values */ - if(blendpercent < 2.34375) { + if ( blendpercent < 2.34375 ) { // 1.5625% blending divider = 6; clearmask = 0x03030303; - } else if(blendpercent < 4.6875) { + } else if ( blendpercent < 4.6875 ) { // 3.125% blending divider = 5; clearmask = 0x07070707; - } else if(blendpercent < 9.375) { + } else if ( blendpercent < 9.375 ) { // 6.25% blending divider = 4; clearmask = 0x0F0F0F0F; - } else if(blendpercent < 18.75) { + } else if ( blendpercent < 18.75 ) { // 12.5% blending divider = 3; clearmask = 0x1F1F1F1F; - } else if(blendpercent < 37.5) { + } else if ( blendpercent < 37.5 ) { // 25% blending divider = 2; clearmask = 0x3F3F3F3F; @@ -3269,21 +3041,21 @@ __attribute__((noinline)) void std_fastblend(const uint8_t* col1, const uint8_t* static double current_blendpercent = 0.0; const uint8_t* const max_ptr = result + count; - if(current_blendpercent != blendpercent) { + if ( current_blendpercent != blendpercent ) { /* Attempt to match the blending percent to one of the possible values */ - if(blendpercent < 2.34375) { + if ( blendpercent < 2.34375 ) { // 1.5625% blending divider = 6; - } else if(blendpercent < 4.6875) { + } else if ( blendpercent < 4.6875 ) { // 3.125% blending divider = 5; - } else if(blendpercent < 9.375) { + } else if ( blendpercent < 9.375 ) { // 6.25% blending divider = 4; - } else if(blendpercent < 18.75) { + } else if ( blendpercent < 18.75 ) { // 12.5% blending divider = 3; - } else if(blendpercent < 37.5) { + } else if ( blendpercent < 37.5 ) { // 25% blending divider = 2; } else { @@ -3293,8 +3065,7 @@ __attribute__((noinline)) void std_fastblend(const uint8_t* col1, const uint8_t* current_blendpercent = blendpercent; } - - while(result < max_ptr) { + 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]; @@ -3319,11 +3090,11 @@ __attribute__((noinline)) void std_fastblend(const uint8_t* col1, const uint8_t* } /* FastBlend Neon for AArch32 */ -#if (defined(__arm__) && !defined(ZM_STRIP_NEON)) +#if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON)) __attribute__((noinline,__target__("fpu=neon"))) #endif void neon32_armv7_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent) { -#if (defined(__arm__) && !defined(ZM_STRIP_NEON)) +#if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON)) static int8_t divider = 0; static double current_blendpercent = 0.0; @@ -3492,9 +3263,8 @@ __attribute__((noinline)) void std_blend(const uint8_t* col1, const uint8_t* col double opacity = 1.0 - divide; const uint8_t* const max_ptr = result + count; - while(result < max_ptr) { + while ( result < max_ptr ) { *result++ = (*col1++ * opacity) + (*col2++ * divide); - } } @@ -3710,11 +3480,11 @@ __attribute__((noinline)) void std_delta8_abgr(const uint8_t* col1, const uint8_ } /* Grayscale Neon for AArch32 */ -#if (defined(__arm__) && !defined(ZM_STRIP_NEON)) +#if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON)) __attribute__((noinline,__target__("fpu=neon"))) #endif void neon32_armv7_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count) { -#if (defined(__arm__) && !defined(ZM_STRIP_NEON)) +#if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON)) /* Q0(D0,D1) = col1+0 */ /* Q1(D2,D3) = col1+16 */ @@ -3786,11 +3556,11 @@ __attribute__((noinline)) void neon64_armv8_delta8_gray8(const uint8_t* col1, co } /* RGB32 Neon for AArch32 */ -#if (defined(__arm__) && !defined(ZM_STRIP_NEON)) +#if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON)) __attribute__((noinline,__target__("fpu=neon"))) #endif void neon32_armv7_delta8_rgb32(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, uint32_t multiplier) { -#if (defined(__arm__) && !defined(ZM_STRIP_NEON)) +#if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON)) /* Q0(D0,D1) = col1+0 */ /* Q1(D2,D3) = col1+16 */ @@ -4694,8 +4464,7 @@ __attribute__((noinline)) void zm_convert_rgb565_rgba(const uint8_t* col1, uint8 /************************************************* DEINTERLACE FUNCTIONS *************************************************/ /* Grayscale */ -__attribute__((noinline)) void std_deinterlace_4field_gray8(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) -{ +__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; @@ -4705,8 +4474,7 @@ __attribute__((noinline)) void std_deinterlace_4field_gray8(uint8_t* col1, uint8 pabove = col1; pnabove = col2; pbelow = col1 + (width*2); - while(pcurrent < max_ptr) - { + while(pcurrent < max_ptr) { max_ptr2 = pcurrent + width; while(pcurrent < max_ptr2) { if((unsigned int)((abs(*pnabove - *pabove) + abs(*pncurrent - *pcurrent)) >> 1) >= threshold) { @@ -4723,7 +4491,6 @@ __attribute__((noinline)) void std_deinterlace_4field_gray8(uint8_t* col1, uint8 pabove += width; pnabove += width; pbelow += width; - } /* Special case for the last line */ @@ -4740,8 +4507,7 @@ __attribute__((noinline)) void std_deinterlace_4field_gray8(uint8_t* col1, uint8 } /* RGB */ -__attribute__((noinline)) void std_deinterlace_4field_rgb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) -{ +__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)); @@ -4754,8 +4520,7 @@ __attribute__((noinline)) void std_deinterlace_4field_rgb(uint8_t* col1, uint8_t pabove = col1; pnabove = col2; pbelow = col1 + ((width*2)*3); - while(pcurrent < max_ptr) - { + while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { r = abs(pnabove[0] - pabove[0]); @@ -4782,7 +4547,6 @@ __attribute__((noinline)) void std_deinterlace_4field_rgb(uint8_t* col1, uint8_t pabove += row_width; pnabove += row_width; pbelow += row_width; - } /* Special case for the last line */ @@ -4809,8 +4573,7 @@ __attribute__((noinline)) void std_deinterlace_4field_rgb(uint8_t* col1, uint8_t } /* BGR */ -__attribute__((noinline)) void std_deinterlace_4field_bgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) -{ +__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)); @@ -4823,8 +4586,7 @@ __attribute__((noinline)) void std_deinterlace_4field_bgr(uint8_t* col1, uint8_t pabove = col1; pnabove = col2; pbelow = col1 + ((width*2)*3); - while(pcurrent < max_ptr) - { + while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { b = abs(pnabove[0] - pabove[0]); @@ -4878,8 +4640,7 @@ __attribute__((noinline)) void std_deinterlace_4field_bgr(uint8_t* col1, uint8_t } /* RGBA */ -__attribute__((noinline)) void std_deinterlace_4field_rgba(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) -{ +__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)); @@ -4892,8 +4653,7 @@ __attribute__((noinline)) void std_deinterlace_4field_rgba(uint8_t* col1, uint8_ pabove = col1; pnabove = col2; pbelow = col1 + (row_width*2); - while(pcurrent < max_ptr) - { + while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { r = abs(pnabove[0] - pabove[0]); @@ -4920,7 +4680,6 @@ __attribute__((noinline)) void std_deinterlace_4field_rgba(uint8_t* col1, uint8_ pabove += row_width; pnabove += row_width; pbelow += row_width; - } /* Special case for the last line */ @@ -4947,8 +4706,7 @@ __attribute__((noinline)) void std_deinterlace_4field_rgba(uint8_t* col1, uint8_ } /* BGRA */ -__attribute__((noinline)) void std_deinterlace_4field_bgra(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) -{ +__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)); @@ -4961,8 +4719,7 @@ __attribute__((noinline)) void std_deinterlace_4field_bgra(uint8_t* col1, uint8_ pabove = col1; pnabove = col2; pbelow = col1 + (row_width*2); - while(pcurrent < max_ptr) - { + while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { b = abs(pnabove[0] - pabove[0]); @@ -4989,7 +4746,6 @@ __attribute__((noinline)) void std_deinterlace_4field_bgra(uint8_t* col1, uint8_ pabove += row_width; pnabove += row_width; pbelow += row_width; - } /* Special case for the last line */ @@ -5016,8 +4772,7 @@ __attribute__((noinline)) void std_deinterlace_4field_bgra(uint8_t* col1, uint8_ } /* ARGB */ -__attribute__((noinline)) void std_deinterlace_4field_argb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) -{ +__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)); @@ -5030,8 +4785,7 @@ __attribute__((noinline)) void std_deinterlace_4field_argb(uint8_t* col1, uint8_ pabove = col1; pnabove = col2; pbelow = col1 + (row_width*2); - while(pcurrent < max_ptr) - { + while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { r = abs(pnabove[1] - pabove[1]); @@ -5058,7 +4812,6 @@ __attribute__((noinline)) void std_deinterlace_4field_argb(uint8_t* col1, uint8_ pabove += row_width; pnabove += row_width; pbelow += row_width; - } /* Special case for the last line */ @@ -5085,8 +4838,7 @@ __attribute__((noinline)) void std_deinterlace_4field_argb(uint8_t* col1, uint8_ } /* ABGR */ -__attribute__((noinline)) void std_deinterlace_4field_abgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height) -{ +__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)); @@ -5099,8 +4851,7 @@ __attribute__((noinline)) void std_deinterlace_4field_abgr(uint8_t* col1, uint8_ pabove = col1; pnabove = col2; pbelow = col1 + (row_width*2); - while(pcurrent < max_ptr) - { + while(pcurrent < max_ptr) { max_ptr2 = pcurrent + row_width; while(pcurrent < max_ptr2) { b = abs(pnabove[1] - pabove[1]); diff --git a/src/zm_image.h b/src/zm_image.h index 104ccdcdc..66e7ef32e 100644 --- a/src/zm_image.h +++ b/src/zm_image.h @@ -160,16 +160,16 @@ public: static void Initialise(); static void Deinitialise(); - inline unsigned int Width() const { return( width ); } - inline unsigned int Height() const { return( height ); } - inline unsigned int Pixels() const { return( pixels ); } - inline unsigned int Colours() const { return( colours ); } - inline unsigned int SubpixelOrder() const { return( subpixelorder ); } - inline unsigned int Size() const { return( size ); } + inline unsigned int Width() const { return width; } + inline unsigned int Height() const { return height; } + inline unsigned int Pixels() const { return pixels; } + inline unsigned int Colours() const { return colours; } + inline unsigned int SubpixelOrder() const { return subpixelorder; } + inline unsigned int Size() const { return size; } /* Internal buffer should not be modified from functions outside of this class */ - inline const uint8_t* Buffer() const { return( buffer ); } - inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return( &buffer[colours*((y*width)+x)] ); } + inline const uint8_t* Buffer() const { return buffer; } + inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return &buffer[colours*((y*width)+x)]; } /* Request writeable buffer */ uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder); @@ -196,7 +196,7 @@ public: } inline Image &operator=( const unsigned char *new_buffer ) { (*fptr_imgbufcpy)(buffer, new_buffer, size); - return( *this ); + return *this; } bool ReadRaw( const char *filename ); @@ -274,6 +274,7 @@ void std_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, void std_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void std_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void std_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); + void neon32_armv7_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void neon32_armv7_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); void neon32_armv7_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count); diff --git a/src/zm_local_camera.cpp b/src/zm_local_camera.cpp index aeba75ae3..635861d4c 100644 --- a/src/zm_local_camera.cpp +++ b/src/zm_local_camera.cpp @@ -41,26 +41,21 @@ static unsigned int BigEndian; -static int vidioctl( int fd, int request, void *arg ) -{ +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 ); + do { + result = ioctl(fd, request, arg); + } while( result == -1 && errno == EINTR ); + return result; } #if HAVE_LIBSWSCALE -static _AVPIXELFORMAT getFfPixFormatFromV4lPalette( int v4l_version, int palette ) -{ +static _AVPIXELFORMAT getFfPixFormatFromV4lPalette(int v4l_version, int palette) { _AVPIXELFORMAT pixFormat = AV_PIX_FMT_NONE; #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - switch( palette ) - { + if ( v4l_version == 2 ) { + switch( palette ) { #if defined(V4L2_PIX_FMT_RGB444) && defined(AV_PIX_FMT_RGB444) case V4L2_PIX_FMT_RGB444 : pixFormat = AV_PIX_FMT_RGB444; @@ -151,7 +146,7 @@ static _AVPIXELFORMAT getFfPixFormatFromV4lPalette( int v4l_version, int palette //case V4L2_PIX_FMT_YVYU : default : { - Fatal( "Can't find swscale format for palette %d", palette ); + 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; @@ -179,22 +174,20 @@ static _AVPIXELFORMAT getFfPixFormatFromV4lPalette( int v4l_version, int palette //pixFormat = AV_PIX_FMT_VDPAU_MPEG1; //pixFormat = AV_PIX_FMT_VDPAU_MPEG2; } - } - } + } // end switch palette + } // end if v4l2 #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - switch( palette ) - { + if ( v4l_version == 1 ) { + switch( palette ) { case VIDEO_PALETTE_RGB32 : - if(BigEndian) + if ( BigEndian ) pixFormat = AV_PIX_FMT_ARGB; else pixFormat = AV_PIX_FMT_BGRA; break; case VIDEO_PALETTE_RGB24 : - if(BigEndian) + if ( BigEndian ) pixFormat = AV_PIX_FMT_RGB24; else pixFormat = AV_PIX_FMT_BGR24; @@ -220,7 +213,7 @@ static _AVPIXELFORMAT getFfPixFormatFromV4lPalette( int v4l_version, int palette break; default : { - Fatal( "Can't find swscale format for palette %d", palette ); + 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; @@ -249,19 +242,47 @@ static _AVPIXELFORMAT getFfPixFormatFromV4lPalette( int v4l_version, int palette //pixFormat = AV_PIX_FMT_VDPAU_MPEG1; //pixFormat = AV_PIX_FMT_VDPAU_MPEG2; } - } - } + } // end switch palette + } // end if v4l1 #endif // ZM_HAS_V4L1 - return( pixFormat ); -} + return pixFormat; +} // end getFfPixFormatFromV4lPalette #endif // HAVE_LIBSWSCALE #if ZM_HAS_V4L2 static char palette_desc[32]; /* 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}; +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 +}; #endif @@ -320,41 +341,35 @@ LocalCamera::LocalCamera( v4l_multi_buffer = p_v4l_multi_buffer; v4l_captures_per_frame = p_v4l_captures_per_frame; - if ( capture ) - { - if ( device_prime ) - { + if ( capture ) { + if ( device_prime ) { Debug( 2, "V4L support enabled, using V4L%d api", v4l_version ); } - if ( !last_camera || channel != last_camera->channel ) - { + 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 - { + } 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) { + if ( *(unsigned char*)&checkval == 0xDD ) { BigEndian = 0; Debug(2,"little-endian processor detected"); - } else if(*(unsigned char*)&checkval == 0xAA) { + } 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 ) { @@ -366,13 +381,14 @@ LocalCamera::LocalCamera( palette = V4L2_PIX_FMT_YUYV; } else { if ( capture ) { - Info("Selected capture palette: %s (0x%02hhx%02hhx%02hhx%02hhx)", palette_desc, (palette>>24)&0xff, (palette>>16)&0xff, (palette>>8)&0xff, (palette)&0xff); + Info("Selected capture palette: %s (0x%02hhx%02hhx%02hhx%02hhx)", + palette_desc, (palette>>24)&0xff, (palette>>16)&0xff, (palette>>8)&0xff, (palette)&0xff); } } } #endif - if( capture ) { + 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" ); @@ -425,13 +441,14 @@ LocalCamera::LocalCamera( 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 ( capture ) { #if HAVE_LIBSWSCALE Info("No direct match for the selected palette (0x%02hhx%02hhx%02hhx%02hhx) and target colorspace (%02u). Format conversion is required, performance penalty expected", (capturePixFormat>>24)&0xff,((capturePixFormat>>16)&0xff),((capturePixFormat>>8)&0xff),((capturePixFormat)&0xff), colours); #else - Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); + Info("No direct match for the selected palette and target colorspace. Format conversion is required, performance penalty expected"); #endif + } #if HAVE_LIBSWSCALE /* Try using swscale for the conversion */ conversion_type = 1; @@ -467,27 +484,27 @@ LocalCamera::LocalCamera( 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) { + 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) { + if ( palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG ) { Debug(2,"Using JPEG image decoding"); conversion_type = 3; } - if(conversion_type == 2) { + if ( conversion_type == 2 ) { Debug(2,"Using ZM for image conversion"); - if(palette == V4L2_PIX_FMT_RGB32 && colours == ZM_COLOUR_GRAY8) { + 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) { + } 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) { + } 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) { + if ( config.cpu_extensions && sseversion >= 35 ) { conversion_fptr = &ssse3_convert_yuyv_gray8; Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); } else { @@ -495,85 +512,85 @@ LocalCamera::LocalCamera( Debug(2,"Using standard YUYV->grayscale fast conversion"); } subpixelorder = ZM_SUBPIX_ORDER_NONE; - } else if(palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_RGB24) { + } 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) { + } 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) { + } 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) { + } 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) { + } 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) { + } 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."); } - } - } - } + } // end if conversion_type == 2 + } // end if needs conversion + } // end if v4l2 #endif // ZM_HAS_V4L2 /* V4L1 format matching */ #if ZM_HAS_V4L1 - if ( v4l_version == 1) { + 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) { + if ( palette == VIDEO_PALETTE_RGB32 && colours == ZM_COLOUR_RGB32 ) { conversion_type = 0; - if(BigEndian) { + 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) { + } else if ( palette == VIDEO_PALETTE_RGB24 && colours == ZM_COLOUR_RGB24 ) { conversion_type = 0; - if(BigEndian) { + 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) { + } 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 ) + 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) { + if ( colours == ZM_COLOUR_RGB32 ) { subpixelorder = ZM_SUBPIX_ORDER_RGBA; imagePixFormat = AV_PIX_FMT_RGBA; - } else if(colours == ZM_COLOUR_RGB24) { + } else if ( colours == ZM_COLOUR_RGB24 ) { subpixelorder = ZM_SUBPIX_ORDER_RGB; imagePixFormat = AV_PIX_FMT_RGB24; - } else if(colours == ZM_COLOUR_GRAY8) { + } else if ( colours == ZM_COLOUR_GRAY8 ) { subpixelorder = ZM_SUBPIX_ORDER_NONE; imagePixFormat = AV_PIX_FMT_GRAY8; } else { - Panic("Unexpected colours: %u",colours); + Panic("Unexpected colours: %u", colours); } - if( capture ) { - if(!sws_isSupportedInput(capturePixFormat)) { + 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)) { + if ( !sws_isSupportedOutput(imagePixFormat) ) { Error("swscale does not support the target format"); conversion_type = 2; /* Try ZM format conversions */ } @@ -583,23 +600,23 @@ LocalCamera::LocalCamera( 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)) { + if ( colours == ZM_COLOUR_GRAY8 && (palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) ) { conversion_type = 2; } - if(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) { + 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) { + } 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) { + if ( config.cpu_extensions && sseversion >= 35 ) { conversion_fptr = &ssse3_convert_yuyv_gray8; Debug(2,"Using SSSE3 YUYV->grayscale fast conversion"); } else { @@ -607,22 +624,22 @@ LocalCamera::LocalCamera( 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) { + } 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) { + } 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) { + } 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) { + } 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) { + } 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) { + } else if ( palette == VIDEO_PALETTE_RGB565 && colours == ZM_COLOUR_RGB32 ) { conversion_fptr = &zm_convert_rgb565_rgba; subpixelorder = ZM_SUBPIX_ORDER_RGBA; } else { @@ -638,35 +655,35 @@ LocalCamera::LocalCamera( #if HAVE_LIBSWSCALE /* Initialize swscale stuff */ - if(capture && conversion_type == 1) { + 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" ); + 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 ); + unsigned int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1); #else - int pSize = avpicture_get_size( imagePixFormat, width, height ); + unsigned int pSize = avpicture_get_size(imagePixFormat, width, height); #endif - if( (unsigned int)pSize != imagesize) { - Fatal("Image size mismatch. Required: %d Available: %u",pSize,imagesize); + if ( pSize != imagesize ) { + Fatal("Image size mismatch. Required: %d Available: %u", pSize, imagesize); } - imgConversionContext = sws_getContext(width, height, capturePixFormat, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL ); + imgConversionContext = sws_getContext(width, height, capturePixFormat, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL); if ( !imgConversionContext ) { - Fatal( "Unable to initialise image scaling context" ); + Fatal("Unable to initialise image scaling context"); } } else { tmpPicture = NULL; imgConversionContext = NULL; } #endif -} +} // end LocalCamera::LocalCamera LocalCamera::~LocalCamera() { if ( device_prime && capture ) @@ -683,116 +700,133 @@ LocalCamera::~LocalCamera() { #endif } -void LocalCamera::Initialise() -{ +void LocalCamera::Initialise() { #if HAVE_LIBSWSCALE if ( logDebugging() ) - av_log_set_level( AV_LOG_DEBUG ); + av_log_set_level(AV_LOG_DEBUG); else - av_log_set_level( AV_LOG_QUIET ); + av_log_set_level(AV_LOG_QUIET); #endif // HAVE_LIBSWSCALE - - Debug( 3, "Opening video device %s", device.c_str() ); + 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 ( (vid_fd = open(device.c_str(), O_RDWR, 0)) < 0 ) + Fatal("Failed to open video device %s: %s", device.c_str(), strerror(errno)); 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) ); + Fatal("File %s is not device file: %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) ); + 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" ); + Fatal("Video device is not video capture device"); if ( !(vid_cap.capabilities & V4L2_CAP_STREAMING) ) - Fatal( "Video device does not support streaming i/o" ); + Fatal("Video device does not support streaming i/o"); - Debug( 3, "Setting up video format" ); + Debug(3, "Setting up video format"); - memset( &v4l2_data.fmt, 0, sizeof(v4l2_data.fmt) ); + 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) ); + 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 ); + Debug(4, + " v4l2_data.fmt.type = %08x\n" + " v4l2_data.fmt.fmt.pix.width = %08x\n" + " v4l2_data.fmt.fmt.pix.height = %08x\n" + " v4l2_data.fmt.fmt.pix.pixelformat = %08x\n" + " v4l2_data.fmt.fmt.pix.field = %08x\n" + " v4l2_data.fmt.fmt.pix.bytesperline = %08x\n" + " v4l2_data.fmt.fmt.pix.sizeimage = %08x\n" + " v4l2_data.fmt.fmt.pix.colorspace = %08x\n" + " v4l2_data.fmt.fmt.pix.priv = %08x\n" + , v4l2_data.fmt.type + , v4l2_data.fmt.fmt.pix.width + , v4l2_data.fmt.fmt.pix.height + , v4l2_data.fmt.fmt.pix.pixelformat + , v4l2_data.fmt.fmt.pix.field + , v4l2_data.fmt.fmt.pix.bytesperline + , v4l2_data.fmt.fmt.pix.sizeimage + , v4l2_data.fmt.fmt.pix.colorspace + , 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 ) - { + 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) ); + 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) ); + 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) ); + 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 ); + Debug(4, + " v4l2_data.fmt.type = %08x\n" + " v4l2_data.fmt.fmt.pix.width = %08x\n" + " v4l2_data.fmt.fmt.pix.height = %08x\n" + " v4l2_data.fmt.fmt.pix.pixelformat = %08x\n" + " v4l2_data.fmt.fmt.pix.field = %08x\n" + " v4l2_data.fmt.fmt.pix.bytesperline = %08x\n" + " v4l2_data.fmt.fmt.pix.sizeimage = %08x\n" + " v4l2_data.fmt.fmt.pix.colorspace = %08x\n" + " v4l2_data.fmt.fmt.pix.priv = %08x\n" + , v4l2_data.fmt.type + , v4l2_data.fmt.fmt.pix.width + , v4l2_data.fmt.fmt.pix.height + , v4l2_data.fmt.fmt.pix.pixelformat + , v4l2_data.fmt.fmt.pix.field + , v4l2_data.fmt.fmt.pix.bytesperline + , v4l2_data.fmt.fmt.pix.sizeimage + , v4l2_data.fmt.fmt.pix.colorspace + , 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) + 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) + 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) { + if ( palette == V4L2_PIX_FMT_JPEG || palette == V4L2_PIX_FMT_MJPEG ) { + v4l2_jpegcompression jpeg_comp; + 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) ); + 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) { + 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; @@ -800,24 +834,24 @@ void LocalCamera::Initialise() 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) ); + 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) ); + 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); } } } - } + } // end if JPEG/MJPEG - Debug( 3, "Setting up request buffers" ); + Debug(3, "Setting up request buffers"); - memset( &v4l2_data.reqbufs, 0, sizeof(v4l2_data.reqbufs) ); + memset(&v4l2_data.reqbufs, 0, sizeof(v4l2_data.reqbufs)); if ( channel_count > 1 ) { - Debug( 3, "Channel count is %d", channel_count ); + Debug(3, "Channel count is %d", channel_count); if ( v4l_multi_buffer ){ v4l2_data.reqbufs.count = 2*channel_count; } else { @@ -826,37 +860,33 @@ void LocalCamera::Initialise() } else { v4l2_data.reqbufs.count = 8; } - Debug( 3, "Request buffers count is %d", v4l2_data.reqbufs.count ); + 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 ( 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 ); + 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 ); + 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++ ) - { + for ( unsigned int i = 0; i < v4l2_data.reqbufs.count; i++ ) { struct v4l2_buffer vid_buf; - memset( &vid_buf, 0, sizeof(vid_buf) ); + memset(&vid_buf, 0, sizeof(vid_buf)); //vid_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vid_buf.type = v4l2_data.fmt.type; @@ -864,14 +894,15 @@ void LocalCamera::Initialise() 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) ); + 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 ); + 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 %u (%u bytes) to memory: %s(%d)", i, vid_buf.length, strerror(errno), errno ); + Fatal("Can't map video buffer %u (%u bytes) to memory: %s(%d)", + i, vid_buf.length, strerror(errno), errno); #if HAVE_LIBSWSCALE #if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101) @@ -880,48 +911,49 @@ void LocalCamera::Initialise() capturePictures[i] = avcodec_alloc_frame(); #endif if ( !capturePictures[i] ) - Fatal( "Could not allocate picture" ); + Fatal("Could not allocate picture"); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - av_image_fill_arrays(capturePictures[i]->data, + av_image_fill_arrays( + capturePictures[i]->data, capturePictures[i]->linesize, - (uint8_t*)v4l2_data.buffers[i].start,capturePixFormat, + (uint8_t*)v4l2_data.buffers[i].start, + capturePixFormat, v4l2_data.fmt.fmt.pix.width, - v4l2_data.fmt.fmt.pix.height, 1); + v4l2_data.fmt.fmt.pix.height, + 1); #else - avpicture_fill( (AVPicture *)capturePictures[i], + 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 ); + v4l2_data.fmt.fmt.pix.height + ); #endif #endif // HAVE_LIBSWSCALE - } + } // end foreach request buf - Debug( 3, "Configuring video source" ); + 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) ); + 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) ); + memset(&input, 0, sizeof(input)); - if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) - { - Fatal( "Failed to enumerate input %d: %s", channel, strerror(errno) ); + 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 ); + 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) ); + 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); @@ -931,49 +963,42 @@ void LocalCamera::Initialise() } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - Debug( 3, "Configuring picture attributes" ); + 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) ); + 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 ); + Debug(4, + "Old Palette:%d, depth:%d, brightness:%d, hue:%d, colour:%d, contrast:%d", + vid_pic.palette, + vid_pic.depth, + vid_pic.brightness, + vid_pic.hue, + vid_pic.colour, + vid_pic.contrast + ); - switch (vid_pic.palette = palette) - { + 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; - } } if ( brightness >= 0 ) vid_pic.brightness = brightness; @@ -981,26 +1006,21 @@ void LocalCamera::Initialise() if ( colour >= 0 ) vid_pic.colour = colour; if ( contrast >= 0 ) vid_pic.contrast = contrast; - if ( ioctl( vid_fd, VIDIOCSPICT, &vid_pic ) < 0 ) - { - Error( "Failed to set picture attributes: %s", strerror(errno) ); + if ( ioctl(vid_fd, VIDIOCSPICT, &vid_pic) < 0 ) { + Error("Failed to set picture attributes: %s", strerror(errno)); if ( config.strict_video_config ) exit(-1); } - Debug( 3, "Configuring window attributes" ); + Debug(3, "Configuring window attributes"); 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); + memset(&vid_win, 0, sizeof(vid_win)); + if ( ioctl(vid_fd, VIDIOCGWIN, &vid_win) < 0 ) { + Fatal("Failed to get window attributes: %s", strerror(errno)); } - 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 ); + Debug(4, "Old X:%d Y:%d W:%d H:%d", + vid_win.x, vid_win.y, vid_win.width, vid_win.height); vid_win.x = 0; vid_win.y = 0; @@ -1008,36 +1028,33 @@ void LocalCamera::Initialise() vid_win.height = height; vid_win.flags &= ~VIDEO_WINDOW_INTERLACE; - if ( ioctl( vid_fd, VIDIOCSWIN, &vid_win ) < 0 ) - { - Error( "Failed to set window attributes: %s", strerror(errno) ); + if ( ioctl(vid_fd, VIDIOCSWIN, &vid_win) < 0 ) { + Error("Failed to set window attributes: %s", strerror(errno)); if ( config.strict_video_config ) exit(-1); } - Info( "vid_win.width = %08x", vid_win.width ); - Info( "vid_win.height = %08x", vid_win.height ); - Info( "vid_win.flags = %08x", vid_win.flags ); + Info("vid_win.width = %08x, vid_win.height = %08x, vid_win.flags = %08x", + vid_win.width, vid_win.height, 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) ); + 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(4, "vmb.frames = %d, vmb.size = %d", + v4l1_data.frames.frames, v4l1_data.frames.size); - Debug( 3, "Setting up %d frame buffers", v4l1_data.frames.frames ); + 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 ); + 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) ); + 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++ ) - { + 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; @@ -1049,67 +1066,66 @@ void LocalCamera::Initialise() capturePictures[i] = avcodec_alloc_frame(); #endif if ( !capturePictures[i] ) - Fatal( "Could not allocate picture" ); + Fatal("Could not allocate picture"); #if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0) - av_image_fill_arrays(capturePictures[i]->data, + 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], + 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) ); + 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) ); + 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, F:%d, Fl:%x, T:%d", + vid_src.channel, vid_src.norm, vid_src.flags, 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 ( 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) ); + 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 ); + Info("vid_win.width = %08x, vid_win.height = %08x, vid_win.flags = %08x", + vid_win.width, vid_win.height, 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 ); + Debug(4, "New X:%d Y:%d W:%d H:%d", + vid_win.x, vid_win.y, vid_win.width, vid_win.height); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) - Fatal( "Failed to get window data: %s", strerror(errno) ); + 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 ); - } + Debug(4, + "New Palette:%d, depth:%d, brightness:%d, hue:%d, colour:%d, contrast:%d", + vid_pic.palette, + vid_pic.depth, + vid_pic.brightness, + vid_pic.hue, + vid_pic.colour, + vid_pic.contrast + ); + } // end if v4l #endif // ZM_HAS_V4L1 -} +} // end LocalCamera::Initialize void LocalCamera::Terminate() { #if ZM_HAS_V4L2 @@ -1134,28 +1150,28 @@ void LocalCamera::Terminate() { 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 */ + 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]); + av_frame_free(&capturePictures[i]); #else - av_freep(&capturePictures[i]); + av_freep(&capturePictures[i]); #endif - } -#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); @@ -1173,8 +1189,9 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { 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) ); + 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; } @@ -1183,8 +1200,8 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { 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) { - if (nIndex >= 64 ) { + while ( vidioctl(enum_fd, VIDIOC_ENUM_FMT, &fmtinfo) >= 0 ) { + if ( nIndex >= 64 ) { Error("More than 64 formats detected, can't handle that."); break; } @@ -1193,7 +1210,12 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { fmt_fcc[nIndex] = fmtinfo.pixelformat; Debug(3, "Got format: %s (0x%02hhx%02hhx%02hhx%02hhx) at index %d", - fmt_desc[nIndex], (fmt_fcc[nIndex]>>24)&0xff, (fmt_fcc[nIndex]>>16)&0xff, (fmt_fcc[nIndex]>>8)&0xff, (fmt_fcc[nIndex])&0xff ,nIndex); + fmt_desc[nIndex], + (fmt_fcc[nIndex]>>24)&0xff, + (fmt_fcc[nIndex]>>16)&0xff, + (fmt_fcc[nIndex]>>8)&0xff, + (fmt_fcc[nIndex])&0xff, + nIndex); /* Proceed to the next index */ memset(&fmtinfo, 0, sizeof(fmtinfo)); @@ -1205,11 +1227,11 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { int nIndexUsed = -1; unsigned int n_preferedformats = 0; const uint32_t* preferedformats; - if(p_colours == ZM_COLOUR_RGB32) { + 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) { + } else if ( p_colours == ZM_COLOUR_GRAY8 ) { /* Grayscale */ preferedformats = prefered_gray8_formats; n_preferedformats = sizeof(prefered_gray8_formats) / sizeof(uint32_t); @@ -1218,9 +1240,9 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { 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] ) { + 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 (0x%02hhx%02hhx%02hhx%02hhx) at index %u", 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! */ @@ -1234,7 +1256,7 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { } /* Have we found a match? */ - if(nIndexUsed >= 0) { + if ( nIndexUsed >= 0 ) { /* Found a match */ selected_palette = fmt_fcc[nIndexUsed]; strcpy(palette_desc,fmt_desc[nIndexUsed]); @@ -1251,281 +1273,257 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) { #define capString(test,prefix,yesString,noString,capability) \ (test) ? (prefix yesString " " capability "\n") : (prefix noString " " capability "\n") -bool LocalCamera::GetCurrentSettings( const char *device, char *output, int version, bool verbose ) -{ +bool LocalCamera::GetCurrentSettings( const char *device, char *output, int version, bool verbose ) { output[0] = 0; char queryDevice[PATH_MAX] = ""; int devIndex = 0; - do - { + do { if ( device ) - strncpy( queryDevice, device, sizeof(queryDevice)-1 ); + strncpy(queryDevice, device, sizeof(queryDevice)-1); 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) ); + 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) ); + 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 ); + sprintf(output+strlen(output), "error%d\n", errno); + return false; + } else { + return true; } } if ( verbose ) - sprintf( output+strlen(output), "Video Device: %s\n", queryDevice ); + sprintf(output+strlen(output), "Video Device: %s\n", queryDevice); else - sprintf( output+strlen(output), "d:%s|", queryDevice ); + sprintf(output+strlen(output), "d:%s|", queryDevice); #if ZM_HAS_V4L2 - if ( version == 2 ) - { + 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 ( 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) ); + sprintf(output, "Error, failed to query video capabilities %s: %s\n", + queryDevice, strerror(errno)); else - sprintf( output, "error%d\n", errno ); - return( false ); + 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)" ) + 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 ); + } 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" ); + sprintf(output+strlen(output), " Standards:\n"); else - sprintf( output+strlen(output), "S:" ); + sprintf(output+strlen(output), "S:"); struct v4l2_standard standard; int standardIndex = 0; - do - { - memset( &standard, 0, sizeof(standard) ); + 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) ); + 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) ); + } else { + Error("Failed to enumerate standard %d: %d %s", standard.index, errno, strerror(errno)); if ( verbose ) - sprintf( output, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno) ); + sprintf(output, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno)); else - sprintf( output, "error%d\n", errno ); - return( false ); + sprintf(output, "error%d\n", errno); + return false; } } if ( verbose ) - sprintf( output+strlen(output), " %s\n", standard.name ); + sprintf(output+strlen(output), " %s\n", standard.name); else - sprintf( output+strlen(output), "%s/", standard.name ); + 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" ); + sprintf(output+strlen(output), " Formats:\n"); else - sprintf( output+strlen(output), "F:" ); + sprintf(output+strlen(output), "F:"); struct v4l2_fmtdesc format; int formatIndex = 0; - do - { - memset( &format, 0, sizeof(format) ); + 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 ) - { + 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) ); + } 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) ); + sprintf(output, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno)); else - sprintf( output, "error%d\n", errno ); - return( false ); + sprintf(output, "error%d\n", errno); + return false; } } if ( verbose ) - sprintf( output+strlen(output), " %s (0x%02hhx%02hhx%02hhx%02hhx)\n", - format.description, (format.pixelformat>>24)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>8)&0xff, format.pixelformat&0xff); + sprintf( + output+strlen(output), + " %s (0x%02hhx%02hhx%02hhx%02hhx)\n", + format.description, + (format.pixelformat>>24)&0xff, + (format.pixelformat>>16)&0xff, + (format.pixelformat>>8)&0xff, + format.pixelformat&0xff); else - sprintf( output+strlen(output), "0x%02hhx%02hhx%02hhx%02hhx/", - (format.pixelformat>>24)&0xff, (format.pixelformat>>16)&0xff, (format.pixelformat>>8)&0xff, (format.pixelformat)&0xff); - } - while ( formatIndex++ >= 0 ); + sprintf( + output+strlen(output), + "0x%02hhx%02hhx%02hhx%02hhx/", + (format.pixelformat>>24)&0xff, + (format.pixelformat>>16)&0xff, + (format.pixelformat>>8)&0xff, + (format.pixelformat)&0xff); + } while ( formatIndex++ >= 0 ); if ( !verbose ) output[strlen(output)-1] = '|'; - - if(verbose) - sprintf( output+strlen(output), "Crop Capabilities\n" ); + else + sprintf(output+strlen(output), "Crop Capabilities\n"); struct v4l2_cropcap cropcap; - memset( &cropcap, 0, sizeof(cropcap) ); + memset(&cropcap, 0, sizeof(cropcap)); cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if ( vidioctl( vid_fd, VIDIOC_CROPCAP, &cropcap ) < 0 ) - { - if(errno != EINVAL) { + 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) ); + Error("Failed to query crop capabilities: %s", strerror(errno)); } - if(verbose) { - sprintf( output+strlen(output), " Cropping is not supported\n"); + 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); + sprintf(output+strlen(output), "B:%dx%d|",0,0); } } else { struct v4l2_crop crop; - memset( &crop, 0, sizeof(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 ) - { + 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) ); + Error("Failed to query crop: %s", strerror(errno)); } if ( verbose ) { - sprintf( output+strlen(output), " Cropping is not supported\n"); + 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); + 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 ); + 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 ); + 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) ); + do { + memset(&input, 0, sizeof(input)); input.index = inputIndex; - if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) - { - if ( errno == EINVAL ) - { + if ( vidioctl(vid_fd, VIDIOC_ENUMINPUT, &input) < 0 ) { + if ( errno == EINVAL ) { break; - } + } + 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 - { - 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 ); - } + sprintf(output, "error%d\n", errno); + return false; } - } - while ( inputIndex++ >= 0 ); + } while ( inputIndex++ >= 0 ); if ( verbose ) - sprintf( output+strlen(output), "Inputs: %d\n", inputIndex ); + sprintf(output+strlen(output), "Inputs: %d\n", inputIndex); else - sprintf( output+strlen(output), "I:%d|", inputIndex ); + sprintf(output+strlen(output), "I:%d|", inputIndex); inputIndex = 0; - do - { - memset( &input, 0, sizeof(input) ); + do { + memset(&input, 0, sizeof(input)); input.index = inputIndex; - if ( vidioctl( vid_fd, VIDIOC_ENUMINPUT, &input ) < 0 ) - { - if ( errno == EINVAL ) - { + if ( vidioctl(vid_fd, VIDIOC_ENUMINPUT, &input) < 0 ) { + if ( errno == EINVAL ) { inputIndex = -1; break; } + 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 - { - 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 ); - } + 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 ( 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) ); + sprintf(output, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno)); else - sprintf( output, "error%d\n", errno ); - return( false ); + sprintf(output, "error%d\n", errno); + return false; } if ( verbose ) { @@ -1551,8 +1549,7 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers 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 ); + } while ( inputIndex++ >= 0 ); if ( !verbose ) output[strlen(output)-1] = '\n'; } @@ -1609,25 +1606,21 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers struct video_window vid_win; memset( &vid_win, 0, sizeof(video_window) ); - if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win ) < 0 ) - { + 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 ); + return false; } - if ( verbose ) - { + 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 - { + } 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 ); @@ -1636,17 +1629,15 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers struct video_picture vid_pic; memset( &vid_pic, 0, sizeof(video_picture) ); - if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic ) < 0 ) - { + 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 ); + return false; } - if ( verbose ) - { + 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":( @@ -1674,9 +1665,7 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers 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 - { + } 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 ); @@ -1686,22 +1675,19 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers sprintf( output+strlen(output), "w:%d|", vid_pic.whiteness ); } - for ( int chan = 0; chan < vid_cap.channels; chan++ ) - { + 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 ) - { + 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 ); + return false; } - if ( verbose ) - { + 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 ); @@ -1719,9 +1705,7 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers vid_src.norm==VIDEO_MODE_SECAM?"SECAM":( vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown" )))); - } - else - { + } 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 ); @@ -1736,301 +1720,267 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers close( vid_fd ); if ( device ) break; - } - while ( ++devIndex < 32 ); - return( true ); + } while ( ++devIndex < 32 ); + return true; } -int LocalCamera::Brightness( int p_brightness ) -{ +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) ); + 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" ) + 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 ) - { + } 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 ) + 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 ); + 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 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); + 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 ) - { + 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 ); + if ( ioctl(vid_fd, VIDIOCSPICT, &vid_pic) < 0 ) { + Error("Failed to set picture attributes: %s", strerror(errno)); + return -1; } } - return( vid_pic.brightness ); + return vid_pic.brightness; } #endif // ZM_HAS_V4L1 - return( -1 ); + return -1; } -int LocalCamera::Hue( int p_hue ) -{ +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 ) - { + if ( vidioctl(vid_fd, VIDIOC_G_CTRL, &vid_control) < 0 ) { if ( errno != EINVAL ) - Error( "Unable to query hue: %s", strerror(errno) ) + Error("Unable to query hue: %s", strerror(errno)) else - Warning( "Hue control is not supported" ) - } - else if ( p_hue >= 0 ) - { + 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 ) + 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 ); + 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 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); + 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 ) - { + 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 ); + if ( ioctl(vid_fd, VIDIOCSPICT, &vid_pic) < 0 ) { + Error("Failed to set picture attributes: %s", strerror(errno)); + return -1; } } - return( vid_pic.hue ); + return vid_pic.hue; } #endif // ZM_HAS_V4L1 - return( -1 ); + return -1; } -int LocalCamera::Colour( int p_colour ) -{ +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) ); + 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 ) - { + 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 ) + 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 ); + 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 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); + 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 ) - { + 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 ); + if ( ioctl(vid_fd, VIDIOCSPICT, &vid_pic) < 0 ) { + Error("Failed to set picture attributes: %s", strerror(errno)); + return -1; } } - return( vid_pic.colour ); + return vid_pic.colour; } #endif // ZM_HAS_V4L1 - return( -1 ); + return -1; } -int LocalCamera::Contrast( int p_contrast ) -{ +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) ); + 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 ) - { + 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 ) + 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 ); + 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 ) - { - Error( "Failed to get picture attributes: %s", strerror(errno) ); - return( -1 ); + 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 ) - { + 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 ); + if ( ioctl(vid_fd, VIDIOCSPICT, &vid_pic) < 0 ) { + Error("Failed to set picture attributes: %s", strerror(errno)); + return -1; } } - return( vid_pic.contrast ); + return vid_pic.contrast; } #endif // ZM_HAS_V4L1 - return( -1 ); + return -1; } int LocalCamera::PrimeCapture() { Initialise(); - Debug( 2, "Priming capture" ); + Debug(2, "Priming capture"); #if ZM_HAS_V4L2 if ( v4l_version == 2 ) { - Debug( 3, "Queueing buffers" ); + 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) ); + 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 ( 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" ); + 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) ); + 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 ); + 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 ); -} + return 0; +} // end LocalCamera::PrimeCapture int LocalCamera::PreCapture() { - Debug( 5, "Pre-capturing" ); - return( 0 ); + //Debug(5, "Pre-capturing"); + return 0; } -int LocalCamera::Capture( Image &image ) { - Debug( 3, "Capturing" ); +int LocalCamera::Capture(Image &image) { + Debug(3, "Capturing"); static uint8_t* buffer = NULL; int buffer_bytesused = 0; int capture_frame = -1; @@ -2040,7 +1990,7 @@ int LocalCamera::Capture( Image &image ) { 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 ); + 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 @@ -2049,64 +1999,69 @@ int LocalCamera::Capture( Image &image ) { if ( v4l_version == 2 ) { static struct v4l2_buffer vid_buf; - memset( &vid_buf, 0, sizeof(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 ); + 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 ); + 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; + bytes += vid_buf.bytesused; + 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 ); + if ( vidioctl(vid_fd, VIDIOC_QBUF, &vid_buf) < 0 ) { + Error("Unable to requeue buffer %d: %s", vid_buf.index, strerror(errno)); + return -1; } } } // while captures_per_frame - Debug( 3, "Captured frame %d/%d from channel %d", capture_frame, v4l2_data.bufptr->sequence, channel ); + 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; + bytes += buffer_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); + 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); } - } // end if v4l2 #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 if ( v4l_version == 1 ) { - Debug( 3, "Capturing %d frames", captures_per_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 ) { - Error( "Sync failure for frame %d buffer %d: %s", v4l1_data.active_frame, captures_per_frame, strerror(errno) ); - return( -1 ); + 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 ); + 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 ); + Debug(3, "Captured %d for channel %d", capture_frame, channel); buffer = v4l1_data.bufptr+v4l1_data.frames.offsets[capture_frame]; } @@ -2115,7 +2070,7 @@ int LocalCamera::Capture( Image &image ) { if ( conversion_type != 0 ) { - Debug( 3, "Performing format conversion" ); + Debug(3, "Performing format conversion"); /* Request a writeable buffer of the target image */ uint8_t* directbuffer = image.WriteBuffer(width, height, colours, subpixelorder); @@ -2126,7 +2081,7 @@ int LocalCamera::Capture( Image &image ) { #if HAVE_LIBSWSCALE if ( conversion_type == 1 ) { - Debug( 9, "Calling sws_scale to perform the conversion" ); + 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, @@ -2146,17 +2101,17 @@ int LocalCamera::Capture( Image &image ) { } #endif if ( conversion_type == 2 ) { - Debug( 9, "Calling the conversion function" ); + 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" ); + 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" ); + 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); @@ -2165,81 +2120,69 @@ int LocalCamera::Capture( Image &image ) { return 1; } // end int LocalCamera::Capture() -int LocalCamera::PostCapture() -{ - Debug( 4, "Post-capturing" ); +int LocalCamera::PostCapture() { + Debug(4, "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 ( channel_count > 1 || channel_prime ) { #if ZM_HAS_V4L2 - if ( v4l_version == 2 ) - { - if ( channel_count > 1 ) - { + 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 ) - { - Error( "Failed to set camera source %d: %s", channels[next_channel], strerror(errno) ); - return( -1 ); + 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 ( 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 ); + 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("Unable to requeue buffer due to not v4l2_data") } } #endif // ZM_HAS_V4L2 #if ZM_HAS_V4L1 - if ( v4l_version == 1 ) - { - if ( channel_count > 1 ) - { - Debug( 3, "Switching video source" ); + 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) ); + 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); + 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 ); + 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 ); + 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; } #endif // ZM_HAS_V4L1 } - return( 0 ); + return 0; } #endif // ZM_HAS_V4L diff --git a/src/zm_logger.cpp b/src/zm_logger.cpp index b91e88505..736d36377 100644 --- a/src/zm_logger.cpp +++ b/src/zm_logger.cpp @@ -38,7 +38,7 @@ #endif bool Logger::smInitialised = false; -Logger *Logger::smInstance = 0; +Logger *Logger::smInstance = NULL; Logger::StringMap Logger::smCodes; Logger::IntMap Logger::smSyslogPriorities; @@ -57,10 +57,10 @@ static void subtractTime( struct timeval * const tp1, struct timeval * const tp2 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 ); + 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()); } @@ -79,7 +79,7 @@ Logger::Logger() : mFlush(false) { if ( smInstance ) { - Panic( "Attempt to create second instance of Logger class" ); + Panic("Attempt to create second instance of Logger class"); } if ( !smInitialised ) { @@ -133,11 +133,11 @@ void Logger::initialise(const std::string &id, const Options &options) { std::string tempLogFile; - if ( (envPtr = getTargettedEnv("LOG_FILE")) ) + if ( (envPtr = getTargettedEnv("LOG_FILE")) ) { tempLogFile = envPtr; - else if ( options.mLogFile.size() ) + } else if ( options.mLogFile.size() ) { tempLogFile = options.mLogFile; - else { + } else { if ( options.mLogPath.size() ) { mLogPath = options.mLogPath; } @@ -169,7 +169,7 @@ void Logger::initialise(const std::string &id, const Options &options) { tempSyslogLevel = config.log_level_syslog >= DEBUG1 ? DEBUG9 : config.log_level_syslog; // Legacy - if ( (envPtr = getenv( "LOG_PRINT" )) ) + if ( (envPtr = getenv("LOG_PRINT")) ) tempTerminalLevel = atoi(envPtr) ? DEBUG9 : NOLOG; if ( (envPtr = getTargettedEnv("LOG_LEVEL")) ) @@ -218,7 +218,7 @@ void Logger::initialise(const std::string &id, const Options &options) { mFlush = false; if ( (envPtr = getenv("LOG_FLUSH")) ) { - mFlush = atoi( envPtr ); + mFlush = atoi(envPtr); } else if ( config.log_debug ) { mFlush = true; } @@ -335,6 +335,10 @@ Logger::Level Logger::level(Logger::Level level) { mEffectiveLevel = mSyslogLevel; if ( mEffectiveLevel > mLevel) mEffectiveLevel = mLevel; + + // DEBUG levels should flush + if ( mLevel > INFO ) + mFlush = true; } return mLevel; } @@ -577,12 +581,12 @@ void Logger::logPrint( bool hex, const char * const filepath, const int line, co } } -void logInit( const char *name, const Logger::Options &options ) { +void logInit(const char *name, const Logger::Options &options) { if ( !Logger::smInstance ) Logger::smInstance = new Logger(); Logger::Options tempOptions = options; tempOptions.mLogPath = staticConfig.PATH_LOGS; - Logger::smInstance->initialise( name, tempOptions ); + Logger::smInstance->initialise(name, tempOptions); } void logTerm() { diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 58292ef52..8ff0a0a64 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -423,15 +423,9 @@ Monitor::Monitor( snprintf(monitor_dir, sizeof(monitor_dir), "%s/%d", storage->Path(), id); if ( purpose == CAPTURE ) { - struct stat statbuf; - - if ( stat(monitor_dir, &statbuf) ) { - if ( errno == ENOENT || errno == ENOTDIR ) { - if ( mkdir(monitor_dir, 0755) ) { - Error("Can't mkdir %s: %s", monitor_dir, strerror(errno)); - } - } else { - Warning("Error stat'ing %s, may be fatal. error is %s", monitor_dir, strerror(errno)); + if ( mkdir(monitor_dir, 0755) ) { + if ( errno != EEXIST ) { + Error("Can't mkdir %s: %s", monitor_dir, strerror(errno)); } } @@ -609,16 +603,23 @@ bool Monitor::connect() { 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()); - } + if ( purpose == ANALYSIS ) { + if ( 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].timestamp = {0,0}; + pre_event_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder()); + } + } + + timestamps = new struct timeval *[pre_event_count]; + images = new Image *[pre_event_count]; + last_signal = shared_data->signal; } Debug(3, "Success connecting"); return true; @@ -1261,6 +1262,7 @@ bool Monitor::Analyse() { int index; if ( adaptive_skip ) { + // I think the idea behind adaptive skip is if we are falling behind, then skip a bunch, but not all int read_margin = shared_data->last_read_index - shared_data->last_write_index; if ( read_margin < 0 ) read_margin += image_buffer_count; @@ -1274,7 +1276,10 @@ bool Monitor::Analyse() { int pending_frames = shared_data->last_write_index - shared_data->last_read_index; if ( pending_frames < 0 ) pending_frames += image_buffer_count; - Debug( 4, "ReadIndex:%d, WriteIndex: %d, PendingFrames = %d, ReadMargin = %d, Step = %d", shared_data->last_read_index, shared_data->last_write_index, pending_frames, read_margin, step ); + Debug(4, + "ReadIndex:%d, WriteIndex: %d, PendingFrames = %d, ReadMargin = %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 { @@ -1294,17 +1299,17 @@ bool Monitor::Analyse() { if ( shared_data->action ) { // Can there be more than 1 bit set in the action? Shouldn't these be elseifs? if ( shared_data->action & RELOAD ) { - Info( "Received reload indication at count %d", image_count ); + 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 ); + Info("Received suspend indication at count %d", image_count); shared_data->active = false; //closeEvent(); } else { - Info( "Received suspend indication at count %d, but wasn't active", image_count ); + Info("Received suspend indication at count %d, but wasn't active", image_count); } if ( config.max_suspend_time ) { auto_resume_time = now.tv_sec + config.max_suspend_time; @@ -1313,7 +1318,7 @@ bool Monitor::Analyse() { } if ( shared_data->action & RESUME ) { if ( Enabled() && !Active() ) { - Info( "Received resume indication at count %d", image_count ); + Info("Received resume indication at count %d", image_count); shared_data->active = true; ref_image = *snap_image; ready_count = image_count+(warmup_count/2); @@ -1324,24 +1329,14 @@ bool Monitor::Analyse() { } // end if shared_data->action if ( auto_resume_time && (now.tv_sec >= auto_resume_time) ) { - Info( "Auto resuming at count %d", image_count ); + 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 int last_section_mod = 0; - static bool last_signal; - - if ( static_undef ) { -// Sure would be nice to be able to assume that these were already initialized. It's just 1 compare/branch, but really not neccessary. - 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; @@ -1394,27 +1389,24 @@ bool Monitor::Analyse() { } 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 = DetectMotion(*snap_image, zoneSet); + int new_motion_score = DetectMotion(*snap_image, zoneSet); Debug(3, "After motion detection, last_motion_score(%d), new motion score(%d)", - last_motion_score, motion_score + last_motion_score, new_motion_score ); - // Why are we updating the last_motion_score too? - last_motion_score = motion_score; + last_motion_score = new_motion_score; } - //int motion_score = DetectBlack( *snap_image, zoneSet ); - if ( motion_score ) { + if ( last_motion_score ) { if ( !event ) { - score += motion_score; + score += last_motion_score; if ( cause.length() ) cause += ", "; cause += MOTION_CAUSE; } else { - score += motion_score; + score += last_motion_score; } noteSetMap[MOTION_CAUSE] = zoneSet; } // end if motion_score @@ -1436,7 +1428,7 @@ bool Monitor::Analyse() { first_link = false; } } - noteSet.insert( linked_monitors[i]->Name() ); + noteSet.insert(linked_monitors[i]->Name()); score += 50; } } else { @@ -1450,14 +1442,15 @@ bool Monitor::Analyse() { //TODO: What happens is the event closes and sets recording to false then recording to true again so quickly that our capture daemon never picks it up. Maybe need a refresh flag? if ( (!signal_change && signal) && (function == RECORD || function == MOCORD) ) { if ( event ) { - //TODO: We shouldn't have to do this every time. Not sure why it clears itself if this isn't here?? - //snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); - Debug( 3, "Detected new event at (%d.%d)", timestamp->tv_sec,timestamp->tv_usec ); + Debug(3, "Detected new event at (%d.%d)", timestamp->tv_sec, timestamp->tv_usec); if ( section_length && ( timestamp->tv_sec >= section_length ) ) { // TODO: Wouldn't this be clearer if we just did something like if now - event->start > section_length ? int section_mod = timestamp->tv_sec % section_length; - Debug( 3, "Section length (%d) Last Section Mod(%d), new section mod(%d)", section_length, last_section_mod, section_mod ); + Debug(3, + "Section length (%d) Last Section Mod(%d), new section mod(%d)", + section_length, last_section_mod, section_mod + ); if ( section_mod < last_section_mod ) { //if ( state == IDLE || state == TAPE || event_close_mode == CLOSE_TIME ) { //if ( state == TAPE ) { @@ -1480,7 +1473,7 @@ bool Monitor::Analyse() { if ( ! event ) { // Create event - event = new Event( this, *timestamp, "Continuous", noteSetMap, videoRecording ); + event = new Event(this, *timestamp, "Continuous", noteSetMap, videoRecording); shared_data->last_event = event->Id(); //set up video store data snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile()); @@ -1554,6 +1547,10 @@ bool Monitor::Analyse() { int pre_index; int pre_event_images = pre_event_count; +if ( event ) { +// SHouldn't be able to happen because +Error("Creating new event when one exists"); +} if ( analysis_fps && pre_event_count ) { // If analysis fps is set, // compute the index for pre event images in the dedicated buffer @@ -1576,8 +1573,8 @@ bool Monitor::Analyse() { else pre_index = ((index + image_buffer_count) - pre_event_count)%image_buffer_count; - Debug(4,"Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d) % %d", - pre_index, index, image_buffer_count, pre_event_count, image_buffer_count); + Debug(4,"Resulting pre_index(%d) from index(%d) + image_buffer_count(%d) - pre_event_count(%d)", + pre_index, index, image_buffer_count, pre_event_count); // Seek forward the next filled slot in to the buffer (oldest data) // from the current position @@ -1622,7 +1619,6 @@ bool Monitor::Analyse() { pre_index = (pre_index + 1)%image_buffer_count; } } - event->AddFrames( pre_event_images, images, timestamps ); } if ( alarm_frame_count ) { @@ -1735,6 +1731,7 @@ bool Monitor::Analyse() { } shared_data->state = state = IDLE; last_section_mod = 0; + trigger_data->trigger_state = TRIGGER_CANCEL; } // end if ( trigger_data->trigger_state != TRIGGER_OFF ) if ( (!signal_change && signal) && (function == MODECT || function == MOCORD) ) { @@ -2113,6 +2110,7 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { Camera *camera = 0; if ( type == "Local" ) { +#if ZM_HAS_V4L int extras = (deinterlacing>>24)&0xff; camera = new LocalCamera( @@ -2135,6 +2133,9 @@ Monitor *Monitor::Load(MYSQL_ROW dbrow, bool load_zones, Purpose purpose) { record_audio, extras ); +#else + Fatal("ZoneMinder not built with Local Camera support"); +#endif } else if ( type == "Remote" ) { if ( protocol == "http" ) { camera = new RemoteCameraHttp( @@ -2387,7 +2388,9 @@ int Monitor::Capture() { } if ( captureResult < 0 ) { - Warning("Return from Capture (%d), signal loss", captureResult); + Info("Return from Capture (%d), signal loss", captureResult); + // Tell zma to end the event. zma will reset TRIGGER + trigger_data->trigger_state = TRIGGER_OFF; // Unable to capture image for temporary reason // Fake a signal loss image Rgb signalcolor; @@ -2473,19 +2476,17 @@ int Monitor::Capture() { //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: images:%d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec", name, image_count, new_fps, new_capture_bandwidth); last_fps_time = now; - if ( new_fps != fps ) { - fps = new_fps; - - db_mutex.lock(); - static char sql[ZM_SQL_SML_BUFSIZ]; - snprintf(sql, sizeof(sql), - "INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth) VALUES (%d, %.2lf,%u) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf, CaptureBandwidth=%u", - id, fps, new_capture_bandwidth, fps, new_capture_bandwidth); - if ( mysql_query(&dbconn, sql) ) { - Error("Can't run query: %s", mysql_error(&dbconn)); - } - db_mutex.unlock(); - } // end if new_fps != fps + fps = new_fps; + db_mutex.lock(); + static char sql[ZM_SQL_SML_BUFSIZ]; + snprintf(sql, sizeof(sql), + "INSERT INTO Monitor_Status (MonitorId,CaptureFPS,CaptureBandwidth) VALUES (%d, %.2lf,%u) ON DUPLICATE KEY UPDATE CaptureFPS = %.2lf, CaptureBandwidth=%u", + id, fps, new_capture_bandwidth, fps, new_capture_bandwidth); + if ( mysql_query(&dbconn, sql) ) { + Error("Can't run query: %s", mysql_error(&dbconn)); + } + db_mutex.unlock(); + Debug(4,sql); } // end if time has changed since last update } // end if it might be time to report the fps } // end if captureResult diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 6eb40a7a2..d7be9555e 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -127,24 +127,25 @@ protected: uint8_t format; /* +55 */ uint32_t imagesize; /* +56 */ uint32_t epadding1; /* +60 */ - uint32_t epadding2; /* +64 */ /* ** 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. + ** Because startup_time is 64bit it may be aligned to a 64bit boundary. So it's offset SHOULD be a multiple + ** of 8. Add or delete epadding's to achieve this. */ - union { /* +68 */ + union { /* +64 */ time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */ uint64_t extrapad1; }; - union { /* +76 */ + union { /* +72 */ time_t last_write_time; uint64_t extrapad2; }; - union { /* +84 */ + union { /* +80 */ time_t last_read_time; uint64_t extrapad3; }; - uint8_t control_state[256]; /* +92 */ + uint8_t control_state[256]; /* +88 */ char alarm_cause[256]; @@ -254,39 +255,41 @@ protected: VideoWriter videowriter; std::string encoderparams; std::vector encoderparamsvec; - bool record_audio; // Whether to store the audio that we receive + bool record_audio; // Whether to store the audio that we receive - 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, + 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 + 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 - int signal_check_points; // Number of points in the image to check for signal - 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 + 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 + int signal_check_points; // Number of points in the image to check for signal + 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 + + bool last_signal; double fps; unsigned int last_camera_bytes; diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index 48d83200c..65e9af579 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -482,7 +482,7 @@ void MonitorStream::runStream() { swap_path = staticConfig.PATH_SWAP; Debug( 3, "Checking swap path folder: %s", swap_path.c_str() ); - if ( checkSwapPath(swap_path.c_str(), false) ) { + if ( checkSwapPath(swap_path.c_str(), true) ) { swap_path += stringtf("/zmswap-m%d", monitor->Id()); Debug(4, "Checking swap path subfolder: %s", swap_path.c_str()); @@ -531,6 +531,12 @@ void MonitorStream::runStream() { Debug(2, "Have checking command Queue for connkey: %d", connkey ); got_command = true; } + // Update modified time of the socket .lock file so that we can tell which ones are stale. + if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) { + touch(sock_path_lock); + last_comm_update = now; + } + } if ( paused ) { @@ -633,7 +639,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey ); // Send the next frame Monitor::Snapshot *snap = &monitor->image_buffer[index]; - //Debug(2, "sending Frame."); + Debug(2, "sending Frame."); if ( !sendFrame(snap->image, snap->timestamp) ) { Debug(2, "sendFrame failed, quiting."); zm_terminate = true; @@ -687,7 +693,7 @@ Debug(2, "Have checking command Queue for connkey: %d", connkey ); } // end if buffered playback frame_count++; } else { - Debug(5,"Waiting for capture"); + Debug(4,"Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index); } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))); diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp index 897859435..703328e2e 100644 --- a/src/zm_rtsp.cpp +++ b/src/zm_rtsp.cpp @@ -388,7 +388,7 @@ int RtspThread::run() { std::string trackUrl = mUrl; std::string controlUrl; - _AVCODECID codecId; + _AVCODECID codecId = AV_CODEC_ID_NONE; if ( mFormatContext->nb_streams >= 1 ) { for ( unsigned int i = 0; i < mFormatContext->nb_streams; i++ ) { diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index f911f2c78..461a6d74d 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -267,7 +267,20 @@ bool StreamBase::sendTextFrame( const char *frame_text ) { void StreamBase::openComms() { if ( connkey > 0 ) { - unsigned int length = snprintf(sock_path_lock, sizeof(sock_path_lock), "%s/zms-%06d.lock", staticConfig.PATH_SOCKS.c_str(), connkey); + // Have to mkdir because systemd is now chrooting and the dir may not exist + if ( mkdir(staticConfig.PATH_SOCKS.c_str(), 0755) ) { + if ( errno != EEXIST ) { + Error("Can't mkdir ZM_PATH_SOCKS %s: %s.", staticConfig.PATH_SOCKS.c_str(), strerror(errno)); + } + } + + unsigned int length = snprintf( + sock_path_lock, + sizeof(sock_path_lock), + "%s/zms-%06d.lock", + staticConfig.PATH_SOCKS.c_str(), + connkey + ); if ( length >= sizeof(sock_path_lock) ) { Warning("Socket lock path was truncated."); } @@ -275,14 +288,14 @@ void StreamBase::openComms() { 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) ); + 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) ); + 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); + Debug(1, "We have obtained a lock on %s fd: %d", sock_path_lock, lock_fd); } sd = socket(AF_UNIX, SOCK_DGRAM, 0); @@ -292,7 +305,13 @@ void StreamBase::openComms() { Debug(1, "Have socket %d", sd); } - length = snprintf(loc_sock_path, sizeof(loc_sock_path), "%s/zms-%06ds.sock", staticConfig.PATH_SOCKS.c_str(), connkey); + length = snprintf( + loc_sock_path, + sizeof(loc_sock_path), + "%s/zms-%06ds.sock", + staticConfig.PATH_SOCKS.c_str(), + connkey + ); if ( length >= sizeof(loc_sock_path) ) { Warning("Socket path was truncated."); length = sizeof(loc_sock_path)-1; @@ -306,13 +325,15 @@ void StreamBase::openComms() { strncpy(loc_addr.sun_path, loc_sock_path, sizeof(loc_addr.sun_path)); loc_addr.sun_family = AF_UNIX; Debug(3, "Binding to %s", loc_sock_path); - if ( bind(sd, (struct sockaddr *)&loc_addr, strlen(loc_addr.sun_path)+sizeof(loc_addr.sun_family)+1) < 0 ) { + 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", staticConfig.PATH_SOCKS.c_str(), connkey); strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)-1); rem_addr.sun_family = AF_UNIX; + + gettimeofday(&last_comm_update, NULL); } // end if connKey > 0 Debug(2, "comms open"); } // end void StreamBase::openComms() diff --git a/src/zm_stream.h b/src/zm_stream.h index 4e8343b8c..f9364bbef 100644 --- a/src/zm_stream.h +++ b/src/zm_stream.h @@ -85,6 +85,7 @@ protected: int step; struct timeval now; + struct timeval last_comm_update; double base_fps; double effective_fps; diff --git a/src/zm_utils.cpp b/src/zm_utils.cpp index 94c681dd5..adc10cf8e 100644 --- a/src/zm_utils.cpp +++ b/src/zm_utils.cpp @@ -24,6 +24,8 @@ #include #include #include +#include /* Definition of AT_* constants */ +#include #if defined(__arm__) #include #endif @@ -414,3 +416,22 @@ Warning("ZM Compiled without LIBCURL. UriDecoding not implemented."); #endif } +void touch(const char *pathname) { + int fd = open(pathname, + O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK, + 0666); + if ( fd < 0 ) { + // Couldn't open that path. + Error("Couldn't open() path \"%s in touch", pathname); + return; + } + int rc = utimensat(AT_FDCWD, + pathname, + nullptr, + 0); + if ( rc ) { + Error("Couldn't utimensat() path %s in touch", pathname); + return; + } +} + diff --git a/src/zm_utils.h b/src/zm_utils.h index 8352edecb..d1340cf4b 100644 --- a/src/zm_utils.h +++ b/src/zm_utils.h @@ -63,5 +63,5 @@ extern unsigned int neonversion; char *timeval_to_string( struct timeval tv ); std::string UriDecode( const std::string &encoded ); - +void touch( const char *pathname ); #endif // ZM_UTILS_H diff --git a/src/zm_videostore.cpp b/src/zm_videostore.cpp index 7d5d2de59..a3b66646b 100644 --- a/src/zm_videostore.cpp +++ b/src/zm_videostore.cpp @@ -64,7 +64,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, } // Couldn't deduce format from filename, trying from format name - if (!oc) { + if ( !oc ) { avformat_alloc_output_context2(&oc, NULL, format, filename); if (!oc) { Error( @@ -108,7 +108,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, Debug(2, "Success creating video out stream"); } - if (!video_out_ctx->codec_tag) { + if ( !video_out_ctx->codec_tag ) { video_out_ctx->codec_tag = av_codec_get_tag(oc->oformat->codec_tag, video_in_ctx->codec_id); Debug(2, "No codec_tag, setting to %d", video_out_ctx->codec_tag); @@ -127,9 +127,10 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, #else video_out_stream = - avformat_new_stream(oc,(AVCodec *)(video_in_ctx->codec)); + avformat_new_stream(oc, NULL); +//(AVCodec *)(video_in_ctx->codec)); //avformat_new_stream(oc,(const AVCodec *)(video_in_ctx->codec)); - if (!video_out_stream) { + if ( !video_out_stream ) { Fatal("Unable to create video out stream\n"); } else { Debug(2, "Success creating video out stream"); @@ -158,6 +159,9 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, // Just copy them from the in, no reason to choose different video_out_ctx->time_base = video_in_ctx->time_base; + if ( ! (video_out_ctx->time_base.num && video_out_ctx->time_base.den) ) { + video_out_ctx->time_base = AV_TIME_BASE_Q; + } video_out_stream->time_base = video_in_stream->time_base; Debug(3, @@ -178,7 +182,6 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, } Monitor::Orientation orientation = monitor->getOrientation(); - Debug(3, "Have orientation"); if (orientation) { if (orientation == Monitor::ROTATE_0) { } else if (orientation == Monitor::ROTATE_90) { @@ -245,18 +248,18 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, // Copy params from instream to ctx ret = avcodec_parameters_to_context(audio_out_ctx, audio_in_stream->codecpar); - if (ret < 0) { - Error("Unable to copy audio params to ctx %s\n", + if ( ret < 0 ) { + Error("Unable to copy audio params to ctx %s", av_make_error_string(ret).c_str()); } ret = avcodec_parameters_from_context(audio_out_stream->codecpar, audio_out_ctx); - if (ret < 0) { - Error("Unable to copy audio params to stream %s\n", + if ( ret < 0 ) { + Error("Unable to copy audio params to stream %s", av_make_error_string(ret).c_str()); } - if (!audio_out_ctx->codec_tag) { + if ( !audio_out_ctx->codec_tag ) { audio_out_ctx->codec_tag = av_codec_get_tag( oc->oformat->codec_tag, audio_in_ctx->codec_id); Debug(2, "Setting audio codec tag to %d", @@ -268,12 +271,12 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, ret = avcodec_copy_context(audio_out_ctx, audio_in_ctx); audio_out_ctx->codec_tag = 0; #endif - if (ret < 0) { - Error("Unable to copy audio ctx %s\n", + if ( ret < 0 ) { + Error("Unable to copy audio ctx %s", av_make_error_string(ret).c_str()); audio_out_stream = NULL; } else { - if (audio_out_ctx->channels > 1) { + if ( audio_out_ctx->channels > 1 ) { Warning("Audio isn't mono, changing it."); audio_out_ctx->channels = 1; } else { @@ -283,7 +286,7 @@ VideoStore::VideoStore(const char *filename_in, const char *format_in, } // end if audio_out_stream } // end if is AAC - if (audio_out_stream) { + if ( audio_out_stream ) { if (oc->oformat->flags & AVFMT_GLOBALHEADER) { #if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0) audio_out_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; @@ -336,14 +339,17 @@ bool VideoStore::open() { } else if (av_dict_count(opts) != 0) { Warning("some options not set\n"); } + if (opts) av_dict_free(&opts); if (ret < 0) { Error("Error occurred when writing out file header to %s: %s\n", filename, av_make_error_string(ret).c_str()); + /* free the stream */ + avio_closep(&oc->pb); + //avformat_free_context(oc); return false; } - if (opts) av_dict_free(&opts); return true; -} +} // end VideoStore::open() VideoStore::~VideoStore() { @@ -353,6 +359,9 @@ VideoStore::~VideoStore() { // The codec queues data. We need to send a flush command and out // whatever we get. Failures are not fatal. AVPacket pkt; + // Without these we seg fault I don't know why. + pkt.data = NULL; + pkt.size = 0; av_init_packet(&pkt); while (1) { @@ -398,20 +407,22 @@ VideoStore::~VideoStore() { pkt.stream_index = audio_out_stream->index; av_interleaved_write_frame(oc, &pkt); zm_av_packet_unref(&pkt); - } // while have buffered frames - } // end if audio_out_codec + } // while have buffered frames + } // end if audio_out_codec // Flush Queues + Debug(1,"Flushing interleaved queues"); av_interleaved_write_frame(oc, NULL); + Debug(1,"Writing trailer"); /* Write the trailer before close */ if (int rc = av_write_trailer(oc)) { Error("Error writing trailer %s", av_err2str(rc)); } else { - Debug(3, "Sucess Writing trailer"); + Debug(3, "Success Writing trailer"); } - // WHen will be not using a file ? + // When will we not be using a file ? if ( !(out_format->flags & AVFMT_NOFILE) ) { /* Close the out file. */ Debug(2, "Closing"); @@ -422,7 +433,7 @@ VideoStore::~VideoStore() { } else { Debug(3, "Not closing avio because we are not writing to a file."); } - } + } // end if ( oc->pb ) // I wonder if we should be closing the file first. // I also wonder if we really need to be doing all the ctx // allocation/de-allocation constantly, or whether we can just re-use it. @@ -507,16 +518,23 @@ bool VideoStore::setup_resampler() { } Debug(2, "Have audio out codec"); +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) // audio_out_ctx = audio_out_stream->codec; audio_out_ctx = avcodec_alloc_context3(audio_out_codec); - if (!audio_out_ctx) { - Error("could not allocate codec ctx for AAC\n"); + if ( !audio_out_ctx ) { + Error("could not allocate codec ctx for AAC"); audio_out_stream = NULL; return false; } Debug(2, "Have audio_out_ctx"); + // Now copy them to the out stream + audio_out_stream = avformat_new_stream(oc, NULL); +#else + audio_out_stream = avformat_new_stream(oc, NULL); + audio_out_ctx = audio_out_stream->codec; +#endif /* put sample parameters */ audio_out_ctx->bit_rate = audio_in_ctx->bit_rate; @@ -524,7 +542,17 @@ bool VideoStore::setup_resampler() { audio_out_ctx->channels = audio_in_ctx->channels; audio_out_ctx->channel_layout = audio_in_ctx->channel_layout; audio_out_ctx->sample_fmt = audio_in_ctx->sample_fmt; +#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) +#else audio_out_ctx->refcounted_frames = 1; +#endif + if ( ! audio_out_ctx->channel_layout ) { + Debug(3, "Correcting channel layout from (%d) to (%d)", + audio_out_ctx->channel_layout, + av_get_default_channel_layout(audio_out_ctx->channels) + ); + audio_out_ctx->channel_layout = av_get_default_channel_layout(audio_out_ctx->channels); + } if (audio_out_codec->supported_samplerates) { int found = 0; @@ -556,8 +584,6 @@ bool VideoStore::setup_resampler() { audio_out_ctx->time_base = (AVRational){1, audio_out_ctx->sample_rate}; - // Now copy them to the out stream - audio_out_stream = avformat_new_stream(oc, audio_out_codec); #if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0) ret = avcodec_parameters_from_context(audio_out_stream->codecpar, diff --git a/src/zm_zone.cpp b/src/zm_zone.cpp index 401366b2d..6d4ae15e3 100644 --- a/src/zm_zone.cpp +++ b/src/zm_zone.cpp @@ -254,7 +254,11 @@ bool Zone::CheckAlarms(const Image *delta_image) { return false; } - score = (100*alarm_pixels)/polygon.Area(); + if (max_alarm_pixels != 0) + score = (100*alarm_pixels)/max_alarm_pixels; + else + 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); @@ -331,7 +335,11 @@ bool Zone::CheckAlarms(const Image *delta_image) { return false; } - score = (100*alarm_filter_pixels)/polygon.Area(); + if (max_filter_pixels != 0) + score = (100*alarm_filter_pixels)/max_filter_pixels; + else + 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); @@ -597,8 +605,12 @@ bool Zone::CheckAlarms(const Image *delta_image) { /* No blobs */ return false; } - - score = (100*alarm_blob_pixels)/(polygon.Area()); + + if (max_blob_pixels != 0) + score = (100*alarm_blob_pixels)/(max_blob_pixels); + else + 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); diff --git a/src/zma.cpp b/src/zma.cpp index 83035f011..33dedbe54 100644 --- a/src/zma.cpp +++ b/src/zma.cpp @@ -83,7 +83,7 @@ int main( int argc, char *argv[] ) { while (1) { int option_index = 0; - int c = getopt_long (argc, argv, "m:h:v", long_options, &option_index); + int c = getopt_long(argc, argv, "m:h:v", long_options, &option_index); if ( c == -1 ) { break; } @@ -144,7 +144,7 @@ int main( int argc, char *argv[] ) { unsigned int analysis_update_delay = monitor->GetAnalysisUpdateDelay(); time_t last_analysis_update_time, cur_time; monitor->UpdateAdaptiveSkip(); - last_analysis_update_time = time( 0 ); + last_analysis_update_time = time(0); while( (!zm_terminate) && monitor->ShmValid() ) { // Process the next image @@ -181,5 +181,5 @@ int main( int argc, char *argv[] ) { Image::Deinitialise(); logTerm(); zmDbClose(); - return( 0 ); + return 0; } diff --git a/src/zmc.cpp b/src/zmc.cpp index 77b88ddc6..9270c75cd 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -300,19 +300,22 @@ int main(int argc, char *argv[]) { 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); + Error("Failed to pre-capture monitor %d %d (%d/%d)", + monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); monitors[i]->Close(); 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); + Info("Failed to capture image from monitor %d %s (%d/%d)", + monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); monitors[i]->Close(); 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); + Error("Failed to post-capture monitor %d %s (%d/%d)", + monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors); monitors[i]->Close(); result = -1; break; diff --git a/src/zms.cpp b/src/zms.cpp index 74dab2ed9..a5fef0134 100644 --- a/src/zms.cpp +++ b/src/zms.cpp @@ -66,7 +66,7 @@ int main( int argc, const char *argv[] ) { double maxfps = 10.0; unsigned int bitrate = 100000; unsigned int ttl = 0; - EventStream::StreamMode replay = EventStream::MODE_SINGLE; + EventStream::StreamMode replay = EventStream::MODE_NONE; std::string username; std::string password; char auth[64] = ""; @@ -137,8 +137,17 @@ int main( int argc, const char *argv[] ) { } 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; + if ( !strcmp(value, "gapless") ) { + replay = EventStream::MODE_ALL_GAPLESS; + } else if ( !strcmp(value, "all") ) { + replay = EventStream::MODE_ALL; + } else if ( !strcmp(value, "none") ) { + replay = EventStream::MODE_NONE; + } else if ( !strcmp(value, "single") ) { + replay = EventStream::MODE_SINGLE; + } else { + Error("Unsupported value %s for replay, defaulting to none", value); + } } else if ( !strcmp( name, "connkey" ) ) { connkey = atoi(value); } else if ( !strcmp( name, "buffer" ) ) { diff --git a/utils/do_debian_package.sh b/utils/do_debian_package.sh index d2408da14..71f1ec460 100755 --- a/utils/do_debian_package.sh +++ b/utils/do_debian_package.sh @@ -23,7 +23,7 @@ case $i in shift # past argument=value ;; -d=*|--distro=*) - DISTRO="${i#*=}" + DISTROS="${i#*=}" shift # past argument=value ;; -i=*|--interactive=*) @@ -74,11 +74,15 @@ else echo "Doing $TYPE build" fi; -if [ "$DISTRO" == "" ]; then - DISTRO=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`; - echo "Defaulting to $DISTRO for distribution"; +if [ "$DISTROS" == "" ]; then + if [ "$RELEASE" != "" ]; then + DISTROS="xenial,bionic,trusty" + else + DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`; + fi; + echo "Defaulting to $DISTROS for distribution"; else - echo "Building for $DISTRO"; + echo "Building for $DISTROS"; fi; # Release is a special mode... it uploads to the release ppa and cannot have a snapshot @@ -93,7 +97,8 @@ if [ "$RELEASE" != "" ]; then else GITHUB_FORK="ZoneMinder"; fi - BRANCH="release-$RELEASE" + # We use a tag instead of a branch atm. + BRANCH=$RELEASE else if [ "$GITHUB_FORK" == "" ]; then echo "Defaulting to ZoneMinder upstream git" @@ -115,6 +120,18 @@ else fi; fi +PPA=""; +if [ "$RELEASE" != "" ]; then + # We need to use our official tarball for the original source, so grab it and overwrite our generated one. + IFS='.' read -r -a VERSION <<< "$RELEASE" + PPA="ppa:iconnor/zoneminder-${VERSION[0]}.${VERSION[1]}" +else + if [ "$BRANCH" == "" ]; then + PPA="ppa:iconnor/zoneminder-master"; + else + PPA="ppa:iconnor/zoneminder-$BRANCH"; + fi; +fi; # Instead of cloning from github each time, if we have a fork lying around, update it and pull from there instead. if [ ! -d "${GITHUB_FORK}_zoneminder_release" ]; then @@ -153,6 +170,11 @@ if [ "$SNAPSHOT" != "stable" ] && [ "$SNAPSHOT" != "" ]; then fi; DIRECTORY="zoneminder_$VERSION"; +if [ -d "$DIRECTORY.orig" ]; then + echo "$DIRECTORY.orig already exists. Please delete it." + exit 0; +fi; + echo "Doing $TYPE release $DIRECTORY"; mv "${GITHUB_FORK}_zoneminder_release" "$DIRECTORY.orig"; if [ $? -ne 0 ]; then @@ -160,102 +182,150 @@ if [ $? -ne 0 ]; then echo "Setting up build dir failed."; exit $?; fi; + cd "$DIRECTORY.orig"; +# Init submodules git submodule init git submodule update --init --recursive -if [ "$DISTRO" == "trusty" ] || [ "$DISTRO" == "precise" ]; then - mv distros/ubuntu1204 debian -else - if [ "$DISTRO" == "wheezy" ]; then - mv distros/debian debian - else - mv distros/ubuntu1604 debian - fi; -fi; - -if [ "$DEBEMAIL" != "" ] && [ "$DEBFULLNAME" != "" ]; then - AUTHOR="$DEBFULLNAME <$DEBEMAIL>" -else - 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 -fi - -if [ "$URGENCY" = "" ]; then - URGENCY="medium" -fi; - -if [ "$SNAPSHOT" == "stable" ]; then -cat < debian/changelog -zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY - - * Release $VERSION - - -- $AUTHOR $DATE - -EOF -cat < debian/NEWS -zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY - - * Release $VERSION - - -- $AUTHOR $DATE -EOF -else -cat < debian/changelog -zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY - - * - - -- $AUTHOR $DATE -EOF -cat < debian/changelog -zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY - - * - - -- $AUTHOR $DATE -EOF -fi; +# Cleanup rm -rf .git rm .gitignore cd ../ -tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig -cd $DIRECTORY.orig -if [ $TYPE == "binary" ]; then - # Auto-install all ZoneMinder's depedencies using the Debian control file - sudo apt-get install devscripts equivs - sudo mk-build-deps -ir ./debian/control - echo "Status: $?" - DEBUILD=debuild -else - if [ $TYPE == "local" ]; then +if [ ! -e "$DIRECTORY.orig.tar.gz" ]; then + tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig +fi; + +IFS=',' ;for DISTRO in `echo "$DISTROS"`; do + echo "Generating package for $DISTRO"; + cd $DIRECTORY.orig + + if [ -e "debian" ]; then + rm -rf debian + fi; + + # Generate Changlog + if [ "$DISTRO" == "trusty" ] || [ "$DISTRO" == "precise" ]; then + cp -Rpd distros/ubuntu1204 debian + else + if [ "$DISTRO" == "wheezy" ]; then + cp -Rpd distros/debian debian + else + cp -Rpd distros/ubuntu1604 debian + fi; + fi; + + if [ "$DEBEMAIL" != "" ] && [ "$DEBFULLNAME" != "" ]; then + AUTHOR="$DEBFULLNAME <$DEBEMAIL>" + else + 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 + fi + + if [ "$URGENCY" = "" ]; then + URGENCY="medium" + fi; + + if [ "$SNAPSHOT" == "stable" ]; then + cat < debian/changelog +zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY + + * Release $VERSION + + -- $AUTHOR $DATE + +EOF + cat < debian/NEWS +zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY + + * Release $VERSION + + -- $AUTHOR $DATE +EOF + else + cat < debian/changelog +zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY + + * + + -- $AUTHOR $DATE +EOF + cat < debian/changelog +zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY + + * + + -- $AUTHOR $DATE +EOF + fi; + + if [ $TYPE == "binary" ]; then # Auto-install all ZoneMinder's depedencies using the Debian control file sudo apt-get install devscripts equivs sudo mk-build-deps -ir ./debian/control echo "Status: $?" - DEBUILD="debuild -i -us -uc -b" - else - # Source build, don't need build depends. - DEBUILD="debuild -S -sa" + DEBUILD=debuild + else + if [ $TYPE == "local" ]; then + # Auto-install all ZoneMinder's depedencies using the Debian control file + sudo apt-get install devscripts equivs + sudo mk-build-deps -ir ./debian/control + echo "Status: $?" + DEBUILD="debuild -i -us -uc -b" + else + # Source build, don't need build depends. + DEBUILD="debuild -S -sa" + fi; + fi; + if [ "$DEBSIGN_KEYID" != "" ]; then + DEBUILD="$DEBUILD -k$DEBSIGN_KEYID" + fi + eval $DEBUILD + if [ $? -ne 0 ]; then + echo "Error status code is: $?" + echo "Build failed."; + exit $?; fi; -fi; -if [ "$DEBSIGN_KEYID" != "" ]; then - DEBUILD="$DEBUILD -k$DEBSIGN_KEYID" -fi -$DEBUILD -if [ $? -ne 0 ]; then -echo "Error status code is: $?" - echo "Build failed."; - exit $?; -fi; -cd ../ + cd ../ + + if [ $TYPE == "binary" ]; then + if [ "$INTERACTIVE" != "no" ]; then + read -p "Not doing dput since it's a binary release. Do you want to install it? (y/N)" + if [[ $REPLY == [yY] ]]; then + sudo dpkg -i $DIRECTORY*.deb + fi; + read -p "Do you want to upload this binary to zmrepo? (y/N)" + if [[ $REPLY == [yY] ]]; then + if [ "$RELEASE" != "" ]; then + scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/" + else + if [ "$BRANCH" == "" ]; then + scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/" + else + scp "$DIRECTORY-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/${BRANCH}/mini-dinstall/incoming/" + fi; + fi; + fi; + fi; + else + SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes"; + + dput="Y"; + if [ "$INTERACTIVE" != "no" ]; then + read -p "Ready to dput $SC to $PPA ? Y/N..."; + if [[ "$REPLY" == [yY] ]]; then + dput $PPA $SC + fi; + fi; + fi; +done; # foreach distro + if [ "$INTERACTIVE" != "no" ]; then 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 "$DIRECTORY.orig" zoneminder_release; echo "The checked out copy is preserved in zoneminder_release"; } || { rm -fr "$DIRECTORY.orig"; echo "The checked out copy has been deleted"; } @@ -264,45 +334,4 @@ else rm -fr "$DIRECTORY.orig"; echo "The checked out copy has been deleted"; fi -if [ $TYPE == "binary" ]; then - if [ "$INTERACTIVE" != "no" ]; then - read -p "Not doing dput since it's a binary release. Do you want to install it? (y/N)" - if [[ $REPLY == [yY] ]]; then - sudo dpkg -i $DIRECTORY*.deb - fi; - read -p "Do you want to upload this binary to zmrepo? (y/N)" - if [[ $REPLY == [yY] ]]; then - if [ "$RELEASE" != "" ]; then - scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/stable/mini-dinstall/incoming/" - else - if [ "$BRANCH" == "" ]; then - scp "zoneminder_${VERSION}-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/master/mini-dinstall/incoming/" - else - scp "$DIRECTORY-${DISTRO}"* "zoneminder-doc_${VERSION}-${DISTRO}"* "zoneminder-dbg_${VERSION}-${DISTRO}"* "zoneminder_${VERSION}.orig.tar.gz" "zmrepo@zmrepo.connortechnology.com:debian/${BRANCH}/mini-dinstall/incoming/" - fi; - fi; - fi; - fi; -else - SC="zoneminder_${VERSION}-${DISTRO}${PACKAGE_VERSION}_source.changes"; - PPA=""; - if [ "$RELEASE" != "" ]; then - PPA="ppa:iconnor/zoneminder"; - else - if [ "$BRANCH" == "" ]; then - PPA="ppa:iconnor/zoneminder-master"; - else - PPA="ppa:iconnor/zoneminder-$BRANCH"; - fi; - fi; - - dput="Y"; - if [ "$INTERACTIVE" != "no" ]; then - read -p "Ready to dput $SC to $PPA ? Y/N..."; - if [[ "$REPLY" == [yY] ]]; then - dput $PPA $SC - fi; - fi; -fi; - diff --git a/utils/packpack/startpackpack.sh b/utils/packpack/startpackpack.sh index 3ed227a44..677fa1f32 100755 --- a/utils/packpack/startpackpack.sh +++ b/utils/packpack/startpackpack.sh @@ -154,7 +154,7 @@ movecrud () { echo "Unpacking CakePHP-Enum-Behavior plugin..." tar -xzf build/cakephp-enum-behavior-${CEBVER}.tar.gz rmdir web/api/app/Plugin/CakePHP-Enum-Behavior - mv -f crud-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior + mv -f CakePHP-Enum-Behavior-${CEBVER} web/api/app/Plugin/CakePHP-Enum-Behavior fi } diff --git a/utils/zm-alarm.pl b/utils/zm-alarm.pl index 528328c61..d68c82663 100755 --- a/utils/zm-alarm.pl +++ b/utils/zm-alarm.pl @@ -14,9 +14,17 @@ $| = 1; my @monitors; my $dbh = zmDbConnect(); -my $sql = "SELECT * 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 '$sql': ".$sth->errstr() ); + +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 die( "Can't prepare '$sql': ".$dbh->errstr() ); + +my $res = $sth->execute() + or die( "Can't execute '$sql': ".$sth->errstr() ); while ( my $monitor = $sth->fetchrow_hashref() ) { push( @monitors, $monitor ); @@ -24,6 +32,12 @@ while ( my $monitor = $sth->fetchrow_hashref() ) { while (1) { foreach my $monitor (@monitors) { + # Check shared memory ok + if ( !zmMemVerify( $monitor ) ) { + zmMemInvalidate( $monitor ); + next; + } + my $monitorState = zmGetMonitorState($monitor); printState($monitor->{Id}, $monitor->{Name}, $monitorState); } diff --git a/version b/version index d56f906c5..c78d39b8e 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.31.47 +1.32.2 diff --git a/web/ajax/add_monitors.php b/web/ajax/add_monitors.php index c5664f06b..69ce0332f 100644 --- a/web/ajax/add_monitors.php +++ b/web/ajax/add_monitors.php @@ -93,7 +93,7 @@ Info("Testing connection to " . $url_bits['host'].':'.$port); foreach ( $available_streams as &$stream ) { # check for existence in db. $stream['url'] = unparse_url( $stream, array('path'=>'/','query'=>'action=stream') ); - $monitors = Monitor::find_all( array('Path'=>$stream['url']) ); + $monitors = Monitor::find( array('Path'=>$stream['url']) ); if ( count($monitors) ) { Info("Found monitors matching " . $stream['url'] ); $stream['Monitor'] = $monitors[0]; @@ -135,7 +135,7 @@ if ( canEdit( 'Monitors' ) ) { if ( 0 ) { // Shortcut test - $monitors = Monitor::find_all( array( 'Path'=>$_REQUEST['url'] ) ); + $monitors = Monitor::find( array('Path'=>$_REQUEST['url']) ); if ( count( $monitors ) ) { Info("Monitor found for " . $_REQUEST['url']); $url_bits['url'] = $_REQUEST['url']; diff --git a/web/ajax/log.php b/web/ajax/log.php index 375b53a05..fca79fda4 100644 --- a/web/ajax/log.php +++ b/web/ajax/log.php @@ -33,7 +33,7 @@ switch ( $_REQUEST['task'] ) { if ( !canView('System') ) ajaxError('Insufficient permissions to view log entries'); - $servers = Server::find_all(); + $servers = Server::find(); $servers_by_Id = array(); # There is probably a better way to do this. foreach ( $servers as $server ) { @@ -112,7 +112,7 @@ switch ( $_REQUEST['task'] ) { } else if ( $field == 'ServerId' ) { $options['ServerId'][$value] = ( $value and isset($servers_by_Id[$value]) ) ? $servers_by_Id[$value]->Name() : ''; } else if ( isset($log[$field]) ) { - $options[$field][$log[$field]] = $log[$field]; + $options[$field][$log[$field]] = $value; } } $logs[] = $log; @@ -125,7 +125,7 @@ switch ( $_REQUEST['task'] ) { 'available' => isset($available) ? $available : $total, 'logs' => $logs, 'state' => logState(), - 'options' => $options + 'options' => $options, ) ); break; } @@ -153,7 +153,7 @@ switch ( $_REQUEST['task'] ) { } $sortOrder = (isset($_POST['sortOrder']) and $_POST['sortOrder']) == 'asc' ? 'asc':'desc'; - $servers = Server::find_all(); + $servers = Server::find(); $servers_by_Id = array(); # There is probably a better way to do this. foreach ( $servers as $server ) { @@ -164,14 +164,23 @@ switch ( $_REQUEST['task'] ) { $where = array(); $values = array(); if ( $minTime ) { - preg_match('/(.+)(\.\d+)/', $minTime, $matches); - $minTime = strtotime($matches[1]).$matches[2]; + Logger::Debug("MinTime: $minTime"); + if ( preg_match('/(.+)(\.\d+)/', $minTime, $matches) ) { + # This handles sub second precision + $minTime = strtotime($matches[1]).$matches[2]; + Logger::Debug("MinTime: $minTime"); + } else { + $minTime = strtotime($minTime); + } $where[] = 'TimeKey >= ?'; $values[] = $minTime; } if ( $maxTime ) { - preg_match('/(.+)(\.\d+)/', $maxTime, $matches); - $maxTime = strtotime($matches[1]).$matches[2]; + if ( preg_match('/(.+)(\.\d+)/', $maxTime, $matches) ) { + $maxTime = strtotime($matches[1]).$matches[2]; + } else { + $maxTime = strtotime($maxTime); + } $where[] = 'TimeKey <= ?'; $values[] = $maxTime; } @@ -209,8 +218,15 @@ switch ( $_REQUEST['task'] ) { } $exportKey = substr(md5(rand()),0,8); $exportFile = "zm-log.$exportExt"; - $exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt"; - if ( !($exportFP = fopen( $exportPath, "w" )) ) + if ( ! file_exists(ZM_DIR_EXPORTS) ) { + Logger::Debug('Creating ' . ZM_DIR_EXPORTS); + if ( ! mkdir(ZM_DIR_EXPORTS) ) { + Fatal("Can't create exports dir at '".ZM_DIR_EXPORTS."'"); + } + } + $exportPath = ZM_DIR_EXPORTS."/zm-log-$exportKey.$exportExt"; + Logger::Debug("Exporting to $exportPath"); + if ( !($exportFP = fopen($exportPath, 'w')) ) Fatal("Unable to open log export file $exportPath"); $logs = array(); foreach ( dbFetchAll($sql, NULL, $values) as $log ) { @@ -218,6 +234,8 @@ switch ( $_REQUEST['task'] ) { $log['Server'] = ( $log['ServerId'] and isset($servers_by_Id[$log['ServerId']]) ) ? $servers_by_Id[$log['ServerId']]->Name() : ''; $logs[] = $log; } + Logger::Debug(count($logs)." lines being exported by $sql " . implode(',',$values)); + switch( $format ) { case 'text' : { @@ -390,7 +408,7 @@ switch ( $_REQUEST['task'] ) { } $exportFile = "zm-log.$exportExt"; - $exportPath = ZM_PATH_SWAP."/zm-log-$exportKey.$exportExt"; + $exportPath = ZM_DIR_EXPORTS."/zm-log-$exportKey.$exportExt"; header('Pragma: public'); header('Expires: 0'); diff --git a/web/ajax/status.php b/web/ajax/status.php index ee9a554a4..dbac0773b 100644 --- a/web/ajax/status.php +++ b/web/ajax/status.php @@ -383,7 +383,7 @@ function getNearEvents() { parseSort(); if ( $user['MonitorIds'] ) - $midSql = ' and MonitorId in ('.join( ',', preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).')'; + $midSql = ' AND MonitorId IN ('.join( ',', preg_split( '/["\'\s]*,["\'\s]*/', $user['MonitorIds'] ) ).')'; else $midSql = ''; @@ -392,32 +392,40 @@ function getNearEvents() { $sortOrder = 'asc'; } - $sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." ORDER BY $sortColumn ".($sortOrder=='asc'?'desc':'asc') . ' LIMIT 2'; - $result = dbQuery( $sql ); - while ( $id = dbFetchNext( $result, 'Id' ) ) { - if ( $id == $eventId ) { - $prevEvent = dbFetchNext( $result ); - break; - } + $sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'<=':'>=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql.' AND E.Id<'.$event['Id'] . " ORDER BY $sortColumn ".($sortOrder=='asc'?'desc':'asc'); + if ( $sortColumn != 'E.Id' ) { + # When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id + $sql .= ', E.Id DESC'; } + $sql .= ' LIMIT 1'; + $result = dbQuery( $sql ); + $prevEvent = dbFetchNext( $result ); - $sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql." ORDER BY $sortColumn $sortOrder LIMIT 2"; - $result = dbQuery( $sql ); - while ( $id = dbFetchNext( $result, 'Id' ) ) { - if ( $id == $eventId ) { - $nextEvent = dbFetchNext( $result ); - break; - } + $sql = "SELECT E.Id AS Id, E.StartTime AS StartTime FROM Events AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE $sortColumn ".($sortOrder=='asc'?'>=':'<=')." '".$event[$_REQUEST['sort_field']]."'".$_REQUEST['filter']['sql'].$midSql.' AND E.Id>'.$event['Id'] . " ORDER BY $sortColumn $sortOrder"; + if ( $sortColumn != 'E.Id' ) { + # When sorting by starttime, if we have two events with the same starttime (diffreent monitors) then we should sort secondly by Id + $sql .= ', E.Id ASC'; } + $sql .= ' LIMIT 1'; + $result = dbQuery( $sql ); + $nextEvent = dbFetchNext( $result ); $result = array( 'EventId'=>$eventId ); - $result['PrevEventId'] = empty($prevEvent)?0:$prevEvent['Id']; - $result['NextEventId'] = empty($nextEvent)?0:$nextEvent['Id']; - $result['PrevEventStartTime'] = empty($prevEvent)?0:$prevEvent['StartTime']; - $result['NextEventStartTime'] = empty($nextEvent)?0:$nextEvent['StartTime']; - $result['PrevEventDefVideoPath'] = empty($prevEvent)?0:(getEventDefaultVideoPath($prevEvent['Id'])); - $result['NextEventDefVideoPath'] = empty($nextEvent)?0:(getEventDefaultVideoPath($nextEvent['Id'])); - return( $result ); + if ( $prevEvent ) { + $result['PrevEventId'] = $prevEvent['Id']; + $result['PrevEventStartTime'] = $prevEvent['StartTime']; + $result['PrevEventDefVideoPath'] = getEventDefaultVideoPath($prevEvent['Id']); + } else { + $result['PrevEventId'] = $result['PrevEventStartTime'] = $result['PrevEventDefVideoPath'] = 0; + } + if ( $nextEvent ) { + $result['NextEventId'] = $nextEvent['Id']; + $result['NextEventStartTime'] = $nextEvent['StartTime']; + $result['NextEventDefVideoPath'] = getEventDefaultVideoPath($nextEvent['Id']); + } else { + $result['NextEventId'] = $result['NextEventStartTime'] = $result['NextEventDefVideoPath'] = 0; + } + return $result; } ?> diff --git a/web/api/app/Config/core.php.default b/web/api/app/Config/core.php.default index 39a51690c..64f439420 100644 --- a/web/api/app/Config/core.php.default +++ b/web/api/app/Config/core.php.default @@ -50,7 +50,7 @@ */ Configure::write('Error', array( 'handler' => 'ErrorHandler::handleError', - 'level' => E_ALL & ~E_DEPRECATED, + 'level' => E_ALL & ~E_DEPRECATED & ~E_NOTICE, 'trace' => true )); diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php index 6dd9e5211..017bce798 100644 --- a/web/api/app/Controller/HostController.php +++ b/web/api/app/Controller/HostController.php @@ -69,7 +69,7 @@ class HostController extends AppController { $this->Session->read('user'); # this is needed for command line/curl to recognize a session $zmAuthRelay = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_RELAY')))['Config']['Value']; if ( $zmAuthRelay == 'hashed' ) { - $zmAuthHashIps= $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_IPS')))['Config']['Value']; + $zmAuthHashIps = $this->Config->find('first',array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_AUTH_HASH_IPS')))['Config']['Value']; $credentials = 'auth='.generateAuthHash($zmAuthHashIps); } else if ( $zmAuthRelay == 'plain' ) { // user will need to append the store password here @@ -101,27 +101,25 @@ class HostController extends AppController { $this->loadModel('Monitor'); // If $mid is passed, see if it is valid - if ($mid) { - if (!$this->Monitor->exists($mid)) { + 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' ]; + $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) { + #if ('/' === "" || strrpos($zm_dir_events, '/', -strlen($zm_dir_events)) !== TRUE) { + if ( substr($zm_dir_events, 0, 1) != '/' ) { // relative - so add the full path - $zm_dir_events = Configure::read('ZM_PATH_WEB') . '/' . $zm_dir_events; + $zm_dir_events = ZM_PATH_WEB . '/' . $zm_dir_events; } - if ($mid) { + if ( $mid ) { // Get disk usage for $mid - $usage = shell_exec ("du -sh0 $zm_dir_events/$mid | awk '{print $1}'"); + Logger::Debug("Executing du -s0 $zm_dir_events/$mid | awk '{print $1}'"); + $usage = shell_exec("du -s0 $zm_dir_events/$mid | awk '{print $1}'"); } else { $monitors = $this->Monitor->find('all', array( 'fields' => array('Id', 'Name', 'WebColour') diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 5ce4bb476..185a06c84 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -207,8 +207,10 @@ class MonitorsController extends AppController { if ( !$this->Monitor->exists() ) { throw new NotFoundException(__('Invalid monitor')); } - if ( $this->Session->Read('systemPermission') != 'Edit' ) { - throw new UnauthorizedException(__('Insufficient privileges')); + global $user; + $canEdit = (!$user) || ($user['System'] == 'Edit'); + if ( !$canEdit ) { + throw new UnauthorizedException(__('Insufficient privileges')); return; } $this->request->allowMethod('post', 'delete'); diff --git a/web/api/app/Controller/StatesController.php b/web/api/app/Controller/StatesController.php index 29201d2c1..b96efe0aa 100644 --- a/web/api/app/Controller/StatesController.php +++ b/web/api/app/Controller/StatesController.php @@ -59,8 +59,9 @@ public function add() { if ($this->request->is('post')) { - if ($this->Session->Read('systemPermission') != 'Edit') - { + global $user; + $canEdit = (!$user) || ($user['System'] == 'Edit'); + if ( !$canEdit ) { throw new UnauthorizedException(__('Insufficient privileges')); return; } diff --git a/web/api/app/Controller/StorageController.php b/web/api/app/Controller/StorageController.php new file mode 100644 index 000000000..16791788c --- /dev/null +++ b/web/api/app/Controller/StorageController.php @@ -0,0 +1,157 @@ +Storage->recursive = -1; + + $options = ''; + $storage_areas = $this->Storage->find('all',$options); + $this->set(array( + 'storage' => $storage_areas, + '_serialize' => array('storage') + )); + } + +/** + * view method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function view($id = null) { + $this->Storage->recursive = 0; + if (!$this->Storage->exists($id)) { + throw new NotFoundException(__('Invalid storage area')); + } + $restricted = ''; + + $options = array('conditions' => array( + array('Storage.' . $this->Storage->primaryKey => $id), + $restricted + ) + ); + $storage = $this->Storage->find('first', $options); + $this->set(array( + 'storage' => $storage, + '_serialize' => array('storage') + )); + } + +/** + * add method + * + * @return void + */ + public function add() { + if ( $this->request->is('post') ) { + + global $user; + $canEdit = (!$user) || ($user['System'] == 'Edit'); + if ( !$canEdit ) { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + + $this->Storage->create(); + if ( $this->Storage->save($this->request->data) ) { + # Might be nice to send it a start request + #$this->daemonControl($this->Storage->id, 'start', $this->request->data); + return $this->flash(__('The storage area has been saved.'), array('action' => 'index')); + } + } + } + +/** + * edit method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function edit($id = null) { + $this->Storage->id = $id; + + global $user; + $canEdit = (!$user) || ($user['System'] == 'Edit'); + if ( !$canEdit ) { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + + if ( !$this->Storage->exists($id) ) { + throw new NotFoundException(__('Invalid storage area')); + } + if ( $this->Storage->save($this->request->data) ) { + $message = 'Saved'; + } else { + $message = 'Error'; + } + + $this->set(array( + 'message' => $message, + '_serialize' => array('message') + )); + // - restart this storage area after change + #$this->daemonControl($this->Storage->id, 'restart', $this->request->data); + } + +/** + * delete method + * + * @throws NotFoundException + * @param string $id + * @return void + */ + public function delete($id = null) { + global $user; + $canEdit = (!$user) || ($user['System'] == 'Edit'); + if ( !$canEdit ) { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + + $this->Storage->id = $id; + if ( !$this->Storage->exists() ) { + throw new NotFoundException(__('Invalid storage area')); + } + $this->request->allowMethod('post', 'delete'); + + #$this->daemonControl($this->Storage->id, 'stop'); + + if ( $this->Storage->delete() ) { + return $this->flash(__('The storage area has been deleted.'), array('action' => 'index')); + } else { + return $this->flash(__('The storage area could not be deleted. Please, try again.'), array('action' => 'index')); + } + } +} diff --git a/web/api/app/Model/Monitor.php b/web/api/app/Model/Monitor.php index 98ea21841..2e6584794 100644 --- a/web/api/app/Model/Monitor.php +++ b/web/api/app/Model/Monitor.php @@ -110,14 +110,21 @@ class Monitor extends AppModel { ); public $actsAs = array( 'CakePHP-Enum-Behavior.Enum' => array( - 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL'), + 'Type' => array('Local','Remote','File','Ffmpeg','Libvlc','cURL','WebSite'), 'Function' => array('None','Monitor','Modect','Record','Mocord','Nodect'), 'Orientation' => array('0','90','180','270','hori','vert'), 'OutputCodec' => array('h264','mjpeg','mpeg1','mpeg2'), 'OutputContainer' => array('auto','mp4','mkv'), 'DefaultView' => array('Events','Control'), - 'Status' => array('Unknown','NotRunning','Running','NoSignal','Signal'), + #'Status' => array('Unknown','NotRunning','Running','NoSignal','Signal'), ) ); + public $hasOne = array( + 'Monitor_Status' => array( + 'className' => 'Monitor_Status', + 'foreignKey' => 'MonitorId', + 'joinTable' => 'Monitor_Status', + ) + ); } diff --git a/web/api/app/Model/Monitor_Status.php b/web/api/app/Model/Monitor_Status.php new file mode 100644 index 000000000..40f4aa2c2 --- /dev/null +++ b/web/api/app/Model/Monitor_Status.php @@ -0,0 +1,59 @@ + 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 + ), + ), + ); + + public $actsAs = array( + 'CakePHP-Enum-Behavior.Enum' => array( + 'Status' => array('Unknown','NotRunning','Running','NoSignal','Signal'), + ) + ); + + //The Associations below have been created with all possible keys, those that are not needed can be removed +} diff --git a/web/api/lib/Cake/Network/CakeResponse.php b/web/api/lib/Cake/Network/CakeResponse.php index 21dfd7b2e..982398e87 100644 --- a/web/api/lib/Cake/Network/CakeResponse.php +++ b/web/api/lib/Cake/Network/CakeResponse.php @@ -1168,6 +1168,9 @@ class CakeResponse { if ($modifiedSince) { $timeMatches = strtotime($this->modified()) === strtotime($modifiedSince); } + if (!isset($etagMatches, $timeMatches)) { + return false; + } $checks = compact('etagMatches', 'timeMatches'); if (empty($checks)) { return false; diff --git a/web/css/overlay.css b/web/css/overlay.css index de7ed5ecb..28714ebf5 100644 --- a/web/css/overlay.css +++ b/web/css/overlay.css @@ -19,7 +19,7 @@ .overlayHeader { float: left; - background-color: #dddddd; + background-color: #853131; width: 100%; border-bottom: 1px solid #666666; color: black; diff --git a/web/fonts/license.md b/web/fonts/license.md new file mode 100644 index 000000000..197f20b13 --- /dev/null +++ b/web/fonts/license.md @@ -0,0 +1,31 @@ + +ZoneMinder uses certain 3rd party media assets/libraries for UI display purposes. Their licenses are listed in this file + +### Material Design icons + +Origin: https://github.com/google/material-design-icons + +License: Apache 2.0 (https://github.com/google/material-design-icons/blob/master/LICENSE) + +### Glyphicon halflings font + +Origin: http://www.glyphicons.com/ + +License: MIT, As clarified below (http://www.glyphicons.com/license/) + +``` +License for GLYPHICONS Halflings in Bootstrap + +GLYPHICONS Halflings font is also released as an extension of a Bootstrap www.getbootstrap.com for free and +it is released under the same license as Bootstrap. While you are not required to include attribution on your +Bootstrap-based projects, I would certainly appreciate any form of support, even a nice Tweet is enough. +Of course if you want, you can say thank you and support me by buying more icons on GLYPHICONS.com. + +Jan Kovařík +``` + +ZoneMinder uses Bootstrap for UI and qualifies as a Bootstrap-based project. +Bootstrap is MIT licensed (https://github.com/twbs/bootstrap/blob/v4.1.3/LICENSE) + + + diff --git a/web/includes/Control.php b/web/includes/Control.php index 74beeca05..5d8647995 100644 --- a/web/includes/Control.php +++ b/web/includes/Control.php @@ -166,8 +166,7 @@ private $defaults = array( } } } - public static function find_all( $parameters = null, $options = null ) { - $filters = array(); + public static function find( $parameters = null, $options = null ) { $sql = 'SELECT * FROM Controls '; $values = array(); @@ -189,15 +188,37 @@ private $defaults = array( } $sql .= implode(' AND ', $fields ); } - if ( $options and isset($options['order']) ) { - $sql .= ' ORDER BY ' . $options['order']; + if ( $options ) { + if ( isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } + if ( isset($options['limit']) ) { + if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) { + $sql .= ' LIMIT ' . $options['limit']; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Error("Invalid value for limit(".$options['limit'].") passed to Control::find from $file:$line"); + return; + } + } } + $controls = array(); $result = dbQuery($sql, $values); $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Control'); foreach ( $results as $row => $obj ) { - $filters[] = $obj; + $controls[] = $obj; } - return $filters; + return $controls; + } + + public static function find_one( $parameters = array() ) { + $results = Control::find( $parameters, array('limit'=>1) ); + if ( ! sizeof($results) ) { + return; + } + return $results[0]; } public function save( $new_values = null ) { diff --git a/web/includes/Event.php b/web/includes/Event.php index 9c00b3670..2046f5ddf 100644 --- a/web/includes/Event.php +++ b/web/includes/Event.php @@ -84,7 +84,12 @@ class Event { } public function Monitor() { - return new Monitor( isset($this->{'MonitorId'}) ? $this->{'MonitorId'} : NULL ); + if ( isset($this->{'MonitorId'}) ) { + $Monitor = Monitor::find_one(array('Id'=>$this->{'MonitorId'})); + if ( $Monitor ) + return $Monitor; + } + return new Monitor(); } public function __call( $fn, array $args){ @@ -94,10 +99,14 @@ class Event { if ( array_key_exists( $fn, $this ) ) { return $this->{$fn}; - $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - Warning("Unknown function call Event->$fn from $file:$line"); + $backTrace = debug_backtrace(); + $file = $backTrace[0]['file']; + $line = $backTrace[0]['line']; + Warning("Unknown function call Event->$fn from $file:$line"); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Warning("Unknown function call Event->$fn from $file:$line"); + Warning(print_r( $this, true )); } } @@ -190,14 +199,22 @@ class Event { public function getStreamSrc( $args=array(), $querySep='&' ) { $streamSrc = ''; + $Server = null; if ( $this->Storage()->ServerId() ) { + # The Event may have been moved to Storage on another server, + # So prefer viewing the Event from the Server that is actually + # storing the video $Server = $this->Storage()->Server(); } else if ( $this->Monitor()->ServerId() ) { # Assume that the server that recorded it has it $Server = $this->Monitor()->Server(); } else { - $Server = new Server; + # A default Server will result in the use of ZM_DIR_EVENTS + $Server = new Server(); } + + # If we are in a multi-port setup, then use the multiport, else by + # passing null Server->Url will use the Port set in the Server setting $streamSrc .= $Server->Url( ZM_MIN_STREAMING_PORT ? ZM_MIN_STREAMING_PORT+$this->{'MonitorId'} : @@ -223,10 +240,10 @@ class Event { if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { $args['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS); - } elseif ( ZM_AUTH_RELAY == 'plain' ) { + } else if ( ZM_AUTH_RELAY == 'plain' ) { $args['user'] = $_SESSION['username']; $args['pass'] = $_SESSION['password']; - } elseif ( ZM_AUTH_RELAY == 'none' ) { + } else if ( ZM_AUTH_RELAY == 'none' ) { $args['user'] = $_SESSION['username']; } } @@ -240,7 +257,7 @@ class Event { if ( is_null($new) or ( $new != '' ) ) { $this->{'DiskSpace'} = $new; } - if ( null === $this->{'DiskSpace'} ) { + if ( (!array_key_exists('DiskSpace',$this)) or (null === $this->{'DiskSpace'}) ) { $this->{'DiskSpace'} = folder_size($this->Path()); dbQuery('UPDATE Events SET DiskSpace=? WHERE Id=?', array($this->{'DiskSpace'}, $this->{'Id'})); } @@ -314,13 +331,14 @@ class Event { # We always store at least 1 image when capturing $streamSrc = ''; + $Server = null; if ( $this->Storage()->ServerId() ) { $Server = $this->Storage()->Server(); } else if ( $this->Monitor()->ServerId() ) { # Assume that the server that recorded it has it $Server = $this->Monitor()->Server(); } else { - $Server = new Server; + $Server = new Server(); } $streamSrc .= $Server->Url( ZM_MIN_STREAMING_PORT ? @@ -337,10 +355,10 @@ class Event { if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { $args['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS); - } elseif ( ZM_AUTH_RELAY == 'plain' ) { + } else if ( ZM_AUTH_RELAY == 'plain' ) { $args['user'] = $_SESSION['username']; $args['pass'] = $_SESSION['password']; - } elseif ( ZM_AUTH_RELAY == 'none' ) { + } else if ( ZM_AUTH_RELAY == 'none' ) { $args['user'] = $_SESSION['username']; } } @@ -474,7 +492,7 @@ class Event { isset($event_cache[$parameters['Id']]) ) { return $event_cache[$parameters['Id']]; } - $results = Event::find_all( $parameters, $options ); + $results = Event::find( $parameters, $options ); if ( count($results) > 1 ) { Error("Event Returned more than 1"); return $results[0]; @@ -485,8 +503,7 @@ class Event { } } - public static function find_all( $parameters = null, $options = null ) { - $filters = array(); + public static function find( $parameters = null, $options = null ) { $sql = 'SELECT * FROM Events '; $values = array(); @@ -508,13 +525,29 @@ class Event { } $sql .= implode(' AND ', $fields ); } - if ( $options and isset($options['order']) ) { - $sql .= ' ORDER BY ' . $options['order']; + if ( $options ) { + if ( isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } + if ( isset($options['limit']) ) { + if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) { + $sql .= ' LIMIT ' . $options['limit']; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Error("Invalid value for limit(".$options['limit'].") passed to Event::find from $file:$line"); + return array(); + } + } } + $filters = array(); $result = dbQuery($sql, $values); - $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Event'); - foreach ( $results as $row => $obj ) { - $filters[] = $obj; + if ( $result ) { + $results = $result->fetchALL(); + foreach ( $results as $row ) { + $filters[] = new Event($row); + } } return $filters; } diff --git a/web/includes/Filter.php b/web/includes/Filter.php index 721302edb..ac2ebb61a 100644 --- a/web/includes/Filter.php +++ b/web/includes/Filter.php @@ -11,6 +11,7 @@ public $defaults = array( 'AutoDelete' => 0, 'AutoArchive' => 0, 'AutoVideo' => 0, + 'AutoUpload' => 0, 'AutoMessage' => 0, 'AutoMove' => 0, 'AutoMoveTo' => 0, @@ -26,12 +27,12 @@ public $defaults = array( public function __construct( $IdOrRow=NULL ) { $row = NULL; if ( $IdOrRow ) { - if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { - $row = dbFetchOne( 'SELECT * FROM Filters WHERE Id=?', NULL, array( $IdOrRow ) ); + if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) { + $row = dbFetchOne('SELECT * FROM Filters WHERE Id=?', NULL, array($IdOrRow)); if ( ! $row ) { - Error('Unable to load Filter record for Id=' . $IdOrRow ); + Error('Unable to load Filter record for Id=' . $IdOrRow); } - } elseif ( is_array( $IdOrRow ) ) { + } elseif ( is_array($IdOrRow) ) { $row = $IdOrRow; } else { $backTrace = debug_backtrace(); @@ -47,8 +48,8 @@ public $defaults = array( foreach ($row as $k => $v) { $this->{$k} = $v; } - if ( array_key_exists( 'Query', $this ) and $this->{'Query'} ) { - $this->{'Query'} = jsonDecode( $this->{'Query'} ); + if ( array_key_exists('Query', $this) and $this->{'Query'} ) { + $this->{'Query'} = jsonDecode($this->{'Query'}); } else { $this->{'Query'} = array(); } @@ -111,18 +112,62 @@ public $defaults = array( return $this->defaults{'limit'}; } - public static function find_all() { + public static function find( $parameters = null, $options = null ) { $filters = array(); - $result = dbQuery( 'SELECT * FROM Filters ORDER BY Name'); - $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Filter' ); + $sql = 'SELECT * FROM Filters '; + $values = array(); + + if ( $parameters ) { + $fields = array(); + $sql .= 'WHERE '; + foreach ( $parameters as $field => $value ) { + if ( $value == null ) { + $fields[] = $field.' IS NULL'; + } else if ( is_array( $value ) ) { + $func = function(){return '?';}; + $fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')'; + $values += $value; + } else { + $fields[] = $field.'=?'; + $values[] = $value; + } + } + $sql .= implode(' AND ', $fields); + } + if ( $options ) { + if ( isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } + if ( isset($options['limit']) ) { + if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) { + $sql .= ' LIMIT ' . $options['limit']; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Error("Invalid value for limit(".$options['limit'].") passed to Filter::find from $file:$line"); + return array(); + } + } + } + $result = dbQuery($sql, $values); + $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Filter'); foreach ( $results as $row => $obj ) { $filters[] = $obj; } return $filters; - } + } # end find() + + public static function find_one( $parameters = array() ) { + $results = Filter::find($parameters, array('limit'=>1)); + if ( ! sizeof($results) ) { + return; + } + return $results[0]; + } # end find_one() public function delete() { - dbQuery( 'DELETE FROM Filters WHERE Id = ?', array($this->{'Id'}) ); + dbQuery('DELETE FROM Filters WHERE Id = ?', array($this->{'Id'})); } # end function delete() public function set( $data ) { @@ -140,9 +185,7 @@ public $defaults = array( $this->{$k} = $v; } } - } - - -} # end class + } # end function set +} # end class Filter ?> diff --git a/web/includes/Frame.php b/web/includes/Frame.php index 82c9d2daf..74a18ef59 100644 --- a/web/includes/Frame.php +++ b/web/includes/Frame.php @@ -55,7 +55,7 @@ class Frame { #return $_SERVER['PHP_SELF'].'?view=image&fid='.$this->{'Id'}.'&show='.$show.'&filename='.$this->Event()->MonitorId().'_'.$this->{'EventId'}.'_'.$this->{'FrameId'}.'.jpg'; } // end function getImageSrc - public static function find( $parameters = array(), $limit = NULL ) { + public static function find( $parameters = array(), $options = NULL ) { $sql = 'SELECT * FROM Frames'; $values = array(); if ( sizeof($parameters) ) { @@ -65,17 +65,23 @@ class Frame { ) ); $values = array_values( $parameters ); } - if ( $limit ) { - if ( is_integer( $limit ) or ctype_digit( $limit ) ) { - $sql .= ' LIMIT ' . $limit; - } else { - $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - Error("Invalid value for limit($limit) passed to Frame::find from $file:$line"); - return array(); - } - } + if ( $options ) { + if ( isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } + if ( isset($options['limit']) ) { + if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) { + $sql .= ' LIMIT ' . $options['limit']; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Error("Invalid value for limit(".$options['limit'].") passed to Frame::find from $file:$line"); + return array(); + } + } + } + $results = dbFetchAll($sql, NULL, $values); if ( $results ) { return array_map( function($id){ return new Frame($id); }, $results ); @@ -83,8 +89,9 @@ class Frame { return array(); } - public static function find_one( $parameters = array() ) { - $results = Frame::find( $parameters, 1 ); + public static function find_one( $parameters = array(), $options = null ) { + $options['limit'] = 1; + $results = Frame::find($parameters, $options); if ( ! sizeof($results) ) { return; } diff --git a/web/includes/Group.php b/web/includes/Group.php index 441eff70e..6c8338a55 100644 --- a/web/includes/Group.php +++ b/web/includes/Group.php @@ -58,27 +58,7 @@ class Group { } } - public static function find_one($parameters = null, $options = null) { - global $group_cache; - if ( - ( count($parameters) == 1 ) and - isset($parameters['Id']) and - isset($group_cache[$parameters['Id']]) ) { - return $group_cache[$parameters['Id']]; - } - $results = Group::find_all($parameters, $options); - if ( count($results) > 1 ) { - Error("Group::find_one Returned more than 1"); - return $results[0]; - } else if ( count($results) ) { - return $results[0]; - } else { - return null; - } - } - - public static function find_all( $parameters = null ) { - $filters = array(); + public static function find( $parameters = null, $options = null ) { $sql = 'SELECT * FROM Groups '; $values = array(); @@ -90,22 +70,56 @@ class Group { $fields[] = $field.' IS NULL'; } else if ( is_array( $value ) ) { $func = function(){return '?';}; - $fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')'; + $fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')'; $values += $value; } else { $fields[] = $field.'=?'; $values[] = $value; } } - $sql .= implode(' AND ', $fields ); + $sql .= implode(' AND ', $fields); + } # end if parameters + if ( $options ) { + if ( isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } + if ( isset($options['limit']) ) { + if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) { + $sql .= ' LIMIT ' . $options['limit']; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Error("Invalid value for limit(".$options['limit'].") passed to Group::find from $file:$line"); + return array(); + } + } + } # end if options + + $results = dbFetchAll($sql, NULL, $values); + if ( $results ) { + return array_map( function($row){ return new Group($row); }, $results ); } - $sql .= ' ORDER BY Name'; - $result = dbQuery($sql, $values); - $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Group'); - foreach ( $results as $row => $obj ) { - $filters[] = $obj; + return array(); + } # end find() + + public static function find_one($parameters = null, $options = null) { + global $group_cache; + if ( + ( count($parameters) == 1 ) and + isset($parameters['Id']) and + isset($group_cache[$parameters['Id']]) ) { + return $group_cache[$parameters['Id']]; + } + $results = Group::find($parameters, $options); + if ( count($results) > 1 ) { + Error("Group::find_one Returned more than 1"); + return $results[0]; + } else if ( count($results) ) { + return $results[0]; + } else { + return null; } - return $filters; } public function delete() { @@ -182,7 +196,7 @@ class Group { public static function get_dropdown_options() { $Groups = array(); - foreach ( Group::find_all( ) as $Group ) { + foreach ( Group::find( ) as $Group ) { $Groups[$Group->Id()] = $Group; } diff --git a/web/includes/Monitor.php b/web/includes/Monitor.php index 13cad6d1e..500ae171f 100644 --- a/web/includes/Monitor.php +++ b/web/includes/Monitor.php @@ -2,25 +2,102 @@ require_once('database.php'); require_once('Server.php'); +$monitor_cache = array(); + class Monitor { private $defaults = array( 'Id' => null, 'Name' => '', - 'StorageId' => 0, 'ServerId' => 0, + 'StorageId' => 0, 'Type' => 'Ffmpeg', 'Function' => 'None', 'Enabled' => 1, 'LinkedMonitors' => null, + 'Triggers' => null, + 'Device' => '', + 'Channel' => 0, + 'Format' => '0', + 'V4LMultiBuffer' => null, + 'V4LCapturesPerFrame' => null, + 'Protocol' => null, + 'Method' => '', + 'Host' => null, + 'Port' => '', + 'SubPath' => '', + 'Path' => null, + 'Options' => null, + 'User' => null, + 'Pass' => null, + // These are NOT NULL default 0 in the db, but 0 is not a valid value. FIXME 'Width' => null, 'Height' => null, + 'Colours' => 1, + 'Palette' => '0', 'Orientation' => null, + 'Deinterlacing' => 0, + 'SaveJPEGs' => 3, + 'VideoWriter' => '0', + 'OutputCodec' => null, + 'OutputContainer' => null, + 'EncoderParameters' => null, + 'RecordAudio' => 0, + 'RTSPDescribe' => null, + 'Brightness' => -1, + 'Contrast' => -1, + 'Hue' => -1, + 'Colour' => -1, + 'EventPrefix' => 'Event-', + 'LabelFormat' => null, + 'LabelX' => 0, + 'LabelY' => 0, + 'LabelSize' => 1, + 'ImageBufferCount' => 100, + 'WarmupCount' => 0, + 'PreEventCount' => 0, + 'PostEventCount' => 0, + 'StreamReplayBuffer' => 0, + 'AlarmFrameCount' => 1, + 'SectionLength' => 600, + 'FrameSkip' => 0, 'AnalysisFPSLimit' => null, - 'ZoneCount' => 0, - 'Triggers' => null, + 'AnalysisUpdateDelete' => 0, 'MaxFPS' => null, 'AlarmMaxFPS' => null, + 'FPSReportIneterval' => 100, + 'RefBlencPerc' => 6, + 'AlarmRefBlendPerc' => 6, + 'Controllable' => 0, + 'ControlId' => null, + 'ControlDevice' => null, + 'ControlAddress' => null, + 'AutoStopTimeout' => null, + 'TrackMotion' => 0, + 'TrackDelay' => null, + 'ReturnLocation' => -1, + 'ReturnDelay' => null, + 'DefaultView' => 'Events', + 'DefaultRate' => 100, + 'DefaultScale' => 100, + 'SignalCheckPoints' => 0, + 'SignalCheckColour' => '#0000BE', + 'WebColour' => 'red', + 'Exif' => 0, + 'Sequence' => null, + 'TotalEvents' => null, + 'TotalEventDiskSpace' => null, + 'HourEvents' => null, + 'HourEventDiskSpace' => null, + 'DayEvents' => null, + 'DayEventDiskSpace' => null, + 'WeekEvents' => null, + 'WeekEventDiskSpace' => null, + 'MonthEvents' => null, + 'MonthEventDiskSpace' => null, + 'ArchivedEvents' => null, + 'ArchivedEventDiskSpace' => null, + 'ZoneCount' => 0, 'Refresh' => null, ); private $status_fields = array( @@ -150,21 +227,27 @@ private $control_fields = array( } if ( $this->{'Controllable'} ) { $s = dbFetchOne('SELECT * FROM Controls WHERE Id=?', NULL, array($this->{'ControlId'}) ); - foreach ($s as $k => $v) { - if ( $k == 'Id' ) { - continue; -# The reason for these is that the name overlaps Monitor fields. - } else if ( $k == 'Protocol' ) { - $this->{'ControlProtocol'} = $v; - } else if ( $k == 'Name' ) { - $this->{'ControlName'} = $v; - } else if ( $k == 'Type' ) { - $this->{'ControlType'} = $v; - } else { - $this->{$k} = $v; + if ( $s ) { + foreach ($s as $k => $v) { + if ( $k == 'Id' ) { + continue; + # The reason for these is that the name overlaps Monitor fields. + } else if ( $k == 'Protocol' ) { + $this->{'ControlProtocol'} = $v; + } else if ( $k == 'Name' ) { + $this->{'ControlName'} = $v; + } else if ( $k == 'Type' ) { + $this->{'ControlType'} = $v; + } else { + $this->{$k} = $v; + } } + } else { + Warning('No Controls found for monitor '.$this->{'Id'} . ' ' . $this->{'Name'}.' althrough it is marked as controllable'); } } + global $monitor_cache; + $monitor_cache[$row['Id']] = $this; } else { Error('No row for Monitor ' . $IdOrRow); @@ -271,8 +354,7 @@ private $control_fields = array( } # end if method_exists } # end foreach $data as $k=>$v } - public static function find_all( $parameters = null, $options = null ) { - $filters = array(); + public static function find( $parameters = null, $options = null ) { $sql = 'SELECT * FROM Monitors '; $values = array(); @@ -282,9 +364,9 @@ private $control_fields = array( foreach ( $parameters as $field => $value ) { if ( $value == null ) { $fields[] = $field.' IS NULL'; - } else if ( is_array( $value ) ) { + } else if ( is_array($value) ) { $func = function(){return '?';}; - $fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')'; + $fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')'; $values += $value; } else { @@ -292,18 +374,47 @@ private $control_fields = array( $values[] = $value; } } - $sql .= implode(' AND ', $fields ); + $sql .= implode(' AND ', $fields); } - if ( $options and isset($options['order']) ) { - $sql .= ' ORDER BY ' . $options['order']; + if ( $options ) { + if ( isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } + if ( isset($options['limit']) ) { + if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) { + $sql .= ' LIMIT ' . $options['limit']; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Error("Invalid value for limit(".$options['limit'].") passed to Control::find from $file:$line"); + return array(); + } + } } + $monitors = array(); $result = dbQuery($sql, $values); - $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Monitor'); - foreach ( $results as $row => $obj ) { - $filters[] = $obj; + $results = $result->fetchALL(); + foreach ( $results as $row ) { + $monitors[] = new Monitor($row); } - return $filters; - } + return $monitors; + } # end find + + public static function find_one( $parameters = array() ) { + global $monitor_cache; + if ( + ( count($parameters) == 1 ) and + isset($parameters['Id']) and + isset($monitor_cache[$parameters['Id']]) ) { + return $monitor_cache[$parameters['Id']]; + } + $results = Monitor::find( $parameters, array('limit'=>1) ); + if ( ! sizeof($results) ) { + return; + } + return $results[0]; + } # end find_one public function save($new_values = null) { @@ -342,7 +453,7 @@ private $control_fields = array( } else if ( $this->ServerId() ) { $Server = $this->Server(); - $url = $Server->Url() . '/zm/api/monitors/'.$this->{'Id'}.'.json'; + $url = ZM_BASE_PROTOCOL . '://'.$Server->Hostname().'/zm/api/monitors/'.$this->{'Id'}.'.json'; if ( ZM_OPT_USE_AUTH ) { if ( ZM_AUTH_RELAY == 'hashed' ) { $url .= '?auth='.generateAuthHash( ZM_AUTH_HASH_IPS ); @@ -482,9 +593,14 @@ private $control_fields = array( $source = preg_replace( '/^.*\//', '', $this->{'Path'} ); } elseif ( $this->{'Type'} == 'Ffmpeg' || $this->{'Type'} == 'Libvlc' || $this->{'Type'} == 'WebSite' ) { $url_parts = parse_url( $this->{'Path'} ); - if ( ZM_WEB_FILTER_SOURCE == 'Hostname' ) { # Filter out everything but the hostname - $source = $url_parts['host']; - } elseif ( ZM_WEB_FILTER_SOURCE == 'NoCredentials' ) { + if ( ZM_WEB_FILTER_SOURCE == 'Hostname' ) { + # Filter out everything but the hostname + if ( isset($url_parts['host']) ) { + $source = $url_parts['host']; + } else { + $source = $this->{'Path'}; + } + } elseif ( ZM_WEB_FILTER_SOURCE == "NoCredentials" ) { # Filter out sensitive and common items unset($url_parts['user']); unset($url_parts['pass']); @@ -503,5 +619,10 @@ private $control_fields = array( } return $source; } // end function Source + + public function Url() { + return $this->Server()->Url( ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null ); + } + } // end class Monitor ?> diff --git a/web/includes/Server.php b/web/includes/Server.php index 591b26f90..7d0b5f436 100644 --- a/web/includes/Server.php +++ b/web/includes/Server.php @@ -1,11 +1,15 @@ null, 'Name' => '', + 'Protocol' => '', 'Hostname' => '', + 'Port' => null, 'PathPrefix' => '/zm', 'zmaudit' => 1, 'zmstats' => 1, @@ -13,6 +17,7 @@ class Server { ); public function __construct($IdOrRow = NULL) { + global $server_cache; $row = NULL; if ( $IdOrRow ) { if ( is_integer($IdOrRow) or ctype_digit($IdOrRow) ) { @@ -28,51 +33,17 @@ class Server { foreach ($row as $k => $v) { $this->{$k} = $v; } + $server_cache[$row['Id']] = $this; } else { # Set defaults foreach ( $this->defaults as $k => $v ) $this->{$k} = $v; } } - public static function find_all($parameters = null, $options = null) { - $filters = array(); - $sql = 'SELECT * FROM Servers '; - $values = array(); + public function Hostname( $new = null ) { + if ( $new != null ) + $this->{'Hostname'} = $new; - if ( $parameters ) { - $fields = array(); - $sql .= 'WHERE '; - foreach ( $parameters as $field => $value ) { - if ( $value == null ) { - $fields[] = $field.' IS NULL'; - } else if ( is_array($value) ) { - $func = function(){return '?';}; - $fields[] = $field.' IN ('.implode(',', array_map($func, $value) ). ')'; - $values += $value; - - } else { - $fields[] = $field.'=?'; - $values[] = $value; - } - } - $sql .= implode(' AND ', $fields); - } - if ( $options and isset($options['order']) ) { - $sql .= ' ORDER BY ' . $options['order']; - } - $result = dbQuery($sql, $values); - $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Server'); - foreach ( $results as $row => $obj ) { - $filters[] = $obj; - } - return $filters; - } - - public function Url($port=null) { - return ZM_BASE_PROTOCOL.'://'.$this->Hostname().($port ? ':'.$port : '').$this->{'PathPrefix'}; - } - - public function Hostname() { if ( isset( $this->{'Hostname'}) and ( $this->{'Hostname'} != '' ) ) { return $this->{'Hostname'}; } else if ( $this->Id() ) { @@ -81,6 +52,52 @@ class Server { return $_SERVER['SERVER_NAME']; } + public function Protocol( $new = null ) { + if ( $new != null ) + $this->{'Protocol'} = $new; + + if ( isset($this->{'Protocol'}) and ( $this->{'Protocol'} != '' ) ) { + return $this->{'Protocol'}; + } + return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http'; + } + + public function Port( $new = '' ) { + if ( $new != '' ) + $this->{'Port'} = $new; + + if ( isset($this->{'Port'}) and $this->{'Port'} ) { + return $this->{'Port'}; + } + return $_SERVER['SERVER_PORT']; + } + + public function Url( $port = null ) { + $url = $this->Protocol().'://'; + if ( $this->Id() ) { + $url .= $this->Hostname(); + } else { + $url .= $_SERVER['SERVER_NAME']; + } + if ( $port ) { + $url .= ':'.$port; + } else { + $url .= ':'.$this->Port(); + } + $url .= $this->PathPrefix(); + return $url; + } + + public function PathPrefix( $new = null ) { + if ( $new != null ) + $this->{'PathPrefix'} = $new; + + if ( isset($this->{'PathPrefix'}) and $this->{'PathPrefix'} ) { + return $this->{'PathPrefix'}; + } + return $_SERVER['PHP_SELF']; + } + public function __call($fn, array $args){ if ( count($args) ) { $this->{$fn} = $args[0]; @@ -98,35 +115,62 @@ class Server { } } } - - public static function find($parameters = array(), $limit = NULL) { - $sql = 'SELECT * FROM Servers'; + public static function find( $parameters = null, $options = null ) { + $filters = array(); + $sql = 'SELECT * FROM Servers '; $values = array(); - if ( sizeof($parameters) ) { - $sql .= ' WHERE ' . implode(' AND ', array_map( - function($v){ return $v.'=?'; }, - array_keys($parameters) - )); - $values = array_values($parameters); + + if ( $parameters ) { + $fields = array(); + $sql .= 'WHERE '; + foreach ( $parameters as $field => $value ) { + if ( $value == null ) { + $fields[] = $field.' IS NULL'; + } else if ( is_array( $value ) ) { + $func = function(){return '?';}; + $fields[] = $field.' IN ('.implode(',', array_map( $func, $value ) ). ')'; + $values += $value; + + } else { + $fields[] = $field.'=?'; + $values[] = $value; + } + } + $sql .= implode(' AND ', $fields ); } - if ( is_integer($limit) or ctype_digit($limit) ) { - $sql .= ' LIMIT ' . $limit; - } else { - $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - Error("Invalid value for limit($limit) passed to Server::find from $file:$line"); - return; + if ( $options ) { + if ( isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } + if ( isset($options['limit']) ) { + if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) { + $sql .= ' LIMIT ' . $options['limit']; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Error("Invalid value for limit(".$options['limit'].") passed to Server::find from $file:$line"); + return array(); + } + } } - $results = dbFetchAll($sql, NULL, $values); + $results = dbFetchAll( $sql, NULL, $values ); if ( $results ) { return array_map(function($id){ return new Server($id); }, $results); } + return array(); } - public static function find_one($parameters = array()) { - $results = Server::find($parameters, 1); - if ( !sizeof($results) ) { + public static function find_one( $parameters = array() ) { + global $server_cache; + if ( + ( count($parameters) == 1 ) and + isset($parameters['Id']) and + isset($server_cache[$parameters['Id']]) ) { + return $server_cache[$parameters['Id']]; + } + $results = Server::find( $parameters, array('limit'=>1) ); + if ( ! sizeof($results) ) { return; } return $results[0]; diff --git a/web/includes/Storage.php b/web/includes/Storage.php index 70f2f831d..95f1dab84 100644 --- a/web/includes/Storage.php +++ b/web/includes/Storage.php @@ -3,15 +3,27 @@ require_once('database.php'); $storage_cache = array(); class Storage { + private $defaults = array( + 'Id' => null, + 'Path' => '', + 'Name' => '', + 'Type' => 'local', + 'Url' => '', + 'DiskSpace' => null, + 'Scheme' => 'Medium', + 'ServerId' => 0, + 'DoDelete' => 1, + ); + public function __construct( $IdOrRow = NULL ) { global $storage_cache; $row = NULL; if ( $IdOrRow ) { - if ( is_integer( $IdOrRow ) or is_numeric( $IdOrRow ) ) { - $row = dbFetchOne( 'SELECT * FROM Storage WHERE Id=?', NULL, array( $IdOrRow ) ); + if ( is_integer($IdOrRow) or is_numeric($IdOrRow) ) { + $row = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, array($IdOrRow)); if ( ! $row ) { - Error("Unable to load Storage record for Id=" . $IdOrRow ); + Error('Unable to load Storage record for Id=' . $IdOrRow); } } else if ( is_array($IdOrRow) ) { $row = $IdOrRow; @@ -40,7 +52,6 @@ class Storage { $this->{'Path'} = ZM_DIR_EVENTS; } return $this->{'Path'}; - } return $this->{'Name'}; } @@ -53,19 +64,25 @@ class Storage { return $this->{'Name'}; } - public function __call( $fn, array $args= NULL){ - if ( count( $args ) ) { + public function __call( $fn, array $args= NULL ) { + if ( count($args) ) { $this->{$fn} = $args[0]; } - if ( array_key_exists( $fn, $this ) ) { + if ( array_key_exists($fn, $this) ) return $this->{$fn}; - $backTrace = debug_backtrace(); - $file = $backTrace[1]['file']; - $line = $backTrace[1]['line']; - Warning( "Unknown function call Storage->$fn from $file:$line" ); - } + if ( array_key_exists( $fn, $this->defaults ) ) + return $this->defaults{$fn}; + + $backTrace = debug_backtrace(); + $file = $backTrace[0]['file']; + $line = $backTrace[0]['line']; + Warning("Unknown function call Storage->$fn from $file:$line"); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Warning("Unknown function call Storage->$fn from $file:$line"); } + public static function find_one( $parameters = null, $options = null ) { global $storage_cache; if ( @@ -74,7 +91,8 @@ class Storage { isset($storage_cache[$parameters['Id']]) ) { return $storage_cache[$parameters['Id']]; } - $results = Storage::find_all( $parameters, $options ); + + $results = Storage::find($parameters, $options); if ( count($results) > 1 ) { Error("Storage Returned more than 1"); return $results[0]; @@ -84,8 +102,7 @@ class Storage { return null; } } - public static function find_all( $parameters = null, $options = null ) { - $filters = array(); + public static function find( $parameters = null, $options = null ) { $sql = 'SELECT * FROM Storage '; $values = array(); @@ -95,7 +112,7 @@ class Storage { foreach ( $parameters as $field => $value ) { if ( $value == null ) { $fields[] = $field.' IS NULL'; - } else if ( is_array( $value ) ) { + } else if ( is_array($value) ) { $func = function(){return '?';}; $fields[] = $field.' IN ('.implode(',', array_map($func, $value)). ')'; $values += $value; @@ -106,32 +123,47 @@ class Storage { } } $sql .= implode(' AND ', $fields); - } - if ( $options and isset($options['order']) ) { - $sql .= ' ORDER BY ' . $options['order']; - } + } # end if parameters + if ( $options ) { + if ( isset($options['order']) ) { + $sql .= ' ORDER BY ' . $options['order']; + } # end if options + if ( isset($options['limit']) ) { + if ( is_integer($options['limit']) or ctype_digit($options['limit']) ) { + $sql .= ' LIMIT ' . $option['limit']; + } else { + $backTrace = debug_backtrace(); + $file = $backTrace[1]['file']; + $line = $backTrace[1]['line']; + Error("Invalid value for limit(".$options['limit'].") passed to Control::find from $file:$line"); + return array(); + } + } # end if limit + } # end if options + $storage_areas = array(); $result = dbQuery($sql, $values); if ( $result ) { - $results = $result->fetchALL(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'Storage'); - foreach ( $results as $row => $obj ) { - $filters[] = $obj; + $results = $result->fetchALL(); + foreach ( $results as $row ) { + $storage_areas[] = new Storage($row); } } - return $filters; - } + return $storage_areas; + } # end find() + public function disk_usage_percent() { $path = $this->Path(); if ( ! $path ) { - Warning("Storage::disk_usage_percent: path is empty"); + Warning('Storage::disk_usage_percent: path is empty'); return 0; - } else if ( ! file_exists( $path ) ) { + } else if ( ! file_exists($path) ) { Warning("Storage::disk_usage_percent: path $path does not exist"); return 0; } $total = $this->disk_total_space(); if ( ! $total ) { - Error("disk_total_space returned false for " . $path ); + Error('disk_total_space returned false for ' . $path ); return 0; } $used = $this->disk_used_space(); @@ -139,6 +171,7 @@ class Storage { //Logger::Debug("Used $usage = round( ( $used / $total ) * 100 )"); return $usage; } + public function disk_total_space() { if ( !array_key_exists('disk_total_space', $this) ) { $path = $this->Path(); @@ -156,7 +189,7 @@ class Storage { # This isn't a function like this in php, so we have to add up the space used in each event. if ( ( !array_key_exists('disk_used_space', $this)) or !$this->{'disk_used_space'} ) { if ( $this->{'Type'} == 's3fs' ) { - $this->{'disk_used_space'} = $this->disk_event_space(); + $this->{'disk_used_space'} = $this->event_disk_space(); } else { $path = $this->Path(); if ( file_exists($path) ) { @@ -175,8 +208,8 @@ class Storage { if ( (! array_key_exists('DiskSpace', $this)) or (!$this->{'DiskSpace'}) ) { $used = dbFetchOne('SELECT SUM(DiskSpace) AS DiskSpace FROM Events WHERE StorageId=? AND DiskSpace IS NOT NULL', 'DiskSpace', array($this->Id()) ); - foreach ( Event::find_all( array('StorageId'=>$this->Id(), 'DiskSpace'=>null) ) as $Event ) { - $Event->Storage( $this ); // Prevent further db hit + foreach ( Event::find(array('StorageId'=>$this->Id(), 'DiskSpace'=>null)) as $Event ) { + $Event->Storage($this); // Prevent further db hit $used += $Event->DiskSpace(); } $this->{'DiskSpace'} = $used; diff --git a/web/includes/actions.php b/web/includes/actions.php index a38515f3d..de0861fe0 100644 --- a/web/includes/actions.php +++ b/web/includes/actions.php @@ -29,16 +29,16 @@ function do_request($method, $url, $data=array(), $optional_headers = null) { 'method' => $method, 'content' => $data )); - if ($optional_headers !== null) { + if ( $optional_headers !== null ) { $params['http']['header'] = $optional_headers; } $ctx = stream_context_create($params); $fp = @fopen($url, 'rb', false, $ctx); - if (!$fp) { + if ( !$fp ) { throw new Exception("Problem with $url, $php_errormsg"); } $response = @stream_get_contents($fp); - if ($response === false) { + if ( $response === false ) { throw new Exception("Problem reading data from $url, $php_errormsg"); } return $response; @@ -49,16 +49,16 @@ function do_post_request($url, $data, $optional_headers = null) { 'method' => 'POST', 'content' => $data )); - if ($optional_headers !== null) { + if ( $optional_headers !== null ) { $params['http']['header'] = $optional_headers; } $ctx = stream_context_create($params); $fp = @fopen($url, 'rb', false, $ctx); - if (!$fp) { + if ( !$fp ) { throw new Exception("Problem with $url, $php_errormsg"); } $response = @stream_get_contents($fp); - if ($response === false) { + if ( $response === false ) { throw new Exception("Problem reading data from $url, $php_errormsg"); } return $response; @@ -106,17 +106,17 @@ if ( $action == 'login' && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 're $responseData = json_decode($res,true); // PP - credit: https://github.com/google/recaptcha/blob/master/src/ReCaptcha/Response.php // if recaptcha resulted in error, we might have to deny login - if (isset($responseData['success']) && $responseData['success'] == false) { + if ( isset($responseData['success']) && $responseData['success'] == false ) { // PP - before we deny auth, let's make sure the error was not 'invalid secret' // because that means the user did not configure the secret key correctly // in this case, we prefer to let him login in and display a message to correct // the key. Unfortunately, there is no way to check for invalid site key in code // as it produces the same error as when you don't answer a recaptcha - if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) { - if (!in_array('invalid-input-secret',$responseData['error-codes'])) { + if ( isset($responseData['error-codes']) && is_array($responseData['error-codes']) ) { + if ( !in_array('invalid-input-secret',$responseData['error-codes']) ) { Error('reCaptcha authentication failed'); userLogout(); - $view='login'; + $view = 'login'; $refreshParent = true; return; } else { @@ -140,19 +140,19 @@ if ( $action == 'login' && isset($_REQUEST['username']) && ( ZM_AUTH_TYPE == 're $view = 'none'; } else if ( $action == 'bandwidth' && isset($_REQUEST['newBandwidth']) ) { $_COOKIE['zmBandwidth'] = validStr($_REQUEST['newBandwidth']); - setcookie( 'zmBandwidth', validStr($_REQUEST['newBandwidth']), time()+3600*24*30*12*10 ); + setcookie('zmBandwidth', validStr($_REQUEST['newBandwidth']), time()+3600*24*30*12*10); $refreshParent = true; } // Event scope actions, view permissions only required if ( canView('Events') ) { - if ( isset( $_REQUEST['object'] ) and ( $_REQUEST['object'] == 'filter' ) ) { + if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) { if ( $action == 'addterm' ) { - $_REQUEST['filter'] = addFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] ); + $_REQUEST['filter'] = addFilterTerm($_REQUEST['filter'], $_REQUEST['line']); } elseif ( $action == 'delterm' ) { - $_REQUEST['filter'] = delFilterTerm( $_REQUEST['filter'], $_REQUEST['line'] ); - } else if ( canEdit( 'Events' ) ) { + $_REQUEST['filter'] = delFilterTerm($_REQUEST['filter'], $_REQUEST['line']); + } else if ( canEdit('Events') ) { if ( $action == 'delete' ) { if ( ! empty($_REQUEST['Id']) ) { dbQuery('DELETE FROM Filters WHERE Id=?', array($_REQUEST['Id'])); @@ -210,11 +210,13 @@ if ( canView('Events') ) { dbQuery('UPDATE Events SET Name=? WHERE Id=?', array($_REQUEST['eventName'], $_REQUEST['eid'])); } else if ( $action == 'eventdetail' ) { if ( !empty($_REQUEST['eid']) ) { - dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid'] ) ); + dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?', + array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $_REQUEST['eid']) ); } else { $dbConn->beginTransaction(); foreach( getAffectedIds('markEid') as $markEid ) { - dbQuery( 'UPDATE Events SET Cause=?, Notes=? WHERE Id=?', array( $_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid ) ); + dbQuery('UPDATE Events SET Cause=?, Notes=? WHERE Id=?', + array($_REQUEST['newEvent']['Cause'], $_REQUEST['newEvent']['Notes'], $markEid) ); } $dbConn->commit(); } @@ -226,7 +228,7 @@ if ( canView('Events') ) { dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $_REQUEST['eid'])); } else { $dbConn->beginTransaction(); - foreach( getAffectedIds( 'markEid' ) as $markEid ) { + foreach( getAffectedIds('markEid') as $markEid ) { dbQuery('UPDATE Events SET Archived=? WHERE Id=?', array($archiveVal, $markEid)); } $dbConn->commit(); @@ -234,8 +236,8 @@ if ( canView('Events') ) { } } elseif ( $action == 'delete' ) { $dbConn->beginTransaction(); - foreach( getAffectedIds( 'markEid' ) as $markEid ) { - deleteEvent( $markEid ); + foreach( getAffectedIds('markEid') as $markEid ) { + deleteEvent($markEid); } $dbConn->commit(); $refreshParent = true; @@ -245,45 +247,47 @@ if ( canView('Events') ) { } // end canView(Events) // Monitor control actions, require a monitor id and control view permissions for that monitor -if ( !empty($_REQUEST['mid']) && canView( 'Control', $_REQUEST['mid'] ) ) { - require_once( 'control_functions.php' ); - require_once( 'Monitor.php' ); +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 = new Monitor( $mid ); + $monitor = new Monitor($mid); - $ctrlCommand = buildControlCommand( $monitor ); - sendControlCommand( $monitor->Id(), $ctrlCommand ); - } elseif ( $action == 'settings' ) { + $ctrlCommand = buildControlCommand($monitor); + sendControlCommand($monitor->Id(), $ctrlCommand); + } else if ( $action == 'settings' ) { $args = ' -m ' . escapeshellarg($mid); $args .= ' -B' . escapeshellarg($_REQUEST['newBrightness']); $args .= ' -C' . escapeshellarg($_REQUEST['newContrast']); $args .= ' -H' . escapeshellarg($_REQUEST['newHue']); $args .= ' -O' . escapeshellarg($_REQUEST['newColour']); - $zmuCommand = getZmuCommand( $args ); + $zmuCommand = getZmuCommand($args); - $zmuOutput = exec( $zmuCommand ); - list( $brightness, $contrast, $hue, $colour ) = explode( ' ', $zmuOutput ); - dbQuery( 'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?', array($brightness, $contrast, $hue, $colour, $mid)); + $zmuOutput = exec($zmuCommand); + list($brightness, $contrast, $hue, $colour) = explode(' ', $zmuOutput); + dbQuery( + 'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?', + array($brightness, $contrast, $hue, $colour, $mid)); } } // Control capability actions, require control edit permissions if ( canEdit('Control') ) { if ( $action == 'controlcap' ) { - require_once( 'Control.php' ); + require_once('Control.php'); $Control = new Control( !empty($_REQUEST['cid']) ? $_REQUEST['cid'] : null ); //$changes = getFormChanges( $control, $_REQUEST['newControl'], $types, $columns ); - $Control->save( $_REQUEST['newControl'] ); + $Control->save($_REQUEST['newControl']); $refreshParent = true; $view = 'none'; } elseif ( $action == 'delete' ) { if ( isset($_REQUEST['markCids']) ) { foreach( $_REQUEST['markCids'] as $markCid ) { - dbQuery( 'delete from Controls where Id = ?', array($markCid) ); - dbQuery( 'update Monitors set Controllable = 0, ControlId = 0 where ControlId = ?', array($markCid) ); + dbQuery('DELETE FROM Controls WHERE Id = ?', array($markCid)); + dbQuery('UPDATE Monitors SET Controllable = 0, ControlId = 0 WHERE ControlId = ?', array($markCid)); $refreshParent = true; } } @@ -293,59 +297,59 @@ if ( canEdit('Control') ) { if ( isset($_REQUEST['object']) and $_REQUEST['object'] == 'Monitor' ) { if ( $action == 'save' ) { foreach ( $_REQUEST['mids'] as $mid ) { - $mid = ValidInt( $mid ); - if ( ! canEdit('Monitors', $mid ) ) { + $mid = ValidInt($mid); + if ( ! canEdit('Monitors', $mid) ) { Warning("Cannot edit monitor $mid"); continue; } - $Monitor = new Monitor( $mid ); + $Monitor = new Monitor($mid); if ( $Monitor->Type() != 'WebSite' ) { $Monitor->zmaControl('stop'); $Monitor->zmcControl('stop'); } - $Monitor->save( $_REQUEST['newMonitor'] ); - if ($Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) { + $Monitor->save($_REQUEST['newMonitor']); + if ( $Monitor->Function() != 'None' && $Monitor->Type() != 'WebSite' ) { $Monitor->zmcControl('start'); if ( $Monitor->Enabled() ) { $Monitor->zmaControl('start'); } } - } // end foreach mid $refreshParent = true; } // end if action == save } // end if object is Monitor // Monitor edit actions, require a monitor id and edit permissions for that monitor -if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) { +if ( !empty($_REQUEST['mid']) && canEdit('Monitors', $_REQUEST['mid']) ) { $mid = validInt($_REQUEST['mid']); if ( $action == 'function' ) { - $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) ); + $monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id=?', NULL, array($mid)); $newFunction = validStr($_REQUEST['newFunction']); # Because we use a checkbox, it won't get passed in the request. So not being in _REQUEST means 0 - $newEnabled = ( !isset( $_REQUEST['newEnabled'] ) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1'; + $newEnabled = ( !isset($_REQUEST['newEnabled']) or $_REQUEST['newEnabled'] != '1' ) ? '0' : '1'; $oldFunction = $monitor['Function']; $oldEnabled = $monitor['Enabled']; if ( $newFunction != $oldFunction || $newEnabled != $oldEnabled ) { - dbQuery( 'UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?', array( $newFunction, $newEnabled, $mid ) ); + dbQuery('UPDATE Monitors SET Function=?, Enabled=? WHERE Id=?', + array($newFunction, $newEnabled, $mid)); $monitor['Function'] = $newFunction; $monitor['Enabled'] = $newEnabled; - if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { + if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) { $restart = ($oldFunction == 'None') || ($newFunction == 'None') || ($newEnabled != $oldEnabled); - zmaControl( $monitor, 'stop' ); - zmcControl( $monitor, $restart?'restart':'' ); - zmaControl( $monitor, 'start' ); + zmaControl($monitor, 'stop'); + zmcControl($monitor, $restart?'restart':''); + zmaControl($monitor, 'start'); } $refreshParent = true; } - } elseif ( $action == 'zone' && isset( $_REQUEST['zid'] ) ) { + } else if ( $action == 'zone' && isset($_REQUEST['zid']) ) { $zid = validInt($_REQUEST['zid']); - $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) ); + $monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id=?', NULL, array($mid)); if ( !empty($zid) ) { - $zone = dbFetchOne( 'SELECT * FROM Zones WHERE MonitorId=? AND Id=?', NULL, array( $mid, $zid ) ); + $zone = dbFetchOne('SELECT * FROM Zones WHERE MonitorId=? AND Id=?', NULL, array($mid, $zid)); } else { $zone = array(); } @@ -365,74 +369,74 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) { unset( $_REQUEST['newZone']['Points'] ); $types = array(); - $changes = getFormChanges( $zone, $_REQUEST['newZone'], $types ); + $changes = getFormChanges($zone, $_REQUEST['newZone'], $types); - if ( count( $changes ) ) { + if ( count($changes) ) { if ( $zid > 0 ) { - dbQuery( 'UPDATE Zones SET '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zid) ); + dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?', array($mid, $zid)); } else { - dbQuery( 'INSERT INTO Zones SET MonitorId=?, '.implode( ', ', $changes ), array( $mid ) ); + dbQuery('INSERT INTO Zones SET MonitorId=?, '.implode(', ', $changes), array($mid)); } - if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { + if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) { if ( $_REQUEST['newZone']['Type'] == 'Privacy' ) { - zmaControl( $monitor, 'stop' ); - zmcControl( $monitor, 'restart' ); - zmaControl( $monitor, 'start' ); + zmaControl($monitor, 'stop'); + zmcControl($monitor, 'restart'); + zmaControl($monitor, 'start'); } else { - zmaControl( $mid, 'restart' ); + zmaControl($monitor, 'restart'); } } - if ( $_REQUEST['newZone']['Type'] == 'Privacy' && $monitor['Controllable'] ) { - require_once( 'control_functions.php' ); - sendControlCommand( $mid, 'quit' ); + if ( ($_REQUEST['newZone']['Type'] == 'Privacy') && $monitor['Controllable'] ) { + require_once('control_functions.php'); + sendControlCommand($mid, 'quit'); } $refreshParent = true; } $view = 'none'; - } elseif ( $action == 'plugin' && isset($_REQUEST['pl'])) { - $sql='SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?'; - $pconfs=dbFetchAll( $sql, NULL, array( $mid, $_REQUEST['zid'], $_REQUEST['pl'] ) ); - $changes=0; - foreach( $pconfs as $pconf ) { - $value=$_REQUEST['pluginOpt'][$pconf['Name']]; - if(array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value']!=$value)) { - dbQuery('UPDATE PluginsConfig SET Value=? WHERE id=?', array( $value, $pconf['Id'] ) ); + } elseif ( $action == 'plugin' && isset($_REQUEST['pl']) ) { + $sql = 'SELECT * FROM PluginsConfig WHERE MonitorId=? AND ZoneId=? AND pluginName=?'; + $pconfs=dbFetchAll($sql, NULL, array($mid, $_REQUEST['zid'], $_REQUEST['pl'])); + $changes = 0; + foreach ( $pconfs as $pconf ) { + $value = $_REQUEST['pluginOpt'][$pconf['Name']]; + if ( array_key_exists($pconf['Name'], $_REQUEST['pluginOpt']) && ($pconf['Value'] != $value) ) { + dbQuery('UPDATE PluginsConfig SET Value=? WHERE id=?', array($value, $pconf['Id'])); $changes++; } } - if($changes>0) { - if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { - zmaControl( $mid, 'restart' ); + if ( $changes > 0 ) { + if ( daemonCheck() && ($monitor['Type'] != 'WebSite') ) { + zmaControl($mid, 'restart'); } $refreshParent = true; } $view = 'none'; - } elseif ( $action == 'sequence' && isset($_REQUEST['smid']) ) { + } elseif ( ($action == 'sequence') && isset($_REQUEST['smid']) ) { $smid = validInt($_REQUEST['smid']); - $monitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($mid) ); - $smonitor = dbFetchOne( 'select * from Monitors where Id = ?', NULL, array($smid) ); + $monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($mid)); + $smonitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($smid)); - dbQuery( 'update Monitors set Sequence=? where Id=?', array( $smonitor['Sequence'], $monitor['Id'] ) ); - dbQuery( 'update Monitors set Sequence=? WHERE Id=?', array( $monitor['Sequence'], $smonitor['Id'] ) ); + dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($smonitor['Sequence'], $monitor['Id'])); + dbQuery('UPDATE Monitors SET Sequence=? WHERE Id=?', array($monitor['Sequence'], $smonitor['Id'])); $refreshParent = true; fixSequences(); } elseif ( $action == 'delete' ) { if ( isset($_REQUEST['markZids']) ) { $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) ); + 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; } if ( $deletedZid ) { if ( daemonCheck() && $monitor['Type'] != 'WebSite' ) { if ( $zone['Type'] == 'Privacy' ) { - zmaControl( $mid, 'stop' ); - zmcControl( $mid, 'restart' ); - zmaControl( $mid, 'start' ); + zmaControl($mid, 'stop'); + zmcControl($mid, 'restart'); + zmaControl($mid, 'start'); } else { - zmaControl( $mid, 'restart' ); + zmaControl($mid, 'restart'); } } // end if daemonCheck() $refreshParent = true; @@ -442,15 +446,15 @@ if ( !empty($_REQUEST['mid']) && canEdit( 'Monitors', $_REQUEST['mid'] ) ) { } // end if $mid and canEdit($mid) // Monitor edit actions, monitor id derived, require edit permissions for that monitor -if ( canEdit( 'Monitors' ) ) { +if ( canEdit('Monitors') ) { if ( $action == 'monitor' ) { $mid = 0; if ( !empty($_REQUEST['mid']) ) { $mid = validInt($_REQUEST['mid']); - $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id=?', NULL, array($mid) ); + $monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id=?', NULL, array($mid)); if ( ZM_OPT_X10 ) { - $x10Monitor = dbFetchOne( 'SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid) ); + $x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId=?', NULL, array($mid)); if ( !$x10Monitor ) $x10Monitor = array(); } @@ -476,18 +480,19 @@ if ( canEdit( 'Monitors' ) ) { ); if ( $_REQUEST['newMonitor']['ServerId'] == 'auto' ) { - $_REQUEST['newMonitor']['ServerId'] = dbFetchOne('SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id'); - Logger::Debug("Auto selecting server: Got " . $_REQUEST['newMonitor']['ServerId'] ); + $_REQUEST['newMonitor']['ServerId'] = dbFetchOne( + 'SELECT Id FROM Servers WHERE Status=\'Running\' ORDER BY FreeMem DESC, CpuLoad ASC LIMIT 1', 'Id'); + Logger::Debug('Auto selecting server: Got ' . $_REQUEST['newMonitor']['ServerId'] ); if ( ( ! $_REQUEST['newMonitor'] ) and defined('ZM_SERVER_ID') ) { $_REQUEST['newMonitor']['ServerId'] = ZM_SERVER_ID; - Logger::Debug("Auto selecting server to " . ZM_SERVER_ID); + Logger::Debug('Auto selecting server to ' . ZM_SERVER_ID); } } $columns = getTableColumns('Monitors'); $changes = getFormChanges($monitor, $_REQUEST['newMonitor'], $types, $columns); - if ( count( $changes ) ) { + if ( count($changes) ) { if ( $mid ) { # If we change anything that changes the shared mem size, zma can complain. So let's stop first. @@ -495,19 +500,19 @@ if ( canEdit( 'Monitors' ) ) { zmaControl($monitor, 'stop'); zmcControl($monitor, 'stop'); } - dbQuery( 'UPDATE Monitors SET '.implode( ', ', $changes ).' WHERE Id=?', array($mid) ); + dbQuery('UPDATE Monitors SET '.implode(', ', $changes).' WHERE Id=?', array($mid)); // Groups will be added below if ( isset($changes['Name']) or isset($changes['StorageId']) ) { - $OldStorage = new Storage( $monitor['StorageId'] ); - $saferOldName = basename( $monitor['Name'] ); - if ( file_exists( $OldStorage->Path().'/'.$saferOldName ) ) - unlink( $OldStorage->Path().'/'.$saferOldName ); + $OldStorage = new Storage($monitor['StorageId']); + $saferOldName = basename($monitor['Name']); + if ( file_exists($OldStorage->Path().'/'.$saferOldName) ) + unlink($OldStorage->Path().'/'.$saferOldName); - $NewStorage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); - if ( ! file_exists( $NewStorage->Path().'/'.$mid ) ) - mkdir( $NewStorage->Path().'/'.$mid, 0755 ); - $saferNewName = basename( $_REQUEST['newMonitor']['Name'] ); - symlink( $mid, $NewStorage->Path().'/'.$saferNewName ); + $NewStorage = new Storage($_REQUEST['newMonitor']['StorageId']); + if ( ! file_exists($NewStorage->Path().'/'.$mid) ) + mkdir($NewStorage->Path().'/'.$mid, 0755); + $saferNewName = basename($_REQUEST['newMonitor']['Name']); + symlink($mid, $NewStorage->Path().'/'.$saferNewName); } if ( isset($changes['Width']) || isset($changes['Height']) ) { $newW = $_REQUEST['newMonitor']['Width']; @@ -517,15 +522,15 @@ if ( canEdit( 'Monitors' ) ) { $oldH = $monitor['Height']; $oldA = $oldW * $oldH; - $zones = dbFetchAll( 'SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid) ); + $zones = dbFetchAll('SELECT * FROM Zones WHERE MonitorId=?', NULL, array($mid)); foreach ( $zones as $zone ) { $newZone = $zone; - $points = coordsToPoints( $zone['Coords'] ); + $points = coordsToPoints($zone['Coords']); for ( $i = 0; $i < count($points); $i++ ) { $points[$i]['x'] = intval(($points[$i]['x']*($newW-1))/($oldW-1)); $points[$i]['y'] = intval(($points[$i]['y']*($newH-1))/($oldH-1)); } - $newZone['Coords'] = pointsToCoords( $points ); + $newZone['Coords'] = pointsToCoords($points); $newZone['Area'] = intval(round(($zone['Area']*$newA)/$oldA)); $newZone['MinAlarmPixels'] = intval(round(($newZone['MinAlarmPixels']*$newA)/$oldA)); $newZone['MaxAlarmPixels'] = intval(round(($newZone['MaxAlarmPixels']*$newA)/$oldA)); @@ -534,41 +539,43 @@ if ( canEdit( 'Monitors' ) ) { $newZone['MinBlobPixels'] = intval(round(($newZone['MinBlobPixels']*$newA)/$oldA)); $newZone['MaxBlobPixels'] = intval(round(($newZone['MaxBlobPixels']*$newA)/$oldA)); - $changes = getFormChanges( $zone, $newZone, $types ); + $changes = getFormChanges($zone, $newZone, $types); - if ( count( $changes ) ) { - dbQuery( 'update Zones set '.implode( ', ', $changes ).' WHERE MonitorId=? AND Id=?', array( $mid, $zone['Id'] ) ); + if ( count($changes) ) { + dbQuery('UPDATE Zones SET '.implode(', ', $changes).' WHERE MonitorId=? AND Id=?', + array($mid, $zone['Id'])); } - } - } + } // end foreach zone + } // end if width and height $restart = true; - } else if ( ! $user['MonitorIds'] ) { // Can only create new monitors if we are not restricted to specific monitors + } else if ( ! $user['MonitorIds'] ) { + // Can only create new monitors if we are not restricted to specific monitors # FIXME This is actually a race condition. Should lock the table. $maxSeq = dbFetchOne('SELECT MAX(Sequence) AS MaxSequence FROM Monitors', 'MaxSequence'); $changes[] = 'Sequence = '.($maxSeq+1); - if ( dbQuery( 'INSERT INTO Monitors SET '.implode( ', ', $changes ) ) ) { + if ( dbQuery('INSERT INTO Monitors SET '.implode(', ', $changes)) ) { $mid = dbInsertId(); $zoneArea = $_REQUEST['newMonitor']['Width'] * $_REQUEST['newMonitor']['Height']; - dbQuery( "insert into Zones set MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) ); + dbQuery("INSERT INTO Zones SET MonitorId = ?, Name = 'All', Type = 'Active', Units = 'Percent', NumCoords = 4, Coords = ?, Area=?, AlarmRGB = 0xff0000, CheckMethod = 'Blobs', MinPixelThreshold = 25, MinAlarmPixels=?, MaxAlarmPixels=?, FilterX = 3, FilterY = 3, MinFilterPixels=?, MaxFilterPixels=?, MinBlobPixels=?, MinBlobs = 1", array( $mid, sprintf( "%d,%d %d,%d %d,%d %d,%d", 0, 0, $_REQUEST['newMonitor']['Width']-1, 0, $_REQUEST['newMonitor']['Width']-1, $_REQUEST['newMonitor']['Height']-1, 0, $_REQUEST['newMonitor']['Height']-1 ), $zoneArea, intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*3)/100), intval(($zoneArea*75)/100), intval(($zoneArea*2)/100) ) ); //$view = 'none'; - $Storage = new Storage( $_REQUEST['newMonitor']['StorageId'] ); - mkdir( $Storage->Path().'/'.$mid, 0755 ); + $Storage = new Storage($_REQUEST['newMonitor']['StorageId']); + mkdir($Storage->Path().'/'.$mid, 0755); $saferName = basename($_REQUEST['newMonitor']['Name']); - symlink( $mid, $Storage->Path().'/'.$saferName ); + symlink($mid, $Storage->Path().'/'.$saferName); } else { - Error("Error saving new Monitor."); + Error('Error saving new Monitor.'); return; } } else { - Error("Users with Monitors restrictions cannot create new monitors."); + Error('Users with Monitors restrictions cannot create new monitors.'); return; } $restart = true; } else { - Logger::Debug("No action due to no changes to Monitor"); + Logger::Debug('No action due to no changes to Monitor'); } # end if count(changes) if ( @@ -589,21 +596,21 @@ if ( canEdit( 'Monitors' ) ) { } // end if there has been a change of groups if ( ZM_OPT_X10 ) { - $x10Changes = getFormChanges( $x10Monitor, $_REQUEST['newX10Monitor'] ); + $x10Changes = getFormChanges($x10Monitor, $_REQUEST['newX10Monitor']); - if ( count( $x10Changes ) ) { + if ( count($x10Changes) ) { if ( $x10Monitor && isset($_REQUEST['newX10Monitor']) ) { - dbQuery( 'update TriggersX10 set '.implode( ', ', $x10Changes ).' where MonitorId=?', array($mid) ); + dbQuery('UPDATE TriggersX10 SET '.implode(', ', $x10Changes).' WHERE MonitorId=?', array($mid)); } elseif ( !$user['MonitorIds'] ) { if ( !$x10Monitor ) { - dbQuery( 'insert into TriggersX10 set MonitorId = ?, '.implode( ', ', $x10Changes ), array( $mid ) ); + dbQuery('INSERT INTO TriggersX10 SET MonitorId = ?, '.implode(', ', $x10Changes), array($mid)); } else { - dbQuery( 'delete from TriggersX10 where MonitorId = ?', array($mid) ); + dbQuery('DELETE FROM TriggersX10 WHERE MonitorId = ?', array($mid)); } } $restart = true; - } - } + } # end if has x10Changes + } # end if ZM_OPT_X10 if ( $restart ) { @@ -616,8 +623,8 @@ if ( canEdit( 'Monitors' ) ) { } if ( $new_monitor->Controllable() ) { - require_once( 'control_functions.php' ); - sendControlCommand( $mid, 'quit' ); + require_once('control_functions.php'); + sendControlCommand($mid, 'quit'); } // really should thump zmwatch and maybe zmtrigger too. //daemonControl( 'restart', 'zmwatch.pl' ); @@ -626,11 +633,11 @@ if ( canEdit( 'Monitors' ) ) { $view = 'none'; } elseif ( $action == 'delete' ) { if ( isset($_REQUEST['markMids']) && !$user['MonitorIds'] ) { - require_once( 'Monitor.php' ); - foreach( $_REQUEST['markMids'] as $markMid ) { + require_once('Monitor.php'); + foreach ( $_REQUEST['markMids'] as $markMid ) { if ( canEdit('Monitors', $markMid) ) { // This could be faster as a select all - if ( $monitor = dbFetchOne( 'SELECT * FROM Monitors WHERE Id = ?', NULL, array($markMid) ) ) { + if ( $monitor = dbFetchOne('SELECT * FROM Monitors WHERE Id = ?', NULL, array($markMid)) ) { $Monitor = new Monitor($monitor); $Monitor->delete(); } // end if monitor found in db @@ -641,15 +648,17 @@ if ( canEdit( 'Monitors' ) ) { } // Device view actions -if ( canEdit( 'Devices' ) ) { +if ( canEdit('Devices') ) { if ( $action == 'device' ) { if ( !empty($_REQUEST['command']) ) { - setDeviceStatusX10( $_REQUEST['key'], $_REQUEST['command'] ); - } elseif ( isset( $_REQUEST['newDevice'] ) ) { + setDeviceStatusX10($_REQUEST['key'], $_REQUEST['command']); + } else if ( isset($_REQUEST['newDevice']) ) { if ( isset($_REQUEST['did']) ) { - dbQuery( 'update Devices set Name=?, KeyString=? where Id=?', array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) ); + dbQuery('UPDATE Devices SET Name=?, KeyString=? WHERE Id=?', + array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'], $_REQUEST['did']) ); } else { - dbQuery( 'insert into Devices set Name=?, KeyString=?', array( $_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString'] ) ); + dbQuery('INSERT INTO Devices SET Name=?, KeyString=?', + array($_REQUEST['newDevice']['Name'], $_REQUEST['newDevice']['KeyString']) ); } $refreshParent = true; $view = 'none'; @@ -657,7 +666,7 @@ if ( canEdit( 'Devices' ) ) { } elseif ( $action == 'delete' ) { if ( isset($_REQUEST['markDids']) ) { foreach( $_REQUEST['markDids'] as $markDid ) { - dbQuery( 'delete from Devices where Id=?', array($markDid) ); + dbQuery('DELETE FROM Devices WHERE Id=?', array($markDid)); $refreshParent = true; } } @@ -665,47 +674,59 @@ if ( canEdit( 'Devices' ) ) { } // end if canedit devices // Group view actions -if ( canView( 'Groups' ) && $action == 'setgroup' ) { +if ( canView('Groups') && ($action == 'setgroup') ) { if ( !empty($_REQUEST['gid']) ) { - setcookie( 'zmGroup', validInt($_REQUEST['gid']), time()+3600*24*30*12*10 ); + setcookie('zmGroup', validInt($_REQUEST['gid']), time()+3600*24*30*12*10); } else { - setcookie( 'zmGroup', '', time()-3600*24*2 ); + setcookie('zmGroup', '', time()-3600*24*2); } $refreshParent = true; } // Group edit actions -# Should probably verify 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 -if ( canEdit( 'Groups' ) ) { +# Should probably verify 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 +if ( canEdit('Groups') ) { if ( $action == 'group' ) { - $monitors = empty( $_POST['newGroup']['MonitorIds'] ) ? '' : implode(',', $_POST['newGroup']['MonitorIds']); + $monitors = empty($_POST['newGroup']['MonitorIds']) ? '' : implode(',', $_POST['newGroup']['MonitorIds']); $group_id = null; if ( !empty($_POST['gid']) ) { $group_id = $_POST['gid']; - dbQuery( 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?', - array($_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), $group_id) ); - dbQuery( 'DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id) ); + dbQuery( + 'UPDATE Groups SET Name=?, ParentId=? WHERE Id=?', + array( + $_POST['newGroup']['Name'], + ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), + $group_id, + ) + ); + dbQuery('DELETE FROM Groups_Monitors WHERE GroupId=?', array($group_id)); } else { - dbQuery( 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)', - array( $_POST['newGroup']['Name'], ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ) ) ); - $group_id=dbInsertId(); + dbQuery( + 'INSERT INTO Groups (Name,ParentId) VALUES (?,?)', + array( + $_POST['newGroup']['Name'], + ( $_POST['newGroup']['ParentId'] == '' ? null : $_POST['newGroup']['ParentId'] ), + ) + ); + $group_id = dbInsertId(); } if ( $group_id ) { foreach ( $_POST['newGroup']['MonitorIds'] as $mid ) { - dbQuery( 'INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid) ); + dbQuery('INSERT INTO Groups_Monitors (GroupId,MonitorId) VALUES (?,?)', array($group_id, $mid)); } } $view = 'none'; $refreshParent = true; } else if ( $action == 'delete' ) { if ( !empty($_REQUEST['gid']) ) { - if ( is_array( $_REQUEST['gid'] ) ) { - foreach( $_REQUEST['gid'] as $gid ) { - $Group = new Group( $gid ); + if ( is_array($_REQUEST['gid']) ) { + foreach ( $_REQUEST['gid'] as $gid ) { + $Group = new Group($gid); $Group->delete(); } } else { - $Group = new Group( $_REQUEST['gid'] ); + $Group = new Group($_REQUEST['gid'] ); $Group->delete(); } } @@ -714,23 +735,23 @@ if ( canEdit( 'Groups' ) ) { } // end if can edit groups // System edit actions -if ( canEdit( 'System' ) ) { - if ( isset( $_REQUEST['object'] ) ) { +if ( canEdit('System') ) { + if ( isset($_REQUEST['object']) ) { if ( $_REQUEST['object'] == 'MontageLayout' ) { require_once('MontageLayout.php'); if ( $action == 'Save' ) { $Layout = null; if ( $_REQUEST['Name'] != '' ) { $Layout = new MontageLayout(); - $Layout->Name( $_REQUEST['Name'] ); + $Layout->Name($_REQUEST['Name']); } else { - $Layout = new MontageLayout( $_REQUEST['zmMontageLayout'] ); + $Layout = new MontageLayout($_REQUEST['zmMontageLayout']); } - $Layout->Positions( $_REQUEST['Positions'] ); + $Layout->Positions($_REQUEST['Positions']); $Layout->save(); session_start(); $_SESSION['zmMontageLayout'] = $Layout->Id(); - setcookie('zmMontageLayout', $Layout->Id(), 1 ); + setcookie('zmMontageLayout', $Layout->Id(), 1); session_write_close(); $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=montagereview'; } // end if save @@ -738,19 +759,24 @@ if ( canEdit( 'System' ) ) { } else if ( $_REQUEST['object'] == 'server' ) { if ( $action == 'Save' ) { - if ( !empty($_REQUEST['id']) ) - $dbServer = dbFetchOne( 'SELECT * FROM Servers WHERE Id=?', NULL, array($_REQUEST['id']) ); - else + 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 ); + $changes = getFormChanges($dbServer, $_REQUEST['newServer'], $types); - if ( count( $changes ) ) { + if ( count($changes) ) { if ( !empty($_REQUEST['id']) ) { - dbQuery( 'UPDATE Servers SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) ); + dbQuery('UPDATE Servers SET '.implode(', ', $changes).' WHERE Id = ?', + array($_REQUEST['id']) ); } else { - dbQuery( 'INSERT INTO Servers set '.implode( ', ', $changes ) ); + dbQuery('INSERT INTO Servers SET '.implode(', ', $changes)); } $refreshParent = true; } @@ -758,27 +784,27 @@ if ( canEdit( 'System' ) ) { } else if ( $action == 'delete' ) { if ( !empty($_REQUEST['markIds']) ) { foreach( $_REQUEST['markIds'] as $Id ) - dbQuery( 'DELETE FROM Servers WHERE Id=?', array($Id) ); + dbQuery('DELETE FROM Servers WHERE Id=?', array($Id)); } $refreshParent = true; } else { - Error( "Unknown action $action in saving Server" ); + Error("Unknown action $action in saving Server"); } } else if ( $_REQUEST['object'] == 'storage' ) { if ( $action == 'Save' ) { if ( !empty($_REQUEST['id']) ) - $dbStorage = dbFetchOne( 'SELECT * FROM Storage WHERE Id=?', NULL, array($_REQUEST['id']) ); + $dbStorage = dbFetchOne('SELECT * FROM Storage WHERE Id=?', NULL, array($_REQUEST['id'])); else $dbStorage = array(); $types = array(); - $changes = getFormChanges( $dbStorage, $_REQUEST['newStorage'], $types ); + $changes = getFormChanges($dbStorage, $_REQUEST['newStorage'], $types); - if ( count( $changes ) ) { + if ( count($changes) ) { if ( !empty($_REQUEST['id']) ) { - dbQuery( 'UPDATE Storage SET '.implode( ', ', $changes ).' WHERE Id = ?', array($_REQUEST['id']) ); + dbQuery('UPDATE Storage SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['id'])); } else { - dbQuery( 'INSERT INTO Storage set '.implode( ', ', $changes ) ); + dbQuery('INSERT INTO Storage set '.implode(', ', $changes)); } $refreshParent = true; } @@ -786,11 +812,11 @@ if ( canEdit( 'System' ) ) { } else if ( $action == 'delete' ) { if ( !empty($_REQUEST['markIds']) ) { foreach( $_REQUEST['markIds'] as $Id ) - dbQuery( 'DELETE FROM Storage WHERE Id=?', array($Id) ); + dbQuery('DELETE FROM Storage WHERE Id=?', array($Id)); } $refreshParent = true; } else { - Error( "Unknown action $action in saving Storage" ); + Error("Unknown action $action in saving Storage"); } } # end if isset($_REQUEST['object'] ) @@ -804,7 +830,7 @@ if ( canEdit( 'System' ) ) { } case 'ignore' : { - dbQuery( "update Config set Value = '".ZM_DYN_LAST_VERSION."' where Name = 'ZM_DYN_CURR_VERSION'" ); + dbQuery("UPDATE Config SET Value = '".ZM_DYN_LAST_VERSION."' WHERE Name = 'ZM_DYN_CURR_VERSION'"); break; } case 'hour' : @@ -819,12 +845,12 @@ if ( canEdit( 'System' ) ) { } elseif ( $option == 'week' ) { $nextReminder += 7*24*60*60; } - dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_NEXT_REMINDER'" ); + dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_NEXT_REMINDER'"); break; } case 'never' : { - dbQuery( "update Config set Value = '0' where Name = 'ZM_CHECK_FOR_UPDATES'" ); + dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_CHECK_FOR_UPDATES'"); break; } } @@ -852,30 +878,51 @@ if ( canEdit( 'System' ) ) { } elseif ( $option == 'month' ) { $nextReminder += 30*24*60*60; } - dbQuery( "update Config set Value = '".$nextReminder."' where Name = 'ZM_DYN_DONATE_REMINDER_TIME'" ); + dbQuery("UPDATE Config SET Value = '".$nextReminder."' WHERE Name = 'ZM_DYN_DONATE_REMINDER_TIME'"); break; } case 'never' : case 'already' : { - dbQuery( "update Config set Value = '0' where Name = 'ZM_DYN_SHOW_DONATE_REMINDER'" ); + dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_DYN_SHOW_DONATE_REMINDER'"); break; } } // end switch option } + if ( ($action == 'privacy') && isset($_REQUEST['option']) ) { + switch( $_REQUEST['option'] ) { + case 'decline' : + { + dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_SHOW_PRIVACY'"); + dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_TELEMETRY_DATA'"); + $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console'; + break; + } + case 'accept' : + { + dbQuery("UPDATE Config SET Value = '0' WHERE Name = 'ZM_SHOW_PRIVACY'"); + dbQuery("UPDATE Config SET Value = '1' WHERE Name = 'ZM_TELEMETRY_DATA'"); + $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=console'; + break; + } + default: # Enable the privacy statement if we somehow submit something other than accept or decline + dbQuery("UPDATE Config SET Value = '1' WHERE Name = 'ZM_SHOW_PRIVACY'"); + } // end switch option + return; + } if ( $action == 'options' && isset($_REQUEST['tab']) ) { $configCat = $configCats[$_REQUEST['tab']]; $changed = false; foreach ( $configCat as $name=>$value ) { - unset( $newValue ); + unset($newValue); if ( $value['Type'] == 'boolean' && empty($_REQUEST['newConfig'][$name]) ) { $newValue = 0; } else if ( isset($_REQUEST['newConfig'][$name]) ) { - $newValue = preg_replace( "/\r\n/", "\n", stripslashes( $_REQUEST['newConfig'][$name] ) ); + $newValue = preg_replace("/\r\n/", "\n", stripslashes($_REQUEST['newConfig'][$name])); } if ( isset($newValue) && ($newValue != $value['Value']) ) { - dbQuery( 'UPDATE Config SET Value=? WHERE Name=?', array( $newValue, $name ) ); + dbQuery('UPDATE Config SET Value=? WHERE Name=?', array($newValue, $name)); $changed = true; } } @@ -901,29 +948,30 @@ if ( canEdit( 'System' ) ) { } $redirect = ZM_BASE_URL.$_SERVER['PHP_SELF'].'?view=options&tab='.$_REQUEST['tab']; } - loadConfig( false ); + loadConfig(false); + return; } elseif ( $action == 'user' ) { if ( !empty($_REQUEST['uid']) ) - $dbUser = dbFetchOne( "SELECT * FROM Users WHERE Id=?", NULL, array($_REQUEST['uid']) ); + $dbUser = dbFetchOne('SELECT * FROM Users WHERE Id=?', NULL, array($_REQUEST['uid'])); else $dbUser = array(); $types = array(); - $changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types ); + $changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types); if ( $_REQUEST['newUser']['Password'] ) $changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')'; else - unset( $changes['Password'] ); + unset($changes['Password']); - if ( count( $changes ) ) { + if ( count($changes) ) { if ( !empty($_REQUEST['uid']) ) { - dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id = ?', array($_REQUEST['uid']) ); + dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id = ?', array($_REQUEST['uid'])); # If we are updating the logged in user, then update our session user data. if ( $user and ( $dbUser['Username'] == $user['Username'] ) ) - userLogin( $dbUser['Username'], $dbUser['Password'] ); + userLogin($dbUser['Username'], $dbUser['Password']); } else { - dbQuery( 'insert into Users set '.implode( ', ', $changes ) ); + dbQuery('INSERT INTO Users SET '.implode(', ', $changes)); } $refreshParent = true; } @@ -931,29 +979,28 @@ if ( canEdit( 'System' ) ) { } elseif ( $action == 'state' ) { if ( !empty($_REQUEST['runState']) ) { //if ( $cookies ) session_write_close(); - packageControl( $_REQUEST['runState'] ); + packageControl($_REQUEST['runState']); $refreshParent = true; } } elseif ( $action == 'save' ) { if ( !empty($_REQUEST['runState']) || !empty($_REQUEST['newState']) ) { $sql = 'SELECT Id,Function,Enabled FROM Monitors ORDER BY Id'; $definitions = array(); - foreach( dbFetchAll( $sql ) as $monitor ) - { + foreach( dbFetchAll($sql) as $monitor ) { $definitions[] = $monitor['Id'].':'.$monitor['Function'].':'.$monitor['Enabled']; } - $definition = join( ',', $definitions ); + $definition = join(',', $definitions); if ( $_REQUEST['newState'] ) $_REQUEST['runState'] = $_REQUEST['newState']; - dbQuery( 'replace into States set Name=?, Definition=?', array( $_REQUEST['runState'],$definition) ); + dbQuery('REPLACE INTO States SET Name=?, Definition=?', array($_REQUEST['runState'],$definition)); } } elseif ( $action == 'delete' ) { if ( isset($_REQUEST['runState']) ) - dbQuery( 'delete from States where Name=?', array($_REQUEST['runState']) ); + dbQuery('DELETE FROM States WHERE Name=?', array($_REQUEST['runState'])); if ( isset($_REQUEST['markUids']) ) { foreach( $_REQUEST['markUids'] as $markUid ) - dbQuery( 'delete from Users where Id = ?', array($markUid) ); + dbQuery('DELETE FROM Users WHERE Id = ?', array($markUid)); if ( $markUid == $user['Id'] ) userLogout(); } @@ -962,17 +1009,17 @@ if ( canEdit( 'System' ) ) { if ( ZM_USER_SELF_EDIT && $action == 'user' ) { $uid = $user['Id']; - $dbUser = dbFetchOne( 'SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid) ); + $dbUser = dbFetchOne('SELECT Id, Password, Language FROM Users WHERE Id = ?', NULL, array($uid)); $types = array(); - $changes = getFormChanges( $dbUser, $_REQUEST['newUser'], $types ); + $changes = getFormChanges($dbUser, $_REQUEST['newUser'], $types); if ( !empty($_REQUEST['newUser']['Password']) ) $changes['Password'] = 'Password = password('.dbEscape($_REQUEST['newUser']['Password']).')'; else - unset( $changes['Password'] ); - if ( count( $changes ) ) { - dbQuery( 'update Users set '.implode( ', ', $changes ).' where Id=?', array($uid) ); + unset($changes['Password']); + if ( count($changes) ) { + dbQuery('UPDATE Users SET '.implode(', ', $changes).' WHERE Id=?', array($uid)); $refreshParent = true; } $view = 'none'; @@ -981,8 +1028,8 @@ if ( canEdit( 'System' ) ) { if ( $action == 'reset' ) { session_start(); - $_SESSION['zmEventResetTime'] = strftime( STRF_FMT_DATETIME_DB ); - setcookie( 'zmEventResetTime', $_SESSION['zmEventResetTime'], time()+3600*24*30*12*10 ); + $_SESSION['zmEventResetTime'] = strftime(STRF_FMT_DATETIME_DB); + setcookie('zmEventResetTime', $_SESSION['zmEventResetTime'], time()+3600*24*30*12*10); session_write_close(); } diff --git a/web/includes/auth.php b/web/includes/auth.php index 9cf8d37c1..c7df780ba 100644 --- a/web/includes/auth.php +++ b/web/includes/auth.php @@ -116,7 +116,7 @@ function generateAuthHash($useRemoteAddr, $force=false) { $time = time(); $mintime = $time - ( ZM_AUTH_HASH_TTL * 1800 ); - if ( $force or ( !isset($_SESSION['AuthHash']) ) or ( $_SESSION['AuthHashGeneratedAt'] < $mintime ) ) { + if ( $force or ( !isset($_SESSION['AuthHash'.$_SESSION['remoteAddr']]) ) or ( $_SESSION['AuthHashGeneratedAt'] < $mintime ) ) { # Don't both regenerating Auth Hash if an hour hasn't gone by yet $local_time = localtime(); $authKey = ''; @@ -133,7 +133,7 @@ function generateAuthHash($useRemoteAddr, $force=false) { session_start(); $close_session = 1; } - $_SESSION['AuthHash'] = $auth; + $_SESSION['AuthHash'.$_SESSION['remoteAddr']] = $auth; $_SESSION['AuthHashGeneratedAt'] = $time; session_write_close(); } else { @@ -143,7 +143,7 @@ function generateAuthHash($useRemoteAddr, $force=false) { #} else { #Logger::Debug("Using cached auth " . $_SESSION['AuthHash'] ." beacuse generatedat:" . $_SESSION['AuthHashGeneratedAt'] . ' < now:'. $time . ' - ' . ZM_AUTH_HASH_TTL . ' * 1800 = '. $mintime); } # end if AuthHash is not cached - return $_SESSION['AuthHash']; + return $_SESSION['AuthHash'.$_SESSION['remoteAddr']]; } # end if using AUTH and AUTH_RELAY return ''; } diff --git a/web/includes/database.php b/web/includes/database.php index 2c3a818c1..95fabee11 100644 --- a/web/includes/database.php +++ b/web/includes/database.php @@ -98,7 +98,14 @@ function dbLog( $sql, $update=false ) { } function dbError( $sql ) { - Error( "SQL-ERR '".$dbConn->errorInfo()."', statement was '".$sql."'" ); + global $dbConn; + $error = $dbConn->errorInfo(); + if ( ! $error[0] ) + return ''; + + $message = "SQL-ERR '".implode("\n",$dbConn->errorInfo())."', statement was '".$sql."'"; + Error($message); + return $message; } function dbEscape( $string ) { @@ -136,12 +143,16 @@ function dbQuery( $sql, $params=NULL ) { Logger::Debug("SQL: $sql values:" . ($params?implode(',',$params):'') ); } $result = $dbConn->query($sql); + if ( ! $result ) { + Error("SQL: Error preparing $sql: " . $pdo->errorInfo); + return NULL; + } } if ( defined('ZM_DB_DEBUG') ) { if ( $params ) - Warning("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() ); + Logger::Debug("SQL: $sql" . implode(',',$params) . ' rows: '.$result->rowCount() ); else - Warning("SQL: $sql: rows:" . $result->rowCount() ); + Logger::Debug("SQL: $sql: rows:" . $result->rowCount() ); } } catch(PDOException $e) { Error( "SQL-ERR '".$e->getMessage()."', statement was '".$sql."' params:" . ($params?implode(',',$params):'') ); diff --git a/web/includes/functions.php b/web/includes/functions.php index 09a86c03c..53ea7e199 100644 --- a/web/includes/functions.php +++ b/web/includes/functions.php @@ -36,25 +36,31 @@ function noCacheHeaders() { } function CORSHeaders() { - if ( isset( $_SERVER['HTTP_ORIGIN'] ) ) { + if ( isset($_SERVER['HTTP_ORIGIN']) ) { # The following is left for future reference/use. $valid = false; - $servers = dbFetchAll( 'SELECT * FROM Servers' ); - if ( sizeof($servers) <= 1 ) { + $Servers = Server::find(); + if ( sizeof($Servers) < 1 ) { # Only need CORSHeaders in the event that there are multiple servers in use. + # ICON: Might not be true. multi-port? return; } - foreach( $servers as $row ) { - $Server = new Server( $row ); - if ( $_SERVER['HTTP_ORIGIN'] == $Server->Url() ) { + foreach( $Servers as $Server ) { + if ( + preg_match('/^(https?:\/\/)?'.preg_quote($Server->Hostname(),'/').'/i', $_SERVER['HTTP_ORIGIN']) + or + preg_match('/^(https?:\/\/)?'.preg_quote($Server->Name(),'/').'/i', $_SERVER['HTTP_ORIGIN']) + ) { $valid = true; - header('Access-Control-Allow-Origin: ' . $Server->Url() ); + Logger::Debug("Setting Access-Controll-Allow-Origin from " . $_SERVER['HTTP_ORIGIN']); + header('Access-Control-Allow-Origin: ' . $_SERVER['HTTP_ORIGIN']); header('Access-Control-Allow-Headers: x-requested-with,x-request'); + break; } } - if ( ! $valid ) { - Warning( $_SERVER['HTTP_ORIGIN'] . ' is not found in servers list.' ); + if ( !$valid ) { + Warning($_SERVER['HTTP_ORIGIN'] . ' is not found in servers list.'); } } } @@ -910,7 +916,7 @@ function reScale( $dimension, $dummy ) { $new_dimension = $dimension; for ( $i = 1; $i < func_num_args(); $i++ ) { $scale = func_get_arg( $i ); - if ( !empty($scale) && $scale != SCALE_BASE ) + if ( !empty($scale) && ($scale != 'auto') && ($scale != SCALE_BASE) ) $new_dimension = (int)(($new_dimension*$scale)/SCALE_BASE); } return( $new_dimension ); @@ -1907,23 +1913,24 @@ function logState() { # This is an expensive request, as it has to hit every row of the Logs Table $sql = 'SELECT Level, COUNT(Level) AS LevelCount FROM Logs WHERE Level < '.Logger::INFO.' AND TimeKey > unix_timestamp(now() - interval '.ZM_LOG_CHECK_PERIOD.' second) GROUP BY Level ORDER BY Level ASC'; - $counts = dbFetchAll( $sql ); - - foreach ( $counts as $count ) { - if ( $count['Level'] <= Logger::PANIC ) - $count['Level'] = Logger::FATAL; - if ( !($levelCount = $levelCounts[$count['Level']]) ) { - Error( "Unexpected Log level ".$count['Level'] ); - next; - } - if ( $levelCount[1] && $count['LevelCount'] >= $levelCount[1] ) { - $state = 'alarm'; - break; - } elseif ( $levelCount[0] && $count['LevelCount'] >= $levelCount[0] ) { - $state = 'alert'; + $counts = dbFetchAll($sql); + if ( $counts ) { + foreach ( $counts as $count ) { + if ( $count['Level'] <= Logger::PANIC ) + $count['Level'] = Logger::FATAL; + if ( !($levelCount = $levelCounts[$count['Level']]) ) { + Error('Unexpected Log level '.$count['Level']); + next; + } + if ( $levelCount[1] && $count['LevelCount'] >= $levelCount[1] ) { + $state = 'alarm'; + break; + } elseif ( $levelCount[0] && $count['LevelCount'] >= $levelCount[0] ) { + $state = 'alert'; + } } } - return( $state ); + return $state; } function isVector ( &$array ) { @@ -2271,4 +2278,30 @@ function unparse_url($parsed_url, $substitutions = array() ) { $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; return "$scheme$user$pass$host$port$path$query$fragment"; } + +// The following works around php not being built with semaphore functions. +if (!function_exists('sem_get')) { + function sem_get($key) { + return fopen(__FILE__ . '.sem.' . $key, 'w+'); + } + function sem_acquire($sem_id) { + return flock($sem_id, LOCK_EX); + } + function sem_release($sem_id) { + return flock($sem_id, LOCK_UN); + } +} + +if( !function_exists('ftok') ) { + function ftok($filename = "", $proj = "") { + if ( empty($filename) || !file_exists($filename) ) { + return -1; + } else { + $filename = $filename . (string) $proj; + for($key = array(); sizeof($key) < strlen($filename); $key[] = ord(substr($filename, sizeof($key), 1))); + return dechex(array_sum($key)); + } + } +} + ?> diff --git a/web/includes/lang.php b/web/includes/lang.php index 0cab566b7..0c40e7f60 100644 --- a/web/includes/lang.php +++ b/web/includes/lang.php @@ -49,8 +49,14 @@ function loadLanguage( $prefix="" ) return( false ); } -if ( $langFile = loadLanguage() ) +if ( $langFile = loadLanguage() ) { require_once( $langFile ); + require_once( 'lang/default.php' ); + foreach ($DLANG as $key => $value) { + if ( ! array_key_exists( $key, $SLANG ) ) + $SLANG[$key] = $DLANG[$key]; + } +} // diff --git a/web/includes/logger.php b/web/includes/logger.php index 0d7334ac7..8bc81fb97 100644 --- a/web/includes/logger.php +++ b/web/includes/logger.php @@ -406,7 +406,7 @@ class Logger { $result = $stmt->execute( array( sprintf( '%d.%06d', $time['sec'], $time['usec'] ), $this->id, getmypid(), $level, $code, $string, $file, $line ) ); } catch(PDOException $ex) { $this->databaseLevel = self::NOLOG; - Fatal( "Can't write log entry '$sql': ". $ex->getMessage() ); + Error("Can't write log entry '$sql': ". $ex->getMessage()); } } // This has to be last as trigger_error can be fatal diff --git a/web/index.php b/web/index.php index 7f823f1fd..addddce06 100644 --- a/web/index.php +++ b/web/index.php @@ -18,17 +18,17 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -error_reporting( E_ALL ); +error_reporting(E_ALL); $debug = false; if ( $debug ) { // Use these for debugging, though not both at once! - phpinfo( INFO_VARIABLES ); + phpinfo(INFO_VARIABLES); //error_reporting( E_ALL ); } // Use new style autoglobals where possible -if ( version_compare( phpversion(), '4.1.0', '<') ) { +if ( version_compare(phpversion(), '4.1.0', '<') ) { $_SESSION = &$HTTP_SESSION_VARS; $_SERVER = &$HTTP_SERVER_VARS; } @@ -36,20 +36,20 @@ if ( version_compare( phpversion(), '4.1.0', '<') ) { // Useful debugging lines for mobile devices if ( false ) { ob_start(); - phpinfo( INFO_VARIABLES ); - $fp = fopen( '/tmp/env.html', 'w' ); - fwrite( $fp, ob_get_contents() ); - fclose( $fp ); + phpinfo(INFO_VARIABLES); + $fp = fopen('/tmp/env.html', 'w'); + fwrite($fp, ob_get_contents()); + fclose($fp); ob_end_clean(); } -require_once( 'includes/config.php' ); -require_once( 'includes/logger.php' ); -require_once( 'includes/Server.php' ); -require_once( 'includes/Storage.php' ); -require_once( 'includes/Event.php' ); -require_once( 'includes/Group.php' ); -require_once( 'includes/Monitor.php' ); +require_once('includes/config.php'); +require_once('includes/logger.php'); +require_once('includes/Server.php'); +require_once('includes/Storage.php'); +require_once('includes/Event.php'); +require_once('includes/Group.php'); +require_once('includes/Monitor.php'); if ( @@ -61,13 +61,13 @@ if ( } else { $protocol = 'http'; } -define( 'ZM_BASE_PROTOCOL', $protocol ); +define('ZM_BASE_PROTOCOL', $protocol); // Absolute URL's are unnecessary and break compatibility with reverse proxies // define( "ZM_BASE_URL", $protocol.'://'.$_SERVER['HTTP_HOST'] ); // Use relative URL's instead -define( 'ZM_BASE_URL', '' ); +define('ZM_BASE_URL', ''); // Check time zone is set if (!ini_get('date.timezone') || !date_default_timezone_set(ini_get('date.timezone'))) { @@ -85,10 +85,10 @@ if ( isset($_GET['skin']) ) { $skin = 'classic'; } -$skins = array_map( 'basename', glob('skins/*', GLOB_ONLYDIR ) ); +$skins = array_map('basename', glob('skins/*', GLOB_ONLYDIR)); -if ( ! in_array( $skin, $skins ) ) { - Error( "Invalid skin '$skin' setting to " . $skins[0] ); +if ( ! in_array($skin, $skins) ) { + Error("Invalid skin '$skin' setting to " . $skins[0]); $skin = $skins[0]; } @@ -96,25 +96,25 @@ if ( isset($_GET['css']) ) { $css = $_GET['css']; } elseif ( isset($_COOKIE['zmCSS']) ) { $css = $_COOKIE['zmCSS']; -} elseif (defined('ZM_CSS_DEFAULT')) { +} elseif ( defined('ZM_CSS_DEFAULT') ) { $css = ZM_CSS_DEFAULT; } else { $css = 'classic'; } -$css_skins = array_map( 'basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR) ); -if ( ! in_array( $css, $css_skins ) ) { - Error( "Invalid skin css '$css' setting to " . $css_skins[0] ); +$css_skins = array_map('basename', glob('skins/'.$skin.'/css/*',GLOB_ONLYDIR)); +if ( !in_array($css, $css_skins) ) { + Error("Invalid skin css '$css' setting to " . $css_skins[0]); $css = $css_skins[0]; } -define( 'ZM_BASE_PATH', dirname( $_SERVER['REQUEST_URI'] ) ); -define( 'ZM_SKIN_PATH', "skins/$skin" ); -define( 'ZM_SKIN_NAME', $skin ); +define('ZM_BASE_PATH', dirname($_SERVER['REQUEST_URI'])); +define('ZM_SKIN_PATH', "skins/$skin"); +define('ZM_SKIN_NAME', $skin); $skinBase = array(); // To allow for inheritance of skins -if ( !file_exists( ZM_SKIN_PATH ) ) - Fatal( "Invalid skin '$skin'" ); +if ( !file_exists(ZM_SKIN_PATH) ) + Fatal("Invalid skin '$skin'"); $skinBase[] = $skin; $currentCookieParams = session_get_cookie_params(); @@ -127,25 +127,25 @@ session_set_cookie_params( true ); -ini_set( 'session.name', 'ZMSESSID' ); +ini_set('session.name', 'ZMSESSID'); session_start(); if ( !isset($_SESSION['skin']) || isset($_REQUEST['skin']) || !isset($_COOKIE['zmSkin']) || $_COOKIE['zmSkin'] != $skin ) { $_SESSION['skin'] = $skin; - setcookie( 'zmSkin', $skin, time()+3600*24*30*12*10 ); + setcookie('zmSkin', $skin, time()+3600*24*30*12*10); } if ( !isset($_SESSION['css']) || isset($_REQUEST['css']) || !isset($_COOKIE['zmCSS']) || $_COOKIE['zmCSS'] != $css ) { $_SESSION['css'] = $css; - setcookie( 'zmCSS', $css, time()+3600*24*30*12*10 ); + setcookie('zmCSS', $css, time()+3600*24*30*12*10); } if ( ZM_OPT_USE_AUTH ) { - if ( isset( $_SESSION['user'] ) ) { + if ( isset($_SESSION['user']) ) { $user = $_SESSION['user']; } else { - unset( $user ); + unset($user); } } else { $user = $defaultUser; @@ -154,9 +154,9 @@ if ( ZM_OPT_USE_AUTH ) { # Any file/page that sets session variables must re-open it. session_write_close(); -require_once( 'includes/lang.php' ); -require_once( 'includes/functions.php' ); -require_once( 'includes/auth.php' ); +require_once('includes/lang.php'); +require_once('includes/functions.php'); +require_once('includes/auth.php'); # Running is global but only do the daemonCheck if it is actually needed $running = null; @@ -179,20 +179,21 @@ $request = null; if ( isset($_REQUEST['request']) ) $request = detaintPath($_REQUEST['request']); -foreach ( getSkinIncludes( 'skin.php' ) as $includeFile ) +foreach ( getSkinIncludes('skin.php') as $includeFile ) require_once $includeFile; if ( ZM_OPT_USE_AUTH ) { - if ( ZM_AUTH_HASH_LOGINS ) { - if ( empty($user) && ! empty($_REQUEST['auth']) ) { - if ( $authUser = getAuthUser( $_REQUEST['auth'] ) ) { - userLogin( $authUser['Username'], $authUser['Password'], true ); + if ( ZM_AUTH_HASH_LOGINS && empty($user) && ! empty($_REQUEST['auth']) ) { + if ( $authUser = getAuthUser($_REQUEST['auth']) ) { + userLogin($authUser['Username'], $authUser['Password'], true); } } + else if ( isset($_REQUEST['username']) and isset($_REQUEST['password']) ) { + userLogin($_REQUEST['username'], $_REQUEST['password'], false); } - if ( ! empty($user) ) { + if ( !empty($user) ) { // generate it once here, while session is open. Value will be cached in session and return when called later on - generateAuthHash( ZM_AUTH_HASH_IPS ); + generateAuthHash(ZM_AUTH_HASH_IPS); } } @@ -205,40 +206,52 @@ isset($view) || $view = NULL; isset($request) || $request = NULL; isset($action) || $action = NULL; -if ( ZM_ENABLE_CSRF_MAGIC && $action != 'login' && $view != 'view_video' && $request != 'control' && $view != 'frames' && $view != 'archive' ) { +Logger::Debug("View: $view Request: $request Action: $action"); +if ( + ZM_ENABLE_CSRF_MAGIC && + ( $action != 'login' ) && + ( $view != 'view_video' ) && + ( $view != 'image' ) && + ( $request != 'control' ) && + ( $view != 'frames' ) && + ( $view != 'archive' ) +) { require_once( 'includes/csrf/csrf-magic.php' ); #Logger::Debug("Calling csrf_check with the following values: \$request = \"$request\", \$view = \"$view\", \$action = \"$action\""); csrf_check(); } # Need to include actions because it does auth -require_once( 'includes/actions.php' ); +require_once('includes/actions.php'); # If I put this here, it protects all views and popups, but it has to go after actions.php because actions.php does the actual logging in. -if ( ZM_OPT_USE_AUTH and ! isset($user) ) { - Logger::Debug("Redirecting to login" ); +if ( ZM_OPT_USE_AUTH and !isset($user) ) { + Logger::Debug('Redirecting to login'); $view = 'login'; $request = null; +} else if ( ZM_SHOW_PRIVACY && ($action != 'privacy') && ($view !='options') && (!$request) && canEdit('System') ) { + Logger::Debug('Redirecting to privacy'); + $view = 'privacy'; + $request = null; } - if ( $redirect ) { header('Location: '.$redirect); return; } if ( $request ) { - foreach ( getSkinIncludes( 'ajax/'.$request.'.php', true, true ) as $includeFile ) { - if ( !file_exists( $includeFile ) ) - Fatal( "Request '$request' does not exist" ); + foreach ( getSkinIncludes('ajax/'.$request.'.php', true, true) as $includeFile ) { + if ( !file_exists($includeFile) ) + Fatal("Request '$request' does not exist"); require_once $includeFile; } return; } else { - if ( $includeFiles = getSkinIncludes( 'views/'.$view.'.php', true, true ) ) { + if ( $includeFiles = getSkinIncludes('views/'.$view.'.php', true, true) ) { foreach ( $includeFiles as $includeFile ) { - if ( !file_exists( $includeFile ) ) - Fatal( "View '$view' does not exist" ); + if ( !file_exists($includeFile) ) + Fatal("View '$view' does not exist"); require_once $includeFile; } // If the view overrides $view to 'error', and the user is not logged in, then the @@ -246,14 +259,14 @@ if ( $request ) { // The login view should handle redirecting to the correct location afterward. if ( $view == 'error' && !isset($user) ) { $view = 'login'; - foreach ( getSkinIncludes( 'views/login.php', true, true ) as $includeFile ) + foreach ( getSkinIncludes('views/login.php', true, true) as $includeFile ) require_once $includeFile; } } // If the view is missing or the view still returned error with the user logged in, // then it is not recoverable. if ( !$includeFiles || $view == 'error' ) { - foreach ( getSkinIncludes( 'views/error.php', true, true ) as $includeFile ) + foreach ( getSkinIncludes('views/error.php', true, true) as $includeFile ) require_once $includeFile; } } diff --git a/web/lang/ba_ba.php b/web/lang/ba_ba.php new file mode 100644 index 000000000..e60e69066 --- /dev/null +++ b/web/lang/ba_ba.php @@ -0,0 +1,995 @@ + 'Dnevnik', + 'DateTime' => 'Datum/Vrijeme', + 'Component' => 'Komponenta', + 'Pid' => 'PID', + 'Level' => 'Nivo', + 'Message' => 'Poruka', + 'Line' => 'Linija', + 'More' => 'Više', + 'Clear' => 'Očisti', + '24BitColour' => '24 bitne boje', + '32BitColour' => '32 bitne boje', + '8BitGrey' => '8 bit siva nijansa', + 'Action' => 'Action', + 'Actual' => 'Stvarno', + 'AddNewControl' => 'Dodaj kontrolu', + 'AddNewMonitor' => 'Dodaj monitor', + 'AddNewServer' => 'Dodaj novi server', + 'AddNewStorage' => 'Dodaj novi disk', + 'AddNewUser' => 'Dodaj novog korisnika', + 'AddNewZone' => 'Dodaj novu zonu', + 'Alarm' => 'Alarm', + 'AlarmBrFrames' => 'Alarm
Sličice', + 'AlarmFrame' => 'Alarm sličica', + 'AlarmFrameCount' => 'Brzina snimanja alarma (u frejmovima)', + 'AlarmLimits' => 'Alarm limiti', + 'AlarmMaximumFPS' => 'Alarm Max SPS', + 'AlarmPx' => 'Alarm Px', + 'AlarmRefImageBlendPct' => 'Alarm Reference Image Blend %ge', + 'AlarmRGBUnset' => 'Morate postaviti RGB boju za alarm', + 'Alert' => 'Uzbuna', + 'All' => 'Sve', + 'AnalysisFPS' => 'Analiza frejmova', + 'AnalysisUpdateDelay' => 'Analysis Update Delay', + 'Apply' => 'Primjeni', + 'ApplyingStateChange' => 'Primjenjujem promjenu stanja', + 'ArchArchived' => 'Samo arhivirano', + 'Archive' => 'Arhiva', + 'Archived' => 'Ahivirano', + 'ArchUnarchived' => 'Samo nearhivirano', + 'Area' => 'Oblast', + 'AreaUnits' => 'Oblast (px/%)', + 'AttrAlarmFrames' => 'Alarm frejmovi', + 'AttrArchiveStatus' => 'Status arhive', + 'AttrAvgScore' => 'Prosj. score', + 'AttrCause' => 'Uzrok', + 'AttrStartDate' => 'Pocetni datum', + 'AttrEndDate' => 'Krajnji datum', + 'AttrStartDateTime' => 'Pocetni Datum/Vrijeme', + 'AttrEndDateTime' => 'Krajnji Datum/Vrijeme', + 'AttrDiskSpace' => 'Disk prostor', + 'AttrDiskBlocks' => 'Disk blokovi', + 'AttrDiskPercent' => 'Disk procentualno', + 'AttrDuration' => 'Trajanje', + 'AttrFrames' => 'Frejmovi', + 'AttrId' => 'Id', + 'AttrMaxScore' => 'Max. Score', + 'AttrMonitorId' => 'ID Kamere', + 'AttrMonitorName' => 'Naziv Kamere', + 'AttrStorageArea' => 'Storage Area', + 'AttrFilterServer' => 'Server Filter je pokrenut na', + 'AttrMonitorServer' => 'Server Monitor je pokrenut na', + 'AttrStorageServer' => 'Server Hosting Storage', + 'AttrStateId' => 'Status', + 'AttrName' => 'Naziv', + 'AttrNotes' => 'Bilješke', + 'AttrSystemLoad' => 'Opterećenje sistema', + 'AttrStartTime' => 'Vrijeme početka', + 'AttrEndTime' => 'Vrijeme završetka', + 'AttrTotalScore' => 'Ukupan score', + 'AttrStartWeekday' => 'Početni dan', + 'AttrEndWeekday' => 'Krajnji dan', + 'Auto' => 'Automatski', + 'AutoStopTimeout' => 'Auto Stop Timeout', + 'Available' => 'Dostupno', + 'AvgBrScore' => 'Avg.
Score', + 'Available' => 'Dostupno', + 'Background' => 'Pozadina', + 'BackgroundFilter' => 'Pokreni filter u pozadini', + 'BadAlarmFrameCount' => 'Brojač alarm frejmova mora biti tipa integer počevši od jedan ili više', + 'BadAlarmMaxFPS' => 'Max FPS za alarm mora biti pozitivan cjeli broj ili broj sa pomičnim zarezom', + 'BadAnalysisFPS' => 'Broj frejmova za analitiku mora pozitivan cjeli broj ili broj sa pomičnim zarezom', + 'BadAnalysisUpdateDelay'=> 'Vrijeme zadrške analitike mora biti broj od nula ili više', + 'BadChannel' => 'Kanal mora biti postavljen na cjeli broj nula ili više', + 'BadDevice' => 'Uredaj mora biti postavljen na validnu vrijednost', + 'BadFormat' => 'Format mora biti postavljen na validnu vrijenost', + 'BadFPSReportInterval' => 'FPS report interval buffer count must be an integer of 0 or more', + 'BadFrameSkip' => 'Frame skip count must be an integer of zero or more', + 'BadMotionFrameSkip' => 'Motion Frame skip count must be an integer of zero or more', + 'BadHeight' => 'Height must be set to a valid value', + 'BadHost' => 'Host must be set to a valid ip address or hostname, do not include http://', + 'BadImageBufferCount' => 'Image buffer size must be an integer of 10 or more', + 'BadLabelX' => 'Label X co-ordinate must be set to an integer of zero or more', + 'BadLabelY' => 'Label Y co-ordinate must be set to an integer of zero or more', + 'BadMaxFPS' => 'Maximum FPS must be a positive integer or floating point value', + 'BadNameChars' => 'Names may only contain alphanumeric characters plus spaces, hyphen and underscore', + 'BadPalette' => 'Palette must be set to a valid value', + 'BadColours' => 'Target colour must be set to a valid value', + 'BadPath' => 'Path must be set to a valid value', + 'BadPort' => 'Port must be set to a valid number', + 'BadPostEventCount' => 'Post event image count must be an integer of zero or more', + 'BadPreEventCount' => 'Pre event image count must be at least zero, and less than image buffer size', + 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', + 'BadSectionLength' => 'Section length must be an integer of 30 or more', + 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadStreamReplayBuffer' => 'Stream replay buffer must be an integer of zero or more', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', + 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', + 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', + 'BadWidth' => 'Width must be set to a valid value', + 'Bandwidth' => 'Propusnost', + 'BandwidthHead' => 'propusnost', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing + 'BlobPx' => 'Blob Px', + 'Blobs' => 'Blobs', + 'BlobSizes' => 'Blob velicine', + 'Brightness' => 'Svjetloća', + 'Buffer' => 'Bufer', + 'Buffers' => 'Buferi', + 'CanAutoFocus' => 'Podržava Auto fokusiranje', + 'CanAutoGain' => 'Podržava Auto pojačanje', + 'CanAutoIris' => 'Podržava Auto blenda', + 'CanAutoWhite' => 'Podržava Auto balans bijel.', + 'CanAutoZoom' => 'Podržava Auto zum', + 'Cancel' => 'otkaži', + 'CancelForcedAlarm' => 'Otkaži prisilni alarm', + 'CanFocusAbs' => 'Podržava Abs fokus', + 'CanFocus' => 'Podržava Fokus', + 'CanFocusCon' => 'Podržava Kontinuirani fokus', + 'CanFocusRel' => 'Podržava Relativni fokus', + 'CanGainAbs' => 'Podržava Aps. pojačanje', + 'CanGain' => 'Podržava Pojačanje ', + 'CanGainCon' => 'Podržava Kontinuirano pojačanje', + 'CanGainRel' => 'Podržava Relativno pojačanje', + 'CanIrisAbs' => 'Podržava Aps. blenda', + 'CanIris' => 'Podržava Blenda', + 'CanIrisCon' => 'Podržava kontinuirana blenda', + 'CanIrisRel' => 'Podržava Relativna blenda', + 'CanMoveAbs' => 'Podržava Aps. kretanje', + 'CanMove' => 'Podržava Kretanje', + 'CanMoveCon' => 'Podržava Kontinuirano kretanje', + 'CanMoveDiag' => 'Podržava Dijagonalno kretanje', + 'CanMoveMap' => 'Podržava Mapirano kretanje', + 'CanMoveRel' => 'Podržava Relativno kretanje', + 'CanPan' => 'Podržava Pomak' , + 'CanReset' => 'PodržavaReset', + 'CanSetPresets' => 'Podržava presetove', + 'CanSleep' => 'Podržava Sleep', + 'CanTilt' => 'Podržava nagib', + 'CanWake' => 'Podržava Wake', + 'CanWhiteAbs' => 'Podržava Aps. balans bijele boje', + 'CanWhiteBal' => 'Podržava balans bijel.', + 'CanWhite' => 'Podržava bijelu', + 'CanWhiteCon' => 'Podržava kont. balans bijele boje', + 'CanWhiteRel' => 'Podržava relativ. balans bijele boje', + 'CanZoomAbs' => 'Podržava Aps. zoom', + 'CanZoom' => 'Podržava Zoom', + 'CanZoomCon' => 'Podržava kontinuirani Zoom', + 'CanZoomRel' => 'Podržava Relativni zoom', + 'CaptureHeight' => 'Visina slike', + 'CaptureMethod' => 'Metoda snimanja', + 'CaptureResolution' => 'Snimi rezoluciju', + 'CapturePalette' => 'Paleta boja', + 'CaptureWidth' => 'Širina slike', + 'Cause' => 'Uzrok', + 'CheckMethod' => 'Metoda provjere alarma', + 'ChooseDetectedCamera' => 'Odaberi otkrivenu kameru', + 'ChooseFilter' => 'Odaberi filter', + 'ChooseLogFormat' => 'Odaberi dugi format', + 'ChooseLogSelection' => 'Odaberi dugu selekciju', + 'ChoosePreset' => 'Odaberi preset', + 'CloneMonitor' => 'Kloniraj', + 'Close' => 'Zatvori', + 'Colour' => 'Bojs', + 'Command' => 'Komanda', + 'ConcurrentFilter' => 'Istovremeno pokreni filter', + 'Config' => 'Postavke', + 'ConfiguredFor' => 'Podešeno za', + 'ConfirmDeleteEvents' => 'Sigurni ste da želite izbrisati odabrane događaje?', + 'ConfirmPassword' => 'Potvrdi lozinku', + 'ConjAnd' => 'i', + 'ConjOr' => 'ili', + 'Console' => 'Konzola', + 'ContactAdmin' => 'Molimo konkatirajte svog administratora za detalje.', + 'Continue' => 'Nastavi', + 'Contrast' => 'Kontrast', + 'ControlAddress' => 'Kontrolna adresa', + 'ControlCap' => 'Control Capability', + 'ControlCaps' => 'Control Capabilities', + 'Control' => 'PTZ kontole', + 'ControlDevice' => 'Kontroliši uređaj', + 'Controllable' => 'Moguće kontrolisati', + 'ControlType' => 'Tipa kontrole', + 'Current' => 'Tekuće', + 'Cycle' => 'Kruži', + 'CycleWatch' => 'Kružni prikaz', + 'Day' => 'Dan', + 'Debug' => 'Debug', + 'DefaultRate' => 'Podrazumjevana stopa', + 'DefaultScale' => 'Podrazumjevani razmjer', + 'DefaultView' => 'Podrazumjevani prikaz', + 'Deinterlacing' => 'Deinterlacing', + 'RTSPDescribe' => 'Use RTSP Response Media URL', + 'Delay' => 'Zadrška', + 'DeleteAndNext' => 'Izbriši & Sljedeće', + 'DeleteAndPrev' => 'Izbriši & Preth', + 'Delete' => 'Izbriši', + 'DeleteSavedFilter' => 'Izbriši spremljeni filter', + 'Description' => 'Opis', + 'DetectedCameras' => 'Detektovane kamere:', + 'DetectedProfiles' => 'Otkriveni profili', + 'DeviceChannel' => 'Kanal', + 'DeviceFormat' => 'Sistem boja', + 'DeviceNumber' => 'Broj uređaja', + 'DevicePath' => 'Putanja uređaja', + 'Device' => 'Uređaj', + 'Devices' => 'Uređaji', + 'Dimensions' => 'Dimenzije', + 'DisableAlarms' => 'Onemogući alarme', + 'Disk' => 'Disk', + 'Display' => 'Prikaz', + 'Displaying' => 'Prikazujem', + 'DonateAlready' => 'Ne, već sam napravio donaciju.', + 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', + 'Donate' => 'Molimo donirajte', + 'DonateRemindDay' => 'Ne još, podsjetime za 1 dan', + 'DonateRemindHour' => 'Ne još, podsjetime za 1 sat', + 'DonateRemindMonth' => 'Ne još, podsjeti me za jedan mjesec', + 'DonateRemindNever' => 'Ne, ne želim donirati, nemoj me više podsjećati.', + 'DonateRemindWeek' => 'Ne još, podsjeti me za sedam dana.', + 'DonateYes' => 'Da, želim da doniram sada.', + 'DoNativeMotionDetection'=> 'Nativna detekcija pokreta', + 'Download' => 'Preuzmi', + 'DuplicateMonitorName' => 'Dupliciraj ime monitora', + 'Duration' => 'Trajanje', + 'Edit' => 'Uredi', + 'EditLayout' => 'Uredi raspored', + 'Email' => 'Email', + 'EnableAlarms' => 'Omogući alarme', + 'Enabled' => 'Omogućeno', + 'EnterNewFilterName' => 'Unesi novo ime za filter', + 'ErrorBrackets' => 'Greška, provjerite da li imate jednak broj otvorenih i zatvorenih zagrada.', + 'Error' => 'Greška', + 'ErrorValidValue' => 'Greška, osigurajte se da svi pojmovi imaju valide vrijednosti', + 'Etc' => 'itd', + 'Event' => 'Događaj', + 'EventFilter' => 'Filter događaja', + 'EventId' => 'ID događaja', + 'EventName' => 'Naziv događaja', + 'EventPrefix' => 'Prefiks događaja', + 'Events' => 'Događaji', + 'Exclude' => 'Isključi', + 'Execute' => 'Izvrši', + 'ExportDetails' => 'Izvezi detalje o događaju', + 'Exif' => 'Umetni EXIF podatke u sliku', + 'Export' => 'Izvezi', + 'DownloadVideo' => 'Preuzmi video', + 'GenerateDownload' => 'Generiši preuzimanje', + 'ExportFailed' => 'Izvoz nije uspio', + 'ExportFormat' => 'Format za izvoz', + 'ExportFormatTar' => 'Tar', + 'ExportFormatZip' => 'Zip', + 'ExportFrames' => 'Izvezi detalje frejma', + 'ExportImageFiles' => 'Izvezi slike', + 'ExportLog' => 'Izvezi zapisnik', + 'Exporting' => 'Izvozim', + 'ExportMiscFiles' => 'Izvezi druge fajlove (ukoliko postoje)', + 'ExportOptions' => 'Opcije izvoženja', + 'ExportSucceeded' => 'Izvoz uspio', + 'ExportVideoFiles' => 'Izvezi video fileove (ukoliko postoje)', + 'Far' => 'Far', + 'FastForward' => 'Naprijed', + 'Feed' => 'Feed', + 'Ffmpeg' => 'Ffmpeg', + 'File' => 'File', + 'FilterArchiveEvents' => 'Arhiviraj pronađeno', + 'FilterUpdateDiskSpace' => 'Ažuriraj korišteni prostor na disku', + 'FilterDeleteEvents' => 'Izbriši sve pronađeno', + 'FilterMoveEvents' => 'Premjesti pronađeno', + 'FilterEmailEvents' => 'Pošalji detalje mailom', + 'FilterExecuteEvents' => 'Izvrši sljededeću komandu', + 'FilterLog' => 'Filtriraj zapis', + 'FilterMessageEvents' => 'Message details of all matches', + 'FilterPx' => 'Filter Px', + 'Filter' => 'Filter', + 'Filters' => 'Filteri', + 'FilterUnset' => 'Morate navesti širinu i visinu filtera', + 'FilterUploadEvents' => 'Učitaj sve događaje', + 'FilterVideoEvents' => 'Napravi video', + 'First' => 'Prvi', + 'FlippedHori' => 'Zaokrenuto horizontalno', + 'FlippedVert' => 'Zaokrenuto vertikalno', + 'FnNone' => 'nijedan', // Added 2013.08.16. + 'FnMonitor' => 'Monitor', // Added 2013.08.16. + 'FnModect' => 'Modect', // Added 2013.08.16. + 'FnRecord' => 'Record', // Added 2013.08.16. + 'FnMocord' => 'Mocord', // Added 2013.08.16. + 'FnNodect' => 'Nodect', // Added 2013.08.16. + 'Focus' => 'Fokus', + 'ForceAlarm' => 'Prisilni alarm', + 'Format' => 'Format', + 'FPS' => 'fps', + 'FPSReportInterval' => 'FPS Report Interval', + 'Frame' => 'Frame', + 'FrameId' => 'Frame Id', + 'FrameRate' => 'Frame Rate', + 'Frames' => 'Frejmovi', + 'FrameSkip' => 'Preskoči frejm', + 'MotionFrameSkip' => 'Motion Frame Skip', + 'FTP' => 'FTP', + 'Func' => 'Func', + 'Function' => 'Funkcija', + 'Gain' => 'Pojačanje', + 'General' => 'Opšte', + 'GenerateVideo' => 'Generiši video', + 'GeneratingVideo' => 'Generiši video', + 'GoToZoneMinder' => 'Idi na ZoneMinder.com', + 'Grey' => 'Siva', + 'Group' => 'Grupa', + 'Groups' => 'Grupe', + 'HasFocusSpeed' => 'Posjeduje brzo fokusiranja', + 'HasGainSpeed' => 'Posjeduje brzo pojačanja', + 'HasHomePreset' => 'Has Home Preset', + 'HasIrisSpeed' => 'Posjeduje brzu blendu', + 'HasPanSpeed' => 'Posjeduje brzi pomak', + 'HasPresets' => 'Posjeduje pre-setove', + 'HasTiltSpeed' => 'Posjeduje brzi nagiba', + 'HasTurboPan' => 'Posjeduje turbo pomak', + 'HasTurboTilt' => 'Posjeduje turbo nagib', + 'HasWhiteSpeed' => 'Posjeduje brzo podeš.bijele', + 'HasZoomSpeed' => 'Posjeduje brzi zoom', + 'HighBW' => 'High B/W', + 'High' => 'veliku', + 'Home' => 'Početna', + 'Hostname' => 'Hostname', + 'Hour' => 'Sat', + 'Hue' => 'Nijansa', + 'Id' => 'Id', + 'Idle' => 'Na čekanju', + 'Ignore' => 'Zanemari', + 'ImageBufferSize' => 'Veličina slikovnog bufera (u frejmovima)', + 'Image' => 'Slika', + 'Images' => 'Slike', + 'Include' => 'Uključi', + 'In' => 'U', + 'Inverted' => 'Invertirano', + 'Iris' => 'Blenda', + 'KeyString' => 'Key String', + 'Label' => 'Oznaka', + 'Language' => 'Jezik', + 'Last' => 'Zadnje', + 'Layout' => 'Raspored', + 'Libvlc' => 'Libvlc', + 'LimitResultsPost' => 'results only', // This is used at the end of the phrase 'Limit to first N results only' + 'LimitResultsPre' => 'Limit to first', // This is used at the beginning of the phrase 'Limit to first N results only' + 'LinkedMonitors' => 'Povezani monitori', + 'List' => 'Popis', + 'ListMatches' => 'Prikaži pronađeno', + 'Load' => 'Opterećenje', + 'Local' => 'Lokalno', + 'Log' => 'Zapis', + 'Logs' => 'Zapisi', + 'Logging' => 'Dnevnik događaja', + 'LoggedInAs' => 'Prijavljen kao', + 'LoggingIn' => 'Prijavljujem', + 'Login' => 'prijava', + 'Logout' => 'odjava', + 'LowBW' => 'Low B/W', + 'Low' => 'nisku', + 'Main' => 'Glavno', + 'Man' => 'Man', + 'Manual' => 'Ručno', + 'Mark' => 'Označi', + 'MaxBandwidth' => 'Max propusnost', + 'MaxBrScore' => 'Max.
Score', + 'MaxFocusRange' => 'Max raspon fokusa', + 'MaxFocusSpeed' => 'Max brzina fokusa', + 'MaxFocusStep' => 'Max korak fokusa', + 'MaxGainRange' => 'Max raspon pojačanja', + 'MaxGainSpeed' => 'Max brzina pojačanja', + 'MaxGainStep' => 'Max korak pojačanja', + 'MaximumFPS' => 'Maximum FPS', + 'MaxIrisRange' => 'Max raspon blende', + 'MaxIrisSpeed' => 'Max brzina blende', + 'MaxIrisStep' => 'Max korak blende', + 'Max' => 'Max', + 'MaxPanRange' => 'Max raspon pomaka', + 'MaxPanSpeed' => 'Max brzina pomaka', + 'MaxPanStep' => 'Max korak pomaka', + 'MaxTiltRange' => 'Max raspon nagiba', + 'MaxTiltSpeed' => 'Max brzina nagiba', + 'MaxTiltStep' => 'Max korak nagiba', + 'MaxWhiteRange' => 'Max raspon bijele', + 'MaxWhiteSpeed' => 'Max brzina bijele', + 'MaxWhiteStep' => 'Max korak bijele', + 'MaxZoomRange' => 'Max raspon zumiranja', + 'MaxZoomSpeed' => 'Max brzina zumiranja', + 'MaxZoomStep' => 'Max korak zumiranja', + 'MediumBW' => 'Medium B/W', + 'Medium' => 'srednju', + 'MinAlarmAreaLtMax' => 'Min područje alarma mora biti manje od maksimalnog', + 'MinAlarmAreaUnset' => 'Morate zadati minimalni broj alarm piksela', + 'MinBlobAreaLtMax' => 'Min blob područje mora biti manje od maksimalnog', + 'MinBlobAreaUnset' => 'Morate zadati minimalni broj blob piksela', + 'MinBlobLtMinFilter' => 'Min blob oblast mora biti manja ili jednaka minimalnoj oblasti filtera', + 'MinBlobsLtMax' => 'Min blob mora biti manji od maksimalne', + 'MinBlobsUnset' => 'morate zadati minimalni broj blob-ova', + 'MinFilterAreaLtMax' => 'Minimalna oblast filtera mora biti manja od maksimalne', + 'MinFilterAreaUnset' => 'Morate zadati minimalni broj filter piksela', + 'MinFilterLtMinAlarm' => 'Min oblast filtera mora biti manja ili jednaka minimalnoj oblasti alarmne oblasti', + 'MinFocusRange' => 'Min raspon fokusiranja', + 'MinFocusSpeed' => 'Min brzina fokusiranja', + 'MinFocusStep' => 'Min korak fokusiranja', + 'MinGainRange' => 'Min raspon pojačanja', + 'MinGainSpeed' => 'Min brzina pojačanja', + 'MinGainStep' => 'Min korak pojačanja', + 'MinIrisRange' => 'Min raspon blende', + 'MinIrisSpeed' => 'Min brzina blende', + 'MinIrisStep' => 'Min korak blende', + 'MinPanRange' => 'Min raspon pomaka', + 'MinPanSpeed' => 'Min brzina pomaka', + 'MinPanStep' => 'Min korak pomaka', + 'MinPixelThresLtMax' => 'Min prag piksela mora biti manji od maksimalnog', + 'MinPixelThresUnset' => 'Morate zadati minimalni prag piksela', + 'MinTiltRange' => 'Min Tilt Range', + 'MinTiltSpeed' => 'Min Tilt Speed', + 'MinTiltStep' => 'Min Tilt Step', + 'MinWhiteRange' => 'Min raspon bijelog balansa', + 'MinWhiteSpeed' => 'Min brzina bijelog balansa', + 'MinWhiteStep' => 'Min White Bal. Step', + 'MinZoomRange' => 'Min raspon zumiranja', + 'MinZoomSpeed' => 'Min brzina zumiranja', + 'MinZoomStep' => 'Min korak zumiranja', + 'Misc' => 'Razno', + 'Mode' => 'Modus', + 'MonitorIds' => 'Monitor Ids', + 'Monitor' => 'Monitor', + 'MonitorPresetIntro' => 'Odaberite odgovarajuće pre-setove sa popisa.

Imajte u vidu da ovo može prepisati bilo koju vrijednost koja postoji za odabrane monitore.

', + 'MonitorPreset' => 'Monitor Preset', + 'MonitorProbeIntro' => 'Donji popis prikazuje otkrivene analogne i mrežne kamere, te da li se iste već koriste i da li su dostupne.

Odaberite željenu kameru sa donjeg popisa.

Imajte u vidu da ovo može prepisati bilo koju vrijednost koja postoji za odabrane monitore.

', + 'MonitorProbe' => 'Detektuj kameru', + 'Monitors' => 'Monitori', + 'Montage' => 'Montage', + 'MontageReview' => 'Montage pregled', + 'Month' => 'Mjesec', + 'Move' => 'Pomjeri', + 'MtgDefault' => 'Podrazumjevano', // Added 2013.08.15. + 'Mtg2widgrd' => '2-struka rešetka', // Added 2013.08.15. + 'Mtg3widgrd' => '3-struka rešetka', // Added 2013.08.15. + 'Mtg4widgrd' => '4-struka rešetka', // Added 2013.08.15. + 'Mtg3widgrx' => '3-wide grid, scaled, enlarge on alarm', // Added 2013.08.15. + 'MustBeGe' => 'mora biti veće ili jednako', + 'MustBeLe' => 'mora biti manje ili jednako', + 'MustConfirmPassword' => 'Morate potvrditi lozinku', + 'MustSupplyPassword' => 'Morate unjeti lozinku', + 'MustSupplyUsername' => 'Morate unjeti korisničko ime', + 'Name' => 'Ime', + 'Near' => 'Blizu', + 'Network' => 'Mreža', + 'NewGroup' => 'Nova grupa', + 'NewLabel' => 'Nova oznaka', + 'New' => 'Novo', + 'NewPassword' => 'Nova lozinka', + 'NewState' => 'Novi radni modus', + 'NewUser' => 'Novi korisnik', + 'Next' => 'Sljedeće', + 'NoDetectedCameras' => 'Nema otkrivenih kamera', + 'NoDetectedProfiles' => 'Nema otkrivenih profila', + 'NoFramesRecorded' => 'Nije ništa snimljeno za ovaj događaj', + 'NoGroup' => 'Nema grupe', + 'NoneAvailable' => 'Nijedno dostupno', + 'None' => 'Nijedno', + 'No' => 'Ne', + 'Normal' => 'Normalno', + 'NoSavedFilters' => 'NemaSnimljenihFiltera', + 'NoStatisticsRecorded' => 'Nema snimljenih statistika za ovaj događaj', + 'Notes' => 'Bilješke', + 'NumPresets' => 'Num Presets', + 'Off' => 'Isključeno', + 'On' => 'Uključeno', + 'OnvifProbe' => 'ONVIF detekcija', + 'OnvifProbeIntro' => 'The list below shows detected ONVIF cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', + 'OnvifCredentialsIntro' => 'Please supply user name and password for the selected camera.
If no user has been created for the camera then the user given here will be created with the given password.

', + 'Open' => 'Otvori', + 'OpEq' => 'jednako', + 'OpGtEq' => 'veće ili jednako od', + 'OpGt' => 'veće ', + 'OpIn' => 'in set', + 'OpLtEq' => 'manje ili jednako od', + 'OpLt' => 'manje od', + 'OpMatches' => 'matches', + 'OpNe' => 'nije jednako', + 'OpNotIn' => 'nije u ', + 'OpNotMatches' => 'ne poklapa se', + 'OpIs' => 'je', + 'OpIsNot' => 'nije', + 'OptionalEncoderParam' => 'Opcionalni parametri enkodera', + 'OptionHelp' => 'Option Help', + 'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.', + 'Options' => 'Opcije', + 'Order' => 'Redosljed', + 'OrEnterNewName' => 'ili unesi novo ime', + 'Orientation' => 'Orijentacija', + 'Out' => 'Izlaz', + 'OverwriteExisting' => 'Prepiši preko postojećeg', + 'Paged' => 'stranično', + 'PanLeft' => 'Pomak lijevo', + 'Pan' => 'Pomak', + 'PanRight' => 'Pomak desno', + 'PanTilt' => 'Pomak/Nagib', + 'Parameter' => 'Parametar', + 'Password' => 'Lozinka', + 'PasswordsDifferent' => 'Nova i potvrđena lozinka se razlikuju', + 'Paths' => 'Putanje', + 'Pause' => 'Pauza', + 'PhoneBW' => 'Telefon B/W', + 'Phone' => 'Telefon', + 'PixelDiff' => 'Piksel razli.', + 'Pixels' => 'pikseli', + 'PlayAll' => 'play all', + 'Play' => 'Play', + 'Plugins' => 'Plugini', + 'PleaseWait' => 'Molim čekati', + 'Point' => 'Point', + 'PostEventImageBuffer' => 'Br. frejmova poslije događaja', + 'PreEventImageBuffer' => 'Br. frejmova prije događaja', + 'PreserveAspect' => 'Zadrži omjer', + 'Preset' => 'Preset', + 'Presets' => 'Presets', + 'Prev' => 'Preth', + 'Privacy' => 'Privatnost', + 'PrivacyAbout' => 'O', + 'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.', + 'PrivacyContact' => 'Konakt', + 'PrivacyContactText' => 'Please contact us here for any questions regarding our privacy policy or to have your information removed.

For support, there are three primary ways to engage with the community:

Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.

', + 'PrivacyCookies' => 'Kolačići', + 'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.', + 'PrivacyTelemetry' => 'Telemetry', + 'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.', + 'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:
  • A unique identifier (UUID)
  • City based location is gathered by querying ipinfo.io. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!
  • Current time
  • Total number of monitors
  • Total number of events
  • System architecture
  • Operating system kernel, distro, and distro version
  • Version of ZoneMinder
  • Total amount of memory
  • Number of cpu cores
', + 'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:
  • Id
  • Name
  • Type
  • Function
  • Width
  • Height
  • Colours
  • MaxFPS
  • AlarmMaxFPS
', + 'PrivacyConclusionText' => 'We are NOT collecting any image specific data from your cameras. We don�t know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.', + 'Probe' => 'Detektuj kameru', + 'ProfileProbe' => 'Stream proba', + 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', + 'Progress' => 'Napredak', + 'Protocol' => 'Protkol', + 'Rate' => 'Stopa', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', + 'Real' => 'Stvarno', + 'Record' => 'Snimaj', + 'RefImageBlendPct' => 'Reference Image Blend %ge', + 'Refresh' => 'Osvježi', + 'RemoteHostName' => 'Naziv uređaja', + 'RemoteHostPath' => 'Putanja', + 'RemoteHostSubPath' => 'Pod-putanja', + 'RemoteHostPort' => 'Port', + 'RemoteImageColours' => 'Boje slike', + 'RemoteMethod' => 'Metoda', + 'RemoteProtocol' => 'Protokol', + 'Remote' => 'Udaljeno', + 'Rename' => 'Preimenuj', + 'ReplayAll' => 'Svi događaji', + 'ReplayGapless' => 'Gapless Events', + 'Replay' => 'Ponovo odigraj', + 'ReplaySingle' => 'Jedan događaj', + 'ReportEventAudit' => 'Audit Events Report', + 'ResetEventCounts' => 'Resetiraj događaje', + 'Reset' => 'Reset', + 'Restarting' => 'Restartiram', + 'Restart' => 'Restaruj', + 'RestrictedCameraIds' => 'Restricted Camera Ids', + 'RestrictedMonitors' => 'Ograničeni monitori', + 'ReturnDelay' => 'Vrati kašnjenje', + 'ReturnLocation' => 'Vrati lokaciju', + 'Rewind' => 'Premotaj', + 'RotateLeft' => 'Rotoraj ulijevo', + 'RotateRight' => 'Rotiraj udesno', + 'RTSPTransport' => 'RTSP Transport Protocol', + 'RunAudit' => 'Run Audit Process', + 'RunLocalUpdate' => 'Pokrenite zmupdate.pl za ažuriranje', + 'RunMode' => 'Modus rada', + 'Running' => 'Pokrenuto', + 'RunState' => 'Radni modus', + 'RunStats' => 'Pokreni stats proces', + 'RunTrigger' => 'Pokreni triger proces', + 'SaveAs' => 'Spremi kao', + 'SaveFilter' => 'Spremi Filter', + 'SaveJPEGs' => 'Spremi JPEGs', + 'Save' => 'Spremi', + 'Scale' => 'Razmjer', + 'Score' => 'Zbir', + 'Secs' => 'Secs', + 'Sectionlength' => 'Odaberi dužinu', + 'SelectMonitors' => 'SOdaberi monitore', + 'Select' => 'Odaberi', + 'SelectFormat' => 'Odaberi format', + 'SelectLog' => 'Odaberi zapis', + 'SelfIntersecting' => 'Polygon edges must not intersect', + 'SetNewBandwidth' => 'Postavi propusnost na', + 'SetPreset' => 'Postavi pozicije', + 'Set' => 'Postavi', + 'Settings' => 'Postavke', + 'ShowFilterWindow' => 'Prikaži prozor za filter', + 'ShowTimeline' => 'Prikaži vremensku liniju', + 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', + 'Size' => 'Veličina', + 'SkinDescription' => 'Izmjeni izgled za ovu sesiju', + 'CSSDescription' => 'Izmjeni css za ovu sesiju', + 'Sleep' => 'Sleep', + 'SortAsc' => 'Rastuće', + 'SortBy' => 'Sortiraj po', + 'SortDesc' => 'Padajuće', + 'Source' => 'Izvor', + 'SourceColours' => 'Source Colours', + 'SourcePath' => 'Putanja izvora ', + 'SourceType' => 'Izvor videa', + 'SpeedHigh' => 'Velika brzina', + 'SpeedLow' => 'Niska brzina', + 'SpeedMedium' => 'Srednja brzina', + 'Speed' => 'brzina', + 'SpeedTurbo' => 'Turbo brzina', + 'Start' => 'Start', + 'State' => 'Stanje', + 'Stats' => 'Statistka', + 'Status' => 'Status', + 'StatusUnknown' => 'Nepoznato', + 'StatusConnected' => 'Snimam', + 'StatusNotRunning' => 'Nije pokrenuto', + 'StatusRunning' => 'Ne snima', + 'StepBack' => 'Korak nazad', + 'StepForward' => 'Korak naprijed', + 'StepLarge' => 'Veliki korak', + 'StepMedium' => 'Srednji korak', + 'StepNone' => 'Bez koraka', + 'StepSmall' => 'Mali korak', + 'Step' => 'Korak', + 'Stills' => 'Stills', + 'Stopped' => 'Zaustavljeno', + 'Stop' => 'Zaustavi', + 'StorageArea' => 'Storage Area', + 'StorageDoDelete' => 'Brisanja', + 'StorageScheme' => 'Šema', + 'StreamReplayBuffer' => 'Stream Replay Image Buffer', + 'Stream' => 'Stream', + 'Submit' => 'Pošalji', + 'System' => 'Sistem', + 'TargetColorspace' => 'Rezolucija boja', + 'Tele' => 'Udaljeno', + 'Thumbnail' => 'Sličica', + 'Tilt' => 'Tilt', + 'TimeDelta' => 'Vremenska razlika', + 'Timeline' => 'Vremenska linija', + 'TimelineTip1' => 'Pass your mouse over the graph to view a snapshot image and event details.', // Added 2013.08.15. + 'TimelineTip2' => 'Click on the coloured sections of the graph, or the image, to view the event.', // Added 2013.08.15. + 'TimelineTip3' => 'Click on the background to zoom in to a smaller time period based around your click.', // Added 2013.08.15. + 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. + 'TimestampLabelFormat' => 'Timestamp format oznake', + 'TimestampLabelX' => 'Timestamp oznaka X', + 'TimestampLabelY' => 'Timestamp oznaka Y', + 'TimestampLabelSize' => 'Veličina fonta', + 'Timestamp' => 'Timestamp', + 'TimeStamp' => 'Vremenski pečat', + 'Time' => 'Vrijeme', + 'Today' => 'Danas', + 'Tools' => 'Alati', + 'Total' => 'Ukupno', + 'TotalBrScore' => 'Total
Score', + 'TrackDelay' => 'Kašnjenje', + 'TrackMotion' => 'Prati pokret', + 'Triggers' => 'Okidači', + 'TurboPanSpeed' => 'Turbo Pan brzina', + 'TurboTiltSpeed' => 'Turbo Tilt brzina', + 'Type' => 'Tip', + 'Unarchive' => 'Dearhiviraj', + 'Undefined' => 'Nedefinisano', + 'Units' => 'Mjere', + 'Unknown' => 'Nepoznato', + 'UpdateAvailable' => 'Dostupno je novo ažurranje za Zoneminder .', + 'UpdateNotNecessary' => 'Ažuriranje nije potrebno.', + 'Update' => 'Ažuiriaj', + 'Upload' => 'Upload', + 'Updated' => 'Ažurirano', + 'UsedPlugins' => 'Korišteni plugini ', + 'UseFilterExprsPost' => ' filter expressions', // This is used at the end of the phrase 'use N filter expressions' + 'UseFilterExprsPre' => 'Use ', // This is used at the beginning of the phrase 'use N filter expressions' + 'UseFilter' => 'Koristi filter', + 'Username' => 'Korisničko ime', + 'Users' => 'Korisnici', + 'User' => 'Korisnik', + 'Value' => 'Vrijednost', + 'VersionIgnore' => 'Ignoriši ovu verziju', + 'VersionRemindDay' => 'Podsjeti me za jedan dan', + 'VersionRemindHour' => 'Podsjeti me za jedan sat', + 'VersionRemindNever' => 'Ne podsjecaj me na nove verzije', + 'VersionRemindWeek' => 'Podsjeti me za sedam dana', + 'Version' => 'Verzija', + 'VideoFormat' => 'Video Format', + 'VideoGenFailed' => 'Generisanje videa nije uspjelo!', + 'VideoGenFiles' => 'Postojece video datoteke', + 'VideoGenNoFiles' => 'Video datoteke nisu pronadjene', + 'VideoGenParms' => 'Parametri za generisanje videa', + 'VideoGenSucceeded' => 'Generisanje videa uspjelo!', + 'VideoSize' => 'Velicina videa', + 'VideoWriter' => 'Video pisac', + 'Video' => 'Video', + 'ViewAll' => 'Pregledaj sve', + 'ViewEvent' => 'Pregled događaja', + 'ViewPaged' => 'Stanični pregled', + 'View' => 'Pregled', + 'V4L' => 'V4L', + 'V4LCapturesPerFrame' => 'Snimci po frejmu', + 'V4LMultiBuffer' => 'Višestr. bafer', + 'Wake' => 'Budi', + 'WarmupFrames' => 'Warmup frejmovi', + 'Watch' => 'Gledaj', + 'WebColour' => 'Web boja', + 'Web' => 'Web', + 'WebSiteUrl' => 'URL web stranice', + 'Week' => 'Sedmica', + 'WhiteBalance' => 'Balans bijele', + 'White' => 'Bijelo', + 'Wide' => 'Široko', + 'X10ActivationString' => 'X10 znakovni niz za aktiviranje', + 'X10InputAlarmString' => 'X10 ulazni znakovni niz za alarm', + 'X10OutputAlarmString' => 'X10 izlazni znakovni niz za alarm', + 'X10' => 'X10', + 'X' => 'X', + 'Yes' => 'Da', + 'YouNoPerms' => 'Nemate potrebne dozvole za pristup ovom resursu.', + 'Y' => 'Y', + 'ZoneAlarmColour' => 'Boja alarma (Red/Green/Blue)', + 'ZoneArea' => 'Oblast zone', + 'ZoneFilterSize' => 'Filter Width/Height (pixels)', + 'ZoneMinderLog' => 'ZoneMinder zapisnik', + 'ZoneMinMaxAlarmArea' => 'Min/Max alarmirana oblast', + 'ZoneMinMaxBlobArea' => 'Min/Max blob oblast', + 'ZoneMinMaxBlobs' => 'Min/Max Blobovi', + 'ZoneMinMaxFiltArea' => 'Min/Max filtrirane oblasti', + 'ZoneMinMaxPixelThres' => 'Min/Max Pixel Threshold (0-255)', + 'ZoneOverloadFrames' => 'Overload Frame Ignore Count', + 'ZoneExtendAlarmFrames' => 'Extend Alarm Frame Count', + 'Zones' => 'Zone', + 'Zone' => 'Zona', + 'ZoomIn' => 'Zoom In', + 'ZoomOut' => 'Zoom Out', + 'Zoom' => 'Zumiranje', +); + +// Complex replacements with formatting and/or placements, must be passed through sprintf +$CLANG = array( + 'CurrentLogin' => 'Prijavljeni ste kao \'%1$s\'', + 'EventCount' => '%1$s %2$s', // For example '37 Events' (from Vlang below) + 'LastEvents' => 'Last %1$s %2$s', // For example 'Last 37 Events' (from Vlang below) + 'LatestRelease' => 'Zadnja verzija servera je v%1$s, vi imate v%2$s.', + 'MonitorCount' => '%1$s %2$s', // For example '4 Monitors' (from Vlang below) + 'MonitorFunction' => 'Monitor %1$s Function', + 'RunningRecentVer' => 'Koristite najnoviju verziju Zoneminder servera, v%s.', + 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', +); + +// The next section allows you to describe a series of word ending and counts used to +// generate the correctly conjugated forms of words depending on a count that is associated +// with that word. +// This intended to allow phrases such a '0 potatoes', '1 potato', '2 potatoes' etc to +// conjugate correctly with the associated count. +// In some languages such as English this is fairly simple and can be expressed by assigning +// a count with a singular or plural form of a word and then finding the nearest (lower) value. +// So '0' of something generally ends in 's', 1 of something is singular and has no extra +// ending and 2 or more is a plural and ends in 's' also. So to find the ending for '187' of +// something you would find the nearest lower count (2) and use that ending. +// +// So examples of this would be +// $zmVlangPotato = array( 0=>'Potatoes', 1=>'Potato', 2=>'Potatoes' ); +// $zmVlangSheep = array( 0=>'Sheep' ); +// +// where you can have as few or as many entries in the array as necessary +// If your language is similar in form to this then use the same format and choose the +// appropriate zmVlang function below. +// If however you have a language with a different format of plural endings then another +// approach is required . For instance in Russian the word endings change continuously +// depending on the last digit (or digits) of the numerator. In this case then zmVlang +// arrays could be written so that the array index just represents an arbitrary 'type' +// and the zmVlang function does the calculation about which version is appropriate. +// +// So an example in Russian might be (using English words, and made up endings as I +// don't know any Russian!!) +// 'Potato' => array( 1=>'Potati', 2=>'Potaton', 3=>'Potaten' ), +// +// and the zmVlang function decides that the first form is used for counts ending in +// 0, 5-9 or 11-19 and the second form when ending in 1 etc. +// + +// Variable arrays expressing plurality, see the zmVlang description above +$VLANG = array( + 'Event' => array( 0=>'Events', 1=>'Event', 2=>'Events' ), + 'Monitor' => array( 0=>'Monitors', 1=>'Monitor', 2=>'Monitors' ), +); +// You will need to choose or write a function that can correlate the plurality string arrays +// with variable counts. This is used to conjugate the Vlang arrays above with a number passed +// in to generate the correct noun form. +// +// In languages such as English this is fairly simple +// Note this still has to be used with printf etc to get the right formatting +function zmVlang( $langVarArray, $count ) +{ + krsort( $langVarArray ); + foreach ( $langVarArray as $key=>$value ) + { + if ( abs($count) >= $key ) + { + return( $value ); + } + } + die( 'Error, unable to correlate variable language string' ); +} + +// This is an version that could be used in the Russian example above +// The rules are that the first word form is used if the count ends in +// 0, 5-9 or 11-19. The second form is used then the count ends in 1 +// (not including 11 as above) and the third form is used when the +// count ends in 2-4, again excluding any values ending in 12-14. +// +// function zmVlang( $langVarArray, $count ) +// { +// $secondlastdigit = substr( $count, -2, 1 ); +// $lastdigit = substr( $count, -1, 1 ); +// // or +// // $secondlastdigit = ($count/10)%10; +// // $lastdigit = $count%10; +// +// // Get rid of the special cases first, the teens +// if ( $secondlastdigit == 1 && $lastdigit != 0 ) +// { +// return( $langVarArray[1] ); +// } +// switch ( $lastdigit ) +// { +// case 0 : +// case 5 : +// case 6 : +// case 7 : +// case 8 : +// case 9 : +// { +// return( $langVarArray[1] ); +// break; +// } +// case 1 : +// { +// return( $langVarArray[2] ); +// break; +// } +// case 2 : +// case 3 : +// case 4 : +// { +// return( $langVarArray[3] ); +// break; +// } +// } +// die( 'Error, unable to correlate variable language string' ); +// } + +// This is an example of how the function is used in the code which you can uncomment and +// use to test your custom function. +//$monitors = array(); +//$monitors[] = 1; // Choose any number +//echo sprintf( $CLANG['MonitorCount'], count($monitors), zmVlang( $VLANG['VlangMonitor'], count($monitors) ) ); + +// In this section you can override the default prompt and help texts for the options area +// These overrides are in the form show below where the array key represents the option name minus the initial ZM_ +// So for example, to override the help text for ZM_LANG_DEFAULT do +$OLANG = array( + 'OPTIONS_FFMPEG' => array( + 'Help' => "Parameters in this field are passed on to FFmpeg. Multiple parameters can be separated by ,~~ ". + "Examples (do not enter quotes)~~~~". + "\"allowed_media_types=video\" Set datatype to request fromcam (audio, video, data)~~~~". + "\"reorder_queue_size=nnn\" Set number of packets to buffer for handling of reordered packets~~~~". + "\"loglevel=debug\" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug)" + ), + 'OPTIONS_RTSPTrans' => array( + 'Help' => "This sets the RTSP Transport Protocol for FFmpeg.~~ ". + "TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~". + "UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some 'smearing' while using UDP, if so try TCP~~". + "UDP Multicast - Use UDP Multicast as transport protocol~~". + "HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~" + ), + 'OPTIONS_LIBVLC' => array( + 'Help' => "Parameters in this field are passed on to libVLC. Multiple parameters can be separated by ,~~ ". + "Examples (do not enter quotes)~~~~". + "\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~". + "\"--verbose=2\" Set verbosity of libVLC" + ), + 'OPTIONS_EXIF' => array( + 'Help' => "Enable this option to embed EXIF data into each jpeg frame." + ), + 'OPTIONS_RTSPDESCRIBE' => array( + 'Help' => "Sometimes, during the initial RTSP handshake, the camera will send an updated media URL. ". + "Enable this option to tell ZoneMinder to use this URL. Disable this option to ignore the ". + "value from the camera and use the value as entered in the monitor configuration~~~~". + "Generally this should be enabled. However, there are cases where the camera can get its". + "own URL incorrect, such as when the camera is streaming through a firewall"), + 'OPTIONS_MAXFPS' => array( + 'Help' => "This field has certain limitations when used for non-local devices.~~ ". + "Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, ". + "and missed events~~". + "For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the". + " camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ". + "In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~". + "Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera ". + "for new images. In this case, it is safe to use the field." + ), + +// 'LANG_DEFAULT' => array( +// 'Prompt' => "This is a new prompt for this option", +// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked" +// ), +); + +?> diff --git a/web/lang/big5_big5.php b/web/lang/big5_big5.php index b8bc173a5..63ffb2c1d 100644 --- a/web/lang/big5_big5.php +++ b/web/lang/big5_big5.php @@ -82,6 +82,8 @@ $SLANG = array( 'Actual' => 'Actual', 'AddNewControl' => '新增控制', 'AddNewMonitor' => '新增監視', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => '新增使用者', 'AddNewZone' => '新增監視區', 'Alarm' => '警報', @@ -109,22 +111,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archive Status', 'AttrAvgScore' => 'Average Score', 'AttrCause' => 'Cause', - 'AttrDate' => 'Date', - 'AttrDateTime' => 'Date/Time', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duration', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Frames', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. Score', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Monitor Name', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Name', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Time', 'AttrTotalScore' => 'Total Score', - 'AttrWeekday' => 'Weekday', 'Auto' => '自動', 'AutoStopTimeout' => '時間過自動停止', 'Available' => 'Available', // Added - 2009-03-31 @@ -157,9 +169,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => '頻寬', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -223,10 +237,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => '關閉', 'Colour' => 'Colour', 'Command' => 'Command', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => '配置為', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -284,9 +300,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => '下載', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => '歷時', 'Edit' => '編輯', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => '啟動警報', 'Enabled' => '啟用', @@ -303,6 +321,7 @@ $SLANG = array( 'Events' => '事件', 'Exclude' => '不包含', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => '輸出', 'ExportDetails' => '輸出事件細項', 'ExportFailed' => '輸出失敗', @@ -332,8 +351,10 @@ $SLANG = array( 'FilterExecuteEvents' => '自動執行符合指令', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => '自動發出符合訊息', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', 'FilterUnset' => '您必需設定濾鏡的寬度和高度', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => '自動上傳符合項目', 'FilterVideoEvents' => '自動產生符合的影像檔', 'Filters' => '濾鏡', @@ -358,6 +379,7 @@ $SLANG = array( 'Function' => '功能', 'Gain' => 'Gain', 'General' => '一般', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => '輸出影片', 'GeneratingVideo' => '輸出影片中', 'GoToZoneMinder' => 'Go to ZoneMinder.com', @@ -378,6 +400,7 @@ $SLANG = array( 'High' => '高', 'HighBW' => 'High B/W', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => '時', 'Hue' => 'Hue', 'Id' => 'Id', @@ -402,6 +425,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => '列出', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => '載入', 'Local' => 'Local', 'Log' => 'Log', // Added - 2011-06-16 @@ -488,6 +512,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => '監視', 'Montage' => '全部顯示', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => '月', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -514,6 +539,7 @@ $SLANG = array( 'Next' => '下一步', 'No' => 'No', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'There are no frames recorded for this event', 'NoGroup' => 'No Group', // Added - 2009-02-08 'NoSavedFilters' => 'NoSavedFilters', @@ -532,6 +558,8 @@ $SLANG = array( 'OpGt' => 'greater than', 'OpGtEq' => 'greater than or equal to', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'less than', 'OpLtEq' => 'less than or equal to', 'OpMatches' => 'matches', @@ -541,6 +569,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'OptionHelp', 'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => '銓垣專用',//進階選項 'OrEnterNewName' => 'or enter new name', 'Order' => '順序', @@ -578,9 +607,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Rate', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => '錄影', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => '參考影像混合 %ge', 'Refresh' => '更新', 'Remote' => 'Remote', @@ -596,6 +629,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => '重新啟動', @@ -614,6 +648,7 @@ $SLANG = array( 'Save' => '存檔', 'SaveAs' => '儲存為', 'SaveFilter' => 'Save Filter', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Scale', 'Score' => '分數', 'Secs' => 'Secs', @@ -630,6 +665,7 @@ $SLANG = array( 'ShowFilterWindow' => '顯示過濾視窗', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -649,6 +685,10 @@ $SLANG = array( 'State' => 'State', 'Stats' => 'Stats', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -659,6 +699,8 @@ $SLANG = array( 'Stills' => '靜止', 'Stop' => '停止', 'Stopped' => '已停止', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => '串流', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', @@ -678,6 +720,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => '時間格式', 'TimestampLabelFormat' => '時間標示格式', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => '時間標示 X', 'TimestampLabelY' => '時間標示 Y', 'Today' => 'Today', @@ -724,6 +767,7 @@ $SLANG = array( 'VideoGenParms' => '輸出影片參數', 'VideoGenSucceeded' => 'Video Generation Succeeded!', // Added - 2009-02-08 'VideoSize' => '影片尺寸', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => '檢視', 'ViewAll' => '全部檢視', 'ViewEvent' => 'View Event', // Added - 2009-02-08 @@ -733,6 +777,7 @@ $SLANG = array( 'Watch' => 'Watch', 'Web' => 'Web', 'WebColour' => 'Web Colour', // Added - 2009-02-08 + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => '週', 'White' => 'White', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/cn_zh.php b/web/lang/cn_zh.php index 9ed821417..177f44377 100644 --- a/web/lang/cn_zh.php +++ b/web/lang/cn_zh.php @@ -78,6 +78,8 @@ $SLANG = array( 'Actual' => '实际', 'AddNewControl' => '新建控制', 'AddNewMonitor' => '新建监视器', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => '新建用户', 'AddNewZone' => '新建区域', 'Alarm' => '报警', @@ -105,22 +107,32 @@ $SLANG = array( 'AttrArchiveStatus' => '存档状态', 'AttrAvgScore' => '平均分数', 'AttrCause' => '原因', - 'AttrDate' => '日期', - 'AttrDateTime' => '日期/时间', 'AttrDiskBlocks' => '磁碟区块', 'AttrDiskPercent' => '磁碟百分比', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => '过程', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => '帧', 'AttrId' => 'Id', 'AttrMaxScore' => '最大分数', 'AttrMonitorId' => '监视器 Id', 'AttrMonitorName' => '监视器名称', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => '名称', 'AttrNotes' => '备注', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => '系统负载', - 'AttrTime' => '时间', 'AttrTotalScore' => '总分数', - 'AttrWeekday' => '星期', 'Auto' => '自动', 'AutoStopTimeout' => '超时自动停止', 'Available' => 'Available', // Added - 2009-03-31 @@ -153,9 +165,11 @@ $SLANG = array( 'BadRefBlendPerc' => '参考混合百分比必须设为一个正整数', 'BadSectionLength' => '节长度必须设为30的整数倍', 'BadSignalCheckColour' => '信号检查颜色必须设为有效的RGB颜色字符', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => '流重放缓冲必须为零或更多整数', 'BadWarmupCount' => '预热帪必须设为零或更多整数', 'BadWebColour' => 'Web颜色必须设为有效Web颜色字符', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => '宽度必须设为有效值', 'Bandwidth' => '带宽', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -219,10 +233,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => '选择预置', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => '关闭', 'Colour' => '彩色', 'Command' => '命令', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => '配置', 'ConfiguredFor' => '配置标的', 'ConfirmDeleteEvents' => '确认希望删除所选事件?', @@ -280,9 +296,11 @@ $SLANG = array( 'DonateRemindWeek' => '现在不,1星期内再次提醒我', 'DonateYes' => '好,我现在就捐款', 'Download' => '下载', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Duration', 'Edit' => '编辑', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => '启动报警', 'Enabled' => '已启动', @@ -299,6 +317,7 @@ $SLANG = array( 'Events' => '事件', 'Exclude' => '排除', 'Execute' => '执行', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => '导出', 'ExportDetails' => '导出时间详情', 'ExportFailed' => '导出失败', @@ -328,8 +347,10 @@ $SLANG = array( 'FilterExecuteEvents' => '执行全部匹配项命令', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => '全部匹配项的信息详情', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => '过滤器像素', 'FilterUnset' => '您必须指定过滤器宽度和高度', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => '上传全部匹配项', 'FilterVideoEvents' => '为全部匹配项创建视频', 'Filters' => '过滤器', @@ -354,6 +375,7 @@ $SLANG = array( 'Function' => '功能', 'Gain' => '增益', 'General' => '一般', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => '创建视频', 'GeneratingVideo' => '正在创建视频', 'GoToZoneMinder' => '访问 ZoneMinder.com', @@ -374,6 +396,7 @@ $SLANG = array( 'High' => '高', 'HighBW' => '高 B/W', 'Home' => '主页', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => '小时', 'Hue' => '色调', 'Id' => 'Id', @@ -398,6 +421,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => '管理监视器', 'List' => '列表', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => '加载', 'Local' => '本地', 'Log' => 'Log', // Added - 2011-06-16 @@ -484,6 +508,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => '监视器', 'Montage' => '镜头组接', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => '月', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -510,6 +535,7 @@ $SLANG = array( 'Next' => '下一个', 'No' => '不', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => '该事件没有相关帧的记录', 'NoGroup' => '无组', 'NoSavedFilters' => '没有保存过滤器', @@ -528,6 +554,8 @@ $SLANG = array( 'OpGt' => '大于', 'OpGtEq' => '大于等于', 'OpIn' => '在集', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => '小于', 'OpLtEq' => '小于等于', 'OpMatches' => '匹配', @@ -537,6 +565,7 @@ $SLANG = array( 'Open' => '打开', 'OptionHelp' => '选项帮助', 'OptionRestartWarning' => '这些改动在系统运行时可以不会完全生效.\n 当你设置完毕改动后\n请确认\n您重新启动 ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => '选项', 'OrEnterNewName' => '或输入新名词', 'Order' => '次序', @@ -574,9 +603,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => '协议', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => '速率', 'Real' => '实际', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => '记录', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => '参考影像混合 %ge', 'Refresh' => '刷新', 'Remote' => '远程', @@ -592,6 +625,7 @@ $SLANG = array( 'ReplayAll' => '全部事件', 'ReplayGapless' => '无间隙事件', 'ReplaySingle' => '单一事件', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => '重置', 'ResetEventCounts' => '重置事件数', 'Restart' => '重启动', @@ -610,6 +644,7 @@ $SLANG = array( 'Save' => '保存', 'SaveAs' => '另存为', 'SaveFilter' => '存储过滤器', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => '比例', 'Score' => '分数', 'Secs' => '秒', @@ -626,6 +661,7 @@ $SLANG = array( 'ShowFilterWindow' => '显示过滤器视窗', 'ShowTimeline' => '显示时间轴', 'SignalCheckColour' => '型号检查颜色', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => '大小', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => '睡眠', @@ -645,6 +681,10 @@ $SLANG = array( 'State' => '状态', 'Stats' => '统计', 'Status' => '状况', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => '步进', 'StepBack' => '单步后退', 'StepForward' => '单步前进', @@ -655,6 +695,8 @@ $SLANG = array( 'Stills' => '静止', 'Stop' => '停止', 'Stopped' => '已停止', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => '流', 'StreamReplayBuffer' => '流重放影像缓冲', 'Submit' => '发送', @@ -674,6 +716,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => '时间戳', 'TimestampLabelFormat' => '时间戳标签格式', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => '时间戳标签 X', 'TimestampLabelY' => '时间戳标签 Y', 'Today' => '今天', @@ -720,6 +763,7 @@ $SLANG = array( 'VideoGenParms' => '视频产生参数', 'VideoGenSucceeded' => '视频产生成功!', 'VideoSize' => '视频尺寸', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => '查看', 'ViewAll' => '查看全部', 'ViewEvent' => '查看事件', @@ -729,6 +773,7 @@ $SLANG = array( 'Watch' => '观察', 'Web' => 'Web', 'WebColour' => 'Web颜色', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => '周', 'White' => '白', 'WhiteBalance' => '白平衡', diff --git a/web/lang/cs_cz.php b/web/lang/cs_cz.php index adcdeb61d..a7939dbff 100644 --- a/web/lang/cs_cz.php +++ b/web/lang/cs_cz.php @@ -78,6 +78,8 @@ $SLANG = array( 'Actual' => 'Skutečná', 'AddNewControl' => 'Přidat nové řízení', 'AddNewMonitor' => 'Přidat kameru', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Přidat uživatele', 'AddNewZone' => 'Přidat zónu', 'Alarm' => 'Alarm', @@ -105,22 +107,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archiv status', 'AttrAvgScore' => 'Prům. skóre', 'AttrCause' => 'Příčina', - 'AttrDate' => 'Datum', - 'AttrDateTime' => 'Datum/Čas', 'AttrDiskBlocks' => 'Bloky disku', 'AttrDiskPercent' => 'Zaplnění disku', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Průběh', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Snímky', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. skóre', 'AttrMonitorId' => 'Kamera Id', 'AttrMonitorName' => 'Jméno kamery', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Jméno', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Čas', 'AttrTotalScore' => 'Celkové skóre', - 'AttrWeekday' => 'Den v týdnu', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Časový limit pro vypršení', 'Available' => 'Available', // Added - 2009-03-31 @@ -153,9 +165,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Rychlost sítě', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -219,10 +233,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Zavřít', 'Colour' => 'Barva', 'Command' => 'Příkaz', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Nastavení', 'ConfiguredFor' => 'Nastaveno pro', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -280,9 +296,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Nyní ne, připomenout za týden', 'DonateYes' => 'Ano, chcit podpořit ZoneMinder nyní', 'Download' => 'Stáhnout', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Průběh', 'Edit' => 'Editovat', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Povolit alarmy', 'Enabled' => 'Povoleno', @@ -299,6 +317,7 @@ $SLANG = array( 'Events' => 'Záznamy', 'Exclude' => 'Vyjmout', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportovat', 'ExportDetails' => 'Exportovat detaily záznamu', 'ExportFailed' => 'Chyba při exportu', @@ -328,8 +347,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Spustit příkaz na všech nalezených', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Podat zprávu o všech nalezených', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filtr Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Uploadovat nalezené', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filtry', @@ -354,6 +375,7 @@ $SLANG = array( 'Function' => 'Funkce', 'Gain' => 'Zisk', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Generovat video', 'GeneratingVideo' => 'Generuji video', 'GoToZoneMinder' => 'Jít na ZoneMinder.com', @@ -374,6 +396,7 @@ $SLANG = array( 'High' => 'Rychlá', 'HighBW' => 'Rychlá B/W', 'Home' => 'Domů', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Hodina', 'Hue' => 'Odstín', 'Id' => 'Id', @@ -398,6 +421,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'Seznam', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Load', 'Local' => 'Lokální', 'Log' => 'Log', // Added - 2011-06-16 @@ -484,6 +508,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Kamery', 'Montage' => 'Sestřih', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Měsíc', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -510,6 +535,7 @@ $SLANG = array( 'Next' => 'Další', 'No' => 'Ne', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Pro tento snímek nejsou žádné záznamy', 'NoGroup' => 'No Group', 'NoSavedFilters' => 'Žádné uložené filtry', @@ -528,6 +554,8 @@ $SLANG = array( 'OpGt' => 'větší', 'OpGtEq' => 'větší nebo rovno', 'OpIn' => 'nin set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'menší', 'OpLtEq' => 'menší nebo rovno', 'OpMatches' => 'obsahuje', @@ -537,6 +565,7 @@ $SLANG = array( 'Open' => 'Otevřít', 'OptionHelp' => 'MožnostHelp', 'OptionRestartWarning' => 'Tyto změny se neprojeví\ndokud systém běží. Jakmile\ndokončíte provádění změn prosím\nrestartujte ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Možnosti', 'OrEnterNewName' => 'nebo vložte nové jméno', 'Order' => 'Pořadí', @@ -574,9 +603,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Rychlost', 'Real' => 'Skutečná', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Nahrávat', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Obnovit', 'Remote' => 'Síťová', @@ -592,6 +625,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Resetovat počty záznamů', 'Restart' => 'Restartovat', @@ -610,6 +644,7 @@ $SLANG = array( 'Save' => 'Uložit', 'SaveAs' => 'Uložit jako', 'SaveFilter' => 'Uložit filtr', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Velikost', 'Score' => 'Skóre', 'Secs' => 'Délka(s)', @@ -626,6 +661,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Zobrazit filtr', 'ShowTimeline' => 'Zobrazit časovou linii ', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Velikost', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Spát', @@ -645,6 +681,10 @@ $SLANG = array( 'State' => 'Stav', 'Stats' => 'Statistiky', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Krok', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -655,6 +695,8 @@ $SLANG = array( 'Stills' => 'Snímky', 'Stop' => 'Zastavit', 'Stopped' => 'Zastaven', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Potvrdit', @@ -674,6 +716,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Razítko', 'TimestampLabelFormat' => 'Formát časového razítka', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Časové razítko X', 'TimestampLabelY' => 'Časové razítko Y', 'Today' => 'Dnes', @@ -720,6 +763,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametry generování videa', 'VideoGenSucceeded' => 'Video vygenerováno úspěšně!', 'VideoSize' => 'Velikost videa', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Zobrazit', 'ViewAll' => 'Zobrazit všechny', 'ViewEvent' => 'Zobrazit záznam', @@ -729,6 +773,7 @@ $SLANG = array( 'Watch' => 'Sledovat', 'Web' => 'Web', 'WebColour' => 'Webová barva', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Týden', 'White' => 'Bílá', 'WhiteBalance' => 'Vyvážení bílé', diff --git a/web/lang/de_de.php b/web/lang/de_de.php index 424d91e94..fb7af310d 100644 --- a/web/lang/de_de.php +++ b/web/lang/de_de.php @@ -80,6 +80,8 @@ $SLANG = array( 'Actual' => 'Original', 'AddNewControl' => 'Neues Steuerelement hinzufügen', 'AddNewMonitor' => 'Neuer Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Neuer Benutzer', 'AddNewZone' => 'Neue Zone', 'Alarm' => 'Alarm', @@ -107,22 +109,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archivstatus', 'AttrAvgScore' => 'Mittlere Wertung', 'AttrCause' => 'Grund', - 'AttrDate' => 'Datum', - 'AttrDateTime' => 'Datum/Zeit', 'AttrDiskBlocks' => 'Disk-Blöcke', 'AttrDiskPercent' => 'Disk-Prozent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Dauer', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Bilder', 'AttrId' => 'ID', 'AttrMaxScore' => 'Maximale Wertung', 'AttrMonitorId' => 'Monitor-ID', 'AttrMonitorName' => 'Monitorname', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Name', 'AttrNotes' => 'Bemerkungen', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Systemlast', - 'AttrTime' => 'Zeit', 'AttrTotalScore' => 'Gesamtwertung', - 'AttrWeekday' => 'Wochentag', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto-Stopp-Zeitüberschreitung', 'Available' => 'Verfügbar', // Added - 2009-03-31 @@ -155,9 +167,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Der Referenz-Blenden-Prozentwert muss ganzzahlig 0 oder größer sein', 'BadSectionLength' => 'Die Bereichslänge muss ganzzahlig 0 oder größer sein', 'BadSignalCheckColour' => 'Die Signalprüffarbe muss auf einen gültigen Farbwert eingestellt sein', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Der Wiedergabestrompuffer muss eine ganze Zahl von null oder mehr betragen', 'BadWarmupCount' => 'Die Anzahl der Vorwärmbilder muss ganzzahlig 0 oder größer sein', 'BadWebColour' => 'Die Webfarbe muss auf einen gültigen Farbwert eingestellt sein', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Die Breite muss auf einen gültigen Wert eingestellt sein', 'Bandwidth' => 'Bandbreite', 'BandwidthHead' => 'Bandbreite', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -221,10 +235,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Log-Auswahl', // Added - 2011-06-17 'ChoosePreset' => 'Voreinstellung auswählen', 'Clear' => 'Leeren', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Schließen', 'Colour' => 'Farbe', 'Command' => 'Kommando', 'Component' => 'Komponente', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Konfig.', 'ConfiguredFor' => 'Konfiguriert für', 'ConfirmDeleteEvents' => 'Sind Sie sicher, dass Sie die ausgewählten Ereignisse löschen wollen?', @@ -282,9 +298,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Noch nicht, erinnere mich in einer Woche noch mal.', 'DonateYes' => 'Ja, ich möchte jetzt spenden.', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Monitornamen Duplizieren', // Added - 2009-03-31 'Duration' => 'Dauer', 'Edit' => 'Bearbeiten', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'E-Mail', 'EnableAlarms' => 'Alarme aktivieren', 'Enabled' => 'Aktiviert', @@ -301,6 +319,7 @@ $SLANG = array( 'Events' => 'Ereignisse', 'Exclude' => 'Ausschluss;', 'Execute' => 'Ausführen', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportieren', 'ExportDetails' => 'Exportiere Ereignis-Details', 'ExportFailed' => 'Exportieren fehlgeschlagen', @@ -330,8 +349,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Ausführen bei allen Treffern', 'FilterLog' => 'Log filtern', // Added - 2015-04-18 'FilterMessageEvents' => 'Detaillierte Nachricht zu allen Treffern', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter-Pixel', 'FilterUnset' => 'Sie müssen eine Breite und Höhe für das Filter angeben', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Hochladen aller Treffer', 'FilterVideoEvents' => 'Video für alle Treffer erstellen', 'Filters' => 'Filter', @@ -356,6 +377,7 @@ $SLANG = array( 'Function' => 'Funktion', 'Gain' => 'Verstärkung', 'General' => 'Allgemeines', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Erzeuge Video', 'GeneratingVideo' => 'Erzeuge Video...', 'GoToZoneMinder' => 'Besuche ZoneMinder.com', @@ -376,6 +398,7 @@ $SLANG = array( 'High' => 'hohe', 'HighBW' => 'Hohe B/W', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Stunde', 'Hue' => 'Farbton', 'Id' => 'ID', @@ -400,6 +423,7 @@ $SLANG = array( 'Line' => 'Zeile', // Added - 2011-06-16 'LinkedMonitors' => 'Verbundene Monitore', 'List' => 'Liste', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Last', 'Local' => 'Lokal', 'Log' => 'Log', // Added - 2011-06-16 @@ -486,6 +510,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'Die nachfolgende Liste zeigt erkannte Analog- und Netzwerkkameras, ob sie bereits genutzt werden und ob sie zur Auswahl verfügbar sind.

Wähle den gewünschten Eintrag aus der folgenden Liste.

Bitte Beachten: Nicht alle Kameras können erkannt werden. Die Auswahl einer Kamera kann bereits eingetragene Werte im aktuellen Monitor überschreiben.

', // Added - 2009-03-31 'Monitors' => 'Monitore', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Monat', 'More' => 'Mehr', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -512,6 +537,7 @@ $SLANG = array( 'Next' => 'Nächstes', 'No' => 'Nein', 'NoDetectedCameras' => 'Keine Kameras erkannt', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Es gibt keine Aufnahmen von diesem Ereignis.', 'NoGroup' => 'Keine Gruppe', 'NoSavedFilters' => 'Keine gespeicherten Filter', @@ -530,6 +556,8 @@ $SLANG = array( 'OpGt' => 'groesser als', 'OpGtEq' => 'groesser oder gleich wie', 'OpIn' => 'in Satz', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'kleiner als', 'OpLtEq' => 'kleiner oder gleich wie', 'OpMatches' => 'zutreffend', @@ -539,6 +567,7 @@ $SLANG = array( 'Open' => 'öffnen', 'OptionHelp' => 'Hilfe', 'OptionRestartWarning' => 'Veränderungen werden erst nach einem Neustart des Programms aktiv.\nFür eine sofortige änderung starten Sie das Programm bitte neu.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Optionen', 'OrEnterNewName' => 'oder neuen Namen eingeben', 'Order' => 'Reihenfolge', @@ -576,9 +605,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'Die folgende Liste zeigt die verfügbaren Streamingprofile der ausgewählten Kamera.

Wähle den gewünschten Eintrag aus der folgenden Liste.

Bitte Beachten: Zoneminder kann keine zusätzlichen Profile konfigurieren. Die Auswahl einer Kamera kann bereits eingetragene Werte im aktuellen Monitor überschreiben.

', // Added - 2015-04-18 'Progress' => 'Fortschritt', // Added - 2015-04-18 'Protocol' => 'Protokoll', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Abspielgeschwindigkeit', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Aufnahme', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Referenz-Bildblende', 'Refresh' => 'Aktualisieren', 'Remote' => 'Remote', @@ -594,6 +627,7 @@ $SLANG = array( 'ReplayAll' => 'Alle Ereignisse', 'ReplayGapless' => 'Lückenlose Ereignisse', 'ReplaySingle' => 'Einzelereignis', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Zurücksetzen', 'ResetEventCounts' => 'Lösche Ereignispunktzahl', 'Restart' => 'Neustart', @@ -612,6 +646,7 @@ $SLANG = array( 'Save' => 'Speichern', 'SaveAs' => 'Speichere als', 'SaveFilter' => 'Speichere Filter', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Skalierung', 'Score' => 'Wertung', 'Secs' => 'Sekunden', @@ -628,6 +663,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Zeige Filterfenster', 'ShowTimeline' => 'Zeige Zeitstrahl', 'SignalCheckColour' => 'Farbe des Signalchecks', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Größe', 'SkinDescription' => 'Wähle den standard Skin für diesen Computer.', // Added - 2011-01-30 'Sleep' => 'Schlaf', @@ -647,6 +683,10 @@ $SLANG = array( 'State' => 'Status', 'Stats' => 'Statistik', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Stufe', 'StepBack' => 'Einen Schritt rückwärts', 'StepForward' => 'Einen Schritt vorwärts', @@ -657,6 +697,8 @@ $SLANG = array( 'Stills' => 'Standbilder', 'Stop' => 'Stop', 'Stopped' => 'Gestoppt', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', 'StreamReplayBuffer' => 'Stream-Wiedergabe-Bildpuffer', 'Submit' => 'Absenden', @@ -676,9 +718,9 @@ $SLANG = array( 'TimelineTip4' => 'Verwenden Sie die Steuerelemente unten, um zu Zoomen oder navigieren Sie vorwärts und rückwärts durch die Zeit.', // Added 2013.08.15. 'Timestamp' => 'Zeitstempel', 'TimestampLabelFormat' => 'Format des Zeitstempels', + 'TimestampLabelSize' => 'Schriftgröße', 'TimestampLabelX' => 'Zeitstempel-X', 'TimestampLabelY' => 'Zeitstempel-Y', - 'TimestampLabelSize' => 'Schriftgröße', 'Today' => 'Heute', 'Tools' => 'Werkzeuge', 'Total' => 'Insgesamt', // Added - 2011-06-16 @@ -723,6 +765,7 @@ $SLANG = array( 'VideoGenParms' => 'Parameter der Videoerzeugung', 'VideoGenSucceeded' => 'Videoerzeugung erfolgreich!', 'VideoSize' => 'Videogröße', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Ansicht', 'ViewAll' => 'Alles ansehen', 'ViewEvent' => 'Zeige Ereignis', @@ -732,6 +775,7 @@ $SLANG = array( 'Watch' => 'Beobachte', 'Web' => 'Web', 'WebColour' => 'Webfarbe', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Woche', 'White' => 'Weiß', 'WhiteBalance' => 'Weiß-Abgleich', diff --git a/web/lang/default.php b/web/lang/default.php new file mode 100644 index 000000000..3f29757d7 --- /dev/null +++ b/web/lang/default.php @@ -0,0 +1,43 @@ + 'Privacy', + 'PrivacyAbout' => 'About', + 'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.', + 'PrivacyContact' => 'Contact', + 'PrivacyContactText' => 'Please contact us here for any questions regarding our privacy policy or to have your information removed.

For support, there are three primary ways to engage with the community:

Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.

', + 'PrivacyCookies' => 'Cookies', + 'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.', + 'PrivacyTelemetry' => 'Telemetry', + 'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.', + 'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:
  • A unique identifier (UUID)
  • City based location is gathered by querying ipinfo.io. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!
  • Current time
  • Total number of monitors
  • Total number of events
  • System architecture
  • Operating system kernel, distro, and distro version
  • Version of ZoneMinder
  • Total amount of memory
  • Number of cpu cores
', + 'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:
  • Id
  • Name
  • Type
  • Function
  • Width
  • Height
  • Colours
  • MaxFPS
  • AlarmMaxFPS
', + 'PrivacyConclusionText' => 'We are NOT collecting any image specific data from your cameras. We don’t know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.', +); + +?> diff --git a/web/lang/dk_dk.php b/web/lang/dk_dk.php index 605f9fb19..88ee943dc 100644 --- a/web/lang/dk_dk.php +++ b/web/lang/dk_dk.php @@ -72,15 +72,6 @@ header( "Content-Type: text/html; charset=utf-8" ); // Simple String Replacements $SLANG = array( - 'SystemLog' => 'System Log', - 'DateTime' => 'Dato/Tid', - 'Component' => 'Komponent', - 'Pid' => 'PID', - 'Level' => 'Niveau', - 'Message' => 'Meddelelse', - 'Line' => 'Linie', - 'More' => 'Mere', - 'Clear' => 'Slet', '24BitColour' => '24 bit farve', '32BitColour' => '32 bit farve', '8BitGrey' => '8 bit gråskala', @@ -89,6 +80,7 @@ $SLANG = array( 'AddNewControl' => 'Tilføj Ny Kontrol', 'AddNewMonitor' => 'Tilføj Ny Monitor', 'AddNewServer' => 'Tilføj Ny Server', + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Tilføj Ny Bruger', 'AddNewZone' => 'Tilføj Ny Zone', 'Alarm' => 'Alarm', @@ -98,8 +90,8 @@ $SLANG = array( 'AlarmLimits' => 'Alarm Grænser', 'AlarmMaximumFPS' => 'Alarm Maksimum FPS', 'AlarmPx' => 'Alarm Px', - 'AlarmRefImageBlendPct' => 'Alarm Reference Billede Blandings %', 'AlarmRGBUnset' => 'Du skal vælge en alarm RGB farve', + 'AlarmRefImageBlendPct' => 'Alarm Reference Billede Blandings %', 'Alert' => 'Advarsel', 'All' => 'Alle', 'AnalysisFPS' => 'Analyse FPS', @@ -107,37 +99,45 @@ $SLANG = array( 'Apply' => 'Udfør', 'ApplyingStateChange' => 'Udfører tilstandsændring', 'ArchArchived' => 'Kun arkiverede', + 'ArchUnarchived' => 'Kun ikke-arkiverede', 'Archive' => 'Arkivér', 'Archived' => 'Arkiverede', - 'ArchUnarchived' => 'Kun ikke-arkiverede', 'Area' => 'Område', 'AreaUnits' => 'Område (px/%)', 'AttrAlarmFrames' => 'Alarm Rammer', 'AttrArchiveStatus' => 'Arkiverings Status', 'AttrAvgScore' => 'Middel Score', 'AttrCause' => 'Årsag', - 'AttrDate' => 'Dato', - 'AttrDateTime' => 'Dato/Tid', 'AttrDiskBlocks' => 'Disk Blokke', 'AttrDiskPercent' => 'Disk Procent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Varighed', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Rammer', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. Score', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Monitor Navn', - 'AttrServer' => 'Server', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Navn', 'AttrNotes' => 'Noter', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Belastning', - 'AttrTime' => 'Tid', 'AttrTotalScore' => 'Total Score', - 'AttrWeekday' => 'Ugedag', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Tilgængelig', 'AvgBrScore' => 'Middel
Score', - 'Available' => 'Tilgængelig', 'Background' => 'Baggrund', 'BackgroundFilter' => 'Kør filteret i baggrunden', 'BadAlarmFrameCount' => 'Antal alarm rammer skal være et positivt heltal', @@ -145,20 +145,20 @@ $SLANG = array( 'BadAnalysisFPS' => 'Analyse FPS skal være et positivt heltal eller flydende tal', 'BadAnalysisUpdateDelay'=> 'Analyse opdaterings forsinkelse skal være et heltal på 0 eller mere', 'BadChannel' => 'Kanal skal sættes til et heltal på 0 eller mere', + 'BadColours' => 'Målfarven skal sættes til en gyldig værdi', 'BadDevice' => 'Enhed skal sættes til en gyldig værdi', - 'BadFormat' => 'Format skal sættes til en gyldig værdi', 'BadFPSReportInterval' => 'Antal FPS report interval buffere skal være et heltal på 0 eller mere', + 'BadFormat' => 'Format skal sættes til en gyldig værdi', 'BadFrameSkip' => 'Antal Frame skip skal være et heltal på 0 eller mere', - 'BadMotionFrameSkip' => 'Antal Motion Frame skip skal være et heltal på 0 eller mere', 'BadHeight' => 'Højde skal sættes til en gyldig værdi', 'BadHost' => 'Host skal vare en gyldig IP adresse eller hostname, inkludér ikke http://', 'BadImageBufferCount' => 'Billed buffer størrelse skal være et heltal på 10 eller mere', 'BadLabelX' => 'Mærkat X co-ordinaten skal sættes til et heltal på 0 eller mere', 'BadLabelY' => 'Mærkat Y co-ordinaten skal sættes til et heltal på 0 eller mere', 'BadMaxFPS' => 'Maximum FPS skal være et positivt heltal eller flydende tal', + 'BadMotionFrameSkip' => 'Antal Motion Frame skip skal være et heltal på 0 eller mere', 'BadNameChars' => 'Navne kan kun indeholde alfanumeriske tegn samt mellemrum, bindestreg og understregning', 'BadPalette' => 'Palette skal sættes til en gyldig værdi', - 'BadColours' => 'Målfarven skal sættes til en gyldig værdi', 'BadPath' => 'Sti skal sættes til en gyldig værdi', 'BadPort' => 'Port skal sættes til et gyldigt nummer', 'BadPostEventCount' => 'Antal rammer efter hændelsen skal være et heltal på 0 eller mere', @@ -166,39 +166,40 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blandings procentdelen skal være et positivt heltal', 'BadSectionLength' => 'Sektionslængden skal være et heltal på 30 eller mere', 'BadSignalCheckColour' => 'Signal check farve skal være en gyldig RGB farve streng', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => 'Videostrøm genspilsbufferen skal sættes til et heltal på 0 eller mere', 'BadWarmupCount' => 'Opvarmnings rammer skal være et heltal på 0 eller mere', 'BadWebColour' => 'Web farve skal være en gyldigt web farve streng', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Bredde skal sættes til en gyldig værdi', 'Bandwidth' => 'Båndbredde', 'BandwidthHead' => 'Båndbredde', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing 'BlobPx' => 'Blob Px', - 'Blobs' => 'Blobs', 'BlobSizes' => 'Blob Størrelser', + 'Blobs' => 'Blobs', 'Brightness' => 'Lysstyrke', 'Buffer' => 'Buffer', 'Buffers' => 'Buffere', + 'CSSDescription' => 'SKift standard css for denne computer', 'CanAutoFocus' => 'Can Auto Focus', 'CanAutoGain' => 'Can Auto Gain', 'CanAutoIris' => 'Can Auto Iris', 'CanAutoWhite' => 'Can Auto White Bal.', 'CanAutoZoom' => 'Can Auto Zoom', - 'Cancel' => 'Fortryd', - 'CancelForcedAlarm' => 'Fortryd Tvungen Alarm', - 'CanFocusAbs' => 'Can Focus Absolute', 'CanFocus' => 'Can Focus', + 'CanFocusAbs' => 'Can Focus Absolute', 'CanFocusCon' => 'Can Focus Continuous', 'CanFocusRel' => 'Can Focus Relative', - 'CanGainAbs' => 'Can Gain Absolute', 'CanGain' => 'Can Gain ', + 'CanGainAbs' => 'Can Gain Absolute', 'CanGainCon' => 'Can Gain Continuous', 'CanGainRel' => 'Can Gain Relative', - 'CanIrisAbs' => 'Can Iris Absolute', 'CanIris' => 'Can Iris', + 'CanIrisAbs' => 'Can Iris Absolute', 'CanIrisCon' => 'Can Iris Continuous', 'CanIrisRel' => 'Can Iris Relative', - 'CanMoveAbs' => 'Can Move Absolute', 'CanMove' => 'Can Move', + 'CanMoveAbs' => 'Can Move Absolute', 'CanMoveCon' => 'Can Move Continuous', 'CanMoveDiag' => 'Can Move Diagonally', 'CanMoveMap' => 'Can Move Mapped', @@ -209,19 +210,21 @@ $SLANG = array( 'CanSleep' => 'Can Sleep', 'CanTilt' => 'Can Tilt', 'CanWake' => 'Can Wake', + 'CanWhite' => 'Can White Balance', 'CanWhiteAbs' => 'Can White Bal. Absolute', 'CanWhiteBal' => 'Can White Bal.', - 'CanWhite' => 'Can White Balance', 'CanWhiteCon' => 'Can White Bal. Continuous', 'CanWhiteRel' => 'Can White Bal. Relative', - 'CanZoomAbs' => 'Can Zoom Absolute', 'CanZoom' => 'Can Zoom', + 'CanZoomAbs' => 'Can Zoom Absolute', 'CanZoomCon' => 'Can Zoom Continuous', 'CanZoomRel' => 'Can Zoom Relative', + 'Cancel' => 'Fortryd', + 'CancelForcedAlarm' => 'Fortryd Tvungen Alarm', 'CaptureHeight' => 'Capture Højde', 'CaptureMethod' => 'Capture Metode', - 'CaptureResolution' => 'Capture Opløsning', 'CapturePalette' => 'Capture Palette', + 'CaptureResolution' => 'Capture Opløsning', 'CaptureWidth' => 'Capture Bredde', 'Cause' => 'Årsag', 'CheckMethod' => 'Alarm Check Metode', @@ -230,10 +233,13 @@ $SLANG = array( 'ChooseLogFormat' => 'Vælg et lognings format', 'ChooseLogSelection' => 'Vælg et lognings udvælgelse', 'ChoosePreset' => 'Vælg Forudindstilling', + 'Clear' => 'Slet', 'CloneMonitor' => 'Klon Monitor', 'Close' => 'Luk', 'Colour' => 'Farve', 'Command' => 'Kommando', + 'Component' => 'Komponent', + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Konfigurer', 'ConfiguredFor' => 'Konfigureret for', 'ConfirmDeleteEvents' => 'Er du sikker på, at du vil slette de markerede hændelser?', @@ -244,62 +250,64 @@ $SLANG = array( 'ContactAdmin' => 'Venligst kontakt din administrator for detaljer.', 'Continue' => 'Fortsæt', 'Contrast' => 'Kontrast', + 'Control' => 'Control', 'ControlAddress' => 'Control Address', 'ControlCap' => 'Control Capability', 'ControlCaps' => 'Control Capabilities', - 'Control' => 'Control', 'ControlDevice' => 'Control Device', - 'Controllable' => 'Controllable', 'ControlType' => 'Control Type', + 'Controllable' => 'Controllable', 'Current' => 'Nuværende', 'Cycle' => 'Cyklisk', 'CycleWatch' => 'Cyklisk Overvågning', + 'DateTime' => 'Dato/Tid', 'Day' => 'Dag', 'Debug' => 'Fejlfind', 'DefaultRate' => 'Standard Rate', 'DefaultScale' => 'Standard Skalering', 'DefaultView' => 'Standard Visning', 'Deinterlacing' => 'Deinterlacing', - 'RTSPDescribe' => 'Brug RTSP Response Media URL', 'Delay' => 'Forsilkelse', + 'Delete' => 'Slet', 'DeleteAndNext' => 'Slet & Næste', 'DeleteAndPrev' => 'Slet & Forrige', - 'Delete' => 'Slet', 'DeleteSavedFilter' => 'Slet gemt filter', 'Description' => 'Beskrivelse', 'DetectedCameras' => 'Fundne Kameraer', 'DetectedProfiles' => 'Fundne Profiler', + 'Device' => 'Enheds', 'DeviceChannel' => 'Enheds Kanal', 'DeviceFormat' => 'Enheds Format', 'DeviceNumber' => 'Enheds Number', 'DevicePath' => 'Sti Til Enhed', - 'Device' => 'Enheds', 'Devices' => 'Enheder', 'Dimensions' => 'Dimensioner', 'DisableAlarms' => 'Deaktiver Alarmer', 'Disk' => 'Disk', 'Display' => 'Display', 'Displaying' => 'Displaying', + 'DoNativeMotionDetection'=> 'Do Native Motion Detection', + 'Donate' => 'Venligst Donér', 'DonateAlready' => 'Nej, jeg har allerede doneret', 'DonateEnticement' => 'You\'ve been running ZoneMinder for a while now and hopefully are finding it a useful addition to your home or workplace security. Although ZoneMinder is, and will remain, free and open source, it costs money to develop and support. If you would like to help support future development and new features then please consider donating. Donating is, of course, optional but very much appreciated and you can donate as much or as little as you like.

If you would like to donate please select the option below or go to http://www.zoneminder.com/donate.html in your browser.

Thank you for using ZoneMinder and don\'t forget to visit the forums on ZoneMinder.com for support or suggestions about how to make your ZoneMinder experience even better.', - 'Donate' => 'Venligst Donér', 'DonateRemindDay' => 'Ikke endnu, påmind igen on 1 dag', 'DonateRemindHour' => 'Ikke endnu, påmind igen on 1 time', 'DonateRemindMonth' => 'Ikke endnu, påmind igen on 1 måned', 'DonateRemindNever' => 'Nej, jeg ønsker ikke at donere, påmind ikke igen', 'DonateRemindWeek' => 'Ikke endnu, påmind igen on 1 uge', 'DonateYes' => 'Ja, jeg vil gerne donere nu', - 'DoNativeMotionDetection'=> 'Do Native Motion Detection', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Dupliket Monitor Navn', 'Duration' => 'Varighed', 'Edit' => 'Ret', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Aktivér Alarmer', 'Enabled' => 'Virksom', 'EnterNewFilterName' => 'Indtast nyt filternavn', - 'ErrorBrackets' => 'Fejl, venligst check, at du har samme antal open og lukke klammer', 'Error' => 'Fejl', + 'ErrorBrackets' => 'Fejl, venligst check, at du har samme antal open og lukke klammer', 'ErrorValidValue' => 'Fejl, venligst check at alle parametre har en gyldig værdi', 'Etc' => 'etc', 'Event' => 'Hændelse', @@ -310,9 +318,9 @@ $SLANG = array( 'Events' => 'Hændelser', 'Exclude' => 'Ekskluder', 'Execute' => 'Udfør', - 'ExportDetails' => 'Exporter Hændelses Detaljer', 'Exif' => 'Indlejre EXIF data i billede', 'Export' => 'Exporter', + 'ExportDetails' => 'Exporter Hændelses Detaljer', 'ExportFailed' => 'Export Mislykkedes', 'ExportFormat' => 'Export Fil Format', 'ExportFormatTar' => 'Tar', @@ -320,53 +328,55 @@ $SLANG = array( 'ExportFrames' => 'Exporter Ramme Detaljer', 'ExportImageFiles' => 'Exporter billed filer', 'ExportLog' => 'Export Log', - 'Exporting' => 'Exporterer', 'ExportMiscFiles' => 'Exporter Andre Filer (hvis tilstede)', 'ExportOptions' => 'Export Indstillinger', 'ExportSucceeded' => 'Export Lykkedes', 'ExportVideoFiles' => 'Exporter Video Filer (hvis tilstede)', + 'Exporting' => 'Exporterer', + 'FPS' => 'fps', + 'FPSReportInterval' => 'FPS Rapport Interval', + 'FTP' => 'FTP', 'Far' => 'Fjern', 'FastForward' => 'Hurtigt Frem', 'Feed' => 'Feed', 'Ffmpeg' => 'Ffmpeg', 'File' => 'Fil', + 'Filter' => 'Filter', 'FilterArchiveEvents' => 'Arkiver alle matchende', 'FilterDeleteEvents' => 'Slet alle matchende', 'FilterEmailEvents' => 'Email detaljer for alle matchende', 'FilterExecuteEvents' => 'Udfør kommando for alle matchende', 'FilterLog' => 'Filter log', 'FilterMessageEvents' => 'Meddel detaljer for alle matchende', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', - 'Filter' => 'Filter', - 'Filters' => 'Filtre', 'FilterUnset' => 'Du skal angive filter bredde og højde', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Upload alle match', 'FilterVideoEvents' => 'Opret video for alle match', + 'Filters' => 'Filtre', 'First' => 'Første', 'FlippedHori' => 'Spejlet Horizontalt', 'FlippedVert' => 'Spejlet Vertikalt', - 'FnNone' => 'None', // Added 2013.08.16. - 'FnMonitor' => 'Monitor', // Added 2013.08.16. - 'FnModect' => 'Modect', // Added 2013.08.16. - 'FnRecord' => 'Record', // Added 2013.08.16. 'FnMocord' => 'Mocord', // Added 2013.08.16. + 'FnModect' => 'Modect', // Added 2013.08.16. + 'FnMonitor' => 'Monitor', // Added 2013.08.16. 'FnNodect' => 'Nodect', // Added 2013.08.16. + 'FnNone' => 'None', // Added 2013.08.16. + 'FnRecord' => 'Record', // Added 2013.08.16. 'Focus' => 'Focus', 'ForceAlarm' => 'Force Alarm', 'Format' => 'Format', - 'FPS' => 'fps', - 'FPSReportInterval' => 'FPS Rapport Interval', 'Frame' => 'Ramme', 'FrameId' => 'Ramme Id', 'FrameRate' => 'Billedhastighed', - 'Frames' => 'Rammer', 'FrameSkip' => 'Spring over antal rammer', - 'MotionFrameSkip' => 'Spring over antal bevægelsesrammer', - 'FTP' => 'FTP', + 'Frames' => 'Rammer', 'Func' => 'Funk', 'Function' => 'Funktion', 'Gain' => 'Gain', 'General' => 'Generelt', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Generer Video', 'GeneratingVideo' => 'Genererer Video', 'GoToZoneMinder' => 'Gå til ZoneMinder.com', @@ -384,8 +394,8 @@ $SLANG = array( 'HasTurboTilt' => 'Has Turbo Tilt', 'HasWhiteSpeed' => 'Has White Bal. Speed', 'HasZoomSpeed' => 'Has Zoom Speed', - 'HighBW' => 'High B/W', 'High' => 'Høj', + 'HighBW' => 'High B/W', 'Home' => 'Hjemme', 'Hostname' => 'Hostname', 'Hour' => 'Time', @@ -393,11 +403,11 @@ $SLANG = array( 'Id' => 'Id', 'Idle' => 'Afventende', 'Ignore' => 'Ignorer', - 'ImageBufferSize' => 'Billed Buffer Størrelse (rammer)', 'Image' => 'Billede', + 'ImageBufferSize' => 'Billed Buffer Størrelse (rammer)', 'Images' => 'Billeder', - 'Include' => 'Inkluder', 'In' => 'I', + 'Include' => 'Inkluder', 'Inverted' => 'Inverteret', 'Iris' => 'Blænde', 'KeyString' => 'Nøgle Streng', @@ -405,26 +415,30 @@ $SLANG = array( 'Language' => 'Sprog', 'Last' => 'Sidste', 'Layout' => 'Layout', + 'Level' => 'Niveau', 'Libvlc' => 'Libvlc', 'LimitResultsPost' => 'resultater', // This is used at the end of the phrase 'Limit to first N results only' 'LimitResultsPre' => 'Begræns til kun de første', // This is used at the beginning of the phrase 'Limit to first N results only' + 'Line' => 'Linie', 'LinkedMonitors' => 'Sammenkædede Monitorer', 'List' => 'Liste', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Belastning', 'Local' => 'Lokal', 'Log' => 'Log', - 'Logs' => 'Logs', - 'Logging' => 'Logning', 'LoggedInAs' => 'Logget ind som', + 'Logging' => 'Logning', 'LoggingIn' => 'Logger ind', 'Login' => 'Logind', 'Logout' => 'Logud', - 'LowBW' => 'Lav B/W', + 'Logs' => 'Logs', 'Low' => 'Lav', + 'LowBW' => 'Lav B/W', 'Main' => 'Hoved', 'Man' => 'Man', 'Manual' => 'Manuel', 'Mark' => 'Markér', + 'Max' => 'Max', 'MaxBandwidth' => 'Max Båndbredde', 'MaxBrScore' => 'Max.
Score', 'MaxFocusRange' => 'Max Focus Range', @@ -433,11 +447,9 @@ $SLANG = array( 'MaxGainRange' => 'Max Gain Range', 'MaxGainSpeed' => 'Max Gain Speed', 'MaxGainStep' => 'Max Gain Step', - 'MaximumFPS' => 'Maximum FPS', 'MaxIrisRange' => 'Max Iris Range', 'MaxIrisSpeed' => 'Max Iris Speed', 'MaxIrisStep' => 'Max Iris Step', - 'Max' => 'Max', 'MaxPanRange' => 'Max Pan Range', 'MaxPanSpeed' => 'Max Pan Speed', 'MaxPanStep' => 'Max Pan Step', @@ -450,8 +462,10 @@ $SLANG = array( 'MaxZoomRange' => 'Max Zoom Range', 'MaxZoomSpeed' => 'Max Zoom Speed', 'MaxZoomStep' => 'Max Zoom Step', - 'MediumBW' => 'Medium B/W', + 'MaximumFPS' => 'Maximum FPS', 'Medium' => 'Medium', + 'MediumBW' => 'Medium B/W', + 'Message' => 'Meddelelse', 'MinAlarmAreaLtMax' => 'Minimum alarm område skal være mindre end maksimum', 'MinAlarmAreaUnset' => 'Du skal angive det minimale antal alarm pixels', 'MinBlobAreaLtMax' => 'Minimum blob område skal være mindre end maksimum', @@ -487,22 +501,24 @@ $SLANG = array( 'MinZoomStep' => 'Min Zoom Step', 'Misc' => 'Diverse', 'Mode' => 'Mode', - 'MonitorIds' => 'Monitor Ids', 'Monitor' => 'Monitor', - 'MonitorPresetIntro' => 'Vælg en passende forudindstilling fra listen herunder.

Vær opmærksom på, at dette kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', + 'MonitorIds' => 'Monitor Ids', 'MonitorPreset' => 'Monitor Forudindstillinger', - 'MonitorProbeIntro' => 'Listen herunder viser fundne analoge og nætværks kameraer samt hvorvidt de allerede er i brug eller tilgængelige for udvælgelse.

Vælg det ønskede fra listen herunder.

Vær opmærksom på, at muligvis ikke alle kameraer er fundet og at valg af et kamera kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', + 'MonitorPresetIntro' => 'Vælg en passende forudindstilling fra listen herunder.

Vær opmærksom på, at dette kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', 'MonitorProbe' => 'Monitor Probe', + 'MonitorProbeIntro' => 'Listen herunder viser fundne analoge og nætværks kameraer samt hvorvidt de allerede er i brug eller tilgængelige for udvælgelse.

Vælg det ønskede fra listen herunder.

Vær opmærksom på, at muligvis ikke alle kameraer er fundet og at valg af et kamera kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', 'Monitors' => 'Monitorer', 'Montage' => 'Montage', 'MontageReview' => 'Montage Review', 'Month' => 'Måned', + 'More' => 'Mere', + 'MotionFrameSkip' => 'Spring over antal bevægelsesrammer', 'Move' => 'Bevæg', - 'MtgDefault' => 'Standard', // Added 2013.08.15. 'Mtg2widgrd' => '2-bred gitter', // Added 2013.08.15. 'Mtg3widgrd' => '3-bred gitter', // Added 2013.08.15. - 'Mtg4widgrd' => '4-bred gitter', // Added 2013.08.15. 'Mtg3widgrx' => '3-bred gitter, skaleret, forstørret ved alarm', // Added 2013.08.15. + 'Mtg4widgrd' => '4-bred gitter', // Added 2013.08.15. + 'MtgDefault' => 'Standard', // Added 2013.08.15. 'MustBeGe' => 'Skal være større end eller lig med', 'MustBeLe' => 'Skal være mindre end eller lig med', 'MustConfirmPassword' => 'Du skal bekræfte adgangskoden', @@ -511,53 +527,55 @@ $SLANG = array( 'Name' => 'Navn', 'Near' => 'Nær', 'Network' => 'Netværk', + 'New' => 'Ny', 'NewGroup' => 'Ny Gruppe', 'NewLabel' => 'Ny Mærkat', - 'New' => 'Ny', 'NewPassword' => 'Ny Adgangskode', 'NewState' => 'Ny Tilstand', 'NewUser' => 'Ny bruger', 'Next' => 'Næste', + 'No' => 'Nej', 'NoDetectedCameras' => 'Ingen Detected Cameras', 'NoDetectedProfiles' => 'Ingen Fundne Profiler', 'NoFramesRecorded' => 'Der er ingen billeder optaget for denne hændelse', 'NoGroup' => 'Ingen gruppe', - 'NoneAvailable' => 'Ingen tilgængelig', - 'None' => 'Ingen', - 'No' => 'Nej', - 'Normal' => 'Normalt', 'NoSavedFilters' => 'IngenGemteFiltre', 'NoStatisticsRecorded' => 'Der er ingen statistik noteret for denne hændelse/ramme', + 'None' => 'Ingen', + 'NoneAvailable' => 'Ingen tilgængelig', + 'Normal' => 'Normalt', 'Notes' => 'Noter', 'NumPresets' => 'Num Forudinst.', 'Off' => 'Fra', 'On' => 'Til', + 'OnvifCredentialsIntro' => 'Venligst lever brugernavn og adgangskodefor de valgte kamera.
Hvis der ikke er oprettet nogen bruger for kameraet, så vil brugeren givet her blive oprettet med den angivne adgangskode.

', 'OnvifProbe' => 'ONVIF', 'OnvifProbeIntro' => 'Listen nedenfor viser fundne ONVIF kameraer samt hvorvidt de allerede er i brug eller tilgængelige for udvælgelse.

Vælg det ønskede fra listen herunder.

Vær opmærksom på, at muligvis ikke alle kameraer er fundet og at valg af et kamera kan overskrive værdier, du allerede har angivet for den aktuelle monitor.

', - 'OnvifCredentialsIntro' => 'Venligst lever brugernavn og adgangskodefor de valgte kamera.
Hvis der ikke er oprettet nogen bruger for kameraet, så vil brugeren givet her blive oprettet med den angivne adgangskode.

', - 'Open' => 'Åben', 'OpEq' => 'lig med', - 'OpGtEq' => 'større end eller lig med', 'OpGt' => 'større end', + 'OpGtEq' => 'større end eller lig med', 'OpIn' => 'indeholdt i', - 'OpLtEq' => 'mindre end eller lig med', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'mindre end', + 'OpLtEq' => 'mindre end eller lig med', 'OpMatches' => 'matcher', 'OpNe' => 'ikke lig med', 'OpNotIn' => 'ikke indeholdt i', 'OpNotMatches' => 'matcher ikke', - 'OptionalEncoderParam' => 'Optionelle Encoder Parametre', + 'Open' => 'Åben', 'OptionHelp' => 'Indstillinger hjælp', 'OptionRestartWarning' => 'Disse ændringer har muligvis ikke fuld effekt\nmens systemet er kørende. Når du har\nafsluttet dine ændringer, skal du huske at\ngenstarte ZoneMinder.', + 'OptionalEncoderParam' => 'Optionelle Encoder Parametre', 'Options' => 'Indstillinger', - 'Order' => 'Rækkefølge', 'OrEnterNewName' => 'eller indtast nyt navn', + 'Order' => 'Rækkefølge', 'Orientation' => 'Orientering', 'Out' => 'Ud', 'OverwriteExisting' => 'Overskriv Eksisterende', 'Paged' => 'Sidevis', - 'PanLeft' => 'Pan Left', 'Pan' => 'Pan', + 'PanLeft' => 'Pan Left', 'PanRight' => 'Pan Right', 'PanTilt' => 'Pan/Tilt', 'Parameter' => 'Parameter', @@ -565,14 +583,15 @@ $SLANG = array( 'PasswordsDifferent' => 'Den nye og den bekræftende adgangskode er forskellige', 'Paths' => 'Stier', 'Pause' => 'Pause', - 'PhoneBW' => 'Telefon B/W', 'Phone' => 'Telefon', + 'PhoneBW' => 'Telefon B/W', + 'Pid' => 'PID', 'PixelDiff' => 'Pixel Forskel', 'Pixels' => 'pixels', - 'PlayAll' => 'Afspil Alle', 'Play' => 'Afspil', - 'Plugins' => 'Plugins', + 'PlayAll' => 'Afspil Alle', 'PleaseWait' => 'Vent venligst', + 'Plugins' => 'Plugins', 'Point' => 'Point', 'PostEventImageBuffer' => 'Antal Billeder Efter Hændelse', 'PreEventImageBuffer' => 'Antal Billeder Før Hændelse', @@ -585,30 +604,33 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', 'Progress' => 'Position', 'Protocol' => 'Protokol', + 'RTSPDescribe' => 'Brug RTSP Response Media URL', + 'RTSPTransport' => 'RTSP Transport Protocol', 'Rate' => 'Rate', - 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP - 'RecordAudio' => 'Skal lydsporet gemmes sammen med en hændelse.', 'Real' => 'Naturtro', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // added Sep 24 2015 - PP 'Record' => 'Optag', + 'RecordAudio' => 'Skal lydsporet gemmes sammen med en hændelse.', 'RefImageBlendPct' => 'Reference Billede Blandings %', 'Refresh' => 'Genindlæs', + 'Remote' => 'Remote', 'RemoteHostName' => 'Remote Host Name', 'RemoteHostPath' => 'Remote Host Path', - 'RemoteHostSubPath' => 'Remote Host SubPath', 'RemoteHostPort' => 'Remote Host Port', + 'RemoteHostSubPath' => 'Remote Host SubPath', 'RemoteImageColours' => 'Remote Image Colours', 'RemoteMethod' => 'Remote Method', 'RemoteProtocol' => 'Remote Protocol', - 'Remote' => 'Remote', 'Rename' => 'Omdøb', + 'Replay' => 'Genafspil', 'ReplayAll' => 'Alle Hændelser', 'ReplayGapless' => 'Hændelser uafbrudt', - 'Replay' => 'Genafspil', 'ReplaySingle' => 'Enkelt Hændelse', - 'ResetEventCounts' => 'Nulstil Hændelses Tæller', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Nulstil', - 'Restarting' => 'Genstarter', + 'ResetEventCounts' => 'Nulstil Hændelses Tæller', 'Restart' => 'Genstart', + 'Restarting' => 'Genstarter', 'RestrictedCameraIds' => 'Restricted Camera Ids', 'RestrictedMonitors' => 'Restricted Monitors', 'ReturnDelay' => 'Return Delay', @@ -616,34 +638,33 @@ $SLANG = array( 'Rewind' => 'Hurtigt Tilbage', 'RotateLeft' => 'Roter til venstrte', 'RotateRight' => 'Roter til højre', - 'RTSPTransport' => 'RTSP Transport Protocol', 'RunLocalUpdate' => 'Kør venligst zmupdate.pl for at opdatere', 'RunMode' => 'Driftsmåde', - 'Running' => 'Kørende', 'RunState' => 'Drift Tilstand', + 'Running' => 'Kørende', + 'Save' => 'Gem', 'SaveAs' => 'Gem som', 'SaveFilter' => 'Gem Filter', - 'SaveJPEGS' => 'Gem JPEGs', - 'Save' => 'Gem', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Skaler', 'Score' => 'Score', 'Secs' => 'Sek.', 'Sectionlength' => 'Sektions længde', - 'SelectMonitors' => 'Vælg Monitorer', 'Select' => 'Vælg', 'SelectFormat' => 'Vælg Format', 'SelectLog' => 'Vælg Log', + 'SelectMonitors' => 'Vælg Monitorer', 'SelfIntersecting' => 'Polygonens kanter må ikke krydses', + 'Set' => 'Sæt', 'SetNewBandwidth' => 'Vælg ny båndbredde', 'SetPreset' => 'Set Preset', - 'Set' => 'Sæt', 'Settings' => 'Indstillinger', 'ShowFilterWindow' => 'Vis Filter Vindue', 'ShowTimeline' => 'Vis Tidslinie', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Størrelse', 'SkinDescription' => 'SKift standard skin for denne computer', - 'CSSDescription' => 'SKift standard css for denne computer', 'Sleep' => 'Sover', 'SortAsc' => 'Voksende', 'SortBy' => 'Sortér efter', @@ -652,46 +673,53 @@ $SLANG = array( 'SourceColours' => 'Kilde Farver', 'SourcePath' => 'Kilde Sti', 'SourceType' => 'Kilde Type', + 'Speed' => 'Speed', 'SpeedHigh' => 'High Speed', 'SpeedLow' => 'Low Speed', 'SpeedMedium' => 'Medium Speed', - 'Speed' => 'Speed', 'SpeedTurbo' => 'Turbo Speed', 'Start' => 'Start', 'State' => 'Tilstand', 'Stats' => 'Stats', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 + 'Step' => 'Skridt', 'StepBack' => 'Skridt Tilbage', 'StepForward' => 'Skridt Frem', 'StepLarge' => 'Langt Skridt', 'StepMedium' => 'Medium Skridt', 'StepNone' => 'Ingen Skridt', 'StepSmall' => 'Lille Skridt', - 'Step' => 'Skridt', 'Stills' => 'Stilbilleder', - 'Stopped' => 'Stoppet', 'Stop' => 'Stop', - 'StreamReplayBuffer' => 'Stream Replay Image Buffer', + 'Stopped' => 'Stoppet', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', + 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Påtryk', 'System' => 'System', + 'SystemLog' => 'System Log', 'TargetColorspace' => 'Target colorspace', 'Tele' => 'Tele', 'Thumbnail' => 'Thumbnail', 'Tilt' => 'Tilt', + 'Time' => 'Tidspunkt', 'TimeDelta' => 'Tidsforskel', + 'TimeStamp' => 'Tids stempel', 'Timeline' => 'Tidslinie', 'TimelineTip1' => 'Før musen over grafen for at vise snapshot billede og detaljer om hændelsen.', // Added 2013.08.15. 'TimelineTip2' => 'Klip på de farvede områder af grafen, eller billedet, for at se hændelsen.', // Added 2013.08.15. 'TimelineTip3' => 'Klik på baggrunden for at zoome ind på en mindre tidsperiode omkring dit klik.', // Added 2013.08.15. 'TimelineTip4' => 'Brug kontrollerne nedenfor for at zoome ud eller navigere frem eller tilbage i tiden.', // Added 2013.08.15. + 'Timestamp' => 'Tidsstempel', 'TimestampLabelFormat' => 'Tidsstempel Mærkat Format', + 'TimestampLabelSize' => 'Font Størrelse', 'TimestampLabelX' => 'Tidsstempel Mærkat X', 'TimestampLabelY' => 'Tidsstempel Mærkat Y', - 'TimestampLabelSize' => 'Font Størrelse', - 'Timestamp' => 'Tidsstempel', - 'TimeStamp' => 'Tids stempel', - 'Time' => 'Tidspunkt', 'Today' => 'Idag', 'Tools' => 'Værktøjer', 'Total' => 'Total', @@ -706,25 +734,29 @@ $SLANG = array( 'Undefined' => 'Udefineret', 'Units' => 'Enheder', 'Unknown' => 'Ukendt', + 'Update' => 'Opdater', 'UpdateAvailable' => 'En opdatering til ZoneMinder er tilgængelig.', 'UpdateNotNecessary' => 'Ingen opdatering er nødvendig.', - 'Update' => 'Opdater', - 'Upload' => 'Upload', 'Updated' => 'Opdateret', - 'UsedPlugins' => 'Anvendte Plugins', + 'Upload' => 'Upload', + 'UseFilter' => 'Anvend Filter', 'UseFilterExprsPost' => ' filter udtryk', // This is used at the end of the phrase 'use N filter expressions' 'UseFilterExprsPre' => 'Anvend ', // This is used at the beginning of the phrase 'use N filter expressions' - 'UseFilter' => 'Anvend Filter', + 'UsedPlugins' => 'Anvendte Plugins', + 'User' => 'Bruger', 'Username' => 'Brugernavn', 'Users' => 'Brugere', - 'User' => 'Bruger', + 'V4L' => 'V4L', + 'V4LCapturesPerFrame' => 'Captures Per Frame', + 'V4LMultiBuffer' => 'Multi Buffering', 'Value' => 'Værdi', + 'Version' => 'Version', 'VersionIgnore' => 'Ignorer denne værdi', 'VersionRemindDay' => 'Påmind igen om 1 dag', 'VersionRemindHour' => 'Påmind igen om 1 time', 'VersionRemindNever' => 'Påmind ikke om nye versioner', 'VersionRemindWeek' => 'Påmind igen om 1 uge', - 'Version' => 'Version', + 'Video' => 'Video', 'VideoFormat' => 'Video Format', 'VideoGenFailed' => 'Video Generering Fejlede!', 'VideoGenFiles' => 'Existerende Video Filer', @@ -733,47 +765,44 @@ $SLANG = array( 'VideoGenSucceeded' => 'Video Generering Succeeded!', 'VideoSize' => 'Video Størrelse', 'VideoWriter' => 'Video Skriver', - 'Video' => 'Video', + 'View' => 'Vis', 'ViewAll' => 'Vis Alle', 'ViewEvent' => 'Vis Hændelse', 'ViewPaged' => 'Vis Sidevis', - 'View' => 'Vis', - 'V4L' => 'V4L', - 'V4LCapturesPerFrame' => 'Captures Per Frame', - 'V4LMultiBuffer' => 'Multi Buffering', 'Wake' => 'Vågen', 'WarmupFrames' => 'Opvarmningsbilleder', 'Watch' => 'Ur', - 'WebColour' => 'Web Farve', 'Web' => 'Web', + 'WebColour' => 'Web Farve', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Uge', - 'WhiteBalance' => 'Hvidbalance', 'White' => 'Hvid', + 'WhiteBalance' => 'Hvidbalance', 'Wide' => 'Bred', + 'X' => 'X', + 'X10' => 'X10', 'X10ActivationString' => 'X10 Activerings Streng', 'X10InputAlarmString' => 'X10 Input Alarm Streng', 'X10OutputAlarmString' => 'X10 Output Alarm Streng', - 'X10' => 'X10', - 'X' => 'X', + 'Y' => 'Y', 'Yes' => 'Ja', 'YouNoPerms' => 'Du har ikke tilladelse til at tilgå denne ressurse.', - 'Y' => 'Y', + 'Zone' => 'Zone', 'ZoneAlarmColour' => 'Alarm Farve (Rød/Grøn/Blå)', 'ZoneArea' => 'Zone Område', + 'ZoneExtendAlarmFrames' => 'Udvid Antal Alarm Rammer', 'ZoneFilterSize' => 'Filter Bredde/Højde (pixels)', - 'ZoneMinderLog' => 'ZoneMinder Log', 'ZoneMinMaxAlarmArea' => 'Min/Max Alarmeret Område', 'ZoneMinMaxBlobArea' => 'Min/Max Blob Område', 'ZoneMinMaxBlobs' => 'Min/Max Blobs', 'ZoneMinMaxFiltArea' => 'Min/Max Filtreret Område', 'ZoneMinMaxPixelThres' => 'Min/Max Pixel Grænseværdi (0-255)', + 'ZoneMinderLog' => 'ZoneMinder Log', 'ZoneOverloadFrames' => 'Antal Rammer At Ignorere Efter Overload', - 'ZoneExtendAlarmFrames' => 'Udvid Antal Alarm Rammer', 'Zones' => 'Zoner', - 'Zone' => 'Zone', + 'Zoom' => 'Zoom', 'ZoomIn' => 'Zoom Ind', 'ZoomOut' => 'Zoom Ud', - 'Zoom' => 'Zoom', ); // Complex replacements with formatting and/or placements, must be passed through sprintf diff --git a/web/lang/en_gb.php b/web/lang/en_gb.php index 06b02c3a4..01a458b21 100644 --- a/web/lang/en_gb.php +++ b/web/lang/en_gb.php @@ -237,6 +237,7 @@ $SLANG = array( 'Cause' => 'Cause', 'CheckMethod' => 'Alarm Check Method', 'ChooseDetectedCamera' => 'Choose Detected Camera', + 'ChooseDetectedProfile' => 'Choose Detected Profile', 'ChooseFilter' => 'Choose Filter', 'ChooseLogFormat' => 'Choose a log format', 'ChooseLogSelection' => 'Choose a log selection', @@ -601,6 +602,18 @@ $SLANG = array( 'Preset' => 'Preset', 'Presets' => 'Presets', 'Prev' => 'Prev', + 'Privacy' => 'Privacy', + 'PrivacyAbout' => 'About', + 'PrivacyAboutText' => 'Since 2002, ZoneMinder has been the premier free and open-source Video Management System (VMS) solution for Linux platforms. ZoneMinder is supported by the community and is managed by those who choose to volunteer their spare time to the project. The best way to improve ZoneMinder is to get involved.', + 'PrivacyContact' => 'Contact', + 'PrivacyContactText' => 'Please contact us here for any questions regarding our privacy policy or to have your information removed.

For support, there are three primary ways to engage with the community:

Our Github forum is only for bug reporting. Please use our user forum or slack channel for all other questions or comments.

', + 'PrivacyCookies' => 'Cookies', + 'PrivacyCookiesText' => 'Whether you use a web browser or a mobile app to communicate with the ZoneMinder server, a ZMSESSID cookie is created on the client to uniquely identify a session with the ZoneMinder server. ZmCSS and zmSkin cookies are created to remember your style and skin choices.', + 'PrivacyTelemetry' => 'Telemetry', + 'PrivacyTelemetryText' => 'Because ZoneMinder is open-source, anyone can install it without registering. This makes it difficult to answer questions such as: how many systems are out there, what is the largest system out there, what kind of systems are out there, or where are these systems located? Knowing the answers to these questions, helps users who ask us these questions, and it helps us set priorities based on the majority user base.', + 'PrivacyTelemetryList' => 'The ZoneMinder Telemetry daemon collects the following data about your system:
  • A unique identifier (UUID)
  • City based location is gathered by querying ipinfo.io. City, region, country, latitude, and longitude parameters are saved. The latitude and longitude coordinates are accurate down to the city or town level only!
  • Current time
  • Total number of monitors
  • Total number of events
  • System architecture
  • Operating system kernel, distro, and distro version
  • Version of ZoneMinder
  • Total amount of memory
  • Number of cpu cores
', + 'PrivacyMonitorList' => 'The following configuration parameters from each monitor are collected:
  • Id
  • Name
  • Type
  • Function
  • Width
  • Height
  • Colours
  • MaxFPS
  • AlarmMaxFPS
', + 'PrivacyConclusionText' => 'We are NOT collecting any image specific data from your cameras. We don’t know what your cameras are watching. This data will not be sold or used for any purpose not stated herein. By clicking accept, you agree to send us this data to help make ZoneMinder a better product. By clicking decline, you can still freely use ZoneMinder and all its features.', 'Probe' => 'Probe', 'ProfileProbe' => 'Stream Probe', 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', @@ -639,10 +652,13 @@ $SLANG = array( 'RotateLeft' => 'Rotate Left', 'RotateRight' => 'Rotate Right', 'RTSPTransport' => 'RTSP Transport Protocol', + 'RunAudit' => 'Run Audit Process', 'RunLocalUpdate' => 'Please run zmupdate.pl to update', 'RunMode' => 'Run Mode', 'Running' => 'Running', 'RunState' => 'Run State', + 'RunStats' => 'Run Stats Process', + 'RunTrigger' => 'Run Trigger Process', 'SaveAs' => 'Save as', 'SaveFilter' => 'Save Filter', 'SaveJPEGs' => 'Save JPEGs', @@ -699,6 +715,7 @@ $SLANG = array( 'Stopped' => 'Stopped', 'Stop' => 'Stop', 'StorageArea' => 'Storage Area', + 'StorageDoDelete' => 'Do Deletes', 'StorageScheme' => 'Scheme', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Stream' => 'Stream', diff --git a/web/lang/es_ar.php b/web/lang/es_ar.php index cbb3c2d88..e9c84af93 100644 --- a/web/lang/es_ar.php +++ b/web/lang/es_ar.php @@ -29,6 +29,8 @@ $SLANG = array( 'Actual' => 'Actual', 'AddNewControl' => 'Add New Control', 'AddNewMonitor' => 'Agregar Nuevo Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Agregar Nuevo Usuario', 'AddNewZone' => 'Agregar Nueva Zona', 'Alarm' => 'Alarma', @@ -56,22 +58,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Estado Archivo', 'AttrAvgScore' => 'Puntaje Prom.', 'AttrCause' => 'Cause', - 'AttrDate' => 'Fecha', - 'AttrDateTime' => 'Fecha/Hora', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duración', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Cuadros', 'AttrId' => 'Id', 'AttrMaxScore' => 'Puntaje Máximo', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Nombre Monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Name', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Hora', 'AttrTotalScore' => 'Puntaje Total', - 'AttrWeekday' => 'Día Semana', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Available', // Added - 2009-03-31 @@ -104,9 +116,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Velocidad', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -170,10 +184,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Cerrar', 'Colour' => 'Color', 'Command' => 'Command', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config.', 'ConfiguredFor' => 'Configurado Para', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -231,9 +247,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Duración', 'Edit' => 'Editar', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Enable Alarms', 'Enabled' => 'Habilitado', @@ -250,6 +268,7 @@ $SLANG = array( 'Events' => 'Eventos', 'Exclude' => 'Excluir', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Export', 'ExportDetails' => 'Export Event Details', 'ExportFailed' => 'Export Failed', @@ -279,8 +298,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Ejecutar un comando en las coincidencias', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Mandar un mensaje de los eventos', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filtro Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Subir los eventos que coincidan', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filters', @@ -305,6 +326,7 @@ $SLANG = array( 'Function' => 'Función', 'Gain' => 'Gain', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Crear Video', 'GeneratingVideo' => 'Creando Video', 'GoToZoneMinder' => 'Ir a Zoneminder.com', @@ -325,6 +347,7 @@ $SLANG = array( 'High' => 'Alta', 'HighBW' => 'Alta B/W', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Hora', 'Hue' => 'Saturación', 'Id' => 'Id', @@ -349,6 +372,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Carga', 'Local' => 'Local', 'Log' => 'Log', // Added - 2011-06-16 @@ -435,6 +459,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Monitores', 'Montage' => 'Cámara Múltiple', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mes', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -461,6 +486,7 @@ $SLANG = array( 'Next' => 'Siguiente', 'No' => 'No', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'No hay movimientos grabados para este evento', 'NoGroup' => 'No Group', 'NoSavedFilters' => 'FiltrosNoGuardados', @@ -479,6 +505,8 @@ $SLANG = array( 'OpGt' => 'mayor que', 'OpGtEq' => 'mayor o igual que', 'OpIn' => 'En sistema', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'menor que', 'OpLtEq' => 'menor o igual que', 'OpMatches' => 'Coincide', @@ -488,6 +516,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'Ayuda', 'OptionRestartWarning' => 'Estos cambios no se guardaran completamente\nmientras el sistema se ejecute. Cuando termine\nde realizar los cambios asegurese de\nreiniciar Zoneminder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opciones', 'OrEnterNewName' => 'o agregue nombre', 'Order' => 'Order', @@ -525,9 +554,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Ritmo', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Registro', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Actualizar', 'Remote' => 'Remote', @@ -543,6 +576,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Borrar Contador Eventos', 'Restart' => 'Reiniciar', @@ -561,6 +595,7 @@ $SLANG = array( 'Save' => 'Guardar', 'SaveAs' => 'Guardar Como', 'SaveFilter' => 'Guardar Filtro', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Escala', 'Score' => 'Res.', 'Secs' => 'Seg', @@ -577,6 +612,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Abrir Filtro', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -596,6 +632,10 @@ $SLANG = array( 'State' => 'Estado', 'Stats' => 'Est.', 'Status' => 'Estado', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -606,6 +646,8 @@ $SLANG = array( 'Stills' => 'Fotos', 'Stop' => 'Desactivar', 'Stopped' => 'Apagado', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', @@ -625,6 +667,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Etiqueta Hora', 'TimestampLabelFormat' => 'Formato Etiqueta Hora', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Eje X Etiqueta Hora', 'TimestampLabelY' => 'Eje Y Etiqueta Hora', 'Today' => 'Today', @@ -671,6 +714,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametros Generacion Video', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'Tamaño Video', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Ver', 'ViewAll' => 'Ver Todo', 'ViewEvent' => 'View Event', @@ -680,6 +724,7 @@ $SLANG = array( 'Watch' => 'Monitor', 'Web' => 'Web', 'WebColour' => 'Web Colour', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Semana', 'White' => 'White', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/es_es.php b/web/lang/es_es.php index 3e27d0fae..7b64fdd57 100644 --- a/web/lang/es_es.php +++ b/web/lang/es_es.php @@ -78,6 +78,8 @@ $SLANG = array( 'Actual' => 'Actual', 'AddNewControl' => 'Añadir nuevo control', 'AddNewMonitor' => 'Añadir nuevo monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Añadir nuevo usuario', 'AddNewZone' => 'Añadir nueva zona', 'Alarm' => 'Alarma', @@ -105,22 +107,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Estado de archivo', 'AttrAvgScore' => 'Promed. señal', 'AttrCause' => 'Causa', - 'AttrDate' => 'Fecha', - 'AttrDateTime' => 'Fecha/Hora', 'AttrDiskBlocks' => 'Bloques del disco', 'AttrDiskPercent' => 'Porcentaje del disco', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duración', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Marcos', 'AttrId' => 'Id', 'AttrMaxScore' => 'Señal máxima', 'AttrMonitorId' => 'Id monitor', 'AttrMonitorName' => 'Nombre del monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nombre', 'AttrNotes' => 'Notas', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Carga del sistema', - 'AttrTime' => 'Hora', 'AttrTotalScore' => 'Señal total', - 'AttrWeekday' => 'Día de la semana', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Autodetener tiempo de espera', 'Available' => 'Disponible', @@ -153,9 +165,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'El porcentaje de la referencia de mezcla debe ser un entero positivo', 'BadSectionLength' => 'La duración de la sección debe ser un entero de 30 o más', 'BadSignalCheckColour' => 'El color de verificación de señal debe ser una cadena de color RGB válida', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => 'La secuencia de búfer de reproducción debe ser un entero de cero o más', 'BadWarmupCount' => 'Los marcos de calentamiento deben ser un entero de cero o más', 'BadWebColour' => 'El color web debe ser una cadena de color web válida', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'El ancho debe tener un valor válido', 'Bandwidth' => 'Ancho de banda', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -219,10 +233,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Elegir selección de registro', 'ChoosePreset' => 'Elegir preprogramación', 'Clear' => 'Limpiar', + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Cerrar', 'Colour' => 'Color', 'Command' => 'Comando', 'Component' => 'Componente', + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'Configurado para', 'ConfirmDeleteEvents' => '¿Seguro que desea borrar los eventos seleccionados?', @@ -280,9 +296,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Aún no, recordarme de nuevo en 1 semana', 'DonateYes' => 'Sí, me gustaría hacer una donación ahora', 'Download' => 'Descargar', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicar nombre de monitor', 'Duration' => 'Duración', 'Edit' => 'Editar', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Habilitar alarmas', 'Enabled' => 'Habilitado', @@ -299,6 +317,7 @@ $SLANG = array( 'Events' => 'Eventos', 'Exclude' => 'Excluir', 'Execute' => 'Ejecutar', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportar', 'ExportDetails' => 'Exportar detalles de evento', 'ExportFailed' => 'Fallo al exportar', @@ -328,8 +347,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Ejecutar comando para todas las coincidencias', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Detalles de mensaje de todas las coincidencias', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filtrar Px', 'FilterUnset' => 'Debe especificar un ancho y un alto para el filtro', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Subir todas las coincidencias', 'FilterVideoEvents' => 'Create video for all matches', // Added - 2011-08-23 'Filters' => 'Filtros', @@ -354,6 +375,7 @@ $SLANG = array( 'Function' => 'Función', 'Gain' => 'Ganancia', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Generate Video', // Added - 2011-08-23 'GeneratingVideo' => 'Generating Video', // Added - 2011-08-23 'GoToZoneMinder' => 'Ir a ZoneMinder.com', @@ -374,6 +396,7 @@ $SLANG = array( 'High' => 'Alto', 'HighBW' => 'Alto B/B', 'Home' => 'Inicio', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Hora', 'Hue' => 'Matiz', 'Id' => 'Id', @@ -398,6 +421,7 @@ $SLANG = array( 'Line' => 'Línea', 'LinkedMonitors' => 'Monitores enlazados', 'List' => 'Lista', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Carga', 'Local' => 'Local', 'Log' => 'Registro', @@ -484,6 +508,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'La lista de debajo muestra las cámaras analógicas y en red detectadas y si ya están siendo usadas o están disponibles para seleccionar.

Seleccione la entrada deseada de la lista de debajo.

Por favor tenga en cuenta que podrían no detectarse todas las cámaras y que elegir una cámara aquí podría sobrescribir cualquier valor que ya hubiera configurado para el monitor actual.

', 'Monitors' => 'Monitores', 'Montage' => 'Montaje', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mes', 'More' => 'Más', 'MotionFrameSkip' => 'Motion Frame Skip', @@ -510,6 +535,7 @@ $SLANG = array( 'Next' => 'Siguiente', 'No' => 'No', 'NoDetectedCameras' => 'No se detectaron cámaras', + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'No hay marcos grabados para este evento', 'NoGroup' => 'Sin grupo', 'NoSavedFilters' => 'No hay filtros guardados', @@ -528,6 +554,8 @@ $SLANG = array( 'OpGt' => 'mayor que', 'OpGtEq' => 'mayor que o igual a', 'OpIn' => 'en conjunto', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'menor que', 'OpLtEq' => 'menor que o igual a', 'OpMatches' => 'coincidencias', @@ -537,6 +565,7 @@ $SLANG = array( 'Open' => 'Abrir', 'OptionHelp' => 'Ayuda de la opción', 'OptionRestartWarning' => 'Estos cambios podrían no surtir un efecto completo mientras el sistema esté ejecutándose. Cuando haya terminado haciendo cambios por favor asegúrese de reiniciar ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opciones', 'OrEnterNewName' => 'o introduzca un nuevo nombre', 'Order' => 'Orden', @@ -574,9 +603,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocolo', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Valorar', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Grabar', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Referencia de mezcla de imagen %ge', 'Refresh' => 'Refrescar', 'Remote' => 'Remoto', @@ -592,6 +625,7 @@ $SLANG = array( 'ReplayAll' => 'Todos los eventos', 'ReplayGapless' => 'Eventos sin espacios', 'ReplaySingle' => 'Evento individual', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Restablecer', 'ResetEventCounts' => 'Restablecer número de eventos', 'Restart' => 'Reiniciar', @@ -610,6 +644,7 @@ $SLANG = array( 'Save' => 'Guardar', 'SaveAs' => 'Guardar cómo', 'SaveFilter' => 'Guardar filtro', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Escalar', 'Score' => 'Cuenta', 'Secs' => 'Segs', @@ -626,6 +661,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Mostrar ventana de filtros', 'ShowTimeline' => 'Mostrar línea de tiempo', 'SignalCheckColour' => 'Color de comprobación de señal', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Tamaño', 'SkinDescription' => 'Cambiar el tema por defecto para este ordenador', 'Sleep' => 'Dormir', @@ -645,6 +681,10 @@ $SLANG = array( 'State' => 'Estado', 'Stats' => 'Estadísticas', 'Status' => 'Estado', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Salto', 'StepBack' => 'Salto atrás', 'StepForward' => 'Salto adelante', @@ -655,6 +695,8 @@ $SLANG = array( 'Stills' => 'Fijas', 'Stop' => 'Detener', 'Stopped' => 'Detenido', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Corriente', 'StreamReplayBuffer' => 'Secuencia de búfer de reproducción', 'Submit' => 'Enviar', @@ -674,9 +716,9 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Marca de tiempo', 'TimestampLabelFormat' => 'Formato de hora multinacional', + 'TimestampLabelSize' => 'Tamaño de fuente', 'TimestampLabelX' => 'Etiqueta de tiempo X', 'TimestampLabelY' => 'Etiqueta de tiempo Y', - 'TimestampLabelSize' => 'Tamaño de fuente', 'Today' => 'Hoy', 'Tools' => 'Herramientas', 'Total' => 'Total', @@ -721,6 +763,7 @@ $SLANG = array( 'VideoGenParms' => 'Video Generation Parameters', // Added - 2011-08-23 'VideoGenSucceeded' => 'Video Generation Succeeded!', // Added - 2011-08-23 'VideoSize' => 'Video Size', // Added - 2011-08-23 + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Ver', 'ViewAll' => 'Ver todos', 'ViewEvent' => 'Ver evento', @@ -730,6 +773,7 @@ $SLANG = array( 'Watch' => 'Observar', 'Web' => 'Web', 'WebColour' => 'Color web', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Semana', 'White' => 'Blanco', 'WhiteBalance' => 'Balance de blancos', diff --git a/web/lang/et_ee.php b/web/lang/et_ee.php index 250728550..3a2f68ad9 100644 --- a/web/lang/et_ee.php +++ b/web/lang/et_ee.php @@ -85,6 +85,8 @@ $SLANG = array( 'Actual' => 'Aktuaalne', 'AddNewControl' => 'Lisa uus Kontroll', 'AddNewMonitor' => 'Lisa uus Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Lisa uus Kasutaja', 'AddNewZone' => 'Lisa uus Tsoon', 'Alarm' => 'Alarm', @@ -112,22 +114,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Arhiivi Staatus', 'AttrAvgScore' => 'Keskm. Skoor', 'AttrCause' => 'Põhjus', - 'AttrDate' => 'Kp.', - 'AttrDateTime' => 'Kp/Kellaaeg', 'AttrDiskBlocks' => 'Ketta Blokk', 'AttrDiskPercent' => 'Ketta Protsent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Kestvus', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Kaadrid', 'AttrId' => 'Id', 'AttrMaxScore' => 'Maks. Skoor', 'AttrMonitorId' => 'Monitori Id', 'AttrMonitorName' => 'Monitori Nimi', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nimi', 'AttrNotes' => 'Märkmed', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Süsteemi Koormus', - 'AttrTime' => 'Kellaaeg', 'AttrTotalScore' => 'Skoor Kokku', - 'AttrWeekday' => 'Tööpäevad', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Ajalimiit', 'Available' => 'Saadaval', @@ -160,9 +172,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer' => 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Ribalaius', 'BandwidthHead' => 'Ribalaius', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -226,10 +240,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Sule', 'Colour' => 'Värv', 'Command' => 'Käsk', 'Component' => 'Komponent', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Seadistus', 'ConfiguredFor' => 'Seadistatud', 'ConfirmDeleteEvents' => 'Oled sa kindel kustamaks valitud sündmused?', @@ -287,9 +303,11 @@ $SLANG = array( 'DonateRemindWeek' => 'EI veel, tuleta meelde nädala pärast', 'DonateYes' => 'Jah, Ma soovin annetada', 'Download' => 'Lae alla', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Dubleeri Monitori Nimi', 'Duration' => 'Kestvus', 'Edit' => 'Muuda', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Luba Alarmid', 'Enabled' => 'Lubatud', @@ -306,6 +324,7 @@ $SLANG = array( 'Events' => 'Sündmuseid', 'Exclude' => 'Jäta välja', 'Execute' => 'Käivita', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Eksport', 'ExportDetails' => 'Ekspordi Sündmuste Detailid', 'ExportFailed' => 'Eksportimine Ebaõnnestus', @@ -335,8 +354,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Execute command on all matches', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Message details of all matches', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Upload all matches', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filtrid', @@ -361,6 +382,7 @@ $SLANG = array( 'Function' => 'Funktsioon', 'Gain' => 'Gain', 'General' => 'Peamine', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Genereeri Video', 'GeneratingVideo' => 'Genereerin Videot', 'GoToZoneMinder' => 'Mine ZoneMinder.com', @@ -381,6 +403,7 @@ $SLANG = array( 'High' => 'Suurim', 'HighBW' => 'High B/W', 'Home' => 'Koju', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Tunnis', 'Hue' => 'Hue', 'Id' => 'Id', @@ -405,6 +428,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Lingitud monitorid', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Koormus', 'Local' => 'Local', 'Log' => 'Logi', // Added - 2011-06-16 @@ -491,6 +515,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', 'Monitors' => 'Monitors', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Kuus', 'More' => 'Veel', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -517,6 +542,7 @@ $SLANG = array( 'Next' => 'Järgmine', 'No' => 'Ei', 'NoDetectedCameras' => 'Ei leidnud kaameraid', + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Ei ole kaadreid salvetatud selles sündmuses', 'NoGroup' => 'Ei krupp', 'NoSavedFilters' => 'EiSalvestatudFiltreid', @@ -535,6 +561,8 @@ $SLANG = array( 'OpGt' => 'Suurem kui', 'OpGtEq' => 'suurem kui või võrdne', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'vähem kui', 'OpLtEq' => 'vähem kui või võrdne', 'OpMatches' => 'klapib', @@ -544,6 +572,7 @@ $SLANG = array( 'Open' => 'Ava', 'OptionHelp' => 'Valik Aita', 'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Seaded', 'OrEnterNewName' => 'või sisesta uus nimi', 'Order' => 'Järjekord', @@ -581,9 +610,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Rate', 'Real' => 'Reaaalne', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Salvesta', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Värskenda', 'Remote' => 'Remote', @@ -599,6 +632,7 @@ $SLANG = array( 'ReplayAll' => 'Kõik sündmused', 'ReplayGapless' => 'Lünkadeta sündmused', 'ReplaySingle' => 'Üksik sündmus', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => 'Taaskäivita', @@ -617,6 +651,7 @@ $SLANG = array( 'Save' => 'Salvesta', 'SaveAs' => 'Salvesta kui', 'SaveFilter' => 'Salvesta Filter', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Skaala', 'Score' => 'Skoor', 'Secs' => 'Secs', @@ -633,6 +668,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Näita Filtri Akent', 'ShowTimeline' => 'Näita Timeline', 'SignalCheckColour' => 'Signaali Kontroll Värv', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Suurus', 'SkinDescription' => 'Vaheta veebilehe välimus selles arvutis', // Added - 2011-03-02 'Sleep' => 'Maga', @@ -652,6 +688,10 @@ $SLANG = array( 'State' => 'Olek', 'Stats' => 'Statistika', 'Status' => 'Staatus', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Samm', 'StepBack' => 'Samm tagasi', 'StepForward' => 'Samm edasi', @@ -662,6 +702,8 @@ $SLANG = array( 'Stills' => 'Stills', 'Stop' => 'Stop', 'Stopped' => 'Stopitud', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Striim', 'StreamReplayBuffer' => 'Striimi Replay Pildi Puhver', 'Submit' => 'Submit', @@ -681,6 +723,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Timestamp', 'TimestampLabelFormat' => 'Timestamp Label Format', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Timestamp Label X', 'TimestampLabelY' => 'Timestamp Label Y', 'Today' => 'Täna', @@ -727,6 +770,7 @@ $SLANG = array( 'VideoGenParms' => 'Video Genereerimise Parameetrid', 'VideoGenSucceeded' => 'Video Genereerimine Õnnestus!!!', 'VideoSize' => 'Video Suurus', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Vaata', 'ViewAll' => 'View All', 'ViewEvent' => 'Vaata Sündmust', @@ -736,6 +780,7 @@ $SLANG = array( 'Watch' => 'Vaata', 'Web' => 'Veeb', 'WebColour' => 'Veebi värv', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Nädalas', 'White' => 'White', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/fr_fr.php b/web/lang/fr_fr.php index b140886fe..46590f4aa 100644 --- a/web/lang/fr_fr.php +++ b/web/lang/fr_fr.php @@ -84,6 +84,8 @@ $SLANG = array( 'Actual' => 'Réel', 'AddNewControl' => 'Ajouter contrôle', 'AddNewMonitor' => 'Ajouter caméra', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Ajouter utilisateur', 'AddNewZone' => 'Ajouter zone', 'Alarm' => 'Alarme', @@ -111,22 +113,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Etat Archive', 'AttrAvgScore' => 'Score moy.', 'AttrCause' => 'Cause', - 'AttrDate' => 'Date', - 'AttrDateTime' => 'Date/Heure', 'AttrDiskBlocks' => 'Blocs disque', 'AttrDiskPercent' => '% disque', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Durée', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Images', 'AttrId' => 'Id', 'AttrMaxScore' => 'Score max.', 'AttrMonitorId' => 'N°', 'AttrMonitorName' => 'Nom caméra', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nom', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Charge système', - 'AttrTime' => 'Heure', 'AttrTotalScore' => 'Score total', - 'AttrWeekday' => 'Semaine', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Temporisation arrêt', 'Available' => 'Disponibles', // Added - 2009-03-31 @@ -159,9 +171,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Le pourcentage de fusion de l\'image de référence doit être un entier supérieur à 0 et inférieur à 100', 'BadSectionLength' => 'La longueur de la section doit être un entier supérieur ou égal à 30', 'BadSignalCheckColour' => 'La chaîne de caractères pour la couleur d\'état du signal est invalide', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Le tampon d\'images pour la relecture doit être un entier supérieur ou égal à 0', 'BadWarmupCount' => 'Le nombre d\'images tests doit être un entier supérieur ou égal à 0', 'BadWebColour' => 'La chaîne de caractères pour la couleur web est invalide', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'La valeur de la largeur est invalide', 'Bandwidth' => 'Débit', 'BandwidthHead' => 'Débit', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -225,10 +239,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choisir une sélection de journaux', // Added - 2011-06-17 'ChoosePreset' => 'Choisir préréglage', 'Clear' => 'Effacer', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Fermer', 'Colour' => 'Couleur', 'Command' => 'Commande', 'Component' => 'Composant', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'Configuré pour', 'ConfirmDeleteEvents' => 'Etes-vous sûr de vouloir effacer le(s) événement(s) sélectionné(s)?', @@ -286,9 +302,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Pas encore, me rappeler dans 1 semaine', 'DonateYes' => 'Oui, je souhaiterais faire un don maintenant', 'Download' => 'Télécharger', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Dupliquer le nom de la caméra', // Added - 2009-03-31 'Duration' => 'Durée', 'Edit' => 'Editer', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Activer les alarmes', 'Enabled' => 'Activé', @@ -305,6 +323,7 @@ $SLANG = array( 'Events' => 'Evénements', 'Exclude' => 'Exclure', 'Execute' => 'Exécuter', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exporter', 'ExportDetails' => 'Exporter détails événements', 'ExportFailed' => 'Exportation échouée', @@ -334,8 +353,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Exécuter une commande', 'FilterLog' => 'Filtre', // Added - 2015-04-18 'FilterMessageEvents' => 'Envoyer les détails par message', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filtre Px', 'FilterUnset' => 'Vous devez spécifier une largeur et une hauteur de filtre', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Transférer', 'FilterVideoEvents' => 'Créer vidéo', 'Filters' => 'Filtres', @@ -360,6 +381,7 @@ $SLANG = array( 'Function' => 'Mode', 'Gain' => 'Gain', 'General' => 'Général', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Générer vidéo', 'GeneratingVideo' => 'Génération vidéo', 'GoToZoneMinder' => 'Aller sur ZoneMinder.com', @@ -380,6 +402,7 @@ $SLANG = array( 'High' => 'Haut', 'HighBW' => 'Haut débit', 'Home' => 'Maison', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Heure', 'Hue' => 'Teinte', 'Id' => 'N°', @@ -404,6 +427,7 @@ $SLANG = array( 'Line' => 'Ligne', // Added - 2011-06-16 'LinkedMonitors' => 'Caméra(s) liée(s)', 'List' => 'Liste', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Charge', 'Local' => 'Local', 'Log' => 'Journal', // Added - 2011-06-16 @@ -490,6 +514,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'La liste ci-dessous montre les caméras détectées localement ou sur le réseau, qu\'elles soient déjà configurées ou non.

Sélectionnez la caméra désirée dans la liste.

Veuillez noter que toutes les caméras ne sont pas forcément détectées et que la sauvegarde entraînera l\'écrasement des paramètres déjà configurés pour la caméra en cours.

', // Added - 2009-03-31 'Monitors' => 'Caméras', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mois', 'More' => 'Plus', // Added - 2011-06-16 'MotionFrameSkip' => 'Saut image en alarme', @@ -516,6 +541,7 @@ $SLANG = array( 'Next' => 'Suivant', 'No' => 'Non', 'NoDetectedCameras' => 'Pas de caméras détectées', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Pas d\'images enregistrées pour cet événement', 'NoGroup' => 'Pas de groupe', 'NoSavedFilters' => 'Pas de filtres sauvegardés', @@ -534,6 +560,8 @@ $SLANG = array( 'OpGt' => 'sup. à', 'OpGtEq' => 'plus grand ou égal à', 'OpIn' => 'en lot', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'inf. à', 'OpLtEq' => 'inf. ou égal à', 'OpMatches' => 'correspond', @@ -543,6 +571,7 @@ $SLANG = array( 'Open' => 'Ouvrir', 'OptionHelp' => 'Aide', 'OptionRestartWarning' => 'Ces changements peuvent nécessiter un redémarrage de ZoneMinder pour être pleinement opérationnels.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Options', 'OrEnterNewName' => 'ou entrez nouv. nom', 'Order' => 'Ordre', @@ -580,9 +609,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'La liste ci-dessous montre les profils de flux existants pour la caméra sélectionnée.

Sélectionnez le profil désiré dans la liste ci-dessous.

Veuillez noter que ZoneMinder ne peut pas configurer de profils additionels et que la sauvegarde entraînera l\'écrasement des paramètres déjà configurés pour la caméra en cours.

', // Added - 2015-04-18 'Progress' => 'Progression', // Added - 2015-04-18 'Protocol' => 'Protocole', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Vitesse', 'Real' => 'Réel', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Enregistrer', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => '% fusion image référence', 'Refresh' => 'Rafraîchir', 'Remote' => 'Distant', @@ -598,6 +631,7 @@ $SLANG = array( 'ReplayAll' => 'Tous les événements', 'ReplayGapless' => 'Rejouer sans blancs', 'ReplaySingle' => 'Rejouer seul', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'RàZ', 'ResetEventCounts' => 'RàZ compteur évts', 'Restart' => 'Redémarrer', @@ -616,6 +650,7 @@ $SLANG = array( 'Save' => 'Sauvegarder', 'SaveAs' => 'Sauvegarder sous', 'SaveFilter' => 'Sauvegarder filtre', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Echelle', 'Score' => 'Score', 'Secs' => 'Secs', @@ -632,6 +667,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Filtres', 'ShowTimeline' => 'Afficher chronologie', 'SignalCheckColour' => 'Couleur vérif. signal', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Taille', 'SkinDescription' => 'Remplacer le skin par défaut', // Added - 2011-01-30 'Sleep' => 'Veille', @@ -651,6 +687,10 @@ $SLANG = array( 'State' => 'Etat', 'Stats' => 'Stats', 'Status' => 'Statut', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Pas', 'StepBack' => 'Reculer', 'StepForward' => 'Avancer', @@ -661,6 +701,8 @@ $SLANG = array( 'Stills' => 'Photos', 'Stop' => 'Arrêter', 'Stopped' => 'Arrêté', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Flux', 'StreamReplayBuffer' => 'Nb d\'image(s) pour relecture', 'Submit' => 'Soumettre', @@ -680,9 +722,9 @@ $SLANG = array( 'TimelineTip4' => 'Utilisez les contrôles ci-dessous pour faire un zoom arrière ou naviguer en arrière et avancer sur l\'intervalle de temps.', // Added 2013.08.15. 'Timestamp' => 'Horodatage', 'TimestampLabelFormat' => 'Format', + 'TimestampLabelSize' => 'Taille de police', 'TimestampLabelX' => 'Coordonnée X', 'TimestampLabelY' => 'Coordonnée Y', - 'TimestampLabelSize' => 'Taille de police', 'Today' => 'Aujourd\'hui', 'Tools' => 'Outils', 'Total' => 'Total', // Added - 2011-06-16 @@ -727,6 +769,7 @@ $SLANG = array( 'VideoGenParms' => 'Paramètres génération vidéo', 'VideoGenSucceeded' => 'Vidéo générée avec succès !', 'VideoSize' => 'Taille vidéo', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Voir', 'ViewAll' => 'Tout voir', 'ViewEvent' => 'Voir événement', @@ -736,6 +779,7 @@ $SLANG = array( 'Watch' => 'Regarder', 'Web' => 'Web', 'WebColour' => 'Couleur web', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Semaine', 'White' => 'Blanc', 'WhiteBalance' => 'Balance des blancs', diff --git a/web/lang/he_il.php b/web/lang/he_il.php index f8c8897a4..67b7b87bd 100644 --- a/web/lang/he_il.php +++ b/web/lang/he_il.php @@ -78,6 +78,8 @@ $SLANG = array( 'Actual' => 'î÷åøé', 'AddNewControl' => 'äåñó ÷åðèøåì çãù', 'AddNewMonitor' => 'äåñó îåðéèåø çãù', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'äåñó îùúîù çãù', 'AddNewZone' => 'äåñó àéæåø çãù', 'Alarm' => 'àæò÷ä', @@ -105,22 +107,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archive Status', 'AttrAvgScore' => 'ðé÷åã îîåöò', 'AttrCause' => 'ñéáä', - 'AttrDate' => 'úàøéê', - 'AttrDateTime' => 'úàøéê/ùòä', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'îùê æîï', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'ôøééîéí', 'AttrId' => 'Id', 'AttrMaxScore' => 'ðé÷åã î÷ñéîìé', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'ùí îåðéèåø', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'ùí', 'AttrNotes' => 'äòøåú', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'ùòä', 'AttrTotalScore' => 'ñê ñëåí', - 'AttrWeekday' => 'éåí áùáåò', 'Auto' => 'àåèå', 'AutoStopTimeout' => 'ôñ÷ æîï òöéøä àåèå', 'Available' => 'Available', // Added - 2009-03-31 @@ -153,9 +165,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'øåçá ôñ', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -219,10 +233,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'ñâåø', 'Colour' => 'öáò', 'Command' => 'ô÷åãä', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'úöåøä', 'ConfiguredFor' => 'úöåøä òáåø', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -280,9 +296,11 @@ $SLANG = array( 'DonateRemindWeek' => 'òãééï ìà, äæëø ìé áòåã ùáåò àçã', 'DonateYes' => 'ëï, àðé îòåðééï ìúøåí òëùéå', 'Download' => 'äåøã', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'îùê æîï', 'Edit' => 'òøåê', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'ãåà"ì', 'EnableAlarms' => 'àôùø àæò÷åú', 'Enabled' => 'àôùø', @@ -299,6 +317,7 @@ $SLANG = array( 'Events' => 'àéøåòéí', 'Exclude' => 'ììà', 'Execute' => 'áöò', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'éöà', 'ExportDetails' => 'éöà ôøèé àéøåò', 'ExportFailed' => 'éöåà ðëùì', @@ -328,8 +347,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Execute command on all matches', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Message details of all matches', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', 'FilterUnset' => 'òìéê ìöééï øåçá åâåáä îñðï', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'òìä àú ëì äúåàîéí', 'FilterVideoEvents' => 'öåø åéãàå ìëì äúåàîéí', 'Filters' => 'îñððéí', @@ -354,6 +375,7 @@ $SLANG = array( 'Function' => 'ôåð÷öéä', 'Gain' => 'Gain', 'General' => 'ëììé', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'öåø åéãàå', 'GeneratingVideo' => 'îééöø åéãàå', 'GoToZoneMinder' => 'á÷ø ZoneMinder.com', @@ -374,6 +396,7 @@ $SLANG = array( 'High' => 'âáåä', 'HighBW' => 'âáåä ø/ô', 'Home' => 'áéú', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'ùòä', 'Hue' => 'Hue', 'Id' => 'æéäåé', @@ -398,6 +421,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'îåðéèåøéí î÷åùøéí', 'List' => 'øùéîä', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'èòï', 'Local' => 'î÷åîé', 'Log' => 'Log', // Added - 2011-06-16 @@ -484,6 +508,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'îåðéèåøéí', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'çåãù', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -510,6 +535,7 @@ $SLANG = array( 'Next' => 'äáà', 'No' => 'ìà', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'There are no frames recorded for this event', 'NoGroup' => 'ììà ÷áåöä', 'NoSavedFilters' => 'NoSavedFilters', @@ -528,6 +554,8 @@ $SLANG = array( 'OpGt' => 'âãåì î', 'OpGtEq' => 'greater than or equal to', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'ôçåú î', 'OpLtEq' => 'less than or equal to', 'OpMatches' => 'matches', @@ -537,6 +565,7 @@ $SLANG = array( 'Open' => 'ôúç', 'OptionHelp' => 'OptionHelp', 'OptionRestartWarning' => 'These changes may not come into effect fully\nwhile the system is running. When you have\nfinished making your changes please ensure that\nyou restart ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'àôùøåéåú', 'OrEnterNewName' => 'or enter new name', 'Order' => 'îéåï', @@ -574,9 +603,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'ãéøåâ', 'Real' => 'àîéúé', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'ä÷ìèä', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'øòðåï', 'Remote' => 'îøåç÷', @@ -592,6 +625,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'àôñ', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => 'àúçì', @@ -610,6 +644,7 @@ $SLANG = array( 'Save' => 'ùîåø', 'SaveAs' => 'ùîåø áùí', 'SaveFilter' => 'ùîåø îñðï', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'ñ÷àìä', 'Score' => 'ðé÷åã', 'Secs' => 'ùðéåú', @@ -626,6 +661,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Show Filter Window', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'âåãì', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'ùéðä', @@ -645,6 +681,10 @@ $SLANG = array( 'State' => 'îöá', 'Stats' => 'îöáéí', 'Status' => 'ñèèåñ', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'öòã', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -655,6 +695,8 @@ $SLANG = array( 'Stills' => 'ñèéìñ', 'Stop' => 'òöåø', 'Stopped' => 'ðòöø', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'ñèøéí', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', @@ -674,6 +716,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'çåúîú æîï', 'TimestampLabelFormat' => 'Timestamp Label Format', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Timestamp Label X', 'TimestampLabelY' => 'Timestamp Label Y', 'Today' => 'äéåí', @@ -720,6 +763,7 @@ $SLANG = array( 'VideoGenParms' => 'Video Generation Parameters', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'âåãì åéãàå', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'äöâ', 'ViewAll' => 'äöâ äëì', 'ViewEvent' => 'äöâ àéøåò', @@ -729,6 +773,7 @@ $SLANG = array( 'Watch' => 'öôä', 'Web' => 'àéðèøðè', 'WebColour' => 'öáò àéðèøðè', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'ùáåò', 'White' => 'ìáï', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/hu_hu.php b/web/lang/hu_hu.php index 3f0928be1..7c1336a87 100644 --- a/web/lang/hu_hu.php +++ b/web/lang/hu_hu.php @@ -121,6 +121,8 @@ $SLANG = array( 'Actual' => 'Valós', 'AddNewControl' => 'Új vezérlés', 'AddNewMonitor' => 'Új kamera', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Új felhasználó', 'AddNewZone' => 'Új zóna', 'Alarm' => 'Riadó', @@ -148,22 +150,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archivált állapot', 'AttrAvgScore' => 'Átlagérték', 'AttrCause' => 'Okozó', - 'AttrDate' => 'Dátum', - 'AttrDateTime' => 'Dátum/Idő', 'AttrDiskBlocks' => 'Tárhely blokk', 'AttrDiskPercent' => 'Tárhely százalék', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Időtartam', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Képkocka', 'AttrId' => 'Azonosító', 'AttrMaxScore' => 'Max. érték', 'AttrMonitorId' => 'Kamera azonosító', 'AttrMonitorName' => 'Kamera név', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Név', 'AttrNotes' => 'Megjegyzés', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Rendszerterhelés', - 'AttrTime' => 'Idő', 'AttrTotalScore' => 'Összérték', - 'AttrWeekday' => 'Hétköznap', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto megállási idő túllépés', 'Available' => 'Elérhető', @@ -196,9 +208,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Változás a referenciaképtől legyen legalább 1', 'BadSectionLength' => 'Fix időtartamú esemény hossza legyen legalább 30', 'BadSignalCheckColour' => 'Szín a jel kimaradásakor legyen egy érvényes HTML szín-kód', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Folyam visszajátszó puffer legyen legalább 0', 'BadWarmupCount' => 'Bemelegítő képkockák száma legyen legalább 0', 'BadWebColour' => 'Szín az idővonal ablakban legyen egy érvényes HTML szín-kód', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'A képszélesség legyen érvényes érték képpontban megadva', 'Bandwidth' => 'Sávszélesség', 'BandwidthHead' => 'sávszélességre', @@ -262,10 +276,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Válasszon naplót', 'ChoosePreset' => 'Válasszon profilt', 'Clear' => 'Törlés', + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Bezárás', 'Colour' => 'Szín', 'Command' => 'Parancs', 'Component' => 'Komponens', + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Beállítás', 'ConfiguredFor' => 'Beállítva', 'ConfirmDeleteEvents' => 'Biztos benne, hogy törli a kiválasztott eseményeket?', @@ -323,9 +339,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Nem most, figyelmeztessen egy hét múlva', 'DonateYes' => 'Igen, most szeretném támogatni', 'Download' => 'Letöltés', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Kameranév duplikálás', 'Duration' => 'Időtartam', 'Edit' => 'Szerkesztés', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'E-mail', 'EnableAlarms' => 'Riasztások engedélyezése', 'Enabled' => 'Engedélyezve', @@ -342,6 +360,7 @@ $SLANG = array( 'Events' => 'Események', 'Exclude' => 'Kizárás', 'Execute' => 'Végrehajtás', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportálás', 'ExportDetails' => 'Esemény adatainak exportálása', 'ExportFailed' => 'Az exportálás sikertelen', @@ -371,8 +390,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Parancs futtatása minden találaton', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Minden találat adatainak üzenése', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Szűrt képkockák', 'FilterUnset' => 'Meg kell adnod a szűrő szélességét és magasságát', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Minden találat feltöltése', 'FilterVideoEvents' => 'Videó készítése minden találatról', 'Filters' => 'Szűrők', @@ -397,6 +418,7 @@ $SLANG = array( 'Function' => 'Funkció', 'Gain' => 'Erősítés', 'General' => 'Általános', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Videó készítés', 'GeneratingVideo' => 'Videó készítése folyamatban', 'GoToZoneMinder' => 'Ellenőrzés a ZoneMinder.com-on', @@ -417,6 +439,7 @@ $SLANG = array( 'High' => 'Magas', 'HighBW' => 'Magas
sávszél', 'Home' => 'Alapba', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Órában', 'Hue' => 'Színárnyalat', 'Id' => 'Az.', @@ -441,6 +464,7 @@ $SLANG = array( 'Line' => 'Sor', 'LinkedMonitors' => 'Összefüggés más kamerákkal
(jelölés Ctrl+kattintással)', 'List' => 'Lista', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Terhelés', 'Local' => 'Helyi', 'Log' => 'Napló', @@ -527,6 +551,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'Az alábbi listában találhatók az automatikusan érzékelt analóg és hálózati kamerákat, illetve azt, hogy közülük melyik van használatban, vagy kiválasztható.

Válasszon egyet az alábbi listából.

Figyelem: nem biztos, hogy minden kamerát lehet automatikusan érzékelni. Az itt kiválasztott kamara adatai felülírhatják azokat, amelyeket már ehhez a monitorhoz beállított.

', 'Monitors' => 'Kamerák', 'Montage' => 'Többkamerás nézet', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Hónapban', 'More' => 'Több', 'MotionFrameSkip' => 'Motion Frame Skip', @@ -553,6 +578,7 @@ $SLANG = array( 'Next' => 'Következő', 'No' => 'Nem', 'NoDetectedCameras' => 'Nem érzékelhetőek kamerák', + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Nincs rögzített képkocka ehhez az eseményhez', 'NoGroup' => 'Nincs csoport', 'NoSavedFilters' => 'Nincs mentett szűrő', @@ -571,6 +597,8 @@ $SLANG = array( 'OpGt' => 'nagyobb mint', 'OpGtEq' => 'nagyobb van egyenlő', 'OpIn' => 'beállítva', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'kisebb mint', 'OpLtEq' => 'kisebb vagy egyenlő', 'OpMatches' => 'találatok', @@ -580,6 +608,7 @@ $SLANG = array( 'Open' => 'Megnyitás', 'OptionHelp' => 'Beállítási segítség', 'OptionRestartWarning' => 'Ez a beállítás nem tud érvénybe lépni miközben az élő rendszer fut.\nHa végzett minden beállítással, kérem, indítsa újra a ZoneMinder szolgáltatást.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Beállítások', 'OrEnterNewName' => 'vagy új néven:', 'Order' => 'Sorrend', @@ -617,9 +646,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'FPS', 'Real' => 'Valós', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Felvétel', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Változás a referenciaképtől %-ban', 'Refresh' => 'Frissítés', 'Remote' => 'Hálózati', @@ -635,6 +668,7 @@ $SLANG = array( 'ReplayAll' => 'Mindet', 'ReplayGapless' => 'Szünet nélkülieket', 'ReplaySingle' => 'Egyenként', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Alapértékre', 'ResetEventCounts' => 'Eseményszámláló nullázása', 'Restart' => 'A szolgáltatás újraindítása', @@ -653,6 +687,7 @@ $SLANG = array( 'Save' => 'Mentés', 'SaveAs' => 'Mentés erre:', 'SaveFilter' => 'Szűrő mentése', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Méret', 'Score' => 'Pontszám', 'Secs' => 'mp.', @@ -669,6 +704,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Szűrőablak megjelenítése', 'ShowTimeline' => 'Idővonal megjelenítése', 'SignalCheckColour' => 'Szín a jel kimaradásakor', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Fájlméret', 'SkinDescription' => 'Alapértelmezett felület ebben a böngészőben', 'Sleep' => 'Alvás', @@ -688,6 +724,10 @@ $SLANG = array( 'State' => 'Állapot', 'Stats' => 'Statisztikák', 'Status' => 'Státusz', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Ugrás', 'StepBack' => 'Visszalépés', 'StepForward' => 'Előrelépés', @@ -698,6 +738,8 @@ $SLANG = array( 'Stills' => 'Állóképek', 'Stop' => 'A szolgáltatás leállítása', 'Stopped' => 'Leállítva', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Élő folyam', 'StreamReplayBuffer' => 'Képkockák száma a pufferben visszajátszáskor', 'Submit' => 'Küldés', @@ -717,6 +759,7 @@ $SLANG = array( 'TimelineTip4' => 'Használja az alábbi gombokat hogy az időskálát csúsztassa, vagy kicsinyítse.', 'Timestamp' => 'Időbélyeg', 'TimestampLabelFormat' => 'Időbélyeg formátuma', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Elhelyezés X pozició', 'TimestampLabelY' => 'Elhelyezés Y pozició', 'Today' => 'Ma', @@ -763,6 +806,7 @@ $SLANG = array( 'VideoGenParms' => 'Videó készítési paraméterek', 'VideoGenSucceeded' => 'A videó elkészült.', 'VideoSize' => 'Képméret', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Megtekintés', 'ViewAll' => 'Az összes listázása', 'ViewEvent' => 'Események nézet', @@ -772,6 +816,7 @@ $SLANG = array( 'Watch' => 'Figyelés', 'Web' => 'Web', 'WebColour' => 'Szín az idővonal skálán', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Héten', 'White' => 'Fehér', 'WhiteBalance' => 'Fehér egyensúly', diff --git a/web/lang/it_it.php b/web/lang/it_it.php index 338f2c07f..8890650e8 100644 --- a/web/lang/it_it.php +++ b/web/lang/it_it.php @@ -83,6 +83,8 @@ $SLANG = array( 'Actual' => 'Attuale', 'AddNewControl' => 'Aggiungi nuovo Controllo', 'AddNewMonitor' => 'Aggiungi nuovo Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Aggiungi nuovo Utente', 'AddNewZone' => 'Aggiungi nuova Zona', 'Alarm' => 'Allarme', @@ -110,22 +112,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Stato Archivio', 'AttrAvgScore' => 'Punteggio medio', 'AttrCause' => 'Causa', - 'AttrDate' => 'Data', - 'AttrDateTime' => 'Data/Ora', 'AttrDiskBlocks' => 'Blocchi del Disco', 'AttrDiskPercent' => 'Percentuale del Disco', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Durata', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Immagini', 'AttrId' => 'Id', 'AttrMaxScore' => 'Punteggio massimo', 'AttrMonitorId' => 'Id Monitor', 'AttrMonitorName' => 'Nome Monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nome', 'AttrNotes' => 'Note', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Ora', 'AttrTotalScore' => 'Punteggio totale', - 'AttrWeekday' => 'Giorno della settimana', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Disponibile', // Added - 2009-03-31 @@ -158,9 +170,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'La percentuale di miscela di riferimento deve essere un intero positivo', 'BadSectionLength' => 'La lunghezza della sezione deve essere un numero intero pari a 30 o maggiore', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Il numero di frame di allarme deve essere un numero intero maggiore o uguale a zero', 'BadWebColour' => 'L\'identificativo del colore deve essere una stringa valida', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'La larghezza deve essere impostata con un valore valido', 'Bandwidth' => 'Banda', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -224,10 +238,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Scegli Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Chiudi', 'Colour' => 'Colori', 'Command' => 'Comando', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Configura', 'ConfiguredFor' => 'Configurato per', 'ConfirmDeleteEvents' => 'Sei sicuro di voler cancellare gli eventi selezionati', @@ -285,9 +301,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Non ancora, ricordamelo ancora tra 1 settimana', 'DonateYes' => 'Si,mi piacerebbe donare qualcosa ora', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Il nome del monitor e\' gia\' presente', // Added - 2009-03-31 'Duration' => 'Durata', 'Edit' => 'Modifica', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Abilita Allarmi', 'Enabled' => 'Attivo', @@ -304,6 +322,7 @@ $SLANG = array( 'Events' => 'Eventi', 'Exclude' => 'Escludi', 'Execute' => 'Esegui', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Esporta', 'ExportDetails' => 'Esp. dettagli eventi', 'ExportFailed' => 'Esp. Fallita ', @@ -333,8 +352,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Esegui un comando', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Invia dettagli tramite messaggio', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Px Filtro', 'FilterUnset' => 'Devi specificare altezza e larghezza per il filtro', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Fai upload eventi (FTP)', 'FilterVideoEvents' => 'Crea video per tutte le corrispondenze', 'Filters' => 'Filtri', @@ -359,6 +380,7 @@ $SLANG = array( 'Function' => 'Funzione', 'Gain' => 'Gain', 'General' => 'Generale', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Genera Video', 'GeneratingVideo' => 'Sto generando il Video', 'GoToZoneMinder' => 'Vai su zoneminder.com', @@ -379,6 +401,7 @@ $SLANG = array( 'High' => 'Alta', 'HighBW' => 'Banda Alta', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Ora', 'Hue' => 'Tinta', 'Id' => 'Id', @@ -403,6 +426,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Monitor Collegati', 'List' => 'Lista', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Carico Sistema', 'Local' => 'Locale', 'Log' => 'Log', // Added - 2011-06-16 @@ -489,6 +513,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Monitors', 'Montage' => 'Montaggio', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mese', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -515,6 +540,7 @@ $SLANG = array( 'Next' => 'Prossimo', 'No' => 'No', 'NoDetectedCameras' => 'Nessuna telecamera rilevata', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Non ci sono immagini salvate per questo evento', 'NoGroup' => 'Nessun gruppo', // Added - 2009-02-08 'NoSavedFilters' => 'NessunFiltroSalvato', @@ -533,6 +559,8 @@ $SLANG = array( 'OpGt' => 'maggiore di', 'OpGtEq' => 'maggiore o uguale a', 'OpIn' => 'impostato', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'minore di', 'OpLtEq' => 'minore o uguale a', 'OpMatches' => 'corrisponde', @@ -542,6 +570,7 @@ $SLANG = array( 'Open' => 'Apri', 'OptionHelp' => 'Opzioni di Aiuto', 'OptionRestartWarning' => 'Queste modifiche potrebbero essere attive solo dopo un riavvio del sistema. Riavviare ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opzioni', 'OrEnterNewName' => 'o inserisci un nuovo nome', 'Order' => 'Ordine', @@ -579,9 +608,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Velocita\'', 'Real' => 'Reale', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Registra', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Riferimento Miscela Immagine percentuale', 'Refresh' => 'Aggiorna', 'Remote' => 'Remoto', @@ -597,6 +630,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Resetta', 'ResetEventCounts' => 'Resetta Contatore Eventi', 'Restart' => 'Riavvia', @@ -615,6 +649,7 @@ $SLANG = array( 'Save' => 'Salva', 'SaveAs' => 'Salva come', 'SaveFilter' => 'salva Filtro', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Scala', 'Score' => 'Punteggio', 'Secs' => 'Secs', @@ -631,6 +666,7 @@ $SLANG = array( 'ShowFilterWindow' => 'MostraFinestraFiltri', 'ShowTimeline' => 'Mostra linea temporale', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'grandezza', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -650,6 +686,10 @@ $SLANG = array( 'State' => 'Stato', 'Stats' => 'Statistiche', 'Status' => 'Stato', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Passo', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -660,6 +700,8 @@ $SLANG = array( 'Stills' => 'Foto', 'Stop' => 'Stop', 'Stopped' => 'Inattivo', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Flusso', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Accetta', @@ -679,6 +721,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Timestamp', 'TimestampLabelFormat' => 'Formato etichetta timestamp', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'coordinata X etichetta', 'TimestampLabelY' => 'coordinata Y etichetta', 'Today' => 'Oggi ', @@ -725,6 +768,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametri Generazione Video', 'VideoGenSucceeded' => 'Successo: Generato Video !', 'VideoSize' => 'Dimensioni Video', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'vedi', 'ViewAll' => 'Vedi Tutto', 'ViewEvent' => 'Vedi Evento', @@ -734,6 +778,7 @@ $SLANG = array( 'Watch' => 'Guarda', 'Web' => 'Web', 'WebColour' => 'Colore Web', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Settimana', 'White' => 'Bianco', 'WhiteBalance' => 'Bil. Bianco ', diff --git a/web/lang/ja_jp.php b/web/lang/ja_jp.php index 340d2caac..4d1854550 100644 --- a/web/lang/ja_jp.php +++ b/web/lang/ja_jp.php @@ -79,6 +79,8 @@ $SLANG = array( 'Actual' => '生中継', 'AddNewControl' => 'Add New Control', 'AddNewMonitor' => 'モニター追加', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'ユーザ追加', 'AddNewZone' => 'ゾーン追加', 'Alarm' => 'アラーム', @@ -106,22 +108,32 @@ $SLANG = array( 'AttrArchiveStatus' => '保存状態', 'AttrAvgScore' => '平均スコアー', 'AttrCause' => 'Cause', - 'AttrDate' => '日付', - 'AttrDateTime' => '日時', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => '継続時間', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'フレーム', 'AttrId' => 'Id', 'AttrMaxScore' => '最高スコアー', 'AttrMonitorId' => 'モニター Id', 'AttrMonitorName' => 'モニター 名前', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Name', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => '時間', 'AttrTotalScore' => '合計スコアー', - 'AttrWeekday' => '曜日', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Available', // Added - 2009-03-31 @@ -154,9 +166,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => '帯域幅', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -220,10 +234,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => '閉じる', 'Colour' => '色', 'Command' => 'Command', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => '設定:', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -281,12 +297,14 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => '継続時間', 'Edit' => '編集', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'メール', 'EnableAlarms' => 'Enable Alarms', - 'Enabled' => '使用可能\', + 'Enabled' => '使用可能', 'EnterNewFilterName' => '新しいフィルター名の入力', 'Error' => 'エラー', 'ErrorBrackets' => 'エラー、開き括弧と閉じ括弧の数が合っているのかを確認してください', @@ -300,6 +318,7 @@ $SLANG = array( 'Events' => 'イベント', 'Exclude' => '排除', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Export', 'ExportDetails' => 'Export Event Details', 'ExportFailed' => 'Export Failed', @@ -329,8 +348,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Execute command on all matches', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Message details of all matches', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'フィルター Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Upload all matches', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filters', @@ -351,10 +372,11 @@ $SLANG = array( 'FrameRate' => 'フレームレート', 'FrameSkip' => 'フレームスキップ', 'Frames' => 'フレーム', - 'Func' => '機能\', - 'Function' => '機能\', + 'Func' => '機能', + 'Function' => '機能', 'Gain' => 'Gain', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'ビデオの生成', 'GeneratingVideo' => 'ビデオ生成中', 'GoToZoneMinder' => 'ZoneMinder.comに行く', @@ -375,6 +397,7 @@ $SLANG = array( 'High' => '高', 'HighBW' => '高帯域', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => '時', 'Hue' => '色相', 'Id' => 'ID', @@ -399,6 +422,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Load', 'Local' => 'ローカル', 'Log' => 'Log', // Added - 2011-06-16 @@ -485,6 +509,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'モニター', 'Montage' => 'モンタージュ', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => '月', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -511,6 +536,7 @@ $SLANG = array( 'Next' => '次', 'No' => 'いいえ', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'このイベントのフレームは登録されていません', 'NoGroup' => 'No Group', 'NoSavedFilters' => '保存されたフィルターはありません', @@ -529,6 +555,8 @@ $SLANG = array( 'OpGt' => '以下', 'OpGtEq' => '同等か以上', 'OpIn' => 'セットに入っている', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => '以下', 'OpLtEq' => '同等か以下', 'OpMatches' => '一致する', @@ -538,6 +566,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'オプション ヘルプ', 'OptionRestartWarning' => 'この変更は起動中反映されない場合があります。\n変更してからZoneMinderを再起動してください。', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'オプション', 'OrEnterNewName' => '又は新しい名前を入力してください', 'Order' => 'Order', @@ -575,9 +604,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'レート', 'Real' => '生中継', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => '録画', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'イメージ ブレンド 参照 %', 'Refresh' => '最新の情報に更新', 'Remote' => 'リモート', @@ -593,6 +626,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'イベント カウント リセット', 'Restart' => '再起動', @@ -611,6 +645,7 @@ $SLANG = array( 'Save' => '保存', 'SaveAs' => '名前をつけて保存', 'SaveFilter' => 'フィルターを保存', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'スケール', 'Score' => 'スコアー', 'Secs' => '秒', @@ -627,6 +662,7 @@ $SLANG = array( 'ShowFilterWindow' => 'フィルター ウインドーの表示', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -646,6 +682,10 @@ $SLANG = array( 'State' => '状態', 'Stats' => '統計', 'Status' => '状態', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -656,6 +696,8 @@ $SLANG = array( 'Stills' => 'スチール画像', 'Stop' => '停止', 'Stopped' => '停止状態', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'ストリーム', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', @@ -675,6 +717,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'タイムスタンプ', 'TimestampLabelFormat' => 'タイムスタンプ ラベル フォーマット', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'タイムスタンプ ラベル X', 'TimestampLabelY' => 'タイムスタンプ ラベル Y', 'Today' => 'Today', @@ -721,6 +764,7 @@ $SLANG = array( 'VideoGenParms' => 'ビデオ生成 パラメータ', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'ビデオ サイズ', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => '表示', 'ViewAll' => '全部表示', 'ViewEvent' => 'View Event', @@ -730,6 +774,7 @@ $SLANG = array( 'Watch' => '監視', 'Web' => 'ウェブ', 'WebColour' => 'Web Colour', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => '週', 'White' => 'White', 'WhiteBalance' => 'White Balance', @@ -767,7 +812,7 @@ $CLANG = array( 'LastEvents' => '最終 %1$s %2$s', 'LatestRelease' => '最新バージョンは v%1$s、ご利用バージョンはv%2$s.', 'MonitorCount' => '%1$s %2$s', - 'MonitorFunction' => 'モニター%1$s 機能\', + 'MonitorFunction' => 'モニター%1$s 機能', 'RunningRecentVer' => 'あなたはZoneMinderの最新バージョン v%s.を使っています', 'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.', // Added - 2011-05-25 ); diff --git a/web/lang/nl_nl.php b/web/lang/nl_nl.php index fca70a829..a50fdd082 100644 --- a/web/lang/nl_nl.php +++ b/web/lang/nl_nl.php @@ -79,6 +79,8 @@ $SLANG = array( 'Actual' => 'Origineel', 'AddNewControl' => 'Nieuwe controle toevoegen', 'AddNewMonitor' => 'Nieuwe monitor toevoegen', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Nieuwe gebruiker toevoegen', 'AddNewZone' => 'Nieuw gebied toevoegen', 'Alarm' => 'Alarm', @@ -106,22 +108,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Archiefstatus', 'AttrAvgScore' => 'Gem. score', 'AttrCause' => 'Oorzaak', - 'AttrDate' => 'Datum', - 'AttrDateTime' => 'Datum/tijd', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Disk Percent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duur', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Frames', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. Score', 'AttrMonitorId' => 'Monitor Id', 'AttrMonitorName' => 'Monitor Naam', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Naam', 'AttrNotes' => 'Notities', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Systembelasting', - 'AttrTime' => 'Tijd', 'AttrTotalScore' => 'Totale Score', - 'AttrWeekday' => 'Weekdag', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Beschikbaar', @@ -154,9 +166,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage moet een geldige waarde van nul of groter zijn', 'BadSectionLength' => 'Sectielengte moet een getal van 30 of groter zijn', 'BadSignalCheckColour' => 'Signaalcontrolekleur moet een geldige RGB waarde zijn', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer moet een geldige waarde van nul of groter zijn', 'BadWarmupCount' => 'Opwarm frames moet een geldig getal van nul of groter zijn', 'BadWebColour' => 'Webkleur moet een geldige webkleurwaarde bevatten', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Breedte moet een geldige waarde zijn', 'Bandwidth' => 'Bandbreedte', 'BandwidthHead' => 'bandbreedte', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -220,10 +234,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Kies een logselectie', 'ChoosePreset' => 'Kies voorkeur', 'Clear' => 'Legen', + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Sluiten', 'Colour' => 'Kleur', 'Command' => 'Commando', 'Component' => 'Component', + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Configuratie', 'ConfiguredFor' => 'Geconfigureerd voor', 'ConfirmDeleteEvents' => 'Weet u zeker dat u deze gebeurtenissen wilt verwijderen?', @@ -281,9 +297,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Nu niet, herinner mij over een week hieraan', 'DonateYes' => 'Ja, ik wil nu doneren', 'Download' => 'Downloaden', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Kopieer monitornaam', 'Duration' => 'Duur', 'Edit' => 'Bewerken', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Alarmen inschakelen', 'Enabled' => 'Ingeschakeld', @@ -300,6 +318,7 @@ $SLANG = array( 'Events' => 'Gebeurtenissen', 'Exclude' => 'Uitsluiten', 'Execute' => 'Uitvoeren', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exporteren', 'ExportDetails' => 'Exporteer gebeurtenisdetails', 'ExportFailed' => 'Exporteren mislukt', @@ -329,8 +348,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Voer opdrachten uit op alle overeenkomsten', 'FilterLog' => 'Filterlog', 'FilterMessageEvents' => 'Bericht de details van alle overeenkomsten', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter px', 'FilterUnset' => 'Je moet de filterhoogte en -breedte opgeven', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Verstuur alle overeenkomsten', 'FilterVideoEvents' => 'Maak video voor alle overeenkomsten', 'Filters' => 'Filters', @@ -355,6 +376,7 @@ $SLANG = array( 'Function' => 'Functie', 'Gain' => 'Gain', 'General' => 'Algemeen', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Genereer Video', 'GeneratingVideo' => 'Video wordt gegenereerd', 'GoToZoneMinder' => 'Ga naar ZoneMinder.com', @@ -375,6 +397,7 @@ $SLANG = array( 'High' => 'Hoog', 'HighBW' => 'Hoog B/W', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Uur', 'Hue' => 'Hue', 'Id' => 'Id', @@ -399,6 +422,7 @@ $SLANG = array( 'Line' => 'Lijn', 'LinkedMonitors' => 'Gekoppelde monitoren', 'List' => 'Lijst', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Systeemlast', 'Local' => 'Lokaal', 'Log' => 'Log', @@ -485,6 +509,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'Deze lijst toont gedeteerde analoge en netwerk cameras en of deze al in gebruik of beschikbaar zijn.

Selecteer de gewenste waarde uit de lijst hieronder.

Let op dat mogelijk niet alle cameras hier worden weergegeven en dat alle ingevoerde waarden voor de huidige monitor zullen worden overschreven.

', 'Monitors' => 'Monitoren', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Maand', 'More' => 'Meer', 'MotionFrameSkip' => 'Motion Frame Skip', @@ -511,6 +536,7 @@ $SLANG = array( 'Next' => 'Volgende', 'No' => 'Nee', 'NoDetectedCameras' => 'Geen cameras gedetecteerd', + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Er zijn geen beelden opgenomen voor deze gebeurtenis', 'NoGroup' => 'Geen Groep', 'NoSavedFilters' => 'Geen Opgeslagen Filters', @@ -529,6 +555,8 @@ $SLANG = array( 'OpGt' => 'groter dan', 'OpGtEq' => 'groter dan of gelijk aan', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'kleiner dan', 'OpLtEq' => 'kleiner dan of gelijk aan', 'OpMatches' => 'Komt overeen met', @@ -538,6 +566,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'OptieHelp', 'OptionRestartWarning' => 'Deze veranderingen worden niet\ndoorgevoerd als het systeem loopt.\nVergeet niet ZoneMinder te herstarten\nwanneer u klaar bent.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opties', 'OrEnterNewName' => 'of voer een nieuwe naam in', 'Order' => 'Sorteren', @@ -575,9 +604,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Voortgang', 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Snelheid', 'Real' => 'Echte', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Record', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Referentie beeld blend percentage', 'Refresh' => 'Verversen', 'Remote' => 'Remote', @@ -593,6 +626,7 @@ $SLANG = array( 'ReplayAll' => 'Alle Gebeurtenissen', 'ReplayGapless' => 'Opvolgende Gebeurtenissen', 'ReplaySingle' => 'Enkele Gebeurtenis', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Resetten', 'ResetEventCounts' => 'Gebeurtenisteller resetten', 'Restart' => 'Herstart', @@ -611,6 +645,7 @@ $SLANG = array( 'Save' => 'Opslaan', 'SaveAs' => 'Opslaan als', 'SaveFilter' => 'Filter opslaan', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Schaal', 'Score' => 'Score', 'Secs' => 'Sec.', @@ -627,6 +662,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Toon Filtervenster', 'ShowTimeline' => 'Toon Tijdlijn', 'SignalCheckColour' => 'Signaalcontrolekleur', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Groote', 'SkinDescription' => 'Wijzig standaarduiterlijk voor deze computer', 'Sleep' => 'Slaap', @@ -646,6 +682,10 @@ $SLANG = array( 'State' => 'Status', 'Stats' => 'Stats', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Stap', 'StepBack' => 'Stap Terug', 'StepForward' => 'Stap Vooruit', @@ -656,6 +696,8 @@ $SLANG = array( 'Stills' => 'Beelden', 'Stop' => 'Stoppen', 'Stopped' => 'Gestopt', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Stream', 'StreamReplayBuffer' => 'Stream Replay beeldbuffer', 'Submit' => 'Verzenden', @@ -675,6 +717,7 @@ $SLANG = array( 'TimelineTip4' => 'Gebruik de knoppen hieronder om uit te zoomen of voor- en achteruit te navigeren.', 'Timestamp' => 'Tijdstempel', 'TimestampLabelFormat' => 'Formaat tijdstempel', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Tijdstempel X-positie', 'TimestampLabelY' => 'Tijdstempel Y-positie', 'Today' => 'Vandaag', @@ -721,6 +764,7 @@ $SLANG = array( 'VideoGenParms' => 'Videogeneratie Parameters', 'VideoGenSucceeded' => 'Videogeneratie voltooid!', 'VideoSize' => 'Videogrootte', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Bekijk', 'ViewAll' => 'Bekijk Alles', 'ViewEvent' => 'Bekijk Gebeurtenis', @@ -730,6 +774,7 @@ $SLANG = array( 'Watch' => 'Observeer', 'Web' => 'Web', 'WebColour' => 'Webkleur', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Week', 'White' => 'Wit', 'WhiteBalance' => 'Witbalans', diff --git a/web/lang/pl_pl.php b/web/lang/pl_pl.php index 325c2e1b9..48c34b1a3 100644 --- a/web/lang/pl_pl.php +++ b/web/lang/pl_pl.php @@ -93,6 +93,8 @@ $SLANG = array( 'Actual' => 'Domyślna', 'AddNewControl' => 'Add New Control', 'AddNewMonitor' => 'Dodaj nowy monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Dodaj użytkownika', 'AddNewZone' => 'Dodaj nową strefę', 'Alarm' => 'Alarm', @@ -120,22 +122,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Status archiwum', 'AttrAvgScore' => 'Śred. wynik', 'AttrCause' => 'Powód', - 'AttrDate' => 'Data', - 'AttrDateTime' => 'Data/Czas', 'AttrDiskBlocks' => 'Dysk Bloki', 'AttrDiskPercent' => 'Procent zajętości', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Czas trwania', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Klatki', 'AttrId' => 'Id', 'AttrMaxScore' => 'Maks. wynik', 'AttrMonitorId' => 'Nr monitora', 'AttrMonitorName' => 'Nazwa monitora', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nazwa', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Obiążenie systemu', - 'AttrTime' => 'Czas', 'AttrTotalScore' => 'Całkowity wynik', - 'AttrWeekday' => 'Dzień roboczy', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Dostępne', // Added - 2009-03-31 @@ -168,9 +180,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Przepustowość', 'BandwidthHead' => 'Przepustowość', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -234,10 +248,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Wyczyść', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Zamknij', 'Colour' => 'Nasycenie', 'Command' => 'Polecenie', 'Component' => 'Komponent', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Konfiguracja', 'ConfiguredFor' => 'Ustawiona', 'ConfirmDeleteEvents' => 'Jesteś pewien, że chcesz usunąć zaznaczone zdarzenia?', @@ -295,9 +311,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => 'Pobierz', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Czas trwania', 'Edit' => 'Edycja', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Enable Alarms', 'Enabled' => 'Aktywny', @@ -314,6 +332,7 @@ $SLANG = array( 'Events' => 'Zdarzenia', 'Exclude' => 'Wyklucz', 'Execute' => 'Wykonaj', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Eksport', 'ExportDetails' => 'Eksport szczegółów zdarzenia', 'ExportFailed' => 'Eksport nie powiódł się', @@ -343,8 +362,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Wywołuj komendę dla wszystkich pasujących', 'FilterLog' => 'Filtr logów', // Added - 2015-04-18 'FilterMessageEvents' => 'Wyświetlaj komunikat na wszystkie pasujące', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filtr Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Wysyłaj wszystkie pasujące', 'FilterVideoEvents' => 'Utwórz nagranie dla zaznaczonych', 'Filters' => 'Filtry', @@ -369,6 +390,7 @@ $SLANG = array( 'Function' => 'Funkcja', 'Gain' => 'Gain', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Generowanie Video', 'GeneratingVideo' => 'Generuję Video', 'GoToZoneMinder' => 'PrzejdŹ na ZoneMinder.com', @@ -389,6 +411,7 @@ $SLANG = array( 'High' => 'wysoka', 'HighBW' => 'Wys. prz.', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Godzina', 'Hue' => 'Odcień', 'Id' => 'Nr', @@ -413,6 +436,7 @@ $SLANG = array( 'Line' => 'Linia', // Added - 2011-06-16 'LinkedMonitors' => 'Połączone monitory', 'List' => 'Lista', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Obc.', 'Local' => 'Lokalny', 'Log' => 'Logi', // Added - 2011-06-16 @@ -499,6 +523,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Monitory', 'Montage' => 'Montaż', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Miesiąc', 'More' => 'Pokaż więcej', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -525,6 +550,7 @@ $SLANG = array( 'Next' => 'Następny', 'No' => 'Nie', 'NoDetectedCameras' => 'Nie wykryto kamer', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Brak zapisanych ramek dla tego zdarzenia', 'NoGroup' => 'Brak grupy', 'NoSavedFilters' => 'BrakZapisanychFiltrów', @@ -543,6 +569,8 @@ $SLANG = array( 'OpGt' => 'większe od', 'OpGtEq' => 'większe lub równe od', 'OpIn' => 'w zestawie', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'mniejsze od', 'OpLtEq' => 'mniejsze lub równe od', 'OpMatches' => 'pasujące', @@ -552,6 +580,7 @@ $SLANG = array( 'Open' => 'Otwórz', 'OptionHelp' => 'OpcjePomoc', 'OptionRestartWarning' => 'Te zmiany nie przyniosą natychmiastowego efektu\ndopóki system pracuje. Kiedy zakończysz robić zmiany\nproszę koniecznie zrestartować ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opcje', 'OrEnterNewName' => 'lub wpisz nową nazwę', 'Order' => 'Kolejność', @@ -589,9 +618,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Postęp', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Tempo', 'Real' => 'Rzeczywiste', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Zapis', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Miks z obrazem odniesienia', 'Refresh' => 'Odśwież', 'Remote' => 'Zdalny', @@ -607,6 +640,7 @@ $SLANG = array( 'ReplayAll' => 'Wszystko', 'ReplayGapless' => 'Wszystko i powtarzaj', 'ReplaySingle' => 'Bieżące zdarzenie', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Kasuj licznik zdarzeń', 'Restart' => 'Restart', @@ -625,6 +659,7 @@ $SLANG = array( 'Save' => 'Zapisz', 'SaveAs' => 'Zapisz jako', 'SaveFilter' => 'Zapisz filtr', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Skala', 'Score' => 'Wynik', 'Secs' => 'Sekund', @@ -641,6 +676,7 @@ $SLANG = array( 'ShowFilterWindow' => 'PokażOknoFiltru', 'ShowTimeline' => 'Pokaż oś czasu', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Rozmiar', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -660,6 +696,10 @@ $SLANG = array( 'State' => 'Stan', 'Stats' => 'Statystyki', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Krok', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -670,6 +710,8 @@ $SLANG = array( 'Stills' => 'Podgląd klatek', 'Stop' => 'Stop', 'Stopped' => 'Zatrzymany', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Odtwarzacz', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Zatwierdź', @@ -689,6 +731,7 @@ $SLANG = array( 'TimelineTip4' => 'Użyj opcji poniżej, w celu nawigacji.', // Added 2013.08.15. 'Timestamp' => 'Czas', 'TimestampLabelFormat' => 'Format etykiety czasu', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Wsp. X etykiety czasu', 'TimestampLabelY' => 'Wsp. Y etykiety czasu', 'Today' => 'Dziś', @@ -735,6 +778,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametery generowania filmu Video', 'VideoGenSucceeded' => 'Wygenerowano pomyślnie!', 'VideoSize' => 'Rozmiar filmu Video', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Podgląd', 'ViewAll' => 'Pokaż wszystko', 'ViewEvent' => 'Pokaż zdarzenie', @@ -744,6 +788,7 @@ $SLANG = array( 'Watch' => 'podgląd', 'Web' => 'Sieć', 'WebColour' => 'Web Colour', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Tydzień', 'White' => 'Biel', 'WhiteBalance' => 'Balans bieli', diff --git a/web/lang/pt_br.php b/web/lang/pt_br.php index c7c3caf42..ec4807765 100644 --- a/web/lang/pt_br.php +++ b/web/lang/pt_br.php @@ -18,6 +18,8 @@ $SLANG = array( 'Actual' => 'Atual', 'AddNewControl' => 'Add New Control', 'AddNewMonitor' => 'Adicionar Monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Adicionar Usuário', 'AddNewZone' => 'Adicionar Zona', 'Alarm' => 'Alarme', @@ -45,22 +47,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Status/Arquivamento', 'AttrAvgScore' => 'Maior Score', 'AttrCause' => 'Cause', - 'AttrDate' => 'Data', - 'AttrDateTime' => 'Data/Horario', 'AttrDiskBlocks' => 'Blocos de Disco', 'AttrDiskPercent' => 'Porcentagem de Disco', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Duração', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Imagens', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. Score', 'AttrMonitorId' => 'Id do Monitor', 'AttrMonitorName' => 'Nome do Monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nome', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Horário', 'AttrTotalScore' => 'Score Total', - 'AttrWeekday' => 'Dia/Semana', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Available', // Added - 2009-03-31 @@ -93,9 +105,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Larg/Banda', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -159,10 +173,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Fechar', 'Colour' => 'Cor', 'Command' => 'Command', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'Configurado para', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -220,9 +236,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Duração', 'Edit' => 'Editar', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Enable Alarms', 'Enabled' => 'Habilitado', @@ -239,6 +257,7 @@ $SLANG = array( 'Events' => 'Eventos', 'Exclude' => 'Excluir', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Export', 'ExportDetails' => 'Export Event Details', 'ExportFailed' => 'Export Failed', @@ -268,8 +287,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Executar comando p/ resultados', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Enviar Mensagem dos resultados', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Px de Filtro', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Fazer upload dos resultados', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filters', @@ -294,6 +315,7 @@ $SLANG = array( 'Function' => 'Função', 'Gain' => 'Gain', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Gerar Video', 'GeneratingVideo' => 'Gerando Video', 'GoToZoneMinder' => 'Ir Para ZoneMinder.com', @@ -314,6 +336,7 @@ $SLANG = array( 'High' => 'Alto', 'HighBW' => 'Alta L/B', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Hora', 'Hue' => 'Saturação', 'Id' => 'Id', @@ -338,6 +361,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Carga', 'Local' => 'Local', 'Log' => 'Log', // Added - 2011-06-16 @@ -424,6 +448,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Monitores', 'Montage' => 'Montagem', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Mês', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -450,6 +475,7 @@ $SLANG = array( 'Next' => 'Próx', 'No' => 'Não', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Não há imagens gravadas neste evento', 'NoGroup' => 'No Group', 'NoSavedFilters' => 'SemFiltrosSalvos', @@ -468,6 +494,8 @@ $SLANG = array( 'OpGt' => 'maior que', 'OpGtEq' => 'maior que ou igual a', 'OpIn' => 'no set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'menor que', 'OpLtEq' => 'menor que ou igual a', 'OpMatches' => 'combina', @@ -477,6 +505,7 @@ $SLANG = array( 'Open' => 'Open', 'OptionHelp' => 'OpçãoAjuda', 'OptionRestartWarning' => 'Reinicie o Zoneminder para que as mudanças tenham efeito', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opções', 'OrEnterNewName' => 'ou defina novo nome', 'Order' => 'Order', @@ -514,9 +543,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Vel.', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Gravar', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Referência de imagem Blend %ge', 'Refresh' => 'Atualizar', 'Remote' => 'Remoto', @@ -532,6 +565,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Resetar contagem de eventos', 'Restart' => 'Reiniciar', @@ -550,6 +584,7 @@ $SLANG = array( 'Save' => 'Salvar', 'SaveAs' => 'Salvar Como', 'SaveFilter' => 'Salvar Filtro', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Tamanho', 'Score' => 'Score', 'Secs' => 'Segs', @@ -566,6 +601,7 @@ $SLANG = array( 'ShowFilterWindow' => 'MostrarJanelaDeFiltros', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -585,6 +621,10 @@ $SLANG = array( 'State' => 'Estado', 'Stats' => 'Status', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -595,6 +635,8 @@ $SLANG = array( 'Stills' => 'Imagens', 'Stop' => 'Parar', 'Stopped' => 'Parado', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Contínuo', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Submit', @@ -614,6 +656,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Tempo', 'TimestampLabelFormat' => 'Formato de etiqueta de tempo', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'posição de etiqueta X', 'TimestampLabelY' => 'posição de etiqueta Y', 'Today' => 'Today', @@ -660,6 +703,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametros de geração de vídeo', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'Tamanho do vídeo', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Ver', 'ViewAll' => 'Ver Tudo', 'ViewEvent' => 'View Event', @@ -669,6 +713,7 @@ $SLANG = array( 'Watch' => 'Assistir', 'Web' => 'Web', 'WebColour' => 'Web Colour', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Semana', 'White' => 'White', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/ro_ro.php b/web/lang/ro_ro.php index a36c4de6c..1f2d17e2e 100644 --- a/web/lang/ro_ro.php +++ b/web/lang/ro_ro.php @@ -49,6 +49,8 @@ $SLANG = array( 'Actual' => 'Real', 'AddNewControl' => 'Adaugă control nou', 'AddNewMonitor' => 'Adaugă monitor', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Adaugă utilizator', 'AddNewZone' => 'Adaugă zonă', 'Alarm' => 'Alarma', @@ -76,22 +78,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Stare arhiva', 'AttrAvgScore' => 'Cota medie', 'AttrCause' => 'Cauza', - 'AttrDate' => 'Data', - 'AttrDateTime' => 'Data/Timp', 'AttrDiskBlocks' => 'Disk Blocks', 'AttrDiskPercent' => 'Procentaj disc', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Durata', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Cadre', 'AttrId' => 'Nr.', 'AttrMaxScore' => 'Cota max', 'AttrMonitorId' => 'Monitor nr.', 'AttrMonitorName' => 'Nume monitor', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Nume', 'AttrNotes' => 'Notes', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'System Load', - 'AttrTime' => 'Time', 'AttrTotalScore' => 'Cota total', - 'AttrWeekday' => 'Zi săpt.', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Auto Stop Timeout', 'Available' => 'Available', // Added - 2009-03-31 @@ -124,9 +136,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Reference blend percentage must be a positive integer', 'BadSectionLength' => 'Section length must be an integer of 30 or more', 'BadSignalCheckColour' => 'Signal check colour must be a valid RGB colour string', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Stream replay buffer must be an integer of zero or more', 'BadWarmupCount' => 'Warmup frames must be an integer of zero or more', 'BadWebColour' => 'Web colour must be a valid web colour string', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Width must be set to a valid value', 'Bandwidth' => 'Laţime de bandă', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -190,10 +204,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Choose Preset', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Închide', 'Colour' => 'Culoare', 'Command' => 'Comanda', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'Configurat pentru', 'ConfirmDeleteEvents' => 'Are you sure you wish to delete the selected events?', @@ -251,9 +267,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Not yet, remind again in 1 week', 'DonateYes' => 'Yes, I\'d like to donate now', 'Download' => 'Download', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Durata', 'Edit' => 'Modific', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Enable Alarms', 'Enabled' => 'Activ', @@ -270,6 +288,7 @@ $SLANG = array( 'Events' => 'Evenim.', 'Exclude' => 'Exclude', 'Execute' => 'Execute', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Export', 'ExportDetails' => 'Export Event Details', 'ExportFailed' => 'Export Failed', @@ -299,8 +318,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Execută comanda pentru toate rezultatele', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Trimite mesaj pentru toate rezultatele', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Încarcă toate rezultatele', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Filters', @@ -325,6 +346,7 @@ $SLANG = array( 'Function' => 'Funcţie', 'Gain' => 'Gain', 'General' => 'General', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Generează video', 'GeneratingVideo' => 'Generez video', 'GoToZoneMinder' => 'Du-te la ZoneMinder.com', @@ -345,6 +367,7 @@ $SLANG = array( 'High' => 'Mare', 'HighBW' => 'B/W mare', 'Home' => 'Home', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Ora', 'Hue' => 'Nuanţă', 'Id' => 'Nr.', @@ -369,6 +392,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Linked Monitors', 'List' => 'List', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Load', 'Local' => 'Local', 'Log' => 'Log', // Added - 2011-06-16 @@ -455,6 +479,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Monitoare', 'Montage' => 'Montage', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Luna', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -481,6 +506,7 @@ $SLANG = array( 'Next' => 'Urmator', 'No' => 'Nu', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Nu exista cadre inregistrate pentru acest eveniment.', 'NoGroup' => 'No Group', 'NoSavedFilters' => 'LipsaFiltruSalvat', @@ -499,6 +525,8 @@ $SLANG = array( 'OpGt' => 'mai mare ca', 'OpGtEq' => 'mai mare sau egal cu', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'mai mic decât', 'OpLtEq' => 'mai mic sau egal cu', 'OpMatches' => 'matches', @@ -508,6 +536,7 @@ $SLANG = array( 'Open' => 'Deschide', 'OptionHelp' => 'OptionHelp', 'OptionRestartWarning' => 'Aceste schimbari nu se aplica in timpul rularii.\n Dupa ce ati terminat setarile va rugam reporniti ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Opţiuni', 'OrEnterNewName' => 'sau denumire nouă', 'Order' => 'Order', @@ -545,9 +574,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protocol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Rate', 'Real' => 'Real', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Înregistrare', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Combinare imagine referinta(%)', 'Refresh' => 'Actualizează', 'Remote' => 'Remote', @@ -563,6 +596,7 @@ $SLANG = array( 'ReplayAll' => 'All Events', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Single Event', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Reset', 'ResetEventCounts' => 'Reset Event Counts', 'Restart' => 'Reporneşte', @@ -581,6 +615,7 @@ $SLANG = array( 'Save' => 'Salvez', 'SaveAs' => 'Salvează ca', 'SaveFilter' => 'Salvează filtru', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Scara', 'Score' => 'Cota', 'Secs' => 'Sec', @@ -597,6 +632,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Fereastra filtre', 'ShowTimeline' => 'Show Timeline', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -616,6 +652,10 @@ $SLANG = array( 'State' => 'Stare', 'Stats' => 'Statistici', 'Status' => 'Stare', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Step', 'StepBack' => 'Step Back', 'StepForward' => 'Step Forward', @@ -626,6 +666,8 @@ $SLANG = array( 'Stills' => 'Statice', 'Stop' => 'Opreşte', 'Stopped' => 'Oprit', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Flux', 'StreamReplayBuffer' => 'Stream Replay Image Buffer', 'Submit' => 'Trimite', @@ -645,6 +687,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Format timp', 'TimestampLabelFormat' => 'Format eticheta format timp', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Format timp eticheta X', 'TimestampLabelY' => 'Format timp eticheta Y', 'Today' => 'Azi', @@ -691,6 +734,7 @@ $SLANG = array( 'VideoGenParms' => 'Parametrii generare video', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'Mărime video', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Vizual', 'ViewAll' => 'Vizual. tot', 'ViewEvent' => 'View Event', @@ -700,6 +744,7 @@ $SLANG = array( 'Watch' => 'Watch', 'Web' => 'Web', 'WebColour' => 'Web Colour', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Săpt.', 'White' => 'Alb', 'WhiteBalance' => 'Balans alb', diff --git a/web/lang/ru_ru.php b/web/lang/ru_ru.php index 8d0d24a7d..0416f3358 100644 --- a/web/lang/ru_ru.php +++ b/web/lang/ru_ru.php @@ -76,8 +76,11 @@ $SLANG = array( '32BitColour' => '32 битный цвет', // Added - 2011-06-15 '8BitGrey' => '256 оттенков серого', 'Action' => 'Действие', + 'Actual' => 'Actual', // Added - 2018-08-30 'AddNewControl' => 'Добавить новый', 'AddNewMonitor' => 'Добавить монитор', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Добавить пользователя', 'AddNewZone' => 'Добавить зону', 'Alarm' => 'Тревога', @@ -89,7 +92,6 @@ $SLANG = array( 'AlarmPx' => 'Пкс трев.', 'AlarmRGBUnset' => 'You must set an alarm RGB colour', 'AlarmRefImageBlendPct'=> 'Смешение опорного кадра тревоги, %', // Added - 2015-04-18 - 'AlmRefImageBlendPct' => 'Смешение опорного кадра тревоги, %', 'Alert' => 'Настороже', 'All' => 'Все', 'AnalysisFPS' => 'Частота анализа (к/с)', // Added - 2015-07-22 @@ -106,24 +108,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Статус архивации', 'AttrAvgScore' => 'Сред. оценка', 'AttrCause' => 'Причина', - 'AttrDate' => 'Дата', - 'AttrDateTime' => 'Дата/Время', 'AttrDiskBlocks' => 'Диск, блоки', 'AttrDiskPercent' => 'Диск, проценты', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Длительность', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Кол-во кадров', 'AttrId' => 'ИД', 'AttrMaxScore' => 'Макс. оценка', 'AttrMonitorId' => 'ИД Монитора', 'AttrMonitorName' => 'Название Монитора', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Имя', 'AttrNotes' => 'Примечание', - 'AttrServerId' => 'ИД сервера', - 'AttrServerName' => 'Имя сервера', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Нагрузка проц.', - 'AttrTime' => 'Время', 'AttrTotalScore' => 'Сумм. оценка', - 'AttrWeekday' => 'День недели', 'Auto' => 'Auto', 'AutoStopTimeout' => 'Тайм-аут автоостановки', 'Available' => 'Доступно', // Added - 2009-03-31 @@ -156,9 +166,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Смешение опорного кадра должно быть положительным и целочисленным', 'BadSectionLength' => 'Длина секции должна быть целочисленной и большей либо равной тридцати', 'BadSignalCheckColour' => 'Цвет проверки сигнала должен быть правильной строкой формата RGB', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Буфер потока повторного воспроизведения должен быть целочисленным и большим либо равным нулю', 'BadWarmupCount' => 'Кол-во кадров разогрева должно быть целочисленным и большим либо равным нулю', 'BadWebColour' => 'Цвет отметки должен быть правильным Web-цветом', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Неправильная ширина', 'Bandwidth' => 'канал', 'BandwidthHead' => 'канал', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing; @@ -222,10 +234,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Выберите предустановку', 'Clear' => 'Очистить', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Закрыть', 'Colour' => 'Цвет', 'Command' => 'Command', 'Component' => 'Компонент', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Config', 'ConfiguredFor' => 'настроен на', 'ConfirmDeleteEvents' => 'Вы действительно хотите удалить выбранные события?', @@ -283,9 +297,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Нет, не сейчас, напомнить через неделю', 'DonateYes' => 'Да, я хотел бы сделать пожертвование', 'Download' => 'Скачать', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Длительность', 'Edit' => 'Редактирование', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'Email', 'EnableAlarms' => 'Разрешить тревогу', 'Enabled' => 'Включен', @@ -302,6 +318,7 @@ $SLANG = array( 'Events' => 'События', 'Exclude' => 'Исключить', 'Execute' => 'Выполнить', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Экспорт', 'ExportDetails' => 'Экспортировать описание события', 'ExportFailed' => 'Ошибка экспорта', @@ -331,8 +348,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Выполнить команду над выбранным', 'FilterLog' => 'Фильтр лога', // Added - 2015-04-18 'FilterMessageEvents' => 'Message details of all matches', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Пкс фильтра', 'FilterUnset' => 'You must specify a filter width and height', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Upload all matches', 'FilterVideoEvents' => 'Create video for all matches', 'Filters' => 'Фильтры', @@ -357,6 +376,7 @@ $SLANG = array( 'Function' => 'Функция', 'Gain' => 'Gain', 'General' => 'Основные', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Генерировать видео', 'GeneratingVideo' => 'Генерируется видео', 'GoToZoneMinder' => 'Перейти на ZoneMinder.com', @@ -377,6 +397,7 @@ $SLANG = array( 'High' => 'широкий', 'HighBW' => 'Широкий канал', 'Home' => 'Домой', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Час', 'Hue' => 'Оттенок', 'Id' => 'ИД', @@ -401,6 +422,7 @@ $SLANG = array( 'Line' => 'Строка', // Added - 2011-06-16 'LinkedMonitors' => 'Привязанные мониторы', 'List' => 'Список', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Нагрузка', 'Local' => 'Локальный', 'Log' => 'Лог', // Added - 2011-06-16; @@ -487,6 +509,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'В этом списке показаны найденные аналоговые и сетевые камеры, как уже заведенные, так и доступные для выбора.

Выберите нужную из списка ниже.

Обратите внимание, что не все камеры могут быть найдены, и что выбор камеры может переписать настройки определенные для этого монитора.

', // Added - 2009-03-31 'Monitors' => 'Мониторы', 'Montage' => 'Монтаж', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Месяц', 'More' => 'Еще', // Added - 2011-06-16 'MotionFrameSkip' => 'Кол-во пропуск. кадров движения', @@ -513,6 +536,7 @@ $SLANG = array( 'Next' => 'След.', 'No' => 'Нет', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Это событие не содержит кадров', 'NoGroup' => 'No Group', 'NoSavedFilters' => 'нет сохраненных фильтров', @@ -531,6 +555,8 @@ $SLANG = array( 'OpGt' => 'больше', 'OpGtEq' => 'больше либо равно', 'OpIn' => 'в списке', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'меньше', 'OpLtEq' => 'меньше или равно', 'OpMatches' => 'совпадает', @@ -540,6 +566,7 @@ $SLANG = array( 'Open' => 'Открыть', 'OptionHelp' => 'Справка', 'OptionRestartWarning' => 'Эти изменения подействуют только после перезапуска программы.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Опции', 'OrEnterNewName' => 'или введите новое имя', 'Order' => 'Сортировка', @@ -556,7 +583,6 @@ $SLANG = array( 'PasswordsDifferent' => 'Пароли не совпадают', 'Paths' => 'Пути', 'Pause' => 'Пауза', - 'Paused' => 'Пауза', 'Phone' => 'Phone', 'PhoneBW' => 'Телефонная линия', 'Pid' => 'PID', // Added - 2011-06-16 @@ -578,9 +604,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'В этом списке показаны существующие профили потока выбранной камеры.

Выберите нужный из списка ниже.

Обратите внимание, что ZoneMinder не может добавить дополнительный профиль, и что выбор профиля может переписать настройки определенные для этого монитора.

', // Added - 2015-04-18 'Progress' => 'Прогресс', // Added - 2015-04-18 'Protocol' => 'Протокол', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Скорость', 'Real' => 'Реальная', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Record', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Смешение опорного кадра, %', 'Refresh' => 'Обновить', 'Remote' => 'Удаленный', @@ -596,6 +626,7 @@ $SLANG = array( 'ReplayAll' => 'Все события', 'ReplayGapless' => 'События подряд', 'ReplaySingle' => 'Одно событие', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Сбросить', 'ResetEventCounts' => 'Обнулить счетчик событий', 'Restart' => 'Перезапустить', @@ -614,6 +645,7 @@ $SLANG = array( 'Save' => 'Сохранить', 'SaveAs' => 'Сохранить как', 'SaveFilter' => 'Сохранить фильтр', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Масштаб', 'Score' => 'Оценка', 'Secs' => 'Сек.', @@ -623,7 +655,6 @@ $SLANG = array( 'SelectLog' => 'Выберите лог', // Added - 2011-06-17 'SelectMonitors' => 'Select Monitors', 'SelfIntersecting' => 'Polygon edges must not intersect', - 'Server' => 'Сервер', 'Set' => 'Set', 'SetNewBandwidth' => 'Установка новой ширина канала', 'SetPreset' => 'Set Preset', @@ -631,6 +662,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Показать окно фильтра', 'ShowTimeline' => 'Показать график', 'SignalCheckColour' => 'Цвет проверки сигнала', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Size', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Sleep', @@ -650,6 +682,10 @@ $SLANG = array( 'State' => 'Состояние', 'Stats' => 'Статистика', 'Status' => 'Статус', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Шаг', 'StepBack' => 'Кадр назад', 'StepForward' => 'Кадр вперед', @@ -660,6 +696,8 @@ $SLANG = array( 'Stills' => 'Стоп-кадры', 'Stop' => 'Остановить', 'Stopped' => 'Остановлен', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Поток', 'StreamReplayBuffer' => 'Буфер потока повторного воспр.', 'Submit' => 'Применить', @@ -679,9 +717,9 @@ $SLANG = array( 'TimelineTip4' => 'Используйте кнопки снизу для отдаления и перемещения по временной шкале.', // Added 2013.08.15. 'Timestamp' => 'Метка времени', 'TimestampLabelFormat' => 'Формат метки', + 'TimestampLabelSize' => 'Размер метки', 'TimestampLabelX' => 'X-координата метки', 'TimestampLabelY' => 'Y-координата метки', - 'TimestampLabelSize' => 'Размер метки', 'Today' => 'Сегодня', 'Tools' => 'Инструменты', 'Total' => 'Всего', // Added - 2011-06-16 @@ -726,6 +764,7 @@ $SLANG = array( 'VideoGenParms' => 'Параметры генерации видео', 'VideoGenSucceeded' => 'Video Generation Succeeded!', 'VideoSize' => 'Размер изображения', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Просмотр', 'ViewAll' => 'Просм. все', 'ViewEvent' => 'View Event', @@ -735,6 +774,7 @@ $SLANG = array( 'Watch' => 'Watch', 'Web' => 'Интерфейс', 'WebColour' => 'Цвет отметки', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Неделя', 'White' => 'Бал. белого', 'WhiteBalance' => 'White Balance', diff --git a/web/lang/se_se.php b/web/lang/se_se.php index e624e5696..995e88156 100644 --- a/web/lang/se_se.php +++ b/web/lang/se_se.php @@ -79,6 +79,8 @@ $SLANG = array( 'Actual' => 'Verklig', 'AddNewControl' => 'Ny kontroll', 'AddNewMonitor' => 'Ny bevakare', + 'AddNewServer' => 'Add New Server', // Added - 2018-08-30 + 'AddNewStorage' => 'Add New Storage', // Added - 2018-08-30 'AddNewUser' => 'Ny användare', 'AddNewZone' => 'Ny zon', 'Alarm' => 'Larm', @@ -106,22 +108,32 @@ $SLANG = array( 'AttrArchiveStatus' => 'Arkivstatus', 'AttrAvgScore' => 'Ung. värde', 'AttrCause' => 'Orsak', - 'AttrDate' => 'Datum', - 'AttrDateTime' => 'Datum/Tid', 'AttrDiskBlocks' => 'Diskblock', 'AttrDiskPercent' => 'Diskprocent', + 'AttrDiskSpace' => 'Disk Space', // Added - 2018-08-30 'AttrDuration' => 'Längd', + 'AttrEndDate' => 'End Date', // Added - 2018-08-30 + 'AttrEndDateTime' => 'End Date/Time', // Added - 2018-08-30 + 'AttrEndTime' => 'End Time', // Added - 2018-08-30 + 'AttrEndWeekday' => 'End Weekday', // Added - 2018-08-30 + 'AttrFilterServer' => 'Server Filter is Running On', // Added - 2018-08-30 'AttrFrames' => 'Ramar', 'AttrId' => 'Id', 'AttrMaxScore' => 'Max. värde', 'AttrMonitorId' => 'Bevakningsid', 'AttrMonitorName' => 'Bevakningsnamn', + 'AttrMonitorServer' => 'Server Monitor is Running On', // Added - 2018-08-30 'AttrName' => 'Namn', 'AttrNotes' => 'Notering', + 'AttrStartDate' => 'Start Date', // Added - 2018-08-30 + 'AttrStartDateTime' => 'Start Date/Time', // Added - 2018-08-30 + 'AttrStartTime' => 'Start Time', // Added - 2018-08-30 + 'AttrStartWeekday' => 'Start Weekday', // Added - 2018-08-30 + 'AttrStateId' => 'Run State', // Added - 2018-08-30 + 'AttrStorageArea' => 'Storage Area', // Added - 2018-08-30 + 'AttrStorageServer' => 'Server Hosting Storage', // Added - 2018-08-30 'AttrSystemLoad' => 'Systemlast', - 'AttrTime' => 'Tid', 'AttrTotalScore' => 'Totalvärde', - 'AttrWeekday' => 'Veckodag', 'Auto' => 'Automatik', 'AutoStopTimeout' => 'Tidsutlösning för automatstop', 'Available' => 'Available', // Added - 2009-03-31 @@ -154,9 +166,11 @@ $SLANG = array( 'BadRefBlendPerc' => 'Mixprocenten för referensen måste hara ett positivt heltal', 'BadSectionLength' => 'Sektionslängden måste vara ett heltal på minst 30 eller högre', 'BadSignalCheckColour' => 'Kontrollfärgen på signalen måste vara en giltig RGB färgsträng', + 'BadSourceType' => 'Source Type \"Web Site\" requires the Function to be set to \"Monitor\"', // Added - 2018-08-30 'BadStreamReplayBuffer'=> 'Buffern för strömmande uppspelning måste vara ett heltal på 0 eller högre', 'BadWarmupCount' => 'Uppvärmingsramen måste vara ett heltal på 0 eller högre', 'BadWebColour' => 'Webbfärgen måste vara en giltig sträng för webbfärg', + 'BadWebSitePath' => 'Please enter a complete website url, including the http:// or https:// prefix.', // Added - 2018-08-30 'BadWidth' => 'Bredden måste sättas til ett giltigt värde', 'Bandwidth' => 'Bandbredd', 'BandwidthHead' => 'Bandwidth', // This is the end of the bandwidth status on the top of the console, different in many language due to phrasing @@ -220,10 +234,12 @@ $SLANG = array( 'ChooseLogSelection' => 'Choose a log selection', // Added - 2011-06-17 'ChoosePreset' => 'Välj standard', 'Clear' => 'Clear', // Added - 2011-06-16 + 'CloneMonitor' => 'Clone', // Added - 2018-08-30 'Close' => 'Stäng', 'Colour' => 'Färg', 'Command' => 'Kommando', 'Component' => 'Component', // Added - 2011-06-16 + 'ConcurrentFilter' => 'Run filter concurrently', // Added - 2018-08-30 'Config' => 'Konfigurera', 'ConfiguredFor' => 'Konfigurerad för', 'ConfirmDeleteEvents' => 'Är du säker på att du vill ta bort dom valda händelserna?', @@ -281,9 +297,11 @@ $SLANG = array( 'DonateRemindWeek' => 'Inte än, påminn om 1 vecka', 'DonateYes' => 'Ja, jag vill gärna donera nu', 'Download' => 'Ladda ner', + 'DownloadVideo' => 'Download Video', // Added - 2018-08-30 'DuplicateMonitorName' => 'Duplicate Monitor Name', // Added - 2009-03-31 'Duration' => 'Längd', 'Edit' => 'Redigera', + 'EditLayout' => 'Edit Layout', // Added - 2018-08-30 'Email' => 'E-post', 'EnableAlarms' => 'Aktivera larm', 'Enabled' => 'Aktiverad', @@ -300,6 +318,7 @@ $SLANG = array( 'Events' => 'Händelser', 'Exclude' => 'Exkludera', 'Execute' => 'Utför', + 'Exif' => 'Embed EXIF data into image', // Added - 2018-08-30 'Export' => 'Exportera', 'ExportDetails' => 'Exportera händelsedetaljer', 'ExportFailed' => 'Exporten misslyckades', @@ -329,8 +348,10 @@ $SLANG = array( 'FilterExecuteEvents' => 'Utför kommando på alla träffar', 'FilterLog' => 'Filter log', // Added - 2015-04-18 'FilterMessageEvents' => 'Meddela detaljer om alla träffar', + 'FilterMoveEvents' => 'Move all matches', // Added - 2018-08-30 'FilterPx' => 'Filter Px', 'FilterUnset' => 'Du måste specificera filtrets bredd och höjd', + 'FilterUpdateDiskSpace'=> 'Update used disk space', // Added - 2018-08-30 'FilterUploadEvents' => 'Ladda upp alla träffar', 'FilterVideoEvents' => 'Skapa video för alla träffar', 'Filters' => 'Filter', @@ -355,6 +376,7 @@ $SLANG = array( 'Function' => 'Funktion', 'Gain' => 'Nivå', 'General' => 'Generell', + 'GenerateDownload' => 'Generate Download', // Added - 2018-08-30 'GenerateVideo' => 'Skapa video', 'GeneratingVideo' => 'Skapar video', 'GoToZoneMinder' => 'Gå till ZoneMinder.com', @@ -375,6 +397,7 @@ $SLANG = array( 'High' => 'Hög', 'HighBW' => 'Hög bandbredd', 'Home' => 'Hem', + 'Hostname' => 'Hostname', // Added - 2018-08-30 'Hour' => 'Timme', 'Hue' => 'Hue', 'Id' => 'nr', @@ -399,6 +422,7 @@ $SLANG = array( 'Line' => 'Line', // Added - 2011-06-16 'LinkedMonitors' => 'Länkade övervakare', 'List' => 'Lista', + 'ListMatches' => 'List Matches', // Added - 2018-08-30 'Load' => 'Belastning', 'Local' => 'Lokal', 'Log' => 'Log', // Added - 2011-06-16 @@ -485,6 +509,7 @@ $SLANG = array( 'MonitorProbeIntro' => 'The list below shows detected analog and network cameras and whether they are already being used or available for selection.

Select the desired entry from the list below.

Please note that not all cameras may be detected and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2009-03-31 'Monitors' => 'Bevakare', 'Montage' => 'Montera', + 'MontageReview' => 'Montage Review', // Added - 2018-08-30 'Month' => 'Månad', 'More' => 'More', // Added - 2011-06-16 'MotionFrameSkip' => 'Motion Frame Skip', @@ -511,6 +536,7 @@ $SLANG = array( 'Next' => 'Nästa', 'No' => 'Nej', 'NoDetectedCameras' => 'No Detected Cameras', // Added - 2009-03-31 + 'NoDetectedProfiles' => 'No Detected Profiles', // Added - 2018-08-30 'NoFramesRecorded' => 'Det finns inga ramar inspelade för denna händelse', 'NoGroup' => 'Ingen grupp', 'NoSavedFilters' => 'Inga sparade filter', @@ -529,6 +555,8 @@ $SLANG = array( 'OpGt' => 'större än', 'OpGtEq' => 'större än eller lika med', 'OpIn' => 'in set', + 'OpIs' => 'is', // Added - 2018-08-30 + 'OpIsNot' => 'is not', // Added - 2018-08-30 'OpLt' => 'mindre än', 'OpLtEq' => 'mindre än eller lika med', 'OpMatches' => 'matchar', @@ -538,6 +566,7 @@ $SLANG = array( 'Open' => 'Öppna', 'OptionHelp' => 'Optionhjälp', 'OptionRestartWarning' => 'Dessa ändringar kommer inte att vara implementerade\nnär systemet körs. När du är klar starta om\n ZoneMinder.', + 'OptionalEncoderParam' => 'Optional Encoder Parameters', // Added - 2018-08-30 'Options' => 'Alternativ', 'OrEnterNewName' => 'eller skriv in nytt namn', 'Order' => 'Sortera', @@ -575,9 +604,13 @@ $SLANG = array( 'ProfileProbeIntro' => 'The list below shows the existing stream profiles of the selected camera .

Select the desired entry from the list below.

Please note that ZoneMinder cannot configure additional profiles and that choosing a camera here may overwrite any values you already have configured for the current monitor.

', // Added - 2015-04-18 'Progress' => 'Progress', // Added - 2015-04-18 'Protocol' => 'Protokol', + 'RTSPDescribe' => 'Use RTSP Response Media URL', // Added - 2018-08-30 + 'RTSPTransport' => 'RTSP Transport Protocol', // Added - 2018-08-30 'Rate' => 'Hastighet', 'Real' => 'Verklig', + 'RecaptchaWarning' => 'Your reCaptcha secret key is invalid. Please correct it, or reCaptcha will not work', // Added - 2018-08-30 'Record' => 'Spela in', + 'RecordAudio' => 'Whether to store the audio stream when saving an event.', // Added - 2018-08-30 'RefImageBlendPct' => 'Reference Image Blend %ge', 'Refresh' => 'Uppdatera', 'Remote' => 'Fjärr', @@ -593,6 +626,7 @@ $SLANG = array( 'ReplayAll' => 'Alla händelser', 'ReplayGapless' => 'Gapless Events', 'ReplaySingle' => 'Ensam händelse', + 'ReportEventAudit' => 'Audit Events Report', // Added - 2018-08-30 'Reset' => 'Återställ', 'ResetEventCounts' => 'Återställ händelseräknare', 'Restart' => 'Återstart', @@ -611,6 +645,7 @@ $SLANG = array( 'Save' => 'Spara', 'SaveAs' => 'Spara som', 'SaveFilter' => 'Spara filter', + 'SaveJPEGs' => 'Save JPEGs', // Added - 2018-08-30 'Scale' => 'Skala', 'Score' => 'Resultat', 'Secs' => 'Sek', @@ -627,6 +662,7 @@ $SLANG = array( 'ShowFilterWindow' => 'Visa fönsterfilter', 'ShowTimeline' => 'Visa tidslinje', 'SignalCheckColour' => 'Signal Check Colour', + 'SignalCheckPoints' => 'Signal Check Points', // Added - 2018-08-30 'Size' => 'Storlek', 'SkinDescription' => 'Change the default skin for this computer', // Added - 2011-01-30 'Sleep' => 'Vila', @@ -646,6 +682,10 @@ $SLANG = array( 'State' => 'Läge', 'Stats' => 'Statistik', 'Status' => 'Status', + 'StatusConnected' => 'Capturing', // Added - 2018-08-30 + 'StatusNotRunning' => 'Not Running', // Added - 2018-08-30 + 'StatusRunning' => 'Not Capturing', // Added - 2018-08-30 + 'StatusUnknown' => 'Unknown', // Added - 2018-08-30 'Step' => 'Steg', 'StepBack' => 'Stepga bakåt', 'StepForward' => 'Stega framåt', @@ -656,6 +696,8 @@ $SLANG = array( 'Stills' => 'Stillbilder', 'Stop' => 'Stopp', 'Stopped' => 'Stoppad', + 'StorageArea' => 'Storage Area', // Added - 2018-08-30 + 'StorageScheme' => 'Scheme', // Added - 2018-08-30 'Stream' => 'Strömmande', 'StreamReplayBuffer' => 'Buffert för strömmande uppspelning', 'Submit' => 'Skicka', @@ -675,6 +717,7 @@ $SLANG = array( 'TimelineTip4' => 'Use the controls below to zoom out or navigate back and forward through the time range.', // Added 2013.08.15. 'Timestamp' => 'Tidsstämpel', 'TimestampLabelFormat' => 'Format på tidsstämpel', + 'TimestampLabelSize' => 'Font Size', // Added - 2018-08-30 'TimestampLabelX' => 'Värde på tidsstämpel X', 'TimestampLabelY' => 'Värde på tidsstämpel Y', 'Today' => 'Idag', @@ -721,6 +764,7 @@ $SLANG = array( 'VideoGenParms' => 'Inställningar för videogenerering', 'VideoGenSucceeded' => 'Videogenereringen lyckades!', 'VideoSize' => 'Videostorlek', + 'VideoWriter' => 'Video Writer', // Added - 2018-08-30 'View' => 'Visa', 'ViewAll' => 'Visa alla', 'ViewEvent' => 'Visa händelse', @@ -730,6 +774,7 @@ $SLANG = array( 'Watch' => 'Se', 'Web' => 'Webb', 'WebColour' => 'Webbfärg', + 'WebSiteUrl' => 'Website URL', // Added - 2018-08-30 'Week' => 'Vecka', 'White' => 'Vit', 'WhiteBalance' => 'Vitbalans', diff --git a/web/robots.txt b/web/robots.txt index 1f53798bb..9bef8dea6 100644 --- a/web/robots.txt +++ b/web/robots.txt @@ -1,2 +1,3 @@ User-agent: * Disallow: / +### \ No newline at end of file diff --git a/web/skins/classic/css/base/views/privacy.css b/web/skins/classic/css/base/views/privacy.css new file mode 100644 index 000000000..f368fa110 --- /dev/null +++ b/web/skins/classic/css/base/views/privacy.css @@ -0,0 +1,14 @@ +h6 { + text-align: left; + font-weight: bold; + text-decoration: underline; +} + +p { + text-align: left; +} + +ul { + text-align: left; + list-style-type: disc; +} diff --git a/web/skins/classic/css/dark/views/control.css b/web/skins/classic/css/dark/views/control.css index af0eeccf2..31a937323 100644 --- a/web/skins/classic/css/dark/views/control.css +++ b/web/skins/classic/css/dark/views/control.css @@ -21,7 +21,7 @@ } .ptzControls input[type=image] { - border: 0px; + border: 0; } .ptzControls .controlsPanel .arrowControl { @@ -115,7 +115,7 @@ } .ptzControls .controlsPanel .pantiltPanel .pantiltControls .centerBtn { - background: url("../skins/classic/graphics/graphics/center.png") no-repeat 0 0; + background: url("../skins/classic/graphics/center.png") no-repeat 0 0; } .ptzControls .controlsPanel .pantiltPanel .pantiltControls .rightBtn { diff --git a/web/skins/classic/includes/functions.php b/web/skins/classic/includes/functions.php index e39df3ebe..50dd19f7c 100644 --- a/web/skins/classic/includes/functions.php +++ b/web/skins/classic/includes/functions.php @@ -120,15 +120,16 @@ echo output_link_if_exists( array( - - + + + @@ -195,6 +202,9 @@ echo output_link_if_exists( array( } // end function xhtmlHeaders( $file, $title ) function getNavBarHTML($reload = null) { + # Provide a facility to turn off the headers if you put headers=0 into the url + if ( isset($_REQUEST['navbar']) and $_REQUEST['navbar']=='0' ) + return ''; $versionClass = (ZM_DYN_DB_VERSION&&(ZM_DYN_DB_VERSION!=ZM_VERSION))?'errorText':''; global $running; @@ -290,6 +300,7 @@ if (isset($_REQUEST['filter']['Query']['terms']['attr'])) { } ?>
  • >
  • +
  • @@ -307,6 +318,7 @@ if (isset($_REQUEST['filter']['Query']['terms']['attr'])) { +
    > 90 ? ' class="warning"' : '' ).'>'.translate('DB').':'.$connections.'/'.$max_connections.''; ?>
  • : Path()] = $area; @@ -352,7 +364,7 @@ if ($reload == 'reload') ob_start(); return ''.$S->Name() . ': ' . $S->disk_usage_percent().'%' . ''; }; #$func = function($S){ return ''.$S->Name() . ': ' . $S->disk_usage_percent().'%' . ''; }; if ( count($storage_areas) >= 4 ) - $storage_areas = Storage::find_all( array('ServerId'=>null) ); + $storage_areas = Storage::find( array('ServerId'=>null) ); if ( count($storage_areas) < 4 ) echo implode( ', ', array_map ( $func, $storage_areas ) ); echo ' ' . ZM_PATH_MAP .': '. getDiskPercent(ZM_PATH_MAP).'%'; @@ -363,12 +375,13 @@ if ($reload == 'reload') ob_start();
  • + ; var closePopup = "; var auth_hash; -auth_hash = ''; +auth_hash = ''; diff --git a/web/skins/classic/views/_monitor_filters.php b/web/skins/classic/views/_monitor_filters.php index c0c6021d2..f37649c18 100644 --- a/web/skins/classic/views/_monitor_filters.php +++ b/web/skins/classic/views/_monitor_filters.php @@ -18,26 +18,26 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -$servers = Server::find_all(null, array('order'=>'lower(Name)')); +$servers = Server::find(null, array('order'=>'lower(Name)')); $ServersById = array(); foreach ( $servers as $S ) { $ServersById[$S->Id()] = $S; } session_start(); foreach ( array('Group','Function','ServerId','StorageId','Status','MonitorId','MonitorName','Source') as $var ) { - if ( isset( $_REQUEST[$var] ) ) { + if ( isset($_REQUEST[$var]) ) { if ( $_REQUEST[$var] != '' ) { $_SESSION[$var] = $_REQUEST[$var]; } else { - unset( $_SESSION[$var] ); + unset($_SESSION[$var]); } - } else if ( isset( $_REQUEST['filtering'] ) ) { - unset( $_SESSION[$var] ); + } else if ( isset($_REQUEST['filtering']) ) { + unset($_SESSION[$var]); } } session_write_close(); -$storage_areas = Storage::find_all(); +$storage_areas = Storage::find(); $StorageById = array(); foreach ( $storage_areas as $S ) { $StorageById[$S->Id()] = $S; @@ -50,13 +50,13 @@ $html = '; $GroupsById = array(); -foreach ( Group::find_all() as $G ) { +foreach ( Group::find() as $G ) { $GroupsById[$G->Id()] = $G; } $groupSql = ''; if ( count($GroupsById) ) { - $html .= ''; + $html .= ''; # This will end up with the group_id of the deepest selection $group_id = isset($_SESSION['Group']) ? $_SESSION['Group'] : null; $html .= Group::get_group_dropdown(); @@ -78,17 +78,17 @@ if ( $groupSql ) foreach ( array('ServerId','StorageId','Status','Function') as $filter ) { if ( isset($_SESSION[$filter]) ) { if ( is_array($_SESSION[$filter]) ) { - $conditions[] = $filter . ' IN ('.implode(',', array_map(function(){return '?';}, $_SESSION[$filter] ) ). ')'; - $values = array_merge( $values, $_SESSION[$filter] ); + $conditions[] = $filter . ' IN ('.implode(',', array_map(function(){return '?';}, $_SESSION[$filter])). ')'; + $values = array_merge($values, $_SESSION[$filter]); } else { $conditions[] = $filter . '=?'; $values[] = $_SESSION[$filter]; } } } # end foreach filter -if ( ! empty( $user['MonitorIds'] ) ) { - $ids = explode(',', $user['MonitorIds'] ); - $conditions[] = 'M.Id IN ('.implode(',',array_map( function(){return '?';}, $ids) ).')'; +if ( ! empty($user['MonitorIds']) ) { + $ids = explode(',', $user['MonitorIds']); + $conditions[] = 'M.Id IN ('.implode(',',array_map(function(){return '?';}, $ids)).')'; $values += $ids; } @@ -114,8 +114,8 @@ $html .= htmlSelect('Function[]', $Functions, $html .= ''; if ( count($ServersById) > 1 ) { - $html .= ''; - $html .= htmlSelect( 'ServerId[]', $ServersById, + $html .= ''; + $html .= htmlSelect('ServerId[]', $ServersById, (isset($_SESSION['ServerId'])?$_SESSION['ServerId']:''), array( 'onchange'=>'this.form.submit();', @@ -128,8 +128,8 @@ if ( count($ServersById) > 1 ) { } # end if have Servers if ( count($StorageById) > 1 ) { - $html .= ''; - $html .= htmlSelect( 'StorageId[]',$StorageById, + $html .= ''; + $html .= htmlSelect('StorageId[]', $StorageById, (isset($_SESSION['StorageId'])?$_SESSION['StorageId']:''), array( 'onchange'=>'this.form.submit();', @@ -140,7 +140,7 @@ if ( count($StorageById) > 1 ) { $html .= ''; } # end if have Storage Areas -$html .= ''; +$html .= ''; $status_options = array( 'Unknown' => translate('StatusUnknown'), 'NotRunning' => translate('StatusNotRunning'), @@ -157,7 +157,7 @@ $html .= htmlSelect( 'Status[]', $status_options, ) ); $html .= ''; - $html .= ''; + $html .= ''; $html .= ''; $html .= ''; @@ -232,7 +232,7 @@ $html .= htmlSelect( 'Status[]', $status_options, $displayMonitors[] = $monitors[$i]; } # end foreach monitor - $html .= ''; + $html .= ''; $html .= htmlSelect('MonitorId[]', $monitors_dropdown, $selected_monitor_ids, array( 'onchange'=>'this.form.submit();', diff --git a/web/skins/classic/views/add_monitors.php b/web/skins/classic/views/add_monitors.php index f73a22864..98eb87b16 100644 --- a/web/skins/classic/views/add_monitors.php +++ b/web/skins/classic/views/add_monitors.php @@ -44,11 +44,11 @@ xhtmlHeaders(__FILE__, translate('AddMonitors'));
    Enter by IP or URL -

    -Simply enter the ip address or full url to the stream. -It will be probed for available streams, or checked to see if it has already been entered. -If streams are found, they will be listed in the results column. Click Add to add them. -

    +

    + Simply enter the ip address or full url to the stream. + It will be probed for available streams, or checked to see if it has already been entered. + If streams are found, they will be listed in the results column. Click Add to add them. +

    @@ -80,7 +80,7 @@ If streams are found, they will be listed in the results column. Click Add to ad ?> Id()] = $S; @@ -92,7 +92,7 @@ If streams are found, they will be listed in the results column. Click Add to ad Id()] = $S; diff --git a/web/skins/classic/views/console.php b/web/skins/classic/views/console.php index 864bb956f..1ebf49136 100644 --- a/web/skins/classic/views/console.php +++ b/web/skins/classic/views/console.php @@ -217,7 +217,7 @@ ob_start(); echo ''. $j .''; } ?> - + @@ -324,7 +324,7 @@ for( $monitor_i = 0; $monitor_i < count($displayMonitors); $monitor_i += 1 ) { ?> disabled="disabled"/> - + Id() ) { @@ -174,7 +174,6 @@ if ( ZM_WEB_STREAM_METHOD == 'mpeg' && ZM_MPEG_LIVE_FORMAT ) { outputVideoStream( "evtStream", $streamSrc, reScale( $Event->Width(), $scale ), reScale( $Event->Height(), $scale ), ZM_MPEG_LIVE_FORMAT ); } else { $streamSrc = $Event->getStreamSrc( array( 'mode'=>'jpeg', 'frame'=>$fid, 'scale'=>$scale, 'rate'=>$rate, 'maxfps'=>ZM_WEB_VIDEO_MAXFPS, 'replay'=>$replayMode) ); -Warning("Streamsrc: $streamSrc"); if ( canStreamNative() ) { outputImageStream( 'evtStream', $streamSrc, reScale( $Event->Width(), $scale ), reScale( $Event->Height(), $scale ), validHtmlStr($Event->Name()) ); } else { @@ -215,8 +214,8 @@ Warning("Streamsrc: $streamSrc");
    -
    - +
    +
    diff --git a/web/skins/classic/views/events.php b/web/skins/classic/views/events.php index eb5ccb14d..6d2505f61 100644 --- a/web/skins/classic/views/events.php +++ b/web/skins/classic/views/events.php @@ -44,7 +44,7 @@ if ( $_REQUEST['filter']['sql'] ) { $countSql .= $_REQUEST['filter']['sql']; $eventsSql .= $_REQUEST['filter']['sql']; } -$eventsSql .= " ORDER BY $sortColumn $sortOrder"; +$eventsSql .= " ORDER BY $sortColumn $sortOrder,Id $sortOrder"; $page = isset($_REQUEST['page']) ? validInt($_REQUEST['page']) : 0; $limit = isset($_REQUEST['limit']) ? validInt($_REQUEST['limit']) : 0; @@ -83,7 +83,7 @@ if ( $_POST ) { exit(); } -$storage_areas = Storage::find_all(); +$storage_areas = Storage::find(); $StorageById = array(); foreach ( $storage_areas as $S ) { $StorageById[$S->Id()] = $S; @@ -94,7 +94,7 @@ xhtmlHeaders(__FILE__, translate('Events') ); ?>
    - + diff --git a/web/skins/classic/views/filter.php b/web/skins/classic/views/filter.php index d573c9d56..2be6c264c 100644 --- a/web/skins/classic/views/filter.php +++ b/web/skins/classic/views/filter.php @@ -253,7 +253,7 @@ for ( $i=0; $i < count($terms); $i++ ) { - + Monitor(); if ( !empty($fid) ) { @@ -39,7 +39,7 @@ if ( !empty($fid) ) { } else { $frame = dbFetchOne( 'SELECT * FROM Frames WHERE EventId = ? AND Score = ?', NULL, array( $eid, $Event->MaxScore() ) ); } -$Frame = new Frame( $frame ); +$Frame = new Frame($frame); $maxFid = $Event->Frames(); @@ -91,10 +91,10 @@ xhtmlHeaders(__FILE__, translate('Frame').' - '.$Event->Id()." - ".$Frame->Frame
    -
    -

    Id()."-".$Frame->FrameId()." (".$Frame->Score().")" ?>

    - - +
    +

    Id().'-'.$Frame->FrameId().' ('.$Frame->Score().')' ?>

    + +
    @@ -109,11 +109,13 @@ xhtmlHeaders(__FILE__, translate('Frame').' - '.$Event->Id()." - ".$Frame->Frame

    FrameId() > 1 ) { ?> - - -FrameId() < $maxFid ) { ?> - - + + +FrameId() < $maxFid ) { ?> + +

    diff --git a/web/skins/classic/views/frames.php b/web/skins/classic/views/frames.php index c50da5e7e..f27f004c3 100644 --- a/web/skins/classic/views/frames.php +++ b/web/skins/classic/views/frames.php @@ -18,11 +18,11 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canView( 'Events' ) ) { +if ( !canView('Events') ) { $view = 'error'; return; } -require_once( 'includes/Frame.php' ); +require_once('includes/Frame.php'); $Event = new Event( $_REQUEST['eid'] ); $sql = 'SELECT *, unix_timestamp( TimeStamp ) AS UnixTimeStamp FROM Frames WHERE EventID = ? ORDER BY FrameId'; @@ -30,7 +30,7 @@ $frames = dbFetchAll( $sql, NULL, array( $_REQUEST['eid'] ) ); $focusWindow = true; -xhtmlHeaders(__FILE__, translate('Frames')." - ".$Event->Id() ); +xhtmlHeaders(__FILE__, translate('Frames').' - '.$Event->Id() ); ?>
    diff --git a/web/skins/classic/views/group.php b/web/skins/classic/views/group.php index 5a4446544..d765e445f 100644 --- a/web/skins/classic/views/group.php +++ b/web/skins/classic/views/group.php @@ -18,18 +18,18 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // -if ( !canEdit( 'Groups' ) ) { +if ( !canEdit('Groups') ) { $view = 'error'; return; } if ( !empty($_REQUEST['gid']) ) { - $newGroup = new Group( $_REQUEST['gid'] ); + $newGroup = new Group($_REQUEST['gid']); } else { $newGroup = new Group(); } -xhtmlHeaders( __FILE__, translate('Group').' - '.$newGroup->Name() ); +xhtmlHeaders(__FILE__, translate('Group').' - '.$newGroup->Name()); ?>
    @@ -52,7 +52,7 @@ xhtmlHeaders( __FILE__, translate('Group').' - '.$newGroup->Name() ); Id()] = $Group; } @@ -76,7 +76,7 @@ function get_children($Group) { $kids = array(); if ( isset( $children[$Group->Id()] ) ) { - $kids += array_map( 'get_Id', $children[$Group->Id()] ); + $kids += array_map('get_Id', $children[$Group->Id()]); foreach ( $children[$Group->Id()] as $G ) { foreach ( get_children($G) as $id ) { $kids[] = $id; @@ -89,13 +89,12 @@ function get_children($Group) { $kids = get_children($newGroup); if ( $newGroup->Id() ) $kids[] = $newGroup->Id(); -$sql = 'SELECT Id,Name from Groups'.(count($kids)?' WHERE Id NOT IN ('.implode(',',array_map(function(){return '?';}, $kids )).')' : '').' ORDER BY Name'; +$sql = 'SELECT Id,Name from Groups'.(count($kids)?' WHERE Id NOT IN ('.implode(',',array_map(function(){return '?';}, $kids)).')' : '').' ORDER BY Name'; $options = array(''=>'None'); -foreach ( dbFetchAll( $sql, null, $kids ) as $option ) { - - $options[$option['Id']] = str_repeat('  ', $Groups[$option['Id']]->depth() ) . $option['Name']; +foreach ( dbFetchAll($sql, null, $kids) as $option ) { + $options[$option['Id']] = str_repeat('  ', $Groups[$option['Id']]->depth()) . $option['Name']; } -echo htmlSelect( 'newGroup[ParentId]', $options, $newGroup->ParentId(), array('onchange'=>'configureButtons(this);' )); +echo htmlSelect('newGroup[ParentId]', $options, $newGroup->ParentId(), array('onchange'=>'configureButtons(this);')); ?> @@ -104,10 +103,10 @@ echo htmlSelect( 'newGroup[ParentId]', $options, $newGroup->ParentId(), array('o Id() ? '' : ' disabled="disabled"'?>/> +
    diff --git a/web/skins/classic/views/groups.php b/web/skins/classic/views/groups.php index ecf85b3c1..fb383026d 100644 --- a/web/skins/classic/views/groups.php +++ b/web/skins/classic/views/groups.php @@ -28,7 +28,7 @@ $group_id = 0; $max_depth = 0; $Groups = array(); -foreach ( Group::find_all( ) as $Group ) { +foreach ( Group::find( ) as $Group ) { $Groups[$Group->Id()] = $Group; } @@ -41,7 +41,7 @@ foreach ( $Groups as $id=>$Group ) { if ( $max_depth < $Group->depth() ) $max_depth = $Group->depth(); } -xhtmlHeaders(__FILE__, translate('Groups') ); +xhtmlHeaders(__FILE__, translate('Groups')); ?>
    @@ -64,33 +64,37 @@ function group_line( $Group ) { global $children; global $max_depth; $html = ''; - $html .= str_repeat( ' ', $Group->depth() ); + $html .= str_repeat(' ', $Group->depth()); $html .= ''; if ( canEdit('Groups') ) { $html .= ''. validHtmlStr($Group->Id() . ' ' . $Group->Name()).''; } else { $html .= validHtmlStr($Group->Name()); } - $html .= ''. monitorIdsToNames( $Group->MonitorIds(), 30 ).' + $html .= ''. monitorIdsToNames($Group->MonitorIds(), 30).' '; if ( isset( $children[$Group->Id()] ) ) { foreach ( $children[$Group->Id()] as $G ) { - $html .= group_line( $G ); + $html .= group_line($G); } } return $html; } if ( isset( $children[null] ) ) foreach ( $children[null] as $Group ) - echo group_line( $Group ); + echo group_line($Group); ?>
    - /> - + +
    diff --git a/web/skins/classic/views/js/event.js b/web/skins/classic/views/js/event.js index 9d963c745..7f46936a9 100644 --- a/web/skins/classic/views/js/event.js +++ b/web/skins/classic/views/js/event.js @@ -42,7 +42,7 @@ function vjsReplay() { $j.ajaxSetup ({timeout: AJAX_TIMEOUT }); //sets timeout for all getJSON. -var cueFrames = null; //make cueFrames availaible even if we don't send another ajax query +var cueFrames = null; //make cueFrames available even if we don't send another ajax query function initialAlarmCues (eventId) { $j.getJSON(thisUrl + '?view=request&request=status&entity=frames&id=' + eventId, setAlarmCues); //get frames data for alarmCues and inserts into html @@ -55,70 +55,78 @@ function setAlarmCues (data) { } function renderAlarmCues (containerEl) { - if (cueFrames) { - var cueRatio = containerEl.width() / (cueFrames[cueFrames.length - 1].Delta * 100); - var minAlarm = Math.ceil(1/cueRatio); - var spanTimeStart = 0; - var spanTimeEnd = 0; - var alarmed = 0; - var alarmHtml = ""; - var pixSkew = 0; - var skip = 0; - for (let i = 0; i < cueFrames.length; i++) { - skip = 0; - frame = cueFrames[i]; - if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm. - alarmed = 1; - if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan - spanTimeEnd = frame.Delta * 100; - spanTime = spanTimeEnd - spanTimeStart; - let pix = cueRatio * spanTime; - pixSkew += pix - Math.round(pix);//average out the rounding errors. - pix = Math.round(pix); - if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { //add skew if it's a pixel and won't zero out span. - pix += Math.round(pixSkew); - pixSkew = pixSkew - Math.round(pixSkew); - } - alarmHtml += ''; - spanTimeStart = spanTimeEnd; - } else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing. - futNone = 0; - indexPlus = i+1; - if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < cueFrames.length) continue; //alarm is too short and there is more event - while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan - if (indexPlus >= cueFrames.length) break; //check if end of event. - futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100); - if (cueFrames[indexPlus].Type == "Alarm") { - i = --indexPlus; - skip = 1; - break; - } - indexPlus++; - } - if (skip == 1) continue; //javascript doesn't support continue 2; - spanTimeEnd = frame.Delta *100; - spanTime = spanTimeEnd - spanTimeStart; - alarmed = 0; - pix = cueRatio * spanTime; - pixSkew += pix - Math.round(pix); - pix = Math.round(pix); - if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { - pix += Math.round(pixSkew); - pixSkew = pixSkew - Math.round(pixSkew); - } - alarmHtml += ''; - spanTimeStart = spanTimeEnd; - } else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm - spanTimeEnd = frame.Delta * 100; - spanTime = spanTimeEnd - spanTimeStart; - alarmed = 0; - pix = Math.round(cueRatio * spanTime); - if (pixSkew >= .5 || pixSkew <= -.5) pix += Math.round(pixSkew); - alarmHtml += ''; - } - } - return alarmHtml; + if ( !( cueFrames && cueFrames.length ) ) { + console.log("No cue frames for event"); + return; } + // This uses the Delta of the last frame to get the length of the event. I can't help but wonder though + // if we shouldn't just use the event length endtime-starttime + var cueRatio = containerEl.width() / (cueFrames[cueFrames.length - 1].Delta * 100); + var minAlarm = Math.ceil(1/cueRatio); + var spanTimeStart = 0; + var spanTimeEnd = 0; + var alarmed = 0; + var alarmHtml = ""; + var pixSkew = 0; + var skip = 0; +var num_cueFrames = cueFrames.length; + for ( let i = 0; i < num_cueFrames; i++ ) { + skip = 0; + frame = cueFrames[i]; + if (frame.Type == "Alarm" && alarmed == 0) { //From nothing to alarm. End nothing and start alarm. + alarmed = 1; + if (frame.Delta == 0) continue; //If event starts with an alarm or too few for a nonespan + spanTimeEnd = frame.Delta * 100; + spanTime = spanTimeEnd - spanTimeStart; + let pix = cueRatio * spanTime; + pixSkew += pix - Math.round(pix);//average out the rounding errors. + pix = Math.round(pix); + if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { //add skew if it's a pixel and won't zero out span. + pix += Math.round(pixSkew); + pixSkew = pixSkew - Math.round(pixSkew); + } + alarmHtml += ''; + spanTimeStart = spanTimeEnd; + } else if (frame.Type !== "Alarm" && alarmed == 1) { //from alarm to nothing. End alarm and start nothing. + futNone = 0; + indexPlus = i+1; + if (((frame.Delta * 100) - spanTimeStart) < minAlarm && indexPlus < num_cueFrames) { + //alarm is too short and there is more event + continue; + } + while (futNone < minAlarm) { //check ahead to see if there's enough for a nonespan + if (indexPlus >= cueFrames.length) break; //check if end of event. + futNone = (cueFrames[indexPlus].Delta *100) - (frame.Delta *100); + if (cueFrames[indexPlus].Type == "Alarm") { + i = --indexPlus; + skip = 1; + break; + } + indexPlus++; + } + if (skip == 1) continue; //javascript doesn't support continue 2; + spanTimeEnd = frame.Delta *100; + spanTime = spanTimeEnd - spanTimeStart; + alarmed = 0; + pix = cueRatio * spanTime; + pixSkew += pix - Math.round(pix); + pix = Math.round(pix); + if ((pixSkew > 1 || pixSkew < -1) && pix + Math.round(pixSkew) > 0) { + pix += Math.round(pixSkew); + pixSkew = pixSkew - Math.round(pixSkew); + } + alarmHtml += ''; + spanTimeStart = spanTimeEnd; + } else if (frame.Type == "Alarm" && alarmed == 1 && i + 1 >= cueFrames.length) { //event ends on an alarm + spanTimeEnd = frame.Delta * 100; + spanTime = spanTimeEnd - spanTimeStart; + alarmed = 0; + pix = Math.round(cueRatio * spanTime); + if (pixSkew >= .5 || pixSkew <= -.5) pix += Math.round(pixSkew); + alarmHtml += ''; + } + } + return alarmHtml; } function setButtonState( element, butClass ) { @@ -147,7 +155,7 @@ function changeScale() { } else { eventViewer = $j(vid ? '#videoobj' : '#evtStream'); } - if (scale == "auto") { + if ( scale == "auto" ) { let newSize = scaleToFit(eventData.Width, eventData.Height, eventViewer, bottomEl); newWidth = newSize.width; newHeight = newSize.height; @@ -157,21 +165,22 @@ function changeScale() { newWidth = eventData.Width * scale / SCALE_BASE; newHeight = eventData.Height * scale / SCALE_BASE; } - if (!(streamMode == 'stills')) eventViewer.width(newWidth); //stills handles its own width + if ( !(streamMode == 'stills') ) + eventViewer.width(newWidth); //stills handles its own width eventViewer.height(newHeight); if ( !vid ) { // zms needs extra sizing streamScale(scale == "auto" ? autoScale : scale); drawProgressBar(); } - if (streamMode == 'stills') { + if ( streamMode == 'stills' ) { slider.autosize(); alarmCue.html(renderAlarmCues($j('#thumbsSliderPanel'))); } else { alarmCue.html(renderAlarmCues(eventViewer));//just re-render alarmCues. skip ajax call } - if (scale == "auto") { + if ( scale == "auto" ) { Cookie.write('zmEventScaleAuto', 'auto', {duration: 10*365}); - }else{ + } else { Cookie.write('zmEventScale'+eventData.MonitorId, scale, {duration: 10*365}); Cookie.dispose('zmEventScaleAuto'); } @@ -180,7 +189,7 @@ function changeScale() { function changeReplayMode() { var replayMode = $('replayMode').get('value'); - Cookie.write( 'replayMode', replayMode, { duration: 10*365 }); + Cookie.write('replayMode', replayMode, { duration: 10*365 }); refreshWindow(); } @@ -193,8 +202,10 @@ var lastEventId = 0; var zmsBroke = false; //Use alternate navigation if zms has crashed function getCmdResponse( respObj, respText ) { - if ( checkStreamForErrors( "getCmdResponse", respObj ) ) { + if ( checkStreamForErrors("getCmdResponse", respObj) ) { console.log('Got an error from getCmdResponse'); + console.log(respObj); + console.log(respText); zmsBroke = true; return; } @@ -202,18 +213,24 @@ function getCmdResponse( respObj, respText ) { zmsBroke = false; if ( streamCmdTimer ) - streamCmdTimer = clearTimeout( streamCmdTimer ); + streamCmdTimer = clearTimeout(streamCmdTimer); streamStatus = respObj.status; - if (streamStatus.progress >= Math.round(parseFloat(eventData.Length))) streamStatus.progress = parseFloat(eventData.Length); //Limit progress to reality + if ( streamStatus.progress >= Math.round(parseFloat(eventData.Length)) ) + streamStatus.progress = parseFloat(eventData.Length); //Limit progress to reality var eventId = streamStatus.event; - if ( eventId != lastEventId && lastEventId != 0) { //Doesn't run on first load, prevents a double hit on event and nearEvents ajax - eventQuery( eventId ); - initialAlarmCues(eventId); //zms uses this instead of a page reload, must call ajax+render - lastEventId = eventId; + if ( lastEventId ) { + if ( eventId != lastEventId ) { + //Doesn't run on first load, prevents a double hit on event and nearEvents ajax + eventQuery(eventId); + initialAlarmCues(eventId); //zms uses this instead of a page reload, must call ajax+render + lastEventId = eventId; + } + } else { + lastEventId = eventId; //Only fires on first load. } - if (lastEventId == 0) lastEventId = eventId; //Only fires on first load. + if ( streamStatus.paused == true ) { streamPause( ); } else { @@ -231,7 +248,7 @@ function getCmdResponse( respObj, respText ) { if ( streamStatus.auth ) { // Try to reload the image stream. - var streamImg = document.getElementById('evtStream'); + var streamImg = $j('#evtStream'); if ( streamImg ) streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth ); } // end if haev a new auth hash @@ -239,18 +256,24 @@ function getCmdResponse( respObj, respText ) { streamCmdTimer = streamQuery.delay( streamTimeout ); //Timeout is refresh rate for progressBox and time display } -var streamReq = new Request.JSON( { url: thisUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', onSuccess: getCmdResponse } ); +var streamReq = new Request.JSON( { + url: thisUrl, + method: 'get', + timeout: AJAX_TIMEOUT, + link: 'chain', + onSuccess: getCmdResponse +} ); function pauseClicked() { - if (vid) { + if ( vid ) { vid.pause(); } else { - streamReq.send( streamParms+"&command="+CMD_PAUSE ); + streamReq.send(streamParms+"&command="+CMD_PAUSE); streamPause(); } } -function vjsPause () { +function vjsPause() { stopFastRev(); streamPause(); } @@ -267,8 +290,8 @@ function streamPause( ) { } function playClicked( ) { - if (vid) { - if (vid.paused()) { + if ( vid ) { + if ( vid.paused() ) { vid.play(); } else { vjsPlay(); //handles fast forward and rewind @@ -279,7 +302,7 @@ function playClicked( ) { } } -function vjsPlay () { //catches if we change mode programatically +function vjsPlay() { //catches if we change mode programatically stopFastRev(); $j('#rateValue').html(vid.playbackRate()); streamPlay(); @@ -302,13 +325,14 @@ function streamFastFwd( action ) { setButtonState( $('slowFwdBtn'), 'unavail' ); setButtonState( $('slowRevBtn'), 'unavail' ); setButtonState( $('fastRevBtn'), 'inactive' ); - if (vid) { - if (revSpeed != .5) stopFastRev(); + if ( vid ) { + if ( revSpeed != .5 ) stopFastRev(); vid.playbackRate(rates[rates.indexOf(vid.playbackRate()*100)-1]/100); - if (rates.indexOf(vid.playbackRate()*100)-1 == -1) setButtonState($('fastFwdBtn'), 'unavail'); + if ( rates.indexOf(vid.playbackRate()*100)-1 == -1 ) + setButtonState($('fastFwdBtn'), 'unavail'); $j('#rateValue').html(vid.playbackRate()); } else { - streamReq.send( streamParms+"&command="+CMD_FASTFWD ); + streamReq.send(streamParms+"&command="+CMD_FASTFWD); } } @@ -317,22 +341,22 @@ var intervalRewind; var revSpeed = .5; function streamSlowFwd( action ) { - if (vid) { + if ( vid ) { vid.currentTime(vid.currentTime() + spf); } else { - streamReq.send( streamParms+"&command="+CMD_SLOWFWD ); + streamReq.send(streamParms+"&command="+CMD_SLOWFWD); } } function streamSlowRev( action ) { - if (vid) { + if ( vid ) { vid.currentTime(vid.currentTime() - spf); } else { - streamReq.send( streamParms+"&command="+CMD_SLOWREV ); + streamReq.send(streamParms+"&command="+CMD_SLOWREV); } } -function stopFastRev () { +function stopFastRev() { clearInterval(intervalRewind); vid.playbackRate(1); revSpeed = .5; @@ -345,9 +369,9 @@ function streamFastRev( action ) { setButtonState( $('slowFwdBtn'), 'unavail' ); setButtonState( $('slowRevBtn'), 'unavail' ); setButtonState( $('fastRevBtn'), 'active' ); - if (vid) { //There is no reverse play with mp4. Set the speed to 0 and manualy set the time back. + if ( vid ) { //There is no reverse play with mp4. Set the speed to 0 and manualy set the time back. revSpeed = rates[rates.indexOf(revSpeed*100)-1]/100; - if (rates.indexOf(revSpeed*100) == 0) { + if ( rates.indexOf(revSpeed*100) == 0 ) { setButtonState( $('fastRevBtn'), 'unavail' ); } clearInterval(intervalRewind); @@ -367,9 +391,12 @@ function streamFastRev( action ) { } function streamPrev(action) { - if (action) { + if ( action ) { $j(".vjsMessage").remove(); - if (vid && PrevEventDefVideoPath.indexOf("view_video") > 0) { + location.replace(thisUrl + '?view=event&eid=' + prevEventId + filterQuery + sortQuery); + return; + + if ( vid && PrevEventDefVideoPath.indexOf("view_video") > 0 ) { CurEventDefVideoPath = PrevEventDefVideoPath; eventQuery(prevEventId); } else if (zmsBroke || (vid && PrevEventDefVideoPath.indexOf("view_video") < 0) || $j("#vjsMessage").length || PrevEventDefVideoPath.indexOf("view_video") > 0) {//zms broke, leaving videojs, last event, moving to videojs @@ -382,20 +409,25 @@ function streamPrev(action) { } function streamNext(action) { - if (action) { + if ( action ) { $j(".vjsMessage").remove();//This shouldn't happen - if (nextEventId == 0) { //handles deleting last event. + if ( nextEventId == 0 ) { //handles deleting last event. pauseClicked(); let hideContainer = $j('#eventVideo'); let hideStream = $j(vid ? "#videoobj" : "#evtStream").height() + (vid ? 0 :$j("#progressBar").height()); hideContainer.prepend('

    No more events

    '); - if (vid == null) zmsBroke = true; + if ( vid == null ) zmsBroke = true; return; } - if (vid && NextEventDefVideoPath.indexOf("view_video") > 0) { //on and staying with videojs + // We used to try to dynamically update all the bits in the page, which is really complex + // How about we just reload the page? + // + location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); + return; + if ( vid && ( NextEventDefVideoPath.indexOf("view_video") > 0 ) ) { //on and staying with videojs CurEventDefVideoPath = NextEventDefVideoPath; eventQuery(nextEventId); - } else if (zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0) {//reload zms, leaving vjs, moving to vjs + } else if ( zmsBroke || (vid && NextEventDefVideoPath.indexOf("view_video") < 0) || NextEventDefVideoPath.indexOf("view_video") > 0) {//reload zms, leaving vjs, moving to vjs location.replace(thisUrl + '?view=event&eid=' + nextEventId + filterQuery + sortQuery); } else { streamReq.send(streamParms+"&command="+CMD_NEXT); @@ -855,7 +887,7 @@ function unarchiveEvent() { } function showEventFrames() { - createPopup( '?view=frames&eid='+eventData.Id, 'zmFrames', 'frames' ); + createPopup( '?view=frames&eid='+eventData.Id, 'zmFrames', 'frames', WEB_LIST_THUMB_WIDTH, WEB_LIST_THUMB_HEIGHT ); } function showStream() { @@ -969,18 +1001,20 @@ function initPage() { progressBarNav (); streamCmdTimer = streamQuery.delay( 250 ); if ( canStreamNative ) { - var streamImg = $('imageFeed').getElement('img'); - if ( !streamImg ) - streamImg = $('imageFeed').getElement('object'); - $(streamImg).addEvent( 'click', function( event ) { handleClick( event ); } ); + var imageFeed = $('imageFeed'); + if ( !imageFeed ) { + console.log('No element with id tag imageFeed found.'); + } else { + var streamImg = imageFeed.getElement('img'); + if ( !streamImg ) + streamImg = imageFeed.getElement('object'); + $(streamImg).addEvent( 'click', function( event ) { handleClick( event ); } ); + } } } nearEventsQuery(eventData.Id); initialAlarmCues(eventData.Id); //call ajax+renderAlarmCues if (scale == "auto") changeScale(); - if (window.history.length == 1) { - $j('#closeWindow').html(''); - } } // Kick everything off diff --git a/web/skins/classic/views/js/event.js.php b/web/skins/classic/views/js/event.js.php index dbdc5c514..b1924a752 100644 --- a/web/skins/classic/views/js/event.js.php +++ b/web/skins/classic/views/js/event.js.php @@ -55,3 +55,5 @@ var streamMode = ''; // var deleteString = ""; var causeString = ""; +var WEB_LIST_THUMB_WIDTH = ''; +var WEB_LIST_THUMB_HEIGHT = ''; diff --git a/web/skins/classic/views/js/log.js b/web/skins/classic/views/js/log.js index 0d1d0b3be..7a78dada1 100644 --- a/web/skins/classic/views/js/log.js +++ b/web/skins/classic/views/js/log.js @@ -80,7 +80,16 @@ function logResponse( respObj ) { } } ); - options = respObj.options; + if ( typeof(respObj.options) == 'object' ) { + $j.each( respObj.options, + function( field ) { + if ( options[field] ) + options[field] = Object.assign(options[field], respObj.options[field]); + else + options[field] = respObj.options[field]; + } + ); + } updateFilterSelectors(); $('lastUpdate').set('text', respObj.updated); $('logState').set('text', respObj.state); @@ -217,38 +226,38 @@ function exportRequest() { function updateFilterSelectors() { Object.each(options, - function( values, key ) { - var selector = $('filter['+key+']'); - if ( ! selector ) { - if ( window.console && window.console.log ) { - window.console.log("No selector found for " + key ); - } - return; + function( values, key ) { + var selector = $('filter['+key+']'); + if ( ! selector ) { + if ( window.console && window.console.log ) { + window.console.log("No selector found for " + key ); + } + return; + } + selector.options.length = 1; + if ( key == 'Level' ) { + Object.each(values, + function( value, label ) { + selector.options[selector.options.length] = new Option(value, label); } - selector.options.length = 1; - if ( key == 'Level' ) { - Object.each(values, - function( value, label ) { - selector.options[selector.options.length] = new Option(value, label); - } - ); - } else if ( key == 'ServerId' ) { - Object.each(values, - function( value, label ) { - selector.options[selector.options.length] = new Option(value, label); - } - ); - } else { - Object.each(values, - function( value, label ) { - selector.options[selector.options.length] = new Option(value, label); - } - ); + ); + } else if ( key == 'ServerId' ) { + Object.each(values, + function( value, label ) { + selector.options[selector.options.length] = new Option(value, label); } - if ( filter[key] ) - selector.set('value', filter[key]); + ); + } else { + Object.each(values, + function( value, label ) { + selector.options[selector.options.length] = new Option(value, label); + } + ); + } + if ( filter[key] ) + selector.set('value', filter[key]); - } + } ); } diff --git a/web/skins/classic/views/js/montage.js b/web/skins/classic/views/js/montage.js index b9dc84f00..750678797 100644 --- a/web/skins/classic/views/js/montage.js +++ b/web/skins/classic/views/js/montage.js @@ -3,11 +3,11 @@ var requestQueue = new Request.Queue( { concurrent: monitorData.length, stopOnFa function Monitor( monitorData ) { this.id = monitorData.id; this.connKey = monitorData.connKey; - this.server_url = monitorData.server_url; + this.url = monitorData.url; this.status = null; this.alarmState = STATE_IDLE; this.lastAlarmState = STATE_IDLE; - this.streamCmdParms = this.server_url+'?view=request&request=stream&connkey='+this.connKey; + this.streamCmdParms = 'view=request&request=stream&connkey='+this.connKey; this.onclick = monitorData.onclick; if ( auth_hash ) this.streamCmdParms += '&auth='+auth_hash; @@ -38,14 +38,14 @@ function Monitor( monitorData ) { console.log('onerror: ' + text + ' error:'+error); // Requeue, but want to wait a while. var streamCmdTimeout = 1000*statusRefreshTimeout; - this.streamCmdTimer = this.streamCmdQuery.delay( streamCmdTimeout, this ); + this.streamCmdTimer = this.streamCmdQuery.delay(streamCmdTimeout, this); }; this.onFailure = function( xhr ) { console.log('onFailure: ' + this.connKey); - console.log(xhr ); + console.log(xhr); if ( ! requestQueue.hasNext("cmdReq"+this.id) ) { console.log("Not requeuing because there is one already"); - requestQueue.addRequest( "cmdReq"+this.id, this.streamCmdReq ); + requestQueue.addRequest("cmdReq"+this.id, this.streamCmdReq); } if ( 0 ) { // Requeue, but want to wait a while. @@ -153,7 +153,7 @@ function Monitor( monitorData ) { if ( this.type != 'WebSite' ) { this.streamCmdReq = new Request.JSON( { - url: this.server_url, + url: this.url, method: 'get', timeout: 1000+AJAX_TIMEOUT, onSuccess: this.getStreamCmdResponse.bind( this ), @@ -397,6 +397,20 @@ function reloadWebSite(ndx) { var monitors = new Array(); function initPage() { + + jQuery(document).ready(function(){ + jQuery("#hdrbutton").click(function(){ + jQuery("#flipMontageHeader").slideToggle("slow"); + jQuery("#hdrbutton").toggleClass('glyphicon-menu-down').toggleClass('glyphicon-menu-up'); + Cookie.write( 'zmMontageHeaderFlip', jQuery('#hdrbutton').hasClass('glyphicon-menu-up') ? 'up' : 'down', { duration: 10*365 } ); + }); + }); + if ( Cookie.read('zmMontageHeaderFlip') == 'down' ) { + // The chosen dropdowns require the selects to be visible, so once chosen has initialized, we can hide the header + jQuery("#flipMontageHeader").slideToggle("fast"); + jQuery("#hdrbutton").toggleClass('glyphicon-menu-down').toggleClass('glyphicon-menu-up'); + } + for ( var i = 0; i < monitorData.length; i++ ) { monitors[i] = new Monitor(monitorData[i]); var delay = Math.round( (Math.random()+0.5)*statusRefreshTimeout ); @@ -408,6 +422,9 @@ function initPage() { } selectLayout('#zmMontageLayout'); + if ( 0 ) { + // What is the purpose of this code? I think it just starts up a second ajax thread, + //increasing the load on the server. for ( var i = 0; i < monitorData.length; i++ ) { if ( monitors[i].type == 'WebSite' ) continue; @@ -416,6 +433,7 @@ function initPage() { monitors[i].streamCmdQuery.delay( delay, monitors[i] ); //monitors[i].zm_startup(delay); } + } } // Kick everything off window.addEvent( 'domready', initPage ); diff --git a/web/skins/classic/views/js/montage.js.php b/web/skins/classic/views/js/montage.js.php index 1b67d764b..183f17648 100644 --- a/web/skins/classic/views/js/montage.js.php +++ b/web/skins/classic/views/js/montage.js.php @@ -35,7 +35,7 @@ monitorData[monitorData.length] = { 'connKey': connKey() ?>, 'width': Width() ?>, 'height':Height() ?>, - 'server_url': 'Server()->Url().$_SERVER['PHP_SELF'] ?>', + 'url': 'Url() ?>', 'onclick': function(){createPopup( '?view=watch&mid=Id() ?>', 'zmWatchId() ?>', 'watch', Width(), $monitor->PopupScale() ); ?>, Height(), $monitor->PopupScale() ); ?> );}, 'type': 'Type() ?>', 'refresh': 'Refresh() ?>' diff --git a/web/skins/classic/views/js/montagereview.js b/web/skins/classic/views/js/montagereview.js index d43d7dc4f..aad114325 100644 --- a/web/skins/classic/views/js/montagereview.js +++ b/web/skins/classic/views/js/montagereview.js @@ -866,6 +866,14 @@ function changeDateTime(e) { // >>>>>>>>> Initialization that runs on window load by being at the bottom function initPage() { + + jQuery(document).ready(function(){ + jQuery("#hdrbutton").click(function(){ + jQuery("#flipMontageHeader").slideToggle("slow"); + jQuery("#hdrbutton").toggleClass('glyphicon-menu-down').toggleClass('glyphicon-menu-up'); + }); + }); + for ( var i = 0, len = monitorPtr.length; i < len; i += 1 ) { var monId = monitorPtr[i]; if ( ! monId ) continue; diff --git a/web/skins/classic/views/js/montagereview.js.php b/web/skins/classic/views/js/montagereview.js.php index 7754f13cc..9f7990590 100644 --- a/web/skins/classic/views/js/montagereview.js.php +++ b/web/skins/classic/views/js/montagereview.js.php @@ -119,12 +119,12 @@ echo " };\n"; } // end if initialmodeislive echo "\nvar Storage = [];\n"; -foreach ( Storage::find_all() as $Storage ) { -echo 'Storage[' . $Storage->Id() . '] = ' . json_encode($Storage). ";\n"; +foreach ( Storage::find() as $Storage ) { + echo 'Storage[' . $Storage->Id() . '] = ' . json_encode($Storage). ";\n"; } echo "\nvar Servers = [];\n"; -foreach ( Server::find_all() as $Server ) { -echo 'Servers[' . $Server->Id() . '] = new Server(' . json_encode($Server). ");\n"; +foreach ( Server::find() as $Server ) { + echo 'Servers[' . $Server->Id() . '] = new Server(' . json_encode($Server). ");\n"; } echo ' var monitorName = []; diff --git a/web/skins/classic/views/js/privacy.js b/web/skins/classic/views/js/privacy.js new file mode 100644 index 000000000..4b3499981 --- /dev/null +++ b/web/skins/classic/views/js/privacy.js @@ -0,0 +1,9 @@ +function submitForm( element ) { + var form = element.form; + if ( form.option.selectedIndex == 0 ) + form.view.value = currentView; + else + form.view.value = 'none'; + form.submit(); +} + diff --git a/web/skins/classic/views/js/watch.js b/web/skins/classic/views/js/watch.js index c6a014f2b..bab0888c7 100644 --- a/web/skins/classic/views/js/watch.js +++ b/web/skins/classic/views/js/watch.js @@ -110,10 +110,11 @@ if ( monitorType != 'WebSite' ) { if ( auth_hash ) streamCmdParms += '&auth='+auth_hash; var streamCmdReq = new Request.JSON( { - url: monitorUrl+thisUrl, + url: monitorUrl, method: 'get', timeout: AJAX_TIMEOUT, link: 'chain', + onError: getStreamCmdError, onSuccess: getStreamCmdResponse, onFailure: getStreamCmdFailure } ); @@ -122,22 +123,28 @@ if ( monitorType != 'WebSite' ) { var streamStatus; -function getStreamCmdFailure(xhr) { -console.log(xhr); +function getStreamCmdError(text,error) { + console.log(error); + // Error are normally due to failed auth. reload the page. + window.location.reload(); } -function getStreamCmdResponse( respObj, respText ) { +function getStreamCmdFailure(xhr) { + console.log(xhr); +} +function getStreamCmdResponse(respObj, respText) { watchdogOk("stream"); + console.log('stream'); if ( streamCmdTimer ) - streamCmdTimer = clearTimeout( streamCmdTimer ); + streamCmdTimer = clearTimeout(streamCmdTimer); if ( respObj.result == 'Ok' ) { // The get status command can get backed up, in which case we won't be able to get the semaphore and will exit. if ( respObj.status ) { streamStatus = respObj.status; - $('fpsValue').set( 'text', streamStatus.fps ); + $('fpsValue').set('text', streamStatus.fps); - setAlarmState( streamStatus.state ); + setAlarmState(streamStatus.state); - $('levelValue').set( 'text', streamStatus.level ); + $('levelValue').set('text', streamStatus.level); if ( streamStatus.level > 95 ) $('levelValue').className = "alarm"; else if ( streamStatus.level > 80 ) @@ -145,34 +152,34 @@ function getStreamCmdResponse( respObj, respText ) { else $('levelValue').className = "ok"; - var delayString = secsToTime( streamStatus.delay ); + var delayString = secsToTime(streamStatus.delay); if ( streamStatus.paused == true ) { - $('modeValue').set( 'text', "Paused" ); - $('rate').addClass( 'hidden' ); - $('delayValue').set( 'text', delayString ); - $('delay').removeClass( 'hidden' ); - $('level').removeClass( 'hidden' ); - streamCmdPause( false ); + $('modeValue').set('text', 'Paused'); + $('rate').addClass('hidden'); + $('delayValue').set('text', delayString); + $('delay').removeClass('hidden'); + $('level').removeClass('hidden'); + streamCmdPause(false); } else if ( streamStatus.delayed == true ) { - $('modeValue').set( 'text', "Replay" ); - $('rateValue').set( 'text', streamStatus.rate ); - $('rate').removeClass( 'hidden' ); - $('delayValue').set( 'text', delayString ); - $('delay').removeClass( 'hidden' ); - $('level').removeClass( 'hidden' ); + $('modeValue').set('text', 'Replay'); + $('rateValue').set('text', streamStatus.rate); + $('rate').removeClass('hidden'); + $('delayValue').set('text', delayString); + $('delay').removeClass('hidden'); + $('level').removeClass('hidden'); if ( streamStatus.rate == 1 ) { - streamCmdPlay( false ); + streamCmdPlay(false); } else if ( streamStatus.rate > 0 ) { if ( streamStatus.rate < 1 ) - streamCmdSlowFwd( false ); + streamCmdSlowFwd(false); else - streamCmdFastFwd( false ); + streamCmdFastFwd(false); } else { if ( streamStatus.rate > -1 ) - streamCmdSlowRev( false ); + streamCmdSlowRev(false); else - streamCmdFastRev( false ); + streamCmdFastRev(false); } // rate } else { $('modeValue').set( 'text', "Live" ); @@ -209,16 +216,19 @@ function getStreamCmdResponse( respObj, respText ) { } // end if canEditMonitors if ( streamStatus.auth ) { - console.log("Haev a new auth hash" + streamStatus.auth); + console.log("Have a new auth hash" + streamStatus.auth); // Try to reload the image stream. var streamImg = $('liveStream'); if ( streamImg ) - streamImg.src = streamImg.src.replace( /auth=\w+/i, 'auth='+streamStatus.auth ); - } // end if haev a new auth hash + streamImg.src = streamImg.src.replace(/auth=\w+/i, 'auth='+streamStatus.auth); + } // end if have a new auth hash } // end if respObj.status } else { - checkStreamForErrors("getStreamCmdResponse",respObj);//log them + checkStreamForErrors("getStreamCmdResponse", respObj);//log them // Try to reload the image stream. + // If it's an auth error, we should reload the whole page. + window.location.reload(); + if ( 0 ) { var streamImg = $('liveStream'+monitorId); if ( streamImg ) { streamImg.src = streamImg.src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); @@ -226,6 +236,7 @@ function getStreamCmdResponse( respObj, respText ) { } else { console.log("Unable to find streamImg liveStream"); } + } } var streamCmdTimeout = statusRefreshTimeout; @@ -356,18 +367,18 @@ if ( monitorType != 'WebSite' ) { var statusCmdParms = "view=request&request=status&entity=monitor&id="+monitorId+"&element[]=Status&element[]=FrameRate"; if ( auth_hash ) statusCmdParms += '&auth='+auth_hash; - var statusCmdReq = new Request.JSON( { url: monitorUrl+thisUrl, method: 'get', data: statusCmdParms, timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getStatusCmdResponse } ); + var statusCmdReq = new Request.JSON( { url: monitorUrl, method: 'get', data: statusCmdParms, timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getStatusCmdResponse } ); var statusCmdTimer = null; } -function getStatusCmdResponse( respObj, respText ) { +function getStatusCmdResponse(respObj, respText) { watchdogOk("status"); if ( statusCmdTimer ) - statusCmdTimer = clearTimeout( statusCmdTimer ); + statusCmdTimer = clearTimeout(statusCmdTimer); if ( respObj.result == 'Ok' ) { - $('fpsValue').set( 'text', respObj.monitor.FrameRate ); - setAlarmState( respObj.monitor.Status ); + $('fpsValue').set('text', respObj.monitor.FrameRate); + setAlarmState(respObj.monitor.Status); } else checkStreamForErrors("getStatusCmdResponse", respObj); @@ -386,8 +397,8 @@ if ( monitorType != 'WebSite' ) { if ( auth_hash ) alarmCmdParms += '&auth='+auth_hash; var alarmCmdReq = new Request.JSON( { - url: monitorUrl+thisUrl, - method: 'post', + url: monitorUrl, + method: 'get', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getAlarmCmdResponse, @@ -401,19 +412,19 @@ function getAlarmCmdResponse( respObj, respText ) { } function cmdDisableAlarms() { - alarmCmdReq.send( alarmCmdParms+"&command=disableAlarms" ); + alarmCmdReq.send(alarmCmdParms+"&command=disableAlarms"); } function cmdEnableAlarms() { - alarmCmdReq.send( alarmCmdParms+"&command=enableAlarms" ); + alarmCmdReq.send(alarmCmdParms+"&command=enableAlarms"); } function cmdForceAlarm() { - alarmCmdReq.send( alarmCmdParms+"&command=forceAlarm" ); + alarmCmdReq.send(alarmCmdParms+"&command=forceAlarm"); } function cmdCancelForcedAlarm() { - alarmCmdReq.send( alarmCmdParms+"&command=cancelForcedAlarm" ); + alarmCmdReq.send(alarmCmdParms+"&command=cancelForcedAlarm"); return false; } @@ -429,7 +440,13 @@ function getActResponse( respObj, respText ) { function deleteEvent( event, eventId ) { var actParms = "view=request&request=event&action=delete&id="+eventId; - var actReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: 3000, data: actParms, onSuccess: getActResponse } ); + var actReq = new Request.JSON( { + url: thisUrl, + method: 'post', + timeout: 3000, + data: actParms, + onSuccess: getActResponse + } ); actReq.send(); event.stop(); } @@ -438,13 +455,21 @@ if ( monitorType != 'WebSite' ) { var eventCmdParms = "view=request&request=status&entity=events&id="+monitorId+"&count="+maxDisplayEvents+"&sort=Id%20desc"; if ( auth_hash ) eventCmdParms += '&auth='+auth_hash; - var eventCmdReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, data: eventCmdParms, link: 'cancel', onSuccess: getEventCmdResponse, onTimeout: eventCmdQuery } ); + var eventCmdReq = new Request.JSON( { + url: monitorUrl, + method: 'get', + timeout: AJAX_TIMEOUT, + data: eventCmdParms, + link: 'cancel', + onSuccess: getEventCmdResponse, + onTimeout: eventCmdQuery + } ); var eventCmdTimer = null; var eventCmdFirst = true; } function highlightRow( row ) { - $(row).toggleClass( 'highlight' ); + $(row).toggleClass('highlight'); } function getEventCmdResponse( respObj, respText ) { @@ -476,11 +501,11 @@ function getEventCmdResponse( respObj, respText ) { var cells = row.getElements( 'td' ); - var link = new Element( 'a', { 'href': '#', 'events': { 'click': createEventPopup.pass( [ event.Id, '&trms=1&attr1=MonitorId&op1=%3d&val1='+monitorId+'&page=1&popup=1', event.Width, event.Height ] ) } }); + var link = new Element( 'a', { 'href': '#', 'events': { 'click': createEventPopup.pass( [ event.Id, '&terms=1&attr1=MonitorId&op1=%3d&val1='+monitorId+'&page=1&popup=1', event.Width, event.Height ] ) } }); link.set( 'text', event.Id ); link.inject( row.getElement( 'td.colId' ) ); - link = new Element( 'a', { 'href': '#', 'events': { 'click': createEventPopup.pass( [ event.Id, '&trms=1&attr1=MonitorId&op1=%3d&val1='+monitorId+'&page=1&popup=1', event.Width, event.Height ] ) } }); + link = new Element( 'a', { 'href': '#', 'events': { 'click': createEventPopup.pass( [ event.Id, '&terms=1&attr1=MonitorId&op1=%3d&val1='+monitorId+'&page=1&popup=1', event.Width, event.Height ] ) } }); link.set( 'text', event.Name ); link.inject( row.getElement( 'td.colName' ) ); @@ -548,7 +573,7 @@ if ( monitorType != 'WebSite' ) { var controlParms = "view=request&request=control&id="+monitorId; if ( auth_hash ) controlParms += '&auth='+auth_hash; - var controlReq = new Request.JSON( { url: thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getControlResponse } ); + var controlReq = new Request.JSON( { url: monitorUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getControlResponse } ); } function getControlResponse( respObj, respText ) { @@ -600,7 +625,7 @@ function controlCmdImage( x, y ) { controlReq.send( imageControlParms+"&x="+x+"&y="+y ); if ( streamMode == "single" ) fetchImage.pass( $('imageFeed').getElement('img') ).delay( 1000 ); -} +} function fetchImage( streamImage ) { streamImage.src = streamImage.src.replace(/rand=\d+/i,'rand='+Math.floor((Math.random() * 1000000) )); @@ -649,7 +674,7 @@ var watchdogFunctions = { //Make sure the various refreshes are still taking effect function watchdogCheck( type ) { if ( watchdogInactive[type] ) { - console.log( "Detected streamWatch of type: " + type + " stopped, restarting" ); + console.log("Detected streamWatch of type: " + type + " stopped, restarting"); watchdogFunctions[type](); watchdogInactive[type] = false; } else { @@ -662,7 +687,7 @@ function watchdogOk( type ) { } function reloadWebSite() { - document.getElementById('imageFeed').innerHTML = document.getElementById('imageFeed').innerHTML; + document.getElementById('imageFeed').innerHTML = document.getElementById('imageFeed').innerHTML; } function initPage() { @@ -683,16 +708,16 @@ function initPage() { if ( !streamImg ) streamImg = $('imageFeed').getElement('object'); if ( streamMode == "single" ) { - streamImg.addEvent( 'click', fetchImage.pass( streamImg ) ); - fetchImage.pass( streamImg ).periodical( imageRefreshTimeout ); + streamImg.addEvent('click', fetchImage.pass(streamImg)); + fetchImage.pass(streamImg).periodical(imageRefreshTimeout); } else - streamImg.addEvent( 'click', function( event ) { handleClick( event ); } ); + streamImg.addEvent('click', function(event) { handleClick(event); }); } if ( refreshApplet && appletRefreshTime ) - appletRefresh.delay( appletRefreshTime*1000 ); - if (scale == "auto") changeScale(); - if (window.history.length == 1) { + appletRefresh.delay(appletRefreshTime*1000); + if ( scale == "auto" ) changeScale(); + if ( window.history.length == 1 ) { $j('#closeControl').html(''); } } else if ( monitorRefresh > 0 ) { @@ -701,4 +726,4 @@ function initPage() { } // Kick everything off -window.addEvent( 'domready', initPage ); +window.addEvent('domready', initPage); diff --git a/web/skins/classic/views/js/watch.js.php b/web/skins/classic/views/js/watch.js.php index c446703cf..51c1801e8 100644 --- a/web/skins/classic/views/js/watch.js.php +++ b/web/skins/classic/views/js/watch.js.php @@ -48,7 +48,7 @@ var maxDisplayEvents = ; var monitorId = Id() ?>; var monitorWidth = Width() ?>; var monitorHeight = Height() ?>; -var monitorUrl = 'Server()->Url() . ( ZM_MIN_STREAMING_PORT ? ':'. (ZM_MIN_STREAMING_PORT+$monitor->Id()) : '' ) ) ?>'; +var monitorUrl = 'Url(); ?>'; var monitorType = 'Type() ) ?>'; var monitorRefresh = 'Refresh() ) ?>'; diff --git a/web/skins/classic/views/js/zone.js b/web/skins/classic/views/js/zone.js index 023671588..bfcd91943 100644 --- a/web/skins/classic/views/js/zone.js +++ b/web/skins/classic/views/js/zone.js @@ -471,7 +471,15 @@ function setAlarmState( currentAlarmState ) { } var streamCmdParms = "view=request&request=stream&connkey="+connKey; -var streamCmdReq = new Request.JSON( { url: monitorUrl+thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getStreamCmdResponse } ); +if ( auth_hash ) + streamCmdParms += '&auth='+auth_hash; +var streamCmdReq = new Request.JSON( { + url: monitorUrl, + method: 'get', + timeout: AJAX_TIMEOUT, + link: 'cancel', + onSuccess: getStreamCmdResponse +} ); var streamCmdTimer = null; var streamStatus; @@ -546,7 +554,16 @@ function streamCmdQuery() { } var statusCmdParms = "view=request&request=status&entity=monitor&id="+monitorId+"&element[]=Status&element[]=FrameRate"; -var statusCmdReq = new Request.JSON( { url: monitorUrl+thisUrl, method: 'post', data: statusCmdParms, timeout: AJAX_TIMEOUT, link: 'cancel', onSuccess: getStatusCmdResponse } ); +if ( auth_hash ) + statusCmdParms += '&auth='+auth_hash; +var statusCmdReq = new Request.JSON( { + url: monitorUrl, + method: 'get', + data: statusCmdParms, + timeout: AJAX_TIMEOUT, + link: 'cancel', + onSuccess: getStatusCmdResponse +} ); var statusCmdTimer = null; function getStatusCmdResponse( respObj, respText ) { @@ -652,11 +669,13 @@ function initPage() { // Imported from watch.js and modified for new zone edit view // + var delay = (Math.random()+0.1)*statusRefreshTimeout; + //console.log("Delay for status updates is: " + delay ); if ( streamMode == "single" ) { - statusCmdTimer = statusCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout ); + statusCmdTimer = statusCmdQuery.delay( delay ); watchdogCheck.pass('status').periodical(statusRefreshTimeout*2); } else { - streamCmdTimer = streamCmdQuery.delay( (Math.random()+0.1)*statusRefreshTimeout ); + streamCmdTimer = streamCmdQuery.delay( delay ); watchdogCheck.pass('stream').periodical(statusRefreshTimeout*2); } diff --git a/web/skins/classic/views/js/zone.js.php b/web/skins/classic/views/js/zone.js.php index 49946f767..434fec679 100644 --- a/web/skins/classic/views/js/zone.js.php +++ b/web/skins/classic/views/js/zone.js.php @@ -101,7 +101,7 @@ var streamMode = ""; var connKey = ''; var monitorId = Id() ?>; -var monitorUrl = 'Server()->Url() ) ?>'; +var monitorUrl = 'Url() ) ?>'; var streamSrc = ""; diff --git a/web/skins/classic/views/js/zones.js b/web/skins/classic/views/js/zones.js index a01f8b56f..9ada175bc 100644 --- a/web/skins/classic/views/js/zones.js +++ b/web/skins/classic/views/js/zones.js @@ -1,5 +1,5 @@ var streamCmdParms = "view=request&request=stream&connkey="+connKey; -var streamCmdReq = new Request.JSON( { url: monitorUrl+thisUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel' } ); +var streamCmdReq = new Request.JSON( { url: monitorUrl, method: 'post', timeout: AJAX_TIMEOUT, link: 'cancel' } ); function streamCmdQuit( action ) { if ( action ) diff --git a/web/skins/classic/views/js/zones.js.php b/web/skins/classic/views/js/zones.js.php index ca234085e..d7144d18a 100644 --- a/web/skins/classic/views/js/zones.js.php +++ b/web/skins/classic/views/js/zones.js.php @@ -1,4 +1,4 @@ var connKey = ''; -var monitorUrl = 'Server()->Url() ) ?>'; +var monitorUrl = 'Url() ) ?>'; var CMD_QUIT = ; diff --git a/web/skins/classic/views/log.php b/web/skins/classic/views/log.php index 4f2650865..d13381e2f 100644 --- a/web/skins/classic/views/log.php +++ b/web/skins/classic/views/log.php @@ -45,17 +45,16 @@ xhtmlHeaders(__FILE__, translate('SystemLog') );
    - - - - - + + + + +
    - - - -
    + + +
    @@ -89,7 +88,7 @@ xhtmlHeaders(__FILE__, translate('SystemLog') ); - +
    @@ -137,8 +136,8 @@ xhtmlHeaders(__FILE__, translate('SystemLog') );
    :
    - - + +
    diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 92d6325bd..c030a151f 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -173,11 +173,11 @@ if ( !empty($_REQUEST['preset']) ) { } } if ( !empty($_REQUEST['probe']) ) { - $probe = unserialize($_REQUEST['probe']); + $probe = json_decode(base64_decode($_REQUEST['probe'])); foreach ( $probe as $name=>$value ) { if ( isset($value) ) { # Does isset handle NULL's? I don't think this code is correct. - $monitor->$name = $value; + $monitor->$name = urldecode($value); } } if ( ZM_HAS_V4L && $monitor->Type() == 'Local' ) { @@ -921,17 +921,31 @@ if ( $monitor->Type() == 'Local' ) { 'Disabled', - 1 => 'X264 Encode', ); - if ($monitor->Type() == 'Ffmpeg' ) - $videowriteropts[2] = 'H264 Camera Passthrough'; + + if (stripos(php_uname('m'), 'arm') === false ) + $videowriteropts[1] = 'X264 Encode'; + else + $videowriteropts[1] = array('text'=>'X264 Encode - Not compatible on Arm','disabled'=>1); + + if ($monitor->Type() == 'Ffmpeg' ) + $videowriteropts[2] = 'H264 Camera Passthrough'; else $videowriteropts[2] = array('text'=>'H264 Camera Passthrough - only for FFMPEG','disabled'=>1); - echo htmlselect( 'newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter() ); + + echo htmlselect( 'newMonitor[VideoWriter]', $videowriteropts, $monitor->VideoWriter() ); ?> - RecordAudio() ) { ?> checked="checked"/> + +Type() == 'Ffmpeg' ) { ?> + RecordAudio() ) { ?> checked="checked"/> + + Audio recording only available with FFMPEG using H264 Passthrough + + + + $_REQUEST['mids'] ) ); +$monitors = Monitor::find(array('Id' => $_REQUEST['mids'])); $monitor = $monitors[0]; -$servers = Server::find_all(); +$servers = Server::find(); $ServersById = array(); foreach ( $servers as $S ) { $ServersById[$S->Id()] = $S; } -$storage_areas = Storage::find_all(); +$storage_areas = Storage::find(); $StorageById = array(); foreach ( $storage_areas as $S ) { $StorageById[$S->Id()] = $S; } - $focusWindow = true; xhtmlHeaders(__FILE__, translate('Function')); @@ -48,7 +47,7 @@ xhtmlHeaders(__FILE__, translate('Function'));
    The following monitors will have these settings update when you click Save:

    - ', array_map( function($m){return $m->Id().' ' .$m->Name();}, $monitors ) ); ?> + ', array_map(function($m){return $m->Id().' ' .$m->Name();}, $monitors)); ?>
    @@ -56,31 +55,31 @@ The following monitors will have these settings update when you click Save:
    Id().'"/>'; - }, $monitors ) + }, $monitors) ); if ( count($ServersById) > 0 ) { ?> -

    -'None')+$ServersById, $monitor->ServerId() ); ?> -

    +

    + 'None')+$ServersById, $monitor->ServerId()); ?> +

    0 ) { + } + if ( count($StorageById) > 0 ) { ?> -

    -'All')+$StorageById, $monitor->StorageId() ); ?> -

    +

    + 'All')+$StorageById, $monitor->StorageId()); ?> +

    Function() ); + $options = array(); + foreach ( getEnumValues('Monitors', 'Function') as $opt ) { + $options[$opt] = translate('Fn'.$opt); + } + echo htmlSelect('newMonitor[Function]', $options, $monitor->Function()); ?>

    @@ -89,7 +88,7 @@ echo htmlSelect( 'newMonitor[Function]', $options, $monitor->Function() );

    - +
    diff --git a/web/skins/classic/views/montage.php b/web/skins/classic/views/montage.php index 902ea1aae..a0ebdce0a 100644 --- a/web/skins/classic/views/montage.php +++ b/web/skins/classic/views/montage.php @@ -70,8 +70,8 @@ session_start(); $layout_id = ''; if ( isset($_COOKIE['zmMontageLayout']) ) { $layout_id = $_SESSION['zmMontageLayout'] = $_COOKIE['zmMontageLayout']; -} elseif ( isset($_SESSION['zmMontageLayout']) ) { - $layout_id = $_SESSION['zmMontageLayout']; +#} elseif ( isset($_SESSION['zmMontageLayout']) ) { + #$layout_id = $_SESSION['zmMontageLayout']; } $options = array(); @@ -87,15 +87,15 @@ if ( $Layout and ( $Layout->Name() != 'Freeform' ) ) { if ( isset($_COOKIE['zmMontageWidth']) and $_COOKIE['zmMontageWidth'] ) { $_SESSION['zmMontageWidth'] = $options['width'] = $_COOKIE['zmMontageWidth']; -} elseif ( isset($_SESSION['zmMontageWidth']) and $_SESSION['zmMontageWidth'] ) { - $options['width'] = $_SESSION['zmMontageWidth']; +#} elseif ( isset($_SESSION['zmMontageWidth']) and $_SESSION['zmMontageWidth'] ) { + #$options['width'] = $_SESSION['zmMontageWidth']; } else $options['width'] = ''; if ( isset($_COOKIE['zmMontageHeight']) and $_COOKIE['zmMontageHeight'] ) $_SESSION['zmMontageHeight'] = $options['height'] = $_COOKIE['zmMontageHeight']; -else if ( isset($_SESSION['zmMontageHeight']) and $_SESSION['zmMontageHeight'] ) - $options['height'] = $_SESSION['zmMontageHeight']; +#else if ( isset($_SESSION['zmMontageHeight']) and $_SESSION['zmMontageHeight'] ) + #$options['height'] = $_SESSION['zmMontageHeight']; else $options['height'] = ''; @@ -134,8 +134,10 @@ xhtmlHeaders(__FILE__, translate('Montage'));
    -