Compare commits
221 Commits
strftime_e
...
1.36.12_he
Author | SHA1 | Date |
---|---|---|
zhiyang7 | 35ca55e4eb | |
Isaac Connor | 3a8c740a69 | |
Isaac Connor | 15b01c2730 | |
pkubaj | a577c3fe79 | |
Isaac Connor | 5fdad04a9e | |
Isaac Connor | a0fd8d64d2 | |
Isaac Connor | bfbba5474f | |
Isaac Connor | b7b49437b9 | |
Isaac Connor | 327f481893 | |
Isaac Connor | 756aa56710 | |
Petter Reinholdtsen | ea0e65d300 | |
Isaac Connor | fb0f184d1d | |
Isaac Connor | 7f8195b248 | |
Isaac Connor | 59e8bca3bc | |
Isaac Connor | 9764875449 | |
Isaac Connor | 0f476998d4 | |
Isaac Connor | afc21cd14d | |
Isaac Connor | 5adf5dab50 | |
Isaac Connor | 7d470fa059 | |
Isaac Connor | 11c12f5d10 | |
Isaac Connor | 209d45c5f0 | |
Isaac Connor | 765886ae72 | |
Isaac Connor | 4030fa8bc4 | |
Isaac Connor | eeb655fd77 | |
Isaac Connor | 4ef5056a91 | |
Isaac Connor | c14d7889a5 | |
Isaac Connor | 48c744caed | |
Isaac Connor | 56fdae0f19 | |
Isaac Connor | 7a181fc082 | |
Isaac Connor | 2e62826e6c | |
Isaac Connor | a0b60aa4f7 | |
Isaac Connor | d8eb0b2350 | |
Isaac Connor | b7d8add5ad | |
Isaac Connor | cb8ccaaa49 | |
Isaac Connor | 498d565034 | |
Isaac Connor | 51e7aa0983 | |
Isaac Connor | f8e6fae013 | |
Isaac Connor | 4b08b0ae84 | |
Isaac Connor | d5f9eb11c6 | |
Isaac Connor | 1b3e0eda13 | |
Isaac Connor | 90e3345440 | |
Isaac Connor | 29fe7f76a2 | |
Isaac Connor | fa533d04ff | |
Isaac Connor | bbf1269e6f | |
Isaac Connor | 601df481ed | |
Isaac Connor | 92bc6ed552 | |
BlueMax | 8a4a530cf3 | |
Isaac Connor | e3d2b14b69 | |
Isaac Connor | 9b723d73a1 | |
Isaac Connor | 00919314e7 | |
Isaac Connor | 55e739d4ea | |
Isaac Connor | dd542d5b30 | |
Isaac Connor | 5d93e9a957 | |
Isaac Connor | 2aa7293326 | |
Isaac Connor | 84b8e43034 | |
Isaac Connor | 15a7c22b94 | |
ColorfullyZhang | d7abdb1505 | |
Isaac Connor | 7fd8efeaef | |
Isaac Connor | 94752e0cf3 | |
Isaac Connor | bd8ed71ffc | |
Isaac Connor | e344141222 | |
Steve Gilvarry | 8061b4f71d | |
Isaac Connor | 198d9c0f5a | |
Isaac Connor | bf7c13558c | |
Isaac Connor | 615ce5d4c2 | |
Andrea Vezzali | 649eec4dae | |
Andrea Vezzali | 0dbcd680df | |
Andrea Vezzali | 64b60613d8 | |
Isaac Connor | 8600a0af87 | |
Isaac Connor | 06f1ab4d46 | |
Isaac Connor | 42ab65f7a0 | |
Isaac Connor | 67504b4f39 | |
Isaac Connor | 7568dd63ed | |
Isaac Connor | 2de36f01db | |
Isaac Connor | 33a067c085 | |
Isaac Connor | 65418abf98 | |
SzymekCRX | 9da9d1840c | |
Isaac Connor | d03365c9e7 | |
Isaac Connor | 48494a22db | |
Isaac Connor | 1f75af7534 | |
Isaac Connor | 0dc9017472 | |
Isaac Connor | ee30c0f05e | |
Isaac Connor | a6795cd026 | |
Isaac Connor | 6e68a35861 | |
Isaac Connor | dd758aacac | |
Isaac Connor | 8893a29b51 | |
Isaac Connor | 1ab58aabc6 | |
Isaac Connor | 4b5bc09c41 | |
Isaac Connor | 22f398dd6f | |
Isaac Connor | d4467dba36 | |
Isaac Connor | c600725a1a | |
Isaac Connor | 7517bdc6ec | |
Isaac Connor | 522f8dd5ba | |
Isaac Connor | a258567c16 | |
Isaac Connor | 501aa3fa4a | |
Isaac Connor | 6245a4df6a | |
Isaac Connor | 5f317b3651 | |
Isaac Connor | 7cd8f3d887 | |
Isaac Connor | 1a2665d8e5 | |
Isaac Connor | b2e1f7ed56 | |
Isaac Connor | b5bb5e67ff | |
Isaac Connor | 1dd19d425a | |
Isaac Connor | ba38e71b29 | |
Isaac Connor | f2e537f177 | |
Isaac Connor | a1a8f4d09b | |
Isaac Connor | f5b54caa61 | |
Isaac Connor | 41d193afe3 | |
Isaac Connor | 7b5ab0adae | |
Isaac Connor | ed901b0235 | |
Isaac Connor | ca1b7ebdc7 | |
Isaac Connor | de5ad5fc61 | |
Isaac Connor | 0a7e0c4781 | |
Isaac Connor | aa44bb0d12 | |
Isaac Connor | ff87856951 | |
Isaac Connor | 1a27ac9ab3 | |
Isaac Connor | d622ae9251 | |
Isaac Connor | 7905f11be6 | |
Isaac Connor | 369e0d74b7 | |
Isaac Connor | cab4c24d06 | |
Mike Dussault | b670bf98e0 | |
Isaac Connor | 2b18dd5978 | |
Isaac Connor | 6b8dc07018 | |
Isaac Connor | bbe90dad6e | |
Isaac Connor | ef74294d32 | |
Isaac Connor | fbe0aa7401 | |
Isaac Connor | 31a6ab2224 | |
Isaac Connor | 4bc522e273 | |
Isaac Connor | f299d57a39 | |
Isaac Connor | 4553592caa | |
Isaac Connor | 77a4601d71 | |
Isaac Connor | 0162ec2c39 | |
Andrea Vezzali | ded2c86858 | |
Isaac Connor | c47a66559a | |
Isaac Connor | 5475b44852 | |
Isaac Connor | b75cc07028 | |
Isaac Connor | 8c92fa5dc3 | |
Isaac Connor | f5b4fbeb89 | |
Isaac Connor | 12fcae7574 | |
Isaac Connor | 6b095a17aa | |
Isaac Connor | ad1db2c960 | |
Isaac Connor | a42786afd1 | |
criadoperez | 4d09e5f93f | |
criadoperez | 8e051ca5f2 | |
Peter Keresztes Schmidt | 9cc4bbbe86 | |
Isaac Connor | 1f5ae94c20 | |
Isaac Connor | 0e24b03785 | |
Isaac Connor | ae90ebf74f | |
Isaac Connor | 09d4f0f9c2 | |
Isaac Connor | 29e8d39c74 | |
Isaac Connor | ae0f7acd56 | |
Isaac Connor | 3e3ce151d8 | |
anon8675309 | 4cbda5e645 | |
anon8675309 | f5272c99b3 | |
anon8675309 | 74641731bb | |
anon8675309 | a03b505324 | |
anon8675309 | 85504a8d01 | |
anon8675309 | 2f32098f46 | |
anon8675309 | 022e9ad014 | |
anon8675309 | 6e32de6a91 | |
anon8675309 | 15ed9bf06d | |
anon8675309 | 3cf48fc51c | |
anon8675309 | ffb0e0dd8c | |
anon8675309 | ddecc3800b | |
anon8675309 | d4c2c99de4 | |
anon8675309 | 456290ca19 | |
Peter Keresztes Schmidt | bc7079a0be | |
gmanproxtreme | 75b16c9084 | |
Peter Keresztes Schmidt | 53cbe524c1 | |
Isaac Connor | b0df3fd2f6 | |
Isaac Connor | 583c259951 | |
Isaac Connor | 8da5a1d2c9 | |
Isaac Connor | 9ebff52c44 | |
Isaac Connor | e9dab48834 | |
Isaac Connor | 4612f4ae75 | |
Isaac Connor | f319d02373 | |
Isaac Connor | e529d8fcd1 | |
Isaac Connor | 55080da9dc | |
Isaac Connor | be46b063c8 | |
r01k | 2d965e7d50 | |
Isaac Connor | 4c213ab453 | |
Isaac Connor | 706e2ff536 | |
Isaac Connor | dab9bce8f4 | |
Isaac Connor | 25f6935280 | |
Isaac Connor | 4c261eb413 | |
Isaac Connor | ca4ec91ef3 | |
Isaac Connor | 30aad6ab9a | |
Isaac Connor | bed79039f3 | |
Isaac Connor | d884d86b38 | |
Isaac Connor | 391fc1fec8 | |
Isaac Connor | a1fe4e2638 | |
Isaac Connor | a2f3583481 | |
Isaac Connor | 809183716a | |
Isaac Connor | 3ca920f1a9 | |
Isaac Connor | 2e09334b9c | |
Isaac Connor | a69882ffaf | |
Isaac Connor | b306f92116 | |
Isaac Connor | ae1e3d88b2 | |
Isaac Connor | cf87f2cc40 | |
Isaac Connor | c089702fab | |
Isaac Connor | 2c7af3886c | |
Isaac Connor | 2f7c44dce2 | |
Isaac Connor | ad9ce720fd | |
Isaac Connor | 50326cf80c | |
Isaac Connor | c280279cf7 | |
Isaac Connor | 6a6d6935e8 | |
Isaac Connor | 814e8559aa | |
Isaac Connor | 8d5207636a | |
Isaac Connor | 2273deaf17 | |
Isaac Connor | 06ff94de2f | |
Steven Gilvarry | 59a03d6d59 | |
Isaac Connor | 17ec2f922e | |
Isaac Connor | 6d9c582e13 | |
Isaac Connor | cb58b70078 | |
Isaac Connor | 431417ea8b | |
Isaac Connor | e0e81a3769 | |
Isaac Connor | 94662dc170 | |
Isaac Connor | 132fc84c31 | |
Isaac Connor | c00651e826 | |
gmanproxtreme | 6a0e27db4c | |
Isaac Connor | 199e86e92a | |
Isaac Connor | 85ade02cba |
|
@ -2,7 +2,7 @@
|
|||
|
||||
github: [connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: zoneminder # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
open_collective: zoneminder # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
|
|
|
@ -9,7 +9,7 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-16.04
|
||||
runs-on: zm-xenial-ci
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
default:
|
||||
image:
|
||||
name: ubuntu:latest
|
||||
before_script:
|
||||
- apt-get update -yq
|
||||
- DEBIAN_FRONTEND=noninteractive apt-get install -yq devscripts sudo
|
||||
|
||||
deb:
|
||||
stage: build
|
||||
tags:
|
||||
- docker
|
||||
script:
|
||||
- yes "" | ./utils/do_debian_package.sh --snapshot=stable --type=binary --interactive=no --dput=no --debbuild-extra=--no-sign || true
|
||||
artifacts:
|
||||
paths:
|
||||
- '*.deb'
|
||||
expire_in: 1 week
|
|
@ -30,7 +30,7 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde
|
|||
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
|
||||
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
|
||||
- Fedora via [RPM Fusion](http://rpmfusion.org)
|
||||
- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE)
|
||||
- OpenSuse via [third party repository](https://wiki.zoneminder.com/Installing_using_ZoneMinder_RPMs_for_SuSE)
|
||||
- Mageia from their default repository
|
||||
- Arch via the [AUR](https://aur.archlinux.org/packages/zoneminder/)
|
||||
- Gentoo via [Portage Overlays](http://gpo.zugaina.org/www-misc/zoneminder)
|
||||
|
|
|
@ -539,7 +539,7 @@ CREATE TABLE `Monitors` (
|
|||
`Longitude` DECIMAL(11,8),
|
||||
`RTSPServer` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
`RTSPStreamName` varchar(255) NOT NULL default '',
|
||||
`Importance` enum('Not','Less','Normal'),
|
||||
`Importance` enum('Normal','Less','Not') NOT NULL default 'Normal',
|
||||
PRIMARY KEY (`Id`)
|
||||
) ENGINE=@ZM_MYSQL_ENGINE@;
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ SET @s = (SELECT IF(
|
|||
AND table_name = 'Monitors'
|
||||
AND column_name = 'TotalEvents'
|
||||
) > 0,
|
||||
"SELECT 'Column TotalEvents is already removed from Monitors'",
|
||||
"ALTER TABLE `Monitors` DROP `TotalEvents`"
|
||||
"ALTER TABLE `Monitors` DROP `TotalEvents`",
|
||||
"SELECT 'Column TotalEvents is already removed from Monitors'"
|
||||
));
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
|
@ -50,8 +50,8 @@ SET @s = (SELECT IF(
|
|||
AND table_name = 'Monitors'
|
||||
AND column_name = 'TotalEventDiskSpace'
|
||||
) > 0,
|
||||
"SELECT 'Column TotalEventDiskSpace is already removed from Monitors'",
|
||||
"ALTER TABLE `Monitors` DROP `TotalEventDiskSpace`"
|
||||
"ALTER TABLE `Monitors` DROP `TotalEventDiskSpace`",
|
||||
"SELECT 'Column TotalEventDiskSpace is already removed from Monitors'"
|
||||
));
|
||||
PREPARE stmt FROM @s;
|
||||
EXECUTE stmt;
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
UPDATE Monitors set Importance = 'Normal' where Importance IS NULL;
|
||||
ALTER TABLE `Monitors` MODIFY `Importance` enum('Normal','Less','Not') NOT NULL default 'Normal';
|
|
@ -36,7 +36,7 @@
|
|||
%global _hardened_build 1
|
||||
|
||||
Name: zoneminder
|
||||
Version: 1.36.5
|
||||
Version: 1.36.12
|
||||
Release: 1%{?dist}
|
||||
Summary: A camera monitoring and analysis tool
|
||||
Group: System Environment/Daemons
|
||||
|
@ -430,6 +430,27 @@ ln -sf %{_sysconfdir}/zm/www/zoneminder.nginx.conf %{_sysconfdir}/zm/www/zonemin
|
|||
%dir %attr(755,nginx,nginx) %{_localstatedir}/log/zoneminder
|
||||
|
||||
%changelog
|
||||
* Fri Dec 10 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.12-1
|
||||
- 1.36.12 release
|
||||
|
||||
* Wed Nov 17 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.11-1
|
||||
- 1.36.11 release
|
||||
|
||||
* Mon Oct 25 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.10-1
|
||||
- 1.36.10 release
|
||||
|
||||
* Tue Oct 19 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.9-1
|
||||
- 1.36.9 release
|
||||
|
||||
* Wed Oct 06 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.8-1
|
||||
- 1.36.8 release
|
||||
|
||||
* Mon Sep 13 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.7-1
|
||||
- 1.36.7 release
|
||||
|
||||
* Wed Sep 08 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.6-1
|
||||
- 1.36.6 release
|
||||
|
||||
* Tue Jun 22 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.5-1
|
||||
- 1.36.5 release
|
||||
|
||||
|
|
|
@ -5,6 +5,12 @@ set +e
|
|||
create_db () {
|
||||
echo "Checking for db"
|
||||
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Cannot talk to database. You will have to create the db manually with something like:";
|
||||
echo "cat /usr/share/zoneminder/db/zm_create.sql | mysql -u root";
|
||||
return;
|
||||
fi
|
||||
|
||||
# test if database if already present...
|
||||
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
|
||||
echo "Creating zm db"
|
||||
|
|
|
@ -225,7 +225,7 @@ change the 3 to a 1
|
|||
|
||||
I can't see more than 6 monitors in montage on my browser
|
||||
---------------------------------------------------------
|
||||
Browsers such a Chrome and Safari only support upto 6 streams from the same domain. To work around that, take a look at the multi-port configuration discussed in the ``MIN_STREAMING_PORT`` configuration in :doc:`/userguide/options/options_network`
|
||||
Browsers such a Chrome and Safari only support up to 6 streams from the same domain. To work around that, take a look at the multi-port configuration discussed in the ``MIN_STREAMING_PORT`` configuration in :doc:`/userguide/options/options_network`
|
||||
|
||||
Why is ZoneMinder using so much CPU?
|
||||
---------------------------------------
|
||||
|
|
|
@ -3,6 +3,50 @@ Debian
|
|||
|
||||
.. contents::
|
||||
|
||||
Easy Way: Debian 11 (Bullseye)
|
||||
------------------------------
|
||||
|
||||
This procedure will guide you through the installation of ZoneMinder on Debian 11 (Bullseye).
|
||||
|
||||
**Step 1:** Setup Sudo (optional but recommended)
|
||||
|
||||
By default Debian does not come with sudo, so you have to install it and configure it manually.
|
||||
This step is optional but recommended and the following instructions assume that you have setup sudo.
|
||||
If you prefer to setup ZoneMinder as root, do it at your own risk and adapt the following instructions accordingly.
|
||||
|
||||
::
|
||||
|
||||
apt install sudo
|
||||
usermod -a -G sudo <username>
|
||||
exit
|
||||
|
||||
Now your terminal session is back under your normal user. You can check that
|
||||
you are now part of the sudo group with the command ``groups``, "sudo" should
|
||||
appear in the list. If not, run ``newgrp sudo`` and check again with ``groups``.
|
||||
|
||||
**Step 2:** Update system and install zoneminder
|
||||
|
||||
Run the following commands.
|
||||
|
||||
::
|
||||
|
||||
sudo apt update
|
||||
sudo apt upgrade
|
||||
sudo apt install mariadb-server
|
||||
sudo apt install zoneminder
|
||||
|
||||
When mariadb is installed for the first time, it doesn't add a password to the root user. Therefore, for security, it is recommended to run ``mysql secure installation``.
|
||||
|
||||
**Step 3:** Setup permissions for zm.conf
|
||||
|
||||
To make sure zoneminder can read the configuration file, run the following command.
|
||||
|
||||
::
|
||||
|
||||
sudo chgrp -c www-data /etc/zm/zm.conf
|
||||
|
||||
Congratulations! You should now be able to access zoneminder at ``http://yourhostname/zm``
|
||||
|
||||
Easy Way: Debian Buster
|
||||
------------------------
|
||||
|
||||
|
@ -56,13 +100,13 @@ Add the following to the /etc/apt/sources.list.d/zoneminder.list file
|
|||
::
|
||||
|
||||
# ZoneMinder repository
|
||||
deb https://zmrepo.zoneminder.com/debian/release-1.34 buster/
|
||||
deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/
|
||||
|
||||
You can do this using:
|
||||
|
||||
.. code-block::
|
||||
::
|
||||
|
||||
echo "deb https://zmrepo.zoneminder.com/debian/release-1.34 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list
|
||||
echo "deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list
|
||||
|
||||
Because ZoneMinder's package repository provides a secure connection through HTTPS, apt must be enabled for HTTPS.
|
||||
::
|
||||
|
@ -158,7 +202,7 @@ You are now ready to go with ZoneMinder. Open a browser and type either ``localh
|
|||
Easy Way: Debian Stretch
|
||||
------------------------
|
||||
|
||||
This procedure will guide you through the installation of ZoneMinder on Debian 9 (Stretch). This section has been tested with ZoneMinder 1.34 on Debian 9.8.
|
||||
This procedure will guide you through the installation of ZoneMinder on Debian 9 (Stretch). This section has been tested with ZoneMinder 1.36 on Debian 9.8.
|
||||
|
||||
**Step 1:** Make sure your system is up to date
|
||||
|
||||
|
@ -204,7 +248,7 @@ Add the following to the bottom of the file
|
|||
::
|
||||
|
||||
# ZoneMinder repository
|
||||
deb https://zmrepo.zoneminder.com/debian/release-1.34 stretch/
|
||||
deb https://zmrepo.zoneminder.com/debian/release-1.36 stretch/
|
||||
|
||||
CTRL+o and <Enter> to save
|
||||
CTRL+x to exit
|
||||
|
|
|
@ -2,7 +2,7 @@ An Easy To Use Docker Image
|
|||
===========================
|
||||
If you are interested in trying out ZoneMinder quickly, user Dan Landon maintains an easy to use docker image for ZoneMinder. With a few simple configuration changes, it also provides complete Event Notification Server and Machine Learning hook support. Please follow instructions in his repostory. He maintains two repositories:
|
||||
|
||||
* If you want to run the latest stable release, please use his `zoneminder repository <https://github.com/dlandon/zoneminder>`__.
|
||||
* If you want to run the latest stable release, please use his `zoneminder machine learning repository <https://github.com/dlandon/zoneminder.machine.learning>`__.
|
||||
* If you want to run the latest zoneminder master, please use his `zoneminder master repository <https://github.com/dlandon/zoneminder.master-docker>`__.
|
||||
|
||||
In both cases, instructions are provided in the repo README files.
|
||||
|
|
|
@ -41,7 +41,7 @@ guide you with a quick search.
|
|||
|
||||
::
|
||||
|
||||
add-apt-repository ppa:iconnor/zoneminder-1.34
|
||||
add-apt-repository ppa:iconnor/zoneminder-1.36
|
||||
|
||||
Update repo and upgrade.
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ Source Path
|
|||
Use this field to enter the full URL of the stream or file your camera supports. This is usually an RTSP url. There are several methods to learn this:
|
||||
|
||||
* Check the documentation that came with your camera
|
||||
* Look for your camera in the hardware compatibilty list in the `hardware compatibility wiki <https://wiki.zoneminder.com/Hardware_Compatibility_List>`__
|
||||
* Look for your camera in the hardware compatibility list in the `hardware compatibility wiki <https://wiki.zoneminder.com/Hardware_Compatibility_List>`__
|
||||
* Try ZoneMinder's new ONVIF probe feature
|
||||
* Download and install the `ONVIF Device Manager <https://sourceforge.net/projects/onvifdm/>`__ onto a Windows machine
|
||||
* Use Google to find third party sites, such as ispy, which document this information
|
||||
|
@ -179,12 +179,12 @@ 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.
|
||||
Records video in individual JPEG frames. Storing JPEG frames requires more storage space than h264 but it allows one 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.
|
||||
* Analysis images only (if available) – video is recorded in individual 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 individual 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.
|
||||
|
|
|
@ -34,7 +34,7 @@ Here is what the filter window looks like
|
|||
* Update used disk space: calculates how much disk space is currently taken by the event and updates the db record.
|
||||
* Create video for all matches: creates a video file of all the events that match
|
||||
* Create video for all matches: ffmpeg will be used to create a video file (mp4) out of all the stored jpgs if using jpeg storage.
|
||||
* Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceeded by replacement arguents. eg: /usr/bin/script.sh %MN% will excecute as /usr/bin/script.sh MonitorName /path/to/event. Please note that urls may contain characters like & that need quoting. So you may need to put quotes around them like /usr/bin/scrupt.sh "%MN%".
|
||||
* Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceded by replacement arguents. eg: /usr/bin/script.sh %MN% will execute as /usr/bin/script.sh MonitorName /path/to/event. Please note that urls may contain characters like & that need quoting. So you may need to put quotes around them like /usr/bin/scrupt.sh "%MN%".
|
||||
* Delete all matches: Deletes all the matched events.
|
||||
* Email details of all matches: Sends an email to the configured address with details about the event.
|
||||
* Copy all matches: copies the event files to another location, specified in the Copy To dropdown. The other location must be setup in the Storage Tab under options.
|
||||
|
|
|
@ -53,7 +53,7 @@ This screen is called the "console" screen in ZoneMinder and shows a summary of
|
|||
* **B**: This brings up a color coded log window that shows various system and component level logs. This window is useful if you are trying to diagnose issues. Refer to :doc:`logging`.
|
||||
* **C**: ZoneMinder allows you to group monitors for logical separation. This option lets you create new groups, associate monitors to them and edit/delete existing groups.
|
||||
* **D**: Filters are a powerful mechanism to perform actions when certain conditions are met. ZoneMinder comes with some preset filters that keep a tab of disk space and others. Many users create their own filters for more advanced actions like sending emails when certain events occur and more. Refer to :doc:`filterevents`.
|
||||
* **E**: The Cycle option allows you to rotate between live views of each cofigured monitor.
|
||||
* **E**: The Cycle option allows you to rotate between live views of each configured monitor.
|
||||
* **F**: The Montage option shows a collage of your monitors. You can customize them including moving them around.
|
||||
* **G**: Montage Review allows you to simultaneously view past events for different monitors. Note that this is a very resource intensive page and its performance will vary based on your system capabilities.
|
||||
* **H**: Audit Events Report is more of a power user feature. This option looks for recording gaps in events and recording issues in mp4 files.
|
||||
|
|
|
@ -101,6 +101,6 @@ FROM_EMAIL - The emails or messages that will be sent to you informing you of ev
|
|||
|
||||
URL - The emails or messages that will be sent to you informing you of events can include a link to the events themselves for easy viewing. If you intend to use this feature then set this option to the url of your installation as it would appear from where you read your email, e.g. ``http://host.your.domain/zm/index.php``.
|
||||
|
||||
SSMTP_MAIL - 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 the ZoneMinder `SSMTP Wiki page <http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder>`__ for setup and configuration help.
|
||||
SSMTP_MAIL - 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 the ZoneMinder `SSMTP Wiki page <https://wiki.zoneminder.com/How_to_get_ssmtp_working_with_Zoneminder>`__ for setup and configuration help.
|
||||
|
||||
SSMTP_PATH - The path to the SSMTP application. If path is not defined. Zoneminder will try to determine the path via shell command. Example path: /usr/sbin/ssmtp.
|
||||
SSMTP_PATH - The path to the SSMTP application. If path is not defined. Zoneminder will try to determine the path via shell command. Example path: /usr/sbin/ssmtp.
|
||||
|
|
|
@ -30,7 +30,7 @@ This screen allows you to configure various permissions on a per user basis. The
|
|||
.. note:: if you are using zmNinja, users are required to have 'View' access to system because multi-server information is only available as part of this permission
|
||||
|
||||
- Bandwidth
|
||||
- Specifies the maximum bandwith that this user can configure (Low, Medium or High)
|
||||
- Specifies the maximum bandwidth that this user can configure (Low, Medium or High)
|
||||
|
||||
- API enabled
|
||||
- Specifies if the ZoneMinder API is enabled for this user (needs to be on, if you are using a mobile app such as zmNinja)
|
||||
|
@ -42,4 +42,4 @@ Here is an example of a restricted user, for example:
|
|||
|
||||
.. image:: images/Options_Users_Example.png
|
||||
|
||||
This user "home" is enabled, can view live streams and events, but only from "DoorBell" and "DeckCamera". This user also cannot control PTZ.
|
||||
This user "home" is enabled, can view live streams and events, but only from "DoorBell" and "DeckCamera". This user also cannot control PTZ.
|
||||
|
|
|
@ -5,10 +5,7 @@ This screen lets you customize several aspects of the web interface of ZoneMinde
|
|||
.. image:: images/Options_web.png
|
||||
|
||||
|
||||
WEB_TITLE -
|
||||
|
||||
.. todo ::
|
||||
not quite sure what this does. Seems to change the "target" name - not sure what effect it is supposed to have.
|
||||
WEB_TITLE - The actual text that is shown on the login screen. It is possible that it also appears in other areas.
|
||||
|
||||
WEB_TITLE_PREFIX - If you have more than one installation of ZoneMinder it can be helpful to display different titles for each one. Changing this option allows you to customise the window titles to include further information to aid identification.
|
||||
|
||||
|
@ -48,4 +45,4 @@ WEB_USE_OBJECT_TAGS - There are two methods of including media content in web pa
|
|||
|
||||
WEB_XFRAME_WARN - When creating a Web Site monitor, if the target web site has X-Frame-Options set to sameorigin in the header, the site will not display in ZoneMinder. This is a design feature in most modern browsers. When this condition occurs, ZoneMinder will write a warning to the log file. To get around this, one can install a browser plugin or extension to ignore X-Frame headers, and then the page will display properly. Once the plugin or extension has ben installed, the end user may choose to turn this warning off
|
||||
|
||||
WEB_FILTER_SOURCE - This option only affects monitors with a source type of Ffmpeg, Libvlc, or WebSite. This setting controls what information is displayed in the Source column on the console. Selecting 'None' will not filter anything. The entire source string will be displayed, which may contain sensitive information. Selecting 'NoCredentials' will strip out usernames and passwords from the string. If there are any port numbers in the string and they are common (80, 554, etc) then those will be removed as well. Selecting 'Hostname' will filter out all information except for the hostname or ip address. When in doubt, stay with the default 'Hostname'. This feature uses the php function 'url_parts' to identify the various pieces of the url. If the url in question is unusual or not standard in some way, then filtering may not produce the desired results.
|
||||
WEB_FILTER_SOURCE - This option only affects monitors with a source type of Ffmpeg, Libvlc, or WebSite. This setting controls what information is displayed in the Source column on the console. Selecting 'None' will not filter anything. The entire source string will be displayed, which may contain sensitive information. Selecting 'NoCredentials' will strip out usernames and passwords from the string. If there are any port numbers in the string and they are common (80, 554, etc) then those will be removed as well. Selecting 'Hostname' will filter out all information except for the hostname or ip address. When in doubt, stay with the default 'Hostname'. This feature uses the php function 'url_parts' to identify the various pieces of the url. If the url in question is unusual or not standard in some way, then filtering may not produce the desired results.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
failregex = ^\s*web_php\[\d+\]\.ERR \[<HOST>\].*includes/auth.php
|
||||
datepattern = ^%%m/%%d/%%y %%H:%%M:%%S(?:\.%%f)
|
|
@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
|
|||
|
||||
=item * Cells
|
||||
|
||||
A “1” denotes a cell where motion is detected and a “0” an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
A "1" denotes a cell where motion is detected and a "0" an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2709,7 +2709,7 @@ Returns a L<ONVIF::Device::Elements::GetNetworkInterfacesResponse|ONVIF::Device:
|
|||
|
||||
=head3 SetNetworkInterfaces
|
||||
|
||||
For interoperability with a client unaware of the IEEE 802.11 extension a device shall retain its IEEE 802.11 configuration if the IEEE 802.11 configuration element isn’t present in the request.
|
||||
For interoperability with a client unaware of the IEEE 802.11 extension a device shall retain its IEEE 802.11 configuration if the IEEE 802.11 configuration element isn't present in the request.
|
||||
|
||||
Returns a L<ONVIF::Device::Elements::SetNetworkInterfacesResponse|ONVIF::Device::Elements::SetNetworkInterfacesResponse> object.
|
||||
|
||||
|
@ -3093,7 +3093,7 @@ Returns a L<ONVIF::Device::Elements::SetRelayOutputStateResponse|ONVIF::Device::
|
|||
|
||||
=head3 SendAuxiliaryCommand
|
||||
|
||||
tt:IRLamp|Auto – Request to configure an IR illuminator attached to the unit so that it automatically turns ON and OFF. A device that indicates auxiliary service capability shall support this command.
|
||||
tt:IRLamp|Auto - Request to configure an IR illuminator attached to the unit so that it automatically turns ON and OFF. A device that indicates auxiliary service capability shall support this command.
|
||||
|
||||
Returns a L<ONVIF::Device::Elements::SendAuxiliaryCommandResponse|ONVIF::Device::Elements::SendAuxiliaryCommandResponse> object.
|
||||
|
||||
|
@ -3288,7 +3288,7 @@ Returns a L<ONVIF::Device::Elements::GetSystemUrisResponse|ONVIF::Device::Elemen
|
|||
|
||||
=head3 StartFirmwareUpgrade
|
||||
|
||||
The value of the Content-Type header in the HTTP POST request shall be “application/octetstream”.
|
||||
The value of the Content-Type header in the HTTP POST request shall be "application/octetstream".
|
||||
|
||||
Returns a L<ONVIF::Device::Elements::StartFirmwareUpgradeResponse|ONVIF::Device::Elements::StartFirmwareUpgradeResponse> object.
|
||||
|
||||
|
@ -3298,7 +3298,7 @@ Returns a L<ONVIF::Device::Elements::StartFirmwareUpgradeResponse|ONVIF::Device:
|
|||
|
||||
=head3 StartSystemRestore
|
||||
|
||||
The value of the Content-Type header in the HTTP POST request shall be “application/octetstream”.
|
||||
The value of the Content-Type header in the HTTP POST request shall be "application/octetstream".
|
||||
|
||||
Returns a L<ONVIF::Device::Elements::StartSystemRestoreResponse|ONVIF::Device::Elements::StartSystemRestoreResponse> object.
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
|
|||
|
||||
=item * Cells
|
||||
|
||||
A “1” denotes a cell where motion is detected and a “0” an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
A "1" denotes a cell where motion is detected and a "0" an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -2147,7 +2147,7 @@ Returns a L<ONVIF::Media::Elements::GetAudioOutputsResponse|ONVIF::Media::Elemen
|
|||
|
||||
=head3 CreateProfile
|
||||
|
||||
This operation creates a new empty media profile. The media profile shall be created in the device and shall be persistent (remain after reboot). A created profile shall be deletable and a device shall set the “fixed” attribute to false in the returned Profile.
|
||||
This operation creates a new empty media profile. The media profile shall be created in the device and shall be persistent (remain after reboot). A created profile shall be deletable and a device shall set the "fixed" attribute to false in the returned Profile.
|
||||
|
||||
Returns a L<ONVIF::Media::Elements::CreateProfileResponse|ONVIF::Media::Elements::CreateProfileResponse> object.
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
|
|||
|
||||
=item * Cells
|
||||
|
||||
A “1” denotes a cell where motion is detected and a “0” an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
A "1" denotes a cell where motion is detected and a "0" an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -987,7 +987,7 @@ Returns a L<ONVIF::PTZ::Elements::GotoHomePositionResponse|ONVIF::PTZ::Elements:
|
|||
|
||||
=head3 SetHomePosition
|
||||
|
||||
Operation to save current position as the home position. The SetHomePosition command returns with a failure if the “home” position is fixed and cannot be overwritten. If the SetHomePosition is successful, it is possible to recall the Home Position with the GotoHomePosition command.
|
||||
Operation to save current position as the home position. The SetHomePosition command returns with a failure if the "home" position is fixed and cannot be overwritten. If the SetHomePosition is successful, it is possible to recall the Home Position with the GotoHomePosition command.
|
||||
|
||||
Returns a L<ONVIF::PTZ::Elements::SetHomePositionResponse|ONVIF::PTZ::Elements::SetHomePositionResponse> object.
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ This attribute is of type L<SOAP::WSDL::XSD::Typelib::Builtin::integer|SOAP::WSD
|
|||
|
||||
=item * Cells
|
||||
|
||||
A “1” denotes a cell where motion is detected and a “0” an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
A "1" denotes a cell where motion is detected and a "0" an empty cell. The first cell is in the upper left corner. Then the cell order goes first from left to right and then from up to down. If the number of cells is not a multiple of 8 the last byte is filled with zeros. The information is run length encoded according to Packbit coding in ISO 12369 (TIFF, Revision 6.0).
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ of the corresponding class can be passed instead of the marked hash ref.
|
|||
You may pass any combination of objects, hash and list refs to these
|
||||
methods, as long as you meet the structure.
|
||||
|
||||
List items (i.e. multiple occurences) are not displayed in the synopsis.
|
||||
List items (i.e. multiple occurrences) are not displayed in the synopsis.
|
||||
You may generally pass a list ref of hash refs (or objects) instead of a hash
|
||||
ref - this may result in invalid XML if used improperly, though. Note that
|
||||
SOAP::WSDL always expects list references at maximum depth position.
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ not checked yet.
|
|||
|
||||
The current implementation of union resorts to inheriting from the base type,
|
||||
which means (quoted from the XML Schema specs): "If the <list> or <union>
|
||||
alternative is chosen, then the simple ur-type definition·."
|
||||
alternative is chosen, then the simple ur-type definition."
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -3766,7 +3766,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 the ZoneMinder [SSMTP Wiki page](http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder)
|
||||
Please visit the ZoneMinder [SSMTP Wiki page](https://wiki.zoneminder.com/How_to_get_ssmtp_working_with_Zoneminder)
|
||||
for setup and configuration help.
|
||||
`,
|
||||
type => $types{boolean},
|
||||
|
|
|
@ -220,14 +220,14 @@ sub moveConUpRight {
|
|||
my $self = shift;
|
||||
Debug('Move Diagonally Up Right');
|
||||
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
|
||||
$$self{LastCmd} = 'code=RightUp&channel=0&arg1=0&arg2=1&arg3=0';
|
||||
$$self{LastCmd} = 'code=RightUp&channel=0&arg1=1&arg2=1&arg3=0';
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
|
||||
}
|
||||
|
||||
sub moveConDownRight {
|
||||
my $self = shift;
|
||||
Debug('Move Diagonally Down Right');
|
||||
$$self{LastCmd} = 'code=RightDown&channel=0&arg1=0&arg2=1&arg3=0';
|
||||
$$self{LastCmd} = 'code=RightDown&channel=0&arg1=1&arg2=1&arg3=0';
|
||||
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ sub moveConUpLeft {
|
|||
my $self = shift;
|
||||
Debug('Move Diagonally Up Left');
|
||||
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
|
||||
$$self{LastCmd} = 'code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0';
|
||||
$$self{LastCmd} = 'code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0';
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
|
||||
}
|
||||
|
||||
|
@ -244,7 +244,7 @@ sub moveConDownLeft {
|
|||
my $self = shift;
|
||||
Debug('Move Diagonally Down Left');
|
||||
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
|
||||
$$self{LastCmd} = 'code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0';
|
||||
$$self{LastCmd} = 'code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0';
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
|
||||
}
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ None by default.
|
|||
=head1 SEE ALSO
|
||||
|
||||
See if there are better instructions for the DCS-5020L at
|
||||
http://www.zoneminder.com/wiki/index.php/Dlink
|
||||
https://wiki.zoneminder.com/Dlink
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ sub presetSet
|
|||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam( $params, 'preset' );
|
||||
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&PresetNumber=1&Preset=0";
|
||||
my $cmd = 'form/presetSet?flag=3&existFlag=1&language=cn&presetNum='.$preset;
|
||||
$self->sendCmd( $cmd );
|
||||
}
|
||||
|
||||
|
@ -294,7 +294,7 @@ sub presetGoto
|
|||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $preset = $self->getParam( $params, 'preset' );
|
||||
my $cmd = "cgi/ptz_set?Channel=1&Group=PTZCtrlInfo&PresetNumber=1&Preset=1";
|
||||
my $cmd = 'form/presetSet?flag=4&existFlag=1&language=cn&presetNum='.$preset;
|
||||
$self->sendCmd( $cmd );
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,7 @@ sub zmDbConnect {
|
|||
.$socket . $sslOptions . ($options?join(';', '', map { $_.'='.$$options{$_} } keys %{$options} ) : '')
|
||||
, $ZoneMinder::Config::Config{ZM_DB_USER}
|
||||
, $ZoneMinder::Config::Config{ZM_DB_PASS}
|
||||
, { mysql_enable_utf8 => 1, }
|
||||
);
|
||||
};
|
||||
if ( !$dbh or $@ ) {
|
||||
|
|
|
@ -584,6 +584,7 @@ sub DiskSpace {
|
|||
return $_[0]{DiskSpace};
|
||||
}
|
||||
|
||||
# Icon: I removed the locking from this. So we now have an assumption that the Event object is up to date.
|
||||
sub CopyTo {
|
||||
my ( $self, $NewStorage ) = @_;
|
||||
|
||||
|
@ -614,16 +615,12 @@ sub CopyTo {
|
|||
Debug("$NewPath is good");
|
||||
}
|
||||
|
||||
$ZoneMinder::Database::dbh->begin_work();
|
||||
$self->lock_and_load();
|
||||
# data is reloaded, so need to check that the move hasn't already happened.
|
||||
if ( $$self{StorageId} == $$NewStorage{Id} ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return 'Event has already been moved by someone else.';
|
||||
}
|
||||
|
||||
if ( $$OldStorage{Id} != $$self{StorageId} ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return 'Old Storage path changed, Event has moved somewhere else.';
|
||||
}
|
||||
|
||||
|
@ -661,39 +658,21 @@ sub CopyTo {
|
|||
}
|
||||
|
||||
my $event_path = $subpath.$self->RelativePath();
|
||||
if ( 0 ) { # Not neccessary
|
||||
Debug("Making directory $event_path/");
|
||||
if ( !$bucket->add_key($event_path.'/', '') ) {
|
||||
Warning("Unable to add key for $event_path/ :". $s3->err . ': '. $s3->errstr());
|
||||
}
|
||||
}
|
||||
|
||||
my @files = glob("$OldPath/*");
|
||||
Debug("Files to move @files");
|
||||
foreach my $file ( @files ) {
|
||||
foreach my $file (@files) {
|
||||
next if $file =~ /^\./;
|
||||
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
|
||||
($file) = ($file =~ /^(.*)$/); # De-taint
|
||||
my $starttime = [gettimeofday];
|
||||
Debug("Moving file $file to $NewPath");
|
||||
my $size = -s $file;
|
||||
if ( ! $size ) {
|
||||
if (!$size) {
|
||||
Info('Not moving file with 0 size');
|
||||
}
|
||||
if ( 0 ) {
|
||||
my $file_contents = File::Slurp::read_file($file);
|
||||
if ( ! $file_contents ) {
|
||||
die 'Loaded empty file, but it had a size. Giving up';
|
||||
}
|
||||
|
||||
my $filename = $event_path.'/'.File::Basename::basename($file);
|
||||
if ( ! $bucket->add_key($filename, $file_contents) ) {
|
||||
die "Unable to add key for $filename : ".$s3->err . ': ' . $s3->errstr;
|
||||
}
|
||||
} else {
|
||||
my $filename = $event_path.'/'.File::Basename::basename($file);
|
||||
if ( ! $bucket->add_key_filename($filename, $file) ) {
|
||||
die "Unable to add key for $filename " . $s3->err . ': '. $s3->errstr;
|
||||
}
|
||||
my $filename = $event_path.'/'.File::Basename::basename($file);
|
||||
if (!$bucket->add_key_filename($filename, $file)) {
|
||||
die "Unable to add key for $filename " . $s3->err . ': '. $s3->errstr;
|
||||
}
|
||||
|
||||
my $duration = tv_interval($starttime);
|
||||
|
@ -704,16 +683,15 @@ sub CopyTo {
|
|||
};
|
||||
Error($@) if $@;
|
||||
} else {
|
||||
Error("Unable to parse S3 Url into it's component parts.");
|
||||
Error('Unable to parse S3 Url into it\'s component parts.');
|
||||
}
|
||||
#die $@ if $@;
|
||||
} # end if Url
|
||||
} # end if s3
|
||||
|
||||
my $error = '';
|
||||
if ( !$moved ) {
|
||||
if (!$moved) {
|
||||
File::Path::make_path($NewPath, {error => \my $err});
|
||||
if ( @$err ) {
|
||||
if (@$err) {
|
||||
for my $diag (@$err) {
|
||||
my ($file, $message) = %$diag;
|
||||
next if $message eq 'File exists';
|
||||
|
@ -724,23 +702,16 @@ sub CopyTo {
|
|||
}
|
||||
}
|
||||
}
|
||||
if ( $error ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return $error;
|
||||
}
|
||||
return $error if $error;
|
||||
my @files = glob("$OldPath/*");
|
||||
if ( ! @files ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return 'No files to move.';
|
||||
}
|
||||
return 'No files to move.' if !@files;
|
||||
|
||||
for my $file (@files) {
|
||||
next if $file =~ /^\./;
|
||||
( $file ) = ( $file =~ /^(.*)$/ ); # De-taint
|
||||
($file) = ($file =~ /^(.*)$/); # De-taint
|
||||
my $starttime = [gettimeofday];
|
||||
Debug("Moving file $file to $NewPath");
|
||||
my $size = -s $file;
|
||||
if ( ! File::Copy::copy( $file, $NewPath ) ) {
|
||||
if (!File::Copy::copy($file, $NewPath)) {
|
||||
$error .= "Copy failed: for $file to $NewPath: $!";
|
||||
last;
|
||||
}
|
||||
|
@ -749,20 +720,21 @@ sub CopyTo {
|
|||
} # end foreach file.
|
||||
} # end if ! moved
|
||||
|
||||
if ( $error ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return $error;
|
||||
}
|
||||
return $error;
|
||||
} # end sub CopyTo
|
||||
|
||||
sub MoveTo {
|
||||
my ( $self, $NewStorage ) = @_;
|
||||
my ($self, $NewStorage) = @_;
|
||||
|
||||
if ( !$self->canEdit() ) {
|
||||
if (!$self->canEdit()) {
|
||||
Warning('No permission to move event.');
|
||||
return 'No permission to move event.';
|
||||
}
|
||||
|
||||
my $was_in_transaction = !$ZoneMinder::Database::dbh->{AutoCommit};
|
||||
$ZoneMinder::Database::dbh->begin_work() if !$was_in_transaction;
|
||||
$self->lock_and_load(); # The fact that we are in a transaction might not imply locking
|
||||
|
||||
my $OldStorage = $self->Storage(undef);
|
||||
|
||||
my $error = $self->CopyTo($NewStorage);
|
||||
|
@ -772,11 +744,11 @@ sub MoveTo {
|
|||
$$self{StorageId} = $$NewStorage{Id};
|
||||
$self->Storage($NewStorage);
|
||||
$error .= $self->save();
|
||||
if ( $error ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
return $error;
|
||||
}
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
|
||||
# Going to leave it to upper layer as to whether we rollback or not
|
||||
$ZoneMinder::Database::dbh->commit() if !$was_in_transaction;
|
||||
return $error if $error;
|
||||
|
||||
$self->delete_files($OldStorage);
|
||||
return $error;
|
||||
} # end sub MoveTo
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder Event_Summary Module
|
||||
# Copyright (C) 2020 ZoneMinder
|
||||
#
|
||||
# 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::Event_Summary;
|
||||
|
||||
use 5.006;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
require ZoneMinder::Base;
|
||||
require ZoneMinder::Object;
|
||||
|
||||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||
use parent qw(ZoneMinder::Object);
|
||||
|
||||
use vars qw/ $table $primary_key %fields $serial %defaults $debug/;
|
||||
$table = 'Event_Summaries';
|
||||
$serial = $primary_key = 'MonitorId';
|
||||
%fields = map { $_ => $_ } qw(
|
||||
MonitorId
|
||||
TotalEvents
|
||||
TotalEventDiskSpace
|
||||
HourEvents
|
||||
HourEventDiskSpace
|
||||
DayEvents
|
||||
DayEventDiskSpace
|
||||
WeekEvents
|
||||
WeekEventDiskSpace
|
||||
MonthEvents
|
||||
MonthEventDiskSpace
|
||||
ArchivedEvents
|
||||
ArchivedEventDiskSpace
|
||||
);
|
||||
|
||||
%defaults = (
|
||||
TotalEvents => undef,
|
||||
TotalEventDiskSpace => undef,
|
||||
HourEvents => undef,
|
||||
HourEventDiskSpace => undef,
|
||||
DayEvents => undef,
|
||||
DayEventDiskSpace => undef,
|
||||
WeekEvents => undef,
|
||||
WeekEventDiskSpace => undef,
|
||||
MonthEvents => undef,
|
||||
MonthEventDiskSpace => undef,
|
||||
ArchivedEvents => undef,
|
||||
ArchivedEventDiskSpace => undef,
|
||||
);
|
||||
|
||||
sub Monitor {
|
||||
return new ZoneMinder::Monitor( $_[0]{MonitorId} );
|
||||
} # end sub Monitor
|
||||
|
||||
1;
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
ZoneMinder::Event_Summary - Perl Class for Event Summaries
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use ZoneMinder::Event_Summary;
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) 2001-2017 ZoneMinder LLC
|
||||
|
||||
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
|
|
@ -127,9 +127,11 @@ sub Execute {
|
|||
foreach my $term ( @{$$self{PostSQLConditions}} ) {
|
||||
if ( $$term{attr} eq 'ExistsInFileSystem' ) {
|
||||
foreach my $row ( @results ) {
|
||||
my $event = new ZoneMinder::Event($row);
|
||||
my $event = new ZoneMinder::Event($$row{Id}, $row);
|
||||
if ( -e $event->Path() ) {
|
||||
push @filtered_events, $row;
|
||||
push @filtered_events, $row if $$term{val} eq 'true';
|
||||
} else {
|
||||
push @filtered_events, $row if $$term{val} eq 'false';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,138 +166,143 @@ sub Sql {
|
|||
if ( exists($term->{obr}) ) {
|
||||
$self->{Sql} .= str_repeat('(', $term->{obr}).' ';
|
||||
}
|
||||
if (!$term->{attr}) {
|
||||
Error("Invalid term in filter $$self{Id}. Empty attr");
|
||||
next;
|
||||
}
|
||||
|
||||
my $value = $term->{val};
|
||||
my @value_list;
|
||||
if ( $term->{attr} ) {
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$term->{op} = 'EXISTS';
|
||||
} elsif ( $term->{attr} =~ /^Monitor/ ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
||||
$self->{Sql} .= 'M.'.$temp_attr_name;
|
||||
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
$self->{Sql} .= 'M.ServerId';
|
||||
} elsif ( $term->{attr} eq 'StorageServerId' ) {
|
||||
$self->{Sql} .= '(SELECT Storage.ServerId FROM Storage WHERE Storage.Id=E.StorageId)';
|
||||
} elsif ( $term->{attr} eq 'FilterServerId' ) {
|
||||
$self->{Sql} .= $Config{ZM_SERVER_ID};
|
||||
# StartTime options
|
||||
} elsif ( $term->{attr} eq 'DateTime' ) {
|
||||
$self->{Sql} .= 'E.StartDateTime';
|
||||
} elsif ( $term->{attr} eq 'Date' ) {
|
||||
$self->{Sql} .= 'to_days( E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'StartDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
|
||||
$self->{Sql} .= 'extract( hour_second from E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'Weekday' or $term->{attr} eq 'StartWeekday' ) {
|
||||
$self->{Sql} .= 'weekday( E.StartDateTime )';
|
||||
|
||||
# EndTIme options
|
||||
} elsif ( $term->{attr} eq 'EndDateTime' ) {
|
||||
$self->{Sql} .= 'E.EndDateTime';
|
||||
} elsif ( $term->{attr} eq 'EndDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'EndTime' ) {
|
||||
$self->{Sql} .= 'extract( hour_second from E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'EndWeekday' ) {
|
||||
$self->{Sql} .= 'weekday( E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'ExistsInFileSystem' ) {
|
||||
push @{$self->{PostSQLConditions}}, $term;
|
||||
$self->{Sql} .= 'TRUE /* ExistsInFileSystem */';
|
||||
} elsif ( $term->{attr} eq 'DiskPercent' ) {
|
||||
$self->{Sql} .= 'zmDiskPercent';
|
||||
$self->{HasDiskPercent} = !undef;
|
||||
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
|
||||
$self->{Sql} .= 'zmDiskBlocks';
|
||||
$self->{HasDiskBlocks} = !undef;
|
||||
} elsif ( $term->{attr} eq 'SystemLoad' ) {
|
||||
$self->{Sql} .= 'zmSystemLoad';
|
||||
$self->{HasSystemLoad} = !undef;
|
||||
} else {
|
||||
$self->{Sql} .= 'E.'.$term->{attr};
|
||||
}
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$term->{op} = 'EXISTS';
|
||||
} elsif ( $term->{attr} =~ /^Monitor/ ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
||||
$self->{Sql} .= 'M.'.$temp_attr_name;
|
||||
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
$self->{Sql} .= 'M.ServerId';
|
||||
} elsif ( $term->{attr} eq 'StorageServerId' ) {
|
||||
$self->{Sql} .= '(SELECT Storage.ServerId FROM Storage WHERE Storage.Id=E.StorageId)';
|
||||
} elsif ( $term->{attr} eq 'FilterServerId' ) {
|
||||
$self->{Sql} .= $Config{ZM_SERVER_ID};
|
||||
# StartTime options
|
||||
} elsif ( $term->{attr} eq 'DateTime' ) {
|
||||
$self->{Sql} .= 'E.StartDateTime';
|
||||
} elsif ( $term->{attr} eq 'Date' ) {
|
||||
$self->{Sql} .= 'to_days( E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'StartDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
|
||||
$self->{Sql} .= 'extract( hour_second from E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'Weekday' or $term->{attr} eq 'StartWeekday' ) {
|
||||
$self->{Sql} .= 'weekday( E.StartDateTime )';
|
||||
|
||||
if ( $term->{attr} eq 'ExistsInFileSystem' ) {
|
||||
# PostCondition, so no further SQL
|
||||
} else {
|
||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
|
||||
# EndTIme options
|
||||
} elsif ( $term->{attr} eq 'EndDateTime' ) {
|
||||
$self->{Sql} .= 'E.EndDateTime';
|
||||
} elsif ( $term->{attr} eq 'EndDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'EndTime' ) {
|
||||
$self->{Sql} .= 'extract( hour_second from E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'EndWeekday' ) {
|
||||
$self->{Sql} .= 'weekday( E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'ExistsInFileSystem' ) {
|
||||
push @{$self->{PostSQLConditions}}, $term;
|
||||
$self->{Sql} .= 'TRUE /* ExistsInFileSystem */';
|
||||
} elsif ( $term->{attr} eq 'DiskPercent' ) {
|
||||
$self->{Sql} .= 'zmDiskPercent';
|
||||
$self->{HasDiskPercent} = !undef;
|
||||
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
|
||||
$self->{Sql} .= 'zmDiskBlocks';
|
||||
$self->{HasDiskBlocks} = !undef;
|
||||
} elsif ( $term->{attr} eq 'SystemLoad' ) {
|
||||
$self->{Sql} .= 'zmSystemLoad';
|
||||
$self->{HasSystemLoad} = !undef;
|
||||
} else {
|
||||
$self->{Sql} .= 'E.'.$term->{attr};
|
||||
}
|
||||
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
|
||||
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
|
||||
$value = "'$temp_value'";
|
||||
} elsif ( $term->{attr} =~ /ServerId/) {
|
||||
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
|
||||
if ( $temp_value eq 'ZM_SERVER_ID' ) {
|
||||
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";
|
||||
# This gets used later, I forget for what
|
||||
$$self{Server} = new ZoneMinder::Server($ZoneMinder::Config::Config{ZM_SERVER_ID});
|
||||
} elsif ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = "'$temp_value'";
|
||||
# This gets used later, I forget for what
|
||||
$$self{Server} = new ZoneMinder::Server($temp_value);
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'StorageId' ) {
|
||||
$value = "'$temp_value'";
|
||||
$$self{Storage} = new ZoneMinder::Storage($temp_value);
|
||||
} elsif ( $term->{attr} eq 'Name'
|
||||
|| $term->{attr} eq 'Cause'
|
||||
|| $term->{attr} eq 'Notes'
|
||||
) {
|
||||
if ( $term->{op} eq 'LIKE'
|
||||
|| $term->{op} eq 'NOT LIKE'
|
||||
) {
|
||||
$temp_value = '%'.$temp_value.'%' if $temp_value !~ /%/;
|
||||
}
|
||||
$value = "'$temp_value'";
|
||||
} elsif ( $term->{attr} eq 'DateTime' or $term->{attr} eq 'StartDateTime' or $term->{attr} eq 'EndDateTime' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "'$value'";
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} elsif ( $temp_value eq 'CURDATE()' or $temp_value eq 'NOW()' ) {
|
||||
$value = 'to_days('.$temp_value.')';
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "to_days( '$value' )";
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' or $term->{attr} eq 'EndTime' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "extract( hour_second from '$value' )";
|
||||
}
|
||||
} else {
|
||||
if ( $term->{attr} eq 'ExistsInFileSystem' ) {
|
||||
# PostCondition, so no further SQL
|
||||
} else {
|
||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||
# Empty value will result in () from split
|
||||
foreach my $temp_value ( $stripped_value ? split( /["'\s]*?,["'\s]*?/, $stripped_value ) : $stripped_value ) {
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
|
||||
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
|
||||
$value = "'$temp_value'";
|
||||
} elsif ( $term->{attr} =~ /ServerId/) {
|
||||
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
|
||||
if ( $temp_value eq 'ZM_SERVER_ID' ) {
|
||||
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";
|
||||
# This gets used later, I forget for what
|
||||
$$self{Server} = new ZoneMinder::Server($ZoneMinder::Config::Config{ZM_SERVER_ID});
|
||||
} elsif ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = "'$temp_value'";
|
||||
# This gets used later, I forget for what
|
||||
$$self{Server} = new ZoneMinder::Server($temp_value);
|
||||
}
|
||||
push @value_list, $value;
|
||||
} # end foreach temp_value
|
||||
} # end if has an attr
|
||||
} elsif ( $term->{attr} eq 'StorageId' ) {
|
||||
# Empty means NULL, otherwise must be an integer
|
||||
$value = $temp_value ne '' ? int($temp_value) : 'NULL';
|
||||
$$self{Storage} = new ZoneMinder::Storage($temp_value);
|
||||
} elsif ( $term->{attr} eq 'Name'
|
||||
|| $term->{attr} eq 'Cause'
|
||||
|| $term->{attr} eq 'Notes'
|
||||
) {
|
||||
if ( $term->{op} eq 'LIKE'
|
||||
|| $term->{op} eq 'NOT LIKE'
|
||||
) {
|
||||
$temp_value = '%'.$temp_value.'%' if $temp_value !~ /%/;
|
||||
}
|
||||
$value = "'$temp_value'";
|
||||
} elsif ( $term->{attr} eq 'DateTime' or $term->{attr} eq 'StartDateTime' or $term->{attr} eq 'EndDateTime' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "'$value'";
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} elsif ( $temp_value eq 'CURDATE()' or $temp_value eq 'NOW()' ) {
|
||||
$value = 'to_days('.$temp_value.')';
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "to_days( '$value' )";
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' or $term->{attr} eq 'EndTime' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "extract( hour_second from '$value' )";
|
||||
}
|
||||
} else {
|
||||
$value = $temp_value;
|
||||
}
|
||||
push @value_list, $value;
|
||||
} # end foreach temp_value
|
||||
|
||||
if ( $term->{op} ) {
|
||||
if ( $term->{op} eq '=~' ) {
|
||||
|
|
|
@ -35,6 +35,7 @@ require ZoneMinder::Storage;
|
|||
require ZoneMinder::Server;
|
||||
require ZoneMinder::Memory;
|
||||
require ZoneMinder::Monitor_Status;
|
||||
require ZoneMinder::Event_Summary;
|
||||
require ZoneMinder::Zone;
|
||||
|
||||
#our @ISA = qw(Exporter ZoneMinder::Base);
|
||||
|
@ -136,8 +137,8 @@ $serial = $primary_key = 'Id';
|
|||
%defaults = (
|
||||
ServerId => 0,
|
||||
StorageId => 0,
|
||||
Type => 'Ffmpeg',
|
||||
Function => 'Mocord',
|
||||
Type => q`'Ffmpeg'`,
|
||||
Function => q`'Mocord'`,
|
||||
Enabled => 1,
|
||||
LinkedMonitors => undef,
|
||||
Device => '',
|
||||
|
@ -166,15 +167,15 @@ $serial = $primary_key = 'Id';
|
|||
VideoWriter => 0,
|
||||
OutputCodec => undef,
|
||||
OutputContainer => undef,
|
||||
EncoderParameters => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
|
||||
EncoderParameters => '',
|
||||
RecordAudio=>0,
|
||||
RTSPDescribe=>0,
|
||||
Brightness => -1,
|
||||
Contrast => -1,
|
||||
Hue => -1,
|
||||
Colour => -1,
|
||||
EventPrefix => 'Event-',
|
||||
LabelFormat => '%N - %d/%m/%y %H:%M:%S',
|
||||
EventPrefix => q`'Event-'`,
|
||||
LabelFormat => '',
|
||||
LabelX => 0,
|
||||
LabelY => 0,
|
||||
LabelSize => 1,
|
||||
|
@ -208,13 +209,13 @@ $serial = $primary_key = 'Id';
|
|||
DefaultRate => 100,
|
||||
DefaultScale => 100,
|
||||
SignalCheckPoints => 0,
|
||||
SignalCheckColour => '#0000BE',
|
||||
WebColour => '#ff0000',
|
||||
SignalCheckColour => q`'#0000BE'`,
|
||||
WebColour => q`'#ff0000'`,
|
||||
Exif => 0,
|
||||
Sequence => undef,
|
||||
ZoneCount => 0,
|
||||
Refresh => undef,
|
||||
DefaultCodec => 'auto',
|
||||
DefaultCodec => q`'auto'`,
|
||||
Latitude => undef,
|
||||
Longitude => undef,
|
||||
);
|
||||
|
@ -266,6 +267,15 @@ sub Status {
|
|||
return $$self{Status};
|
||||
}
|
||||
|
||||
sub Event_Summary {
|
||||
my $self = shift;
|
||||
$$self{Event_Summary} = shift if @_;
|
||||
if ( ! $$self{Event_Summary} ) {
|
||||
$$self{Event_Summary} = ZoneMinder::Event_Summary->find_one(MonitorId=>$$self{Id});
|
||||
}
|
||||
return $$self{Event_Summary};
|
||||
}
|
||||
|
||||
sub connect {
|
||||
my $self = shift;
|
||||
return ZoneMinder::Memory::zmMemVerify($self);
|
||||
|
@ -279,21 +289,37 @@ sub disconnect {
|
|||
sub suspendMotionDetection {
|
||||
my $self = shift;
|
||||
return 0 if ! ZoneMinder::Memory::zmMemVerify($self);
|
||||
while (ZoneMinder::Memory::zmMemRead($self, 'shared_data:active', 1)) {
|
||||
return if $$self{Function} eq 'Nodect' or $$self{Function} eq 'Monitor' or $$self{Function} eq 'None';
|
||||
my $count = 50;
|
||||
while ($count and ZoneMinder::Memory::zmMemRead($self, 'shared_data:active', 1)) {
|
||||
ZoneMinder::Logger::Debug(1, 'Suspending motion detection');
|
||||
ZoneMinder::Memory::zmMonitorSuspend($self);
|
||||
usleep(100000);
|
||||
$count -= 1;
|
||||
}
|
||||
if (!$count) {
|
||||
ZoneMinder::Logger::Error('Unable to suspend motion detection after 5 seconds.');
|
||||
ZoneMinder::Memory::zmMemInvalidate($self); # Close our file handle to the zmc process we are about to end
|
||||
} else {
|
||||
ZoneMinder::Logger::Debug(1, 'shared_data:active='.ZoneMinder::Memory::zmMemRead($self, 'shared_data:active', 1));
|
||||
}
|
||||
ZoneMinder::Logger::Debug(1,ZoneMinder::Memory::zmMemRead($self, 'shared_data:active', 1));
|
||||
}
|
||||
|
||||
sub resumeMotionDetection {
|
||||
my $self = shift;
|
||||
return 0 if ! ZoneMinder::Memory::zmMemVerify($self);
|
||||
#while (zmMemRead($self, 'shared_data:active', 1)) {
|
||||
return if $$self{Function} eq 'Nodect' or $$self{Function} eq 'Monitor' or $$self{Function} eq 'None';
|
||||
my $count = 50;
|
||||
while ($count and !ZoneMinder::Memory::zmMemRead($self, 'shared_data:active', 1)) {
|
||||
ZoneMinder::Logger::Debug(1, 'Resuming motion detection');
|
||||
ZoneMinder::Memory::zmMonitorResume($self);
|
||||
#}
|
||||
ZoneMinder::Memory::zmMonitorResume($self);
|
||||
usleep(100000);
|
||||
$count -= 1;
|
||||
}
|
||||
if (!$count) {
|
||||
ZoneMinder::Logger::Error('Unable to resume motion detection after 5 seconds.');
|
||||
ZoneMinder::Memory::zmMemInvalidate($self); # Close our file handle to the zmc process we are about to end
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,18 +43,6 @@ $serial = $primary_key = 'MonitorId';
|
|||
CaptureFPS
|
||||
AnalysisFPS
|
||||
CaptureBandwidth
|
||||
TotalEvents
|
||||
TotalEventDiskSpace
|
||||
HourEvents
|
||||
HourEventDiskSpace
|
||||
DayEvents
|
||||
DayEventDiskSpace
|
||||
WeekEvents
|
||||
WeekEventDiskSpace
|
||||
MonthEvents
|
||||
MonthEventDiskSpace
|
||||
ArchivedEvents
|
||||
ArchivedEventDiskSpace
|
||||
);
|
||||
|
||||
%defaults = (
|
||||
|
@ -62,18 +50,6 @@ $serial = $primary_key = 'MonitorId';
|
|||
CaptureFPS => undef,
|
||||
AnalysisFPS => undef,
|
||||
CaptureBandwidth => undef,
|
||||
TotalEvents => undef,
|
||||
TotalEventDiskSpace => undef,
|
||||
HourEvents => undef,
|
||||
HourEventDiskSpace => undef,
|
||||
DayEvents => undef,
|
||||
DayEventDiskSpace => undef,
|
||||
WeekEvents => undef,
|
||||
WeekEventDiskSpace => undef,
|
||||
MonthEvents => undef,
|
||||
MonthEventDiskSpace => undef,
|
||||
ArchivedEvents => undef,
|
||||
ArchivedEventDiskSpace => undef,
|
||||
);
|
||||
|
||||
sub Monitor {
|
||||
|
|
|
@ -218,7 +218,7 @@ sub save {
|
|||
my $serial = eval '$'.$type.'::serial';
|
||||
my @identified_by = eval '@'.$type.'::identified_by';
|
||||
|
||||
my $ac = ZoneMinder::Database::start_transaction( $local_dbh );
|
||||
my $ac = ZoneMinder::Database::start_transaction( $local_dbh ) if $local_dbh->{AutoCommit};
|
||||
if ( ! $serial ) {
|
||||
my $insert = $force_insert;
|
||||
my %serial = eval '%'.$type.'::serial';
|
||||
|
@ -234,8 +234,8 @@ $log->debug("No serial") if $debug;
|
|||
if ( ! ( ( $_ = $local_dbh->prepare("DELETE FROM `$table` WHERE $where") ) and $_->execute( @$self{@identified_by} ) ) ) {
|
||||
$where =~ s/\?/\%s/g;
|
||||
$log->error("Error deleting: DELETE FROM $table WHERE " . sprintf($where, map { defined $_ ? $_ : 'undef' } ( @$self{@identified_by}) ).'):' . $local_dbh->errstr);
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $local_dbh->errstr;
|
||||
} elsif ( $debug ) {
|
||||
$log->debug("SQL succesful DELETE FROM $table WHERE $where");
|
||||
|
@ -267,8 +267,8 @@ $log->debug("No serial") if $debug;
|
|||
my $error = $local_dbh->errstr;
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->error('SQL statement execution failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $local_dbh->errstr);
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
|
@ -282,8 +282,8 @@ $log->debug("No serial") if $debug;
|
|||
my $error = $local_dbh->errstr;
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->error('SQL failed: ('.sprintf($command, , map { defined $_ ? $_ : 'undef' } ( @sql{@keys, @$fields{@identified_by}}) ).'):' . $local_dbh->errstr);
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
|
@ -321,8 +321,8 @@ $log->debug("No serial") if $debug;
|
|||
$command =~ s/\?/\%s/g;
|
||||
my $error = $local_dbh->errstr;
|
||||
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}) ).'):' . $error);
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
|
@ -340,8 +340,8 @@ $log->debug("No serial") if $debug;
|
|||
my $error = $local_dbh->errstr;
|
||||
$command =~ s/\?/\%s/g;
|
||||
$log->error('SQL failed: ('.sprintf($command, map { defined $_ ? $_ : 'undef' } ( @sql{@keys}, @sql{@$fields{@identified_by}} ) ).'):' . $error) if $log;
|
||||
$local_dbh->rollback();
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
$local_dbh->rollback() if $ac;
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
return $error;
|
||||
} # end if
|
||||
if ( $debug or DEBUG_ALL ) {
|
||||
|
@ -350,7 +350,7 @@ $log->debug("No serial") if $debug;
|
|||
} # end if
|
||||
} # end if
|
||||
} # end if
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac );
|
||||
ZoneMinder::Database::end_transaction( $local_dbh, $ac ) if $ac;
|
||||
#$self->load();
|
||||
#if ( $$fields{id} ) {
|
||||
#if ( ! $ZoneMinder::Object::cache{$type}{$$self{id}} ) {
|
||||
|
|
|
@ -40,6 +40,10 @@ $serial = $primary_key = 'Id';
|
|||
MonitorId
|
||||
Type
|
||||
Units
|
||||
NumCoords
|
||||
Coords
|
||||
Area
|
||||
AlarmRGB
|
||||
CheckMethod
|
||||
MinPixelThreshold
|
||||
MaxPixelThreshold
|
||||
|
@ -59,9 +63,13 @@ $serial = $primary_key = 'Id';
|
|||
|
||||
%defaults = (
|
||||
Name => '',
|
||||
Type => 'Active',
|
||||
Units => 'Pixels',
|
||||
CheckMethod => 'Blobs',
|
||||
Type => q`'Active'`,
|
||||
Units => q`'Pixels'`,
|
||||
NumCoords => 0,
|
||||
Coords => '',
|
||||
Area => 0,
|
||||
AlarmRGB => 0,
|
||||
CheckMethod => q`'Blobs'`,
|
||||
MinPixelThreshold => undef,
|
||||
MaxPixelThreshold => undef,
|
||||
MinAlarmPixels => undef,
|
||||
|
|
|
@ -61,12 +61,12 @@ GetOptions(
|
|||
'autostop' =>\$options{autostop},
|
||||
) or pod2usage(-exitstatus => -1);
|
||||
|
||||
if ( !$id ) {
|
||||
if (!$id) {
|
||||
print(STDERR "Please give a valid monitor id\n");
|
||||
pod2usage(-exitstatus => -1);
|
||||
}
|
||||
|
||||
( $id ) = $id =~ /^(\w+)$/;
|
||||
($id) = $id =~ /^(\w+)$/;
|
||||
logInit($id?(id=>'zmcontrol_'.$id):());
|
||||
|
||||
my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock';
|
||||
|
@ -76,7 +76,7 @@ socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
|||
|
||||
my $saddr = sockaddr_un($sock_file);
|
||||
|
||||
if ( $options{command} ) {
|
||||
if ($options{command}) {
|
||||
# Have a command, so we are the client, connect to the server and send it.
|
||||
|
||||
my $tries = 10;
|
||||
|
@ -101,18 +101,16 @@ if ( $options{command} ) {
|
|||
Error("Unable to connect to zmcontrol server at $sock_file");
|
||||
}
|
||||
} else {
|
||||
|
||||
# The server isn't there
|
||||
my $monitor = zmDbGetMonitorAndControl($id);
|
||||
if ( !$monitor ) {
|
||||
Fatal("Unable to load control data for monitor $id");
|
||||
}
|
||||
Fatal("Unable to load control data for monitor $id") if !$monitor;
|
||||
|
||||
my $protocol = $monitor->{Protocol};
|
||||
if ( !$protocol ) {
|
||||
if (!$protocol) {
|
||||
Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field');
|
||||
}
|
||||
|
||||
if ( -x $protocol ) {
|
||||
if (-x $protocol) {
|
||||
# Protocol is actually a script!
|
||||
# Holdover from previous versions
|
||||
my $command .= $protocol.' '.$arg_string;
|
||||
|
@ -120,11 +118,11 @@ if ( $options{command} ) {
|
|||
|
||||
my $output = qx($command);
|
||||
my $status = $? >> 8;
|
||||
if ( $status || logDebugging() ) {
|
||||
if ($status || logDebugging()) {
|
||||
chomp($output);
|
||||
Debug("Output: $output");
|
||||
}
|
||||
if ( $status ) {
|
||||
if ($status) {
|
||||
Error("Command '$command' exited with status: $status");
|
||||
exit($status);
|
||||
}
|
||||
|
@ -134,7 +132,7 @@ if ( $options{command} ) {
|
|||
Info("Starting control server $id/$protocol");
|
||||
close(CLIENT);
|
||||
|
||||
if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) {
|
||||
if (!can_load(modules => {'ZoneMinder::Control::'.$protocol => undef})) {
|
||||
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
|
||||
}
|
||||
|
||||
|
@ -159,7 +157,7 @@ if ( $options{command} ) {
|
|||
$control->open();
|
||||
|
||||
# If we have a command when starting up, then do it.
|
||||
if ( $options{command} ) {
|
||||
if ($options{command}) {
|
||||
my $command = $options{command};
|
||||
$control->$command(\%options);
|
||||
}
|
||||
|
|
|
@ -429,10 +429,20 @@ sub start {
|
|||
# It's not running, or at least it's not been started by us
|
||||
$process = { daemon=>$daemon, args=>\@args, command=>$command, keepalive=>!undef };
|
||||
} elsif ( $process->{pid} && $pid_hash{$process->{pid}} ) {
|
||||
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
|
||||
if ($process->{term_sent_at}) {
|
||||
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' was told to term at "
|
||||
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{term_sent_at}))
|
||||
.", pid = $process->{pid}\n"
|
||||
);
|
||||
$process->{keepalive} = !undef;
|
||||
$process->{delay} = 0;
|
||||
delete $terminating_processes{$command};
|
||||
} else {
|
||||
dPrint(ZoneMinder::Logger::INFO, "'$process->{command}' already running at "
|
||||
.strftime('%y/%m/%d %H:%M:%S', localtime($process->{started}))
|
||||
.", pid = $process->{pid}\n"
|
||||
);
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -523,7 +533,7 @@ sub send_stop {
|
|||
."\n"
|
||||
);
|
||||
sigprocmask(SIG_UNBLOCK, $blockset) or die "dying at unblock...\n";
|
||||
return();
|
||||
return ();
|
||||
}
|
||||
|
||||
my $pid = $process->{pid};
|
||||
|
@ -586,7 +596,7 @@ sub check_for_processes_to_kill {
|
|||
|
||||
sub stop {
|
||||
my ( $daemon, @args ) = @_;
|
||||
my $command = join(' ', $daemon, @args );
|
||||
my $command = join(' ', $daemon, @args);
|
||||
my $process = $cmd_hash{$command};
|
||||
if ( !$process ) {
|
||||
dPrint(ZoneMinder::Logger::WARNING, "Can't find process with command of '$command'");
|
||||
|
|
|
@ -358,25 +358,27 @@ sub checkFilter {
|
|||
}
|
||||
} # end if AutoDelete
|
||||
|
||||
if ( $filter->{AutoMove} ) {
|
||||
my $NewStorage = new ZoneMinder::Storage($filter->{AutoMoveTo});
|
||||
Info("Moving event $Event->{Id} to datastore $filter->{AutoMoveTo}");
|
||||
$_ = $Event->MoveTo($NewStorage);
|
||||
Error($_) if $_;
|
||||
if ($filter->{AutoMove}) {
|
||||
my $NewStorage = ZoneMinder::Storage->find_one(Id=>$filter->{AutoMoveTo});
|
||||
if ($NewStorage) {
|
||||
Info("Moving event $Event->{Id} to datastore $filter->{AutoMoveTo}");
|
||||
$_ = $Event->MoveTo($NewStorage);
|
||||
Error($_) if $_;
|
||||
} else {
|
||||
Error("No storage area found for move to operation. AutoMoveTo was $$filter{AutoMoveTo}");
|
||||
}
|
||||
}
|
||||
if ( $filter->{AutoCopy} ) {
|
||||
if ($filter->{AutoCopy}) {
|
||||
# Copy To is different from MoveTo in that it JUST copies the files
|
||||
# So we still need to update the Event object with the new SecondaryStorageId
|
||||
my $NewStorage = ZoneMinder::Storage->find_one(Id=>$filter->{AutoCopyTo});
|
||||
if ( $NewStorage ) {
|
||||
Info("Copying event $Event->{Id} to datastore $filter->{AutoCopyTo}");
|
||||
$_ = $Event->CopyTo($NewStorage);
|
||||
if ( $_ ) {
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
if ($_) {
|
||||
Error($_);
|
||||
} else {
|
||||
$Event->save({SecondaryStorageId=>$$NewStorage{Id}});
|
||||
$ZoneMinder::Database::dbh->commit();
|
||||
}
|
||||
} else {
|
||||
Error("No storage area found for copy to operation. AutoCopyTo was $$filter{AutoCopyTo}");
|
||||
|
@ -399,7 +401,6 @@ sub checkFilter {
|
|||
) {
|
||||
$Event->save();
|
||||
}
|
||||
$ZoneMinder::Database::dbh->commit() if !$$filter{LockRows};
|
||||
} # end if UpdateDiskSpace
|
||||
} # end foreach event
|
||||
ZoneMinder::Database::end_transaction($dbh, $in_transaction) if $$filter{LockRows};
|
||||
|
@ -666,10 +667,10 @@ sub substituteTags {
|
|||
# We have a filter and an event, do we need any more
|
||||
# monitor information?
|
||||
my $need_monitor = $text =~ /%(?:MN|MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||
my $need_status = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||
my $need_summary = $text =~ /%(?:MET|MEH|MED|MEW|MEN|MEA)%/;
|
||||
|
||||
my $Monitor = $Event->Monitor() if $need_monitor;
|
||||
my $Status = $Monitor->Status() if $need_status;
|
||||
my $Summary = $Monitor->Event_Summary() if $need_summary;
|
||||
|
||||
# Do we need the image information too?
|
||||
my $need_images = $text =~ /%(?:EPI1|EPIM|EI1|EIM|EI1A|EIMA|EIMOD|EIMODG)%/;
|
||||
|
@ -693,19 +694,19 @@ sub substituteTags {
|
|||
}
|
||||
$rows ++;
|
||||
}
|
||||
Debug("Frames: rows: $rows first alarm frame: $first_alarm_frame max_alaarm_frame: $max_alarm_frame, score: $max_alarm_score");
|
||||
Debug("Frames: rows: $rows first alarm frame: $first_alarm_frame max_alarm_frame: $max_alarm_frame, score: $max_alarm_score");
|
||||
$sth->finish();
|
||||
}
|
||||
|
||||
my $url = $Config{ZM_URL};
|
||||
$text =~ s/%ZP%/$url/g;
|
||||
$text =~ s/%MN%/$Monitor->{Name}/g;
|
||||
$text =~ s/%MET%/$Status->{TotalEvents}/g;
|
||||
$text =~ s/%MEH%/$Status->{HourEvents}/g;
|
||||
$text =~ s/%MED%/$Status->{DayEvents}/g;
|
||||
$text =~ s/%MEW%/$Status->{WeekEvents}/g;
|
||||
$text =~ s/%MEM%/$Status->{MonthEvents}/g;
|
||||
$text =~ s/%MEA%/$Status->{ArchivedEvents}/g;
|
||||
$text =~ s/%MET%/$Summary->{TotalEvents}/g;
|
||||
$text =~ s/%MEH%/$Summary->{HourEvents}/g;
|
||||
$text =~ s/%MED%/$Summary->{DayEvents}/g;
|
||||
$text =~ s/%MEW%/$Summary->{WeekEvents}/g;
|
||||
$text =~ s/%MEM%/$Summary->{MonthEvents}/g;
|
||||
$text =~ s/%MEA%/$Summary->{ArchivedEvents}/g;
|
||||
$text =~ s/%MP%/$url?view=watch&mid=$Event->{MonitorId}/g;
|
||||
$text =~ s/%MPS%/$url?view=watch&mid=$Event->{MonitorId}&mode=stream/g;
|
||||
$text =~ s/%MPI%/$url?view=watch&mid=$Event->{MonitorId}&mode=still/g;
|
||||
|
@ -843,7 +844,7 @@ sub sendEmail {
|
|||
return 0;
|
||||
}
|
||||
|
||||
Info('Creating notification email');
|
||||
Debug('Creating notification email');
|
||||
|
||||
my $subject = substituteTags($$filter{EmailSubject}, $filter, $Event);
|
||||
return 0 if !$subject;
|
||||
|
@ -851,7 +852,7 @@ sub sendEmail {
|
|||
my $body = substituteTags($$filter{EmailBody}, $filter, $Event, \@attachments);
|
||||
return 0 if !$body;
|
||||
|
||||
Info("Sending notification email '$subject'");
|
||||
Debug("Sending notification email '$subject'");
|
||||
|
||||
eval {
|
||||
if ( $Config{ZM_NEW_MAIL_MODULES} ) {
|
||||
|
@ -864,7 +865,7 @@ sub sendEmail {
|
|||
);
|
||||
### Add the text message part
|
||||
$mail->attach (
|
||||
Type => 'TEXT',
|
||||
Type => (($body=~/<html/)?'text/html':'text/plain'),
|
||||
Data => $body
|
||||
);
|
||||
### Add the attachments
|
||||
|
@ -886,9 +887,7 @@ sub sendEmail {
|
|||
if ( $Config{ZM_SSMTP_MAIL} ) {
|
||||
my $ssmtp_location = $Config{ZM_SSMTP_PATH};
|
||||
if ( !$ssmtp_location ) {
|
||||
if ( logDebugging() ) {
|
||||
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
|
||||
}
|
||||
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
|
||||
$ssmtp_location = qx('which ssmtp');
|
||||
}
|
||||
if ( !$ssmtp_location ) {
|
||||
|
@ -916,7 +915,7 @@ sub sendEmail {
|
|||
foreach my $attachment ( @attachments ) {
|
||||
my $size = -s $attachment->{path};
|
||||
$total_size += $size;
|
||||
Info("Attaching '$attachment->{path}' which is $size bytes");
|
||||
Debug("Attaching '$attachment->{path}' which is $size bytes");
|
||||
|
||||
$mail->attach(
|
||||
Path => $attachment->{path},
|
||||
|
@ -934,7 +933,7 @@ sub sendEmail {
|
|||
Error("Unable to send email: $@");
|
||||
return 0;
|
||||
} else {
|
||||
Info('Notification email sent');
|
||||
Info("Notification email sent to $$filter{EmailTo}");
|
||||
}
|
||||
my $sql = 'UPDATE `Events` SET `Emailed` = 1 WHERE `Id` = ?';
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
|
|
|
@ -27,7 +27,7 @@ zmupdate.pl - check and upgrade ZoneMinder database
|
|||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
zmupdate.pl -c,--check | -f,--freshen | -v<version>,--version=<version> [-u<dbuser> -p<dbpass>]
|
||||
zmupdate.pl -c,--check | -f,--freshen | -v<version>,--version=<version> [-u <dbuser> -p <dbpass>]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
|
|
|
@ -98,19 +98,19 @@ while (!$zm_terminate) {
|
|||
next if $monitor->{Type} eq 'WebSite';
|
||||
my $now = time();
|
||||
my $restart = 0;
|
||||
if ( zmMemVerify($monitor) ) {
|
||||
if (zmMemVerify($monitor)) {
|
||||
# Check we have got an image recently
|
||||
my $capture_time = zmGetLastWriteTime($monitor);
|
||||
if ( !defined($capture_time) ) {
|
||||
if (!defined($capture_time)) {
|
||||
# Can't read from shared data
|
||||
Debug('LastWriteTime is not defined.');
|
||||
zmMemInvalidate($monitor);
|
||||
next;
|
||||
}
|
||||
Debug("Monitor $$monitor{Id} LastWriteTime is $capture_time.");
|
||||
if ( !$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}) {
|
||||
Warning(
|
||||
"Restarting capture daemon for $$monitor{Name}, no image since startup. ".
|
||||
"Startup time was $startup_time - now $now > $Config{ZM_WATCH_MAX_DELAY}"
|
||||
|
@ -122,7 +122,7 @@ while (!$zm_terminate) {
|
|||
next;
|
||||
}
|
||||
}
|
||||
if ( ! $restart ) {
|
||||
if (!$restart) {
|
||||
my $max_image_delay = (
|
||||
$monitor->{MaxFPS}
|
||||
&&($monitor->{MaxFPS}>0)
|
||||
|
@ -144,29 +144,28 @@ while (!$zm_terminate) {
|
|||
$restart = 1;
|
||||
}
|
||||
|
||||
if ( $restart ) {
|
||||
if ($restart) {
|
||||
my $command;
|
||||
if ( $monitor->{Type} eq 'Local' ) {
|
||||
$command = "zmdc.pl restart zmc -d $monitor->{Device}";
|
||||
if ($monitor->{Type} eq 'Local') {
|
||||
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
|
||||
} else {
|
||||
$command = "zmdc.pl restart zmc -m $monitor->{Id}";
|
||||
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
|
||||
}
|
||||
runCommand($command);
|
||||
} elsif ( $monitor->{Function} ne 'Monitor' ) {
|
||||
} elsif ($monitor->{Function} ne 'Monitor') {
|
||||
# Now check analysis daemon
|
||||
$restart = 0;
|
||||
# Check we have got an image recently
|
||||
my $image_time = zmGetLastReadTime($monitor);
|
||||
if ( !defined($image_time) ) {
|
||||
if (!defined($image_time)) {
|
||||
# Can't read from shared data
|
||||
$restart = 1;
|
||||
Error("Error reading shared data for $$monitor{Id} $$monitor{Name}");
|
||||
} elsif ( !$image_time ) {
|
||||
} elsif (!$image_time) {
|
||||
# We can't get the last capture time so can't be sure it's died.
|
||||
#$restart = 1;
|
||||
Error("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.");
|
||||
} else {
|
||||
|
||||
my $max_image_delay = ( $monitor->{MaxFPS}
|
||||
&&($monitor->{MaxFPS}>0)
|
||||
&&($monitor->{MaxFPS}<1)
|
||||
|
@ -175,7 +174,7 @@ while (!$zm_terminate) {
|
|||
;
|
||||
my $image_delay = $now-$image_time;
|
||||
Debug("Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay");
|
||||
if ( $image_delay > $max_image_delay ) {
|
||||
if ($image_delay > $max_image_delay) {
|
||||
Warning("Analysis daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
|
||||
." time since last analysis $image_delay seconds ($now-$image_time)"
|
||||
);
|
||||
|
@ -183,13 +182,13 @@ while (!$zm_terminate) {
|
|||
}
|
||||
}
|
||||
|
||||
if ( $restart ) {
|
||||
Info("Restarting analysis daemon for $$monitor{Id} $$monitor{Name}\n");
|
||||
if ($restart) {
|
||||
Info("Restarting analysis daemon for $$monitor{Id} $$monitor{Name}");
|
||||
my $command;
|
||||
if ( $monitor->{Type} eq 'Local' ) {
|
||||
$command = "zmdc.pl restart zmc -d $monitor->{Device}";
|
||||
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
|
||||
} else {
|
||||
$command = "zmdc.pl restart zmc -m $monitor->{Id}";
|
||||
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
|
||||
}
|
||||
runCommand($command);
|
||||
} # end if restart
|
||||
|
@ -201,7 +200,7 @@ while (!$zm_terminate) {
|
|||
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
||||
} # end while (!$zm_terminate)
|
||||
|
||||
Info("Watchdog exiting");
|
||||
Info('Watchdog exiting');
|
||||
exit();
|
||||
|
||||
1;
|
||||
|
|
|
@ -642,6 +642,7 @@ bool EventStream::checkEventLoaded() {
|
|||
else
|
||||
curr_frame_id = 1;
|
||||
Debug(2, "New frame id = %ld", curr_frame_id);
|
||||
gettimeofday(&start, nullptr);
|
||||
return true;
|
||||
} else {
|
||||
Debug(2, "No next event loaded using %s. Pausing", sql.c_str());
|
||||
|
@ -927,11 +928,13 @@ void EventStream::runStream() {
|
|||
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
||||
Debug(3, "frame delta %uus ", delta_us);
|
||||
// if effective > base we should speed up frame delivery
|
||||
delta_us = (unsigned int)((delta_us * base_fps)/effective_fps);
|
||||
Debug(3, "delta %u = base_fps(%f)/effective fps(%f)", delta_us, base_fps, effective_fps);
|
||||
// but must not exceed maxfps
|
||||
delta_us = std::max(delta_us, int(1000000/maxfps));
|
||||
Debug(3, "delta %u = base_fps(%f)/effective fps(%f) from 30fps", delta_us, base_fps, effective_fps);
|
||||
if (base_fps < effective_fps) {
|
||||
delta_us = (unsigned int)((delta_us * base_fps)/effective_fps);
|
||||
Debug(3, "delta %u = base_fps(%f)/effective fps(%f)", delta_us, base_fps, effective_fps);
|
||||
// but must not exceed maxfps
|
||||
delta_us = std::max(delta_us, int(1000000/maxfps));
|
||||
Debug(3, "delta %u = base_fps(%f)/effective fps(%f) from 30fps", delta_us, base_fps, effective_fps);
|
||||
}
|
||||
|
||||
// +/- 1? What if we are skipping frames?
|
||||
curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod;
|
||||
|
|
|
@ -321,6 +321,7 @@ bool Image::Assign(const AVFrame *frame, SwsContext *convert_context, AVFrame *t
|
|||
return false;
|
||||
}
|
||||
zm_dump_video_frame(temp_frame, "dest frame after convert");
|
||||
update_function_pointers();
|
||||
return true;
|
||||
} // end Image::Assign(const AVFrame *frame, SwsContext *convert_context, AVFrame *temp_frame)
|
||||
|
||||
|
@ -695,6 +696,7 @@ void Image::AssignDirect(
|
|||
subpixelorder = p_subpixelorder;
|
||||
pixels = width * height;
|
||||
size = new_buffer_size;
|
||||
update_function_pointers();
|
||||
} // end void Image::AssignDirect
|
||||
|
||||
void Image::Assign(
|
||||
|
@ -796,6 +798,7 @@ void Image::Assign(const Image &image) {
|
|||
linesize = image.linesize;
|
||||
}
|
||||
|
||||
update_function_pointers();
|
||||
if ( image.buffer != buffer )
|
||||
(*fptr_imgbufcpy)(buffer, image.buffer, size);
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ class Image {
|
|||
|
||||
/* 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[(y*linesize)+x]; }
|
||||
inline const uint8_t* Buffer(unsigned int x, unsigned int y=0) const { return &buffer[(y*linesize) + x*colours]; }
|
||||
/* 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);
|
||||
// Is only acceptable on a pre-allocated buffer
|
||||
|
|
|
@ -23,7 +23,7 @@ void bind_libvnc_symbols() {
|
|||
|
||||
libvnc_lib = dlopen("libvncclient.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||
if (!libvnc_lib) {
|
||||
Error("Error loading libvncclient: %s", dlerror());
|
||||
Error("Error loading libvncclient.so: %s", dlerror());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -135,11 +135,6 @@ VncCamera::VncCamera(
|
|||
}
|
||||
|
||||
VncCamera::~VncCamera() {
|
||||
if (capture and mRfb) {
|
||||
if (mRfb->frameBuffer)
|
||||
free(mRfb->frameBuffer);
|
||||
(*rfbClientCleanup_f)(mRfb);
|
||||
}
|
||||
if (libvnc_lib) {
|
||||
dlclose(libvnc_lib);
|
||||
libvnc_lib = nullptr;
|
||||
|
@ -253,6 +248,12 @@ int VncCamera::PostCapture() {
|
|||
}
|
||||
|
||||
int VncCamera::Close() {
|
||||
if (capture and mRfb) {
|
||||
if (mRfb->frameBuffer)
|
||||
free(mRfb->frameBuffer);
|
||||
(*rfbClientCleanup_f)(mRfb);
|
||||
mRfb = nullptr;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if ZM_HAS_V4L
|
||||
|
||||
|
|
|
@ -89,9 +89,10 @@ std::string load_monitor_sql =
|
|||
"`SectionLength`, `MinSectionLength`, `FrameSkip`, `MotionFrameSkip`, "
|
||||
"`FPSReportInterval`, `RefBlendPerc`, `AlarmRefBlendPerc`, `TrackMotion`, `Exif`,"
|
||||
"`RTSPServer`, `RTSPStreamName`,"
|
||||
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-2 FROM `Monitors`";
|
||||
"`SignalCheckPoints`, `SignalCheckColour`, `Importance`-1 FROM `Monitors`";
|
||||
|
||||
std::string CameraType_Strings[] = {
|
||||
"Unknown",
|
||||
"Local",
|
||||
"Remote",
|
||||
"File",
|
||||
|
@ -99,10 +100,21 @@ std::string CameraType_Strings[] = {
|
|||
"LibVLC",
|
||||
"NVSOCKET",
|
||||
"CURL",
|
||||
"VNC",
|
||||
"VNC"
|
||||
};
|
||||
|
||||
std::string Function_Strings[] = {
|
||||
"Unknown",
|
||||
"None",
|
||||
"Monitor",
|
||||
"Modect",
|
||||
"Record",
|
||||
"Mocord",
|
||||
"Nodect"
|
||||
};
|
||||
|
||||
std::string State_Strings[] = {
|
||||
"Unknown",
|
||||
"IDLE",
|
||||
"PREALARM",
|
||||
"ALARM",
|
||||
|
@ -151,12 +163,12 @@ bool Monitor::MonitorLink::connect() {
|
|||
Debug(1, "link.mem.size=%jd", mem_size);
|
||||
#if ZM_MEM_MAPPED
|
||||
map_fd = open(mem_file.c_str(), O_RDWR, (mode_t)0600);
|
||||
if ( map_fd < 0 ) {
|
||||
if (map_fd < 0) {
|
||||
Debug(3, "Can't open linked memory map file %s: %s", mem_file.c_str(), strerror(errno));
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
while ( map_fd <= 2 ) {
|
||||
while (map_fd <= 2) {
|
||||
int new_map_fd = dup(map_fd);
|
||||
Warning("Got one of the stdio fds for our mmap handle. map_fd was %d, new one is %d", map_fd, new_map_fd);
|
||||
close(map_fd);
|
||||
|
@ -164,31 +176,31 @@ bool Monitor::MonitorLink::connect() {
|
|||
}
|
||||
|
||||
struct stat map_stat;
|
||||
if ( fstat(map_fd, &map_stat) < 0 ) {
|
||||
if (fstat(map_fd, &map_stat) < 0) {
|
||||
Error("Can't stat linked memory map file %s: %s", mem_file.c_str(), strerror(errno));
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( map_stat.st_size == 0 ) {
|
||||
if (map_stat.st_size == 0) {
|
||||
Error("Linked memory map file %s is empty: %s", mem_file.c_str(), strerror(errno));
|
||||
disconnect();
|
||||
return false;
|
||||
} else if ( map_stat.st_size < mem_size ) {
|
||||
} else if (map_stat.st_size < mem_size) {
|
||||
Error("Got unexpected memory map file size %ld, expected %jd", map_stat.st_size, mem_size);
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
mem_ptr = (unsigned char *)mmap(nullptr, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0);
|
||||
if ( mem_ptr == MAP_FAILED ) {
|
||||
if (mem_ptr == MAP_FAILED) {
|
||||
Error("Can't map file %s (%jd bytes) to memory: %s", mem_file.c_str(), mem_size, strerror(errno));
|
||||
disconnect();
|
||||
return false;
|
||||
}
|
||||
#else // ZM_MEM_MAPPED
|
||||
shm_id = shmget((config.shm_key&0xffff0000)|id, mem_size, 0700);
|
||||
if ( shm_id < 0 ) {
|
||||
if (shm_id < 0) {
|
||||
Debug(3, "Can't shmget link memory: %s", strerror(errno));
|
||||
connected = false;
|
||||
return false;
|
||||
|
@ -204,7 +216,7 @@ bool Monitor::MonitorLink::connect() {
|
|||
shared_data = (SharedData *)mem_ptr;
|
||||
trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData));
|
||||
|
||||
if ( !shared_data->valid ) {
|
||||
if (!shared_data->valid) {
|
||||
Debug(3, "Linked memory not initialised by capture daemon");
|
||||
disconnect();
|
||||
return false;
|
||||
|
@ -220,23 +232,23 @@ bool Monitor::MonitorLink::connect() {
|
|||
} // end bool Monitor::MonitorLink::connect()
|
||||
|
||||
bool Monitor::MonitorLink::disconnect() {
|
||||
if ( connected ) {
|
||||
if (connected) {
|
||||
connected = false;
|
||||
|
||||
#if ZM_MEM_MAPPED
|
||||
if ( mem_ptr > (void *)0 ) {
|
||||
msync( mem_ptr, mem_size, MS_ASYNC );
|
||||
munmap( mem_ptr, mem_size );
|
||||
if (mem_ptr > (void *)0) {
|
||||
msync(mem_ptr, mem_size, MS_ASYNC);
|
||||
munmap(mem_ptr, mem_size);
|
||||
}
|
||||
if ( map_fd >= 0 )
|
||||
close( map_fd );
|
||||
if (map_fd >= 0)
|
||||
close(map_fd);
|
||||
|
||||
map_fd = -1;
|
||||
#else // ZM_MEM_MAPPED
|
||||
struct shmid_ds shm_data;
|
||||
if ( shmctl( shm_id, IPC_STAT, &shm_data ) < 0 ) {
|
||||
Debug( 3, "Can't shmctl: %s", strerror(errno) );
|
||||
return( false );
|
||||
if (shmctl(shm_id, IPC_STAT, &shm_data) < 0) {
|
||||
Debug(3, "Can't shmctl: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
shm_id = 0;
|
||||
|
@ -252,7 +264,6 @@ bool Monitor::MonitorLink::disconnect() {
|
|||
Debug(3, "Can't shmdt: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // ZM_MEM_MAPPED
|
||||
mem_size = 0;
|
||||
mem_ptr = nullptr;
|
||||
|
@ -445,7 +456,7 @@ Monitor::Monitor()
|
|||
"SectionLength, MinSectionLength, FrameSkip, MotionFrameSkip, "
|
||||
"FPSReportInterval, RefBlendPerc, AlarmRefBlendPerc, TrackMotion, Exif,"
|
||||
"`RTSPServer`,`RTSPStreamName`,
|
||||
"SignalCheckPoints, SignalCheckColour, Importance-2 FROM Monitors";
|
||||
"SignalCheckPoints, SignalCheckColour, Importance-1 FROM Monitors";
|
||||
*/
|
||||
|
||||
void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
||||
|
@ -482,8 +493,9 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
Debug(1, "Have camera type %s", CameraType_Strings[type].c_str());
|
||||
col++;
|
||||
function = (Function)atoi(dbrow[col]); col++;
|
||||
enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
|
||||
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
|
||||
enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
||||
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
|
||||
// See below after save_jpegs for a recalculation of decoding_enabled
|
||||
|
||||
ReloadLinkedMonitors(dbrow[col]); col++;
|
||||
|
||||
|
@ -546,6 +558,17 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
videowriter = (VideoWriter)atoi(dbrow[col]); col++;
|
||||
encoderparams = dbrow[col] ? dbrow[col] : ""; col++;
|
||||
|
||||
decoding_enabled = !(
|
||||
( function == RECORD or function == NODECT )
|
||||
and
|
||||
( savejpegs == 0 )
|
||||
and
|
||||
( videowriter == PASSTHROUGH )
|
||||
and
|
||||
!decoding_enabled
|
||||
);
|
||||
Debug(3, "Decoding enabled: %d function %d %s savejpegs %d videowriter %d", decoding_enabled, function, Function_Strings[function].c_str(), savejpegs, videowriter);
|
||||
|
||||
/*"`OutputCodec`, `Encoder`, `OutputContainer`, " */
|
||||
output_codec = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
|
||||
encoder = dbrow[col] ? dbrow[col] : ""; col++;
|
||||
|
@ -595,7 +618,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
rtsp_server = (*dbrow[col] != '0'); col++;
|
||||
rtsp_streamname = dbrow[col]; col++;
|
||||
|
||||
/*"SignalCheckPoints, SignalCheckColour, Importance-2 FROM Monitors"; */
|
||||
/*"SignalCheckPoints, SignalCheckColour, Importance-1 FROM Monitors"; */
|
||||
signal_check_points = atoi(dbrow[col]); col++;
|
||||
signal_check_colour = strtol(dbrow[col][0] == '#' ? dbrow[col]+1 : dbrow[col], 0, 16); col++;
|
||||
|
||||
|
@ -607,6 +630,7 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
grayscale_val = signal_check_colour & 0xff; /* Clear all bytes but lowest byte */
|
||||
|
||||
importance = dbrow[col] ? atoi(dbrow[col]) : 0;// col++;
|
||||
if (importance < 0) importance = 0; // Should only be >= 0
|
||||
|
||||
// How many frames we need to have before we start analysing
|
||||
ready_count = std::max(warmup_count, pre_event_count);
|
||||
|
@ -654,18 +678,6 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
|
|||
Error("Can't mkdir %s: %s", monitor_dir.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
// Do this here to save a few cycles with all the comparisons
|
||||
decoding_enabled = !(
|
||||
( function == RECORD or function == NODECT )
|
||||
and
|
||||
( savejpegs == 0 )
|
||||
and
|
||||
( videowriter == PASSTHROUGH )
|
||||
and
|
||||
!decoding_enabled
|
||||
);
|
||||
Debug(1, "Decoding enabled: %d", decoding_enabled);
|
||||
|
||||
if ( config.record_diag_images ) {
|
||||
if ( config.record_diag_images_fifo ) {
|
||||
diag_path_ref = stringtf("%s/diagpipe-r-%d.jpg", staticConfig.PATH_SOCKS.c_str(), id);
|
||||
|
@ -899,7 +911,6 @@ std::shared_ptr<Monitor> Monitor::Load(unsigned int p_id, bool load_zones, Purpo
|
|||
}
|
||||
|
||||
bool Monitor::connect() {
|
||||
|
||||
if (mem_ptr != nullptr) {
|
||||
Warning("Already connected. Please call disconnect first.");
|
||||
}
|
||||
|
@ -1041,7 +1052,7 @@ bool Monitor::connect() {
|
|||
video_store_data->size = sizeof(VideoStoreData);
|
||||
usedsubpixorder = camera->SubpixelOrder(); // Used in CheckSignal
|
||||
shared_data->valid = true;
|
||||
} else if ( !shared_data->valid ) {
|
||||
} else if (!shared_data->valid) {
|
||||
Error("Shared data not initialised by capture daemon for monitor %s", name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
@ -1063,6 +1074,13 @@ bool Monitor::disconnect() {
|
|||
return true;
|
||||
}
|
||||
|
||||
if (purpose == CAPTURE) {
|
||||
if (unlink(mem_file.c_str()) < 0) {
|
||||
Warning("Can't unlink '%s': %s", mem_file.c_str(), strerror(errno));
|
||||
}
|
||||
Debug(1, "Setting shared_data->valid = false");
|
||||
shared_data->valid = false;
|
||||
}
|
||||
#if ZM_MEM_MAPPED
|
||||
msync(mem_ptr, mem_size, MS_ASYNC);
|
||||
munmap(mem_ptr, mem_size);
|
||||
|
@ -1072,9 +1090,6 @@ bool Monitor::disconnect() {
|
|||
mem_ptr = nullptr;
|
||||
shared_data = nullptr;
|
||||
|
||||
if (purpose == CAPTURE and (unlink(mem_file.c_str()) < 0) ) {
|
||||
Warning("Can't unlink '%s': %s", mem_file.c_str(), strerror(errno));
|
||||
}
|
||||
#else // ZM_MEM_MAPPED
|
||||
struct shmid_ds shm_data;
|
||||
if (shmctl(shm_id, IPC_STAT, &shm_data) < 0) {
|
||||
|
@ -1095,7 +1110,7 @@ bool Monitor::disconnect() {
|
|||
}
|
||||
#endif // ZM_MEM_MAPPED
|
||||
|
||||
for ( int32_t i = 0; i < image_buffer_count; i++ ) {
|
||||
for (int32_t i = 0; i < image_buffer_count; i++) {
|
||||
// We delete the image because it is an object pointing to space that won't be free'd.
|
||||
delete image_buffer[i];
|
||||
image_buffer[i] = nullptr;
|
||||
|
@ -1109,10 +1124,6 @@ Monitor::~Monitor() {
|
|||
|
||||
if (mem_ptr != nullptr) {
|
||||
if (purpose != QUERY) {
|
||||
shared_data->state = state = IDLE;
|
||||
shared_data->last_read_index = image_buffer_count;
|
||||
shared_data->last_read_time = 0;
|
||||
shared_data->valid = false;
|
||||
memset(mem_ptr, 0, mem_size);
|
||||
} // end if purpose != query
|
||||
disconnect();
|
||||
|
@ -1745,10 +1756,6 @@ bool Monitor::Analyse() {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Store the it that points to our snap we will need it later
|
||||
packetqueue_iterator snap_it = *analysis_it;
|
||||
packetqueue.increment_it(analysis_it);
|
||||
|
||||
// signal is set by capture
|
||||
bool signal = shared_data->signal;
|
||||
bool signal_change = (signal != last_signal);
|
||||
|
@ -1858,7 +1865,14 @@ bool Monitor::Analyse() {
|
|||
while (!snap->decoded and !zm_terminate and !analysis_thread->Stopped()) {
|
||||
// Need to wait for the decoder thread.
|
||||
Debug(1, "Waiting for decode");
|
||||
packet_lock->wait();
|
||||
packetqueue.unlock(packet_lock); // This will delete packet_lock and notify_all
|
||||
packetqueue.wait();
|
||||
|
||||
// Another thread may have moved our it. Unlikely but possible
|
||||
packet_lock = packetqueue.get_packet(analysis_it);
|
||||
if (!packet_lock) return false;
|
||||
snap = packet_lock->packet_;
|
||||
|
||||
if (!snap->image and snap->decoded) {
|
||||
Debug(1, "No image but was decoded, giving up");
|
||||
delete packet_lock;
|
||||
|
@ -1890,7 +1904,7 @@ bool Monitor::Analyse() {
|
|||
if (snap->image) {
|
||||
// decoder may not have been able to provide an image
|
||||
if (!ref_image.Buffer()) {
|
||||
Debug(1, "Assigning instead of Dectecting");
|
||||
Debug(1, "Assigning instead of Detecting");
|
||||
ref_image.Assign(*(snap->image));
|
||||
} else {
|
||||
Debug(1, "Detecting motion on image %d, image %p", snap->image_index, snap->image);
|
||||
|
@ -1961,14 +1975,14 @@ bool Monitor::Analyse() {
|
|||
// Must start on a keyframe so rewind. Only for passthrough though I guess.
|
||||
// FIXME this iterator is not protected from invalidation
|
||||
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
|
||||
snap_it, 0 /* pre_event_count */
|
||||
*analysis_it, 0 /* pre_event_count */
|
||||
);
|
||||
|
||||
// This gets a lock on the starting packet
|
||||
|
||||
ZMLockedPacket *starting_packet_lock = nullptr;
|
||||
std::shared_ptr<ZMPacket> starting_packet = nullptr;
|
||||
if (*start_it != snap_it) {
|
||||
if (*start_it != *analysis_it) {
|
||||
starting_packet_lock = packetqueue.get_packet(start_it);
|
||||
if (!starting_packet_lock) {
|
||||
Warning("Unable to get starting packet lock");
|
||||
|
@ -1982,12 +1996,12 @@ bool Monitor::Analyse() {
|
|||
|
||||
event = new Event(this, starting_packet->timestamp, "Continuous", noteSetMap);
|
||||
// Write out starting packets, do not modify packetqueue it will garbage collect itself
|
||||
while (starting_packet and ((*start_it) != snap_it)) {
|
||||
while (starting_packet and ((*start_it) != *analysis_it)) {
|
||||
event->AddPacket(starting_packet);
|
||||
// Have added the packet, don't want to unlock it until we have locked the next
|
||||
|
||||
packetqueue.increment_it(start_it);
|
||||
if ((*start_it) == snap_it) {
|
||||
if ((*start_it) == *analysis_it) {
|
||||
if (starting_packet_lock) delete starting_packet_lock;
|
||||
break;
|
||||
}
|
||||
|
@ -2026,8 +2040,7 @@ bool Monitor::Analyse() {
|
|||
} // end if ! event
|
||||
} // end if RECORDING
|
||||
|
||||
if (score) {
|
||||
|
||||
if (score and (function != MONITOR)) {
|
||||
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
|
||||
// If we should end then previous continuous event and start a new non-continuous event
|
||||
if (event && event->Frames()
|
||||
|
@ -2065,12 +2078,12 @@ bool Monitor::Analyse() {
|
|||
|
||||
if (!event) {
|
||||
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
|
||||
snap_it,
|
||||
*analysis_it,
|
||||
(pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count)
|
||||
);
|
||||
ZMLockedPacket *starting_packet_lock = nullptr;
|
||||
std::shared_ptr<ZMPacket> starting_packet = nullptr;
|
||||
if (*start_it != snap_it) {
|
||||
if (*start_it != *analysis_it) {
|
||||
starting_packet_lock = packetqueue.get_packet(start_it);
|
||||
if (!starting_packet_lock) return false;
|
||||
starting_packet = starting_packet_lock->packet_;
|
||||
|
@ -2085,11 +2098,11 @@ bool Monitor::Analyse() {
|
|||
shared_data->state = state = ALARM;
|
||||
|
||||
// Write out starting packets, do not modify packetqueue it will garbage collect itself
|
||||
while (*start_it != snap_it) {
|
||||
while (*start_it != *analysis_it) {
|
||||
event->AddPacket(starting_packet);
|
||||
|
||||
packetqueue.increment_it(start_it);
|
||||
if ( (*start_it) == snap_it ) {
|
||||
if ( (*start_it) == (*analysis_it) ) {
|
||||
if (starting_packet_lock) delete starting_packet_lock;
|
||||
break;
|
||||
}
|
||||
|
@ -2280,6 +2293,7 @@ bool Monitor::Analyse() {
|
|||
if (function == MODECT or function == MOCORD)
|
||||
UpdateAnalysisFPS();
|
||||
}
|
||||
packetqueue.increment_it(analysis_it);
|
||||
packetqueue.unlock(packet_lock);
|
||||
shared_data->last_read_time = time(nullptr);
|
||||
|
||||
|
@ -3058,7 +3072,7 @@ int Monitor::PrimeCapture() {
|
|||
Debug(1, "Creating decoder thread");
|
||||
decoder = ZM::make_unique<DecoderThread>(this);
|
||||
} else {
|
||||
Debug(1, "Restartg decoder thread");
|
||||
Debug(1, "Restarting decoder thread");
|
||||
decoder->Start();
|
||||
}
|
||||
}
|
||||
|
@ -3082,9 +3096,6 @@ int Monitor::PrimeCapture() {
|
|||
int Monitor::PreCapture() const { return camera->PreCapture(); }
|
||||
int Monitor::PostCapture() const { return camera->PostCapture(); }
|
||||
int Monitor::Close() {
|
||||
if (close_event_thread.joinable()) {
|
||||
close_event_thread.join();
|
||||
}
|
||||
// Because the stream indexes may change we have to clear out the packetqueue
|
||||
if (decoder) {
|
||||
decoder->Stop();
|
||||
|
@ -3102,10 +3113,14 @@ int Monitor::Close() {
|
|||
video_fifo = nullptr;
|
||||
}
|
||||
|
||||
if (close_event_thread.joinable()) {
|
||||
close_event_thread.join();
|
||||
}
|
||||
std::lock_guard<std::mutex> lck(event_mutex);
|
||||
if (event) {
|
||||
Info("%s: image_count:%d - Closing event %" PRIu64 ", shutting down", name.c_str(), image_count, event->Id());
|
||||
closeEvent();
|
||||
close_event_thread.join();
|
||||
}
|
||||
if (camera) camera->Close();
|
||||
return 1;
|
||||
|
|
|
@ -89,7 +89,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
break;
|
||||
case CMD_PLAY :
|
||||
Debug(1, "Got PLAY command");
|
||||
if ( paused ) {
|
||||
if (paused) {
|
||||
paused = false;
|
||||
delayed = true;
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
break;
|
||||
case CMD_VARPLAY :
|
||||
Debug(1, "Got VARPLAY command");
|
||||
if ( paused ) {
|
||||
if (paused) {
|
||||
paused = false;
|
||||
delayed = true;
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
break;
|
||||
case CMD_FASTFWD :
|
||||
Debug(1, "Got FAST FWD command");
|
||||
if ( paused ) {
|
||||
if (paused) {
|
||||
paused = false;
|
||||
delayed = true;
|
||||
}
|
||||
|
@ -135,27 +135,27 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
}
|
||||
break;
|
||||
case CMD_SLOWFWD :
|
||||
Debug( 1, "Got SLOW FWD command" );
|
||||
Debug(1, "Got SLOW FWD command");
|
||||
paused = true;
|
||||
delayed = true;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
step = 1;
|
||||
break;
|
||||
case CMD_SLOWREV :
|
||||
Debug( 1, "Got SLOW REV command" );
|
||||
Debug(1, "Got SLOW REV command");
|
||||
paused = true;
|
||||
delayed = true;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
step = -1;
|
||||
break;
|
||||
case CMD_FASTREV :
|
||||
Debug( 1, "Got FAST REV command" );
|
||||
if ( paused ) {
|
||||
Debug(1, "Got FAST REV command");
|
||||
if (paused) {
|
||||
paused = false;
|
||||
delayed = true;
|
||||
}
|
||||
// Set play rate
|
||||
switch ( replay_rate ) {
|
||||
switch (replay_rate) {
|
||||
case -2 * ZM_RATE_BASE :
|
||||
replay_rate = -5 * ZM_RATE_BASE;
|
||||
break;
|
||||
|
@ -229,6 +229,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
break;
|
||||
case CMD_QUIT :
|
||||
Info("User initiated exit - CMD_QUIT");
|
||||
zm_terminate = true;
|
||||
break;
|
||||
case CMD_QUERY :
|
||||
Debug(1, "Got QUERY command, sending STATUS");
|
||||
|
@ -255,7 +256,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
} status_data;
|
||||
|
||||
status_data.id = monitor->Id();
|
||||
if ( ! monitor->ShmValid() ) {
|
||||
if (!monitor->ShmValid()) {
|
||||
status_data.fps = 0.0;
|
||||
status_data.capture_fps = 0.0;
|
||||
status_data.analysis_fps = 0.0;
|
||||
|
@ -265,7 +266,14 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
status_data.forced = false;
|
||||
status_data.buffer_level = 0;
|
||||
} else {
|
||||
status_data.fps = monitor->GetFPS();
|
||||
int elapsed = now.tv_sec - last_fps_update.tv_sec;
|
||||
if (elapsed) {
|
||||
actual_fps = (frame_count - last_frame_count) / elapsed;
|
||||
last_frame_count = frame_count;
|
||||
last_fps_update = now;
|
||||
}
|
||||
|
||||
status_data.fps = actual_fps;
|
||||
status_data.capture_fps = monitor->get_capture_fps();
|
||||
status_data.analysis_fps = monitor->get_analysis_fps();
|
||||
status_data.state = monitor->shared_data->state;
|
||||
|
@ -308,16 +316,6 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
}
|
||||
}
|
||||
Debug(2, "Number of bytes sent to (%s): (%d)", rem_addr.sun_path, nbytes);
|
||||
|
||||
// quit after sending a status, if this was a quit request
|
||||
if ( (MsgCommand)msg->msg_data[0] == CMD_QUIT ) {
|
||||
zm_terminate = true;
|
||||
Debug(2, "Quitting");
|
||||
return;
|
||||
}
|
||||
|
||||
//Debug(2,"Updating framerate");
|
||||
//updateFrameRate(monitor->GetFPS());
|
||||
} // end void MonitorStream::processCommand(const CmdMsg *msg)
|
||||
|
||||
bool MonitorStream::sendFrame(const char *filepath, const timeval ×tamp) {
|
||||
|
@ -380,9 +378,10 @@ bool MonitorStream::sendFrame(const char *filepath, const timeval ×tamp) {
|
|||
} // end bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp)
|
||||
|
||||
bool MonitorStream::sendFrame(Image *image, const timeval ×tamp) {
|
||||
if (!config.timestamp_on_capture) {
|
||||
monitor->TimestampImage(image, timestamp);
|
||||
}
|
||||
Image *send_image = prepareImage(image);
|
||||
if (!config.timestamp_on_capture)
|
||||
monitor->TimestampImage(send_image, timestamp);
|
||||
|
||||
fputs("--" BOUNDARY "\r\n", stdout);
|
||||
#if HAVE_LIBAVCODEC
|
||||
|
@ -520,7 +519,7 @@ void MonitorStream::runStream() {
|
|||
Image *paused_image = nullptr;
|
||||
struct timeval paused_timestamp;
|
||||
|
||||
if ( connkey && ( playback_buffer > 0 ) ) {
|
||||
if (connkey && (playback_buffer > 0)) {
|
||||
// 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
|
||||
const int max_swap_len_suffix = 15;
|
||||
|
||||
|
@ -529,27 +528,27 @@ void MonitorStream::runStream() {
|
|||
int subfolder2_length = snprintf(nullptr, 0, "/zmswap-q%06d", connkey) + 1;
|
||||
int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;
|
||||
|
||||
if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) {
|
||||
if (total_swap_path_length + max_swap_len_suffix > PATH_MAX) {
|
||||
Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
|
||||
} else {
|
||||
swap_path = staticConfig.PATH_SWAP;
|
||||
|
||||
Debug(3, "Checking swap path folder: %s", swap_path.c_str());
|
||||
if ( checkSwapPath(swap_path.c_str(), true) ) {
|
||||
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());
|
||||
if ( checkSwapPath(swap_path.c_str(), true) ) {
|
||||
if (checkSwapPath(swap_path.c_str(), true)) {
|
||||
swap_path += stringtf("/zmswap-q%06d", connkey);
|
||||
|
||||
Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
|
||||
if ( checkSwapPath(swap_path.c_str(), true) ) {
|
||||
if (checkSwapPath(swap_path.c_str(), true)) {
|
||||
buffered_playback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !buffered_playback ) {
|
||||
if (!buffered_playback) {
|
||||
Error("Unable to validate swap image path, disabling buffered playback");
|
||||
} else {
|
||||
Debug(2, "Assigning temporary buffer");
|
||||
|
@ -560,14 +559,13 @@ void MonitorStream::runStream() {
|
|||
}
|
||||
} else {
|
||||
Debug(2, "Not using playback_buffer");
|
||||
} // end if connkey & playback_buffer
|
||||
} // end if connkey && playback_buffer
|
||||
|
||||
while (!zm_terminate) {
|
||||
bool got_command = false;
|
||||
if ( feof(stdout) ) {
|
||||
if (feof(stdout)) {
|
||||
Debug(2, "feof stdout");
|
||||
break;
|
||||
} else if ( ferror(stdout) ) {
|
||||
} else if (ferror(stdout)) {
|
||||
Debug(2, "ferror stdout");
|
||||
break;
|
||||
} else if (!monitor->ShmValid()) {
|
||||
|
@ -578,8 +576,9 @@ void MonitorStream::runStream() {
|
|||
gettimeofday(&now, nullptr);
|
||||
|
||||
bool was_paused = paused;
|
||||
if ( connkey ) {
|
||||
while ( checkCommandQueue() && !zm_terminate ) {
|
||||
bool got_command = false; // commands like zoom should output a frame even if paused
|
||||
if (connkey) {
|
||||
while (checkCommandQueue() && !zm_terminate) {
|
||||
// Loop in here until all commands are processed.
|
||||
Debug(2, "Have checking command Queue for connkey: %d", connkey);
|
||||
got_command = true;
|
||||
|
@ -591,35 +590,31 @@ void MonitorStream::runStream() {
|
|||
}
|
||||
} // end if connkey
|
||||
|
||||
if ( paused ) {
|
||||
if ( !was_paused ) {
|
||||
if (paused) {
|
||||
if (!was_paused) {
|
||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
||||
Debug(1, "Saving paused image from index %d",index);
|
||||
paused_image = new Image(*monitor->image_buffer[index]);
|
||||
paused_timestamp = monitor->shared_timestamps[index];
|
||||
}
|
||||
} else if ( paused_image ) {
|
||||
Debug(1, "Clearing paused_image");
|
||||
} else if (paused_image) {
|
||||
delete paused_image;
|
||||
paused_image = nullptr;
|
||||
}
|
||||
|
||||
if ( buffered_playback && delayed ) {
|
||||
if ( temp_read_index == temp_write_index ) {
|
||||
if (buffered_playback && delayed) {
|
||||
if (temp_read_index == temp_write_index) {
|
||||
// Go back to live viewing
|
||||
Debug(1, "Exceeded temporary streaming buffer");
|
||||
// Clear paused flag
|
||||
paused = false;
|
||||
// Clear delayed_play flag
|
||||
delayed = false;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
} else {
|
||||
if ( !paused ) {
|
||||
if (!paused) {
|
||||
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
||||
// Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index );
|
||||
SwapImage *swap_image = &temp_image_buffer[temp_index];
|
||||
|
||||
if ( !swap_image->valid ) {
|
||||
if (!swap_image->valid) {
|
||||
paused = true;
|
||||
delayed = true;
|
||||
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
|
||||
|
@ -632,13 +627,14 @@ void MonitorStream::runStream() {
|
|||
// If the next frame is due
|
||||
if ( actual_delta_time > expected_delta_time ) {
|
||||
// Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time );
|
||||
if ( temp_index%frame_mod == 0 ) {
|
||||
if ((temp_index % frame_mod) == 0) {
|
||||
Debug(2, "Sending delayed frame %d", temp_index);
|
||||
// Send the next frame
|
||||
if (!sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp)) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp));
|
||||
frame_count++;
|
||||
last_frame_timestamp = swap_image->timestamp;
|
||||
// frame_sent = true;
|
||||
}
|
||||
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count);
|
||||
|
@ -650,17 +646,15 @@ void MonitorStream::runStream() {
|
|||
SwapImage *swap_image = &temp_image_buffer[temp_read_index];
|
||||
|
||||
// Send the next frame
|
||||
if ( !sendFrame(
|
||||
if (!sendFrame(
|
||||
temp_image_buffer[temp_read_index].file_name,
|
||||
temp_image_buffer[temp_read_index].timestamp
|
||||
) ) {
|
||||
temp_image_buffer[temp_read_index].timestamp)
|
||||
) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
memcpy(
|
||||
&last_frame_timestamp,
|
||||
&(swap_image->timestamp),
|
||||
sizeof(last_frame_timestamp)
|
||||
);
|
||||
frame_count++;
|
||||
|
||||
last_frame_timestamp = swap_image->timestamp;
|
||||
// frame_sent = true;
|
||||
step = 0;
|
||||
} else {
|
||||
|
@ -675,12 +669,13 @@ void MonitorStream::runStream() {
|
|||
if ( !sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp) ) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
frame_count++;
|
||||
// frame_sent = true;
|
||||
}
|
||||
} // end if (!paused) or step or paused
|
||||
} // end if have exceeded buffer or not
|
||||
|
||||
if ( temp_read_index == temp_write_index ) {
|
||||
if (temp_read_index == temp_write_index) {
|
||||
// Go back to live viewing
|
||||
Warning("Rewound over write index, resuming live play");
|
||||
// Clear paused flag
|
||||
|
@ -689,13 +684,13 @@ void MonitorStream::runStream() {
|
|||
delayed = false;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
}
|
||||
} // end if ( buffered_playback && delayed )
|
||||
} // end if (buffered_playback && delayed)
|
||||
|
||||
if ( last_read_index != monitor->shared_data->last_write_index ) {
|
||||
if (last_read_index != monitor->shared_data->last_write_index) {
|
||||
// have a new image to send
|
||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
|
||||
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
|
||||
if ( !paused && !delayed ) {
|
||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
||||
if ((frame_mod == 1) || ((frame_count%frame_mod) == 0)) {
|
||||
if (!paused && !delayed) {
|
||||
last_read_index = monitor->shared_data->last_write_index;
|
||||
Debug(2, "Sending frame index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)",
|
||||
index, frame_mod, frame_count, paused, delayed);
|
||||
|
@ -710,9 +705,8 @@ void MonitorStream::runStream() {
|
|||
zm_terminate = true;
|
||||
break;
|
||||
}
|
||||
//frame_sent = true;
|
||||
//
|
||||
if ( frame_count == 0 ) {
|
||||
frame_count++;
|
||||
if (frame_count == 0) {
|
||||
// Chrome will not display the first frame until it receives another.
|
||||
// Firefox is fine. So just send the first frame twice.
|
||||
if ( !sendFrame(image, last_frame_timestamp) ) {
|
||||
|
@ -724,16 +718,18 @@ void MonitorStream::runStream() {
|
|||
|
||||
temp_read_index = temp_write_index;
|
||||
} else {
|
||||
if ( delayed && !buffered_playback ) {
|
||||
if (delayed && !buffered_playback) {
|
||||
Debug(2, "Can't delay when not buffering.");
|
||||
delayed = false;
|
||||
}
|
||||
if ( last_zoom != zoom ) {
|
||||
if (last_zoom != zoom) {
|
||||
Debug(2, "Sending 2 frames because change in zoom %d ?= %d", last_zoom, zoom);
|
||||
if (!sendFrame(paused_image, paused_timestamp))
|
||||
zm_terminate = true;
|
||||
if (!sendFrame(paused_image, paused_timestamp))
|
||||
zm_terminate = true;
|
||||
frame_count++;
|
||||
frame_count++;
|
||||
} else {
|
||||
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
||||
if ( actual_delta_time > 5 ) {
|
||||
|
@ -744,17 +740,20 @@ void MonitorStream::runStream() {
|
|||
// Send the next frame
|
||||
if (!sendFrame(paused_image, paused_timestamp))
|
||||
zm_terminate = true;
|
||||
frame_count++;
|
||||
} else {
|
||||
Debug(2, "Would have sent keepalive frame, but had no paused_image");
|
||||
}
|
||||
} // end if actual_delta_time > 5
|
||||
} // end if change in zoom
|
||||
} // end if paused or not
|
||||
} else {
|
||||
frame_count++;
|
||||
} // end if should send frame
|
||||
|
||||
if ( buffered_playback && !paused ) {
|
||||
if ( monitor->shared_data->valid ) {
|
||||
if ( monitor->shared_timestamps[index].tv_sec ) {
|
||||
if (buffered_playback && !paused) {
|
||||
if (monitor->shared_data->valid) {
|
||||
if (monitor->shared_timestamps[index].tv_sec) {
|
||||
int temp_index = temp_write_index%temp_image_buffer_count;
|
||||
Debug(2, "Storing frame %d", temp_index);
|
||||
if ( !temp_image_buffer[temp_index].valid ) {
|
||||
|
@ -772,7 +771,7 @@ void MonitorStream::runStream() {
|
|||
config.jpeg_file_quality
|
||||
);
|
||||
temp_write_index = MOD_ADD(temp_write_index, 1, temp_image_buffer_count);
|
||||
if ( temp_write_index == temp_read_index ) {
|
||||
if (temp_write_index == temp_read_index) {
|
||||
// Go back to live viewing
|
||||
Warning("Exceeded temporary buffer, resuming live play");
|
||||
paused = false;
|
||||
|
@ -786,7 +785,6 @@ void MonitorStream::runStream() {
|
|||
Warning("Unable to store frame as shared memory invalid");
|
||||
}
|
||||
} // end if buffered playback
|
||||
frame_count++;
|
||||
} else {
|
||||
Debug(3, "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 )
|
||||
|
@ -815,16 +813,16 @@ void MonitorStream::runStream() {
|
|||
Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)",
|
||||
frame_mod, frame_count);
|
||||
}
|
||||
} // end while
|
||||
} // end while ! zm_terminate
|
||||
|
||||
if ( buffered_playback ) {
|
||||
if (buffered_playback) {
|
||||
Debug(1, "Cleaning swap files from %s", swap_path.c_str());
|
||||
struct stat stat_buf;
|
||||
if ( stat(swap_path.c_str(), &stat_buf) < 0 ) {
|
||||
if ( errno != ENOENT ) {
|
||||
struct stat stat_buf = {};
|
||||
if (stat(swap_path.c_str(), &stat_buf) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
Error("Can't stat '%s': %s", swap_path.c_str(), strerror(errno));
|
||||
}
|
||||
} else if ( !S_ISDIR(stat_buf.st_mode) ) {
|
||||
} else if (!S_ISDIR(stat_buf.st_mode)) {
|
||||
Error("Swap image path '%s' is not a directory", swap_path.c_str());
|
||||
} else {
|
||||
char glob_pattern[PATH_MAX] = "";
|
||||
|
@ -832,21 +830,21 @@ void MonitorStream::runStream() {
|
|||
snprintf(glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path.c_str());
|
||||
glob_t pglob;
|
||||
int glob_status = glob(glob_pattern, 0, 0, &pglob);
|
||||
if ( glob_status != 0 ) {
|
||||
if ( glob_status < 0 ) {
|
||||
if (glob_status != 0) {
|
||||
if (glob_status < 0) {
|
||||
Error("Can't glob '%s': %s", glob_pattern, strerror(errno));
|
||||
} else {
|
||||
Debug(1, "Can't glob '%s': %d", glob_pattern, glob_status);
|
||||
}
|
||||
} else {
|
||||
for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) {
|
||||
if ( unlink(pglob.gl_pathv[i]) < 0 ) {
|
||||
for (unsigned int i = 0; i < pglob.gl_pathc; i++) {
|
||||
if (unlink(pglob.gl_pathv[i]) < 0) {
|
||||
Error("Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
globfree(&pglob);
|
||||
if ( rmdir(swap_path.c_str()) < 0 ) {
|
||||
if (rmdir(swap_path.c_str()) < 0) {
|
||||
Error("Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno));
|
||||
}
|
||||
} // end if checking for swap_path
|
||||
|
@ -867,14 +865,16 @@ void MonitorStream::SingleImage(int scale) {
|
|||
Debug(1, "write index: %d %d", monitor->shared_data->last_write_index, index);
|
||||
Image *snap_image = monitor->image_buffer[index];
|
||||
|
||||
if (!config.timestamp_on_capture) {
|
||||
monitor->TimestampImage(snap_image, monitor->shared_timestamps[index]);
|
||||
}
|
||||
|
||||
if ( scale != ZM_SCALE_BASE ) {
|
||||
scaled_image.Assign(*snap_image);
|
||||
scaled_image.Scale(scale);
|
||||
snap_image = &scaled_image;
|
||||
}
|
||||
if ( !config.timestamp_on_capture ) {
|
||||
monitor->TimestampImage(snap_image, monitor->shared_timestamps[index]);
|
||||
}
|
||||
|
||||
snap_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
||||
|
||||
fprintf(stdout,
|
||||
|
|
|
@ -41,7 +41,6 @@ class MonitorStream : public StreamBase {
|
|||
time_t ttl;
|
||||
int playback_buffer;
|
||||
bool delayed;
|
||||
int frame_count;
|
||||
|
||||
protected:
|
||||
bool checkSwapPath(const char *path, bool create_path);
|
||||
|
@ -62,9 +61,9 @@ class MonitorStream : public StreamBase {
|
|||
temp_write_index(0),
|
||||
ttl(0),
|
||||
playback_buffer(0),
|
||||
delayed(false),
|
||||
frame_count(0) {
|
||||
}
|
||||
delayed(false)
|
||||
{}
|
||||
|
||||
void setStreamBuffer(int p_playback_buffer) {
|
||||
playback_buffer = p_playback_buffer;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "zm_ffmpeg.h"
|
||||
#include "zm_packet.h"
|
||||
#include "zm_signal.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
PacketQueue::PacketQueue():
|
||||
video_stream_id(-1),
|
||||
|
@ -87,60 +86,6 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
|||
{
|
||||
std::unique_lock<std::mutex> lck(mutex);
|
||||
|
||||
if (add_packet->packet.stream_index == video_stream_id) {
|
||||
if ((max_video_packet_count > 0) and (packet_counts[video_stream_id] > max_video_packet_count)) {
|
||||
Warning("You have set the max video packets in the queue to %u."
|
||||
" The queue is full. Either Analysis is not keeping up or"
|
||||
" your camera's keyframe interval is larger than this setting."
|
||||
" We are dropping packets.", max_video_packet_count);
|
||||
if (add_packet->keyframe) {
|
||||
// Have a new keyframe, so delete everything
|
||||
while ((*pktQueue.begin() != add_packet) and (packet_counts[video_stream_id] > max_video_packet_count)) {
|
||||
std::shared_ptr <ZMPacket>zm_packet = *pktQueue.begin();
|
||||
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
|
||||
if (!lp->trylock()) {
|
||||
Debug(1, "Found locked packet when trying to free up video packets. Can't continue");
|
||||
delete lp;
|
||||
break;
|
||||
}
|
||||
delete lp;
|
||||
|
||||
for (
|
||||
std::list<packetqueue_iterator *>::iterator iterators_it = iterators.begin();
|
||||
iterators_it != iterators.end();
|
||||
++iterators_it
|
||||
) {
|
||||
packetqueue_iterator *iterator_it = *iterators_it;
|
||||
// Have to check each iterator and make sure it doesn't point to the packet we are about to delete
|
||||
if ( *(*iterator_it) == zm_packet ) {
|
||||
Debug(1, "Bumping IT because it is at the front that we are deleting");
|
||||
++(*iterators_it);
|
||||
}
|
||||
} // end foreach iterator
|
||||
|
||||
pktQueue.pop_front();
|
||||
packet_counts[zm_packet->packet.stream_index] -= 1;
|
||||
Debug(1,
|
||||
"Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%zu",
|
||||
zm_packet->packet.stream_index,
|
||||
zm_packet->image_index,
|
||||
zm_packet->keyframe,
|
||||
packet_counts[video_stream_id],
|
||||
max_video_packet_count,
|
||||
pktQueue.size());
|
||||
} // end while
|
||||
}
|
||||
} // end if too many video packets
|
||||
if (max_video_packet_count > 0) {
|
||||
while (packet_counts[video_stream_id] > max_video_packet_count) {
|
||||
Error("Unable to free up older packets. Waiting.");
|
||||
condition.wait(lck);
|
||||
if (deleting or zm_terminate)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // end if this packet is a video packet
|
||||
|
||||
pktQueue.push_back(add_packet);
|
||||
packet_counts[add_packet->packet.stream_index] += 1;
|
||||
Debug(2, "packet counts for %d is %d",
|
||||
|
@ -148,25 +93,80 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
|
|||
packet_counts[add_packet->packet.stream_index]);
|
||||
|
||||
for (
|
||||
std::list<packetqueue_iterator *>::iterator iterators_it = iterators.begin();
|
||||
auto iterators_it = iterators.begin();
|
||||
iterators_it != iterators.end();
|
||||
++iterators_it
|
||||
) {
|
||||
packetqueue_iterator *iterator_it = *iterators_it;
|
||||
if (*iterator_it == pktQueue.end()) {
|
||||
Debug(4, "pointing it %p to back", iterator_it);
|
||||
--(*iterator_it);
|
||||
} else {
|
||||
Debug(4, "it %p not at end", iterator_it);
|
||||
}
|
||||
} // end foreach iterator
|
||||
|
||||
if (
|
||||
(add_packet->packet.stream_index == video_stream_id)
|
||||
and
|
||||
(max_video_packet_count > 0)
|
||||
and
|
||||
(packet_counts[video_stream_id] > max_video_packet_count)
|
||||
) {
|
||||
Warning("You have set the max video packets in the queue to %u."
|
||||
" The queue is full. Either Analysis is not keeping up or"
|
||||
" your camera's keyframe interval is larger than this setting."
|
||||
, max_video_packet_count);
|
||||
|
||||
for (
|
||||
auto it = ++pktQueue.begin();
|
||||
it != pktQueue.end() and *it != add_packet;
|
||||
// iterator is incremented by erase
|
||||
) {
|
||||
std::shared_ptr <ZMPacket>zm_packet = *it;
|
||||
|
||||
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
|
||||
if (!lp->trylock()) {
|
||||
Warning("Found locked packet when trying to free up video packets. This basically means that decoding is not keeping up.");
|
||||
delete lp;
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (
|
||||
auto iterators_it = iterators.begin();
|
||||
iterators_it != iterators.end();
|
||||
++iterators_it
|
||||
) {
|
||||
auto iterator_it = *iterators_it;
|
||||
// Have to check each iterator and make sure it doesn't point to the packet we are about to delete
|
||||
if ((*iterator_it!=pktQueue.end()) and (*(*iterator_it) == zm_packet)) {
|
||||
Debug(1, "Bumping IT because it is at the front that we are deleting");
|
||||
++(*iterator_it);
|
||||
}
|
||||
} // end foreach iterator
|
||||
|
||||
it = pktQueue.erase(it);
|
||||
packet_counts[zm_packet->packet.stream_index] -= 1;
|
||||
Debug(1,
|
||||
"Deleting a packet with stream index:%d image_index:%d with keyframe:%d, video frames in queue:%d max: %d, queuesize:%zu",
|
||||
zm_packet->packet.stream_index,
|
||||
zm_packet->image_index,
|
||||
zm_packet->keyframe,
|
||||
packet_counts[video_stream_id],
|
||||
max_video_packet_count,
|
||||
pktQueue.size());
|
||||
|
||||
delete lp;
|
||||
|
||||
if (zm_packet->packet.stream_index == video_stream_id)
|
||||
break;
|
||||
} // end while
|
||||
} // end if not able catch up
|
||||
} // end lock scope
|
||||
// We signal on every packet because someday we may analyze sound
|
||||
Debug(4, "packetqueue queuepacket, unlocked signalling");
|
||||
condition.notify_all();
|
||||
|
||||
return true;
|
||||
} // end bool PacketQueue::queuePacket(ZMPacket* zm_packet)
|
||||
} // end bool PacketQueue::queuePacket(ZMPacket* zm_packet)
|
||||
|
||||
void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
||||
// Only do queueCleaning if we are adding a video keyframe, so that we guarantee that there is one.
|
||||
|
@ -179,6 +179,7 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
// So start at the beginning, counting video packets until the next keyframe.
|
||||
// Then if deleting those packets doesn't break 1 and 2, then go ahead and delete them.
|
||||
if (deleting) return;
|
||||
if (!pktQueue.size()) return;
|
||||
|
||||
if (keep_keyframes and ! (
|
||||
add_packet->packet.stream_index == video_stream_id
|
||||
|
@ -197,7 +198,6 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
return;
|
||||
}
|
||||
std::unique_lock<std::mutex> lck(mutex);
|
||||
if (!pktQueue.size()) return;
|
||||
|
||||
// If analysis_it isn't at the end, we need to keep that many additional packets
|
||||
int tail_count = 0;
|
||||
|
@ -240,8 +240,8 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
return;
|
||||
}
|
||||
|
||||
packetqueue_iterator it = pktQueue.begin();
|
||||
packetqueue_iterator next_front = pktQueue.begin();
|
||||
auto it = pktQueue.begin();
|
||||
auto next_front = pktQueue.begin();
|
||||
|
||||
// First packet is special because we know it is a video keyframe and only need to check for lock
|
||||
std::shared_ptr<ZMPacket> zm_packet = *it;
|
||||
|
@ -249,32 +249,32 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
return;
|
||||
}
|
||||
|
||||
Debug(1, "trying lock on first packet");
|
||||
ZMLockedPacket *lp = new ZMLockedPacket(zm_packet);
|
||||
if (lp->trylock()) {
|
||||
int video_packets_to_delete = 0; // This is a count of how many packets we will delete so we know when to stop looking
|
||||
Debug(1, "Have lock on first packet");
|
||||
Debug(4, "Have lock on first packet");
|
||||
++it;
|
||||
delete lp;
|
||||
|
||||
if (it == pktQueue.end()) {
|
||||
Debug(1, "Hit end already");
|
||||
it = pktQueue.begin();
|
||||
} else {
|
||||
// Since we have many packets in the queue, we should NOT be pointing at end so don't need to test for that
|
||||
while (*it != add_packet) {
|
||||
zm_packet = *it;
|
||||
lp = new ZMLockedPacket(zm_packet);
|
||||
if (!lp->trylock()) {
|
||||
Debug(3, "Failed locking packet %d", zm_packet->image_index);
|
||||
delete lp;
|
||||
break;
|
||||
}
|
||||
delete lp;
|
||||
|
||||
if (is_there_an_iterator_pointing_to_packet(zm_packet) and (pktQueue.begin() == next_front)) {
|
||||
Warning("Found iterator at beginning of queue. Some thread isn't keeping up");
|
||||
#if 0
|
||||
// There are no threads that follow analysis thread. So there cannot be an it pointing here
|
||||
if (is_there_an_iterator_pointing_to_packet(zm_packet)) {
|
||||
if (pktQueue.begin() == next_front)
|
||||
Warning("Found iterator at beginning of queue. Some thread isn't keeping up");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (zm_packet->packet.stream_index == video_stream_id) {
|
||||
if (zm_packet->keyframe) {
|
||||
|
@ -282,17 +282,16 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
next_front = it;
|
||||
}
|
||||
++video_packets_to_delete;
|
||||
Debug(4, "Counted %d video packets. Which would leave %d in packetqueue tail count is %d",
|
||||
video_packets_to_delete, packet_counts[video_stream_id]-video_packets_to_delete, tail_count);
|
||||
Debug(3, "Counted %d video packets. Which would leave %d in packetqueue tail count is %d",
|
||||
video_packets_to_delete, packet_counts[video_stream_id]-video_packets_to_delete, tail_count);
|
||||
if (packet_counts[video_stream_id] - video_packets_to_delete <= pre_event_video_packet_count + tail_count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
} // end while
|
||||
}
|
||||
} // end if first packet not locked
|
||||
Debug(1, "Resulting pointing at latest packet? %d, next front points to begin? %d",
|
||||
Debug(1, "Resulting it pointing at latest packet? %d, next front points to begin? %d",
|
||||
( *it == add_packet ),
|
||||
( next_front == pktQueue.begin() )
|
||||
);
|
||||
|
@ -314,7 +313,6 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
pktQueue.size());
|
||||
pktQueue.pop_front();
|
||||
packet_counts[zm_packet->packet.stream_index] -= 1;
|
||||
//delete zm_packet;
|
||||
}
|
||||
} // end if have at least max_video_packet_count video packets remaining
|
||||
// We signal on every packet because someday we may analyze sound
|
||||
|
@ -325,6 +323,8 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
|
|||
void PacketQueue::clear() {
|
||||
deleting = true;
|
||||
condition.notify_all();
|
||||
if (!packet_counts) // special case, not initialised
|
||||
return;
|
||||
Debug(1, "Clearing packetqueue");
|
||||
std::unique_lock<std::mutex> lck(mutex);
|
||||
|
||||
|
@ -662,3 +662,12 @@ void PacketQueue::setPreEventVideoPackets(int p) {
|
|||
pre_event_video_packet_count = 1;
|
||||
// We can simplify a lot of logic in queuePacket if we can assume at least 1 packet in queue
|
||||
}
|
||||
|
||||
void PacketQueue::notify_all() {
|
||||
condition.notify_all();
|
||||
};
|
||||
|
||||
void PacketQueue::wait() {
|
||||
std::unique_lock<std::mutex> lck(mutex);
|
||||
condition.wait(lck);
|
||||
}
|
||||
|
|
|
@ -81,6 +81,8 @@ class PacketQueue {
|
|||
);
|
||||
bool is_there_an_iterator_pointing_to_packet(const std::shared_ptr<ZMPacket> &zm_packet);
|
||||
void unlock(ZMLockedPacket *lp);
|
||||
void notify_all();
|
||||
void wait();
|
||||
};
|
||||
|
||||
#endif /* ZM_PACKETQUEUE_H */
|
||||
|
|
|
@ -100,7 +100,8 @@ void RemoteCamera::Initialise() {
|
|||
|
||||
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
|
||||
if ( ret != 0 ) {
|
||||
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
|
||||
Error( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
|
||||
return;
|
||||
}
|
||||
struct addrinfo *p = nullptr;
|
||||
int addr_count = 0;
|
||||
|
|
|
@ -144,6 +144,14 @@ void RemoteCameraHttp::Initialise() {
|
|||
int RemoteCameraHttp::Connect() {
|
||||
struct addrinfo *p = nullptr;
|
||||
|
||||
if (!hp) {
|
||||
RemoteCamera::Initialise();
|
||||
if (!hp) {
|
||||
Error("Unable to resolve address for remote camera, aborting");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for ( p = hp; p != nullptr; p = p->ai_next ) {
|
||||
sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol );
|
||||
if ( sd < 0 ) {
|
||||
|
|
47
src/zm_rgb.h
47
src/zm_rgb.h
|
@ -118,41 +118,34 @@ constexpr Rgb kRGBTransparent = 0x01000000;
|
|||
|
||||
/* Convert RGB colour value into BGR\ARGB\ABGR */
|
||||
inline Rgb rgb_convert(Rgb p_col, int p_subpixorder) {
|
||||
Rgb result;
|
||||
Rgb result = 0;
|
||||
|
||||
switch(p_subpixorder) {
|
||||
|
||||
switch (p_subpixorder) {
|
||||
case ZM_SUBPIX_ORDER_BGR:
|
||||
case ZM_SUBPIX_ORDER_BGRA:
|
||||
{
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
BLUE_PTR_BGRA(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_BGRA(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_BGRA(&result) = RED_VAL_RGBA(p_col);
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ARGB:
|
||||
{
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
BLUE_PTR_ARGB(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ARGB(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ARGB(&result) = RED_VAL_RGBA(p_col);
|
||||
break;
|
||||
case ZM_SUBPIX_ORDER_ABGR:
|
||||
{
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
}
|
||||
break;
|
||||
/* Grayscale */
|
||||
BLUE_PTR_ABGR(&result) = BLUE_VAL_RGBA(p_col);
|
||||
GREEN_PTR_ABGR(&result) = GREEN_VAL_RGBA(p_col);
|
||||
RED_PTR_ABGR(&result) = RED_VAL_RGBA(p_col);
|
||||
break;
|
||||
/* Grayscale */
|
||||
case ZM_SUBPIX_ORDER_NONE:
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
result = p_col & 0xff;
|
||||
break;
|
||||
default:
|
||||
return p_col;
|
||||
break;
|
||||
result = p_col;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -279,9 +279,8 @@ int main(int argc, char *argv[]) {
|
|||
Warning("Unknown format in %s", videoFifoPath.c_str());
|
||||
}
|
||||
if (videoSource == nullptr) {
|
||||
Error("Unable to create source");
|
||||
Error("Unable to create source for %s", videoFifoPath.c_str());
|
||||
rtspServer->RemoveSession(sessions[monitor->Id()]->GetMediaSessionId());
|
||||
delete sessions[monitor->Id()];
|
||||
sessions.erase(monitor->Id());
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ RETSIGTYPE zm_die_handler(int signal, siginfo_t * info, void *context)
|
|||
RETSIGTYPE zm_die_handler(int signal)
|
||||
#endif
|
||||
{
|
||||
zm_terminate = true;
|
||||
Error("Got signal %d (%s), crashing", signal, strsignal(signal));
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
// Get more information if available
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
StreamBase::~StreamBase() {
|
||||
#if HAVE_LIBAVCODEC
|
||||
|
|
|
@ -102,7 +102,6 @@ protected:
|
|||
int last_scale;
|
||||
int zoom;
|
||||
int last_zoom;
|
||||
double maxfps;
|
||||
int bitrate;
|
||||
unsigned short last_x, last_y;
|
||||
unsigned short x, y;
|
||||
|
@ -122,8 +121,14 @@ protected:
|
|||
struct timeval now;
|
||||
struct timeval last_comm_update;
|
||||
|
||||
double base_fps;
|
||||
double effective_fps;
|
||||
double maxfps;
|
||||
double base_fps; // Should be capturing fps, hence a rough target
|
||||
double effective_fps; // Target fps after taking max_fps into account
|
||||
double actual_fps; // sliding calculated actual streaming fps achieved
|
||||
struct timeval last_fps_update;
|
||||
int frame_count; // Count of frames sent
|
||||
int last_frame_count; // Used in calculating actual_fps from frame_count - last_frame_count
|
||||
|
||||
int frame_mod;
|
||||
|
||||
double last_frame_sent;
|
||||
|
@ -154,7 +159,6 @@ public:
|
|||
last_scale(DEFAULT_SCALE),
|
||||
zoom(DEFAULT_ZOOM),
|
||||
last_zoom(DEFAULT_ZOOM),
|
||||
maxfps(DEFAULT_MAXFPS),
|
||||
bitrate(DEFAULT_BITRATE),
|
||||
last_x(0),
|
||||
last_y(0),
|
||||
|
@ -166,7 +170,15 @@ public:
|
|||
sd(-1),
|
||||
lock_fd(0),
|
||||
paused(false),
|
||||
step(0)
|
||||
step(0),
|
||||
maxfps(DEFAULT_MAXFPS),
|
||||
base_fps(0.0),
|
||||
effective_fps(0.0),
|
||||
actual_fps(0.0),
|
||||
last_fps_update({}),
|
||||
frame_count(0),
|
||||
last_frame_count(0),
|
||||
frame_mod(1)
|
||||
{
|
||||
memset(&loc_sock_path, 0, sizeof(loc_sock_path));
|
||||
memset(&loc_addr, 0, sizeof(loc_addr));
|
||||
|
@ -174,12 +186,8 @@ public:
|
|||
memset(&rem_addr, 0, sizeof(rem_addr));
|
||||
memset(&sock_path_lock, 0, sizeof(sock_path_lock));
|
||||
|
||||
base_fps = 0.0;
|
||||
effective_fps = 0.0;
|
||||
frame_mod = 1;
|
||||
|
||||
#if HAVE_LIBAVCODEC
|
||||
vid_stream = 0;
|
||||
vid_stream = nullptr;
|
||||
#endif // HAVE_LIBAVCODEC
|
||||
last_frame_sent = 0.0;
|
||||
last_frame_timestamp = {};
|
||||
|
|
|
@ -224,8 +224,15 @@ void HwCapsDetect() {
|
|||
#elif defined(__arm__)
|
||||
// ARM processor in 32bit mode
|
||||
// To see if it supports NEON, we need to get that information from the kernel
|
||||
#ifdef __linux__
|
||||
unsigned long auxval = getauxval(AT_HWCAP);
|
||||
if (auxval & HWCAP_ARM_NEON) {
|
||||
#elif defined(__FreeBSD__)
|
||||
unsigned long auxval = 0;
|
||||
elf_aux_info(AT_HWCAP, &auxval, sizeof(auxval));
|
||||
if (auxval & HWCAP_NEON) {
|
||||
#error Unsupported OS.
|
||||
#endif
|
||||
Debug(1,"Detected ARM (AArch32) processor with Neon");
|
||||
neonversion = 1;
|
||||
} else {
|
||||
|
|
|
@ -638,7 +638,8 @@ VideoStore::~VideoStore() {
|
|||
|
||||
Debug(1, "Writing trailer");
|
||||
/* Write the trailer before close */
|
||||
if ( int rc = av_write_trailer(oc) ) {
|
||||
int rc;
|
||||
if ((rc = av_write_trailer(oc)) < 0) {
|
||||
Error("Error writing trailer %s", av_err2str(rc));
|
||||
} else {
|
||||
Debug(3, "Success Writing trailer");
|
||||
|
@ -648,7 +649,7 @@ VideoStore::~VideoStore() {
|
|||
if (!(out_format->flags & AVFMT_NOFILE)) {
|
||||
/* Close the out file. */
|
||||
Debug(4, "Closing");
|
||||
if (int rc = avio_close(oc->pb)) {
|
||||
if ((rc = avio_close(oc->pb)) < 0) {
|
||||
Error("Error closing avio %s", av_err2str(rc));
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -874,16 +874,23 @@ std::vector<Zone> Zone::Load(Monitor *monitor) {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (polygon.Extent().Lo().x_ < 0 || polygon.Extent().Hi().x_ > static_cast<int32>(monitor->Width())
|
||||
|| polygon.Extent().Lo().y_ < 0 || polygon.Extent().Hi().y_ > static_cast<int32>(monitor->Height())) {
|
||||
Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d), fixing",
|
||||
if (polygon.Extent().Lo().x_ < 0
|
||||
||
|
||||
polygon.Extent().Hi().x_ > static_cast<int32>(monitor->Width())
|
||||
||
|
||||
polygon.Extent().Lo().y_ < 0
|
||||
||
|
||||
polygon.Extent().Hi().y_ > static_cast<int32>(monitor->Height())) {
|
||||
Error("Zone %d/%s for monitor %s extends outside of image dimensions, (%d,%d), (%d,%d) != (%d,%d), fixing",
|
||||
Id,
|
||||
Name,
|
||||
monitor->Name(),
|
||||
polygon.Extent().Lo().x_,
|
||||
polygon.Extent().Lo().y_,
|
||||
polygon.Extent().Hi().x_,
|
||||
polygon.Extent().Hi().y_);
|
||||
polygon.Extent().Hi().y_,
|
||||
monitor->Width(),
|
||||
monitor->Height());
|
||||
|
||||
polygon.Clip(Box(
|
||||
{0, 0},
|
||||
|
|
|
@ -376,6 +376,7 @@ int main(int argc, char *argv[]) {
|
|||
monitor->Id());
|
||||
zmDbDo(sql);
|
||||
}
|
||||
monitors.clear();
|
||||
|
||||
Image::Deinitialise();
|
||||
Debug(1, "terminating");
|
||||
|
|
84
src/zmu.cpp
84
src/zmu.cpp
|
@ -197,6 +197,7 @@ bool ValidateAccess(User *user, int mon_id, int function) {
|
|||
|
||||
void exit_zmu(int exit_code) {
|
||||
logTerm();
|
||||
dbQueue.stop();
|
||||
zmDbClose();
|
||||
|
||||
exit(exit_code);
|
||||
|
@ -248,7 +249,7 @@ int main(int argc, char *argv[]) {
|
|||
{nullptr, 0, nullptr, 0}
|
||||
};
|
||||
|
||||
const char *device = nullptr;
|
||||
std::string device;
|
||||
int mon_id = 0;
|
||||
bool verbose = false;
|
||||
int function = ZMU_BOGUS;
|
||||
|
@ -256,9 +257,16 @@ int main(int argc, char *argv[]) {
|
|||
int image_idx = -1;
|
||||
int scale = -1;
|
||||
int brightness = -1;
|
||||
bool have_brightness = false;
|
||||
|
||||
int contrast = -1;
|
||||
bool have_contrast = false;
|
||||
|
||||
int hue = -1;
|
||||
bool have_hue = false;
|
||||
int colour = -1;
|
||||
bool have_colour = false;
|
||||
|
||||
char *zoneString = nullptr;
|
||||
char *username = nullptr;
|
||||
char *password = nullptr;
|
||||
|
@ -275,13 +283,13 @@ int main(int argc, char *argv[]) {
|
|||
int option_index = 0;
|
||||
|
||||
int c = getopt_long(argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::RWU:P:A:V:T:", long_options, &option_index);
|
||||
if ( c == -1 ) {
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'd':
|
||||
if ( optarg )
|
||||
if (optarg)
|
||||
device = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
|
@ -295,7 +303,7 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
case 'i':
|
||||
function |= ZMU_IMAGE;
|
||||
if ( optarg )
|
||||
if (optarg)
|
||||
image_idx = atoi(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
|
@ -303,7 +311,7 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
case 't':
|
||||
function |= ZMU_TIME;
|
||||
if ( optarg )
|
||||
if (optarg)
|
||||
image_idx = atoi(optarg);
|
||||
break;
|
||||
case 'R':
|
||||
|
@ -320,7 +328,7 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
case 'z':
|
||||
function |= ZMU_ZONES;
|
||||
if ( optarg )
|
||||
if (optarg)
|
||||
zoneString = optarg;
|
||||
break;
|
||||
case 'a':
|
||||
|
@ -352,23 +360,31 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
case 'B':
|
||||
function |= ZMU_BRIGHTNESS;
|
||||
if ( optarg )
|
||||
if (optarg) {
|
||||
have_brightness = true;
|
||||
brightness = atoi(optarg);
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
function |= ZMU_CONTRAST;
|
||||
if ( optarg )
|
||||
if (optarg) {
|
||||
have_contrast = true;
|
||||
contrast = atoi(optarg);
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
function |= ZMU_HUE;
|
||||
if ( optarg )
|
||||
if (optarg) {
|
||||
have_hue = true;
|
||||
hue = atoi(optarg);
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
function |= ZMU_COLOUR;
|
||||
if ( optarg )
|
||||
if (optarg) {
|
||||
have_colour = true;
|
||||
colour = atoi(optarg);
|
||||
}
|
||||
break;
|
||||
case 'U':
|
||||
username = optarg;
|
||||
|
@ -408,7 +424,7 @@ int main(int argc, char *argv[]) {
|
|||
Usage();
|
||||
}
|
||||
|
||||
if ( device && !(function&ZMU_QUERY) ) {
|
||||
if ( !device.empty() && !(function&ZMU_QUERY) ) {
|
||||
fprintf(stderr, "Error, -d option cannot be used with this option\n");
|
||||
Usage();
|
||||
}
|
||||
|
@ -643,60 +659,60 @@ int main(int argc, char *argv[]) {
|
|||
monitor->DumpSettings(monString, verbose);
|
||||
printf("%s\n", monString);
|
||||
}
|
||||
if ( function & ZMU_BRIGHTNESS ) {
|
||||
if ( verbose ) {
|
||||
if ( brightness >= 0 )
|
||||
if (function & ZMU_BRIGHTNESS) {
|
||||
if (verbose) {
|
||||
if (have_brightness)
|
||||
printf("New brightness: %d\n", monitor->actionBrightness(brightness));
|
||||
else
|
||||
printf("Current brightness: %d\n", monitor->actionBrightness());
|
||||
} else {
|
||||
if ( have_output ) fputc(separator, stdout);
|
||||
if ( brightness >= 0 )
|
||||
if (have_output) fputc(separator, stdout);
|
||||
if (have_brightness)
|
||||
printf("%d", monitor->actionBrightness(brightness));
|
||||
else
|
||||
printf("%d", monitor->actionBrightness());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_CONTRAST ) {
|
||||
if ( verbose ) {
|
||||
if ( contrast >= 0 )
|
||||
printf("New brightness: %d\n", monitor->actionContrast(contrast));
|
||||
if (function & ZMU_CONTRAST) {
|
||||
if (verbose) {
|
||||
if (have_contrast)
|
||||
printf("New contrast: %d\n", monitor->actionContrast(contrast));
|
||||
else
|
||||
printf("Current contrast: %d\n", monitor->actionContrast());
|
||||
} else {
|
||||
if ( have_output ) fputc(separator, stdout);
|
||||
if ( contrast >= 0 )
|
||||
if (have_output) fputc(separator, stdout);
|
||||
if (have_contrast)
|
||||
printf("%d", monitor->actionContrast(contrast));
|
||||
else
|
||||
printf("%d", monitor->actionContrast());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_HUE ) {
|
||||
if ( verbose ) {
|
||||
if ( hue >= 0 )
|
||||
if (function & ZMU_HUE) {
|
||||
if (verbose) {
|
||||
if (have_hue)
|
||||
printf("New hue: %d\n", monitor->actionHue(hue));
|
||||
else
|
||||
printf("Current hue: %d\n", monitor->actionHue());
|
||||
} else {
|
||||
if ( have_output ) fputc(separator, stdout);
|
||||
if ( hue >= 0 )
|
||||
if (have_output) fputc(separator, stdout);
|
||||
if (have_hue)
|
||||
printf("%d", monitor->actionHue(hue));
|
||||
else
|
||||
printf("%d", monitor->actionHue());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_COLOUR ) {
|
||||
if ( verbose ) {
|
||||
if ( colour >= 0 )
|
||||
if (function & ZMU_COLOUR) {
|
||||
if (verbose) {
|
||||
if (have_colour)
|
||||
printf("New colour: %d\n", monitor->actionColour(colour));
|
||||
else
|
||||
printf("Current colour: %d\n", monitor->actionColour());
|
||||
} else {
|
||||
if ( have_output ) fputc(separator, stdout);
|
||||
if ( colour >= 0 )
|
||||
if (have_output) fputc(separator, stdout);
|
||||
if (have_colour)
|
||||
printf("%d", monitor->actionColour(colour));
|
||||
else
|
||||
printf("%d", monitor->actionColour());
|
||||
|
@ -704,7 +720,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
if ( have_output ) {
|
||||
if (have_output) {
|
||||
printf("\n");
|
||||
}
|
||||
if ( !function ) {
|
||||
|
@ -714,7 +730,7 @@ int main(int argc, char *argv[]) {
|
|||
if ( function & ZMU_QUERY ) {
|
||||
#if ZM_HAS_V4L
|
||||
char vidString[0x10000] = "";
|
||||
bool ok = LocalCamera::GetCurrentSettings(device, vidString, v4lVersion, verbose);
|
||||
bool ok = LocalCamera::GetCurrentSettings(device.c_str(), vidString, v4lVersion, verbose);
|
||||
printf("%s", vidString);
|
||||
exit_zmu(ok ? 0 : -1);
|
||||
#else // ZM_HAS_V4L
|
||||
|
|
|
@ -58,6 +58,14 @@ case $i in
|
|||
PACKAGE_VERSION="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
-x=*|--debbuild-extra=*)
|
||||
DEBBUILD_EXTRA="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--dput=*)
|
||||
DPUT="${i#*=}"
|
||||
shift
|
||||
;;
|
||||
--default)
|
||||
DEFAULT=YES
|
||||
shift # past argument with no value
|
||||
|
@ -80,7 +88,7 @@ fi;
|
|||
|
||||
if [ "$DISTROS" == "" ]; then
|
||||
if [ "$RELEASE" != "" ]; then
|
||||
DISTROS="xenial,bionic,focal,groovy,hirsute"
|
||||
DISTROS="xenial,bionic,focal,hirsute,impish"
|
||||
else
|
||||
DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
|
||||
fi;
|
||||
|
@ -112,6 +120,11 @@ else
|
|||
if [ "$BRANCH" == "" ]; then
|
||||
#REV=$(git rev-list --tags --max-count=1)
|
||||
BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`;
|
||||
if [ -z "$BRANCH" ]; then
|
||||
# This should only happen in CI environments where tag info isn't available
|
||||
BRANCH=`cat version`
|
||||
echo "Building branch $BRANCH"
|
||||
fi
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
echo "Unable to determine latest stable branch!"
|
||||
exit 0;
|
||||
|
@ -133,6 +146,14 @@ else
|
|||
fi;
|
||||
fi
|
||||
|
||||
if [ "$PACKAGE_VERSION" == "NOW" ]; then
|
||||
PACKAGE_VERSION=`date +%Y%m%d%H%M%S`;
|
||||
else
|
||||
if [ "$PACKAGE_VERSION" == "CURRENT" ]; then
|
||||
PACKAGE_VERSION="`date +%Y%m%d.`$(git rev-list ${versionhash}..HEAD --count)"
|
||||
fi;
|
||||
fi;
|
||||
|
||||
IFS='.' read -r -a VERSION_PARTS <<< "$RELEASE"
|
||||
if [ "$PPA" == "" ]; then
|
||||
if [ "$RELEASE" != "" ]; then
|
||||
|
@ -216,8 +237,12 @@ rm -rf .git
|
|||
rm .gitignore
|
||||
cd ../
|
||||
|
||||
|
||||
if [ ! -e "$DIRECTORY.orig.tar.gz" ]; then
|
||||
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
|
||||
read -p "$DIRECTORY.orig.tar.gz does not exist, create it? [Y/n]"
|
||||
if [[ "$REPLY" == "" || "$REPLY" == [yY] ]]; then
|
||||
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
|
||||
fi;
|
||||
fi;
|
||||
|
||||
IFS=',' ;for DISTRO in `echo "$DISTROS"`; do
|
||||
|
@ -229,7 +254,7 @@ IFS=',' ;for DISTRO in `echo "$DISTROS"`; do
|
|||
fi;
|
||||
|
||||
# Generate Changlog
|
||||
if [ "$DISTRO" == "focal" ] || [ "$DISTRO" == "buster" ] || [ "$DISTRO" == "hirsute" ]; then
|
||||
if [ "$DISTRO" == "focal" ] || [ "$DISTRO" == "buster" ] || [ "$DISTRO" == "hirsute" ] || [ "$DISTRO" == "impish" ]; then
|
||||
cp -Rpd distros/ubuntu2004 debian
|
||||
elif [ "$DISTRO" == "beowulf" ]
|
||||
then
|
||||
|
@ -285,30 +310,37 @@ zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
|
|||
EOF
|
||||
fi;
|
||||
|
||||
# Leave the .orig so that we don't pollute it when building deps
|
||||
cd ..
|
||||
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
|
||||
# Auto-install all ZoneMinder's depedencies using the Debian control file
|
||||
sudo apt-get install devscripts equivs
|
||||
sudo mk-build-deps -ir $DIRECTORY.orig/debian/control
|
||||
echo "Status: $?"
|
||||
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;
|
||||
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 $DIRECTORY.orig/debian/control
|
||||
echo "Status: $?"
|
||||
DEBUILD="debuild -i -us -uc -b"
|
||||
else
|
||||
# Source build, don't need build depends.
|
||||
DEBUILD="debuild -S -sa"
|
||||
fi;
|
||||
fi;
|
||||
|
||||
cd $DIRECTORY.orig
|
||||
|
||||
if [ "$DEBSIGN_KEYID" != "" ]; then
|
||||
DEBUILD="$DEBUILD -k$DEBSIGN_KEYID"
|
||||
fi
|
||||
# Add any extra options specified on the CLI
|
||||
DEBUILD="$DEBUILD $DEBBUILD_EXTRA"
|
||||
eval $DEBUILD
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error status code is: $?"
|
||||
echo "Error status code is: $?"
|
||||
echo "Build failed.";
|
||||
exit $?;
|
||||
fi;
|
||||
|
@ -340,12 +372,14 @@ EOF
|
|||
dput="Y";
|
||||
if [ "$INTERACTIVE" != "no" ]; then
|
||||
read -p "Ready to dput $SC to $PPA ? Y/n...";
|
||||
if [[ "$REPLY" == [yY] ]]; then
|
||||
if [[ "$REPLY" == "" || "$REPLY" == [yY] ]]; then
|
||||
dput $PPA $SC
|
||||
fi;
|
||||
else
|
||||
echo "dputting to $PPA";
|
||||
dput $PPA $SC
|
||||
if [ "$DPUT" != "no" ]; then
|
||||
echo "dputting to $PPA";
|
||||
dput $PPA $SC
|
||||
fi;
|
||||
fi;
|
||||
fi;
|
||||
done; # foreach distro
|
||||
|
|
|
@ -223,7 +223,7 @@ setdebpkgname () {
|
|||
if [ "" == "$VERSION" ]; then
|
||||
export VERSION="${versionfile}~${thedate}.${numcommits}"
|
||||
fi
|
||||
export RELEASE="${DIST}"
|
||||
export RELEASE="${DIST}${PACKAGE_VERSION}"
|
||||
|
||||
checkvars
|
||||
|
||||
|
@ -369,7 +369,7 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia
|
|||
setdebpkgname
|
||||
movecrud
|
||||
|
||||
if [ "${DIST}" == "focal" ] || [ "${DIST}" == "groovy" ] || [ "${DIST}" == "hirsuit" ] || [ "${DIST}" == "buster" ]; then
|
||||
if [ "${DIST}" == "bionic" ] || [ "${DIST}" == "focal" ] || [ "${DIST}" == "hirsute" ] || [ "${DIST}" == "impish" ] || [ "${DIST}" == "buster" ] || [ "${DIST}" == "bullseye" ]; then
|
||||
ln -sfT distros/ubuntu2004 debian
|
||||
elif [ "${DIST}" == "beowulf" ]; then
|
||||
ln -sfT distros/beowulf debian
|
||||
|
|
|
@ -5,7 +5,7 @@ if ( empty($_REQUEST['id']) && empty($_REQUEST['eids']) ) {
|
|||
ajaxError('No event id(s) supplied');
|
||||
}
|
||||
|
||||
if ( canView('Events') ) {
|
||||
if ( canView('Events') or canView('Snapshots') ) {
|
||||
switch ( $_REQUEST['action'] ) {
|
||||
case 'video' :
|
||||
if ( empty($_REQUEST['videoFormat']) ) {
|
||||
|
@ -74,10 +74,15 @@ if ( canView('Events') ) {
|
|||
else
|
||||
$exportCompress = false;
|
||||
|
||||
if ( !empty($_REQUEST['exportStructure']) )
|
||||
$exportStructure = $_SESSION['export']['structure'] = $_REQUEST['exportStructure'];
|
||||
else
|
||||
$exportStructure = false;
|
||||
|
||||
session_write_close();
|
||||
|
||||
$exportIds = !empty($_REQUEST['eids']) ? $_REQUEST['eids'] : $_REQUEST['id'];
|
||||
if ( $exportFile = exportEvents(
|
||||
if ($exportFile = exportEvents(
|
||||
$exportIds,
|
||||
(isset($_REQUEST['connkey'])?$_REQUEST['connkey']:''),
|
||||
$exportDetail,
|
||||
|
@ -86,11 +91,14 @@ if ( canView('Events') ) {
|
|||
$exportVideo,
|
||||
$exportMisc,
|
||||
$exportFormat,
|
||||
$exportCompress
|
||||
) )
|
||||
ajaxResponse(array('exportFile'=>$exportFile));
|
||||
else
|
||||
$exportCompress,
|
||||
$exportStructure,
|
||||
(!empty($_REQUEST['exportFile'])?$_REQUEST['exportFile']:'zmExport')
|
||||
)) {
|
||||
ajaxResponse(array('exportFile'=>$exportFile));
|
||||
} else {
|
||||
ajaxError('Export Failed');
|
||||
}
|
||||
break;
|
||||
case 'download' :
|
||||
require_once(ZM_SKIN_PATH.'/includes/export_functions.php');
|
||||
|
@ -104,7 +112,7 @@ if ( canView('Events') ) {
|
|||
false,#detail
|
||||
false,#frames
|
||||
false,#images
|
||||
$exportVideo,
|
||||
true, #$exportVideo,
|
||||
false,#Misc
|
||||
$exportFormat,
|
||||
false#,#Compress
|
||||
|
|
|
@ -6,28 +6,30 @@ $data = array();
|
|||
// INITIALIZE AND CHECK SANITY
|
||||
//
|
||||
|
||||
if ( !canView('Events') ) $message = 'Insufficient permissions for user '.$user['Username'];
|
||||
if (!canView('Events'))
|
||||
$message = 'Insufficient permissions for user '.$user['Username'].'<br/>';
|
||||
|
||||
if ( empty($_REQUEST['task']) ) {
|
||||
$message = 'Must specify a task';
|
||||
if (empty($_REQUEST['task'])) {
|
||||
$message = 'Must specify a task<br/>';
|
||||
} else {
|
||||
$task = $_REQUEST['task'];
|
||||
}
|
||||
|
||||
if ( empty($_REQUEST['eids']) ) {
|
||||
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != 'query' ) $message = 'No event id(s) supplied';
|
||||
if (empty($_REQUEST['eids'])) {
|
||||
if (isset($_REQUEST['task']) && $_REQUEST['task'] != 'query')
|
||||
$message = 'No event id(s) supplied<br/>';
|
||||
} else {
|
||||
$eids = $_REQUEST['eids'];
|
||||
}
|
||||
|
||||
if ( $message ) {
|
||||
if ($message) {
|
||||
ajaxError($message);
|
||||
return;
|
||||
}
|
||||
|
||||
require_once('includes/Filter.php');
|
||||
$filter = isset($_REQUEST['filter']) ? ZM\Filter::parse($_REQUEST['filter']) : new ZM\Filter();
|
||||
if ( $user['MonitorIds'] ) {
|
||||
if ($user['MonitorIds']) {
|
||||
$filter = $filter->addTerm(array('cnj'=>'and', 'attr'=>'MonitorId', 'op'=>'IN', 'val'=>$user['MonitorIds']));
|
||||
}
|
||||
|
||||
|
@ -39,10 +41,19 @@ $search = isset($_REQUEST['search']) ? $_REQUEST['search'] : '';
|
|||
$advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'], JSON_OBJECT_AS_ARRAY) : array();
|
||||
|
||||
// Order specifies the sort direction, either asc or desc
|
||||
$order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc')) ? 'ASC' : 'DESC';
|
||||
$order = $filter->sort_asc() ? 'ASC' : 'DESC';
|
||||
if (isset($_REQUEST['order'])) {
|
||||
if (strtolower($_REQUEST['order']) == 'asc') {
|
||||
$order = 'ASC';
|
||||
} else if (strtolower($_REQUEST['order']) == 'desc') {
|
||||
$order = 'DESC';
|
||||
} else {
|
||||
Warning("Invalid value for order " . $_REQUEST['order']);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort specifies the name of the column to sort on
|
||||
$sort = 'StartDateTime';
|
||||
$sort = $filter->sort_field();
|
||||
if (isset($_REQUEST['sort'])) {
|
||||
$sort = $_REQUEST['sort'];
|
||||
if ($sort == 'EndDateTime') {
|
||||
|
@ -56,20 +67,19 @@ if (isset($_REQUEST['sort'])) {
|
|||
|
||||
// Offset specifies the starting row to return, used for pagination
|
||||
$offset = 0;
|
||||
if ( isset($_REQUEST['offset']) ) {
|
||||
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
|
||||
if (isset($_REQUEST['offset'])) {
|
||||
if ((!is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']))) {
|
||||
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
|
||||
} else {
|
||||
$offset = $_REQUEST['offset'];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Limit specifies the number of rows to return
|
||||
// Set the default to 0 for events view, to prevent an issue with ALL pagination
|
||||
$limit = 0;
|
||||
if ( isset($_REQUEST['limit']) ) {
|
||||
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
||||
if (isset($_REQUEST['limit'])) {
|
||||
if ((!is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']))) {
|
||||
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
|
||||
} else {
|
||||
$limit = $_REQUEST['limit'];
|
||||
|
@ -80,25 +90,24 @@ if ( isset($_REQUEST['limit']) ) {
|
|||
// MAIN LOOP
|
||||
//
|
||||
|
||||
switch ( $task ) {
|
||||
switch ($task) {
|
||||
case 'archive' :
|
||||
foreach ( $eids as $eid ) archiveRequest($task, $eid);
|
||||
foreach ($eids as $eid) archiveRequest($task, $eid);
|
||||
break;
|
||||
case 'unarchive' :
|
||||
# The idea is that anyone can archive, but only people with Event Edit permission can unarchive..
|
||||
if ( !canEdit('Events') ) {
|
||||
if (!canEdit('Events')) {
|
||||
ajaxError('Insufficient permissions for user '.$user['Username']);
|
||||
return;
|
||||
}
|
||||
foreach ( $eids as $eid ) archiveRequest($task, $eid);
|
||||
foreach ($eids as $eid) archiveRequest($task, $eid);
|
||||
break;
|
||||
case 'delete' :
|
||||
if ( !canEdit('Events') ) {
|
||||
if (!canEdit('Events')) {
|
||||
ajaxError('Insufficient permissions for user '.$user['Username']);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $eids as $eid ) $data[] = deleteRequest($eid);
|
||||
foreach ($eids as $eid) $data[] = deleteRequest($eid);
|
||||
break;
|
||||
case 'query' :
|
||||
$data = queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit);
|
||||
|
@ -128,6 +137,8 @@ function deleteRequest($eid) {
|
|||
$message[] = array($eid=>'Event not found.');
|
||||
} else if ( $event->Archived() ) {
|
||||
$message[] = array($eid=>'Event is archived, cannot delete it.');
|
||||
} else if (!$event->canEdit()) {
|
||||
$message[] = array($eid=>'You do not have permission to delete event '.$event->Id());
|
||||
} else {
|
||||
$event->delete();
|
||||
}
|
||||
|
@ -136,7 +147,6 @@ function deleteRequest($eid) {
|
|||
}
|
||||
|
||||
function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $limit) {
|
||||
|
||||
$data = array(
|
||||
'total' => 0,
|
||||
'totalNotFiltered' => 0,
|
||||
|
@ -145,7 +155,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
);
|
||||
|
||||
$failed = !$filter->test_pre_sql_conditions();
|
||||
if ( $failed ) {
|
||||
if ($failed) {
|
||||
ZM\Debug('Pre conditions failed, not doing sql');
|
||||
return $data;
|
||||
}
|
||||
|
@ -160,7 +170,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
// The names of columns shown in the event view that are NOT dB columns in the database
|
||||
$col_alt = array('Monitor', 'Storage');
|
||||
|
||||
if ( !in_array($sort, array_merge($columns, $col_alt)) ) {
|
||||
if (!in_array($sort, array_merge($columns, $col_alt))) {
|
||||
ZM\Error('Invalid sort field: ' . $sort);
|
||||
$sort = 'Id';
|
||||
}
|
||||
|
@ -175,7 +185,7 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
|
||||
$storage_areas = ZM\Storage::find();
|
||||
$StorageById = array();
|
||||
foreach ( $storage_areas as $S ) {
|
||||
foreach ($storage_areas as $S) {
|
||||
$StorageById[$S->Id()] = $S;
|
||||
}
|
||||
|
||||
|
@ -184,41 +194,43 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
|
||||
ZM\Debug('Calling the following sql query: ' .$sql);
|
||||
$query = dbQuery($sql, $values);
|
||||
if ( $query ) {
|
||||
while ( $row = dbFetchNext($query) ) {
|
||||
$event = new ZM\Event($row);
|
||||
$event->remove_from_cache();
|
||||
if ( !$filter->test_post_sql_conditions($event) ) {
|
||||
continue;
|
||||
}
|
||||
$event_ids[] = $event->Id();
|
||||
$unfiltered_rows[] = $row;
|
||||
} # end foreach row
|
||||
if (!$query) {
|
||||
ajaxError(dbError($sql));
|
||||
return;
|
||||
}
|
||||
while ($row = dbFetchNext($query)) {
|
||||
$event = new ZM\Event($row);
|
||||
$event->remove_from_cache();
|
||||
if (!$filter->test_post_sql_conditions($event)) {
|
||||
continue;
|
||||
}
|
||||
$event_ids[] = $event->Id();
|
||||
$unfiltered_rows[] = $row;
|
||||
} # end foreach row
|
||||
|
||||
ZM\Debug('Have ' . count($unfiltered_rows) . ' events matching base filter.');
|
||||
|
||||
$filtered_rows = null;
|
||||
|
||||
if ( count($advsearch) or $search != '' ) {
|
||||
if (count($advsearch) or $search != '') {
|
||||
$search_filter = new ZM\Filter();
|
||||
$search_filter = $search_filter->addTerm(array('cnj'=>'and', 'attr'=>'Id', 'op'=>'IN', 'val'=>$event_ids));
|
||||
|
||||
// There are two search bars in the log view, normal and advanced
|
||||
// Making an exuctive decision to ignore the normal search, when advanced search is in use
|
||||
// Alternatively we could try to do both
|
||||
if ( count($advsearch) ) {
|
||||
if (count($advsearch)) {
|
||||
$terms = array();
|
||||
foreach ( $advsearch as $col=>$text ) {
|
||||
foreach ($advsearch as $col=>$text) {
|
||||
$terms[] = array('cnj'=>'and', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$text);
|
||||
} # end foreach col in advsearch
|
||||
$terms[0]['obr'] = 1;
|
||||
$terms[count($terms)-1]['cbr'] = 1;
|
||||
$search_filter->addTerms($terms);
|
||||
} else if ( $search != '' ) {
|
||||
} else if ($search != '') {
|
||||
$search = '%' .$search. '%';
|
||||
$terms = array();
|
||||
foreach ( $columns as $col ) {
|
||||
foreach ($columns as $col) {
|
||||
$terms[] = array('cnj'=>'or', 'attr'=>$col, 'op'=>'LIKE', 'val'=>$search);
|
||||
}
|
||||
$terms[0]['obr'] = 1;
|
||||
|
@ -228,15 +240,17 @@ function queryRequest($filter, $search, $advsearch, $sort, $offset, $order, $lim
|
|||
} # end if search
|
||||
|
||||
$sql = 'SELECT ' .$col_str. ' FROM `Events` AS E INNER JOIN Monitors AS M ON E.MonitorId = M.Id WHERE '.$search_filter->sql().' ORDER BY ' .$sort. ' ' .$order;
|
||||
ZM\Debug('Calling the following sql query: ' .$sql);
|
||||
$filtered_rows = dbFetchAll($sql);
|
||||
ZM\Debug('Have ' . count($filtered_rows) . ' events matching search filter.');
|
||||
ZM\Debug('Have ' . count($filtered_rows) . ' events matching search filter: '.$sql);
|
||||
} else {
|
||||
$filtered_rows = $unfiltered_rows;
|
||||
} # end if search_filter->terms() > 1
|
||||
|
||||
if ($limit)
|
||||
$filtered_rows = array_slice($filtered_rows, $offset, $limit);
|
||||
|
||||
$returned_rows = array();
|
||||
foreach ( array_slice($filtered_rows, $offset, $limit) as $row ) {
|
||||
foreach ($filtered_rows as $row) {
|
||||
$event = new ZM\Event($row);
|
||||
|
||||
$scale = intval(5*100*ZM_WEB_LIST_THUMB_WIDTH / $event->Width());
|
||||
|
|
|
@ -8,21 +8,23 @@ $data = array();
|
|||
// INITIALIZE AND CHECK SANITY
|
||||
//
|
||||
|
||||
if ( !canView('Snapshots') ) $message = 'Insufficient permissions for user '.$user['Username'];
|
||||
if (!canView('Snapshots'))
|
||||
$message = 'Insufficient permissions for user '.$user['Username'];
|
||||
|
||||
if ( empty($_REQUEST['task']) ) {
|
||||
$task = '';
|
||||
if (empty($_REQUEST['task'])) {
|
||||
$message = 'Must specify a task';
|
||||
} else {
|
||||
$task = $_REQUEST['task'];
|
||||
}
|
||||
|
||||
if ( empty($_REQUEST['ids']) ) {
|
||||
if ( isset($_REQUEST['task']) && $_REQUEST['task'] != 'query' ) $message = 'No snapshot id(s) supplied';
|
||||
if (empty($_REQUEST['ids'])) {
|
||||
if ($task != 'query') $message = 'No snapshot id(s) supplied';
|
||||
} else {
|
||||
$eids = $_REQUEST['ids'];
|
||||
$ids = $_REQUEST['ids'];
|
||||
}
|
||||
|
||||
if ( $message ) {
|
||||
if ($message) {
|
||||
ajaxError($message);
|
||||
return;
|
||||
}
|
||||
|
@ -36,15 +38,15 @@ $advsearch = isset($_REQUEST['advsearch']) ? json_decode($_REQUEST['advsearch'],
|
|||
|
||||
// Sort specifies the name of the column to sort on
|
||||
$sort = 'Id';
|
||||
if ( isset($_REQUEST['sort']) ) {
|
||||
if (isset($_REQUEST['sort'])) {
|
||||
$sort = $_REQUEST['sort'];
|
||||
}
|
||||
|
||||
// Offset specifies the starting row to return, used for pagination
|
||||
$offset = 0;
|
||||
if ( isset($_REQUEST['offset']) ) {
|
||||
if ( ( !is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']) ) ) {
|
||||
ZM\Error('Invalid value for offset: ' . $_REQUEST['offset']);
|
||||
if (isset($_REQUEST['offset'])) {
|
||||
if ((!is_int($_REQUEST['offset']) and !ctype_digit($_REQUEST['offset']))) {
|
||||
ZM\Error('Invalid value for offset: '.$_REQUEST['offset']);
|
||||
} else {
|
||||
$offset = $_REQUEST['offset'];
|
||||
}
|
||||
|
@ -56,8 +58,8 @@ $order = (isset($_REQUEST['order']) and (strtolower($_REQUEST['order']) == 'asc'
|
|||
// Limit specifies the number of rows to return
|
||||
// Set the default to 0 for events view, to prevent an issue with ALL pagination
|
||||
$limit = 0;
|
||||
if ( isset($_REQUEST['limit']) ) {
|
||||
if ( ( !is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']) ) ) {
|
||||
if (isset($_REQUEST['limit'])) {
|
||||
if ((!is_int($_REQUEST['limit']) and !ctype_digit($_REQUEST['limit']))) {
|
||||
ZM\Error('Invalid value for limit: ' . $_REQUEST['limit']);
|
||||
} else {
|
||||
$limit = $_REQUEST['limit'];
|
||||
|
@ -68,14 +70,14 @@ if ( isset($_REQUEST['limit']) ) {
|
|||
// MAIN LOOP
|
||||
//
|
||||
|
||||
switch ( $task ) {
|
||||
switch ($task) {
|
||||
case 'delete' :
|
||||
if ( !canEdit('Snapshots') ) {
|
||||
if (!canEdit('Snapshots')) {
|
||||
ajaxError('Insufficient permissions for user '.$user['Username']);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $ids as $id ) $data[] = deleteRequest($id);
|
||||
foreach ($ids as $id) $data[] = deleteRequest($id);
|
||||
break;
|
||||
case 'query' :
|
||||
$data = queryRequest($search, $advsearch, $sort, $offset, $order, $limit);
|
||||
|
@ -99,6 +101,7 @@ function deleteRequest($id) {
|
|||
//$message[] = array($id=>'Event is archived, cannot delete it.');
|
||||
} else {
|
||||
$snapshot->delete();
|
||||
$message[] = array($id=>'Snapshot deleted.');
|
||||
}
|
||||
|
||||
return $message;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
|
||||
if ( empty($_REQUEST['eid']) ) ajaxError('Event Id Not Provided');
|
||||
if ( empty($_REQUEST['fid']) ) ajaxError('Frame Id Not Provided');
|
||||
if (empty($_REQUEST['eid'])) ajaxError('Event Id Not Provided');
|
||||
if (empty($_REQUEST['fid'])) ajaxError('Frame Id Not Provided');
|
||||
|
||||
$eid = $_REQUEST['eid'];
|
||||
$fid = $_REQUEST['fid'];
|
||||
|
@ -9,32 +8,26 @@ $row = ( isset($_REQUEST['row']) ) ? $_REQUEST['row'] : '';
|
|||
$raw = isset($_REQUEST['raw']);
|
||||
$data = array();
|
||||
|
||||
// Not sure if this is required
|
||||
if ( ZM_OPT_USE_AUTH && (ZM_AUTH_RELAY == 'hashed') ) {
|
||||
$auth_hash = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
if ( isset($_REQUEST['auth']) and ($_REQUEST['auth'] != $auth_hash) ) {
|
||||
$data['auth'] = $auth_hash;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $raw ) {
|
||||
$sql = 'SELECT S.*,E.*,Z.Name AS ZoneName,Z.Units,Z.Area,M.Name AS MonitorName FROM Stats AS S LEFT JOIN Events AS E ON S.EventId = E.Id LEFT JOIN Zones AS Z ON S.ZoneId = Z.Id LEFT JOIN Monitors AS M ON E.MonitorId = M.Id WHERE S.EventId = ? AND S.FrameId = ? ORDER BY S.ZoneId';
|
||||
$stat = dbFetchOne( $sql, NULL, array( $eid, $fid ) );
|
||||
if ( $stat ) {
|
||||
if ($raw) {
|
||||
$sql = 'SELECT S.*,E.*,Z.Name AS ZoneName,Z.Units,Z.Area,M.Name AS MonitorName
|
||||
FROM Stats AS S LEFT JOIN Events AS E ON S.EventId = E.Id LEFT JOIN Zones AS Z ON S.ZoneId = Z.Id LEFT JOIN Monitors AS M ON E.MonitorId = M.Id
|
||||
WHERE S.EventId = ? AND S.FrameId = ? ORDER BY S.ZoneId';
|
||||
$stats = dbFetchAll($sql, NULL, array($eid, $fid));
|
||||
foreach ($stats as $stat) {
|
||||
$stat['ZoneName'] = validHtmlStr($stat['ZoneName']);
|
||||
$stat['PixelDiff'] = validHtmlStr($stat['PixelDiff']);
|
||||
$stat['AlarmPixels'] = sprintf( "%d (%d%%)", $stat['AlarmPixels'], (100*$stat['AlarmPixels']/$stat['Area']) );
|
||||
$stat['FilterPixels'] = sprintf( "%d (%d%%)", $stat['FilterPixels'], (100*$stat['FilterPixels']/$stat['Area']) );
|
||||
$stat['BlobPixels'] = sprintf( "%d (%d%%)", $stat['BlobPixels'], (100*$stat['BlobPixels']/$stat['Area']) );
|
||||
$stat['AlarmPixels'] = sprintf('%d (%d%%)', $stat['AlarmPixels'], (100*$stat['AlarmPixels']/$stat['Area']));
|
||||
$stat['FilterPixels'] = sprintf('%d (%d%%)', $stat['FilterPixels'], (100*$stat['FilterPixels']/$stat['Area']));
|
||||
$stat['BlobPixels'] = sprintf('%d (%d%%)', $stat['BlobPixels'], (100*$stat['BlobPixels']/$stat['Area']));
|
||||
$stat['Blobs'] = validHtmlStr($stat['Blobs']);
|
||||
if ( $stat['Blobs'] > 1 ) {
|
||||
$stat['BlobSizes'] = sprintf( "%d-%d (%d%%-%d%%)", $stat['MinBlobSize'], $stat['MaxBlobSize'], (100*$stat['MinBlobSize']/$stat['Area']), (100*$stat['MaxBlobSize']/$stat['Area']) );
|
||||
if ($stat['Blobs'] > 1) {
|
||||
$stat['BlobSizes'] = sprintf('%d-%d (%d%%-%d%%)', $stat['MinBlobSize'], $stat['MaxBlobSize'], (100*$stat['MinBlobSize']/$stat['Area']), (100*$stat['MaxBlobSize']/$stat['Area']));
|
||||
} else {
|
||||
$stat['BlobSizes'] = sprintf( "%d (%d%%)", $stat['MinBlobSize'], 100*$stat['MinBlobSize']/$stat['Area'] );
|
||||
$stat['BlobSizes'] = sprintf('%d (%d%%)', $stat['MinBlobSize'], 100*$stat['MinBlobSize']/$stat['Area']);
|
||||
}
|
||||
$stat['AlarmLimits'] = validHtmlStr($stat['MinX'].",".$stat['MinY']."-".$stat['MaxX'].",".$stat['MaxY']);
|
||||
}
|
||||
$data['raw'] = $stat;
|
||||
$stat['AlarmLimits'] = validHtmlStr($stat['MinX'].','.$stat['MinY'].'-'.$stat['MaxX'].','.$stat['MaxY']);
|
||||
$data['raw'][] = $stat;
|
||||
} # end foreach stat/zone
|
||||
} else {
|
||||
$data['html'] = getStatsTableHTML($eid, $fid, $row);
|
||||
$data['id'] = '#contentStatsTable' .$row;
|
||||
|
|
|
@ -266,7 +266,7 @@ class MonitorsController extends AppController {
|
|||
if ($mToken) {
|
||||
$auth = ' -T '.$mToken;
|
||||
} else if (ZM_AUTH_RELAY == 'hashed') {
|
||||
$auth = ' -A '.calculateAuthHash(ZM_AUTH_HASH_IPS?$_SERVER['REMOTE_ADDR']:'');
|
||||
$auth = ' -A '.calculateAuthHash(''); # Can't do REMOTE_IP because zmu doesn't normally have access to it.
|
||||
} else if (ZM_AUTH_RELAY == 'plain') {
|
||||
# Plain requires the plain text password which must either be in request or stored in session
|
||||
$password = $this->request->query('pass') ? $this->request->query('pass') : $this->request->data('pass');;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 0bd63fb464957080ead342db58ca9e01532cf1ef
|
||||
Subproject commit 14292374ccf1328f2d5db20897bd06f99ba4d938
|
|
@ -1,6 +1,12 @@
|
|||
|
||||
ZoneMinder uses certain 3rd party media assets/libraries for UI display purposes. Their licenses are listed in this file
|
||||
|
||||
### Font Awesome icons
|
||||
|
||||
Origin: http://fontawesome.io
|
||||
|
||||
License: Font: SIL OFL 1.1, CSS: MIT License (http://fontawesome.io/license)
|
||||
|
||||
### Material Design icons
|
||||
|
||||
Origin: https://github.com/google/material-design-icons
|
||||
|
|
|
@ -627,6 +627,46 @@ class Event extends ZM_Object {
|
|||
return 'Unknown reason';
|
||||
}
|
||||
|
||||
function canView($u=null) {
|
||||
global $user;
|
||||
if (!$u) $u=$user;
|
||||
if (!$u) {
|
||||
# auth turned on and not logged in
|
||||
return false;
|
||||
}
|
||||
if (!empty($u['MonitorIds']) ) {
|
||||
if (in_array($this->{'MonitorId'}, explode(',', $u['MonitorIds']))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ($u['Events'] != 'None') {
|
||||
return true;
|
||||
}
|
||||
if ($u['Snapshots'] != 'None') {
|
||||
# If the event is contained in a snapshot, then we can still view it.
|
||||
if (dbFetchOne('SELECT * FROM Snapshot_Events WHERE EventId=?', $this->Id()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function canEdit($u=null) {
|
||||
global $user;
|
||||
if (!$u) $u=$user;
|
||||
if (!$u) {
|
||||
# auth turned on and not logged in
|
||||
return false;
|
||||
}
|
||||
if (!empty($u['MonitorIds']) ) {
|
||||
if (!in_array($this->{'MonitorId'}, explode(',', $u['MonitorIds']))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($u['Events'] != 'Edit') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} # end class
|
||||
|
||||
?>
|
||||
|
|
|
@ -60,6 +60,9 @@ class Filter extends ZM_Object {
|
|||
foreach ( $this->FilterTerms() as $term ) {
|
||||
$this->_querystring .= $term->querystring($objectname, $separator);
|
||||
} # end foreach term
|
||||
$this->_querystring .= $separator.urlencode($objectname.'[Query][sort_asc]').'='.$this->sort_asc();
|
||||
$this->_querystring .= $separator.urlencode($objectname.'[Query][sort_field]').'='.$this->sort_field();
|
||||
$this->_querystring .= $separator.urlencode($objectname.'[Query][limit]').'='.$this->limit();
|
||||
if ( $this->Id() ) {
|
||||
$this->_querystring .= $separator.$objectname.urlencode('[Id]').'='.$this->Id();
|
||||
}
|
||||
|
|
|
@ -167,9 +167,15 @@ class Group extends ZM_Object {
|
|||
public function Parents() {
|
||||
$Parents = array();
|
||||
$Parent = $this->Parent();
|
||||
while( $Parent ) {
|
||||
$seen_parents = array();
|
||||
while ($Parent) {
|
||||
$seen_parents[$Parent->Id()] = $Parent;
|
||||
array_unshift($Parents, $Parent);
|
||||
$Parent = $Parent->Parent();
|
||||
if ($Parent and isset($seen_parents[$Parent->Id()])) {
|
||||
Warning("Detected hierarchy loop in group {$Parent->Name()}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $Parents;
|
||||
}
|
||||
|
@ -189,6 +195,9 @@ class Group extends ZM_Object {
|
|||
public function canView($u=null) {
|
||||
global $user;
|
||||
if (!$u) $u = $user;
|
||||
if (!count($this->Monitors()) and !count($this->Children())) {
|
||||
return true;
|
||||
}
|
||||
# Can view if we can view any of the monitors in it.
|
||||
foreach ($this->Monitors() as $monitor) {
|
||||
if ($monitor->canView($u)) return true;
|
||||
|
|
|
@ -10,7 +10,7 @@ require_once('Group.php');
|
|||
$FunctionTypes = null;
|
||||
|
||||
function getMonitorFunctionTypes() {
|
||||
if ( !isset($FunctionTypes ) ) {
|
||||
if (!isset($FunctionTypes)) {
|
||||
$FunctionTypes = array(
|
||||
'None' => translate('FnNone'),
|
||||
'Monitor' => translate('FnMonitor'),
|
||||
|
@ -23,6 +23,21 @@ function getMonitorFunctionTypes() {
|
|||
return $FunctionTypes;
|
||||
}
|
||||
|
||||
$Statuses = null;
|
||||
function getMonitorStatuses() {
|
||||
if (!isset($Statuses)) {
|
||||
$Statuses = array(
|
||||
-1 => 'Unknown',
|
||||
0 => 'Idle',
|
||||
1 => 'PreAlarm',
|
||||
2 => 'Alarm',
|
||||
3 => 'Alert',
|
||||
4 => 'Tape'
|
||||
);
|
||||
}
|
||||
return $Statuses;
|
||||
}
|
||||
|
||||
class Monitor extends ZM_Object {
|
||||
protected static $table = 'Monitors';
|
||||
|
||||
|
@ -126,6 +141,7 @@ class Monitor extends ZM_Object {
|
|||
'Longitude' => null,
|
||||
'RTSPServer' => array('type'=>'boolean', 'default'=>0),
|
||||
'RTSPStreamName' => '',
|
||||
'Importance' => 'Normal',
|
||||
);
|
||||
private $status_fields = array(
|
||||
'Status' => null,
|
||||
|
@ -149,46 +165,46 @@ class Monitor extends ZM_Object {
|
|||
);
|
||||
|
||||
public function Control() {
|
||||
if ( !property_exists($this, 'Control') ) {
|
||||
if ( $this->ControlId() )
|
||||
if (!property_exists($this, 'Control')) {
|
||||
if ($this->ControlId())
|
||||
$this->{'Control'} = Control::find_one(array('Id'=>$this->{'ControlId'}));
|
||||
|
||||
if ( !(property_exists($this, 'Control') and $this->{'Control'}) )
|
||||
if (!(property_exists($this, 'Control') and $this->{'Control'}))
|
||||
$this->{'Control'} = new Control();
|
||||
}
|
||||
return $this->{'Control'};
|
||||
}
|
||||
|
||||
public function Server() {
|
||||
if ( !property_exists($this, 'Server') ) {
|
||||
if ( $this->ServerId() )
|
||||
if (!property_exists($this, 'Server')) {
|
||||
if ($this->ServerId())
|
||||
$this->{'Server'} = Server::find_one(array('Id'=>$this->{'ServerId'}));
|
||||
if ( !property_exists($this, 'Server') ) {
|
||||
if (!property_exists($this, 'Server')) {
|
||||
$this->{'Server'} = new Server();
|
||||
}
|
||||
}
|
||||
return $this->{'Server'};
|
||||
}
|
||||
|
||||
public function __call($fn, array $args){
|
||||
if ( count($args) ) {
|
||||
if ( is_array($this->defaults[$fn]) and $this->defaults[$fn]['type'] == 'set' ) {
|
||||
public function __call($fn, array $args) {
|
||||
if (count($args)) {
|
||||
if (is_array($this->defaults[$fn]) and $this->defaults[$fn]['type'] == 'set') {
|
||||
$this->{$fn} = is_array($args[0]) ? implode(',', $args[0]) : $args[0];
|
||||
} else {
|
||||
$this->{$fn} = $args[0];
|
||||
}
|
||||
}
|
||||
if ( property_exists($this, $fn) ) {
|
||||
if (property_exists($this, $fn)) {
|
||||
return $this->{$fn};
|
||||
} else if ( array_key_exists($fn, $this->defaults) ) {
|
||||
} else if (array_key_exists($fn, $this->defaults)) {
|
||||
if ( is_array($this->defaults[$fn]) ) {
|
||||
return $this->defaults[$fn]['default'];
|
||||
}
|
||||
return $this->defaults[$fn];
|
||||
} else if ( array_key_exists($fn, $this->status_fields) ) {
|
||||
} else if (array_key_exists($fn, $this->status_fields)) {
|
||||
$sql = 'SELECT * FROM `Monitor_Status` WHERE `MonitorId`=?';
|
||||
$row = dbFetchOne($sql, NULL, array($this->{'Id'}));
|
||||
if ( !$row ) {
|
||||
if (!$row) {
|
||||
Warning('Unable to load Monitor status record for Id='.$this->{'Id'}.' using '.$sql);
|
||||
return null;
|
||||
} else {
|
||||
|
@ -197,10 +213,10 @@ class Monitor extends ZM_Object {
|
|||
}
|
||||
}
|
||||
return $this->{$fn};
|
||||
} else if ( array_key_exists($fn, $this->summary_fields) ) {
|
||||
} else if (array_key_exists($fn, $this->summary_fields)) {
|
||||
$sql = 'SELECT * FROM `Event_Summaries` WHERE `MonitorId`=?';
|
||||
$row = dbFetchOne($sql, NULL, array($this->{'Id'}));
|
||||
if ( !$row ) {
|
||||
if (!$row) {
|
||||
Warning('Unable to load Event Summary record for Id='.$this->{'Id'}.' using '.$sql);
|
||||
return null;
|
||||
} else {
|
||||
|
@ -218,7 +234,6 @@ class Monitor extends ZM_Object {
|
|||
}
|
||||
|
||||
public function getStreamSrc($args, $querySep='&') {
|
||||
|
||||
$streamSrc = $this->Server()->UrlToZMS(
|
||||
ZM_MIN_STREAMING_PORT ?
|
||||
ZM_MIN_STREAMING_PORT+$this->{'Id'} :
|
||||
|
@ -226,8 +241,8 @@ class Monitor extends ZM_Object {
|
|||
|
||||
$args['monitor'] = $this->{'Id'};
|
||||
|
||||
if ( ZM_OPT_USE_AUTH ) {
|
||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
if (ZM_OPT_USE_AUTH) {
|
||||
if (ZM_AUTH_RELAY == 'hashed') {
|
||||
$args['auth'] = generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
} elseif ( ZM_AUTH_RELAY == 'plain' ) {
|
||||
$args['user'] = $_SESSION['username'];
|
||||
|
@ -236,24 +251,24 @@ class Monitor extends ZM_Object {
|
|||
$args['user'] = $_SESSION['username'];
|
||||
}
|
||||
}
|
||||
if ( (!isset($args['mode'])) or ( $args['mode'] != 'single' ) ) {
|
||||
if ((!isset($args['mode'])) or ($args['mode'] != 'single')) {
|
||||
$args['connkey'] = $this->connKey();
|
||||
}
|
||||
if ( ZM_RAND_STREAM ) {
|
||||
if (ZM_RAND_STREAM) {
|
||||
$args['rand'] = time();
|
||||
}
|
||||
|
||||
# zms doesn't support width & height, so if no scale is set, default it
|
||||
if ( ! isset($args['scale']) ) {
|
||||
if ( isset($args['width']) and intval($args['width']) ) {
|
||||
if (!isset($args['scale'])) {
|
||||
if (isset($args['width']) and intval($args['width'])) {
|
||||
$args['scale'] = intval((100*intval($args['width']))/$this->ViewWidth());
|
||||
} else if ( isset($args['height']) and intval($args['height']) ) {
|
||||
} else if (isset($args['height']) and intval($args['height'])) {
|
||||
$args['scale'] = intval((100*intval($args['height']))/$this->ViewHeight());
|
||||
}
|
||||
}
|
||||
if ( isset($args['width']) )
|
||||
if (isset($args['width']))
|
||||
unset($args['width']);
|
||||
if ( isset($args['height']) )
|
||||
if (isset($args['height']))
|
||||
unset($args['height']);
|
||||
|
||||
$streamSrc .= '?'.http_build_query($args, '', $querySep);
|
||||
|
@ -269,21 +284,21 @@ class Monitor extends ZM_Object {
|
|||
}
|
||||
|
||||
public function ViewWidth($new = null) {
|
||||
if ( $new )
|
||||
if ($new)
|
||||
$this->{'Width'} = $new;
|
||||
|
||||
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Height' : 'Width';
|
||||
if ( property_exists($this, $field) )
|
||||
if (property_exists($this, $field))
|
||||
return $this->{$field};
|
||||
return $this->defaults[$field];
|
||||
} // end function Width
|
||||
|
||||
public function ViewHeight($new=null) {
|
||||
if ( $new )
|
||||
if ($new)
|
||||
$this->{'Height'} = $new;
|
||||
|
||||
$field = ( $this->Orientation() == 'ROTATE_90' or $this->Orientation() == 'ROTATE_270' ) ? 'Width' : 'Height';
|
||||
if ( property_exists($this, $field) )
|
||||
if (property_exists($this, $field))
|
||||
return $this->{$field};
|
||||
return $this->defaults[$field];
|
||||
} // end function Height
|
||||
|
@ -296,50 +311,50 @@ class Monitor extends ZM_Object {
|
|||
|
||||
// Validate that it's a valid colour (we seem to allow color names, not just hex).
|
||||
// This also helps prevent XSS.
|
||||
if ( property_exists($this, $field) && preg_match('/^[#0-9a-zA-Z]+$/', $this->{$field})) {
|
||||
if (property_exists($this, $field) && preg_match('/^[#0-9a-zA-Z]+$/', $this->{$field})) {
|
||||
return $this->{$field};
|
||||
}
|
||||
return $this->defaults[$field];
|
||||
} // end function SignalCheckColour
|
||||
|
||||
public static function find( $parameters = array(), $options = array() ) {
|
||||
public static function find($parameters = array(), $options = array()) {
|
||||
return ZM_Object::_find(get_class(), $parameters, $options);
|
||||
}
|
||||
|
||||
public static function find_one( $parameters = array(), $options = array() ) {
|
||||
public static function find_one($parameters = array(), $options = array()) {
|
||||
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
||||
}
|
||||
|
||||
function zmcControl( $mode=false ) {
|
||||
if ( !(property_exists($this,'Id') and $this->{'Id'}) ) {
|
||||
function zmcControl($mode=false) {
|
||||
if (!(property_exists($this,'Id') and $this->{'Id'})) {
|
||||
Warning('Attempt to control a monitor with no Id');
|
||||
return;
|
||||
}
|
||||
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||
if ( $this->Type() == 'Local' ) {
|
||||
if ((!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) )) {
|
||||
if ($this->Type() == 'Local') {
|
||||
$zmcArgs = '-d '.$this->{'Device'};
|
||||
} else {
|
||||
$zmcArgs = '-m '.$this->{'Id'};
|
||||
}
|
||||
|
||||
if ( $mode == 'stop' ) {
|
||||
if ($mode == 'stop') {
|
||||
daemonControl('stop', 'zmc', $zmcArgs);
|
||||
} else {
|
||||
if ( $mode == 'restart' ) {
|
||||
if ($mode == 'restart') {
|
||||
daemonControl('stop', 'zmc', $zmcArgs);
|
||||
}
|
||||
if ( $this->{'Function'} != 'None' ) {
|
||||
if ($this->{'Function'} != 'None') {
|
||||
daemonControl('start', 'zmc', $zmcArgs);
|
||||
}
|
||||
}
|
||||
} else if ( $this->ServerId() ) {
|
||||
} else if ($this->ServerId()) {
|
||||
$Server = $this->Server();
|
||||
|
||||
$url = $Server->UrlToApi().'/monitors/daemonControl/'.$this->{'Id'}.'/'.$mode.'/zmc.json';
|
||||
if ( ZM_OPT_USE_AUTH ) {
|
||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
if (ZM_OPT_USE_AUTH) {
|
||||
if (ZM_AUTH_RELAY == 'hashed') {
|
||||
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||
} else if (ZM_AUTH_RELAY == 'plain') {
|
||||
$url .= '?user='.$_SESSION['username'];
|
||||
$url .= '?pass='.$_SESSION['password'];
|
||||
} else {
|
||||
|
@ -349,13 +364,13 @@ class Monitor extends ZM_Object {
|
|||
}
|
||||
Debug('sending command to '.$url);
|
||||
|
||||
$context = stream_context_create();
|
||||
$context = stream_context_create();
|
||||
try {
|
||||
$result = file_get_contents($url, false, $context);
|
||||
if ( $result === FALSE ) { /* Handle error */
|
||||
if ($result === FALSE) { /* Handle error */
|
||||
Error("Error restarting zmc using $url");
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
} catch (Exception $e) {
|
||||
Error("Except $e thrown trying to restart zmc");
|
||||
}
|
||||
} else {
|
||||
|
@ -363,19 +378,19 @@ class Monitor extends ZM_Object {
|
|||
}
|
||||
} // end function zmcControl
|
||||
|
||||
public function GroupIds( $new='' ) {
|
||||
if ( $new != '' ) {
|
||||
if ( !is_array($new) ) {
|
||||
public function GroupIds($new='') {
|
||||
if ($new != '') {
|
||||
if (!is_array($new)) {
|
||||
$this->{'GroupIds'} = array($new);
|
||||
} else {
|
||||
$this->{'GroupIds'} = $new;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !property_exists($this, 'GroupIds') ) {
|
||||
if ( property_exists($this, 'Id') and $this->{'Id'} ) {
|
||||
$this->{'GroupIds'} = dbFetchAll('SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=?', 'GroupId', array($this->{'Id'}) );
|
||||
if ( ! $this->{'GroupIds'} )
|
||||
if (!property_exists($this, 'GroupIds')) {
|
||||
if (property_exists($this, 'Id') and $this->{'Id'}) {
|
||||
$this->{'GroupIds'} = dbFetchAll('SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=?', 'GroupId', array($this->{'Id'}));
|
||||
if (!$this->{'GroupIds'})
|
||||
$this->{'GroupIds'} = array();
|
||||
} else {
|
||||
$this->{'GroupIds'} = array();
|
||||
|
@ -385,8 +400,8 @@ class Monitor extends ZM_Object {
|
|||
}
|
||||
|
||||
public function delete() {
|
||||
if ( ! $this->{'Id'} ) {
|
||||
Warning("Attempt to delete a monitor without id.");
|
||||
if (!$this->{'Id'}) {
|
||||
Warning('Attempt to delete a monitor without id.');
|
||||
return;
|
||||
}
|
||||
$this->zmcControl('stop');
|
||||
|
@ -394,17 +409,17 @@ class Monitor extends ZM_Object {
|
|||
// If fast deletes are on, then zmaudit will clean everything else up later
|
||||
// If fast deletes are off and there are lots of events then this step may
|
||||
// well time out before completing, in which case zmaudit will still tidy up
|
||||
if ( !ZM_OPT_FAST_DELETE ) {
|
||||
if (!ZM_OPT_FAST_DELETE) {
|
||||
$markEids = dbFetchAll('SELECT Id FROM Events WHERE MonitorId=?', 'Id', array($this->{'Id'}));
|
||||
foreach ($markEids as $markEid)
|
||||
deleteEvent($markEid);
|
||||
|
||||
if ( $this->{'Name'} )
|
||||
if ($this->{'Name'})
|
||||
deletePath(ZM_DIR_EVENTS.'/'.basename($this->{'Name'}));
|
||||
deletePath(ZM_DIR_EVENTS.'/'.$this->{'Id'});
|
||||
$Storage = $this->Storage();
|
||||
if ( $Storage->Path() != ZM_DIR_EVENTS ) {
|
||||
if ( $this->{'Name'} )
|
||||
if ($Storage->Path() != ZM_DIR_EVENTS) {
|
||||
if ($this->{'Name'})
|
||||
deletePath($Storage->Path().'/'.basename($this->{'Name'}));
|
||||
deletePath($Storage->Path().'/'.$this->{'Id'});
|
||||
}
|
||||
|
@ -420,14 +435,14 @@ class Monitor extends ZM_Object {
|
|||
} // end function delete
|
||||
|
||||
public function Storage($new = null) {
|
||||
if ( $new ) {
|
||||
if ($new) {
|
||||
$this->{'Storage'} = $new;
|
||||
}
|
||||
if ( ! ( property_exists($this, 'Storage') and $this->{'Storage'} ) ) {
|
||||
if (!(property_exists($this, 'Storage') and $this->{'Storage'})) {
|
||||
$this->{'Storage'} = isset($this->{'StorageId'}) ?
|
||||
Storage::find_one(array('Id'=>$this->{'StorageId'})) :
|
||||
new Storage(NULL);
|
||||
if ( ! $this->{'Storage'} )
|
||||
if (!$this->{'Storage'})
|
||||
$this->{'Storage'} = new Storage(NULL);
|
||||
}
|
||||
return $this->{'Storage'};
|
||||
|
@ -435,42 +450,42 @@ class Monitor extends ZM_Object {
|
|||
|
||||
public function Source( ) {
|
||||
$source = '';
|
||||
if ( $this->{'Type'} == 'Local' ) {
|
||||
if ($this->{'Type'} == 'Local') {
|
||||
$source = $this->{'Device'}.' ('.$this->{'Channel'}.')';
|
||||
} else if ( $this->{'Type'} == 'Remote' ) {
|
||||
$source = preg_replace( '/^.*@/', '', $this->{'Host'} );
|
||||
if ( $this->{'Port'} != '80' and $this->{'Port'} != '554' ) {
|
||||
} else if ($this->{'Type'} == 'Remote') {
|
||||
$source = preg_replace('/^.*@/', '', $this->{'Host'});
|
||||
if ($this->{'Port'} != '80' and $this->{'Port'} != '554') {
|
||||
$source .= ':'.$this->{'Port'};
|
||||
}
|
||||
} else if ( $this->{'Type'} == 'VNC' ) {
|
||||
} else if ($this->{'Type'} == 'VNC') {
|
||||
$source = preg_replace( '/^.*@/', '', $this->{'Host'} );
|
||||
if ( $this->{'Port'} != '5900' ) {
|
||||
if ($this->{'Port'} != '5900') {
|
||||
$source .= ':'.$this->{'Port'};
|
||||
}
|
||||
} else if ( $this->{'Type'} == 'Ffmpeg' || $this->{'Type'} == 'Libvlc' || $this->{'Type'} == 'WebSite' ) {
|
||||
$url_parts = parse_url( $this->{'Path'} );
|
||||
if ( ZM_WEB_FILTER_SOURCE == 'Hostname' ) {
|
||||
} else if ($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
|
||||
if ( isset($url_parts['host']) ) {
|
||||
if (isset($url_parts['host'])) {
|
||||
$source = $url_parts['host'];
|
||||
} else {
|
||||
$source = $this->{'Path'};
|
||||
}
|
||||
} else if ( ZM_WEB_FILTER_SOURCE == 'NoCredentials' ) {
|
||||
} else if (ZM_WEB_FILTER_SOURCE == 'NoCredentials') {
|
||||
# Filter out sensitive and common items
|
||||
unset($url_parts['user']);
|
||||
unset($url_parts['pass']);
|
||||
#unset($url_parts['scheme']);
|
||||
unset($url_parts['query']);
|
||||
#unset($url_parts['path']);
|
||||
if ( isset($url_parts['port']) and ( $url_parts['port'] == '80' or $url_parts['port'] == '554' ) )
|
||||
if (isset($url_parts['port']) and ($url_parts['port'] == '80' or $url_parts['port'] == '554'))
|
||||
unset($url_parts['port']);
|
||||
$source = unparse_url($url_parts);
|
||||
} else { # Don't filter anything
|
||||
$source = $this->{'Path'};
|
||||
}
|
||||
}
|
||||
if ( $source == '' ) {
|
||||
if ($source == '') {
|
||||
$source = 'Monitor ' . $this->{'Id'};
|
||||
}
|
||||
return $source;
|
||||
|
@ -478,7 +493,10 @@ class Monitor extends ZM_Object {
|
|||
|
||||
public function UrlToIndex($port=null) {
|
||||
return $this->Server()->UrlToIndex($port);
|
||||
//ZM_MIN_STREAMING_PORT ? (ZM_MIN_STREAMING_PORT+$this->Id()) : null);
|
||||
}
|
||||
|
||||
public function UrlToZMS($port=null) {
|
||||
return $this->Server()->UrlToZMS($port).'?mid='.$this->Id();
|
||||
}
|
||||
|
||||
public function sendControlCommand($command) {
|
||||
|
@ -486,78 +504,78 @@ class Monitor extends ZM_Object {
|
|||
|
||||
$options = array();
|
||||
# Convert from a command line params to an option array
|
||||
foreach ( explode(' ', $command) as $option ) {
|
||||
if ( preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches) ) {
|
||||
foreach (explode(' ', $command) as $option) {
|
||||
if (preg_match('/--([^=]+)(?:=(.+))?/', $option, $matches)) {
|
||||
$options[$matches[1]] = $matches[2]?$matches[2]:1;
|
||||
} else if ( $option != '' and $option != 'quit' and $option != 'start' and $option != 'stop' ) {
|
||||
} else if ($option != '' and $option != 'quit' and $option != 'start' and $option != 'stop') {
|
||||
Warning("Ignored command for zmcontrol $option in $command");
|
||||
}
|
||||
}
|
||||
if ( !count($options) ) {
|
||||
if ( $command == 'quit' or $command == 'start' or $command == 'stop' ) {
|
||||
if (!count($options)) {
|
||||
if ($command == 'quit' or $command == 'start' or $command == 'stop') {
|
||||
# These are special as we now run zmcontrol as a daemon through zmdc.
|
||||
$status = daemonStatus('zmcontrol.pl', array('--id', $this->{'Id'}));
|
||||
Debug("Current status $status");
|
||||
if ( $status or ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) ) {
|
||||
if ($status or ((!defined('ZM_SERVER_ID')) or (property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'})))) {
|
||||
daemonControl($command, 'zmcontrol.pl', '--id '.$this->{'Id'});
|
||||
return;
|
||||
}
|
||||
$options['command'] = $command;
|
||||
} else {
|
||||
Warning("No commands to send to zmcontrol from $command");
|
||||
Warning('No commands to send to zmcontrol from '.$command);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||
if ((!defined('ZM_SERVER_ID')) or (property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}))) {
|
||||
# Local
|
||||
Debug('Trying to send options ' . print_r($options, true));
|
||||
|
||||
$optionString = jsonEncode($options);
|
||||
Debug("Trying to send options $optionString");
|
||||
Debug('Trying to send options '.$optionString);
|
||||
// Either connects to running zmcontrol.pl or runs zmcontrol.pl to send the command.
|
||||
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
|
||||
if ( $socket < 0 ) {
|
||||
if ($socket < 0) {
|
||||
Error('socket_create() failed: '.socket_strerror($socket));
|
||||
return false;
|
||||
}
|
||||
$sockFile = ZM_PATH_SOCKS.'/zmcontrol-'.$this->{'Id'}.'.sock';
|
||||
if ( @socket_connect($socket, $sockFile) ) {
|
||||
if ( !socket_write($socket, $optionString) ) {
|
||||
if (@socket_connect($socket, $sockFile)) {
|
||||
if (!socket_write($socket, $optionString)) {
|
||||
Error('Can\'t write to control socket: '.socket_strerror(socket_last_error($socket)));
|
||||
return false;
|
||||
}
|
||||
} else if ( $command != 'quit' ) {
|
||||
} else if ($command != 'quit') {
|
||||
$command = ZM_PATH_BIN.'/zmcontrol.pl '.$command.' --id '.$this->{'Id'};
|
||||
|
||||
// Can't connect so use script
|
||||
$ctrlOutput = exec(escapeshellcmd($command));
|
||||
}
|
||||
socket_close($socket);
|
||||
} else if ( $this->ServerId() ) {
|
||||
} else if ($this->ServerId()) {
|
||||
$Server = $this->Server();
|
||||
|
||||
$url = $Server->UrlToApi().'/monitors/daemonControl/'.$this->{'Id'}.'/'.$command.'/zmcontrol.pl.json';
|
||||
if ( ZM_OPT_USE_AUTH ) {
|
||||
if ( ZM_AUTH_RELAY == 'hashed' ) {
|
||||
if (ZM_OPT_USE_AUTH) {
|
||||
if (ZM_AUTH_RELAY == 'hashed') {
|
||||
$url .= '?auth='.generateAuthHash(ZM_AUTH_HASH_IPS);
|
||||
} else if ( ZM_AUTH_RELAY == 'plain' ) {
|
||||
} else if (ZM_AUTH_RELAY == 'plain') {
|
||||
$url .= '?user='.$_SESSION['username'];
|
||||
$url .= '?pass='.$_SESSION['password'];
|
||||
} else if ( ZM_AUTH_RELAY == 'none' ) {
|
||||
} else if (ZM_AUTH_RELAY == 'none') {
|
||||
$url .= '?user='.$_SESSION['username'];
|
||||
}
|
||||
}
|
||||
Debug("sending command to $url");
|
||||
Debug('sending command to '.$url);
|
||||
|
||||
$context = stream_context_create();
|
||||
try {
|
||||
$result = file_get_contents($url, false, $context);
|
||||
if ( $result === FALSE ) { /* Handle error */
|
||||
if ($result === FALSE) { /* Handle error */
|
||||
Error("Error sending command using $url");
|
||||
return false;
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
} catch (Exception $e) {
|
||||
Error("Exception $e thrown trying to send command to $url");
|
||||
return false;
|
||||
}
|
||||
|
@ -569,18 +587,18 @@ class Monitor extends ZM_Object {
|
|||
} // end function sendControlCommand($mid, $command)
|
||||
|
||||
function Groups($new='') {
|
||||
if ( $new != '' )
|
||||
if ($new != '')
|
||||
$this->Groups = $new;
|
||||
if ( !property_exists($this, 'Groups') ) {
|
||||
if (!property_exists($this, 'Groups')) {
|
||||
$this->Groups = Group::find(array('Id'=>$this->GroupIds()));
|
||||
}
|
||||
return $this->Groups;
|
||||
}
|
||||
function connKey($new='') {
|
||||
if ( $new )
|
||||
if ($new)
|
||||
$this->connKey = $new;
|
||||
if ( !isset($this->connKey) ) {
|
||||
if ( !empty($GLOBALS['connkey']) ) {
|
||||
if (!isset($this->connKey)) {
|
||||
if (!empty($GLOBALS['connkey'])) {
|
||||
$this->connKey = $GLOBALS['connkey'];
|
||||
} else {
|
||||
$this->connKey = generateConnKey();
|
||||
|
@ -596,11 +614,21 @@ class Monitor extends ZM_Object {
|
|||
|
||||
function canView() {
|
||||
global $user;
|
||||
return ( $user && ($user['Monitors'] != 'None') && ( !$this->{'Id'} || visibleMonitor($this->{'Id'}) ));
|
||||
if (!$user) {
|
||||
# auth turned on and not logged in
|
||||
return false;
|
||||
}
|
||||
if (!empty($user['MonitorIds']) ) {
|
||||
# For the purposes of viewing, having specified monitors trumps the Monitor->canView setting.
|
||||
if (in_array($this->{'Id'}, explode(',', $user['MonitorIds']))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return ($user['Monitors'] != 'None');
|
||||
}
|
||||
|
||||
function AlarmCommand($cmd) {
|
||||
if ( (!defined('ZM_SERVER_ID')) or ( property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}) ) ) {
|
||||
if ((!defined('ZM_SERVER_ID')) or (property_exists($this, 'ServerId') and (ZM_SERVER_ID==$this->{'ServerId'}))) {
|
||||
switch ($cmd) {
|
||||
case 'on' : $cmd = ' -a'; break;
|
||||
case 'off': $cmd = ' -c'; break;
|
||||
|
@ -617,7 +645,7 @@ class Monitor extends ZM_Object {
|
|||
return $output;
|
||||
}
|
||||
|
||||
if ( $this->ServerId() ) {
|
||||
if ($this->ServerId()) {
|
||||
$Server = $this->Server();
|
||||
|
||||
$url = $Server->UrlToApi().'/monitors/alarm/id:'.$this->{'Id'}.'/command:'.$cmd.'.json';
|
||||
|
@ -629,15 +657,15 @@ class Monitor extends ZM_Object {
|
|||
$context = stream_context_create();
|
||||
try {
|
||||
$result = file_get_contents($url, false, $context);
|
||||
if ( $result === FALSE ) { /* Handle error */
|
||||
if ($result === FALSE) { /* Handle error */
|
||||
Error('Error sending command using '.$url);
|
||||
return false;
|
||||
}
|
||||
Debug("Result $result");
|
||||
Debug('Result '.$result);
|
||||
$json = json_decode($result, true);
|
||||
return $json['status'];
|
||||
|
||||
} catch ( Exception $e ) {
|
||||
} catch (Exception $e) {
|
||||
Error("Exception $e thrown trying to send command to $url");
|
||||
return false;
|
||||
}
|
||||
|
@ -647,10 +675,10 @@ class Monitor extends ZM_Object {
|
|||
}
|
||||
function TriggerOn() {
|
||||
$output = $this->AlarmCommand('on');
|
||||
if ( $output and preg_match('/Alarmed event id: (\d+)$/', $output, $matches) ) {
|
||||
if ($output and preg_match('/Alarmed event id: (\d+)$/', $output, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
Warning("No event returned from TriggerOn");
|
||||
Warning('No event returned from TriggerOn');
|
||||
}
|
||||
function TriggerOff() {
|
||||
$output = $this->AlarmCommand('off');
|
||||
|
|
|
@ -434,6 +434,11 @@ class ZM_Object {
|
|||
$row = dbFetchOne("SELECT * FROM `$table` WHERE `Id`=?", NULL, array($this->Id()));
|
||||
if ( !$row ) {
|
||||
Error("Unable to lock $class record for Id=".$this->Id());
|
||||
} else {
|
||||
// row may have been modified since initial load
|
||||
foreach ($row as $k => $v) {
|
||||
$this->{$k} = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
public function remove_from_cache() {
|
||||
|
|
|
@ -30,22 +30,25 @@ class Server extends ZM_Object {
|
|||
return ZM_Object::_find_one(get_class(), $parameters, $options);
|
||||
}
|
||||
|
||||
public function Hostname( $new = null ) {
|
||||
if ( $new != null )
|
||||
public function Hostname($new = null) {
|
||||
if ($new != null)
|
||||
$this->{'Hostname'} = $new;
|
||||
|
||||
if ( isset( $this->{'Hostname'}) and ( $this->{'Hostname'} != '' ) ) {
|
||||
if (isset( $this->{'Hostname'}) and ($this->{'Hostname'} != '')) {
|
||||
return $this->{'Hostname'};
|
||||
} else if ( $this->Id() ) {
|
||||
return $this->{'Name'};
|
||||
}
|
||||
# This theoretically will match ipv6 addresses as well
|
||||
if ( preg_match( '/^(\[[[:xdigit:]:]+\]|[^:]+)(:[[:digit:]]+)?$/', $_SERVER['HTTP_HOST'], $matches ) ) {
|
||||
return $matches[1];
|
||||
}
|
||||
if (isset($_SERVER['HTTP_HOST'])) {
|
||||
# This theoretically will match ipv6 addresses as well
|
||||
if ( preg_match( '/^(\[[[:xdigit:]:]+\]|[^:]+)(:[[:digit:]]+)?$/', $_SERVER['HTTP_HOST'], $matches ) ) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
$result = explode(':', $_SERVER['HTTP_HOST']);
|
||||
return $result[0];
|
||||
$result = explode(':', $_SERVER['HTTP_HOST']);
|
||||
return $result[0];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
public function Protocol( $new = null ) {
|
||||
|
|
|
@ -29,6 +29,7 @@ class Snapshot extends ZM_Object {
|
|||
public function delete() {
|
||||
if ( property_exists($this, 'Id') ) {
|
||||
dbQuery('DELETE FROM `Snapshot_Events` WHERE `SnapshotId`=?', array($this->{'Id'}));
|
||||
dbQuery('DELETE FROM `Snapshots` WHERE `Id`=?', array($this->{'Id'}));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,40 +20,39 @@
|
|||
|
||||
global $error_message;
|
||||
// Event scope actions, view permissions only required
|
||||
if ( !canView('Events') ) {
|
||||
if (!canView('Events')) {
|
||||
$error_message = 'You do not have permission to view Events.';
|
||||
ZM\Warning($error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
|
||||
if ( $action == 'addterm' ) {
|
||||
if (isset($_REQUEST['object']) and ($_REQUEST['object'] == 'filter')) {
|
||||
if ($action == 'addterm') {
|
||||
$_REQUEST['filter'] = addFilterTerm($_REQUEST['filter'], $_REQUEST['line']);
|
||||
} elseif ( $action == 'delterm' ) {
|
||||
} else if ($action == 'delterm') {
|
||||
$_REQUEST['filter'] = delFilterTerm($_REQUEST['filter'], $_REQUEST['line']);
|
||||
} else if ( canEdit('Events') ) {
|
||||
|
||||
} else if (canEdit('Events')) {
|
||||
require_once('includes/Filter.php');
|
||||
$filter = new ZM\Filter($_REQUEST['Id']);
|
||||
|
||||
if ( $action == 'delete' ) {
|
||||
if ( !empty($_REQUEST['Id']) ) {
|
||||
if ( $filter->Background() ) {
|
||||
if ($action == 'delete') {
|
||||
if (!empty($_REQUEST['Id'])) {
|
||||
if ($filter->Background()) {
|
||||
$filter->control('stop');
|
||||
}
|
||||
$filter->delete();
|
||||
|
||||
} else {
|
||||
ZM\Error('No filter id passed when deleting');
|
||||
}
|
||||
} else if ( ( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' ) ) {
|
||||
|
||||
} else if (( $action == 'Save' ) or ( $action == 'SaveAs' ) or ( $action == 'execute' )) {
|
||||
$_REQUEST['filter']['Query']['sort_field'] = validStr($_REQUEST['filter']['Query']['sort_field']);
|
||||
$_REQUEST['filter']['Query']['sort_asc'] = validStr($_REQUEST['filter']['Query']['sort_asc']);
|
||||
$_REQUEST['filter']['Query']['limit'] = validInt($_REQUEST['filter']['Query']['limit']);
|
||||
|
||||
$_REQUEST['filter']['AutoCopy'] = empty($_REQUEST['filter']['AutoCopy']) ? 0 : 1;
|
||||
$_REQUEST['filter']['AutoCopyTo'] = empty($_REQUEST['filter']['AutoCopyTo']) ? 0 : $_REQUEST['filter']['AutoCopyTo'];
|
||||
$_REQUEST['filter']['AutoMove'] = empty($_REQUEST['filter']['AutoMove']) ? 0 : 1;
|
||||
$_REQUEST['filter']['AutoMoveTo'] = empty($_REQUEST['filter']['AutoMoveTo']) ? 0 : $_REQUEST['filter']['AutoMoveTo'];
|
||||
$_REQUEST['filter']['AutoArchive'] = empty($_REQUEST['filter']['AutoArchive']) ? 0 : 1;
|
||||
$_REQUEST['filter']['AutoVideo'] = empty($_REQUEST['filter']['AutoVideo']) ? 0 : 1;
|
||||
$_REQUEST['filter']['AutoUpload'] = empty($_REQUEST['filter']['AutoUpload']) ? 0 : 1;
|
||||
|
@ -65,44 +64,39 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
|
|||
$_REQUEST['filter']['Background'] = empty($_REQUEST['filter']['Background']) ? 0 : 1;
|
||||
$_REQUEST['filter']['Concurrent'] = empty($_REQUEST['filter']['Concurrent']) ? 0 : 1;
|
||||
$changes = $filter->changes($_REQUEST['filter']);
|
||||
ZM\Debug('Changes: ' . print_r($changes,true));
|
||||
ZM\Debug('Changes: ' . print_r($changes, true));
|
||||
|
||||
if ($filter->Id() and ($action == 'Save')) {
|
||||
if ($filter->Background()) $filter->control('stop');
|
||||
if (!$filter->save($changes)) {
|
||||
$error_message = $filter->get_last_error();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ( $action == 'execute' ) {
|
||||
if ( count($changes) ) {
|
||||
$filter->Name('_TempFilter'.time());
|
||||
$filter->Id(null);
|
||||
}
|
||||
} else if ( $action == 'SaveAs' ) {
|
||||
$filter->Id(null);
|
||||
}
|
||||
if (!$filter->save($changes)) {
|
||||
$error_message = $filter->get_last_error();
|
||||
return;
|
||||
}
|
||||
if (count($changes)) {
|
||||
if ($filter->Id() and ($action == 'Save') and $filter->Background()) {
|
||||
$filter->control('stop');
|
||||
} else if ($action == 'execute') {
|
||||
# If there are changes use a temp filter to do the execute
|
||||
$filter->Name('_TempFilter'.time());
|
||||
$filter->Id(null);
|
||||
} else if ($action == 'SaveAs') {
|
||||
$filter->Id(null);
|
||||
}
|
||||
if (!$filter->save($changes)) {
|
||||
$error_message = $filter->get_last_error();
|
||||
return;
|
||||
}
|
||||
// We update the request id so that the newly saved filter is auto-selected
|
||||
$_REQUEST['Id'] = $filter->Id();
|
||||
} # end if changes
|
||||
|
||||
// We update the request id so that the newly saved filter is auto-selected
|
||||
$_REQUEST['Id'] = $filter->Id();
|
||||
}
|
||||
|
||||
if ( $action == 'execute' ) {
|
||||
if ($action == 'execute') {
|
||||
$filter->execute();
|
||||
if ( count($changes) )
|
||||
if (count($changes)) {
|
||||
$filter->delete();
|
||||
|
||||
$view = 'events';
|
||||
} else if ( $filter->Background() ) {
|
||||
$filter->Id(null);
|
||||
}
|
||||
} else if ($filter->Background()) {
|
||||
$filter->control('start');
|
||||
}
|
||||
global $redirect;
|
||||
$redirect = '?view=filter'.$filter->querystring('filter', '&');
|
||||
|
||||
} else if ( $action == 'control' ) {
|
||||
} else if ($action == 'control') {
|
||||
if ( $_REQUEST['command'] == 'start'
|
||||
or $_REQUEST['command'] == 'stop'
|
||||
or $_REQUEST['command'] == 'restart'
|
||||
|
@ -114,5 +108,4 @@ if ( isset($_REQUEST['object']) and ( $_REQUEST['object'] == 'filter' ) ) {
|
|||
} // end if save or execute
|
||||
} // end if canEdit(Events)
|
||||
} // end if object == filter
|
||||
|
||||
?>
|
||||
|
|
|
@ -205,7 +205,7 @@ if ( $action == 'save' ) {
|
|||
} // end if changes in width or height
|
||||
} else {
|
||||
global $error_message;
|
||||
$error_message = dbError();
|
||||
$error_message = dbError('unknown');
|
||||
} // end if successful save
|
||||
$restart = true;
|
||||
} else { // new monitor
|
||||
|
|
|
@ -20,18 +20,18 @@
|
|||
|
||||
|
||||
// Monitor control actions, require a monitor id and control view permissions for that monitor
|
||||
if ( empty($_REQUEST['mid']) ) {
|
||||
if (empty($_REQUEST['mid'])) {
|
||||
ZM\Warning('Settings requires a monitor id');
|
||||
return;
|
||||
}
|
||||
if ( ! canView('Control', $_REQUEST['mid']) ) {
|
||||
if (!canView('Control', $_REQUEST['mid'])) {
|
||||
ZM\Warning('Settings requires the Control permission');
|
||||
return;
|
||||
}
|
||||
|
||||
require_once('includes/Monitor.php');
|
||||
$mid = validInt($_REQUEST['mid']);
|
||||
if ( $action == 'settings' ) {
|
||||
if ($action == 'settings') {
|
||||
$args = ' -m ' . escapeshellarg($mid);
|
||||
$args .= ' -B' . escapeshellarg($_REQUEST['newBrightness']);
|
||||
$args .= ' -C' . escapeshellarg($_REQUEST['newContrast']);
|
||||
|
@ -45,5 +45,7 @@ if ( $action == 'settings' ) {
|
|||
dbQuery(
|
||||
'UPDATE Monitors SET Brightness = ?, Contrast = ?, Hue = ?, Colour = ? WHERE Id = ?',
|
||||
array($brightness, $contrast, $hue, $colour, $mid));
|
||||
global $redirect;
|
||||
$redirect = '?view=watch&mid='.$mid;
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -47,9 +47,12 @@ if ( $action == 'create' ) {
|
|||
$monitor = new ZM\Monitor($monitor_id);
|
||||
$monitor->TriggerOff();
|
||||
}
|
||||
$dbConn->beginTransaction();
|
||||
foreach ( $snapshot->Events() as $event ) {
|
||||
$event->lock();
|
||||
$event->save(array('Archived'=>1));
|
||||
}
|
||||
$dbConn->commit();
|
||||
$redirect = '?view=snapshot&id='.$snapshot->Id();
|
||||
return;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue