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

This commit is contained in:
Isaac Connor 2021-10-12 12:25:01 -04:00
commit 0ba3d863a6
147 changed files with 3812 additions and 3369 deletions

View File

@ -1,17 +1,19 @@
task:
name: freebsd-build
freebsd_instance:
image_family: freebsd-12-2
matrix:
- image_family: freebsd-12-2
- image_family: freebsd-13-0
prepare_script:
- pkg install -yq git cmake pkgconf jpeg-turbo mysql80-client ffmpeg libvncserver libjwt catch p5-DBI p5-DBD-mysql p5-Date-Manip p5-Test-LWP-UserAgent p5-Sys-Mmap
- pkg install -yq git cmake pkgconf jpeg-turbo mysql80-client ffmpeg libvncserver libjwt catch p5-DBI p5-DBD-mysql p5-Date-Manip p5-Test-LWP-UserAgent p5-Sys-Mmap v4l_compat
configure_script:
- git submodule update --init --recursive
- mkdir build
- cd build
- cmake --version
- cmake ../ -DBUILD_MAN=0 -DBUILD_TEST_SUITE=1 -DENABLE_WERROR=1
- cmake ../ -DBUILD_MAN=0 -DBUILD_TEST_SUITE=1 -DENABLE_WERROR=1 -DCMAKE_C_FLAGS="-Wno-deprecated-declarations" -DCMAKE_CXX_FLAGS="-Wno-deprecated-declarations"
build_script:
- cd build

2
.github/FUNDING.yml vendored
View File

@ -2,7 +2,7 @@
github: [connortechnology,pliablepixels] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: zoneminder # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
open_collective: zoneminder # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry

View File

@ -14,6 +14,8 @@ jobs:
os_dist:
- os: debian
dist: buster
- os: debian
dist: bullseye
runs-on: ubuntu-latest
steps:

1
.gitignore vendored
View File

@ -124,6 +124,7 @@ src/zmc
src/zmf
src/zms
src/zmu
src/zm_rtsp_server
src/zoneminder-zmc.8
src/zoneminder-zmc.8.gz
src/zoneminder-zmf.8

View File

@ -10,7 +10,8 @@ deb:
tags:
- docker
script:
- yes "" | ./utils/do_debian_package.sh --snapshot=stable --branch=1.36.1 --type=binary --interactive=no --dput=no --debbuild-extra=--no-sign || true
- yes "" | ./utils/do_debian_package.sh --snapshot=stable --type=binary --interactive=no --dput=no --debbuild-extra=--no-sign || true
timeout: 2h
artifacts:
paths:
- '*.deb'

View File

