Merge branch 'master' of github.com:zoneminder/ZoneMinder

This commit is contained in:
Isaac Connor committed 2020-08-14 13:08:04 -04:00
commit dbb2535a6b
190 files changed
+16148 -4638

No files matched your search

+1
View File
@@ -3,6 +3,7 @@
module.exports = {
"env": {
"browser": true,
"es6": true,
},
"extends": ["google"],
"overrides": [{
+2 -2
View File
@@ -1,10 +1,10 @@
# Configuration for probot-stale - https://github.com/probot/stale
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
daysUntilStale: 180
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
daysUntilClose: 30
# Issues with these labels will never be considered stale
exemptLabels:
+5 -10
View File
@@ -32,25 +32,20 @@ install:
- update-binfmts --enable qemu-arm
env:
- SMPFLAGS=-j4 OS=el DIST=7
- SMPFLAGS=-j4 OS=eslint DIST=eslint
- SMPFLAGS=-j4 OS=el DIST=7 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=el DIST=8 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=fedora DIST=30
- SMPFLAGS=-j4 OS=fedora DIST=31
- SMPFLAGS=-j4 OS=fedora DIST=31 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=fedora DIST=32 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=bionic DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=disco DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=eoan DOCKER_REPO=knnniggett/packpack:ubuntu-eoan
- SMPFLAGS=-j4 OS=ubuntu DIST=focal DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=debian DIST=jessie DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=debian DIST=stretch DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=debian DIST=buster DOCKER_REPO=iconzm/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty ARCH=i386
- SMPFLAGS=-j4 OS=ubuntu DIST=xenial ARCH=i386
- SMPFLAGS=-j4 OS=ubuntu DIST=disco ARCH=i386
- SMPFLAGS=-j4 OS=debian DIST=buster ARCH=i386
- SMPFLAGS=-j4 OS=debian DIST=stretch ARCH=i386
- SMPFLAGS=-j4 OS=eslint DIST=eslint
compiler:
- gcc
-1
View File
@@ -899,7 +899,6 @@ configure_file(zmlinkcontent.sh.in "${CMAKE_CURRENT_BINARY_DIR}/zmlinkcontent.sh
# Create a target for man pages
include(Pod2Man)
ADD_MANPAGE_TARGET()
# Process subdirectories
+1 -2
View File
@@ -53,8 +53,7 @@ MACRO(POD2MAN PODFILE MANFILE SECTION MANPAGE_DEST_PREFIX)
SET(MANPAGE_TARGET "man-${MANFILE}")
ADD_CUSTOM_TARGET(${MANPAGE_TARGET} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz)
ADD_DEPENDENCIES(man ${MANPAGE_TARGET})
ADD_CUSTOM_TARGET(${MANPAGE_TARGET} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz)
INSTALL(
FILES ${CMAKE_CURRENT_BINARY_DIR}/${MANFILE}.${SECTION}.gz
+3
View File
@@ -46,3 +46,6 @@ ZM_PATH_SWAP=@ZM_TMPDIR@
# Full path to optional arp binary
# ZoneMinder will find the arp binary automatically on most systems
ZM_PATH_ARP="@ZM_PATH_ARP@"
#Full path to shutdown binary
ZM_PATH_SHUTDOWN="@ZM_PATH_SHUTDOWN@"
+5
View File
@@ -282,6 +282,7 @@ DROP TABLE IF EXISTS `Filters`;
CREATE TABLE `Filters` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Name` varchar(64) NOT NULL default '',
`UserId` int(10) unsigned,
`Query_json` text NOT NULL,
`AutoArchive` tinyint(3) unsigned NOT NULL default '0',
`AutoVideo` tinyint(3) unsigned NOT NULL default '0',
@@ -787,6 +788,7 @@ INSERT INTO `Filters`
`AutoCopy`,
`AutoCopyTo`,
`UpdateDiskSpace`,
`UserId`,
`Background`,
`Concurrent`
)
@@ -807,6 +809,7 @@ INSERT INTO `Filters`
0/*AutoMove*/,0/*MoveTo*/,
0/*AutoCopy*/,0/*CopyTo*/,
0/*UpdateDiskSpace*/,
1/*UserId = admin*/,
1/*Background*/,
0/*Concurrent*/
);
@@ -830,6 +833,7 @@ INSERT INTO `Filters`
`AutoCopy`,
`AutoCopyTo`,
`UpdateDiskSpace`,
`UserId`,
`Background`,
`Concurrent`
)
@@ -849,6 +853,7 @@ VALUES (
0/*AutoMove*/,0/*MoveTo*/,
0/*AutoCopy*/,0/*CopyTo*/,
1/*UpdateDiskSpace*/,
1/*UserId=admin*/,
1/*Background*/,
0/*Concurrent*/
);
+11
View File
@@ -0,0 +1,11 @@
SET @s = (SELECT IF(
(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = DATABASE()
AND table_name = 'Filters'
AND column_name = 'UserId'
) > 0,
"SELECT 'Column UserId already exists in Filters'",
"ALTER TABLE `Filters` ADD `UserId` int(10) unsigned AFTER `Name`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
+2
View File
@@ -0,0 +1,2 @@
/* This was done in 1.31.0 but zm_create.sql.in wasn't updated to match */
ALTER TABLE Monitors MODIFY LinkedMonitors varchar(255);
+10
View File
@@ -0,0 +1,10 @@
zoneminder (1.28.1-1) unstable; urgency=low
This version is no longer automatically initialize or upgrade database.
See README.Debian for details.
Changed installation paths (please correct your web server configuration):
/usr/share/zoneminder --> /usr/share/zoneminder/www
/usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin
-- Dmitry Smirnov <onlyjob@debian.org> Tue, 31 Mar 2015 15:12:17 +1100
+130
View File
@@ -0,0 +1,130 @@
Zoneminder for Debian
---------------------
Initializing database
---------------------
pv /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf
OR
cat /usr/share/zoneminder/db/zm_create.sql | sudo mysql --defaults-file=/etc/mysql/debian.cnf
echo 'grant lock tables,alter,create,index,select,insert,update,delete on zm.* to 'zmuser'@localhost identified by "zmpass";'\
| sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql
Hint: generate secure password with `pwgen` and update "/etc/zm/zm.conf"
accordingly.
The following command can help to ensure that zoneminder can read its
configuration file:
chgrp -c www-data /etc/zm/zm.conf
Upgrading database
------------------
The database is updated automatically on installation. You should not need to take this step.
Assuming that database is on "localhost" then the following command can be
used to upgrade "zm" database:
zmupdate.pl
Additional permissions may be required to perform upgrade:
echo 'grant lock tables, create, alter on zm.* to 'zmuser'@localhost identified by "zmpass";'\
| sudo mysql --defaults-file=/etc/mysql/debian.cnf mysql
The following command prints the current version of zoneminder database:
echo 'select Value from Config where Name = "ZM_DYN_CURR_VERSION";' \
| sudo mysql --defaults-file=/etc/mysql/debian.cnf --skip-column-names zm
Enabling service
----------------
By default Zoneminder service is not automatically started and needs to be
manually enabled once database is configured:
sudo systemctl enable zoneminder.service
Web server set-up
-----------------
There are few manual steps to get the web interface working:
## Apache2
Apache can be configured as folder "/zm" using sample .conf:
sudo a2enconf zoneminder
Alternatively Apache web site configuration template can be used to setup
zoneminder as "http://zoneminder":
sudo cp -v /usr/share/doc/zoneminder/examples/apache.conf /etc/apache2/sites-available/
sudo a2ensite zoneminder.conf
Common configuration steps for Apache2:
sudo a2enmod cgi
sudo service apache2 reload
## nginx / fcgiwrap
Nginx needs "php-fpm" package to support PHP and "fcgiwrap" package
for binary "cgi-bin" applications:
sudo apt-get install php-fpm fcgiwrap
To enable a URL alias that makes Zoneminder available from
http://yourserver/zm
the following line is to be added to "server" section of a web site
configuration:
include /usr/share/doc/zoneminder/examples/nginx.conf;
For "default" web site it would be sufficient to include the above
statement to the file
/etc/nginx/sites-enabled/default
To avoid problems with feeds from multiple cameras "fcgiwrap" should be
configured to start at least as many processes as there are cameras.
It can be done by adjusting DAEMON_OPTS in "/etc/default/fcgiwrap".
Systemd users may be affected by the following bug:
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=792705
## Note:
When Zoneminder web site is running it may be necessary to set
Options/Paths/PATH_ZMS to "/zm/cgi-bin/nph-zms" or according to chosen web
site configuration.
Changing the location for images and events
-------------------------------------------
ZoneMinder is now able to be configured to use an alternative location for storing
events and images at compile time. This package makes use of that, so symlinks in
/usr/share/zoneminder/www are no longer necessary.
Access to /dev/video*
---------------------
For cameras which require access to /dev/video*, zoneminder may need the
www-data user added to the video group in order to see those cameras:
adduser www-data video
Note that all web applications running on the zoneminder server will then have
access to all video devices on the system.
-- Vagrant Cascadian <vagrant@debian.org> Sun, 27 Mar 2011 13:06:56 -0700
+12
View File
@@ -0,0 +1,12 @@
## Separate substantial /usr/share into its own arch-all package.
## Decide how to handle database updates.
* Consider possibility that database may be on another machine (#469239).
* Consider dbconfig-common? Probably not (what if database is not on localhost?).
### Run `zmupdate.pl` from service control scripts (init.d, service) on start?
Automatic upgrade will break "one DB, many zoneminders" setup (unimportant?).
+3
View File
@@ -0,0 +1,3 @@
zoneminder (1.31.39~20180223.27-stretch-1) unstable; urgency=low
*
-- Isaac Connor <iconnor@connortechnology.com> Fri, 23 Feb 2018 14:15:59 -0500
+3
View File
@@ -0,0 +1,3 @@
.gitattributes
web/api/.gitattributes
web/api/.gitignore
+1
View File
@@ -0,0 +1 @@
9
@@ -0,0 +1,57 @@
# Remember to enable cgi mod (i.e. "a2enmod cgi").
ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
<Directory "/usr/lib/zoneminder/cgi-bin">
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
AllowOverride All
Require all granted
</Directory>
# Order matters. This alias must come first.
Alias /zm/cache /var/cache/zoneminder/cache
<Directory /var/cache/zoneminder/cache>
Options -Indexes +FollowSymLinks
AllowOverride None
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order deny,allow
Allow from all
</IfModule>
</Directory>
Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www>
Options -Indexes +FollowSymLinks
<IfModule mod_dir.c>
DirectoryIndex index.php
</IfModule>
</Directory>
# For better visibility, the following directives have been migrated from the
# default .htaccess files included with the CakePHP project.
# Parameters not set here are inherited from the parent directive above.
<Directory "/usr/share/zoneminder/www/api">
RewriteEngine on
RewriteRule ^$ app/webroot/ [L]
RewriteRule (.*) app/webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app">
RewriteEngine on
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
RewriteBase /zm/api
</Directory>
<Directory "/usr/share/zoneminder/www/api/app/webroot">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
RewriteBase /zm/api
</Directory>
+166
View File
@@ -0,0 +1,166 @@
Source: zoneminder
Section: net
Priority: optional
Maintainer: Isaac Connor <isaac@zoneminder.com>
Uploaders: Isaac Connor <isaac@zoneminder.com>
Build-Depends: debhelper, sphinx-doc, dh-linktree, dh-apache2
,cmake
,libx264-dev, libmp4v2-dev
,libavdevice-dev
,libavcodec-dev
,libavformat-dev
,libavutil-dev
,libswresample-dev
,libswscale-dev
,ffmpeg
,net-tools
,libbz2-dev
,libgcrypt20-dev
,libcurl4-gnutls-dev
,libturbojpeg0-dev
,default-libmysqlclient-dev | libmysqlclient-dev | libmariadbclient-dev-compat
,libpcre3-dev
,libpolkit-gobject-1-dev
,libv4l-dev [!hurd-any]
,libvlc-dev
,libdate-manip-perl
,libdbd-mysql-perl
,libphp-serialization-perl
,libsys-mmap-perl [!hurd-any]
,libwww-perl
,libdata-uuid-perl
,libssl-dev
,libcrypt-eksblowfish-perl
,libdata-entropy-perl
# Unbundled (dh_linktree):
,libjs-jquery
,libjs-mootools
Standards-Version: 3.9.8
Homepage: http://www.zoneminder.com/
Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git
Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git
Package: zoneminder
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,javascript-common
,libmp4v2-2, libx264-155
,libswscale5
,libswresample3
,ffmpeg
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
,libdbd-mysql-perl
,libphp-serialization-perl
,libmodule-load-conditional-perl
,libnet-sftp-foreign-perl
,libarchive-zip-perl
,libdbd-mysql-perl
,libdevice-serialport-perl
,libimage-info-perl
,libjson-maybexs-perl
,libsys-mmap-perl [!hurd-any]
,liburi-encode-perl
,libwww-perl, liburi-perl
,libdata-dump-perl
,libdatetime-perl
,libclass-std-fast-perl
,libsoap-wsdl-perl
,libio-socket-multicast-perl
,libdigest-sha-perl
,libsys-cpu-perl, libsys-meminfo-perl
,libdata-uuid-perl
,libnumber-bytes-human-perl
,libfile-slurp-perl
,mysql-client | mariadb-client | virtual-mysql-client
,perl-modules
,php-mysql, php-gd, php-apcu, php-apc | php-apcu-bc, php-json
,policykit-1
,rsyslog | system-log-daemon
,zip
,libpcre3
,libcrypt-eksblowfish-perl
,libdata-entropy-perl
Recommends: ${misc:Recommends}
,libapache2-mod-php | php-fpm
,mysql-server | mariadb-server | virtual-mysql-server
,zoneminder-doc (>= ${source:Version})
,ffmpeg
Suggests: fcgiwrap, logrotate
Description: video camera security and surveillance solution
ZoneMinder is intended for use in single or multi-camera video security
applications, including commercial or home CCTV, theft prevention and child
or family member or home monitoring and other care scenarios. It
supports capture, analysis, recording, and monitoring of video data coming
from one or more video or network cameras attached to a Linux system.
ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom
cameras using a variety of protocols. It is suitable for use as a home
video security system and for commercial or professional video security
and surveillance. It can also be integrated into a home automation system
via X.10 or other protocols.
#Package: libzoneminder-perl
#Section: perl
#Architecture: all
#Multi-Arch: foreign
#Depends: ${misc:Depends}, ${perl:Depends}
# ,libarchive-zip-perl
# ,libdbd-mysql-perl
# ,libdevice-serialport-perl
# ,libimage-info-perl
# ,libjson-maybexs-perl
# ,libsys-mmap-perl [!hurd-any]
# ,liburi-encode-perl
# ,libwww-perl
#Description: ZoneMinder Perl libraries
# ZoneMinder is intended for use in single or multi-camera video security
# applications, including commercial or home CCTV, theft prevention and child
# or family member or home monitoring and other care scenarios. It
# supports capture, analysis, recording, and monitoring of video data coming
# from one or more video or network cameras attached to a Linux system.
# ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom
# cameras using a variety of protocols. It is suitable for use as a home
# video security system and for commercial or professional video security
# and surveillance. It can also be integrated into a home automation system
# via X.10 or other protocols.
# .
# This package provides ZoneMinder Perl libraries; it can be used to
# write custom interfaces as well.
Package: zoneminder-doc
Section: doc
Architecture: all
Multi-Arch: foreign
Depends: ${misc:Depends}, ${sphinxdoc:Depends}, python-sphinx-rtd-theme | python3-sphinx-rtd-theme
Suggests: www-browser
Description: ZoneMinder documentation
ZoneMinder is intended for use in single or multi-camera video security
applications, including commercial or home CCTV, theft prevention and child
or family member or home monitoring and other care scenarios. It
supports capture, analysis, recording, and monitoring of video data coming
from one or more video or network cameras attached to a Linux system.
ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom
cameras using a variety of protocols. It is suitable for use as a home
video security system and for commercial or professional video security
and surveillance. It can also be integrated into a home automation system
via X.10 or other protocols.
.
This package provides ZoneMinder documentation in HTML format.
Package: zoneminder-dbg
Section: debug
Priority: extra
Architecture: any
Depends: zoneminder (= ${binary:Version}), ${misc:Depends}
Description: Zoneminder -- debugging symbols
ZoneMinder is intended for use in single or multi-camera video security
applications, including commercial or home CCTV, theft prevention and child
or family member or home monitoring and other care scenarios. It
supports capture, analysis, recording, and monitoring of video data coming
from one or more video or network cameras attached to a Linux system.
ZoneMinder also support web and semi-automatic control of Pan/Tilt/Zoom
cameras using a variety of protocols. It is suitable for use as a home
video security system and for commercial or professional video security
and surveillance. It can also be integrated into a home automation system
via X.10 or other protocols.
.
This package provides debugging symbols
+174
View File
@@ -0,0 +1,174 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: ZoneMinder
Upstream-Contact: Philip Coombes <philip.coombes@zoneminder.com>
Source: https://github.com/ZoneMinder/ZoneMinder
Comment:
This package was originally debianized by matrix <matrix@cecilia>
on Mon, 7 Mar 2005 02:07:57 -0500.
It was re-done for submission to the Debian project by Peter Howard
<pjh@northern-ridge.com.au> on Fri, 8 Dec 2006 10:19:43 +1100
Files-Excluded:
web/skins/*/js/jquery-*
web/tools/mootools/*-yc.js
Files: *
Copyright: 2001-2014 Philip Coombes <philip.coombes@zoneminder.com>
2008 Brian Rudy <brudyNO@SPAMpraecogito.com>
2014 Vincent Giovannone
2013 Tim Craig <timcraigNO@SPAMsonic.net>
2003-2008 Corey DeLasaux
2001-2010 Chris Kistner
License: GPL-2+
Files: distros/*
Copyright: 2001-2008 Philip Coombes <philip.coombes@zoneminder.com>
2014 Isaac Connor <iconnor@connortechnology.com>
2005 Serg Oskin
License: GPL-2+
Files: web/skins/*/js/jquery-*
Copyright: 2010 John Resig
2010 The Dojo Foundation
License: GPL-2 or Expat
Comment:
Dual licensed under the MIT or GPL Version 2 licenses.
http://jquery.org/license
.
Includes Sizzle.js http://sizzlejs.com/
Released under the MIT, BSD, and GPL Licenses.
Files: web/tools/mootools/*.js
Copyright: 2009 Marcelo Jorge Vieira (metal) <metal@alucinados.com>
2006-2010 Valerio Proietti (http://mad4milk.net/)
License: Expat
Files: web/api/*
Copyright: 2005-2013 Cake Software Foundation, Inc. (http://cakefoundation.org)
License: Expat
Files:
cmake/Modules/CheckPrototypeDefinition*.cmake
cmake/Modules/FindGLIB2.cmake
cmake/Modules/FindPolkit.cmake
cmake/Modules/GNUInstallDirs.cmake
Copyright:
2005-2011 Kitware, Inc.
2010-2011 Andreas Schneider <asn@cryptomilk.org>
2009 Dario Freddi <drf@kde.org>
2008 Laurent Montel, <montel@kde.org>
2011 Nikita Krupen'ko <krnekit@gmail.com>
License: BSD-3-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
.
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
.
* The names of Kitware, Inc., the Insight Consortium, or the names of
any consortium members, or of any contributors, may not be used to
endorse or promote products derived from this software without
specific prior written permission.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Files: cmake/Modules/FindPerlModules.cmake
Copyright: 2012 Iowa State University
License: Boost-1.0
Boost Software License - Version 1.0 - August 17th, 2003
.
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
.
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
Files: debian/*
Copyright: 2015 Dmitry Smirnov <onlyjob@debian.org>
2007-2014 Peter Howard <pjh@northern-ridge.com.au>
2010-2012 Vagrant Cascadian <vagrant@debian.org>
2001-2008 Philip Coombes <philip.coombes@zoneminder.com>
License: GPL-2+
License: Expat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
License: GPL-2+
This package is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
.
You should have received a copy of the GNU General Public
License along with this package; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
.
The complete text of the GNU General Public License version 2
can be found in "/usr/share/common-licenses/GPL-2".
License: GPL-2
This package is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; version 2 of the License.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
.
You should have received a copy of the GNU General Public
License along with this package; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
.
The complete text of the GNU General Public License version 2
can be found in "/usr/share/common-licenses/GPL-2".
+32
View File
@@ -0,0 +1,32 @@
location /zm/cgi-bin {
gzip off;
alias /usr/lib/zoneminder/cgi-bin;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_pass unix:/var/run/fcgiwrap.socket;
}
location /zm {
# if ($scheme ~ ^http:){
# rewrite ^(.*)$ https://$host$1 permanent;
# }
gzip off;
alias /usr/share/zoneminder/www;
index index.php;
location ~ \.php$ {
if (!-f $request_filename) { return 404; }
expires epoch;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_index index.php;
fastcgi_pass unix:/var/run/php5-fpm.sock;
}
location ~ \.(jpg|jpeg|gif|png|ico)$ {
access_log off;
expires 33d;
}
}
+7
View File
@@ -0,0 +1,7 @@
[dch]
id-length = 0
[import-orig]
pristine-tar = False
merge = False
@@ -0,0 +1,2 @@
usr/share/man/man3
usr/share/perl5
View File
Whitespace-only changes.
+87
View File
@@ -0,0 +1,87 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
export DEB_LDFLAGS_MAINT_APPEND += -Wl,--as-needed
ifeq ($(DEB_BUILD_ARCH_OS),hurd)
ARGS:= -DZM_NO_MMAP=ON
endif
%:
dh $@ --parallel --buildsystem=cmake --builddirectory=dbuild \
--with sphinxdoc,apache2,linktree
override_dh_auto_configure:
dh_auto_configure -- $(ARGS) \
-DCMAKE_VERBOSE_MAKEFILE=ON \
-DCMAKE_BUILD_TYPE=Release \
-DZM_CONFIG_DIR="/etc/zm" \
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
-DZM_RUNDIR="/run/zm" \
-DZM_SOCKDIR="/run/zm" \
-DZM_TMPDIR="/tmp/zm" \
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
-DZM_DIR_EVENTS="/var/cache/zoneminder/events" \
-DZM_PATH_SHUTDOWN="/sbin/shutdown" \
-DZM_PATH_ZMS="/zm/cgi-bin/nph-zms"
override_dh_clean:
dh_clean $(MANPAGES1)
$(RM) -r docs/_build
build-indep:
#$(MAKE) -C docs text
$(MAKE) -C docs html
MANPAGES1 = dbuild/scripts/zmupdate.pl.1
$(MANPAGES1):
# generate man page(s):
pod2man -s1 --stderr --utf8 $(patsubst %.1, %, $@) $@
## reproducible build:
LAST_CHANGE=$(shell dpkg-parsechangelog -S Date)
BUILD_DATE=$(shell LC_ALL=C date -u "+%B %d, %Y" -d "$(LAST_CHANGE)")
override_dh_installman: $(MANPAGES1)
$(MAKE) -C docs man SPHINXOPTS="-D today=\"$(BUILD_DATE)\""
dh_installman --language=C $(MANPAGES1)
override_dh_auto_install:
dh_auto_install --destdir=$(CURDIR)/debian/tmp
# remove worthless files:
$(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*/*/.packlist
$(RM) -v $(CURDIR)/debian/tmp/usr/share/perl5/*/*.in
# remove empty directories:
find $(CURDIR)/debian/tmp/usr -type d -empty -delete -printf 'removed %p\n'
# remove extra-license-file:
$(RM) -v $(CURDIR)/debian/tmp/usr/share/zoneminder/www/api/lib/Cake/LICENSE.txt
override_dh_fixperms:
dh_fixperms
#
# As requested by the Debian Webapps Policy Manual §3.2.1
chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
override_dh_strip:
[ -d "$(CURDIR)/debian/zoneminder-dbg" ] \
&& dh_strip --dbg-package=zoneminder-dbg \
|| dh_strip
#%:
# dh $@ --parallel --buildsystem=autoconf --with autoreconf
#
#override_dh_auto_configure:
# dh_auto_configure -- \
# --sysconfdir=/etc/zm \
# --with-mysql=/usr \
# --with-webdir=/usr/share/zoneminder \
# --with-ffmpeg=/usr \
# --with-cgidir=/usr/lib/cgi-bin \
# --with-webuser=www-data \
# --with-webgroup=www-data \
# --enable-mmap=yes
+1
View File
@@ -0,0 +1 @@
3.0 (quilt)
+9
View File
@@ -0,0 +1,9 @@
## Actually sources are there: "*-nc.js".
source-is-missing web/tools/mootools/mootools-*-yc.js
## We're using "libjs-jquery" instead.
source-is-missing web/skins/*/js/jquery-1.4.2.min.js
## Acknowledged, will repack eventually.
source-contains-prebuilt-javascript-object web/tools/mootools/mootools-*-yc.js
source-contains-prebuilt-javascript-object web/skins/*/js/jquery-1.4.2.min.js
+8
View File
@@ -0,0 +1,8 @@
Document: zoneminder-doc
Title: Zoneminder documentation
Abstract: This document describes how to use Zoneminder.
Section: System/Administration
Format: HTML
Index: /usr/share/doc/zoneminder-doc/html/index.html
Files: /usr/share/doc/zoneminder-doc/html/*
+1
View File
@@ -0,0 +1 @@
docs/_build/html usr/share/doc/zoneminder-doc/
+2
View File
@@ -0,0 +1,2 @@
## Convenience symlink:
/usr/share/doc/zoneminder-doc/html /usr/share/doc/zoneminder/html
+1
View File
@@ -0,0 +1 @@
conf debian/conf/apache2/zoneminder.conf nginx
+5
View File
@@ -0,0 +1,5 @@
Unless bug is specific to Debian please consider reporting it directly to
upstream developer(s):
https://github.com/ZoneMinder/ZoneMinder/issues
+9
View File
@@ -0,0 +1,9 @@
var/log/zm
var/lib/zm
var/cache/zoneminder/events
var/cache/zoneminder/images
var/cache/zoneminder/temp
var/cache/zoneminder/cache
usr/share/zoneminder/db
etc/zm/
etc/zm/conf.d
+1
View File
@@ -0,0 +1 @@
README.md
+2
View File
@@ -0,0 +1,2 @@
debian/examples/*
dbuild/misc/apache.conf
+91
View File
@@ -0,0 +1,91 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: zoneminder
# Required-Start: $network $remote_fs $syslog
# Required-Stop: $network $remote_fs $syslog
# Should-Start: mysql
# Should-Stop: mysql
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Control ZoneMinder as a Service
# Description: ZoneMinder CCTV recording and surveillance system
### END INIT INFO
# chkconfig: 2345 20 20
# Source function library.
. /lib/lsb/init-functions
prog=ZoneMinder
ZM_PATH_BIN="/usr/bin"
RUNDIR="/run/zm"
TMPDIR="/tmp/zm"
command="$ZM_PATH_BIN/zmpkg.pl"
start() {
echo -n "Starting $prog: "
export TZ=:/etc/localtime
mkdir -p "$RUNDIR" && chown www-data:www-data "$RUNDIR"
mkdir -p "$TMPDIR" && chown www-data:www-data "$TMPDIR"
$command start
RETVAL=$?
[ $RETVAL = 0 ] && echo success
[ $RETVAL != 0 ] && echo failure
echo
[ $RETVAL = 0 ] && touch /var/lock/zm
return $RETVAL
}
stop() {
echo -n "Stopping $prog: "
#
# Why is this status check being done?
# as $command stop returns 1 if zoneminder
# is stopped, which will result in
# this returning 1, which will stuff
# dpkg when it tries to stop zoneminder before
# uninstalling . . .
#
result=`$command status`
if [ ! "$result" = "running" ]; then
echo "Zoneminder already stopped"
echo
RETVAL=0
else
$command stop
RETVAL=$?
[ $RETVAL = 0 ] && echo success
[ $RETVAL != 0 ] && echo failure
echo
[ $RETVAL = 0 ] && rm -f /var/lock/zm
fi
}
status() {
result=`$command status`
if [ "$result" = "running" ]; then
echo "ZoneMinder is running"
RETVAL=0
else
echo "ZoneMinder is stopped"
RETVAL=1
fi
}
case "$1" in
'start')
start
;;
'stop')
stop
;;
'restart' | 'force-reload')
stop
start
;;
'status')
status
;;
*)
echo "Usage: $0 { start | stop | restart | status }"
RETVAL=1
;;
esac
exit $RETVAL
+11
View File
@@ -0,0 +1,11 @@
etc/zm/zm.conf
etc/zm/conf.d/*
usr/bin
usr/lib/zoneminder
usr/share/polkit-1
usr/share/zoneminder/db
usr/share/zoneminder/www
# libzoneminder-perl files:
usr/share/man/man3
usr/share/perl5
+1
View File
@@ -0,0 +1 @@
/var/tmp /usr/share/zoneminder/www/api/app/tmp
+14
View File
@@ -0,0 +1,14 @@
## cakephp
#replace /usr/share/php/Cake /usr/share/zoneminder/www/api/lib/Cake
## libjs-mootools
replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core.js
replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-nc.js
replace /usr/share/javascript/mootools/mootools.js /usr/share/zoneminder/www/tools/mootools/mootools-core-1.3.2-yc.js
replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more.js
replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-nc.js
replace /usr/share/javascript/mootools/mootools-more.js /usr/share/zoneminder/www/tools/mootools/mootools-more-1.3.2.1-yc.js
## libjs-jquery
replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/classic/js/jquery-1.4.2.min.js
replace /usr/share/javascript/jquery/jquery.min.js /usr/share/zoneminder/www/skins/flat/js/jquery-1.4.2.min.js
@@ -0,0 +1,14 @@
# Depends: policykit-1
unusual-interpreter usr/bin/zmsystemctl.pl #!/usr/bin/pkexec
# Intentionally not others-readable, #637685.
non-standard-file-perm etc/zm/zm.conf 0640 != 0644
# Bundled Cake PHP framework, not intended for direct execution:
script-not-executable usr/share/zoneminder/www/api/*
# Annoying but seems to be too much troubles to fix; should be fixed upstream:
script-with-language-extension usr/bin/*.pl
# dh-linktree:
package-contains-broken-symlink usr/share/zoneminder/www/api/lib/Cake/*
+13
View File
@@ -0,0 +1,13 @@
/var/log/zm/*.log {
missingok
notifempty
sharedscripts
delaycompress
compress
postrotate
/usr/bin/zmpkg.pl logrot >>/dev/null 2>&1 || :
endscript
daily
rotate 7
maxage 7
}
+1
View File
@@ -0,0 +1 @@
rm_conffile /etc/zm/apache.conf 1.28.1-5~
+1
View File
@@ -0,0 +1 @@
docs/_build/man/*.1
+66
View File
@@ -0,0 +1,66 @@
#! /bin/sh
set -e
if [ "$1" = "configure" ]; then
. /etc/zm/zm.conf
for i in /etc/zm/conf.d/*.conf; do
. $i
done;
# The logs can contain passwords, etc... so by setting group root, only www-data can read them, not people in the www-data group
chown www-data:root /var/log/zm
chown www-data:www-data /var/lib/zm
if [ -z "$2" ]; then
chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/*
fi
# Do this every time the package is installed or upgraded
# Ensure zoneminder is stopped
invoke-rc.d zoneminder stop || true
if [ "$ZM_DB_HOST" = "localhost" ]; then
if [ -e "/etc/init.d/mysql" ]; then
#
# Get mysql started if it isn't
#
if ! $(/etc/init.d/mysql status >/dev/null 2>&1); then
invoke-rc.d mysql start
fi
if $(/etc/init.d/mysql status >/dev/null 2>&1); then
mysqladmin --defaults-file=/etc/mysql/debian.cnf -f reload
# test if database if already present...
if ! $(echo quit | mysql --defaults-file=/etc/mysql/debian.cnf zm > /dev/null 2> /dev/null) ; then
cat /usr/share/zoneminder/db/zm_create.sql | mysql --defaults-file=/etc/mysql/debian.cnf
if [ $? -ne 0 ]; then
echo "Error creating db."
exit 1;
fi
# This creates the user.
echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost identified by \"${ZM_DB_PASS}\";" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
else
echo "grant lock tables, alter,drop,select,insert,update,delete,create,index,alter routine,create routine, trigger,execute on ${ZM_DB_NAME}.* to '${ZM_DB_USER}'@localhost;" | mysql --defaults-file=/etc/mysql/debian.cnf mysql
fi
zmupdate.pl --nointeractive
zmupdate.pl --nointeractive -f
# Add any new PTZ control configurations to the database (will not overwrite)
zmcamtool.pl --import >/dev/null 2>&1
else
echo 'NOTE: mysql not running, please start mysql and run dpkg-reconfigure zoneminder when it is running.'
fi
else
echo 'mysql not found, assuming remote server.'
fi
else
echo "Not doing database upgrade due to remote db server ($ZM_DB_HOST)"
fi
echo "Done Updating, starting ZoneMinder"
invoke-rc.d zoneminder start || true
fi
#DEBHELPER#
+14
View File
@@ -0,0 +1,14 @@
#! /bin/sh
set -e
if [ "$1" = "purge" ]; then
echo "
Reminder: to completely remove \"zoneminder\" it may be necessary
* to delete database using the following sample command:
sudo mysqladmin --defaults-file=/etc/mysql/debian.cnf -f drop zm
* to delete remaining data files in "/var/cache/zoneminder".
"
fi
#DEBHELPER#
+11
View File
@@ -0,0 +1,11 @@
#!/bin/sh
set -e
## Remove obsolete symlink which is in the way of dh_apache2:
ol="/etc/apache2/conf-available/zoneminder.conf"
if [ -h "${ol}" ]; then
[ "$(readlink ${ol})" = "/etc/zm/apache.conf" ] && rm -f "${ol}"
fi
#DEBHELPER#
+4
View File
@@ -0,0 +1,4 @@
d /run/zm 0755 www-data www-data
d /tmp/zm 0755 www-data www-data
d /var/tmp/zm 0755 www-data www-data
d /var/cache/zoneminder/cache 0755 www-data www-data
+9 -3
View File
@@ -28,7 +28,7 @@
%global _hardened_build 1
Name: zoneminder
Version: 1.35.4
Version: 1.35.5
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
@@ -43,6 +43,7 @@ Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zon
Source1: https://github.com/ZoneMinder/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz
Source2: https://github.com/ZoneMinder/CakePHP-Enum-Behavior/archive/%{ceb_version}.tar.gz#/cakephp-enum-behavior-%{ceb_version}.tar.gz
%{?rhel:BuildRequires: epel-rpm-macros}
BuildRequires: systemd-devel
BuildRequires: mariadb-devel
BuildRequires: perl-podlators
@@ -206,16 +207,20 @@ mv -f CakePHP-Enum-Behavior-%{ceb_version} ./web/api/app/Plugin/CakePHP-Enum-Beh
./utils/zmeditconfigdata.sh ZM_OPT_FAST_DELETE no
%build
# Disable LTO due to top level asm
# See https://fedoraproject.org/wiki/LTOByDefault
%define _lto_cflags %{nil}
%cmake3 \
-DZM_WEB_USER="%{zmuid_final}" \
-DZM_WEB_GROUP="%{zmgid_final}" \
-DZM_TARGET_DISTRO="%{zmtargetdistro}" \
.
%make_build
%cmake3_build
%install
%make_install
%cmake3_install
desktop-file-install \
--dir %{buildroot}%{_datadir}/applications \
@@ -357,6 +362,7 @@ EOF
%{_bindir}/zmtelemetry.pl
%{_bindir}/zmx10.pl
%{_bindir}/zmonvif-probe.pl
%{_bindir}/zmonvif-trigger.pl
%{_bindir}/zmstats.pl
%{_bindir}/zmrecover.pl
+1 -1
View File
@@ -83,7 +83,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libssl | libssl1.0.0 | libssl1.1
,libcrypt-eksblowfish-perl
,libdata-entropy-perl
,libvncclient1
,libvncclient1|libvncclient0
Recommends: ${misc:Recommends}
,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm
,mysql-server | mariadb-server | virtual-mysql-server
+2 -2
View File
@@ -21,8 +21,8 @@ override_dh_auto_configure:
-DCMAKE_BUILD_TYPE=Release \
-DZM_CONFIG_DIR="/etc/zm" \
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
-DZM_RUNDIR="/var/run/zm" \
-DZM_SOCKDIR="/var/run/zm" \
-DZM_RUNDIR="/run/zm" \
-DZM_SOCKDIR="/run/zm" \
-DZM_TMPDIR="/tmp/zm" \
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
+1 -1
View File
@@ -3,7 +3,7 @@ Section: net
Priority: optional
Maintainer: Isaac Connor <isaac@zoneminder.com>
Uploaders: Isaac Connor <isaac@zoneminder.com>
Build-Depends: debhelper, dh-systemd, python3-sphinx, dh-linktree, dh-systemd, dh-apache2
Build-Depends: debhelper, dh-systemd, sphinx-doc, dh-linktree, dh-systemd, dh-apache2
,cmake
,libx264-dev, libmp4v2-dev
,libavdevice-dev
+2 -2
View File
@@ -21,8 +21,8 @@ override_dh_auto_configure:
-DCMAKE_BUILD_TYPE=Release \
-DZM_CONFIG_DIR="/etc/zm" \
-DZM_CONFIG_SUBDIR="/etc/zm/conf.d" \
-DZM_RUNDIR="/var/run/zm" \
-DZM_SOCKDIR="/var/run/zm" \
-DZM_RUNDIR="/run/zm" \
-DZM_SOCKDIR="/run/zm" \
-DZM_TMPDIR="/tmp/zm" \
-DZM_CGIDIR="/usr/lib/zoneminder/cgi-bin" \
-DZM_CACHEDIR="/var/cache/zoneminder/cache" \
+151 -5
View File
@@ -3,10 +3,157 @@ Debian
.. contents::
Easy Way: Debian Buster
------------------------
This procedure will guide you through the installation of ZoneMinder on Debian 10 (Buster).
**Step 1:** Make sure your system is up to date
Open a console and use ``su`` command to become root.
::
apt update
apt upgrade
**Step 2:** 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 3:** Install Apache and MySQL
These are not dependencies for the ZoneMinder package as they could be
installed elsewhere. If they are not installed yet in your system, you have to
trigger their installation manually.
::
sudo apt install apache2 mysql-server
**Step 4:** Add ZoneMinder's Package repository to your apt sources
ZoneMinder's Debian packages are not included in Debian's official package
repositories. To be able to install ZoneMinder with APT, you have to edit the
list of apt sources and add ZoneMinder's repository.
::
sudo nano /etc/apt/sources.list
Add the following to the bottom of the file
::
# ZoneMinder repository
deb https://zmrepo.zoneminder.com/debian/release-1.34 buster/
CTRL+o and <Enter> to save
CTRL+x to exit
Because ZoneMinder's package repository provides a secure connection through HTTPS, apt must be enabled for HTTPS.
::
sudo apt install apt-transport-https
Finally, download the GPG key for ZoneMinder's repository:
::
wget -O - https://zmrepo.zoneminder.com/debian/archive-keyring.gpg | sudo apt-key add -
**Step 5:** Install ZoneMinder
::
sudo apt update
sudo apt install zoneminder
**Step 6:** Read the Readme
The rest of the install process is covered in the README.Debian, so feel free to have
a read.
::
zcat /usr/share/doc/zoneminder/README.Debian.gz
**Step 7:** Enable ZoneMinder service
::
sudo systemctl enable zoneminder.service
**Step 8:** Configure Apache
The following commands will setup the default /zm virtual directory and configure
required apache modules.
::
sudo a2enconf zoneminder
sudo a2enmod rewrite
sudo a2enmod cgi # this is done automatically when installing the package. Redo this command manually only for troubleshooting.
**Step 9:** Edit Timezone in PHP
Automated way:
::
sudo sed -i "s/;date.timezone =/date.timezone = $(sed 's/\//\\\//' /etc/timezone)/g" /etc/php/7.0/apache2/php.ini
Manual way
::
sudo nano /etc/php/7.0/apache2/php.ini
Search for [Date] (Ctrl + w then type Date and press Enter) and change
date.timezone for your time zone. Don't forget to remove the ; from in front
of date.timezone.
::
[Date]
; Defines the default timezone used by the date functions
; http://php.net/date.timezone
date.timezone = America/New_York
CTRL+o then [Enter] to save
CTRL+x to exit
**Step 10:** Start ZoneMinder
Reload Apache to enable your changes and then start ZoneMinder.
::
sudo systemctl reload apache2
sudo systemctl start zoneminder
You are now ready to go with ZoneMinder. Open a browser and type either ``localhost/zm`` one the local machine or ``{IP-OF-ZM-SERVER}/zm`` if you connect from a remote computer.
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.32.3 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.34 on Debian 9.8.
**Step 1:** Make sure your system is up to date
@@ -52,7 +199,7 @@ Add the following to the bottom of the file
::
# ZoneMinder repository
deb https://zmrepo.zoneminder.com/debian/release stretch/
deb https://zmrepo.zoneminder.com/debian/release-1.34 stretch/
CTRL+o and <Enter> to save
CTRL+x to exit
@@ -275,7 +422,6 @@ CTRL+x to exit
**Step 12:** Please check the configuration
Zoneminder 1.32.x
1. Check path of ZM_PATH in '/etc/zm/conf.d/zmcustom.conf' is ZM_PATH_ZMS=/zm/cgi-bin/nph-zms
::
cat /etc/zm/conf.d/zmcustom.conf
@@ -309,8 +455,8 @@ Reload Apache to enable your changes and then start ZoneMinder.
::
{
"version": "1.29.0",
"apiversion": "1.29.0.1"
"version": "1.34.0",
"apiversion": "1.34.0.1"
}
**Congratulations** Your installation is complete
+2 -2
View File
@@ -53,7 +53,7 @@ ZoneMinder releases are now being hosted at RPM Fusion. New users should navigat
Note that RHEL/CentOS 7 users should use yum instead of dnf.
Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder. ZoneMinder will not run without completing the steps outlined in the README.
Once ZoneMinder has been installed, it is critically important that you read the README file under /usr/share/doc/zoneminder-common. ZoneMinder will not run without completing the steps outlined in the README.
How to Install Nightly Development Builds
-----------------------------------------
@@ -191,7 +191,7 @@ Now clone the ZoneMinder git repository from your home folder:
git clone https://github.com/ZoneMinder/zoneminder
cd zoneminder
This will create a sub-folder called ZoneMinder, which will contain the latest development source code.
This will create a sub-folder called zoneminder, which will contain the latest development source code.
If you have previsouly cloned the ZoneMinder git repo and wish to update it to the most recent, then issue these commands instead:
+1 -3
View File
@@ -32,12 +32,10 @@ BULK_FRAME_INTERVAL - Traditionally ZoneMinder writes an entry into the Frames d
EVENT_CLOSE_MODE - When a monitor is running in a continuous recording mode (Record or Mocord) events are usually closed after a fixed period of time (the section length). However in Mocord mode it is possible that motion detection may occur near the end of a section. This option controls what happens when an alarm occurs in Mocord mode. The 'time' setting means that the event will be closed at the end of the section regardless of alarm activity. The 'idle' setting means that the event will be closed at the end of the section if there is no alarm activity occurring at the time otherwise it will be closed once the alarm is over meaning the event may end up being longer than the normal section length. The 'alarm' setting means that if an alarm occurs during the event, the event will be closed once the alarm is over regardless of when this occurs. This has the effect of limiting the number of alarms to one per event and the events will be shorter than the section length if an alarm has occurred.
CREATE_ANALYSIS_IMAGES - By default during an alarm ZoneMinder records both the raw captured image and one that has been analysed and had areas where motion was detected outlined. This can be very useful during zone configuration or in analysing why events occurred. However it also incurs some overhead and in a stable system may no longer be necessary. This parameter allows you to switch the generation of these images off.
WEIGHTED_ALARM_CENTRES - ZoneMinder will always calculate the centre point of an alarm in a zone to give some indication of where on the screen it is. This can be used by the experimental motion tracking feature or your own custom extensions. In the alarmed or filtered pixels mode this is a simple midpoint between the extents of the detected pxiesl. However in the blob method this can instead be calculated using weighted pixel locations to give more accurate positioning for irregularly shaped blobs. This method, while more precise is also slower and so is turned off by default.
EVENT_IMAGE_DIGITS - As event images are captured they are stored to the filesystem with a numerical index. By default this index has three digits so the numbers start 001, 002 etc. This works works for most scenarios as events with more than 999 frames are rarely captured. However if you have extremely long events and use external applications then you may wish to increase this to ensure correct sorting of images in listings etc. Warning, increasing this value on a live system may render existing events unviewable as the event will have been saved with the previous scheme. Decreasing this value should have no ill effects.
DEFAULT_ASPECT_RATIO - When specifying the dimensions of monitors you can click a checkbox to ensure that the width stays in the correct ratio to the height, or vice versa. This setting allows you to indicate what the ratio of these settings should be. This should be specified in the format <width value>:<height value> and the default of 4:3 normally be acceptable but 11:9 is another common setting. If the checkbox is not clicked when specifying monitor dimensions this setting has no effect.
USER_SELF_EDIT - Ordinarily only users with system edit privilege are able to change users details. Switching this option on allows ordinary users to change their passwords and their language settings
USER_SELF_EDIT - Ordinarily only users with system edit privilege are able to change users details. Switching this option on allows ordinary users to change their passwords and their language settings
@@ -20,18 +20,16 @@ sub START {
sub Subscribe {
my ($self, $body, $header) = @_;
die "Subscribe must be called as object method (\$self is <$self>)" if not blessed($self);
#print " proxy/lib/WSNotification/Interfaces/WSBaseNotificationSender/NotificationProducerPort.pm Subscribe\n";
return $self->SUPER::call({
operation => 'Subscribe',
soap_action => 'http://docs.oasis-open.org/wsn/bw-2/Subscribe',
style => 'document',
body => {
'use' => 'literal',
namespace => 'http://schemas.xmlsoap.org/wsdl/soap/',
encodingStyle => '',
parts => [qw( WSNotification::Elements::Subscribe )],
use => 'literal',
namespace => 'http://schemas.xmlsoap.org/wsdl/soap/',
encodingStyle => '',
parts => [qw( WSNotification::Elements::Subscribe )],
},
header => {
@@ -42,39 +40,30 @@ sub Subscribe {
}, $body, $header);
}
sub GetCurrentMessage {
my ($self, $body, $header) = @_;
die "GetCurrentMessage must be called as object method (\$self is <$self>)" if not blessed($self);
return $self->SUPER::call({
operation => 'GetCurrentMessage',
soap_action => 'http://docs.oasis-open.org/wsn/bw-2/GetCurrentMessage',
style => 'document',
body => {
my ($self, $body, $header) = @_;
die "GetCurrentMessage must be called as object method (\$self is <$self>)" if not blessed($self);
return $self->SUPER::call({
operation => 'GetCurrentMessage',
soap_action => 'http://docs.oasis-open.org/wsn/bw-2/GetCurrentMessage',
style => 'document',
body => {
use => 'literal',
namespace => 'http://schemas.xmlsoap.org/wsdl/soap/',
encodingStyle => '',
parts => [qw( WSNotification::Elements::GetCurrentMessage )],
'use' => 'literal',
namespace => 'http://schemas.xmlsoap.org/wsdl/soap/',
encodingStyle => '',
parts => [qw( WSNotification::Elements::GetCurrentMessage )],
},
header => {
},
header => {
},
headerfault => {
}
}, $body, $header);
},
headerfault => {
}
}, $body, $header);
}
1;
__END__
=pod
+2 -2
View File
@@ -30,10 +30,10 @@ configure_file(zm.in "${CMAKE_CURRENT_BINARY_DIR}/zm" @ONLY)
#configure_file(zmeventdump.in zmeventdump @ONLY)
# Generate man files for the perl scripts destined for the bin folder
file(GLOB perlscripts "*.pl")
file(GLOB perlscripts "${CMAKE_CURRENT_BINARY_DIR}/*.pl")
FOREACH(PERLSCRIPT ${perlscripts})
get_filename_component(PERLSCRIPTNAME ${PERLSCRIPT} NAME)
POD2MAN(${PERLSCRIPT} zoneminder-${PERLSCRIPTNAME} 8 ${ZM_MANPAGE_DEST_PREFIX})
POD2MAN(${PERLSCRIPT} ${PERLSCRIPTNAME} 8 ${ZM_MANPAGE_DEST_PREFIX})
ENDFOREACH(PERLSCRIPT ${perlscripts})
# Install the perl scripts
@@ -252,7 +252,7 @@ our @options = (
authentication which passes an independently authentication
'remote' user via http. In this case ZoneMinder would use the
supplied user without additional authentication provided such a
user is configured ion ZoneMinder.
user is configured in ZoneMinder.
`,
requires => [ { name=>'ZM_OPT_USE_AUTH', value=>'yes' } ],
type => {
@@ -1513,6 +1513,26 @@ our @options = (
type => $types{boolean},
category => 'logging',
},
{
name => 'ZM_WEB_NAVBAR_TYPE',
default => 'normal',
description => 'Style of the web console navigation bar',
help => q`
Choose between different navigation bar styles for the web
console. The "normal" style has a menu across the top, which
collapses to a pull down menu on small screens. The "collapsed"
style is collapsed all the time. Instead of a menu across the
top, menu items are accessed from the drop down menu on the
right.
`,
type => {
db_type => 'string',
hint => 'normal|collapsed',
pattern => qr|^([nc])|i,
format => q( ($1 =~ /^n/) ? 'normal' : 'collapsed' )
},
category => 'web',
},
{
name => 'ZM_WEB_TITLE',
default => 'ZoneMinder',
@@ -1558,6 +1578,20 @@ our @options = (
type => $types{string},
category => 'web',
},
{
name => 'ZM_HOME_ABOUT',
default => 'yes',
description => 'Whether to enable the ZoneMinder About menu.',
help => q`
When enabled, the ZoneMinder logo in the top left corner of the
navigation bar becomes a menu with links to: the ZoneMinder
website, ZoneMinder Documentation, and the ZoneMinder forum.
End users wishing to rebrand their system may want to disable this
as the menu items are currently hard coded.
`,
type => $types{boolean},
category => 'web',
},
{
name => 'ZM_WEB_CONSOLE_BANNER',
default => '',
@@ -2635,22 +2669,6 @@ our @options = (
type => $types{boolean},
category => 'hidden',
},
{
name => 'ZM_CREATE_ANALYSIS_IMAGES',
default => 'yes',
description => 'Create analysed alarm images with motion outlined',
help => q`
By default during an alarm ZoneMinder records both the raw
captured image and one that has been analysed and had areas
where motion was detected outlined. This can be very useful
during zone configuration or in analysing why events occurred.
However it also incurs some overhead and in a stable system may
no longer be necessary. This parameter allows you to switch the
generation of these images off.
`,
type => $types{boolean},
category => 'config',
},
{
name => 'ZM_WEIGHTED_ALARM_CENTRES',
default => 'no',
@@ -45,7 +45,7 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
use URI;
our $ADDRESS;
our $uri;
sub open {
my $self = shift;
@@ -55,8 +55,7 @@ sub open {
# Has no scheme at the beginning, so won't parse as a URI
$self->{Monitor}->{ControlAddress} = 'http://'.$self->{Monitor}->{ControlAddress};
}
my $uri = URI->new($self->{Monitor}->{ControlAddress});
$ADDRESS = $uri->scheme.'://'.$uri->authority().$uri->path().($uri->port()?':'.$uri->port():'');
$uri = URI->new($self->{Monitor}->{ControlAddress});
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
@@ -65,14 +64,21 @@ sub open {
$self->{state} = 'closed';
my ( $username, $password, $host ) = ( $uri->authority() =~ /^([^:]+):([^@]*)@(.+)$/ );
$uri->userinfo(undef);
my $realm = $self->{Monitor}->{ControlDevice};
$self->{ua}->credentials($ADDRESS, $realm, $username, $password);
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
my $url = '/axis-cgi/param.cgi?action=list&group=Properties.PTZ.PTZ';
# test auth
my $res = $self->{ua}->get($ADDRESS.'/cgi/ptdc.cgi');
my $res = $self->{ua}->get($uri->canonical().$url);
if ( $res->is_success ) {
if ( $res->content() ne "Properties.PTZ.PTZ=yes\n" ) {
Warning('Response suggests that camera doesn\'t support PTZ. Content:('.$res->content().')');
}
$self->{state} = 'open';
return;
}
@@ -85,23 +91,18 @@ sub open {
}
if ( $$headers{'www-authenticate'} ) {
Debug('Authenticating');
my ( $auth, $tokens ) = $$headers{'www-authenticate'} =~ /^(\w+)\s+(.*)$/;
if ( $tokens =~ /\w+="([^"]+)"/i ) {
if ( $realm ne $1 ) {
$realm = $1;
Debug("Changing REALM to $realm");
$self->{ua}->credentials($host, $realm, $username, $password);
$res = $self->{ua}->get($ADDRESS);
$self->{ua}->credentials($uri->host_port(), $realm, $username, $password);
$res = $self->{ua}->get($uri->canonical().$url);
if ( $res->is_success() ) {
Info("Auth succeeded after setting realm to $realm. You can set this value in the Control Device field to speed up connections and remove these log entries.");
$self->{state} = 'open';
return;
}
Error('Authentication still failed after updating REALM'.$res->status_line);
$headers = $res->headers();
foreach my $k ( keys %$headers ) {
Debug("Initial Header $k => $$headers{$k}");
} # end foreach
Error('Authentication still failed after updating REALM status: '.$res->status_line);
} else {
Error('Authentication failed, not a REALM problem');
}
@@ -111,6 +112,8 @@ sub open {
} else {
Debug('No headers line');
} # end if headers
} else {
Debug('Failed to open '.$uri->canonical().$url.' status: '.$res->status_line());
} # end if $res->status_line() eq '401 Unauthorized'
} # end sub open
@@ -120,11 +123,11 @@ sub sendCmd {
$self->printMsg($cmd, 'Tx');
my $url = $ADDRESS.$cmd;
my $url = $uri->canonical().$cmd;
my $res = $self->{ua}->get($url);
if ( $res->is_success ) {
Debug('sndCmd command: ' . $url . ' content: '.$res->content);
Debug('sndCmd command: '.$url.' content: '.$res->content);
return !undef;
}
@@ -143,10 +143,10 @@ sub PutCmd {
} else {
Info('Missing username and password in Control Address.');
}
Fatal($res->status_line);
Error($res->status_line);
}
} else {
Fatal($res->status_line);
Error($res->status_line);
}
} # end unless res->is_success
} # end sub putCmd
@@ -133,6 +133,7 @@ sub sendCmd {
my $result = undef;
$self->printMsg($cmd, 'Tx');
$self->printMsg($msg, 'Tx');
my $server_endpoint = 'http://'.$address.':'.$port.'/'.$cmd;
my $req = HTTP::Request->new(POST => $server_endpoint);
@@ -147,6 +148,7 @@ sub sendCmd {
if ( $res->is_success ) {
$result = !undef;
Debug("Result: " . $res->content());
} else {
Error("After sending PTZ command, camera returned the following error:'".$res->status_line()."'\nMSG:$msg\nResponse:".$res->content);
}
@@ -208,12 +210,56 @@ sub reset {
$self->sendCmd($cmd, $msg, $content_type);
}
sub moveMap {
my $self = shift;
my $params = shift;
my $x = $self->getParam($params,'xcoord');
my $y = $self->getParam($params,'ycoord');
Debug("Move map to $x x $y");
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<AbsoluteMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>' . $profileToken . '</ProfileToken>
<Position><PanTilt x="'.$x.'" y="'.$y.'" xmlns="http://www.onvif.org/ver10/schema"/></Position>
<Speed><Zoom x="1" xmlns="http://www.onvif.org/ver10/schema"/></Speed>
</AbsoluteMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
}
sub moveRel {
my $self = shift;
my $params = shift;
my $x = $self->getParam($params,'xcoord');
my $speed = $self->getParam($params,'speed');
my $y = $self->getParam($params,'ycoord');
Debug("Move rel to $x x $y");
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<RelativeMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>' . $profileToken . '</ProfileToken>
<Translation>
<PanTilt x="'.$x.'" y="'.$y.'" xmlns="http://www.onvif.org/ver10/schema" space="http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace"/>
<Zoom x="1"/>
</Translation>
<!--<Speed><Zoom x="'.$speed.'" xmlns="http://www.onvif.org/ver10/schema" space="http://www.onvif.org/ver10/tptz/PanTiltSpaces/PositionGenericSpace"/></Speed>_-->
</RelativeMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
}
#Up Arrow
sub moveConUp {
Debug('Move Up');
my $self = shift;
my $cmd = 'onvif/PTZ';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl"><ProfileToken>' . $profileToken . '</ProfileToken><Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity></ContinuousMove></s:Body></s:Envelope>';
my $msg ='<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">' . ((%identity) ? authentificationHeader($identity{username}, $identity{password}) : '') . '<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>' . $profileToken . '</ProfileToken>
<Velocity><PanTilt x="0" y="0.5" xmlns="http://www.onvif.org/ver10/schema"/></Velocity>
</ContinuousMove></s:Body></s:Envelope>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
@@ -82,6 +82,13 @@ sub sendCmd {
return $result;
}
sub reboot {
my $self = shift;
Debug('Camera Rebot');
my $cmd = '/admin/reboot.cgi?type=0';
$self->sendCmd($cmd);
}
sub reset {
my $self = shift;
Debug('Camera Reset');
@@ -146,15 +146,15 @@ sub zmDbGetMonitors {
if ( $function ) {
if ( $function == DB_MON_CAPT ) {
$sql .= " where Function >= 'Monitor'";
$sql .= " where `Function` >= 'Monitor'";
} elsif ( $function == DB_MON_ACTIVE ) {
$sql .= " where Function > 'Monitor'";
$sql .= " where `Function` > 'Monitor'";
} elsif ( $function == DB_MON_MOTION ) {
$sql .= " where Function = 'Modect' or Function = 'Mocord'";
$sql .= " where `Function` = 'Modect' or Function = 'Mocord'";
} elsif ( $function == DB_MON_RECORD ) {
$sql .= " where Function = 'Record' or Function = 'Mocord'";
$sql .= " where `Function` = 'Record' or Function = 'Mocord'";
} elsif ( $function == DB_MON_PASSIVE ) {
$sql .= " where Function = 'Nodect'";
$sql .= " where `Function` = 'Nodect'";
}
}
my $sth = $dbh->prepare_cached( $sql );
+40 -4
View File
@@ -352,6 +352,11 @@ sub GenerateVideo {
sub delete {
my $event = $_[0];
if ( !$event->canEdit() ) {
Warning('No permission to delete event.');
return 'No permission to delete event.';
}
my $in_zmaudit = ( $0 =~ 'zmaudit.pl$');
if ( ! $in_zmaudit ) {
@@ -363,7 +368,7 @@ sub delete {
return;
}
if ( !($event->Storage()->Path() and -e $event->Storage()->Path()) ) {
Warning('Not deleting event because storage path doesn\'t exist');
Warning('Not deleting event because storage path ('.$event->Storage()->Path().') doesn\'t exist');
return;
}
}
@@ -402,6 +407,11 @@ sub delete {
sub delete_files {
my $event = shift;
if ( !$event->canEdit() ) {
Warning('No permission to delete event.');
return 'No permission to delete event.';
}
foreach my $Storage (
@_ ? ($_[0]) : (
new ZoneMinder::Storage($$event{StorageId}),
@@ -570,6 +580,11 @@ sub DiskSpace {
sub CopyTo {
my ( $self, $NewStorage ) = @_;
if ( !$self->canEdit() ) {
Warning('No permission to copy event.');
return 'No permission to copy event.';
}
my $OldStorage = $self->Storage(undef);
my ( $OldPath ) = ( $self->Path() =~ /^(.*)$/ ); # De-taint
if ( ! -e $OldPath ) {
@@ -734,8 +749,13 @@ sub CopyTo {
} # end sub CopyTo
sub MoveTo {
my ( $self, $NewStorage ) = @_;
if ( !$self->canEdit() ) {
Warning('No permission to move event.');
return 'No permission to move event.';
}
my $OldStorage = $self->Storage(undef);
my $error = $self->CopyTo($NewStorage);
@@ -857,16 +877,32 @@ sub files {
sub has_capture_jpegs {
@{$_[0]{capture_jpegs}} = grep(/^\d+\-capture\.jpg$/, $_[0]->files());
Debug("have " . @{$_[0]{capture_jpegs}} . " capture jpegs");
Debug('have ' . @{$_[0]{capture_jpegs}} . ' capture jpegs');
return @{$_[0]{capture_jpegs}} ? 1 : 0;
}
sub has_analyse_jpegs {
@{$_[0]{analyse_jpegs}} = grep(/^\d+\-analyse\.jpg$/, $_[0]->files());
Debug("have " . @{$_[0]{analyse_jpegs}} . " analyse jpegs");
Debug('have ' . @{$_[0]{analyse_jpegs}} . ' analyse jpegs');
return @{$_[0]{analyse_jpegs}} ? 1 : 0;
}
sub canEdit {
my $self = shift;
if ( !$ZoneMinder::user ) {
# No user loaded... assume running as system
return 1;
}
if ( !$$ZoneMinder::user{MonitorIds} ) {
# User has no monitor limitations
return 1;
}
if ( $$ZoneMinder::user{Events} eq 'Edit' ) {
return 1;
}
return 0;
} # end sub canEdit
1;
__END__
+37 -48
View File
@@ -49,49 +49,34 @@ use ZoneMinder::Database qw(:all);
require ZoneMinder::Storage;
require ZoneMinder::Server;
sub Name {
if ( @_ > 1 ) {
$_[0]{Name} = $_[1];
}
return $_[0]{Name};
} # end sub Path
use vars qw/ $table $primary_key %fields /;
$table = 'Users';
$primary_key = 'Id';
sub find {
shift if $_[0] eq 'ZoneMinder::Filter';
my %sql_filters = @_;
my $sql = 'SELECT * FROM Filters';
my @sql_filters;
my @sql_values;
if ( exists $sql_filters{Name} ) {
push @sql_filters , ' Name = ? ';
push @sql_values, $sql_filters{Name};
}
$sql .= ' WHERE ' . join(' AND ', @sql_filters ) if @sql_filters;
$sql .= ' LIMIT ' . $sql_filters{limit} if $sql_filters{limit};
my $sth = $ZoneMinder::Database::dbh->prepare_cached($sql)
or Fatal("Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr());
my $res = $sth->execute(@sql_values)
or Fatal("Can't execute '$sql': ".$sth->errstr());
my @results;
while( my $db_filter = $sth->fetchrow_hashref() ) {
my $filter = new ZoneMinder::Filter($$db_filter{Id}, $db_filter);
push @results, $filter;
} # end while
$sth->finish();
return @results;
}
sub find_one {
my @results = find(@_);
return $results[0] if @results;
}
%fields = map { $_ => $_ } qw(
Id
Name
Query_json
AutoArchive
AutoVideo
AutoUpload
AutoEmail
EmailTo
EmailSubject
EmailBody
AutoMessage
AutoExecute
AutoExecuteCmd
AutoDelete
AutoMove
AutoMoveTo
AutoCopy
AutoCopyTo
UpdateDiskSpace
UserId
Background
Concurrent
);
sub Execute {
my $self = $_[0];
@@ -324,14 +309,9 @@ sub Sql {
} # end if terms
if ( $self->{Sql} ) {
if ( $self->{AutoMessage} ) {
# Include all events, including events that are still ongoing
# and have no EndTime yet
$sql .= ' WHERE ( '.$self->{Sql}.' )';
} else {
# Only include closed events (events with valid EndTime)
$sql .= ' WHERE (E.EndTime IS NOT NULL) AND ( '.$self->{Sql}.' )';
}
$sql .= ' WHERE ( '.$self->{Sql}.' )';
}
my @auto_terms;
if ( $self->{AutoArchive} ) {
@@ -458,6 +438,15 @@ sub DateTimeToSQL {
return POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime($dt_val));
}
sub User {
my $self = shift;
$$self{User} = shift if @_;
if ( ! $$self{User} and $$self{UserId} ) {
$$self{User} = ZoneMinder::User->find_one(Id=>$$self{UserId});
}
return $$self{User};
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
+39 -2
View File
@@ -28,6 +28,9 @@ our %EXPORT_TAGS = (
makePath
jsonEncode
jsonDecode
systemStatus
packageControl
daemonControl
) ]
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
@@ -342,7 +345,7 @@ sub createEvent {
." to ".$frame->{capturePath}.": $!"
);
setFileOwner( $frame->{capturePath} );
if ( 0 && $Config{ZM_CREATE_ANALYSIS_IMAGES} ) {
if ( $event->{SaveJPEGs} > 1 ) {
$frame->{analysePath} = sprintf(
"%s/%0".$Config{ZM_EVENT_IMAGE_DIGITS}
."d-analyse.jpg"
@@ -531,6 +534,38 @@ sub jsonDecode {
return $result;
}
sub packageControl {
my $command = shift;
my $string = $Config{ZM_PATH_BIN}.'/zmpkg.pl '.$command;
$string .= ' 2>/dev/null >&- <&- >/dev/null';
executeShellCommand($string);
}
sub daemonControl {
my ($command, $daemon, $args) = @_;
my $string = $Config{ZM_PATH_BIN}.'/zmdc.pl '.$command;
if ( $daemon ) {
$string .= ' ' . $daemon;
if ( $args ) {
$string .= ' ' . $args;
}
}
#$string .= ' 2>/dev/null >&- <&- >/dev/null';
executeShellCommand($string);
}
sub systemStatus {
my $command = $Config{ZM_PATH_BIN}.'/zmdc.pl check';
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
$output = '' if !defined($output);
chomp($output);
Debug("Command: $command Output: $output");
}
return $output;
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
@@ -542,7 +577,6 @@ ZoneMinder::General - Utility Functions for ZoneMinder
=head1 SYNOPSIS
use ZoneMinder::General;
blah blah blah
=head1 DESCRIPTION
@@ -561,6 +595,9 @@ of the ZoneMinder scripts
makePath
jsonEncode
jsonDecode
packageControl
daemonControl
systemStatus
) ]
@@ -162,6 +162,8 @@ our $mem_data = {
imagesize => { type=>'uint32', seq=>$mem_seq++ },
epadding1 => { type=>'uint32', seq=>$mem_seq++ },
startup_time => { type=>'time_t64', seq=>$mem_seq++ },
zmc_heartbeat_time => { type=>'time_t64', seq=>$mem_seq++ },
zma_heartbeat_time => { type=>'time_t64', seq=>$mem_seq++ },
last_write_time => { type=>'time_t64', seq=>$mem_seq++ },
last_read_time => { type=>'time_t64', seq=>$mem_seq++ },
control_state => { type=>'uint8[256]', seq=>$mem_seq++ },
+80
View File
@@ -0,0 +1,80 @@
# ==========================================================================
#
# ZoneMinder User Module
# Copyright (C) 2020 ZoneMinder LLC
#
# 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.
#
# ==========================================================================
package ZoneMinder::Frame;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Object;
use parent qw(ZoneMinder::Object);
use vars qw/ $table $primary_key %fields /;
$table = 'Users';
$primary_key = 'Id';
%fields = map { $_ => $_ } qw(
Id
Username
Password
Language
Enabled
Stream
Events
Control
Monitors
Groups
Devices
System
MaxBandwidth
MonitorIds
TokenMinExpiry
APIEnabled
);
1;
__END__
=head1 NAME
ZoneMinder::User - Perl Class for Users
=head1 SYNOPSIS
use ZoneMinder::User;
=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
+3 -3
View File
@@ -1059,6 +1059,7 @@ sub delete_empty_directories {
}
} # end sub delete_empty_directories
# The idea is that the youngest file in the event directory gives us the event starttime
sub time_of_youngest_file {
my $dir = shift;
@@ -1067,14 +1068,13 @@ sub time_of_youngest_file {
return;
}
my $youngest = (stat($dir))[9];
Debug("stat of $dir is $youngest");
Debug("stat of dir $dir is $youngest");
foreach my $file ( readdir( DIR ) ) {
next if $file =~ /^\./;
$_ = (stat($dir))[9];
$youngest = $_ if $_ and ( $_ < $youngest );
#Debug("stat of $dir is $_ < $youngest");
Debug("Found younger file $file at $youngest");
}
Debug("stat of $dir is $youngest");
return $youngest;
} # end sub time_of_youngest_file
+3 -3
View File
@@ -49,9 +49,9 @@ my %options;
GetOptions(
'id=i' =>\$id,
'command=s' =>\$options{command},
'xcoord=i' =>\$options{xcoord},
'ycoord=i' =>\$options{ycoord},
'speed=i' =>\$options{speed},
'xcoord=f' =>\$options{xcoord},
'ycoord=f' =>\$options{ycoord},
'speed=f' =>\$options{speed},
'step=i' =>\$options{step},
'panspeed=i' =>\$options{panspeed},
'tiltspeed=i' =>\$options{tiltspeed},
+9 -8
View File
@@ -160,7 +160,7 @@ if ( !$server_up ) {
use ZoneMinder::Server qw(CpuLoad);
if ( ! defined $dbh->do(q{UPDATE Servers SET Status=?,TotalMem=?,FreeMem=?,TotalSwap=?,FreeSwap=? WHERE Id=?}, undef,
'NotRunning', &totalmem, &freemem, &totalswap, &freeswap, $Config{ZM_SERVER_ID} ) ) {
Error("Failed Updating status of Server record to Not RUnning for Id=$Config{ZM_SERVER_ID}" . $dbh->errstr());
Error('Failed Updating status of Server record to Not Running for Id='.$Config{ZM_SERVER_ID}.': '.$dbh->errstr());
}
}
# Server is not up. Some commands can still be handled
@@ -173,7 +173,7 @@ if ( !$server_up ) {
print("stopped\n");
exit();
} elsif ( $command ne 'startup' ) {
print('Unable to connect to server using socket at ' . SOCK_FILE . "\n");
print('Unable to connect to server using socket at '.SOCK_FILE."\n");
exit(-1);
}
@@ -189,10 +189,10 @@ if ( !$server_up ) {
socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
my $attempts = 0;
while( !connect(CLIENT, $saddr) ) {
while ( !connect(CLIENT, $saddr) ) {
$attempts++;
Debug('Waiting for zmdc.pl server process at '.SOCK_FILE.", attempt $attempts");
Fatal("Can't connect: $!") if $attempts > MAX_CONNECT_DELAY;
Debug('Waiting for zmdc.pl server process at '.SOCK_FILE.', attempt '.$attempts);
Fatal('Can\'t connect to zmdc.pl server process at '.SOCK_FILE.': '.$!) if $attempts > MAX_CONNECT_DELAY;
usleep(200000);
} # end while
} elsif ( defined($cpid) ) {
@@ -255,13 +255,13 @@ sub run {
close($PID);
} else {
# Log not initialized at this point so use die instead
die "Can't open pid file at ".ZM_PID."\n";
die 'Can\'t open pid file at '.ZM_PID."\n";
}
my $fd = 0;
# This also closes dbh and CLIENT and SERVER
while( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) {
while ( $fd < POSIX::sysconf(&POSIX::_SC_OPEN_MAX) ) {
POSIX::close($fd++);
}
@@ -301,13 +301,14 @@ sub run {
my $timeout = 1;
my $secs_count = 0;
while( !$zm_terminate ) {
while ( !$zm_terminate ) {
if ( $Config{ZM_SERVER_ID} ) {
if ( ! ( $secs_count % 60 ) ) {
while ( (!$zm_terminate) and !($dbh and $dbh->ping()) ) {
Warning("Not connected to db ($dbh)".($dbh?' ping('.$dbh->ping().')':''). ($DBI::errstr?" errstr($DBI::errstr)":'').' Reconnecting');
$dbh = zmDbConnect();
sleep 10 if !$dbh;
}
last if $zm_terminate;
+7 -7
View File
@@ -102,7 +102,7 @@ $SIG{__DIE__} = \&handler;
# =========================================================================
# Debug
use Data::Hexdumper qw(hexdump);
#use Data::Hexdumper qw(hexdump);
#use Devel::Peek;
sub dump_mapped {
@@ -116,12 +116,12 @@ sub dump_mapped {
printf("Mapped at %x\n", $monitor->{MMapAddr});
# Dump($mmap);
if ( $mmap && $$mmap ) {
print hexdump(
data => $$mmap, # what to dump
output_format => ' %4a : %S %S %S %S %S %S %S %S : %d',
# start_position => 336, # start at this offset ...
end_position => 400 # ... and end at this offset
);
#print hexdump(
#data => $$mmap, # what to dump
#output_format => ' %4a : %S %S %S %S %S %S %S %S : %d',
## start_position => 336, # start at this offset ...
#end_position => 400 # ... and end at this offset
#);
}
}
+43
View File
@@ -48,6 +48,49 @@ while( 1 ) {
$dbh->do('DELETE FROM Events_Week WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 week)') or Error($dbh->errstr());
$dbh->do('DELETE FROM Events_Month WHERE StartTime < DATE_SUB(NOW(), INTERVAL 1 month)') or Error($dbh->errstr());
# Prune the Logs table if required
if ( $Config{ZM_LOG_DATABASE_LIMIT} ) {
if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^\d+$/ ) {
# Number of rows
my $selectLogRowCountSql = 'SELECT count(*) AS `Rows` FROM `Logs`';
my $selectLogRowCountSth = $dbh->prepare_cached( $selectLogRowCountSql )
or Fatal("Can't prepare '$selectLogRowCountSql': ".$dbh->errstr());
my $res = $selectLogRowCountSth->execute()
or Fatal("Can't execute: ".$selectLogRowCountSth->errstr());
my $row = $selectLogRowCountSth->fetchrow_hashref();
my $logRows = $row->{Rows};
if ( $logRows > $Config{ZM_LOG_DATABASE_LIMIT} ) {
my $deleteLogByRowsSql = 'DELETE low_priority FROM `Logs` ORDER BY `TimeKey` ASC LIMIT ?';
my $deleteLogByRowsSth = $dbh->prepare_cached( $deleteLogByRowsSql )
or Fatal("Can't prepare '$deleteLogByRowsSql': ".$dbh->errstr());
$res = $deleteLogByRowsSth->execute( $logRows - $Config{ZM_LOG_DATABASE_LIMIT} )
or Fatal("Can't execute: ".$deleteLogByRowsSth->errstr());
if ( $deleteLogByRowsSth->rows() ) {
Debug('Deleted '.$deleteLogByRowsSth->rows().' log table entries by count');
}
}
} else {
# Time of record
# 7 days is invalid. We need to remove the s
if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^(.*)s$/ ) {
$Config{ZM_LOG_DATABASE_LIMIT} = $1;
}
my $deleted_rows;
do {
my $deleteLogByTimeSql =
'DELETE FROM `Logs`
WHERE `TimeKey` < unix_timestamp(now() - interval '.$Config{ZM_LOG_DATABASE_LIMIT}.') LIMIT 100';
my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql )
or Fatal("Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr());
my $res = $deleteLogByTimeSth->execute()
or Fatal("Can't execute: ".$deleteLogByTimeSth->errstr());
$deleted_rows = $deleteLogByTimeSth->rows();
Debug("Deleted $deleted_rows log table entries by time");
} while ( $deleted_rows );
}
} # end if ZM_LOG_DATABASE_LIMIT
sleep($Config{ZM_STATS_UPDATE_INTERVAL});
} # end while (1)
-1
View File
@@ -119,7 +119,6 @@ if ( !$monitor->{CanMoveMap} ) {
}
}
Debug("Found monitor for id '$monitor'");
exit(-1) if !zmMemVerify($monitor);
sub Suspend {
+112 -85
View File
@@ -119,6 +119,9 @@ GetOptions(
) or pod2usage(-exitstatus => -1);
my $dbh = zmDbConnect(undef, { mysql_multi_statements=>1 } );
if ( !$dbh ) {
die "Unable to connect to db\n";
}
$Config{ZM_DB_USER} = $dbUser;
$Config{ZM_DB_PASS} = $dbPass;
# we escape dbpass with single quotes so that $ in the password has no effect, but dbpass could have a ' in it.
@@ -128,18 +131,18 @@ if ( ! ($check || $freshen || $rename || $zoneFix || $migrateEvents || $version)
if ( $Config{ZM_DYN_DB_VERSION} ) {
$version = $Config{ZM_DYN_DB_VERSION};
} else {
print( STDERR "Please give a valid option\n" );
print(STDERR "Please give a valid option\n");
pod2usage(-exitstatus => -1);
}
}
if ( ($check + $freshen + $rename + $zoneFix + $migrateEvents + ($version?1:0)) > 1 ) {
print( STDERR "Please give only one option\n" );
print(STDERR "Please give only one option\n");
pod2usage(-exitstatus => -1);
}
if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
print( "Update agent starting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
print('Update agent starting at '.strftime('%y/%m/%d %H:%M:%S', localtime() )."\n");
my $currVersion = $Config{ZM_DYN_CURR_VERSION};
my $lastVersion = $Config{ZM_DYN_LAST_VERSION};
@@ -149,23 +152,23 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
$currVersion = $Config{ZM_VERSION};
my $sql = "update Config set Value = ? where Name = 'ZM_DYN_CURR_VERSION'";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute( "$currVersion" ) or die( "Can't execute: ".$sth->errstr() );
my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute($currVersion) or die("Can't execute: ".$sth->errstr());
$sth->finish();
}
while( 1 ) {
while ( 1 ) {
my $now = time();
if ( !$lastVersion || !$lastCheck || (($now-$lastCheck) > CHECK_INTERVAL) ) {
Info( "Checking for updates\n" );
Info('Checking for updates');
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
$ua->agent( "ZoneMinder Update Agent/".ZM_VERSION );
$ua->agent('ZoneMinder Update Agent/'.ZM_VERSION);
if ( $Config{ZM_UPDATE_CHECK_PROXY} ) {
$ua->proxy( 'http', $Config{ZM_UPDATE_CHECK_PROXY} );
$ua->proxy('http', $Config{ZM_UPDATE_CHECK_PROXY});
}
my $req = HTTP::Request->new( GET=>'https://update.zoneminder.com/version.txt' );
my $req = HTTP::Request->new(GET=>'https://update.zoneminder.com/version.txt');
my $res = $ua->request($req);
if ( $res->is_success ) {
@@ -173,63 +176,63 @@ if ( $check && $Config{ZM_CHECK_FOR_UPDATES} ) {
chomp($lastVersion);
$lastCheck = $now;
Info( "Got version: '".$lastVersion."'\n" );
Info('Got version: '.$lastVersion);
my $lv_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_VERSION'";
my $lv_sth = $dbh->prepare_cached( $lv_sql ) or die( "Can't prepare '$lv_sql': ".$dbh->errstr() );
my $lv_res = $lv_sth->execute( $lastVersion ) or die( "Can't execute: ".$lv_sth->errstr() );
my $lv_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_VERSION\'';
my $lv_sth = $dbh->prepare_cached($lv_sql) or die("Can't prepare '$lv_sql': ".$dbh->errstr());
my $lv_res = $lv_sth->execute($lastVersion) or die("Can't execute: ".$lv_sth->errstr());
$lv_sth->finish();
my $lc_sql = "update Config set Value = ? where Name = 'ZM_DYN_LAST_CHECK'";
my $lc_sth = $dbh->prepare_cached( $lc_sql ) or die( "Can't prepare '$lc_sql': ".$dbh->errstr() );
my $lc_res = $lc_sth->execute( $lastCheck ) or die( "Can't execute: ".$lc_sth->errstr() );
my $lc_sql = 'UPDATE Config SET Value = ? WHERE Name = \'ZM_DYN_LAST_CHECK\'';
my $lc_sth = $dbh->prepare_cached($lc_sql) or die("Can't prepare '$lc_sql': ".$dbh->errstr());
my $lc_res = $lc_sth->execute($lastCheck) or die("Can't execute: ".$lc_sth->errstr());
$lc_sth->finish();
} else {
Error( "Error check failed: '".$res->status_line()."'\n" );
Error('Error check failed: \''.$res->status_line().'\'');
}
}
sleep( 3600 );
sleep(3600);
}
print( "Update agent exiting at ".strftime( '%y/%m/%d %H:%M:%S', localtime() )."\n" );
print('Update agent exiting at '.strftime('%y/%m/%d %H:%M:%S', localtime())."\n");
}
if ( $rename ) {
require File::Find;
chdir( EVENT_PATH );
chdir(EVENT_PATH);
sub renameImage {
my $file = $_;
# Ignore directories
if ( -d $file ) {
print( "Checking directory '$file'\n" );
print("Checking directory '$file'\n");
return;
}
if ( $file !~ /(capture|analyse)-(\d+)(\.jpg)/ ) {
return;
}
my $newFile = "$2-$1$3";
my $newFile = $2.'-'.$1.$3;
print( "Renaming '$file' to '$newFile'\n" );
rename( $file, $newFile ) or warn( "Can't rename '$file' to '$newFile'" );
print("Renaming '$file' to '$newFile'\n");
rename($file, $newFile) or warn("Can't rename '$file' to '$newFile'");
}
File::Find::find( \&renameImage, '.' );
}
if ( $zoneFix ) {
File::Find::find(\&renameImage, '.');
} # end if rename
my $sql = "select Z.*, M.Width as MonitorWidth, M.Height as MonitorHeight from Zones as Z inner join Monitors as M on Z.MonitorId = M.Id where Z.Units = 'Percent'";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
if ( $zoneFix ) {
my $sql = "SELECT Z.*, M.Width AS MonitorWidth, M.Height AS MonitorHeight FROM Zones AS Z INNER JOIN Monitors AS M ON Z.MonitorId = M.Id WHERE Z.Units = 'Percent'";
my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute() or die("Can't execute: ".$sth->errstr());
my @zones;
while( my $zone = $sth->fetchrow_hashref() ) {
push( @zones, $zone );
while ( my $zone = $sth->fetchrow_hashref() ) {
push(@zones, $zone);
}
$sth->finish();
$sql = 'update Zones set MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? where Id = ?';
$sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
$sql = 'UPDATE Zones SET MinAlarmPixels = ?, MaxAlarmPixels = ?, MinFilterPixels = ?, MaxFilterPixels = ?, MinBlobPixels = ?, MaxBlobPixels = ? WHERE Id = ?';
$sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
foreach my $zone ( @zones ) {
my $zone_width = (($zone->{HiX}*$zone->{MonitorWidth})-($zone->{LoX}*$zone->{MonitorWidth}))/100;
my $zone_height = (($zone->{HiY}*$zone->{MonitorHeight})-($zone->{LoY}*$zone->{MonitorHeight}))/100;
@@ -243,78 +246,84 @@ if ( $zoneFix ) {
($zone->{MinBlobPixels}*$monitor_area)/$zone_area,
($zone->{MaxBlobPixels}*$monitor_area)/$zone_area,
$zone->{Id}
) or die( "Can't execute: ".$sth->errstr() );
) or die("Can't execute: ".$sth->errstr());
}
$sth->finish();
}
} # end if zoneFix
if ( $migrateEvents ) {
my $webUid = (getpwnam( $Config{ZM_WEB_USER} ))[2];
my $webGid = (getgrnam( $Config{ZM_WEB_USER} ))[2];
my $webUid = (getpwnam($Config{ZM_WEB_USER}))[2];
my $webGid = (getgrnam($Config{ZM_WEB_USER}))[2];
if ( !(($> == 0) || ($> == $webUid)) ) {
print( "Error, migrating events can only be done as user root or ".$Config{ZM_WEB_USER}.".\n" );
exit( -1 );
print("Error, migrating events can only be done as user root or ".$Config{ZM_WEB_USER}.".\n");
exit(-1);
}
# Run as web user/group
$( = $webGid;
$) = $webGid;
$) = $webGid;
$< = $webUid;
$> = $webUid;
print( "\nAbout to convert saved events to deep storage, please ensure that ZoneMinder is fully stopped before proceeding.\nThis process is not easily reversible. Are you sure you wish to proceed?\n\nPress 'y' to continue or 'n' to abort : " );
print('
About to convert saved events to deep storage, please ensure that ZoneMinder is fully stopped before proceeding.
This process is not easily reversible. Are you sure you wish to proceed?
Press \'y\' to continue or \'n\' to abort : ');
my $response = <STDIN>;
chomp( $response );
chomp($response);
while ( $response !~ /^[yYnN]$/ ) {
print( "Please press 'y' to continue or 'n' to abort only : " );
print("Please press 'y' to continue or 'n' to abort only : ");
$response = <STDIN>;
chomp( $response );
chomp($response);
}
if ( $response =~ /^[yY]$/ ) {
print( "Converting all events to deep storage.\n" );
print("Converting all events to deep storage.\n");
chdir( $Config{ZM_PATH_WEB} );
my $sql = "select *, unix_timestamp(StartTime) as UnixStartTime from Events";
my $sth = $dbh->prepare_cached( $sql ) or Fatal( "Can't prepare '$sql': ".$dbh->errstr() );
chdir($Config{ZM_PATH_WEB});
my $sql = 'SELECT *, unix_timestamp(StartTime) AS UnixStartTime FROM Events';
my $sth = $dbh->prepare_cached($sql) or Fatal("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute();
if ( !$res ) {
Fatal( "Can't fetch Events: ".$sth->errstr() );
Fatal("Can't fetch Events: ".$sth->errstr());
}
while( my $event = $sth->fetchrow_hashref() ) {
my $oldEventPath = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.$event->{Id};
if ( !-d $oldEventPath ) {
print( "Warning, can't find old event path '$oldEventPath', already converted?\n" );
print("Warning, can't find old event path '$oldEventPath', already converted?\n");
next;
}
print( "Converting event ".$event->{Id}."\n" );
my $newDatePath = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.strftime( "%y/%m/%d", localtime($event->{UnixStartTime}) );
my $newTimePath = strftime( "%H/%M/%S", localtime($event->{UnixStartTime}) );
print('Converting event '.$event->{Id}."\n");
my $newDatePath = $Config{ZM_DIR_EVENTS}.'/'.$event->{MonitorId}.'/'.strftime('%y/%m/%d', localtime($event->{UnixStartTime}));
my $newTimePath = strftime('%H/%M/%S', localtime($event->{UnixStartTime}));
my $newEventPath = $newDatePath.'/'.$newTimePath;
( my $truncEventPath = $newEventPath ) =~ s|/\d+$||;
makePath( $truncEventPath, $Config{ZM_PATH_WEB} );
makePath($truncEventPath, $Config{ZM_PATH_WEB});
my $idLink = $newDatePath.'/.'.$event->{Id};
symlink( $newTimePath, $idLink ) or die( "Can't symlink $newTimePath -> $idLink: $!" );
rename( $oldEventPath, $newEventPath ) or die( "Can't move $oldEventPath -> $newEventPath: $!" );
symlink($newTimePath, $idLink) or die("Can't symlink $newTimePath -> $idLink: $!");
rename($oldEventPath, $newEventPath) or die("Can't move $oldEventPath -> $newEventPath: $!");
}
$sth->finish();
print( "Updating configuration.\n" );
print("Updating configuration.\n");
$sql = "UPDATE `Config` SET `Value` = ? WHERE `Name` = 'ZM_USE_DEEP_STORAGE'";
$sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
$res = $sth->execute( 1 ) or die( "Can't execute: ".$sth->errstr() );
$sth = $dbh->prepare_cached($sql) or die( "Can't prepare '$sql': ".$dbh->errstr());
$res = $sth->execute(1) or die("Can't execute: ".$sth->errstr());
$sth->finish();
print( "All events converted.\n\n" );
print("All events converted.\n\n");
} else {
print( "Aborting event conversion.\n\n" );
print("Aborting event conversion.\n\n");
}
}
if ( $freshen ) {
print( "\nFreshening configuration in database\n" );
print("\nFreshening configuration in database\n");
migratePaths();
migratePasswords();
ZoneMinder::Config::loadConfigFromDB();
@@ -326,8 +335,8 @@ if ( $interactive ) {
# Now check for MyISAM Tables
my @MyISAM_Tables;
my $sql = "SELECT `table_name` FROM INFORMATION_SCHEMA.TABLES WHERE `table_schema`='zm' AND `engine` = 'MyISAM'";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute() or die("Can't execute: ".$sth->errstr());
while( my $dbTable = $sth->fetchrow() ) {
push @MyISAM_Tables, $dbTable;
@@ -335,17 +344,22 @@ if ( $interactive ) {
$sth->finish();
if ( @MyISAM_Tables ) {
print( "\nPrevious versions of ZoneMinder used the MyISAM database engine.\nHowever, the recommended database engine is InnoDB.\n");
print( "\nHint: InnoDB tables are much less likely to be corrupted during an unclean shutdown.\n\nPress 'y' to convert your tables to InnoDB or 'n' to skip : ");
print('
Previous versions of ZoneMinder used the MyISAM database engine.
However, the recommended database engine is InnoDB.
Hint: InnoDB tables are much less likely to be corrupted during an unclean shutdown.
Press \'y\' to convert your tables to InnoDB or \'n\' to skip : ');
my $response = <STDIN>;
chomp( $response );
if ( $response =~ /^[yY]$/ ) {
$dbh->do(q|SET sql_mode='traditional'|); # Elevate warnings to errors
print "\nConverting MyISAM tables to InnoDB. Please wait.\n";
foreach (@MyISAM_Tables) {
print "\nConverting MyISAM tables to InnoDB. Please wait.\n";
foreach ( @MyISAM_Tables ) {
my $sql = "ALTER TABLE `$_` ENGINE = InnoDB";
my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
my $res = $sth->execute() or die( "Can't execute: ".$sth->errstr() );
my $sth = $dbh->prepare_cached($sql) or die("Can't prepare '$sql': ".$dbh->errstr());
my $res = $sth->execute() or die("Can't execute: ".$sth->errstr());
$sth->finish();
}
$dbh->do(q|SET sql_mode=''|); # Set mode back to default
@@ -362,23 +376,31 @@ if ( $version ) {
exit(0);
}
print( "\nInitiating database upgrade to version ".ZM_VERSION." from version $version\n" );
my $start_zm = 0;
print("\nInitiating database upgrade to version ".ZM_VERSION." from version $version\n");
if ( $interactive ) {
if ( $Config{ZM_DYN_DB_VERSION} && $Config{ZM_DYN_DB_VERSION} ne $version ) {
print( "\nWARNING - You have specified an upgrade from version $version but the database version found is ".$Config{ZM_DYN_DB_VERSION}.". Is this correct?\nPress enter to continue or ctrl-C to abort : " );
if ( $Config{ZM_DYN_DB_VERSION} && ($Config{ZM_DYN_DB_VERSION} ne $version) ) {
print("\nWARNING - You have specified an upgrade from version $version but the database version found is $Config{ZM_DYN_DB_VERSION}. Is this correct?\nPress enter to continue or ctrl-C to abort : ");
my $response = <STDIN>;
}
print( "\nPlease ensure that ZoneMinder is stopped on your system prior to upgrading the database.\nPress enter to continue or ctrl-C to stop : " );
my $response = <STDIN>;
if ( systemStatus() eq 'running' ) {
print"\nZoneMinder system appears to be running. While not strictly required, it is advised to stop ZM during the update process. Would you like to stop ZM now? [Yn]:";
my $response = <STDIN>;
chomp($response);
if ( $response !~ /Yy/ ) {
packageControl('stop');
$start_zm = 1;
}
}
print( "\nDo you wish to take a backup of your database prior to upgrading?\nThis may result in a large file in @ZM_TMPDIR@ if you have a lot of events.\nPress 'y' for a backup or 'n' to continue : " );
$response = <STDIN>;
chomp( $response );
print("\nDo you wish to take a backup of your database prior to upgrading?\nThis may result in a large file in @ZM_TMPDIR@ if you have a lot of events.\nPress 'y' for a backup or 'n' to continue : ");
my $response = <STDIN>;
chomp($response);
while ( $response !~ /^[yYnN]$/ ) {
print( "Please press 'y' for a backup or 'n' to continue only : " );
print("Please press 'y' for a backup or 'n' to continue only : ");
$response = <STDIN>;
chomp( $response );
chomp($response);
}
if ( $response =~ /^[yY]$/ ) {
@@ -400,8 +422,8 @@ if ( $version ) {
my $backup = '@ZM_TMPDIR@/'.$Config{ZM_DB_NAME}.'-'.$version.'.dump';
$command .= ' --add-drop-table --databases '.$Config{ZM_DB_NAME}.' > '.$backup;
print("Creating backup to $backup. This may take several minutes.\n");
print("Executing '$command'\n") if logDebugging();
($command) = $command =~ /(.*)/; # detaint
print("Executing '$command'\n") if logDebugging();
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
@@ -936,7 +958,12 @@ if ( $version ) {
#my $sth = $dbh->prepare_cached( $sql ) or die( "Can't prepare '$sql': ".$dbh->errstr() );
#my $res = $sth->execute( ) or die( "Can't execute: ".$sth->errstr() );
#$sth->finish();
print("\nDatabase upgrade to version ".ZM_VERSION." successful.\n\n");
if ( $start_zm ) {
print("Starting ZM since we stopped it for the update\n");
packageControl('start');
}
} # end if version
zmDbDisconnect();
@@ -972,7 +999,7 @@ sub patchDB {
my $dbh = shift;
my $version = shift;
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ ) if $Config{ZM_DB_HOST};
my $command = 'mysql';
if ( defined($portOrSocket) ) {
if ( $portOrSocket =~ /^\// ) {
@@ -980,7 +1007,7 @@ sub patchDB {
} else {
$command .= ' -h'.$host.' -P'.$portOrSocket;
}
} else {
} elsif ( $host ) {
$command .= ' -h'.$host;
}
if ( $dbUser ) {
+1 -1
View File
@@ -27,7 +27,7 @@ target_link_libraries(zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS} ${CMAKE_DL_LIBS} bc
# Generate man files for the binaries destined for the bin folder
FOREACH(CBINARY zma zmc zmu)
POD2MAN(${CMAKE_CURRENT_SOURCE_DIR}/${CBINARY}.cpp zoneminder-${CBINARY} 8 ${ZM_MANPAGE_DEST_PREFIX})
POD2MAN(${CMAKE_CURRENT_SOURCE_DIR}/${CBINARY}.cpp ${CBINARY} 8 ${ZM_MANPAGE_DEST_PREFIX})
ENDFOREACH(CBINARY zma zmc zmu)
install(TARGETS zmc zma zmu RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
+4 -3
View File
@@ -48,11 +48,12 @@ Camera::Camera(
record_audio(p_record_audio),
bytes(0)
{
linesize = width * colours;
pixels = width * height;
imagesize = pixels * colours;
imagesize = height * linesize;
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",
monitor_id, width, height, colours, subpixelorder, capture);
Debug(2, "New camera id: %d width: %d line size: %d height: %d colours: %d subpixelorder: %d capture: %d",
monitor_id, width, linesize, height, colours, subpixelorder, capture);
monitor = NULL;
}
+2
View File
@@ -41,6 +41,7 @@ protected:
Monitor * monitor; // Null on instantiation, set as soon as possible.
SourceType type;
unsigned int width;
unsigned int linesize;
unsigned int height;
unsigned int colours;
unsigned int subpixelorder;
@@ -70,6 +71,7 @@ public:
bool IscURL() const { return type == CURL_SRC; }
bool IsVNC() const { return type == VNC_SRC; }
unsigned int Width() const { return width; }
unsigned int LineSize() const { return linesize; }
unsigned int Height() const { return height; }
unsigned int Colours() const { return colours; }
unsigned int SubpixelOrder() const { return subpixelorder; }
+4 -4
View File
@@ -96,10 +96,10 @@ void zmLoadConfig() {
}
if ( staticConfig.SERVER_ID ) {
Debug(3, "Multi-server configuration detected. Server is %d.", staticConfig.SERVER_ID);
} else {
Debug(3, "Single server configuration assumed because no Server ID or Name was specified.");
}
Debug(3, "Multi-server configuration detected. Server is %d.", staticConfig.SERVER_ID);
} else {
Debug(3, "Single server configuration assumed because no Server ID or Name was specified.");
}
}
snprintf(staticConfig.capture_file_format, sizeof(staticConfig.capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits);
+82 -94
View File
@@ -87,7 +87,7 @@ Event::Event(
char sql[ZM_SQL_MED_BUFSIZ];
struct tm *stime = localtime(&start_time.tv_sec);
snprintf(sql, sizeof(sql), "INSERT INTO Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs, Scheme ) VALUES ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '', %d, '%s' )",
snprintf(sql, sizeof(sql), "INSERT INTO Events ( MonitorId, StorageId, Name, StartTime, Width, Height, Cause, Notes, StateId, Orientation, Videoed, DefaultVideo, SaveJPEGs, Scheme ) VALUES ( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '%s', %d, '%s' )",
monitor->Id(),
storage->Id(),
start_time.tv_sec,
@@ -98,6 +98,7 @@ Event::Event(
state_id,
monitor->getOrientation(),
videoEvent,
( monitor->GetOptVideoWriter() != 0 ? "video.mp4" : "" ),
monitor->GetOptSaveJPEGs(),
storage->SchemeString().c_str()
);
@@ -108,23 +109,7 @@ Event::Event(
return;
}
id = mysql_insert_id(&dbconn);
//
/* Update event record with DefaultVideo name if possible so image.php can extract frames
if needed, while recording is in progress */
if ( monitor->GetOptVideoWriter() != 0 ) {
video_name[0] = 0;
snprintf(video_name, sizeof(video_name), "%" PRIu64 "-%s", id, "video.mp4");
Debug(1, "Updating inserted event with DefaultVideo=%s",video_name);
snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name,id);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql);
db_mutex.unlock();
return;
}
} else {
Debug (1, "GetOptVideoWriter() returned 0, not updating DefaultVideo");
}
db_mutex.unlock();
if ( untimedEvent ) {
Warning("Event %d has zero time, setting to current", id);
@@ -136,14 +121,13 @@ Event::Event(
max_score = 0;
alarm_frame_written = false;
char id_file[PATH_MAX];
std::string id_file;
char *path_ptr = path;
path_ptr += snprintf(path_ptr, sizeof(path), "%s/%d", storage->Path(), monitor->Id());
path = stringtf("%s/%d", storage->Path(), monitor->Id());
// Try to make the Monitor Dir. Normally this would exist, but in odd cases might not.
if ( mkdir(path, 0755) ) {
if ( mkdir(path.c_str(), 0755) ) {
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
}
if ( storage->Scheme() == Storage::DEEP ) {
@@ -156,97 +140,105 @@ Event::Event(
dt_parts[4] = stime->tm_min;
dt_parts[5] = stime->tm_sec;
char date_path[PATH_MAX] = "";
char time_path[PATH_MAX] = "";
char *time_path_ptr = time_path;
for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) {
path_ptr += snprintf(path_ptr, sizeof(path)-(path_ptr-path), "/%02d", dt_parts[i]);
std::string date_path;
std::string time_path;
errno = 0;
if ( mkdir(path, 0755) ) {
for ( unsigned int i = 0; i < sizeof(dt_parts)/sizeof(*dt_parts); i++ ) {
path += stringtf("/%02d", dt_parts[i]);
if ( mkdir(path.c_str(), 0755) ) {
// FIXME This should not be fatal. Should probably move to a different storage area.
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
}
if ( i == 2 )
strncpy(date_path, path, sizeof(date_path));
else if ( i >= 3 )
time_path_ptr += snprintf(time_path_ptr, sizeof(time_path)-(time_path_ptr-time_path), "%s%02d", i>3?"/":"", dt_parts[i]);
date_path = path;
}
time_path = stringtf("%02d/%02d/%02d", stime->tm_hour, stime->tm_min, stime->tm_sec);
// Create event id symlink
snprintf(id_file, sizeof(id_file), "%s/.%" PRIu64, date_path, id);
if ( symlink(time_path, id_file) < 0 )
Error("Can't symlink %s -> %s: %s", id_file, path, strerror(errno));
id_file = stringtf("%s/.%" PRIu64, date_path.c_str(), id);
if ( symlink(time_path.c_str(), id_file.c_str()) < 0 )
Error("Can't symlink %s -> %s: %s", id_file.c_str(), time_path.c_str(), strerror(errno));
} else if ( storage->Scheme() == Storage::MEDIUM ) {
path_ptr += snprintf(
path_ptr, sizeof(path), "/%04d-%02d-%02d",
path += stringtf("/%04d-%02d-%02d",
stime->tm_year+1900, stime->tm_mon+1, stime->tm_mday
);
if ( mkdir(path, 0755) ) {
if ( mkdir(path.c_str(), 0755) ) {
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
}
path_ptr += snprintf(path_ptr, sizeof(path), "/%" PRIu64, id);
if ( mkdir(path, 0755) ) {
path += stringtf("/%" PRIu64, id);
if ( mkdir(path.c_str(), 0755) ) {
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
}
} else {
path_ptr += snprintf(path_ptr, sizeof(path), "/%" PRIu64, id);
if ( mkdir(path, 0755) ) {
path += stringtf("/%" PRIu64, id);
if ( mkdir(path.c_str(), 0755) ) {
if ( errno != EEXIST )
Error("Can't mkdir %s: %s", path, strerror(errno));
Error("Can't mkdir %s: %s", path.c_str(), strerror(errno));
}
// Create empty id tag file
snprintf(id_file, sizeof(id_file), "%s/.%" PRIu64, path, id);
if ( FILE *id_fp = fopen(id_file, "w") )
id_file = stringtf("%s/.%" PRIu64, path.c_str(), id);
if ( FILE *id_fp = fopen(id_file.c_str(), "w") ) {
fclose(id_fp);
else
Error("Can't fopen %s: %s", id_file, strerror(errno));
} else {
Error("Can't fopen %s: %s", id_file.c_str(), strerror(errno));
}
} // deep storage or not
last_db_frame = 0;
video_name[0] = 0;
video_name = "";
snprintf(snapshot_file, sizeof(snapshot_file), "%s/snapshot.jpg", path);
snprintf(alarm_file, sizeof(alarm_file), "%s/alarm.jpg", path);
snapshot_file = path + "/snapshot.jpg";
alarm_file = path + "/alarm.jpg";
/* Save as video */
if ( monitor->GetOptVideoWriter() != 0 ) {
snprintf(video_name, sizeof(video_name), "%" PRIu64 "-%s", id, "video.mp4");
snprintf(video_file, sizeof(video_file), staticConfig.video_file_format, path, video_name);
Debug(1,"Writing video file to %s", video_file );
video_name = stringtf("%" PRIu64 "-%s", id, "video.mp4");
snprintf(sql, sizeof(sql), "UPDATE Events SET DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id);
if ( mysql_query(&dbconn, sql) ) {
Error("Can't update event: %s. sql was (%s)", mysql_error(&dbconn), sql);
db_mutex.unlock();
return;
}
video_file = path + "/" + video_name;
Debug(1, "Writing video file to %s", video_file.c_str());
/* X264 MP4 video writer */
if ( monitor->GetOptVideoWriter() == Monitor::X264ENCODE ) {
#if ZM_HAVE_VIDEOWRITER_X264MP4
videowriter = new X264MP4Writer(video_file, monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder(), monitor->GetOptEncoderParams());
videowriter = new X264MP4Writer(video_file.c_str(),
monitor->Width(),
monitor->Height(),
monitor->Colours(),
monitor->SubpixelOrder(),
monitor->GetOptEncoderParamsVec());
#else
Error("ZoneMinder was not compiled with the X264 MP4 video writer, check dependencies (x264 and mp4v2)");
#endif
}
if ( videowriter != NULL ) {
/* Open the video stream */
int nRet = videowriter->Open();
if ( nRet != 0 ) {
Error("Failed opening video stream");
delete videowriter;
videowriter = NULL;
}
}
if ( videowriter != NULL ) {
/* Open the video stream */
int nRet = videowriter->Open();
if ( nRet != 0 ) {
Error("Failed opening video stream");
delete videowriter;
videowriter = NULL;
}
}
}
} else {
/* No video object */
videowriter = NULL;
}
} // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent )
Event::~Event() {
// We close the videowriter first, because if we finish the event, we might try to view the file, but we aren't done writing it yet.
/* Close the video file */
@@ -276,13 +268,12 @@ Event::~Event() {
// update frame deltas to refer to video start time which may be a few frames before event start
struct timeval video_offset = {0};
struct timeval video_start_time = monitor->GetVideoWriterStartTime();
if (video_start_time.tv_sec > 0) {
if ( video_start_time.tv_sec > 0 ) {
timersub(&video_start_time, &start_time, &video_offset);
Debug(1, "Updating frames delta by %d sec %d usec",
video_offset.tv_sec, video_offset.tv_usec);
UpdateFramesDelta(video_offset.tv_sec + video_offset.tv_usec*1e-6);
}
else {
} else {
Debug(3, "Video start_time %d sec %d usec not valid -- frame deltas not updated",
video_start_time.tv_sec, video_start_time.tv_usec);
}
@@ -290,12 +281,12 @@ Event::~Event() {
// Should not be static because we might be multi-threaded
char sql[ZM_SQL_LGE_BUFSIZ];
snprintf(sql, sizeof(sql),
"UPDATE Events SET Name='%s %" PRIu64 "', EndTime = from_unixtime( %ld ), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo = '%s' WHERE Id = %" PRIu64,
"UPDATE Events SET Name='%s%" PRIu64 "', EndTime = from_unixtime(%ld), Length = %s%ld.%02ld, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64,
monitor->EventPrefix(), id, end_time.tv_sec,
delta_time.positive?"":"-", delta_time.sec, delta_time.fsec,
frames, alarm_frames,
tot_score, (int)(alarm_frames?(tot_score/alarm_frames):0), max_score,
video_name, id );
id);
db_mutex.lock();
while ( mysql_query(&dbconn, sql) && !zm_terminate ) {
db_mutex.unlock();
@@ -304,7 +295,6 @@ Event::~Event() {
db_mutex.lock();
}
db_mutex.unlock();
} // Event::~Event()
void Event::createNotes(std::string &notes) {
@@ -329,7 +319,7 @@ bool Event::WriteFrameImage(
int thisquality =
(alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality)) ?
config.jpeg_alarm_file_quality : 0 ; // quality to use, zero is default
config.jpeg_alarm_file_quality : 0; // quality to use, zero is default
bool rc;
@@ -507,17 +497,16 @@ void Event::AddFramesInternal(int n_frames, int start_frame, Image **images, str
frames++;
if ( monitor->GetOptSaveJPEGs() & 1 ) {
static char event_file[PATH_MAX];
snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames);
std::string event_file = stringtf(staticConfig.capture_file_format, path.c_str(), frames);
Debug(1, "Writing pre-capture frame %d", frames);
WriteFrameImage(images[i], *(timestamps[i]), event_file);
WriteFrameImage(images[i], *(timestamps[i]), event_file.c_str());
}
//If this is the first frame, we should add a thumbnail to the event directory
// ICON: We are working through the pre-event frames so this snapshot won't
// neccessarily be of the motion. But some events are less than 10 frames,
// so I am changing this to 1, but we should overwrite it later with a better snapshot.
if ( frames == 1 ) {
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file);
WriteFrameImage(images[i], *(timestamps[i]), snapshot_file.c_str());
}
if ( videowriter != NULL ) {
@@ -589,14 +578,14 @@ void Event::WriteDbFrames() {
void Event::UpdateFramesDelta(double offset) {
char sql[ZM_SQL_MED_BUFSIZ];
if (offset == 0.0) return;
if ( offset == 0.0 ) return;
// the table is set to auto update timestamp so we force it to keep current value
snprintf(sql, sizeof(sql),
"UPDATE Frames SET timestamp = timestamp, Delta = Delta - (%.4f) WHERE EventId = %" PRIu64,
offset, id);
db_mutex.lock();
if (mysql_query(&dbconn, sql)) {
if ( mysql_query(&dbconn, sql) ) {
db_mutex.unlock();
Error("Can't update frames: %s, sql was %s", mysql_error(&dbconn), sql);
return;
@@ -605,7 +594,6 @@ void Event::UpdateFramesDelta(double offset) {
Info("Updating frames delta by %0.2f sec to match video file", offset);
}
void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) {
if ( !timestamp.tv_sec ) {
Debug(1, "Not adding new frame, zero timestamp");
@@ -620,10 +608,9 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
score = 0;
if ( monitor->GetOptSaveJPEGs() & 1 ) {
static char event_file[PATH_MAX];
snprintf(event_file, sizeof(event_file), staticConfig.capture_file_format, path, frames);
Debug(1, "Writing capture frame %d to %s", frames, event_file);
if ( !WriteFrameImage(image, timestamp, event_file) ) {
std::string event_file = stringtf(staticConfig.capture_file_format, path.c_str(), frames);
Debug(1, "Writing capture frame %d to %s", frames, event_file.c_str());
if ( !WriteFrameImage(image, timestamp, event_file.c_str()) ) {
Error("Failed to write frame image");
}
}
@@ -631,7 +618,7 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
// If this is the first frame, we should add a thumbnail to the event directory
if ( (frames == 1) || (score > (int)max_score) ) {
write_to_db = true; // web ui might show this as thumbnail, so db needs to know about it.
WriteFrameImage(image, timestamp, snapshot_file);
WriteFrameImage(image, timestamp, snapshot_file.c_str());
}
// We are writing an Alarm frame
@@ -640,7 +627,7 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
if ( !alarm_frame_written ) {
write_to_db = true; // OD processing will need it, so the db needs to know about it
alarm_frame_written = true;
WriteFrameImage(image, timestamp, alarm_file);
WriteFrameImage(image, timestamp, alarm_file.c_str());
}
alarm_frames++;
@@ -650,10 +637,9 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
if ( alarm_image ) {
if ( monitor->GetOptSaveJPEGs() & 2 ) {
static char event_file[PATH_MAX];
snprintf(event_file, sizeof(event_file), staticConfig.analyse_file_format, path, frames);
std::string event_file = stringtf(staticConfig.analyse_file_format, path.c_str(), frames);
Debug(1, "Writing analysis frame %d", frames);
if ( ! WriteFrameImage(alarm_image, timestamp, event_file, true) ) {
if ( ! WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true) ) {
Error("Failed to write analysis frame image");
}
}
@@ -673,10 +659,12 @@ void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *a
// The idea is to write out 1/sec
frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score));
if ( write_to_db || (frame_data.size() > (int)monitor->get_fps()) ) {
Debug(1, "Adding %d frames to DB", frame_data.size());
if ( write_to_db || ( monitor->get_fps() && (frame_data.size() > monitor->get_fps())) ) {
Debug(1, "Adding %d frames to DB because write_to_db:%d or frames > analysis fps %f",
frame_data.size(), write_to_db, monitor->get_fps());
WriteDbFrames();
last_db_frame = frames;
Debug(1, "Adding %d frames to DB, done", frame_data.size());
}
// We are writing a Bulk frame
+32 -23
View File
@@ -89,15 +89,17 @@ class Event {
bool alarm_frame_written;
unsigned int tot_score;
unsigned int max_score;
char path[PATH_MAX];
char snapshot_file[PATH_MAX];
char alarm_file[PATH_MAX];
std::string path;
std::string snapshot_file;
std::string alarm_file;
VideoWriter* videowriter;
FILE* timecodes_fd;
char video_name[PATH_MAX];
char video_file[PATH_MAX];
char timecodes_name[PATH_MAX];
char timecodes_file[PATH_MAX];
std::string video_name;
std::string video_file;
std::string timecodes_name;
std::string timecodes_file;
int last_db_frame;
Storage::Schemes scheme;
@@ -137,15 +139,15 @@ class Event {
public:
static const char *getSubPath( struct tm *time ) {
static char subpath[PATH_MAX] = "";
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
return( subpath );
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec);
return subpath;
}
static const char *getSubPath( time_t *time ) {
return Event::getSubPath( localtime( time ) );
}
char* getEventFile(void) {
return video_file;
const char* getEventFile(void) {
return video_file.c_str();
}
public:
@@ -153,28 +155,35 @@ class Event {
return pre_alarm_count;
}
static void EmptyPreAlarmFrames() {
if ( pre_alarm_count > 0 ) {
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ ) {
delete pre_alarm_data[i].image;
delete pre_alarm_data[i].alarm_frame;
}
memset( pre_alarm_data, 0, sizeof(pre_alarm_data) );
}
while ( pre_alarm_count > 0 ) {
int i = pre_alarm_count - 1;
delete pre_alarm_data[i].image;
pre_alarm_data[i].image = NULL;
if ( pre_alarm_data[i].alarm_frame ) {
delete pre_alarm_data[i].alarm_frame;
pre_alarm_data[i].alarm_frame = NULL;
}
pre_alarm_count--;
}
pre_alarm_count = 0;
}
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ) {
pre_alarm_data[pre_alarm_count].image = new Image( *image );
static void AddPreAlarmFrame(Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL) {
pre_alarm_data[pre_alarm_count].image = new Image(*image);
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
pre_alarm_data[pre_alarm_count].score = score;
if ( alarm_frame ) {
pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame );
pre_alarm_data[pre_alarm_count].alarm_frame = new Image(*alarm_frame);
}
pre_alarm_count++;
}
void SavePreAlarmFrames() {
for ( int i = 0; i < pre_alarm_count; i++ ) {
AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame );
}
AddFrame(
pre_alarm_data[i].image,
pre_alarm_data[i].timestamp,
pre_alarm_data[i].score,
pre_alarm_data[i].alarm_frame);
}
EmptyPreAlarmFrames();
}
};
+24 -15
View File
@@ -187,13 +187,13 @@ bool EventStream::loadEventData(uint64_t event_id) {
if ( storage_path[0] == '/' )
snprintf(event_data->path, sizeof(event_data->path),
"%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d",
"%s/%d/%02d/%02d/%02d/%02d/%02d/%02d",
storage_path, event_data->monitor_id,
event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday,
event_time->tm_hour, event_time->tm_min, event_time->tm_sec);
else
snprintf(event_data->path, sizeof(event_data->path),
"%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d",
"%s/%s/%d/%02d/%02d/%02d/%02d/%02d/%02d",
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday,
event_time->tm_hour, event_time->tm_min, event_time->tm_sec);
@@ -201,23 +201,23 @@ bool EventStream::loadEventData(uint64_t event_id) {
struct tm *event_time = localtime(&event_data->start_time);
if ( storage_path[0] == '/' )
snprintf(event_data->path, sizeof(event_data->path),
"%s/%ld/%04d-%02d-%02d/%" PRIu64,
"%s/%d/%04d-%02d-%02d/%" PRIu64,
storage_path, event_data->monitor_id,
event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday,
event_data->event_id);
else
snprintf(event_data->path, sizeof(event_data->path),
"%s/%s/%ld/%04d-%02d-%02d/%" PRIu64,
"%s/%s/%d/%04d-%02d-%02d/%" PRIu64,
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday,
event_data->event_id);
} else {
if ( storage_path[0] == '/' )
snprintf(event_data->path, sizeof(event_data->path), "%s/%ld/%" PRIu64,
snprintf(event_data->path, sizeof(event_data->path), "%s/%d/%" PRIu64,
storage_path, event_data->monitor_id, event_data->event_id);
else
snprintf(event_data->path, sizeof(event_data->path), "%s/%s/%ld/%" PRIu64,
snprintf(event_data->path, sizeof(event_data->path), "%s/%s/%d/%" PRIu64,
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
event_data->event_id);
}
@@ -566,11 +566,11 @@ bool EventStream::checkEventLoaded() {
if ( curr_frame_id <= 0 ) {
snprintf(sql, sizeof(sql),
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %ld AND `Id` < %" PRIu64 " ORDER BY `Id` DESC LIMIT 1",
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %d AND `Id` < %" PRIu64 " ORDER BY `Id` DESC LIMIT 1",
event_data->monitor_id, event_data->event_id);
} else if ( (unsigned int)curr_frame_id > event_data->frame_count ) {
snprintf(sql, sizeof(sql),
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %ld AND `Id` > %" PRIu64 " ORDER BY `Id` ASC LIMIT 1",
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %d AND `Id` > %" PRIu64 " ORDER BY `Id` ASC LIMIT 1",
event_data->monitor_id, event_data->event_id);
} else {
// No event change required
@@ -810,29 +810,38 @@ bool EventStream::sendFrame(int delta_us) {
if ( send_raw ) {
#if HAVE_SENDFILE
fprintf(stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size);
if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size) ) {
fclose(fdj); /* Close the file handle */
Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
return false;
}
if ( zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size ) {
/* sendfile() failed, use standard way instead */
img_buffer_size = fread(img_buffer, 1, sizeof(temp_img_buffer), fdj);
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
fclose(fdj); /* Close the file handle */
Error("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
return false;
}
}
#else
fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size);
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
if (
(0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size) )
||
( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 )
) {
fclose(fdj); /* Close the file handle */
Error("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
Info("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
return false;
}
#endif
fclose(fdj); /* Close the file handle */
} else {
Debug(3, "Content length: %d", img_buffer_size);
fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size);
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
if (
(0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size) )
||
( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) ) {
Error("Unable to send stream frame: %s", strerror(errno));
return false;
}
+1 -1
View File
@@ -52,7 +52,7 @@ class EventStream : public StreamBase {
struct EventData {
uint64_t event_id;
unsigned long monitor_id;
unsigned int monitor_id;
unsigned long storage_id;
unsigned long frame_count;
time_t start_time;
+83 -47
View File
@@ -63,31 +63,31 @@ static enum AVPixelFormat get_hw_format(
}
#if !LIBAVUTIL_VERSION_CHECK(56, 22, 0, 14, 0)
static enum AVPixelFormat find_fmt_by_hw_type(const enum AVHWDeviceType type) {
enum AVPixelFormat fmt;
switch (type) {
enum AVPixelFormat fmt;
switch (type) {
case AV_HWDEVICE_TYPE_VAAPI:
fmt = AV_PIX_FMT_VAAPI;
break;
fmt = AV_PIX_FMT_VAAPI;
break;
case AV_HWDEVICE_TYPE_DXVA2:
fmt = AV_PIX_FMT_DXVA2_VLD;
break;
fmt = AV_PIX_FMT_DXVA2_VLD;
break;
case AV_HWDEVICE_TYPE_D3D11VA:
fmt = AV_PIX_FMT_D3D11;
break;
fmt = AV_PIX_FMT_D3D11;
break;
case AV_HWDEVICE_TYPE_VDPAU:
fmt = AV_PIX_FMT_VDPAU;
break;
fmt = AV_PIX_FMT_VDPAU;
break;
case AV_HWDEVICE_TYPE_CUDA:
fmt = AV_PIX_FMT_CUDA;
break;
fmt = AV_PIX_FMT_CUDA;
break;
case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
fmt = AV_PIX_FMT_VIDEOTOOLBOX;
break;
fmt = AV_PIX_FMT_VIDEOTOOLBOX;
break;
default:
fmt = AV_PIX_FMT_NONE;
break;
}
return fmt;
fmt = AV_PIX_FMT_NONE;
break;
}
return fmt;
}
#endif
#endif
@@ -174,6 +174,26 @@ FfmpegCamera::FfmpegCamera(
} else {
Panic("Unexpected colours: %d", colours);
}
frame_buffer = NULL;
// sws_scale needs 32bit aligned width and an extra 16 bytes padding, so recalculate imagesize, which was width*height*bytes_per_pixel
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
alignment = 32;
imagesize = av_image_get_buffer_size(imagePixFormat, width, height, alignment);
// av_image_get_linesize isn't aligned, so we have to do that.
linesize = FFALIGN(av_image_get_linesize(imagePixFormat, width, 0), alignment);
#else
alignment = 1;
linesize = FFALIGN(av_image_get_linesize(imagePixFormat, width, 0), alignment);
imagesize = avpicture_get_size(imagePixFormat, width, height);
#endif
if ( linesize != width * colours ) {
Debug(1, "linesize %d != width %d * colours %d = %d, allocating frame_buffer", linesize, width, colours, width*colours);
frame_buffer = (uint8_t *)av_malloc(imagesize);
}
Debug(1, "ffmpegcamera: width %d height %d linesize %d colours %d image linesize %d imagesize %d",
width, height, linesize, colours, width*colours, imagesize);
} // FfmpegCamera::FfmpegCamera
FfmpegCamera::~FfmpegCamera() {
@@ -194,12 +214,12 @@ void FfmpegCamera::Terminate() {
int FfmpegCamera::PrimeCapture() {
if ( mCanCapture ) {
Info("Priming capture from %s, Closing", mPath.c_str());
Debug(1, "Priming capture from %s, Closing", mPath.c_str());
Close();
}
mVideoStreamId = -1;
mAudioStreamId = -1;
Info("Priming capture from %s", mPath.c_str());
Debug(1, "Priming capture from %s", mPath.c_str());
return OpenFfmpeg();
}
@@ -608,16 +628,6 @@ int FfmpegCamera::OpenFfmpeg() {
mFrame->width = width;
mFrame->height = height;
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
int pSize = av_image_get_buffer_size(imagePixFormat, width, height, 1);
#else
int pSize = avpicture_get_size(imagePixFormat, width, height);
#endif
if ( (unsigned int)pSize != imagesize ) {
Error("Image size mismatch. Required: %d Available: %d", pSize, imagesize);
return -1;
}
#if HAVE_LIBSWSCALE
if ( !sws_isSupportedInput(mVideoCodecContext->pix_fmt) ) {
@@ -976,10 +986,12 @@ int FfmpegCamera::CaptureAndRecord(
return -1;
}
#if HAVE_LIBAVUTIL_HWCONTEXT_H
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
if ( (ret == AVERROR_INVALIDDATA ) && (hw_pix_fmt != AV_PIX_FMT_NONE) ) {
use_hwaccel = false;
return -1;
}
#endif
#endif
}
zm_av_packet_unref(&packet);
@@ -1074,29 +1086,41 @@ int FfmpegCamera::transfer_to_image(
AVFrame *output_frame,
AVFrame *input_frame
) {
uint8_t* directbuffer;
uint8_t* image_buffer; // pointer to buffer in image
uint8_t* buffer; // pointer to either image_buffer or frame_buffer
/* Request a writeable buffer of the target image */
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
if ( directbuffer == NULL ) {
image_buffer = image.WriteBuffer(width, height, colours, subpixelorder);
if ( image_buffer == NULL ) {
Error("Failed requesting writeable buffer for the captured image.");
return -1;
}
// if image_buffer was allocated then use it.
buffer = frame_buffer ? frame_buffer : image_buffer;
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
// From what I've read, we should align the linesizes to 32bit so that ffmpeg can use SIMD instructions too.
int size = av_image_fill_arrays(
output_frame->data, output_frame->linesize,
directbuffer, imagePixFormat, width, height,
(AV_PIX_FMT_RGBA == imagePixFormat ? 32 : 1)
buffer, imagePixFormat, width, height,
alignment
);
if ( size < 0 ) {
Error("Problem setting up data pointers into image %s",
av_make_error_string(size).c_str());
} else {
Debug(4, "av_image_fill_array %dx%d alignment: %d = %d, buffer size is %d",
width, height, alignment, size, image.Size());
}
Debug(1, "ffmpegcamera: width %d height %d linesize %d colours %d imagesize %d", width, height, linesize, colours, imagesize);
if ( linesize != (unsigned int)output_frame->linesize[0] ) {
Error("Bad linesize expected %d got %d", linesize, output_frame->linesize[0]);
}
#else
avpicture_fill((AVPicture *)output_frame, directbuffer,
avpicture_fill((AVPicture *)output_frame, buffer,
imagePixFormat, width, height);
#endif
#if HAVE_LIBSWSCALE
if ( !mConvertContext ) {
mConvertContext = sws_getContext(
@@ -1141,21 +1165,33 @@ int FfmpegCamera::transfer_to_image(
);
return -1;
}
Debug(4, "Able to convert format %u %s linesize %d,%d height %d to format %u %s linesize %d,%d at frame %d codec %u %s %dx%d ",
input_frame->format, av_get_pix_fmt_name((AVPixelFormat)input_frame->format),
input_frame->linesize[0], input_frame->linesize[1], mVideoCodecContext->height,
imagePixFormat,
av_get_pix_fmt_name(imagePixFormat),
output_frame->linesize[0], output_frame->linesize[1],
frameCount,
mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt),
output_frame->width,
output_frame->height
);
Debug(4, "Able to convert format %u %s linesize %d,%d height %d to format %u %s linesize %d,%d at frame %d codec %u %s %dx%d ",
input_frame->format, av_get_pix_fmt_name((AVPixelFormat)input_frame->format),
input_frame->linesize[0], input_frame->linesize[1], mVideoCodecContext->height,
imagePixFormat,
av_get_pix_fmt_name(imagePixFormat),
output_frame->linesize[0], output_frame->linesize[1],
frameCount,
mVideoCodecContext->pix_fmt, av_get_pix_fmt_name(mVideoCodecContext->pix_fmt),
output_frame->width,
output_frame->height
);
#else // HAVE_LIBSWSCALE
Fatal("You must compile ffmpeg with the --enable-swscale "
"option to use ffmpeg cameras");
#endif // HAVE_LIBSWSCALE
if ( buffer != image_buffer ) {
Debug(1, "Copying image-buffer to buffer");
// Have to copy contents of image_buffer to directbuffer.
// Since linesize isn't the same have to copy line by line
uint8_t *image_buffer_ptr = image_buffer;
int row_size = output_frame->width * colours;
for ( int i = 0; i < output_frame->height; i++ ) {
memcpy(image_buffer_ptr, buffer, row_size);
image_buffer_ptr += row_size;
buffer += output_frame->linesize[0];
}
}
return 0;
} // end int FfmpegCamera::transfer_to_image
+3
View File
@@ -45,6 +45,8 @@ class FfmpegCamera : public Camera {
std::string hwaccel_device;
int frameCount;
int alignment; /* ffmpeg wants line sizes to be 32bit aligned. Especially 4.3+ */
#if HAVE_LIBAVFORMAT
AVFormatContext *mFormatContext;
@@ -82,6 +84,7 @@ class FfmpegCamera : public Camera {
#if HAVE_LIBSWSCALE
struct SwsContext *mConvertContext;
#endif
uint8_t *frame_buffer;
int error_count;
+269 -266
View File
File diff suppressed because it is too large. Load diff
+28 -10
View File
@@ -152,9 +152,11 @@ protected:
static struct zm_error_mgr jpg_err;
unsigned int width;
unsigned int linesize;
unsigned int height;
unsigned int pixels;
unsigned int colours;
unsigned int padding;
unsigned int size;
unsigned int subpixelorder;
unsigned long allocation;
@@ -165,15 +167,18 @@ protected:
public:
Image();
explicit Image( const char *filename );
Image( int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0);
explicit Image(const char *filename);
Image(int p_width, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0, unsigned int padding=0);
Image(int p_width, int p_linesize, int p_height, int p_colours, int p_subpixelorder, uint8_t *p_buffer=0, unsigned int padding=0);
explicit Image( const Image &p_image );
explicit Image( const AVFrame *frame );
~Image();
static void Initialise();
static void Deinitialise();
inline unsigned int Width() const { return width; }
inline unsigned int LineSize() const { return linesize; }
inline unsigned int Height() const { return height; }
inline unsigned int Pixels() const { return pixels; }
inline unsigned int Colours() const { return colours; }
@@ -182,7 +187,7 @@ public:
/* Internal buffer should not be modified from functions outside of this class */
inline const uint8_t* Buffer() const { return buffer; }
inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return &buffer[colours*((y*width)+x)]; }
inline const uint8_t* Buffer( unsigned int x, unsigned int y= 0 ) const { return &buffer[(y*linesize)+x]; }
/* Request writeable buffer */
uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder);
@@ -193,21 +198,34 @@ public:
if ( !holdbuffer )
DumpImgBuffer();
width = height = colours = size = pixels = subpixelorder = 0;
width = linesize = height = colours = size = pixels = subpixelorder = 0;
}
void Assign( unsigned int p_width, unsigned int p_height, unsigned int p_colours, unsigned int p_subpixelorder, const uint8_t* new_buffer, const size_t buffer_size);
void Assign( const Image &image );
void AssignDirect( const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder, uint8_t *new_buffer, const size_t buffer_size, const int p_buffertype);
void Assign(
unsigned int p_width,
unsigned int p_height,
unsigned int p_colours,
unsigned int p_subpixelorder,
const uint8_t* new_buffer,
const size_t buffer_size);
void Assign(const Image &image);
void AssignDirect(
const unsigned int p_width,
const unsigned int p_height,
const unsigned int p_colours,
const unsigned int p_subpixelorder,
uint8_t *new_buffer,
const size_t buffer_size,
const int p_buffertype);
inline void CopyBuffer( const Image &image ) {
inline void CopyBuffer(const Image &image) {
Assign(image);
}
inline Image &operator=( const Image &image ) {
inline Image &operator=(const Image &image) {
Assign(image);
return *this;
}
inline Image &operator=( const unsigned char *new_buffer ) {
inline Image &operator=(const unsigned char *new_buffer) {
(*fptr_imgbufcpy)(buffer, new_buffer, size);
return *this;
}
+1 -2
View File
@@ -198,8 +198,7 @@ void LibvlcCamera::Terminate() {
}
int LibvlcCamera::PrimeCapture() {
Info("Priming capture from %s", mPath.c_str());
Info("Libvlc Version %s", (*libvlc_get_version_f)());
Debug(1, "Priming capture from %s, libvlc version %s", mPath.c_str(), (*libvlc_get_version_f)());
StringVector opVect = split(Options(), ",");
+1 -1
View File
@@ -148,7 +148,7 @@ void VncCamera::Terminate() {
}
int VncCamera::PrimeCapture() {
Info("Priming capture from %s", mHost.c_str());
Debug(1, "Priming capture from %s", mHost.c_str());
return 0;
}
+216 -175
View File
@@ -504,7 +504,7 @@ LocalCamera::LocalCamera(
subpixelorder = ZM_SUBPIX_ORDER_NONE;
} else if ( palette == V4L2_PIX_FMT_YUYV && colours == ZM_COLOUR_GRAY8 ) {
/* Fast YUYV->Grayscale conversion by extracting the Y channel */
if ( config.cpu_extensions && sseversion >= 35 ) {
if ( config.cpu_extensions && sse_version >= 35 ) {
conversion_fptr = &ssse3_convert_yuyv_gray8;
Debug(2,"Using SSSE3 YUYV->grayscale fast conversion");
} else {
@@ -616,7 +616,7 @@ LocalCamera::LocalCamera(
}
} else if ( (palette == VIDEO_PALETTE_YUYV || palette == VIDEO_PALETTE_YUV422) && colours == ZM_COLOUR_GRAY8 ) {
/* Fast YUYV->Grayscale conversion by extracting the Y channel */
if ( config.cpu_extensions && sseversion >= 35 ) {
if ( config.cpu_extensions && sse_version >= 35 ) {
conversion_fptr = &ssse3_convert_yuyv_gray8;
Debug(2,"Using SSSE3 YUYV->grayscale fast conversion");
} else {
@@ -1274,56 +1274,62 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) {
#define capString(test,prefix,yesString,noString,capability) \
(test) ? (prefix yesString " " capability "\n") : (prefix noString " " capability "\n")
bool LocalCamera::GetCurrentSettings( const char *device, char *output, int version, bool verbose ) {
bool LocalCamera::GetCurrentSettings(const char *device, char *output, int version, bool verbose) {
output[0] = 0;
char *output_ptr = output;
char queryDevice[PATH_MAX] = "";
int devIndex = 0;
do {
if ( device )
if ( device ) {
strncpy(queryDevice, device, sizeof(queryDevice)-1);
else
} else {
sprintf(queryDevice, "/dev/video%d", devIndex);
}
if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) {
if ( device ) {
Error("Failed to open video device %s: %s", queryDevice, strerror(errno));
if ( verbose )
sprintf(output+strlen(output), "Error, failed to open video device %s: %s\n",
output_ptr += sprintf(output_ptr, "Error, failed to open video device %s: %s\n",
queryDevice, strerror(errno));
else
sprintf(output+strlen(output), "error%d\n", errno);
output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false;
} else {
return true;
}
}
if ( verbose )
sprintf(output+strlen(output), "Video Device: %s\n", queryDevice);
else
sprintf(output+strlen(output), "d:%s|", queryDevice);
if ( verbose ) {
output_ptr += sprintf(output_ptr, "Video Device: %s\n", queryDevice);
} else {
output_ptr += sprintf(output_ptr, "d:%s|", queryDevice);
}
#if ZM_HAS_V4L2
if ( version == 2 ) {
struct v4l2_capability vid_cap;
if ( vidioctl(vid_fd, VIDIOC_QUERYCAP, &vid_cap) < 0 ) {
Error("Failed to query video device: %s", strerror(errno));
if ( verbose )
sprintf(output, "Error, failed to query video capabilities %s: %s\n",
if ( verbose ) {
output_ptr += sprintf(output_ptr, "Error, failed to query video capabilities %s: %s\n",
queryDevice, strerror(errno));
else
sprintf(output, "error%d\n", errno);
return false;
} else {
output_ptr += sprintf(output_ptr, "error%d\n", errno);
}
if ( device )
return false;
}
if ( verbose ) {
sprintf(output+strlen(output), "General Capabilities\n");
sprintf(output+strlen(output), " Driver: %s\n", vid_cap.driver);
sprintf(output+strlen(output), " Card: %s\n", vid_cap.card);
sprintf(output+strlen(output), " Bus: %s\n", vid_cap.bus_info);
sprintf(output+strlen(output), " Version: %u.%u.%u\n",
(vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff);
sprintf(output+strlen(output), " Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
output_ptr += sprintf(output_ptr, "General Capabilities\n"
" Driver: %s\n"
" Card: %s\n"
" Bus: %s\n"
" Version: %u.%u.%u\n"
" Type: 0x%x\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
vid_cap.driver, vid_cap.card, vid_cap.bus_info,
(vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff,
vid_cap.capabilities,
capString(vid_cap.capabilities&V4L2_CAP_VIDEO_CAPTURE, " ", "Supports", "Does not support", "video capture (X)"),
capString(vid_cap.capabilities&V4L2_CAP_VIDEO_OUTPUT, " ", "Supports", "Does not support", "video output"),
@@ -1345,17 +1351,16 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
capString(vid_cap.capabilities&V4L2_CAP_STREAMING, " ", "Supports", "Does not support", "streaming i/o (X)")
);
} else {
sprintf(output+strlen(output), "D:%s|", vid_cap.driver);
sprintf(output+strlen(output), "C:%s|", vid_cap.card);
sprintf(output+strlen(output), "B:%s|", vid_cap.bus_info);
sprintf(output+strlen(output), "V:%u.%u.%u|", (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff);
sprintf(output+strlen(output), "T:0x%x|", vid_cap.capabilities);
output_ptr += sprintf(output_ptr, "D:%s|C:%s|B:%s|V:%u.%u.%u|T:0x%x|"
, vid_cap.driver
, vid_cap.card
, vid_cap.bus_info
, (vid_cap.version>>16)&0xff, (vid_cap.version>>8)&0xff, vid_cap.version&0xff
, vid_cap.capabilities);
}
if ( verbose )
sprintf(output+strlen(output), " Standards:\n");
else
sprintf(output+strlen(output), "S:");
output_ptr += sprintf(output_ptr, verbose ? " Standards:\n" : "S:");
struct v4l2_standard standard;
int standardIndex = 0;
do {
@@ -1370,25 +1375,20 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
} else {
Error("Failed to enumerate standard %d: %d %s", standard.index, errno, strerror(errno));
if ( verbose )
sprintf(output, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno));
output_ptr += sprintf(output_ptr, "Error, failed to enumerate standard %d: %d %s\n", standard.index, errno, strerror(errno));
else
sprintf(output, "error%d\n", errno);
output_ptr += sprintf(output_ptr, "error%d\n", errno);
// Why return? Why not continue trying other things?
return false;
}
}
if ( verbose )
sprintf(output+strlen(output), " %s\n", standard.name);
else
sprintf(output+strlen(output), "%s/", standard.name);
}
while ( standardIndex++ >= 0 );
if ( !verbose && output[strlen(output)-1] == '/')
output[strlen(output)-1] = '|';
output_ptr += sprintf(output_ptr, (verbose ? " %s\n" : "%s/"), standard.name);
} while ( standardIndex++ >= 0 );
if ( verbose )
sprintf(output+strlen(output), " Formats:\n");
else
sprintf(output+strlen(output), "F:");
if ( !verbose && (*(output_ptr-1) == '/') )
*(output_ptr-1) = '|';
output_ptr += sprintf(output_ptr, verbose ? " Formats:\n" : "F:");
struct v4l2_fmtdesc format;
int formatIndex = 0;
do {
@@ -1403,15 +1403,15 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
} else {
Error("Failed to enumerate format %d: %s", format.index, strerror(errno));
if ( verbose )
sprintf(output, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno));
output_ptr += sprintf(output_ptr, "Error, failed to enumerate format %d: %s\n", format.index, strerror(errno));
else
sprintf(output, "error%d\n", errno);
output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false;
}
}
if ( verbose )
sprintf(
output+strlen(output),
output_ptr += sprintf(
output_ptr,
" %s (0x%02hhx%02hhx%02hhx%02hhx)\n",
format.description,
(format.pixelformat>>24)&0xff,
@@ -1419,33 +1419,34 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
(format.pixelformat>>8)&0xff,
format.pixelformat&0xff);
else
sprintf(
output+strlen(output),
output_ptr += sprintf(
output_ptr,
"0x%02hhx%02hhx%02hhx%02hhx/",
(format.pixelformat>>24)&0xff,
(format.pixelformat>>16)&0xff,
(format.pixelformat>>8)&0xff,
(format.pixelformat)&0xff);
} while ( formatIndex++ >= 0 );
if ( !verbose )
output[strlen(output)-1] = '|';
*(output_ptr-1) = '|';
else
sprintf(output+strlen(output), "Crop Capabilities\n");
output_ptr += sprintf(output_ptr, "Crop Capabilities\n");
struct v4l2_cropcap cropcap;
memset(&cropcap, 0, sizeof(cropcap));
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( vidioctl( vid_fd, VIDIOC_CROPCAP, &cropcap ) < 0 ) {
if ( vidioctl(vid_fd, VIDIOC_CROPCAP, &cropcap) < 0 ) {
if ( errno != EINVAL ) {
/* Failed querying crop capability, write error to the log and continue as if crop is not supported */
Error("Failed to query crop capabilities: %s", strerror(errno));
}
if ( verbose ) {
sprintf(output+strlen(output), " Cropping is not supported\n");
output_ptr += sprintf(output_ptr, " Cropping is not supported\n");
} else {
/* Send fake crop bounds to not confuse things parsing this, such as monitor probe */
sprintf(output+strlen(output), "B:%dx%d|",0,0);
output_ptr += sprintf(output_ptr, "B:%dx%d|", 0, 0);
}
} else {
struct v4l2_crop crop;
@@ -1459,19 +1460,23 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
}
if ( verbose ) {
sprintf(output+strlen(output), " Cropping is not supported\n");
output_ptr += sprintf(output_ptr, " Cropping is not supported\n");
} else {
/* Send fake crop bounds to not confuse things parsing this, such as monitor probe */
sprintf(output+strlen(output), "B:%dx%d|",0,0);
output_ptr += sprintf(output_ptr, "B:%dx%d|",0,0);
}
} else {
/* Cropping supported */
if ( verbose ) {
sprintf(output+strlen(output), " Bounds: %d x %d\n", cropcap.bounds.width, cropcap.bounds.height);
sprintf(output+strlen(output), " Default: %d x %d\n", cropcap.defrect.width, cropcap.defrect.height);
sprintf(output+strlen(output), " Current: %d x %d\n", crop.c.width, crop.c.height);
output_ptr += sprintf(output_ptr,
" Bounds: %d x %d\n"
" Default: %d x %d\n"
" Current: %d x %d\n"
, cropcap.bounds.width, cropcap.bounds.height
, cropcap.defrect.width, cropcap.defrect.height
, crop.c.width, crop.c.height);
} else {
sprintf(output+strlen(output), "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height);
output_ptr += sprintf(output_ptr, "B:%dx%d|", cropcap.bounds.width, cropcap.bounds.height);
}
}
} /* Crop code */
@@ -1488,17 +1493,14 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
}
Error("Failed to enumerate input %d: %s", input.index, strerror(errno));
if ( verbose )
sprintf(output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno));
output_ptr += sprintf(output_ptr, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno));
else
sprintf(output, "error%d\n", errno);
output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false;
}
} while ( inputIndex++ >= 0 );
if ( verbose )
sprintf(output+strlen(output), "Inputs: %d\n", inputIndex);
else
sprintf(output+strlen(output), "I:%d|", inputIndex);
output_ptr += sprintf(output_ptr, verbose?"Inputs: %d\n":"I:%d|", inputIndex);
inputIndex = 0;
do {
@@ -1512,65 +1514,84 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
}
Error("Failed to enumerate input %d: %s", input.index, strerror(errno));
if ( verbose )
sprintf(output, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno));
output_ptr += sprintf(output_ptr, "Error, failed to enumerate input %d: %s\n", input.index, strerror(errno));
else
sprintf(output, "error%d\n", errno);
output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false;
}
if ( vidioctl(vid_fd, VIDIOC_S_INPUT, &input.index) < 0 ) {
Error("Failed to set video input %d: %s", input.index, strerror(errno));
if ( verbose )
sprintf(output, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno));
output_ptr += sprintf(output_ptr, "Error, failed to switch to input %d: %s\n", input.index, strerror(errno));
else
sprintf(output, "error%d\n", errno);
output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false;
}
if ( verbose ) {
sprintf( output+strlen(output), " Input %d\n", input.index );
sprintf( output+strlen(output), " Name: %s\n", input.name );
sprintf( output+strlen(output), " Type: %s\n", input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") );
sprintf( output+strlen(output), " Audioset: %08x\n", input.audioset );
sprintf( output+strlen(output), " Standards: 0x%llx\n", input.std );
output_ptr += sprintf( output,
" Input %d\n"
" Name: %s\n"
" Type: %s\n"
" Audioset: %08x\n"
" Standards: 0x%llx\n"
, input.index
, input.name
, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown")
, input.audioset
, input.std );
} else {
sprintf( output+strlen(output), "i%d:%s|", input.index, input.name );
sprintf( output+strlen(output), "i%dT:%s|", input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown") );
sprintf( output+strlen(output), "i%dS:%llx|", input.index, input.std );
output_ptr += sprintf( output_ptr, "i%d:%s|i%dT:%s|i%dS:%llx|"
, input.index, input.name
, input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown")
, input.index, input.std);
}
if ( verbose ) {
sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)" ) );
sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)" ) );
sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "" ) );
sprintf( output+strlen(output), " %s", capString( input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", "" ) );
output_ptr += sprintf( output_ptr, " %s %s %s %s"
, capString(input.status&V4L2_IN_ST_NO_POWER, "Power ", "off", "on", " (X)")
, capString(input.status&V4L2_IN_ST_NO_SIGNAL, "Signal ", "not detected", "detected", " (X)")
, capString(input.status&V4L2_IN_ST_NO_COLOR, "Colour Signal ", "not detected", "detected", "")
, capString(input.status&V4L2_IN_ST_NO_H_LOCK, "Horizontal Lock ", "not detected", "detected", ""));
} else {
sprintf( output+strlen(output), "i%dSP:%d|", input.index, (input.status&V4L2_IN_ST_NO_POWER)?0:1 );
sprintf( output+strlen(output), "i%dSS:%d|", input.index, (input.status&V4L2_IN_ST_NO_SIGNAL)?0:1 );
sprintf( output+strlen(output), "i%dSC:%d|", input.index, (input.status&V4L2_IN_ST_NO_COLOR)?0:1 );
sprintf( output+strlen(output), "i%dHP:%d|", input.index, (input.status&V4L2_IN_ST_NO_H_LOCK)?0:1 );
output_ptr += sprintf( output_ptr, "i%dSP:%d|i%dSS:%d|i%dSC:%d|i%dHP:%d|"
, input.index, (input.status&V4L2_IN_ST_NO_POWER)?0:1
, input.index, (input.status&V4L2_IN_ST_NO_SIGNAL)?0:1
, input.index, (input.status&V4L2_IN_ST_NO_COLOR)?0:1
, input.index, (input.status&V4L2_IN_ST_NO_H_LOCK)?0:1 );
}
} while ( inputIndex++ >= 0 );
if ( !verbose )
output[strlen(output)-1] = '\n';
*(output_ptr-1) = '\n';
}
#endif // ZM_HAS_V4L2
#if ZM_HAS_V4L1
if ( version == 1 ) {
struct video_capability vid_cap;
memset( &vid_cap, 0, sizeof(video_capability) );
if ( ioctl( vid_fd, VIDIOCGCAP, &vid_cap ) < 0 ) {
Error( "Failed to get video capabilities: %s", strerror(errno) );
memset(&vid_cap, 0, sizeof(video_capability));
if ( ioctl(vid_fd, VIDIOCGCAP, &vid_cap) < 0 ) {
Error("Failed to get video capabilities: %s", strerror(errno));
if ( verbose )
sprintf( output, "Error, failed to get video capabilities %s: %s\n", queryDevice, strerror(errno) );
output_ptr += sprintf(output_ptr,
"Error, failed to get video capabilities %s: %s\n",
queryDevice, strerror(errno));
else
sprintf( output, "error%d\n", errno );
return( false );
output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false;
}
if ( verbose ) {
sprintf( output+strlen(output), "Video Capabilities\n" );
sprintf( output+strlen(output), " Name: %s\n", vid_cap.name );
sprintf( output+strlen(output), " Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s", vid_cap.type,
output_ptr += sprintf( output_ptr, "Video Capabilities\n"
" Name: %s\n"
" Type: %d\n%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
" Video Channels: %d\n"
" Audio Channels: %d\n"
" Maximum Width: %d\n"
" Maximum Height: %d\n"
" Minimum Width: %d\n"
" Minimum Height: %d\n",
vid_cap.name,
vid_cap.type,
(vid_cap.type&VID_TYPE_CAPTURE)?" Can capture\n":"",
(vid_cap.type&VID_TYPE_TUNER)?" Can tune\n":"",
(vid_cap.type&VID_TYPE_TELETEXT)?" Does teletext\n":"",
@@ -1584,63 +1605,72 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
(vid_cap.type&VID_TYPE_MPEG_DECODER)?" Can decode MPEG streams\n":"",
(vid_cap.type&VID_TYPE_MPEG_ENCODER)?" Can encode MPEG streams\n":"",
(vid_cap.type&VID_TYPE_MJPEG_DECODER)?" Can decode MJPEG streams\n":"",
(vid_cap.type&VID_TYPE_MJPEG_ENCODER)?" Can encode MJPEG streams\n":""
);
sprintf( output+strlen(output), " Video Channels: %d\n", vid_cap.channels );
sprintf( output+strlen(output), " Audio Channels: %d\n", vid_cap.audios );
sprintf( output+strlen(output), " Maximum Width: %d\n", vid_cap.maxwidth );
sprintf( output+strlen(output), " Maximum Height: %d\n", vid_cap.maxheight );
sprintf( output+strlen(output), " Minimum Width: %d\n", vid_cap.minwidth );
sprintf( output+strlen(output), " Minimum Height: %d\n", vid_cap.minheight );
}
else
{
sprintf( output+strlen(output), "N:%s|", vid_cap.name );
sprintf( output+strlen(output), "T:%d|", vid_cap.type );
sprintf( output+strlen(output), "nC:%d|", vid_cap.channels );
sprintf( output+strlen(output), "nA:%d|", vid_cap.audios );
sprintf( output+strlen(output), "mxW:%d|", vid_cap.maxwidth );
sprintf( output+strlen(output), "mxH:%d|", vid_cap.maxheight );
sprintf( output+strlen(output), "mnW:%d|", vid_cap.minwidth );
sprintf( output+strlen(output), "mnH:%d|", vid_cap.minheight );
(vid_cap.type&VID_TYPE_MJPEG_ENCODER)?" Can encode MJPEG streams\n":"",
vid_cap.channels,
vid_cap.audios,
vid_cap.maxwidth,
vid_cap.maxheight,
vid_cap.minwidth,
vid_cap.minheight );
} else {
output_ptr += sprintf(output_ptr, "N:%s|T:%d|nC:%d|nA:%d|mxW:%d|mxH:%d|mnW:%d|mnH:%d|"
, vid_cap.name
, vid_cap.type
, vid_cap.channels
, vid_cap.audios
, vid_cap.maxwidth
, vid_cap.maxheight
, vid_cap.minwidth
, vid_cap.minheight);
}
struct video_window vid_win;
memset( &vid_win, 0, sizeof(video_window) );
if ( ioctl( vid_fd, VIDIOCGWIN, &vid_win ) < 0 ) {
Error( "Failed to get window attributes: %s", strerror(errno) );
memset(&vid_win, 0, sizeof(video_window));
if ( ioctl(vid_fd, VIDIOCGWIN, &vid_win) < 0 ) {
Error("Failed to get window attributes: %s", strerror(errno));
if ( verbose )
sprintf( output, "Error, failed to get window attributes: %s\n", strerror(errno) );
output_ptr += sprintf(output_ptr, "Error, failed to get window attributes: %s\n", strerror(errno));
else
sprintf( output, "error%d\n", errno );
output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false;
}
if ( verbose ) {
sprintf( output+strlen(output), "Window Attributes\n" );
sprintf( output+strlen(output), " X Offset: %d\n", vid_win.x );
sprintf( output+strlen(output), " Y Offset: %d\n", vid_win.y );
sprintf( output+strlen(output), " Width: %d\n", vid_win.width );
sprintf( output+strlen(output), " Height: %d\n", vid_win.height );
output_ptr += sprintf(output_ptr,
"Window Attributes\n"
" X Offset: %d\n"
" Y Offset: %d\n"
" Width: %d\n"
" Height: %d\n"
, vid_win.x
, vid_win.y
, vid_win.width
, vid_win.height );
} else {
sprintf( output+strlen(output), "X:%d|", vid_win.x );
sprintf( output+strlen(output), "Y:%d|", vid_win.y );
sprintf( output+strlen(output), "W:%d|", vid_win.width );
sprintf( output+strlen(output), "H:%d|", vid_win.height );
output_ptr += sprintf(output_ptr, "X:%d|Y:%d|W:%d|H:%d|",
vid_win.height, vid_win.x, vid_win.y, vid_win.width);
}
struct video_picture vid_pic;
memset( &vid_pic, 0, sizeof(video_picture) );
if ( ioctl( vid_fd, VIDIOCGPICT, &vid_pic ) < 0 ) {
Error( "Failed to get picture attributes: %s", strerror(errno) );
memset(&vid_pic, 0, sizeof(video_picture));
if ( ioctl(vid_fd, VIDIOCGPICT, &vid_pic) < 0 ) {
Error("Failed to get picture attributes: %s", strerror(errno));
if ( verbose )
sprintf( output, "Error, failed to get picture attributes: %s\n", strerror(errno) );
output_ptr += sprintf(output_ptr, "Error, failed to get picture attributes: %s\n", strerror(errno));
else
sprintf( output, "error%d\n", errno );
output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false;
}
if ( verbose ) {
sprintf( output+strlen(output), "Picture Attributes\n" );
sprintf( output+strlen(output), " Palette: %d - %s\n", vid_pic.palette,
output_ptr += sprintf(output_ptr,
"Picture Attributes\n"
" Palette: %d - %s\n"
" Colour Depth: %d\n"
" Brightness: %d\n"
" Hue: %d\n"
" Colour :%d\n"
" Contrast: %d\n"
" Whiteness: %d\n"
, vid_pic.palette,
vid_pic.palette==VIDEO_PALETTE_GREY?"Linear greyscale":(
vid_pic.palette==VIDEO_PALETTE_HI240?"High 240 cube (BT848)":(
vid_pic.palette==VIDEO_PALETTE_RGB565?"565 16 bit RGB":(
@@ -1659,66 +1689,77 @@ bool LocalCamera::GetCurrentSettings( const char *device, char *output, int vers
vid_pic.palette==VIDEO_PALETTE_YUV411P?"YUV 4:1:1 Planar":(
vid_pic.palette==VIDEO_PALETTE_YUV420P?"YUV 4:2:0 Planar":(
vid_pic.palette==VIDEO_PALETTE_YUV410P?"YUV 4:1:0 Planar":"Unknown"
))))))))))))))))));
sprintf( output+strlen(output), " Colour Depth: %d\n", vid_pic.depth );
sprintf( output+strlen(output), " Brightness: %d\n", vid_pic.brightness );
sprintf( output+strlen(output), " Hue: %d\n", vid_pic.hue );
sprintf( output+strlen(output), " Colour :%d\n", vid_pic.colour );
sprintf( output+strlen(output), " Contrast: %d\n", vid_pic.contrast );
sprintf( output+strlen(output), " Whiteness: %d\n", vid_pic.whiteness );
))))))))))))))))),
vid_pic.depth,
vid_pic.brightness,
vid_pic.hue,
vid_pic.colour,
vid_pic.contrast,
vid_pic.whiteness
);
} else {
sprintf( output+strlen(output), "P:%d|", vid_pic.palette );
sprintf( output+strlen(output), "D:%d|", vid_pic.depth );
sprintf( output+strlen(output), "B:%d|", vid_pic.brightness );
sprintf( output+strlen(output), "h:%d|", vid_pic.hue );
sprintf( output+strlen(output), "Cl:%d|", vid_pic.colour );
sprintf( output+strlen(output), "Cn:%d|", vid_pic.contrast );
sprintf( output+strlen(output), "w:%d|", vid_pic.whiteness );
output_ptr += sprintf(output_ptr, "P:%d|D:%d|B:%d|h:%d|Cl:%d|Cn:%d|w:%d|",
vid_pic.palette,
vid_pic.depth,
vid_pic.brightness,
vid_pic.hue,
vid_pic.colour,
vid_pic.contrast,
vid_pic.whiteness
);
}
for ( int chan = 0; chan < vid_cap.channels; chan++ ) {
struct video_channel vid_src;
memset( &vid_src, 0, sizeof(video_channel) );
memset(&vid_src, 0, sizeof(video_channel));
vid_src.channel = chan;
if ( ioctl( vid_fd, VIDIOCGCHAN, &vid_src ) < 0 ) {
Error( "Failed to get channel %d attributes: %s", chan, strerror(errno) );
if ( ioctl(vid_fd, VIDIOCGCHAN, &vid_src) < 0 ) {
Error("Failed to get channel %d attributes: %s", chan, strerror(errno));
if ( verbose )
sprintf( output, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno) );
output_ptr += sprintf(output_ptr, "Error, failed to get channel %d attributes: %s\n", chan, strerror(errno));
else
sprintf( output, "error%d\n", errno );
output_ptr += sprintf(output_ptr, "error%d\n", errno);
return false;
}
if ( verbose ) {
sprintf( output+strlen(output), "Channel %d Attributes\n", chan );
sprintf( output+strlen(output), " Name: %s\n", vid_src.name );
sprintf( output+strlen(output), " Channel: %d\n", vid_src.channel );
sprintf( output+strlen(output), " Flags: %d\n%s%s", vid_src.flags,
(vid_src.flags&VIDEO_VC_TUNER)?" Channel has a tuner\n":"",
(vid_src.flags&VIDEO_VC_AUDIO)?" Channel has audio\n":""
);
sprintf( output+strlen(output), " Type: %d - %s\n", vid_src.type,
output_ptr += sprintf(output_ptr,
"Channel %d Attributes\n"
" Name: %s\n"
" Channel: %d\n"
" Flags: %d\n%s%s"
" Type: %d - %s\n"
" Format: %d - %s\n"
, chan
, vid_src.name
, vid_src.channel
, vid_src.flags
, (vid_src.flags&VIDEO_VC_TUNER)?" Channel has a tuner\n":""
, (vid_src.flags&VIDEO_VC_AUDIO)?" Channel has audio\n":""
, vid_src.type,
vid_src.type==VIDEO_TYPE_TV?"TV":(
vid_src.type==VIDEO_TYPE_CAMERA?"Camera":"Unknown"
));
sprintf( output+strlen(output), " Format: %d - %s\n", vid_src.norm,
)
, vid_src.norm,
vid_src.norm==VIDEO_MODE_PAL?"PAL":(
vid_src.norm==VIDEO_MODE_NTSC?"NTSC":(
vid_src.norm==VIDEO_MODE_SECAM?"SECAM":(
vid_src.norm==VIDEO_MODE_AUTO?"AUTO":"Unknown"
))));
} else {
sprintf( output+strlen(output), "n%d:%s|", chan, vid_src.name );
sprintf( output+strlen(output), "C%d:%d|", chan, vid_src.channel );
sprintf( output+strlen(output), "Fl%d:%x|", chan, vid_src.flags );
sprintf( output+strlen(output), "T%d:%d|", chan, vid_src.type );
sprintf( output+strlen(output), "F%d:%d%s|", chan, vid_src.norm, chan==(vid_cap.channels-1)?"":"," );
output_ptr += sprintf(output_ptr, "n%d:%s|C%d:%d|Fl%d:%x|T%d:%d|F%d:%d%s|"
, chan, vid_src.name
, chan, vid_src.channel
, chan, vid_src.flags
, chan, vid_src.type
, chan, vid_src.norm, chan==(vid_cap.channels-1)?"":","
);
}
}
if ( !verbose )
output[strlen(output)-1] = '\n';
*output_ptr = '\n';
}
#endif // ZM_HAS_V4L1
close( vid_fd );
close(vid_fd);
if ( device )
break;
} while ( ++devIndex < 32 );
+76 -78
View File
@@ -106,17 +106,17 @@ std::vector<std::string> split(const std::string &s, char delim) {
return elems;
}
Monitor::MonitorLink::MonitorLink( int p_id, const char *p_name ) :
id( p_id ),
Monitor::MonitorLink::MonitorLink(unsigned int p_id, const char *p_name) :
id(p_id),
shared_data(NULL),
trigger_data(NULL),
video_store_data(NULL)
{
strncpy( name, p_name, sizeof(name)-1 );
strncpy(name, p_name, sizeof(name)-1);
#if ZM_MEM_MAPPED
map_fd = -1;
snprintf( mem_file, sizeof(mem_file), "%s/zm.mmap.%d", staticConfig.PATH_MAP.c_str(), id );
snprintf(mem_file, sizeof(mem_file), "%s/zm.mmap.%d", staticConfig.PATH_MAP.c_str(), id);
#else // ZM_MEM_MAPPED
shm_id = 0;
#endif // ZM_MEM_MAPPED
@@ -135,61 +135,61 @@ Monitor::MonitorLink::~MonitorLink() {
}
bool Monitor::MonitorLink::connect() {
if ( !last_connect_time || (time( 0 ) - last_connect_time) > 60 ) {
last_connect_time = time( 0 );
if ( !last_connect_time || (time(0) - last_connect_time) > 60 ) {
last_connect_time = time(0);
mem_size = sizeof(SharedData) + sizeof(TriggerData);
Debug( 1, "link.mem.size=%d", mem_size );
Debug(1, "link.mem.size=%d", mem_size);
#if ZM_MEM_MAPPED
map_fd = open( mem_file, O_RDWR, (mode_t)0600 );
map_fd = open(mem_file, O_RDWR, (mode_t)0600);
if ( map_fd < 0 ) {
Debug( 3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno) );
Debug(3, "Can't open linked memory map file %s: %s", mem_file, strerror(errno));
disconnect();
return( false );
return false;
}
while ( map_fd <= 2 ) {
int new_map_fd = dup(map_fd);
Warning( "Got one of the stdio fds for our mmap handle. map_fd was %d, new one is %d", map_fd, new_map_fd );
Warning("Got one of the stdio fds for our mmap handle. map_fd was %d, new one is %d", map_fd, new_map_fd);
close(map_fd);
map_fd = new_map_fd;
}
struct stat map_stat;
if ( fstat( map_fd, &map_stat ) < 0 ) {
Error( "Can't stat linked memory map file %s: %s", mem_file, strerror(errno) );
if ( fstat(map_fd, &map_stat) < 0 ) {
Error("Can't stat linked memory map file %s: %s", mem_file, strerror(errno));
disconnect();
return( false );
return false;
}
if ( map_stat.st_size == 0 ) {
Error( "Linked memory map file %s is empty: %s", mem_file, strerror(errno) );
Error("Linked memory map file %s is empty: %s", mem_file, strerror(errno));
disconnect();
return( false );
return false;
} else if ( map_stat.st_size < mem_size ) {
Error( "Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size );
Error("Got unexpected memory map file size %ld, expected %d", map_stat.st_size, mem_size);
disconnect();
return( false );
return false;
}
mem_ptr = (unsigned char *)mmap( NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0 );
mem_ptr = (unsigned char *)mmap(NULL, mem_size, PROT_READ|PROT_WRITE, MAP_SHARED, map_fd, 0);
if ( mem_ptr == MAP_FAILED ) {
Error( "Can't map file %s (%d bytes) to memory: %s", mem_file, mem_size, strerror(errno) );
Error("Can't map file %s (%d bytes) to memory: %s", mem_file, mem_size, strerror(errno));
disconnect();
return( false );
return false;
}
#else // ZM_MEM_MAPPED
shm_id = shmget( (config.shm_key&0xffff0000)|id, mem_size, 0700 );
if ( shm_id < 0 ) {
Debug( 3, "Can't shmget link memory: %s", strerror(errno) );
Debug(3, "Can't shmget link memory: %s", strerror(errno );
connected = false;
return( false );
return false;
}
mem_ptr = (unsigned char *)shmat( shm_id, 0, 0 );
mem_ptr = (unsigned char *)shmat(shm_id, 0, 0);
if ( mem_ptr < (void *)0 ) {
Debug( 3, "Can't shmat link memory: %s", strerror(errno) );
Debug(3, "Can't shmat link memory: %s", strerror(errno));
connected = false;
return( false );
return false;
}
#endif // ZM_MEM_MAPPED
@@ -197,18 +197,18 @@ bool Monitor::MonitorLink::connect() {
trigger_data = (TriggerData *)((char *)shared_data + sizeof(SharedData));
if ( !shared_data->valid ) {
Debug( 3, "Linked memory not initialised by capture daemon" );
Debug(3, "Linked memory not initialised by capture daemon");
disconnect();
return( false );
return false;
}
last_state = shared_data->state;
last_event = shared_data->last_event;
connected = true;
return( true );
return true;
}
return( false );
return false;
}
bool Monitor::MonitorLink::disconnect() {
@@ -276,7 +276,7 @@ bool Monitor::MonitorLink::hasAlarmed() {
}
Monitor::Monitor(
int p_id,
unsigned int p_id,
const char *p_name,
const unsigned int p_server_id,
const unsigned int p_storage_id,
@@ -383,6 +383,7 @@ Monitor::Monitor(
strncpy(event_prefix, p_event_prefix, sizeof(event_prefix)-1);
strncpy(label_format, p_label_format, sizeof(label_format)-1);
Debug(1, "encoder params %s", encoderparams.c_str());
// Change \n to actual line feeds
char *token_ptr = label_format;
@@ -529,7 +530,8 @@ Monitor::Monitor(
shared_data->last_write_index, shared_data->last_write_time );
sleep(1);
}
ref_image.Assign( width, height, camera->Colours(), camera->SubpixelOrder(),
ref_image.Assign(width, height, camera->Colours(), camera->SubpixelOrder(),
image_buffer[shared_data->last_write_index].image->Buffer(), camera->ImageSize());
adaptive_skip = true;
@@ -538,7 +540,7 @@ Monitor::Monitor(
if ( config.record_diag_images ) {
diag_path_r = stringtf(config.record_diag_images_fifo ? "%s/%d/diagpipe-r.jpg" : "%s/%d/diag-r.jpg", storage->Path(), id);
diag_path_d = stringtf(config.record_diag_images_fifo ? "%s/%d/diagpipe-d.jpg" : "%s/%d/diag-d.jpg", storage->Path(), id);
if (config.record_diag_images_fifo){
if ( config.record_diag_images_fifo ) {
FifoStream::fifo_create_if_missing(diag_path_r.c_str());
FifoStream::fifo_create_if_missing(diag_path_d.c_str());
}
@@ -602,7 +604,7 @@ bool Monitor::connect() {
if ( mem_ptr == MAP_FAILED )
Fatal("Can't map file %s (%d bytes) to memory: %s(%d)", mem_file, mem_size, strerror(errno), errno);
if ( mem_ptr == NULL ) {
Error("mmap gave a null address:");
Error("mmap gave a NULL address:");
} else {
Debug(3, "mmapped to %p", mem_ptr);
}
@@ -631,13 +633,13 @@ bool Monitor::connect() {
image_buffer = new Snapshot[image_buffer_count];
for ( int i = 0; i < image_buffer_count; i++ ) {
image_buffer[i].timestamp = &(shared_timestamps[i]);
image_buffer[i].image = new Image( width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]) );
image_buffer[i].image = new Image(width, height, camera->Colours(), camera->SubpixelOrder(), &(shared_images[i*camera->ImageSize()]));
image_buffer[i].image->HoldBuffer(true); /* Don't release the internal buffer or replace it with another */
}
if ( (deinterlacing & 0xff) == 4) {
if ( (deinterlacing & 0xff) == 4 ) {
/* Four field motion adaptive deinterlacing in use */
/* Allocate a buffer for the next image */
next_buffer.image = new Image( width, height, camera->Colours(), camera->SubpixelOrder());
next_buffer.image = new Image(width, height, camera->Colours(), camera->SubpixelOrder());
next_buffer.timestamp = new struct timeval;
}
if ( purpose == ANALYSIS ) {
@@ -726,9 +728,11 @@ Monitor::~Monitor() {
}
delete[] pre_event_buffer;
}
if ( Event::PreAlarmCount() )
Event::EmptyPreAlarmFrames();
} else if ( purpose == CAPTURE ) {
shared_data->valid = false;
memset( mem_ptr, 0, mem_size );
memset(mem_ptr, 0, mem_size);
}
#if ZM_MEM_MAPPED
@@ -1403,9 +1407,6 @@ bool Monitor::Analyse() {
score += trigger_data->trigger_score;
Debug(1, "Triggered on score += %d => %d", trigger_data->trigger_score, score);
if ( !event ) {
// How could it have a length already?
//if ( cause.length() )
//cause += ", ";
cause += trigger_data->trigger_cause;
}
Event::StringSet noteSet;
@@ -1485,6 +1486,7 @@ bool Monitor::Analyse() {
score += 50;
}
} else {
Debug(1, "Linked monitor %d %d is not connected. Connecting.", i, linked_monitors[i]->Id());
linked_monitors[i]->connect();
}
} // end foreach linked_monit
@@ -1499,7 +1501,7 @@ bool Monitor::Analyse() {
if ( section_length
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
&& ( (event_close_mode != CLOSE_TIME) || ! ( timestamp->tv_sec % section_length ) )
&& ( (function == MOCORD && (event_close_mode != CLOSE_TIME)) || ! ( timestamp->tv_sec % section_length ) )
) {
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %d - %d = %d >= %d",
name, image_count, event->Id(),
@@ -1512,7 +1514,6 @@ bool Monitor::Analyse() {
} // end if event
if ( !event ) {
// Create event
event = new Event(this, *timestamp, "Continuous", noteSetMap, videoRecording);
shared_data->last_event = event->Id();
@@ -1526,7 +1527,6 @@ bool Monitor::Analyse() {
if ( state == IDLE ) {
shared_data->state = state = TAPE;
}
} // end if ! event
} // end if function == RECORD || function == MOCORD)
} // end if !signal_change && signal
@@ -1538,23 +1538,24 @@ bool Monitor::Analyse() {
&& (!event->AlarmFrames())
&& (event_close_mode == CLOSE_ALARM)
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= min_section_length )
&& ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) )
) {
Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins",
Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins",
name, image_count, event->Id());
closeEvent();
} else if ( event ) {
// This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames
Debug(3, "pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d",
Event::PreAlarmCount(), event->Frames(), event->AlarmFrames(),
( timestamp->tv_sec - video_store_data->recording.tv_sec ), min_section_length
);
}
if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) ) {
shared_data->state = state = ALARM;
// lets construct alarm cause. It will contain cause + names of zones alarmed
std::string alarm_cause = "";
for ( int i=0; i < n_zones; i++ ) {
if ( zones[i]->Alarmed() ) {
// This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames
Debug(3, "pre-alarm-count in event %d, event frames %d, alarm frames %d event length %d >=? %d min",
Event::PreAlarmCount(), event->Frames(), event->AlarmFrames(),
( timestamp->tv_sec - video_store_data->recording.tv_sec ), min_section_length
);
}
if ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) ) {
// lets construct alarm cause. It will contain cause + names of zones alarmed
std::string alarm_cause = "";
for ( int i=0; i < n_zones; i++ ) {
if ( zones[i]->Alarmed() ) {
alarm_cause = alarm_cause + "," + std::string(zones[i]->Label());
}
}
@@ -1630,11 +1631,14 @@ bool Monitor::Analyse() {
}
}
event->AddFrames(pre_event_images, images, timestamps);
}
if ( alarm_frame_count ) {
} // end if pre_event_images
if ( ( alarm_frame_count > 1 ) && Event::PreAlarmCount() ) {
Debug(1, "alarm frame count so SavePreAlarmFrames");
event->SavePreAlarmFrames();
}
}
} // end if event
shared_data->state = state = ALARM;
} else if ( state != PREALARM ) {
Info("%s: %03d - Gone into prealarm state", name, image_count);
shared_data->state = state = PREALARM;
@@ -1673,14 +1677,15 @@ bool Monitor::Analyse() {
} else {
shared_data->state = state = TAPE;
}
}
if ( Event::PreAlarmCount() )
Event::EmptyPreAlarmFrames();
} // end if score or not
// Not in PREALARM state anymore, can clear PreAlarmCount
if ( Event::PreAlarmCount() )
Event::EmptyPreAlarmFrames();
}
} // end if score or not
if ( state != IDLE ) {
if ( state == PREALARM || state == ALARM ) {
if ( config.create_analysis_images ) {
if ( savejpegs > 1 ) {
bool got_anal_image = false;
alarm_image.Assign(*snap_image);
for ( int i = 0; i < n_zones; i++ ) {
@@ -1694,17 +1699,11 @@ bool Monitor::Analyse() {
} // end if zone is alarmed
} // end foreach zone
if ( got_anal_image ) {
if ( state == PREALARM )
Event::AddPreAlarmFrame(snap_image, *timestamp, score, &alarm_image);
else
event->AddFrame(snap_image, *timestamp, score, &alarm_image);
if ( state == PREALARM ) {
Event::AddPreAlarmFrame(snap_image, *timestamp, score, (got_anal_image?&alarm_image:NULL));
} else {
if ( state == PREALARM )
Event::AddPreAlarmFrame(snap_image, *timestamp, score);
else
event->AddFrame(snap_image, *timestamp, score);
}
event->AddFrame(snap_image, *timestamp, score, (got_anal_image?&alarm_image:NULL));
}
} else {
// Not doing alarm frame storage
if ( state == PREALARM ) {
@@ -1718,7 +1717,7 @@ bool Monitor::Analyse() {
}
} // end if config.record_event_stats
}
} // end if config.create_analysis_images
} // end if savejpegs > 1
if ( event ) {
if ( noteSetMap.size() > 0 )
@@ -1740,7 +1739,6 @@ bool Monitor::Analyse() {
//set up video store data
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
video_store_data->recording = event->StartTime();
}
} // end if event
@@ -2516,7 +2514,7 @@ int Monitor::Capture() {
if ( capture_image->Size() > camera->ImageSize() ) {
Error("Captured image %d does not match expected size %d check width, height and colour depth",
capture_image->Size(),camera->ImageSize() );
capture_image->Size(), camera->ImageSize() );
return -1;
}
+25 -15
View File
@@ -138,15 +138,23 @@ protected:
time_t startup_time; /* When the zmc process started. zmwatch uses this to see how long the process has been running without getting any images */
uint64_t extrapad1;
};
union { /* +72 */
time_t last_write_time;
union { /* +72 */
time_t zmc_heartbeat_time; /* Constantly updated by zmc. Used to determine if the process is alive or hung or dead */
uint64_t extrapad2;
};
union { /* +80 */
time_t last_read_time;
union { /* +80 */
time_t zma_heartbeat_time; /* Constantly updated by zma. Used to determine if the process is alive or hung or dead */
uint64_t extrapad3;
};
uint8_t control_state[256]; /* +88 */
union { /* +88 */
time_t last_write_time;
uint64_t extrapad4;
};
union { /* +96 */
time_t last_read_time;
uint64_t extrapad5;
};
uint8_t control_state[256]; /* +104 */
char alarm_cause[256];
@@ -210,21 +218,21 @@ protected:
uint64_t last_event;
public:
MonitorLink( int p_id, const char *p_name );
MonitorLink(unsigned int p_id, const char *p_name);
~MonitorLink();
inline int Id() const {
inline unsigned int Id() const {
return id;
}
inline const char *Name() const {
return( name );
return name;
}
inline bool isConnected() const {
return( connected );
return connected && shared_data->valid;
}
inline time_t getLastConnectTime() const {
return( last_connect_time );
return last_connect_time;
}
bool connect();
@@ -358,12 +366,12 @@ protected:
std::vector<Group *> groups;
public:
explicit Monitor( int p_id );
explicit Monitor(unsigned int p_id);
// OurCheckAlarms seems to be unused. Check it on zm_monitor.cpp for more info.
//bool OurCheckAlarms( Zone *zone, const Image *pImage );
Monitor(
int p_id,
unsigned int p_id,
const char *p_name,
unsigned int p_server_id,
unsigned int p_storage_id,
@@ -415,11 +423,12 @@ public:
void AddPrivacyBitmask( Zone *p_zones[] );
bool connect();
inline int ShmValid() const {
return( shared_data->valid );
return shared_data->valid;
}
inline int Id() const {
inline unsigned int Id() const {
return id;
}
inline const char *Name() const {
@@ -464,7 +473,8 @@ public:
int GetOptSaveJPEGs() const { return savejpegs; }
VideoWriter GetOptVideoWriter() const { return videowriter; }
const std::vector<EncoderParameter_t>* GetOptEncoderParams() const { return &encoderparamsvec; }
const std::vector<EncoderParameter_t>* GetOptEncoderParamsVec() const { return &encoderparamsvec; }
const std::string GetOptEncoderParams() const { return encoderparams; }
uint64_t GetVideoWriterEventId() const { return video_store_data->current_event; }
void SetVideoWriterEventId( unsigned long long p_event_id ) { video_store_data->current_event = p_event_id; }
struct timeval GetVideoWriterStartTime() const { return video_store_data->recording; }
+16 -12
View File
@@ -76,9 +76,9 @@ bool MonitorStream::checkSwapPath(const char *path, bool create_path) {
} // end bool MonitorStream::checkSwapPath( const char *path, bool create_path )
void MonitorStream::processCommand(const CmdMsg *msg) {
Debug( 2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0] );
Debug(2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0]);
// Check for incoming command
switch( (MsgCommand)msg->msg_data[0] ) {
switch ( (MsgCommand)msg->msg_data[0] ) {
case CMD_PAUSE :
Debug(1, "Got PAUSE command");
paused = true;
@@ -328,11 +328,12 @@ bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp) {
struct timeval frameStartTime;
gettimeofday(&frameStartTime, NULL);
fputs("--" BOUNDARY "\r\nContent-Type: image/jpeg\r\n", stdout);
fprintf(stdout, "Content-Length: %d\r\n"
"X-Timestamp: %d.%06d\r\n"
"\r\n", img_buffer_size, (int)timestamp->tv_sec, (int)timestamp->tv_usec);
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
if (
(0 > fprintf(stdout, "Content-Length: %d\r\nX-Timestamp: %d.%06d\r\n\r\n",
img_buffer_size, (int)timestamp->tv_sec, (int)timestamp->tv_usec))
||
(fwrite(img_buffer, img_buffer_size, 1, stdout) != 1)
) {
if ( !zm_terminate )
Warning("Unable to send stream frame: %s", strerror(errno));
return false;
@@ -387,7 +388,8 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
struct timeval frameStartTime;
gettimeofday(&frameStartTime, NULL);
switch( type ) {
fputs("--ZoneMinderFrame\r\n", stdout);
switch ( type ) {
case STREAM_JPEG :
send_image->EncodeJpeg(img_buffer, &img_buffer_size);
fputs("Content-Type: image/jpeg\r\n", stdout);
@@ -412,10 +414,12 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
Error("Unexpected frame type %d", type);
return false;
}
fprintf(stdout, "Content-Length: %d\r\n"
"X-Timestamp: %d.%06d\r\n"
"\r\n", img_buffer_size, (int)timestamp->tv_sec, (int)timestamp->tv_usec);
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
if (
( 0 > fprintf(stdout, "Content-Length: %d\r\nX-Timestamp: %d.%06d\r\n\r\n",
img_buffer_size, (int)timestamp->tv_sec, (int)timestamp->tv_usec) )
||
(fwrite(img_buffer, img_buffer_size, 1, stdout) != 1)
) {
if ( !zm_terminate ) {
// If the pipe was closed, we will get signalled SIGPIPE to exit, which will set zm_terminate
Warning("Unable to send stream frame: %s", strerror(errno));
+11 -13
View File
@@ -44,19 +44,17 @@ class MonitorStream : public StreamBase {
time_t ttl;
int playback_buffer;
bool delayed;
int frame_count;
protected:
bool checkSwapPath( const char *path, bool create_path );
bool sendFrame( const char *filepath, struct timeval *timestamp );
bool sendFrame( Image *image, struct timeval *timestamp );
void processCommand( const CmdMsg *msg );
void SingleImage( int scale=100 );
void SingleImageRaw( int scale=100 );
bool checkSwapPath(const char *path, bool create_path);
bool sendFrame(const char *filepath, struct timeval *timestamp);
bool sendFrame(Image *image, struct timeval *timestamp);
void processCommand(const CmdMsg *msg);
void SingleImage(int scale=100);
void SingleImageRaw(int scale=100);
#ifdef HAVE_ZLIB_H
void SingleImageZip( int scale=100 );
void SingleImageZip(int scale=100);
#endif
public:
@@ -64,14 +62,14 @@ class MonitorStream : public StreamBase {
temp_image_buffer(NULL), temp_image_buffer_count(0), temp_read_index(0), temp_write_index(0),
ttl(0), playback_buffer(0), delayed(false), frame_count(0) {
}
void setStreamBuffer( int p_playback_buffer ) {
void setStreamBuffer(int p_playback_buffer) {
playback_buffer = p_playback_buffer;
}
void setStreamTTL( time_t p_ttl ) {
void setStreamTTL(time_t p_ttl) {
ttl = p_ttl;
}
bool setStreamStart( int monitor_id ) {
return loadMonitor( monitor_id );
bool setStreamStart(int monitor_id) {
return loadMonitor(monitor_id);
}
void runStream();
};
+31 -40
View File
@@ -40,16 +40,7 @@ VideoStream::MimeData VideoStream::mime_data[] = {
};
void VideoStream::Initialise( ) {
if ( logDebugging() ) {
av_log_set_level( AV_LOG_DEBUG );
} else {
av_log_set_level( AV_LOG_QUIET );
}
av_register_all( );
#if LIBAVFORMAT_VERSION_CHECK(53, 13, 0, 19, 0)
avformat_network_init();
#endif
FFMPEGInit();
initialised = true;
}
@@ -57,23 +48,23 @@ void VideoStream::SetupFormat( ) {
/* allocate the output media context */
ofc = NULL;
#if (LIBAVFORMAT_VERSION_CHECK(53, 2, 0, 2, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100))
avformat_alloc_output_context2( &ofc, NULL, format, filename );
avformat_alloc_output_context2(&ofc, NULL, format, filename);
#else
AVFormatContext *s= avformat_alloc_context();
if(!s) {
Fatal( "avformat_alloc_context failed %d \"%s\"", (size_t)ofc, av_err2str((size_t)ofc) );
AVFormatContext *s = avformat_alloc_context();
if ( !s ) {
Fatal("avformat_alloc_context failed %d \"%s\"", (size_t)ofc, av_err2str((size_t)ofc));
return;
}
AVOutputFormat *oformat;
if (format) {
if ( format ) {
#if LIBAVFORMAT_VERSION_CHECK(52, 45, 0, 45, 0)
oformat = av_guess_format(format, NULL, NULL);
#else
oformat = guess_format(format, NULL, NULL);
#endif
if (!oformat) {
Fatal( "Requested output format '%s' is not a suitable output format", format );
if ( !oformat ) {
Fatal("Requested output format '%s' is not a suitable output format", format);
}
} else {
#if LIBAVFORMAT_VERSION_CHECK(52, 45, 0, 45, 0)
@@ -81,47 +72,47 @@ void VideoStream::SetupFormat( ) {
#else
oformat = guess_format(NULL, filename, NULL);
#endif
if (!oformat) {
Fatal( "Unable to find a suitable output format for '%s'", format );
if ( !oformat ) {
Fatal("Unable to find a suitable output format for '%s'", format);
}
}
s->oformat = oformat;
if (s->oformat->priv_data_size > 0) {
if ( s->oformat->priv_data_size > 0 ) {
s->priv_data = av_mallocz(s->oformat->priv_data_size);
if ( !(s->priv_data) ) {
Fatal( "Could not allocate private data for output format." );
Fatal("Could not allocate private data for output format.");
}
#if LIBAVFORMAT_VERSION_CHECK(52, 92, 0, 92, 0)
if (s->oformat->priv_class) {
if ( s->oformat->priv_class ) {
*(const AVClass**)s->priv_data = s->oformat->priv_class;
av_opt_set_defaults(s->priv_data);
}
#endif
} else {
Debug(1,"No allocating priv_data");
Debug(1, "No allocating priv_data");
s->priv_data = NULL;
}
if ( filename ) {
snprintf( s->filename, sizeof(s->filename), "%s", filename );
snprintf(s->filename, sizeof(s->filename), "%s", filename);
}
ofc = s;
#endif
if ( !ofc ) {
Fatal( "avformat_alloc_..._context failed: %d", ofc );
Fatal("avformat_alloc_..._context failed: %d", ofc);
}
of = ofc->oformat;
Debug( 1, "Using output format: %s (%s)", of->name, of->long_name );
Debug(1, "Using output format: %s (%s)", of->name, of->long_name);
}
void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int height, int bitrate, double frame_rate ) {
/* ffmpeg format matching */
switch(colours) {
switch ( colours ) {
case ZM_COLOUR_RGB24:
if(subpixelorder == ZM_SUBPIX_ORDER_BGR) {
if ( subpixelorder == ZM_SUBPIX_ORDER_BGR ) {
/* BGR subpixel order */
pf = AV_PIX_FMT_BGR24;
} else {
@@ -130,13 +121,13 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
}
break;
case ZM_COLOUR_RGB32:
if(subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
if ( subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
/* ARGB subpixel order */
pf = AV_PIX_FMT_ARGB;
} else if(subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
} else if ( subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
/* ABGR subpixel order */
pf = AV_PIX_FMT_ABGR;
} else if(subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
} else if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
/* BGRA subpixel order */
pf = AV_PIX_FMT_BGRA;
} else {
@@ -152,7 +143,7 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
break;
}
if ( strcmp( "rtp", of->name ) == 0 ) {
if ( strcmp("rtp", of->name) == 0 ) {
// RTP must have a packet_size.
// Not sure what this value should be really...
ofc->packet_size = width*height;
@@ -169,12 +160,12 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
AVCodec *a = avcodec_find_encoder_by_name(codec_name);
if ( a ) {
codec_id = a->id;
Debug( 1, "Using codec \"%s\"", codec_name );
Debug(1, "Using codec \"%s\"", codec_name);
} else {
#if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100))
Debug( 1, "Could not find codec \"%s\". Using default \"%s\"", codec_name, avcodec_get_name( codec_id ) );
Debug(1, "Could not find codec \"%s\". Using default \"%s\"", codec_name, avcodec_get_name(codec_id));
#else
Debug( 1, "Could not find codec \"%s\". Using default \"%d\"", codec_name, codec_id );
Debug(1, "Could not find codec \"%s\". Using default \"%d\"", codec_name, codec_id);
#endif
}
}
@@ -183,19 +174,19 @@ void VideoStream::SetupCodec( int colours, int subpixelorder, int width, int hei
and initialize the codecs */
ost = NULL;
if ( codec_id != AV_CODEC_ID_NONE ) {
codec = avcodec_find_encoder( codec_id );
codec = avcodec_find_encoder(codec_id);
if ( !codec ) {
#if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100))
Fatal( "Could not find encoder for '%s'", avcodec_get_name( codec_id ) );
Fatal("Could not find encoder for '%s'", avcodec_get_name(codec_id));
#else
Fatal( "Could not find encoder for '%d'", codec_id );
Fatal("Could not find encoder for '%d'", codec_id);
#endif
}
#if (LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 11, 0) && (LIBAVFORMAT_VERSION_MICRO >= 100))
Debug( 1, "Found encoder for '%s'", avcodec_get_name( codec_id ) );
Debug(1, "Found encoder for '%s'", avcodec_get_name(codec_id));
#else
Debug( 1, "Found encoder for '%d'", codec_id );
Debug(1, "Found encoder for '%d'", codec_id);
#endif
#if LIBAVFORMAT_VERSION_CHECK(53, 10, 0, 17, 0)
+38 -14
View File
@@ -624,8 +624,8 @@ int RemoteCameraHttp::GetResponse() {
static char *content_type_header;
static char *boundary_header;
static char *authenticate_header;
static char subcontent_length_header[32];
static char subcontent_type_header[64];
static char subcontent_length_header[33];
static char subcontent_type_header[65];
static char http_version[16];
static char status_code[16];
@@ -896,25 +896,37 @@ int RemoteCameraHttp::GetResponse() {
}
}
Debug( 6, "%d: %s", subheader_len, subheader_ptr );
Debug(6, "%d: %s", subheader_len, subheader_ptr);
if ( (crlf = mempbrk( subheader_ptr, "\r\n", subheader_len )) ) {
if ( (crlf = mempbrk(subheader_ptr, "\r\n", subheader_len)) ) {
//subheaders[n_subheaders++] = subheader_ptr;
n_subheaders++;
if ( !boundary_header && (strncasecmp( subheader_ptr, content_boundary, content_boundary_len ) == 0) ) {
if ( !boundary_header && (strncasecmp(subheader_ptr, content_boundary, content_boundary_len) == 0) ) {
boundary_header = subheader_ptr;
Debug( 4, "Got boundary subheader '%s'", subheader_ptr );
} else if ( !subcontent_length_header[0] && (strncasecmp( subheader_ptr, content_length_match, content_length_match_len) == 0) ) {
strncpy( subcontent_length_header, subheader_ptr+content_length_match_len, sizeof(subcontent_length_header) );
*(subcontent_length_header+strcspn( subcontent_length_header, "\r\n" )) = '\0';
Debug( 4, "Got content length subheader '%s'", subcontent_length_header );
Debug(4, "Got boundary subheader '%s'", subheader_ptr);
} else if (
!subcontent_length_header[0]
&&
(strncasecmp(subheader_ptr, content_length_match, content_length_match_len) == 0)
) {
strncpy(
subcontent_length_header,
subheader_ptr+content_length_match_len,
sizeof(subcontent_length_header)-1
);
*(subcontent_length_header+strcspn(subcontent_length_header, "\r\n")) = '\0';
Debug(4, "Got content length subheader '%s'", subcontent_length_header);
} else if ( !subcontent_type_header[0] && (strncasecmp( subheader_ptr, content_type_match, content_type_match_len) == 0) ) {
strncpy( subcontent_type_header, subheader_ptr+content_type_match_len, sizeof(subcontent_type_header) );
*(subcontent_type_header+strcspn( subcontent_type_header, "\r\n" )) = '\0';
Debug( 4, "Got content type subheader '%s'", subcontent_type_header );
strncpy(
subcontent_type_header,
subheader_ptr+content_type_match_len,
sizeof(subcontent_type_header)-1
);
*(subcontent_type_header+strcspn(subcontent_type_header, "\r\n")) = '\0';
Debug(4, "Got content type subheader '%s'", subcontent_type_header);
} else {
Debug( 6, "Got ignored subheader '%s' found", subheader_ptr );
Debug(6, "Got ignored subheader '%s' found", subheader_ptr);
}
subheader_ptr = crlf;
subheader_len -= buffer.consume( subheader_ptr-(char *)buffer );
@@ -1057,6 +1069,18 @@ int RemoteCameraHttp::GetResponse() {
return( 0 );
}
int RemoteCameraHttp::PrimeCapture() {
if ( sd < 0 ) {
Connect();
if ( sd < 0 ) {
return -1;
}
mode = SINGLE_IMAGE;
buffer.clear();
}
return 0;
}
int RemoteCameraHttp::PreCapture() {
if ( sd < 0 ) {
Connect();
+1
View File
@@ -55,6 +55,7 @@ public:
int ReadData( Buffer &buffer, unsigned int bytes_expected=0 );
int GetData();
int GetResponse();
int PrimeCapture();
int PreCapture();
int Capture( Image &image );
int PostCapture();
+19 -20
View File
@@ -104,13 +104,13 @@ protected:
CmdMsg msg;
protected:
bool loadMonitor( int monitor_id );
bool loadMonitor(int monitor_id);
bool checkInitialised();
void updateFrameRate( double fps );
Image *prepareImage( Image *image );
bool sendTextFrame( const char *text );
void updateFrameRate(double fps);
Image *prepareImage(Image *image);
bool sendTextFrame(const char *text);
bool checkCommandQueue();
virtual void processCommand( const CmdMsg *msg )=0;
virtual void processCommand(const CmdMsg *msg)=0;
public:
StreamBase():
@@ -135,13 +135,12 @@ public:
lock_fd(0),
paused(false),
step(0)
{
memset( &loc_sock_path, 0, sizeof(loc_sock_path) );
memset( &loc_addr, 0, sizeof(loc_addr) );
memset( &rem_sock_path, 0, sizeof(rem_sock_path) );
memset( &rem_addr, 0, sizeof(rem_addr) );
memset( &sock_path_lock, 0, sizeof(sock_path_lock) );
memset(&loc_sock_path, 0, sizeof(loc_sock_path));
memset(&loc_addr, 0, sizeof(loc_addr));
memset(&rem_sock_path, 0, sizeof(rem_sock_path));
memset(&rem_addr, 0, sizeof(rem_addr));
memset(&sock_path_lock, 0, sizeof(sock_path_lock));
base_fps = 0.0;
effective_fps = 0.0;
@@ -156,28 +155,28 @@ public:
}
virtual ~StreamBase();
void setStreamType( StreamType p_type ) {
void setStreamType(StreamType p_type) {
type = p_type;
}
void setStreamFormat( const char *p_format ) {
void setStreamFormat(const char *p_format) {
format = p_format;
}
void setStreamScale( int p_scale ) {
void setStreamScale(int p_scale) {
scale = p_scale;
if ( ! scale )
if ( !scale )
scale = DEFAULT_SCALE;
}
void setStreamReplayRate( int p_rate ) {
Debug(2,"Setting replay_rate %d", p_rate);
void setStreamReplayRate(int p_rate) {
Debug(2, "Setting replay_rate %d", p_rate);
replay_rate = p_rate;
}
void setStreamMaxFPS( double p_maxfps ) {
void setStreamMaxFPS(double p_maxfps) {
maxfps = p_maxfps;
}
void setStreamBitrate( int p_bitrate ) {
void setStreamBitrate(int p_bitrate) {
bitrate = p_bitrate;
}
void setStreamQueue( int p_connkey ) {
void setStreamQueue(int p_connkey) {
connkey = p_connkey;
}
virtual void openComms();
+57 -70
View File
@@ -35,7 +35,7 @@
#include <curl/curl.h>
#endif
unsigned int sseversion = 0;
unsigned int sse_version = 0;
unsigned int neonversion = 0;
std::string trimSet(std::string str, std::string trimset) {
@@ -44,12 +44,9 @@ std::string trimSet(std::string str, std::string trimset) {
size_t endpos = str.find_last_not_of(trimset); // Find the first character position from reverse af
// if all spaces or empty return an empty string
if(( std::string::npos == startpos ) || ( std::string::npos == endpos))
{
if ( ( std::string::npos == startpos ) || ( std::string::npos == endpos ) )
return std::string("");
}
else
return str.substr( startpos, endpos-startpos+1 );
return str.substr(startpos, endpos-startpos+1);
}
std::string trimSpaces(const std::string &str) {
@@ -57,48 +54,46 @@ std::string trimSpaces(const std::string &str) {
}
std::string replaceAll(std::string str, std::string from, std::string to) {
if(from.empty())
if ( from.empty() )
return str;
size_t start_pos = 0;
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
while ( (start_pos = str.find(from, start_pos)) != std::string::npos ) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
return str;
}
const std::string stringtf( const char *format, ... )
{
const std::string stringtf( const char *format, ... ) {
va_list ap;
char tempBuffer[8192];
std::string tempString;
va_start(ap, format );
vsnprintf( tempBuffer, sizeof(tempBuffer), format , ap );
va_start(ap, format);
vsnprintf(tempBuffer, sizeof(tempBuffer), format , ap);
va_end(ap);
tempString = tempBuffer;
return( tempString );
return tempString;
}
const std::string stringtf( const std::string format, ... )
{
const std::string stringtf(const std::string format, ...) {
va_list ap;
char tempBuffer[8192];
std::string tempString;
va_start(ap, format );
vsnprintf( tempBuffer, sizeof(tempBuffer), format.c_str() , ap );
va_start(ap, format);
vsnprintf(tempBuffer, sizeof(tempBuffer), format.c_str(), ap);
va_end(ap);
tempString = tempBuffer;
return( tempString );
return tempString;
}
bool startsWith(const std::string &haystack, const std::string &needle) {
return( haystack.substr(0, needle.length()) == needle );
return ( haystack.substr(0, needle.length()) == needle );
}
StringVector split(const std::string &string, const std::string &chars, int limit) {
@@ -145,8 +140,7 @@ const std::string join(const StringVector &v, const char * delim=",") {
const std::string base64Encode(const std::string &inString) {
static char base64_table[64] = { '\0' };
if ( !base64_table[0] )
{
if ( !base64_table[0] ) {
int i = 0;
for ( char c = 'A'; c <= 'Z'; c++ )
base64_table[i++] = c;
@@ -159,59 +153,52 @@ const std::string base64Encode(const std::string &inString) {
}
std::string outString;
outString.reserve( 2 * inString.size() );
outString.reserve(2 * inString.size());
const char *inPtr = inString.c_str();
while( *inPtr )
{
while ( *inPtr ) {
unsigned char selection = *inPtr >> 2;
unsigned char remainder = (*inPtr++ & 0x03) << 4;
outString += base64_table[selection];
if ( *inPtr )
{
if ( *inPtr ) {
selection = remainder | (*inPtr >> 4);
remainder = (*inPtr++ & 0x0f) << 2;
outString += base64_table[selection];
if ( *inPtr )
{
if ( *inPtr ) {
selection = remainder | (*inPtr >> 6);
outString += base64_table[selection];
selection = (*inPtr++ & 0x3f);
outString += base64_table[selection];
}
else
{
} else {
outString += base64_table[remainder];
outString += '=';
}
}
else
{
} else {
outString += base64_table[remainder];
outString += '=';
outString += '=';
}
}
return( outString );
return outString;
}
int split(const char* string, const char delim, std::vector<std::string>& items) {
if(string == NULL)
if ( string == NULL )
return -1;
if(string[0] == 0)
if ( string[0] == 0 )
return -2;
std::string str(string);
while(true) {
while ( true ) {
size_t pos = str.find(delim);
items.push_back(str.substr(0, pos));
str.erase(0, pos+1);
if(pos == std::string::npos)
if ( pos == std::string::npos )
break;
}
@@ -219,16 +206,16 @@ int split(const char* string, const char delim, std::vector<std::string>& items)
}
int pairsplit(const char* string, const char delim, std::string& name, std::string& value) {
if(string == NULL)
if ( string == NULL )
return -1;
if(string[0] == 0)
if ( string[0] == 0 )
return -2;
std::string str(string);
size_t pos = str.find(delim);
if(pos == std::string::npos || pos == 0 || pos >= str.length())
if ( pos == std::string::npos || pos == 0 || pos >= str.length() )
return -3;
name = str.substr(0, pos);
@@ -240,7 +227,7 @@ int pairsplit(const char* string, const char delim, std::string& name, std::stri
/* Detect special hardware features, such as SIMD instruction sets */
void hwcaps_detect() {
neonversion = 0;
sseversion = 0;
sse_version = 0;
#if (defined(__i386__) || defined(__x86_64__))
/* x86 or x86-64 processor */
uint32_t r_edx, r_ecx, r_ebx;
@@ -277,33 +264,33 @@ void hwcaps_detect() {
);
#endif
if (r_ebx & 0x00000020) {
sseversion = 52; /* AVX2 */
Debug(1,"Detected a x86\\x86-64 processor with AVX2");
} else if (r_ecx & 0x10000000) {
sseversion = 51; /* AVX */
Debug(1,"Detected a x86\\x86-64 processor with AVX");
} else if (r_ecx & 0x00100000) {
sseversion = 42; /* SSE4.2 */
Debug(1,"Detected a x86\\x86-64 processor with SSE4.2");
} else if (r_ecx & 0x00080000) {
sseversion = 41; /* SSE4.1 */
Debug(1,"Detected a x86\\x86-64 processor with SSE4.1");
} else if (r_ecx & 0x00000200) {
sseversion = 35; /* SSSE3 */
if ( r_ebx & 0x00000020 ) {
sse_version = 52; /* AVX2 */
Debug(1, "Detected a x86\\x86-64 processor with AVX2");
} else if ( r_ecx & 0x10000000 ) {
sse_version = 51; /* AVX */
Debug(1, "Detected a x86\\x86-64 processor with AVX");
} else if ( r_ecx & 0x00100000 ) {
sse_version = 42; /* SSE4.2 */
Debug(1, "Detected a x86\\x86-64 processor with SSE4.2");
} else if ( r_ecx & 0x00080000 ) {
sse_version = 41; /* SSE4.1 */
Debug(1, "Detected a x86\\x86-64 processor with SSE4.1");
} else if ( r_ecx & 0x00000200 ) {
sse_version = 35; /* SSSE3 */
Debug(1,"Detected a x86\\x86-64 processor with SSSE3");
} else if (r_ecx & 0x00000001) {
sseversion = 30; /* SSE3 */
Debug(1,"Detected a x86\\x86-64 processor with SSE3");
} else if (r_edx & 0x04000000) {
sseversion = 20; /* SSE2 */
Debug(1,"Detected a x86\\x86-64 processor with SSE2");
} else if (r_edx & 0x02000000) {
sseversion = 10; /* SSE */
Debug(1,"Detected a x86\\x86-64 processor with SSE");
} else if ( r_ecx & 0x00000001 ) {
sse_version = 30; /* SSE3 */
Debug(1, "Detected a x86\\x86-64 processor with SSE3");
} else if ( r_edx & 0x04000000 ) {
sse_version = 20; /* SSE2 */
Debug(1, "Detected a x86\\x86-64 processor with SSE2");
} else if ( r_edx & 0x02000000 ) {
sse_version = 10; /* SSE */
Debug(1, "Detected a x86\\x86-64 processor with SSE");
} else {
sseversion = 0;
Debug(1,"Detected a x86\\x86-64 processor");
sse_version = 0;
Debug(1, "Detected a x86\\x86-64 processor");
}
#elif defined(__arm__)
// ARM processor in 32bit mode
@@ -333,7 +320,7 @@ __attribute__((noinline,__target__("sse2")))
#endif
void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
#if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
if(bytes > 128) {
if ( bytes > 128 ) {
unsigned int remainder = bytes % 128;
const uint8_t* lastsrc = (uint8_t*)src + (bytes - remainder);
@@ -375,7 +362,7 @@ void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes) {
}
#else
/* Non x86\x86-64 platform, use memcpy */
memcpy(dest,src,bytes);
memcpy(dest, src, bytes);
#endif
return dest;
}
+1 -1
View File
@@ -59,7 +59,7 @@ void* sse2_aligned_memcpy(void* dest, const void* src, size_t bytes);
void timespec_diff(struct timespec *start, struct timespec *end, struct timespec *diff);
void hwcaps_detect();
extern unsigned int sseversion;
extern unsigned int sse_version;
extern unsigned int neonversion;
char *timeval_to_string( struct timeval tv );
+39 -13
View File
@@ -37,11 +37,12 @@ VideoStore::VideoStore(
const char *format_in,
AVStream *p_video_in_stream,
AVStream *p_audio_in_stream,
Monitor *monitor
Monitor *p_monitor
) {
video_in_stream = p_video_in_stream;
audio_in_stream = p_audio_in_stream;
monitor = p_monitor;
#if LIBAVCODEC_VERSION_CHECK(57, 64, 0, 64, 0)
//video_in_ctx = avcodec_alloc_context3(NULL);
@@ -213,11 +214,18 @@ VideoStore::VideoStore(
}
#if LIBAVCODEC_VERSION_CHECK(56, 35, 0, 64, 0)
#if 0
# This is commented out because we are only doing passthrough right now
/* I'm not entirely sure that this is a good idea. We may have to do it someday but really only when transcoding
* * think what I was trying to achieve here was to have zm_dump_codecpar output nice info
* */
#if 0
AVDictionary *opts = 0;
ret = av_dict_parse_string(&opts, monitor->GetOptEncoderParams().c_str(), "=", ",", 0);
if ( ret < 0 ) {
Warning("Could not parse ffmpeg encoder options '%s'", monitor->GetOptEncoderParams().c_str());
}
if ( (ret = avcodec_open2(video_out_ctx, video_out_codec, &opts)) < 0 ) {
Warning("Can't open video codec (%s) %s",
video_out_codec->name,
@@ -404,21 +412,39 @@ bool VideoStore::open() {
}
zm_dump_stream_format(oc, 0, 0, 1);
if (audio_out_stream) zm_dump_stream_format(oc, 1, 0, 1);
if ( audio_out_stream ) zm_dump_stream_format(oc, 1, 0, 1);
AVDictionary *opts = NULL;
// av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0);
// Shiboleth reports that this may break seeking in mp4 before it downloads
av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
// av_dict_set(&opts, "movflags",
// "frag_keyframe+empty_moov+default_base_moof", 0);
std::string option_string = monitor->GetOptEncoderParams();
ret = av_dict_parse_string(&opts, option_string.c_str(), "=", ",\n", 0);
if ( ret < 0 ) {
Warning("Could not parse ffmpeg output options '%s'", option_string.c_str());
}
const AVDictionaryEntry *movflags_entry = av_dict_get(opts, "movflags", NULL, AV_DICT_MATCH_CASE);
if ( !movflags_entry ) {
Debug(1, "setting movflags to frag_keyframe+empty_moov");
// av_dict_set(&opts, "movflags", "frag_custom+dash+delay_moov", 0);
// Shiboleth reports that this may break seeking in mp4 before it downloads
av_dict_set(&opts, "movflags", "frag_keyframe+empty_moov", 0);
// av_dict_set(&opts, "movflags",
// "frag_keyframe+empty_moov+default_base_moof", 0);
} else {
Debug(1, "using movflags %s", movflags_entry->value);
}
if ( (ret = avformat_write_header(oc, &opts)) < 0 ) {
// if ((ret = avformat_write_header(oc, &opts)) < 0) {
Warning("Unable to set movflags to frag_custom+dash+delay_moov");
/* Write the stream header, if any. */
Warning("Unable to set movflags trying with defaults.");
ret = avformat_write_header(oc, NULL);
} else if (av_dict_count(opts) != 0) {
Warning("some options not set");
} else if ( av_dict_count(opts) != 0 ) {
Info("some options not used, turn on debugging for a list.");
AVDictionaryEntry *e = NULL;
while ( (e = av_dict_get(opts, "", e, AV_DICT_IGNORE_SUFFIX)) != NULL ) {
Debug(1, "Encoder Option %s=>%s", e->key, e->value);
if ( !e->value ) {
av_dict_set(&opts, e->key, NULL, 0);
}
}
}
if ( opts ) av_dict_free(&opts);
if ( ret < 0 ) {
+1 -1
View File
@@ -19,7 +19,6 @@ extern "C" {
class VideoStore {
private:
AVOutputFormat *out_format;
AVFormatContext *oc;
@@ -30,6 +29,7 @@ private:
AVStream *video_in_stream;
AVStream *audio_in_stream;
Monitor *monitor;
// Move this into the object so that we aren't constantly allocating/deallocating it on the stack
AVPacket opkt;
Loaded 100 of 190 files, more files were not shown because too many files have changed in this diff. Show more