Merge branch 'master' of github.com:ZoneMinder/zoneminder
This commit is contained in:
commit
0ba3d863a6
|
@ -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,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
|
||||
|
|
|
@ -14,6 +14,8 @@ jobs:
|
|||
os_dist:
|
||||
- os: debian
|
||||
dist: buster
|
||||
- os: debian
|
||||
dist: bullseye
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
|
@ -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,
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `Monitors` MODIFY `Longitude` DECIMAL(11,8);
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE `Monitors` MODIFY `Longitude` DECIMAL(11,8);
|
|
@ -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';
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
||||
::
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ Here are some options for using ZoneMinder on Mobile devices:
|
|||
Third party mobile clients
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
* zmNinja (`source code <https://github.com/pliablepixels/zmNinja>`__, needs APIs to be installed to work)
|
||||
* Available in App Store, Play Store and for Desktops - `website <http://pliablepixels.github.io/zmNinja/>`__
|
||||
* Available in App Store, Play Store and for Desktops - `website <http://pliablepixels.github.io/>`__
|
||||
|
||||
Using the existing web console
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -17,4 +17,4 @@ Discontinued clients
|
|||
The following are a list of clients that do not work and have not been updated:
|
||||
|
||||
* eyeZM
|
||||
* zmView
|
||||
* zmView
|
||||
|
|
|
@ -101,6 +101,6 @@ FROM_EMAIL - The emails or messages that will be sent to you informing you of ev
|
|||
|
||||
URL - The emails or messages that will be sent to you informing you of events can include a link to the events themselves for easy viewing. If you intend to use this feature then set this option to the url of your installation as it would appear from where you read your email, e.g. ``http://host.your.domain/zm/index.php``.
|
||||
|
||||
SSMTP_MAIL - SSMTP is a lightweight and efficient method to send email. The SSMTP application is not installed by default. NEW_MAIL_MODULES must also be enabled. Please visit the ZoneMinder `SSMTP Wiki page <http://www.zoneminder.com/wiki/index.php/How_to_get_ssmtp_working_with_Zoneminder>`__ for setup and configuration help.
|
||||
SSMTP_MAIL - SSMTP is a lightweight and efficient method to send email. The SSMTP application is not installed by default. NEW_MAIL_MODULES must also be enabled. Please visit the ZoneMinder `SSMTP Wiki page <https://wiki.zoneminder.com/How_to_get_ssmtp_working_with_Zoneminder>`__ for setup and configuration help.
|
||||
|
||||
SSMTP_PATH - The path to the SSMTP application. If path is not defined. Zoneminder will try to determine the path via shell command. Example path: /usr/sbin/ssmtp.
|
||||
SSMTP_PATH - The path to the SSMTP application. If path is not defined. Zoneminder will try to determine the path via shell command. Example path: /usr/sbin/ssmtp.
|
||||
|
|
|
@ -5,10 +5,7 @@ This screen lets you customize several aspects of the web interface of ZoneMinde
|
|||
.. image:: images/Options_web.png
|
||||
|
||||
|
||||
WEB_TITLE -
|
||||
|
||||
.. todo ::
|
||||
not quite sure what this does. Seems to change the "target" name - not sure what effect it is supposed to have.
|
||||
WEB_TITLE - The actual text that is shown on the login screen. It is possible that it also appears in other areas.
|
||||
|
||||
WEB_TITLE_PREFIX - If you have more than one installation of ZoneMinder it can be helpful to display different titles for each one. Changing this option allows you to customise the window titles to include further information to aid identification.
|
||||
|
||||
|
@ -48,4 +45,4 @@ WEB_USE_OBJECT_TAGS - There are two methods of including media content in web pa
|
|||
|
||||
WEB_XFRAME_WARN - When creating a Web Site monitor, if the target web site has X-Frame-Options set to sameorigin in the header, the site will not display in ZoneMinder. This is a design feature in most modern browsers. When this condition occurs, ZoneMinder will write a warning to the log file. To get around this, one can install a browser plugin or extension to ignore X-Frame headers, and then the page will display properly. Once the plugin or extension has ben installed, the end user may choose to turn this warning off
|
||||
|
||||
WEB_FILTER_SOURCE - This option only affects monitors with a source type of Ffmpeg, Libvlc, or WebSite. This setting controls what information is displayed in the Source column on the console. Selecting 'None' will not filter anything. The entire source string will be displayed, which may contain sensitive information. Selecting 'NoCredentials' will strip out usernames and passwords from the string. If there are any port numbers in the string and they are common (80, 554, etc) then those will be removed as well. Selecting 'Hostname' will filter out all information except for the hostname or ip address. When in doubt, stay with the default 'Hostname'. This feature uses the php function 'url_parts' to identify the various pieces of the url. If the url in question is unusual or not standard in some way, then filtering may not produce the desired results.
|
||||
WEB_FILTER_SOURCE - This option only affects monitors with a source type of Ffmpeg, Libvlc, or WebSite. This setting controls what information is displayed in the Source column on the console. Selecting 'None' will not filter anything. The entire source string will be displayed, which may contain sensitive information. Selecting 'NoCredentials' will strip out usernames and passwords from the string. If there are any port numbers in the string and they are common (80, 554, etc) then those will be removed as well. Selecting 'Hostname' will filter out all information except for the hostname or ip address. When in doubt, stay with the default 'Hostname'. This feature uses the php function 'url_parts' to identify the various pieces of the url. If the url in question is unusual or not standard in some way, then filtering may not produce the desired results.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
failregex = ^\s*web_php\[\d+\]\.ERR \[<HOST>\].*includes/auth.php
|
||||
datepattern = ^%%m/%%d/%%y %%H:%%M:%%S(?:\.%%f)
|
|
@ -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},
|
||||
|
|
|
@ -220,14 +220,14 @@ sub moveConUpRight {
|
|||
my $self = shift;
|
||||
Debug('Move Diagonally Up Right');
|
||||
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
|
||||
$$self{LastCmd} = 'code=RightUp&channel=0&arg1=0&arg2=1&arg3=0';
|
||||
$$self{LastCmd} = 'code=RightUp&channel=0&arg1=1&arg2=1&arg3=0';
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
|
||||
}
|
||||
|
||||
sub moveConDownRight {
|
||||
my $self = shift;
|
||||
Debug('Move Diagonally Down Right');
|
||||
$$self{LastCmd} = 'code=RightDown&channel=0&arg1=0&arg2=1&arg3=0';
|
||||
$$self{LastCmd} = 'code=RightDown&channel=0&arg1=1&arg2=1&arg3=0';
|
||||
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ sub moveConUpLeft {
|
|||
my $self = shift;
|
||||
Debug('Move Diagonally Up Left');
|
||||
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
|
||||
$$self{LastCmd} = 'code=LeftUp&channel=0&arg1=0&arg2=1&arg3=0';
|
||||
$$self{LastCmd} = 'code=LeftUp&channel=0&arg1=1&arg2=1&arg3=0';
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
|
||||
}
|
||||
|
||||
|
@ -244,7 +244,7 @@ sub moveConDownLeft {
|
|||
my $self = shift;
|
||||
Debug('Move Diagonally Down Left');
|
||||
$$self{Monitor}->suspendMotionDetection() if !$self->{Monitor}->{ModectDuringPTZ};
|
||||
$$self{LastCmd} = 'code=LeftDown&channel=0&arg1=0&arg2=1&arg3=0';
|
||||
$$self{LastCmd} = 'code=LeftDown&channel=0&arg1=1&arg2=1&arg3=0';
|
||||
$self->sendCmd('cgi-bin/ptz.cgi?action=start&'.$$self{LastCmd});
|
||||
}
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ None by default.
|
|||
=head1 SEE ALSO
|
||||
|
||||
See if there are better instructions for the DCS-5020L at
|
||||
http://www.zoneminder.com/wiki/index.php/Dlink
|
||||
https://wiki.zoneminder.com/Dlink
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# ==========================================================================
|
||||
#
|
||||
# ZoneMinder ONVIF Control Protocol Module, $Date: 2021-02-25 22:07:00 +0000 (Thu, 25 Feb 2021) $, $Revision: 0001 $
|
||||
# ZoneMinder ONVIF Control Protocol Module
|
||||
# Based on the Netcat onvif script by Andrew Bauer (knnniggett@users.sourceforge.net)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
|
@ -182,6 +182,7 @@ sub sendCmd {
|
|||
$req->header('connection' => 'Close');
|
||||
$req->content($msg);
|
||||
|
||||
|
||||
my $res = $self->{ua}->request($req);
|
||||
|
||||
if ( $res->is_success ) {
|
||||
|
@ -235,22 +236,39 @@ sub getCamParams {
|
|||
sub autoStop {
|
||||
my $self = shift;
|
||||
my $autostop = shift;
|
||||
my $iszoom = shift;
|
||||
|
||||
if ( $autostop ) {
|
||||
Debug('Auto Stop');
|
||||
my $cmd = $controlUri;
|
||||
my $msg_body = '
|
||||
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
|
||||
<ProfileToken>'.$profileToken.'</ProfileToken>
|
||||
<PanTilt>
|
||||
true
|
||||
</PanTilt>
|
||||
<Zoom>
|
||||
false
|
||||
</Zoom>
|
||||
</Stop>
|
||||
</s:Body>';
|
||||
my $msg_body;
|
||||
if( $iszoom) {
|
||||
$msg_body = '
|
||||
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
|
||||
<ProfileToken>'.$profileToken.'</ProfileToken>
|
||||
<PanTilt>
|
||||
false
|
||||
</PanTilt>
|
||||
<Zoom>
|
||||
true
|
||||
</Zoom>
|
||||
</Stop>
|
||||
</s:Body>';
|
||||
} else {
|
||||
$msg_body = '
|
||||
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
|
||||
<ProfileToken>'.$profileToken.'</ProfileToken>
|
||||
<PanTilt>
|
||||
true
|
||||
</PanTilt>
|
||||
<Zoom>
|
||||
false
|
||||
</Zoom>
|
||||
</Stop>
|
||||
</s:Body>';
|
||||
}
|
||||
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
usleep($autostop);
|
||||
|
@ -340,9 +358,9 @@ sub moveCamera {
|
|||
my $type = shift;
|
||||
my $x = shift;
|
||||
my $y = shift;
|
||||
my $msg_move_body = "";
|
||||
my $msg_move_body = '';
|
||||
|
||||
if ( $type == "move" ){
|
||||
if ( $type eq 'move' ) {
|
||||
$msg_move_body = '
|
||||
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
|
||||
|
@ -356,7 +374,7 @@ sub moveCamera {
|
|||
</ContinuousMove>
|
||||
</s:Body>';
|
||||
|
||||
} elsif ( $type == "zoom" ) {
|
||||
} elsif ( $type eq 'zoom' ) {
|
||||
$msg_move_body = '
|
||||
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<ContinuousMove xmlns="http://www.onvif.org/ver20/ptz/wsdl">
|
||||
|
@ -371,7 +389,6 @@ sub moveCamera {
|
|||
}
|
||||
|
||||
return $msg_move_body;
|
||||
|
||||
}
|
||||
|
||||
#Up Arrow
|
||||
|
@ -382,7 +399,7 @@ sub moveConUp {
|
|||
my $msg_body = moveCamera("move", "0","0.5");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -394,7 +411,7 @@ sub moveConDown {
|
|||
my $msg_body = moveCamera("move","0","-0.5");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
|
||||
}
|
||||
|
||||
#Left Arrow
|
||||
|
@ -405,7 +422,7 @@ sub moveConLeft {
|
|||
my $msg_body = moveCamera("move","-0.49","0");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
|
||||
}
|
||||
|
||||
#Right Arrow
|
||||
|
@ -416,7 +433,7 @@ sub moveConRight {
|
|||
my $msg_body = moveCamera("move","0.49","0");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
|
||||
}
|
||||
|
||||
#Zoom In
|
||||
|
@ -427,7 +444,7 @@ sub zoomConTele {
|
|||
my $msg_body = moveCamera("zoom","0.49","0");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},1);
|
||||
}
|
||||
|
||||
#Zoom Out
|
||||
|
@ -438,9 +455,19 @@ sub zoomConWide {
|
|||
my $msg_body = moveCamera("zoom","-0.49","0");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},1);
|
||||
}
|
||||
|
||||
sub zoomStop {
|
||||
Debug('Zoom Stop');
|
||||
my $self = shift;
|
||||
my $cmd = $controlUri;
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},1);
|
||||
Error('Zoom Stop not implemented');
|
||||
}
|
||||
|
||||
|
||||
|
||||
#Diagonally Up Right Arrow
|
||||
#This camera does not have builtin diagonal commands so we emulate them
|
||||
sub moveConUpRight {
|
||||
|
@ -450,7 +477,7 @@ sub moveConUpRight {
|
|||
my $msg_body = moveCamera("move","0.5","0.5");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
|
||||
}
|
||||
|
||||
#Diagonally Down Right Arrow
|
||||
|
@ -462,7 +489,7 @@ sub moveConDownRight {
|
|||
my $msg_body = moveCamera("move","0.5","-0.5");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
|
||||
}
|
||||
|
||||
#Diagonally Up Left Arrow
|
||||
|
@ -474,7 +501,7 @@ sub moveConUpLeft {
|
|||
my $msg_body = moveCamera("move","-0.5","0.5");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
|
||||
}
|
||||
|
||||
#Diagonally Down Left Arrow
|
||||
|
@ -486,7 +513,7 @@ sub moveConDownLeft {
|
|||
my $msg_body = moveCamera("move","-0.5","-0.5");
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
$self->sendCmd($cmd, $msg_body, $content_type);
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout});
|
||||
$self->autoStop($self->{Monitor}->{AutoStopTimeout},0);
|
||||
}
|
||||
|
||||
#Stop
|
||||
|
@ -499,7 +526,7 @@ sub moveStop {
|
|||
<Stop xmlns="http://www.onvif.org/ver20/ptz/wsdl">
|
||||
<ProfileToken>'.$profileToken.'</ProfileToken>
|
||||
<PanTilt>true</PanTilt>
|
||||
<Zoom>false</Zoom>
|
||||
<Zoom>true</Zoom>
|
||||
</Stop>
|
||||
</s:Body>';
|
||||
my $content_type = 'application/soap+xml; charset=utf-8; action="http://www.onvif.org/ver20/ptz/wsdl/ContinuousMove"';
|
||||
|
|
|
@ -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 $@ ) {
|
||||
|
|
|
@ -127,9 +127,11 @@ sub Execute {
|
|||
foreach my $term ( @{$$self{PostSQLConditions}} ) {
|
||||
if ( $$term{attr} eq 'ExistsInFileSystem' ) {
|
||||
foreach my $row ( @results ) {
|
||||
my $event = new ZoneMinder::Event($row);
|
||||
my $event = new ZoneMinder::Event($$row{Id}, $row);
|
||||
if ( -e $event->Path() ) {
|
||||
push @filtered_events, $row;
|
||||
push @filtered_events, $row if $$term{val} eq 'true';
|
||||
} else {
|
||||
push @filtered_events, $row if $$term{val} eq 'false';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,138 +166,142 @@ sub Sql {
|
|||
if ( exists($term->{obr}) ) {
|
||||
$self->{Sql} .= str_repeat('(', $term->{obr}).' ';
|
||||
}
|
||||
if (!$term->{attr}) {
|
||||
Error("Invalid term in filter $$self{Id}. Empty attr");
|
||||
next;
|
||||
}
|
||||
|
||||
my $value = $term->{val};
|
||||
my @value_list;
|
||||
if ( $term->{attr} ) {
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$term->{op} = 'EXISTS';
|
||||
} elsif ( $term->{attr} =~ /^Monitor/ ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
||||
$self->{Sql} .= 'M.'.$temp_attr_name;
|
||||
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
$self->{Sql} .= 'M.ServerId';
|
||||
} elsif ( $term->{attr} eq 'StorageServerId' ) {
|
||||
$self->{Sql} .= '(SELECT Storage.ServerId FROM Storage WHERE Storage.Id=E.StorageId)';
|
||||
} elsif ( $term->{attr} eq 'FilterServerId' ) {
|
||||
$self->{Sql} .= $Config{ZM_SERVER_ID};
|
||||
# StartTime options
|
||||
} elsif ( $term->{attr} eq 'DateTime' ) {
|
||||
$self->{Sql} .= 'E.StartDateTime';
|
||||
} elsif ( $term->{attr} eq 'Date' ) {
|
||||
$self->{Sql} .= 'to_days( E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'StartDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
|
||||
$self->{Sql} .= 'extract( hour_second from E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'Weekday' or $term->{attr} eq 'StartWeekday' ) {
|
||||
$self->{Sql} .= 'weekday( E.StartDateTime )';
|
||||
|
||||
# EndTIme options
|
||||
} elsif ( $term->{attr} eq 'EndDateTime' ) {
|
||||
$self->{Sql} .= 'E.EndDateTime';
|
||||
} elsif ( $term->{attr} eq 'EndDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'EndDateTime' ) {
|
||||
$self->{Sql} .= 'extract( hour_second from E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'EndWeekday' ) {
|
||||
$self->{Sql} .= "weekday( E.EndDateTime )";
|
||||
} elsif ( $term->{attr} eq 'ExistsInFileSystem' ) {
|
||||
push @{$self->{PostSQLConditions}}, $term;
|
||||
$self->{Sql} .= 'TRUE /* ExistsInFileSystem */';
|
||||
} elsif ( $term->{attr} eq 'DiskPercent' ) {
|
||||
$self->{Sql} .= 'zmDiskPercent';
|
||||
$self->{HasDiskPercent} = !undef;
|
||||
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
|
||||
$self->{Sql} .= 'zmDiskBlocks';
|
||||
$self->{HasDiskBlocks} = !undef;
|
||||
} elsif ( $term->{attr} eq 'SystemLoad' ) {
|
||||
$self->{Sql} .= 'zmSystemLoad';
|
||||
$self->{HasSystemLoad} = !undef;
|
||||
} else {
|
||||
$self->{Sql} .= 'E.'.$term->{attr};
|
||||
}
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$term->{op} = 'EXISTS';
|
||||
} elsif ( $term->{attr} =~ /^Monitor/ ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
|
||||
$self->{Sql} .= 'M.'.$temp_attr_name;
|
||||
} elsif ( $term->{attr} eq 'ServerId' or $term->{attr} eq 'MonitorServerId' ) {
|
||||
$sql = 'SELECT E.*, unix_timestamp(E.StartDateTime) as Time, M.Name as MonitorName
|
||||
FROM Events as E INNER JOIN Monitors as M on M.Id = E.MonitorId';
|
||||
$self->{Sql} .= 'M.ServerId';
|
||||
} elsif ( $term->{attr} eq 'StorageServerId' ) {
|
||||
$self->{Sql} .= '(SELECT Storage.ServerId FROM Storage WHERE Storage.Id=E.StorageId)';
|
||||
} elsif ( $term->{attr} eq 'FilterServerId' ) {
|
||||
$self->{Sql} .= $Config{ZM_SERVER_ID};
|
||||
# StartTime options
|
||||
} elsif ( $term->{attr} eq 'DateTime' ) {
|
||||
$self->{Sql} .= 'E.StartDateTime';
|
||||
} elsif ( $term->{attr} eq 'Date' ) {
|
||||
$self->{Sql} .= 'to_days( E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'StartDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' ) {
|
||||
$self->{Sql} .= 'extract( hour_second from E.StartDateTime )';
|
||||
} elsif ( $term->{attr} eq 'Weekday' or $term->{attr} eq 'StartWeekday' ) {
|
||||
$self->{Sql} .= 'weekday( E.StartDateTime )';
|
||||
|
||||
if ( $term->{attr} eq 'ExistsInFileSystem' ) {
|
||||
# PostCondition, so no further SQL
|
||||
} else {
|
||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
|
||||
# EndTIme options
|
||||
} elsif ( $term->{attr} eq 'EndDateTime' ) {
|
||||
$self->{Sql} .= 'E.EndDateTime';
|
||||
} elsif ( $term->{attr} eq 'EndDate' ) {
|
||||
$self->{Sql} .= 'to_days( E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'EndTime' ) {
|
||||
$self->{Sql} .= 'extract( hour_second from E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'EndWeekday' ) {
|
||||
$self->{Sql} .= 'weekday( E.EndDateTime )';
|
||||
} elsif ( $term->{attr} eq 'ExistsInFileSystem' ) {
|
||||
push @{$self->{PostSQLConditions}}, $term;
|
||||
$self->{Sql} .= 'TRUE /* ExistsInFileSystem */';
|
||||
} elsif ( $term->{attr} eq 'DiskPercent' ) {
|
||||
$self->{Sql} .= 'zmDiskPercent';
|
||||
$self->{HasDiskPercent} = !undef;
|
||||
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
|
||||
$self->{Sql} .= 'zmDiskBlocks';
|
||||
$self->{HasDiskBlocks} = !undef;
|
||||
} elsif ( $term->{attr} eq 'SystemLoad' ) {
|
||||
$self->{Sql} .= 'zmSystemLoad';
|
||||
$self->{HasSystemLoad} = !undef;
|
||||
} else {
|
||||
$self->{Sql} .= 'E.'.$term->{attr};
|
||||
}
|
||||
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
|
||||
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
|
||||
$value = "'$temp_value'";
|
||||
} elsif ( $term->{attr} =~ /ServerId/) {
|
||||
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
|
||||
if ( $temp_value eq 'ZM_SERVER_ID' ) {
|
||||
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";
|
||||
# This gets used later, I forget for what
|
||||
$$self{Server} = new ZoneMinder::Server($ZoneMinder::Config::Config{ZM_SERVER_ID});
|
||||
} elsif ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = "'$temp_value'";
|
||||
# This gets used later, I forget for what
|
||||
$$self{Server} = new ZoneMinder::Server($temp_value);
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'StorageId' ) {
|
||||
$value = "'$temp_value'";
|
||||
$$self{Storage} = new ZoneMinder::Storage($temp_value);
|
||||
} elsif ( $term->{attr} eq 'Name'
|
||||
|| $term->{attr} eq 'Cause'
|
||||
|| $term->{attr} eq 'Notes'
|
||||
) {
|
||||
if ( $term->{op} eq 'LIKE'
|
||||
|| $term->{op} eq 'NOT LIKE'
|
||||
) {
|
||||
$temp_value = '%'.$temp_value.'%' if $temp_value !~ /%/;
|
||||
}
|
||||
$value = "'$temp_value'";
|
||||
} elsif ( $term->{attr} eq 'DateTime' or $term->{attr} eq 'StartDateTime' or $term->{attr} eq 'EndDateTime' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "'$value'";
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} elsif ( $temp_value eq 'CURDATE()' or $temp_value eq 'NOW()' ) {
|
||||
$value = 'to_days('.$temp_value.')';
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "to_days( '$value' )";
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' or $term->{attr} eq 'EndTime' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "extract( hour_second from '$value' )";
|
||||
}
|
||||
} else {
|
||||
if ( $term->{attr} eq 'ExistsInFileSystem' ) {
|
||||
# PostCondition, so no further SQL
|
||||
} else {
|
||||
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
|
||||
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
|
||||
|
||||
if ( $term->{attr} eq 'AlarmedZoneId' ) {
|
||||
$value = '(SELECT * FROM Stats WHERE EventId=E.Id AND Score > 0 AND ZoneId='.$value.')';
|
||||
} elsif ( $term->{attr} =~ /^MonitorName/ ) {
|
||||
$value = "'$temp_value'";
|
||||
} elsif ( $term->{attr} =~ /ServerId/) {
|
||||
Debug("ServerId, temp_value is ($temp_value) ($ZoneMinder::Config::Config{ZM_SERVER_ID})");
|
||||
if ( $temp_value eq 'ZM_SERVER_ID' ) {
|
||||
$value = "'$ZoneMinder::Config::Config{ZM_SERVER_ID}'";
|
||||
# This gets used later, I forget for what
|
||||
$$self{Server} = new ZoneMinder::Server($ZoneMinder::Config::Config{ZM_SERVER_ID});
|
||||
} elsif ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = "'$temp_value'";
|
||||
# This gets used later, I forget for what
|
||||
$$self{Server} = new ZoneMinder::Server($temp_value);
|
||||
}
|
||||
push @value_list, $value;
|
||||
} # end foreach temp_value
|
||||
} # end if has an attr
|
||||
} elsif ( $term->{attr} eq 'StorageId' ) {
|
||||
$value = "'$temp_value'";
|
||||
$$self{Storage} = new ZoneMinder::Storage($temp_value);
|
||||
} elsif ( $term->{attr} eq 'Name'
|
||||
|| $term->{attr} eq 'Cause'
|
||||
|| $term->{attr} eq 'Notes'
|
||||
) {
|
||||
if ( $term->{op} eq 'LIKE'
|
||||
|| $term->{op} eq 'NOT LIKE'
|
||||
) {
|
||||
$temp_value = '%'.$temp_value.'%' if $temp_value !~ /%/;
|
||||
}
|
||||
$value = "'$temp_value'";
|
||||
} elsif ( $term->{attr} eq 'DateTime' or $term->{attr} eq 'StartDateTime' or $term->{attr} eq 'EndDateTime' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "'$value'";
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'Date' or $term->{attr} eq 'StartDate' or $term->{attr} eq 'EndDate' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} elsif ( $temp_value eq 'CURDATE()' or $temp_value eq 'NOW()' ) {
|
||||
$value = 'to_days('.$temp_value.')';
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "to_days( '$value' )";
|
||||
}
|
||||
} elsif ( $term->{attr} eq 'Time' or $term->{attr} eq 'StartTime' or $term->{attr} eq 'EndTime' ) {
|
||||
if ( $temp_value eq 'NULL' ) {
|
||||
$value = $temp_value;
|
||||
} else {
|
||||
$value = DateTimeToSQL($temp_value);
|
||||
if ( !$value ) {
|
||||
Error("Error parsing date/time '$temp_value', skipping filter '$self->{Name}'");
|
||||
return;
|
||||
}
|
||||
$value = "extract( hour_second from '$value' )";
|
||||
}
|
||||
} else {
|
||||
$value = $temp_value;
|
||||
}
|
||||
push @value_list, $value;
|
||||
} # end foreach temp_value
|
||||
|
||||
if ( $term->{op} ) {
|
||||
if ( $term->{op} eq '=~' ) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,10 @@ $serial = $primary_key = 'Id';
|
|||
MonitorId
|
||||
Type
|
||||
Units
|
||||
NumCoords
|
||||
Coords
|
||||
Area
|
||||
AlarmRGB
|
||||
CheckMethod
|
||||
MinPixelThreshold
|
||||
MaxPixelThreshold
|
||||
|
@ -59,9 +63,13 @@ $serial = $primary_key = 'Id';
|
|||
|
||||
%defaults = (
|
||||
Name => '',
|
||||
Type => 'Active',
|
||||
Units => 'Pixels',
|
||||
CheckMethod => 'Blobs',
|
||||
Type => q`'Active'`,
|
||||
Units => q`'Pixels'`,
|
||||
NumCoords => 0,
|
||||
Coords => '',
|
||||
Area => 0,
|
||||
AlarmRGB => 0,
|
||||
CheckMethod => q`'Blobs'`,
|
||||
MinPixelThreshold => undef,
|
||||
MaxPixelThreshold => undef,
|
||||
MinAlarmPixels => undef,
|
||||
|
|
|
@ -61,12 +61,12 @@ GetOptions(
|
|||
'autostop' =>\$options{autostop},
|
||||
) or pod2usage(-exitstatus => -1);
|
||||
|
||||
if ( !$id ) {
|
||||
if (!$id) {
|
||||
print(STDERR "Please give a valid monitor id\n");
|
||||
pod2usage(-exitstatus => -1);
|
||||
}
|
||||
|
||||
( $id ) = $id =~ /^(\w+)$/;
|
||||
($id) = $id =~ /^(\w+)$/;
|
||||
logInit($id?(id=>'zmcontrol_'.$id):());
|
||||
|
||||
my $sock_file = $Config{ZM_PATH_SOCKS}.'/zmcontrol-'.$id.'.sock';
|
||||
|
@ -76,7 +76,7 @@ socket(CLIENT, PF_UNIX, SOCK_STREAM, 0) or Fatal("Can't open socket: $!");
|
|||
|
||||
my $saddr = sockaddr_un($sock_file);
|
||||
|
||||
if ( $options{command} ) {
|
||||
if ($options{command}) {
|
||||
# Have a command, so we are the client, connect to the server and send it.
|
||||
|
||||
my $tries = 10;
|
||||
|
@ -101,18 +101,16 @@ if ( $options{command} ) {
|
|||
Error("Unable to connect to zmcontrol server at $sock_file");
|
||||
}
|
||||
} else {
|
||||
|
||||
# The server isn't there
|
||||
my $monitor = zmDbGetMonitorAndControl($id);
|
||||
if ( !$monitor ) {
|
||||
Fatal("Unable to load control data for monitor $id");
|
||||
}
|
||||
Fatal("Unable to load control data for monitor $id") if !$monitor;
|
||||
|
||||
my $protocol = $monitor->{Protocol};
|
||||
if ( !$protocol ) {
|
||||
if (!$protocol) {
|
||||
Fatal('No protocol is set in monitor. Please edit the monitor, edit control type, select the control capability and fill in the Protocol field');
|
||||
}
|
||||
|
||||
if ( -x $protocol ) {
|
||||
if (-x $protocol) {
|
||||
# Protocol is actually a script!
|
||||
# Holdover from previous versions
|
||||
my $command .= $protocol.' '.$arg_string;
|
||||
|
@ -120,11 +118,11 @@ if ( $options{command} ) {
|
|||
|
||||
my $output = qx($command);
|
||||
my $status = $? >> 8;
|
||||
if ( $status || logDebugging() ) {
|
||||
if ($status || logDebugging()) {
|
||||
chomp($output);
|
||||
Debug("Output: $output");
|
||||
}
|
||||
if ( $status ) {
|
||||
if ($status) {
|
||||
Error("Command '$command' exited with status: $status");
|
||||
exit($status);
|
||||
}
|
||||
|
@ -134,7 +132,7 @@ if ( $options{command} ) {
|
|||
Info("Starting control server $id/$protocol");
|
||||
close(CLIENT);
|
||||
|
||||
if ( ! can_load( modules => { "ZoneMinder::Control::$protocol" => undef } ) ) {
|
||||
if (!can_load(modules => {'ZoneMinder::Control::'.$protocol => undef})) {
|
||||
Fatal("Can't load ZoneMinder::Control::$protocol\n$Module::Load::Conditional::ERROR");
|
||||
}
|
||||
|
||||
|
@ -159,7 +157,7 @@ if ( $options{command} ) {
|
|||
$control->open();
|
||||
|
||||
# If we have a command when starting up, then do it.
|
||||
if ( $options{command} ) {
|
||||
if ($options{command}) {
|
||||
my $command = $options{command};
|
||||
$control->$command(\%options);
|
||||
}
|
||||
|
|
|
@ -399,7 +399,6 @@ sub checkFilter {
|
|||
) {
|
||||
$Event->save();
|
||||
}
|
||||
$ZoneMinder::Database::dbh->commit() if !$$filter{LockRows};
|
||||
} # end if UpdateDiskSpace
|
||||
} # end foreach event
|
||||
ZoneMinder::Database::end_transaction($dbh, $in_transaction) if $$filter{LockRows};
|
||||
|
@ -527,7 +526,7 @@ sub uploadArchFile {
|
|||
return(0);
|
||||
}
|
||||
|
||||
my $archFile = $Event->{MonitorName}.'-'.$Event->{Id};
|
||||
my $archFile = $Event->Monitor()->Name().'-'.$Event->{Id};
|
||||
my $archImagePath = $Event->Path()
|
||||
.'/'
|
||||
.(
|
||||
|
@ -548,6 +547,10 @@ sub uploadArchFile {
|
|||
|
||||
my $status = &AZ_OK;
|
||||
foreach my $imageFile ( @archImageFiles ) {
|
||||
if (! -e $imageFile) {
|
||||
Debug("Not adding $imageFile because it doesn't exist");
|
||||
next;
|
||||
}
|
||||
Debug("Adding $imageFile");
|
||||
my $member = $zip->addFile($imageFile);
|
||||
if ( !$member ) {
|
||||
|
@ -839,7 +842,7 @@ sub sendEmail {
|
|||
return 0;
|
||||
}
|
||||
|
||||
Info('Creating notification email');
|
||||
Debug('Creating notification email');
|
||||
|
||||
my $subject = substituteTags($$filter{EmailSubject}, $filter, $Event);
|
||||
return 0 if !$subject;
|
||||
|
@ -847,7 +850,7 @@ sub sendEmail {
|
|||
my $body = substituteTags($$filter{EmailBody}, $filter, $Event, \@attachments);
|
||||
return 0 if !$body;
|
||||
|
||||
Info("Sending notification email '$subject'");
|
||||
Debug("Sending notification email '$subject'");
|
||||
|
||||
eval {
|
||||
if ( $Config{ZM_NEW_MAIL_MODULES} ) {
|
||||
|
@ -860,7 +863,7 @@ sub sendEmail {
|
|||
);
|
||||
### Add the text message part
|
||||
$mail->attach (
|
||||
Type => 'TEXT',
|
||||
Type => (($body=~/<html/)?'text/html':'text/plain'),
|
||||
Data => $body
|
||||
);
|
||||
### Add the attachments
|
||||
|
@ -882,9 +885,7 @@ sub sendEmail {
|
|||
if ( $Config{ZM_SSMTP_MAIL} ) {
|
||||
my $ssmtp_location = $Config{ZM_SSMTP_PATH};
|
||||
if ( !$ssmtp_location ) {
|
||||
if ( logDebugging() ) {
|
||||
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
|
||||
}
|
||||
Debug("which ssmtp: $ssmtp_location - set ssmtp path in options to suppress this message");
|
||||
$ssmtp_location = qx('which ssmtp');
|
||||
}
|
||||
if ( !$ssmtp_location ) {
|
||||
|
@ -912,7 +913,7 @@ sub sendEmail {
|
|||
foreach my $attachment ( @attachments ) {
|
||||
my $size = -s $attachment->{path};
|
||||
$total_size += $size;
|
||||
Info("Attaching '$attachment->{path}' which is $size bytes");
|
||||
Debug("Attaching '$attachment->{path}' which is $size bytes");
|
||||
|
||||
$mail->attach(
|
||||
Path => $attachment->{path},
|
||||
|
@ -930,7 +931,7 @@ sub sendEmail {
|
|||
Error("Unable to send email: $@");
|
||||
return 0;
|
||||
} else {
|
||||
Info('Notification email sent');
|
||||
Info("Notification email sent to $$filter{EmailTo}");
|
||||
}
|
||||
my $sql = 'UPDATE `Events` SET `Emailed` = 1 WHERE `Id` = ?';
|
||||
my $sth = $dbh->prepare_cached($sql)
|
||||
|
|
|
@ -27,7 +27,7 @@ zmupdate.pl - check and upgrade ZoneMinder database
|
|||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
zmupdate.pl -c,--check | -f,--freshen | -v<version>,--version=<version> [-u<dbuser> -p<dbpass>]
|
||||
zmupdate.pl -c,--check | -f,--freshen | -v<version>,--version=<version> [-u <dbuser> -p <dbpass>]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
|
|
|
@ -98,19 +98,19 @@ while (!$zm_terminate) {
|
|||
next if $monitor->{Type} eq 'WebSite';
|
||||
my $now = time();
|
||||
my $restart = 0;
|
||||
if ( zmMemVerify($monitor) ) {
|
||||
if (zmMemVerify($monitor)) {
|
||||
# Check we have got an image recently
|
||||
my $capture_time = zmGetLastWriteTime($monitor);
|
||||
if ( !defined($capture_time) ) {
|
||||
if (!defined($capture_time)) {
|
||||
# Can't read from shared data
|
||||
Debug('LastWriteTime is not defined.');
|
||||
zmMemInvalidate($monitor);
|
||||
next;
|
||||
}
|
||||
Debug("Monitor $$monitor{Id} LastWriteTime is $capture_time.");
|
||||
if ( !$capture_time ) {
|
||||
if (!$capture_time) {
|
||||
my $startup_time = zmGetStartupTime($monitor);
|
||||
if ( ( $now - $startup_time ) > $Config{ZM_WATCH_MAX_DELAY} ) {
|
||||
if (($now - $startup_time) > $Config{ZM_WATCH_MAX_DELAY}) {
|
||||
Warning(
|
||||
"Restarting capture daemon for $$monitor{Name}, no image since startup. ".
|
||||
"Startup time was $startup_time - now $now > $Config{ZM_WATCH_MAX_DELAY}"
|
||||
|
@ -122,7 +122,7 @@ while (!$zm_terminate) {
|
|||
next;
|
||||
}
|
||||
}
|
||||
if ( ! $restart ) {
|
||||
if (!$restart) {
|
||||
my $max_image_delay = (
|
||||
$monitor->{MaxFPS}
|
||||
&&($monitor->{MaxFPS}>0)
|
||||
|
@ -144,29 +144,28 @@ while (!$zm_terminate) {
|
|||
$restart = 1;
|
||||
}
|
||||
|
||||
if ( $restart ) {
|
||||
if ($restart) {
|
||||
my $command;
|
||||
if ( $monitor->{Type} eq 'Local' ) {
|
||||
$command = "zmdc.pl restart zmc -d $monitor->{Device}";
|
||||
if ($monitor->{Type} eq 'Local') {
|
||||
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
|
||||
} else {
|
||||
$command = "zmdc.pl restart zmc -m $monitor->{Id}";
|
||||
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
|
||||
}
|
||||
runCommand($command);
|
||||
} elsif ( $monitor->{Function} ne 'Monitor' ) {
|
||||
} elsif ($monitor->{Function} ne 'Monitor') {
|
||||
# Now check analysis daemon
|
||||
$restart = 0;
|
||||
# Check we have got an image recently
|
||||
my $image_time = zmGetLastReadTime($monitor);
|
||||
if ( !defined($image_time) ) {
|
||||
if (!defined($image_time)) {
|
||||
# Can't read from shared data
|
||||
$restart = 1;
|
||||
Error("Error reading shared data for $$monitor{Id} $$monitor{Name}");
|
||||
} elsif ( !$image_time ) {
|
||||
} elsif (!$image_time) {
|
||||
# We can't get the last capture time so can't be sure it's died.
|
||||
#$restart = 1;
|
||||
Error("Last analyse time for $$monitor{Id} $$monitor{Name} was zero.");
|
||||
} else {
|
||||
|
||||
my $max_image_delay = ( $monitor->{MaxFPS}
|
||||
&&($monitor->{MaxFPS}>0)
|
||||
&&($monitor->{MaxFPS}<1)
|
||||
|
@ -175,7 +174,7 @@ while (!$zm_terminate) {
|
|||
;
|
||||
my $image_delay = $now-$image_time;
|
||||
Debug("Monitor $monitor->{Id} last analysed $image_delay seconds ago, max is $max_image_delay");
|
||||
if ( $image_delay > $max_image_delay ) {
|
||||
if ($image_delay > $max_image_delay) {
|
||||
Warning("Analysis daemon for $$monitor{Id} $$monitor{Name} needs restarting,"
|
||||
." time since last analysis $image_delay seconds ($now-$image_time)"
|
||||
);
|
||||
|
@ -183,13 +182,13 @@ while (!$zm_terminate) {
|
|||
}
|
||||
}
|
||||
|
||||
if ( $restart ) {
|
||||
Info("Restarting analysis daemon for $$monitor{Id} $$monitor{Name}\n");
|
||||
if ($restart) {
|
||||
Info("Restarting analysis daemon for $$monitor{Id} $$monitor{Name}");
|
||||
my $command;
|
||||
if ( $monitor->{Type} eq 'Local' ) {
|
||||
$command = "zmdc.pl restart zmc -d $monitor->{Device}";
|
||||
$command = 'zmdc.pl restart zmc -d '.$monitor->{Device};
|
||||
} else {
|
||||
$command = "zmdc.pl restart zmc -m $monitor->{Id}";
|
||||
$command = 'zmdc.pl restart zmc -m '.$monitor->{Id};
|
||||
}
|
||||
runCommand($command);
|
||||
} # end if restart
|
||||
|
@ -201,7 +200,7 @@ while (!$zm_terminate) {
|
|||
sleep($Config{ZM_WATCH_CHECK_INTERVAL});
|
||||
} # end while (!$zm_terminate)
|
||||
|
||||
Info("Watchdog exiting");
|
||||
Info('Watchdog exiting');
|
||||
exit();
|
||||
|
||||
1;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
//
|
||||
// ZoneMinder cURL Camera Class Implementation, $Date: 2009-01-16 12:18:50 +0000 (Fri, 16 Jan 2009) $, $Revision: 2713 $
|
||||
// Copyright (C) 2001-2008 Philip Coombes
|
||||
//
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
@ -73,19 +73,35 @@ void bind_libcurl_symbols() {
|
|||
*(void**) (&curl_easy_cleanup_f) = dlsym(curl_lib, "curl_easy_cleanup");
|
||||
}
|
||||
|
||||
cURLCamera::cURLCamera( const Monitor* monitor, const std::string &p_path, const std::string &p_user, const std::string &p_pass, unsigned int p_width, unsigned int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
|
||||
Camera( monitor, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio ),
|
||||
mPath( p_path ), mUser( p_user ), mPass ( p_pass ), bTerminate( false ), bReset( false ), mode ( MODE_UNSET )
|
||||
cURLCamera::cURLCamera(
|
||||
const Monitor* monitor,
|
||||
const std::string &p_path,
|
||||
const std::string &p_user,
|
||||
const std::string &p_pass,
|
||||
unsigned int p_width,
|
||||
unsigned int p_height,
|
||||
int p_colours,
|
||||
int p_brightness,
|
||||
int p_contrast,
|
||||
int p_hue,
|
||||
int p_colour,
|
||||
bool p_capture,
|
||||
bool p_record_audio) :
|
||||
Camera(monitor, CURL_SRC, p_width, p_height, p_colours, ZM_SUBPIX_ORDER_DEFAULT_FOR_COLOUR(p_colours), p_brightness, p_contrast, p_hue, p_colour, p_capture, p_record_audio),
|
||||
mPath(p_path),
|
||||
mUser(p_user),
|
||||
mPass(p_pass),
|
||||
bTerminate(false),
|
||||
bReset(false),
|
||||
mode(MODE_UNSET)
|
||||
{
|
||||
|
||||
if ( capture ) {
|
||||
if (capture) {
|
||||
Initialise();
|
||||
}
|
||||
}
|
||||
|
||||
cURLCamera::~cURLCamera() {
|
||||
if ( capture ) {
|
||||
|
||||
if (capture) {
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
@ -95,40 +111,40 @@ void cURLCamera::Initialise() {
|
|||
content_type_match_len = strlen(content_type_match);
|
||||
|
||||
databuffer.expand(CURL_BUFFER_INITIAL_SIZE);
|
||||
|
||||
|
||||
bind_libcurl_symbols();
|
||||
/* cURL initialization */
|
||||
CURLcode cRet = (*curl_global_init_f)(CURL_GLOBAL_ALL);
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
Error("libcurl initialization failed: %s", (*curl_easy_strerror_f)(cRet));
|
||||
dlclose(curl_lib);
|
||||
return;
|
||||
}
|
||||
|
||||
Debug(2,"libcurl version: %s", (*curl_version_f)());
|
||||
Debug(2, "libcurl version: %s", (*curl_version_f)());
|
||||
|
||||
/* Create the shared data mutex */
|
||||
int nRet = pthread_mutex_init(&shareddata_mutex, nullptr);
|
||||
if(nRet != 0) {
|
||||
if (nRet != 0) {
|
||||
Error("Shared data mutex creation failed: %s",strerror(nRet));
|
||||
return;
|
||||
}
|
||||
/* Create the data available condition variable */
|
||||
nRet = pthread_cond_init(&data_available_cond, nullptr);
|
||||
if(nRet != 0) {
|
||||
if (nRet != 0) {
|
||||
Error("Data available condition variable creation failed: %s",strerror(nRet));
|
||||
return;
|
||||
}
|
||||
/* Create the request complete condition variable */
|
||||
nRet = pthread_cond_init(&request_complete_cond, nullptr);
|
||||
if(nRet != 0) {
|
||||
if (nRet != 0) {
|
||||
Error("Request complete condition variable creation failed: %s",strerror(nRet));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create the thread */
|
||||
nRet = pthread_create(&thread, nullptr, thread_func_dispatcher, this);
|
||||
if(nRet != 0) {
|
||||
if (nRet != 0) {
|
||||
Error("Thread creation failed: %s",strerror(nRet));
|
||||
return;
|
||||
}
|
||||
|
@ -151,18 +167,19 @@ void cURLCamera::Terminate() {
|
|||
/* cURL cleanup */
|
||||
(*curl_global_cleanup_f)();
|
||||
|
||||
if(curl_lib)
|
||||
if (curl_lib)
|
||||
dlclose(curl_lib);
|
||||
}
|
||||
|
||||
int cURLCamera::PrimeCapture() {
|
||||
getVideoStream();
|
||||
//Info( "Priming capture from %s", mPath.c_str() );
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cURLCamera::PreCapture() {
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
// Nothing to do here
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
||||
|
@ -178,10 +195,9 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
|||
/* Grab the mutex to ensure exclusive access to the shared data */
|
||||
lock();
|
||||
|
||||
while ( !frameComplete ) {
|
||||
|
||||
while (!frameComplete) {
|
||||
/* If the work thread did a reset, reset our local variables */
|
||||
if ( bReset ) {
|
||||
if (bReset) {
|
||||
SubHeadersParsingComplete = false;
|
||||
frame_content_length = 0;
|
||||
frame_content_type.clear();
|
||||
|
@ -189,40 +205,40 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
|||
bReset = false;
|
||||
}
|
||||
|
||||
if ( mode == MODE_UNSET ) {
|
||||
if (mode == MODE_UNSET) {
|
||||
/* Don't have a mode yet. Sleep while waiting for data */
|
||||
nRet = pthread_cond_wait(&data_available_cond,&shareddata_mutex);
|
||||
if ( nRet != 0 ) {
|
||||
if (nRet != 0) {
|
||||
Error("Failed waiting for available data condition variable: %s",strerror(nRet));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ( mode == MODE_STREAM ) {
|
||||
if (mode == MODE_STREAM) {
|
||||
|
||||
/* Subheader parsing */
|
||||
while( !SubHeadersParsingComplete && !need_more_data ) {
|
||||
while (!SubHeadersParsingComplete && !need_more_data) {
|
||||
|
||||
size_t crlf_start, crlf_end, crlf_size;
|
||||
std::string subheader;
|
||||
|
||||
/* Check if the buffer contains something */
|
||||
if ( databuffer.empty() ) {
|
||||
if (databuffer.empty()) {
|
||||
/* Empty buffer, wait for data */
|
||||
need_more_data = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Find crlf start */
|
||||
crlf_start = memcspn(reinterpret_cast<const char *>(databuffer.head()),"\r\n",databuffer.size());
|
||||
if ( crlf_start == databuffer.size() ) {
|
||||
if (crlf_start == databuffer.size()) {
|
||||
/* Not found, wait for more data */
|
||||
need_more_data = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* See if we have enough data for determining crlf length */
|
||||
if ( databuffer.size() < crlf_start+5 ) {
|
||||
if (databuffer.size() < crlf_start+5) {
|
||||
/* Need more data */
|
||||
need_more_data = true;
|
||||
break;
|
||||
|
@ -233,18 +249,22 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
|||
crlf_size = (crlf_start + crlf_end) - crlf_start;
|
||||
|
||||
/* Is this the end of a previous stream? (This is just before the boundary) */
|
||||
if ( crlf_start == 0 ) {
|
||||
if (crlf_start == 0) {
|
||||
databuffer.consume(crlf_size);
|
||||
continue;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for invalid CRLF size */
|
||||
if ( crlf_size > 4 ) {
|
||||
if (crlf_size > 4) {
|
||||
Error("Invalid CRLF length");
|
||||
}
|
||||
|
||||
/* Check if the crlf is \n\n or \r\n\r\n (marks end of headers, this is the last header) */
|
||||
if( (crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0) || (crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0) ) {
|
||||
if (
|
||||
(crlf_size == 2 && memcmp(((const char*)databuffer.head())+crlf_start,"\n\n",2) == 0)
|
||||
||
|
||||
(crlf_size == 4 && memcmp(((const char*)databuffer.head())+crlf_start,"\r\n\r\n",4) == 0)
|
||||
) {
|
||||
/* This is the last header */
|
||||
SubHeadersParsingComplete = true;
|
||||
}
|
||||
|
@ -255,48 +275,56 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
|||
/* Advance the buffer past this one */
|
||||
databuffer.consume(crlf_start+crlf_size);
|
||||
|
||||
Debug(7,"Got subheader: %s",subheader.c_str());
|
||||
Debug(7, "Got subheader: %s",subheader.c_str());
|
||||
|
||||
/* Find where the data in this header starts */
|
||||
size_t subheader_data_start = subheader.rfind(' ');
|
||||
if ( subheader_data_start == std::string::npos ) {
|
||||
if (subheader_data_start == std::string::npos) {
|
||||
subheader_data_start = subheader.find(':');
|
||||
}
|
||||
|
||||
/* Extract the data into a string */
|
||||
std::string subheader_data = subheader.substr(subheader_data_start+1, std::string::npos);
|
||||
|
||||
Debug(8,"Got subheader data: %s",subheader_data.c_str());
|
||||
Debug(8, "Got subheader data: %s", subheader_data.c_str());
|
||||
|
||||
/* Check the header */
|
||||
if(strncasecmp(subheader.c_str(),content_length_match,content_length_match_len) == 0) {
|
||||
if (strncasecmp(subheader.c_str(), content_length_match, content_length_match_len) == 0) {
|
||||
/* Found the content-length header */
|
||||
frame_content_length = atoi(subheader_data.c_str());
|
||||
Debug(6,"Got content-length subheader: %d",frame_content_length);
|
||||
} else if(strncasecmp(subheader.c_str(),content_type_match,content_type_match_len) == 0) {
|
||||
} else if (strncasecmp(subheader.c_str(), content_type_match, content_type_match_len) == 0) {
|
||||
/* Found the content-type header */
|
||||
frame_content_type = subheader_data;
|
||||
Debug(6,"Got content-type subheader: %s",frame_content_type.c_str());
|
||||
Debug(6,"Got content-type subheader: %s", frame_content_type.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Attempt to extract the frame */
|
||||
if(!need_more_data) {
|
||||
if(!SubHeadersParsingComplete) {
|
||||
if (!need_more_data) {
|
||||
if (!SubHeadersParsingComplete) {
|
||||
/* We haven't parsed all headers yet */
|
||||
need_more_data = true;
|
||||
} else if ( ! frame_content_length ) {
|
||||
} else if (!frame_content_length) {
|
||||
/* Invalid frame */
|
||||
Error("Invalid frame: invalid content length");
|
||||
} else if ( frame_content_type != "image/jpeg" ) {
|
||||
} else if (frame_content_type != "image/jpeg") {
|
||||
/* Unsupported frame type */
|
||||
Error("Unsupported frame: %s",frame_content_type.c_str());
|
||||
} else if(frame_content_length > databuffer.size()) {
|
||||
Error("Unsupported frame: %s", frame_content_type.c_str());
|
||||
} else if (frame_content_length > databuffer.size()) {
|
||||
/* Incomplete frame, wait for more data */
|
||||
need_more_data = true;
|
||||
} else {
|
||||
/* All good. decode the image */
|
||||
if (!zm_packet->image) {
|
||||
Debug(4, "Allocating image");
|
||||
zm_packet->image = new Image(width, height, colours, subpixelorder);
|
||||
}
|
||||
zm_packet->keyframe = 1;
|
||||
zm_packet->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
zm_packet->packet.stream_index = mVideoStreamId;
|
||||
zm_packet->stream = mVideoStream;
|
||||
zm_packet->image->DecodeJpeg(databuffer.extract(frame_content_length), frame_content_length, colours, subpixelorder);
|
||||
frameComplete = true;
|
||||
}
|
||||
|
@ -312,11 +340,19 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
|||
need_more_data = false;
|
||||
}
|
||||
|
||||
} else if (mode == MODE_SINGLE) {
|
||||
} else if (mode == MODE_SINGLE) {
|
||||
/* Check if we have anything */
|
||||
if (!single_offsets.empty()) {
|
||||
if ((single_offsets.front() > 0) && (databuffer.size() >= single_offsets.front())) {
|
||||
/* Extract frame */
|
||||
if (!zm_packet->image) {
|
||||
Debug(4, "Allocating image");
|
||||
zm_packet->image = new Image(width, height, colours, subpixelorder);
|
||||
}
|
||||
zm_packet->keyframe = 1;
|
||||
zm_packet->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||
zm_packet->packet.stream_index = mVideoStreamId;
|
||||
zm_packet->stream = mVideoStream;
|
||||
zm_packet->image->DecodeJpeg(databuffer.extract(single_offsets.front()), single_offsets.front(), colours, subpixelorder);
|
||||
single_offsets.pop_front();
|
||||
frameComplete = true;
|
||||
|
@ -345,15 +381,15 @@ int cURLCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
|||
/* Release the mutex */
|
||||
unlock();
|
||||
|
||||
if(!frameComplete)
|
||||
if (!frameComplete)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cURLCamera::PostCapture() {
|
||||
// Nothing to do here
|
||||
return( 0 );
|
||||
// Nothing to do here
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata) {
|
||||
|
@ -364,7 +400,7 @@ size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *
|
|||
|
||||
/* Signal data available */
|
||||
int nRet = pthread_cond_signal(&data_available_cond);
|
||||
if ( nRet != 0 ) {
|
||||
if (nRet != 0) {
|
||||
Error("Failed signaling data available condition variable: %s",strerror(nRet));
|
||||
unlock();
|
||||
return -16;
|
||||
|
@ -379,18 +415,18 @@ size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *
|
|||
size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata) {
|
||||
std::string header;
|
||||
header.assign((const char*)buffer, size*nmemb);
|
||||
|
||||
Debug(4,"Got header: %s",header.c_str());
|
||||
|
||||
/* Check Content-Type header */
|
||||
if(strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) {
|
||||
Debug(4, "Got header: %s", header.c_str());
|
||||
|
||||
/* Check Content-Type header */
|
||||
if (strncasecmp(header.c_str(),content_type_match,content_type_match_len) == 0) {
|
||||
size_t pos = header.find(';');
|
||||
if(pos != std::string::npos) {
|
||||
if (pos != std::string::npos) {
|
||||
header.erase(pos, std::string::npos);
|
||||
}
|
||||
|
||||
pos = header.rfind(' ');
|
||||
if(pos == std::string::npos) {
|
||||
if (pos == std::string::npos) {
|
||||
pos = header.find(':');
|
||||
}
|
||||
|
||||
|
@ -401,17 +437,17 @@ size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, voi
|
|||
|
||||
const char* multipart_match = "multipart/x-mixed-replace";
|
||||
const char* image_jpeg_match = "image/jpeg";
|
||||
if(strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) {
|
||||
Debug(7,"Content type matched as multipart/x-mixed-replace");
|
||||
if (strncasecmp(content_type.c_str(),multipart_match,strlen(multipart_match)) == 0) {
|
||||
Debug(7, "Content type matched as multipart/x-mixed-replace");
|
||||
mode = MODE_STREAM;
|
||||
} else if(strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) {
|
||||
Debug(7,"Content type matched as image/jpeg");
|
||||
} else if (strncasecmp(content_type.c_str(),image_jpeg_match,strlen(image_jpeg_match)) == 0) {
|
||||
Debug(7, "Content type matched as image/jpeg");
|
||||
mode = MODE_SINGLE;
|
||||
}
|
||||
|
||||
unlock();
|
||||
}
|
||||
|
||||
|
||||
/* Return bytes processed */
|
||||
return size*nmemb;
|
||||
}
|
||||
|
@ -421,7 +457,7 @@ void* cURLCamera::thread_func() {
|
|||
double dSize;
|
||||
|
||||
c = (*curl_easy_init_f)();
|
||||
if(c == nullptr) {
|
||||
if (c == nullptr) {
|
||||
dlclose(curl_lib);
|
||||
Error("Failed getting easy handle from libcurl");
|
||||
tRet = -51;
|
||||
|
@ -431,99 +467,99 @@ void* cURLCamera::thread_func() {
|
|||
CURLcode cRet;
|
||||
/* Set URL */
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_URL, mPath.c_str());
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
Error("Failed setting libcurl URL: %s", (*curl_easy_strerror_f)(cRet));
|
||||
tRet = -52;
|
||||
return (void*)tRet;
|
||||
}
|
||||
|
||||
|
||||
/* Header callback */
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_HEADERFUNCTION, &header_callback_dispatcher);
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
Error("Failed setting libcurl header callback function: %s", (*curl_easy_strerror_f)(cRet));
|
||||
tRet = -53;
|
||||
return (void*)tRet;
|
||||
}
|
||||
|
||||
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_HEADERDATA, this);
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
Error("Failed setting libcurl header callback object: %s", (*curl_easy_strerror_f)(cRet));
|
||||
tRet = -54;
|
||||
return (void*)tRet;
|
||||
}
|
||||
/* Data callback */
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_WRITEFUNCTION, &data_callback_dispatcher);
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
Error("Failed setting libcurl data callback function: %s", (*curl_easy_strerror_f)(cRet));
|
||||
tRet = -55;
|
||||
return (void*)tRet;
|
||||
}
|
||||
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_WRITEDATA, this);
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
Error("Failed setting libcurl data callback object: %s", (*curl_easy_strerror_f)(cRet));
|
||||
tRet = -56;
|
||||
return (void*)tRet;
|
||||
}
|
||||
/* Progress callback */
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_NOPROGRESS, 0);
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
Error("Failed enabling libcurl progress callback function: %s", (*curl_easy_strerror_f)(cRet));
|
||||
tRet = -57;
|
||||
return (void*)tRet;
|
||||
}
|
||||
|
||||
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_PROGRESSFUNCTION, &progress_callback_dispatcher);
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
Error("Failed setting libcurl progress callback function: %s", (*curl_easy_strerror_f)(cRet));
|
||||
tRet = -58;
|
||||
return (void*)tRet;
|
||||
}
|
||||
|
||||
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_PROGRESSDATA, this);
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
Error("Failed setting libcurl progress callback object: %s", (*curl_easy_strerror_f)(cRet));
|
||||
tRet = -59;
|
||||
return (void*)tRet;
|
||||
}
|
||||
/* Set username and password */
|
||||
if(!mUser.empty()) {
|
||||
if (!mUser.empty()) {
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_USERNAME, mUser.c_str());
|
||||
if(cRet != CURLE_OK)
|
||||
if (cRet != CURLE_OK)
|
||||
Error("Failed setting username: %s", (*curl_easy_strerror_f)(cRet));
|
||||
}
|
||||
if(!mPass.empty()) {
|
||||
if (!mPass.empty()) {
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_PASSWORD, mPass.c_str());
|
||||
if(cRet != CURLE_OK)
|
||||
if (cRet != CURLE_OK)
|
||||
Error("Failed setting password: %s", (*curl_easy_strerror_f)(cRet));
|
||||
}
|
||||
|
||||
/* Authenication preference */
|
||||
cRet = (*curl_easy_setopt_f)(c, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
|
||||
if(cRet != CURLE_OK)
|
||||
if (cRet != CURLE_OK)
|
||||
Warning("Failed setting libcurl acceptable http authenication methods: %s", (*curl_easy_strerror_f)(cRet));
|
||||
|
||||
|
||||
/* Work loop */
|
||||
for(int attempt=1;attempt<=CURL_MAXRETRY;attempt++) {
|
||||
for (int attempt=1;attempt<=CURL_MAXRETRY;attempt++) {
|
||||
tRet = 0;
|
||||
while(!bTerminate) {
|
||||
while (!bTerminate) {
|
||||
/* Do the work */
|
||||
cRet = (*curl_easy_perform_f)(c);
|
||||
|
||||
if(mode == MODE_SINGLE) {
|
||||
if(cRet != CURLE_OK) {
|
||||
if (mode == MODE_SINGLE) {
|
||||
if (cRet != CURLE_OK) {
|
||||
break;
|
||||
}
|
||||
/* Attempt to get the size of the file */
|
||||
cRet = (*curl_easy_getinfo_f)(c, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dSize);
|
||||
if(cRet != CURLE_OK) {
|
||||
if (cRet != CURLE_OK) {
|
||||
break;
|
||||
}
|
||||
/* We need to lock for the offsets array and the condition variable */
|
||||
lock();
|
||||
/* Push the size into our offsets array */
|
||||
if(dSize > 0) {
|
||||
if (dSize > 0) {
|
||||
single_offsets.push_back(dSize);
|
||||
} else {
|
||||
Error("Unable to get the size of the image");
|
||||
|
@ -532,7 +568,7 @@ void* cURLCamera::thread_func() {
|
|||
}
|
||||
/* Signal the request complete condition variable */
|
||||
tRet = pthread_cond_signal(&request_complete_cond);
|
||||
if(tRet != 0) {
|
||||
if (tRet != 0) {
|
||||
Error("Failed signaling request completed condition variable: %s",strerror(tRet));
|
||||
tRet = -61;
|
||||
return (void*)tRet;
|
||||
|
@ -546,13 +582,13 @@ void* cURLCamera::thread_func() {
|
|||
}
|
||||
|
||||
/* Return value checking */
|
||||
if(cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) {
|
||||
if (cRet == CURLE_ABORTED_BY_CALLBACK || bTerminate) {
|
||||
/* Aborted */
|
||||
break;
|
||||
} else if (cRet != CURLE_OK) {
|
||||
/* Some error */
|
||||
Error("cURL Request failed: %s",(*curl_easy_strerror_f)(cRet));
|
||||
if(attempt < CURL_MAXRETRY) {
|
||||
if (attempt < CURL_MAXRETRY) {
|
||||
Error("Retrying.. Attempt %d of %d",attempt,CURL_MAXRETRY);
|
||||
/* Do a reset */
|
||||
lock();
|
||||
|
@ -565,11 +601,11 @@ void* cURLCamera::thread_func() {
|
|||
tRet = -50;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Cleanup */
|
||||
(*curl_easy_cleanup_f)(c);
|
||||
c = nullptr;
|
||||
|
||||
|
||||
return (void*)tRet;
|
||||
}
|
||||
|
||||
|
@ -597,9 +633,9 @@ int cURLCamera::unlock() {
|
|||
|
||||
int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) {
|
||||
/* Signal the curl thread to terminate */
|
||||
if(bTerminate)
|
||||
if (bTerminate)
|
||||
return -10;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
17
src/zm_db.h
17
src/zm_db.h
|
@ -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
|
||||
|
|
130
src/zm_event.cpp
130
src/zm_event.cpp
|
@ -32,8 +32,6 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//#define USE_PREPARED_SQL 1
|
||||
|
||||
const char * Event::frame_type_names[3] = { "Normal", "Bulk", "Alarm" };
|
||||
#define MAX_DB_FRAMES 100
|
||||
|
||||
|
@ -43,13 +41,13 @@ Event::PreAlarmData Event::pre_alarm_data[MAX_PRE_ALARM_FRAMES] = {};
|
|||
|
||||
Event::Event(
|
||||
Monitor *p_monitor,
|
||||
struct timeval p_start_time,
|
||||
SystemTimePoint p_start_time,
|
||||
const std::string &p_cause,
|
||||
const StringSetMap &p_noteSetMap
|
||||
) :
|
||||
id(0),
|
||||
monitor(p_monitor),
|
||||
start_time(SystemTimePoint(zm::chrono::duration_cast<Microseconds>(p_start_time))),
|
||||
start_time(p_start_time),
|
||||
end_time(),
|
||||
cause(p_cause),
|
||||
noteSetMap(p_noteSetMap),
|
||||
|
@ -111,7 +109,7 @@ Event::Event(
|
|||
"INSERT INTO `Events` "
|
||||
"( `MonitorId`, `StorageId`, `Name`, `StartDateTime`, `Width`, `Height`, `Cause`, `Notes`, `StateId`, `Orientation`, `Videoed`, `DefaultVideo`, `SaveJPEGs`, `Scheme` )"
|
||||
" VALUES "
|
||||
"( %d, %d, 'New Event', from_unixtime( %ld ), %d, %d, '%s', '%s', %d, %d, %d, '%s', %d, '%s' )",
|
||||
"( %d, %d, 'New Event', from_unixtime(%" PRId64 "), %u, %u, '%s', '%s', %d, %d, %d, '%s', %d, '%s' )",
|
||||
monitor->Id(),
|
||||
storage->Id(),
|
||||
static_cast<int64>(std::chrono::system_clock::to_time_t(start_time)),
|
||||
|
@ -122,12 +120,11 @@ Event::Event(
|
|||
state_id,
|
||||
monitor->getOrientation(),
|
||||
0,
|
||||
"",
|
||||
"",
|
||||
save_jpegs,
|
||||
storage->SchemeString().c_str()
|
||||
);
|
||||
|
||||
id = zmDbDoInsert(sql.c_str());
|
||||
id = zmDbDoInsert(sql);
|
||||
|
||||
if ( !SetPath(storage) ) {
|
||||
// Try another
|
||||
|
@ -140,7 +137,7 @@ Event::Event(
|
|||
Debug(1, "%s", sql.c_str());
|
||||
storage = nullptr;
|
||||
|
||||
MYSQL_RES *result = zmDbFetch(sql.c_str());
|
||||
MYSQL_RES *result = zmDbFetch(sql);
|
||||
if ( result ) {
|
||||
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||
storage = new Storage(atoi(dbrow[0]));
|
||||
|
@ -159,7 +156,7 @@ Event::Event(
|
|||
if ( monitor->ServerId() )
|
||||
sql += stringtf(" OR ServerId != %u", monitor->ServerId());
|
||||
|
||||
result = zmDbFetch(sql.c_str());
|
||||
result = zmDbFetch(sql);
|
||||
if ( result ) {
|
||||
for ( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row(result); i++ ) {
|
||||
storage = new Storage(atoi(dbrow[0]));
|
||||
|
@ -177,7 +174,7 @@ Event::Event(
|
|||
Warning("Failed to find a storage area to save events.");
|
||||
}
|
||||
sql = stringtf("UPDATE Events SET StorageId = '%d' WHERE Id=%" PRIu64, storage->Id(), id);
|
||||
zmDbDo(sql.c_str());
|
||||
zmDbDo(sql);
|
||||
} // end if ! setPath(Storage)
|
||||
Debug(1, "Using storage area at %s", path.c_str());
|
||||
|
||||
|
@ -213,14 +210,14 @@ Event::Event(
|
|||
if ( ! ( save_jpegs & 1 ) ) {
|
||||
save_jpegs |= 1; // Turn on jpeg storage
|
||||
sql = stringtf("UPDATE Events SET SaveJpegs=%d WHERE Id=%" PRIu64, save_jpegs, id);
|
||||
zmDbDo(sql.c_str());
|
||||
zmDbDo(sql);
|
||||
}
|
||||
} else {
|
||||
sql = stringtf("UPDATE Events SET Videoed=1, DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id);
|
||||
zmDbDo(sql.c_str());
|
||||
zmDbDo(sql);
|
||||
}
|
||||
} // end if GetOptVideoWriter
|
||||
} // Event::Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent )
|
||||
}
|
||||
|
||||
Event::~Event() {
|
||||
// We close the videowriter first, because if we finish the event, we might try to view the file, but we aren't done writing it yet.
|
||||
|
@ -255,7 +252,7 @@ Event::~Event() {
|
|||
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
|
||||
id);
|
||||
|
||||
if (!zmDbDoUpdate(sql.c_str())) {
|
||||
if (!zmDbDoUpdate(sql)) {
|
||||
// Name might have been changed during recording, so just do the update without changing the name.
|
||||
sql = stringtf(
|
||||
"UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64,
|
||||
|
@ -264,7 +261,7 @@ Event::~Event() {
|
|||
frames, alarm_frames,
|
||||
tot_score, static_cast<uint32>(alarm_frames ? (tot_score / alarm_frames) : 0), max_score,
|
||||
id);
|
||||
zmDbDoUpdate(sql.c_str());
|
||||
zmDbDoUpdate(sql);
|
||||
} // end if no changed rows due to Name change during recording
|
||||
} // Event::~Event()
|
||||
|
||||
|
@ -282,20 +279,14 @@ void Event::createNotes(std::string ¬es) {
|
|||
}
|
||||
} // void Event::createNotes(std::string ¬es)
|
||||
|
||||
bool Event::WriteFrameImage(
|
||||
Image *image,
|
||||
timeval timestamp,
|
||||
const char *event_file,
|
||||
bool alarm_frame) const {
|
||||
|
||||
bool Event::WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame) const {
|
||||
int thisquality =
|
||||
(alarm_frame && (config.jpeg_alarm_file_quality > config.jpeg_file_quality)) ?
|
||||
config.jpeg_alarm_file_quality : 0; // quality to use, zero is default
|
||||
|
||||
bool rc;
|
||||
|
||||
SystemTimePoint jpeg_timestamp =
|
||||
monitor->Exif() ? SystemTimePoint(zm::chrono::duration_cast<Microseconds>(timestamp)) : SystemTimePoint();
|
||||
SystemTimePoint jpeg_timestamp = monitor->Exif() ? timestamp : SystemTimePoint();
|
||||
|
||||
if (!config.timestamp_on_capture) {
|
||||
// stash the image we plan to use in another pointer regardless if timestamped.
|
||||
|
@ -309,7 +300,7 @@ bool Event::WriteFrameImage(
|
|||
}
|
||||
|
||||
return rc;
|
||||
} // end Event::WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame )
|
||||
}
|
||||
|
||||
bool Event::WritePacket(const std::shared_ptr<ZMPacket>&packet) {
|
||||
if (videoStore->writePacket(packet) < 0)
|
||||
|
@ -357,65 +348,15 @@ void Event::updateNotes(const StringSetMap &newNoteSetMap) {
|
|||
} // end if have old notes
|
||||
} // end if have new notes
|
||||
|
||||
if ( update ) {
|
||||
if (update) {
|
||||
std::string notes;
|
||||
createNotes(notes);
|
||||
|
||||
Debug(2, "Updating notes for event %" PRIu64 ", '%s'", id, notes.c_str());
|
||||
#if USE_PREPARED_SQL
|
||||
static MYSQL_STMT *stmt = 0;
|
||||
|
||||
char notesStr[ZM_SQL_MED_BUFSIZ] = "";
|
||||
unsigned long notesLen = 0;
|
||||
|
||||
if ( !stmt ) {
|
||||
const char *sql = "UPDATE `Events` SET `Notes` = ? WHERE `Id` = ?";
|
||||
|
||||
stmt = mysql_stmt_init(&dbconn);
|
||||
if ( mysql_stmt_prepare(stmt, sql, strlen(sql)) ) {
|
||||
Fatal("Unable to prepare sql '%s': %s", sql, mysql_stmt_error(stmt));
|
||||
}
|
||||
|
||||
/* Get the parameter count from the statement */
|
||||
if ( mysql_stmt_param_count(stmt) != 2 ) {
|
||||
Error("Unexpected parameter count %ld in sql '%s'", mysql_stmt_param_count(stmt), sql);
|
||||
}
|
||||
|
||||
MYSQL_BIND bind[2];
|
||||
memset(bind, 0, sizeof(bind));
|
||||
|
||||
/* STRING PARAM */
|
||||
bind[0].buffer_type = MYSQL_TYPE_STRING;
|
||||
bind[0].buffer = (char *)notesStr;
|
||||
bind[0].buffer_length = sizeof(notesStr);
|
||||
bind[0].is_null = 0;
|
||||
bind[0].length = ¬esLen;
|
||||
|
||||
bind[1].buffer_type= MYSQL_TYPE_LONG;
|
||||
bind[1].buffer= (char *)&id;
|
||||
bind[1].is_null= 0;
|
||||
bind[1].length= 0;
|
||||
|
||||
/* Bind the buffers */
|
||||
if ( mysql_stmt_bind_param(stmt, bind) ) {
|
||||
Error("Unable to bind sql '%s': %s", sql, mysql_stmt_error(stmt));
|
||||
}
|
||||
} // end if ! stmt
|
||||
|
||||
strncpy(notesStr, notes.c_str(), sizeof(notesStr));
|
||||
|
||||
if ( mysql_stmt_execute(stmt) ) {
|
||||
Error("Unable to execute sql '%s': %s", sql, mysql_stmt_error(stmt));
|
||||
}
|
||||
#else
|
||||
char sql[ZM_SQL_LGE_BUFSIZ];
|
||||
static char escapedNotes[ZM_SQL_MED_BUFSIZ];
|
||||
|
||||
mysql_real_escape_string(&dbconn, escapedNotes, notes.c_str(), notes.length());
|
||||
|
||||
snprintf(sql, sizeof(sql), "UPDATE `Events` SET `Notes` = '%s' WHERE `Id` = %" PRIu64, escapedNotes, id);
|
||||
std::string sql = stringtf("UPDATE `Events` SET `Notes` = '%s' WHERE `Id` = %" PRIu64,
|
||||
zmDbEscapeString(notes).c_str(), id);
|
||||
dbQueue.push(std::move(sql));
|
||||
#endif
|
||||
} // end if update
|
||||
} // void Event::updateNotes(const StringSetMap &newNoteSetMap)
|
||||
|
||||
|
@ -439,7 +380,7 @@ void Event::AddPacket(const std::shared_ptr<ZMPacket>&packet) {
|
|||
if ((packet->codec_type == AVMEDIA_TYPE_VIDEO) or packet->image) {
|
||||
AddFrame(packet->image, packet->timestamp, packet->zone_stats, packet->score, packet->analysis_image);
|
||||
}
|
||||
end_time = SystemTimePoint(zm::chrono::duration_cast<Microseconds>(packet->timestamp));
|
||||
end_time = packet->timestamp;
|
||||
}
|
||||
|
||||
void Event::WriteDbFrames() {
|
||||
|
@ -456,7 +397,7 @@ void Event::WriteDbFrames() {
|
|||
frame_insert_sql += stringtf("\n( %" PRIu64 ", %d, '%s', from_unixtime( %ld ), %.2f, %d ),",
|
||||
id, frame->frame_id,
|
||||
frame_type_names[frame->type],
|
||||
frame->timestamp.tv_sec,
|
||||
std::chrono::system_clock::to_time_t(frame->timestamp),
|
||||
std::chrono::duration_cast<FPSeconds>(frame->delta).count(),
|
||||
frame->score);
|
||||
if (config.record_event_stats and frame->zone_stats.size()) {
|
||||
|
@ -483,7 +424,7 @@ void Event::WriteDbFrames() {
|
|||
} // end while frames
|
||||
// The -1 is for the extra , added for values above
|
||||
frame_insert_sql.erase(frame_insert_sql.size()-1);
|
||||
//zmDbDo(frame_insert_sql.c_str());
|
||||
//zmDbDo(frame_insert_sql);
|
||||
dbQueue.push(std::move(frame_insert_sql));
|
||||
if (stats_insert_sql.size() > 208) {
|
||||
// The -1 is for the extra , added for values above
|
||||
|
@ -493,13 +434,12 @@ void Event::WriteDbFrames() {
|
|||
}
|
||||
} // end void Event::WriteDbFrames()
|
||||
|
||||
void Event::AddFrame(
|
||||
Image *image,
|
||||
struct timeval timestamp,
|
||||
const std::vector<ZoneStats> &zone_stats,
|
||||
int score,
|
||||
Image *alarm_image) {
|
||||
if (!timestamp.tv_sec) {
|
||||
void Event::AddFrame(Image *image,
|
||||
SystemTimePoint timestamp,
|
||||
const std::vector<ZoneStats> &zone_stats,
|
||||
int score,
|
||||
Image *alarm_image) {
|
||||
if (timestamp.time_since_epoch() == Seconds(0)) {
|
||||
Warning("Not adding new frame, zero timestamp");
|
||||
return;
|
||||
}
|
||||
|
@ -525,7 +465,7 @@ void Event::AddFrame(
|
|||
|
||||
if (image) {
|
||||
if (save_jpegs & 1) {
|
||||
std::string event_file = stringtf(staticConfig.capture_file_format, path.c_str(), frames);
|
||||
std::string event_file = stringtf(staticConfig.capture_file_format.c_str(), path.c_str(), frames);
|
||||
Debug(1, "Writing capture frame %d to %s", frames, event_file.c_str());
|
||||
if (!WriteFrameImage(image, timestamp, event_file.c_str())) {
|
||||
Error("Failed to write frame image");
|
||||
|
@ -555,7 +495,7 @@ void Event::AddFrame(
|
|||
}
|
||||
|
||||
if (alarm_image and (save_jpegs & 2)) {
|
||||
std::string event_file = stringtf(staticConfig.analyse_file_format, path.c_str(), frames);
|
||||
std::string event_file = stringtf(staticConfig.analyse_file_format.c_str(), path.c_str(), frames);
|
||||
Debug(1, "Writing analysis frame %d", frames);
|
||||
if (!WriteFrameImage(alarm_image, timestamp, event_file.c_str(), true)) {
|
||||
Error("Failed to write analysis frame image");
|
||||
|
@ -576,12 +516,10 @@ void Event::AddFrame(
|
|||
or ( monitor_state == Monitor::ALARM )
|
||||
or ( monitor_state == Monitor::PREALARM );
|
||||
|
||||
SystemTimePoint timestamp_us = SystemTimePoint(zm::chrono::duration_cast<Microseconds>(timestamp));
|
||||
|
||||
if (db_frame) {
|
||||
Microseconds delta_time = std::chrono::duration_cast<Microseconds>(timestamp_us - start_time);
|
||||
Microseconds delta_time = std::chrono::duration_cast<Microseconds>(timestamp - start_time);
|
||||
Debug(1, "Frame delta is %.2f s - %.2f s = %.2f s, score %u zone_stats.size %zu",
|
||||
FPSeconds(timestamp_us.time_since_epoch()).count(),
|
||||
FPSeconds(timestamp.time_since_epoch()).count(),
|
||||
FPSeconds(start_time.time_since_epoch()).count(),
|
||||
FPSeconds(delta_time).count(),
|
||||
score,
|
||||
|
@ -621,8 +559,8 @@ void Event::AddFrame(
|
|||
if (score > (int) max_score) {
|
||||
max_score = score;
|
||||
}
|
||||
end_time = timestamp_us;
|
||||
} // end void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image)
|
||||
end_time = timestamp;
|
||||
}
|
||||
|
||||
bool Event::SetPath(Storage *storage) {
|
||||
scheme = storage->Scheme();
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "zm_define.h"
|
||||
#include "zm_storage.h"
|
||||
#include "zm_time.h"
|
||||
#include "zm_utils.h"
|
||||
#include "zm_zone.h"
|
||||
|
||||
#include <map>
|
||||
|
@ -96,12 +97,10 @@ class Event {
|
|||
static bool OpenFrameSocket(int);
|
||||
static bool ValidateFrameSocket(int);
|
||||
|
||||
Event(
|
||||
Monitor *p_monitor,
|
||||
struct timeval p_start_time,
|
||||
const std::string &p_cause,
|
||||
const StringSetMap &p_noteSetMap
|
||||
);
|
||||
Event(Monitor *p_monitor,
|
||||
SystemTimePoint p_start_time,
|
||||
const std::string &p_cause,
|
||||
const StringSetMap &p_noteSetMap);
|
||||
~Event();
|
||||
|
||||
uint64_t Id() const { return id; }
|
||||
|
@ -109,46 +108,38 @@ class Event {
|
|||
int Frames() const { return frames; }
|
||||
int AlarmFrames() const { return alarm_frames; }
|
||||
|
||||
timeval StartTime() const { return zm::chrono::duration_cast<timeval>(start_time.time_since_epoch()); }
|
||||
timeval EndTime() const { return zm::chrono::duration_cast<timeval>(end_time.time_since_epoch()); }
|
||||
SystemTimePoint StartTime() const { return start_time; }
|
||||
SystemTimePoint EndTime() const { return end_time; }
|
||||
|
||||
void AddPacket(const std::shared_ptr<ZMPacket> &p);
|
||||
bool WritePacket(const std::shared_ptr<ZMPacket> &p);
|
||||
bool SendFrameImage(const Image *image, bool alarm_frame=false);
|
||||
bool WriteFrameImage(
|
||||
Image *image,
|
||||
struct timeval timestamp,
|
||||
const char *event_file,
|
||||
bool alarm_frame=false
|
||||
) const;
|
||||
bool WriteFrameImage(Image *image, SystemTimePoint timestamp, const char *event_file, bool alarm_frame = false) const;
|
||||
|
||||
void updateNotes(const StringSetMap &stringSetMap);
|
||||
|
||||
void AddFrame(
|
||||
Image *image,
|
||||
struct timeval timestamp,
|
||||
const std::vector<ZoneStats> &stats,
|
||||
int score=0,
|
||||
Image *alarm_image=nullptr
|
||||
);
|
||||
void AddFrame(Image *image,
|
||||
SystemTimePoint timestamp,
|
||||
const std::vector<ZoneStats> &stats,
|
||||
int score = 0,
|
||||
Image *alarm_image = nullptr);
|
||||
|
||||
private:
|
||||
void WriteDbFrames();
|
||||
bool SetPath(Storage *storage);
|
||||
|
||||
public:
|
||||
static const char *getSubPath(tm time) {
|
||||
static char subpath[PATH_MAX] = "";
|
||||
snprintf(subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
time.tm_year-100, time.tm_mon+1, time.tm_mday,
|
||||
time.tm_hour, time.tm_min, time.tm_sec);
|
||||
return subpath;
|
||||
}
|
||||
static const char *getSubPath(time_t *time) {
|
||||
tm time_tm = {};
|
||||
localtime_r(time, &time_tm);
|
||||
return Event::getSubPath(time_tm);
|
||||
}
|
||||
static std::string getSubPath(tm time) {
|
||||
std::string subpath = stringtf("%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
time.tm_year - 100, time.tm_mon + 1, time.tm_mday,
|
||||
time.tm_hour, time.tm_min, time.tm_sec);
|
||||
return subpath;
|
||||
}
|
||||
static std::string getSubPath(time_t *time) {
|
||||
tm time_tm = {};
|
||||
localtime_r(time, &time_tm);
|
||||
return Event::getSubPath(time_tm);
|
||||
}
|
||||
|
||||
const char* getEventFile() const {
|
||||
return video_file.c_str();
|
||||
|
@ -174,7 +165,7 @@ class Event {
|
|||
}
|
||||
static void AddPreAlarmFrame(
|
||||
Image *image,
|
||||
struct timeval timestamp,
|
||||
SystemTimePoint timestamp,
|
||||
int score=0,
|
||||
Image *alarm_frame=nullptr
|
||||
) {
|
||||
|
|
|
@ -44,9 +44,9 @@ constexpr Milliseconds EventStream::STREAM_PAUSE_WAIT;
|
|||
bool EventStream::loadInitialEventData(int monitor_id, SystemTimePoint event_time) {
|
||||
std::string sql = stringtf("SELECT `Id` FROM `Events` WHERE "
|
||||
"`MonitorId` = %d AND unix_timestamp(`EndDateTime`) > %ld "
|
||||
"ORDER BY `Id` ASC LIMIT 1", monitor_id, event_time);
|
||||
"ORDER BY `Id` ASC LIMIT 1", monitor_id, std::chrono::system_clock::to_time_t(event_time));
|
||||
|
||||
MYSQL_RES *result = zmDbFetch(sql.c_str());
|
||||
MYSQL_RES *result = zmDbFetch(sql);
|
||||
if (!result)
|
||||
exit(-1);
|
||||
|
||||
|
@ -117,7 +117,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS FramesDuration, "
|
||||
"`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id);
|
||||
|
||||
MYSQL_RES *result = zmDbFetch(sql.c_str());
|
||||
MYSQL_RES *result = zmDbFetch(sql);
|
||||
if (!result) {
|
||||
exit(-1);
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
event_data->duration = std::chrono::duration_cast<Microseconds>(event_data->end_time - event_data->start_time);
|
||||
event_data->frames_duration =
|
||||
std::chrono::duration_cast<Microseconds>(dbrow[5] ? FPSeconds(atof(dbrow[5])) : FPSeconds(0.0));
|
||||
strncpy(event_data->video_file, dbrow[6], sizeof(event_data->video_file) - 1);
|
||||
event_data->video_file = std::string(dbrow[6]);
|
||||
std::string scheme_str = std::string(dbrow[7]);
|
||||
if ( scheme_str == "Deep" ) {
|
||||
event_data->scheme = Storage::DEEP;
|
||||
|
@ -180,44 +180,41 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
time_t start_time_t = std::chrono::system_clock::to_time_t(event_data->start_time);
|
||||
localtime_r(&start_time_t, &event_time);
|
||||
|
||||
if ( storage_path[0] == '/' )
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
"%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
storage_path, event_data->monitor_id,
|
||||
event_time.tm_year-100, event_time.tm_mon+1, event_time.tm_mday,
|
||||
event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
|
||||
else
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
"%s/%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
||||
event_time.tm_year-100, event_time.tm_mon+1, event_time.tm_mday,
|
||||
event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
|
||||
} else if ( event_data->scheme == Storage::MEDIUM ) {
|
||||
if (storage_path[0] == '/') {
|
||||
event_data->path = stringtf("%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
storage_path, event_data->monitor_id,
|
||||
event_time.tm_year - 100, event_time.tm_mon + 1, event_time.tm_mday,
|
||||
event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
|
||||
} else {
|
||||
event_data->path = stringtf("%s/%s/%u/%02d/%02d/%02d/%02d/%02d/%02d",
|
||||
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
||||
event_time.tm_year - 100, event_time.tm_mon + 1, event_time.tm_mday,
|
||||
event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
|
||||
}
|
||||
} else if (event_data->scheme == Storage::MEDIUM) {
|
||||
tm event_time = {};
|
||||
time_t start_time_t = std::chrono::system_clock::to_time_t(event_data->start_time);
|
||||
localtime_r(&start_time_t, &event_time);
|
||||
|
||||
if ( storage_path[0] == '/' )
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
"%s/%u/%04d-%02d-%02d/%" PRIu64,
|
||||
storage_path, event_data->monitor_id,
|
||||
event_time.tm_year+1900, event_time.tm_mon+1, event_time.tm_mday,
|
||||
event_data->event_id);
|
||||
else
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
"%s/%s/%u/%04d-%02d-%02d/%" PRIu64,
|
||||
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
||||
event_time.tm_year+1900, event_time.tm_mon+1, event_time.tm_mday,
|
||||
event_data->event_id);
|
||||
|
||||
if (storage_path[0] == '/') {
|
||||
event_data->path = stringtf("%s/%u/%04d-%02d-%02d/%" PRIu64,
|
||||
storage_path, event_data->monitor_id,
|
||||
event_time.tm_year + 1900, event_time.tm_mon + 1, event_time.tm_mday,
|
||||
event_data->event_id);
|
||||
} else {
|
||||
event_data->path = stringtf("%s/%s/%u/%04d-%02d-%02d/%" PRIu64,
|
||||
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
||||
event_time.tm_year + 1900, event_time.tm_mon + 1, event_time.tm_mday,
|
||||
event_data->event_id);
|
||||
}
|
||||
} else {
|
||||
if ( storage_path[0] == '/' )
|
||||
snprintf(event_data->path, sizeof(event_data->path), "%s/%u/%" PRIu64,
|
||||
storage_path, event_data->monitor_id, event_data->event_id);
|
||||
else
|
||||
snprintf(event_data->path, sizeof(event_data->path), "%s/%s/%u/%" PRIu64,
|
||||
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
||||
event_data->event_id);
|
||||
if (storage_path[0] == '/') {
|
||||
event_data->path = stringtf("%s/%u/%" PRIu64, storage_path, event_data->monitor_id, event_data->event_id);
|
||||
} else {
|
||||
event_data->path = stringtf("%s/%s/%u/%" PRIu64,
|
||||
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
||||
event_data->event_id);
|
||||
}
|
||||
}
|
||||
|
||||
double fps = 1.0;
|
||||
|
@ -229,7 +226,7 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
sql = stringtf("SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` "
|
||||
"FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id);
|
||||
|
||||
result = zmDbFetch(sql.c_str());
|
||||
result = zmDbFetch(sql);
|
||||
if (!result) {
|
||||
exit(-1);
|
||||
}
|
||||
|
@ -289,17 +286,17 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
}
|
||||
mysql_free_result(result);
|
||||
|
||||
if ( event_data->video_file[0] || (monitor->GetOptVideoWriter() > 0) ) {
|
||||
if ( !event_data->video_file[0] ) {
|
||||
snprintf(event_data->video_file, sizeof(event_data->video_file), "%" PRIu64 "-%s", event_data->event_id, "video.mp4");
|
||||
if (!event_data->video_file.empty() || (monitor->GetOptVideoWriter() > 0)) {
|
||||
if (event_data->video_file.empty()) {
|
||||
event_data->video_file = stringtf("%" PRIu64 "-%s", event_data->event_id, "video.mp4");
|
||||
}
|
||||
std::string filepath = std::string(event_data->path) + "/" + std::string(event_data->video_file);
|
||||
|
||||
std::string filepath = event_data->path + "/" + event_data->video_file;
|
||||
Debug(1, "Loading video file from %s", filepath.c_str());
|
||||
if ( ffmpeg_input )
|
||||
delete ffmpeg_input;
|
||||
delete ffmpeg_input;
|
||||
|
||||
ffmpeg_input = new FFmpeg_Input();
|
||||
if ( 0 > ffmpeg_input->Open(filepath.c_str()) ) {
|
||||
if (ffmpeg_input->Open(filepath.c_str()) < 0) {
|
||||
Warning("Unable to open ffmpeg_input %s", filepath.c_str());
|
||||
delete ffmpeg_input;
|
||||
ffmpeg_input = nullptr;
|
||||
|
@ -640,7 +637,7 @@ bool EventStream::checkEventLoaded() {
|
|||
if ( forceEventChange || ( (mode != MODE_SINGLE) && (mode != MODE_NONE) ) ) {
|
||||
Debug(1, "Checking for next event %s", sql.c_str());
|
||||
|
||||
MYSQL_RES *result = zmDbFetch(sql.c_str());
|
||||
MYSQL_RES *result = zmDbFetch(sql);
|
||||
if (!result) {
|
||||
exit(-1);
|
||||
}
|
||||
|
@ -690,32 +687,30 @@ bool EventStream::checkEventLoaded() {
|
|||
} // void EventStream::checkEventLoaded()
|
||||
|
||||
Image * EventStream::getImage( ) {
|
||||
static char filepath[PATH_MAX];
|
||||
|
||||
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
|
||||
Debug(2, "EventStream::getImage path(%s) from %s frame(%ld) ", filepath, event_data->path, curr_frame_id);
|
||||
Image *image = new Image(filepath);
|
||||
std::string path = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||
Debug(2, "EventStream::getImage path(%s) from %s frame(%ld) ", path.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||
Image *image = new Image(path.c_str());
|
||||
return image;
|
||||
}
|
||||
|
||||
bool EventStream::sendFrame(Microseconds delta_us) {
|
||||
Debug(2, "Sending frame %ld", curr_frame_id);
|
||||
|
||||
static char filepath[PATH_MAX];
|
||||
static struct stat filestat;
|
||||
std::string filepath;
|
||||
struct stat filestat = {};
|
||||
|
||||
// This needs to be abstracted. If we are saving jpgs, then load the capture file.
|
||||
// If we are only saving analysis frames, then send that.
|
||||
if ( event_data->SaveJPEGs & 1 ) {
|
||||
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
|
||||
} else if ( event_data->SaveJPEGs & 2 ) {
|
||||
snprintf(filepath, sizeof(filepath), staticConfig.analyse_file_format, event_data->path, curr_frame_id);
|
||||
if ( stat(filepath, &filestat) < 0 ) {
|
||||
Debug(1, "analyze file %s not found will try to stream from other", filepath);
|
||||
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
|
||||
if ( stat(filepath, &filestat) < 0 ) {
|
||||
Debug(1, "capture file %s not found either", filepath);
|
||||
filepath[0] = 0;
|
||||
if (event_data->SaveJPEGs & 1) {
|
||||
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||
} else if (event_data->SaveJPEGs & 2) {
|
||||
filepath = stringtf(staticConfig.analyse_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||
if (stat(filepath.c_str(), &filestat) < 0) {
|
||||
Debug(1, "analyze file %s not found will try to stream from other", filepath.c_str());
|
||||
filepath = stringtf(staticConfig.capture_file_format.c_str(), event_data->path.c_str(), curr_frame_id);
|
||||
if (stat(filepath.c_str(), &filestat) < 0) {
|
||||
Debug(1, "capture file %s not found either", filepath.c_str());
|
||||
filepath = "";
|
||||
}
|
||||
}
|
||||
} else if ( !ffmpeg_input ) {
|
||||
|
@ -724,7 +719,7 @@ bool EventStream::sendFrame(Microseconds delta_us) {
|
|||
}
|
||||
|
||||
if ( type == STREAM_MPEG ) {
|
||||
Image image(filepath);
|
||||
Image image(filepath.c_str());
|
||||
|
||||
Image *send_image = prepareImage(&image);
|
||||
|
||||
|
@ -739,20 +734,20 @@ bool EventStream::sendFrame(Microseconds delta_us) {
|
|||
config.mpeg_timed_frames,
|
||||
delta_us.count() * 1000);
|
||||
} else {
|
||||
bool send_raw = (type == STREAM_JPEG) && ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)) && filepath[0];
|
||||
bool send_raw = (type == STREAM_JPEG) && ((scale >= ZM_SCALE_BASE) && (zoom == ZM_SCALE_BASE)) && !filepath.empty();
|
||||
|
||||
fprintf(stdout, "--" BOUNDARY "\r\n");
|
||||
|
||||
if ( send_raw ) {
|
||||
if ( !send_file(filepath) ) {
|
||||
Error("Can't send %s: %s", filepath, strerror(errno));
|
||||
if (send_raw) {
|
||||
if (!send_file(filepath)) {
|
||||
Error("Can't send %s: %s", filepath.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Image *image = nullptr;
|
||||
|
||||
if ( filepath[0] ) {
|
||||
image = new Image(filepath);
|
||||
if (!filepath.empty()) {
|
||||
image = new Image(filepath.c_str());
|
||||
} else if ( ffmpeg_input ) {
|
||||
// Get the frame from the mp4 input
|
||||
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
||||
|
@ -1088,24 +1083,24 @@ void EventStream::runStream() {
|
|||
closeComms();
|
||||
} // end void EventStream::runStream()
|
||||
|
||||
bool EventStream::send_file(const char *filepath) {
|
||||
bool EventStream::send_file(const std::string &filepath) {
|
||||
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||
|
||||
int img_buffer_size = 0;
|
||||
uint8_t *img_buffer = temp_img_buffer;
|
||||
|
||||
FILE *fdj = nullptr;
|
||||
fdj = fopen(filepath, "rb");
|
||||
fdj = fopen(filepath.c_str(), "rb");
|
||||
if ( !fdj ) {
|
||||
Error("Can't open %s: %s", filepath, strerror(errno));
|
||||
std::string error_message = stringtf("Can't open %s: %s", filepath, strerror(errno));
|
||||
Error("Can't open %s: %s", filepath.c_str(), strerror(errno));
|
||||
std::string error_message = stringtf("Can't open %s: %s", filepath.c_str(), strerror(errno));
|
||||
return sendTextFrame(error_message.c_str());
|
||||
}
|
||||
#if HAVE_SENDFILE
|
||||
static struct stat filestat;
|
||||
if ( fstat(fileno(fdj), &filestat) < 0 ) {
|
||||
fclose(fdj); /* Close the file handle */
|
||||
Error("Failed getting information about file %s: %s", filepath, strerror(errno));
|
||||
Error("Failed getting information about file %s: %s", filepath.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
if ( !filestat.st_size ) {
|
||||
|
@ -1134,7 +1129,7 @@ bool EventStream::send_file(const char *filepath) {
|
|||
}
|
||||
|
||||
return send_buffer(img_buffer, img_buffer_size);
|
||||
} // end bool EventStream::send_file(const char * filepath)
|
||||
}
|
||||
|
||||
bool EventStream::send_buffer(uint8_t* buffer, int size) {
|
||||
if ( 0 > fprintf(stdout, "Content-Length: %d\r\n\r\n", size) ) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -27,16 +27,10 @@
|
|||
|
||||
extern "C" {
|
||||
#include <libavutil/time.h>
|
||||
#if HAVE_LIBAVUTIL_HWCONTEXT_H
|
||||
#include <libavutil/hwcontext.h>
|
||||
#endif
|
||||
|
||||
#include <libavutil/pixdesc.h>
|
||||
}
|
||||
|
||||
#include <string>
|
||||
TimePoint start_read_time;
|
||||
|
||||
time_t start_read_time;
|
||||
#if HAVE_LIBAVUTIL_HWCONTEXT_H
|
||||
#if LIBAVCODEC_VERSION_CHECK(57, 89, 0, 89, 0)
|
||||
static enum AVPixelFormat hw_pix_fmt;
|
||||
|
@ -61,31 +55,26 @@ static enum AVPixelFormat get_hw_format(
|
|||
}
|
||||
#if !LIBAVUTIL_VERSION_CHECK(56, 22, 0, 14, 0)
|
||||
static enum AVPixelFormat find_fmt_by_hw_type(const enum AVHWDeviceType type) {
|
||||
enum AVPixelFormat fmt;
|
||||
switch (type) {
|
||||
case AV_HWDEVICE_TYPE_VAAPI:
|
||||
fmt = AV_PIX_FMT_VAAPI;
|
||||
break;
|
||||
return AV_PIX_FMT_VAAPI;
|
||||
case AV_HWDEVICE_TYPE_DXVA2:
|
||||
fmt = AV_PIX_FMT_DXVA2_VLD;
|
||||
break;
|
||||
return AV_PIX_FMT_DXVA2_VLD;
|
||||
case AV_HWDEVICE_TYPE_D3D11VA:
|
||||
fmt = AV_PIX_FMT_D3D11;
|
||||
break;
|
||||
return AV_PIX_FMT_D3D11;
|
||||
case AV_HWDEVICE_TYPE_VDPAU:
|
||||
fmt = AV_PIX_FMT_VDPAU;
|
||||
break;
|
||||
return AV_PIX_FMT_VDPAU;
|
||||
case AV_HWDEVICE_TYPE_CUDA:
|
||||
fmt = AV_PIX_FMT_CUDA;
|
||||
break;
|
||||
return AV_PIX_FMT_CUDA;
|
||||
#ifdef AV_HWDEVICE_TYPE_MMAL
|
||||
case AV_HWDEVICE_TYPE_MMAL:
|
||||
return AV_PIX_FMT_MMAL;
|
||||
#endif
|
||||
case AV_HWDEVICE_TYPE_VIDEOTOOLBOX:
|
||||
fmt = AV_PIX_FMT_VIDEOTOOLBOX;
|
||||
break;
|
||||
return AV_PIX_FMT_VIDEOTOOLBOX;
|
||||
default:
|
||||
fmt = AV_PIX_FMT_NONE;
|
||||
break;
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
@ -169,7 +158,7 @@ FfmpegCamera::~FfmpegCamera() {
|
|||
}
|
||||
|
||||
int FfmpegCamera::PrimeCapture() {
|
||||
start_read_time = time(nullptr);
|
||||
start_read_time = std::chrono::steady_clock::now();
|
||||
if ( mCanCapture ) {
|
||||
Debug(1, "Priming capture from %s, Closing", mPath.c_str());
|
||||
Close();
|
||||
|
@ -188,7 +177,7 @@ int FfmpegCamera::PreCapture() {
|
|||
int FfmpegCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
||||
if (!mCanCapture) return -1;
|
||||
|
||||
start_read_time = time(nullptr);
|
||||
start_read_time = std::chrono::steady_clock::now();
|
||||
int ret;
|
||||
AVFormatContext *formatContextPtr;
|
||||
|
||||
|
@ -237,8 +226,8 @@ int FfmpegCamera::Capture(std::shared_ptr<ZMPacket> &zm_packet) {
|
|||
zm_packet->set_packet(&packet);
|
||||
zm_packet->stream = stream;
|
||||
zm_packet->pts = av_rescale_q(packet.pts, stream->time_base, AV_TIME_BASE_Q);
|
||||
if ( packet.pts != AV_NOPTS_VALUE ) {
|
||||
if ( stream == mVideoStream ) {
|
||||
if (packet.pts != AV_NOPTS_VALUE) {
|
||||
if (stream == mVideoStream) {
|
||||
if (mFirstVideoPTS == AV_NOPTS_VALUE)
|
||||
mFirstVideoPTS = packet.pts;
|
||||
|
||||
|
@ -558,11 +547,12 @@ int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
|
|||
Debug(1, "Received terminate in cb");
|
||||
return zm_terminate;
|
||||
}
|
||||
time_t now = time(nullptr);
|
||||
if (now - start_read_time > 10) {
|
||||
Debug(1, "timeout in ffmpeg camera now %" PRIi64 " - %" PRIi64 " > 10",
|
||||
static_cast<int64>(now),
|
||||
static_cast<int64>(start_read_time));
|
||||
|
||||
TimePoint now = std::chrono::steady_clock::now();
|
||||
if (now - start_read_time > Seconds(10)) {
|
||||
Debug(1, "timeout in ffmpeg camera now %" PRIi64 " - %" PRIi64 " > 10 s",
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(now.time_since_epoch()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(start_read_time.time_since_epoch()).count()));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -29,32 +29,26 @@
|
|||
#define RAW_BUFFER 512
|
||||
#define PIPE_SIZE 1024*1024
|
||||
|
||||
void Fifo::file_create_if_missing(
|
||||
const char * path,
|
||||
bool is_fifo,
|
||||
bool delete_fake_fifo
|
||||
) {
|
||||
static struct stat st;
|
||||
if ( stat(path, &st) == 0 ) {
|
||||
if ( (!is_fifo) || S_ISFIFO(st.st_mode) || !delete_fake_fifo )
|
||||
void Fifo::file_create_if_missing(const std::string &path, bool is_fifo, bool delete_fake_fifo) {
|
||||
struct stat st = {};
|
||||
|
||||
if (stat(path.c_str(), &st) == 0) {
|
||||
if ((!is_fifo) || S_ISFIFO(st.st_mode) || !delete_fake_fifo)
|
||||
return;
|
||||
Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path);
|
||||
unlink(path);
|
||||
Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path.c_str());
|
||||
unlink(path.c_str());
|
||||
}
|
||||
if (!is_fifo) {
|
||||
Debug(5, "Creating non fifo file as requested: %s", path);
|
||||
int fd = ::open(path, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
|
||||
Debug(5, "Creating non fifo file as requested: %s", path.c_str());
|
||||
int fd = ::open(path.c_str(), O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
::close(fd);
|
||||
return;
|
||||
}
|
||||
Debug(5, "Making fifo file of: %s", path);
|
||||
mkfifo(path, S_IRUSR|S_IWUSR);
|
||||
Debug(5, "Making fifo file of: %s", path.c_str());
|
||||
mkfifo(path.c_str(), S_IRUSR | S_IWUSR);
|
||||
}
|
||||
|
||||
void Fifo::fifo_create_if_missing(
|
||||
const char * path,
|
||||
bool delete_fake_fifo
|
||||
) {
|
||||
void Fifo::fifo_create_if_missing(const std::string &path, bool delete_fake_fifo) {
|
||||
file_create_if_missing(path, true, delete_fake_fifo);
|
||||
}
|
||||
|
||||
|
@ -62,7 +56,8 @@ Fifo::~Fifo() {
|
|||
close();
|
||||
}
|
||||
bool Fifo::open() {
|
||||
fifo_create_if_missing(path.c_str());
|
||||
fifo_create_if_missing(path);
|
||||
|
||||
if (!on_blocking_abort) {
|
||||
if ( (outfile = fopen(path.c_str(), "wb")) == nullptr ) {
|
||||
Error("Can't open %s for writing: %s", path.c_str(), strerror(errno));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -29,9 +29,9 @@
|
|||
#define RAW_BUFFER 512
|
||||
bool FifoStream::sendRAWFrames() {
|
||||
static unsigned char buffer[RAW_BUFFER];
|
||||
int fd = open(stream_path, O_RDONLY);
|
||||
int fd = open(stream_path.c_str(), O_RDONLY);
|
||||
if ( fd < 0 ) {
|
||||
Error("Can't open %s: %s", stream_path, strerror(errno));
|
||||
Error("Can't open %s: %s", stream_path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
while ( (bytes_read = read(fd, buffer, RAW_BUFFER)) ) {
|
||||
|
@ -56,9 +56,9 @@ bool FifoStream::sendRAWFrames() {
|
|||
|
||||
bool FifoStream::sendMJEGFrames() {
|
||||
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
|
||||
int fd = open(stream_path, O_RDONLY);
|
||||
int fd = open(stream_path.c_str(), O_RDONLY);
|
||||
if ( fd < 0 ) {
|
||||
Error("Can't open %s: %s", stream_path, strerror(errno));
|
||||
Error("Can't open %s: %s", stream_path.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
total_read = 0;
|
||||
|
@ -97,28 +97,26 @@ bool FifoStream::sendMJEGFrames() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void FifoStream::setStreamStart(const char * path) {
|
||||
stream_path = strdup(path);
|
||||
void FifoStream::setStreamStart(const std::string &path) {
|
||||
stream_path = path;
|
||||
}
|
||||
|
||||
void FifoStream::setStreamStart(int monitor_id, const char * format) {
|
||||
char diag_path[PATH_MAX];
|
||||
void FifoStream::setStreamStart(int monitor_id, const char *format) {
|
||||
std::string diag_path;
|
||||
std::shared_ptr<Monitor> monitor = Monitor::Load(monitor_id, false, Monitor::QUERY);
|
||||
|
||||
if ( !strcmp(format, "reference") ) {
|
||||
snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-r-%u.jpg",
|
||||
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
if (!strcmp(format, "reference")) {
|
||||
diag_path = stringtf("%s/diagpipe-r-%u.jpg", staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
stream_type = MJPEG;
|
||||
} else if ( !strcmp(format, "delta") ) {
|
||||
snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-d-%u.jpg",
|
||||
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
} else if (!strcmp(format, "delta")) {
|
||||
diag_path = stringtf("%s/diagpipe-d-%u.jpg", staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
stream_type = MJPEG;
|
||||
} else {
|
||||
if ( strcmp(format, "raw") ) {
|
||||
if (strcmp(format, "raw")) {
|
||||
Warning("Unknown or unspecified format. Defaulting to raw");
|
||||
}
|
||||
snprintf(diag_path, sizeof(diag_path), "%s/dbgpipe-%u.log",
|
||||
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
|
||||
diag_path = stringtf("%s/dbgpipe-%u.log", staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
||||
stream_type = RAW;
|
||||
}
|
||||
|
||||
|
@ -126,46 +124,48 @@ void FifoStream::setStreamStart(int monitor_id, const char * format) {
|
|||
}
|
||||
|
||||
void FifoStream::runStream() {
|
||||
if ( stream_type == MJPEG ) {
|
||||
if (stream_type == MJPEG) {
|
||||
fprintf(stdout, "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n\r\n");
|
||||
} else {
|
||||
fprintf(stdout, "Content-Type: text/html\r\n\r\n");
|
||||
}
|
||||
|
||||
/* only 1 person can read from a fifo at a time, so use a lock */
|
||||
char lock_file[PATH_MAX];
|
||||
snprintf(lock_file, sizeof(lock_file), "%s.rlock", stream_path);
|
||||
std::string lock_file = stringtf("%s.rlock", stream_path.c_str());
|
||||
Fifo::file_create_if_missing(lock_file, false);
|
||||
Debug(1, "Locking %s", lock_file);
|
||||
Debug(1, "Locking %s", lock_file.c_str());
|
||||
|
||||
int fd_lock = open(lock_file, O_RDONLY);
|
||||
if ( fd_lock < 0 ) {
|
||||
Error("Can't open %s: %s", lock_file, strerror(errno));
|
||||
int fd_lock = open(lock_file.c_str(), O_RDONLY);
|
||||
if (fd_lock < 0) {
|
||||
Error("Can't open %s: %s", lock_file.c_str(), strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
int res = flock(fd_lock, LOCK_EX | LOCK_NB);
|
||||
while ( (res < 0 and errno == EAGAIN) and (! zm_terminate) ) {
|
||||
Warning("Flocking problem on %s: - %s", lock_file, strerror(errno));
|
||||
while ((res < 0 and errno == EAGAIN) and (!zm_terminate)) {
|
||||
Warning("Flocking problem on %s: - %s", lock_file.c_str(), strerror(errno));
|
||||
sleep(1);
|
||||
res = flock(fd_lock, LOCK_EX | LOCK_NB);
|
||||
}
|
||||
if ( res < 0 ) {
|
||||
Error("Flocking problem on %d != %d %s: - %s", EAGAIN, res, lock_file, strerror(errno));
|
||||
|
||||
if (res < 0) {
|
||||
Error("Flocking problem on %d != %d %s: - %s", EAGAIN, res, lock_file.c_str(), strerror(errno));
|
||||
close(fd_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
while ( !zm_terminate ) {
|
||||
while (!zm_terminate) {
|
||||
now = std::chrono::system_clock::now();
|
||||
checkCommandQueue();
|
||||
|
||||
if ( stream_type == MJPEG ) {
|
||||
if ( !sendMJEGFrames() )
|
||||
if (stream_type == MJPEG) {
|
||||
if (!sendMJEGFrames())
|
||||
zm_terminate = true;
|
||||
} else {
|
||||
if ( !sendRAWFrames() )
|
||||
if (!sendRAWFrames())
|
||||
zm_terminate = true;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd_lock);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
#include "zm_packet.h"
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
FileCamera::FileCamera(
|
||||
const Monitor *monitor,
|
||||
|
@ -49,20 +48,20 @@ FileCamera::FileCamera(
|
|||
p_capture,
|
||||
p_record_audio)
|
||||
{
|
||||
strncpy( path, p_path, sizeof(path)-1 );
|
||||
if ( capture ) {
|
||||
path = std::string(p_path);
|
||||
if (capture) {
|
||||
Initialise();
|
||||
}
|
||||
}
|
||||
|
||||
FileCamera::~FileCamera() {
|
||||
if ( capture ) {
|
||||
if (capture) {
|
||||
Terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void FileCamera::Initialise() {
|
||||
if ( !path[0] ) {
|
||||
if (path.empty()) {
|
||||
Fatal("No path specified for file image");
|
||||
}
|
||||
}
|
||||
|
@ -71,9 +70,9 @@ void FileCamera::Terminate() {
|
|||
}
|
||||
|
||||
int FileCamera::PreCapture() {
|
||||
struct stat statbuf;
|
||||
if ( stat(path, &statbuf) < 0 ) {
|
||||
Error("Can't stat %s: %s", path, strerror(errno));
|
||||
struct stat statbuf = {};
|
||||
if (stat(path.c_str(), &statbuf) < 0) {
|
||||
Error("Can't stat %s: %s", path.c_str(), strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
bytes += statbuf.st_size;
|
||||
|
@ -81,8 +80,8 @@ int FileCamera::PreCapture() {
|
|||
// This waits until 1 second has passed since it was modified. Effectively limiting fps to 60.
|
||||
// Which is kinda bogus. If we were writing to this jpg constantly faster than we are monitoring it here
|
||||
// we would never break out of this loop
|
||||
while ( (time(nullptr) - statbuf.st_mtime) < 1 ) {
|
||||
usleep(100000);
|
||||
while ((time(nullptr) - statbuf.st_mtime) < 1) {
|
||||
std::this_thread::sleep_for(Milliseconds(100));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -27,26 +27,19 @@
|
|||
// accessed using a single file which contains the latest jpeg data
|
||||
//
|
||||
class FileCamera : public Camera {
|
||||
protected:
|
||||
char path[PATH_MAX];
|
||||
|
||||
public:
|
||||
FileCamera(
|
||||
const Monitor *monitor,
|
||||
const char *p_path,
|
||||
int p_width,
|
||||
int p_height,
|
||||
int p_colours,
|
||||
int p_brightness,
|
||||
int p_contrast,
|
||||
int p_hue,
|
||||
int p_colour,
|
||||
bool p_capture,
|
||||
bool p_record_audio
|
||||
);
|
||||
~FileCamera();
|
||||
|
||||
const char *Path() const { return path; }
|
||||
public:
|
||||
FileCamera(const Monitor *monitor,
|
||||
const char *p_path,
|
||||
int p_width,
|
||||
int p_height,
|
||||
int p_colours,
|
||||
int p_brightness,
|
||||
int p_contrast,
|
||||
int p_hue,
|
||||
int p_colour,
|
||||
bool p_capture,
|
||||
bool p_record_audio);
|
||||
~FileCamera() override;
|
||||
|
||||
void Initialise();
|
||||
void Terminate();
|
||||
|
@ -54,6 +47,11 @@ public:
|
|||
int Capture(std::shared_ptr<ZMPacket> &p) override;
|
||||
int PostCapture() override;
|
||||
int Close() override { return 0; };
|
||||
|
||||
const std::string &Path() const { return path; }
|
||||
|
||||
private:
|
||||
std::string path;
|
||||
};
|
||||
|
||||
#endif // ZM_FILE_CAMERA_H
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "zm_group.h"
|
||||
|
||||
#include "zm_logger.h"
|
||||
#include "zm_utils.h"
|
||||
#include <cstring>
|
||||
|
||||
Group::Group() {
|
||||
|
@ -39,27 +40,27 @@ Group::Group(const MYSQL_ROW &dbrow) {
|
|||
|
||||
/* If a zero or invalid p_id is passed, then the old default path will be assumed. */
|
||||
Group::Group(unsigned int p_id) {
|
||||
id = 0;
|
||||
id = 0;
|
||||
|
||||
if ( p_id ) {
|
||||
char sql[ZM_SQL_SML_BUFSIZ];
|
||||
snprintf(sql, sizeof(sql), "SELECT `Id`, `ParentId`, `Name` FROM `Group` WHERE `Id`=%u", p_id);
|
||||
Debug(2,"Loading Group for %u using %s", p_id, sql);
|
||||
zmDbRow dbrow;
|
||||
if ( !dbrow.fetch(sql) ) {
|
||||
Error("Unable to load group for id %u: %s", p_id, mysql_error(&dbconn));
|
||||
} else {
|
||||
unsigned int index = 0;
|
||||
id = atoi(dbrow[index++]);
|
||||
parent_id = dbrow[index] ? atoi(dbrow[index]): 0; index++;
|
||||
strncpy(name, dbrow[index++], sizeof(name)-1);
|
||||
Debug(1, "Loaded Group area %d '%s'", id, this->Name());
|
||||
}
|
||||
}
|
||||
if ( ! id ) {
|
||||
Debug(1,"No id passed to Group constructor.");
|
||||
strcpy(name, "Default");
|
||||
}
|
||||
if (p_id) {
|
||||
std::string sql = stringtf("SELECT `Id`, `ParentId`, `Name` FROM `Group` WHERE `Id`=%u", p_id);
|
||||
Debug(2, "Loading Group for %u using %s", p_id, sql.c_str());
|
||||
zmDbRow dbrow;
|
||||
if (!dbrow.fetch(sql)) {
|
||||
Error("Unable to load group for id %u: %s", p_id, mysql_error(&dbconn));
|
||||
} else {
|
||||
unsigned int index = 0;
|
||||
id = atoi(dbrow[index++]);
|
||||
parent_id = dbrow[index] ? atoi(dbrow[index]) : 0;
|
||||
index++;
|
||||
strncpy(name, dbrow[index++], sizeof(name) - 1);
|
||||
Debug(1, "Loaded Group area %d '%s'", id, this->Name());
|
||||
}
|
||||
}
|
||||
if (!id) {
|
||||
Debug(1, "No id passed to Group constructor.");
|
||||
strcpy(name, "Default");
|
||||
}
|
||||
}
|
||||
|
||||
Group::~Group() {
|
||||
|
|
391
src/zm_image.cpp
391
src/zm_image.cpp
|
@ -313,6 +313,7 @@ bool Image::Assign(const AVFrame *frame, SwsContext *convert_context, AVFrame *t
|
|||
return false;
|
||||
}
|
||||
zm_dump_video_frame(temp_frame, "dest frame after convert");
|
||||
update_function_pointers();
|
||||
return true;
|
||||
} // end Image::Assign(const AVFrame *frame, SwsContext *convert_context, AVFrame *temp_frame)
|
||||
|
||||
|
@ -687,6 +688,7 @@ void Image::AssignDirect(
|
|||
subpixelorder = p_subpixelorder;
|
||||
pixels = width * height;
|
||||
size = new_buffer_size;
|
||||
update_function_pointers();
|
||||
} // end void Image::AssignDirect
|
||||
|
||||
void Image::Assign(
|
||||
|
@ -788,6 +790,7 @@ void Image::Assign(const Image &image) {
|
|||
linesize = image.linesize;
|
||||
}
|
||||
|
||||
update_function_pointers();
|
||||
if ( image.buffer != buffer )
|
||||
(*fptr_imgbufcpy)(buffer, image.buffer, size);
|
||||
}
|
||||
|
@ -933,125 +936,123 @@ bool Image::WriteRaw(const char *filename) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Image::ReadJpeg(const char *filename, unsigned int p_colours, unsigned int p_subpixelorder) {
|
||||
bool Image::ReadJpeg(const std::string &filename, unsigned int p_colours, unsigned int p_subpixelorder) {
|
||||
unsigned int new_width, new_height, new_colours, new_subpixelorder;
|
||||
struct jpeg_decompress_struct *cinfo = readjpg_dcinfo;
|
||||
|
||||
if ( !cinfo ) {
|
||||
cinfo = readjpg_dcinfo = new jpeg_decompress_struct;
|
||||
cinfo->err = jpeg_std_error(&jpg_err.pub);
|
||||
if (!readjpg_dcinfo) {
|
||||
readjpg_dcinfo = new jpeg_decompress_struct;
|
||||
readjpg_dcinfo->err = jpeg_std_error(&jpg_err.pub);
|
||||
jpg_err.pub.error_exit = zm_jpeg_error_exit;
|
||||
jpg_err.pub.emit_message = zm_jpeg_emit_message;
|
||||
jpeg_create_decompress(cinfo);
|
||||
jpeg_create_decompress(readjpg_dcinfo);
|
||||
}
|
||||
|
||||
FILE *infile;
|
||||
if ( (infile = fopen(filename, "rb")) == nullptr ) {
|
||||
Error("Can't open %s: %s", filename, strerror(errno));
|
||||
if ((infile = fopen(filename.c_str(), "rb")) == nullptr) {
|
||||
Error("Can't open %s: %s", filename.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( setjmp(jpg_err.setjmp_buffer) ) {
|
||||
jpeg_abort_decompress(cinfo);
|
||||
if (setjmp(jpg_err.setjmp_buffer)) {
|
||||
jpeg_abort_decompress(readjpg_dcinfo);
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
jpeg_stdio_src(cinfo, infile);
|
||||
jpeg_stdio_src(readjpg_dcinfo, infile);
|
||||
|
||||
jpeg_read_header(cinfo, TRUE);
|
||||
jpeg_read_header(readjpg_dcinfo, true);
|
||||
|
||||
if ( (cinfo->num_components != 1) && (cinfo->num_components != 3) ) {
|
||||
if ((readjpg_dcinfo->num_components != 1) && (readjpg_dcinfo->num_components != 3)) {
|
||||
Error("Unexpected colours when reading jpeg image: %d", colours);
|
||||
jpeg_abort_decompress(cinfo);
|
||||
jpeg_abort_decompress(readjpg_dcinfo);
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if the image has at least one huffman table defined. If not, use the standard ones */
|
||||
/* This is required for the MJPEG capture palette of USB devices */
|
||||
if ( cinfo->dc_huff_tbl_ptrs[0] == nullptr ) {
|
||||
zm_use_std_huff_tables(cinfo);
|
||||
if (readjpg_dcinfo->dc_huff_tbl_ptrs[0] == nullptr) {
|
||||
zm_use_std_huff_tables(readjpg_dcinfo);
|
||||
}
|
||||
|
||||
new_width = cinfo->image_width;
|
||||
new_height = cinfo->image_height;
|
||||
new_width = readjpg_dcinfo->image_width;
|
||||
new_height = readjpg_dcinfo->image_height;
|
||||
|
||||
if ( (width != new_width) || (height != new_height) ) {
|
||||
if ((width != new_width) || (height != new_height)) {
|
||||
Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u", width, height, new_width, new_height);
|
||||
}
|
||||
|
||||
switch ( p_colours ) {
|
||||
switch (p_colours) {
|
||||
case ZM_COLOUR_GRAY8:
|
||||
cinfo->out_color_space = JCS_GRAYSCALE;
|
||||
new_colours = ZM_COLOUR_GRAY8;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
break;
|
||||
readjpg_dcinfo->out_color_space = JCS_GRAYSCALE;
|
||||
new_colours = ZM_COLOUR_GRAY8;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
break;
|
||||
case ZM_COLOUR_RGB32:
|
||||
#ifdef JCS_EXTENSIONS
|
||||
new_colours = ZM_COLOUR_RGB32;
|
||||
if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
|
||||
cinfo->out_color_space = JCS_EXT_BGRX;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA;
|
||||
} else if ( p_subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
|
||||
cinfo->out_color_space = JCS_EXT_XRGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB;
|
||||
} else if ( p_subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
|
||||
cinfo->out_color_space = JCS_EXT_XBGR;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
|
||||
} else {
|
||||
/* Assume RGBA */
|
||||
cinfo->out_color_space = JCS_EXT_RGBX;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
}
|
||||
break;
|
||||
new_colours = ZM_COLOUR_RGB32;
|
||||
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
||||
readjpg_dcinfo->out_color_space = JCS_EXT_BGRX;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA;
|
||||
} else if (p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
||||
readjpg_dcinfo->out_color_space = JCS_EXT_XRGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB;
|
||||
} else if (p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||
readjpg_dcinfo->out_color_space = JCS_EXT_XBGR;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
|
||||
} else {
|
||||
/* Assume RGBA */
|
||||
readjpg_dcinfo->out_color_space = JCS_EXT_RGBX;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead.");
|
||||
Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead.");
|
||||
#endif
|
||||
case ZM_COLOUR_RGB24:
|
||||
default:
|
||||
new_colours = ZM_COLOUR_RGB24;
|
||||
if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGR ) {
|
||||
new_colours = ZM_COLOUR_RGB24;
|
||||
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||
#ifdef JCS_EXTENSIONS
|
||||
cinfo->out_color_space = JCS_EXT_BGR;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_BGR;
|
||||
readjpg_dcinfo->out_color_space = JCS_EXT_BGR;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_BGR;
|
||||
#else
|
||||
Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead.");
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead.");
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
#endif
|
||||
} else {
|
||||
/* Assume RGB */
|
||||
/*
|
||||
} else {
|
||||
/* Assume RGB */
|
||||
/*
|
||||
#ifdef JCS_EXTENSIONS
|
||||
cinfo->out_color_space = JCS_EXT_RGB;
|
||||
#else
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
#endif
|
||||
*/
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
}
|
||||
break;
|
||||
*/
|
||||
readjpg_dcinfo->out_color_space = JCS_RGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
}
|
||||
break;
|
||||
} // end switch p_colours
|
||||
|
||||
if ( WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == nullptr ) {
|
||||
if (WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == nullptr) {
|
||||
Error("Failed requesting writeable buffer for reading JPEG image.");
|
||||
jpeg_abort_decompress(cinfo);
|
||||
jpeg_abort_decompress(readjpg_dcinfo);
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
jpeg_start_decompress(cinfo);
|
||||
jpeg_start_decompress(readjpg_dcinfo);
|
||||
|
||||
JSAMPROW row_pointer = buffer;
|
||||
while ( cinfo->output_scanline < cinfo->output_height ) {
|
||||
jpeg_read_scanlines(cinfo, &row_pointer, 1);
|
||||
while (readjpg_dcinfo->output_scanline < readjpg_dcinfo->output_height) {
|
||||
jpeg_read_scanlines(readjpg_dcinfo, &row_pointer, 1);
|
||||
row_pointer += linesize;
|
||||
}
|
||||
|
||||
jpeg_finish_decompress(cinfo);
|
||||
|
||||
jpeg_finish_decompress(readjpg_dcinfo);
|
||||
fclose(infile);
|
||||
|
||||
return true;
|
||||
|
@ -1060,71 +1061,74 @@ cinfo->out_color_space = JCS_RGB;
|
|||
// Multiple calling formats to permit inclusion (or not) of non blocking, quality_override and timestamp (exif), with suitable defaults.
|
||||
// Note quality=zero means default
|
||||
|
||||
bool Image::WriteJpeg(const char *filename, int quality_override) const {
|
||||
bool Image::WriteJpeg(const std::string &filename, int quality_override) const {
|
||||
return Image::WriteJpeg(filename, quality_override, {}, false);
|
||||
}
|
||||
bool Image::WriteJpeg(const char *filename) const {
|
||||
bool Image::WriteJpeg(const std::string &filename) const {
|
||||
return Image::WriteJpeg(filename, 0, {}, false);
|
||||
}
|
||||
bool Image::WriteJpeg(const char *filename, bool on_blocking_abort) const {
|
||||
bool Image::WriteJpeg(const std::string &filename, bool on_blocking_abort) const {
|
||||
return Image::WriteJpeg(filename, 0, {}, on_blocking_abort);
|
||||
}
|
||||
bool Image::WriteJpeg(const char *filename, SystemTimePoint timestamp) const {
|
||||
bool Image::WriteJpeg(const std::string &filename, SystemTimePoint timestamp) const {
|
||||
return Image::WriteJpeg(filename, 0, timestamp, false);
|
||||
}
|
||||
|
||||
bool Image::WriteJpeg(const char *filename, int quality_override, SystemTimePoint timestamp) const {
|
||||
bool Image::WriteJpeg(const std::string &filename, int quality_override, SystemTimePoint timestamp) const {
|
||||
return Image::WriteJpeg(filename, quality_override, timestamp, false);
|
||||
}
|
||||
|
||||
bool Image::WriteJpeg(const char *filename,
|
||||
int quality_override,
|
||||
bool Image::WriteJpeg(const std::string &filename,
|
||||
const int &quality_override,
|
||||
SystemTimePoint timestamp,
|
||||
bool on_blocking_abort) const {
|
||||
if ( config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8) ) {
|
||||
if (config.colour_jpeg_files && (colours == ZM_COLOUR_GRAY8)) {
|
||||
Image temp_image(*this);
|
||||
temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
|
||||
return temp_image.WriteJpeg(filename, quality_override, timestamp, on_blocking_abort);
|
||||
}
|
||||
int quality = quality_override ? quality_override : config.jpeg_file_quality;
|
||||
|
||||
struct jpeg_compress_struct *cinfo = writejpg_ccinfo[quality];
|
||||
jpeg_compress_struct *cinfo = writejpg_ccinfo[quality];
|
||||
FILE *outfile = nullptr;
|
||||
int raw_fd = 0;
|
||||
|
||||
if ( !cinfo ) {
|
||||
if (!cinfo) {
|
||||
cinfo = writejpg_ccinfo[quality] = new jpeg_compress_struct;
|
||||
cinfo->err = jpeg_std_error(&jpg_err.pub);
|
||||
jpeg_create_compress(cinfo);
|
||||
}
|
||||
if ( !on_blocking_abort ) {
|
||||
if (!on_blocking_abort) {
|
||||
jpg_err.pub.error_exit = zm_jpeg_error_exit;
|
||||
jpg_err.pub.emit_message = zm_jpeg_emit_message;
|
||||
} else {
|
||||
jpg_err.pub.error_exit = zm_jpeg_error_silent;
|
||||
jpg_err.pub.emit_message = zm_jpeg_emit_silence;
|
||||
if ( setjmp(jpg_err.setjmp_buffer) ) {
|
||||
if (setjmp(jpg_err.setjmp_buffer)) {
|
||||
jpeg_abort_compress(cinfo);
|
||||
Debug(1, "Aborted a write mid-stream and %s and %d", (outfile == nullptr) ? "closing file" : "file not opened", raw_fd);
|
||||
if ( raw_fd )
|
||||
Debug(1,
|
||||
"Aborted a write mid-stream and %s and %d",
|
||||
(outfile == nullptr) ? "closing file" : "file not opened",
|
||||
raw_fd);
|
||||
if (raw_fd)
|
||||
close(raw_fd);
|
||||
if ( outfile )
|
||||
if (outfile)
|
||||
fclose(outfile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !on_blocking_abort ) {
|
||||
if ( (outfile = fopen(filename, "wb")) == nullptr ) {
|
||||
Error("Can't open %s for writing: %s", filename, strerror(errno));
|
||||
if (!on_blocking_abort) {
|
||||
if ((outfile = fopen(filename.c_str(), "wb")) == nullptr) {
|
||||
Error("Can't open %s for writing: %s", filename.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
raw_fd = open(filename, O_WRONLY|O_NONBLOCK|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
|
||||
if ( raw_fd < 0 )
|
||||
raw_fd = open(filename.c_str(), O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (raw_fd < 0)
|
||||
return false;
|
||||
outfile = fdopen(raw_fd, "wb");
|
||||
if ( outfile == nullptr ) {
|
||||
if (outfile == nullptr) {
|
||||
close(raw_fd);
|
||||
return false;
|
||||
}
|
||||
|
@ -1135,58 +1139,58 @@ bool Image::WriteJpeg(const char *filename,
|
|||
cinfo->image_width = width; /* image width and height, in pixels */
|
||||
cinfo->image_height = height;
|
||||
|
||||
switch ( colours ) {
|
||||
switch (colours) {
|
||||
case ZM_COLOUR_GRAY8:
|
||||
cinfo->input_components = 1;
|
||||
cinfo->in_color_space = JCS_GRAYSCALE;
|
||||
break;
|
||||
cinfo->input_components = 1;
|
||||
cinfo->in_color_space = JCS_GRAYSCALE;
|
||||
break;
|
||||
case ZM_COLOUR_RGB32:
|
||||
#ifdef JCS_EXTENSIONS
|
||||
cinfo->input_components = 4;
|
||||
if ( subpixelorder == ZM_SUBPIX_ORDER_RGBA ) {
|
||||
cinfo->in_color_space = JCS_EXT_RGBX;
|
||||
} else if ( subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
|
||||
cinfo->in_color_space = JCS_EXT_BGRX;
|
||||
} else if ( subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
|
||||
cinfo->in_color_space = JCS_EXT_XRGB;
|
||||
} else if ( subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
|
||||
cinfo->in_color_space = JCS_EXT_XBGR;
|
||||
} else {
|
||||
Warning("Unknwon subpixelorder %d", subpixelorder);
|
||||
/* Assume RGBA */
|
||||
cinfo->in_color_space = JCS_EXT_RGBX;
|
||||
}
|
||||
break;
|
||||
cinfo->input_components = 4;
|
||||
if (subpixelorder == ZM_SUBPIX_ORDER_RGBA) {
|
||||
cinfo->in_color_space = JCS_EXT_RGBX;
|
||||
} else if (subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
||||
cinfo->in_color_space = JCS_EXT_BGRX;
|
||||
} else if (subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
||||
cinfo->in_color_space = JCS_EXT_XRGB;
|
||||
} else if (subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||
cinfo->in_color_space = JCS_EXT_XBGR;
|
||||
} else {
|
||||
Warning("Unknwon subpixelorder %d", subpixelorder);
|
||||
/* Assume RGBA */
|
||||
cinfo->in_color_space = JCS_EXT_RGBX;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source");
|
||||
Error("libjpeg-turbo is required for JPEG encoding directly from RGB32 source");
|
||||
jpeg_abort_compress(cinfo);
|
||||
fclose(outfile);
|
||||
return false;
|
||||
#endif
|
||||
case ZM_COLOUR_RGB24:
|
||||
default:
|
||||
cinfo->input_components = 3;
|
||||
if (subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||
#ifdef JCS_EXTENSIONS
|
||||
cinfo->in_color_space = JCS_EXT_BGR;
|
||||
#else
|
||||
Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source");
|
||||
jpeg_abort_compress(cinfo);
|
||||
fclose(outfile);
|
||||
return false;
|
||||
#endif
|
||||
case ZM_COLOUR_RGB24:
|
||||
default:
|
||||
cinfo->input_components = 3;
|
||||
if ( subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||
#ifdef JCS_EXTENSIONS
|
||||
cinfo->in_color_space = JCS_EXT_BGR;
|
||||
#else
|
||||
Error("libjpeg-turbo is required for JPEG encoding directly from BGR24 source");
|
||||
jpeg_abort_compress(cinfo);
|
||||
fclose(outfile);
|
||||
return false;
|
||||
#endif
|
||||
} else {
|
||||
/* Assume RGB */
|
||||
/*
|
||||
} else {
|
||||
/* Assume RGB */
|
||||
/*
|
||||
#ifdef JCS_EXTENSIONS
|
||||
cinfo->out_color_space = JCS_EXT_RGB;
|
||||
#else
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
#endif
|
||||
*/
|
||||
cinfo->in_color_space = JCS_RGB;
|
||||
}
|
||||
break;
|
||||
cinfo->in_color_space = JCS_RGB;
|
||||
}
|
||||
break;
|
||||
} // end switch(colours)
|
||||
|
||||
jpeg_set_defaults(cinfo);
|
||||
|
@ -1194,7 +1198,7 @@ cinfo->out_color_space = JCS_RGB;
|
|||
cinfo->dct_method = JDCT_FASTEST;
|
||||
|
||||
jpeg_start_compress(cinfo, TRUE);
|
||||
if ( config.add_jpeg_comments && !annotation_.empty() ) {
|
||||
if (config.add_jpeg_comments && !annotation_.empty()) {
|
||||
jpeg_write_marker(cinfo, JPEG_COM, reinterpret_cast<const JOCTET *>(annotation_.c_str()), annotation_.size());
|
||||
}
|
||||
// If we have a non-zero time (meaning a parameter was passed in), then form a simple exif segment with that time as DateTimeOriginal and SubsecTimeOriginal
|
||||
|
@ -1218,19 +1222,19 @@ cinfo->out_color_space = JCS_RGB;
|
|||
snprintf(msbuf, sizeof msbuf, "%06d", static_cast<int32>(ts_usec.count()));
|
||||
|
||||
unsigned char exiftimes[82] = {
|
||||
0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x69, 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x03, 0x90, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x91, 0x92,
|
||||
0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x00 };
|
||||
memcpy(&exiftimes[EXIFTIMES_OFFSET], timebuf,EXIFTIMES_LEN);
|
||||
0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||
0x69, 0x87, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x00, 0x03, 0x90, 0x02, 0x00, 0x14, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x91, 0x92,
|
||||
0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0x00};
|
||||
memcpy(&exiftimes[EXIFTIMES_OFFSET], timebuf, EXIFTIMES_LEN);
|
||||
memcpy(&exiftimes[EXIFTIMES_MS_OFFSET], msbuf, EXIFTIMES_MS_LEN);
|
||||
jpeg_write_marker(cinfo, EXIF_CODE, (const JOCTET *)exiftimes, sizeof(exiftimes));
|
||||
jpeg_write_marker(cinfo, EXIF_CODE, (const JOCTET *) exiftimes, sizeof(exiftimes));
|
||||
}
|
||||
|
||||
JSAMPROW row_pointer = buffer; /* pointer to a single row */
|
||||
while ( cinfo->next_scanline < cinfo->image_height ) {
|
||||
while (cinfo->next_scanline < cinfo->image_height) {
|
||||
jpeg_write_scanlines(cinfo, &row_pointer, 1);
|
||||
row_pointer += linesize;
|
||||
}
|
||||
|
@ -1240,121 +1244,116 @@ cinfo->out_color_space = JCS_RGB;
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Image::DecodeJpeg(
|
||||
const JOCTET *inbuffer,
|
||||
int inbuffer_size,
|
||||
unsigned int p_colours,
|
||||
unsigned int p_subpixelorder)
|
||||
bool Image::DecodeJpeg(const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder)
|
||||
{
|
||||
unsigned int new_width, new_height, new_colours, new_subpixelorder;
|
||||
struct jpeg_decompress_struct *cinfo = decodejpg_dcinfo;
|
||||
|
||||
if ( !cinfo ) {
|
||||
cinfo = decodejpg_dcinfo = new jpeg_decompress_struct;
|
||||
cinfo->err = jpeg_std_error( &jpg_err.pub );
|
||||
if (!decodejpg_dcinfo) {
|
||||
decodejpg_dcinfo = new jpeg_decompress_struct;
|
||||
decodejpg_dcinfo->err = jpeg_std_error(&jpg_err.pub);
|
||||
jpg_err.pub.error_exit = zm_jpeg_error_exit;
|
||||
jpg_err.pub.emit_message = zm_jpeg_emit_message;
|
||||
jpeg_create_decompress( cinfo );
|
||||
jpeg_create_decompress(decodejpg_dcinfo);
|
||||
}
|
||||
|
||||
if ( setjmp(jpg_err.setjmp_buffer) ) {
|
||||
jpeg_abort_decompress(cinfo);
|
||||
if (setjmp(jpg_err.setjmp_buffer)) {
|
||||
jpeg_abort_decompress(decodejpg_dcinfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
zm_jpeg_mem_src(cinfo, inbuffer, inbuffer_size);
|
||||
zm_jpeg_mem_src(decodejpg_dcinfo, inbuffer, inbuffer_size);
|
||||
|
||||
jpeg_read_header(cinfo, TRUE);
|
||||
jpeg_read_header(decodejpg_dcinfo, TRUE);
|
||||
|
||||
if ( (cinfo->num_components != 1) && (cinfo->num_components != 3) ) {
|
||||
if ((decodejpg_dcinfo->num_components != 1) && (decodejpg_dcinfo->num_components != 3)) {
|
||||
Error("Unexpected colours when reading jpeg image: %d", colours);
|
||||
jpeg_abort_decompress(cinfo);
|
||||
jpeg_abort_decompress(decodejpg_dcinfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if the image has at least one huffman table defined. If not, use the standard ones */
|
||||
/* This is required for the MJPEG capture palette of USB devices */
|
||||
if ( cinfo->dc_huff_tbl_ptrs[0] == nullptr ) {
|
||||
zm_use_std_huff_tables(cinfo);
|
||||
if (decodejpg_dcinfo->dc_huff_tbl_ptrs[0] == nullptr) {
|
||||
zm_use_std_huff_tables(decodejpg_dcinfo);
|
||||
}
|
||||
|
||||
new_width = cinfo->image_width;
|
||||
new_height = cinfo->image_height;
|
||||
new_width = decodejpg_dcinfo->image_width;
|
||||
new_height = decodejpg_dcinfo->image_height;
|
||||
|
||||
if ( (width != new_width) || (height != new_height) ) {
|
||||
if ((width != new_width) || (height != new_height)) {
|
||||
Debug(9, "Image dimensions differ. Old: %ux%u New: %ux%u",
|
||||
width, height, new_width, new_height);
|
||||
width, height, new_width, new_height);
|
||||
}
|
||||
|
||||
switch (p_colours) {
|
||||
case ZM_COLOUR_GRAY8:
|
||||
cinfo->out_color_space = JCS_GRAYSCALE;
|
||||
new_colours = ZM_COLOUR_GRAY8;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
break;
|
||||
decodejpg_dcinfo->out_color_space = JCS_GRAYSCALE;
|
||||
new_colours = ZM_COLOUR_GRAY8;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_NONE;
|
||||
break;
|
||||
case ZM_COLOUR_RGB32:
|
||||
#ifdef JCS_EXTENSIONS
|
||||
new_colours = ZM_COLOUR_RGB32;
|
||||
if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGRA ) {
|
||||
cinfo->out_color_space = JCS_EXT_BGRX;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA;
|
||||
} else if ( p_subpixelorder == ZM_SUBPIX_ORDER_ARGB ) {
|
||||
cinfo->out_color_space = JCS_EXT_XRGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB;
|
||||
} else if ( p_subpixelorder == ZM_SUBPIX_ORDER_ABGR ) {
|
||||
cinfo->out_color_space = JCS_EXT_XBGR;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
|
||||
} else {
|
||||
/* Assume RGBA */
|
||||
cinfo->out_color_space = JCS_EXT_RGBX;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
}
|
||||
break;
|
||||
new_colours = ZM_COLOUR_RGB32;
|
||||
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
|
||||
decodejpg_dcinfo->out_color_space = JCS_EXT_BGRX;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA;
|
||||
} else if (p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
|
||||
decodejpg_dcinfo->out_color_space = JCS_EXT_XRGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB;
|
||||
} else if (p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
|
||||
decodejpg_dcinfo->out_color_space = JCS_EXT_XBGR;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR;
|
||||
} else {
|
||||
/* Assume RGBA */
|
||||
decodejpg_dcinfo->out_color_space = JCS_EXT_RGBX;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead.");
|
||||
Warning("libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead.");
|
||||
#endif
|
||||
case ZM_COLOUR_RGB24:
|
||||
default:
|
||||
new_colours = ZM_COLOUR_RGB24;
|
||||
if ( p_subpixelorder == ZM_SUBPIX_ORDER_BGR ) {
|
||||
new_colours = ZM_COLOUR_RGB24;
|
||||
if (p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
|
||||
#ifdef JCS_EXTENSIONS
|
||||
cinfo->out_color_space = JCS_EXT_BGR;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_BGR;
|
||||
decodejpg_dcinfo->out_color_space = JCS_EXT_BGR;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_BGR;
|
||||
#else
|
||||
Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead.");
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
Warning("libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead.");
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
#endif
|
||||
} else {
|
||||
/* Assume RGB */
|
||||
/*
|
||||
} else {
|
||||
/* Assume RGB */
|
||||
/*
|
||||
#ifdef JCS_EXTENSIONS
|
||||
cinfo->out_color_space = JCS_EXT_RGB;
|
||||
#else
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
#endif
|
||||
*/
|
||||
cinfo->out_color_space = JCS_RGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
}
|
||||
break;
|
||||
*/
|
||||
decodejpg_dcinfo->out_color_space = JCS_RGB;
|
||||
new_subpixelorder = ZM_SUBPIX_ORDER_RGB;
|
||||
}
|
||||
break;
|
||||
} // end switch
|
||||
|
||||
if ( WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == nullptr ) {
|
||||
if (WriteBuffer(new_width, new_height, new_colours, new_subpixelorder) == nullptr) {
|
||||
Error("Failed requesting writeable buffer for reading JPEG image.");
|
||||
jpeg_abort_decompress(cinfo);
|
||||
jpeg_abort_decompress(decodejpg_dcinfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
jpeg_start_decompress(cinfo);
|
||||
jpeg_start_decompress(decodejpg_dcinfo);
|
||||
|
||||
JSAMPROW row_pointer = buffer; /* pointer to a single row */
|
||||
while ( cinfo->output_scanline < cinfo->output_height ) {
|
||||
jpeg_read_scanlines(cinfo, &row_pointer, 1);
|
||||
while (decodejpg_dcinfo->output_scanline < decodejpg_dcinfo->output_height) {
|
||||
jpeg_read_scanlines(decodejpg_dcinfo, &row_pointer, 1);
|
||||
row_pointer += linesize;
|
||||
}
|
||||
|
||||
jpeg_finish_decompress(cinfo);
|
||||
jpeg_finish_decompress(decodejpg_dcinfo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -37,13 +37,13 @@ void zm_jpeg_emit_silence(j_common_ptr cinfo, int msg_level) {
|
|||
}
|
||||
|
||||
void zm_jpeg_error_exit(j_common_ptr cinfo) {
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
zm_error_ptr zmerr = (zm_error_ptr) cinfo->err;
|
||||
|
||||
(zmerr->pub.format_message)(cinfo, buffer);
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
zmerr->pub.format_message(cinfo, buffer);
|
||||
|
||||
Error("%s", buffer);
|
||||
if ( ++jpeg_err_count == MAX_JPEG_ERRS ) {
|
||||
if (++jpeg_err_count == MAX_JPEG_ERRS) {
|
||||
Fatal("Maximum number (%d) of JPEG errors reached, exiting", jpeg_err_count);
|
||||
}
|
||||
|
||||
|
@ -51,25 +51,25 @@ void zm_jpeg_error_exit(j_common_ptr cinfo) {
|
|||
}
|
||||
|
||||
void zm_jpeg_emit_message(j_common_ptr cinfo, int msg_level) {
|
||||
static char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
|
||||
char buffer[JMSG_LENGTH_MAX];
|
||||
zm_error_ptr zmerr = (zm_error_ptr) cinfo->err;
|
||||
|
||||
if ( msg_level < 0 ) {
|
||||
if (msg_level < 0) {
|
||||
/* It's a warning message. Since corrupt files may generate many warnings,
|
||||
* the policy implemented here is to show only the first warning,
|
||||
* unless trace_level >= 3.
|
||||
*/
|
||||
if ( zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3 ) {
|
||||
(zmerr->pub.format_message)(cinfo, buffer);
|
||||
if ( !strstr(buffer, "Corrupt JPEG data:") )
|
||||
if (zmerr->pub.num_warnings == 0 || zmerr->pub.trace_level >= 3) {
|
||||
zmerr->pub.format_message(cinfo, buffer);
|
||||
if (!strstr(buffer, "Corrupt JPEG data:"))
|
||||
Warning("%s", buffer);
|
||||
}
|
||||
/* Always count warnings in num_warnings. */
|
||||
zmerr->pub.num_warnings++;
|
||||
} else {
|
||||
/* It's a trace message. Show it if trace_level >= msg_level. */
|
||||
if ( zmerr->pub.trace_level >= msg_level ) {
|
||||
(zmerr->pub.format_message)(cinfo, buffer);
|
||||
if (zmerr->pub.trace_level >= msg_level) {
|
||||
zmerr->pub.format_message(cinfo, buffer);
|
||||
Debug(msg_level, "%s", buffer);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -518,18 +518,14 @@ void Logger::logPrint(bool hex, const char *filepath, int line, int level, const
|
|||
|
||||
if (level <= mDatabaseLevel) {
|
||||
if (zmDbConnected) {
|
||||
int syslogSize = syslogEnd - syslogStart;
|
||||
std::string escapedString;
|
||||
escapedString.resize((syslogSize * 2) + 1);
|
||||
mysql_real_escape_string(&dbconn, &escapedString[0], syslogStart, syslogSize);
|
||||
escapedString.resize(std::strlen(escapedString.c_str()));
|
||||
std::string escapedString = zmDbEscapeString({syslogStart, syslogEnd});
|
||||
|
||||
std::string sql_string = stringtf(
|
||||
"INSERT INTO `Logs` "
|
||||
"( `TimeKey`, `Component`, `ServerId`, `Pid`, `Level`, `Code`, `Message`, `File`, `Line` )"
|
||||
" VALUES "
|
||||
"( %ld.%06ld, '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
|
||||
now_sec, now_frac.count(), mId.c_str(), staticConfig.SERVER_ID, tid, level, classString,
|
||||
"( %ld.%06" PRIi64 ", '%s', %d, %d, %d, '%s', '%s', '%s', %d )",
|
||||
now_sec, static_cast<int64>(now_frac.count()), mId.c_str(), staticConfig.SERVER_ID, tid, level, classString,
|
||||
escapedString.c_str(), file, line);
|
||||
dbQueue.push(std::move(sql_string));
|
||||
} else {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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))
|
||||
|
|
|
@ -89,7 +89,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
break;
|
||||
case CMD_PLAY :
|
||||
Debug(1, "Got PLAY command");
|
||||
if ( paused ) {
|
||||
if (paused) {
|
||||
paused = false;
|
||||
delayed = true;
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
break;
|
||||
case CMD_VARPLAY :
|
||||
Debug(1, "Got VARPLAY command");
|
||||
if ( paused ) {
|
||||
if (paused) {
|
||||
paused = false;
|
||||
delayed = true;
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
break;
|
||||
case CMD_FASTFWD :
|
||||
Debug(1, "Got FAST FWD command");
|
||||
if ( paused ) {
|
||||
if (paused) {
|
||||
paused = false;
|
||||
delayed = true;
|
||||
}
|
||||
|
@ -135,27 +135,27 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
}
|
||||
break;
|
||||
case CMD_SLOWFWD :
|
||||
Debug( 1, "Got SLOW FWD command" );
|
||||
Debug(1, "Got SLOW FWD command");
|
||||
paused = true;
|
||||
delayed = true;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
step = 1;
|
||||
break;
|
||||
case CMD_SLOWREV :
|
||||
Debug( 1, "Got SLOW REV command" );
|
||||
Debug(1, "Got SLOW REV command");
|
||||
paused = true;
|
||||
delayed = true;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
step = -1;
|
||||
break;
|
||||
case CMD_FASTREV :
|
||||
Debug( 1, "Got FAST REV command" );
|
||||
if ( paused ) {
|
||||
Debug(1, "Got FAST REV command");
|
||||
if (paused) {
|
||||
paused = false;
|
||||
delayed = true;
|
||||
}
|
||||
// Set play rate
|
||||
switch ( replay_rate ) {
|
||||
switch (replay_rate) {
|
||||
case -2 * ZM_RATE_BASE :
|
||||
replay_rate = -5 * ZM_RATE_BASE;
|
||||
break;
|
||||
|
@ -255,7 +255,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
} status_data;
|
||||
|
||||
status_data.id = monitor->Id();
|
||||
if ( ! monitor->ShmValid() ) {
|
||||
if (!monitor->ShmValid()) {
|
||||
status_data.fps = 0.0;
|
||||
status_data.capture_fps = 0.0;
|
||||
status_data.analysis_fps = 0.0;
|
||||
|
@ -265,7 +265,14 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
status_data.forced = false;
|
||||
status_data.buffer_level = 0;
|
||||
} else {
|
||||
status_data.fps = monitor->GetFPS();
|
||||
FPSeconds elapsed = now - last_fps_update;
|
||||
if (elapsed.count()) {
|
||||
actual_fps = (frame_count - last_frame_count) / elapsed.count();
|
||||
last_frame_count = frame_count;
|
||||
last_fps_update = now;
|
||||
}
|
||||
|
||||
status_data.fps = actual_fps;
|
||||
status_data.capture_fps = monitor->get_capture_fps();
|
||||
status_data.analysis_fps = monitor->get_analysis_fps();
|
||||
status_data.state = monitor->shared_data->state;
|
||||
|
@ -320,7 +327,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
//updateFrameRate(monitor->GetFPS());
|
||||
} // end void MonitorStream::processCommand(const CmdMsg *msg)
|
||||
|
||||
bool MonitorStream::sendFrame(const char *filepath, SystemTimePoint timestamp) {
|
||||
bool MonitorStream::sendFrame(const std::string &filepath, SystemTimePoint timestamp) {
|
||||
bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE));
|
||||
|
||||
if (
|
||||
|
@ -330,19 +337,18 @@ bool MonitorStream::sendFrame(const char *filepath, SystemTimePoint timestamp) {
|
|||
)
|
||||
send_raw = false;
|
||||
|
||||
if ( !send_raw ) {
|
||||
Image temp_image(filepath);
|
||||
if (!send_raw) {
|
||||
Image temp_image(filepath.c_str());
|
||||
return sendFrame(&temp_image, timestamp);
|
||||
} else {
|
||||
int img_buffer_size = 0;
|
||||
static unsigned char img_buffer[ZM_MAX_IMAGE_SIZE];
|
||||
|
||||
FILE *fdj = nullptr;
|
||||
if ( (fdj = fopen(filepath, "r")) ) {
|
||||
if (FILE *fdj = fopen(filepath.c_str(), "r")) {
|
||||
img_buffer_size = fread(img_buffer, 1, sizeof(img_buffer), fdj);
|
||||
fclose(fdj);
|
||||
} else {
|
||||
Error("Can't open %s: %s", filepath, strerror(errno));
|
||||
Error("Can't open %s: %s", filepath.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -377,12 +383,12 @@ bool MonitorStream::sendFrame(const char *filepath, SystemTimePoint timestamp) {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
} // end bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp)
|
||||
}
|
||||
|
||||
bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
||||
Image *send_image = prepareImage(image);
|
||||
if (!config.timestamp_on_capture) {
|
||||
monitor->TimestampImage(send_image, zm::chrono::duration_cast<timeval>(timestamp.time_since_epoch()));
|
||||
monitor->TimestampImage(send_image, timestamp);
|
||||
}
|
||||
|
||||
fputs("--" BOUNDARY "\r\n", stdout);
|
||||
|
@ -461,15 +467,17 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
|||
} // Not mpeg
|
||||
last_frame_sent = now;
|
||||
return true;
|
||||
} // end bool MonitorStream::sendFrame( Image *image, const timeval ×tamp )
|
||||
}
|
||||
|
||||
void MonitorStream::runStream() {
|
||||
if (type == STREAM_SINGLE) {
|
||||
// Not yet migrated over to stream class
|
||||
if (checkInitialised())
|
||||
if (checkInitialised()) {
|
||||
SingleImage(scale);
|
||||
else
|
||||
} else {
|
||||
fputs("Content-Type: multipart/x-mixed-replace; boundary=" BOUNDARY "\r\n\r\n", stdout);
|
||||
sendTextFrame("Unable to send image");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -518,7 +526,7 @@ void MonitorStream::runStream() {
|
|||
Image *paused_image = nullptr;
|
||||
SystemTimePoint paused_timestamp;
|
||||
|
||||
if ( connkey && ( playback_buffer > 0 ) ) {
|
||||
if (connkey && (playback_buffer > 0)) {
|
||||
// 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
|
||||
const int max_swap_len_suffix = 15;
|
||||
|
||||
|
@ -527,27 +535,27 @@ void MonitorStream::runStream() {
|
|||
int subfolder2_length = snprintf(nullptr, 0, "/zmswap-q%06d", connkey) + 1;
|
||||
int total_swap_path_length = swap_path_length + subfolder1_length + subfolder2_length;
|
||||
|
||||
if ( total_swap_path_length + max_swap_len_suffix > PATH_MAX ) {
|
||||
if (total_swap_path_length + max_swap_len_suffix > PATH_MAX) {
|
||||
Error("Swap Path is too long. %d > %d ", total_swap_path_length+max_swap_len_suffix, PATH_MAX);
|
||||
} else {
|
||||
swap_path = staticConfig.PATH_SWAP;
|
||||
|
||||
Debug(3, "Checking swap path folder: %s", swap_path.c_str());
|
||||
if ( checkSwapPath(swap_path.c_str(), true) ) {
|
||||
if (checkSwapPath(swap_path.c_str(), true)) {
|
||||
swap_path += stringtf("/zmswap-m%d", monitor->Id());
|
||||
|
||||
Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
|
||||
if ( checkSwapPath(swap_path.c_str(), true) ) {
|
||||
if (checkSwapPath(swap_path.c_str(), true)) {
|
||||
swap_path += stringtf("/zmswap-q%06d", connkey);
|
||||
|
||||
Debug(4, "Checking swap path subfolder: %s", swap_path.c_str());
|
||||
if ( checkSwapPath(swap_path.c_str(), true) ) {
|
||||
if (checkSwapPath(swap_path.c_str(), true)) {
|
||||
buffered_playback = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !buffered_playback ) {
|
||||
if (!buffered_playback) {
|
||||
Error("Unable to validate swap image path, disabling buffered playback");
|
||||
} else {
|
||||
Debug(2, "Assigning temporary buffer");
|
||||
|
@ -557,14 +565,13 @@ void MonitorStream::runStream() {
|
|||
}
|
||||
} else {
|
||||
Debug(2, "Not using playback_buffer");
|
||||
} // end if connkey & playback_buffer
|
||||
} // end if connkey && playback_buffer
|
||||
|
||||
while (!zm_terminate) {
|
||||
bool got_command = false;
|
||||
if ( feof(stdout) ) {
|
||||
if (feof(stdout)) {
|
||||
Debug(2, "feof stdout");
|
||||
break;
|
||||
} else if ( ferror(stdout) ) {
|
||||
} else if (ferror(stdout)) {
|
||||
Debug(2, "ferror stdout");
|
||||
break;
|
||||
} else if (!monitor->ShmValid()) {
|
||||
|
@ -575,8 +582,9 @@ void MonitorStream::runStream() {
|
|||
now = std::chrono::system_clock::now();
|
||||
|
||||
bool was_paused = paused;
|
||||
if ( connkey ) {
|
||||
while ( checkCommandQueue() && !zm_terminate ) {
|
||||
bool got_command = false; // commands like zoom should output a frame even if paused
|
||||
if (connkey) {
|
||||
while (checkCommandQueue() && !zm_terminate) {
|
||||
// Loop in here until all commands are processed.
|
||||
Debug(2, "Have checking command Queue for connkey: %d", connkey);
|
||||
got_command = true;
|
||||
|
@ -588,35 +596,31 @@ void MonitorStream::runStream() {
|
|||
}
|
||||
} // end if connkey
|
||||
|
||||
if ( paused ) {
|
||||
if ( !was_paused ) {
|
||||
if (paused) {
|
||||
if (!was_paused) {
|
||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
||||
Debug(1, "Saving paused image from index %d",index);
|
||||
paused_image = new Image(*monitor->image_buffer[index]);
|
||||
paused_timestamp = SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
|
||||
}
|
||||
} else if ( paused_image ) {
|
||||
Debug(1, "Clearing paused_image");
|
||||
} else if (paused_image) {
|
||||
delete paused_image;
|
||||
paused_image = nullptr;
|
||||
}
|
||||
|
||||
if ( buffered_playback && delayed ) {
|
||||
if ( temp_read_index == temp_write_index ) {
|
||||
if (buffered_playback && delayed) {
|
||||
if (temp_read_index == temp_write_index) {
|
||||
// Go back to live viewing
|
||||
Debug(1, "Exceeded temporary streaming buffer");
|
||||
// Clear paused flag
|
||||
paused = false;
|
||||
// Clear delayed_play flag
|
||||
delayed = false;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
} else {
|
||||
if ( !paused ) {
|
||||
if (!paused) {
|
||||
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
||||
// Debug( 3, "tri: %d, ti: %d", temp_read_index, temp_index );
|
||||
SwapImage *swap_image = &temp_image_buffer[temp_index];
|
||||
|
||||
if ( !swap_image->valid ) {
|
||||
if (!swap_image->valid) {
|
||||
paused = true;
|
||||
delayed = true;
|
||||
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
|
||||
|
@ -627,12 +631,13 @@ void MonitorStream::runStream() {
|
|||
// If the next frame is due
|
||||
if (actual_delta_time > expected_delta_time) {
|
||||
// Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time );
|
||||
if (temp_index % frame_mod == 0) {
|
||||
if ((temp_index % frame_mod) == 0) {
|
||||
Debug(2, "Sending delayed frame %d", temp_index);
|
||||
// Send the next frame
|
||||
if (!sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp)) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
frame_count++;
|
||||
last_frame_timestamp = swap_image->timestamp;
|
||||
// frame_sent = true;
|
||||
}
|
||||
|
@ -645,9 +650,13 @@ void MonitorStream::runStream() {
|
|||
SwapImage *swap_image = &temp_image_buffer[temp_read_index];
|
||||
|
||||
// Send the next frame
|
||||
if (!sendFrame(temp_image_buffer[temp_read_index].file_name, temp_image_buffer[temp_read_index].timestamp)) {
|
||||
if (!sendFrame(
|
||||
temp_image_buffer[temp_read_index].file_name,
|
||||
temp_image_buffer[temp_read_index].timestamp)
|
||||
) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
frame_count++;
|
||||
|
||||
last_frame_timestamp = swap_image->timestamp;
|
||||
// frame_sent = true;
|
||||
|
@ -663,12 +672,13 @@ void MonitorStream::runStream() {
|
|||
if (!sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp)) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
frame_count++;
|
||||
// frame_sent = true;
|
||||
}
|
||||
} // end if (!paused) or step or paused
|
||||
} // end if have exceeded buffer or not
|
||||
|
||||
if ( temp_read_index == temp_write_index ) {
|
||||
if (temp_read_index == temp_write_index) {
|
||||
// Go back to live viewing
|
||||
Warning("Rewound over write index, resuming live play");
|
||||
// Clear paused flag
|
||||
|
@ -677,13 +687,13 @@ void MonitorStream::runStream() {
|
|||
delayed = false;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
}
|
||||
} // end if ( buffered_playback && delayed )
|
||||
} // end if (buffered_playback && delayed)
|
||||
|
||||
if ( last_read_index != monitor->shared_data->last_write_index ) {
|
||||
if (last_read_index != monitor->shared_data->last_write_index) {
|
||||
// have a new image to send
|
||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; // % shouldn't be neccessary
|
||||
if ( (frame_mod == 1) || ((frame_count%frame_mod) == 0) ) {
|
||||
if ( !paused && !delayed ) {
|
||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
||||
if ((frame_mod == 1) || ((frame_count%frame_mod) == 0)) {
|
||||
if (!paused && !delayed) {
|
||||
last_read_index = monitor->shared_data->last_write_index;
|
||||
Debug(2, "Sending frame index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)",
|
||||
index, frame_mod, frame_count, paused, delayed);
|
||||
|
@ -699,8 +709,7 @@ void MonitorStream::runStream() {
|
|||
zm_terminate = true;
|
||||
break;
|
||||
}
|
||||
//frame_sent = true;
|
||||
//
|
||||
frame_count++;
|
||||
if (frame_count == 0) {
|
||||
// Chrome will not display the first frame until it receives another.
|
||||
// Firefox is fine. So just send the first frame twice.
|
||||
|
@ -713,16 +722,18 @@ void MonitorStream::runStream() {
|
|||
|
||||
temp_read_index = temp_write_index;
|
||||
} else {
|
||||
if ( delayed && !buffered_playback ) {
|
||||
if (delayed && !buffered_playback) {
|
||||
Debug(2, "Can't delay when not buffering.");
|
||||
delayed = false;
|
||||
}
|
||||
if ( last_zoom != zoom ) {
|
||||
if (last_zoom != zoom) {
|
||||
Debug(2, "Sending 2 frames because change in zoom %d ?= %d", last_zoom, zoom);
|
||||
if (!sendFrame(paused_image, paused_timestamp))
|
||||
zm_terminate = true;
|
||||
if (!sendFrame(paused_image, paused_timestamp))
|
||||
zm_terminate = true;
|
||||
frame_count++;
|
||||
frame_count++;
|
||||
} else {
|
||||
SystemTimePoint::duration actual_delta_time = now - last_frame_sent;
|
||||
if (actual_delta_time > Seconds(5)) {
|
||||
|
@ -733,37 +744,32 @@ void MonitorStream::runStream() {
|
|||
// Send the next frame
|
||||
if (!sendFrame(paused_image, paused_timestamp))
|
||||
zm_terminate = true;
|
||||
frame_count++;
|
||||
} else {
|
||||
Debug(2, "Would have sent keepalive frame, but had no paused_image");
|
||||
}
|
||||
} // end if actual_delta_time > 5
|
||||
} // end if change in zoom
|
||||
} // end if paused or not
|
||||
} else {
|
||||
frame_count++;
|
||||
} // end if should send frame
|
||||
|
||||
if ( buffered_playback && !paused ) {
|
||||
if ( monitor->shared_data->valid ) {
|
||||
if ( monitor->shared_timestamps[index].tv_sec ) {
|
||||
if (buffered_playback && !paused) {
|
||||
if (monitor->shared_data->valid) {
|
||||
if (monitor->shared_timestamps[index].tv_sec) {
|
||||
int temp_index = temp_write_index%temp_image_buffer_count;
|
||||
Debug(2, "Storing frame %d", temp_index);
|
||||
if ( !temp_image_buffer[temp_index].valid ) {
|
||||
snprintf(
|
||||
temp_image_buffer[temp_index].file_name,
|
||||
sizeof(temp_image_buffer[0].file_name),
|
||||
"%s/zmswap-i%05d.jpg",
|
||||
swap_path.c_str(),
|
||||
temp_index);
|
||||
temp_image_buffer[temp_index].file_name = stringtf("%s/zmswap-i%05d.jpg", swap_path.c_str(), temp_index);
|
||||
temp_image_buffer[temp_index].valid = true;
|
||||
}
|
||||
|
||||
temp_image_buffer[temp_index].timestamp =
|
||||
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
|
||||
monitor->image_buffer[index]->WriteJpeg(
|
||||
temp_image_buffer[temp_index].file_name,
|
||||
config.jpeg_file_quality
|
||||
);
|
||||
monitor->image_buffer[index]->WriteJpeg(temp_image_buffer[temp_index].file_name, config.jpeg_file_quality);
|
||||
temp_write_index = MOD_ADD(temp_write_index, 1, temp_image_buffer_count);
|
||||
if ( temp_write_index == temp_read_index ) {
|
||||
if (temp_write_index == temp_read_index) {
|
||||
// Go back to live viewing
|
||||
Warning("Exceeded temporary buffer, resuming live play");
|
||||
paused = false;
|
||||
|
@ -777,7 +783,6 @@ void MonitorStream::runStream() {
|
|||
Warning("Unable to store frame as shared memory invalid");
|
||||
}
|
||||
} // end if buffered playback
|
||||
frame_count++;
|
||||
} else {
|
||||
Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index);
|
||||
} // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index )
|
||||
|
@ -808,38 +813,37 @@ void MonitorStream::runStream() {
|
|||
Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)",
|
||||
frame_mod, frame_count);
|
||||
}
|
||||
} // end while
|
||||
} // end while ! zm_terminate
|
||||
|
||||
if ( buffered_playback ) {
|
||||
if (buffered_playback) {
|
||||
Debug(1, "Cleaning swap files from %s", swap_path.c_str());
|
||||
struct stat stat_buf;
|
||||
if ( stat(swap_path.c_str(), &stat_buf) < 0 ) {
|
||||
if ( errno != ENOENT ) {
|
||||
struct stat stat_buf = {};
|
||||
if (stat(swap_path.c_str(), &stat_buf) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
Error("Can't stat '%s': %s", swap_path.c_str(), strerror(errno));
|
||||
}
|
||||
} else if ( !S_ISDIR(stat_buf.st_mode) ) {
|
||||
} else if (!S_ISDIR(stat_buf.st_mode)) {
|
||||
Error("Swap image path '%s' is not a directory", swap_path.c_str());
|
||||
} else {
|
||||
char glob_pattern[PATH_MAX] = "";
|
||||
|
||||
snprintf(glob_pattern, sizeof(glob_pattern), "%s/*.*", swap_path.c_str());
|
||||
std::string glob_pattern = stringtf("%s/*.*", swap_path.c_str());
|
||||
glob_t pglob;
|
||||
int glob_status = glob(glob_pattern, 0, 0, &pglob);
|
||||
if ( glob_status != 0 ) {
|
||||
if ( glob_status < 0 ) {
|
||||
Error("Can't glob '%s': %s", glob_pattern, strerror(errno));
|
||||
|
||||
int glob_status = glob(glob_pattern.c_str(), 0, 0, &pglob);
|
||||
if (glob_status != 0) {
|
||||
if (glob_status < 0) {
|
||||
Error("Can't glob '%s': %s", glob_pattern.c_str(), strerror(errno));
|
||||
} else {
|
||||
Debug(1, "Can't glob '%s': %d", glob_pattern, glob_status);
|
||||
Debug(1, "Can't glob '%s': %d", glob_pattern.c_str(), glob_status);
|
||||
}
|
||||
} else {
|
||||
for ( unsigned int i = 0; i < pglob.gl_pathc; i++ ) {
|
||||
if ( unlink(pglob.gl_pathv[i]) < 0 ) {
|
||||
for (unsigned int i = 0; i < pglob.gl_pathc; i++) {
|
||||
if (unlink(pglob.gl_pathv[i]) < 0) {
|
||||
Error("Can't unlink '%s': %s", pglob.gl_pathv[i], strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
globfree(&pglob);
|
||||
if ( rmdir(swap_path.c_str()) < 0 ) {
|
||||
if (rmdir(swap_path.c_str()) < 0) {
|
||||
Error("Can't rmdir '%s': %s", swap_path.c_str(), strerror(errno));
|
||||
}
|
||||
} // end if checking for swap_path
|
||||
|
@ -854,7 +858,7 @@ void MonitorStream::SingleImage(int scale) {
|
|||
Image scaled_image;
|
||||
while ((monitor->shared_data->last_write_index >= monitor->image_buffer_count) and !zm_terminate) {
|
||||
Debug(1, "Waiting for capture to begin");
|
||||
usleep(100000);
|
||||
std::this_thread::sleep_for(Milliseconds(100));
|
||||
}
|
||||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
||||
Debug(1, "write index: %d %d", monitor->shared_data->last_write_index, index);
|
||||
|
@ -865,8 +869,9 @@ void MonitorStream::SingleImage(int scale) {
|
|||
scaled_image.Scale(scale);
|
||||
snap_image = &scaled_image;
|
||||
}
|
||||
if ( !config.timestamp_on_capture ) {
|
||||
monitor->TimestampImage(snap_image, monitor->shared_timestamps[index]);
|
||||
if (!config.timestamp_on_capture) {
|
||||
monitor->TimestampImage(snap_image,
|
||||
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index])));
|
||||
}
|
||||
snap_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -21,13 +21,7 @@
|
|||
|
||||
#include "zm_logger.h"
|
||||
#include "zm_rgb.h"
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/mathematics.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
#include "zm_time.h"
|
||||
|
||||
bool VideoStream::initialised = false;
|
||||
|
||||
|
@ -537,32 +531,29 @@ int VideoStream::SendPacket(AVPacket *packet) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
void *VideoStream::StreamingThreadCallback(void *ctx){
|
||||
|
||||
Debug( 1, "StreamingThreadCallback started" );
|
||||
|
||||
if (ctx == nullptr) return nullptr;
|
||||
void *VideoStream::StreamingThreadCallback(void *ctx) {
|
||||
Debug(1, "StreamingThreadCallback started");
|
||||
|
||||
VideoStream* videoStream = reinterpret_cast<VideoStream*>(ctx);
|
||||
if (ctx == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint64_t nanosecond_multiplier = 1000000000;
|
||||
VideoStream *videoStream = reinterpret_cast<VideoStream *>(ctx);
|
||||
|
||||
uint64_t target_interval_ns = nanosecond_multiplier * ( ((double)videoStream->codec_context->time_base.num) / (videoStream->codec_context->time_base.den) );
|
||||
TimePoint::duration target_interval = std::chrono::duration_cast<TimePoint::duration>(FPSeconds(
|
||||
videoStream->codec_context->time_base.num / static_cast<double>(videoStream->codec_context->time_base.den)));
|
||||
|
||||
uint64_t frame_count = 0;
|
||||
timespec start_time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||
uint64_t start_time_ns = (start_time.tv_sec*nanosecond_multiplier) + start_time.tv_nsec;
|
||||
while(videoStream->do_streaming) {
|
||||
timespec current_time;
|
||||
clock_gettime(CLOCK_MONOTONIC, ¤t_time);
|
||||
uint64_t current_time_ns = (current_time.tv_sec*nanosecond_multiplier) + current_time.tv_nsec;
|
||||
uint64_t target_ns = start_time_ns + (target_interval_ns * frame_count);
|
||||
|
||||
if ( current_time_ns < target_ns ) {
|
||||
// It's not time to render a frame yet.
|
||||
usleep( (target_ns - current_time_ns) * 0.001 );
|
||||
}
|
||||
uint64_t frame_count = 0;
|
||||
TimePoint start_time = std::chrono::steady_clock::now();
|
||||
|
||||
while (videoStream->do_streaming) {
|
||||
TimePoint current_time = std::chrono::steady_clock::now();
|
||||
TimePoint target = start_time + (target_interval * frame_count);
|
||||
|
||||
if (current_time < target) {
|
||||
// It's not time to render a frame yet.
|
||||
std::this_thread::sleep_for(target - current_time);
|
||||
}
|
||||
|
||||
// By sending the last rendered frame we deliver frames to the client more accurate.
|
||||
// If we're encoding the frame before sending it there will be lag.
|
||||
|
@ -573,27 +564,29 @@ void *VideoStream::StreamingThreadCallback(void *ctx){
|
|||
if (packet->size) {
|
||||
videoStream->SendPacket(packet);
|
||||
}
|
||||
av_packet_unref( packet);
|
||||
av_packet_unref(packet);
|
||||
|
||||
videoStream->packet_index = videoStream->packet_index ? 0 : 1;
|
||||
|
||||
// Lock buffer and render next frame.
|
||||
|
||||
if ( pthread_mutex_lock( videoStream->buffer_copy_lock ) != 0 ) {
|
||||
Fatal( "StreamingThreadCallback: pthread_mutex_lock failed." );
|
||||
}
|
||||
|
||||
if ( videoStream->buffer_copy ) {
|
||||
// Encode next frame.
|
||||
videoStream->ActuallyEncodeFrame( videoStream->buffer_copy, videoStream->buffer_copy_used, videoStream->add_timestamp, videoStream->timestamp );
|
||||
}
|
||||
|
||||
if ( pthread_mutex_unlock( videoStream->buffer_copy_lock ) != 0 ) {
|
||||
Fatal( "StreamingThreadCallback: pthread_mutex_unlock failed." );
|
||||
}
|
||||
|
||||
frame_count++;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
if (pthread_mutex_lock(videoStream->buffer_copy_lock) != 0) {
|
||||
Fatal("StreamingThreadCallback: pthread_mutex_lock failed.");
|
||||
}
|
||||
|
||||
if (videoStream->buffer_copy) {
|
||||
// Encode next frame.
|
||||
videoStream->ActuallyEncodeFrame(videoStream->buffer_copy,
|
||||
videoStream->buffer_copy_used,
|
||||
videoStream->add_timestamp,
|
||||
videoStream->timestamp);
|
||||
}
|
||||
|
||||
if (pthread_mutex_unlock(videoStream->buffer_copy_lock) != 0) {
|
||||
Fatal("StreamingThreadCallback: pthread_mutex_unlock failed.");
|
||||
}
|
||||
|
||||
frame_count++;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "zm_ffmpeg.h"
|
||||
#include "zm_image.h"
|
||||
#include "zm_logger.h"
|
||||
#include <sys/time.h>
|
||||
|
||||
using namespace std;
|
||||
AVPixelFormat target_format = AV_PIX_FMT_NONE;
|
||||
|
@ -31,7 +30,6 @@ ZMPacket::ZMPacket() :
|
|||
stream(nullptr),
|
||||
in_frame(nullptr),
|
||||
out_frame(nullptr),
|
||||
timestamp({}),
|
||||
buffer(nullptr),
|
||||
image(nullptr),
|
||||
analysis_image(nullptr),
|
||||
|
@ -40,13 +38,13 @@ ZMPacket::ZMPacket() :
|
|||
image_index(-1),
|
||||
codec_imgsize(0),
|
||||
pts(0),
|
||||
decoded(0)
|
||||
decoded(false)
|
||||
{
|
||||
av_init_packet(&packet);
|
||||
packet.size = 0; // So we can detect whether it has been filled.
|
||||
}
|
||||
|
||||
ZMPacket::ZMPacket(Image *i, const timeval &tv) :
|
||||
ZMPacket::ZMPacket(Image *i, SystemTimePoint tv) :
|
||||
keyframe(0),
|
||||
stream(nullptr),
|
||||
in_frame(nullptr),
|
||||
|
@ -60,7 +58,7 @@ ZMPacket::ZMPacket(Image *i, const timeval &tv) :
|
|||
image_index(-1),
|
||||
codec_imgsize(0),
|
||||
pts(0),
|
||||
decoded(0)
|
||||
decoded(false)
|
||||
{
|
||||
av_init_packet(&packet);
|
||||
packet.size = 0; // So we can detect whether it has been filled.
|
||||
|
@ -80,12 +78,12 @@ ZMPacket::ZMPacket(ZMPacket &p) :
|
|||
image_index(-1),
|
||||
codec_imgsize(0),
|
||||
pts(0),
|
||||
decoded(0)
|
||||
decoded(false)
|
||||
{
|
||||
av_init_packet(&packet);
|
||||
packet.size = 0;
|
||||
packet.data = nullptr;
|
||||
if ( zm_av_packet_ref(&packet, &p.packet) < 0 ) {
|
||||
if (zm_av_packet_ref(&packet, &p.packet) < 0) {
|
||||
Error("error refing packet");
|
||||
}
|
||||
}
|
||||
|
@ -95,8 +93,8 @@ ZMPacket::~ZMPacket() {
|
|||
if (in_frame) av_frame_free(&in_frame);
|
||||
if (out_frame) av_frame_free(&out_frame);
|
||||
if (buffer) av_freep(&buffer);
|
||||
if (analysis_image) delete analysis_image;
|
||||
if (image) delete image;
|
||||
delete analysis_image;
|
||||
delete image;
|
||||
}
|
||||
|
||||
/* returns < 0 on error, 0 on not ready, int bytes consumed on success
|
||||
|
@ -243,8 +241,8 @@ AVPacket *ZMPacket::set_packet(AVPacket *p) {
|
|||
if (zm_av_packet_ref(&packet, p) < 0) {
|
||||
Error("error refing packet");
|
||||
}
|
||||
//ZM_DUMP_PACKET(packet, "zmpacket:");
|
||||
gettimeofday(×tamp, nullptr);
|
||||
|
||||
timestamp = std::chrono::system_clock::now();
|
||||
keyframe = p->flags & AV_PKT_FLAG_KEY;
|
||||
return &packet;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define ZM_PACKET_H
|
||||
|
||||
#include "zm_logger.h"
|
||||
#include "zm_time.h"
|
||||
#include "zm_zone.h"
|
||||
|
||||
#include <condition_variable>
|
||||
|
@ -31,10 +32,6 @@ extern "C" {
|
|||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/time.h>
|
||||
#endif // __FreeBSD__
|
||||
|
||||
class Image;
|
||||
|
||||
class ZMPacket {
|
||||
|
@ -49,7 +46,7 @@ class ZMPacket {
|
|||
AVPacket packet; // Input packet, undecoded
|
||||
AVFrame *in_frame; // Input image, decoded Theoretically only filled if needed.
|
||||
AVFrame *out_frame; // output image, Only filled if needed.
|
||||
timeval timestamp;
|
||||
SystemTimePoint timestamp;
|
||||
uint8_t *buffer; // buffer used in image
|
||||
Image *image;
|
||||
Image *analysis_image;
|
||||
|
@ -69,8 +66,8 @@ class ZMPacket {
|
|||
Image *set_image(Image *);
|
||||
|
||||
int is_keyframe() { return keyframe; };
|
||||
int decode( AVCodecContext *ctx );
|
||||
explicit ZMPacket(Image *image, const timeval &tv);
|
||||
int decode(AVCodecContext *ctx);
|
||||
explicit ZMPacket(Image *image, SystemTimePoint tv);
|
||||
explicit ZMPacket(ZMPacket &packet);
|
||||
ZMPacket();
|
||||
~ZMPacket();
|
||||
|
@ -91,6 +88,7 @@ class ZMLockedPacket {
|
|||
lck_(packet_->mutex_, std::defer_lock),
|
||||
locked(false) {
|
||||
}
|
||||
|
||||
~ZMLockedPacket() {
|
||||
if (locked) unlock();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -49,45 +49,43 @@ RemoteCamera::RemoteCamera(
|
|||
mNeedAuth(false),
|
||||
mAuthenticator(nullptr)
|
||||
{
|
||||
if ( path[0] != '/' )
|
||||
if (path[0] != '/')
|
||||
path = '/'+path;
|
||||
}
|
||||
|
||||
RemoteCamera::~RemoteCamera() {
|
||||
if ( hp != nullptr ) {
|
||||
if (hp != nullptr) {
|
||||
freeaddrinfo(hp);
|
||||
hp = nullptr;
|
||||
}
|
||||
if ( mAuthenticator ) {
|
||||
if (mAuthenticator) {
|
||||
delete mAuthenticator;
|
||||
mAuthenticator = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteCamera::Initialise() {
|
||||
if( protocol.empty() )
|
||||
Fatal( "No protocol specified for remote camera" );
|
||||
if (protocol.empty())
|
||||
Fatal("No protocol specified for remote camera");
|
||||
|
||||
if( host.empty() )
|
||||
Fatal( "No host specified for remote camera" );
|
||||
if (host.empty())
|
||||
Fatal("No host specified for remote camera");
|
||||
|
||||
if ( port.empty() )
|
||||
if (port.empty())
|
||||
Fatal("No port specified for remote camera");
|
||||
|
||||
//if( path.empty() )
|
||||
//Fatal( "No path specified for remote camera" );
|
||||
|
||||
// Cache as much as we can to speed things up
|
||||
std::string::size_type authIndex = host.rfind( '@' );
|
||||
std::string::size_type authIndex = host.rfind('@');
|
||||
|
||||
if ( authIndex != std::string::npos ) {
|
||||
auth = host.substr( 0, authIndex );
|
||||
host.erase( 0, authIndex+1 );
|
||||
if (authIndex != std::string::npos) {
|
||||
auth = host.substr(0, authIndex);
|
||||
host.erase(0, authIndex+1);
|
||||
auth64 = Base64Encode(auth);
|
||||
|
||||
authIndex = auth.rfind( ':' );
|
||||
authIndex = auth.rfind(':');
|
||||
username = auth.substr(0,authIndex);
|
||||
password = auth.substr( authIndex+1, auth.length() );
|
||||
password = auth.substr(authIndex+1, auth.length());
|
||||
}
|
||||
|
||||
mNeedAuth = false;
|
||||
|
@ -99,12 +97,13 @@ void RemoteCamera::Initialise() {
|
|||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
int ret = getaddrinfo(host.c_str(), port.c_str(), &hints, &hp);
|
||||
if ( ret != 0 ) {
|
||||
Fatal( "Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret) );
|
||||
if (ret != 0) {
|
||||
Error("Can't getaddrinfo(%s port %s): %s", host.c_str(), port.c_str(), gai_strerror(ret));
|
||||
return;
|
||||
}
|
||||
struct addrinfo *p = nullptr;
|
||||
int addr_count = 0;
|
||||
for ( p = hp; p != nullptr; p = p->ai_next ) {
|
||||
for (p = hp; p != nullptr; p = p->ai_next) {
|
||||
addr_count++;
|
||||
}
|
||||
Debug(1, "%d addresses returned", addr_count);
|
||||
|
|
|
@ -144,6 +144,14 @@ void RemoteCameraHttp::Initialise() {
|
|||
int RemoteCameraHttp::Connect() {
|
||||
struct addrinfo *p = nullptr;
|
||||
|
||||
if (!hp) {
|
||||
RemoteCamera::Initialise();
|
||||
if (!hp) {
|
||||
Error("Unable to resolve address for remote camera, aborting");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for ( p = hp; p != nullptr; p = p->ai_next ) {
|
||||
sd = socket( p->ai_family, p->ai_socktype, p->ai_protocol );
|
||||
if ( sd < 0 ) {
|
||||
|
@ -300,15 +308,17 @@ int RemoteCameraHttp::ReadData(Buffer &buffer, unsigned int bytes_expected) {
|
|||
} // end readData
|
||||
|
||||
int RemoteCameraHttp::GetData() {
|
||||
time_t start_time = time(nullptr);
|
||||
int buffer_len = 0;
|
||||
while (!(buffer_len = ReadData(buffer))) {
|
||||
if (zm_terminate or ( (time(nullptr) - start_time) > ZM_WATCH_MAX_DELAY ))
|
||||
return -1;
|
||||
Debug(4, "Timeout waiting for REGEXP HEADER");
|
||||
usleep(100000);
|
||||
}
|
||||
return buffer_len;
|
||||
TimePoint start_time = std::chrono::steady_clock::now();
|
||||
int buffer_len;
|
||||
while (!(buffer_len = ReadData(buffer))) {
|
||||
if (zm_terminate or std::chrono::steady_clock::now() - start_time > FPSeconds(config.watch_max_delay)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Debug(4, "Timeout waiting for REGEXP HEADER");
|
||||
std::this_thread::sleep_for(Milliseconds(100));
|
||||
}
|
||||
return buffer_len;
|
||||
}
|
||||
|
||||
int RemoteCameraHttp::GetResponse() {
|
||||
|
|
|
@ -126,10 +126,11 @@ int RemoteCameraRtsp::Disconnect() {
|
|||
|
||||
int RemoteCameraRtsp::PrimeCapture() {
|
||||
Debug(2, "Waiting for sources");
|
||||
for ( int i = 0; (i < 100) && !rtspThread->hasSources(); i++ ) {
|
||||
usleep(100000);
|
||||
for (int i = 0; i < 100 && !rtspThread->hasSources(); i++) {
|
||||
std::this_thread::sleep_for(Microseconds(100));
|
||||
}
|
||||
if ( !rtspThread->hasSources() ) {
|
||||
|
||||
if (!rtspThread->hasSources()) {
|
||||
Error("No RTSP sources");
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -269,16 +269,16 @@ void RtpCtrlThread::Run() {
|
|||
|
||||
// The only reason I can think of why we would have a timeout period is so that we can regularly send RR packets.
|
||||
// Why 10 seconds? If anything I think this should be whatever timeout value was given in the DESCRIBE response
|
||||
zm::Select select(10 );
|
||||
zm::Select select(Seconds(10));
|
||||
select.addReader( &rtpCtrlServer );
|
||||
|
||||
unsigned char buffer[ZM_NETWORK_BUFSIZ];
|
||||
|
||||
time_t last_receive = time(nullptr);
|
||||
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
||||
TimePoint last_receive = std::chrono::steady_clock::now();
|
||||
bool timeout = false; // used as a flag that we had a timeout, and then sent an RR to see if we wake back up. Real timeout will happen when this is true.
|
||||
|
||||
while (!mTerminate && select.wait() >= 0) {
|
||||
time_t now = time(nullptr);
|
||||
TimePoint now = std::chrono::steady_clock::now();
|
||||
zm::Select::CommsList readable = select.getReadable();
|
||||
if ( readable.size() == 0 ) {
|
||||
if ( ! timeout ) {
|
||||
|
@ -287,20 +287,20 @@ void RtpCtrlThread::Run() {
|
|||
unsigned char *bufferPtr = buffer;
|
||||
bufferPtr += generateRr( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
bufferPtr += generateSdes( bufferPtr, sizeof(buffer)-(bufferPtr-buffer) );
|
||||
Debug(3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %" PRIi64,
|
||||
bufferPtr - buffer, rtpCtrlServer.getWriteDesc(), static_cast<int64>(now - last_receive));
|
||||
Debug(3, "Preventing timeout by sending %zd bytes on sd %d. Time since last receive: %.2f s",
|
||||
bufferPtr - buffer, rtpCtrlServer.getWriteDesc(), FPSeconds(now - last_receive).count());
|
||||
if ( (nBytes = rtpCtrlServer.send(buffer, bufferPtr-buffer)) < 0 )
|
||||
Error("Unable to send: %s", strerror(errno));
|
||||
timeout = true;
|
||||
continue;
|
||||
} else {
|
||||
Debug(1, "RTCP timed out. Time since last receive: %" PRIi64, static_cast<int64>(now - last_receive));
|
||||
Debug(1, "RTCP timed out. Time since last receive: %.2f s", FPSeconds(now - last_receive).count());
|
||||
continue;
|
||||
//break;
|
||||
}
|
||||
} else {
|
||||
timeout = false;
|
||||
last_receive = time(nullptr);
|
||||
last_receive = std::chrono::steady_clock::now();
|
||||
}
|
||||
for (zm::Select::CommsList::iterator iter = readable.begin(); iter != readable.end(); ++iter ) {
|
||||
if ( zm::UdpInetSocket *socket = dynamic_cast<zm::UdpInetSocket *>(*iter) ) {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -66,7 +66,7 @@ RtpSource::RtpSource(
|
|||
|
||||
mRtpFactor = mRtpClock;
|
||||
|
||||
mBaseTimeReal = tvNow();
|
||||
mBaseTimeReal = std::chrono::system_clock::now();
|
||||
mBaseTimeNtp = {};
|
||||
mBaseTimeRtp = rtpTime;
|
||||
|
||||
|
@ -159,12 +159,9 @@ bool RtpSource::updateSeq(uint16_t seq) {
|
|||
}
|
||||
|
||||
void RtpSource::updateJitter( const RtpDataHeader *header ) {
|
||||
if ( mRtpFactor > 0 ) {
|
||||
timeval now = {};
|
||||
gettimeofday(&now, nullptr);
|
||||
|
||||
FPSeconds time_diff =
|
||||
zm::chrono::duration_cast<Microseconds>(now) - zm::chrono::duration_cast<Microseconds>(mBaseTimeReal);
|
||||
if (mRtpFactor > 0) {
|
||||
SystemTimePoint now = std::chrono::system_clock::now();
|
||||
FPSeconds time_diff = std::chrono::duration_cast<FPSeconds>(now - mBaseTimeReal);
|
||||
|
||||
uint32_t localTimeRtp = mBaseTimeRtp + static_cast<uint32>(time_diff.count() * mRtpFactor);
|
||||
uint32_t packetTransit = localTimeRtp - ntohl(header->timestampN);
|
||||
|
@ -202,7 +199,7 @@ void RtpSource::updateRtcpData(
|
|||
Debug(5, "ntpTime: %ld.%06ld, rtpTime: %x", ntpTime.tv_sec, ntpTime.tv_usec, rtpTime);
|
||||
|
||||
if ( mBaseTimeNtp.tv_sec == 0 ) {
|
||||
mBaseTimeReal = tvNow();
|
||||
mBaseTimeReal = std::chrono::system_clock::now();
|
||||
mBaseTimeNtp = ntpTime;
|
||||
mBaseTimeRtp = rtpTime;
|
||||
} else if ( !mRtpClock ) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -86,9 +86,8 @@ bool RtspThread::recvResponse(std::string &response) {
|
|||
|
||||
int RtspThread::requestPorts() {
|
||||
if ( !smMinDataPort ) {
|
||||
char sql[ZM_SQL_SML_BUFSIZ];
|
||||
//FIXME Why not load specifically by Id? This will get ineffeicient with a lot of monitors
|
||||
strncpy(sql, "SELECT `Id` FROM `Monitors` WHERE `Function` != 'None' AND `Type` = 'Remote' AND `Protocol` = 'rtsp' AND `Method` = 'rtpUni' ORDER BY `Id` ASC", sizeof(sql));
|
||||
std::string sql = "SELECT `Id` FROM `Monitors` WHERE `Function` != 'None' AND `Type` = 'Remote' AND `Protocol` = 'rtsp' AND `Method` = 'rtpUni' ORDER BY `Id` ASC";
|
||||
|
||||
MYSQL_RES *result = zmDbFetch(sql);
|
||||
|
||||
|
@ -331,7 +330,8 @@ void RtspThread::Run() {
|
|||
authTried = true;
|
||||
sendCommand(message);
|
||||
// FIXME Why sleep 1?
|
||||
usleep(10000);
|
||||
std::this_thread::sleep_for(Microseconds(10));
|
||||
|
||||
res = recvResponse(response);
|
||||
if ( !res && respCode==401 )
|
||||
mNeedAuth = true;
|
||||
|
@ -438,15 +438,18 @@ void RtspThread::Run() {
|
|||
|
||||
lines = Split(response, "\r\n");
|
||||
std::string session;
|
||||
int timeout = 0;
|
||||
Seconds timeout = Seconds(0);
|
||||
char transport[256] = "";
|
||||
|
||||
for ( size_t i = 0; i < lines.size(); i++ ) {
|
||||
if ( ( lines[i].size() > 8 ) && ( lines[i].substr(0, 8) == "Session:" ) ) {
|
||||
StringVector sessionLine = Split(lines[i].substr(9), ";");
|
||||
session = TrimSpaces(sessionLine[0]);
|
||||
if ( sessionLine.size() == 2 )
|
||||
sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout);
|
||||
if ( sessionLine.size() == 2 ){
|
||||
int32 timeout_val = 0;
|
||||
sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout_val);
|
||||
timeout = Seconds(timeout_val);
|
||||
}
|
||||
}
|
||||
sscanf(lines[i].c_str(), "Transport: %s", transport);
|
||||
}
|
||||
|
@ -454,7 +457,9 @@ void RtspThread::Run() {
|
|||
if ( session.empty() )
|
||||
Fatal("Unable to get session identifier from response '%s'", response.c_str());
|
||||
|
||||
Debug(2, "Got RTSP session %s, timeout %d secs", session.c_str(), timeout);
|
||||
Debug(2, "Got RTSP session %s, timeout %" PRIi64 " secs",
|
||||
session.c_str(),
|
||||
static_cast<int64>(Seconds(timeout).count()));
|
||||
|
||||
if ( !transport[0] )
|
||||
Fatal("Unable to get transport details from response '%s'", response.c_str());
|
||||
|
@ -517,12 +522,18 @@ void RtspThread::Run() {
|
|||
if ( ( lines[i].size() > 9 ) && ( lines[i].substr(0, 9) == "RTP-Info:" ) )
|
||||
rtpInfo = TrimSpaces(lines[i].substr(9));
|
||||
// Check for a timeout again. Some rtsp devices don't send a timeout until after the PLAY command is sent
|
||||
if ( ( lines[i].size() > 8 ) && ( lines[i].substr(0, 8) == "Session:" ) && ( timeout == 0 ) ) {
|
||||
if ((lines[i].size() > 8) && (lines[i].substr(0, 8) == "Session:") && (timeout == Seconds(0))) {
|
||||
StringVector sessionLine = Split(lines[i].substr(9), ";");
|
||||
if ( sessionLine.size() == 2 )
|
||||
sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout);
|
||||
if ( timeout > 0 )
|
||||
Debug(2, "Got timeout %d secs from PLAY command response", timeout);
|
||||
if ( sessionLine.size() == 2 ){
|
||||
int32 timeout_val = 0;
|
||||
sscanf(TrimSpaces(sessionLine[1]).c_str(), "timeout=%d", &timeout_val);
|
||||
timeout = Seconds(timeout_val);
|
||||
}
|
||||
|
||||
if ( timeout > Seconds(0) ) {
|
||||
Debug(2, "Got timeout %" PRIi64 " secs from PLAY command response",
|
||||
static_cast<int64>(Seconds(timeout).count()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -557,8 +568,8 @@ void RtspThread::Run() {
|
|||
Debug( 2, "RTSP Seq is %d", seq );
|
||||
Debug( 2, "RTSP Rtptime is %ld", rtpTime );
|
||||
|
||||
time_t lastKeepalive = time(nullptr);
|
||||
time_t now;
|
||||
TimePoint lastKeepalive = std::chrono::steady_clock::now();
|
||||
TimePoint now;
|
||||
message = "GET_PARAMETER "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
|
||||
|
||||
switch( mMethod ) {
|
||||
|
@ -570,20 +581,21 @@ void RtspThread::Run() {
|
|||
RtpCtrlThread rtpCtrlThread( *this, *source );
|
||||
|
||||
while (!mTerminate) {
|
||||
now = time(nullptr);
|
||||
now = std::chrono::steady_clock::now();
|
||||
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
|
||||
Debug(5, "sendkeepalive %d, timeout %d, now: %" PRIi64 " last: %" PRIi64 " since: %" PRIi64,
|
||||
Debug(5, "sendkeepalive %d, timeout %" PRIi64 " s, now: %" PRIi64 " s last: %" PRIi64 " s since: %" PRIi64 "s ",
|
||||
sendKeepalive,
|
||||
timeout,
|
||||
static_cast<int64>(now),
|
||||
static_cast<int64>(lastKeepalive),
|
||||
static_cast<int64>(now - lastKeepalive));
|
||||
if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) ) {
|
||||
if ( !sendCommand( message ) )
|
||||
static_cast<int64>(Seconds(timeout).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(now.time_since_epoch()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(lastKeepalive.time_since_epoch()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>((now - lastKeepalive)).count()));
|
||||
|
||||
if (sendKeepalive && (timeout > Seconds(0)) && ((now - lastKeepalive) > (timeout - Seconds(5)))) {
|
||||
if (!sendCommand(message))
|
||||
return;
|
||||
lastKeepalive = now;
|
||||
}
|
||||
usleep( 100000 );
|
||||
std::this_thread::sleep_for(Microseconds(100));
|
||||
}
|
||||
#if 0
|
||||
message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
|
||||
|
@ -621,7 +633,7 @@ void RtspThread::Run() {
|
|||
RtpDataThread rtpDataThread( *this, *source );
|
||||
RtpCtrlThread rtpCtrlThread( *this, *source );
|
||||
|
||||
zm::Select select(double(config.http_timeout)/1000.0 );
|
||||
zm::Select select(Milliseconds(config.http_timeout));
|
||||
select.addReader( &mRtspSocket );
|
||||
|
||||
Buffer buffer( ZM_NETWORK_BUFSIZ );
|
||||
|
@ -694,21 +706,23 @@ void RtspThread::Run() {
|
|||
}
|
||||
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
|
||||
// FIXME: Is this really necessary when using tcp ?
|
||||
now = time(nullptr);
|
||||
now = std::chrono::steady_clock::now();
|
||||
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
|
||||
Debug(5, "sendkeepalive %d, timeout %d, now: %" PRIi64 " last: %" PRIi64 " since: %" PRIi64,
|
||||
Debug(5, "sendkeepalive %d, timeout %" PRIi64 " s, now: %" PRIi64 " s last: %" PRIi64 " s since: %" PRIi64 " s",
|
||||
sendKeepalive,
|
||||
timeout,
|
||||
static_cast<int64>(now),
|
||||
static_cast<int64>(lastKeepalive),
|
||||
static_cast<int64>(now - lastKeepalive));
|
||||
if ( sendKeepalive && (timeout > 0) && ((now-lastKeepalive) > (timeout-5)) )
|
||||
{
|
||||
if ( !sendCommand( message ) )
|
||||
static_cast<int64>(Seconds(timeout).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(now.time_since_epoch()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(lastKeepalive.time_since_epoch()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>((now - lastKeepalive)).count()));
|
||||
|
||||
if (sendKeepalive && (timeout > Seconds(0)) && ((now - lastKeepalive) > (timeout - Seconds(5)))) {
|
||||
if (!sendCommand(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastKeepalive = now;
|
||||
}
|
||||
buffer.tidy( 1 );
|
||||
buffer.tidy(true);
|
||||
}
|
||||
#if 0
|
||||
message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
|
||||
|
@ -737,12 +751,14 @@ void RtspThread::Run() {
|
|||
|
||||
while (!mTerminate) {
|
||||
// Send a keepalive message if the server supports this feature and we are close to the timeout expiration
|
||||
if ( sendKeepalive && (timeout > 0) && ((time(nullptr)-lastKeepalive) > (timeout-5)) ) {
|
||||
if ( !sendCommand( message ) )
|
||||
if (sendKeepalive && (timeout > Seconds(0))
|
||||
&& ((std::chrono::steady_clock::now() - lastKeepalive) > (timeout - Seconds(5)))) {
|
||||
if (!sendCommand(message)) {
|
||||
return;
|
||||
lastKeepalive = time(nullptr);
|
||||
}
|
||||
lastKeepalive = std::chrono::steady_clock::now();
|
||||
}
|
||||
usleep(100000);
|
||||
std::this_thread::sleep_for(Microseconds(100));
|
||||
}
|
||||
#if 0
|
||||
message = "PAUSE "+mUrl+" RTSP/1.0\r\nSession: "+session+"\r\n";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -21,32 +21,33 @@
|
|||
|
||||
#include "zm_db.h"
|
||||
#include "zm_logger.h"
|
||||
#include "zm_utils.h"
|
||||
#include <cstring>
|
||||
|
||||
Storage::Storage() : id(0) {
|
||||
Warning("Instantiating default Storage Object. Should not happen.");
|
||||
strcpy(name, "Default");
|
||||
if ( staticConfig.DIR_EVENTS[0] != '/' ) {
|
||||
Warning("Instantiating default Storage Object. Should not happen.");
|
||||
strcpy(name, "Default");
|
||||
if (staticConfig.DIR_EVENTS[0] != '/') {
|
||||
// not using an absolute path. Make it one by appending ZM_PATH_WEB
|
||||
snprintf(path, sizeof(path), "%s/%s",
|
||||
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
|
||||
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
|
||||
} else {
|
||||
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1);
|
||||
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path) - 1);
|
||||
}
|
||||
scheme = MEDIUM;
|
||||
scheme_str = "Medium";
|
||||
}
|
||||
|
||||
Storage::Storage(MYSQL_ROW &dbrow) {
|
||||
unsigned int index = 0;
|
||||
id = atoi(dbrow[index++]);
|
||||
strncpy(name, dbrow[index++], sizeof(name)-1);
|
||||
strncpy(path, dbrow[index++], sizeof(path)-1);
|
||||
unsigned int index = 0;
|
||||
id = atoi(dbrow[index++]);
|
||||
strncpy(name, dbrow[index++], sizeof(name) - 1);
|
||||
strncpy(path, dbrow[index++], sizeof(path) - 1);
|
||||
type_str = std::string(dbrow[index++]);
|
||||
scheme_str = std::string(dbrow[index++]);
|
||||
if ( scheme_str == "Deep" ) {
|
||||
if (scheme_str == "Deep") {
|
||||
scheme = DEEP;
|
||||
} else if ( scheme_str == "Medium" ) {
|
||||
} else if (scheme_str == "Medium") {
|
||||
scheme = MEDIUM;
|
||||
} else {
|
||||
scheme = SHALLOW;
|
||||
|
@ -55,44 +56,42 @@ Storage::Storage(MYSQL_ROW &dbrow) {
|
|||
|
||||
/* If a zero or invalid p_id is passed, then the old default path will be assumed. */
|
||||
Storage::Storage(unsigned int p_id) : id(p_id) {
|
||||
|
||||
if ( id ) {
|
||||
char sql[ZM_SQL_SML_BUFSIZ];
|
||||
snprintf(sql, sizeof(sql), "SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%u", id);
|
||||
Debug(2, "Loading Storage for %u using %s", id, sql);
|
||||
zmDbRow dbrow;
|
||||
if ( !dbrow.fetch(sql) ) {
|
||||
Error("Unable to load storage area for id %d: %s", id, mysql_error(&dbconn));
|
||||
} else {
|
||||
unsigned int index = 0;
|
||||
id = atoi(dbrow[index++]);
|
||||
strncpy(name, dbrow[index++], sizeof(name)-1);
|
||||
strncpy(path, dbrow[index++], sizeof(path)-1);
|
||||
if (id) {
|
||||
std::string sql = stringtf("SELECT `Id`, `Name`, `Path`, `Type`, `Scheme` FROM `Storage` WHERE `Id`=%u", id);
|
||||
Debug(2, "Loading Storage for %u using %s", id, sql.c_str());
|
||||
zmDbRow dbrow;
|
||||
if (!dbrow.fetch(sql)) {
|
||||
Error("Unable to load storage area for id %d: %s", id, mysql_error(&dbconn));
|
||||
} else {
|
||||
unsigned int index = 0;
|
||||
id = atoi(dbrow[index++]);
|
||||
strncpy(name, dbrow[index++], sizeof(name) - 1);
|
||||
strncpy(path, dbrow[index++], sizeof(path) - 1);
|
||||
type_str = std::string(dbrow[index++]);
|
||||
scheme_str = std::string(dbrow[index++]);
|
||||
if ( scheme_str == "Deep" ) {
|
||||
if (scheme_str == "Deep") {
|
||||
scheme = DEEP;
|
||||
} else if ( scheme_str == "Medium" ) {
|
||||
} else if (scheme_str == "Medium") {
|
||||
scheme = MEDIUM;
|
||||
} else {
|
||||
scheme = SHALLOW;
|
||||
}
|
||||
Debug(1, "Loaded Storage area %d '%s'", id, name);
|
||||
}
|
||||
}
|
||||
if ( !id ) {
|
||||
if ( staticConfig.DIR_EVENTS[0] != '/' ) {
|
||||
Debug(1, "Loaded Storage area %d '%s'", id, name);
|
||||
}
|
||||
}
|
||||
if (!id) {
|
||||
if (staticConfig.DIR_EVENTS[0] != '/') {
|
||||
// not using an absolute path. Make it one by appending ZM_PATH_WEB
|
||||
snprintf(path, sizeof(path), "%s/%s",
|
||||
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
|
||||
staticConfig.PATH_WEB.c_str(), staticConfig.DIR_EVENTS.c_str());
|
||||
} else {
|
||||
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path)-1);
|
||||
strncpy(path, staticConfig.DIR_EVENTS.c_str(), sizeof(path) - 1);
|
||||
}
|
||||
Debug(1, "No id passed to Storage constructor. Using default path %s instead", path);
|
||||
strcpy(name, "Default");
|
||||
Debug(1, "No id passed to Storage constructor. Using default path %s instead", path);
|
||||
strcpy(name, "Default");
|
||||
scheme = MEDIUM;
|
||||
scheme_str = "Medium";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Storage::~Storage() {
|
||||
|
|
|
@ -254,18 +254,32 @@ Image *StreamBase::prepareImage(Image *image) {
|
|||
} // end Image *StreamBase::prepareImage(Image *image)
|
||||
|
||||
bool StreamBase::sendTextFrame(const char *frame_text) {
|
||||
Debug(2, "Sending %dx%d * %d text frame '%s'",
|
||||
monitor->Width(), monitor->Height(), scale, frame_text);
|
||||
int width = 640;
|
||||
int height = 480;
|
||||
int colours = ZM_COLOUR_RGB32;
|
||||
int subpixelorder = ZM_SUBPIX_ORDER_RGBA;
|
||||
int labelsize = 2;
|
||||
|
||||
Image image(monitor->Width(), monitor->Height(), monitor->Colours(), monitor->SubpixelOrder());
|
||||
image.Clear();
|
||||
image.Annotate(frame_text, image.centreCoord(frame_text, monitor->LabelSize()), monitor->LabelSize());
|
||||
|
||||
if ( scale != 100 ) {
|
||||
image.Scale(scale);
|
||||
if (monitor) {
|
||||
width = monitor->Width();
|
||||
height = monitor->Height();
|
||||
colours = monitor->Colours();
|
||||
subpixelorder = monitor->SubpixelOrder();
|
||||
labelsize = monitor->LabelSize();
|
||||
}
|
||||
if ( type == STREAM_MPEG ) {
|
||||
if ( !vid_stream ) {
|
||||
Debug(2, "Sending %dx%dx%dx%d * %d scale text frame '%s'",
|
||||
width, height, colours, subpixelorder, scale, frame_text);
|
||||
|
||||
Image image(width, height, colours, subpixelorder);
|
||||
image.Clear();
|
||||
image.Annotate(frame_text, image.centreCoord(frame_text, labelsize), labelsize);
|
||||
|
||||
if (scale != 100) {
|
||||
image.Scale(scale);
|
||||
Debug(2, "Scaled to %dx%d", image.Width(), image.Height());
|
||||
}
|
||||
if (type == STREAM_MPEG) {
|
||||
if (!vid_stream) {
|
||||
vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, image.Colours(), image.SubpixelOrder(), image.Width(), image.Height());
|
||||
fprintf(stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType());
|
||||
vid_stream->OpenStream();
|
||||
|
@ -276,10 +290,11 @@ bool StreamBase::sendTextFrame(const char *frame_text) {
|
|||
int n_bytes = 0;
|
||||
|
||||
image.EncodeJpeg(buffer, &n_bytes);
|
||||
Debug(4, "Encoded to %d bytes", n_bytes);
|
||||
|
||||
fputs("--" BOUNDARY "\r\nContent-Type: image/jpeg\r\n", stdout);
|
||||
fprintf(stdout, "Content-Length: %d\r\n\r\n", n_bytes);
|
||||
if ( fwrite(buffer, n_bytes, 1, stdout) != 1 ) {
|
||||
if (fwrite(buffer, n_bytes, 1, stdout) != 1) {
|
||||
Error("Unable to send stream text frame: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -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 } };
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
115
src/zmc.cpp
115
src/zmc.cpp
|
@ -64,8 +64,6 @@ possible, this should run at more or less constant speed.
|
|||
#include "zm_time.h"
|
||||
#include "zm_utils.h"
|
||||
#include <getopt.h>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
void Usage() {
|
||||
fprintf(stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n");
|
||||
|
@ -192,11 +190,11 @@ int main(int argc, char *argv[]) {
|
|||
HwCapsDetect();
|
||||
|
||||
std::vector<std::shared_ptr<Monitor>> monitors;
|
||||
#if ZM_HAS_V4L
|
||||
#if ZM_HAS_V4L2
|
||||
if ( device[0] ) {
|
||||
monitors = Monitor::LoadLocalMonitors(device, Monitor::CAPTURE);
|
||||
} else
|
||||
#endif // ZM_HAS_V4L
|
||||
#endif // ZM_HAS_V4L2
|
||||
if ( host[0] ) {
|
||||
if ( !port )
|
||||
port = "80";
|
||||
|
@ -214,7 +212,7 @@ int main(int argc, char *argv[]) {
|
|||
Error("No monitors found");
|
||||
exit(-1);
|
||||
} else {
|
||||
Debug(2, "%zu monitors loaded", monitors.size());
|
||||
Debug(2, "%zu monitors loaded", monitors.size());
|
||||
}
|
||||
|
||||
Info("Starting Capture version %s", ZM_VERSION);
|
||||
|
@ -234,7 +232,6 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
while (!zm_terminate) {
|
||||
result = 0;
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
|
||||
for (const std::shared_ptr<Monitor> &monitor : monitors) {
|
||||
monitor->LoadCamera();
|
||||
|
@ -242,52 +239,51 @@ int main(int argc, char *argv[]) {
|
|||
if (!monitor->connect()) {
|
||||
Warning("Couldn't connect to monitor %d", monitor->Id());
|
||||
}
|
||||
time_t now = (time_t)time(nullptr);
|
||||
monitor->setStartupTime(now);
|
||||
monitor->setHeartbeatTime(now);
|
||||
SystemTimePoint now = std::chrono::system_clock::now();
|
||||
monitor->SetStartupTime(now);
|
||||
monitor->SetHeartbeatTime(now);
|
||||
|
||||
snprintf(sql, sizeof(sql),
|
||||
std::string sql = stringtf(
|
||||
"INSERT INTO Monitor_Status (MonitorId,Status,CaptureFPS,AnalysisFPS)"
|
||||
" VALUES (%u, 'Running',0,0) ON DUPLICATE KEY UPDATE Status='Running',CaptureFPS=0,AnalysisFPS=0",
|
||||
monitor->Id());
|
||||
zmDbDo(sql);
|
||||
|
||||
int sleep_time = 0;
|
||||
Seconds sleep_time = Seconds(0);
|
||||
while (monitor->PrimeCapture() <= 0) {
|
||||
if (prime_capture_log_count % 60) {
|
||||
logPrintf(Logger::ERROR+monitor->Importance(),
|
||||
"Failed to prime capture of initial monitor");
|
||||
logPrintf(Logger::ERROR + monitor->Importance(),
|
||||
"Failed to prime capture of initial monitor");
|
||||
} else {
|
||||
Debug(1, "Failed to prime capture of initial monitor");
|
||||
}
|
||||
prime_capture_log_count ++;
|
||||
if (zm_terminate) break;
|
||||
if (sleep_time < 60) sleep_time++;
|
||||
sleep(sleep_time);
|
||||
}
|
||||
if (zm_terminate) break;
|
||||
|
||||
snprintf(sql, sizeof(sql),
|
||||
prime_capture_log_count++;
|
||||
if (zm_terminate) {
|
||||
break;
|
||||
}
|
||||
if (sleep_time < Seconds(60)) {
|
||||
sleep_time++;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(sleep_time);
|
||||
}
|
||||
if (zm_terminate){
|
||||
break;
|
||||
}
|
||||
|
||||
sql = stringtf(
|
||||
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'Connected') ON DUPLICATE KEY UPDATE Status='Connected'",
|
||||
monitor->Id());
|
||||
monitor->Id());
|
||||
zmDbDo(sql);
|
||||
} // end foreach monitor
|
||||
if (zm_terminate) break;
|
||||
|
||||
int *capture_delays = new int[monitors.size()];
|
||||
int *alarm_capture_delays = new int[monitors.size()];
|
||||
struct timeval * last_capture_times = new struct timeval[monitors.size()];
|
||||
|
||||
for (size_t i = 0; i < monitors.size(); i++) {
|
||||
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
|
||||
capture_delays[i] = monitors[i]->GetCaptureDelay();
|
||||
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
|
||||
Debug(2, "capture delay(%u mSecs 1000/capture_fps) alarm delay(%u)",
|
||||
capture_delays[i], alarm_capture_delays[i]);
|
||||
if (zm_terminate){
|
||||
break;
|
||||
}
|
||||
|
||||
timeval now;
|
||||
int sleep_time = 0;
|
||||
std::vector<SystemTimePoint> last_capture_times = std::vector<SystemTimePoint>(monitors.size());
|
||||
Microseconds sleep_time = Microseconds(0);
|
||||
|
||||
while (!zm_terminate) {
|
||||
//sigprocmask(SIG_BLOCK, &block_set, 0);
|
||||
|
@ -314,30 +310,28 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
// capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate.
|
||||
int delay = (monitors[i]->GetState() == Monitor::ALARM) ? alarm_capture_delays[i] : capture_delays[i];
|
||||
if (delay) {
|
||||
gettimeofday(&now, nullptr);
|
||||
if (last_capture_times[i].tv_sec) {
|
||||
Microseconds delta_time = zm::chrono::duration_cast<Microseconds>(now)
|
||||
- zm::chrono::duration_cast<Microseconds>(last_capture_times[i]);
|
||||
Microseconds delay = (monitors[i]->GetState() == Monitor::ALARM) ? monitors[i]->GetAlarmCaptureDelay()
|
||||
: monitors[i]->GetCaptureDelay();
|
||||
if (delay != Seconds(0)) {
|
||||
SystemTimePoint now = std::chrono::system_clock::now();
|
||||
if (last_capture_times[i].time_since_epoch() != Seconds(0)) {
|
||||
Microseconds delta_time = std::chrono::duration_cast<Microseconds>(now - last_capture_times[i]);
|
||||
|
||||
// You have to add back in the previous sleep time
|
||||
sleep_time = delay - (delta_time.count() - sleep_time);
|
||||
sleep_time = delay - (delta_time - sleep_time);
|
||||
Debug(4,
|
||||
"Sleep time is %d from now: %" PRIi64 ".%" PRIi64" last: %" PRIi64 ".% " PRIi64 " delta % " PRIi64 " delay: %d",
|
||||
sleep_time,
|
||||
static_cast<int64>(now.tv_sec),
|
||||
static_cast<int64>(now.tv_usec),
|
||||
static_cast<int64>(last_capture_times[i].tv_sec),
|
||||
static_cast<int64>(last_capture_times[i].tv_usec),
|
||||
"Sleep time is %" PRIi64 " from now: %.2f s last: %.2f s delta % " PRIi64 " us delay: %" PRIi64 " us",
|
||||
static_cast<int64>(Microseconds(sleep_time).count()),
|
||||
FPSeconds(now.time_since_epoch()).count(),
|
||||
FPSeconds(last_capture_times[i].time_since_epoch()).count(),
|
||||
static_cast<int64>(delta_time.count()),
|
||||
delay);
|
||||
static_cast<int64>(Microseconds(delay).count()));
|
||||
|
||||
if (sleep_time > 0) {
|
||||
Debug(4, "usleeping (%d)", sleep_time);
|
||||
usleep(sleep_time);
|
||||
if (sleep_time > Seconds(0)) {
|
||||
std::this_thread::sleep_for(sleep_time);
|
||||
}
|
||||
} // end if has a last_capture time
|
||||
|
||||
last_capture_times[i] = now;
|
||||
} // end if delay
|
||||
} // end foreach n_monitors
|
||||
|
@ -348,30 +342,25 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
} // end while ! zm_terminate and connected
|
||||
|
||||
for (size_t i = 0; i < monitors.size(); i++) {
|
||||
monitors[i]->Close();
|
||||
monitors[i]->disconnect();
|
||||
for (std::shared_ptr<Monitor> & monitor : monitors) {
|
||||
monitor->Close();
|
||||
monitor->disconnect();
|
||||
}
|
||||
|
||||
delete [] alarm_capture_delays;
|
||||
delete [] capture_delays;
|
||||
delete [] last_capture_times;
|
||||
|
||||
if (zm_reload) {
|
||||
for (std::shared_ptr<Monitor> &monitor : monitors) {
|
||||
monitor->Reload();
|
||||
}
|
||||
logTerm();
|
||||
logInit(log_id_string);
|
||||
|
||||
|
||||
zm_reload = false;
|
||||
} // end if zm_reload
|
||||
} // end while ! zm_terminate outer connection loop
|
||||
|
||||
for (std::shared_ptr<Monitor> &monitor : monitors) {
|
||||
static char sql[ZM_SQL_SML_BUFSIZ];
|
||||
snprintf(sql, sizeof(sql),
|
||||
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'NotRunning') ON DUPLICATE KEY UPDATE Status='NotRunning'",
|
||||
std::string sql = stringtf(
|
||||
"INSERT INTO Monitor_Status (MonitorId,Status) VALUES (%u, 'NotRunning') ON DUPLICATE KEY UPDATE Status='NotRunning'",
|
||||
monitor->Id());
|
||||
zmDbDo(sql);
|
||||
}
|
||||
|
@ -382,5 +371,5 @@ int main(int argc, char *argv[]) {
|
|||
dbQueue.stop();
|
||||
zmDbClose();
|
||||
|
||||
return zm_terminate ? 0 : result;
|
||||
return zm_terminate ? 0 : result;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
148
src/zmu.cpp
148
src/zmu.cpp
|
@ -197,6 +197,7 @@ bool ValidateAccess(User *user, int mon_id, int function) {
|
|||
|
||||
void exit_zmu(int exit_code) {
|
||||
logTerm();
|
||||
dbQueue.stop();
|
||||
zmDbClose();
|
||||
|
||||
exit(exit_code);
|
||||
|
@ -248,7 +249,7 @@ int main(int argc, char *argv[]) {
|
|||
{nullptr, 0, nullptr, 0}
|
||||
};
|
||||
|
||||
const char *device = nullptr;
|
||||
std::string device;
|
||||
int mon_id = 0;
|
||||
bool verbose = false;
|
||||
int function = ZMU_BOGUS;
|
||||
|
@ -256,28 +257,35 @@ int main(int argc, char *argv[]) {
|
|||
int image_idx = -1;
|
||||
int scale = -1;
|
||||
int brightness = -1;
|
||||
bool have_brightness = false;
|
||||
|
||||
int contrast = -1;
|
||||
bool have_contrast = false;
|
||||
|
||||
int hue = -1;
|
||||
bool have_hue = false;
|
||||
int colour = -1;
|
||||
bool have_colour = false;
|
||||
|
||||
char *zoneString = nullptr;
|
||||
char *username = nullptr;
|
||||
char *password = nullptr;
|
||||
char *auth = nullptr;
|
||||
std::string jwt_token_str = "";
|
||||
#if ZM_HAS_V4L
|
||||
#if ZM_HAS_V4L2
|
||||
int v4lVersion = 2;
|
||||
#endif // ZM_HAS_V4L
|
||||
#endif // ZM_HAS_V4L2
|
||||
while (1) {
|
||||
int option_index = 0;
|
||||
|
||||
int c = getopt_long(argc, argv, "d:m:vsEDLurwei::S:t::fz::ancqhlB::C::H::O::RWU:P:A:V:T:", long_options, &option_index);
|
||||
if ( c == -1 ) {
|
||||
if (c == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 'd':
|
||||
if ( optarg )
|
||||
if (optarg)
|
||||
device = optarg;
|
||||
break;
|
||||
case 'm':
|
||||
|
@ -291,7 +299,7 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
case 'i':
|
||||
function |= ZMU_IMAGE;
|
||||
if ( optarg )
|
||||
if (optarg)
|
||||
image_idx = atoi(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
|
@ -299,7 +307,7 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
case 't':
|
||||
function |= ZMU_TIME;
|
||||
if ( optarg )
|
||||
if (optarg)
|
||||
image_idx = atoi(optarg);
|
||||
break;
|
||||
case 'R':
|
||||
|
@ -316,7 +324,7 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
case 'z':
|
||||
function |= ZMU_ZONES;
|
||||
if ( optarg )
|
||||
if (optarg)
|
||||
zoneString = optarg;
|
||||
break;
|
||||
case 'a':
|
||||
|
@ -348,23 +356,31 @@ int main(int argc, char *argv[]) {
|
|||
break;
|
||||
case 'B':
|
||||
function |= ZMU_BRIGHTNESS;
|
||||
if ( optarg )
|
||||
if (optarg) {
|
||||
have_brightness = true;
|
||||
brightness = atoi(optarg);
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
function |= ZMU_CONTRAST;
|
||||
if ( optarg )
|
||||
if (optarg) {
|
||||
have_contrast = true;
|
||||
contrast = atoi(optarg);
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
function |= ZMU_HUE;
|
||||
if ( optarg )
|
||||
if (optarg) {
|
||||
have_hue = true;
|
||||
hue = atoi(optarg);
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
function |= ZMU_COLOUR;
|
||||
if ( optarg )
|
||||
if (optarg) {
|
||||
have_colour = true;
|
||||
colour = atoi(optarg);
|
||||
}
|
||||
break;
|
||||
case 'U':
|
||||
username = optarg;
|
||||
|
@ -378,11 +394,11 @@ int main(int argc, char *argv[]) {
|
|||
case 'T':
|
||||
jwt_token_str = std::string(optarg);
|
||||
break;
|
||||
#if ZM_HAS_V4L
|
||||
#if ZM_HAS_V4L2
|
||||
case 'V':
|
||||
v4lVersion = (atoi(optarg)==1)?1:2;
|
||||
break;
|
||||
#endif // ZM_HAS_V4L
|
||||
#endif // ZM_HAS_V4L2
|
||||
case 'h':
|
||||
case '?':
|
||||
Usage(0);
|
||||
|
@ -404,7 +420,7 @@ int main(int argc, char *argv[]) {
|
|||
Usage();
|
||||
}
|
||||
|
||||
if ( device && !(function&ZMU_QUERY) ) {
|
||||
if ( !device.empty() && !(function&ZMU_QUERY) ) {
|
||||
fprintf(stderr, "Error, -d option cannot be used with this option\n");
|
||||
Usage();
|
||||
}
|
||||
|
@ -498,20 +514,27 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
if ( function & ZMU_TIME ) {
|
||||
struct timeval timestamp = monitor->GetTimestamp(image_idx);
|
||||
if ( verbose ) {
|
||||
SystemTimePoint timestamp = monitor->GetTimestamp(image_idx);
|
||||
if (verbose) {
|
||||
char timestamp_str[64] = "None";
|
||||
if ( timestamp.tv_sec ) {
|
||||
if (timestamp.time_since_epoch() != Seconds(0)) {
|
||||
tm tm_info = {};
|
||||
strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S", localtime_r(×tamp.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(×tamp_t, &tm_info));
|
||||
}
|
||||
Seconds ts_sec = std::chrono::duration_cast<Seconds>(timestamp.time_since_epoch());
|
||||
Microseconds ts_usec = std::chrono::duration_cast<Microseconds>(timestamp.time_since_epoch() - ts_sec);
|
||||
if (image_idx == -1) {
|
||||
printf("Time of last image capture: %s.%02d\n", timestamp_str, static_cast<int32>(ts_usec.count()));
|
||||
}
|
||||
else {
|
||||
printf("Time of image %d capture: %s.%02d\n", image_idx, timestamp_str, static_cast<int32>(ts_usec.count()));
|
||||
}
|
||||
if ( image_idx == -1 )
|
||||
printf("Time of last image capture: %s.%02ld\n", timestamp_str, timestamp.tv_usec/10000);
|
||||
else
|
||||
printf("Time of image %d capture: %s.%02ld\n", image_idx, timestamp_str, timestamp.tv_usec/10000);
|
||||
} else {
|
||||
if ( have_output ) fputc(separator, stdout);
|
||||
printf("%ld.%02ld", timestamp.tv_sec, timestamp.tv_usec/10000);
|
||||
if (have_output) {
|
||||
fputc(separator, stdout);
|
||||
}
|
||||
printf("%.2f", FPSeconds(timestamp.time_since_epoch()).count());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
|
@ -585,13 +608,16 @@ int main(int argc, char *argv[]) {
|
|||
// Ensure that we are not recording. So the forced alarm is distinct from what was recording before
|
||||
monitor->ForceAlarmOff();
|
||||
monitor->ForceAlarmOn(config.forced_alarm_score, "Forced Web");
|
||||
int wait = 10*1000*1000; // 10 seconds
|
||||
while ((monitor->GetState() != Monitor::ALARM) and !zm_terminate and wait) {
|
||||
|
||||
Microseconds wait_time = Seconds(10);
|
||||
while ((monitor->GetState() != Monitor::ALARM) and !zm_terminate and wait_time > Seconds(0)) {
|
||||
// Wait for monitor to notice.
|
||||
usleep(1000);
|
||||
wait -= 1000;
|
||||
Microseconds sleep = Microseconds(1);
|
||||
std::this_thread::sleep_for(sleep);
|
||||
wait_time -= sleep;
|
||||
}
|
||||
if ( monitor->GetState() != Monitor::ALARM and !wait ) {
|
||||
|
||||
if (monitor->GetState() != Monitor::ALARM and wait_time == Seconds(0)) {
|
||||
Error("Monitor failed to respond to forced alarm.");
|
||||
} else {
|
||||
printf("Alarmed event id: %" PRIu64 "\n", monitor->GetLastEventId());
|
||||
|
@ -639,60 +665,60 @@ int main(int argc, char *argv[]) {
|
|||
monitor->DumpSettings(monString, verbose);
|
||||
printf("%s\n", monString);
|
||||
}
|
||||
if ( function & ZMU_BRIGHTNESS ) {
|
||||
if ( verbose ) {
|
||||
if ( brightness >= 0 )
|
||||
if (function & ZMU_BRIGHTNESS) {
|
||||
if (verbose) {
|
||||
if (have_brightness)
|
||||
printf("New brightness: %d\n", monitor->actionBrightness(brightness));
|
||||
else
|
||||
printf("Current brightness: %d\n", monitor->actionBrightness());
|
||||
} else {
|
||||
if ( have_output ) fputc(separator, stdout);
|
||||
if ( brightness >= 0 )
|
||||
if (have_output) fputc(separator, stdout);
|
||||
if (have_brightness)
|
||||
printf("%d", monitor->actionBrightness(brightness));
|
||||
else
|
||||
printf("%d", monitor->actionBrightness());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_CONTRAST ) {
|
||||
if ( verbose ) {
|
||||
if ( contrast >= 0 )
|
||||
printf("New brightness: %d\n", monitor->actionContrast(contrast));
|
||||
if (function & ZMU_CONTRAST) {
|
||||
if (verbose) {
|
||||
if (have_contrast)
|
||||
printf("New contrast: %d\n", monitor->actionContrast(contrast));
|
||||
else
|
||||
printf("Current contrast: %d\n", monitor->actionContrast());
|
||||
} else {
|
||||
if ( have_output ) fputc(separator, stdout);
|
||||
if ( contrast >= 0 )
|
||||
if (have_output) fputc(separator, stdout);
|
||||
if (have_contrast)
|
||||
printf("%d", monitor->actionContrast(contrast));
|
||||
else
|
||||
printf("%d", monitor->actionContrast());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_HUE ) {
|
||||
if ( verbose ) {
|
||||
if ( hue >= 0 )
|
||||
if (function & ZMU_HUE) {
|
||||
if (verbose) {
|
||||
if (have_hue)
|
||||
printf("New hue: %d\n", monitor->actionHue(hue));
|
||||
else
|
||||
printf("Current hue: %d\n", monitor->actionHue());
|
||||
} else {
|
||||
if ( have_output ) fputc(separator, stdout);
|
||||
if ( hue >= 0 )
|
||||
if (have_output) fputc(separator, stdout);
|
||||
if (have_hue)
|
||||
printf("%d", monitor->actionHue(hue));
|
||||
else
|
||||
printf("%d", monitor->actionHue());
|
||||
have_output = true;
|
||||
}
|
||||
}
|
||||
if ( function & ZMU_COLOUR ) {
|
||||
if ( verbose ) {
|
||||
if ( colour >= 0 )
|
||||
if (function & ZMU_COLOUR) {
|
||||
if (verbose) {
|
||||
if (have_colour)
|
||||
printf("New colour: %d\n", monitor->actionColour(colour));
|
||||
else
|
||||
printf("Current colour: %d\n", monitor->actionColour());
|
||||
} else {
|
||||
if ( have_output ) fputc(separator, stdout);
|
||||
if ( colour >= 0 )
|
||||
if (have_output) fputc(separator, stdout);
|
||||
if (have_colour)
|
||||
printf("%d", monitor->actionColour(colour));
|
||||
else
|
||||
printf("%d", monitor->actionColour());
|
||||
|
@ -700,7 +726,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
}
|
||||
|
||||
if ( have_output ) {
|
||||
if (have_output) {
|
||||
printf("\n");
|
||||
}
|
||||
if ( !function ) {
|
||||
|
@ -708,15 +734,15 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
} else { // non monitor functions
|
||||
if ( function & ZMU_QUERY ) {
|
||||
#if ZM_HAS_V4L
|
||||
#if ZM_HAS_V4L2
|
||||
char vidString[0x10000] = "";
|
||||
bool ok = LocalCamera::GetCurrentSettings(device, vidString, v4lVersion, verbose);
|
||||
printf("%s", vidString);
|
||||
exit_zmu(ok ? 0 : -1);
|
||||
#else // ZM_HAS_V4L
|
||||
#else // ZM_HAS_V4L2
|
||||
Error("Video4linux is required for device querying");
|
||||
exit_zmu(-1);
|
||||
#endif // ZM_HAS_V4L
|
||||
#endif // ZM_HAS_V4L2
|
||||
}
|
||||
|
||||
if ( function & ZMU_LIST ) {
|
||||
|
@ -726,7 +752,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
sql += " ORDER BY Id ASC";
|
||||
|
||||
MYSQL_RES *result = zmDbFetch(sql.c_str());
|
||||
MYSQL_RES *result = zmDbFetch(sql);
|
||||
if (!result) {
|
||||
exit_zmu(-1);
|
||||
}
|
||||
|
@ -740,13 +766,14 @@ int main(int argc, char *argv[]) {
|
|||
if ( monitor_function > 1 ) {
|
||||
std::shared_ptr<Monitor> monitor = Monitor::Load(monitor_id, false, Monitor::QUERY);
|
||||
if ( monitor && monitor->connect() ) {
|
||||
struct timeval tv = monitor->GetTimestamp();
|
||||
printf( "%4d%5d%6d%9d%11ld.%02ld%6d%6d%8" PRIu64 "%8.2f\n",
|
||||
SystemTimePoint timestamp = monitor->GetTimestamp();
|
||||
|
||||
printf( "%4d%5d%6d%9d%14.2f%6d%6d%8" PRIu64 "%8.2f\n",
|
||||
monitor->Id(),
|
||||
monitor_function,
|
||||
monitor->GetState(),
|
||||
monitor->GetTriggerState(),
|
||||
tv.tv_sec, tv.tv_usec/10000,
|
||||
FPSeconds(timestamp.time_since_epoch()).count(),
|
||||
monitor->GetLastReadIndex(),
|
||||
monitor->GetLastWriteIndex(),
|
||||
monitor->GetLastEventId(),
|
||||
|
@ -754,13 +781,12 @@ int main(int argc, char *argv[]) {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
struct timeval tv = { 0, 0 };
|
||||
printf("%4d%5d%6d%9d%11ld.%02ld%6d%6d%8d%8.2f\n",
|
||||
mon_id,
|
||||
function,
|
||||
0,
|
||||
0,
|
||||
tv.tv_sec, tv.tv_usec/10000,
|
||||
0l, 0l,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
|
|
@ -88,7 +88,7 @@ fi;
|
|||
|
||||
if [ "$DISTROS" == "" ]; then
|
||||
if [ "$RELEASE" != "" ]; then
|
||||
DISTROS="bionic,focal,groovy,hirsute"
|
||||
DISTROS="bionic,focal,hirsute,impish"
|
||||
else
|
||||
DISTROS=`lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'`;
|
||||
fi;
|
||||
|
@ -120,6 +120,11 @@ else
|
|||
if [ "$BRANCH" == "" ]; then
|
||||
#REV=$(git rev-list --tags --max-count=1)
|
||||
BRANCH=`git describe --tags $(git rev-list --tags --max-count=1)`;
|
||||
if [ -z "$BRANCH" ]; then
|
||||
# This should only happen in CI environments where tag info isn't available
|
||||
BRANCH=`cat version`
|
||||
echo "Building branch $BRANCH"
|
||||
fi
|
||||
if [ "$BRANCH" == "" ]; then
|
||||
echo "Unable to determine latest stable branch!"
|
||||
exit 0;
|
||||
|
@ -224,6 +229,7 @@ rm -rf .git
|
|||
rm .gitignore
|
||||
cd ../
|
||||
|
||||
|
||||
if [ ! -e "$DIRECTORY.orig.tar.gz" ]; then
|
||||
tar zcf $DIRECTORY.orig.tar.gz $DIRECTORY.orig
|
||||
fi;
|
||||
|
@ -237,11 +243,10 @@ IFS=',' ;for DISTRO in `echo "$DISTROS"`; do
|
|||
fi;
|
||||
|
||||
# Generate Changlog
|
||||
if [ "$DISTRO" == "focal" ] || [ "$DISTRO" == "buster" ] || [ "$DISTRO" == "hirsute" ]; then
|
||||
cp -Rpd distros/ubuntu2004 debian
|
||||
elif [ "$DISTRO" == "beowulf" ]
|
||||
then
|
||||
if [ "$DISTRO" == "beowulf" ]; then
|
||||
cp -Rpd distros/beowulf debian
|
||||
else
|
||||
cp -Rpd distros/ubuntu2004 debian
|
||||
fi;
|
||||
|
||||
if [ "$DEBEMAIL" != "" ] && [ "$DEBFULLNAME" != "" ]; then
|
||||
|
@ -291,24 +296,29 @@ zoneminder ($VERSION-$DISTRO${PACKAGE_VERSION}) $DISTRO; urgency=$URGENCY
|
|||
EOF
|
||||
fi;
|
||||
|
||||
# Leave the .orig so that we don't pollute it when building deps
|
||||
cd ..
|
||||
if [ $TYPE == "binary" ]; then
|
||||
# Auto-install all ZoneMinder's depedencies using the Debian control file
|
||||
sudo apt-get install devscripts equivs
|
||||
sudo mk-build-deps -ir ./debian/control
|
||||
echo "Status: $?"
|
||||
DEBUILD=debuild
|
||||
# Auto-install all ZoneMinder's depedencies using the Debian control file
|
||||
sudo apt-get install devscripts equivs
|
||||
sudo mk-build-deps -ir $DIRECTORY.orig/debian/control
|
||||
echo "Status: $?"
|
||||
DEBUILD=debuild
|
||||
else
|
||||
if [ $TYPE == "local" ]; then
|
||||
# Auto-install all ZoneMinder's depedencies using the Debian control file
|
||||
sudo apt-get install devscripts equivs
|
||||
sudo mk-build-deps -ir ./debian/control
|
||||
echo "Status: $?"
|
||||
DEBUILD="debuild -i -us -uc -b"
|
||||
else
|
||||
# Source build, don't need build depends.
|
||||
DEBUILD="debuild -S -sa"
|
||||
fi;
|
||||
if [ $TYPE == "local" ]; then
|
||||
# Auto-install all ZoneMinder's depedencies using the Debian control file
|
||||
sudo apt-get install devscripts equivs
|
||||
sudo mk-build-deps -ir $DIRECTORY.orig/debian/control
|
||||
echo "Status: $?"
|
||||
DEBUILD="debuild -i -us -uc -b"
|
||||
else
|
||||
# Source build, don't need build depends.
|
||||
DEBUILD="debuild -S -sa"
|
||||
fi;
|
||||
fi;
|
||||
|
||||
cd $DIRECTORY.orig
|
||||
|
||||
if [ "$DEBSIGN_KEYID" != "" ]; then
|
||||
DEBUILD="$DEBUILD -k$DEBSIGN_KEYID"
|
||||
fi
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue