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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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.

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.

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,11 +236,27 @@ sub getCamParams {
sub autoStop {
my $self = shift;
my $autostop = shift;
my $iszoom = shift;
if ( $autostop ) {
Debug('Auto Stop');
my $cmd = $controlUri;
my $msg_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>
@ -251,6 +268,7 @@ sub autoStop {
</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,9 +166,14 @@ 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/ ) {
@ -199,10 +206,10 @@ sub Sql {
$self->{Sql} .= 'E.EndDateTime';
} elsif ( $term->{attr} eq 'EndDate' ) {
$self->{Sql} .= 'to_days( E.EndDateTime )';
} elsif ( $term->{attr} eq 'EndDateTime' ) {
} elsif ( $term->{attr} eq 'EndTime' ) {
$self->{Sql} .= 'extract( hour_second from E.EndDateTime )';
} elsif ( $term->{attr} eq 'EndWeekday' ) {
$self->{Sql} .= "weekday( E.EndDateTime )";
$self->{Sql} .= 'weekday( E.EndDateTime )';
} elsif ( $term->{attr} eq 'ExistsInFileSystem' ) {
push @{$self->{PostSQLConditions}}, $term;
$self->{Sql} .= 'TRUE /* ExistsInFileSystem */';
@ -295,7 +302,6 @@ sub Sql {
}
push @value_list, $value;
} # end foreach temp_value
} # end if has an attr
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

@ -101,12 +101,10 @@ 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) {
Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field');
@ -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");
}

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");
}
$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

@ -147,9 +147,9 @@ while (!$zm_terminate) {
if ($restart) {
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);
} elsif ($monitor->{Function} ne 'Monitor') {
@ -166,7 +166,6 @@ while (!$zm_terminate) {
#$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)
@ -184,12 +183,12 @@ while (!$zm_terminate) {
}
if ($restart) {
Info("Restarting analysis daemon for $$monitor{Id} $$monitor{Name}\n");
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

@ -73,11 +73,28 @@ 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 ) :
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 )
mPath(p_path),
mUser(p_user),
mPass(p_pass),
bTerminate(false),
bReset(false),
mode(MODE_UNSET)
{
if (capture) {
Initialise();
}
@ -85,7 +102,6 @@ cURLCamera::cURLCamera( const Monitor* monitor, const std::string &p_path, const
cURLCamera::~cURLCamera() {
if (capture) {
Terminate();
}
}
@ -156,13 +172,14 @@ void cURLCamera::Terminate() {
}
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 );
return 1;
}
int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
@ -179,7 +196,6 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
lock();
while (!frameComplete) {
/* If the work thread did a reset, reset our local variables */
if (bReset) {
SubHeadersParsingComplete = false;
@ -244,7 +260,11 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
}
/* 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;
}
@ -297,6 +317,14 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
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;
}
@ -317,6 +345,14 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
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;
@ -353,7 +389,7 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
int cURLCamera::PostCapture() {
// Nothing to do here
return( 0 );
return 1;
}
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata) {

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)),
@ -126,8 +124,7 @@ Event::Event(
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)
@ -362,60 +353,10 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
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,
void Event::AddFrame(Image *image,
SystemTimePoint timestamp,
const std::vector<ZoneStats> &zone_stats,
int score,
Image *alarm_image) {
if (!timestamp.tv_sec) {
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,
Event(Monitor *p_monitor,
SystemTimePoint p_start_time,
const std::string &p_cause,
const StringSetMap &p_noteSetMap
);
const StringSetMap &p_noteSetMap);
~Event();
uint64_t Id() const { return id; }
@ -109,42 +108,34 @@ 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,
void AddFrame(Image *image,
SystemTimePoint timestamp,
const std::vector<ZoneStats> &stats,
int score = 0,
Image *alarm_image=nullptr
);
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",
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 const char *getSubPath(time_t *time) {
static std::string getSubPath(time_t *time) {
tm time_tm = {};
localtime_r(time, &time_tm);
return Event::getSubPath(time_tm);
@ -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,45 +180,42 @@ 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",
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
snprintf(event_data->path, sizeof(event_data->path),
"%s/%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
} 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,
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
snprintf(event_data->path, sizeof(event_data->path),
"%s/%s/%u/%04d-%02d-%02d/%" PRIu64,
} 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,
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;
if ((event_data->frame_count and event_data->duration != Seconds(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;
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);
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), 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;
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));
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;
@ -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 ) {
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];
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());
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());
diag_path = stringtf("%s/diagpipe-d-%u.jpg", staticConfig.PATH_SOCKS.c_str(), monitor->Id());
stream_type = MJPEG;
} else {
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;
}
@ -133,24 +131,25 @@ void FifoStream::runStream() {
}
/* 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);
int fd_lock = open(lock_file.c_str(), O_RDONLY);
if (fd_lock < 0) {
Error("Can't open %s: %s", lock_file, strerror(errno));
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));
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));
Error("Flocking problem on %d != %d %s: - %s", EAGAIN, res, lock_file.c_str(), strerror(errno));
close(fd_lock);
return;
}
@ -167,5 +166,6 @@ void FifoStream::runStream() {
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,7 +48,7 @@ FileCamera::FileCamera(
p_capture,
p_record_audio)
{
strncpy( path, p_path, sizeof(path)-1 );
path = std::string(p_path);
if (capture) {
Initialise();
}
@ -62,7 +61,7 @@ FileCamera::~FileCamera() {
}
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;
@ -82,7 +81,7 @@ int FileCamera::PreCapture() {
// 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);
std::this_thread::sleep_for(Milliseconds(100));
}
return 0;
}

View File

@ -27,12 +27,8 @@
// 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,
FileCamera(const Monitor *monitor,
const char *p_path,
int p_width,
int p_height,
@ -42,11 +38,8 @@ public:
int p_hue,
int p_colour,
bool p_capture,
bool p_record_audio
);
~FileCamera();
const char *Path() const { return path; }
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() {
@ -42,16 +43,16 @@ Group::Group(unsigned int p_id) {
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);
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++;
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());
}

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,49 +936,48 @@ 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);
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)) {
Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u", width, height, new_width, new_height);
@ -983,7 +985,7 @@ bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int
switch (p_colours) {
case ZM_COLOUR_GRAY8:
cinfo->out_color_space = JCS_GRAYSCALE;
readjpg_dcinfo->out_color_space = JCS_GRAYSCALE;
new_colours = ZM_COLOUR_GRAY8;
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
break;
@ -991,17 +993,17 @@ bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int
#ifdef JCS_EXTENSIONS
new_colours = ZM_COLOUR_RGB32;
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
cinfo->out_color_space = JCS_EXT_BGRX;
readjpg_dcinfo->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;
readjpg_dcinfo->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;
readjpg_dcinfo->out_color_space = JCS_EXT_XBGR;
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
} else {
/* Assume RGBA */
cinfo->out_color_space = JCS_EXT_RGBX;
readjpg_dcinfo->out_color_space = JCS_EXT_RGBX;
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
}
break;
@ -1013,7 +1015,7 @@ bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int
new_colours = ZM_COLOUR_RGB24;
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
#ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_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.");
@ -1029,7 +1031,7 @@ cinfo->out_color_space = JCS_EXT_RGB;
cinfo->out_color_space = JCS_RGB;
#endif
*/
cinfo->out_color_space = JCS_RGB;
readjpg_dcinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
}
break;
@ -1037,21 +1039,20 @@ cinfo->out_color_space = JCS_RGB;
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,25 +1061,25 @@ 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)) {
@ -1088,7 +1089,7 @@ bool Image::WriteJpeg(const char *filename,
}
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;
@ -1105,7 +1106,10 @@ bool Image::WriteJpeg(const char *filename,
jpg_err.pub.emit_message = zm_jpeg_emit_silence;
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);
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)
@ -1115,12 +1119,12 @@ bool Image::WriteJpeg(const char *filename,
}
if (!on_blocking_abort) {
if ( (outfile = fopen(filename, "wb")) == nullptr ) {
Error("Can't open %s for writing: %s", filename, strerror(errno));
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);
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");
@ -1240,46 +1244,41 @@ 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);
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)) {
Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u",
@ -1288,7 +1287,7 @@ bool Image::DecodeJpeg(
switch (p_colours) {
case ZM_COLOUR_GRAY8:
cinfo->out_color_space = JCS_GRAYSCALE;
decodejpg_dcinfo->out_color_space = JCS_GRAYSCALE;
new_colours = ZM_COLOUR_GRAY8;
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
break;
@ -1296,17 +1295,17 @@ bool Image::DecodeJpeg(
#ifdef JCS_EXTENSIONS
new_colours = ZM_COLOUR_RGB32;
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
cinfo->out_color_space = JCS_EXT_BGRX;
decodejpg_dcinfo->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;
decodejpg_dcinfo->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;
decodejpg_dcinfo->out_color_space = JCS_EXT_XBGR;
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
} else {
/* Assume RGBA */
cinfo->out_color_space = JCS_EXT_RGBX;
decodejpg_dcinfo->out_color_space = JCS_EXT_RGBX;
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
}
break;
@ -1318,7 +1317,7 @@ bool Image::DecodeJpeg(
new_colours = ZM_COLOUR_RGB24;
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
#ifdef JCS_EXTENSIONS
cinfo->out_color_space = JCS_EXT_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.");
@ -1334,7 +1333,7 @@ cinfo->out_color_space = JCS_EXT_RGB;
cinfo->out_color_space = JCS_RGB;
#endif
*/
cinfo->out_color_space = JCS_RGB;
decodejpg_dcinfo->out_color_space = JCS_RGB;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
}
break;
@ -1342,19 +1341,19 @@ cinfo->out_color_space = JCS_RGB;
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,10 +37,10 @@ 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;
(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) {
@ -51,7 +51,7 @@ 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];
char buffer[JMSG_LENGTH_MAX];
zm_error_ptr zmerr = (zm_error_ptr) cinfo->err;
if (msg_level < 0) {
@ -60,7 +60,7 @@ void zm_jpeg_emit_message(j_common_ptr cinfo, int msg_level) {
* unless trace_level >= 3.
*/
if (zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3) {
(zmerr->pub.format_message)(cinfo, buffer);
zmerr->pub.format_message(cinfo, buffer);
if (!strstr(buffer, "Corrupt JPEG data:"))
Warning("%s", buffer);
}
@ -69,7 +69,7 @@ void zm_jpeg_emit_message(j_common_ptr cinfo, int msg_level) {
} 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);
zmerr->pub.format_message(cinfo, buffer);
Debug(msg_level, "%s", buffer);
}
}

View File

@ -24,8 +24,9 @@
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
/* Workaround for GNU/kFreeBSD and FreeBSD */
#if defined(__FreeBSD_kernel__) || defined(__FreeBSD__)
@ -48,7 +49,6 @@ static int vidioctl(int fd, int request, void *arg) {
static _AVPIXELFORMAT getFfPixFormatFromV4lPalette(int v4l_version, int palette) {
_AVPIXELFORMAT pixFormat = AV_PIX_FMT_NONE;
if ( v4l_version == 2 ) {
switch (palette) {
#if defined(V4L2_PIX_FMT_RGB444) && defined(AV_PIX_FMT_RGB444)
case V4L2_PIX_FMT_RGB444 :
@ -171,7 +171,6 @@ static _AVPIXELFORMAT getFfPixFormatFromV4lPalette(int v4l_version, int palette)
#endif
}
} // end switch palette
} // end if v4l2
return pixFormat;
} // end getFfPixFormatFromV4lPalette
@ -288,7 +287,7 @@ LocalCamera::LocalCamera(
BigEndian = 0;
}
if ( v4l_version == 2 && palette == 0 ) {
if (palette == 0) {
/* Use automatic format selection */
Debug(2,"Using automatic format selection");
palette = AutoSelectFormat(colours);
@ -309,9 +308,6 @@ LocalCamera::LocalCamera(
if (capture) {
if (last_camera) {
if ((p_method == "v4l2" && v4l_version != 2) || (p_method == "v4l1" && v4l_version != 1))
Fatal("Different Video For Linux version used for monitors sharing same device");
if (standard != last_camera->standard)
Warning("Different video standards defined for monitors sharing same device, results may be unpredictable or completely wrong");
@ -327,8 +323,6 @@ LocalCamera::LocalCamera(
imagePixFormat = AV_PIX_FMT_NONE;
}
/* V4L2 format matching */
if ( v4l_version == 2 ) {
/* Try to find a match for the selected palette and target colourspace */
/* RGB32 palette and 32bit target colourspace */
@ -436,7 +430,6 @@ LocalCamera::LocalCamera(
}
} // end if conversion_type == 2
} // end if needs conversion
} // end if v4l2
last_camera = this;
Debug(3, "Selected subpixelorder: %u", subpixelorder);
@ -491,7 +484,6 @@ int LocalCamera::Close() {
void LocalCamera::Initialise() {
Debug(3, "Opening video device %s", device.c_str());
//if ( (vid_fd = open( device.c_str(), O_RDWR|O_NONBLOCK, 0 )) < 0 )
if ((vid_fd = open(device.c_str(), O_RDWR, 0)) < 0)
Fatal("Failed to open video device %s: %s", device.c_str(), strerror(errno));
@ -502,8 +494,6 @@ void LocalCamera::Initialise() {
if (!S_ISCHR(st.st_mode))
Fatal("File %s is not device file: %s", device.c_str(), strerror(errno));
Debug(2, "V4L2 support enabled, using V4L%d api", v4l_version);
if ( v4l_version == 2 ) {
struct v4l2_capability vid_cap;
Debug(3, "Checking video device capabilities");
@ -735,7 +725,6 @@ void LocalCamera::Initialise() {
Brightness(brightness);
Hue(hue);
Colour(colour);
}
} // end LocalCamera::Initialize
void LocalCamera::Terminate() {
@ -864,54 +853,57 @@ uint32_t LocalCamera::AutoSelectFormat(int p_colours) {
(test) ? (prefix yesString " " capability "\n") : (prefix noString " " capability "\n")
bool LocalCamera::GetCurrentSettings(
const char *device,
const std::string& device,
char *output,
int version,
bool verbose) {
output[0] = 0;
char *output_ptr = output;
char queryDevice[PATH_MAX] = "";
std::string queryDevice;
int devIndex = 0;
do {
if ( device ) {
strncpy(queryDevice, device, sizeof(queryDevice)-1);
if (!device.empty()) {
queryDevice = device;
} else {
sprintf(queryDevice, "/dev/video%d", devIndex);
queryDevice = stringtf("/dev/video%d", devIndex);
}
if ( (vid_fd = open(queryDevice, O_RDWR)) <= 0 ) {
if ( device ) {
Error("Failed to open video device %s: %s", queryDevice, strerror(errno));
if ( verbose )
if ((vid_fd = open(queryDevice.c_str(), O_RDWR)) <= 0) {
if (!device.empty()) {
Error("Failed to open video device %s: %s", queryDevice.c_str(), strerror(errno));
if (verbose) {
output_ptr += sprintf(output_ptr, "Error, failed to open video device %s: %s\n",
queryDevice, strerror(errno));
else
queryDevice.c_str(), strerror(errno));
} else {
output_ptr += sprintf(output_ptr, "error%d\n", errno);
}
return false;
} else {
return true;
}
}
if (verbose) {
output_ptr += sprintf(output_ptr, "Video Device: %s\n", queryDevice);
output_ptr += sprintf(output_ptr, "Video Device: %s\n", queryDevice.c_str());
} else {
output_ptr += sprintf(output_ptr, "d:%s|", queryDevice);
output_ptr += sprintf(output_ptr, "d:%s|", queryDevice.c_str());
}
if (version == 2) {
struct v4l2_capability vid_cap;
v4l2_capability vid_cap = {};
if (vidioctl(vid_fd, VIDIOC_QUERYCAP, &vid_cap) < 0) {
Error("Failed to query video device: %s", strerror(errno));
if (verbose) {
output_ptr += sprintf(output_ptr, "Error, failed to query video capabilities %s: %s\n",
queryDevice, strerror(errno));
queryDevice.c_str(), strerror(errno));
} else {
output_ptr += sprintf(output_ptr, "error%d\n", errno);
}
if ( device )
if (!device.empty()) {
return false;
}
}
if ( verbose ) {
output_ptr += sprintf(output_ptr, "General Capabilities\n"
@ -953,7 +945,7 @@ bool LocalCamera::GetCurrentSettings(
output_ptr += sprintf(output_ptr, verbose ? " Standards:\n" : "S:");
struct v4l2_standard standard;
v4l2_standard standard = {};
int standardIndex = 0;
do {
memset(&standard, 0, sizeof(standard));
@ -981,10 +973,10 @@ bool LocalCamera::GetCurrentSettings(
*(output_ptr-1) = '|';
output_ptr += sprintf(output_ptr, verbose ? " Formats:\n" : "F:");
struct v4l2_fmtdesc format;
int formatIndex = 0;
do {
memset(&format, 0, sizeof(format));
v4l2_fmtdesc format = {};
format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format.index = formatIndex;
@ -1025,9 +1017,9 @@ bool LocalCamera::GetCurrentSettings(
else
output_ptr += sprintf(output_ptr, "Crop Capabilities\n");
struct v4l2_cropcap cropcap;
memset(&cropcap, 0, sizeof(cropcap));
v4l2_cropcap cropcap = {};
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( vidioctl(vid_fd, VIDIOC_CROPCAP, &cropcap) < 0 ) {
if ( errno != EINVAL ) {
/* Failed querying crop capability, write error to the log and continue as if crop is not supported */
@ -1041,8 +1033,8 @@ bool LocalCamera::GetCurrentSettings(
output_ptr += sprintf(output_ptr, "B:%dx%d|", 0, 0);
}
} else {
struct v4l2_crop crop;
memset(&crop, 0, sizeof(crop));
v4l2_crop crop = {};
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( vidioctl(vid_fd, VIDIOC_G_CROP, &crop) < 0 ) {
@ -1127,17 +1119,17 @@ bool LocalCamera::GetCurrentSettings(
" Name: %s\n"
" Type: %s\n"
" Audioset: %08x\n"
" Standards: 0x%llx\n"
" Standards: 0x%" PRIx64"\n"
, input.index
, input.name
, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown")
, input.audioset
, input.std );
, static_cast<uint64>(input.std));
} else {
output_ptr += sprintf( output_ptr, "i%d:%s|i%dT:%s|i%dS:%llx|"
output_ptr += sprintf( output_ptr, "i%d:%s|i%dT:%s|i%dS:%" PRIx64 "|"
, input.index, input.name
, input.index, input.type==V4L2_INPUT_TYPE_TUNER?"Tuner":(input.type==V4L2_INPUT_TYPE_CAMERA?"Camera":"Unknown")
, input.index, input.std);
, input.index, static_cast<uint64>(input.std));
}
if ( verbose ) {
@ -1159,132 +1151,54 @@ bool LocalCamera::GetCurrentSettings(
}
close(vid_fd);
if ( device )
if (!device.empty()) {
break;
}
} while ( ++devIndex < 32 );
return true;
}
int LocalCamera::Brightness(int p_brightness) {
if ( v4l_version == 2 ) {
int LocalCamera::Control(int vid_id, int newvalue) {
struct v4l2_control vid_control;
memset(&vid_control, 0, sizeof(vid_control));
vid_control.id = V4L2_CID_BRIGHTNESS;
vid_control.id = vid_id;
if (vidioctl(vid_fd, VIDIOC_G_CTRL, &vid_control) < 0) {
if (errno != EINVAL) {
Error("Unable to query brightness: %s", strerror(errno));
Error("Unable to query control: %s", strerror(errno));
} else {
Warning("Brightness control is not supported");
Warning("Control is not supported");
}
//Info( "Brightness 1 %d", vid_control.value );
} else if ( p_brightness >= 0 ) {
vid_control.value = p_brightness;
} else if (newvalue >= 0) {
vid_control.value = newvalue;
//Info( "Brightness 2 %d", vid_control.value );
/* The driver may clamp the value or return ERANGE, ignored here */
if ( vidioctl(vid_fd, VIDIOC_S_CTRL, &vid_control) ) {
if (errno != ERANGE) {
Error("Unable to set brightness: %s", strerror(errno));
Error("Unable to set control: %s", strerror(errno));
} else {
Warning("Given brightness value (%d) may be out-of-range", p_brightness);
Warning("Given control value (%d) may be out-of-range", newvalue);
}
}
//Info( "Brightness 3 %d", vid_control.value );
}
return vid_control.value;
}
return -1;
int LocalCamera::Brightness(int p_brightness) {
return Control(V4L2_CID_BRIGHTNESS, p_brightness);
}
int LocalCamera::Hue(int p_hue) {
if ( v4l_version == 2 ) {
struct v4l2_control vid_control;
memset( &vid_control, 0, sizeof(vid_control) );
vid_control.id = V4L2_CID_HUE;
if ( vidioctl(vid_fd, VIDIOC_G_CTRL, &vid_control) < 0 ) {
if ( errno != EINVAL )
Error("Unable to query hue: %s", strerror(errno));
else
Warning("Hue control is not supported");
} else if ( p_hue >= 0 ) {
vid_control.value = p_hue;
/* The driver may clamp the value or return ERANGE, ignored here */
if ( vidioctl(vid_fd, VIDIOC_S_CTRL, &vid_control) < 0 ) {
if ( errno != ERANGE ) {
Error("Unable to set hue: %s", strerror(errno));
} else {
Warning("Given hue value (%d) may be out-of-range", p_hue);
}
}
}
return vid_control.value;
}
return -1;
return Control(V4L2_CID_HUE, p_hue);
}
int LocalCamera::Colour( int p_colour ) {
if ( v4l_version == 2 ) {
struct v4l2_control vid_control;
memset(&vid_control, 0, sizeof(vid_control));
vid_control.id = V4L2_CID_SATURATION;
if ( vidioctl(vid_fd, VIDIOC_G_CTRL, &vid_control) < 0 ) {
if ( errno != EINVAL ) {
Error("Unable to query saturation: %s", strerror(errno));
} else {
Warning("Saturation control is not supported");
}
} else if ( p_colour >= 0 ) {
vid_control.value = p_colour;
/* The driver may clamp the value or return ERANGE, ignored here */
if ( vidioctl(vid_fd, VIDIOC_S_CTRL, &vid_control) < 0 ) {
if ( errno != ERANGE ) {
Error("Unable to set saturation: %s", strerror(errno));
} else {
Warning("Given saturation value (%d) may be out-of-range", p_colour);
}
}
}
return vid_control.value;
}
return -1;
return Control(V4L2_CID_SATURATION, p_colour);
}
int LocalCamera::Contrast(int p_contrast) {
if ( v4l_version == 2 ) {
struct v4l2_control vid_control;
memset(&vid_control, 0, sizeof(vid_control));
vid_control.id = V4L2_CID_CONTRAST;
if ( vidioctl(vid_fd, VIDIOC_G_CTRL, &vid_control) < 0 ) {
if ( errno != EINVAL ) {
Error("Unable to query contrast: %s", strerror(errno));
} else {
Warning("Contrast control is not supported");
}
} else if ( p_contrast >= 0 ) {
vid_control.value = p_contrast;
/* The driver may clamp the value or return ERANGE, ignored here */
if ( vidioctl(vid_fd, VIDIOC_S_CTRL, &vid_control) ) {
if ( errno != ERANGE ) {
Error("Unable to set contrast: %s", strerror(errno));
} else {
Warning("Given contrast value (%d) may be out-of-range", p_contrast);
}
}
}
return vid_control.value;
}
return -1;
return Control(V4L2_CID_CONTRAST, p_contrast);
}
int LocalCamera::PrimeCapture() {
@ -1292,8 +1206,6 @@ int LocalCamera::PrimeCapture() {
if (!device_prime)
return 1;
Debug(2, "Priming capture");
if ( v4l_version == 2 ) {
Debug(3, "Queueing (%d) buffers", v4l2_data.reqbufs.count);
for (unsigned int frame = 0; frame < v4l2_data.reqbufs.count; frame++) {
struct v4l2_buffer vid_buf;
@ -1322,13 +1234,11 @@ int LocalCamera::PrimeCapture() {
Error("Failed to start capture stream: %s", strerror(errno));
return -1;
}
} // end if v4l_version == 2
return 1;
} // end LocalCamera::PrimeCapture
int LocalCamera::PreCapture() {
//Debug(5, "Pre-capturing");
return 1;
}
@ -1348,7 +1258,6 @@ int LocalCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
// Do the capture, unless we are the second or subsequent camera on a channel, in which case just reuse the buffer
if (channel_prime) {
if ( v4l_version == 2 ) {
static struct v4l2_buffer vid_buf;
memset(&vid_buf, 0, sizeof(vid_buf));
@ -1393,9 +1302,7 @@ int LocalCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
Error("Captured image dimensions differ: V4L2: %dx%d monitor: %dx%d",
v4l2_data.fmt.fmt.pix.width, v4l2_data.fmt.fmt.pix.height, width, height);
}
} // end if v4l2
if ( v4l_version == 2 ) {
if (channel_count > 1) {
int next_channel = (channel_index+1)%channel_count;
Debug(3, "Switching video source to %d", channels[next_channel]);
@ -1418,7 +1325,6 @@ int LocalCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
} else {
Error("Unable to requeue buffer due to not v4l2_data");
}
}
} /* prime capture */
if (!zm_packet->image) {
@ -1478,4 +1384,4 @@ int LocalCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
int LocalCamera::PostCapture() {
return 1;
}
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2

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 {

View File

@ -33,9 +33,9 @@
#include "zm_utils.h"
#include "zm_zone.h"
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
#include "zm_local_camera.h"
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
#if HAVE_LIBVLC
#include "zm_libvlc_camera.h"
@ -137,8 +137,9 @@ Monitor::MonitorLink::~MonitorLink() {
}
bool Monitor::MonitorLink::connect() {
if ( !last_connect_time || (time(nullptr) - last_connect_time) > 60 ) {
last_connect_time = time(nullptr);
SystemTimePoint now = std::chrono::system_clock::now();
if (!last_connect_time || (now - std::chrono::system_clock::from_time_t(last_connect_time)) > Seconds(60)) {
last_connect_time = std::chrono::system_clock::to_time_t(now);
mem_size = sizeof(SharedData) + sizeof(TriggerData);
@ -230,7 +231,7 @@ bool Monitor::MonitorLink::disconnect() {
struct shmid_ds shm_data;
if (shmctl(shm_id, IPC_STAT, &shm_data) < 0) {
Debug(3, "Can't shmctl: %s", strerror(errno));
return( false );
return false;
}
shm_id = 0;
@ -246,7 +247,6 @@ bool Monitor::MonitorLink::disconnect() {
Debug(3, "Can't shmdt: %s", strerror(errno));
return false;
}
#endif // ZM_MEM_MAPPED
mem_size = 0;
mem_ptr = nullptr;
@ -472,16 +472,22 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
Debug(1, "Have camera type %s", CameraType_Strings[type].c_str());
col++;
function = (Function)atoi(dbrow[col]); col++;
enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : 0; col++;
enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
decoding_enabled = dbrow[col] ? atoi(dbrow[col]) : false; col++;
ReloadLinkedMonitors(dbrow[col]); col++;
/* "AnalysisFPSLimit, AnalysisUpdateDelay, MaxFPS, AlarmMaxFPS," */
analysis_fps_limit = dbrow[col] ? strtod(dbrow[col], nullptr) : 0.0; col++;
analysis_update_delay = strtoul(dbrow[col++], nullptr, 0);
capture_delay = (dbrow[col] && atof(dbrow[col]) > 0.0) ? int(Microseconds::period::den / atof(dbrow[col])) : 0; col++;
alarm_capture_delay = (dbrow[col] && atof(dbrow[col]) > 0.0) ? int(Microseconds::period::den / atof(dbrow[col])) : 0; col++;
analysis_update_delay = Seconds(strtoul(dbrow[col++], nullptr, 0));
capture_delay =
(dbrow[col] && atof(dbrow[col]) > 0.0) ? std::chrono::duration_cast<Microseconds>(FPSeconds(1 / atof(dbrow[col])))
: Microseconds(0);
col++;
alarm_capture_delay =
(dbrow[col] && atof(dbrow[col]) > 0.0) ? std::chrono::duration_cast<Microseconds>(FPSeconds(1 / atof(dbrow[col])))
: Microseconds(0);
col++;
/* "Device, Channel, Format, V4LMultiBuffer, V4LCapturesPerFrame, " // V4L Settings */
device = dbrow[col] ? dbrow[col] : ""; col++;
@ -569,8 +575,8 @@ void Monitor::Load(MYSQL_ROW dbrow, bool load_zones=true, Purpose p = QUERY) {
else if (alarm_frame_count > MAX_PRE_ALARM_FRAMES) alarm_frame_count = MAX_PRE_ALARM_FRAMES;
/* "SectionLength, MinSectionLength, FrameSkip, MotionFrameSkip, " */
section_length = atoi(dbrow[col]); col++;
min_section_length = atoi(dbrow[col]); col++;
section_length = Seconds(atoi(dbrow[col])); col++;
min_section_length = Seconds(atoi(dbrow[col])); col++;
frame_skip = atoi(dbrow[col]); col++;
motion_frame_skip = atoi(dbrow[col]); col++;
@ -678,7 +684,7 @@ void Monitor::LoadCamera() {
switch (type) {
case LOCAL: {
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
int extras = (deinterlacing >> 24) & 0xff;
camera = zm::make_unique<LocalCamera>(this,
@ -873,7 +879,7 @@ std::shared_ptr<Monitor> Monitor::Load(unsigned int p_id, bool load_zones, Purpo
std::string sql = load_monitor_sql + stringtf(" WHERE Id=%d", p_id);
zmDbRow dbrow;
if ( !dbrow.fetch(sql.c_str()) ) {
if (!dbrow.fetch(sql)) {
Error("Can't use query result: %s", mysql_error(&dbconn));
return nullptr;
}
@ -885,7 +891,6 @@ std::shared_ptr<Monitor> Monitor::Load(unsigned int p_id, bool load_zones, Purpo
}
bool Monitor::connect() {
if (mem_ptr != nullptr) {
Warning("Already connected. Please call disconnect first.");
}
@ -1047,6 +1052,13 @@ bool Monitor::disconnect() {
return true;
}
if (purpose == CAPTURE) {
if (unlink(mem_file.c_str()) < 0) {
Warning("Can't unlink '%s': %s", mem_file.c_str(), strerror(errno));
}
Debug(1, "Setting shared_data->valid = false");
shared_data->valid = false;
}
#if ZM_MEM_MAPPED
msync(mem_ptr, mem_size, MS_ASYNC);
munmap(mem_ptr, mem_size);
@ -1056,9 +1068,6 @@ bool Monitor::disconnect() {
mem_ptr = nullptr;
shared_data = nullptr;
if (purpose == CAPTURE and (unlink(mem_file.c_str()) < 0) ) {
Warning("Can't unlink '%s': %s", mem_file.c_str(), strerror(errno));
}
#else // ZM_MEM_MAPPED
struct shmid_ds shm_data;
if (shmctl(shm_id, IPC_STAT, &shm_data) < 0) {
@ -1093,10 +1102,6 @@ Monitor::~Monitor() {
if (mem_ptr != nullptr) {
if (purpose != QUERY) {
shared_data->state = state = IDLE;
shared_data->last_read_index = image_buffer_count;
shared_data->last_read_time = 0;
shared_data->valid = false;
memset(mem_ptr, 0, mem_size);
} // end if purpose != query
disconnect();
@ -1148,7 +1153,7 @@ void Monitor::AddPrivacyBitmask() {
int Monitor::GetImage(int32_t index, int scale) {
if (index < 0 || index > image_buffer_count) {
Warning("Invalid index %d passed. image_buffer_count = %d", index, image_buffer_count);
Debug(1, "Invalid index %d passed. image_buffer_count = %d", index, image_buffer_count);
index = shared_data->last_write_index;
}
if (!image_buffer.size() or static_cast<size_t>(index) >= image_buffer.size()) {
@ -1170,15 +1175,14 @@ int Monitor::GetImage(int32_t index, int scale) {
}
if (!config.timestamp_on_capture) {
TimestampImage(&alarm_image, shared_timestamps[index]);
TimestampImage(&alarm_image, SystemTimePoint(zm::chrono::duration_cast<Microseconds>(shared_timestamps[index])));
}
image = &alarm_image;
} else {
image = image_buffer[index];
}
static char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "Monitor%u.jpg", id);
std::string filename = stringtf("Monitor%u.jpg", id);
image->WriteJpeg(filename);
return 1;
}
@ -1192,20 +1196,20 @@ ZMPacket *Monitor::getSnapshot(int index) const {
return nullptr;
}
if (index != image_buffer_count) {
return new ZMPacket(image_buffer[index], shared_timestamps[index]);
return new ZMPacket(image_buffer[index],
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(shared_timestamps[index])));
} else {
Error("Unable to generate image, no images in buffer");
}
return nullptr;
}
struct timeval Monitor::GetTimestamp(int index) const {
SystemTimePoint Monitor::GetTimestamp(int index) const {
ZMPacket *packet = getSnapshot(index);
if (packet)
return packet->timestamp;
static struct timeval null_tv = { 0, 0 };
return null_tv;
return {};
}
unsigned int Monitor::GetLastReadIndex() const {
@ -1280,16 +1284,14 @@ void Monitor::actionReload() {
void Monitor::actionEnable() {
shared_data->action |= RELOAD;
char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "UPDATE `Monitors` SET `Enabled` = 1 WHERE `Id` = %u", id);
std::string sql = stringtf("UPDATE `Monitors` SET `Enabled` = 1 WHERE `Id` = %u", id);
zmDbDo(sql);
}
void Monitor::actionDisable() {
shared_data->action |= RELOAD;
char sql[ZM_SQL_SML_BUFSIZ];
snprintf(sql, sizeof(sql), "UPDATE `Monitors` SET `Enabled` = 0 WHERE `Id` = %u", id);
std::string sql = stringtf("UPDATE `Monitors` SET `Enabled` = 0 WHERE `Id` = %u", id);
zmDbDo(sql);
}
@ -1302,127 +1304,153 @@ void Monitor::actionResume() {
}
int Monitor::actionBrightness(int p_brightness) {
if ( purpose != CAPTURE ) {
if ( p_brightness >= 0 ) {
if (purpose == CAPTURE) {
// We are the capture process, so take the action
return camera->Brightness(p_brightness);
}
// If we are an outside actor, sending the command
shared_data->brightness = p_brightness;
shared_data->action |= SET_SETTINGS;
int wait_loops = 10;
while (shared_data->action & SET_SETTINGS) {
if (wait_loops--) {
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
} else {
Warning("Timed out waiting to set brightness");
return -1;
}
}
} else {
return shared_data->brightness;
}
int Monitor::actionBrightness() {
if (purpose == CAPTURE) {
// We are the capture process, so take the action
return camera->Brightness();
}
// If we are an outside actor, sending the command
shared_data->action |= GET_SETTINGS;
int wait_loops = 10;
while (shared_data->action & GET_SETTINGS) {
if (wait_loops--) {
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
} else {
Warning("Timed out waiting to get brightness");
return -1;
}
}
}
return shared_data->brightness;
}
return camera->Brightness(p_brightness);
} // end int Monitor::actionBrightness(int p_brightness)
} // end int Monitor::actionBrightness()
int Monitor::actionContrast(int p_contrast) {
if ( purpose != CAPTURE ) {
if ( p_contrast >= 0 ) {
if (purpose == CAPTURE) {
return camera->Contrast(p_contrast);
}
shared_data->contrast = p_contrast;
shared_data->action |= SET_SETTINGS;
int wait_loops = 10;
while (shared_data->action & SET_SETTINGS) {
if (wait_loops--) {
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
} else {
Warning("Timed out waiting to set contrast");
return -1;
}
}
} else {
return shared_data->contrast;
}
int Monitor::actionContrast() {
if (purpose == CAPTURE) {
// We are the capture process, so take the action
return camera->Contrast();
}
shared_data->action |= GET_SETTINGS;
int wait_loops = 10;
while (shared_data->action & GET_SETTINGS) {
if (wait_loops--) {
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
} else {
Warning("Timed out waiting to get contrast");
return -1;
}
}
}
return shared_data->contrast;
}
return camera->Contrast(p_contrast);
} // end int Monitor::actionContrast(int p_contrast)
} // end int Monitor::actionContrast()
int Monitor::actionHue(int p_hue) {
if ( purpose != CAPTURE ) {
if ( p_hue >= 0 ) {
if (purpose == CAPTURE) {
return camera->Hue(p_hue);
}
shared_data->hue = p_hue;
shared_data->action |= SET_SETTINGS;
int wait_loops = 10;
while (shared_data->action & SET_SETTINGS) {
if (wait_loops--) {
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
} else {
Warning("Timed out waiting to set hue");
return -1;
}
}
} else {
return shared_data->hue;
}
int Monitor::actionHue() {
if (purpose == CAPTURE) {
return camera->Hue();
}
shared_data->action |= GET_SETTINGS;
int wait_loops = 10;
while (shared_data->action & GET_SETTINGS) {
if (wait_loops--) {
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
} else {
Warning("Timed out waiting to get hue");
return -1;
}
}
}
return shared_data->hue;
}
return camera->Hue(p_hue);
} // end int Monitor::actionHue(int p_hue)
int Monitor::actionColour(int p_colour) {
if ( purpose != CAPTURE ) {
if ( p_colour >= 0 ) {
if (purpose == CAPTURE) {
return camera->Colour(p_colour);
}
shared_data->colour = p_colour;
shared_data->action |= SET_SETTINGS;
int wait_loops = 10;
while (shared_data->action & SET_SETTINGS) {
if (wait_loops--) {
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
} else {
Warning("Timed out waiting to set colour");
return -1;
}
}
} else {
return shared_data->colour;
}
int Monitor::actionColour() {
if (purpose == CAPTURE) {
return camera->Colour();
}
shared_data->action |= GET_SETTINGS;
int wait_loops = 10;
while (shared_data->action & GET_SETTINGS) {
if (wait_loops--) {
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
} else {
Warning("Timed out waiting to get colour");
return -1;
}
}
}
return shared_data->colour;
}
return camera->Colour(p_colour);
} // end int Monitor::actionColour(int p_colour)
void Monitor::DumpZoneImage(const char *zone_string) {
@ -1447,7 +1475,7 @@ void Monitor::DumpZoneImage(const char *zone_string) {
// Grab the most revent event image
std::string sql = stringtf("SELECT MAX(`Id`) FROM `Events` WHERE `MonitorId`=%d AND `Frames` > 0", id);
zmDbRow eventid_row;
if ( eventid_row.fetch(sql.c_str()) ) {
if (eventid_row.fetch(sql)) {
uint64_t event_id = atoll(eventid_row[0]);
Debug(3, "Got event %" PRIu64, event_id);
@ -1501,20 +1529,20 @@ void Monitor::DumpZoneImage(const char *zone_string) {
zone_image->Outline(extra_colour, extra_zone);
}
static char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "Zones%u.jpg", id);
std::string filename = stringtf("Zones%u.jpg", id);
zone_image->WriteJpeg(filename);
delete zone_image;
} // end void Monitor::DumpZoneImage(const char *zone_string)
void Monitor::DumpImage(Image *dump_image) const {
if (image_count && !(image_count % 10)) {
static char filename[PATH_MAX];
static char new_filename[PATH_MAX];
snprintf(filename, sizeof(filename), "Monitor%u.jpg", id);
snprintf(new_filename, sizeof(new_filename), "Monitor%u-new.jpg", id);
if ( dump_image->WriteJpeg(new_filename) )
rename(new_filename, filename);
std::string filename = stringtf("Monitor%u.jpg", id);
std::string new_filename = stringtf("Monitor%u-new.jpg", id);
if (dump_image->WriteJpeg(new_filename)) {
rename(new_filename.c_str(), filename.c_str());
}
}
} // end void Monitor::DumpImage(Image *dump_image)
@ -1733,10 +1761,6 @@ bool Monitor::Analyse() {
return false;
}
// Store the it that points to our snap we will need it later
packetqueue_iterator snap_it = *analysis_it;
packetqueue.increment_it(analysis_it);
// signal is set by capture
bool signal = shared_data->signal;
bool signal_change = (signal != last_signal);
@ -1846,7 +1870,14 @@ bool Monitor::Analyse() {
while (!snap->decoded and !zm_terminate and !analysis_thread->Stopped()) {
// Need to wait for the decoder thread.
Debug(1, "Waiting for decode");
packet_lock->wait();
packetqueue.unlock(packet_lock); // This will delete packet_lock and notify_all
packetqueue.wait();
// Another thread may have moved our it. Unlikely but possible
packet_lock = packetqueue.get_packet(analysis_it);
if (!packet_lock) return false;
snap = packet_lock->packet_;
if (!snap->image and snap->decoded) {
Debug(1, "No image but was decoded, giving up");
delete packet_lock;
@ -1859,7 +1890,7 @@ bool Monitor::Analyse() {
}
} // end if decoding enabled
struct timeval *timestamp = &snap->timestamp;
SystemTimePoint timestamp = snap->timestamp;
if (Active() and (function == MODECT or function == MOCORD)) {
Debug(3, "signal and active and modect");
@ -1922,23 +1953,18 @@ bool Monitor::Analyse() {
if (event) {
Debug(2, "Have event %" PRIu64 " in record", event->Id());
if (section_length &&
(( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length)
&& (
((function == MOCORD) && (event_close_mode != CLOSE_TIME))
||
( (function == RECORD) && (event_close_mode == CLOSE_TIME) )
|| ! ( timestamp->tv_sec % section_length )
)
) {
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %d",
if (section_length != Seconds(0) && (timestamp - GetVideoWriterStartTime() >= section_length)
&& ((function == MOCORD && event_close_mode != CLOSE_TIME)
|| (function == RECORD && event_close_mode == CLOSE_TIME)
|| std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()) % section_length == Seconds(0))) {
Info("%s: %03d - Closing event %" PRIu64 ", section end forced %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64 ,
name.c_str(),
image_count,
event->Id(),
static_cast<int64>(timestamp->tv_sec),
static_cast<int64>(video_store_data->recording.tv_sec),
static_cast<int64>(timestamp->tv_sec - video_store_data->recording.tv_sec),
section_length);
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(GetVideoWriterStartTime().time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()),
static_cast<int64>(Seconds(section_length).count()));
closeEvent();
} // end if section_length
} // end if event
@ -1949,14 +1975,14 @@ bool Monitor::Analyse() {
// Must start on a keyframe so rewind. Only for passthrough though I guess.
// FIXME this iterator is not protected from invalidation
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
snap_it, 0 /* pre_event_count */
*analysis_it, 0 /* pre_event_count */
);
// This gets a lock on the starting packet
ZMLockedPacket *starting_packet_lock = nullptr;
std::shared_ptr<ZMPacket> starting_packet = nullptr;
if (*start_it != snap_it) {
if (*start_it != *analysis_it) {
starting_packet_lock = packetqueue.get_packet(start_it);
if (!starting_packet_lock) {
Warning("Unable to get starting packet lock");
@ -1970,12 +1996,12 @@ bool Monitor::Analyse() {
event = new Event(this, starting_packet->timestamp, "Continuous", noteSetMap);
// Write out starting packets, do not modify packetqueue it will garbage collect itself
while (starting_packet and ((*start_it) != snap_it)) {
while (starting_packet and ((*start_it) != *analysis_it)) {
event->AddPacket(starting_packet);
// Have added the packet, don't want to unlock it until we have locked the next
packetqueue.increment_it(start_it);
if ((*start_it) == snap_it) {
if ((*start_it) == *analysis_it) {
if (starting_packet_lock) delete starting_packet_lock;
break;
}
@ -1990,7 +2016,7 @@ bool Monitor::Analyse() {
start_it = nullptr;
} else {
// Create event from current snap
event = new Event(this, *timestamp, "Continuous", noteSetMap);
event = new Event(this, timestamp, "Continuous", noteSetMap);
}
shared_data->last_event_id = event->Id();
@ -2004,7 +2030,8 @@ bool Monitor::Analyse() {
}
alarm_cause = cause+" Continuous "+alarm_cause;
strncpy(shared_data->alarm_cause, alarm_cause.c_str(), sizeof(shared_data->alarm_cause)-1);
video_store_data->recording = event->StartTime();
SetVideoWriterStartTime(event->StartTime());
Info("%s: %03d - Opened new event %" PRIu64 ", section start",
name.c_str(), analysis_image_count, event->Id());
/* To prevent cancelling out an existing alert\prealarm\alarm state */
@ -2019,23 +2046,22 @@ bool Monitor::Analyse() {
if ((state == IDLE) || (state == TAPE) || (state == PREALARM)) {
// If we should end then previous continuous event and start a new non-continuous event
if (event && event->Frames()
&& (!event->AlarmFrames())
&& (event_close_mode == CLOSE_ALARM)
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= min_section_length )
&& ( (!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1) )
) {
&& !event->AlarmFrames()
&& event_close_mode == CLOSE_ALARM
&& timestamp - GetVideoWriterStartTime() >= min_section_length
&& (!pre_event_count || Event::PreAlarmCount() >= alarm_frame_count - 1)) {
Info("%s: %03d - Closing event %" PRIu64 ", continuous end, alarm begins",
name.c_str(), image_count, event->Id());
closeEvent();
} else if (event) {
// This is so if we need more than 1 alarm frame before going into alarm, so it is basically if we have enough alarm frames
Debug(3,
"pre_alarm_count in event %d, event frames %d, alarm frames %d event length %" PRIi64 " >=? %d min",
"pre_alarm_count in event %d, event frames %d, alarm frames %d event length %" PRIi64 " >=? %" PRIi64 " min",
Event::PreAlarmCount(),
event->Frames(),
event->AlarmFrames(),
static_cast<int64>(timestamp->tv_sec - video_store_data->recording.tv_sec),
min_section_length);
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()),
static_cast<int64>(Seconds(min_section_length).count()));
}
if ((!pre_event_count) || (Event::PreAlarmCount() >= alarm_frame_count-1)) {
// lets construct alarm cause. It will contain cause + names of zones alarmed
@ -2053,12 +2079,12 @@ bool Monitor::Analyse() {
if (!event) {
packetqueue_iterator *start_it = packetqueue.get_event_start_packet_it(
snap_it,
*analysis_it,
(pre_event_count > alarm_frame_count ? pre_event_count : alarm_frame_count)
);
ZMLockedPacket *starting_packet_lock = nullptr;
std::shared_ptr<ZMPacket> starting_packet = nullptr;
if (*start_it != snap_it) {
if (*start_it != *analysis_it) {
starting_packet_lock = packetqueue.get_packet(start_it);
if (!starting_packet_lock) return false;
starting_packet = starting_packet_lock->packet_;
@ -2069,15 +2095,15 @@ bool Monitor::Analyse() {
event = new Event(this, starting_packet->timestamp, cause, noteSetMap);
shared_data->last_event_id = event->Id();
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
video_store_data->recording = event->StartTime();
SetVideoWriterStartTime(event->StartTime());
shared_data->state = state = ALARM;
// Write out starting packets, do not modify packetqueue it will garbage collect itself
while (*start_it != snap_it) {
while (*start_it != *analysis_it) {
event->AddPacket(starting_packet);
packetqueue.increment_it(start_it);
if ( (*start_it) == snap_it ) {
if ( (*start_it) == (*analysis_it) ) {
if (starting_packet_lock) delete starting_packet_lock;
break;
}
@ -2132,11 +2158,8 @@ bool Monitor::Analyse() {
Info("%s: %03d - Gone into alert state", name.c_str(), analysis_image_count);
shared_data->state = state = ALERT;
} else if (state == ALERT) {
if (
( analysis_image_count-last_alarm_count > post_event_count )
&&
( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= min_section_length )
) {
if (analysis_image_count - last_alarm_count > post_event_count
&& timestamp - GetVideoWriterStartTime() >= min_section_length) {
Info("%s: %03d - Left alarm state (%" PRIu64 ") - %d(%d) images",
name.c_str(), analysis_image_count, event->Id(), event->Frames(), event->AlarmFrames());
//if ( function != MOCORD || event_close_mode == CLOSE_ALARM || event->Cause() == SIGNAL_CAUSE )
@ -2154,14 +2177,14 @@ bool Monitor::Analyse() {
shared_data->state = state = ((function != MOCORD) ? IDLE : TAPE);
} else {
Debug(1,
"State %s because image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%" PRIi64 ") - recording.tv_src(%" PRIi64 ") >= min_section_length(%d)",
"State %s because image_count(%d)-last_alarm_count(%d) > post_event_count(%d) and timestamp.tv_sec(%" PRIi64 ") - recording.tv_src(%" PRIi64 ") >= min_section_length(%" PRIi64 ")",
State_Strings[state].c_str(),
analysis_image_count,
last_alarm_count,
post_event_count,
static_cast<int64>(timestamp->tv_sec),
static_cast<int64>(video_store_data->recording.tv_sec),
min_section_length);
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(GetVideoWriterStartTime().time_since_epoch()).count()),
static_cast<int64>(Seconds(min_section_length).count()));
}
if (Event::PreAlarmCount())
Event::EmptyPreAlarmFrames();
@ -2185,7 +2208,7 @@ bool Monitor::Analyse() {
// incremement pre alarm image count
//have_pre_alarmed_frames ++;
Event::AddPreAlarmFrame(snap->image, *timestamp, score, nullptr);
Event::AddPreAlarmFrame(snap->image, timestamp, score, nullptr);
} else if (state == ALARM) {
for (const Zone &zone : zones) {
if (zone.Alarmed()) {
@ -2199,20 +2222,19 @@ bool Monitor::Analyse() {
if (event) {
if (noteSetMap.size() > 0)
event->updateNotes(noteSetMap);
if ( section_length
&& ( ( timestamp->tv_sec - video_store_data->recording.tv_sec ) >= section_length )
) {
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %d",
if (section_length != Seconds(0) && (timestamp - GetVideoWriterStartTime() >= section_length)) {
Warning("%s: %03d - event %" PRIu64 ", has exceeded desired section length. %" PRIi64 " - %" PRIi64 " = %" PRIi64 " >= %" PRIi64,
name.c_str(), analysis_image_count, event->Id(),
static_cast<int64>(timestamp->tv_sec), static_cast<int64>(video_store_data->recording.tv_sec),
static_cast<int64>(timestamp->tv_sec - video_store_data->recording.tv_sec),
section_length);
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(GetVideoWriterStartTime().time_since_epoch()).count()),
static_cast<int64>(std::chrono::duration_cast<Seconds>(timestamp - GetVideoWriterStartTime()).count()),
static_cast<int64>(Seconds(section_length).count()));
closeEvent();
event = new Event(this, *timestamp, cause, noteSetMap);
event = new Event(this, timestamp, cause, noteSetMap);
shared_data->last_event_id = event->Id();
//set up video store data
snprintf(video_store_data->event_file, sizeof(video_store_data->event_file), "%s", event->getEventFile());
video_store_data->recording = event->StartTime();
SetVideoWriterStartTime(event->StartTime());
}
} else {
Error("ALARM but no event");
@ -2268,8 +2290,9 @@ bool Monitor::Analyse() {
if (function == MODECT or function == MOCORD)
UpdateAnalysisFPS();
}
packetqueue.increment_it(analysis_it);
packetqueue.unlock(packet_lock);
shared_data->last_read_time = time(nullptr);
shared_data->last_read_time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
return true;
} // end Monitor::Analyse
@ -2287,7 +2310,7 @@ void Monitor::Reload() {
}
std::string sql = load_monitor_sql + stringtf(" WHERE Id=%d", id);
zmDbRow *row = zmDbFetchOne(sql.c_str());
zmDbRow *row = zmDbFetchOne(sql);
if (!row) {
Error("Can't run query: %s", mysql_error(&dbconn));
} else if (MYSQL_ROW dbrow = row->mysql_row()) {
@ -2373,7 +2396,7 @@ void Monitor::ReloadLinkedMonitors(const char *p_linked_monitors) {
" AND `Enabled`=1",
link_ids[i]);
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result) {
continue;
}
@ -2397,7 +2420,7 @@ std::vector<std::shared_ptr<Monitor>> Monitor::LoadMonitors(const std::string &w
std::string sql = load_monitor_sql + " WHERE " + where;
Debug(1, "Loading Monitors with %s", sql.c_str());
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result) {
Error("Can't load local monitors: %s", mysql_error(&dbconn));
return {};
@ -2423,7 +2446,7 @@ std::vector<std::shared_ptr<Monitor>> Monitor::LoadMonitors(const std::string &w
return monitors;
}
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
std::vector<std::shared_ptr<Monitor>> Monitor::LoadLocalMonitors
(const char *device, Purpose purpose) {
@ -2435,7 +2458,7 @@ std::vector<std::shared_ptr<Monitor>> Monitor::LoadLocalMonitors
where += stringtf(" AND `ServerId`=%d", staticConfig.SERVER_ID);
return LoadMonitors(where, purpose);
}
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
std::vector<std::shared_ptr<Monitor>> Monitor::LoadRemoteMonitors
(const char *protocol, const char *host, const char *port, const char *path, Purpose purpose) {
@ -2477,8 +2500,8 @@ int Monitor::Capture() {
std::shared_ptr<ZMPacket> packet = std::make_shared<ZMPacket>();
packet->image_index = image_count;
gettimeofday(&(packet->timestamp), nullptr);
shared_data->zmc_heartbeat_time = packet->timestamp.tv_sec;
packet->timestamp = std::chrono::system_clock::now();
shared_data->zmc_heartbeat_time = std::chrono::system_clock::to_time_t(packet->timestamp);
int captureResult = camera->Capture(packet);
Debug(4, "Back from capture result=%d image count %d", captureResult, image_count);
@ -2495,7 +2518,7 @@ int Monitor::Capture() {
shared_data->last_write_index = index;
shared_data->last_write_time = shared_timestamps[index].tv_sec;
image_buffer[index]->Assign(*capture_image);
shared_timestamps[index] = packet->timestamp;
shared_timestamps[index] = zm::chrono::duration_cast<timeval>(packet->timestamp.time_since_epoch());
delete capture_image;
image_count++;
// What about timestamping it?
@ -2506,7 +2529,7 @@ int Monitor::Capture() {
// If we captured, let's assume signal, Decode will detect further
if (!decoding_enabled) {
shared_data->last_write_index = index;
shared_data->last_write_time = packet->timestamp.tv_sec;
shared_data->last_write_time = std::chrono::system_clock::to_time_t(packet->timestamp);
}
Debug(2, "Have packet stream_index:%d ?= videostream_id: %d q.vpktcount %d event? %d image_count %d",
packet->packet.stream_index, video_stream_id, packetqueue.packet_count(video_stream_id), ( event ? 1 : 0 ), image_count);
@ -2598,11 +2621,33 @@ bool Monitor::Decode() {
if (!convert_context) {
AVPixelFormat imagePixFormat = (AVPixelFormat)(packet->image->AVPixFormat());
AVPixelFormat inputPixFormat;
bool changeColorspaceDetails = false;
switch (input_frame->format) {
case AV_PIX_FMT_YUVJ420P:
inputPixFormat = AV_PIX_FMT_YUV420P;
changeColorspaceDetails = true;
break;
case AV_PIX_FMT_YUVJ422P:
inputPixFormat = AV_PIX_FMT_YUV422P;
changeColorspaceDetails = true;
break;
case AV_PIX_FMT_YUVJ444P:
inputPixFormat = AV_PIX_FMT_YUV444P;
changeColorspaceDetails = true;
break;
case AV_PIX_FMT_YUVJ440P:
inputPixFormat = AV_PIX_FMT_YUV440P;
changeColorspaceDetails = true;
break;
default:
inputPixFormat = (AVPixelFormat)input_frame->format;
}
convert_context = sws_getContext(
input_frame->width,
input_frame->height,
(AVPixelFormat)input_frame->format,
inputPixFormat,
camera_width, camera_height,
imagePixFormat, SWS_BICUBIC,
nullptr, nullptr, nullptr);
@ -2616,10 +2661,22 @@ bool Monitor::Decode() {
} else {
Debug(1, "Setup conversion context for %dx%d %s to %dx%d %s",
input_frame->width, input_frame->height,
av_get_pix_fmt_name((AVPixelFormat)input_frame->format),
av_get_pix_fmt_name(inputPixFormat),
camera_width, camera_height,
av_get_pix_fmt_name(imagePixFormat)
);
if (changeColorspaceDetails) {
// change the range of input data by first reading the current color space and then setting it's range as yuvj.
int dummy[4];
int srcRange, dstRange;
int brightness, contrast, saturation;
sws_getColorspaceDetails(convert_context, (int**)&dummy, &srcRange, (int**)&dummy, &dstRange, &brightness, &contrast, &saturation);
const int* coefs = sws_getCoefficients(SWS_CS_DEFAULT);
srcRange = 1; // this marks that values are according to yuvj
sws_setColorspaceDetails(convert_context, coefs, srcRange, coefs, dstRange,
brightness, contrast, saturation);
}
}
}
if (convert_context) {
@ -2702,27 +2759,30 @@ bool Monitor::Decode() {
}
image_buffer[index]->Assign(*(packet->image));
shared_timestamps[index] = packet->timestamp;
shared_timestamps[index] = zm::chrono::duration_cast<timeval>(packet->timestamp.time_since_epoch());
} // end if have image
packet->decoded = true;
shared_data->signal = (capture_image and signal_check_points) ? CheckSignal(capture_image) : true;
shared_data->last_write_index = index;
shared_data->last_write_time = packet->timestamp.tv_sec;
shared_data->last_write_time = std::chrono::system_clock::to_time_t(packet->timestamp);
packetqueue.unlock(packet_lock);
return true;
} // end bool Monitor::Decode()
void Monitor::TimestampImage(Image *ts_image, const timeval &ts_time) const {
void Monitor::TimestampImage(Image *ts_image, SystemTimePoint ts_time) const {
if (!label_format[0])
return;
// Expand the strftime macros first
char label_time_text[256];
tm ts_tm = {};
strftime(label_time_text, sizeof(label_time_text), label_format.c_str(), localtime_r(&ts_time.tv_sec, &ts_tm));
time_t ts_time_t = std::chrono::system_clock::to_time_t(ts_time);
strftime(label_time_text, sizeof(label_time_text), label_format.c_str(), localtime_r(&ts_time_t, &ts_tm));
char label_text[1024];
const char *s_ptr = label_time_text;
char *d_ptr = label_text;
while (*s_ptr && ((d_ptr - label_text) < (unsigned int) sizeof(label_text))) {
if ( *s_ptr == config.timestamp_code_char[0] ) {
bool found_macro = false;
@ -2736,7 +2796,10 @@ void Monitor::TimestampImage(Image *ts_image, const timeval &ts_time) const {
found_macro = true;
break;
case 'f' :
d_ptr += snprintf(d_ptr, sizeof(label_text)-(d_ptr-label_text), "%02ld", ts_time.tv_usec/10000);
typedef std::chrono::duration<int64, std::centi> Centiseconds;
Centiseconds centi_sec = std::chrono::duration_cast<Centiseconds>(
ts_time.time_since_epoch() - std::chrono::duration_cast<Seconds>(ts_time.time_since_epoch()));
d_ptr += snprintf(d_ptr, sizeof(label_text) - (d_ptr - label_text), "%02ld", centi_sec.count());
found_macro = true;
break;
}
@ -2781,8 +2844,8 @@ unsigned int Monitor::DetectMotion(const Image &comp_image, Event::StringSet &zo
ref_image.Delta(comp_image, &delta_image);
if (config.record_diag_images) {
ref_image.WriteJpeg(diag_path_ref.c_str(), config.record_diag_images_fifo);
delta_image.WriteJpeg(diag_path_delta.c_str(), config.record_diag_images_fifo);
ref_image.WriteJpeg(diag_path_ref, config.record_diag_images_fifo);
delta_image.WriteJpeg(diag_path_delta, config.record_diag_images_fifo);
}
// Blank out all exclusion zones
@ -2914,14 +2977,14 @@ bool Monitor::DumpSettings(char *output, bool verbose) {
sprintf( output+strlen(output), "Id : %u\n", id );
sprintf( output+strlen(output), "Name : %s\n", name.c_str() );
sprintf( output+strlen(output), "Type : %s\n", camera->IsLocal()?"Local":(camera->IsRemote()?"Remote":"File") );
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
if ( camera->IsLocal() ) {
LocalCamera* cam = static_cast<LocalCamera*>(camera.get());
sprintf( output+strlen(output), "Device : %s\n", cam->Device().c_str() );
sprintf( output+strlen(output), "Channel : %d\n", cam->Channel() );
sprintf( output+strlen(output), "Standard : %d\n", cam->Standard() );
} else
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
if ( camera->IsRemote() ) {
RemoteCamera* cam = static_cast<RemoteCamera*>(camera.get());
sprintf( output+strlen(output), "Protocol : %s\n", cam->Protocol().c_str() );
@ -2930,7 +2993,7 @@ bool Monitor::DumpSettings(char *output, bool verbose) {
sprintf( output+strlen(output), "Path : %s\n", cam->Path().c_str() );
} else if ( camera->IsFile() ) {
FileCamera* cam = static_cast<FileCamera*>(camera.get());
sprintf( output+strlen(output), "Path : %s\n", cam->Path() );
sprintf( output+strlen(output), "Path : %s\n", cam->Path().c_str() );
}
else if ( camera->IsFfmpeg() ) {
FfmpegCamera* cam = static_cast<FfmpegCamera*>(camera.get());
@ -2938,12 +3001,12 @@ bool Monitor::DumpSettings(char *output, bool verbose) {
}
sprintf( output+strlen(output), "Width : %u\n", camera->Width() );
sprintf( output+strlen(output), "Height : %u\n", camera->Height() );
#if ZM_HAS_V4L
#if ZM_HAS_V4L2
if ( camera->IsLocal() ) {
LocalCamera* cam = static_cast<LocalCamera*>(camera.get());
sprintf( output+strlen(output), "Palette : %d\n", cam->Palette() );
}
#endif // ZM_HAS_V4L
#endif // ZM_HAS_V4L2
sprintf(output+strlen(output), "Colours : %u\n", camera->Colours() );
sprintf(output+strlen(output), "Subpixel Order : %u\n", camera->SubpixelOrder() );
sprintf(output+strlen(output), "Event Prefix : %s\n", event_prefix.c_str() );
@ -2956,10 +3019,10 @@ bool Monitor::DumpSettings(char *output, bool verbose) {
sprintf(output+strlen(output), "Post Event Count : %d\n", post_event_count );
sprintf(output+strlen(output), "Stream Replay Buffer : %d\n", stream_replay_buffer );
sprintf(output+strlen(output), "Alarm Frame Count : %d\n", alarm_frame_count );
sprintf(output+strlen(output), "Section Length : %d\n", section_length);
sprintf(output+strlen(output), "Min Section Length : %d\n", min_section_length);
sprintf(output+strlen(output), "Maximum FPS : %.2f\n", capture_delay ? (double) Microseconds::period::den / capture_delay : 0.0);
sprintf(output+strlen(output), "Alarm Maximum FPS : %.2f\n", alarm_capture_delay ? (double) Microseconds::period::den / alarm_capture_delay : 0.0);
sprintf(output+strlen(output), "Section Length : %" PRIi64 "\n", static_cast<int64>(Seconds(section_length).count()));
sprintf(output+strlen(output), "Min Section Length : %" PRIi64 "\n", static_cast<int64>(Seconds(min_section_length).count()));
sprintf(output+strlen(output), "Maximum FPS : %.2f\n", capture_delay != Seconds(0) ? 1 / FPSeconds(capture_delay).count() : 0.0);
sprintf(output+strlen(output), "Alarm Maximum FPS : %.2f\n", alarm_capture_delay != Seconds(0) ? 1 / FPSeconds(alarm_capture_delay).count() : 0.0);
sprintf(output+strlen(output), "Reference Blend %%ge : %d\n", ref_blend_perc);
sprintf(output+strlen(output), "Alarm Reference Blend %%ge : %d\n", alarm_ref_blend_perc);
sprintf(output+strlen(output), "Track Motion : %d\n", track_motion);
@ -3032,7 +3095,7 @@ int Monitor::PrimeCapture() {
Debug(1, "Creating decoder thread");
decoder = zm::make_unique<DecoderThread>(this);
} else {
Debug(1, "Restartg decoder thread");
Debug(1, "Restarting decoder thread");
decoder->Start();
}
}
@ -3113,7 +3176,6 @@ void Monitor::get_ref_image() {
// can't analyse it anyways, incremement
packetqueue.increment_it(analysis_it);
}
//usleep(10000);
}
if (zm_terminate)
return;
@ -3138,7 +3200,7 @@ std::vector<Group *> Monitor::Groups() {
std::string sql = stringtf(
"SELECT `Id`, `ParentId`, `Name` FROM `Groups` WHERE `Groups.Id` IN "
"(SELECT `GroupId` FROM `Groups_Monitors` WHERE `MonitorId`=%d)", id);
MYSQL_RES *result = zmDbFetch(sql.c_str());
MYSQL_RES *result = zmDbFetch(sql);
if (!result) {
Error("Can't load groups: %s", mysql_error(&dbconn));
return groups;

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

@ -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 (
@ -331,18 +338,17 @@ bool MonitorStream::sendFrame(const char *filepath, SystemTimePoint timestamp) {
send_raw = false;
if (!send_raw) {
Image temp_image(filepath);
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;
}
@ -557,10 +565,9 @@ 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)) {
Debug(2, "feof stdout");
break;
@ -575,6 +582,7 @@ void MonitorStream::runStream() {
now = std::chrono::system_clock::now();
bool was_paused = paused;
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.
@ -596,7 +604,6 @@ void MonitorStream::runStream() {
paused_timestamp = SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
}
} else if (paused_image) {
Debug(1, "Clearing paused_image");
delete paused_image;
paused_image = nullptr;
}
@ -605,15 +612,12 @@ void MonitorStream::runStream() {
if (temp_read_index == temp_write_index) {
// Go back to live viewing
Debug(1, "Exceeded temporary streaming buffer");
// Clear paused flag
paused = false;
// Clear delayed_play flag
delayed = false;
replay_rate = ZM_RATE_BASE;
} else {
if (!paused) {
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
// Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index );
SwapImage *swap_image = &temp_image_buffer[temp_index];
if (!swap_image->valid) {
@ -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,6 +672,7 @@ 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
@ -681,7 +691,7 @@ void MonitorStream::runStream() {
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
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;
@ -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.
@ -723,6 +732,8 @@ void MonitorStream::runStream() {
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,12 +744,15 @@ 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) {
@ -747,21 +761,13 @@ void MonitorStream::runStream() {
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) {
// Go back to live viewing
@ -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,11 +813,11 @@ 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) {
Debug(1, "Cleaning swap files from %s", swap_path.c_str());
struct stat stat_buf;
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));
@ -820,16 +825,15 @@ void MonitorStream::runStream() {
} 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);
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, 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++) {
@ -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);
@ -866,7 +870,8 @@ void MonitorStream::SingleImage(int scale) {
snap_image = &scaled_image;
}
if (!config.timestamp_on_capture) {
monitor->TimestampImage(snap_image, monitor->shared_timestamps[index]);
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;
@ -538,30 +532,27 @@ int VideoStream::SendPacket(AVPacket *packet) {
}
void *VideoStream::StreamingThreadCallback(void *ctx) {
Debug(1, "StreamingThreadCallback started");
if (ctx == nullptr) return nullptr;
if (ctx == nullptr) {
return nullptr;
}
VideoStream *videoStream = reinterpret_cast<VideoStream *>(ctx);
const uint64_t nanosecond_multiplier = 1000000000;
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);
TimePoint start_time = std::chrono::steady_clock::now();
if ( current_time_ns < target_ns ) {
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.
usleep( (target_ns - current_time_ns) * 0.001 );
std::this_thread::sleep_for(target - current_time);
}
// By sending the last rendered frame we deliver frames to the client more accurate.
@ -578,14 +569,16 @@ void *VideoStream::StreamingThreadCallback(void *ctx){
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 );
videoStream->ActuallyEncodeFrame(videoStream->buffer_copy,
videoStream->buffer_copy_used,
videoStream->add_timestamp,
videoStream->timestamp);
}
if (pthread_mutex_unlock(videoStream->buffer_copy_lock) != 0) {

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,7 +78,7 @@ ZMPacket::ZMPacket(ZMPacket &p) :
image_index(-1),
codec_imgsize(0),
pts(0),
decoded(0)
decoded(false)
{
av_init_packet(&packet);
packet.size = 0;
@ -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;
@ -70,7 +67,7 @@ class ZMPacket {
int is_keyframe() { return keyframe; };
int decode(AVCodecContext *ctx);
explicit ZMPacket(Image *image, const timeval &tv);
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

@ -74,8 +74,6 @@ void RemoteCamera::Initialise() {
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('@');
@ -100,7 +98,8 @@ void RemoteCamera::Initialise() {
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
if (ret != 0) {
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
Error("Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret));
return;
}
struct addrinfo *p = nullptr;
int addr_count = 0;

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,13 +308,15 @@ int RemoteCameraHttp::ReadData(Buffer &buffer, unsigned int bytes_expected) {
} // end readData
int RemoteCameraHttp::GetData() {
time_t start_time = time(nullptr);
int buffer_len = 0;
TimePoint start_time = std::chrono::steady_clock::now();
int buffer_len;
while (!(buffer_len = ReadData(buffer))) {
if (zm_terminate or ( (time(nullptr) - start_time) > ZM_WATCH_MAX_DELAY ))
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");
usleep(100000);
std::this_thread::sleep_for(Milliseconds(100));
}
return buffer_len;
}

View File

@ -126,9 +126,10 @@ 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()) {
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);
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;
@ -160,11 +160,8 @@ 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);
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)) ) {
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);
}
usleep(100000);
lastKeepalive = std::chrono::steady_clock::now();
}
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,6 +21,7 @@
#include "zm_db.h"
#include "zm_logger.h"
#include "zm_utils.h"
#include <cstring>
Storage::Storage() : id(0) {
@ -55,11 +56,9 @@ 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);
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));

View File

@ -254,15 +254,29 @@ 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());
if (monitor) {
width = monitor->Width();
height = monitor->Height();
colours = monitor->Colours();
subpixelorder = monitor->SubpixelOrder();
labelsize = monitor->LabelSize();
}
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, monitor->LabelSize()), monitor->LabelSize());
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) {
@ -276,6 +290,7 @@ 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);

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";
@ -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,17 +239,17 @@ 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(),
@ -260,34 +257,33 @@ int main(int argc, char *argv[]) {
} 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());
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,15 +342,11 @@ 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();
@ -369,8 +359,7 @@ int main(int argc, char *argv[]) {
} // 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),
std::string sql = stringtf(
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'NotRunning') ON DUPLICATE KEY UPDATE Status='NotRunning'",
monitor->Id());
zmDbDo(sql);

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,17 +257,24 @@ 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;
@ -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);
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());
@ -641,13 +667,13 @@ int main(int argc, char *argv[]) {
}
if (function & ZMU_BRIGHTNESS) {
if (verbose) {
if ( brightness >= 0 )
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_brightness)
printf("%d", monitor->actionBrightness(brightness));
else
printf("%d", monitor->actionBrightness());
@ -656,13 +682,13 @@ int main(int argc, char *argv[]) {
}
if (function & ZMU_CONTRAST) {
if (verbose) {
if ( contrast >= 0 )
printf("New brightness: %d\n", monitor->actionContrast(contrast));
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_contrast)
printf("%d", monitor->actionContrast(contrast));
else
printf("%d", monitor->actionContrast());
@ -671,13 +697,13 @@ int main(int argc, char *argv[]) {
}
if (function & ZMU_HUE) {
if (verbose) {
if ( hue >= 0 )
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_hue)
printf("%d", monitor->actionHue(hue));
else
printf("%d", monitor->actionHue());
@ -686,13 +712,13 @@ int main(int argc, char *argv[]) {
}
if (function & ZMU_COLOUR) {
if (verbose) {
if ( colour >= 0 )
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_colour)
printf("%d", monitor->actionColour(colour));
else
printf("%d", monitor->actionColour());
@ -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,17 +296,19 @@ 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
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
sudo mk-build-deps -ir $DIRECTORY.orig/debian/control
echo "Status: $?"
DEBUILD="debuild -i -us -uc -b"
else
@ -309,6 +316,9 @@ EOF
DEBUILD="debuild -S -sa"
fi;
fi;
cd $DIRECTORY.orig
if [ "$DEBSIGN_KEYID" != "" ]; then
DEBUILD="$DEBUILD -k$DEBSIGN_KEYID"
fi

View File

@ -223,7 +223,7 @@ setdebpkgname () {
if [ "" == "$VERSION" ]; then
export VERSION="${versionfile}~${thedate}.${numcommits}"
fi
export RELEASE="${DIST}"
export RELEASE="${DIST}${PACKAGE_VERSION}"
checkvars
@ -369,7 +369,7 @@ elif [ "${OS}" == "debian" ] || [ "${OS}" == "ubuntu" ] || [ "${OS}" == "raspbia
setdebpkgname
movecrud
if [ "${DIST}" == "focal" ] || [ "${DIST}" == "groovy" ] || [ "${DIST}" == "hirsuit" ] || [ "${DIST}" == "buster" ]; then
if [ "${DIST}" == "bionic" ] || [ "${DIST}" == "focal" ] || [ "${DIST}" == "hirsute" ] || [ "${DIST}" == "impish" ] || [ "${DIST}" == "buster" ] || [ "${DIST}" == "bullseye" ]; then
ln -sfT distros/ubuntu2004 debian
elif [ "${DIST}" == "beowulf" ]; then
ln -sfT distros/beowulf debian

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