@ -187,6 +187,8 @@ set(ZM_MANPAGE_DEST_PREFIX "share/man" CACHE PATH
"Relative path used to install ZoneMinder's Man pages into a
non-standard folder. Most Linux users will not need to change this.
BSD users may need to set this.")
set(ZM_CAKEPHP_CACHE "Apc" CACHE STRING
"Set the CakePHP cache engine, default: Apc")
# Supported crypto backends. Using OpenSSL by default to be compatible with jwt-cpp.
set(ZM_CRYPTO_BACKEND_OPTIONS gnutls openssl)
@ -207,6 +209,10 @@ if(NOT ZM_JWT_BACKEND IN_LIST ZM_JWT_BACKEND_OPTIONS)
endif()
# Reassign some variables if a target distro has been specified
if(ZM_TARGET_DISTRO MATCHES "^fc")
set(ZM_CAKEPHP_CACHE "Memcached")
endif()
if((ZM_TARGET_DISTRO MATCHES "^el") OR (ZM_TARGET_DISTRO MATCHES "^fc"))
set(ZM_RUNDIR "/var/run/zoneminder")
set(ZM_SOCKDIR "/var/lib/zoneminder/sock")
@ -262,7 +268,6 @@ if(ZM_SYSTEMD OR (IS_DIRECTORY /usr/lib/systemd/system) OR (IS_DIRECTORY /lib/sy
endif()
# System checks
check_include_file("linux/videodev2.h" HAVE_LINUX_VIDEODEV2_H)
check_include_file("execinfo.h" HAVE_EXECINFO_H)
if(HAVE_EXECINFO_H)
check_function_exists("backtrace" HAVE_DECL_BACKTRACE)
@ -522,17 +527,14 @@ if((NOT HAVE_LIBJWT) AND (NOT HAVE_LIBOPENSSL))
message(FATAL_ERROR "Using the jwt-cpp backend requires OpenSSL as crypto backend.")
endif()
# Check for V4L header files and enable ZM_HAS_V4L, ZM_HAS_V4L2 accordingly
# Setting to zeros first is required because ZM uses #define for these
set(ZM_HAS_V4L 0)
set(ZM_HAS_V4L2 0)
if(HAVE_LINUX_VIDEODEV2_H)
set(ZM_HAS_V4L 1)
find_package(V4L2)
if(TARGET V4L2::videodev2)
set(ZM_HAS_V4L2 1)
endif()
if(NOT HAVE_LINUX_VIDEODEV2_H)
else()
set(ZM_HAS_V4L2 0)
message(AUTHOR_WARNING "Video 4 Linux headers weren't found - Analog and USB camera support will not be available")
endif()
# Check for PCRE and enable ZM_PCRE accordingly
set(ZM_PCRE 0)
if(HAVE_LIBPCRE AND HAVE_PCRE_H)

View File

@ -30,7 +30,7 @@ This is the recommended method to install ZoneMinder onto your system. ZoneMinde
- Debian from their [default repository](https://packages.debian.org/search?searchon=names&keywords=zoneminder)
- RHEL/CentOS and clones via [RPM Fusion](http://rpmfusion.org)
- Fedora via [RPM Fusion](http://rpmfusion.org)
- OpenSuse via [third party repository](http://www.zoneminder.com/wiki/index.php/Installing_using_ZoneMinder_RPMs_for_SuSE)
- OpenSuse via [third party repository](https://wiki.zoneminder.com/Installing_using_ZoneMinder_RPMs_for_SuSE)
- Mageia from their default repository
- Arch via the [AUR](https://aur.archlinux.org/packages/zoneminder/)
- Gentoo via [Portage Overlays](http://gpo.zugaina.org/www-misc/zoneminder)

View File

@ -0,0 +1,99 @@
#[=======================================================================[.rst:
FindV4L2
----------
Find V4L2 headers and libv4l2
This module accepts optional COMPONENTS:
videodev2 (default)
libv4l2
IMPORTED Targets
^^^^^^^^^^^^^^^^
This module defines the following :prop_tgt:`IMPORTED` targets::
``V4L2::videodev2``
The Video for Linux Two header file, if found.
``V4L2::libv4l2``
A thin abstraction layer on top of video4linux2 devices, if found.
Result Variables
^^^^^^^^^^^^^^^^
``V4L2_FOUND``
System has v4l2 support. If no components are specified only the videodev2.h header has to be found.
``V4L2_INCLUDE_DIRS``
The v4l2 include directories.
``V4L2_LIBRARIES``
The libraries needed to have v4l2 support according to the specified components.
#]=======================================================================]
find_path(V4L2_VIDEODEV2_INCLUDE_DIR
NAMES linux/videodev2.h)
mark_as_advanced(V4L2_VIDEODEV2_INCLUDE_DIR)
if(EXISTS "${V4L2_VIDEODEV2_INCLUDE_DIR}")
set(V4L2_videodev2_FOUND TRUE)
else()
set(V4L2_videodev2_FOUND FALSE)
endif()
pkg_check_modules(PC_V4L2_LIBV4L2 QUIET libv4l2)
find_path(V4L2_LIBV4L2_INCLUDE_DIR
NAMES libv4l2.h
HINTS
${PC_V4L2_LIBV4L2_INCLUDEDIR}
${PC_V4L2_LIBV4L2_INCLUDE_DIRS})
mark_as_advanced(V4L2_LIBV4L2_INCLUDE_DIR)
find_library(V4L2_LIBV4L2_LIBRARY
NAMES ${PC_V4L2_LIBV4L2_LIBRARIES}
HINTS
${PC_V4L2_LIBV4L2_LIBDIR}
${PC_V4L2_LIBV4L2_LIBRARY_DIR})
mark_as_advanced(V4L2_LIBV4L2_LIBRARY)
if(EXISTS "${V4L2_LIBV4L2_INCLUDE_DIR}" AND
EXISTS "${V4L2_LIBV4L2_LIBRARY}")
set(V4L2_libv4l2_FOUND TRUE)
else()
set(V4L2_libv4l2_FOUND FALSE)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(V4L2
REQUIRED_VARS
V4L2_VIDEODEV2_INCLUDE_DIR
HANDLE_COMPONENTS)
set(V4L2_INCLUDE_DIRS)
set(V4L2_LIBRARIES)
if(V4L2_videodev2_FOUND)
set(V4L2_VIDEODEV2_INCLUDE_DIRS ${V4L2_VIDEODEV2_INCLUDE_DIR})
list(APPEND V4L2_INCLUDE2_DIRS
"${V4L2_VIDEODEV2_INCLUDE_DIRS}")
add_library(V4L2::videodev2 INTERFACE IMPORTED)
set_target_properties(V4L2::videodev2 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${V4L2_VIDEODEV2_INCLUDE_DIRS}")
endif()
if(V4L2_libv4l2_FOUND)
set(V4L2_LIBV4L2_INCLUDE_DIRS ${V4L2_LIBV4L2_INCLUDE_DIR})
set(V4L2_LIBV4L2_LIBRARIES ${V4L2_LIBV4L2_LIBRARY})
list(APPEND V4L2_INCLUDE_DIRS
"${V4L2_LIBV4L2_INCLUDE_DIRS}")
list(APPEND V4L2_LIBRARIES
"${V4L2_LIBV4L2_LIBRARIES}")
add_library(V4L2::libv4l2 UNKNOWN IMPORTED)
set_target_properties(V4L2::libv4l2 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${V4L2_LIBV4L2_INCLUDE_DIRS}"
IMPORTED_LOCATION "${V4L2_LIBV4L2_LIBRARY}")
endif()

View File

@ -536,7 +536,7 @@ CREATE TABLE `Monitors` (
`ZoneCount` TINYINT NOT NULL DEFAULT 0,
`Refresh` int(10) unsigned default NULL,
`Latitude` DECIMAL(10,8),
`Longitude` DECIMAL(10,8),
`Longitude` DECIMAL(11,8),
`RTSPServer` BOOLEAN NOT NULL DEFAULT FALSE,
`RTSPStreamName` varchar(255) NOT NULL default '',
`Importance` enum('Not','Less','Normal'),
@ -1080,11 +1080,11 @@ CREATE TABLE MontageLayouts (
PRIMARY KEY (`Id`)
);
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('Freeform', '{ "default":{"float":"left","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('2 Wide', '{ "default":{"float":"left", "width":"49%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('Freeform', '{ "default":{"float":"left","left":"0px","right":"0px","top":"0px","bottom":"0px","width":"auto"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('2 Wide', '{ "default":{"float":"left", "width":"50%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('3 Wide', '{ "default":{"float":"left", "width":"33%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{"float":"left", "width":"24.5%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"19%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('4 Wide', '{ "default":{"float":"left", "width":"25%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
INSERT INTO MontageLayouts (`Name`,`Positions`) VALUES ('5 Wide', '{ "default":{"float":"left", "width":"20%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' );
CREATE TABLE Sessions (
id char(32) not null,

1
db/zm_update-1.36.6.sql Normal file
View File

@ -0,0 +1 @@
ALTER TABLE `Monitors` MODIFY `Longitude` DECIMAL(11,8);

1
db/zm_update-1.37.1.sql Normal file
View File

@ -0,0 +1 @@
ALTER TABLE `Monitors` MODIFY `Longitude` DECIMAL(11,8);

4
db/zm_update-1.37.2.sql Normal file
View File

@ -0,0 +1,4 @@
UPDATE MontageLayouts SET `Positions` = '{ "default":{"float":"left","left":"0px","right":"0px","top":"0px","bottom":"0px","width":"auto"} }' WHERE `Name`='Freeform';
UPDATE MontageLayouts SET `Positions` = '{ "default":{"float":"left", "width":"49%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' WHERE `Name`='2 Wide';
UPDATE MontageLayouts SET `Positions` = '{ "default":{"float":"left", "width":"25%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' WHERE `Name`='4 Wide';
UPDATE MontageLayouts SET `Positions` = '{ "default":{"float":"left", "width":"20%","left":"0px","right":"0px","top":"0px","bottom":"0px"} }' WHERE `Name`='5 Wide';

View File

@ -17,21 +17,26 @@
# This will tell zoneminder's cmake process we are building against a known distro
%global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}}
# Fedora needs apcu backwards compatibility module
%if 0%{?fedora}
%global with_apcu_bc 1
%endif
# Newer php's keep json functions in a subpackage
%if 0%{?fedora} || 0%{?rhel} >= 8
%global with_php_json 1
%endif
# el7 uses cmake3 package and macros
%if 0%{?rhel} == 7
%global cmake %{cmake3}
%global cmake_build %{cmake3_build}
%global cmake_install %{cmake3_install}
%global cmake_pkg_name cmake3
%else
%global cmake_pkg_name cmake
%endif
# The default for everything but el7 these days
%global _hardened_build 1
Name: zoneminder
Version: 1.36.4
Version: 1.37.1
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
@ -56,7 +61,7 @@ BuildRequires: systemd-devel
BuildRequires: mariadb-devel
BuildRequires: perl-podlators
BuildRequires: polkit-devel
BuildRequires: cmake3
BuildRequires: %{cmake_pkg_name}
BuildRequires: gnutls-devel
BuildRequires: bzip2-devel
BuildRequires: pcre-devel
@ -116,8 +121,8 @@ Requires: php-mysqli
Requires: php-common
Requires: php-gd
%{?with_php_json:Requires: php-json}
Requires: php-pecl-apcu
%{?with_apcu_bc:Requires: php-pecl-apcu-bc}
%{?fedora:Requires: php-pecl-memcached}
%{?rhel:Requires: php-pecl-apcu}
Requires: cambozola
Requires: net-tools
Requires: psmisc
@ -216,16 +221,16 @@ mv -f RtspServer-%{rtspserver_commit} ./dep/RtspServer
# See https://fedoraproject.org/wiki/LTOByDefault
%define _lto_cflags %{nil}
%cmake3 \
%cmake \
-DZM_WEB_USER="%{zmuid_final}" \
-DZM_WEB_GROUP="%{zmgid_final}" \
-DZM_TARGET_DISTRO="%{zmtargetdistro}" \
.
%cmake3_build
%cmake_build
%install
%cmake3_install
%cmake_install
desktop-file-install \
--dir %{buildroot}%{_datadir}/applications \
@ -425,6 +430,16 @@ ln -sf %{_sysconfdir}/zm/www/zoneminder.nginx.conf %{_sysconfdir}/zm/www/zonemin
%dir %attr(755,nginx,nginx) %{_localstatedir}/log/zoneminder
%changelog
* Mon Jul 05 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.37.1-1
- 1.37.x development build
* Tue Jun 22 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.5-1
- 1.36.5 release
* Fri Jun 18 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.4-2
- apcu-bc deprecated on fedora, use memcached instead
- only refer to cmake3 when building on el7
* Tue Jun 08 2021 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.36.4-1
- 1.36.4 release

View File

@ -2,7 +2,7 @@ Source: zoneminder
Section: net
Priority: optional
Maintainer: Isaac Connor <isaac@zoneminder.com>
Build-Depends: debhelper (>= 12), sphinx-doc, python3-sphinx, dh-linktree, dh-apache2
Build-Depends: debhelper (>= 11), sphinx-doc, python3-sphinx, dh-linktree, dh-apache2
,cmake
,libavcodec-dev
,libavformat-dev
@ -30,7 +30,7 @@ Build-Depends: debhelper (>= 12), sphinx-doc, python3-sphinx, dh-linktree, dh-ap
,libcrypt-eksblowfish-perl
,libdata-entropy-perl
,libvncserver-dev
,libjwt-gnutls-dev
,libjwt-gnutls-dev|libjwt-dev
Standards-Version: 4.5.0
Homepage: https://www.zoneminder.com/
@ -39,8 +39,8 @@ Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,sudo
,javascript-common
,libswscale5
,libswresample3
,libswscale5|libswscale4
,libswresample3|libswresample2
,ffmpeg
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
,libdbd-mysql-perl
@ -74,7 +74,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libcrypt-eksblowfish-perl
,libdata-entropy-perl
,libvncclient1|libvncclient0
,libjwt-gnutls0
,libjwt-gnutls0|libjwt0
Recommends: ${misc:Recommends}
,libapache2-mod-php | php-fpm
,default-mysql-server | mariadb-server | virtual-mysql-server

View File

@ -1,4 +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/tmp/zm 0755 www-data www-data 7d
d /var/cache/zoneminder/cache 0755 www-data www-data

View File

@ -56,13 +56,13 @@ Add the following to the /etc/apt/sources.list.d/zoneminder.list file
::
# ZoneMinder repository
deb https://zmrepo.zoneminder.com/debian/release-1.34 buster/
deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/
You can do this using:
.. code-block::
echo "deb https://zmrepo.zoneminder.com/debian/release-1.34 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list
echo "deb https://zmrepo.zoneminder.com/debian/release-1.36 buster/" | sudo tee /etc/apt/sources.list.d/zoneminder.list
Because ZoneMinder's package repository provides a secure connection through HTTPS, apt must be enabled for HTTPS.
::
@ -158,7 +158,7 @@ You are now ready to go with ZoneMinder. Open a browser and type either ``localh
Easy Way: Debian Stretch
------------------------
This procedure will guide you through the installation of ZoneMinder on Debian 9 (Stretch). This section has been tested with ZoneMinder 1.34 on Debian 9.8.
This procedure will guide you through the installation of ZoneMinder on Debian 9 (Stretch). This section has been tested with ZoneMinder 1.36 on Debian 9.8.
**Step 1:** Make sure your system is up to date
@ -204,7 +204,7 @@ Add the following to the bottom of the file
::
# ZoneMinder repository
deb https://zmrepo.zoneminder.com/debian/release-1.34 stretch/
deb https://zmrepo.zoneminder.com/debian/release-1.36 stretch/
CTRL+o and <Enter> to save
CTRL+x to exit

View File

@ -77,7 +77,7 @@ To start the build, simply execute the following command from the root folder of
OS=<distroname> DIST=<distrorel> utils/packpack/startpackpack.sh
Where <distroname> is the name of the distro you wish to build on, such as fedora, and <distrorev> is release name or number of the distro you wish to build on. Redhat distros expect a number for <distrorev> while Debian and Ubuntu distros expect a name. For example:
Where <distroname> is the name of the distro you wish to build on, such as fedora, and <distrorel> is the release name or number of the distro you wish to build on. Redhat distros expect a number for <distrorel> while Debian and Ubuntu distros expect a name. For example:
::

View File

@ -41,7 +41,7 @@ guide you with a quick search.
::
add-apt-repository ppa:iconnor/zoneminder-1.34
add-apt-repository ppa:iconnor/zoneminder-1.36
Update repo and upgrade.

View File

@ -6,7 +6,7 @@ Here are some options for using ZoneMinder on Mobile devices:
Third party mobile clients
^^^^^^^^^^^^^^^^^^^^^^^^^^^
* zmNinja (`source code <https://github.com/pliablepixels/zmNinja>`__, needs APIs to be installed to work)
* Available in App Store, Play Store and for Desktops - `website <http://pliablepixels.github.io/zmNinja/>`__
* Available in App Store, Play Store and for Desktops - `website <http://pliablepixels.github.io/>`__
Using the existing web console
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -17,4 +17,4 @@ Discontinued clients
The following are a list of clients that do not work and have not been updated:
* eyeZM
* zmView
* zmView

View File

@ -101,6 +101,6 @@ FROM_EMAIL - The emails or messages that will be sent to you informing you of ev
URL - The emails or messages that will be sent to you informing you of events can include a link to the events themselves for easy viewing. If you intend to use this feature then set this option to the url of your installation as it would appear from where you read your email, e.g. ``http://host.your.domain/zm/index.php``.
SSMTP_MAIL - SSMTP is a lightweight and efficient method to send email. The SSMTP application is not installed by default. NEW_MAIL_MODULES must also be enabled. Please visit the ZoneMinder `SSMTP Wiki page <http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder>`__ for setup and configuration help.
SSMTP_MAIL - SSMTP is a lightweight and efficient method to send email. The SSMTP application is not installed by default. NEW_MAIL_MODULES must also be enabled. Please visit the ZoneMinder `SSMTP Wiki page <https://wiki.zoneminder.com/How_to_get_ssmtp_working_with_Zoneminder>`__ for setup and configuration help.
SSMTP_PATH - The path to the SSMTP application. If path is not defined. Zoneminder will try to determine the path via shell command. Example path: /usr/sbin/ssmtp.
SSMTP_PATH - The path to the SSMTP application. If path is not defined. Zoneminder will try to determine the path via shell command. Example path: /usr/sbin/ssmtp.

View File

@ -5,10 +5,7 @@ This screen lets you customize several aspects of the web interface of ZoneMinde
.. image:: images/Options_web.png
WEB_TITLE -
.. todo ::
not quite sure what this does. Seems to change the "target" name - not sure what effect it is supposed to have.
WEB_TITLE - The actual text that is shown on the login screen. It is possible that it also appears in other areas.
WEB_TITLE_PREFIX - If you have more than one installation of ZoneMinder it can be helpful to display different titles for each one. Changing this option allows you to customise the window titles to include further information to aid identification.
@ -48,4 +45,4 @@ WEB_USE_OBJECT_TAGS - There are two methods of including media content in web pa
WEB_XFRAME_WARN - When creating a Web Site monitor, if the target web site has X-Frame-Options set to sameorigin in the header, the site will not display in ZoneMinder. This is a design feature in most modern browsers. When this condition occurs, ZoneMinder will write a warning to the log file. To get around this, one can install a browser plugin or extension to ignore X-Frame headers, and then the page will display properly. Once the plugin or extension has ben installed, the end user may choose to turn this warning off
WEB_FILTER_SOURCE - This option only affects monitors with a source type of Ffmpeg, Libvlc, or WebSite. This setting controls what information is displayed in the Source column on the console. Selecting 'None' will not filter anything. The entire source string will be displayed, which may contain sensitive information. Selecting 'NoCredentials' will strip out usernames and passwords from the string. If there are any port numbers in the string and they are common (80, 554, etc) then those will be removed as well. Selecting 'Hostname' will filter out all information except for the hostname or ip address. When in doubt, stay with the default 'Hostname'. This feature uses the php function 'url_parts' to identify the various pieces of the url. If the url in question is unusual or not standard in some way, then filtering may not produce the desired results.
WEB_FILTER_SOURCE - This option only affects monitors with a source type of Ffmpeg, Libvlc, or WebSite. This setting controls what information is displayed in the Source column on the console. Selecting 'None' will not filter anything. The entire source string will be displayed, which may contain sensitive information. Selecting 'NoCredentials' will strip out usernames and passwords from the string. If there are any port numbers in the string and they are common (80, 554, etc) then those will be removed as well. Selecting 'Hostname' will filter out all information except for the hostname or ip address. When in doubt, stay with the default 'Hostname'. This feature uses the php function 'url_parts' to identify the various pieces of the url. If the url in question is unusual or not standard in some way, then filtering may not produce the desired results.

2
misc/fail2ban.rules Normal file
View File

@ -0,0 +1,2 @@
failregex = ^\s*web_php\[\d+\]\.ERR \[<HOST>\].*includes/auth.php
datepattern = ^%%m/%%d/%%y %%H:%%M:%%S(?:\.%%f)

View File

@ -308,7 +308,7 @@ our @options = (
},
{
name => 'ZM_AUTH_HASH_IPS',
default => 'yes',
default => 'no',
description => 'Include IP addresses in the authentication hash',
help => q`
When ZoneMinder is running in hashed authenticated mode it can
@ -346,7 +346,7 @@ our @options = (
},
{
name => 'ZM_AUTH_HASH_LOGINS',
default => 'no',
default => 'yes',
description => 'Allow login by authentication hash',
help => q`
The normal process for logging into ZoneMinder is via the login
@ -508,7 +508,7 @@ our @options = (
},
{
name => 'ZM_SYSTEM_SHUTDOWN',
default => 'yes',
default => 'no',
description => 'Allow Admin users to power off or restart the system from the ZoneMinder UI.',
help => 'The system will need to have sudo installed and the following added to /etc/sudoers~~
~~
@ -1096,7 +1096,7 @@ our @options = (
},
{
name => 'ZM_LOG_LEVEL_SYSLOG',
default => '0',
default => '-1',
description => 'Save logging output to the system log',
help => q`
ZoneMinder logging is now more integrated between
@ -1604,7 +1604,7 @@ our @options = (
},
{
name => 'ZM_WEB_EVENT_DISK_SPACE',
default => 'no',
default => 'yes',
description => 'Whether to show disk space used by each event.',
help => q`
Adds another column to the listing of events
@ -1634,7 +1634,7 @@ our @options = (
},
{
name => 'ZM_WEB_ID_ON_CONSOLE',
default => 'no',
default => 'yes',
description => 'Should the console list the monitor id',
help => q`
Some find it useful to have the id always visible
@ -2270,7 +2270,7 @@ our @options = (
},
{
name => 'ZM_MAX_RESTART_DELAY',
default => '600',
default => '30',
description => 'Maximum delay (in seconds) for daemon restart attempts.',
help => q`
The zmdc (zm daemon control) process controls when processeses
@ -2855,7 +2855,7 @@ our @options = (
},
{
name => 'ZM_WEB_H_REFRESH_MAIN',
default => '60',
default => '240',
introduction => q`
There are now a number of options that are grouped into
bandwidth categories, this allows you to configure the
@ -3113,7 +3113,7 @@ our @options = (
},
{
name => 'ZM_WEB_H_AJAX_TIMEOUT',
default => '3000',
default => '10000',
description => 'How long to wait for Ajax request responses (ms)',
help => q`
The newer versions of the live feed and event views use Ajax to
@ -3766,7 +3766,7 @@ our @options = (
SSMTP is a lightweight and efficient method to send email.
The SSMTP application is not installed by default.
NEW_MAIL_MODULES must also be enabled.
Please visit the ZoneMinder [SSMTP Wiki page](http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder)
Please visit the ZoneMinder [SSMTP Wiki page](https://wiki.zoneminder.com/How_to_get_ssmtp_working_with_Zoneminder)
for setup and configuration help.
`,
type => $types{boolean},

View File

@ -220,14 +220,14 @@ sub moveConUpRight {
my $self = shift;
Debug('Move Diagonally Up Right');
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
$$self{LastCmd} = 'code=RightUp&channel=0&arg1=0&arg2=1&arg3=0';
$$self{LastCmd} = 'code=RightUp&channel=0&arg1=1&arg2=1&arg3=0';
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
}
sub moveConDownRight {
my $self = shift;
Debug('Move Diagonally Down Right');
$$self{LastCmd} = 'code=RightDown&channel=0&arg1=0&arg2=1&arg3=0';
$$self{LastCmd} = 'code=RightDown&channel=0&arg1=1&arg2=1&arg3=0';
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
}
@ -236,7 +236,7 @@ sub moveConUpLeft {
my $self = shift;
Debug('Move Diagonally Up Left');
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
$$self{LastCmd} = 'code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0';
$$self{LastCmd} = 'code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0';
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
}
@ -244,7 +244,7 @@ sub moveConDownLeft {
my $self = shift;
Debug('Move Diagonally Down Left');
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
$$self{LastCmd} = 'code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0';
$$self{LastCmd} = 'code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0';
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
}

View File

@ -283,7 +283,7 @@ None by default.
=head1 SEE ALSO
See if there are better instructions for the DCS-5020L at
http://www.zoneminder.com/wiki/index.php/Dlink
https://wiki.zoneminder.com/Dlink
=head1 AUTHOR

View File

@ -1,6 +1,6 @@
# ==========================================================================
#
# ZoneMinder ONVIF Control Protocol Module, $Date: 2021-02-25 22:07:00 +0000 (Thu, 25 Feb 2021) $, $Revision: 0001 $
# ZoneMinder ONVIF Control Protocol Module
# Based on the Netcat onvif script by Andrew Bauer (knnniggett@users.sourceforge.net)
#
# This program is free software; you can redistribute it and/or
@ -182,6 +182,7 @@ sub sendCmd {
$req->header('connection' => 'Close');
$req->content($msg);
my $res = $self->{ua}->request($req);
if ( $res->is_success ) {
@ -235,22 +236,39 @@ sub getCamParams {
sub autoStop {
my $self = shift;
my $autostop = shift;
my $iszoom = shift;
if ( $autostop ) {
Debug('Auto Stop');
my $cmd = $controlUri;
my $msg_body = '
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>'.$profileToken.'</ProfileToken>
<PanTilt>
true
</PanTilt>
<Zoom>
false
</Zoom>
</Stop>
</s:Body>';
my $msg_body;
if( $iszoom) {
$msg_body = '
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>'.$profileToken.'</ProfileToken>
<PanTilt>
false
</PanTilt>
<Zoom>
true
</Zoom>
</Stop>
</s:Body>';
} else {
$msg_body = '
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>'.$profileToken.'</ProfileToken>
<PanTilt>
true
</PanTilt>
<Zoom>
false
</Zoom>
</Stop>
</s:Body>';
}
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
usleep($autostop);
@ -340,9 +358,9 @@ sub moveCamera {
my $type = shift;
my $x = shift;
my $y = shift;
my $msg_move_body = "";
my $msg_move_body = '';
if ( $type == "move" ){
if ( $type eq 'move' ) {
$msg_move_body = '
<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">
@ -356,7 +374,7 @@ sub moveCamera {
</ContinuousMove>
</s:Body>';
} elsif ( $type == "zoom" ) {
} elsif ( $type eq 'zoom' ) {
$msg_move_body = '
<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">
@ -371,7 +389,6 @@ sub moveCamera {
}
return $msg_move_body;
}
#Up Arrow
@ -382,7 +399,7 @@ sub moveConUp {
my $msg_body = moveCamera("move", "0","0.5");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
}
@ -394,7 +411,7 @@ sub moveConDown {
my $msg_body = moveCamera("move","0","-0.5");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
}
#Left Arrow
@ -405,7 +422,7 @@ sub moveConLeft {
my $msg_body = moveCamera("move","-0.49","0");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
}
#Right Arrow
@ -416,7 +433,7 @@ sub moveConRight {
my $msg_body = moveCamera("move","0.49","0");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
}
#Zoom In
@ -427,7 +444,7 @@ sub zoomConTele {
my $msg_body = moveCamera("zoom","0.49","0");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},1);
}
#Zoom Out
@ -438,9 +455,19 @@ sub zoomConWide {
my $msg_body = moveCamera("zoom","-0.49","0");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},1);
}
sub zoomStop {
Debug('Zoom Stop');
my $self = shift;
my $cmd = $controlUri;
$self->autoStop($self->{Monitor}->{AutoStopTimeout},1);
Error('Zoom Stop not implemented');
}
#Diagonally Up Right Arrow
#This camera does not have builtin diagonal commands so we emulate them
sub moveConUpRight {
@ -450,7 +477,7 @@ sub moveConUpRight {
my $msg_body = moveCamera("move","0.5","0.5");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
}
#Diagonally Down Right Arrow
@ -462,7 +489,7 @@ sub moveConDownRight {
my $msg_body = moveCamera("move","0.5","-0.5");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
}
#Diagonally Up Left Arrow
@ -474,7 +501,7 @@ sub moveConUpLeft {
my $msg_body = moveCamera("move","-0.5","0.5");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
}
#Diagonally Down Left Arrow
@ -486,7 +513,7 @@ sub moveConDownLeft {
my $msg_body = moveCamera("move","-0.5","-0.5");
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
$self->sendCmd($cmd, $msg_body, $content_type);
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
}
#Stop
@ -499,7 +526,7 @@ sub moveStop {
<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
<ProfileToken>'.$profileToken.'</ProfileToken>
<PanTilt>true</PanTilt>
<Zoom>false</Zoom>
<Zoom>true</Zoom>
</Stop>
</s:Body>';
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';

View File

@ -107,6 +107,7 @@ sub zmDbConnect {
.$socket . $sslOptions . ($options?join(';', '', map { $_.'='.$$options{$_} } keys %{$options} ) : '')
, $ZoneMinder::Config::Config{ZM_DB_USER}
, $ZoneMinder::Config::Config{ZM_DB_PASS}
, { mysql_enable_utf8 => 1, }
);
};
if ( !$dbh or $@ ) {

View File

@ -127,9 +127,11 @@ sub Execute {
foreach my $term ( @{$$self{PostSQLConditions}} ) {
if ( $$term{attr} eq 'ExistsInFileSystem' ) {
foreach my $row ( @results ) {
my $event = new ZoneMinder::Event($row);
my $event = new ZoneMinder::Event($$row{Id}, $row);
if ( -e $event->Path() ) {
push @filtered_events, $row;
push @filtered_events, $row if $$term{val} eq 'true';
} else {
push @filtered_events, $row if $$term{val} eq 'false';
}
}
}
@ -164,138 +166,142 @@ sub Sql {
if ( exists($term->{obr}) ) {
$self->{Sql} .= str_repeat('(', $term->{obr}).' ';
}
if (!$term->{attr}) {
Error("Invalid term in filter $$self{Id}. Empty attr");
next;
}
my $value = $term->{val};
my @value_list;
if ( $term->{attr} ) {
if ( $term->{attr} eq 'AlarmedZoneId' ) {
$term->{op} = 'EXISTS';
} elsif ( $term->{attr} =~ /^Monitor/ ) {
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
$self->{Sql} .= 'M.'.$temp_attr_name;
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
$self->{Sql} .= 'M.ServerId';
} elsif ( $term->{attr} eq 'StorageServerId' ) {
$self->{Sql} .= '(SELECT Storage.ServerId FROM Storage WHERE Storage.Id=E.StorageId)';
} elsif ( $term->{attr} eq 'FilterServerId' ) {
$self->{Sql} .= $Config{ZM_SERVER_ID};
# StartTime options
} elsif ( $term->{attr} eq 'DateTime' ) {
$self->{Sql} .= 'E.StartDateTime';
} elsif ( $term->{attr} eq 'Date' ) {
$self->{Sql} .= 'to_days( E.StartDateTime )';
} elsif ( $term->{attr} eq 'StartDate' ) {
$self->{Sql} .= 'to_days( E.StartDateTime )';
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
$self->{Sql} .= 'extract( hour_second from E.StartDateTime )';
} elsif ( $term->{attr} eq 'Weekday' or $term->{attr} eq 'StartWeekday' ) {
$self->{Sql} .= 'weekday( E.StartDateTime )';
# EndTIme options
} elsif ( $term->{attr} eq 'EndDateTime' ) {
$self->{Sql} .= 'E.EndDateTime';
} elsif ( $term->{attr} eq 'EndDate' ) {
$self->{Sql} .= 'to_days( E.EndDateTime )';
} elsif ( $term->{attr} eq 'EndDateTime' ) {
$self->{Sql} .= 'extract( hour_second from E.EndDateTime )';
} elsif ( $term->{attr} eq 'EndWeekday' ) {
$self->{Sql} .= "weekday( E.EndDateTime )";
} elsif ( $term->{attr} eq 'ExistsInFileSystem' ) {
push @{$self->{PostSQLConditions}}, $term;
$self->{Sql} .= 'TRUE /* ExistsInFileSystem */';
} elsif ( $term->{attr} eq 'DiskPercent' ) {
$self->{Sql} .= 'zmDiskPercent';
$self->{HasDiskPercent} = !undef;
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
$self->{Sql} .= 'zmDiskBlocks';
$self->{HasDiskBlocks} = !undef;
} elsif ( $term->{attr} eq 'SystemLoad' ) {
$self->{Sql} .= 'zmSystemLoad';
$self->{HasSystemLoad} = !undef;
} else {
$self->{Sql} .= 'E.'.$term->{attr};
}
if ( $term->{attr} eq 'AlarmedZoneId' ) {
$term->{op} = 'EXISTS';
} elsif ( $term->{attr} =~ /^Monitor/ ) {
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
$self->{Sql} .= 'M.'.$temp_attr_name;
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
$self->{Sql} .= 'M.ServerId';
} elsif ( $term->{attr} eq 'StorageServerId' ) {
$self->{Sql} .= '(SELECT Storage.ServerId FROM Storage WHERE Storage.Id=E.StorageId)';
} elsif ( $term->{attr} eq 'FilterServerId' ) {
$self->{Sql} .= $Config{ZM_SERVER_ID};
# StartTime options
} elsif ( $term->{attr} eq 'DateTime' ) {
$self->{Sql} .= 'E.StartDateTime';
} elsif ( $term->{attr} eq 'Date' ) {
$self->{Sql} .= 'to_days( E.StartDateTime )';
} elsif ( $term->{attr} eq 'StartDate' ) {
$self->{Sql} .= 'to_days( E.StartDateTime )';
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
$self->{Sql} .= 'extract( hour_second from E.StartDateTime )';
} elsif ( $term->{attr} eq 'Weekday' or $term->{attr} eq 'StartWeekday' ) {
$self->{Sql} .= 'weekday( E.StartDateTime )';
if ( $term->{attr} eq 'ExistsInFileSystem' ) {
# PostCondition, so no further SQL
} else {
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
# EndTIme options
} elsif ( $term->{attr} eq 'EndDateTime' ) {
$self->{Sql} .= 'E.EndDateTime';
} elsif ( $term->{attr} eq 'EndDate' ) {
$self->{Sql} .= 'to_days( E.EndDateTime )';
} elsif ( $term->{attr} eq 'EndTime' ) {
$self->{Sql} .= 'extract( hour_second from E.EndDateTime )';
} elsif ( $term->{attr} eq 'EndWeekday' ) {
$self->{Sql} .= 'weekday( E.EndDateTime )';
} elsif ( $term->{attr} eq 'ExistsInFileSystem' ) {
push @{$self->{PostSQLConditions}}, $term;
$self->{Sql} .= 'TRUE /* ExistsInFileSystem */';
} elsif ( $term->{attr} eq 'DiskPercent' ) {
$self->{Sql} .= 'zmDiskPercent';
$self->{HasDiskPercent} = !undef;
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
$self->{Sql} .= 'zmDiskBlocks';
$self->{HasDiskBlocks} = !undef;
} elsif ( $term->{attr} eq 'SystemLoad' ) {
$self->{Sql} .= 'zmSystemLoad';
$self->{HasSystemLoad} = !undef;
} else {
$self->{Sql} .= 'E.'.$term->{attr};
}
if ( $term->{attr} eq 'AlarmedZoneId' ) {
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
$value = "'$temp_value'";
} elsif ( $term->{attr} =~ /ServerId/) {
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
if ( $temp_value eq 'ZM_SERVER_ID' ) {
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";
# This gets used later, I forget for what
$$self{Server} = new ZoneMinder::Server($ZoneMinder::Config::Config{ZM_SERVER_ID});
} elsif ( $temp_value eq 'NULL' ) {
$value = $temp_value;
} else {
$value = "'$temp_value'";
# This gets used later, I forget for what
$$self{Server} = new ZoneMinder::Server($temp_value);
}
} elsif ( $term->{attr} eq 'StorageId' ) {
$value = "'$temp_value'";
$$self{Storage} = new ZoneMinder::Storage($temp_value);
} elsif ( $term->{attr} eq 'Name'
|| $term->{attr} eq 'Cause'
|| $term->{attr} eq 'Notes'
) {
if ( $term->{op} eq 'LIKE'
|| $term->{op} eq 'NOT LIKE'
) {
$temp_value = '%'.$temp_value.'%' if $temp_value !~ /%/;
}
$value = "'$temp_value'";
} elsif ( $term->{attr} eq 'DateTime' or $term->{attr} eq 'StartDateTime' or $term->{attr} eq 'EndDateTime' ) {
if ( $temp_value eq 'NULL' ) {
$value = $temp_value;
} else {
$value = DateTimeToSQL($temp_value);
if ( !$value ) {
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
return;
}
$value = "'$value'";
}
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
if ( $temp_value eq 'NULL' ) {
$value = $temp_value;
} elsif ( $temp_value eq 'CURDATE()' or $temp_value eq 'NOW()' ) {
$value = 'to_days('.$temp_value.')';
} else {
$value = DateTimeToSQL($temp_value);
if ( !$value ) {
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
return;
}
$value = "to_days( '$value' )";
}
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' or $term->{attr} eq 'EndTime' ) {
if ( $temp_value eq 'NULL' ) {
$value = $temp_value;
} else {
$value = DateTimeToSQL($temp_value);
if ( !$value ) {
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
return;
}
$value = "extract( hour_second from '$value' )";
}
} else {
if ( $term->{attr} eq 'ExistsInFileSystem' ) {
# PostCondition, so no further SQL
} else {
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
if ( $term->{attr} eq 'AlarmedZoneId' ) {
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
$value = "'$temp_value'";
} elsif ( $term->{attr} =~ /ServerId/) {
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
if ( $temp_value eq 'ZM_SERVER_ID' ) {
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";
# This gets used later, I forget for what
$$self{Server} = new ZoneMinder::Server($ZoneMinder::Config::Config{ZM_SERVER_ID});
} elsif ( $temp_value eq 'NULL' ) {
$value = $temp_value;
} else {
$value = "'$temp_value'";
# This gets used later, I forget for what
$$self{Server} = new ZoneMinder::Server($temp_value);
}
push @value_list, $value;
} # end foreach temp_value
} # end if has an attr
} elsif ( $term->{attr} eq 'StorageId' ) {
$value = "'$temp_value'";
$$self{Storage} = new ZoneMinder::Storage($temp_value);
} elsif ( $term->{attr} eq 'Name'
|| $term->{attr} eq 'Cause'
|| $term->{attr} eq 'Notes'
) {
if ( $term->{op} eq 'LIKE'
|| $term->{op} eq 'NOT LIKE'
) {
$temp_value = '%'.$temp_value.'%' if $temp_value !~ /%/;
}
$value = "'$temp_value'";
} elsif ( $term->{attr} eq 'DateTime' or $term->{attr} eq 'StartDateTime' or $term->{attr} eq 'EndDateTime' ) {
if ( $temp_value eq 'NULL' ) {
$value = $temp_value;
} else {
$value = DateTimeToSQL($temp_value);
if ( !$value ) {
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
return;
}
$value = "'$value'";
}
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
if ( $temp_value eq 'NULL' ) {
$value = $temp_value;
} elsif ( $temp_value eq 'CURDATE()' or $temp_value eq 'NOW()' ) {
$value = 'to_days('.$temp_value.')';
} else {
$value = DateTimeToSQL($temp_value);
if ( !$value ) {
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
return;
}
$value = "to_days( '$value' )";
}
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' or $term->{attr} eq 'EndTime' ) {
if ( $temp_value eq 'NULL' ) {
$value = $temp_value;
} else {
$value = DateTimeToSQL($temp_value);
if ( !$value ) {
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
return;
}
$value = "extract( hour_second from '$value' )";
}
} else {
$value = $temp_value;
}
push @value_list, $value;
} # end foreach temp_value
if ( $term->{op} ) {
if ( $term->{op} eq '=~' ) {

View File

@ -136,8 +136,8 @@ $serial = $primary_key = 'Id';
%defaults = (
ServerId => 0,
StorageId => 0,
Type => 'Ffmpeg',
Function => 'Mocord',
Type => q`'Ffmpeg'`,
Function => q`'Mocord'`,
Enabled => 1,
LinkedMonitors => undef,
Device => '',
@ -166,15 +166,15 @@ $serial = $primary_key = 'Id';
VideoWriter => 0,
OutputCodec => undef,
OutputContainer => undef,
EncoderParameters => "# Lines beginning with # are a comment \n# For changing quality, use the crf option\n# 1 is best, 51 is worst quality\n#crf=23\n",
EncoderParameters => '',
RecordAudio=>0,
RTSPDescribe=>0,
Brightness => -1,
Contrast => -1,
Hue => -1,
Colour => -1,
EventPrefix => 'Event-',
LabelFormat => '%N - %d/%m/%y %H:%M:%S',
EventPrefix => q`'Event-'`,
LabelFormat => '',
LabelX => 0,
LabelY => 0,
LabelSize => 1,
@ -208,13 +208,13 @@ $serial = $primary_key = 'Id';
DefaultRate => 100,
DefaultScale => 100,
SignalCheckPoints => 0,
SignalCheckColour => '#0000BE',
WebColour => '#ff0000',
SignalCheckColour => q`'#0000BE'`,
WebColour => q`'#ff0000'`,
Exif => 0,
Sequence => undef,
ZoneCount => 0,
Refresh => undef,
DefaultCodec => 'auto',
DefaultCodec => q`'auto'`,
Latitude => undef,
Longitude => undef,
);
@ -279,6 +279,7 @@ sub disconnect {
sub suspendMotionDetection {
my $self = shift;
return 0 if ! ZoneMinder::Memory::zmMemVerify($self);
return if $$self{Function} eq 'Nodect' or $$self{Function} eq 'Monitor' or $$self{Function} eq 'None';
my $count = 50;
while ($count and ZoneMinder::Memory::zmMemRead($self, 'shared_data:active', 1)) {
ZoneMinder::Logger::Debug(1, 'Suspending motion detection');
@ -286,19 +287,29 @@ sub suspendMotionDetection {
usleep(100000);
$count -= 1;
}
ZoneMinder::Logger::Error('Unable to suspend motion detection after 5 seconds.') if !$count;
ZoneMinder::Logger::Debug(1, ZoneMinder::Memory::zmMemRead($self, 'shared_data:active', 1));
if (!$count) {
ZoneMinder::Logger::Error('Unable to suspend motion detection after 5 seconds.');
ZoneMinder::Memory::zmMemInvalidate($self); # Close our file handle to the zmc process we are about to end
} else {
ZoneMinder::Logger::Debug(1, 'shared_data:active='.ZoneMinder::Memory::zmMemRead($self, 'shared_data:active', 1));
}
}
sub resumeMotionDetection {
my $self = shift;
return 0 if ! ZoneMinder::Memory::zmMemVerify($self);
return if $$self{Function} eq 'Nodect' or $$self{Function} eq 'Monitor' or $$self{Function} eq 'None';
my $count = 50;
while ($count and !ZoneMinder::Memory::zmMemRead($self, 'shared_data:active', 1)) {
ZoneMinder::Logger::Debug(1, 'Resuming motion detection');
ZoneMinder::Memory::zmMonitorResume($self);
usleep(100000);
$count -= 1;
}
if (!$count) {
ZoneMinder::Logger::Error('Unable to resume motion detection after 5 seconds.');
ZoneMinder::Memory::zmMemInvalidate($self); # Close our file handle to the zmc process we are about to end
}
ZoneMinder::Logger::Error('Unable to suspend motion detection after 5 seconds.') if !$count;
return 1;
}

View File

@ -40,6 +40,10 @@ $serial = $primary_key = 'Id';
MonitorId
Type
Units
NumCoords
Coords
Area
AlarmRGB
CheckMethod
MinPixelThreshold
MaxPixelThreshold
@ -59,9 +63,13 @@ $serial = $primary_key = 'Id';
%defaults = (
Name => '',
Type => 'Active',
Units => 'Pixels',
CheckMethod => 'Blobs',
Type => q`'Active'`,
Units => q`'Pixels'`,
NumCoords => 0,
Coords => '',
Area => 0,
AlarmRGB => 0,
CheckMethod => q`'Blobs'`,
MinPixelThreshold => undef,
MaxPixelThreshold => undef,
MinAlarmPixels => undef,

View File

@ -61,12 +61,12 @@ GetOptions(
'autostop' =>\$options{autostop},
) or pod2usage(-exitstatus => -1);
if ( !$id ) {
if (!$id) {
print(STDERR "Please give a valid monitor id\n");
pod2usage(-exitstatus => -1);
}
( $id ) = $id =~ /^(\w+)$/;
($id) = $id =~ /^(\w+)$/;
logInit($id?(id=>'zmcontrol_'.$id):());
my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock';
@ -76,7 +76,7 @@ socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
my $saddr = sockaddr_un($sock_file);
if ( $options{command} ) {
if ($options{command}) {
# Have a command, so we are the client, connect to the server and send it.
my $tries = 10;
@ -101,18 +101,16 @@ if ( $options{command} ) {
Error("Unable to connect to zmcontrol server at $sock_file");
}
} else {
# The server isn't there
my $monitor = zmDbGetMonitorAndControl($id);
if ( !$monitor ) {
Fatal("Unable to load control data for monitor $id");
}
Fatal("Unable to load control data for monitor $id") if !$monitor;
my $protocol = $monitor->{Protocol};
if ( !$protocol ) {
if (!$protocol) {
Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field');
}
if ( -x $protocol ) {
if (-x $protocol) {
# Protocol is actually a script!
# Holdover from previous versions
my $command .= $protocol.' '.$arg_string;
@ -120,11 +118,11 @@ if ( $options{command} ) {
my $output = qx($command);
my $status = $? >> 8;
if ( $status || logDebugging() ) {
if ($status || logDebugging()) {
chomp($output);
Debug("Output: $output");
}
if ( $status ) {
if ($status) {
Error("Command '$command' exited with status: $status");
exit($status);
}
@ -134,7 +132,7 @@ if ( $options{command} ) {
Info("Starting control server $id/$protocol");
close(CLIENT);
if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) {
if (!can_load(modules => {'ZoneMinder::Control::'.$protocol => undef})) {
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
}
@ -159,7 +157,7 @@ if ( $options{command} ) {
$control->open();
# If we have a command when starting up, then do it.
if ( $options{command} ) {
if ($options{command}) {
my $command = $options{command};
$control->$command(\%options);
}

View File

@ -399,7 +399,6 @@ sub checkFilter {
) {
$Event->save();
}
$ZoneMinder::Database::dbh->commit() if !$$filter{LockRows};
} # end if UpdateDiskSpace
} # end foreach event
ZoneMinder::Database::end_transaction($dbh, $in_transaction) if $$filter{LockRows};
@ -527,7 +526,7 @@ sub uploadArchFile {
return(0);
}
my $archFile = $Event->{MonitorName}.'-'.$Event->{Id};
my $archFile = $Event->Monitor()->Name().'-'.$Event->{Id};
my $archImagePath = $Event->Path()
.'/'
.(
@ -548,6 +547,10 @@ sub uploadArchFile {
my $status = &AZ_OK;
foreach my $imageFile ( @archImageFiles ) {
if (! -e $imageFile) {
Debug("Not adding $imageFile because it doesn't exist");
next;
}
Debug("Adding $imageFile");
my $member = $zip->addFile($imageFile);
if ( !$member ) {
@ -839,7 +842,7 @@ sub sendEmail {
return 0;
}
Info('Creating notification email');
Debug('Creating notification email');
my $subject = substituteTags($$filter{EmailSubject}, $filter, $Event);
return 0 if !$subject;
@ -847,7 +850,7 @@ sub sendEmail {
my $body = substituteTags($$filter{EmailBody}, $filter, $Event, \@attachments);
return 0 if !$body;
Info("Sending notification email '$subject'");
Debug("Sending notification email '$subject'");
eval {
if ( $Config{ZM_NEW_MAIL_MODULES} ) {
@ -860,7 +863,7 @@ sub sendEmail {
);
### Add the text message part
$mail->attach (
Type => 'TEXT',
Type => (($body=~/<html/)?'text/html':'text/plain'),
Data => $body
);
### Add the attachments
@ -882,9 +885,7 @@ sub sendEmail {
if ( $Config{ZM_SSMTP_MAIL} ) {
my $ssmtp_location = $Config{ZM_SSMTP_PATH};
if ( !$ssmtp_location ) {
if ( logDebugging() ) {
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
}
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
$ssmtp_location = qx('which ssmtp');
}
if ( !$ssmtp_location ) {
@ -912,7 +913,7 @@ sub sendEmail {
foreach my $attachment ( @attachments ) {
my $size = -s $attachment->{path};
$total_size += $size;
Info("Attaching '$attachment->{path}' which is $size bytes");
Debug("Attaching '$attachment->{path}' which is $size bytes");
$mail->attach(
Path => $attachment->{path},
@ -930,7 +931,7 @@ sub sendEmail {
Error("Unable to send email: $@");
return 0;
} else {
Info('Notification email sent');
Info("Notification email sent to $$filter{EmailTo}");
}
my $sql = 'UPDATE `Events` SET `Emailed` = 1 WHERE `Id` = ?';
my $sth = $dbh->prepare_cached($sql)

View File

@ -27,7 +27,7 @@ zmupdate.pl - check and upgrade ZoneMinder database
=head1 SYNOPSIS
zmupdate.pl -c,--check | -f,--freshen | -v<version>,--version=<version> [-u<dbuser> -p<dbpass>]
zmupdate.pl -c,--check | -f,--freshen | -v<version>,--version=<version> [-u <dbuser> -p <dbpass>]
=head1 DESCRIPTION

View File

@ -98,19 +98,19 @@ while (!$zm_terminate) {
next if $monitor->{Type} eq 'WebSite';
my $now = time();
my $restart = 0;
if ( zmMemVerify($monitor) ) {
if (zmMemVerify($monitor)) {
# Check we have got an image recently
my $capture_time = zmGetLastWriteTime($monitor);
if ( !defined($capture_time) ) {
if (!defined($capture_time)) {
# Can't read from shared data
Debug('LastWriteTime is not defined.');
zmMemInvalidate($monitor);
next;
}
Debug("Monitor $$monitor{Id} LastWriteTime is $capture_time.");
if ( !$capture_time ) {
if (!$capture_time) {
my $startup_time = zmGetStartupTime($monitor);
if ( ( $now - $startup_time ) > $Config{ZM_WATCH_MAX_DELAY} ) {
if (($now - $startup_time) > $Config{ZM_WATCH_MAX_DELAY}) {
Warning(
"Restarting capture daemon for $$monitor{Name}, no image since startup. ".
"Startup time was $startup_time - now $now > $Config{ZM_WATCH_MAX_DELAY}"
@ -122,7 +122,7 @@ while (!$zm_terminate) {
next;
}
}
if ( ! $restart ) {
if (!$restart) {
my $max_image_delay = (
$monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0)
@ -144,29 +144,28 @@ while (!$zm_terminate) {
$restart = 1;
}
if ( $restart ) {
if ($restart) {
my $command;
if ( $monitor->{Type} eq 'Local' ) {
$command = "zmdc.pl restart zmc -d $monitor->{Device}";
if ($monitor->{Type} eq 'Local') {
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
} else {
$command = "zmdc.pl restart zmc -m $monitor->{Id}";
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
}
runCommand($command);
} elsif ( $monitor->{Function} ne 'Monitor' ) {
} elsif ($monitor->{Function} ne 'Monitor') {
# Now check analysis daemon
$restart = 0;
# Check we have got an image recently
my $image_time = zmGetLastReadTime($monitor);
if ( !defined($image_time) ) {
if (!defined($image_time)) {
# Can't read from shared data
$restart = 1;
Error("Error reading shared data for $$monitor{Id} $$monitor{Name}");
} elsif ( !$image_time ) {
} elsif (!$image_time) {
# We can't get the last capture time so can't be sure it's died.
#$restart = 1;
Error("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.");
} else {
my $max_image_delay = ( $monitor->{MaxFPS}
&&($monitor->{MaxFPS}>0)
&&($monitor->{MaxFPS}<1)
@ -175,7 +174,7 @@ while (!$zm_terminate) {
;
my $image_delay = $now-$image_time;
Debug("Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay");
if ( $image_delay > $max_image_delay ) {
if ($image_delay > $max_image_delay) {
Warning("Analysis daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
." time since last analysis $image_delay seconds ($now-$image_time)"
);
@ -183,13 +182,13 @@ while (!$zm_terminate) {
}
}
if ( $restart ) {
Info("Restarting analysis daemon for $$monitor{Id} $$monitor{Name}\n");
if ($restart) {
Info("Restarting analysis daemon for $$monitor{Id} $$monitor{Name}");
my $command;
if ( $monitor->{Type} eq 'Local' ) {
$command = "zmdc.pl restart zmc -d $monitor->{Device}";
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
} else {
$command = "zmdc.pl restart zmc -m $monitor->{Id}";
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
}
runCommand($command);
} # end if restart
@ -201,7 +200,7 @@ while (!$zm_terminate) {
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
} # end while (!$zm_terminate)
Info("Watchdog exiting");
Info('Watchdog exiting');
exit();
1;

View File

@ -98,6 +98,12 @@ elseif(${ZM_JWT_BACKEND} STREQUAL "libjwt")
JWT::libjwt)
endif()
if(TARGET V4L2::videodev2)
target_link_libraries(zm
PRIVATE
V4L2::videodev2)
endif()
add_executable(zmc zmc.cpp)
add_executable(zms zms.cpp)
add_executable(zmu zmu.cpp)

View File

@ -74,11 +74,13 @@ int Buffer::read_into(int sd, unsigned int bytes) {
return bytes_read;
}
int Buffer::read_into(int sd, unsigned int bytes, struct timeval timeout) {
int Buffer::read_into(int sd, unsigned int bytes, Microseconds timeout) {
fd_set set;
FD_ZERO(&set); /* clear the set */
FD_SET(sd, &set); /* add our file descriptor to the set */
int rv = select(sd + 1, &set, NULL, NULL, &timeout);
timeval timeout_tv = zm::chrono::duration_cast<timeval>(timeout);
int rv = select(sd + 1, &set, nullptr, nullptr, &timeout_tv);
if (rv == -1) {
Error("Error %d %s from select", errno, strerror(errno));
return rv;
@ -86,5 +88,6 @@ int Buffer::read_into(int sd, unsigned int bytes, struct timeval timeout) {
Debug(1, "timeout"); /* a timeout occured */
return 0;
}
return read_into(sd, bytes);
}

View File

@ -21,6 +21,7 @@
#define ZM_BUFFER_H
#include "zm_logger.h"
#include "zm_time.h"
#include <cstring>
class Buffer {
@ -187,7 +188,7 @@ class Buffer {
return static_cast<int>(mSize);
}
int read_into(int sd, unsigned int bytes);
int read_into(int sd, unsigned int bytes, struct timeval timeout);
int read_into(int sd, unsigned int bytes, Microseconds timeout);
};
#endif // ZM_BUFFER_H

View File

@ -615,19 +615,7 @@ bool zm::TcpUnixServer::accept(TcpUnixSocket *&newSocket) {
return true;
}
void zm::Select::setTimeout(int timeout) {
mTimeout.tv_sec = timeout;
mTimeout.tv_usec = 0;
mHasTimeout = true;
}
void zm::Select::setTimeout(double timeout) {
mTimeout.tv_sec = int(timeout);
mTimeout.tv_usec = suseconds_t((timeout - mTimeout.tv_sec) * 1000000.0);
mHasTimeout = true;
}
void zm::Select::setTimeout(timeval timeout) {
void zm::Select::setTimeout(Microseconds timeout) {
mTimeout = timeout;
mHasTimeout = true;
}
@ -703,7 +691,7 @@ void zm::Select::clearWriters() {
}
int zm::Select::wait() {
timeval tempTimeout = mTimeout;
timeval tempTimeout = zm::chrono::duration_cast<timeval>(mTimeout);
timeval *selectTimeout = mHasTimeout ? &tempTimeout : nullptr;
fd_set rfds;

View File

@ -22,6 +22,7 @@
#include "zm_exception.h"
#include "zm_logger.h"
#include "zm_time.h"
#include <cerrno>
#include <netdb.h>
#include <set>
@ -560,13 +561,9 @@ class Select {
typedef std::vector<CommsBase *> CommsList;
Select() : mHasTimeout(false), mMaxFd(-1) {}
explicit Select(timeval timeout) : mMaxFd(-1) { setTimeout(timeout); }
explicit Select(int timeout) : mMaxFd(-1) { setTimeout(timeout); }
explicit Select(double timeout) : mMaxFd(-1) { setTimeout(timeout); }
explicit Select(Microseconds timeout) : mMaxFd(-1) { setTimeout(timeout); }
void setTimeout(int timeout);
void setTimeout(double timeout);
void setTimeout(timeval timeout);
void setTimeout(Microseconds timeout);
void clearTimeout();
void calcMaxFd();
@ -590,7 +587,7 @@ class Select {
CommsList mReadable;
CommsList mWriteable;
bool mHasTimeout;
timeval mTimeout;
Microseconds mTimeout;
int mMaxFd;
};

View File

@ -39,16 +39,15 @@ void zmLoadStaticConfig() {
// update the Config hash with those values
DIR *configSubFolder = opendir(ZM_CONFIG_SUBDIR);
if (configSubFolder) { // subfolder exists and is readable
char glob_pattern[PATH_MAX] = "";
snprintf(glob_pattern, sizeof(glob_pattern), "%s/*.conf", ZM_CONFIG_SUBDIR);
std::string glob_pattern = stringtf("%s/*.conf", ZM_CONFIG_SUBDIR);
glob_t pglob;
int glob_status = glob(glob_pattern, 0, nullptr, &pglob);
int glob_status = glob(glob_pattern.c_str(), 0, nullptr, &pglob);
if (glob_status != 0) {
if (glob_status < 0) {
Error("Can't glob '%s': %s", glob_pattern, strerror(errno));
Error("Can't glob '%s': %s", glob_pattern.c_str(), strerror(errno));
} else {
Debug(1, "Can't glob '%s': %d", glob_pattern, glob_status);
Debug(1, "Can't glob '%s': %d", glob_pattern.c_str(), glob_status);
}
} else {
for (unsigned int i = 0; i < pglob.gl_pathc; i++) {
@ -75,7 +74,7 @@ void zmLoadDBConfig() {
std::string sql = stringtf("SELECT `Id` FROM `Servers` WHERE `Name`='%s'",
staticConfig.SERVER_NAME.c_str());
zmDbRow dbrow;
if (dbrow.fetch(sql.c_str())) {
if (dbrow.fetch(sql)) {
staticConfig.SERVER_ID = atoi(dbrow[0]);
} else {
Fatal("Can't get ServerId for Server %s", staticConfig.SERVER_NAME.c_str());
@ -87,7 +86,7 @@ void zmLoadDBConfig() {
std::string sql = stringtf("SELECT `Name` FROM `Servers` WHERE `Id`='%d'", staticConfig.SERVER_ID);
zmDbRow dbrow;
if (dbrow.fetch(sql.c_str())) {
if (dbrow.fetch(sql)) {
staticConfig.SERVER_NAME = std::string(dbrow[0]);
} else {
Fatal("Can't get ServerName for Server ID %d", staticConfig.SERVER_ID);
@ -100,13 +99,10 @@ void zmLoadDBConfig() {
}
}
snprintf(staticConfig.capture_file_format, sizeof(staticConfig.capture_file_format), "%%s/%%0%dd-capture.jpg",
config.event_image_digits);
snprintf(staticConfig.analyse_file_format, sizeof(staticConfig.analyse_file_format), "%%s/%%0%dd-analyse.jpg",
config.event_image_digits);
snprintf(staticConfig.general_file_format, sizeof(staticConfig.general_file_format), "%%s/%%0%dd-%%s",
config.event_image_digits);
snprintf(staticConfig.video_file_format, sizeof(staticConfig.video_file_format), "%%s/%%s");
staticConfig.capture_file_format = stringtf("%%s/%%0%dd-capture.jpg", config.event_image_digits);
staticConfig.analyse_file_format = stringtf("%%s/%%0%dd-analyse.jpg", config.event_image_digits);
staticConfig.general_file_format = stringtf("%%s/%%0%dd-%%s", config.event_image_digits);
staticConfig.video_file_format = "%s/%s";
}
void process_configfile(char const *configFile) {

View File

@ -25,10 +25,6 @@
#include "zm_config_defines.h"
#include <string>
#if !defined(PATH_MAX)
#define PATH_MAX 1024
#endif
#define ZM_MAX_IMAGE_WIDTH 2048 // The largest image we imagine ever handling
#define ZM_MAX_IMAGE_HEIGHT 1536 // The largest image we imagine ever handling
#define ZM_MAX_IMAGE_COLOURS 4 // The largest image we imagine ever handling
@ -38,11 +34,6 @@
#define ZM_SCALE_BASE 100 // The factor by which we bump up 'scale' to simulate FP
#define ZM_RATE_BASE 100 // The factor by which we bump up 'rate' to simulate FP
#define ZM_SQL_BATCH_SIZE 50 // Limit the size of multi-row SQL statements
#define ZM_SQL_SML_BUFSIZ 256 // Size of SQL buffer
#define ZM_SQL_MED_BUFSIZ 1024 // Size of SQL buffer
#define ZM_SQL_LGE_BUFSIZ 8192 // Size of SQL buffer
#define ZM_NETWORK_BUFSIZ 32768 // Size of network buffer
#define ZM_MAX_FPS 30 // The maximum frame rate we expect to handle
@ -74,10 +65,10 @@ struct StaticConfig {
std::string PATH_LOGS;
std::string PATH_SWAP;
std::string PATH_ARP;
char capture_file_format[PATH_MAX];
char analyse_file_format[PATH_MAX];
char general_file_format[PATH_MAX];
char video_file_format[PATH_MAX];
std::string capture_file_format;
std::string analyse_file_format;
std::string general_file_format;
std::string video_file_format;
};
extern StaticConfig staticConfig;

View File

@ -1,17 +1,17 @@
//
// ZoneMinder cURL Camera Class Implementation, $Date: 2009-01-16 12:18:50 +0000 (Fri, 16 Jan 2009) $, $Revision: 2713 $
// Copyright (C) 2001-2008 Philip Coombes
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
@ -73,19 +73,35 @@ void bind_libcurl_symbols() {
*(void**) (&curl_easy_cleanup_f) = dlsym(curl_lib, "curl_easy_cleanup");
}
cURLCamera::cURLCamera( const Monitor* monitor, const std::string &p_path, const std::string &p_user, const std::string &p_pass, unsigned int p_width, unsigned int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
Camera( monitor, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET )
cURLCamera::cURLCamera(
const Monitor* monitor,
const std::string &p_path,
const std::string &p_user,
const std::string &p_pass,
unsigned int p_width,
unsigned int p_height,
int p_colours,
int p_brightness,
int p_contrast,
int p_hue,
int p_colour,
bool p_capture,
bool p_record_audio) :
Camera(monitor, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio),
mPath(p_path),
mUser(p_user),
mPass(p_pass),
bTerminate(false),
bReset(false),
mode(MODE_UNSET)
{
if ( capture ) {
if (capture) {
Initialise();
}
}
cURLCamera::~cURLCamera() {
if ( capture ) {
if (capture) {
Terminate();
}
}
@ -95,40 +111,40 @@ void cURLCamera::Initialise() {
content_type_match_len = strlen(content_type_match);
databuffer.expand(CURL_BUFFER_INITIAL_SIZE);
bind_libcurl_symbols();
/* cURL initialization */
CURLcode cRet = (*curl_global_init_f)(CURL_GLOBAL_ALL);
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
Error("libcurl initialization failed: %s", (*curl_easy_strerror_f)(cRet));
dlclose(curl_lib);
return;
}
Debug(2,"libcurl version: %s", (*curl_version_f)());
Debug(2, "libcurl version: %s", (*curl_version_f)());
/* Create the shared data mutex */
int nRet = pthread_mutex_init(&shareddata_mutex, nullptr);
if(nRet != 0) {
if (nRet != 0) {
Error("Shared data mutex creation failed: %s",strerror(nRet));
return;
}
/* Create the data available condition variable */
nRet = pthread_cond_init(&data_available_cond, nullptr);
if(nRet != 0) {
if (nRet != 0) {
Error("Data available condition variable creation failed: %s",strerror(nRet));
return;
}
/* Create the request complete condition variable */
nRet = pthread_cond_init(&request_complete_cond, nullptr);
if(nRet != 0) {
if (nRet != 0) {
Error("Request complete condition variable creation failed: %s",strerror(nRet));
return;
}
/* Create the thread */
nRet = pthread_create(&thread, nullptr, thread_func_dispatcher, this);
if(nRet != 0) {
if (nRet != 0) {
Error("Thread creation failed: %s",strerror(nRet));
return;
}
@ -151,18 +167,19 @@ void cURLCamera::Terminate() {
/* cURL cleanup */
(*curl_global_cleanup_f)();
if(curl_lib)
if (curl_lib)
dlclose(curl_lib);
}
int cURLCamera::PrimeCapture() {
getVideoStream();
//Info( "Priming capture from %s", mPath.c_str() );
return 0;
return 1;
}
int cURLCamera::PreCapture() {
// Nothing to do here
return( 0 );
// Nothing to do here
return 1;
}
int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
@ -178,10 +195,9 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
/* Grab the mutex to ensure exclusive access to the shared data */
lock();
while ( !frameComplete ) {
while (!frameComplete) {
/* If the work thread did a reset, reset our local variables */
if ( bReset ) {
if (bReset) {
SubHeadersParsingComplete = false;
frame_content_length = 0;
frame_content_type.clear();
@ -189,40 +205,40 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
bReset = false;
}
if ( mode == MODE_UNSET ) {
if (mode == MODE_UNSET) {
/* Don't have a mode yet. Sleep while waiting for data */
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
if ( nRet != 0 ) {
if (nRet != 0) {
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
return -1;
}
}
if ( mode == MODE_STREAM ) {
if (mode == MODE_STREAM) {
/* Subheader parsing */
while( !SubHeadersParsingComplete && !need_more_data ) {
while (!SubHeadersParsingComplete && !need_more_data) {
size_t crlf_start, crlf_end, crlf_size;
std::string subheader;
/* Check if the buffer contains something */
if ( databuffer.empty() ) {
if (databuffer.empty()) {
/* Empty buffer, wait for data */
need_more_data = true;
break;
}
/* Find crlf start */
crlf_start = memcspn(reinterpret_cast<const char *>(databuffer.head()),"\r\n",databuffer.size());
if ( crlf_start == databuffer.size() ) {
if (crlf_start == databuffer.size()) {
/* Not found, wait for more data */
need_more_data = true;
break;
}
/* See if we have enough data for determining crlf length */
if ( databuffer.size() < crlf_start+5 ) {
if (databuffer.size() < crlf_start+5) {
/* Need more data */
need_more_data = true;
break;
@ -233,18 +249,22 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
crlf_size = (crlf_start + crlf_end) - crlf_start;
/* Is this the end of a previous stream? (This is just before the boundary) */
if ( crlf_start == 0 ) {
if (crlf_start == 0) {
databuffer.consume(crlf_size);
continue;
continue;
}
/* Check for invalid CRLF size */
if ( crlf_size > 4 ) {
if (crlf_size > 4) {
Error("Invalid CRLF length");
}
/* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */
if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) {
if (
(crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0)
||
(crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0)
) {
/* This is the last header */
SubHeadersParsingComplete = true;
}
@ -255,48 +275,56 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
/* Advance the buffer past this one */
databuffer.consume(crlf_start+crlf_size);
Debug(7,"Got subheader: %s",subheader.c_str());
Debug(7, "Got subheader: %s",subheader.c_str());
/* Find where the data in this header starts */
size_t subheader_data_start = subheader.rfind(' ');
if ( subheader_data_start == std::string::npos ) {
if (subheader_data_start == std::string::npos) {
subheader_data_start = subheader.find(':');
}
/* Extract the data into a string */
std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos);
Debug(8,"Got subheader data: %s",subheader_data.c_str());
Debug(8, "Got subheader data: %s", subheader_data.c_str());
/* Check the header */
if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) {
if (strncasecmp(subheader.c_str(), content_length_match, content_length_match_len) == 0) {
/* Found the content-length header */
frame_content_length = atoi(subheader_data.c_str());
Debug(6,"Got content-length subheader: %d",frame_content_length);
} else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) {
} else if (strncasecmp(subheader.c_str(), content_type_match, content_type_match_len) == 0) {
/* Found the content-type header */
frame_content_type = subheader_data;
Debug(6,"Got content-type subheader: %s",frame_content_type.c_str());
Debug(6,"Got content-type subheader: %s", frame_content_type.c_str());
}
}
/* Attempt to extract the frame */
if(!need_more_data) {
if(!SubHeadersParsingComplete) {
if (!need_more_data) {
if (!SubHeadersParsingComplete) {
/* We haven't parsed all headers yet */
need_more_data = true;
} else if ( ! frame_content_length ) {
} else if (!frame_content_length) {
/* Invalid frame */
Error("Invalid frame: invalid content length");
} else if ( frame_content_type != "image/jpeg" ) {
} else if (frame_content_type != "image/jpeg") {
/* Unsupported frame type */
Error("Unsupported frame: %s",frame_content_type.c_str());
} else if(frame_content_length > databuffer.size()) {
Error("Unsupported frame: %s", frame_content_type.c_str());
} else if (frame_content_length > databuffer.size()) {
/* Incomplete frame, wait for more data */
need_more_data = true;
} else {
/* All good. decode the image */
if (!zm_packet->image) {
Debug(4, "Allocating image");
zm_packet->image = new Image(width, height, colours, subpixelorder);
}
zm_packet->keyframe = 1;
zm_packet->codec_type = AVMEDIA_TYPE_VIDEO;
zm_packet->packet.stream_index = mVideoStreamId;
zm_packet->stream = mVideoStream;
zm_packet->image->DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder);
frameComplete = true;
}
@ -312,11 +340,19 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
need_more_data = false;
}
} else if (mode == MODE_SINGLE) {
} else if (mode == MODE_SINGLE) {
/* Check if we have anything */
if (!single_offsets.empty()) {
if ((single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front())) {
/* Extract frame */
if (!zm_packet->image) {
Debug(4, "Allocating image");
zm_packet->image = new Image(width, height, colours, subpixelorder);
}
zm_packet->keyframe = 1;
zm_packet->codec_type = AVMEDIA_TYPE_VIDEO;
zm_packet->packet.stream_index = mVideoStreamId;
zm_packet->stream = mVideoStream;
zm_packet->image->DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder);
single_offsets.pop_front();
frameComplete = true;
@ -345,15 +381,15 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
/* Release the mutex */
unlock();
if(!frameComplete)
if (!frameComplete)
return 0;
return 1;
}
int cURLCamera::PostCapture() {
// Nothing to do here
return( 0 );
// Nothing to do here
return 1;
}
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata) {
@ -364,7 +400,7 @@ size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *
/* Signal data available */
int nRet = pthread_cond_signal(&data_available_cond);
if ( nRet != 0 ) {
if (nRet != 0) {
Error("Failed signaling data available condition variable: %s",strerror(nRet));
unlock();
return -16;
@ -379,18 +415,18 @@ size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *
size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata) {
std::string header;
header.assign((const char*)buffer, size*nmemb);
Debug(4,"Got header: %s",header.c_str());
/* Check Content-Type header */
if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) {
Debug(4, "Got header: %s", header.c_str());
/* Check Content-Type header */
if (strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) {
size_t pos = header.find(';');
if(pos != std::string::npos) {
if (pos != std::string::npos) {
header.erase(pos, std::string::npos);
}
pos = header.rfind(' ');
if(pos == std::string::npos) {
if (pos == std::string::npos) {
pos = header.find(':');
}
@ -401,17 +437,17 @@ size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, voi
const char* multipart_match = "multipart/x-mixed-replace";
const char* image_jpeg_match = "image/jpeg";
if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) {
Debug(7,"Content type matched as multipart/x-mixed-replace");
if (strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) {
Debug(7, "Content type matched as multipart/x-mixed-replace");
mode = MODE_STREAM;
} else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) {
Debug(7,"Content type matched as image/jpeg");
} else if (strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) {
Debug(7, "Content type matched as image/jpeg");
mode = MODE_SINGLE;
}
unlock();
}
/* Return bytes processed */
return size*nmemb;
}
@ -421,7 +457,7 @@ void* cURLCamera::thread_func() {
double dSize;
c = (*curl_easy_init_f)();
if(c == nullptr) {
if (c == nullptr) {
dlclose(curl_lib);
Error("Failed getting easy handle from libcurl");
tRet = -51;
@ -431,99 +467,99 @@ void* cURLCamera::thread_func() {
CURLcode cRet;
/* Set URL */
cRet = (*curl_easy_setopt_f)(c, CURLOPT_URL, mPath.c_str());
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
Error("Failed setting libcurl URL: %s", (*curl_easy_strerror_f)(cRet));
tRet = -52;
return (void*)tRet;
}
/* Header callback */
cRet = (*curl_easy_setopt_f)(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher);
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
Error("Failed setting libcurl header callback function: %s", (*curl_easy_strerror_f)(cRet));
tRet = -53;
return (void*)tRet;
}
cRet = (*curl_easy_setopt_f)(c, CURLOPT_HEADERDATA, this);
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
Error("Failed setting libcurl header callback object: %s", (*curl_easy_strerror_f)(cRet));
tRet = -54;
return (void*)tRet;
}
/* Data callback */
cRet = (*curl_easy_setopt_f)(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher);
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
Error("Failed setting libcurl data callback function: %s", (*curl_easy_strerror_f)(cRet));
tRet = -55;
return (void*)tRet;
}
cRet = (*curl_easy_setopt_f)(c, CURLOPT_WRITEDATA, this);
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
Error("Failed setting libcurl data callback object: %s", (*curl_easy_strerror_f)(cRet));
tRet = -56;
return (void*)tRet;
}
/* Progress callback */
cRet = (*curl_easy_setopt_f)(c, CURLOPT_NOPROGRESS, 0);
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
Error("Failed enabling libcurl progress callback function: %s", (*curl_easy_strerror_f)(cRet));
tRet = -57;
return (void*)tRet;
}
cRet = (*curl_easy_setopt_f)(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher);
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
Error("Failed setting libcurl progress callback function: %s", (*curl_easy_strerror_f)(cRet));
tRet = -58;
return (void*)tRet;
}
cRet = (*curl_easy_setopt_f)(c, CURLOPT_PROGRESSDATA, this);
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
Error("Failed setting libcurl progress callback object: %s", (*curl_easy_strerror_f)(cRet));
tRet = -59;
return (void*)tRet;
}
/* Set username and password */
if(!mUser.empty()) {
if (!mUser.empty()) {
cRet = (*curl_easy_setopt_f)(c, CURLOPT_USERNAME, mUser.c_str());
if(cRet != CURLE_OK)
if (cRet != CURLE_OK)
Error("Failed setting username: %s", (*curl_easy_strerror_f)(cRet));
}
if(!mPass.empty()) {
if (!mPass.empty()) {
cRet = (*curl_easy_setopt_f)(c, CURLOPT_PASSWORD, mPass.c_str());
if(cRet != CURLE_OK)
if (cRet != CURLE_OK)
Error("Failed setting password: %s", (*curl_easy_strerror_f)(cRet));
}
/* Authenication preference */
cRet = (*curl_easy_setopt_f)(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
if(cRet != CURLE_OK)
if (cRet != CURLE_OK)
Warning("Failed setting libcurl acceptable http authenication methods: %s", (*curl_easy_strerror_f)(cRet));
/* Work loop */
for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) {
for (int attempt=1;attempt<=CURL_MAXRETRY;attempt++) {
tRet = 0;
while(!bTerminate) {
while (!bTerminate) {
/* Do the work */
cRet = (*curl_easy_perform_f)(c);
if(mode == MODE_SINGLE) {
if(cRet != CURLE_OK) {
if (mode == MODE_SINGLE) {
if (cRet != CURLE_OK) {
break;
}
/* Attempt to get the size of the file */
cRet = (*curl_easy_getinfo_f)(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize);
if(cRet != CURLE_OK) {
if (cRet != CURLE_OK) {
break;
}
/* We need to lock for the offsets array and the condition variable */
lock();
/* Push the size into our offsets array */
if(dSize > 0) {
if (dSize > 0) {
single_offsets.push_back(dSize);
} else {
Error("Unable to get the size of the image");
@ -532,7 +568,7 @@ void* cURLCamera::thread_func() {
}
/* Signal the request complete condition variable */
tRet = pthread_cond_signal(&request_complete_cond);
if(tRet != 0) {
if (tRet != 0) {
Error("Failed signaling request completed condition variable: %s",strerror(tRet));
tRet = -61;
return (void*)tRet;
@ -546,13 +582,13 @@ void* cURLCamera::thread_func() {
}
/* Return value checking */
if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) {
if (cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) {
/* Aborted */
break;
} else if (cRet != CURLE_OK) {
/* Some error */
Error("cURL Request failed: %s",(*curl_easy_strerror_f)(cRet));
if(attempt < CURL_MAXRETRY) {
if (attempt < CURL_MAXRETRY) {
Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY);
/* Do a reset */
lock();
@ -565,11 +601,11 @@ void* cURLCamera::thread_func() {
tRet = -50;
}
}
/* Cleanup */
(*curl_easy_cleanup_f)(c);
c = nullptr;
return (void*)tRet;
}
@ -597,9 +633,9 @@ int cURLCamera::unlock() {
int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) {
/* Signal the curl thread to terminate */
if(bTerminate)
if (bTerminate)
return -10;
return 0;
}

View File

@ -102,6 +102,7 @@ bool zmDbConnect() {
if ( mysql_query(&dbconn, "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED") ) {
Error("Can't set isolation level: %s", mysql_error(&dbconn));
}
mysql_set_character_set(&dbconn, "utf8");
zmDbConnected = true;
return zmDbConnected;
}
@ -117,25 +118,25 @@ void zmDbClose() {
}
}
MYSQL_RES * zmDbFetch(const char * query) {
MYSQL_RES *zmDbFetch(const std::string &query) {
std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected) {
Error("Not connected.");
return nullptr;
}
if (mysql_query(&dbconn, query)) {
if (mysql_query(&dbconn, query.c_str())) {
Error("Can't run query: %s", mysql_error(&dbconn));
return nullptr;
}
MYSQL_RES *result = mysql_store_result(&dbconn);
if (!result) {
Error("Can't use query result: %s for query %s", mysql_error(&dbconn), query);
Error("Can't use query result: %s for query %s", mysql_error(&dbconn), query.c_str());
}
return result;
} // end MYSQL_RES * zmDbFetch(const char * query);
}
zmDbRow *zmDbFetchOne(const char *query) {
zmDbRow *zmDbFetchOne(const std::string &query) {
zmDbRow *row = new zmDbRow();
if (row->fetch(query)) {
return row;
@ -144,13 +145,13 @@ zmDbRow *zmDbFetchOne(const char *query) {
return nullptr;
}
MYSQL_RES *zmDbRow::fetch(const char *query) {
MYSQL_RES *zmDbRow::fetch(const std::string &query) {
result_set = zmDbFetch(query);
if (!result_set) return result_set;
int n_rows = mysql_num_rows(result_set);
if (n_rows != 1) {
Error("Bogus number of lines return from query, %d returned for query %s.", n_rows, query);
Error("Bogus number of lines return from query, %d returned for query %s.", n_rows, query.c_str());
mysql_free_result(result_set);
result_set = nullptr;
return result_set;
@ -160,23 +161,23 @@ MYSQL_RES *zmDbRow::fetch(const char *query) {
if (!row) {
mysql_free_result(result_set);
result_set = nullptr;
Error("Error getting row from query %s. Error is %s", query, mysql_error(&dbconn));
Error("Error getting row from query %s. Error is %s", query.c_str(), mysql_error(&dbconn));
} else {
Debug(5, "Success");
}
return result_set;
}
int zmDbDo(const char *query) {
int zmDbDo(const std::string &query) {
std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected)
return 0;
int rc;
while ((rc = mysql_query(&dbconn, query)) and !zm_terminate) {
while ((rc = mysql_query(&dbconn, query.c_str())) and !zm_terminate) {
Logger *logger = Logger::fetch();
Logger::Level oldLevel = logger->databaseLevel();
logger->databaseLevel(Logger::NOLOG);
Error("Can't run query %s: %s", query, mysql_error(&dbconn));
Error("Can't run query %s: %s", query.c_str(), mysql_error(&dbconn));
logger->databaseLevel(oldLevel);
if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) ) {
return rc;
@ -186,36 +187,36 @@ int zmDbDo(const char *query) {
Logger::Level oldLevel = logger->databaseLevel();
logger->databaseLevel(Logger::NOLOG);
Debug(1, "Success running sql query %s", query);
Debug(1, "Success running sql query %s", query.c_str());
logger->databaseLevel(oldLevel);
return 1;
}
int zmDbDoInsert(const char *query) {
int zmDbDoInsert(const std::string &query) {
std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected) return 0;
int rc;
while ( (rc = mysql_query(&dbconn, query)) and !zm_terminate) {
Error("Can't run query %s: %s", query, mysql_error(&dbconn));
while ( (rc = mysql_query(&dbconn, query.c_str())) and !zm_terminate) {
Error("Can't run query %s: %s", query.c_str(), mysql_error(&dbconn));
if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) )
return 0;
}
int id = mysql_insert_id(&dbconn);
Debug(2, "Success running sql insert %s. Resulting id is %d", query, id);
Debug(2, "Success running sql insert %s. Resulting id is %d", query.c_str(), id);
return id;
}
int zmDbDoUpdate(const char *query) {
int zmDbDoUpdate(const std::string &query) {
std::lock_guard<std::mutex> lck(db_mutex);
if (!zmDbConnected) return 0;
int rc;
while ( (rc = mysql_query(&dbconn, query)) and !zm_terminate) {
Error("Can't run query %s: %s", query, mysql_error(&dbconn));
while ( (rc = mysql_query(&dbconn, query.c_str())) and !zm_terminate) {
Error("Can't run query %s: %s", query.c_str(), mysql_error(&dbconn));
if ( (mysql_errno(&dbconn) != ER_LOCK_WAIT_TIMEOUT) )
return -rc;
}
int affected = mysql_affected_rows(&dbconn);
Debug(2, "Success running sql update %s. Rows modified %d", query, affected);
Debug(2, "Success running sql update %s. Rows modified %d", query.c_str(), affected);
return affected;
}
@ -255,7 +256,7 @@ void zmDbQueue::process() {
// My idea for leaving the locking around each sql statement is to allow
// other db writers to get a chance
lock.unlock();
zmDbDo(sql.c_str());
zmDbDo(sql);
lock.lock();
}
}
@ -267,3 +268,15 @@ void zmDbQueue::push(std::string &&sql) {
mQueue.push(std::move(sql));
mCondition.notify_all();
}
std::string zmDbEscapeString(const std::string& to_escape) {
// According to docs, size of safer_whatever must be 2 * length + 1
// due to unicode conversions + null terminator.
std::string escaped((to_escape.length() * 2) + 1, '\0');
size_t escaped_len = mysql_real_escape_string(&dbconn, &escaped[0], to_escape.c_str(), to_escape.length());
escaped.resize(escaped_len);
return escaped;
}

View File

@ -38,7 +38,6 @@ class zmDbQueue {
public:
zmDbQueue();
~zmDbQueue();
void push(const char *sql) { return push(std::string(sql)); };
void push(std::string &&sql);
void process();
void stop();
@ -50,7 +49,7 @@ class zmDbRow {
MYSQL_ROW row;
public:
zmDbRow() : result_set(nullptr), row(nullptr) { };
MYSQL_RES *fetch(const char *query);
MYSQL_RES *fetch(const std::string &query);
zmDbRow(MYSQL_RES *, MYSQL_ROW *row);
~zmDbRow();
@ -67,15 +66,15 @@ extern zmDbQueue dbQueue;
extern bool zmDbConnected;
extern bool zmDbConnected;
bool zmDbConnect();
void zmDbClose();
int zmDbDo(const char *query);
int zmDbDoInsert(const char *query);
int zmDbDoUpdate(const char *query);
int zmDbDo(const std::string &query);
int zmDbDoInsert(const std::string &query);
int zmDbDoUpdate(const std::string &query);
MYSQL_RES * zmDbFetch(const char *query);
zmDbRow *zmDbFetchOne(const char *query);
MYSQL_RES * zmDbFetch(const std::string &query);
zmDbRow *zmDbFetchOne(const std::string &query);
std::string zmDbEscapeString(const std::string& to_escape);
#endif // ZM_DB_H

View File

@ -32,8 +32,6 @@
#include <sys/stat.h>
#include <unistd.h>
//#define USE_PREPARED_SQL 1
const char * Event::frame_type_names[3] = { "Normal", "Bulk", "Alarm" };
#define MAX_DB_FRAMES 100
@ -43,13 +41,13 @@ Event::PreAlarmData Event::pre_alarm_data[MAX_PRE_ALARM_FRAMES] = {};
Event::Event(
Monitor *p_monitor,
struct timeval p_start_time,
SystemTimePoint p_start_time,
const std::string &p_cause,
const StringSetMap &p_noteSetMap
) :
id(0),
monitor(p_monitor),
start_time(SystemTimePoint(zm::chrono::duration_cast<Microseconds>(p_start_time))),
start_time(p_start_time),
end_time(),
cause(p_cause),
noteSetMap(p_noteSetMap),
@ -111,7 +109,7 @@ Event::Event(
"INSERT INTO `Events` "
"( `MonitorId`, `StorageId`, `Name`, `StartDateTime`, `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' )",
"( %d, %d, 'New Event', from_unixtime(%" PRId64 "), %u, %u, '%s', '%s', %d, %d, %d, '%s', %d, '%s' )",
monitor->Id(),
storage->Id(),
static_cast<int64>(std::chrono::system_clock::to_time_t(start_time)),
@ -122,12 +120,11 @@ Event::Event(
state_id,
monitor->getOrientation(),
0,
"",
"",
save_jpegs,
storage->SchemeString().c_str()
);
id = zmDbDoInsert(sql.c_str());
id = zmDbDoInsert(sql);
if ( !SetPath(storage) ) {
// Try another
@ -140,7 +137,7 @@ Event::Event(
Debug(1, "%s", sql.c_str());
storage = nullptr;
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if ( result ) {
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
storage = new Storage(atoi(dbrow[0]));
@ -159,7 +156,7 @@ Event::Event(
if ( monitor->ServerId() )
sql += stringtf(" OR ServerId != %u", monitor->ServerId());
result = zmDbFetch(sql.c_str());
result = zmDbFetch(sql);
if ( result ) {
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
storage = new Storage(atoi(dbrow[0]));
@ -177,7 +174,7 @@ Event::Event(
Warning("Failed to find a storage area to save events.");
}
sql = stringtf("UPDATE Events SET StorageId = '%d' WHERE Id=%" PRIu64, storage->Id(), id);
zmDbDo(sql.c_str());
zmDbDo(sql);
} // end if ! setPath(Storage)
Debug(1, "Using storage area at %s", path.c_str());
@ -213,14 +210,14 @@ Event::Event(
if ( ! ( save_jpegs & 1 ) ) {
save_jpegs |= 1; // Turn on jpeg storage
sql = stringtf("UPDATE Events SET SaveJpegs=%d WHERE Id=%" PRIu64, save_jpegs, id);
zmDbDo(sql.c_str());
zmDbDo(sql);
}
} else {
sql = stringtf("UPDATE Events SET Videoed=1, DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id);
zmDbDo(sql.c_str());
zmDbDo(sql);
}
} // end if GetOptVideoWriter
} // 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.
@ -255,7 +252,7 @@ Event::~Event() {
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
id);
if (!zmDbDoUpdate(sql.c_str())) {
if (!zmDbDoUpdate(sql)) {
// Name might have been changed during recording, so just do the update without changing the name.
sql = stringtf(
"UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64,
@ -264,7 +261,7 @@ Event::~Event() {
frames, alarm_frames,
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
id);
zmDbDoUpdate(sql.c_str());
zmDbDoUpdate(sql);
} // end if no changed rows due to Name change during recording
} // Event::~Event()
@ -282,20 +279,14 @@ void Event::createNotes(std::string &notes) {
}
} // void Event::createNotes(std::string &notes)
bool Event::WriteFrameImage(
Image *image,
timeval timestamp,
const char *event_file,
bool alarm_frame) const {
bool Event::WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame) const {
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
bool rc;
SystemTimePoint jpeg_timestamp =
monitor->Exif() ? SystemTimePoint(zm::chrono::duration_cast<Microseconds>(timestamp)) : SystemTimePoint();
SystemTimePoint jpeg_timestamp = monitor->Exif() ? timestamp : SystemTimePoint();
if (!config.timestamp_on_capture) {
// stash the image we plan to use in another pointer regardless if timestamped.
@ -309,7 +300,7 @@ bool Event::WriteFrameImage(
}
return rc;
} // end Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame )
}
bool Event::WritePacket(const std::shared_ptr<ZMPacket>&packet) {
if (videoStore->writePacket(packet) < 0)
@ -357,65 +348,15 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
} // end if have old notes
} // end if have new notes
if ( update ) {
if (update) {
std::string notes;
createNotes(notes);
Debug(2, "Updating notes for event %" PRIu64 ", '%s'", id, notes.c_str());
#if USE_PREPARED_SQL
static MYSQL_STMT *stmt = 0;
char notesStr[ZM_SQL_MED_BUFSIZ] = "";
unsigned long notesLen = 0;
if ( !stmt ) {
const char *sql = "UPDATE `Events` SET `Notes` = ? WHERE `Id` = ?";
stmt = mysql_stmt_init(&dbconn);
if ( mysql_stmt_prepare(stmt, sql, strlen(sql)) ) {
Fatal("Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt));
}
/* Get the parameter count from the statement */
if ( mysql_stmt_param_count(stmt) != 2 ) {
Error("Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count(stmt), sql);
}
MYSQL_BIND bind[2];
memset(bind, 0, sizeof(bind));
/* STRING PARAM */
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = (char *)notesStr;
bind[0].buffer_length = sizeof(notesStr);
bind[0].is_null = 0;
bind[0].length = &notesLen;
bind[1].buffer_type= MYSQL_TYPE_LONG;
bind[1].buffer= (char *)&id;
bind[1].is_null= 0;
bind[1].length= 0;
/* Bind the buffers */
if ( mysql_stmt_bind_param(stmt, bind) ) {
Error("Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt));
}
} // end if ! stmt
strncpy(notesStr, notes.c_str(), sizeof(notesStr));
if ( mysql_stmt_execute(stmt) ) {
Error("Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt));
}
#else
char sql[ZM_SQL_LGE_BUFSIZ];
static char escapedNotes[ZM_SQL_MED_BUFSIZ];
mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length());
snprintf(sql, sizeof(sql), "UPDATE `Events` SET `Notes` = '%s' WHERE `Id` = %" PRIu64, escapedNotes, id);
std::string sql = stringtf("UPDATE `Events` SET `Notes` = '%s' WHERE `Id` = %" PRIu64,
zmDbEscapeString(notes).c_str(), id);
dbQueue.push(std::move(sql));
#endif
} // end if update
} // void Event::updateNotes(const StringSetMap &newNoteSetMap)
@ -439,7 +380,7 @@ void Event::AddPacket(const std::shared_ptr<ZMPacket>&packet) {
if ((packet->codec_type == AVMEDIA_TYPE_VIDEO) or packet->image) {
AddFrame(packet->image, packet->timestamp, packet->zone_stats, packet->score, packet->analysis_image);
}
end_time = SystemTimePoint(zm::chrono::duration_cast<Microseconds>(packet->timestamp));
end_time = packet->timestamp;
}
void Event::WriteDbFrames() {
@ -456,7 +397,7 @@ void Event::WriteDbFrames() {
frame_insert_sql += stringtf("\n( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %.2f, %d ),",
id, frame->frame_id,
frame_type_names[frame->type],
frame->timestamp.tv_sec,
std::chrono::system_clock::to_time_t(frame->timestamp),
std::chrono::duration_cast<FPSeconds>(frame->delta).count(),
frame->score);
if (config.record_event_stats and frame->zone_stats.size()) {
@ -483,7 +424,7 @@ void Event::WriteDbFrames() {
} // end while frames
// The -1 is for the extra , added for values above
frame_insert_sql.erase(frame_insert_sql.size()-1);
//zmDbDo(frame_insert_sql.c_str());
//zmDbDo(frame_insert_sql);
dbQueue.push(std::move(frame_insert_sql));
if (stats_insert_sql.size() > 208) {
// The -1 is for the extra , added for values above
@ -493,13 +434,12 @@ void Event::WriteDbFrames() {
}
} // end void Event::WriteDbFrames()
void Event::AddFrame(
Image *image,
struct timeval timestamp,
const std::vector<ZoneStats> &zone_stats,
int score,
Image *alarm_image) {
if (!timestamp.tv_sec) {
void Event::AddFrame(Image *image,
SystemTimePoint timestamp,
const std::vector<ZoneStats> &zone_stats,
int score,
Image *alarm_image) {
if (timestamp.time_since_epoch() == Seconds(0)) {
Warning("Not adding new frame, zero timestamp");
return;
}
@ -525,7 +465,7 @@ void Event::AddFrame(
if (image) {
if (save_jpegs & 1) {
std::string event_file = stringtf(staticConfig.capture_file_format, path.c_str(), frames);
std::string event_file = stringtf(staticConfig.capture_file_format.c_str(), 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");
@ -555,7 +495,7 @@ void Event::AddFrame(
}
if (alarm_image and (save_jpegs & 2)) {
std::string event_file = stringtf(staticConfig.analyse_file_format, path.c_str(), frames);
std::string event_file = stringtf(staticConfig.analyse_file_format.c_str(), path.c_str(), frames);
Debug(1, "Writing analysis frame %d", frames);
if (!WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true)) {
Error("Failed to write analysis frame image");
@ -576,12 +516,10 @@ void Event::AddFrame(
or ( monitor_state == Monitor::ALARM )
or ( monitor_state == Monitor::PREALARM );
SystemTimePoint timestamp_us = SystemTimePoint(zm::chrono::duration_cast<Microseconds>(timestamp));
if (db_frame) {
Microseconds delta_time = std::chrono::duration_cast<Microseconds>(timestamp_us - start_time);
Microseconds delta_time = std::chrono::duration_cast<Microseconds>(timestamp - start_time);
Debug(1, "Frame delta is %.2f s - %.2f s = %.2f s, score %u zone_stats.size %zu",
FPSeconds(timestamp_us.time_since_epoch()).count(),
FPSeconds(timestamp.time_since_epoch()).count(),
FPSeconds(start_time.time_since_epoch()).count(),
FPSeconds(delta_time).count(),
score,
@ -621,8 +559,8 @@ void Event::AddFrame(
if (score > (int) max_score) {
max_score = score;
}
end_time = timestamp_us;
} // end void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image)
end_time = timestamp;
}
bool Event::SetPath(Storage *storage) {
scheme = storage->Scheme();

View File

@ -24,6 +24,7 @@
#include "zm_define.h"
#include "zm_storage.h"
#include "zm_time.h"
#include "zm_utils.h"
#include "zm_zone.h"
#include <map>
@ -96,12 +97,10 @@ class Event {
static bool OpenFrameSocket(int);
static bool ValidateFrameSocket(int);
Event(
Monitor *p_monitor,
struct timeval p_start_time,
const std::string &p_cause,
const StringSetMap &p_noteSetMap
);
Event(Monitor *p_monitor,
SystemTimePoint p_start_time,
const std::string &p_cause,
const StringSetMap &p_noteSetMap);
~Event();
uint64_t Id() const { return id; }
@ -109,46 +108,38 @@ class Event {
int Frames() const { return frames; }
int AlarmFrames() const { return alarm_frames; }
timeval StartTime() const { return zm::chrono::duration_cast<timeval>(start_time.time_since_epoch()); }
timeval EndTime() const { return zm::chrono::duration_cast<timeval>(end_time.time_since_epoch()); }
SystemTimePoint StartTime() const { return start_time; }
SystemTimePoint EndTime() const { return end_time; }
void AddPacket(const std::shared_ptr<ZMPacket> &p);
bool WritePacket(const std::shared_ptr<ZMPacket> &p);
bool SendFrameImage(const Image *image, bool alarm_frame=false);
bool WriteFrameImage(
Image *image,
struct timeval timestamp,
const char *event_file,
bool alarm_frame=false
) const;
bool WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame = false) const;
void updateNotes(const StringSetMap &stringSetMap);
void AddFrame(
Image *image,
struct timeval timestamp,
const std::vector<ZoneStats> &stats,
int score=0,
Image *alarm_image=nullptr
);
void AddFrame(Image *image,
SystemTimePoint timestamp,
const std::vector<ZoneStats> &stats,
int score = 0,
Image *alarm_image = nullptr);
private:
void WriteDbFrames();
bool SetPath(Storage *storage);
public:
static const char *getSubPath(tm time) {
static char subpath[PATH_MAX] = "";
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d",
time.tm_year-100, time.tm_mon+1, time.tm_mday,
time.tm_hour, time.tm_min, time.tm_sec);
return subpath;
}
static const char *getSubPath(time_t *time) {
tm time_tm = {};
localtime_r(time, &time_tm);
return Event::getSubPath(time_tm);
}
static std::string getSubPath(tm time) {
std::string subpath = stringtf("%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 std::string getSubPath(time_t *time) {
tm time_tm = {};
localtime_r(time, &time_tm);
return Event::getSubPath(time_tm);
}
const char* getEventFile() const {
return video_file.c_str();
@ -174,7 +165,7 @@ class Event {
}
static void AddPreAlarmFrame(
Image *image,
struct timeval timestamp,
SystemTimePoint timestamp,
int score=0,
Image *alarm_frame=nullptr
) {

View File

@ -44,9 +44,9 @@ constexpr Milliseconds EventStream::STREAM_PAUSE_WAIT;
bool EventStream::loadInitialEventData(int monitor_id, SystemTimePoint event_time) {
std::string sql = stringtf("SELECT `Id` FROM `Events` WHERE "
"`MonitorId` = %d AND unix_timestamp(`EndDateTime`) > %ld "
"ORDER BY `Id` ASC LIMIT 1", monitor_id, event_time);
"ORDER BY `Id` ASC LIMIT 1", monitor_id, std::chrono::system_clock::to_time_t(event_time));
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result)
exit(-1);
@ -117,7 +117,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS FramesDuration, "
"`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id);
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result) {
exit(-1);
}
@ -145,7 +145,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
event_data->duration = std::chrono::duration_cast<Microseconds>(event_data->end_time - event_data->start_time);
event_data->frames_duration =
std::chrono::duration_cast<Microseconds>(dbrow[5] ? FPSeconds(atof(dbrow[5])) : FPSeconds(0.0));
strncpy(event_data->video_file, dbrow[6], sizeof(event_data->video_file) - 1);
event_data->video_file = std::string(dbrow[6]);
std::string scheme_str = std::string(dbrow[7]);
if ( scheme_str == "Deep" ) {
event_data->scheme = Storage::DEEP;
@ -180,44 +180,41 @@ bool EventStream::loadEventData(uint64_t event_id) {
time_t start_time_t = std::chrono::system_clock::to_time_t(event_data->start_time);
localtime_r(&start_time_t, &event_time);
if ( storage_path[0] == '/' )
snprintf(event_data->path, sizeof(event_data->path),
"%s/%u/%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/%u/%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);
} else if ( event_data->scheme == Storage::MEDIUM ) {
if (storage_path[0] == '/') {
event_data->path = stringtf("%s/%u/%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 {
event_data->path = stringtf("%s/%s/%u/%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);
}
} else if (event_data->scheme == Storage::MEDIUM) {
tm event_time = {};
time_t start_time_t = std::chrono::system_clock::to_time_t(event_data->start_time);
localtime_r(&start_time_t, &event_time);
if ( storage_path[0] == '/' )
snprintf(event_data->path, sizeof(event_data->path),
"%s/%u/%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/%u/%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);
if (storage_path[0] == '/') {
event_data->path = stringtf("%s/%u/%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 {
event_data->path = stringtf("%s/%s/%u/%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/%u/%" PRIu64,
storage_path, event_data->monitor_id, event_data->event_id);
else
snprintf(event_data->path, sizeof(event_data->path), "%s/%s/%u/%" PRIu64,
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
event_data->event_id);
if (storage_path[0] == '/') {
event_data->path = stringtf("%s/%u/%" PRIu64, storage_path, event_data->monitor_id, event_data->event_id);
} else {
event_data->path = stringtf("%s/%s/%u/%" PRIu64,
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
event_data->event_id);
}
}
double fps = 1.0;
@ -229,7 +226,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
sql = stringtf("SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` "
"FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id);
result = zmDbFetch(sql.c_str());
result = zmDbFetch(sql);
if (!result) {
exit(-1);
}
@ -289,17 +286,17 @@ bool EventStream::loadEventData(uint64_t event_id) {
}
mysql_free_result(result);
if ( event_data->video_file[0] || (monitor->GetOptVideoWriter() > 0) ) {
if ( !event_data->video_file[0] ) {
snprintf(event_data->video_file, sizeof(event_data->video_file), "%" PRIu64 "-%s", event_data->event_id, "video.mp4");
if (!event_data->video_file.empty() || (monitor->GetOptVideoWriter() > 0)) {
if (event_data->video_file.empty()) {
event_data->video_file = stringtf("%" PRIu64 "-%s", event_data->event_id, "video.mp4");
}
std::string filepath = std::string(event_data->path) + "/" + std::string(event_data->video_file);
std::string filepath = event_data->path + "/" + event_data->video_file;
Debug(1, "Loading video file from %s", filepath.c_str());
if ( ffmpeg_input )
delete ffmpeg_input;
delete ffmpeg_input;
ffmpeg_input = new FFmpeg_Input();
if ( 0 > ffmpeg_input->Open(filepath.c_str()) ) {
if (ffmpeg_input->Open(filepath.c_str()) < 0) {
Warning("Unable to open ffmpeg_input %s", filepath.c_str());
delete ffmpeg_input;
ffmpeg_input = nullptr;
@ -640,7 +637,7 @@ bool EventStream::checkEventLoaded() {
if ( forceEventChange || ( (mode != MODE_SINGLE) && (mode != MODE_NONE) ) ) {
Debug(1, "Checking for next event %s", sql.c_str());
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result) {
exit(-1);
}
@ -690,32 +687,30 @@ bool EventStream::checkEventLoaded() {
} // void EventStream::checkEventLoaded()
Image * EventStream::getImage( ) {
static char filepath[PATH_MAX];
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
Debug(2, "EventStream::getImage path(%s) from %s frame(%ld) ", filepath, event_data->path, curr_frame_id);
Image *image = new Image(filepath);
std::string path = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
Debug(2, "EventStream::getImage path(%s) from %s frame(%ld) ", path.c_str(), event_data->path.c_str(), curr_frame_id);
Image *image = new Image(path.c_str());
return image;
}
bool EventStream::sendFrame(Microseconds delta_us) {
Debug(2, "Sending frame %ld", curr_frame_id);
static char filepath[PATH_MAX];
static struct stat filestat;
std::string filepath;
struct stat filestat = {};
// This needs to be abstracted. If we are saving jpgs, then load the capture file.
// If we are only saving analysis frames, then send that.
if ( event_data->SaveJPEGs & 1 ) {
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
} else if ( event_data->SaveJPEGs & 2 ) {
snprintf(filepath, sizeof(filepath), staticConfig.analyse_file_format, event_data->path, curr_frame_id);
if ( stat(filepath, &filestat) < 0 ) {
Debug(1, "analyze file %s not found will try to stream from other", filepath);
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
if ( stat(filepath, &filestat) < 0 ) {
Debug(1, "capture file %s not found either", filepath);
filepath[0] = 0;
if (event_data->SaveJPEGs & 1) {
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
} else if (event_data->SaveJPEGs & 2) {
filepath = stringtf(staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
if (stat(filepath.c_str(), &filestat) < 0) {
Debug(1, "analyze file %s not found will try to stream from other", filepath.c_str());
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
if (stat(filepath.c_str(), &filestat) < 0) {
Debug(1, "capture file %s not found either", filepath.c_str());
filepath = "";
}
}
} else if ( !ffmpeg_input ) {
@ -724,7 +719,7 @@ bool EventStream::sendFrame(Microseconds delta_us) {
}
if ( type == STREAM_MPEG ) {
Image image(filepath);
Image image(filepath.c_str());
Image *send_image = prepareImage(&image);
@ -739,20 +734,20 @@ bool EventStream::sendFrame(Microseconds delta_us) {
config.mpeg_timed_frames,
delta_us.count() * 1000);
} else {
bool send_raw = (type == STREAM_JPEG) && ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)) && filepath[0];
bool send_raw = (type == STREAM_JPEG) && ((scale >= ZM_SCALE_BASE) && (zoom == ZM_SCALE_BASE)) && !filepath.empty();
fprintf(stdout, "--" BOUNDARY "\r\n");
if ( send_raw ) {
if ( !send_file(filepath) ) {
Error("Can't send %s: %s", filepath, strerror(errno));
if (send_raw) {
if (!send_file(filepath)) {
Error("Can't send %s: %s", filepath.c_str(), strerror(errno));
return false;
}
} else {
Image *image = nullptr;
if ( filepath[0] ) {
image = new Image(filepath);
if (!filepath.empty()) {
image = new Image(filepath.c_str());
} else if ( ffmpeg_input ) {
// Get the frame from the mp4 input
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
@ -1088,24 +1083,24 @@ void EventStream::runStream() {
closeComms();
} // end void EventStream::runStream()
bool EventStream::send_file(const char *filepath) {
bool EventStream::send_file(const std::string &filepath) {
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
int img_buffer_size = 0;
uint8_t *img_buffer = temp_img_buffer;
FILE *fdj = nullptr;
fdj = fopen(filepath, "rb");
fdj = fopen(filepath.c_str(), "rb");
if ( !fdj ) {
Error("Can't open %s: %s", filepath, strerror(errno));
std::string error_message = stringtf("Can't open %s: %s", filepath, strerror(errno));
Error("Can't open %s: %s", filepath.c_str(), strerror(errno));
std::string error_message = stringtf("Can't open %s: %s", filepath.c_str(), strerror(errno));
return sendTextFrame(error_message.c_str());
}
#if HAVE_SENDFILE
static struct stat filestat;
if ( fstat(fileno(fdj), &filestat) < 0 ) {
fclose(fdj); /* Close the file handle */
Error("Failed getting information about file %s: %s", filepath, strerror(errno));
Error("Failed getting information about file %s: %s", filepath.c_str(), strerror(errno));
return false;
}
if ( !filestat.st_size ) {
@ -1134,7 +1129,7 @@ bool EventStream::send_file(const char *filepath) {
}
return send_buffer(img_buffer, img_buffer_size);
} // end bool EventStream::send_file(const char * filepath)
}
bool EventStream::send_buffer(uint8_t* buffer, int size) {
if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", size) ) {

View File

@ -56,10 +56,10 @@ class EventStream : public StreamBase {
SystemTimePoint end_time;
Microseconds duration;
Microseconds frames_duration;
char path[PATH_MAX];
std::string path;
int n_frames; // # of frame rows returned from database
FrameData *frames;
char video_file[PATH_MAX];
std::string video_file;
Storage::Schemes scheme;
int SaveJPEGs;
Monitor::Orientation Orientation;
@ -124,7 +124,7 @@ class EventStream : public StreamBase {
void runStream() override;
Image *getImage();
private:
bool send_file(const char *filepath);
bool send_file(const std::string &filepath);
bool send_buffer(uint8_t * buffer, int size);
Storage *storage;
FFmpeg_Input *ffmpeg_input;

View File

@ -213,7 +213,7 @@ void zm_dump_codecpar(const AVCodecParameters *par) {
void zm_dump_codec(const AVCodecContext *codec) {
Debug(1, "Dumping codec_context codec_type %d %s codec_id %d %s width %d height %d timebase %d/%d format %s profile %d level %d "
"gop_size %d has_b_frames %d max_b_frames %d me_cmp %d me_range %d qmin %d qmax %d bit_rate %ld extradata:%d:%s",
"gop_size %d has_b_frames %d max_b_frames %d me_cmp %d me_range %d qmin %d qmax %d bit_rate %" PRId64 " extradata:%d:%s",
codec->codec_type,
av_get_media_type_string(codec->codec_type),
codec->codec_id,

View File

@ -27,16 +27,10 @@
extern "C" {
#include <libavutil/time.h>
#if HAVE_LIBAVUTIL_HWCONTEXT_H
#include <libavutil/hwcontext.h>
#endif
#include <libavutil/pixdesc.h>
}
#include <string>
TimePoint start_read_time;
time_t start_read_time;
#if HAVE_LIBAVUTIL_HWCONTEXT_H
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
static enum AVPixelFormat hw_pix_fmt;
@ -61,31 +55,26 @@ 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) {
case AV_HWDEVICE_TYPE_VAAPI:
fmt = AV_PIX_FMT_VAAPI;
break;
return AV_PIX_FMT_VAAPI;
case AV_HWDEVICE_TYPE_DXVA2:
fmt = AV_PIX_FMT_DXVA2_VLD;
break;
return AV_PIX_FMT_DXVA2_VLD;
case AV_HWDEVICE_TYPE_D3D11VA:
fmt = AV_PIX_FMT_D3D11;
break;
return AV_PIX_FMT_D3D11;
case AV_HWDEVICE_TYPE_VDPAU:
fmt = AV_PIX_FMT_VDPAU;
break;
return AV_PIX_FMT_VDPAU;
case AV_HWDEVICE_TYPE_CUDA:
fmt = AV_PIX_FMT_CUDA;
break;
return AV_PIX_FMT_CUDA;
#ifdef AV_HWDEVICE_TYPE_MMAL
case AV_HWDEVICE_TYPE_MMAL:
return AV_PIX_FMT_MMAL;
#endif
case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
fmt = AV_PIX_FMT_VIDEOTOOLBOX;
break;
return AV_PIX_FMT_VIDEOTOOLBOX;
default:
fmt = AV_PIX_FMT_NONE;
break;
return AV_PIX_FMT_NONE;
}
return fmt;
}
#endif
#endif
@ -169,7 +158,7 @@ FfmpegCamera::~FfmpegCamera() {
}
int FfmpegCamera::PrimeCapture() {
start_read_time = time(nullptr);
start_read_time = std::chrono::steady_clock::now();
if ( mCanCapture ) {
Debug(1, "Priming capture from %s, Closing", mPath.c_str());
Close();
@ -188,7 +177,7 @@ int FfmpegCamera::PreCapture() {
int FfmpegCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
if (!mCanCapture) return -1;
start_read_time = time(nullptr);
start_read_time = std::chrono::steady_clock::now();
int ret;
AVFormatContext *formatContextPtr;
@ -237,8 +226,8 @@ int FfmpegCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
zm_packet->set_packet(&packet);
zm_packet->stream = stream;
zm_packet->pts = av_rescale_q(packet.pts, stream->time_base, AV_TIME_BASE_Q);
if ( packet.pts != AV_NOPTS_VALUE ) {
if ( stream == mVideoStream ) {
if (packet.pts != AV_NOPTS_VALUE) {
if (stream == mVideoStream) {
if (mFirstVideoPTS == AV_NOPTS_VALUE)
mFirstVideoPTS = packet.pts;
@ -558,11 +547,12 @@ int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
Debug(1, "Received terminate in cb");
return zm_terminate;
}
time_t now = time(nullptr);
if (now - start_read_time > 10) {
Debug(1, "timeout in ffmpeg camera now %" PRIi64 " - %" PRIi64 " > 10",
static_cast<int64>(now),
static_cast<int64>(start_read_time));
TimePoint now = std::chrono::steady_clock::now();
if (now - start_read_time > Seconds(10)) {
Debug(1, "timeout in ffmpeg camera now %" PRIi64 " - %" PRIi64 " > 10 s",
static_cast<int64>(std::chrono::duration_cast<Seconds>(now.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(start_read_time.time_since_epoch()).count()));
return 1;
}
return 0;

View File

@ -269,4 +269,4 @@ AVFrame *FFmpeg_Input::get_frame(int stream_id, double at) {
}
return get_frame(stream_id);
} // end AVFrame *FFmpeg_Input::get_frame( int stream_id, struct timeval at)
}

View File

@ -29,32 +29,26 @@
#define RAW_BUFFER 512
#define PIPE_SIZE 1024*1024
void Fifo::file_create_if_missing(
const char * path,
bool is_fifo,
bool delete_fake_fifo
) {
static struct stat st;
if ( stat(path, &st) == 0 ) {
if ( (!is_fifo) || S_ISFIFO(st.st_mode) || !delete_fake_fifo )
void Fifo::file_create_if_missing(const std::string &path, bool is_fifo, bool delete_fake_fifo) {
struct stat st = {};
if (stat(path.c_str(), &st) == 0) {
if ((!is_fifo) || S_ISFIFO(st.st_mode) || !delete_fake_fifo)
return;
Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path);
unlink(path);
Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path.c_str());
unlink(path.c_str());
}
if (!is_fifo) {
Debug(5, "Creating non fifo file as requested: %s", path);
int fd = ::open(path, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
Debug(5, "Creating non fifo file as requested: %s", path.c_str());
int fd = ::open(path.c_str(), O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
::close(fd);
return;
}
Debug(5, "Making fifo file of: %s", path);
mkfifo(path, S_IRUSR|S_IWUSR);
Debug(5, "Making fifo file of: %s", path.c_str());
mkfifo(path.c_str(), S_IRUSR | S_IWUSR);
}
void Fifo::fifo_create_if_missing(
const char * path,
bool delete_fake_fifo
) {
void Fifo::fifo_create_if_missing(const std::string &path, bool delete_fake_fifo) {
file_create_if_missing(path, true, delete_fake_fifo);
}
@ -62,7 +56,8 @@ Fifo::~Fifo() {
close();
}
bool Fifo::open() {
fifo_create_if_missing(path.c_str());
fifo_create_if_missing(path);
if (!on_blocking_abort) {
if ( (outfile = fopen(path.c_str(), "wb")) == nullptr ) {
Error("Can't open %s for writing: %s", path.c_str(), strerror(errno));

View File

@ -32,11 +32,8 @@ class Fifo {
int raw_fd;
public:
static void file_create_if_missing(
const char * path,
bool is_fifo,
bool delete_fake_fifo = true
);
static void file_create_if_missing(const std::string &path, bool is_fifo, bool delete_fake_fifo = true);
static void fifo_create_if_missing(const std::string &path, bool delete_fake_fifo = true);
Fifo() :
on_blocking_abort(true),
@ -51,12 +48,6 @@ class Fifo {
{}
~Fifo();
static void fifo_create_if_missing(
const char * path,
bool delete_fake_fifo = true);
static bool writePacket(std::string filename, const ZMPacket &packet);
static bool write(std::string filename, uint8_t *data, size_t size);

View File

@ -28,7 +28,7 @@
#define RAW_BUFFER 512
static bool zm_fifodbg_inited = false;
FILE *zm_fifodbg_log_fd = nullptr;
char zm_fifodbg_log[PATH_MAX] = "";
std::string zm_fifodbg_log;
static bool zmFifoDbgOpen() {
if ( zm_fifodbg_log_fd )
@ -36,7 +36,7 @@ static bool zmFifoDbgOpen() {
zm_fifodbg_log_fd = nullptr;
signal(SIGPIPE, SIG_IGN);
Fifo::fifo_create_if_missing(zm_fifodbg_log);
int fd = open(zm_fifodbg_log, O_WRONLY|O_NONBLOCK|O_TRUNC);
int fd = open(zm_fifodbg_log.c_str(), O_WRONLY | O_NONBLOCK | O_TRUNC);
if ( fd < 0 )
return false;
int res = flock(fd, LOCK_EX | LOCK_NB);
@ -54,8 +54,7 @@ static bool zmFifoDbgOpen() {
int zmFifoDbgInit(Monitor *monitor) {
zm_fifodbg_inited = true;
snprintf(zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/dbgpipe-%u.log",
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
zm_fifodbg_log = stringtf("%s/dbgpipe-%u.log", staticConfig.PATH_SOCKS.c_str(), monitor->Id());
zmFifoDbgOpen();
return 1;
}

View File

@ -29,9 +29,9 @@
#define RAW_BUFFER 512
bool FifoStream::sendRAWFrames() {
static unsigned char buffer[RAW_BUFFER];
int fd = open(stream_path, O_RDONLY);
int fd = open(stream_path.c_str(), O_RDONLY);
if ( fd < 0 ) {
Error("Can't open %s: %s", stream_path, strerror(errno));
Error("Can't open %s: %s", stream_path.c_str(), strerror(errno));
return false;
}
while ( (bytes_read = read(fd, buffer, RAW_BUFFER)) ) {
@ -56,9 +56,9 @@ bool FifoStream::sendRAWFrames() {
bool FifoStream::sendMJEGFrames() {
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
int fd = open(stream_path, O_RDONLY);
int fd = open(stream_path.c_str(), O_RDONLY);
if ( fd < 0 ) {
Error("Can't open %s: %s", stream_path, strerror(errno));
Error("Can't open %s: %s", stream_path.c_str(), strerror(errno));
return false;
}
total_read = 0;
@ -97,28 +97,26 @@ bool FifoStream::sendMJEGFrames() {
return true;
}
void FifoStream::setStreamStart(const char * path) {
stream_path = strdup(path);
void FifoStream::setStreamStart(const std::string &path) {
stream_path = path;
}
void FifoStream::setStreamStart(int monitor_id, const char * format) {
char diag_path[PATH_MAX];
void FifoStream::setStreamStart(int monitor_id, const char *format) {
std::string diag_path;
std::shared_ptr<Monitor> monitor = Monitor::Load(monitor_id, false, Monitor::QUERY);
if ( !strcmp(format, "reference") ) {
snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-r-%u.jpg",
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
if (!strcmp(format, "reference")) {
diag_path = stringtf("%s/diagpipe-r-%u.jpg", staticConfig.PATH_SOCKS.c_str(), monitor->Id());
stream_type = MJPEG;
} else if ( !strcmp(format, "delta") ) {
snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-d-%u.jpg",
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
} else if (!strcmp(format, "delta")) {
diag_path = stringtf("%s/diagpipe-d-%u.jpg", staticConfig.PATH_SOCKS.c_str(), monitor->Id());
stream_type = MJPEG;
} else {
if ( strcmp(format, "raw") ) {
if (strcmp(format, "raw")) {
Warning("Unknown or unspecified format. Defaulting to raw");
}
snprintf(diag_path, sizeof(diag_path), "%s/dbgpipe-%u.log",
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
diag_path = stringtf("%s/dbgpipe-%u.log", staticConfig.PATH_SOCKS.c_str(), monitor->Id());
stream_type = RAW;
}
@ -126,46 +124,48 @@ void FifoStream::setStreamStart(int monitor_id, const char * format) {
}
void FifoStream::runStream() {
if ( stream_type == MJPEG ) {
if (stream_type == MJPEG) {
fprintf(stdout, "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n\r\n");
} else {
fprintf(stdout, "Content-Type: text/html\r\n\r\n");
}
/* only 1 person can read from a fifo at a time, so use a lock */
char lock_file[PATH_MAX];
snprintf(lock_file, sizeof(lock_file), "%s.rlock", stream_path);
std::string lock_file = stringtf("%s.rlock", stream_path.c_str());
Fifo::file_create_if_missing(lock_file, false);
Debug(1, "Locking %s", lock_file);
Debug(1, "Locking %s", lock_file.c_str());
int fd_lock = open(lock_file, O_RDONLY);
if ( fd_lock < 0 ) {
Error("Can't open %s: %s", lock_file, strerror(errno));
int fd_lock = open(lock_file.c_str(), O_RDONLY);
if (fd_lock < 0) {
Error("Can't open %s: %s", lock_file.c_str(), strerror(errno));
return;
}
int res = flock(fd_lock, LOCK_EX | LOCK_NB);
while ( (res < 0 and errno == EAGAIN) and (! zm_terminate) ) {
Warning("Flocking problem on %s: - %s", lock_file, strerror(errno));
while ((res < 0 and errno == EAGAIN) and (!zm_terminate)) {
Warning("Flocking problem on %s: - %s", lock_file.c_str(), strerror(errno));
sleep(1);
res = flock(fd_lock, LOCK_EX | LOCK_NB);
}
if ( res < 0 ) {
Error("Flocking problem on %d != %d %s: - %s", EAGAIN, res, lock_file, strerror(errno));
if (res < 0) {
Error("Flocking problem on %d != %d %s: - %s", EAGAIN, res, lock_file.c_str(), strerror(errno));
close(fd_lock);
return;
}
while ( !zm_terminate ) {
while (!zm_terminate) {
now = std::chrono::system_clock::now();
checkCommandQueue();
if ( stream_type == MJPEG ) {
if ( !sendMJEGFrames() )
if (stream_type == MJPEG) {
if (!sendMJEGFrames())
zm_terminate = true;
} else {
if ( !sendRAWFrames() )
if (!sendRAWFrames())
zm_terminate = true;
}
}
close(fd_lock);
}

View File

@ -25,15 +25,10 @@ class Monitor;
class FifoStream : public StreamBase {
private:
char * stream_path;
std::string stream_path;
int total_read;
int bytes_read;
unsigned int frame_count;
static void file_create_if_missing(
const char * path,
bool is_fifo,
bool delete_fake_fifo = true
);
protected:
typedef enum { UNKNOWN, MJPEG, RAW } StreamType;
@ -44,13 +39,13 @@ class FifoStream : public StreamBase {
public:
FifoStream() :
stream_path(nullptr),
total_read(0),
bytes_read(0),
frame_count(0),
stream_type(UNKNOWN)
{}
void setStreamStart(const char * path);
void setStreamStart(const std::string &path);
void setStreamStart(int monitor_id, const char * format);
void runStream() override;
};

View File

@ -21,7 +21,6 @@
#include "zm_packet.h"
#include <sys/stat.h>
#include <unistd.h>
FileCamera::FileCamera(
const Monitor *monitor,
@ -49,20 +48,20 @@ FileCamera::FileCamera(
p_capture,
p_record_audio)
{
strncpy( path, p_path, sizeof(path)-1 );
if ( capture ) {
path = std::string(p_path);
if (capture) {
Initialise();
}
}
FileCamera::~FileCamera() {
if ( capture ) {
if (capture) {
Terminate();
}
}
void FileCamera::Initialise() {
if ( !path[0] ) {
if (path.empty()) {
Fatal("No path specified for file image");
}
}
@ -71,9 +70,9 @@ void FileCamera::Terminate() {
}
int FileCamera::PreCapture() {
struct stat statbuf;
if ( stat(path, &statbuf) < 0 ) {
Error("Can't stat %s: %s", path, strerror(errno));
struct stat statbuf = {};
if (stat(path.c_str(), &statbuf) < 0) {
Error("Can't stat %s: %s", path.c_str(), strerror(errno));
return -1;
}
bytes += statbuf.st_size;
@ -81,8 +80,8 @@ int FileCamera::PreCapture() {
// This waits until 1 second has passed since it was modified. Effectively limiting fps to 60.
// Which is kinda bogus. If we were writing to this jpg constantly faster than we are monitoring it here
// we would never break out of this loop
while ( (time(nullptr) - statbuf.st_mtime) < 1 ) {
usleep(100000);
while ((time(nullptr) - statbuf.st_mtime) < 1) {
std::this_thread::sleep_for(Milliseconds(100));
}
return 0;
}

View File

@ -27,26 +27,19 @@
// accessed using a single file which contains the latest jpeg data
//
class FileCamera : public Camera {
protected:
char path[PATH_MAX];
public:
FileCamera(
const Monitor *monitor,
const char *p_path,
int p_width,
int p_height,
int p_colours,
int p_brightness,
int p_contrast,
int p_hue,
int p_colour,
bool p_capture,
bool p_record_audio
);
~FileCamera();
const char *Path() const { return path; }
public:
FileCamera(const Monitor *monitor,
const char *p_path,
int p_width,
int p_height,
int p_colours,
int p_brightness,
int p_contrast,
int p_hue,
int p_colour,
bool p_capture,
bool p_record_audio);
~FileCamera() override;
void Initialise();
void Terminate();
@ -54,6 +47,11 @@ public:
int Capture(std::shared_ptr<ZMPacket> &p) override;
int PostCapture() override;
int Close() override { return 0; };
const std::string &Path() const { return path; }
private:
std::string path;
};
#endif // ZM_FILE_CAMERA_H

View File

@ -3,7 +3,7 @@
Frame::Frame(event_id_t p_event_id,
int p_frame_id,
FrameType p_type,
struct timeval p_timestamp,
SystemTimePoint p_timestamp,
Microseconds p_delta,
int p_score,
std::vector<ZoneStats> p_stats)

View File

@ -23,8 +23,6 @@
#include "zm_event.h"
#include "zm_time.h"
#include "zm_zone.h"
#include <sys/time.h>
#include <vector>
enum FrameType {
@ -41,7 +39,7 @@ class Frame {
Frame(event_id_t p_event_id,
int p_frame_id,
FrameType p_type,
struct timeval p_timestamp,
SystemTimePoint p_timestamp,
Microseconds p_delta,
int p_score,
std::vector<ZoneStats> p_stats
@ -50,7 +48,7 @@ class Frame {
event_id_t event_id;
int frame_id;
FrameType type;
struct timeval timestamp;
SystemTimePoint timestamp;
Microseconds delta;
int score;
std::vector<ZoneStats> zone_stats;

View File

@ -20,6 +20,7 @@
#include "zm_group.h"
#include "zm_logger.h"
#include "zm_utils.h"
#include <cstring>
Group::Group() {
@ -39,27 +40,27 @@ Group::Group(const MYSQL_ROW &dbrow) {
/* If a zero or invalid p_id is passed, then the old default path will be assumed. */
Group::Group(unsigned int p_id) {
id = 0;
id = 0;
if ( p_id ) {
char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "SELECT `Id`, `ParentId`, `Name` FROM `Group` WHERE `Id`=%u", p_id);
Debug(2,"Loading Group for %u using %s", p_id, sql);
zmDbRow dbrow;
if ( !dbrow.fetch(sql) ) {
Error("Unable to load group for id %u: %s", p_id, mysql_error(&dbconn));
} else {
unsigned int index = 0;
id = atoi(dbrow[index++]);
parent_id = dbrow[index] ? atoi(dbrow[index]): 0; index++;
strncpy(name, dbrow[index++], sizeof(name)-1);
Debug(1, "Loaded Group area %d '%s'", id, this->Name());
}
}
if ( ! id ) {
Debug(1,"No id passed to Group constructor.");
strcpy(name, "Default");
}
if (p_id) {
std::string sql = stringtf("SELECT `Id`, `ParentId`, `Name` FROM `Group` WHERE `Id`=%u", p_id);
Debug(2, "Loading Group for %u using %s", p_id, sql.c_str());
zmDbRow dbrow;
if (!dbrow.fetch(sql)) {
Error("Unable to load group for id %u: %s", p_id, mysql_error(&dbconn));
} else {
unsigned int index = 0;
id = atoi(dbrow[index++]);
parent_id = dbrow[index] ? atoi(dbrow[index]) : 0;
index++;
strncpy(name, dbrow[index++], sizeof(name) - 1);
Debug(1, "Loaded Group area %d '%s'", id, this->Name());
}
}
if (!id) {
Debug(1, "No id passed to Group constructor.");
strcpy(name, "Default");
}
}
Group::~Group() {

View File

@ -313,6 +313,7 @@ bool Image::Assign(const AVFrame *frame, SwsContext *convert_context, AVFrame *t
return false;
}
zm_dump_video_frame(temp_frame, "dest frame after convert");
update_function_pointers();
return true;
} // end Image::Assign(const AVFrame *frame, SwsContext *convert_context, AVFrame *temp_frame)
@ -687,6 +688,7 @@ void Image::AssignDirect(
subpixelorder = p_subpixelorder;
pixels = width * height;
size = new_buffer_size;
update_function_pointers();
} // end void Image::AssignDirect
void Image::Assign(
@ -788,6 +790,7 @@ void Image::Assign(const Image &image) {
linesize = image.linesize;
}
update_function_pointers();
if ( image.buffer != buffer )
(*fptr_imgbufcpy)(buffer, image.buffer, size);
}
@ -933,125 +936,123 @@ bool Image::WriteRaw(const char *filename) const {
return true;
}
bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int p_subpixelorder) {
bool Image::ReadJpeg(const std::string &filename, unsigned int p_colours, unsigned int p_subpixelorder) {
unsigned int new_width, new_height, new_colours, new_subpixelorder;
struct jpeg_decompress_struct *cinfo = readjpg_dcinfo;
if ( !cinfo ) {
cinfo = readjpg_dcinfo = new jpeg_decompress_struct;
cinfo->err = jpeg_std_error(&jpg_err.pub);
if (!readjpg_dcinfo) {
readjpg_dcinfo = new jpeg_decompress_struct;
readjpg_dcinfo->err = jpeg_std_error(&jpg_err.pub);
jpg_err.pub.error_exit = zm_jpeg_error_exit;
jpg_err.pub.emit_message = zm_jpeg_emit_message;
jpeg_create_decompress(cinfo);
jpeg_create_decompress(readjpg_dcinfo);
}
FILE *infile;
if ( (infile = fopen(filename, "rb")) == nullptr ) {
Error("Can't open %s: %s", filename, strerror(errno));
if ((infile = fopen(filename.c_str(), "rb")) == nullptr) {
Error("Can't open %s: %s", filename.c_str(), strerror(errno));
return false;
}
if ( setjmp(jpg_err.setjmp_buffer) ) {
jpeg_abort_decompress(cinfo);
if (setjmp(jpg_err.setjmp_buffer)) {
jpeg_abort_decompress(readjpg_dcinfo);
fclose(infile);
return false;
}
jpeg_stdio_src(cinfo, infile);
jpeg_stdio_src(readjpg_dcinfo, infile);
jpeg_read_header(cinfo, TRUE);
jpeg_read_header(readjpg_dcinfo, true);
if ( (cinfo->num_components != 1) && (cinfo->num_components != 3) ) {
if ((readjpg_dcinfo->num_components != 1) && (readjpg_dcinfo->num_components != 3)) {
Error("Unexpected colours when reading jpeg image: %d", colours);
jpeg_abort_decompress(cinfo);
jpeg_abort_decompress(readjpg_dcinfo);
fclose(infile);
return false;
}
/* Check if the image has at least one huffman table defined. If not, use the standard ones */
/* This is required for the MJPEG capture palette of USB devices */
if ( cinfo->dc_huff_tbl_ptrs[0] == nullptr ) {
zm_use_std_huff_tables(cinfo);
if (readjpg_dcinfo->dc_huff_tbl_ptrs[0] == nullptr) {
zm_use_std_huff_tables(readjpg_dcinfo);
}
new_width = cinfo->image_width;
new_height = cinfo->image_height;
new_width = readjpg_dcinfo->image_width;
new_height = readjpg_dcinfo->image_height;
if ( (width != new_width) || (height != new_height) ) {
if ((width != new_width) || (height != new_height)) {
Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u", width, height, new_width, new_height);
}
switch ( p_colours ) {
switch (p_colours) {
case ZM_COLOUR_GRAY8:
cinfo->out_color_space = JCS_GRAYSCALE;
new_colours = ZM_COLOUR_GRAY8;
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
break;
readjpg_dcinfo->out_color_space = JCS_GRAYSCALE;
new_colours = ZM_COLOUR_GRAY8;
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
break;
case ZM_COLOUR_RGB32:
#ifdef JCS_EXTENSIONS
new_colours = ZM_COLOUR_RGB32;
if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
cinfo->out_color_space = JCS_EXT_BGRX;
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA;
} else if ( p_subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
cinfo->out_color_space = JCS_EXT_XRGB;
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB;
} else if ( p_subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
cinfo->out_color_space = JCS_EXT_XBGR;
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
} else {
/* Assume RGBA */
cinfo->out_color_space = JCS_EXT_RGBX;
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
}
break;
new_colours = ZM_COLOUR_RGB32;
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
readjpg_dcinfo->out_color_space = JCS_EXT_BGRX;
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA;
} else if (p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
readjpg_dcinfo->out_color_space = JCS_EXT_XRGB;
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB;
} else if (p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
readjpg_dcinfo->out_color_space = JCS_EXT_XBGR;
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
} else {
/* Assume RGBA */
readjpg_dcinfo->out_color_space = JCS_EXT_RGBX;
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
}
break;
#else
Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead.");
Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead.");
#endif
case ZM_COLOUR_RGB24:
default:
new_colours = ZM_COLOUR_RGB24;
if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGR ) {
new_colours = ZM_COLOUR_RGB24;
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
#ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_BGR;
new_subpixelorder = ZM_SUBPIX_ORDER_BGR;
readjpg_dcinfo->out_color_space = JCS_EXT_BGR;
new_subpixelorder = ZM_SUBPIX_ORDER_BGR;
#else
Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead.");
cinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead.");
cinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
#endif
} else {
/* Assume RGB */
/*
} else {
/* Assume RGB */
/*
#ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_RGB;
#else
cinfo->out_color_space = JCS_RGB;
#endif
*/
cinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
}
break;
*/
readjpg_dcinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
}
break;
} // end switch p_colours
if ( WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == nullptr ) {
if (WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == nullptr) {
Error("Failed requesting writeable buffer for reading JPEG image.");
jpeg_abort_decompress(cinfo);
jpeg_abort_decompress(readjpg_dcinfo);
fclose(infile);
return false;
}
jpeg_start_decompress(cinfo);
jpeg_start_decompress(readjpg_dcinfo);
JSAMPROW row_pointer = buffer;
while ( cinfo->output_scanline < cinfo->output_height ) {
jpeg_read_scanlines(cinfo, &row_pointer, 1);
while (readjpg_dcinfo->output_scanline < readjpg_dcinfo->output_height) {
jpeg_read_scanlines(readjpg_dcinfo, &row_pointer, 1);
row_pointer += linesize;
}
jpeg_finish_decompress(cinfo);
jpeg_finish_decompress(readjpg_dcinfo);
fclose(infile);
return true;
@ -1060,71 +1061,74 @@ cinfo->out_color_space = JCS_RGB;
// Multiple calling formats to permit inclusion (or not) of non blocking, quality_override and timestamp (exif), with suitable defaults.
// Note quality=zero means default
bool Image::WriteJpeg(const char *filename, int quality_override) const {
bool Image::WriteJpeg(const std::string &filename, int quality_override) const {
return Image::WriteJpeg(filename, quality_override, {}, false);
}
bool Image::WriteJpeg(const char *filename) const {
bool Image::WriteJpeg(const std::string &filename) const {
return Image::WriteJpeg(filename, 0, {}, false);
}
bool Image::WriteJpeg(const char *filename, bool on_blocking_abort) const {
bool Image::WriteJpeg(const std::string &filename, bool on_blocking_abort) const {
return Image::WriteJpeg(filename, 0, {}, on_blocking_abort);
}
bool Image::WriteJpeg(const char *filename, SystemTimePoint timestamp) const {
bool Image::WriteJpeg(const std::string &filename, SystemTimePoint timestamp) const {
return Image::WriteJpeg(filename, 0, timestamp, false);
}
bool Image::WriteJpeg(const char *filename, int quality_override, SystemTimePoint timestamp) const {
bool Image::WriteJpeg(const std::string &filename, int quality_override, SystemTimePoint timestamp) const {
return Image::WriteJpeg(filename, quality_override, timestamp, false);
}
bool Image::WriteJpeg(const char *filename,
int quality_override,
bool Image::WriteJpeg(const std::string &filename,
const int &quality_override,
SystemTimePoint timestamp,
bool on_blocking_abort) const {
if ( config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8) ) {
if (config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8)) {
Image temp_image(*this);
temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
return temp_image.WriteJpeg(filename, quality_override, timestamp, on_blocking_abort);
}
int quality = quality_override ? quality_override : config.jpeg_file_quality;
struct jpeg_compress_struct *cinfo = writejpg_ccinfo[quality];
jpeg_compress_struct *cinfo = writejpg_ccinfo[quality];
FILE *outfile = nullptr;
int raw_fd = 0;
if ( !cinfo ) {
if (!cinfo) {
cinfo = writejpg_ccinfo[quality] = new jpeg_compress_struct;
cinfo->err = jpeg_std_error(&jpg_err.pub);
jpeg_create_compress(cinfo);
}
if ( !on_blocking_abort ) {
if (!on_blocking_abort) {
jpg_err.pub.error_exit = zm_jpeg_error_exit;
jpg_err.pub.emit_message = zm_jpeg_emit_message;
} else {
jpg_err.pub.error_exit = zm_jpeg_error_silent;
jpg_err.pub.emit_message = zm_jpeg_emit_silence;
if ( setjmp(jpg_err.setjmp_buffer) ) {
if (setjmp(jpg_err.setjmp_buffer)) {
jpeg_abort_compress(cinfo);
Debug(1, "Aborted a write mid-stream and %s and %d", (outfile == nullptr) ? "closing file" : "file not opened", raw_fd);
if ( raw_fd )
Debug(1,
"Aborted a write mid-stream and %s and %d",
(outfile == nullptr) ? "closing file" : "file not opened",
raw_fd);
if (raw_fd)
close(raw_fd);
if ( outfile )
if (outfile)
fclose(outfile);
return false;
}
}
if ( !on_blocking_abort ) {
if ( (outfile = fopen(filename, "wb")) == nullptr ) {
Error("Can't open %s for writing: %s", filename, strerror(errno));
if (!on_blocking_abort) {
if ((outfile = fopen(filename.c_str(), "wb")) == nullptr) {
Error("Can't open %s for writing: %s", filename.c_str(), strerror(errno));
return false;
}
} else {
raw_fd = open(filename, O_WRONLY|O_NONBLOCK|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if ( raw_fd < 0 )
raw_fd = open(filename.c_str(), O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (raw_fd < 0)
return false;
outfile = fdopen(raw_fd, "wb");
if ( outfile == nullptr ) {
if (outfile == nullptr) {
close(raw_fd);
return false;
}
@ -1135,58 +1139,58 @@ bool Image::WriteJpeg(const char *filename,
cinfo->image_width = width; /* image width and height, in pixels */
cinfo->image_height = height;
switch ( colours ) {
switch (colours) {
case ZM_COLOUR_GRAY8:
cinfo->input_components = 1;
cinfo->in_color_space = JCS_GRAYSCALE;
break;
cinfo->input_components = 1;
cinfo->in_color_space = JCS_GRAYSCALE;
break;
case ZM_COLOUR_RGB32:
#ifdef JCS_EXTENSIONS
cinfo->input_components = 4;
if ( subpixelorder == ZM_SUBPIX_ORDER_RGBA ) {
cinfo->in_color_space = JCS_EXT_RGBX;
} else if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
cinfo->in_color_space = JCS_EXT_BGRX;
} else if ( subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
cinfo->in_color_space = JCS_EXT_XRGB;
} else if ( subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
cinfo->in_color_space = JCS_EXT_XBGR;
} else {
Warning("Unknwon subpixelorder %d", subpixelorder);
/* Assume RGBA */
cinfo->in_color_space = JCS_EXT_RGBX;
}
break;
cinfo->input_components = 4;
if (subpixelorder == ZM_SUBPIX_ORDER_RGBA) {
cinfo->in_color_space = JCS_EXT_RGBX;
} else if (subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
cinfo->in_color_space = JCS_EXT_BGRX;
} else if (subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
cinfo->in_color_space = JCS_EXT_XRGB;
} else if (subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
cinfo->in_color_space = JCS_EXT_XBGR;
} else {
Warning("Unknwon subpixelorder %d", subpixelorder);
/* Assume RGBA */
cinfo->in_color_space = JCS_EXT_RGBX;
}
break;
#else
Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source");
Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source");
jpeg_abort_compress(cinfo);
fclose(outfile);
return false;
#endif
case ZM_COLOUR_RGB24:
default:
cinfo->input_components = 3;
if (subpixelorder == ZM_SUBPIX_ORDER_BGR) {
#ifdef JCS_EXTENSIONS
cinfo->in_color_space = JCS_EXT_BGR;
#else
Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source");
jpeg_abort_compress(cinfo);
fclose(outfile);
return false;
#endif
case ZM_COLOUR_RGB24:
default:
cinfo->input_components = 3;
if ( subpixelorder == ZM_SUBPIX_ORDER_BGR) {
#ifdef JCS_EXTENSIONS
cinfo->in_color_space = JCS_EXT_BGR;
#else
Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source");
jpeg_abort_compress(cinfo);
fclose(outfile);
return false;
#endif
} else {
/* Assume RGB */
/*
} else {
/* Assume RGB */
/*
#ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_RGB;
#else
cinfo->out_color_space = JCS_RGB;
#endif
*/
cinfo->in_color_space = JCS_RGB;
}
break;
cinfo->in_color_space = JCS_RGB;
}
break;
} // end switch(colours)
jpeg_set_defaults(cinfo);
@ -1194,7 +1198,7 @@ cinfo->out_color_space = JCS_RGB;
cinfo->dct_method = JDCT_FASTEST;
jpeg_start_compress(cinfo, TRUE);
if ( config.add_jpeg_comments && !annotation_.empty() ) {
if (config.add_jpeg_comments && !annotation_.empty()) {
jpeg_write_marker(cinfo, JPEG_COM, reinterpret_cast<const JOCTET *>(annotation_.c_str()), annotation_.size());
}
// If we have a non-zero time (meaning a parameter was passed in), then form a simple exif segment with that time as DateTimeOriginal and SubsecTimeOriginal
@ -1218,19 +1222,19 @@ cinfo->out_color_space = JCS_RGB;
snprintf(msbuf, sizeof msbuf, "%06d", static_cast<int32>(ts_usec.count()));
unsigned char exiftimes[82] = {
0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00,
0x69, 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x03, 0x90, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x91, 0x92,
0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00 };
memcpy(&exiftimes[EXIFTIMES_OFFSET], timebuf,EXIFTIMES_LEN);
0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00,
0x69, 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x03, 0x90, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x91, 0x92,
0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00};
memcpy(&exiftimes[EXIFTIMES_OFFSET], timebuf, EXIFTIMES_LEN);
memcpy(&exiftimes[EXIFTIMES_MS_OFFSET], msbuf, EXIFTIMES_MS_LEN);
jpeg_write_marker(cinfo, EXIF_CODE, (const JOCTET *)exiftimes, sizeof(exiftimes));
jpeg_write_marker(cinfo, EXIF_CODE, (const JOCTET *) exiftimes, sizeof(exiftimes));
}
JSAMPROW row_pointer = buffer; /* pointer to a single row */
while ( cinfo->next_scanline < cinfo->image_height ) {
while (cinfo->next_scanline < cinfo->image_height) {
jpeg_write_scanlines(cinfo, &row_pointer, 1);
row_pointer += linesize;
}
@ -1240,121 +1244,116 @@ cinfo->out_color_space = JCS_RGB;
return true;
}
bool Image::DecodeJpeg(
const JOCTET *inbuffer,
int inbuffer_size,
unsigned int p_colours,
unsigned int p_subpixelorder)
bool Image::DecodeJpeg(const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder)
{
unsigned int new_width, new_height, new_colours, new_subpixelorder;
struct jpeg_decompress_struct *cinfo = decodejpg_dcinfo;
if ( !cinfo ) {
cinfo = decodejpg_dcinfo = new jpeg_decompress_struct;
cinfo->err = jpeg_std_error( &jpg_err.pub );
if (!decodejpg_dcinfo) {
decodejpg_dcinfo = new jpeg_decompress_struct;
decodejpg_dcinfo->err = jpeg_std_error(&jpg_err.pub);
jpg_err.pub.error_exit = zm_jpeg_error_exit;
jpg_err.pub.emit_message = zm_jpeg_emit_message;
jpeg_create_decompress( cinfo );
jpeg_create_decompress(decodejpg_dcinfo);
}
if ( setjmp(jpg_err.setjmp_buffer) ) {
jpeg_abort_decompress(cinfo);
if (setjmp(jpg_err.setjmp_buffer)) {
jpeg_abort_decompress(decodejpg_dcinfo);
return false;
}
zm_jpeg_mem_src(cinfo, inbuffer, inbuffer_size);
zm_jpeg_mem_src(decodejpg_dcinfo, inbuffer, inbuffer_size);
jpeg_read_header(cinfo, TRUE);
jpeg_read_header(decodejpg_dcinfo, TRUE);
if ( (cinfo->num_components != 1) && (cinfo->num_components != 3) ) {
if ((decodejpg_dcinfo->num_components != 1) && (decodejpg_dcinfo->num_components != 3)) {
Error("Unexpected colours when reading jpeg image: %d", colours);
jpeg_abort_decompress(cinfo);
jpeg_abort_decompress(decodejpg_dcinfo);
return false;
}
/* Check if the image has at least one huffman table defined. If not, use the standard ones */
/* This is required for the MJPEG capture palette of USB devices */
if ( cinfo->dc_huff_tbl_ptrs[0] == nullptr ) {
zm_use_std_huff_tables(cinfo);
if (decodejpg_dcinfo->dc_huff_tbl_ptrs[0] == nullptr) {
zm_use_std_huff_tables(decodejpg_dcinfo);
}
new_width = cinfo->image_width;
new_height = cinfo->image_height;
new_width = decodejpg_dcinfo->image_width;
new_height = decodejpg_dcinfo->image_height;
if ( (width != new_width) || (height != new_height) ) {
if ((width != new_width) || (height != new_height)) {
Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u",
width, height, new_width, new_height);
width, height, new_width, new_height);
}
switch (p_colours) {
case ZM_COLOUR_GRAY8:
cinfo->out_color_space = JCS_GRAYSCALE;
new_colours = ZM_COLOUR_GRAY8;
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
break;
decodejpg_dcinfo->out_color_space = JCS_GRAYSCALE;
new_colours = ZM_COLOUR_GRAY8;
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
break;
case ZM_COLOUR_RGB32:
#ifdef JCS_EXTENSIONS
new_colours = ZM_COLOUR_RGB32;
if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
cinfo->out_color_space = JCS_EXT_BGRX;
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA;
} else if ( p_subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
cinfo->out_color_space = JCS_EXT_XRGB;
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB;
} else if ( p_subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
cinfo->out_color_space = JCS_EXT_XBGR;
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
} else {
/* Assume RGBA */
cinfo->out_color_space = JCS_EXT_RGBX;
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
}
break;
new_colours = ZM_COLOUR_RGB32;
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
decodejpg_dcinfo->out_color_space = JCS_EXT_BGRX;
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA;
} else if (p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
decodejpg_dcinfo->out_color_space = JCS_EXT_XRGB;
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB;
} else if (p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
decodejpg_dcinfo->out_color_space = JCS_EXT_XBGR;
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
} else {
/* Assume RGBA */
decodejpg_dcinfo->out_color_space = JCS_EXT_RGBX;
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
}
break;
#else
Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead.");
Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead.");
#endif
case ZM_COLOUR_RGB24:
default:
new_colours = ZM_COLOUR_RGB24;
if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGR ) {
new_colours = ZM_COLOUR_RGB24;
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
#ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_BGR;
new_subpixelorder = ZM_SUBPIX_ORDER_BGR;
decodejpg_dcinfo->out_color_space = JCS_EXT_BGR;
new_subpixelorder = ZM_SUBPIX_ORDER_BGR;
#else
Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead.");
cinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead.");
cinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
#endif
} else {
/* Assume RGB */
/*
} else {
/* Assume RGB */
/*
#ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_RGB;
#else
cinfo->out_color_space = JCS_RGB;
#endif
*/
cinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
}
break;
*/
decodejpg_dcinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
}
break;
} // end switch
if ( WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == nullptr ) {
if (WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == nullptr) {
Error("Failed requesting writeable buffer for reading JPEG image.");
jpeg_abort_decompress(cinfo);
jpeg_abort_decompress(decodejpg_dcinfo);
return false;
}
jpeg_start_decompress(cinfo);
jpeg_start_decompress(decodejpg_dcinfo);
JSAMPROW row_pointer = buffer; /* pointer to a single row */
while ( cinfo->output_scanline < cinfo->output_height ) {
jpeg_read_scanlines(cinfo, &row_pointer, 1);
while (decodejpg_dcinfo->output_scanline < decodejpg_dcinfo->output_height) {
jpeg_read_scanlines(decodejpg_dcinfo, &row_pointer, 1);
row_pointer += linesize;
}
jpeg_finish_decompress(cinfo);
jpeg_finish_decompress(decodejpg_dcinfo);
return true;
}

View File

@ -181,7 +181,7 @@ class Image {
/* Internal buffer should not be modified from functions outside of this class */
inline const uint8_t* Buffer() const { return buffer; }
inline const uint8_t* Buffer(unsigned int x, unsigned int y=0) const { return &buffer[(y*linesize)+x]; }
inline const uint8_t* Buffer(unsigned int x, unsigned int y=0) const { return &buffer[(y*linesize) + x*colours]; }
/* Request writeable buffer */
uint8_t* WriteBuffer(const unsigned int p_width, const unsigned int p_height, const unsigned int p_colours, const unsigned int p_subpixelorder);
// Is only acceptable on a pre-allocated buffer
@ -233,14 +233,17 @@ class Image {
bool ReadRaw(const char *filename);
bool WriteRaw(const char *filename) const;
bool ReadJpeg(const char *filename, unsigned int p_colours, unsigned int p_subpixelorder);
bool ReadJpeg(const std::string &filename, unsigned int p_colours, unsigned int p_subpixelorder);
bool WriteJpeg(const char *filename) const;
bool WriteJpeg(const char *filename, bool on_blocking_abort) const;
bool WriteJpeg(const char *filename, int quality_override) const;
bool WriteJpeg(const char *filename, SystemTimePoint timestamp) const;
bool WriteJpeg(const char *filename, int quality_override, SystemTimePoint timestamp) const;
bool WriteJpeg(const char *filename, int quality_override, SystemTimePoint timestamp, bool on_blocking_abort) const;
bool WriteJpeg(const std::string &filename) const;
bool WriteJpeg(const std::string &filename, bool on_blocking_abort) const;
bool WriteJpeg(const std::string &filename, int quality_override) const;
bool WriteJpeg(const std::string &filename, SystemTimePoint timestamp) const;
bool WriteJpeg(const std::string &filename, int quality_override, SystemTimePoint timestamp) const;
bool WriteJpeg(const std::string &filename,
const int &quality_override,
SystemTimePoint timestamp,
bool on_blocking_abort) const;
bool DecodeJpeg(const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder);
bool EncodeJpeg(JOCTET *outbuffer, int *outbuffer_size, int quality_override=0) const;

View File

@ -37,13 +37,13 @@ void zm_jpeg_emit_silence(j_common_ptr cinfo, int msg_level) {
}
void zm_jpeg_error_exit(j_common_ptr cinfo) {
static char buffer[JMSG_LENGTH_MAX];
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
zm_error_ptr zmerr = (zm_error_ptr) cinfo->err;
(zmerr->pub.format_message)(cinfo, buffer);
char buffer[JMSG_LENGTH_MAX];
zmerr->pub.format_message(cinfo, buffer);
Error("%s", buffer);
if ( ++jpeg_err_count == MAX_JPEG_ERRS ) {
if (++jpeg_err_count == MAX_JPEG_ERRS) {
Fatal("Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count);
}
@ -51,25 +51,25 @@ void zm_jpeg_error_exit(j_common_ptr cinfo) {
}
void zm_jpeg_emit_message(j_common_ptr cinfo, int msg_level) {
static char buffer[JMSG_LENGTH_MAX];
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
char buffer[JMSG_LENGTH_MAX];
zm_error_ptr zmerr = (zm_error_ptr) cinfo->err;
if ( msg_level < 0 ) {
if (msg_level < 0) {
/* It's a warning message. Since corrupt files may generate many warnings,
* the policy implemented here is to show only the first warning,
* unless trace_level >= 3.
*/
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 ) {
(zmerr->pub.format_message)(cinfo, buffer);
if ( !strstr(buffer, "Corrupt JPEG data:") )
if (zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3) {
zmerr->pub.format_message(cinfo, buffer);
if (!strstr(buffer, "Corrupt JPEG data:"))
Warning("%s", buffer);
}
/* Always count warnings in num_warnings. */
zmerr->pub.num_warnings++;
} else {
/* It's a trace message. Show it if trace_level >= msg_level. */
if ( zmerr->pub.trace_level >= msg_level ) {
(zmerr->pub.format_message)(cinfo, buffer);
if (zmerr->pub.trace_level >= msg_level) {
zmerr->pub.format_message(cinfo, buffer);
Debug(msg_level, "%s", buffer);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,7 @@
#include "zm_camera.h"
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
#include <linux/videodev2.h>
@ -113,6 +113,7 @@ public:
int Palette() const { return palette; }
int Extras() const { return extras; }
int Control(int vid_id, int newvalue=-1 );
int Brightness( int p_brightness=-1 ) override;
int Hue( int p_hue=-1 ) override;
int Colour( int p_colour=-1 ) override;
@ -123,9 +124,9 @@ public:
int Capture(std::shared_ptr<ZMPacket> &p) override;
int PostCapture() override;
int Close() override;
static bool GetCurrentSettings(const char *device, char *output, int version, bool verbose);
static bool GetCurrentSettings(const std::string &device, char *output, int version, bool verbose);
};
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
#endif // ZM_LOCAL_CAMERA_H

View File

@ -518,18 +518,14 @@ void Logger::logPrint(bool hex, const char *filepath, int line, int level, const
if (level <= mDatabaseLevel) {
if (zmDbConnected) {
int syslogSize = syslogEnd - syslogStart;
std::string escapedString;
escapedString.resize((syslogSize * 2) + 1);
mysql_real_escape_string(&dbconn, &escapedString[0], syslogStart, syslogSize);
escapedString.resize(std::strlen(escapedString.c_str()));
std::string escapedString = zmDbEscapeString({syslogStart, syslogEnd});
std::string sql_string = stringtf(
"INSERT INTO `Logs` "
"( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )"
" VALUES "
"( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
now_sec, now_frac.count(), mId.c_str(), staticConfig.SERVER_ID, tid, level, classString,
"( %ld.%06" PRIi64 ", '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
now_sec, static_cast<int64>(now_frac.count()), mId.c_str(), staticConfig.SERVER_ID, tid, level, classString,
escapedString.c_str(), file, line);
dbQueue.push(std::move(sql_string));
} else {

File diff suppressed because it is too large Load Diff

View File

@ -64,7 +64,7 @@ public:
} Function;
typedef enum {
LOCAL,
LOCAL=1,
REMOTE,
FILE,
FFMPEG,
@ -313,15 +313,15 @@ protected:
int pre_event_count; // How many images to hold and prepend to an alarm event
int post_event_count; // How many unalarmed images must occur before the alarm state is reset
int stream_replay_buffer; // How many frames to store to support DVR functions, IGNORED from this object, passed directly into zms now
int section_length; // How long events should last in continuous modes
int min_section_length; // Minimum event length when using event_close_mode == ALARM
Seconds section_length; // How long events should last in continuous modes
Seconds min_section_length; // Minimum event length when using event_close_mode == ALARM
bool adaptive_skip; // Whether to use the newer adaptive algorithm for this monitor
int frame_skip; // How many frames to skip in continuous modes
int motion_frame_skip; // How many frames to skip in motion detection
double analysis_fps_limit; // Target framerate for video analysis
unsigned int analysis_update_delay; // How long we wait before updating analysis parameters
int capture_delay; // How long we wait between capture frames
int alarm_capture_delay; // How long we wait between capture frames when in alarm state
Microseconds analysis_update_delay; // How long we wait before updating analysis parameters
Microseconds capture_delay; // How long we wait between capture frames
Microseconds alarm_capture_delay; // How long we wait between capture frames when in alarm state
int alarm_frame_count; // How many alarm frames are required before an event is triggered
int alert_to_alarm_frame_count; // How many alarm frames (consecutive score frames) are required to return alarm from alert
// value for now is the same number configured in alarm_frame_count, maybe getting his own parameter some day
@ -424,7 +424,6 @@ protected:
public:
explicit Monitor();
explicit Monitor(unsigned int p_id);
~Monitor();
@ -453,7 +452,7 @@ public:
inline unsigned int Id() const { return id; }
inline const char *Name() const { return name.c_str(); }
inline unsigned int ServerId() { return server_id; }
inline unsigned int ServerId() const { return server_id; }
inline Storage *getStorage() {
if ( ! storage ) {
storage = new Storage(storage_id);
@ -486,7 +485,7 @@ public:
}
inline bool Exif() const { return embed_exif; }
inline bool RTSPServer() const { return rtsp_server; }
inline bool RecordAudio() { return record_audio; }
inline bool RecordAudio() const { return record_audio; }
/*
inline Purpose Purpose() { return purpose };
@ -513,8 +512,12 @@ public:
uint64_t GetVideoWriterEventId() const { return video_store_data->current_event; }
void SetVideoWriterEventId( uint64_t p_event_id ) { video_store_data->current_event = p_event_id; }
struct timeval GetVideoWriterStartTime() const { return video_store_data->recording; }
void SetVideoWriterStartTime(const struct timeval &t) { video_store_data->recording = t; }
SystemTimePoint GetVideoWriterStartTime() const {
return SystemTimePoint(zm::chrono::duration_cast<Microseconds>(video_store_data->recording));
}
void SetVideoWriterStartTime(SystemTimePoint t) {
video_store_data->recording = zm::chrono::duration_cast<timeval>(t.time_since_epoch());
}
unsigned int GetPreEventCount() const { return pre_event_count; };
int32_t GetImageBufferCount() const { return image_buffer_count; };
@ -525,20 +528,20 @@ public:
AVStream *GetVideoStream() const { return camera ? camera->getVideoStream() : nullptr; };
AVCodecContext *GetVideoCodecContext() const { return camera ? camera->getVideoCodecContext() : nullptr; };
const std::string GetSecondPath() const { return second_path; };
const std::string GetVideoFifoPath() const { return shared_data ? shared_data->video_fifo_path : ""; };
const std::string GetAudioFifoPath() const { return shared_data ? shared_data->audio_fifo_path : ""; };
const std::string GetRTSPStreamName() const { return rtsp_streamname; };
std::string GetSecondPath() const { return second_path; };
std::string GetVideoFifoPath() const { return shared_data ? shared_data->video_fifo_path : ""; };
std::string GetAudioFifoPath() const { return shared_data ? shared_data->audio_fifo_path : ""; };
std::string GetRTSPStreamName() const { return rtsp_streamname; };
int GetImage(int32_t index=-1, int scale=100);
ZMPacket *getSnapshot( int index=-1 ) const;
struct timeval GetTimestamp( int index=-1 ) const;
SystemTimePoint GetTimestamp(int index = -1) const;
void UpdateAdaptiveSkip();
useconds_t GetAnalysisRate();
unsigned int GetAnalysisUpdateDelay() const { return analysis_update_delay; }
Microseconds GetAnalysisUpdateDelay() const { return analysis_update_delay; }
unsigned int GetCaptureMaxFPS() const { return capture_max_fps; }
int GetCaptureDelay() const { return capture_delay; }
int GetAlarmCaptureDelay() const { return alarm_capture_delay; }
Microseconds GetCaptureDelay() const { return capture_delay; }
Microseconds GetAlarmCaptureDelay() const { return alarm_capture_delay; }
unsigned int GetLastReadIndex() const;
unsigned int GetLastWriteIndex() const;
uint64_t GetLastEventId() const;
@ -549,9 +552,11 @@ public:
void ForceAlarmOff();
void CancelForced();
TriggerState GetTriggerState() const { return trigger_data ? trigger_data->trigger_state : TRIGGER_CANCEL; }
inline time_t getStartupTime() const { return shared_data->startup_time; }
inline void setStartupTime( time_t p_time ) { shared_data->startup_time = p_time; }
inline void setHeartbeatTime( time_t p_time ) { shared_data->zmc_heartbeat_time = p_time; }
SystemTimePoint GetStartupTime() const { return std::chrono::system_clock::from_time_t(shared_data->startup_time); }
void SetStartupTime(SystemTimePoint time) { shared_data->startup_time = std::chrono::system_clock::to_time_t(time); }
void SetHeartbeatTime(SystemTimePoint time) {
shared_data->zmc_heartbeat_time = std::chrono::system_clock::to_time_t(time);
}
void get_ref_image();
int LabelSize() const { return label_size; }
@ -562,10 +567,14 @@ public:
void actionSuspend();
void actionResume();
int actionBrightness( int p_brightness=-1 );
int actionHue( int p_hue=-1 );
int actionColour( int p_colour=-1 );
int actionContrast( int p_contrast=-1 );
int actionBrightness(int p_brightness);
int actionBrightness();
int actionHue(int p_hue);
int actionHue();
int actionColour(int p_colour);
int actionColour();
int actionContrast(int p_contrast);
int actionContrast();
int PrimeCapture();
int PreCapture() const;
@ -582,7 +591,7 @@ public:
bool Analyse();
bool Decode();
void DumpImage( Image *dump_image ) const;
void TimestampImage(Image *ts_image, const timeval &ts_time) const;
void TimestampImage(Image *ts_image, SystemTimePoint ts_time) const;
void closeEvent();
void Reload();
@ -595,9 +604,9 @@ public:
StringVector GroupNames();
static std::vector<std::shared_ptr<Monitor>> LoadMonitors(const std::string &sql, Purpose purpose); // Returns # of Monitors loaded, 0 on failure.
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
static std::vector<std::shared_ptr<Monitor>> LoadLocalMonitors(const char *device, Purpose purpose);
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
static std::vector<std::shared_ptr<Monitor>> LoadRemoteMonitors(const char *protocol, const char *host, const char*port, const char*path, Purpose purpose);
static std::vector<std::shared_ptr<Monitor>> LoadFileMonitors(const char *file, Purpose purpose);
static std::vector<std::shared_ptr<Monitor>> LoadFfmpegMonitors(const char *file, Purpose purpose);
@ -614,7 +623,7 @@ public:
double get_analysis_fps( ) const {
return shared_data ? shared_data->analysis_fps : 0.0;
}
int Importance() { return importance; }
int Importance() const { return importance; }
};
#define MOD_ADD( var, delta, limit ) (((var)+(limit)+(delta))%(limit))

View File

@ -89,7 +89,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
break;
case CMD_PLAY :
Debug(1, "Got PLAY command");
if ( paused ) {
if (paused) {
paused = false;
delayed = true;
}
@ -97,7 +97,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
break;
case CMD_VARPLAY :
Debug(1, "Got VARPLAY command");
if ( paused ) {
if (paused) {
paused = false;
delayed = true;
}
@ -110,7 +110,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
break;
case CMD_FASTFWD :
Debug(1, "Got FAST FWD command");
if ( paused ) {
if (paused) {
paused = false;
delayed = true;
}
@ -135,27 +135,27 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
}
break;
case CMD_SLOWFWD :
Debug( 1, "Got SLOW FWD command" );
Debug(1, "Got SLOW FWD command");
paused = true;
delayed = true;
replay_rate = ZM_RATE_BASE;
step = 1;
break;
case CMD_SLOWREV :
Debug( 1, "Got SLOW REV command" );
Debug(1, "Got SLOW REV command");
paused = true;
delayed = true;
replay_rate = ZM_RATE_BASE;
step = -1;
break;
case CMD_FASTREV :
Debug( 1, "Got FAST REV command" );
if ( paused ) {
Debug(1, "Got FAST REV command");
if (paused) {
paused = false;
delayed = true;
}
// Set play rate
switch ( replay_rate ) {
switch (replay_rate) {
case -2 * ZM_RATE_BASE :
replay_rate = -5 * ZM_RATE_BASE;
break;
@ -255,7 +255,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
} status_data;
status_data.id = monitor->Id();
if ( ! monitor->ShmValid() ) {
if (!monitor->ShmValid()) {
status_data.fps = 0.0;
status_data.capture_fps = 0.0;
status_data.analysis_fps = 0.0;
@ -265,7 +265,14 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
status_data.forced = false;
status_data.buffer_level = 0;
} else {
status_data.fps = monitor->GetFPS();
FPSeconds elapsed = now - last_fps_update;
if (elapsed.count()) {
actual_fps = (frame_count - last_frame_count) / elapsed.count();
last_frame_count = frame_count;
last_fps_update = now;
}
status_data.fps = actual_fps;
status_data.capture_fps = monitor->get_capture_fps();
status_data.analysis_fps = monitor->get_analysis_fps();
status_data.state = monitor->shared_data->state;
@ -320,7 +327,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
//updateFrameRate(monitor->GetFPS());
} // end void MonitorStream::processCommand(const CmdMsg *msg)
bool MonitorStream::sendFrame(const char *filepath, SystemTimePoint timestamp) {
bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint timestamp) {
bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE));
if (
@ -330,19 +337,18 @@ bool MonitorStream::sendFrame(const char *filepath, SystemTimePoint timestamp) {
)
send_raw = false;
if ( !send_raw ) {
Image temp_image(filepath);
if (!send_raw) {
Image temp_image(filepath.c_str());
return sendFrame(&temp_image, timestamp);
} else {
int img_buffer_size = 0;
static unsigned char img_buffer[ZM_MAX_IMAGE_SIZE];
FILE *fdj = nullptr;
if ( (fdj = fopen(filepath, "r")) ) {
if (FILE *fdj = fopen(filepath.c_str(), "r")) {
img_buffer_size = fread(img_buffer, 1, sizeof(img_buffer), fdj);
fclose(fdj);
} else {
Error("Can't open %s: %s", filepath, strerror(errno));
Error("Can't open %s: %s", filepath.c_str(), strerror(errno));
return false;
}
@ -377,12 +383,12 @@ bool MonitorStream::sendFrame(const char *filepath, SystemTimePoint timestamp) {
return true;
}
return false;
} // end bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp)
}
bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
Image *send_image = prepareImage(image);
if (!config.timestamp_on_capture) {
monitor->TimestampImage(send_image, zm::chrono::duration_cast<timeval>(timestamp.time_since_epoch()));
monitor->TimestampImage(send_image, timestamp);
}
fputs("--" BOUNDARY "\r\n", stdout);
@ -461,15 +467,17 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
} // Not mpeg
last_frame_sent = now;
return true;
} // end bool MonitorStream::sendFrame( Image *image, const timeval &timestamp )
}
void MonitorStream::runStream() {
if (type == STREAM_SINGLE) {
// Not yet migrated over to stream class
if (checkInitialised())
if (checkInitialised()) {
SingleImage(scale);
else
} else {
fputs("Content-Type: multipart/x-mixed-replace; boundary=" BOUNDARY "\r\n\r\n", stdout);
sendTextFrame("Unable to send image");
}
return;
}
@ -518,7 +526,7 @@ void MonitorStream::runStream() {
Image *paused_image = nullptr;
SystemTimePoint paused_timestamp;
if ( connkey && ( playback_buffer > 0 ) ) {
if (connkey && (playback_buffer > 0)) {
// 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
const int max_swap_len_suffix = 15;
@ -527,27 +535,27 @@ void MonitorStream::runStream() {
int subfolder2_length = snprintf(nullptr, 0, "/zmswap-q%06d", connkey) + 1;
int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;
if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) {
if (total_swap_path_length + max_swap_len_suffix > PATH_MAX) {
Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
} else {
swap_path = staticConfig.PATH_SWAP;
Debug(3, "Checking swap path folder: %s", swap_path.c_str());
if ( checkSwapPath(swap_path.c_str(), true) ) {
if (checkSwapPath(swap_path.c_str(), true)) {
swap_path += stringtf("/zmswap-m%d", monitor->Id());
Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
if ( checkSwapPath(swap_path.c_str(), true) ) {
if (checkSwapPath(swap_path.c_str(), true)) {
swap_path += stringtf("/zmswap-q%06d", connkey);
Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
if ( checkSwapPath(swap_path.c_str(), true) ) {
if (checkSwapPath(swap_path.c_str(), true)) {
buffered_playback = true;
}
}
}
if ( !buffered_playback ) {
if (!buffered_playback) {
Error("Unable to validate swap image path, disabling buffered playback");
} else {
Debug(2, "Assigning temporary buffer");
@ -557,14 +565,13 @@ void MonitorStream::runStream() {
}
} else {
Debug(2, "Not using playback_buffer");
} // end if connkey & playback_buffer
} // end if connkey && playback_buffer
while (!zm_terminate) {
bool got_command = false;
if ( feof(stdout) ) {
if (feof(stdout)) {
Debug(2, "feof stdout");
break;
} else if ( ferror(stdout) ) {
} else if (ferror(stdout)) {
Debug(2, "ferror stdout");
break;
} else if (!monitor->ShmValid()) {
@ -575,8 +582,9 @@ void MonitorStream::runStream() {
now = std::chrono::system_clock::now();
bool was_paused = paused;
if ( connkey ) {
while ( checkCommandQueue() && !zm_terminate ) {
bool got_command = false; // commands like zoom should output a frame even if paused
if (connkey) {
while (checkCommandQueue() && !zm_terminate) {
// Loop in here until all commands are processed.
Debug(2, "Have checking command Queue for connkey: %d", connkey);
got_command = true;
@ -588,35 +596,31 @@ void MonitorStream::runStream() {
}
} // end if connkey
if ( paused ) {
if ( !was_paused ) {
if (paused) {
if (!was_paused) {
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
Debug(1, "Saving paused image from index %d",index);
paused_image = new Image(*monitor->image_buffer[index]);
paused_timestamp = SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
}
} else if ( paused_image ) {
Debug(1, "Clearing paused_image");
} else if (paused_image) {
delete paused_image;
paused_image = nullptr;
}
if ( buffered_playback && delayed ) {
if ( temp_read_index == temp_write_index ) {
if (buffered_playback && delayed) {
if (temp_read_index == temp_write_index) {
// Go back to live viewing
Debug(1, "Exceeded temporary streaming buffer");
// Clear paused flag
paused = false;
// Clear delayed_play flag
delayed = false;
replay_rate = ZM_RATE_BASE;
} else {
if ( !paused ) {
if (!paused) {
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
// Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index );
SwapImage *swap_image = &temp_image_buffer[temp_index];
if ( !swap_image->valid ) {
if (!swap_image->valid) {
paused = true;
delayed = true;
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
@ -627,12 +631,13 @@ void MonitorStream::runStream() {
// If the next frame is due
if (actual_delta_time > expected_delta_time) {
// Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time );
if (temp_index % frame_mod == 0) {
if ((temp_index % frame_mod) == 0) {
Debug(2, "Sending delayed frame %d", temp_index);
// Send the next frame
if (!sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp)) {
zm_terminate = true;
}
frame_count++;
last_frame_timestamp = swap_image->timestamp;
// frame_sent = true;
}
@ -645,9 +650,13 @@ void MonitorStream::runStream() {
SwapImage *swap_image = &temp_image_buffer[temp_read_index];
// Send the next frame
if (!sendFrame(temp_image_buffer[temp_read_index].file_name, temp_image_buffer[temp_read_index].timestamp)) {
if (!sendFrame(
temp_image_buffer[temp_read_index].file_name,
temp_image_buffer[temp_read_index].timestamp)
) {
zm_terminate = true;
}
frame_count++;
last_frame_timestamp = swap_image->timestamp;
// frame_sent = true;
@ -663,12 +672,13 @@ void MonitorStream::runStream() {
if (!sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp)) {
zm_terminate = true;
}
frame_count++;
// frame_sent = true;
}
} // end if (!paused) or step or paused
} // end if have exceeded buffer or not
if ( temp_read_index == temp_write_index ) {
if (temp_read_index == temp_write_index) {
// Go back to live viewing
Warning("Rewound over write index, resuming live play");
// Clear paused flag
@ -677,13 +687,13 @@ void MonitorStream::runStream() {
delayed = false;
replay_rate = ZM_RATE_BASE;
}
} // end if ( buffered_playback && delayed )
} // end if (buffered_playback && delayed)
if ( last_read_index != monitor->shared_data->last_write_index ) {
if (last_read_index != monitor->shared_data->last_write_index) {
// have a new image to send
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
if ( !paused && !delayed ) {
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
if ((frame_mod == 1) || ((frame_count%frame_mod) == 0)) {
if (!paused && !delayed) {
last_read_index = monitor->shared_data->last_write_index;
Debug(2, "Sending frame index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)",
index, frame_mod, frame_count, paused, delayed);
@ -699,8 +709,7 @@ void MonitorStream::runStream() {
zm_terminate = true;
break;
}
//frame_sent = true;
//
frame_count++;
if (frame_count == 0) {
// Chrome will not display the first frame until it receives another.
// Firefox is fine. So just send the first frame twice.
@ -713,16 +722,18 @@ void MonitorStream::runStream() {
temp_read_index = temp_write_index;
} else {
if ( delayed && !buffered_playback ) {
if (delayed && !buffered_playback) {
Debug(2, "Can't delay when not buffering.");
delayed = false;
}
if ( last_zoom != zoom ) {
if (last_zoom != zoom) {
Debug(2, "Sending 2 frames because change in zoom %d ?= %d", last_zoom, zoom);
if (!sendFrame(paused_image, paused_timestamp))
zm_terminate = true;
if (!sendFrame(paused_image, paused_timestamp))
zm_terminate = true;
frame_count++;
frame_count++;
} else {
SystemTimePoint::duration actual_delta_time = now - last_frame_sent;
if (actual_delta_time > Seconds(5)) {
@ -733,37 +744,32 @@ void MonitorStream::runStream() {
// Send the next frame
if (!sendFrame(paused_image, paused_timestamp))
zm_terminate = true;
frame_count++;
} else {
Debug(2, "Would have sent keepalive frame, but had no paused_image");
}
} // end if actual_delta_time > 5
} // end if change in zoom
} // end if paused or not
} else {
frame_count++;
} // end if should send frame
if ( buffered_playback && !paused ) {
if ( monitor->shared_data->valid ) {
if ( monitor->shared_timestamps[index].tv_sec ) {
if (buffered_playback && !paused) {
if (monitor->shared_data->valid) {
if (monitor->shared_timestamps[index].tv_sec) {
int temp_index = temp_write_index%temp_image_buffer_count;
Debug(2, "Storing frame %d", temp_index);
if ( !temp_image_buffer[temp_index].valid ) {
snprintf(
temp_image_buffer[temp_index].file_name,
sizeof(temp_image_buffer[0].file_name),
"%s/zmswap-i%05d.jpg",
swap_path.c_str(),
temp_index);
temp_image_buffer[temp_index].file_name = stringtf("%s/zmswap-i%05d.jpg", swap_path.c_str(), temp_index);
temp_image_buffer[temp_index].valid = true;
}
temp_image_buffer[temp_index].timestamp =
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
monitor->image_buffer[index]->WriteJpeg(
temp_image_buffer[temp_index].file_name,
config.jpeg_file_quality
);
monitor->image_buffer[index]->WriteJpeg(temp_image_buffer[temp_index].file_name, config.jpeg_file_quality);
temp_write_index = MOD_ADD(temp_write_index, 1, temp_image_buffer_count);
if ( temp_write_index == temp_read_index ) {
if (temp_write_index == temp_read_index) {
// Go back to live viewing
Warning("Exceeded temporary buffer, resuming live play");
paused = false;
@ -777,7 +783,6 @@ void MonitorStream::runStream() {
Warning("Unable to store frame as shared memory invalid");
}
} // end if buffered playback
frame_count++;
} else {
Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index);
} // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index )
@ -808,38 +813,37 @@ void MonitorStream::runStream() {
Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)",
frame_mod, frame_count);
}
} // end while
} // end while ! zm_terminate
if ( buffered_playback ) {
if (buffered_playback) {
Debug(1, "Cleaning swap files from %s", swap_path.c_str());
struct stat stat_buf;
if ( stat(swap_path.c_str(), &stat_buf) < 0 ) {
if ( errno != ENOENT ) {
struct stat stat_buf = {};
if (stat(swap_path.c_str(), &stat_buf) < 0) {
if (errno != ENOENT) {
Error("Can't stat '%s': %s", swap_path.c_str(), strerror(errno));
}
} else if ( !S_ISDIR(stat_buf.st_mode) ) {
} else if (!S_ISDIR(stat_buf.st_mode)) {
Error("Swap image path '%s' is not a directory", swap_path.c_str());
} else {
char glob_pattern[PATH_MAX] = "";
snprintf(glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path.c_str());
std::string glob_pattern = stringtf("%s/*.*", swap_path.c_str());
glob_t pglob;
int glob_status = glob(glob_pattern, 0, 0, &pglob);
if ( glob_status != 0 ) {
if ( glob_status < 0 ) {
Error("Can't glob '%s': %s", glob_pattern, strerror(errno));
int glob_status = glob(glob_pattern.c_str(), 0, 0, &pglob);
if (glob_status != 0) {
if (glob_status < 0) {
Error("Can't glob '%s': %s", glob_pattern.c_str(), strerror(errno));
} else {
Debug(1, "Can't glob '%s': %d", glob_pattern, glob_status);
Debug(1, "Can't glob '%s': %d", glob_pattern.c_str(), glob_status);
}
} else {
for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) {
if ( unlink(pglob.gl_pathv[i]) < 0 ) {
for (unsigned int i = 0; i < pglob.gl_pathc; i++) {
if (unlink(pglob.gl_pathv[i]) < 0) {
Error("Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno));
}
}
}
globfree(&pglob);
if ( rmdir(swap_path.c_str()) < 0 ) {
if (rmdir(swap_path.c_str()) < 0) {
Error("Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno));
}
} // end if checking for swap_path
@ -854,7 +858,7 @@ void MonitorStream::SingleImage(int scale) {
Image scaled_image;
while ((monitor->shared_data->last_write_index >= monitor->image_buffer_count) and !zm_terminate) {
Debug(1, "Waiting for capture to begin");
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
}
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
Debug(1, "write index: %d %d", monitor->shared_data->last_write_index, index);
@ -865,8 +869,9 @@ void MonitorStream::SingleImage(int scale) {
scaled_image.Scale(scale);
snap_image = &scaled_image;
}
if ( !config.timestamp_on_capture ) {
monitor->TimestampImage(snap_image, monitor->shared_timestamps[index]);
if (!config.timestamp_on_capture) {
monitor->TimestampImage(snap_image,
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index])));
}
snap_image->EncodeJpeg(img_buffer, &img_buffer_size);

View File

@ -21,14 +21,13 @@
#define ZM_MONITORSTREAM_H
#include "zm_stream.h"
#include <sys/time.h>
class MonitorStream : public StreamBase {
protected:
struct SwapImage {
bool valid = false;
SystemTimePoint timestamp;
char file_name[PATH_MAX] = "";
std::string file_name;
};
private:
@ -41,11 +40,10 @@ class MonitorStream : public StreamBase {
Microseconds ttl;
int playback_buffer;
bool delayed;
int frame_count;
protected:
bool checkSwapPath(const char *path, bool create_path);
bool sendFrame(const char *filepath, SystemTimePoint timestamp);
bool sendFrame(const std::string &filepath, SystemTimePoint timestamp);
bool sendFrame(Image *image, SystemTimePoint timestamp);
void processCommand(const CmdMsg *msg) override;
void SingleImage(int scale=100);
@ -62,8 +60,8 @@ class MonitorStream : public StreamBase {
temp_write_index(0),
ttl(0),
playback_buffer(0),
delayed(false),
frame_count(0) {}
delayed(false)
{}
void setStreamBuffer(int p_playback_buffer) {
playback_buffer = p_playback_buffer;

View File

@ -21,13 +21,7 @@
#include "zm_logger.h"
#include "zm_rgb.h"
#include <cstring>
#include <unistd.h>
extern "C" {
#include <libavutil/mathematics.h>
#include <libavcodec/avcodec.h>
}
#include "zm_time.h"
bool VideoStream::initialised = false;
@ -537,32 +531,29 @@ int VideoStream::SendPacket(AVPacket *packet) {
return ret;
}
void *VideoStream::StreamingThreadCallback(void *ctx){
Debug( 1, "StreamingThreadCallback started" );
if (ctx == nullptr) return nullptr;
void *VideoStream::StreamingThreadCallback(void *ctx) {
Debug(1, "StreamingThreadCallback started");
VideoStream* videoStream = reinterpret_cast<VideoStream*>(ctx);
if (ctx == nullptr) {
return nullptr;
}
const uint64_t nanosecond_multiplier = 1000000000;
VideoStream *videoStream = reinterpret_cast<VideoStream *>(ctx);
uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->codec_context->time_base.num) / (videoStream->codec_context->time_base.den) );
TimePoint::duration target_interval = std::chrono::duration_cast<TimePoint::duration>(FPSeconds(
videoStream->codec_context->time_base.num / static_cast<double>(videoStream->codec_context->time_base.den)));
uint64_t frame_count = 0;
timespec start_time;
clock_gettime(CLOCK_MONOTONIC, &start_time);
uint64_t start_time_ns = (start_time.tv_sec*nanosecond_multiplier) + start_time.tv_nsec;
while(videoStream->do_streaming) {
timespec current_time;
clock_gettime(CLOCK_MONOTONIC, &current_time);
uint64_t current_time_ns = (current_time.tv_sec*nanosecond_multiplier) + current_time.tv_nsec;
uint64_t target_ns = start_time_ns + (target_interval_ns * frame_count);
if ( current_time_ns < target_ns ) {
// It's not time to render a frame yet.
usleep( (target_ns - current_time_ns) * 0.001 );
}
uint64_t frame_count = 0;
TimePoint start_time = std::chrono::steady_clock::now();
while (videoStream->do_streaming) {
TimePoint current_time = std::chrono::steady_clock::now();
TimePoint target = start_time + (target_interval * frame_count);
if (current_time < target) {
// It's not time to render a frame yet.
std::this_thread::sleep_for(target - current_time);
}
// By sending the last rendered frame we deliver frames to the client more accurate.
// If we're encoding the frame before sending it there will be lag.
@ -573,27 +564,29 @@ void *VideoStream::StreamingThreadCallback(void *ctx){
if (packet->size) {
videoStream->SendPacket(packet);
}
av_packet_unref( packet);
av_packet_unref(packet);
videoStream->packet_index = videoStream->packet_index ? 0 : 1;
// Lock buffer and render next frame.
if ( pthread_mutex_lock( videoStream->buffer_copy_lock ) != 0 ) {
Fatal( "StreamingThreadCallback: pthread_mutex_lock failed." );
}
if ( videoStream->buffer_copy ) {
// Encode next frame.
videoStream->ActuallyEncodeFrame( videoStream->buffer_copy, videoStream->buffer_copy_used, videoStream->add_timestamp, videoStream->timestamp );
}
if ( pthread_mutex_unlock( videoStream->buffer_copy_lock ) != 0 ) {
Fatal( "StreamingThreadCallback: pthread_mutex_unlock failed." );
}
frame_count++;
}
return nullptr;
if (pthread_mutex_lock(videoStream->buffer_copy_lock) != 0) {
Fatal("StreamingThreadCallback: pthread_mutex_lock failed.");
}
if (videoStream->buffer_copy) {
// Encode next frame.
videoStream->ActuallyEncodeFrame(videoStream->buffer_copy,
videoStream->buffer_copy_used,
videoStream->add_timestamp,
videoStream->timestamp);
}
if (pthread_mutex_unlock(videoStream->buffer_copy_lock) != 0) {
Fatal("StreamingThreadCallback: pthread_mutex_unlock failed.");
}
frame_count++;
}
return nullptr;
}

View File

@ -21,7 +21,6 @@
#include "zm_ffmpeg.h"
#include "zm_image.h"
#include "zm_logger.h"
#include <sys/time.h>
using namespace std;
AVPixelFormat target_format = AV_PIX_FMT_NONE;
@ -31,7 +30,6 @@ ZMPacket::ZMPacket() :
stream(nullptr),
in_frame(nullptr),
out_frame(nullptr),
timestamp({}),
buffer(nullptr),
image(nullptr),
analysis_image(nullptr),
@ -40,13 +38,13 @@ ZMPacket::ZMPacket() :
image_index(-1),
codec_imgsize(0),
pts(0),
decoded(0)
decoded(false)
{
av_init_packet(&packet);
packet.size = 0; // So we can detect whether it has been filled.
}
ZMPacket::ZMPacket(Image *i, const timeval &tv) :
ZMPacket::ZMPacket(Image *i, SystemTimePoint tv) :
keyframe(0),
stream(nullptr),
in_frame(nullptr),
@ -60,7 +58,7 @@ ZMPacket::ZMPacket(Image *i, const timeval &tv) :
image_index(-1),
codec_imgsize(0),
pts(0),
decoded(0)
decoded(false)
{
av_init_packet(&packet);
packet.size = 0; // So we can detect whether it has been filled.
@ -80,12 +78,12 @@ ZMPacket::ZMPacket(ZMPacket &p) :
image_index(-1),
codec_imgsize(0),
pts(0),
decoded(0)
decoded(false)
{
av_init_packet(&packet);
packet.size = 0;
packet.data = nullptr;
if ( zm_av_packet_ref(&packet, &p.packet) < 0 ) {
if (zm_av_packet_ref(&packet, &p.packet) < 0) {
Error("error refing packet");
}
}
@ -95,8 +93,8 @@ ZMPacket::~ZMPacket() {
if (in_frame) av_frame_free(&in_frame);
if (out_frame) av_frame_free(&out_frame);
if (buffer) av_freep(&buffer);
if (analysis_image) delete analysis_image;
if (image) delete image;
delete analysis_image;
delete image;
}
/* returns < 0 on error, 0 on not ready, int bytes consumed on success
@ -243,8 +241,8 @@ AVPacket *ZMPacket::set_packet(AVPacket *p) {
if (zm_av_packet_ref(&packet, p) < 0) {
Error("error refing packet");
}
//ZM_DUMP_PACKET(packet, "zmpacket:");
gettimeofday(&timestamp, nullptr);
timestamp = std::chrono::system_clock::now();
keyframe = p->flags & AV_PKT_FLAG_KEY;
return &packet;
}

View File

@ -21,6 +21,7 @@
#define ZM_PACKET_H
#include "zm_logger.h"
#include "zm_time.h"
#include "zm_zone.h"
#include <condition_variable>
@ -31,10 +32,6 @@ extern "C" {
#include <libavformat/avformat.h>
}
#ifdef __FreeBSD__
#include <sys/time.h>
#endif // __FreeBSD__
class Image;
class ZMPacket {
@ -49,7 +46,7 @@ class ZMPacket {
AVPacket packet; // Input packet, undecoded
AVFrame *in_frame; // Input image, decoded Theoretically only filled if needed.
AVFrame *out_frame; // output image, Only filled if needed.
timeval timestamp;
SystemTimePoint timestamp;
uint8_t *buffer; // buffer used in image
Image *image;
Image *analysis_image;
@ -69,8 +66,8 @@ class ZMPacket {
Image *set_image(Image *);
int is_keyframe() { return keyframe; };
int decode( AVCodecContext *ctx );
explicit ZMPacket(Image *image, const timeval &tv);
int decode(AVCodecContext *ctx);
explicit ZMPacket(Image *image, SystemTimePoint tv);
explicit ZMPacket(ZMPacket &packet);
ZMPacket();
~ZMPacket();
@ -91,6 +88,7 @@ class ZMLockedPacket {
lck_(packet_->mutex_, std::defer_lock),
locked(false) {
}
~ZMLockedPacket() {
if (locked) unlock();
}

View File

@ -24,7 +24,6 @@
#include "zm_ffmpeg.h"
#include "zm_packet.h"
#include "zm_signal.h"
#include <sys/time.h>
PacketQueue::PacketQueue():
video_stream_id(-1),
@ -93,8 +92,10 @@ bool PacketQueue::queuePacket(std::shared_ptr<ZMPacket> add_packet) {
" The queue is full. Either Analysis is not keeping up or"
" your camera's keyframe interval is larger than this setting."
, max_video_packet_count);
while (packet_counts[video_stream_id] > max_video_packet_count) {
Debug(1, "Capture waiting for room in the queue.");
Error("Unable to free up older packets. Waiting.");
condition.notify_all();
condition.wait(lck);
if (deleting or zm_terminate)
return false;
@ -153,6 +154,7 @@ void PacketQueue::clearPackets(const std::shared_ptr<ZMPacket> &add_packet) {
add_packet->packet.stream_index, video_stream_id, add_packet->keyframe, keep_keyframes, packet_counts[video_stream_id], pre_event_video_packet_count,
( *(pktQueue.begin()) != add_packet )
);
Warning("Keyframe interval may be larger than MaxImageBufferCount and PreEventCount. Please increase MaxImageBufferCount");
return;
}
std::unique_lock<std::mutex> lck(mutex);
@ -620,3 +622,12 @@ void PacketQueue::setPreEventVideoPackets(int p) {
pre_event_video_packet_count = 1;
// We can simplify a lot of logic in queuePacket if we can assume at least 1 packet in queue
}
void PacketQueue::notify_all() {
condition.notify_all();
};
void PacketQueue::wait() {
std::unique_lock<std::mutex> lck(mutex);
condition.wait(lck);
}

View File

@ -81,6 +81,8 @@ class PacketQueue {
);
bool is_there_an_iterator_pointing_to_packet(const std::shared_ptr<ZMPacket> &zm_packet);
void unlock(ZMLockedPacket *lp);
void notify_all();
void wait();
};
#endif /* ZM_PACKETQUEUE_H */

View File

@ -49,45 +49,43 @@ RemoteCamera::RemoteCamera(
mNeedAuth(false),
mAuthenticator(nullptr)
{
if ( path[0] != '/' )
if (path[0] != '/')
path = '/'+path;
}
RemoteCamera::~RemoteCamera() {
if ( hp != nullptr ) {
if (hp != nullptr) {
freeaddrinfo(hp);
hp = nullptr;
}
if ( mAuthenticator ) {
if (mAuthenticator) {
delete mAuthenticator;
mAuthenticator = nullptr;
}
}
void RemoteCamera::Initialise() {
if( protocol.empty() )
Fatal( "No protocol specified for remote camera" );
if (protocol.empty())
Fatal("No protocol specified for remote camera");
if( host.empty() )
Fatal( "No host specified for remote camera" );
if (host.empty())
Fatal("No host specified for remote camera");
if ( port.empty() )
if (port.empty())
Fatal("No port specified for remote camera");
//if( path.empty() )
//Fatal( "No path specified for remote camera" );
// Cache as much as we can to speed things up
std::string::size_type authIndex = host.rfind( '@' );
std::string::size_type authIndex = host.rfind('@');
if ( authIndex != std::string::npos ) {
auth = host.substr( 0, authIndex );
host.erase( 0, authIndex+1 );
if (authIndex != std::string::npos) {
auth = host.substr(0, authIndex);
host.erase(0, authIndex+1);
auth64 = Base64Encode(auth);
authIndex = auth.rfind( ':' );
authIndex = auth.rfind(':');
username = auth.substr(0,authIndex);
password = auth.substr( authIndex+1, auth.length() );
password = auth.substr(authIndex+1, auth.length());
}
mNeedAuth = false;
@ -99,12 +97,13 @@ void RemoteCamera::Initialise() {
hints.ai_socktype = SOCK_STREAM;
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
if ( ret != 0 ) {
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
if (ret != 0) {
Error("Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret));
return;
}
struct addrinfo *p = nullptr;
int addr_count = 0;
for ( p = hp; p != nullptr; p = p->ai_next ) {
for (p = hp; p != nullptr; p = p->ai_next) {
addr_count++;
}
Debug(1, "%d addresses returned", addr_count);

View File

@ -144,6 +144,14 @@ void RemoteCameraHttp::Initialise() {
int RemoteCameraHttp::Connect() {
struct addrinfo *p = nullptr;
if (!hp) {
RemoteCamera::Initialise();
if (!hp) {
Error("Unable to resolve address for remote camera, aborting");
return -1;
}
}
for ( p = hp; p != nullptr; p = p->ai_next ) {
sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol );
if ( sd < 0 ) {
@ -300,15 +308,17 @@ int RemoteCameraHttp::ReadData(Buffer &buffer, unsigned int bytes_expected) {
} // end readData
int RemoteCameraHttp::GetData() {
time_t start_time = time(nullptr);
int buffer_len = 0;
while (!(buffer_len = ReadData(buffer))) {
if (zm_terminate or ( (time(nullptr) - start_time) > ZM_WATCH_MAX_DELAY ))
return -1;
Debug(4, "Timeout waiting for REGEXP HEADER");
usleep(100000);
}
return buffer_len;
TimePoint start_time = std::chrono::steady_clock::now();
int buffer_len;
while (!(buffer_len = ReadData(buffer))) {
if (zm_terminate or std::chrono::steady_clock::now() - start_time > FPSeconds(config.watch_max_delay)) {
return -1;
}
Debug(4, "Timeout waiting for REGEXP HEADER");
std::this_thread::sleep_for(Milliseconds(100));
}
return buffer_len;
}
int RemoteCameraHttp::GetResponse() {

View File

@ -126,10 +126,11 @@ int RemoteCameraRtsp::Disconnect() {
int RemoteCameraRtsp::PrimeCapture() {
Debug(2, "Waiting for sources");
for ( int i = 0; (i < 100) && !rtspThread->hasSources(); i++ ) {
usleep(100000);
for (int i = 0; i < 100 && !rtspThread->hasSources(); i++) {
std::this_thread::sleep_for(Microseconds(100));
}
if ( !rtspThread->hasSources() ) {
if (!rtspThread->hasSources()) {
Error("No RTSP sources");
return -1;
}

View File

@ -269,16 +269,16 @@ void RtpCtrlThread::Run() {
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
zm::Select select(10 );
zm::Select select(Seconds(10));
select.addReader( &rtpCtrlServer );
unsigned char buffer[ZM_NETWORK_BUFSIZ];
time_t last_receive = time(nullptr);
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
TimePoint last_receive = std::chrono::steady_clock::now();
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
while (!mTerminate && select.wait() >= 0) {
time_t now = time(nullptr);
TimePoint now = std::chrono::steady_clock::now();
zm::Select::CommsList readable = select.getReadable();
if ( readable.size() == 0 ) {
if ( ! timeout ) {
@ -287,20 +287,20 @@ void RtpCtrlThread::Run() {
unsigned char *bufferPtr = buffer;
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
Debug(3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %" PRIi64,
bufferPtr - buffer, rtpCtrlServer.getWriteDesc(), static_cast<int64>(now - last_receive));
Debug(3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %.2f s",
bufferPtr - buffer, rtpCtrlServer.getWriteDesc(), FPSeconds(now - last_receive).count());
if ( (nBytes = rtpCtrlServer.send(buffer, bufferPtr-buffer)) < 0 )
Error("Unable to send: %s", strerror(errno));
timeout = true;
continue;
} else {
Debug(1, "RTCP timed out. Time since last receive: %" PRIi64, static_cast<int64>(now - last_receive));
Debug(1, "RTCP timed out. Time since last receive: %.2f s", FPSeconds(now - last_receive).count());
continue;
//break;
}
} else {
timeout = false;
last_receive = time(nullptr);
last_receive = std::chrono::steady_clock::now();
}
for (zm::Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) {
if ( zm::UdpInetSocket *socket = dynamic_cast<zm::UdpInetSocket *>(*iter) ) {

View File

@ -76,7 +76,7 @@ void RtpDataThread::Run() {
}
Debug(3, "Bound to %s:%d", mRtpSource.getLocalHost().c_str(), mRtpSource.getLocalDataPort());
zm::Select select(3);
zm::Select select(Seconds(3));
select.addReader(&rtpDataSocket);
unsigned char buffer[ZM_NETWORK_BUFSIZ];

View File

@ -66,7 +66,7 @@ RtpSource::RtpSource(
mRtpFactor = mRtpClock;
mBaseTimeReal = tvNow();
mBaseTimeReal = std::chrono::system_clock::now();
mBaseTimeNtp = {};
mBaseTimeRtp = rtpTime;
@ -159,12 +159,9 @@ bool RtpSource::updateSeq(uint16_t seq) {
}
void RtpSource::updateJitter( const RtpDataHeader *header ) {
if ( mRtpFactor > 0 ) {
timeval now = {};
gettimeofday(&now, nullptr);
FPSeconds time_diff =
zm::chrono::duration_cast<Microseconds>(now) - zm::chrono::duration_cast<Microseconds>(mBaseTimeReal);
if (mRtpFactor > 0) {
SystemTimePoint now = std::chrono::system_clock::now();
FPSeconds time_diff = std::chrono::duration_cast<FPSeconds>(now - mBaseTimeReal);
uint32_t localTimeRtp = mBaseTimeRtp + static_cast<uint32>(time_diff.count() * mRtpFactor);
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
@ -202,7 +199,7 @@ void RtpSource::updateRtcpData(
Debug(5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime);
if ( mBaseTimeNtp.tv_sec == 0 ) {
mBaseTimeReal = tvNow();
mBaseTimeReal = std::chrono::system_clock::now();
mBaseTimeNtp = ntpTime;
mBaseTimeRtp = rtpTime;
} else if ( !mRtpClock ) {

View File

@ -24,6 +24,7 @@
#include "zm_config.h"
#include "zm_define.h"
#include "zm_ffmpeg.h"
#include "zm_time.h"
#include <condition_variable>
#include <mutex>
#include <string>
@ -68,7 +69,7 @@ private:
// Time keys
uint32_t mRtpClock;
uint32_t mRtpFactor;
struct timeval mBaseTimeReal;
SystemTimePoint mBaseTimeReal;
struct timeval mBaseTimeNtp;
uint32_t mBaseTimeRtp;

View File

@ -86,9 +86,8 @@ bool RtspThread::recvResponse(std::string &response) {
int RtspThread::requestPorts() {
if ( !smMinDataPort ) {
char sql[ZM_SQL_SML_BUFSIZ];
//FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors
strncpy(sql, "SELECT `Id` FROM `Monitors` WHERE `Function` != 'None' AND `Type` = 'Remote' AND `Protocol` = 'rtsp' AND `Method` = 'rtpUni' ORDER BY `Id` ASC", sizeof(sql));
std::string sql = "SELECT `Id` FROM `Monitors` WHERE `Function` != 'None' AND `Type` = 'Remote' AND `Protocol` = 'rtsp' AND `Method` = 'rtpUni' ORDER BY `Id` ASC";
MYSQL_RES *result = zmDbFetch(sql);
@ -331,7 +330,8 @@ void RtspThread::Run() {
authTried = true;
sendCommand(message);
// FIXME Why sleep 1?
usleep(10000);
std::this_thread::sleep_for(Microseconds(10));
res = recvResponse(response);
if ( !res && respCode==401 )
mNeedAuth = true;
@ -438,15 +438,18 @@ void RtspThread::Run() {
lines = Split(response, "\r\n");
std::string session;
int timeout = 0;
Seconds timeout = Seconds(0);
char transport[256] = "";
for ( size_t i = 0; i < lines.size(); i++ ) {
if ( ( lines[i].size() > 8 ) && ( lines[i].substr(0, 8) == "Session:" ) ) {
StringVector sessionLine = Split(lines[i].substr(9), ";");
session = TrimSpaces(sessionLine[0]);
if ( sessionLine.size() == 2 )
sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout);
if ( sessionLine.size() == 2 ){
int32 timeout_val = 0;
sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout_val);
timeout = Seconds(timeout_val);
}
}
sscanf(lines[i].c_str(), "Transport: %s", transport);
}
@ -454,7 +457,9 @@ void RtspThread::Run() {
if ( session.empty() )
Fatal("Unable to get session identifier from response '%s'", response.c_str());
Debug(2, "Got RTSP session %s, timeout %d secs", session.c_str(), timeout);
Debug(2, "Got RTSP session %s, timeout %" PRIi64 " secs",
session.c_str(),
static_cast<int64>(Seconds(timeout).count()));
if ( !transport[0] )
Fatal("Unable to get transport details from response '%s'", response.c_str());
@ -517,12 +522,18 @@ void RtspThread::Run() {
if ( ( lines[i].size() > 9 ) && ( lines[i].substr(0, 9) == "RTP-Info:" ) )
rtpInfo = TrimSpaces(lines[i].substr(9));
// Check for a timeout again. Some rtsp devices don't send a timeout until after the PLAY command is sent
if ( ( lines[i].size() > 8 ) && ( lines[i].substr(0, 8) == "Session:" ) && ( timeout == 0 ) ) {
if ((lines[i].size() > 8) && (lines[i].substr(0, 8) == "Session:") && (timeout == Seconds(0))) {
StringVector sessionLine = Split(lines[i].substr(9), ";");
if ( sessionLine.size() == 2 )
sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout);
if ( timeout > 0 )
Debug(2, "Got timeout %d secs from PLAY command response", timeout);
if ( sessionLine.size() == 2 ){
int32 timeout_val = 0;
sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout_val);
timeout = Seconds(timeout_val);
}
if ( timeout > Seconds(0) ) {
Debug(2, "Got timeout %" PRIi64 " secs from PLAY command response",
static_cast<int64>(Seconds(timeout).count()));
}
}
}
@ -557,8 +568,8 @@ void RtspThread::Run() {
Debug( 2, "RTSP Seq is %d", seq );
Debug( 2, "RTSP Rtptime is %ld", rtpTime );
time_t lastKeepalive = time(nullptr);
time_t now;
TimePoint lastKeepalive = std::chrono::steady_clock::now();
TimePoint now;
message = "GET_PARAMETER "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
switch( mMethod ) {
@ -570,20 +581,21 @@ void RtspThread::Run() {
RtpCtrlThread rtpCtrlThread( *this, *source );
while (!mTerminate) {
now = time(nullptr);
now = std::chrono::steady_clock::now();
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
Debug(5, "sendkeepalive %d, timeout %d, now: %" PRIi64 " last: %" PRIi64 " since: %" PRIi64,
Debug(5, "sendkeepalive %d, timeout %" PRIi64 " s, now: %" PRIi64 " s last: %" PRIi64 " s since: %" PRIi64 "s ",
sendKeepalive,
timeout,
static_cast<int64>(now),
static_cast<int64>(lastKeepalive),
static_cast<int64>(now - lastKeepalive));
if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) ) {
if ( !sendCommand( message ) )
static_cast<int64>(Seconds(timeout).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(now.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(lastKeepalive.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>((now - lastKeepalive)).count()));
if (sendKeepalive && (timeout > Seconds(0)) && ((now - lastKeepalive) > (timeout - Seconds(5)))) {
if (!sendCommand(message))
return;
lastKeepalive = now;
}
usleep( 100000 );
std::this_thread::sleep_for(Microseconds(100));
}
#if 0
message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
@ -621,7 +633,7 @@ void RtspThread::Run() {
RtpDataThread rtpDataThread( *this, *source );
RtpCtrlThread rtpCtrlThread( *this, *source );
zm::Select select(double(config.http_timeout)/1000.0 );
zm::Select select(Milliseconds(config.http_timeout));
select.addReader( &mRtspSocket );
Buffer buffer( ZM_NETWORK_BUFSIZ );
@ -694,21 +706,23 @@ void RtspThread::Run() {
}
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
// FIXME: Is this really necessary when using tcp ?
now = time(nullptr);
now = std::chrono::steady_clock::now();
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
Debug(5, "sendkeepalive %d, timeout %d, now: %" PRIi64 " last: %" PRIi64 " since: %" PRIi64,
Debug(5, "sendkeepalive %d, timeout %" PRIi64 " s, now: %" PRIi64 " s last: %" PRIi64 " s since: %" PRIi64 " s",
sendKeepalive,
timeout,
static_cast<int64>(now),
static_cast<int64>(lastKeepalive),
static_cast<int64>(now - lastKeepalive));
if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) )
{
if ( !sendCommand( message ) )
static_cast<int64>(Seconds(timeout).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(now.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(lastKeepalive.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>((now - lastKeepalive)).count()));
if (sendKeepalive && (timeout > Seconds(0)) && ((now - lastKeepalive) > (timeout - Seconds(5)))) {
if (!sendCommand(message)) {
return;
}
lastKeepalive = now;
}
buffer.tidy( 1 );
buffer.tidy(true);
}
#if 0
message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
@ -737,12 +751,14 @@ void RtspThread::Run() {
while (!mTerminate) {
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
if ( sendKeepalive && (timeout > 0) && ((time(nullptr)-lastKeepalive) > (timeout-5)) ) {
if ( !sendCommand( message ) )
if (sendKeepalive && (timeout > Seconds(0))
&& ((std::chrono::steady_clock::now() - lastKeepalive) > (timeout - Seconds(5)))) {
if (!sendCommand(message)) {
return;
lastKeepalive = time(nullptr);
}
lastKeepalive = std::chrono::steady_clock::now();
}
usleep(100000);
std::this_thread::sleep_for(Microseconds(100));
}
#if 0
message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";

View File

@ -269,9 +269,8 @@ int main(int argc, char *argv[]) {
Warning("Unknown format in %s", videoFifoPath.c_str());
}
if (videoSource == nullptr) {
Error("Unable to create source");
Error("Unable to create source for %s", videoFifoPath.c_str());
rtspServer->RemoveSession(sessions[monitor->Id()]->GetMediaSessionId());
delete sessions[monitor->Id()];
sessions.erase(monitor->Id());
continue;
}

View File

@ -151,7 +151,7 @@ int ZoneMinderFifoSource::getNextFrame() {
}
}
int bytes_read = m_buffer.read_into(m_fd, 32);
int bytes_read = m_buffer.read_into(m_fd, 4096);
//int bytes_read = m_buffer.read_into(m_fd, 4096, {1,0});
if (bytes_read == 0) {
Debug(3, "No bytes read");

View File

@ -21,32 +21,33 @@
#include "zm_db.h"
#include "zm_logger.h"
#include "zm_utils.h"
#include <cstring>
Storage::Storage() : id(0) {
Warning("Instantiating default Storage Object. Should not happen.");
strcpy(name, "Default");
if ( staticConfig.DIR_EVENTS[0] != '/' ) {
Warning("Instantiating default Storage Object. Should not happen.");
strcpy(name, "Default");
if (staticConfig.DIR_EVENTS[0] != '/') {
// not using an absolute path. Make it one by appending ZM_PATH_WEB
snprintf(path, sizeof(path), "%s/%s",
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
} else {
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1);
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path) - 1);
}
scheme = MEDIUM;
scheme_str = "Medium";
}
Storage::Storage(MYSQL_ROW &dbrow) {
unsigned int index = 0;
id = atoi(dbrow[index++]);
strncpy(name, dbrow[index++], sizeof(name)-1);
strncpy(path, dbrow[index++], sizeof(path)-1);
unsigned int index = 0;
id = atoi(dbrow[index++]);
strncpy(name, dbrow[index++], sizeof(name) - 1);
strncpy(path, dbrow[index++], sizeof(path) - 1);
type_str = std::string(dbrow[index++]);
scheme_str = std::string(dbrow[index++]);
if ( scheme_str == "Deep" ) {
if (scheme_str == "Deep") {
scheme = DEEP;
} else if ( scheme_str == "Medium" ) {
} else if (scheme_str == "Medium") {
scheme = MEDIUM;
} else {
scheme = SHALLOW;
@ -55,44 +56,42 @@ Storage::Storage(MYSQL_ROW &dbrow) {
/* If a zero or invalid p_id is passed, then the old default path will be assumed. */
Storage::Storage(unsigned int p_id) : id(p_id) {
if ( id ) {
char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%u", id);
Debug(2, "Loading Storage for %u using %s", id, sql);
zmDbRow dbrow;
if ( !dbrow.fetch(sql) ) {
Error("Unable to load storage area for id %d: %s", id, mysql_error(&dbconn));
} else {
unsigned int index = 0;
id = atoi(dbrow[index++]);
strncpy(name, dbrow[index++], sizeof(name)-1);
strncpy(path, dbrow[index++], sizeof(path)-1);
if (id) {
std::string sql = stringtf("SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%u", id);
Debug(2, "Loading Storage for %u using %s", id, sql.c_str());
zmDbRow dbrow;
if (!dbrow.fetch(sql)) {
Error("Unable to load storage area for id %d: %s", id, mysql_error(&dbconn));
} else {
unsigned int index = 0;
id = atoi(dbrow[index++]);
strncpy(name, dbrow[index++], sizeof(name) - 1);
strncpy(path, dbrow[index++], sizeof(path) - 1);
type_str = std::string(dbrow[index++]);
scheme_str = std::string(dbrow[index++]);
if ( scheme_str == "Deep" ) {
if (scheme_str == "Deep") {
scheme = DEEP;
} else if ( scheme_str == "Medium" ) {
} else if (scheme_str == "Medium") {
scheme = MEDIUM;
} else {
scheme = SHALLOW;
}
Debug(1, "Loaded Storage area %d '%s'", id, name);
}
}
if ( !id ) {
if ( staticConfig.DIR_EVENTS[0] != '/' ) {
Debug(1, "Loaded Storage area %d '%s'", id, name);
}
}
if (!id) {
if (staticConfig.DIR_EVENTS[0] != '/') {
// not using an absolute path. Make it one by appending ZM_PATH_WEB
snprintf(path, sizeof(path), "%s/%s",
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
} else {
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1);
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path) - 1);
}
Debug(1, "No id passed to Storage constructor. Using default path %s instead", path);
strcpy(name, "Default");
Debug(1, "No id passed to Storage constructor. Using default path %s instead", path);
strcpy(name, "Default");
scheme = MEDIUM;
scheme_str = "Medium";
}
}
}
Storage::~Storage() {

View File

@ -254,18 +254,32 @@ Image *StreamBase::prepareImage(Image *image) {
} // end Image *StreamBase::prepareImage(Image *image)
bool StreamBase::sendTextFrame(const char *frame_text) {
Debug(2, "Sending %dx%d * %d text frame '%s'",
monitor->Width(), monitor->Height(), scale, frame_text);
int width = 640;
int height = 480;
int colours = ZM_COLOUR_RGB32;
int subpixelorder = ZM_SUBPIX_ORDER_RGBA;
int labelsize = 2;
Image image(monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder());
image.Clear();
image.Annotate(frame_text, image.centreCoord(frame_text, monitor->LabelSize()), monitor->LabelSize());
if ( scale != 100 ) {
image.Scale(scale);
if (monitor) {
width = monitor->Width();
height = monitor->Height();
colours = monitor->Colours();
subpixelorder = monitor->SubpixelOrder();
labelsize = monitor->LabelSize();
}
if ( type == STREAM_MPEG ) {
if ( !vid_stream ) {
Debug(2, "Sending %dx%dx%dx%d * %d scale text frame '%s'",
width, height, colours, subpixelorder, scale, frame_text);
Image image(width, height, colours, subpixelorder);
image.Clear();
image.Annotate(frame_text, image.centreCoord(frame_text, labelsize), labelsize);
if (scale != 100) {
image.Scale(scale);
Debug(2, "Scaled to %dx%d", image.Width(), image.Height());
}
if (type == STREAM_MPEG) {
if (!vid_stream) {
vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height());
fprintf(stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType());
vid_stream->OpenStream();
@ -276,10 +290,11 @@ bool StreamBase::sendTextFrame(const char *frame_text) {
int n_bytes = 0;
image.EncodeJpeg(buffer, &n_bytes);
Debug(4, "Encoded to %d bytes", n_bytes);
fputs("--" BOUNDARY "\r\nContent-Type: image/jpeg\r\n", stdout);
fprintf(stdout, "Content-Length: %d\r\n\r\n", n_bytes);
if ( fwrite(buffer, n_bytes, 1, stdout) != 1 ) {
if (fwrite(buffer, n_bytes, 1, stdout) != 1) {
Error("Unable to send stream text frame: %s", strerror(errno));
return false;
}

View File

@ -102,7 +102,6 @@ protected:
int last_scale;
int zoom;
int last_zoom;
double maxfps;
int bitrate;
unsigned short last_x, last_y;
unsigned short x, y;
@ -122,8 +121,14 @@ protected:
SystemTimePoint now;
SystemTimePoint last_comm_update;
double base_fps;
double effective_fps;
double maxfps;
double base_fps; // Should be capturing fps, hence a rough target
double effective_fps; // Target fps after taking max_fps into account
double actual_fps; // sliding calculated actual streaming fps achieved
SystemTimePoint last_fps_update;
int frame_count; // Count of frames sent
int last_frame_count; // Used in calculating actual_fps from frame_count - last_frame_count
int frame_mod;
SystemTimePoint last_frame_sent;
@ -152,7 +157,6 @@ public:
last_scale(DEFAULT_SCALE),
zoom(DEFAULT_ZOOM),
last_zoom(DEFAULT_ZOOM),
maxfps(DEFAULT_MAXFPS),
bitrate(DEFAULT_BITRATE),
last_x(0),
last_y(0),
@ -164,7 +168,14 @@ public:
sd(-1),
lock_fd(0),
paused(false),
step(0)
step(0),
maxfps(DEFAULT_MAXFPS),
base_fps(0.0),
effective_fps(0.0),
actual_fps(0.0),
frame_count(0),
last_frame_count(0),
frame_mod(1)
{
memset(&loc_sock_path, 0, sizeof(loc_sock_path));
memset(&loc_addr, 0, sizeof(loc_addr));
@ -172,10 +183,6 @@ public:
memset(&rem_addr, 0, sizeof(rem_addr));
memset(&sock_path_lock, 0, sizeof(sock_path_lock));
base_fps = 0.0;
effective_fps = 0.0;
frame_mod = 1;
vid_stream = nullptr;
msg = { 0, { 0 } };
}

View File

@ -23,12 +23,6 @@
#include <chrono>
#include <sys/time.h>
inline struct timeval tvNow() {
timeval t = {};
gettimeofday(&t, nullptr);
return t;
}
typedef std::chrono::microseconds Microseconds;
typedef std::chrono::milliseconds Milliseconds;
typedef std::chrono::seconds Seconds;

View File

@ -21,6 +21,7 @@
#include "zm_crypt.h"
#include "zm_logger.h"
#include "zm_time.h"
#include "zm_utils.h"
#include <cstring>
@ -84,15 +85,7 @@ bool User::canAccess(int monitor_id) {
// Function to load a user from username and password
// Please note that in auth relay mode = none, password is NULL
User *zmLoadUser(const char *username, const char *password) {
int username_length = strlen(username);
// According to docs, size of safer_whatever must be 2*length+1
// due to unicode conversions + null terminator.
std::string escaped_username((username_length * 2) + 1, '\0');
size_t escaped_len = mysql_real_escape_string(&dbconn, &escaped_username[0], username, username_length);
escaped_username.resize(escaped_len);
std::string escaped_username = zmDbEscapeString(username);
std::string sql = stringtf("SELECT `Id`, `Username`, `Password`, `Enabled`,"
" `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0,"
@ -100,7 +93,7 @@ User *zmLoadUser(const char *username, const char *password) {
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1",
escaped_username.c_str());
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result)
return nullptr;
@ -151,7 +144,7 @@ User *zmLoadTokenUser(const std::string &jwt_token_str, bool use_remote_addr) {
" `Control`+0, `Monitors`+0, `System`+0, `MonitorIds`, `TokenMinExpiry`"
" FROM `Users` WHERE `Username` = '%s' AND `Enabled` = 1", username.c_str());
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result)
return nullptr;
@ -194,7 +187,7 @@ User *zmLoadAuthUser(const char *auth, bool use_remote_addr) {
" `Stream`+0, `Events`+0, `Control`+0, `Monitors`+0, `System`+0,"
" `MonitorIds` FROM `Users` WHERE `Enabled` = 1";
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result)
return nullptr;
@ -205,24 +198,27 @@ User *zmLoadAuthUser(const char *auth, bool use_remote_addr) {
return nullptr;
}
// getting the time is expensive, so only do it once.
time_t now = time(nullptr);
unsigned int hours = config.auth_hash_ttl;
if (!hours) {
SystemTimePoint now = std::chrono::system_clock::now();
Hours hours = Hours(config.auth_hash_ttl);
if (hours == Hours(0)) {
Warning("No value set for ZM_AUTH_HASH_TTL. Defaulting to 2.");
hours = 2;
hours = Hours(2);
} else {
Debug(1, "AUTH_HASH_TTL is %d, time is %" PRIi64, hours, static_cast<int64>(now));
Debug(1, "AUTH_HASH_TTL is %" PRIi64 " h, time is %" PRIi64 " s",
static_cast<int64>(Hours(hours).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(now.time_since_epoch()).count()));
}
while (MYSQL_ROW dbrow = mysql_fetch_row(result)) {
const char *username = dbrow[1];
const char *password = dbrow[2];
time_t our_now = now;
SystemTimePoint our_now = now;
tm now_tm = {};
for (unsigned int i = 0; i < hours; i++, our_now -= 3600) {
localtime_r(&our_now, &now_tm);
for (Hours i = Hours(0); i < hours; i++, our_now -= Hours(1)) {
time_t our_now_t = std::chrono::system_clock::to_time_t(our_now);
localtime_r(&our_now_t, &now_tm);
std::string auth_key = stringtf("%s%s%s%s%d%d%d%d",
config.auth_hash_secret,

View File

@ -22,6 +22,7 @@
#include "zm_config.h"
#include "zm_logger.h"
#include <array>
#include <cstdarg>
#include <cstring>
#include <fcntl.h> /* Definition of AT_* constants */
#include <sstream>
@ -116,6 +117,26 @@ std::string Join(const StringVector &values, const std::string &delim) {
return ss.str();
}
std::string stringtf(const char* format, ...) {
va_list args;
va_start(args, format);
va_list args2;
va_copy(args2, args);
int size = vsnprintf(nullptr, 0, format, args) + 1; // Extra space for '\0'
va_end(args);
if (size <= 0) {
throw std::runtime_error("Error during formatting.");
}
std::unique_ptr<char[]> buf(new char[size]);
vsnprintf(buf.get(), size, format, args2);
va_end(args2);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
std::string ByteArrayToHexString(nonstd::span<const uint8> bytes) {
static constexpr char lowercase_table[] = "0123456789abcdef";
std::string buf;

View File

@ -61,16 +61,8 @@ inline bool StartsWith(const std::string &haystack, const std::string &needle) {
return (haystack.substr(0, needle.length()) == needle);
}
template<typename... Args>
std::string stringtf(const std::string &format, Args... args) {
int size = snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
if (size <= 0) {
throw std::runtime_error("Error during formatting.");
}
std::unique_ptr<char[]> buf(new char[size]);
snprintf(buf.get(), size, format.c_str(), args...);
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
}
__attribute__((format(printf, 1, 2)))
std::string stringtf(const char* format, ...);
std::string ByteArrayToHexString(nonstd::span<const uint8> bytes);

View File

@ -22,6 +22,7 @@
#include "zm_logger.h"
#include "zm_monitor.h"
#include "zm_time.h"
extern "C" {
#include <libavutil/time.h>
@ -92,7 +93,7 @@ VideoStore::VideoStore(
converted_in_samples(nullptr),
filename(filename_in),
format(format_in),
video_first_pts(0), /* starting pts of first in frame/packet */
video_first_pts(0),
video_first_dts(0),
audio_first_pts(0),
audio_first_dts(0),
@ -989,25 +990,25 @@ int VideoStore::writeVideoFramePacket(const std::shared_ptr<ZMPacket> &zm_packet
//zm_packet->out_frame->key_frame = zm_packet->keyframe;
frame->pkt_duration = 0;
int64_t in_pts = zm_packet->timestamp.tv_sec * (uint64_t)1000000 + zm_packet->timestamp.tv_usec;
if (!video_first_pts) {
video_first_pts = in_pts;
Debug(2, "No video_first_pts, set to (%" PRId64 ") secs(%" PRIi64 ") usecs(%" PRIi64 ")",
video_first_pts = static_cast<int64>(std::chrono::duration_cast<Microseconds>(zm_packet->timestamp.time_since_epoch()).count());
Debug(2, "No video_first_pts, set to (%" PRId64 ") secs(%.2f)",
video_first_pts,
static_cast<int64>(zm_packet->timestamp.tv_sec),
static_cast<int64>(zm_packet->timestamp.tv_usec));
FPSeconds(zm_packet->timestamp.time_since_epoch()).count());
frame->pts = 0;
} else {
uint64_t useconds = in_pts - video_first_pts;
frame->pts = av_rescale_q(useconds, AV_TIME_BASE_Q, video_out_ctx->time_base);
Microseconds useconds = std::chrono::duration_cast<Microseconds>(
zm_packet->timestamp - SystemTimePoint(Microseconds(video_first_pts)));
frame->pts = av_rescale_q(useconds.count(), AV_TIME_BASE_Q, video_out_ctx->time_base);
Debug(2,
"Setting pts for frame(%d) to (%" PRId64 ") from (start %" PRIu64 " - %" PRIu64 " - secs(%" PRIi64 ") usecs(%" PRIi64 ") @ %d/%d",
"Setting pts for frame(%d) to (%" PRId64 ") from (zm_packet->timestamp(%" PRIi64 " - first %" PRId64 " us %" PRId64 " ) @ %d/%d",
frame_count,
frame->pts,
static_cast<int64>(std::chrono::duration_cast<Microseconds>(zm_packet->timestamp.time_since_epoch()).count()),
video_first_pts,
useconds,
static_cast<int64>(zm_packet->timestamp.tv_sec),
static_cast<int64>(zm_packet->timestamp.tv_usec),
static_cast<int64>(std::chrono::duration_cast<Microseconds>(useconds).count()),
video_out_ctx->time_base.num,
video_out_ctx->time_base.den);
}

View File

@ -76,7 +76,7 @@ class VideoStore {
const char *format;
// These are for in
int64_t video_first_pts;
int64_t video_first_pts; /* starting pts of first in frame/packet */
int64_t video_first_dts;
int64_t audio_first_pts;
int64_t audio_first_dts;

View File

@ -104,7 +104,8 @@ void Zone::Setup(
diag_path = stringtf("%s/diag-%d-poly.jpg",
monitor->getStorage()->Path(), id);
}
pg_image->WriteJpeg(diag_path.c_str(), config.record_diag_images_fifo);
pg_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
}
} // end Zone::Setup
@ -116,8 +117,7 @@ Zone::~Zone() {
}
void Zone::RecordStats(const Event *event) {
static char sql[ZM_SQL_MED_BUFSIZ];
snprintf(sql, sizeof(sql),
std::string sql = stringtf(
"INSERT INTO Stats SET MonitorId=%d, ZoneId=%d, EventId=%" PRIu64 ", FrameId=%d, "
"PixelDiff=%d, AlarmPixels=%d, FilterPixels=%d, BlobPixels=%d, "
"Blobs=%d, MinBlobSize=%d, MaxBlobSize=%d, "
@ -233,8 +233,9 @@ bool Zone::CheckAlarms(const Image *delta_image) {
} */
std_alarmedpixels(diff_image, pg_image, &stats.alarm_pixels_, &pixel_diff_count);
if (config.record_diag_images)
diff_image->WriteJpeg(diag_path.c_str(), config.record_diag_images_fifo);
if (config.record_diag_images) {
diff_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
}
if (pixel_diff_count && stats.alarm_pixels_)
stats.pixel_diff_ = pixel_diff_count/stats.alarm_pixels_;
@ -317,8 +318,9 @@ bool Zone::CheckAlarms(const Image *delta_image) {
stats.alarm_filter_pixels_ = stats.alarm_pixels_;
}
if (config.record_diag_images)
diff_image->WriteJpeg(diag_path.c_str(), config.record_diag_images_fifo);
if (config.record_diag_images) {
diff_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
}
Debug(5, "Got %d filtered pixels, need %d -> %d",
stats.alarm_filter_pixels_, min_filter_pixels, max_filter_pixels);
@ -541,8 +543,9 @@ bool Zone::CheckAlarms(const Image *delta_image) {
}
}
if (config.record_diag_images)
diff_image->WriteJpeg(diag_path.c_str(), config.record_diag_images_fifo);
if (config.record_diag_images) {
diff_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
}
if (!stats.alarm_blobs_) {
stats.score_ = 0;
@ -593,8 +596,9 @@ bool Zone::CheckAlarms(const Image *delta_image) {
} // end if bs_count
} // end for i < WHITE
if (config.record_diag_images)
diff_image->WriteJpeg(diag_path.c_str(), config.record_diag_images_fifo);
if (config.record_diag_images) {
diff_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
}
Debug(5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d",
stats.alarm_blob_pixels_, stats.alarm_blobs_, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs);
@ -830,7 +834,7 @@ std::vector<Zone> Zone::Load(Monitor *monitor) {
"OverloadFrames,ExtendAlarmFrames"
" FROM Zones WHERE MonitorId = %d ORDER BY Type, Id", monitor->Id());
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result) {
return {};
}

View File

@ -64,8 +64,6 @@ possible, this should run at more or less constant speed.
#include "zm_time.h"
#include "zm_utils.h"
#include <getopt.h>
#include <iostream>
#include <unistd.h>
void Usage() {
fprintf(stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n");
@ -192,11 +190,11 @@ int main(int argc, char *argv[]) {
HwCapsDetect();
std::vector<std::shared_ptr<Monitor>> monitors;
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
if ( device[0] ) {
monitors = Monitor::LoadLocalMonitors(device, Monitor::CAPTURE);
} else
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
if ( host[0] ) {
if ( !port )
port = "80";
@ -214,7 +212,7 @@ int main(int argc, char *argv[]) {
Error("No monitors found");
exit(-1);
} else {
Debug(2, "%zu monitors loaded", monitors.size());
Debug(2, "%zu monitors loaded", monitors.size());
}
Info("Starting Capture version %s", ZM_VERSION);
@ -234,7 +232,6 @@ int main(int argc, char *argv[]) {
while (!zm_terminate) {
result = 0;
static char sql[ZM_SQL_SML_BUFSIZ];
for (const std::shared_ptr<Monitor> &monitor : monitors) {
monitor->LoadCamera();
@ -242,52 +239,51 @@ int main(int argc, char *argv[]) {
if (!monitor->connect()) {
Warning("Couldn't connect to monitor %d", monitor->Id());
}
time_t now = (time_t)time(nullptr);
monitor->setStartupTime(now);
monitor->setHeartbeatTime(now);
SystemTimePoint now = std::chrono::system_clock::now();
monitor->SetStartupTime(now);
monitor->SetHeartbeatTime(now);
snprintf(sql, sizeof(sql),
std::string sql = stringtf(
"INSERT INTO Monitor_Status (MonitorId,Status,CaptureFPS,AnalysisFPS)"
" VALUES (%u, 'Running',0,0) ON DUPLICATE KEY UPDATE Status='Running',CaptureFPS=0,AnalysisFPS=0",
monitor->Id());
zmDbDo(sql);
int sleep_time = 0;
Seconds sleep_time = Seconds(0);
while (monitor->PrimeCapture() <= 0) {
if (prime_capture_log_count % 60) {
logPrintf(Logger::ERROR+monitor->Importance(),
"Failed to prime capture of initial monitor");
logPrintf(Logger::ERROR + monitor->Importance(),
"Failed to prime capture of initial monitor");
} else {
Debug(1, "Failed to prime capture of initial monitor");
}
prime_capture_log_count ++;
if (zm_terminate) break;
if (sleep_time < 60) sleep_time++;
sleep(sleep_time);
}
if (zm_terminate) break;
snprintf(sql, sizeof(sql),
prime_capture_log_count++;
if (zm_terminate) {
break;
}
if (sleep_time < Seconds(60)) {
sleep_time++;
}
std::this_thread::sleep_for(sleep_time);
}
if (zm_terminate){
break;
}
sql = stringtf(
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'Connected') ON DUPLICATE KEY UPDATE Status='Connected'",
monitor->Id());
monitor->Id());
zmDbDo(sql);
} // end foreach monitor
if (zm_terminate) break;
int *capture_delays = new int[monitors.size()];
int *alarm_capture_delays = new int[monitors.size()];
struct timeval * last_capture_times = new struct timeval[monitors.size()];
for (size_t i = 0; i < monitors.size(); i++) {
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
capture_delays[i] = monitors[i]->GetCaptureDelay();
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
Debug(2, "capture delay(%u mSecs 1000/capture_fps) alarm delay(%u)",
capture_delays[i], alarm_capture_delays[i]);
if (zm_terminate){
break;
}
timeval now;
int sleep_time = 0;
std::vector<SystemTimePoint> last_capture_times = std::vector<SystemTimePoint>(monitors.size());
Microseconds sleep_time = Microseconds(0);
while (!zm_terminate) {
//sigprocmask(SIG_BLOCK, &block_set, 0);
@ -314,30 +310,28 @@ int main(int argc, char *argv[]) {
}
// capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate.
int delay = (monitors[i]->GetState() == Monitor::ALARM) ? alarm_capture_delays[i] : capture_delays[i];
if (delay) {
gettimeofday(&now, nullptr);
if (last_capture_times[i].tv_sec) {
Microseconds delta_time = zm::chrono::duration_cast<Microseconds>(now)
- zm::chrono::duration_cast<Microseconds>(last_capture_times[i]);
Microseconds delay = (monitors[i]->GetState() == Monitor::ALARM) ? monitors[i]->GetAlarmCaptureDelay()
: monitors[i]->GetCaptureDelay();
if (delay != Seconds(0)) {
SystemTimePoint now = std::chrono::system_clock::now();
if (last_capture_times[i].time_since_epoch() != Seconds(0)) {
Microseconds delta_time = std::chrono::duration_cast<Microseconds>(now - last_capture_times[i]);
// You have to add back in the previous sleep time
sleep_time = delay - (delta_time.count() - sleep_time);
sleep_time = delay - (delta_time - sleep_time);
Debug(4,
"Sleep time is %d from now: %" PRIi64 ".%" PRIi64" last: %" PRIi64 ".% " PRIi64 " delta % " PRIi64 " delay: %d",
sleep_time,
static_cast<int64>(now.tv_sec),
static_cast<int64>(now.tv_usec),
static_cast<int64>(last_capture_times[i].tv_sec),
static_cast<int64>(last_capture_times[i].tv_usec),
"Sleep time is %" PRIi64 " from now: %.2f s last: %.2f s delta % " PRIi64 " us delay: %" PRIi64 " us",
static_cast<int64>(Microseconds(sleep_time).count()),
FPSeconds(now.time_since_epoch()).count(),
FPSeconds(last_capture_times[i].time_since_epoch()).count(),
static_cast<int64>(delta_time.count()),
delay);
static_cast<int64>(Microseconds(delay).count()));
if (sleep_time > 0) {
Debug(4, "usleeping (%d)", sleep_time);
usleep(sleep_time);
if (sleep_time > Seconds(0)) {
std::this_thread::sleep_for(sleep_time);
}
} // end if has a last_capture time
last_capture_times[i] = now;
} // end if delay
} // end foreach n_monitors
@ -348,30 +342,25 @@ int main(int argc, char *argv[]) {
}
} // end while ! zm_terminate and connected
for (size_t i = 0; i < monitors.size(); i++) {
monitors[i]->Close();
monitors[i]->disconnect();
for (std::shared_ptr<Monitor> & monitor : monitors) {
monitor->Close();
monitor->disconnect();
}
delete [] alarm_capture_delays;
delete [] capture_delays;
delete [] last_capture_times;
if (zm_reload) {
for (std::shared_ptr<Monitor> &monitor : monitors) {
monitor->Reload();
}
logTerm();
logInit(log_id_string);
zm_reload = false;
} // end if zm_reload
} // end while ! zm_terminate outer connection loop
for (std::shared_ptr<Monitor> &monitor : monitors) {
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql),
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'NotRunning') ON DUPLICATE KEY UPDATE Status='NotRunning'",
std::string sql = stringtf(
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'NotRunning') ON DUPLICATE KEY UPDATE Status='NotRunning'",
monitor->Id());
zmDbDo(sql);
}
@ -382,5 +371,5 @@ int main(int argc, char *argv[]) {
dbQueue.stop();
zmDbClose();
return zm_terminate ? 0 : result;
return zm_terminate ? 0 : result;
}

View File

@ -241,7 +241,7 @@ int main(int argc, const char *argv[], char **envp) {
}
fprintf(stdout, "Server: ZoneMinder Video Server/%s\r\n", ZM_VERSION);
time_t now = time(nullptr);
time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
char date_string[64];
tm now_tm = {};
strftime(date_string, sizeof(date_string)-1,
@ -265,7 +265,7 @@ int main(int argc, const char *argv[], char **envp) {
stream.setStreamQueue(connkey);
stream.setStreamBuffer(playback_buffer);
if ( !stream.setStreamStart(monitor_id) ) {
Error("Unable set start stream for monitor %d", monitor_id);
fputs("Content-Type: multipart/x-mixed-replace; boundary=" BOUNDARY "\r\n\r\n", stdout);
stream.sendTextFrame("Unable to connect to monitor");
logTerm();
zmDbClose();

View File

@ -197,6 +197,7 @@ bool ValidateAccess(User *user, int mon_id, int function) {
void exit_zmu(int exit_code) {
logTerm();
dbQueue.stop();
zmDbClose();
exit(exit_code);
@ -248,7 +249,7 @@ int main(int argc, char *argv[]) {
{nullptr, 0, nullptr, 0}
};
const char *device = nullptr;
std::string device;
int mon_id = 0;
bool verbose = false;
int function = ZMU_BOGUS;
@ -256,28 +257,35 @@ int main(int argc, char *argv[]) {
int image_idx = -1;
int scale = -1;
int brightness = -1;
bool have_brightness = false;
int contrast = -1;
bool have_contrast = false;
int hue = -1;
bool have_hue = false;
int colour = -1;
bool have_colour = false;
char *zoneString = nullptr;
char *username = nullptr;
char *password = nullptr;
char *auth = nullptr;
std::string jwt_token_str = "";
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
int v4lVersion = 2;
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
while (1) {
int option_index = 0;
int c = getopt_long(argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::RWU:P:A:V:T:", long_options, &option_index);
if ( c == -1 ) {
if (c == -1) {
break;
}
switch (c) {
case 'd':
if ( optarg )
if (optarg)
device = optarg;
break;
case 'm':
@ -291,7 +299,7 @@ int main(int argc, char *argv[]) {
break;
case 'i':
function |= ZMU_IMAGE;
if ( optarg )
if (optarg)
image_idx = atoi(optarg);
break;
case 'S':
@ -299,7 +307,7 @@ int main(int argc, char *argv[]) {
break;
case 't':
function |= ZMU_TIME;
if ( optarg )
if (optarg)
image_idx = atoi(optarg);
break;
case 'R':
@ -316,7 +324,7 @@ int main(int argc, char *argv[]) {
break;
case 'z':
function |= ZMU_ZONES;
if ( optarg )
if (optarg)
zoneString = optarg;
break;
case 'a':
@ -348,23 +356,31 @@ int main(int argc, char *argv[]) {
break;
case 'B':
function |= ZMU_BRIGHTNESS;
if ( optarg )
if (optarg) {
have_brightness = true;
brightness = atoi(optarg);
}
break;
case 'C':
function |= ZMU_CONTRAST;
if ( optarg )
if (optarg) {
have_contrast = true;
contrast = atoi(optarg);
}
break;
case 'H':
function |= ZMU_HUE;
if ( optarg )
if (optarg) {
have_hue = true;
hue = atoi(optarg);
}
break;
case 'O':
function |= ZMU_COLOUR;
if ( optarg )
if (optarg) {
have_colour = true;
colour = atoi(optarg);
}
break;
case 'U':
username = optarg;
@ -378,11 +394,11 @@ int main(int argc, char *argv[]) {
case 'T':
jwt_token_str = std::string(optarg);
break;
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
case 'V':
v4lVersion = (atoi(optarg)==1)?1:2;
break;
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
case 'h':
case '?':
Usage(0);
@ -404,7 +420,7 @@ int main(int argc, char *argv[]) {
Usage();
}
if ( device && !(function&ZMU_QUERY) ) {
if ( !device.empty() && !(function&ZMU_QUERY) ) {
fprintf(stderr, "Error, -d option cannot be used with this option\n");
Usage();
}
@ -498,20 +514,27 @@ int main(int argc, char *argv[]) {
}
}
if ( function & ZMU_TIME ) {
struct timeval timestamp = monitor->GetTimestamp(image_idx);
if ( verbose ) {
SystemTimePoint timestamp = monitor->GetTimestamp(image_idx);
if (verbose) {
char timestamp_str[64] = "None";
if ( timestamp.tv_sec ) {
if (timestamp.time_since_epoch() != Seconds(0)) {
tm tm_info = {};
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime_r(&timestamp.tv_sec, &tm_info));
time_t timestamp_t = std::chrono::system_clock::to_time_t(timestamp);
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime_r(&timestamp_t, &tm_info));
}
Seconds ts_sec = std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch());
Microseconds ts_usec = std::chrono::duration_cast<Microseconds>(timestamp.time_since_epoch() - ts_sec);
if (image_idx == -1) {
printf("Time of last image capture: %s.%02d\n", timestamp_str, static_cast<int32>(ts_usec.count()));
}
else {
printf("Time of image %d capture: %s.%02d\n", image_idx, timestamp_str, static_cast<int32>(ts_usec.count()));
}
if ( image_idx == -1 )
printf("Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000);
else
printf("Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000);
} else {
if ( have_output ) fputc(separator, stdout);
printf("%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000);
if (have_output) {
fputc(separator, stdout);
}
printf("%.2f", FPSeconds(timestamp.time_since_epoch()).count());
have_output = true;
}
}
@ -585,13 +608,16 @@ int main(int argc, char *argv[]) {
// Ensure that we are not recording. So the forced alarm is distinct from what was recording before
monitor->ForceAlarmOff();
monitor->ForceAlarmOn(config.forced_alarm_score, "Forced Web");
int wait = 10*1000*1000; // 10 seconds
while ((monitor->GetState() != Monitor::ALARM) and !zm_terminate and wait) {
Microseconds wait_time = Seconds(10);
while ((monitor->GetState() != Monitor::ALARM) and !zm_terminate and wait_time > Seconds(0)) {
// Wait for monitor to notice.
usleep(1000);
wait -= 1000;
Microseconds sleep = Microseconds(1);
std::this_thread::sleep_for(sleep);
wait_time -= sleep;
}
if ( monitor->GetState() != Monitor::ALARM and !wait ) {
if (monitor->GetState() != Monitor::ALARM and wait_time == Seconds(0)) {
Error("Monitor failed to respond to forced alarm.");
} else {
printf("Alarmed event id: %" PRIu64 "\n", monitor->GetLastEventId());
@ -639,60 +665,60 @@ int main(int argc, char *argv[]) {
monitor->DumpSettings(monString, verbose);
printf("%s\n", monString);
}
if ( function & ZMU_BRIGHTNESS ) {
if ( verbose ) {
if ( brightness >= 0 )
if (function & ZMU_BRIGHTNESS) {
if (verbose) {
if (have_brightness)
printf("New brightness: %d\n", monitor->actionBrightness(brightness));
else
printf("Current brightness: %d\n", monitor->actionBrightness());
} else {
if ( have_output ) fputc(separator, stdout);
if ( brightness >= 0 )
if (have_output) fputc(separator, stdout);
if (have_brightness)
printf("%d", monitor->actionBrightness(brightness));
else
printf("%d", monitor->actionBrightness());
have_output = true;
}
}
if ( function & ZMU_CONTRAST ) {
if ( verbose ) {
if ( contrast >= 0 )
printf("New brightness: %d\n", monitor->actionContrast(contrast));
if (function & ZMU_CONTRAST) {
if (verbose) {
if (have_contrast)
printf("New contrast: %d\n", monitor->actionContrast(contrast));
else
printf("Current contrast: %d\n", monitor->actionContrast());
} else {
if ( have_output ) fputc(separator, stdout);
if ( contrast >= 0 )
if (have_output) fputc(separator, stdout);
if (have_contrast)
printf("%d", monitor->actionContrast(contrast));
else
printf("%d", monitor->actionContrast());
have_output = true;
}
}
if ( function & ZMU_HUE ) {
if ( verbose ) {
if ( hue >= 0 )
if (function & ZMU_HUE) {
if (verbose) {
if (have_hue)
printf("New hue: %d\n", monitor->actionHue(hue));
else
printf("Current hue: %d\n", monitor->actionHue());
} else {
if ( have_output ) fputc(separator, stdout);
if ( hue >= 0 )
if (have_output) fputc(separator, stdout);
if (have_hue)
printf("%d", monitor->actionHue(hue));
else
printf("%d", monitor->actionHue());
have_output = true;
}
}
if ( function & ZMU_COLOUR ) {
if ( verbose ) {
if ( colour >= 0 )
if (function & ZMU_COLOUR) {
if (verbose) {
if (have_colour)
printf("New colour: %d\n", monitor->actionColour(colour));
else
printf("Current colour: %d\n", monitor->actionColour());
} else {
if ( have_output ) fputc(separator, stdout);
if ( colour >= 0 )
if (have_output) fputc(separator, stdout);
if (have_colour)
printf("%d", monitor->actionColour(colour));
else
printf("%d", monitor->actionColour());
@ -700,7 +726,7 @@ int main(int argc, char *argv[]) {
}
}
if ( have_output ) {
if (have_output) {
printf("\n");
}
if ( !function ) {
@ -708,15 +734,15 @@ int main(int argc, char *argv[]) {
}
} else { // non monitor functions
if ( function & ZMU_QUERY ) {
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
char vidString[0x10000] = "";
bool ok = LocalCamera::GetCurrentSettings(device, vidString, v4lVersion, verbose);
printf("%s", vidString);
exit_zmu(ok ? 0 : -1);
#else // ZM_HAS_V4L
#else // ZM_HAS_V4L2
Error("Video4linux is required for device querying");
exit_zmu(-1);
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
}
if ( function & ZMU_LIST ) {
@ -726,7 +752,7 @@ int main(int argc, char *argv[]) {
}
sql += " ORDER BY Id ASC";
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result) {
exit_zmu(-1);
}
@ -740,13 +766,14 @@ int main(int argc, char *argv[]) {
if ( monitor_function > 1 ) {
std::shared_ptr<Monitor> monitor = Monitor::Load(monitor_id, false, Monitor::QUERY);
if ( monitor && monitor->connect() ) {
struct timeval tv = monitor->GetTimestamp();
printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8" PRIu64 "%8.2f\n",
SystemTimePoint timestamp = monitor->GetTimestamp();
printf( "%4d%5d%6d%9d%14.2f%6d%6d%8" PRIu64 "%8.2f\n",
monitor->Id(),
monitor_function,
monitor->GetState(),
monitor->GetTriggerState(),
tv.tv_sec, tv.tv_usec/10000,
FPSeconds(timestamp.time_since_epoch()).count(),
monitor->GetLastReadIndex(),
monitor->GetLastWriteIndex(),
monitor->GetLastEventId(),
@ -754,13 +781,12 @@ int main(int argc, char *argv[]) {
);
}
} else {
struct timeval tv = { 0, 0 };
printf("%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n",
mon_id,
function,
0,
0,
tv.tv_sec, tv.tv_usec/10000,
0l, 0l,
0,
0,
0,

View File

@ -88,7 +88,7 @@ fi;
if [ "$DISTROS" == "" ]; then
if [ "$RELEASE" != "" ]; then
DISTROS="bionic,focal,groovy,hirsute"
DISTROS="bionic,focal,hirsute,impish"
else
DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
fi;
@ -120,6 +120,11 @@ else
if [ "$BRANCH" == "" ]; then
#REV=$(git rev-list --tags --max-count=1)
BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`;
if [ -z "$BRANCH" ]; then
# This should only happen in CI environments where tag info isn't available
BRANCH=`cat version`
echo "Building branch $BRANCH"
fi
if [ "$BRANCH" == "" ]; then
echo "Unable to determine latest stable branch!"
exit 0;
@ -224,6 +229,7 @@ rm -rf .git
rm .gitignore
cd ../
if [ ! -e "$DIRECTORY.orig.tar.gz" ]; then
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
fi;
@ -237,11 +243,10 @@ IFS=',' ;for DISTRO in `echo "$DISTROS"`; do
fi;
# Generate Changlog
if [ "$DISTRO" == "focal" ] || [ "$DISTRO" == "buster" ] || [ "$DISTRO" == "hirsute" ]; then
cp -Rpd distros/ubuntu2004 debian
elif [ "$DISTRO" == "beowulf" ]
then
if [ "$DISTRO" == "beowulf" ]; then
cp -Rpd distros/beowulf debian
else
cp -Rpd distros/ubuntu2004 debian
fi;
if [ "$DEBEMAIL" != "" ] && [ "$DEBFULLNAME" != "" ]; then
@ -291,24 +296,29 @@ zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
EOF
fi;
# Leave the .orig so that we don't pollute it when building deps
cd ..
if [ $TYPE == "binary" ]; then
# Auto-install all ZoneMinder's depedencies using the Debian control file
sudo apt-get install devscripts equivs
sudo mk-build-deps -ir ./debian/control
echo "Status: $?"
DEBUILD=debuild
# Auto-install all ZoneMinder's depedencies using the Debian control file
sudo apt-get install devscripts equivs
sudo mk-build-deps -ir $DIRECTORY.orig/debian/control
echo "Status: $?"
DEBUILD=debuild
else
if [ $TYPE == "local" ]; then
# Auto-install all ZoneMinder's depedencies using the Debian control file
sudo apt-get install devscripts equivs
sudo mk-build-deps -ir ./debian/control
echo "Status: $?"
DEBUILD="debuild -i -us -uc -b"
else
# Source build, don't need build depends.
DEBUILD="debuild -S -sa"
fi;
if [ $TYPE == "local" ]; then
# Auto-install all ZoneMinder's depedencies using the Debian control file
sudo apt-get install devscripts equivs
sudo mk-build-deps -ir $DIRECTORY.orig/debian/control
echo "Status: $?"
DEBUILD="debuild -i -us -uc -b"
else
# Source build, don't need build depends.
DEBUILD="debuild -S -sa"
fi;
fi;
cd $DIRECTORY.orig
if [ "$DEBSIGN_KEYID" != "" ]; then
DEBUILD="$DEBUILD -k$DEBSIGN_KEYID"
fi

Some files were not shown because too many files have changed in this diff Show More