Merge branch 'master' into filter_by_runstate

This commit is contained in:
Isaac Connor 2017-06-06 20:34:47 -04:00
commit 01717a5c14
316 changed files with 41712 additions and 22667 deletions

4
.eslintignore Normal file
View File

@ -0,0 +1,4 @@
web/api/lib
web/skins/classic/js/jquery-1.11.3.js
web/skins/classic/js/jquery.js
web/tools/mootools

29
.eslintrc.js Normal file
View File

@ -0,0 +1,29 @@
"use strict";
module.exports = {
"env": {
"browser": true,
},
"extends": ["google"],
"rules": {
"brace-style": "off",
"camelcase": "off",
"comma-dangle": "off",
"key-spacing": "off",
"max-len": "off",
"new-cap": ["error", {
capIsNewExceptions: ["Error", "Warning", "Debug", "Polygon_calcArea", "Play", "Stop"],
newIsCapExceptionPattern: "^Asset\.."
}],
"no-array-constructor": "off",
"no-caller": "off",
"no-new-object": "off",
"no-unused-vars": "off",
"no-var": "off",
"object-curly-spacing": "off",
"prefer-rest-params": "off",
"quotes": "off",
"require-jsdoc": "off",
"spaced-comment": "off",
},
};

View File

@ -1,51 +1,56 @@
language: cpp
sudo: required
dist: trusty
git:
depth: 9999999
notifications:
irc: "chat.freenode.net#zoneminder-dev"
irc: chat.freenode.net#zoneminder-dev
branches:
except:
- modern
- modern
cache: ccache
addons:
sauce_connect:
username: "zoneminder"
access_key: "046ec7c1-c598-4e7e-949a-f86e725d1722"
ssh_known_hosts: zmrepo.zoneminder.com
apt:
sources:
- sourceline: ppa:iconnor/zoneminder
- key_url: http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0x4D0BF748776FFB04
packages:
- gdebi
- yum-utils
- patch
- git
- curl
- sshfs
- sed
env:
global:
- LD_LIBRARY_PATH="/usr/local/lib:/opt/libjpeg-turbo/lib:$LD_LIBRARY_PATH"
- DEB_HOST_GNU_TYPE=$(dpkg-architecture -qDEB_HOST_GNU_TYPE)
- DEB_BUILD_GNU_TYPE=$(dpkg-architecture -qDEB_BUILD_GNU_TYPE)
- CFLAGS="-DZM_FFMPEG_CVS -DHAVE_LIBCRYPTO -I/usr/local/include"
- CXXFLAGS="$CFLAGS"
matrix:
- ZM_BUILDMETHOD=cmake
- OS=el DIST=6
- OS=el DIST=7
- OS=fedora DIST=24
- OS=fedora DIST=25
- OS=ubuntu DIST=trusty
- OS=ubuntu DIST=xenial
- OS=ubuntu DIST=trusty ARCH=i386
- OS=ubuntu DIST=xenial ARCH=i386
compiler:
- gcc
- clang
- gcc
services:
- mysql
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y libpolkit-gobject-1-dev zlib1g-dev apache2 php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf cmake libjpeg-turbo8-dev apache2-mpm-prefork libapache2-mod-php5 php5-cli libtheora-dev libvorbis-dev libvpx-dev libx264-dev libvlccore-dev libvlc-dev
install:
- git clone -b n3.0 --depth=1 git://source.ffmpeg.org/ffmpeg.git
- cd ffmpeg
- ./configure --enable-shared --enable-swscale --enable-gpl --enable-libx264 --enable-libvpx --enable-libvorbis --enable-libtheora
- make -j `grep processor /proc/cpuinfo|wc -l`
- sudo make install
- sudo make install-libs
before_script:
- cd $TRAVIS_BUILD_DIR
- mysql -uroot -e "CREATE DATABASE IF NOT EXISTS zm"
- mysql -uroot -e "GRANT ALL ON zm.* TO 'zmuser'@'localhost' IDENTIFIED BY 'zmpass'";
- mysql -uroot -e "FLUSH PRIVILEGES"
- mysql
- docker
script:
- if [ "$ZM_BUILDMETHOD" = "cmake" ]; then cmake -DZM_ONVIF=OFF -DCMAKE_INSTALL_PREFIX="/usr"; fi
- make
- sudo make install
- if [ "$ZM_BUILDMETHOD" = "cmake" ]; then sudo ./zmlinkcontent.sh; fi
- mysql -uzmuser -pzmpass < db/zm_create.sql
- mysql -uzmuser -pzmpass zm < db/test.monitor.sql
- sudo zmpkg.pl start
- sudo zmfilter.pl -f purgewhenfull
- utils/packpack/startpackpack.sh
before_deploy:
- openssl aes-256-cbc -K $encrypted_62a62750aa73_key -iv $encrypted_62a62750aa73_iv -in ./utils/packpack/deploy_rsa.enc -out /tmp/deploy_rsa -d
- eval "$(ssh-agent -s)"
- chmod 600 /tmp/deploy_rsa
- ssh-add /tmp/deploy_rsa
deploy:
provider: script
skip_cleanup: true
script: utils/packpack/rsync_xfer.sh
on:
branch: master

View File

@ -66,6 +66,24 @@ set(CMAKE_CXX_FLAGS_DEBUG "-Wall -D__STDC_CONSTANT_MACROS -g")
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
# GCC below 6.0 doesn't support __target__("fpu=neon") attribute, required for compiling ARM Neon code, otherwise compilation fails.
# Must use -mfpu=neon compiler flag instead, but only do that for processors that support neon, otherwise strip the neon code alltogether,
# because passing -fmpu=neon is unsafe to processors that don't support neon
IF(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" AND CMAKE_SYSTEM_NAME MATCHES "Linux")
IF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
EXEC_PROGRAM(grep ARGS " neon " "/proc/cpuinfo" OUTPUT_VARIABLE neonoutput RETURN_VALUE neonresult)
IF(neonresult EQUAL 0)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -mfpu=neon")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -mfpu=neon")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -mfpu=neon")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -mfpu=neon")
ELSE(neonresult EQUAL 0)
add_definitions(-DZM_STRIP_NEON=1)
message(STATUS "ARM Neon is not available on this processor. Neon functions will be absent")
ENDIF(neonresult EQUAL 0)
ENDIF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
ENDIF(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" AND CMAKE_SYSTEM_NAME MATCHES "Linux")
# Modules that we need:
include (GNUInstallDirs)
include (CheckIncludeFile)
@ -85,7 +103,8 @@ mark_as_advanced(
ZM_PERL_MM_PARMS
ZM_PERL_SEARCH_PATH
ZM_TARGET_DISTRO
ZM_CONFIG_DIR)
ZM_CONFIG_DIR
ZM_SYSTEMD)
set(ZM_RUNDIR "/var/run/zm" CACHE PATH
"Location of transient process files, default: /var/run/zm")
@ -148,6 +167,8 @@ set(ZM_PERL_SEARCH_PATH "" CACHE PATH
installed outside Perl's default search path.")
set(ZM_TARGET_DISTRO "" CACHE STRING
"Build ZoneMinder for a specific distribution. Currently, valid names are: fc24, fc25, el6, el7, OS13, FreeBSD")
set(ZM_SYSTEMD "OFF" CACHE BOOL
"Set to ON to force building ZM with systemd support. default: OFF")
# Reassign some variables if a target distro has been specified
if((ZM_TARGET_DISTRO STREQUAL "fc24") OR (ZM_TARGET_DISTRO STREQUAL "fc25"))
@ -205,6 +226,11 @@ include_directories("${CMAKE_BINARY_DIR}")
# This is required to enable searching in lib64 (if exists), do not change
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON)
# Set the systemd flag if systemd is autodetected or ZM_SYSTEMD has been set
if(ZM_SYSTEMD OR (IS_DIRECTORY /usr/lib/systemd/system) OR (IS_DIRECTORY /lib/systemd/system))
set(WITH_SYSTEMD 1)
endif(ZM_SYSTEMD OR (IS_DIRECTORY /usr/lib/systemd/system) OR (IS_DIRECTORY /lib/systemd/system))
# System checks
check_include_file("libv4l1-videodev.h" HAVE_LIBV4L1_VIDEODEV_H)
if(NOT HAVE_LIBV4L1_VIDEODEV_H)
@ -386,13 +412,13 @@ find_library(MYSQLCLIENT_LIBRARIES mysqlclient PATH_SUFFIXES mysql)
if(MYSQLCLIENT_LIBRARIES)
set(HAVE_LIBMYSQLCLIENT 1)
list(APPEND ZM_BIN_LIBS "${MYSQLCLIENT_LIBRARIES}")
find_path(MYSQLCLIENT_INCLUDE_DIR mysql/mysql.h)
find_path(MYSQLCLIENT_INCLUDE_DIR mysql.h PATH_SUFFIXES mysql)
if(MYSQLCLIENT_INCLUDE_DIR)
include_directories("${MYSQLCLIENT_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${MYSQLCLIENT_INCLUDE_DIR}")
endif(MYSQLCLIENT_INCLUDE_DIR)
mark_as_advanced(FORCE MYSQLCLIENT_LIBRARIES MYSQLCLIENT_INCLUDE_DIR)
check_include_file("mysql/mysql.h" HAVE_MYSQL_H)
check_include_file("mysql.h" HAVE_MYSQL_H)
if(NOT HAVE_MYSQL_H)
message(FATAL_ERROR
"ZoneMinder requires MySQL headers - check that MySQL development packages are installed")
@ -402,6 +428,59 @@ else(MYSQLCLIENT_LIBRARIES)
"ZoneMinder requires mysqlclient but it was not found on your system")
endif(MYSQLCLIENT_LIBRARIES)
# x264 (using find_library and find_path)
find_library(X264_LIBRARIES x264)
if(X264_LIBRARIES)
set(HAVE_LIBX264 1)
list(APPEND ZM_BIN_LIBS "${X264_LIBRARIES}")
find_path(X264_INCLUDE_DIR x264.h)
if(X264_INCLUDE_DIR)
include_directories("${X264_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${X264_INCLUDE_DIR}")
endif(X264_INCLUDE_DIR)
mark_as_advanced(FORCE X264_LIBRARIES X264_INCLUDE_DIR)
check_include_files("stdint.h;x264.h" HAVE_X264_H)
set(optlibsfound "${optlibsfound} x264")
else(X264_LIBRARIES)
set(optlibsnotfound "${optlibsnotfound} x264")
endif(X264_LIBRARIES)
# mp4v2 (using find_library and find_path)
find_library(MP4V2_LIBRARIES mp4v2)
if(MP4V2_LIBRARIES)
set(HAVE_LIBMP4V2 1)
list(APPEND ZM_BIN_LIBS "${MP4V2_LIBRARIES}")
# mp4v2/mp4v2.h
find_path(MP4V2_INCLUDE_DIR mp4v2/mp4v2.h)
if(MP4V2_INCLUDE_DIR)
include_directories("${MP4V2_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
endif(MP4V2_INCLUDE_DIR)
check_include_file("mp4v2/mp4v2.h" HAVE_MP4V2_MP4V2_H)
# mp4v2.h
find_path(MP4V2_INCLUDE_DIR mp4v2.h)
if(MP4V2_INCLUDE_DIR)
include_directories("${MP4V2_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
endif(MP4V2_INCLUDE_DIR)
check_include_file("mp4v2.h" HAVE_MP4V2_H)
# mp4.h
find_path(MP4V2_INCLUDE_DIR mp4.h)
if(MP4V2_INCLUDE_DIR)
include_directories("${MP4V2_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${MP4V2_INCLUDE_DIR}")
endif(MP4V2_INCLUDE_DIR)
check_include_file("mp4.h" HAVE_MP4_H)
mark_as_advanced(FORCE MP4V2_LIBRARIES MP4V2_INCLUDE_DIR)
set(optlibsfound "${optlibsfound} mp4v2")
else(MP4V2_LIBRARIES)
set(optlibsnotfound "${optlibsnotfound} mp4v2")
endif(MP4V2_LIBRARIES)
set(PATH_FFMPEG "")
set(OPT_FFMPEG "no")
# Do not check for ffmpeg if ZM_NO_FFMPEG is on
@ -492,6 +571,23 @@ if(NOT ZM_NO_FFMPEG)
set(optlibsnotfound "${optlibsnotfound} SWScale")
endif(SWSCALE_LIBRARIES)
# rescale (using find_library and find_path)
find_library(AVRESAMPLE_LIBRARIES avresample)
if(AVRESAMPLE_LIBRARIES)
set(HAVE_LIBAVRESAMPLE 1)
list(APPEND ZM_BIN_LIBS "${AVRESAMPLE_LIBRARIES}")
find_path(AVRESAMPLE_INCLUDE_DIR "libavresample/avresample.h" /usr/include/ffmpeg)
if(AVRESAMPLE_INCLUDE_DIR)
include_directories("${AVRESAMPLE_INCLUDE_DIR}")
set(CMAKE_REQUIRED_INCLUDES "${AVRESAMPLE_INCLUDE_DIR}")
endif(AVRESAMPLE_INCLUDE_DIR)
mark_as_advanced(FORCE AVRESAMPLE_LIBRARIES AVRESAMPLE_INCLUDE_DIR)
check_include_file("libavresample/avresample.h" HAVE_LIBAVRESAMPLE_AVRESAMPLE_H)
set(optlibsfound "${optlibsfound} AVResample")
else(AVRESAMPLE_LIBRARIES)
set(optlibsnotfound "${optlibsnotfound} AVResample")
endif(AVRESAMPLE_LIBRARIES)
# Find the path to the ffmpeg executable
find_program(FFMPEG_EXECUTABLE
NAMES ffmpeg avconv
@ -524,6 +620,13 @@ if(NOT ZM_NO_LIBVLC)
endif(LIBVLC_LIBRARIES)
endif(NOT ZM_NO_LIBVLC)
find_package(Boost 1.36.0)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
set(CMAKE_REQUIRED_INCLUDES "${Boost_INCLUDE_DIRS}")
list(APPEND ZM_BIN_LIBS "${Boost_LIBRARIES}")
endif()
# *** END OF LIBRARY CHECKS ***
# Check for gnutls or crypto
@ -659,12 +762,14 @@ endif(NOT ZM_WEB_GROUP)
message(STATUS "Using web user: ${ZM_WEB_USER}")
message(STATUS "Using web group: ${ZM_WEB_GROUP}")
# Check for polkit
find_package(Polkit)
if(NOT POLKIT_FOUND)
message(FATAL_ERROR
"Running ZoneMinder requires polkit. Building ZoneMinder requires the polkit development package.")
endif(NOT POLKIT_FOUND)
if(WITH_SYSTEMD)
# Check for polkit
find_package(Polkit)
if(NOT POLKIT_FOUND)
message(FATAL_ERROR
"Running ZoneMinder requires polkit. Building ZoneMinder requires the polkit development package.")
endif(NOT POLKIT_FOUND)
endif(WITH_SYSTEMD)
# Some variables that zm expects
set(ZM_PID "${ZM_RUNDIR}/zm.pid")
@ -749,3 +854,4 @@ if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif(CCACHE_FOUND)

View File

@ -1,7 +1,7 @@
# ZoneMinder
FROM ubuntu:trusty
MAINTAINER Kyle Johnson <kjohnson@gnulnx.net>
FROM ubuntu:xenial
MAINTAINER Markos Vakondios <mvakondios@gmail.com>
# Resynchronize the package index files
RUN apt-get update && \
@ -10,8 +10,8 @@ RUN apt-get update && \
libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 \
libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm cmake libjpeg-turbo8-dev \
libjpeg-turbo8 libtheora-dev libvorbis-dev libvpx-dev libx264-dev libmp4v2-dev libav-tools mysql-client \
apache2 php5 php5-mysql apache2-mpm-prefork libapache2-mod-php5 php5-cli openssh-server \
mysql-server libvlc-dev libvlc5 libvlccore-dev libvlccore7 vlc-data libcurl4-openssl-dev \
apache2 php php-mysql libapache2-mod-php php-cli \
mysql-server libvlc-dev libvlc5 libvlccore-dev libvlccore8 vlc-data libcurl4-openssl-dev \
libavformat-dev libswscale-dev libavutil-dev libavcodec-dev libavfilter-dev \
libavresample-dev libavdevice-dev libpostproc-dev libv4l-dev libtool libnetpbm10-dev \
libmime-lite-perl dh-autoreconf dpatch \
@ -42,22 +42,12 @@ ADD utils/docker/start.sh /tmp/start.sh
# give files in /usr/local/share/zoneminder/
RUN chown -R www-data:www-data /usr/local/share/zoneminder/
# Creating SSH privilege escalation dir
RUN mkdir /var/run/sshd
# Adding apache virtual hosts file
ADD utils/docker/apache-vhost /etc/apache2/sites-available/000-default.conf
RUN cp misc/apache.conf /etc/apache2/sites-available/000-default.conf
ADD utils/docker/phpdate.ini /etc/php5/apache2/conf.d/25-phpdate.ini
# Set the root passwd
RUN echo 'root:root' | chpasswd
# Add a user we can actually login with
RUN useradd -m -s /bin/bash -G sudo zoneminder
RUN echo 'zoneminder:zoneminder' | chpasswd
# Expose ssh and http ports
EXPOSE 22 80
# Expose http port
EXPOSE 80
# Initial database and apache setup:
RUN "/ZoneMinder/utils/docker/setup.sh"

74
code_of_conduct.md Normal file
View File

@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@ -193,6 +193,7 @@ CREATE TABLE `Events` (
`Length` decimal(10,2) NOT NULL default '0.00',
`Frames` int(10) unsigned default NULL,
`AlarmFrames` int(10) unsigned default NULL,
`DefaultVideo` VARCHAR( 64 ) NOT NULL,
`TotScore` int(10) unsigned NOT NULL default '0',
`AvgScore` smallint(5) unsigned default '0',
`MaxScore` smallint(5) unsigned default '0',
@ -323,7 +324,7 @@ CREATE TABLE `Monitors` (
`Type` enum('Local','Remote','File','Ffmpeg','Libvlc','cURL') NOT NULL default 'Local',
`Function` enum('None','Monitor','Modect','Record','Mocord','Nodect') NOT NULL default 'Monitor',
`Enabled` tinyint(3) unsigned NOT NULL default '1',
`LinkedMonitors` varchar(255) NOT NULL default '',
`LinkedMonitors` varchar(255),
`Triggers` set('X10') NOT NULL default '',
`Device` tinytext NOT NULL default '',
`Channel` tinyint(3) unsigned NOT NULL default '0',
@ -332,26 +333,30 @@ CREATE TABLE `Monitors` (
`V4LCapturesPerFrame` tinyint(3) unsigned,
`Protocol` varchar(16) NOT NULL default '',
`Method` varchar(16) NOT NULL default '',
`Host` varchar(64) NOT NULL default '',
`Host` varchar(64),
`Port` varchar(8) NOT NULL default '',
`SubPath` varchar(64) NOT NULL default '',
`Path` varchar(255) NOT NULL default '',
`Path` varchar(255),
`Options` varchar(255) not null default '',
`User` varchar(64) NOT NULL default '',
`Pass` varchar(64) NOT NULL default '',
`User` varchar(64),
`Pass` varchar(64),
`Width` smallint(5) unsigned NOT NULL default '0',
`Height` smallint(5) unsigned NOT NULL default '0',
`Colours` tinyint(3) unsigned NOT NULL default '1',
`Palette` int(10) unsigned NOT NULL default '0',
`Orientation` enum('0','90','180','270','hori','vert') NOT NULL default '0',
`Deinterlacing` int(10) unsigned NOT NULL default '0',
`RTSPDescribe` tinyint(1) unsigned NOT NULL default '0',
`SaveJPEGs` TINYINT NOT NULL DEFAULT '3' ,
`VideoWriter` TINYINT NOT NULL DEFAULT '0',
`EncoderParameters` TEXT,
`RecordAudio` TINYINT NOT NULL DEFAULT '0',
`RTSPDescribe` tinyint(1) unsigned,
`Brightness` mediumint(7) NOT NULL default '-1',
`Contrast` mediumint(7) NOT NULL default '-1',
`Hue` mediumint(7) NOT NULL default '-1',
`Colour` mediumint(7) NOT NULL default '-1',
`EventPrefix` varchar(32) NOT NULL default 'Event-',
`LabelFormat` varchar(64) NOT NULL default '%N - %y/%m/%d %H:%M:%S',
`LabelFormat` varchar(64),
`LabelX` smallint(5) unsigned NOT NULL default '0',
`LabelY` smallint(5) unsigned NOT NULL default '0',
`LabelSize` smallint(5) unsigned NOT NULL DEFAULT '1',
@ -372,14 +377,14 @@ CREATE TABLE `Monitors` (
`RefBlendPerc` tinyint(3) unsigned NOT NULL default '6',
`AlarmRefBlendPerc` tinyint(3) unsigned NOT NULL default '6',
`Controllable` tinyint(3) unsigned NOT NULL default '0',
`ControlId` int(10) unsigned NOT NULL default '0',
`ControlId` int(10) unsigned,
`ControlDevice` varchar(255) default NULL,
`ControlAddress` varchar(255) default NULL,
`AutoStopTimeout` decimal(5,2) default NULL,
`TrackMotion` tinyint(3) unsigned NOT NULL default '0',
`TrackDelay` smallint(5) unsigned NOT NULL default '0',
`TrackDelay` smallint(5) unsigned,
`ReturnLocation` tinyint(3) NOT NULL default '-1',
`ReturnDelay` smallint(5) unsigned NOT NULL default '0',
`ReturnDelay` smallint(5) unsigned,
`DefaultView` enum('Events','Control') NOT NULL default 'Events',
`DefaultRate` smallint(5) unsigned NOT NULL default '100',
`DefaultScale` smallint(5) unsigned NOT NULL default '100',
@ -468,7 +473,7 @@ CREATE TABLE `Users` (
`Id` int(10) unsigned NOT NULL auto_increment,
`Username` varchar(32) character set latin1 collate latin1_bin NOT NULL default '',
`Password` varchar(64) NOT NULL default '',
`Language` varchar(8) NOT NULL default '',
`Language` varchar(8),
`Enabled` tinyint(3) unsigned NOT NULL default '1',
`Stream` enum('None','View') NOT NULL default 'None',
`Events` enum('None','View','Edit') NOT NULL default 'None',
@ -477,8 +482,8 @@ CREATE TABLE `Users` (
`Groups` enum('None','View','Edit') NOT NULL default 'None',
`Devices` enum('None','View','Edit') NOT NULL default 'None',
`System` enum('None','View','Edit') NOT NULL default 'None',
`MaxBandwidth` varchar(16) NOT NULL default '',
`MonitorIds` tinytext NOT NULL,
`MaxBandwidth` varchar(16),
`MonitorIds` tinytext,
PRIMARY KEY (`Id`),
UNIQUE KEY `UC_Username` (`Username`)
) ENGINE=@ZM_MYSQL_ENGINE@;

View File

@ -1,20 +1,5 @@
--
-- This updates a 1.30.1 database to 1.30.2
--
-- Add StateId Column to Events.
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Events'
AND table_schema = DATABASE()
AND column_name = 'StateId'
) > 0,
"SELECT 'Column StateId exists in Events'",
"ALTER TABLE Events ADD `StateId` int(10) unsigned default NULL AFTER `Notes`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
ALTER TABLE Users MODIFY MonitorIds TEXT NOT NULL;

5
db/zm_update-1.30.3.sql Normal file
View File

@ -0,0 +1,5 @@
--
-- This updates a 1.30.2 database to 1.30.3
--
-- No changes required
--

5
db/zm_update-1.30.4.sql Normal file
View File

@ -0,0 +1,5 @@
--
-- This updates a 1.30.3 database to 1.30.4
--
-- No changes required
--

97
db/zm_update-1.31.0.sql Normal file
View File

@ -0,0 +1,97 @@
--
-- This updates a 1.29.0 database to 1.30.0
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'SaveJPEGs'
) > 0,
"SELECT 'Column SaveJPEGs exists in Monitors'",
"ALTER TABLE `Monitors` ADD `SaveJPEGs` TINYINT NOT NULL DEFAULT '3' AFTER `Deinterlacing`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'VideoWriter'
) > 0,
"SELECT 'Column VideoWriter exists in Monitors'",
"ALTER TABLE `Monitors` ADD `VideoWriter` TINYINT NOT NULL DEFAULT '0' AFTER `SaveJPEGs`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'EncoderParameters'
) > 0,
"SELECT 'Column EncoderParameters exists in Monitors'",
"ALTER TABLE `Monitors` ADD `EncoderParameters` TEXT NOT NULL AFTER `VideoWriter`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Events'
AND table_schema = DATABASE()
AND column_name = 'DefaultVideo'
) > 0,
"SELECT 'Column DefaultVideo exists in Events'",
"ALTER TABLE `Events` ADD `DefaultVideo` VARCHAR( 64 ) NOT NULL AFTER `AlarmFrames`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Monitors'
AND table_schema = DATABASE()
AND column_name = 'RecordAudio'
) > 0,
"SELECT 'Column RecordAudio exists in Monitors'",
"ALTER TABLE `Monitors` ADD `RecordAudio` TINYINT NOT NULL DEFAULT '0' AFTER `EncoderParameters`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;
--
-- The following alters various columns to allow NULLs
--
ALTER TABLE Monitors MODIFY Host varchar(64);
ALTER TABLE Monitors MODIFY LabelFormat varchar(64);
ALTER TABLE Monitors MODIFY LinkedMonitors varchar(255);
ALTER TABLE Monitors MODIFY Options varchar(255);
ALTER TABLE Monitors MODIFY Protocol varchar(16);
ALTER TABLE Monitors MODIFY User varchar(64);
ALTER TABLE Monitors MODIFY Pass varchar(64);
ALTER TABLE Monitors MODIFY RTSPDescribe tinyint(1) unsigned;
ALTER TABLE Monitors MODIFY ControlId int(10) unsigned;
ALTER TABLE Monitors MODIFY TrackDelay smallint(5) unsigned;
ALTER TABLE Monitors MODIFY ReturnDelay smallint(5) unsigned;
ALTER TABLE Monitors MODIFY EncoderParameters TEXT;
ALTER TABLE Monitors MODIFY Path varchar(255);
ALTER TABLE Monitors MODIFY V4LMultiBuffer tinyint(1) unsigned;
ALTER TABLE Users MODIFY MonitorIds tinytext;
ALTER TABLE Users MODIFY Language varchar(8);
ALTER TABLE Users MODIFY MaxBandwidth varchar(16);

20
db/zm_update-1.31.1.sql Normal file
View File

@ -0,0 +1,20 @@
--
-- This updates a 1.30.0 database to 1.30.1
--
-- Add StateId Column to Events.
--
SET @s = (SELECT IF(
(SELECT COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'Events'
AND table_schema = DATABASE()
AND column_name = 'StateId'
) > 0,
"SELECT 'Column StateId exists in Events'",
"ALTER TABLE Events ADD `StateId` int(10) unsigned default NULL AFTER `Notes`"
));
PREPARE stmt FROM @s;
EXECUTE stmt;

View File

@ -1,8 +1,20 @@
Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www>
Options -Indexes +FollowSymLinks
<IfModule mod_dir.c>
DirectoryIndex index.php
</IfModule>
</Directory>
<IfModule mod_fcgid.c>
<Directory /usr/share/zoneminder/www>
Options -Indexes +ExecCGI
AllowOverride All
AddHandler fcgid-script .php
FCGIWrapper /usr/bin/php5-cgi
Order allow,deny
Allow from all
</Directory>
</IfModule>
<IfModule mod_php5.c>
<Directory /usr/share/zoneminder/www>
Options -Indexes +FollowSymLinks
<IfModule mod_dir.c>
DirectoryIndex index.php
</IfModule>
</Directory>
</IfModule>

View File

@ -6,14 +6,14 @@ Build-Depends: debhelper (>= 9), cmake
, libphp-serialization-perl
, libgnutls28-dev | libgnutls-dev
, libmysqlclient-dev | libmariadbclient-dev
, libjpeg8-dev
, libjpeg8-dev | libjpeg-dev
, libpcre3-dev
, libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev
, libavdevice-dev
, libv4l-dev (>= 0.8.3)
, libbz2-dev
, ffmpeg | libav-tools
, libnetpbm10-dev
, libavdevice-dev
, libvlccore-dev, libvlc-dev
, libcurl4-gnutls-dev | libcurl4-nss-dev | libcurl4-openssl-dev
, libgcrypt11-dev, libpolkit-gobject-1-dev
@ -42,11 +42,11 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
, libsys-cpu-perl, libsys-meminfo-perl
, libdata-uuid-perl
, libpcre3
, ffmpeg | libav-tools, libavdevice53
, ffmpeg | libav-tools, libavdevice53 | libavdevice55 | libavdevice57
, rsyslog | system-log-daemon
, netpbm , libjpeg8
, zip
, libvlccore5 | libvlccore7, libvlc5
, libvlccore5 | libvlccore7 | libvlccore8, libvlc5
, libpolkit-gobject-1-0, php5-gd
Recommends: mysql-server | mariadb-server
Description: Video camera security and surveillance solution

View File

@ -27,9 +27,9 @@ override_dh_auto_configure:
override_dh_auto_install:
dh_auto_install --buildsystem=cmake
install -D -m 0644 debian/apache.conf $(INSTDIR)/etc/zm/apache.conf
rm $(INSTDIR)/usr/share/zoneminder/www/api/lib/Cake/LICENSE.txt
rm $(INSTDIR)/usr/share/zoneminder/www/api/.gitignore
rm -r $(INSTDIR)/usr/share/zoneminder/www/api/lib/Cake/Test
rm -f $(INSTDIR)/usr/share/zoneminder/www/api/lib/Cake/LICENSE.txt
rm -f $(INSTDIR)/usr/share/zoneminder/www/api/.gitignore
rm -rf $(INSTDIR)/usr/share/zoneminder/www/api/lib/Cake/Test
override_dh_auto_test:
# do not run tests...

1
distros/fedora Symbolic link
View File

@ -0,0 +1 @@
redhat

View File

@ -1,3 +0,0 @@
Fedora rpm build files have been merged with redhat.
See /distros/redhat.

View File

@ -51,6 +51,7 @@ file(MAKE_DIRECTORY sock swap zoneminder zoneminder-upload events images temp)
# Install the empty folders
install(DIRECTORY sock swap DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/log DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder DESTINATION /var/run DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY zoneminder-upload DESTINATION /var/spool DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(DIRECTORY events images temp DESTINATION /var/lib/zoneminder DIRECTORY_PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)

View File

@ -157,7 +157,9 @@ Upgrades
sudo zmupdate.pl --user=root --pass=<mysql_root_pwd> --version=<from version>
5. Now start zoneminder:
5. Now restart nginx and php-fpm then start and zoneminder:
sudo systemctl restart nginx
sudo systemctl restart php-fpm
sudo systemctl start zoneminder

View File

@ -151,7 +151,8 @@ Upgrades
sudo zmupdate.pl --user=root --pass=<mysql_root_pwd> --version=<from version>
5. Now start zoneminder:
5. Now restart the web server then start zoneminder:
sudo systemctl restart httpd
sudo systemctl start zoneminder

View File

@ -150,7 +150,8 @@ Upgrades
sudo zmupdate.pl --user=root --pass=<mysql_root_pwd> --version=<from version>
5. Now start zoneminder:
5. Now restart the web server then start zoneminder:
sudo service httpd restart
sudo service zoneminder start

View File

@ -142,7 +142,9 @@ Upgrades
sudo zmupdate.pl --user=root --pass=<mysql_root_pwd> --version=<from version>
5. Now start zoneminder:
5. Now restart the web server then start zoneminder:
sudo systemctl restart httpd
sudo systemctl start zoneminder

View File

@ -23,3 +23,7 @@ here are a couple of considerations you may want to take.
directives found in /etc/httpd/conf.d/zoneminder.conf. You should also
comment out the HTTP -> HTTPS Rewrite rule.
3. Install a fully signed certificate from letsencrypt. See the Letsencrypt
site for more information. https://letsencrypt.org/
This service is totally free!

View File

@ -6,9 +6,9 @@
%if "%{zmuid_final}" == "nginx"
%global with_nginx 1
%global wwwconfdir /etc/nginx/default.d
%global wwwconfdir %{_sysconfdir}/nginx/default.d
%else
%global wwwconfdir /etc/httpd/conf.d
%global wwwconfdir %{_sysconfdir}/httpd/conf.d
%endif
%global sslcert %{_sysconfdir}/pki/tls/certs/localhost.crt
@ -17,6 +17,11 @@
# This will tell zoneminder's cmake process we are building against a known distro
%global zmtargetdistro %{?rhel:el%{rhel}}%{!?rhel:fc%{fedora}}
# Fedora >= 25 needs apcu backwards compatibility module
%if 0%{?fedora} >= 25
%global with_apcu_bc 1
%endif
# Include files for SysV init or systemd
%if 0%{?fedora} >= 15 || 0%{?rhel} >= 7
%global with_init_systemd 1
@ -24,19 +29,12 @@
%global with_init_sysv 1
%endif
# php-mysql deprecated in f25
%if 0%{?fedora} >= 25
%global with_php_mysqlnd 1
%else
%global with_php_mysql 1
%endif
%global readme_suffix %{?rhel:Redhat%{?rhel}}%{!?rhel:Fedora}
%global _hardened_build 1
Name: zoneminder
Version: 1.30.1
Release: 2%{?dist}
Version: 1.30.4
Release: 1%{?dist}
Summary: A camera monitoring and analysis tool
Group: System Environment/Daemons
# jscalendar is LGPL (any version): http://www.dynarch.com/projects/calendar/
@ -46,13 +44,15 @@ Group: System Environment/Daemons
License: GPLv2+ and LGPLv2+ and MIT
URL: http://www.zoneminder.com/
Source0: https://github.com/ZoneMinder/ZoneMinder/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz
Source0: https://github.com/ZoneMinder/ZoneMinder/archive/%{version}.tar.gz#/zoneminder-%{version}.tar.gz
Source1: https://github.com/FriendsOfCake/crud/archive/v%{crud_version}.tar.gz#/crud-%{crud_version}.tar.gz
%{?with_init_systemd:BuildRequires: systemd-devel}
%{?with_init_systemd:BuildRequires: mariadb-devel}
%{?with_init_systemd:BuildRequires: perl-podlators}
%{?with_init_systemd:BuildRequires: polkit-devel}
%{?with_init_sysv:BuildRequires: mysql-devel}
%{?el6:BuildRequires: epel-rpm-macros}
BuildRequires: cmake >= 2.8.7
BuildRequires: gnutls-devel
BuildRequires: bzip2-devel
@ -82,17 +82,17 @@ BuildRequires: vlc-devel
BuildRequires: libcurl-devel
BuildRequires: libv4l-devel
BuildRequires: ffmpeg-devel
BuildRequires: polkit-devel
%{?with_nginx:Requires: nginx}
%{?with_nginx:Requires: fcgiwrap}
%{?with_nginx:Requires: php-fpm}
%{!?with_nginx:Requires: httpd php}
%{!?with_nginx:Requires: httpd}
%{!?with_nginx:Requires: php}
%{?with_php_mysqlnd:Requires: php-mysqlnd}
%{?with_php_mysql:Requires: php-mysql}
Requires: php-mysqli
Requires: php-common
Requires: php-gd
Requires: php-pecl-apcu
%{?with_apcu_bc:Requires: php-pecl-apcu-bc}
Requires: cambozola
Requires: net-tools
Requires: psmisc
@ -137,10 +137,9 @@ designed to support as many cameras as you can attach to your computer without
too much degradation of performance.
%prep
%autosetup
%autosetup -a 1
rmdir ./web/api/app/Plugin/Crud
mv -f crud-%{crud_version} ./web/api/app/Plugin/Crud
%autosetup -p 1 -a 1 -n ZoneMinder-%{version}
%{__rm} -rf ./web/api/app/Plugin/Crud
%{__mv} -f crud-%{crud_version} ./web/api/app/Plugin/Crud
# Change the following default values
./utils/zmeditconfigdata.sh ZM_PATH_ZMS /cgi-bin-zm/nph-zms
@ -281,9 +280,9 @@ rm -rf %{_docdir}/%{name}-%{version}
%files
%license COPYING
%doc AUTHORS README.md distros/redhat/readme/README.%{readme_suffix} distros/redhat/readme/README.https distros/redhat/jscalendar-doc
%config(noreplace) %attr(640,root,%{zmgid_final}) /etc/zm/zm.conf
%config(noreplace) %attr(640,root,%{zmgid_final}) %{_sysconfdir}/zm/zm.conf
%config(noreplace) %attr(644,root,root) %{wwwconfdir}/zoneminder.conf
%config(noreplace) /etc/logrotate.d/zoneminder
%config(noreplace) %{_sysconfdir}/logrotate.d/zoneminder
%if 0%{?with_nginx}
%config(noreplace) %{_sysconfdir}/php-fpm.d/zoneminder.conf
@ -292,6 +291,8 @@ rm -rf %{_docdir}/%{name}-%{version}
%if 0%{?with_init_systemd}
%{_tmpfilesdir}/zoneminder.conf
%{_unitdir}/zoneminder.service
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
%endif
%if 0%{?with_init_sysv}
@ -304,7 +305,6 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_bindir}/zmc
%{_bindir}/zmcontrol.pl
%{_bindir}/zmdc.pl
%{_bindir}/zmf
%{_bindir}/zmfilter.pl
%{_bindir}/zmpkg.pl
%{_bindir}/zmtrack.pl
@ -329,9 +329,6 @@ rm -rf %{_docdir}/%{name}-%{version}
%{_libexecdir}/zoneminder/
%{_datadir}/zoneminder/
%{_datadir}/polkit-1/actions/com.zoneminder.systemctl.policy
%{_datadir}/polkit-1/rules.d/com.zoneminder.systemctl.rules
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/events
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/images
@ -340,9 +337,23 @@ rm -rf %{_docdir}/%{name}-%{version}
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_sharedstatedir}/zoneminder/temp
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/log/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/spool/zoneminder-upload
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %ghost %{_localstatedir}/run/zoneminder
%dir %attr(755,%{zmuid_final},%{zmgid_final}) %{_localstatedir}/run/zoneminder
%changelog
* Tue May 09 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.4-1
- modify autosetup macro parameters
- modify requirements for php-pecl-acpu-bc package
- 1.30.4 release
* Tue May 02 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.3-1
- 1.30.3 release
* Thu Mar 30 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.2-2
- 1.30.2 release
* Wed Feb 08 2017 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.2-1
- Bump version for 1.30.2 release candidate 1
* Wed Dec 28 2016 Andrew Bauer <zonexpertconsulting@outlook.com> - 1.30.1-2
- Changes from rpmfusion #4393

View File

@ -1,10 +0,0 @@
zoneminder (1.28.1-1) unstable; urgency=low
This version is no longer automatically initialize or upgrade database.
See README.Debian for details.
Changed installation paths (please correct your web server configuration):
/usr/share/zoneminder --> /usr/share/zoneminder/www
/usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin
-- Dmitry Smirnov <onlyjob@debian.org> Tue, 31 Mar 2015 15:12:17 +1100

View File

@ -1,573 +0,0 @@
zoneminder (1.28.1+1-vivid-SNAPSHOT2015081701) vivid; urgency=medium
* include api, switch to cmake build
-- Isaac Connor <iconnor@connortechnology.com> Mon, 17 Aug 2015 10:29:23 -0400
zoneminder (1.28.1-8) unstable; urgency=medium
* Patchworks:
+ New upstream "980-fix-image-size.patch".
+ New "default_cgi-path.patch" to correct default ZM_PATH_ZMS.
* postinst: set "root" as group owner for "/var/log/zm" to silence
logrotate warnings.
* Minor correction to README.Debian.
-- Dmitry Smirnov <onlyjob@debian.org> Sun, 16 Aug 2015 19:19:50 +1000
zoneminder (1.28.1-7) unstable; urgency=medium
* Build-Depends += "cakephp (<< 3.0.0~)";
Zoneminder is not compatible with latest CakePHP.
* Handle conffile removal from maintscript.
* rules: build man pages reproducibly.
* gbp.conf: renamed old style config section [git-dch] to [dch].
* README
+ added instructions to update owner of the "/etc/zm/zm.conf"
(Closes: #789327).
+ zmupdate.pl needs CREATE rights.
+ added note about required number of "fcgiwrap" workers.
* New upstream patch: "zmtrigger-plus.patch".
-- Dmitry Smirnov <onlyjob@debian.org> Mon, 20 Jul 2015 16:30:15 +1000
zoneminder (1.28.1-6) unstable; urgency=low
* New "zoneminder-doc" and "zoneminder-dbg" packages.
-- Dmitry Smirnov <onlyjob@debian.org> Sun, 19 Apr 2015 14:50:41 +1000
zoneminder (1.28.1-5) unstable; urgency=low
* Move handling of "/var/run/zm" and "/tmp/zm" from .service into .tmpfile.
Let dh_installinit do the job. Thanks, Andrew Bauer.
* Use dh_apache2 to install Apache conf file; remove old conf and symlink.
* Promote "libapache2-mod-php5 | php5-fpm" to Recommends.
* Build-Depends:
+ dh-linktree
+ cakephp (>= 2.6.3)
+ libjs-jquery
+ libjs-mootools
* Depends:
- libjs-jquery
- libjs-mootools
* Build-time replace bundled CakePHP with system one using "dh-linktree".
* Use "dh-linktree" to handle mootools and jquery symlinks.
-- Dmitry Smirnov <onlyjob@debian.org> Sun, 19 Apr 2015 11:45:01 +1000
zoneminder (1.28.1-4) unstable; urgency=low
* New patch to fix HTML export with USE_DEEP_STORAGE (closes: #723706).
* New "783.patch" to describe potential data loss in ZM_USE_DEEP_STORAGE.
* New patch to change default date format to region-neutral ISO notation
with time zone.
* Build sphinx documentation:
+ Install "zoneminder.1" man page.
+ Build-Depends += "python-sphinx | python3-sphinx"
+ Added commented "zoneminder-doc" package.
+ Added "docs.patch" to unlink distro-specific installation docs.
* rules:
+ set ZM_CONTENTDIR, ZM_SOCKDIR and ZM_TMPDIR.
+ remove mistakengly installed Perl module templates.
* Updated startup scripts to create ZM_TMPDIR.
* Hurd improvements:
+ New patch to add PATH_MAX definitions.
+ Build without MMAP support on Hurd.
+ libsys-mmap-perl [!hurd-any].
-- Dmitry Smirnov <onlyjob@debian.org> Mon, 06 Apr 2015 18:18:55 +1000
zoneminder (1.28.1-3) unstable; urgency=low
* Updated Apache2 and nginx configuration templates to support CGI.
* Updated README.Debian to document cgi-bin setup.
* Removed "/usr/share/zoneminder/www/cgi-bin" symlink.
* Added "apache2.patch" to correct Apache2 site configuration example.
* control: Suggests += "fcgiwrap".
* rules: added dh_systemd overrides to prevent automatic service
activation and start.
* Added note about manual service activation to README.Debian
(Closes: #781733).
-- Dmitry Smirnov <onlyjob@debian.org> Thu, 02 Apr 2015 23:20:20 +1100
zoneminder (1.28.1-2) unstable; urgency=low
* Removed word "Linux" from short package description.
* Build-Depends: do not require "libv4l-dev" on Hurd i.e. [!hurd-any].
* Added run-time Perl Depends:
+ libdbd-mysql-perl
+ libimage-info-perl
+ libmodule-load-conditional-perl
+ libnet-sftp-foreign-perl
+ liburi-encode-perl
* Prepare for package split: added commented "libzoneminder-perl"
and "zoneminder-dbg" packages to "debian/control".
* rules: do not install worthless ".packlist" file.
* Updated "libv4l1-videodev.h.patch" to fix v4lv1 detection in CMake.
-- Dmitry Smirnov <onlyjob@debian.org> Thu, 02 Apr 2015 13:25:19 +1100
zoneminder (1.28.1-1) unstable; urgency=low
[ Dmitry Smirnov <onlyjob@debian.org> ]
* New upstream release [February 2015].
* Upload to unstable.
* Disabled automatic database upgrades: post(inst|rm) scripts no longer
touch database or do unexpected stuff (Closes: #779254).
See README.Debian for details.
* Updated installation paths:
+ /usr/share/zoneminder --> /usr/share/zoneminder/www
+ /usr/lib/cgi-bin --> /usr/lib/zoneminder/cgi-bin
* Added logrotate config (Closes: #544826).
Thanks, Alberto Reyes.
* Native systemd service; "--with systemd" added to dh.
* Build with CMake instead of autoconf; rules clean-up.
* Build with all hardening.
* Build and install "zmupdate.pl.1" man page.
* Added nginx/php5-fpm configuration example.
* Install upstream "apache.conf" example.
* Described setup of Zoneminer web site and database in README.Debian.
* Install "/etc/zm/zm.conf" with tighter permissions.
* Added TODO.Debian.
* Added "debian/clean"; "debian/gbp.conf"; bug-presubj.
* Remove bundled Cake tests to take ~5 MB off big-usr-share.
* Standards-Version: 3.9.6; compat/debhelper to version 9.
* Vcs links to new git repository at collab-maint.
* Build-Depends:
+ dh-systemd
+ libgcrypt11-dev --> libgcrypt-dev
+ libcurl4-gnutls-dev
+ libvlc-dev
+ policykit-1 (required by "zmsystemctl.pl")
- dh-autoreconf, autoconf, automake
* Depends:
- apache2
- libapache2-mod-php5 (moved to Suggests)
- libpcre3 (invalid)
- libmodule-load-perl (obsolete; replaced with perl-modules)
- libarchive-tar-perl (obsolete; replaced with perl-modules)
- mysql-server (moved to Recommends, Closes: #759504).
- php5
+ libav-tools
+ libjs-jquery (replaces bundled component)
+ libjs-mootool (replaces bundled component)
+ libjson-any-perl (Closes: #690803).
+ perl-modules (Closes: #745819).
* Recommends:
+ apache2 | httpd
+ mysql-server | virtual-mysql-server (Closes: #732874).
* Suggests:
+ libapache2-mod-php5 | php5-fpm
+ logrotate
* Refreshed, renamed and re-ordered patches; added DEP-3 headers.
* Removed "vendor_perl" patch (applied-upstream).
* New patches:
+ cmake-fix-confpath.patch
+ cmake-gnutls.patch
+ cmake-nossl.patch
+ cmake.patch
+ format-hardening.patch
+ pod_man_fixes.patch
+ pod_name_fixes.patch
+ pod_zmupdate-to-pod2usage.patch
* Lintianisation (incomplete):
- extra-license-file
- init.d-script-missing-lsb-description
- init.d-script-does-not-source-init-functions
- privacy-breach-generic
- package-contains-empty-directory
- manpage-has-errors-from-pod2man
- manpage-has-bad-whatis-entry
- quilt-patch-missing-description
- no-dep5-copyright
* Lintian-overrides:
+ unusual-interpreter usr/bin/zmsystemctl.pl #!/usr/bin/pkexec
+ script-not-executable usr/share/zoneminder/www/api/*
+ script-with-language-extension usr/bin/*.pl
+ source-is-missing web/tools/mootools/mootools-*-yc.js
+ source-is-missing web/skins/*/js/jquery-1.4.2.min.js
+ source-contains-prebuilt-javascript-object
* Renamed files in "debian".
* watch: dfsg repacksuffix and dversionmangle.
* "debian/copyright" to Copyright-Format-1.0.
* Set myself as new Maintainer (Closes: #760314).
[ Vagrant Cascadian <vagrant@debian.org> ]
* Removed obsolete DM-Upload-Allowed flag.
* Update debian/watch to use tarballs from github.
* Add Build-Depends on libgcrypt11-dev (Closes: #745819).
* Use canonical alioth Vcs-Hg URL.
* debian/control: Add Build-Depends: libpolkit-gobject-1-dev.
* Removed configure flag "--enable-crashtrace=no", which is no longer
present upstream.
-- Dmitry Smirnov <onlyjob@debian.org> Tue, 31 Mar 2015 15:11:13 +1100
zoneminder (1.26.5-3.1) experimental; urgency=low
* Non-maintainer upload.
* Add libav10.patch and compile against libav10 (Closes: #739461)
-- Reinhard Tartler <siretart@tauware.de> Wed, 19 Mar 2014 00:31:22 +0000
zoneminder (1.26.5-3) unstable; urgency=low
* Previous release still didn't build on PPC - this has been corrected.
(Closes: #736516)
-- Peter Howard <pjh@northern-ridge.com.au> Tue, 4 Feb 2014 02:02:10 +1000
zoneminder (1.26.5-2) unstable; urgency=low
* Remove dependency on ffmpeg
(Closes: #721161)
* Builds again on non-x86 target architectures.
-- Peter Howard <pjh@northern-ridge.com.au> Thu, 23 Jan 2014 01:02:10 +1000
zoneminder (1.26.5-1) unstable; urgency=low
* New upstream version
(Closes: #694131)
* Change Build-Depends on libgnutls-dev to libgnutls-openssl-dev
(Closes: #731560)
-- Peter Howard <pjh@northern-ridge.com.au> Tue, 17 Dec 2013 01:02:10 +1000
zoneminder (1.25.0-4) unstable; urgency=high
* Add CVE-2013-0232 patch
[SECURITY] CVE-2013-0232: Shell escape commands with untrusted content.
Thanks to James McCoy <jamessan@debian.org> (Closes: #698910)
Thanks also to Salvatore Bonaccorso <carnil@debian.org>
-- Peter Howard <pjh@northern-ridge.com.au> Tue, 12 Jun 2013 12:02:10 +1000
zoneminder (1.25.0-3) unstable; urgency=low
* debian/rules: Export CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS, to ensure
hardening build flags are enabled.
-- Vagrant Cascadian <vagrant@debian.org> Tue, 28 Aug 2012 12:10:03 -0700
zoneminder (1.25.0-2) unstable; urgency=low
[ Vagrant Cascadian ]
* Add a patch to disable checking for updated versions by default, as
upgrades should happen through package management.
* Use dpkg-buildflags in debian/rules to set default compiler flags.
* Ensure zoneminder is stopped before starting (Closes: #657407).
[ Peter Howard ]
* Fix postinst to add permission for table creation during upgrade
(Closes: #657407).
-- Vagrant Cascadian <vagrant@debian.org> Thu, 23 Aug 2012 12:40:34 -0700
zoneminder (1.25.0-1.1) unstable; urgency=low
* Non-maintainer upload.
* Fix "ftbfs with GCC-4.7": add patch Fix-FTBFS-with-gcc-4.7 from Cyril
Brulebois: fix missing <unistd.h> includes.
(Closes: #667428)
-- gregor herrmann <gregoa@debian.org> Sun, 13 May 2012 17:02:21 +0200
zoneminder (1.25.0-1) unstable; urgency=low
* Fix typo in libv4l1-videodev.h patch that caused v4l1 support to be
dropped.
* Fail to build if version in postinst doesn't match upstream version.
* Add Build-Depends: libavdevice-dev to fix MPEG streaming (Closes: #515558).
* debian/rules: Convert to using debhelper overrides.
* Set debian/compat to 7.
* Simplify debian/watch file.
* Refresh debian/patches/use_libjs-mootools.
* Refresh debian/patches/libv4l1-videodev.h.
* Remove dependencies on php4 and related packages.
* Remove build-dependencies on libmysqlclient14-dev and
libmysqlclient15-dev.
* Update Build-Depends to use libjpeg-dev instead of libjpeg62-dev
(Closes: #647114).
* Add patch to fix build by testing for C headers rather than C++ headers.
Thanks to Ryan Niebur. (Closes: #654230)
* Add a patch to fix build problems caused by API changes in libav 0.8.
Thanks again to Ryan Niebur. (Closes: #654230)
-- Vagrant Cascadian <vagrant@debian.org> Mon, 16 Jan 2012 11:58:05 -0800
zoneminder (1.24.4-1) unstable; urgency=low
[ Peter Howard ]
* Initial release of 1.24.4 (Closes: #634985).
- Fix 32/64-bit type declarations (Closes: #614404).
* Update patches.
[ Vagrant Cascadian ]
* Add patch to fix FTBFS by using libv4l1-videodev.h from libv4l-dev.
Thanks to Andreas Metzler for reporting the issue.
(Closes: #619813).
* Document adding the www-data user to the video group in README.Debian.
(Closes: #611324)
* Depend on libsys-mmap-perl to enable mapped memory support.
(Closes: #607331)
* Update libjs-mootools patch to use -nc variants (Closes: #635075).
* Depend on javascript-common, to ensure that /javascript is available in
the web server.
* Set the upstream version in postinst at build time.
* Use dh-autoreconf to properly clean up autogenerated files during build.
* Add Vcs-HG to debian/control.
* Add Build-Depends: libv4l-dev, libbz2-dev, dh-autoreconf, libsys-mmap-perl.
-- Vagrant Cascadian <vagrant@debian.org> Sun, 24 Jul 2011 16:44:30 +0200
zoneminder (1.24.2-9) unstable; urgency=low
* Apply patch from Ubuntu to fix FTBFS with ffmpeg 0.6:
- Add -D__STDC_CONSTANT_MACROS to CPPFLAGS (closes: 614080).
* Update Standards-Version to 3.9.1, no changes necessary.
-- Vagrant Cascadian <vagrant@debian.org> Sun, 20 Feb 2011 23:43:02 -0800
zoneminder (1.24.2-8) unstable; urgency=medium
[ Vagrant Cascadian ]
* Apply patch to fix V4L2 cameras without crop support (closes: #608790).
Thanks to piratebab.
* Add preinst script which aborts if dangerous symlinks exist.
(closes: #608793)
[ Peter Howard ]
* Added to README.Debian with info about images and events directories.
(closes: #608793)
-- Vagrant Cascadian <vagrant@debian.org> Sat, 15 Jan 2011 19:39:26 -0800
zoneminder (1.24.2-7) unstable; urgency=medium
* Do not set ownership of /var/cache/zoneminder when upgrading, which fixes a
regression causing upgrades to take inordinately long with large
installations (closes: #597040).
-- Vagrant Cascadian <vagrant@debian.org> Fri, 17 Sep 2010 11:24:41 -0700
zoneminder (1.24.2-6) unstable; urgency=low
* Only remove database on purge. This requires only creating the database if
it doesn't already exist, and upgrading the database only if the database
is an older version (closes: #497107).
* Do not prompt the user on database upgrades by using the --nointeractive
flag when calling zmupdate.pl from postinst (closes: #595902).
-- Vagrant Cascadian <vagrant@debian.org> Fri, 10 Sep 2010 10:06:06 -0700
zoneminder (1.24.2-5) unstable; urgency=low
[ Peter Howard ]
* Add zip dependency
(closes: #494261)
* Add debian/watch file
(closes: #545552)
* Use packaged libjs-mootools
(closes: #585590)
* Miscellaneous cleanups
[ Vagrant Cascadian ]
* Add vagrant@debian.org as uploader
* Update Standards-Version to 3.9.0, no changes necessary.
-- Vagrant Cascadian <vagrant@debian.org> Fri, 23 Jul 2010 18:12:50 -0500
zoneminder (1.24.2-4.1) unstable; urgency=low
* Non-maintainer upload.
* Fix "package removed, processes still running": apply patch to
debian/postinst by Vagrant Cascadian: use invoke-rc.d and run
mysql-related actions only when mysql is running (closes: #583648).
-- gregor herrmann <gregoa@debian.org> Thu, 01 Jul 2010 19:47:10 +0200
zoneminder (1.24.2-4) unstable; urgency=high
* Update init.d to list mysql dependency
(closes: #583505)
* Change dependency from libmime-perl to libmime-tools-perl
(closes: #585589)
* Problems in changelog format fixed
(closes: #585592)
* Fix debian-rules-ignores-make-clean-error
(closes: #585593)
-- Peter Howard <pjh@northern-ridge.com.au> Mon, 14 jun 2010 15:02:10 +1000
zoneminder (1.24.2-3) unstable; urgency=high
* Changes symbols to build with libjpeg8
(closes: #565326, #568327)
* Note: location of all perl files should have been fixed in previous release
(closes: #553096)
-- Peter Howard <pjh@northern-ridge.com.au> Mon, 26 apr 2010 15:02:10 +1000
zoneminder (1.24.2-2) unstable; urgency=high
* Remove custom perl parth from zmpkg.pl, fix location of manpages.
(closes: #551746, #553092)
* Fix GCC4.4 bug
(closes: #531717)
* Fix potential bug in postinst script
-- Peter Howard <pjh@northern-ridge.com.au> Sat, 14 Nov 2009 15:02:10 +1000
zoneminder (1.24.2-1) unstable; urgency=high
* Initial release of zoneminder 1.24.2
-- Peter Howard <pjh@northern-ridge.com.au> Fri, 11 Sep 2009 07:02:50 +1000
zoneminder (1.24.1-1) unstable; urgency=high
* Initial release of zoneminder 1.24.1, closing CVE-2008-3882,
CVE-2008-3881, CVE-2008-3880
(closes: #497640)
* Change syslog dependency to rsyslog.
(closes: #526918)
* Add missing perl dependency.
* Restore patch to disable "check for updates" by default.
* Removed spurious '$' in init script.
(closes: #486064)
* Change permission of zm.conf from 0600 to 0400 for CVE-2008-6755
(closes: #528252)
-- Peter Howard <pjh@northern-ridge.com.au> Sat, 16 May 2009 07:02:50 +1000
zoneminder (1.23.3-4) unstable; urgency=high
* update to get it building with latest unstable. Thanks to waldi@debian.org
(closes: #517569)
-- Peter Howard <pjh@northern-ridge.com.au> Thu, 16 Apr 2009 01:02:50 +1000
zoneminder (1.23.3-3) unstable; urgency=high
* ffmpeg confirmed working
(closes: #475145)
* Fix upgrade problem intrudouced in 1.23.3-1
(closes: #481637)
* Include libmime-lite-perl in dependencies
(closes: #486312)
-- Peter Howard <pjh@northern-ridge.com.au> Thu, 18 Sep 2008 01:02:50 +1000
zoneminder (1.23.3-2) unstable; urgency=high
* ffmpeg finally working?
-- Peter Howard <pjh@northern-ridge.com.au> Wed, 13 Aug 2008 01:02:50 +1000
zoneminder (1.23.3-1) unstable; urgency=high
* Initial version for 1.23.3 - security fix.
(closes: #479034)
-- Peter Howard <pjh@northern-ridge.com.au> Wed, 19 Mar 2008 01:02:50 +1000
zoneminder (1.23.2-2) unstable; urgency=low
* Update to init.d
(closes: #468856)
* Add dependency on logging daemon
(closes: #471277)
-- Peter Howard <pjh@northern-ridge.com.au> Wed, 19 Mar 2008 01:02:50 +1000
zoneminder (1.23.2-1) unstable; urgency=low
* Initial version for 1.23.2
(closes: #464152)
* Zoneminder 1.23.2 upstream includes fix for GCC 4.3
(closes: #454980)
* Includes ffmpeg patch by Alexander Kushnirenko <kushnir@uni-protvino.ru>
-- Peter Howard <pjh@northern-ridge.com.au> Sat, 01 Mar 2008 16:02:50 +1000
zoneminder (1.22.3-10) unstable; urgency=low
* Fix bug introduced in -9 where perl is put under /usr/local
(closes: #457507)
-- Peter Howard <pjh@northern-ridge.com.au> Mon, 24 Dec 2007 16:02:50 +1000
zoneminder (1.22.3-9) unstable; urgency=low
* Starting zoneminder via init script now invokes "zmfix -a"
(closes: #481637)
* Change apache2-mpm-prefork dependency to apache2
* Temp dir for export under /var/cache/zoneminder (but linked back to
/usr/share/zoneminder for now)
* Redo use of gnutls rather than openssl for md5 hashes
-- Peter Howard <pjh@northern-ridge.com.au> Mon, 10 Dec 2007 16:02:50 +1000
zoneminder (1.22.3-8) unstable; urgency=low
* Build now includes libpcre3
(closes: #437533)
* "Monitor Presets" patch now applied to package during build.
-- Peter Howard <pjh@northern-ridge.com.au> Sat, 18 Aug 2007 14:35:23 +1000
zoneminder (1.22.3-7) unstable; urgency=low
* Turn off debug trace and crash dump on build
(closes:#414857,#414891)
* Additional perl libraries added in dependencies
(closes:#416291)
* Change preferred PHP version from 4 to 5
-- Peter Howard <pjh@northern-ridge.com.au> Sun, 29 Jul 2007 15:11:13 +1000
zoneminder (1.22.3-6) unstable; urgency=low
* Removed a similar bash only statement from zmpkg.pl
(closes:414882)
-- Peter Howard <pjh@northern-ridge.com.au> Sat, 14 Apr 2007 11:46:56 +1000
zoneminder (1.22.3-5) unstable; urgency=low
* Installs with "phone home" feature turned off by default, and permissions
on /etc/zm/zm.conf fixed (now the 0600 it s hould be)
(closes:415349)
* Removed "stupid bash-ism" on mysqld check in postinst file.
-- Peter Howard <pjh@northern-ridge.com.au> Fri, 6 Apr 2007 15:50:00 +1000
zoneminder (1.22.3-4) unstable; urgency=low
* Put libmysqlclient-15-dev in front of -14-dev so sbuild works
(closes: #414410)
-- Peter Howard <pjh@northern-ridge.com.au> Mon, 12 Mar 2007 11:38:56 +1100
zoneminder (1.22.3-3) unstable; urgency=low
* Clean up of postinstall, postrm ; user "zm" definitely was a mistake
* Also in postinstall: check and start MySQL if it's not running.
* init.d script now checks if zoneminder isn't running and still returns 0
(which helps uninstalling)
* Addition of php5 dependency options as well as php4.
-- Peter Howard <pjh@northern-ridge.com.au> Mon, 26 Feb 2007 10:40:52 +1100
zoneminder (1.22.3-2) unstable; urgency=low
* Added zmuser in the mysql creation; this should fix the install problem
for people, but needs to be cleaned up (in -3)
-- Peter Howard <pjh@northern-ridge.com.au> Fri, 16 Feb 2007 14:16:03 +1100
zoneminder (1.22.3-1) unstable; urgency=low
* Initial Version. (closes: #248393)
* Patched out use of openssl; uses gnutls instead for MD5 hashes.
* Removed MakeMaker-inserted Perl licensing (with authors permission) in
various scripts; replaced with GPL.
-- Peter Howard <pjh@northern-ridge.com.au> Wed, 7 Feb 2007 14:09:01 +1100

View File

@ -6,6 +6,7 @@ Uploaders: Vagrant Cascadian <vagrant@debian.org>
Build-Depends: debhelper (>= 9), python-sphinx | python3-sphinx, apache2-dev, dh-linktree
,cmake
,libavcodec-dev, libavformat-dev (>= 3:0.svn20090204), libswscale-dev (>= 3:0.svn20090204), libavutil-dev, libavdevice-dev
,libboost1.55-dev
,libbz2-dev
,libgcrypt-dev
,libcurl4-gnutls-dev
@ -54,7 +55,7 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libdata-uuid-perl
,mysql-client | virtual-mysql-client
,perl-modules
,php5-mysql, php5-gd
,php5-mysql, php5-gd, php5-apcu, php-apc
,policykit-1
,rsyslog | system-log-daemon
,zip

View File

@ -1,16 +0,0 @@
Last-Update: 2015-08-16
Forwarded: no
Author: Dmitry Smirnov <onlyjob@member.fsf.org>
Description: correct path to CGI app according to default web server configuration.
--- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in
+++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in
@@ -428,7 +428,7 @@ our @options =
},
{
name => "ZM_PATH_ZMS",
- default => "/cgi-bin/nph-zms",
+ default => "/zm/cgi-bin/nph-zms",
description => "Web path to zms streaming server",
help => qqq("
The ZoneMinder streaming server is required to send streamed

View File

@ -1,2 +0,0 @@
default_cgi-path.patch
use_libjs-mootools.patch

View File

@ -1,18 +0,0 @@
Last-Update: 2015-03-29
Forwarded: no
Bug-Debian: http://bugs.debian.org/585590
Reviewed-By: Dmitry Smirnov <onlyjob@member.fsf.org>
Description: use mootools shipped by debian, rather than the zoneminder included mootools.
--- a/web/skins/classic/includes/functions.php
+++ b/web/skins/classic/includes/functions.php
@@ -63,9 +63,8 @@
}
?>
<script type="text/javascript" src="tools/mootools/mootools-core.js"></script>
<script type="text/javascript" src="tools/mootools/mootools-more.js"></script>
- <script type="text/javascript" src="js/mootools.ext.js"></script>
<script type="text/javascript" src="js/logger.js"></script>
<script type="text/javascript" src="js/overlay.js"></script>
<?php
if ( $skinJsPhpFile )

View File

@ -58,8 +58,10 @@ override_dh_auto_install:
override_dh_fixperms:
dh_fixperms
## 637685
chmod -c o-r $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
#
# As requested by the Debian Webapps Policy Manual §3.2.1
chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
override_dh_installinit:
dh_installinit --no-start

View File

@ -1 +1 @@
3.0 (native)
3.0 (quilt)

View File

@ -2,9 +2,11 @@
missingok
notifempty
sharedscripts
delaycompress
compress
postrotate
/usr/bin/zmpkg.pl logrot >>/dev/null 2>&1 || :
endscript
weekly
rotate 3
daily
rotate 7
}

View File

@ -1,2 +1,3 @@
d /var/run/zm 0755 www-data www-data
d /tmp/zm 0755 www-data www-data
d /var/tmp/zm 0755 www-data www-data

View File

@ -8,7 +8,7 @@ ScriptAlias /zm/cgi-bin "/usr/lib/zoneminder/cgi-bin"
Alias /zm /usr/share/zoneminder/www
<Directory /usr/share/zoneminder/www>
Options -Indexes +ollowSymLinks
Options -Indexes +FollowSymLinks
<IfModule mod_dir.c>
DirectoryIndex index.php
</IfModule>

View File

@ -5,6 +5,8 @@ Maintainer: Dmitry Smirnov <onlyjob@debian.org>
Uploaders: Vagrant Cascadian <vagrant@debian.org>
Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apache2-dev, dh-linktree
,cmake
,libx264-dev, libmp4v2-dev
,libboost-dev
,libavdevice-dev (>= 6:10~)
,libavcodec-dev (>= 6:10~)
,libavformat-dev (>= 6:10~)
@ -14,7 +16,7 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
,libgcrypt-dev
,libcurl4-gnutls-dev
,libgnutls-openssl-dev
,libjpeg-dev
,libjpeg8-dev | libjpeg9-dev | libjpeg62-turbo-dev
,libmysqlclient-dev
,libpcre3-dev
,libpolkit-gobject-1-dev
@ -25,11 +27,11 @@ Build-Depends: debhelper (>= 9), dh-systemd, python-sphinx | python3-sphinx, apa
,libphp-serialization-perl
,libsys-mmap-perl [!hurd-any]
,libwww-perl
,libdata-uuid-perl
,libdata-uuid-perl
# Unbundled (dh_linktree):
,libjs-jquery
,libjs-mootools
Standards-Version: 3.9.6
Standards-Version: 3.9.8
Homepage: http://www.zoneminder.com/
Vcs-Browser: http://anonscm.debian.org/cgit/collab-maint/zoneminder.git
Vcs-Git: git://anonscm.debian.org/collab-maint/zoneminder.git
@ -38,11 +40,10 @@ Package: zoneminder
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,javascript-common
,libmp4v2-2, libx264-142|libx264-148, libswscale-ffmpeg3|libswscale4|libswscale3
,ffmpeg | libav-tools
,libdate-manip-perl
,libdate-manip-perl, libmime-lite-perl, libmime-tools-perl
,libdbd-mysql-perl
,libmime-lite-perl
,libmime-tools-perl
,libphp-serialization-perl
,libmodule-load-conditional-perl
,libnet-sftp-foreign-perl
@ -60,13 +61,14 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ${perl:Depends}
,libio-socket-multicast-perl
,libdigest-sha-perl
,libsys-cpu-perl, libsys-meminfo-perl
,libdata-uuid-perl
,libdata-uuid-perl
,mysql-client | virtual-mysql-client
,perl-modules
,php5-mysql | php-mysql, php5-gd | php-gd
,php5-mysql | php-mysql, php5-gd | php-gd , php5-apcu | php-apcu , php-apc | php-apcu-bc
,policykit-1
,rsyslog | system-log-daemon
,zip
,libpcre3
Recommends: ${misc:Recommends}
,libapache2-mod-php5 | libapache2-mod-php | php5-fpm | php-fpm
,mysql-server | virtual-mysql-server

View File

@ -1,2 +0,0 @@
default_cgi-path.patch
use_libjs-mootools.patch

View File

@ -28,7 +28,7 @@ override_dh_auto_configure:
override_dh_clean:
dh_clean $(MANPAGES1)
$(RM) -r docs/_build docs/installationguide
$(RM) -r docs/_build
build-indep:
#$(MAKE) -C docs text
@ -58,8 +58,10 @@ override_dh_auto_install:
override_dh_fixperms:
dh_fixperms
## 637685
chmod -c o-r $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
#
# As requested by the Debian Webapps Policy Manual §3.2.1
chown root:www-data $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
chmod 640 $(CURDIR)/debian/zoneminder/etc/zm/zm.conf
override_dh_installinit:
dh_installinit --no-start

View File

@ -1 +1 @@
3.0 (native)
3.0 (quilt)

View File

@ -1,7 +0,0 @@
version=3
opts=\
repacksuffix=+dfsg,\
dversionmangle=s{\+dfsg\d*}{},\
https://github.com/ZoneMinder/ZoneMinder/releases \
.*/ZoneMinder/archive/v(.*).tar.gz

View File

@ -3,6 +3,7 @@ usr/bin
usr/lib/zoneminder
usr/share/polkit-1
usr/share/zoneminder/db
usr/share/zoneminder/icons
usr/share/zoneminder/www
# libzoneminder-perl files:

View File

@ -1,3 +1,4 @@
/var/cache/zoneminder/events /usr/share/zoneminder/www/events
/var/cache/zoneminder/images /usr/share/zoneminder/www/images
/var/cache/zoneminder/temp /usr/share/zoneminder/www/temp
/var/tmp /usr/share/zoneminder/www/api/app/tmp

View File

@ -2,6 +2,8 @@
missingok
notifempty
sharedscripts
delaycompress
compress
postrotate
/usr/bin/zmpkg.pl logrot >>/dev/null 2>&1 || :
endscript

View File

@ -0,0 +1,2 @@
?package(zoneminder):needs="x11" section="Applications/Video" title="ZoneMinder" command="/usr/bin/x-www-browser http://localhost/zm" icon="/usr/share/zoneminder/icons/16x16/icon.xpm"

View File

@ -12,6 +12,10 @@ if [ "$1" = "configure" ]; then
if [ -z "$2" ]; then
chown www-data:www-data /var/cache/zoneminder /var/cache/zoneminder/*
fi
if [ ! -e "/etc/apache2/mods-enabled/cgi.load" ]; then
echo "The cgi module is not enabled in apache2. I am enabling it using a2enmod cgi."
a2enmod cgi
fi
# Do this every time the package is installed or upgraded

View File

@ -1,2 +1,3 @@
d /var/run/zm 0755 www-data www-data
d /tmp/zm 0755 www-data www-data
d /var/run/zm 0755 www-data www-data
d /tmp/zm 0755 www-data www-data
d /var/tmp/zm 0755 www-data www-data

View File

@ -309,7 +309,7 @@ The main causes are.
* Capture frame rates. Unless there's a compelling reason in your case there is often little benefit in running cameras at 25fps when 5-10fps would often get you results just as good. Try changing your monitor settings to limit your cameras to lower frame rates. You can still configure ZM to ignore these limits and capture as fast as possible when motion is detected.
* Run function. Obviously running in Record or Mocord modes or in Modect with lots of events generates a lot of DB and file activity and so CPU and load will increase.
* Basic default detection zones. By default when a camera is added one detection zone is added which covers the whole image with a default set of parameters. If your camera covers a view in which various regions are unlikely to generate a valid alarm (ie the sky) then I would experiment with reducing the zone sizes or adding inactive zones to blank out areas you don't want to monitor. Additionally the actual settings of the zone themselves may not be optimal. When doing motion detection the number of changed pixels above a threshold is examined, then this is filter, then contiguous regions are calculated to see if an alarm is generated. If any maximum or minimum threshold is exceeded according to your zone settings at any time the calculation stops. If your settings always result in the calculations going through to the last stage before being failed then additional CPU time is used unnecessarily. Make sure your maximum and minimumzone thresholds are set to sensible values and experiment by switching RECORD_EVENT_STATS on and seeing what the actual values of alarmed pixels etc are during sample events.
* Optimise your settings. After you've got some settings you're happy with then switching off RECORD_EVENT_STATS will prevent the statistics being written to the database which saves some time. Other settings which might make a difference are ZM_FAST_RGB_DIFFS, ZM_OPT_FRAME_SERVER and the JPEG_xxx_QUALITY ones.
* Optimise your settings. After you've got some settings you're happy with then switching off RECORD_EVENT_STATS will prevent the statistics being written to the database which saves some time. Other settings which might make a difference are ZM_FAST_RGB_DIFFS and the JPEG_xxx_QUALITY ones.
I'm sure there are other things which might make a difference such as what else you have running on the box and memory sizes (make sure there's no swapping going on). Also speed of disk etc will make some difference during event capture and also if you are watching the whole time then you may have a bunch of zms processes running also.

View File

@ -42,8 +42,6 @@ Note that these commands are just an example and might not be secure enough for
7. If you have chosen to change the ZoneMinder database account credentials to something other than zmuser/zmpass, you must now update zm.conf on each ZoneMinder Server. Change ZM_DB_USER and ZM_DB_PASS to the values you created in the previous step.
Additionally, you must also edit /usr/share/zoneminder/www/api/app/Config/database.php in a similar manner on each ZoneMinder Server. Scroll down and change login and password to the values you created in the previous step.
8. All ZoneMinders Servers must share a common events folder. This can be done in any manner supported by the underlying operating system. From the Storage Server, share/export a folder to be used for ZoneMinder events.
9. From each ZoneMinder Server, mount the shared events folder on the Storage Server to the events folder on the local ZoneMinder Server.

View File

@ -169,8 +169,6 @@ Now clone the ZoneMinder git repository:
cd
git clone https://github.com/ZoneMinder/ZoneMinder
cd ZoneMinder
git submodule init
git submodule update
This will create a sub-folder called ZoneMinder, which will contain the latest development.
@ -180,21 +178,21 @@ We want to turn this into a tarball, but first we need to figure out what to nam
ls ~/rpmbuild/SOURCES
The tarball from the previsouly installed SRPM should be there. This is the name we will use. For this example, the name is ZoneMinder-1.28.1.tar.gz. From one folder above the local ZoneMinder git repository, execute the following:
The tarball from the previsouly installed SRPM should be there. This is the name we will use. For this example, the name is ZoneMinder-1.28.1.tar.gz. From the root folder of the local ZoneMinder git repository, execute the following:
::
mv ZoneMinder ZoneMinder-1.28.1
tar -cvzf ~/rpmbuild/SOURCES/ZoneMinder-1.28.1.tar.gz ZoneMinder-1.28.1/*
git archive --prefix=ZoneMinder-1.28.1/ -o ~/rpmbuild/SOURCES/zoneminder-1.28.1.tar.gz HEAD
The trailing "/\*" leaves off the hidden dot "." file and folders from the git repo, which is what we want.
Note that we are overwriting the original tarball. If you wish to keep the original tarball then create a copy prior to creating the new tarball.
Now build a new src.rpm:
From the root of the local ZoneMinder git repo, execute the following:
::
rpmbuild -bs --nodeps ~/rpmbuild/SPECS/zoneminder.el7.spec
rpmbuild -bs --nodeps distros/redhat/zoneminder.spec
Notice we used the rpm specfile that is part of the latest master branch you just downloaded, rather than the one that may be in your ~/rpmbbuild/SOURCES folder.
This step will overwrite the SRPM you originally downloaded, so you may want to back it up prior to completing this step. Note that the name of the specfile will vary slightly depending on the target distro.

View File

@ -287,19 +287,19 @@ To build the latest master snapshot:
::
./do_debian_package.sh `lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'` `date +%Y%m%d`01 local master
./do_debian_package.sh --snapshot=NOW --branch=master --type=local
To build the latest stable release:
::
./do_debian_package.sh `lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'` `date +%Y%m%d`01 local stable
./do_debian_package.sh --snapshot=stable --type=local
Note that the ``lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'``
part simply extracts your distribution name - like "vivid", "trusty" etc. You
can always replace it by your distro name if you know it. As far as the script
Note that the distribution will be guessed using ``lsb_release -a 2>/dev/null | grep Codename | awk '{print $2}'``
which simply extracts your distribution name - like "vivid", "trusty" etc. You
can always specify it using --distro=your distro name if you know it. As far as the script
goes, it checks if your distro is "trusty" in which case it pulls in pre-systemd
release configurations and if its not "trusty" it assumes its based on systemd
and pulls in systemd related config files.
@ -358,24 +358,6 @@ Changed Default DB User
^^^^^^^^^^^^^^^^^^^^^^^
If you have changed your DB login/password from zmuser/zmpass, you need to
update these values in zm.conf and the API's database.php file.
update these values in zm.conf.
1. Edit zm.conf to change ZM_DB_USER and ZM_DB_PASS to the values you used.
2. Edit databse.php which can be found in the web server folder zoneminder/www/api/app/Config
There is a class there called DATABASE_CONFIG -
change the $default array to reflect your new details. Example:
::
public $default = array(
'datasource' => 'Database/Mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'mynewDBusername',
'password' => 'mynewDBpassword'
'database' => 'zm',
'prefix' => '',
//'encoding' => 'utf8',
);

View File

@ -40,6 +40,8 @@ Type
Preset
The preset chooser sets sensible default values based on computational needs (fast v. best) and sensitivity (low, medium, high.) It is not required that you select a preset, and you can alter any of the parameters after choosing a preset. For a small number of monitors with ZoneMinder running on modern equipment, Best, high sensitivity can be chosen as a good starting point.
It is important to understand that the available presets are intended merely as a starting point. Since every camera's view is unique, they are not guaranteed to work properly in every case. Presets tend to work acceptably for indoor cameras, where the objects of interest are relatively close and there typically are few or no unwanted objects moving within the cameras view. Presets, on the other hand, tend to not work acceptably for outdoor cameras, where the field of view is typically much wider, objects of interest are farther away, and changing weather patterns can cause false triggers. For outdoor cameras in particular, you will almost certainly have to tune your motion detection zone to get desired results. Please refer to `this guide <http://www.zoneminder.com/wiki/index.php/Understanding_ZoneMinder%27s_Zoning_system_for_Dummies>`__ to learn how to do this.
Units
* Pixels - Selecting this option will allow many of the following values to be entered (or viewed) in units of pixels.
* Percentage - Selecting this option will allow may of the following values to be entered (or viewed) as a percentage. The sense of the percentage values refers to the area of the zone and not the image as a whole. This makes trying to work out necessary sizes rather easier.

View File

@ -29,7 +29,7 @@ Here is what the filter window looks like
events later and also make sure archived events don't get deleted, for example
* Email details of all matches: Sends an email to the configured address with details about the event.
The email can be customized as per TBD
* Execute command on all matches: Allows you to execute any arbitrary command on the matched events
* Execute command on all matches: Allows you to execute any arbitrary command on the matched events. You can use replacement tokens as subsequent arguents to the command, the last argument will be the absolute path to the event, preceeded by replacement arguents. eg: /usr/bin/script.sh %MN% will excecute as /usr/bin/script.sh MonitorName /path/to/event.
* Delete all matches: Deletes all the matched events
* *E*: Use 'Submit' to 'test' your matching conditions. This will just match and show you what filters match. Use 'Execute' to actually execute the action after matching your conditions. Use 'Save' to save the filter for future use and 'Reset' to clear your settings

View File

@ -0,0 +1,2 @@
The XML images present in this folder have been drawn using http://draw.io
To edit images, simple go to draw.io and load the .xml files

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 93 KiB

File diff suppressed because one or more lines are too long

View File

@ -33,10 +33,6 @@ RUN_AUDIT - The zmaudit daemon exists to check that the saved information in the
AUDIT_CHECK_INTERVAL - The zmaudit daemon exists to check that the saved information in the database and on the filesystem match and are consistent with each other. If an error occurs or if you are using 'fast deletes' it may be that database records are deleted but files remain. In this case, and similar, zmaudit will remove redundant information to synchronise the two data stores. The default check interval of 900 seconds (15 minutes) is fine for most systems however if you have a very large number of events the process of scanning the database and filesystem may take a long time and impact performance. In this case you may prefer to make this interval much larger to reduce the impact on your system. This option determines how often these checks are performed.
OPT_FRAME_SERVER - In some circumstances it is possible for a slow disk to take so long writing images to disk that it causes the analysis daemon to fall behind especially during high frame rate events. Setting this option to yes enables a frame server daemon (zmf) which will be sent the images from the analysis daemon and will do the actual writing of images itself freeing up the analysis daemon to get on with other things. Should this transmission fail or other permanent or transient error occur, this function will fall back to the analysis daemon.
FRAME_SOCKET_SIZE - For large captured images it is possible for the writes from the analysis daemon to the frame server to fail as the amount to be written exceeds the default buffer size. While the images are then written by the analysis daemon so no data is lost, it defeats the object of the frame server daemon in the first place. You can use this option to indicate that a larger buffer size should be used. Note that you may have to change the existing maximum socket buffer size on your system via sysctl (or in /proc/sys/net/core/wmem_max) to allow this new size to be set. Alternatively you can change the default buffer size on your system in the same way in which case that will be used with no change necessary in this option
OPT_CONTROL - ZoneMinder includes limited support for controllable cameras. A number of sample protocols are included and others can easily be added. If you wish to control your cameras via ZoneMinder then select this option otherwise if you only have static cameras or use other control methods then leave this option off.
OPT_TRIGGERS - ZoneMinder can interact with external systems which prompt or cancel alarms. This is done via the zmtrigger.pl script. This option indicates whether you want to use these external triggers. Most people will say no here.

View File

@ -12,7 +12,8 @@ configure_file(zoneminder-tmpfiles.conf.in "${CMAKE_CURRENT_BINARY_DIR}/zonemind
# Do not install the misc files by default
#install(FILES "${CMAKE_CURRENT_BINARY_DIR}/apache.conf" "${CMAKE_CURRENT_BINARY_DIR}/logrotate.conf" "${CMAKE_CURRENT_BINARY_DIR}/syslog.conf" DESTINATION "${CMAKE_INSTALL_DATADIR}/zoneminder/misc")
# Install Policykit rules and actions into the proper folders
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.policy" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/actions")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.rules" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/rules.d")
# Install Policykit rules and actions into the proper folders only on systems with systemd
if(WITH_SYSTEMD)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.policy" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/actions")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/com.zoneminder.systemctl.rules" DESTINATION "${PC_POLKIT_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/polkit-1/rules.d")
endif(WITH_SYSTEMD)

View File

@ -5,13 +5,22 @@
# Some values may need to manually adjusted to suit your setup
#
<VirtualHost *:80>
ServerName @WEB_HOST@
ServerAdmin webmaster@localhost
DocumentRoot "@WEB_PREFIX@"
Alias /zm/ "@WEB_PREFIX@/"
<Directory "@WEB_PREFIX@">
Options -Indexes +FollowSymLinks
AllowOverride All
<IfModule mod_authz_core.c>
# Apache 2.4
Require all granted
</IfModule>
<IfModule !mod_authz_core.c>
# Apache 2.2
Order deny,allow
Allow from all
</IfModule>
</Directory>
ScriptAlias /cgi-bin "@CGI_PREFIX@"

View File

@ -12,7 +12,7 @@ Type=forking
ExecStart=@BINDIR@/zmpkg.pl start
ExecReload=@BINDIR@/zmpkg.pl restart
ExecStop=@BINDIR@/zmpkg.pl stop
PIDFile="@ZM_RUNDIR@/zm.pid"
PIDFile=@ZM_RUNDIR@/zm.pid
Environment=TZ=:/etc/localtime
[Install]

View File

@ -26,9 +26,10 @@ configure_file(zm.in "${CMAKE_CURRENT_BINARY_DIR}/zm" @ONLY)
#configure_file(zmeventdump.in zmeventdump @ONLY)
# Generate man files for the perl scripts destined for the bin folder
file(GLOB perlscripts RELATIVE "${CMAKE_CURRENT_BINARY_DIR}" "*.pl")
file(GLOB perlscripts "*.pl")
FOREACH(PERLSCRIPT ${perlscripts})
POD2MAN(${CMAKE_CURRENT_SOURCE_DIR}/${PERLSCRIPT} zoneminder-${PERLSCRIPT} 8)
get_filename_component(PERLSCRIPTNAME ${PERLSCRIPT} NAME)
POD2MAN(${PERLSCRIPT} zoneminder-${PERLSCRIPTNAME} 8)
ENDFOREACH(PERLSCRIPT ${perlscripts})
# Install the perl scripts

View File

@ -129,7 +129,7 @@ BEGIN {
} # end BEGIN
sub loadConfigFromDB {
print( "Loading config from DB\n" );
print( "Loading config from DB" );
my $dbh = ZoneMinder::Database::zmDbConnect();
if ( !$dbh ) {
print( "Error: unable to load options from database: $DBI::errstr\n" );
@ -160,11 +160,12 @@ sub loadConfigFromDB {
$option_count++;;
}
$sth->finish();
print( " $option_count entries\n" );
return( $option_count );
} # end sub loadConfigFromDB
sub saveConfigToDB {
print( "Saving config to DB\n" );
print( "Saving config to DB " . @options . " entries\n" );
my $dbh = ZoneMinder::Database::zmDbConnect();
if ( !$dbh ) {
print( "Error: unable to save options to database: $DBI::errstr\n" );

File diff suppressed because it is too large Load Diff

View File

@ -215,6 +215,55 @@ sub presetHome
$self->sendCmd( 'decoder_control.cgi?command=25&' );
}
sub moveRelUp
{
my $self = shift;
Debug( "Move Up" );
$self->sendCmd( 'decoder_control.cgi?command=0&onestep=1&' );
}
#Down Arrow
sub moveRelDown
{
my $self = shift;
Debug( "Move Down" );
$self->sendCmd( 'decoder_control.cgi?command=2&onestep=1&' );
}
#Left Arrow
sub moveRelLeft
{
my $self = shift;
Debug( "Move Left" );
$self->sendCmd( 'decoder_control.cgi?command=6&onestep=1&' );
}
#Right Arrow
sub moveRelRight
{
my $self = shift;
Debug( "Move Right" );
$self->sendCmd( 'decoder_control.cgi?command=4&onestep=1&' );
}
#Go to preset
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
my $result = undef;
if ( $preset > 0 && $preset <= 32 ) {
my $command=31+(($preset-1) * 2);
Debug( "Goto Preset $preset with command $command" );
$result=$self->sendCmd( 'decoder_control.cgi?command=' . $command . '&' );
}
else {
Error( "Unsupported preset $preset : must be between 1 and 32" );
}
return $result;
}
1;
__END__

View File

@ -15,7 +15,7 @@
#
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ==========================================================================
#

View File

@ -1,27 +1,34 @@
# ==========================================================================
#
# ZoneMinder SunEyes SP-P1802SWPTZ IP Control Protocol Module, $Date: 2017-03-19 23:00:00 +1000 (Sat, 19 March 2017) $, $Revision: 0002 $
# Copyright (C) 2001-2008 Philip Coombes
# Modified for use with Foscam FI8918W IP Camera by Dave Harris
# Modified Feb 2011 by Howard Durdle (http://durdl.es/x) to:
# fix horizontal panning, add presets and IR on/off
# use Control Device field to pass username and password
# Modified May 2014 by Arun Horne (http://arunhorne.co.uk) to:
# use HTTP basic auth as required by firmware 11.37.x.x upward
# Modified on Sep 28 2015 by Bobby Billingsley
# Changes made
# - Copied FI8918W.pm to SPP1802SWPTZ.pm
# - modified to control a SunEyes SP-P1802SWPTZ
# ==========================================================================
# ZoneMinder SunEyes SP-P1802SWPTZ IP Control Protocol Module
# Modified on 13 March 2017 by Steve Gilvarry
# -Address license and copyright issues
#
# 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 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.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# ==========================================================================
#
# This module contains the implementation of the SunEyes SP-P1802SWPTZ IP
# camera control protocol
#

View File

@ -45,265 +45,239 @@ use ZoneMinder::Config qw(:all);
use Time::HiRes qw( usleep );
sub new
{
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
sub new {
my $class = shift;
my $id = shift;
my $self = ZoneMinder::Control->new( $id );
bless( $self, $class );
srand( time() );
return $self;
}
our $AUTOLOAD;
sub AUTOLOAD
{
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) )
{
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
sub AUTOLOAD {
my $self = shift;
my $class = ref($self) || croak( "$self not object" );
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( exists($self->{$name}) ) {
return( $self->{$name} );
}
Fatal( "Can't access $name member of object of class $class" );
}
sub open
{
my $self = shift;
sub open {
my $self = shift;
$self->loadMonitor();
$self->loadMonitor();
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
use LWP::UserAgent;
$self->{ua} = LWP::UserAgent->new;
$self->{ua}->agent( "ZoneMinder Control Agent/".ZoneMinder::Base::ZM_VERSION );
$self->{state} = 'open';
$self->{state} = 'open';
}
sub close
{
my $self = shift;
$self->{state} = 'closed';
sub close {
my $self = shift;
$self->{state} = 'closed';
}
sub printMsg
{
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
sub printMsg {
my $self = shift;
my $msg = shift;
my $msg_len = length($msg);
Debug( $msg."[".$msg_len."]" );
Debug( $msg."[".$msg_len."]" );
}
sub sendCmd
{
my $self = shift;
my $cmd = shift;
sub sendCmd {
my $self = shift;
my $cmd = shift;
my $result = undef;
my $result = undef;
printMsg( $cmd, "Tx" );
printMsg( $cmd, "Tx" );
my $url;
if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
$url = $self->{Monitor}->{ControlAddress}.$cmd;
} else {
$url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd;
} # en dif
my $req = HTTP::Request->new( GET=>$url );
my $url;
if ( $self->{Monitor}->{ControlAddress} =~ /^http/ ) {
$url = $self->{Monitor}->{ControlAddress}.$cmd;
} else {
$url = 'http://'.$self->{Monitor}->{ControlAddress}.$cmd;
} # en dif
my $req = HTTP::Request->new( GET=>$url );
my $res = $self->{ua}->request($req);
my $res = $self->{ua}->request($req);
if ( $res->is_success )
{
$result = !undef;
}
else
{
Error( "Error check failed: '".$res->status_line()."'" );
}
if ( $res->is_success ) {
$result = !undef;
} else {
Error( "Error check failed: '".$res->status_line()."'" );
}
return( $result );
return( $result );
}
sub reset
{
my $self = shift;
Debug( "Camera Reset" );
my $cmd = "/admin/ptctl.cgi?move=reset";
$self->sendCmd( $cmd );
sub reset {
my $self = shift;
Debug( "Camera Reset" );
my $cmd = "/admin/ptctl.cgi?move=reset";
$self->sendCmd( $cmd );
}
sub moveMap
{
my $self = shift;
my $params = shift;
my $xcoord = $self->getParam( $params, 'xcoord' );
my $ycoord = $self->getParam( $params, 'ycoord' );
sub moveMap {
my $self = shift;
my $params = shift;
my $xcoord = $self->getParam( $params, 'xcoord' );
my $ycoord = $self->getParam( $params, 'ycoord' );
my $hor = $xcoord * 100 / $self->{Monitor}->{Width};
my $ver = $ycoord * 100 / $self->{Monitor}->{Height};
my $hor = $xcoord * 100 / $self->{Monitor}->{Width};
my $ver = $ycoord * 100 / $self->{Monitor}->{Height};
my $maxver = 8;
my $maxhor = 30;
my $horDir = "right";
my $verDir = "up";
my $horSteps = 0;
my $verSteps = 0;
my $maxver = 8;
my $maxhor = 30;
# Horizontal movement
if ($hor < 50) {
# left
$horSteps = ((50 - $hor) / 50) * $maxhor;
$horDir = "left";
}
elsif ($hor > 50) {
# right
$horSteps = (($hor - 50) / 50) * $maxhor;
$horDir = "right";
}
# Vertical movement
if ($ver < 50) {
# up
$verSteps = ((50 - $ver) / 50) * $maxver;
$verDir = "up";
}
elsif ($ver > 50) {
# down
$verSteps = (($ver - 50) / 50) * $maxver;
$verDir = "down";
}
my $horDir = "right";
my $verDir = "up";
my $horSteps = 0;
my $verSteps = 0;
my $v = int($verSteps);
my $h = int($horSteps);
# Horizontal movement
if ( $hor < 50 ) {
# left
$horSteps = ((50 - $hor) / 50) * $maxhor;
$horDir = "left";
}
elsif ( $hor > 50 ) {
# right
$horSteps = (($hor - 50) / 50) * $maxhor;
$horDir = "right";
}
Debug( "Move Map to $xcoord,$ycoord, hor=$h $horDir, ver=$v $verDir");
my $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$horDir&Degree=$h";
$self->sendCmd( $cmd );
$cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$verDir&Degree=$v";
$self->sendCmd( $cmd );
# Vertical movement
if ( $ver < 50 ) {
# up
$verSteps = ((50 - $ver) / 50) * $maxver;
$verDir = "up";
}
elsif ( $ver > 50 ) {
# down
$verSteps = (($ver - 50) / 50) * $maxver;
$verDir = "down";
}
my $v = int($verSteps);
my $h = int($horSteps);
Debug( "Move Map to $xcoord,$ycoord, hor=$h $horDir, ver=$v $verDir");
my $cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$horDir&Degree=$h";
$self->sendCmd( $cmd );
$cmd = "/cgi/admin/ptctrl.cgi?action=movedegree&Cmd=$verDir&Degree=$v";
$self->sendCmd( $cmd );
}
sub moveRelUp
{
my $self = shift;
my $params = shift;
my $step = $self->getParam( $params, 'tiltstep' );
Debug( "Step Up $step" );
my $cmd = "/admin/ptctl.cgi?move=up";
$self->sendCmd( $cmd );
sub moveRelUp {
my $self = shift;
my $params = shift;
my $step = $self->getParam( $params, 'tiltstep' );
Debug( "Step Up $step" );
my $cmd = "/admin/ptctl.cgi?move=up";
$self->sendCmd( $cmd );
}
sub moveRelDown
{
my $self = shift;
my $params = shift;
my $step = $self->getParam( $params, 'tiltstep' );
Debug( "Step Down $step" );
my $cmd = "/admin/ptctl.cgi?move=down";
$self->sendCmd( $cmd );
sub moveRelDown {
my $self = shift;
my $params = shift;
my $step = $self->getParam( $params, 'tiltstep' );
Debug( "Step Down $step" );
my $cmd = "/admin/ptctl.cgi?move=down";
$self->sendCmd( $cmd );
}
sub moveRelLeft
{
my $self = shift;
my $params = shift;
my $step = $self->getParam( $params, 'panstep' );
Debug( "Step Left $step" );
my $cmd = "/admin/ptctl.cgi?move=left";
$self->sendCmd( $cmd );
sub moveRelLeft {
my $self = shift;
my $params = shift;
my $step = $self->getParam( $params, 'panstep' );
if ( $self->{Monitor}->{Orientation} eq "hori" ) {
Debug( "Stepping Right because flipped horizontally " );
$self->sendCmd( "/admin/ptctl.cgi?move=right" );
} else {
Debug( "Step Left" );
$self->sendCmd( "/admin/ptctl.cgi?move=left" );
}
}
sub moveRelRight
{
my $self = shift;
my $params = shift;
my $step = $self->getParam( $params, 'panstep' );
Debug( "Step Right $step" );
my $cmd = "/admin/ptctl.cgi?move=right";
$self->sendCmd( $cmd );
sub moveRelRight {
my $self = shift;
my $params = shift;
my $step = $self->getParam( $params, 'panstep' );
if ( $self->{Monitor}->{Orientation} eq "hori" ) {
Debug( "Stepping Left because flipped horizontally " );
$self->sendCmd( "/admin/ptctl.cgi?move=left" );
} else {
Debug( "Step Right" );
$self->sendCmd( "/admin/ptctl.cgi?move=right" );
}
}
sub presetClear
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Clear Preset $preset" );
#my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset";
#$self->sendCmd( $cmd );
sub presetClear {
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Clear Preset $preset" );
#my $cmd = "/axis-cgi/com/ptz.cgi?removeserverpresetno=$preset";
#$self->sendCmd( $cmd );
}
sub presetSet
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Set Preset $preset" );
my $cmd = "/admin/ptctl.cgi?position=" . ($preset - 1) . "&positionname=zm$preset";
$self->sendCmd( $cmd );
sub presetSet {
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Set Preset $preset" );
my $cmd = "/admin/ptctl.cgi?position=" . ($preset - 1) . "&positionname=zm$preset";
$self->sendCmd( $cmd );
}
sub presetGoto
{
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Goto Preset $preset" );
my $cmd = "/admin/ptctl.cgi?move=p" . ($preset - 1);
$self->sendCmd( $cmd );
sub presetGoto {
my $self = shift;
my $params = shift;
my $preset = $self->getParam( $params, 'preset' );
Debug( "Goto Preset $preset" );
my $cmd = "/admin/ptctl.cgi?move=p" . ($preset - 1);
$self->sendCmd( $cmd );
}
sub presetHome
{
my $self = shift;
Debug( "Home Preset" );
my $cmd = "/admin/ptctl.cgi?move=h";
$self->sendCmd( $cmd );
sub presetHome {
my $self = shift;
Debug( "Home Preset" );
my $cmd = "/admin/ptctl.cgi?move=h";
$self->sendCmd( $cmd );
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
ZoneMinder::Database - Perl extension for blah blah blah
ZoneMinder::Control::SkyIPCam7xx.pm - Module for controlling AirLink101 SkyIPams
=head1 SYNOPSIS
use ZoneMinder::Database;
blah blah blah
use ZoneMinder::Control::SkyIPCam7xx;
=head1 DESCRIPTION
Stub documentation for ZoneMinder, created by h2xs. It looks like the
author of the extension was negligent enough to leave the stub
unedited.
Blah blah blah.
Module for controlling AirLink101 Cameras.
=head2 EXPORT
None by default.
=head1 SEE ALSO
Mention other useful documentation such as the documentation of
related modules or operating system documentation (such as man pages
in UNIX), or any relevant external documentation such as RFCs or
standards.
If you have a mailing list set up for your module, mention it here.
If you have a web site set up for your module, mention it here.
ZoneMinder::Control
=head1 AUTHOR
@ -318,5 +292,4 @@ This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.3 or,
at your option, any later version of Perl 5 you may have available.
=cut

View File

@ -75,9 +75,11 @@ sub zmDbConnect {
if ( $force ) {
zmDbDisconnect();
}
if ( !defined( $dbh ) ) {
my $options = shift;
if ( ( ! defined( $dbh ) ) or ! $dbh->ping() ) {
my ( $host, $portOrSocket ) = ( $ZoneMinder::Config::Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
my $socket;
my ( $host, $portOrSocket ) = ( $Config{ZM_DB_HOST} =~ /^([^:]+)(?::(.+))?$/ );
if ( defined($portOrSocket) ) {
if ( $portOrSocket =~ /^\// ) {
@ -89,7 +91,7 @@ sub zmDbConnect {
$socket = ";host=".$Config{ZM_DB_HOST};
}
$dbh = DBI->connect( "DBI:mysql:database=".$Config{ZM_DB_NAME}
.$socket
.$socket . ($options?';'.join(';', map { $_.'='.$$options{$_} } keys %{$options} ) : '' )
, $Config{ZM_DB_USER}
, $Config{ZM_DB_PASS}
);

View File

@ -28,30 +28,12 @@ use 5.006;
use strict;
use warnings;
require Exporter;
require ZoneMinder::Base;
require ZoneMinder::Object;
require Date::Manip;
our @ISA = qw(Exporter ZoneMinder::Base);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'functions' => [ qw(
) ]
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw();
our $VERSION = $ZoneMinder::Base::VERSION;
#our @ISA = qw(ZoneMinder::Object);
use parent qw(ZoneMinder::Object);
# ==========================================================================
#
@ -62,39 +44,24 @@ our $VERSION = $ZoneMinder::Base::VERSION;
use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Database qw(:all);
require Date::Parse;
use vars qw/ $table $primary_key /;
$table = 'Events';
$primary_key = 'Id';
use POSIX;
sub new {
my ( $parent, $id, $data ) = @_;
my $self = {};
bless $self, $parent;
$$self{dbh} = $ZoneMinder::Database::dbh;
#zmDbConnect();
if ( ( $$self{Id} = $id ) or $data ) {
#$log->debug("loading $parent $id") if $debug or DEBUG_ALL;
$self->load( $data );
sub Time {
if ( @_ > 1 ) {
$_[0]{Time} = $_[1];
}
return $self;
} # end sub new
if ( ! defined $_[0]{Time} ) {
sub load {
my ( $self, $data ) = @_;
my $type = ref $self;
if ( ! $data ) {
#$log->debug("Object::load Loading from db $type");
$data = $$self{dbh}->selectrow_hashref( 'SELECT * FROM Events WHERE Id=?', {}, $$self{Id} );
if ( ! $data ) {
Error( "Failure to load Event record for $$self{Id}: Reason: " . $$self{dbh}->errstr );
} else {
Debug( 3, "Loaded Event $$self{Id}" );
} # end if
} # end if ! $data
if ( $data and %$data ) {
@$self{keys %$data} = values %$data;
} # end if
} # end sub load
$_[0]{Time} = Date::Parse::str2time( $_[0]{StartTime} );
}
return $_[0]{Time};
}
sub Name {
if ( @_ > 1 ) {
@ -130,6 +97,7 @@ sub find {
my $filter = new ZoneMinder::Event( $$db_filter{Id}, $db_filter );
push @results, $filter;
} # end while
$sth->finish();
return @results;
}
@ -138,36 +106,51 @@ sub find_one {
return $results[0] if @results;
}
sub getEventPath {
sub getPath {
return Path( @_ );
}
sub Path {
my $event = shift;
my $event_path = "";
if ( $Config{ZM_USE_DEEP_STORAGE} ) {
$event_path = $Config{ZM_DIR_EVENTS}
.'/'.$event->{MonitorId}
.'/'.strftime( "%y/%m/%d/%H/%M/%S",
localtime($event->{Time})
)
;
} else {
$event_path = $Config{ZM_DIR_EVENTS}
.'/'.$event->{MonitorId}
.'/'.$event->{Id}
;
if ( @_ > 1 ) {
$$event{Path} = $_[1];
if ( ! -e $$event{Path} ) {
Error("Setting path for event $$event{Id} to $_[1] but does not exist!");
}
}
if ( index($Config{ZM_DIR_EVENTS},'/') != 0 ){
$event_path = $Config{ZM_PATH_WEB}
.'/'.$event_path
;
}
return( $event_path );
if ( ! $$event{Path} ) {
my $path = ($Config{ZM_DIR_EVENTS}=~/^\//) ? $Config{ZM_DIR_EVENTS} : $Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS};
if ( $Config{ZM_USE_DEEP_STORAGE} ) {
if ( $event->Time() ) {
$$event{Path} = join('/',
$path,
$event->{MonitorId},
strftime( "%y/%m/%d/%H/%M/%S",
localtime($event->Time())
),
);
} else {
Error("Event $$event{Id} has no value for Time(), unable to determine path");
$$event{Path} = '';
}
} else {
$$event{Path} = join('/',
$path,
$event->{MonitorId},
$event->{Id},
);
}
} # end if
return $$event{Path};
}
sub GenerateVideo {
my ( $self, $rate, $fps, $scale, $size, $overwrite, $format ) = @_;
my $event_path = getEventPath( $self );
my $event_path = $self->getPath( );
chdir( $event_path );
( my $video_name = $self->{Name} ) =~ s/\s/_/g;
@ -228,9 +211,7 @@ sub GenerateVideo {
my $command = $Config{ZM_PATH_FFMPEG}
." -y -r $frame_rate "
.$Config{ZM_FFMPEG_INPUT_OPTIONS}
." -i %0"
.$Config{ZM_EVENT_IMAGE_DIGITS}
."d-capture.jpg -s $video_size "
.' -i ' . ( $$self{DefaultVideo} ? $$self{DefaultVideo} : '%0'.$Config{ZM_EVENT_IMAGE_DIGITS} .'d-capture.jpg' )
#. " -f concat -i /tmp/event_files.txt"
." -s $video_size "
.$Config{ZM_FFMPEG_OUTPUT_OPTIONS}
@ -253,54 +234,143 @@ sub GenerateVideo {
Info( "Video file $video_file already exists for event $self->{Id}\n" );
return $event_path.'/'.$video_file;
}
return;
return;
} # end sub GenerateVideo
sub delete {
my $event = $_[0];
Info( "Deleting event $event->{Id} from Monitor $event->{MonitorId} $event->{StartTime}\n" );
$ZoneMinder::Database::dbh->ping();
# Do it individually to avoid locking up the table for new events
my $sql = 'delete from Events where Id = ?';
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
$sth->finish();
if ( ! $Config{ZM_OPT_FAST_DELETE} ) {
my $sql = 'delete from Frames where EventId = ?';
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
$sth->finish();
$sql = 'delete from Stats where EventId = ?';
$sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
$res = $sth->execute( $event->{Id} )
or Fatal( "Can't execute '$sql': ".$sth->errstr() );
$sth->finish();
$event->delete_files( );
} else {
Debug('Not deleting frames, stats and files for speed.');
}
} # end sub delete
sub delete_files {
my $storage_path = ($Config{ZM_DIR_EVENTS}=~/^\//) ? $Config{ZM_DIR_EVENTS} : $Config{ZM_PATH_WEB}.'/'.$Config{ZM_DIR_EVENTS};
if ( ! $storage_path ) {
Fatal("Empty path when deleting files for event $_[0]{Id} ");
return;
}
chdir ( $storage_path );
if ( $Config{ZM_USE_DEEP_STORAGE} ) {
if ( ! $_[0]{MonitorId} ) {
Error("No monitor id assigned to event $_[0]{Id}");
return;
}
Debug("Deleting files for Event $_[0]{Id} from $storage_path.");
my $link_path = $_[0]{MonitorId}.'/*/*/*/.'.$_[0]{Id};
#Debug( "LP1:$link_path" );
my @links = glob($link_path);
#Debug( "L:".$links[0].": $!" );
if ( @links ) {
( $link_path ) = ( $links[0] =~ /^(.*)$/ ); # De-taint
#Debug( "LP2:$link_path" );
( my $day_path = $link_path ) =~ s/\.\d+//;
#Debug( "DP:$day_path" );
my $event_path = $day_path.readlink( $link_path );
( $event_path ) = ( $event_path =~ /^(.*)$/ ); # De-taint
#Debug( "EP:$event_path" );
my $command = "/bin/rm -rf $event_path";
#Debug( "C:$command" );
ZoneMinder::General::executeShellCommand( $command );
unlink( $link_path ) or Error( "Unable to unlink '$link_path': $!" );
my @path_parts = split( /\//, $event_path );
for ( my $i = int(@path_parts)-2; $i >= 1; $i-- ) {
my $delete_path = join( '/', @path_parts[0..$i] );
#Debug( "DP$i:$delete_path" );
my @has_files = glob( join('/', $storage_path,$delete_path,'*' ) );
#Debug( "HF1:".$has_files[0] ) if ( @has_files );
last if ( @has_files );
@has_files = glob( join('/', $storage_path, $delete_path, '.[0-9]*' ) );
#Debug( "HF2:".$has_files[0] ) if ( @has_files );
last if ( @has_files );
my $command = "/bin/rm -rf $storage_path/$delete_path";
ZoneMinder::General::executeShellCommand( $command );
}
}
} else {
my $command = "/bin/rm -rf $storage_path/$_[0]{MonitorId}/$_[0]{Id}";
ZoneMinder::General::executeShellCommand( $command );
}
} # end sub delete_files
sub Storage {
return new ZoneMinder::Storage( $_[0]{StorageId} );
}
sub check_for_in_filesystem {
my $path = $_[0]->Path();
if ( $path ) {
my @files = glob( $path . '/*' );
Debug("Checking for files for event $_[0]{Id} at $path using glob $path/* found " . scalar @files . " files");
return 1 if @files;
}
Debug("Checking for files for event $_[0]{Id} at $path using glob $path/* found no files");
return 0;
}
sub age {
if ( ! $_[0]{age} ) {
$_[0]{age} = (time() - ($^T - ((-M $_[0]->Path() ) * 24*60*60)));
}
return $_[0]{age};
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
ZoneMinder::Database - Perl extension for blah blah blah
ZoneMinder::Event - Perl Class for events
=head1 SYNOPSIS
use ZoneMinder::Event;
blah blah blah
=head1 DESCRIPTION
Stub documentation for ZoneMinder, created by h2xs. It looks like the
author of the extension was negligent enough to leave the stub
unedited.
Blah blah blah.
=head2 EXPORT
None by default.
=head1 SEE ALSO
Mention other useful documentation such as the documentation of
related modules or operating system documentation (such as man pages
in UNIX), or any relevant external documentation such as RFCs or
standards.
If you have a mailing list set up for your module, mention it here.
If you have a web site set up for your module, mention it here.
The Event class has everything you need to deal with events from Perl.
=head1 AUTHOR
Philip Coombes, E<lt>philip.coombes@zoneminder.comE<gt>
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2001-2008 Philip Coombes
Copyright (C) 2001-2017 ZoneMinder LLC
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.3 or,

View File

@ -28,31 +28,14 @@ use 5.006;
use strict;
use warnings;
require Exporter;
require ZoneMinder::Base;
require Date::Manip;
our @ISA = qw(Exporter ZoneMinder::Base);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use ZoneMinder ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'functions' => [ qw(
) ]
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw();
our $VERSION = $ZoneMinder::Base::VERSION;
use parent qw(ZoneMinder::Object);
use vars qw/ $table $primary_key /;
$table = 'Filters';
$primary_key = 'Id';
# ==========================================================================
#
# General Utility Functions
@ -62,40 +45,10 @@ our $VERSION = $ZoneMinder::Base::VERSION;
use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Database qw(:all);
require ZoneMinder::Server;
use POSIX;
sub new {
my ( $parent, $id, $data ) = @_;
my $self = {};
bless $self, $parent;
$$self{dbh} = $ZoneMinder::Database::dbh;
#zmDbConnect();
if ( ( $$self{Id} = $id ) or $data ) {
#$log->debug("loading $parent $id") if $debug or DEBUG_ALL;
$self->load( $data );
}
return $self;
} # end sub new
sub load {
my ( $self, $data ) = @_;
my $type = ref $self;
if ( ! $data ) {
#$log->debug("Object::load Loading from db $type");
$data = $$self{dbh}->selectrow_hashref( 'SELECT * FROM Filter WHERE Id=?', {}, $$self{Id} );
if ( ! $data ) {
Error( "Failure to load Filter record for $$self{Id}: Reason: " . $$self{dbh}->errstr );
} else {
Debug( 3, "Loaded Filter $$self{Id}" );
} # end if
} # end if ! $data
if ( $data and %$data ) {
@$self{keys %$data} = values %$data;
} # end if
} # end sub load
sub Name {
if ( @_ > 1 ) {
$_[0]{Name} = $_[1];
@ -130,6 +83,8 @@ sub find {
my $filter = new ZoneMinder::Filter( $$db_filter{Id}, $db_filter );
push @results, $filter;
} # end while
$sth->finish();
return @results;
}
@ -140,7 +95,6 @@ sub find_one {
sub Execute {
my $self = $_[0];
my $sql = $self->Sql();
if ( $self->{HasDiskPercent} ) {
@ -156,8 +110,9 @@ sub Execute {
$sql =~ s/zmSystemLoad/$load/g;
}
my $sth = $$self{dbh}->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$$self{dbh}->errstr() );
Debug("Filter::Execute SQL ($sql)");
my $sth = $ZoneMinder::Database::dbh->prepare_cached( $sql )
or Fatal( "Can't prepare '$sql': ".$ZoneMinder::Database::dbh->errstr() );
my $res = $sth->execute();
if ( !$res ) {
Error( "Can't execute filter '$sql', ignoring: ".$sth->errstr() );
@ -169,6 +124,7 @@ sub Execute {
push @results, $event;
}
$sth->finish();
Debug("Loaded " . @results . " events for filter $_[0]{Name} using query ($sql)");
return @results;
}
@ -176,78 +132,74 @@ sub Sql {
my $self = $_[0];
if ( ! $$self{Sql} ) {
my $filter_expr = ZoneMinder::General::jsonDecode( $self->{Query} );
my $sql = "SELECT E.Id,
E.MonitorId,
my $sql = "SELECT E.*,
unix_timestamp(E.StartTime) as Time,
M.Name as MonitorName,
M.DefaultRate,
M.DefaultScale,
E.Name,
E.Cause,
E.Notes,
E.StartTime,
unix_timestamp(E.StartTime) as Time,
E.Length,
E.Frames,
E.AlarmFrames,
E.TotScore,
E.AvgScore,
E.MaxScore,
E.Archived,
E.Videoed,
E.Uploaded,
E.Emailed,
E.Messaged,
E.Executed
M.DefaultScale
FROM Events as E
INNER JOIN Monitors as M on M.Id = E.MonitorId
";
$self->{Sql} = '';
if ( $filter_expr->{terms} ) {
for ( my $i = 0; $i < @{$filter_expr->{terms}}; $i++ ) {
if ( exists($filter_expr->{terms}[$i]->{cnj}) ) {
$self->{Sql} .= " ".$filter_expr->{terms}[$i]->{cnj}." ";
foreach my $term ( @{$filter_expr->{terms}} ) {
if ( exists($term->{cnj}) ) {
$self->{Sql} .= " ".$term->{cnj}." ";
}
if ( exists($filter_expr->{terms}[$i]->{obr}) ) {
$self->{Sql} .= " ".str_repeat( "(", $filter_expr->{terms}[$i]->{obr} )." ";
if ( exists($term->{obr}) ) {
$self->{Sql} .= " ".str_repeat( "(", $term->{obr} )." ";
}
my $value = $filter_expr->{terms}[$i]->{val};
my $value = $term->{val};
my @value_list;
if ( $filter_expr->{terms}[$i]->{attr} ) {
if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) {
my ( $temp_attr_name ) = $filter_expr->{terms}[$i]->{attr} =~ /^Monitor(.+)$/;
if ( $term->{attr} ) {
if ( $term->{attr} =~ /^Monitor/ ) {
my ( $temp_attr_name ) = $term->{attr} =~ /^Monitor(.+)$/;
$self->{Sql} .= "M.".$temp_attr_name;
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) {
} elsif ( $term->{attr} =~ /^Server/ ) {
$self->{Sql} .= "M.".$term->{attr};
} elsif ( $term->{attr} eq 'DateTime' ) {
$self->{Sql} .= "E.StartTime";
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) {
} elsif ( $term->{attr} eq 'Date' ) {
$self->{Sql} .= "to_days( E.StartTime )";
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) {
} elsif ( $term->{attr} eq 'Time' ) {
$self->{Sql} .= "extract( hour_second from E.StartTime )";
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Weekday' ) {
} elsif ( $term->{attr} eq 'Weekday' ) {
$self->{Sql} .= "weekday( E.StartTime )";
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskPercent' ) {
} elsif ( $term->{attr} eq 'DiskPercent' ) {
$self->{Sql} .= "zmDiskPercent";
$self->{HasDiskPercent} = !undef;
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DiskBlocks' ) {
} elsif ( $term->{attr} eq 'DiskBlocks' ) {
$self->{Sql} .= "zmDiskBlocks";
$self->{HasDiskBlocks} = !undef;
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'SystemLoad' ) {
} elsif ( $term->{attr} eq 'SystemLoad' ) {
$self->{Sql} .= "zmSystemLoad";
$self->{HasSystemLoad} = !undef;
} else {
$self->{Sql} .= "E.".$filter_expr->{terms}[$i]->{attr};
$self->{Sql} .= "E.".$term->{attr};
}
( my $stripped_value = $value ) =~ s/^["\']+?(.+)["\']+?$/$1/;
foreach my $temp_value ( split( /["'\s]*?,["'\s]*?/, $stripped_value ) ) {
if ( $filter_expr->{terms}[$i]->{attr} =~ /^Monitor/ ) {
if ( $term->{attr} =~ /^Monitor/ ) {
$value = "'$temp_value'";
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Name'
|| $filter_expr->{terms}[$i]->{attr} eq 'Cause'
|| $filter_expr->{terms}[$i]->{attr} eq 'Notes'
} elsif ( $term->{attr} eq 'ServerId' ) {
if ( $temp_value eq 'ZM_SERVER_ID' ) {
$value = "'$Config{ZM_SERVER_ID}'";
# This gets used later, I forget for what
$$self{Server} = new ZoneMinder::Server( $Config{ZM_SERVER_ID} );
} else {
$value = "'$temp_value'";
# This gets used later, I forget for what
$$self{Server} = new ZoneMinder::Server( $temp_value );
}
} elsif ( $term->{attr} eq 'Name'
|| $term->{attr} eq 'Cause'
|| $term->{attr} eq 'Notes'
) {
$value = "'$temp_value'";
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'DateTime' ) {
} elsif ( $term->{attr} eq 'DateTime' ) {
$value = DateTimeToSQL( $temp_value );
if ( !$value ) {
Error( "Error parsing date/time '$temp_value', "
@ -255,7 +207,7 @@ sub Sql {
return;
}
$value = "'$value'";
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Date' ) {
} elsif ( $term->{attr} eq 'Date' ) {
$value = DateTimeToSQL( $temp_value );
if ( !$value ) {
Error( "Error parsing date/time '$temp_value', "
@ -263,7 +215,7 @@ sub Sql {
return;
}
$value = "to_days( '$value' )";
} elsif ( $filter_expr->{terms}[$i]->{attr} eq 'Time' ) {
} elsif ( $term->{attr} eq 'Time' ) {
$value = DateTimeToSQL( $temp_value );
if ( !$value ) {
Error( "Error parsing date/time '$temp_value', "
@ -277,21 +229,21 @@ sub Sql {
push( @value_list, $value );
} # end foreach temp_value
} # end if has an attr
if ( $filter_expr->{terms}[$i]->{op} ) {
if ( $filter_expr->{terms}[$i]->{op} eq '=~' ) {
if ( $term->{op} ) {
if ( $term->{op} eq '=~' ) {
$self->{Sql} .= " regexp $value";
} elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) {
} elsif ( $term->{op} eq '!~' ) {
$self->{Sql} .= " not regexp $value";
} elsif ( $filter_expr->{terms}[$i]->{op} eq '=[]' ) {
} elsif ( $term->{op} eq '=[]' ) {
$self->{Sql} .= " in (".join( ",", @value_list ).")";
} elsif ( $filter_expr->{terms}[$i]->{op} eq '!~' ) {
} elsif ( $term->{op} eq '!~' ) {
$self->{Sql} .= " not in (".join( ",", @value_list ).")";
} else {
$self->{Sql} .= " ".$filter_expr->{terms}[$i]->{op}." $value";
$self->{Sql} .= " ".$term->{op}." $value";
}
} # end if has an operator
if ( exists($filter_expr->{terms}[$i]->{cbr}) ) {
$self->{Sql} .= " ".str_repeat( ")", $filter_expr->{terms}[$i]->{cbr} )." ";
if ( exists($term->{cbr}) ) {
$self->{Sql} .= " ".str_repeat( ")", $term->{cbr} )." ";
}
} # end foreach term
} # end if terms
@ -308,22 +260,24 @@ sub Sql {
}
my @auto_terms;
if ( $self->{AutoArchive} ) {
push( @auto_terms, "E.Archived = 0" )
push @auto_terms, "E.Archived = 0";
}
# Don't do this, it prevents re-generation and concatenation.
# If the file already exists, then the video won't be re-recreated
if ( $self->{AutoVideo} ) {
push( @auto_terms, "E.Videoed = 0" )
push @auto_terms, "E.Videoed = 0";
}
if ( $self->{AutoUpload} ) {
push( @auto_terms, "E.Uploaded = 0" )
push @auto_terms, "E.Uploaded = 0";
}
if ( $self->{AutoEmail} ) {
push( @auto_terms, "E.Emailed = 0" )
push @auto_terms, "E.Emailed = 0";
}
if ( $self->{AutoMessage} ) {
push( @auto_terms, "E.Messaged = 0" )
push @auto_terms, "E.Messaged = 0";
}
if ( $self->{AutoExecute} ) {
push( @auto_terms, "E.Executed = 0" )
push @auto_terms, "E.Executed = 0";
}
if ( @auto_terms ) {
$sql .= " and ( ".join( " or ", @auto_terms )." )";
@ -361,14 +315,13 @@ sub Sql {
if ( $filter_expr->{limit} ) {
$sql .= " limit 0,".$filter_expr->{limit};
}
Debug( "SQL:$sql\n" );
$self->{Sql} = $sql;
} # end if has Sql
return $self->{Sql};
} # end sub Sql
sub getDiskPercent {
my $command = "df .";
my $command = "df " . ($_[0] ? $_[0] : '.');
my $df = qx( $command );
my $space = -1;
if ( $df =~ /\s(\d+)%/ms ) {

View File

@ -103,8 +103,10 @@ sub getCmdFormat {
my $suffix = "";
my $command = $prefix.$null_command.$suffix;
Debug( "Testing \"$command\"\n" );
my $output = qx($command);
my $output = qx($command 2>&1);
my $status = $? >> 8;
$output //= $!;
if ( !$status ) {
Debug( "Test ok, using format \"$prefix<command>$suffix\"\n" );
return( $prefix, $suffix );
@ -116,8 +118,10 @@ sub getCmdFormat {
$suffix = "'";
$command = $prefix.$null_command.$suffix;
Debug( "Testing \"$command\"\n" );
my $output = qx($command);
my $output = qx($command 2>&1);
my $status = $? >> 8;
$output //= $!;
if ( !$status ) {
Debug( "Test ok, using format \"$prefix<command>$suffix\"\n" );
return( $prefix, $suffix );
@ -129,8 +133,10 @@ sub getCmdFormat {
$suffix = "'";
$command = $prefix.$null_command.$suffix;
Debug( "Testing \"$command\"\n" );
$output = qx($command);
$output = qx($command 2>&1);
$status = $? >> 8;
$output //= $!;
if ( !$status ) {
Debug( "Test ok, using format \"$prefix<command>$suffix\"\n" );
return( $prefix, $suffix );

View File

@ -41,7 +41,7 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'constants' => [ qw(
constants => [ qw(
STATE_IDLE
STATE_PREALARM
STATE_ALARM
@ -56,7 +56,7 @@ our %EXPORT_TAGS = (
TRIGGER_ON
TRIGGER_OFF
) ],
'functions' => [ qw(
functions => [ qw(
zmMemVerify
zmMemInvalidate
zmMemRead
@ -77,12 +77,12 @@ our %EXPORT_TAGS = (
zmTriggerEventOn
zmTriggerEventOff
zmTriggerEventCancel
zmTriggerShowtext
zmTriggerShowtext
) ],
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
our @EXPORT = qw();
@ -115,7 +115,7 @@ use constant TRIGGER_OFF => 2;
use Storable qw( freeze thaw );
if ( "@ENABLE_MMAP@" eq 'yes' ) {
if ( '@ENABLE_MMAP@' eq 'yes' ) {
# 'yes' if memory is mmapped
require ZoneMinder::Memory::Mapped;
ZoneMinder::Memory::Mapped->import();
@ -141,46 +141,45 @@ our $native = $arch/8;
our $mem_seq = 0;
our $mem_data = {
"shared_data" => { "type"=>"SharedData", "seq"=>$mem_seq++, "contents"=> {
"size" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"last_write_index" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"last_read_index" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"state" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"last_event" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"action" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"brightness" => { "type"=>"int32", "seq"=>$mem_seq++ },
"hue" => { "type"=>"int32", "seq"=>$mem_seq++ },
"colour" => { "type"=>"int32", "seq"=>$mem_seq++ },
"contrast" => { "type"=>"int32", "seq"=>$mem_seq++ },
"alarm_x" => { "type"=>"int32", "seq"=>$mem_seq++ },
"alarm_y" => { "type"=>"int32", "seq"=>$mem_seq++ },
"valid" => { "type"=>"uint8", "seq"=>$mem_seq++ },
"active" => { "type"=>"uint8", "seq"=>$mem_seq++ },
"signal" => { "type"=>"uint8", "seq"=>$mem_seq++ },
"format" => { "type"=>"uint8", "seq"=>$mem_seq++ },
"imagesize" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"epadding1" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"epadding2" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"last_write_time" => { "type"=>"time_t64", "seq"=>$mem_seq++ },
"last_read_time" => { "type"=>"time_t64", "seq"=>$mem_seq++ },
"control_state" => { "type"=>"uint8[256]", "seq"=>$mem_seq++ },
shared_data => { type=>'SharedData', seq=>$mem_seq++, contents=> {
size => { type=>'uint32', seq=>$mem_seq++ },
last_write_index => { type=>'uint32', seq=>$mem_seq++ },
last_read_index => { type=>'uint32', seq=>$mem_seq++ },
state => { type=>'uint32', seq=>$mem_seq++ },
last_event => { type=>'uint32', seq=>$mem_seq++ },
action => { type=>'uint32', seq=>$mem_seq++ },
brightness => { type=>'int32', seq=>$mem_seq++ },
hue => { type=>'int32', seq=>$mem_seq++ },
colour => { type=>'int32', seq=>$mem_seq++ },
contrast => { type=>'int32', seq=>$mem_seq++ },
alarm_x => { type=>'int32', seq=>$mem_seq++ },
alarm_y => { type=>'int32', seq=>$mem_seq++ },
valid => { type=>'uint8', seq=>$mem_seq++ },
active => { type=>'uint8', seq=>$mem_seq++ },
signal => { type=>'uint8', seq=>$mem_seq++ },
format => { type=>'uint8', seq=>$mem_seq++ },
imagesize => { type=>'uint32', seq=>$mem_seq++ },
epadding1 => { type=>'uint32', seq=>$mem_seq++ },
epadding2 => { type=>'uint32', seq=>$mem_seq++ },
last_write_time => { type=>'time_t64', seq=>$mem_seq++ },
last_read_time => { type=>'time_t64', seq=>$mem_seq++ },
control_state => { type=>'uint8[256]', seq=>$mem_seq++ },
}
},
"trigger_data" => { "type"=>"TriggerData", "seq"=>$mem_seq++, "contents"=> {
"size" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"trigger_state" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"trigger_score" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"padding" => { "type"=>"uint32", "seq"=>$mem_seq++ },
"trigger_cause" => { "type"=>"int8[32]", "seq"=>$mem_seq++ },
"trigger_text" => { "type"=>"int8[256]", "seq"=>$mem_seq++ },
"trigger_showtext" => { "type"=>"int8[256]", "seq"=>$mem_seq++ },
trigger_data => { type=>'TriggerData', seq=>$mem_seq++, 'contents'=> {
size => { type=>'uint32', seq=>$mem_seq++ },
trigger_state => { type=>'uint32', seq=>$mem_seq++ },
trigger_score => { type=>'uint32', seq=>$mem_seq++ },
padding => { type=>'uint32', seq=>$mem_seq++ },
trigger_cause => { type=>'int8[32]', seq=>$mem_seq++ },
trigger_text => { type=>'int8[256]', seq=>$mem_seq++ },
trigger_showtext => { type=>'int8[256]', seq=>$mem_seq++ },
}
},
"end" => { "seq"=>$mem_seq++, "size"=> 0 }
end => { seq=>$mem_seq++, size=>0 }
};
our $mem_size = 0;
our $mem_verified = {};
sub zmMemInit {
my $offset = 0;
@ -196,28 +195,28 @@ sub zmMemInit {
}
}
foreach my $member_data ( sort { $a->{seq} <=> $b->{seq} } values( %{$section_data->{contents}} ) ) {
if ( $member_data->{type} eq "long"
|| $member_data->{type} eq "ulong"
|| $member_data->{type} eq "size_t"
if ( $member_data->{type} eq 'long'
|| $member_data->{type} eq 'ulong'
|| $member_data->{type} eq 'size_t'
) {
$member_data->{size} = $member_data->{align} = $native;
} elsif ( $member_data->{type} eq "int64"
|| $member_data->{type} eq "uint64"
|| $member_data->{type} eq "time_t64"
} elsif ( $member_data->{type} eq 'int64'
|| $member_data->{type} eq 'uint64'
|| $member_data->{type} eq 'time_t64'
) {
$member_data->{size} = $member_data->{align} = 8;
} elsif ( $member_data->{type} eq "int32"
|| $member_data->{type} eq "uint32"
|| $member_data->{type} eq "bool4"
} elsif ( $member_data->{type} eq 'int32'
|| $member_data->{type} eq 'uint32'
|| $member_data->{type} eq 'bool4'
) {
$member_data->{size} = $member_data->{align} = 4;
} elsif ($member_data->{type} eq "int16"
|| $member_data->{type} eq "uint16"
} elsif ($member_data->{type} eq 'int16'
|| $member_data->{type} eq 'uint16'
) {
$member_data->{size} = $member_data->{align} = 2;
} elsif ( $member_data->{type} eq "int8"
|| $member_data->{type} eq "uint8"
|| $member_data->{type} eq "bool1"
} elsif ( $member_data->{type} eq 'int8'
|| $member_data->{type} eq 'uint8'
|| $member_data->{type} eq 'bool1'
) {
$member_data->{size} = $member_data->{align} = 1;
} elsif ( $member_data->{type} =~ /^u?int8\[(\d+)\]$/ ) {
@ -249,51 +248,52 @@ sub zmMemVerify {
return( undef );
}
my $mem_key = zmMemKey( $monitor );
if ( !defined($mem_verified->{$mem_key}) ) {
my $sd_size = zmMemRead( $monitor, "shared_data:size", 1 );
if ( $sd_size != $mem_data->{shared_data}->{size} ) {
if ( $sd_size ) {
Error( "Shared data size conflict in shared_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{shared_data}->{size}
.", got "
.$sd_size
);
} else {
Debug( "Shared data size conflict in shared_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{shared_data}->{size}
.", got ".$sd_size
);
}
return( undef );
my $sd_size = zmMemRead( $monitor, 'shared_data:size', 1 );
if ( $sd_size != $mem_data->{shared_data}->{size} ) {
if ( $sd_size ) {
Error( "Shared data size conflict in shared_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{shared_data}->{size}
.", got "
.$sd_size
);
} else {
Debug( "Shared data size conflict in shared_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{shared_data}->{size}
.", got ".$sd_size
);
}
my $td_size = zmMemRead( $monitor, "trigger_data:size", 1 );
if ( $td_size != $mem_data->{trigger_data}->{size} ) {
if ( $td_size ) {
Error( "Shared data size conflict in trigger_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{triggger_data}->{size}
.", got "
.$td_size
);
} else {
Debug( "Shared data size conflict in trigger_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{triggger_data}->{size}
.", got "
.$td_size
);
}
return( undef );
}
$mem_verified->{$mem_key} = !undef;
return( undef );
}
my $td_size = zmMemRead( $monitor, 'trigger_data:size', 1 );
if ( $td_size != $mem_data->{trigger_data}->{size} ) {
if ( $td_size ) {
Error( "Shared data size conflict in trigger_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{triggger_data}->{size}
.", got "
.$td_size
);
} else {
Debug( "Shared data size conflict in trigger_data for monitor "
.$monitor->{Name}
.", expected "
.$mem_data->{triggger_data}->{size}
.", got "
.$td_size
);
}
return( undef );
}
if ( !zmMemRead($monitor, 'shared_data:valid',1) ) {
Error( "Shared data not valid for monitor $$monitor{Id}" );
return( undef );
}
return( !undef );
}
@ -325,32 +325,32 @@ sub zmMemRead {
return( undef );
}
my $value;
if ( $type eq "long" ) {
( $value ) = unpack( "l!", $data );
} elsif ( $type eq "ulong" || $type eq "size_t" ) {
( $value ) = unpack( "L!", $data );
} elsif ( $type eq "int64" || $type eq "time_t64" ) {
# The "q" type is only available on 64bit platforms, so use native.
( $value ) = unpack( "l!", $data );
} elsif ( $type eq "uint64" ) {
# The "q" type is only available on 64bit platforms, so use native.
( $value ) = unpack( "L!", $data );
} elsif ( $type eq "int32" ) {
( $value ) = unpack( "l", $data );
} elsif ( $type eq "uint32" || $type eq "bool4" ) {
( $value ) = unpack( "L", $data );
} elsif ( $type eq "int16" ) {
( $value ) = unpack( "s", $data );
} elsif ( $type eq "uint16" ) {
( $value ) = unpack( "S", $data );
} elsif ( $type eq "int8" ) {
( $value ) = unpack( "c", $data );
} elsif ( $type eq "uint8" || $type eq "bool1" ) {
( $value ) = unpack( "C", $data );
if ( $type eq 'long' ) {
( $value ) = unpack( 'l!', $data );
} elsif ( $type eq 'ulong' || $type eq 'size_t' ) {
( $value ) = unpack( 'L!', $data );
} elsif ( $type eq 'int64' || $type eq 'time_t64' ) {
# The 'q' type is only available on 64bit platforms, so use native.
( $value ) = unpack( 'l!', $data );
} elsif ( $type eq 'uint64' ) {
# The 'q' type is only available on 64bit platforms, so use native.
( $value ) = unpack( 'L!', $data );
} elsif ( $type eq 'int32' ) {
( $value ) = unpack( 'l', $data );
} elsif ( $type eq 'uint32' || $type eq 'bool4' ) {
( $value ) = unpack( 'L', $data );
} elsif ( $type eq 'int16' ) {
( $value ) = unpack( 's', $data );
} elsif ( $type eq 'uint16' ) {
( $value ) = unpack( 'S', $data );
} elsif ( $type eq 'int8' ) {
( $value ) = unpack( 'c', $data );
} elsif ( $type eq 'uint8' || $type eq 'bool1' ) {
( $value ) = unpack( 'C', $data );
} elsif ( $type =~ /^int8\[\d+\]$/ ) {
( $value ) = unpack( "Z".$size, $data );
( $value ) = unpack( 'Z'.$size, $data );
} elsif ( $type =~ /^uint8\[\d+\]$/ ) {
( $value ) = unpack( "C".$size, $data );
( $value ) = unpack( 'C'.$size, $data );
} else {
Fatal( "Unexpected type '".$type."' found for '".$field."'" );
}
@ -366,7 +366,6 @@ sub zmMemInvalidate {
my $monitor = shift;
my $mem_key = zmMemKey($monitor);
if ( $mem_key ) {
delete $mem_verified->{$mem_key};
zmMemDetach( $monitor );
}
}
@ -395,34 +394,34 @@ sub zmMemWrite {
my $size = $mem_data->{$section}->{contents}->{$element}->{size};
my $data;
if ( $type eq "long" ) {
$data = pack( "l!", $value );
} elsif ( $type eq "ulong" || $type eq "size_t" ) {
$data = pack( "L!", $value );
} elsif ( $type eq "int64" || $type eq "time_t64" ) {
# The "q" type is only available on 64bit platforms, so use native.
$data = pack( "l!", $value );
} elsif ( $type eq "uint64" ) {
# The "q" type is only available on 64bit platforms, so use native.
$data = pack( "L!", $value );
} elsif ( $type eq "int32" ) {
$data = pack( "l", $value );
} elsif ( $type eq "uint32" || $type eq "bool4" ) {
$data = pack( "L", $value );
} elsif ( $type eq "int16" ) {
$data = pack( "s", $value );
} elsif ( $type eq "uint16" ) {
$data = pack( "S", $value );
} elsif ( $type eq "int8" ) {
$data = pack( "c", $value );
} elsif ( $type eq "uint8" || $type eq "bool1" ) {
$data = pack( "C", $value );
if ( $type eq 'long' ) {
$data = pack( 'l!', $value );
} elsif ( $type eq 'ulong' || $type eq 'size_t' ) {
$data = pack( 'L!', $value );
} elsif ( $type eq 'int64' || $type eq 'time_t64' ) {
# The 'q' type is only available on 64bit platforms, so use native.
$data = pack( 'l!', $value );
} elsif ( $type eq 'uint64' ) {
# The 'q' type is only available on 64bit platforms, so use native.
$data = pack( 'L!', $value );
} elsif ( $type eq 'int32' ) {
$data = pack( 'l', $value );
} elsif ( $type eq 'uint32' || $type eq 'bool4' ) {
$data = pack( 'L', $value );
} elsif ( $type eq 'int16' ) {
$data = pack( 's', $value );
} elsif ( $type eq 'uint16' ) {
$data = pack( 'S', $value );
} elsif ( $type eq 'int8' ) {
$data = pack( 'c', $value );
} elsif ( $type eq 'uint8' || $type eq 'bool1' ) {
$data = pack( 'C', $value );
} elsif ( $type =~ /^int8\[\d+\]$/ ) {
$data = pack( "Z".$size, $value );
$data = pack( 'Z'.$size, $value );
} elsif ( $type =~ /^uint8\[\d+\]$/ ) {
$data = pack( "C".$size, $value );
$data = pack( 'C'.$size, $value );
} else {
Fatal( "Unexpected type '".$type."' found for '".$field."'" );
Fatal( "Unexpected type \"$type\" found for \"$field\"" );
}
if ( !zmMemPut( $monitor, $offset, $size, $data ) ) {
@ -439,26 +438,26 @@ sub zmMemWrite {
sub zmGetMonitorState {
my $monitor = shift;
return( zmMemRead( $monitor, "shared_data:state" ) );
return( zmMemRead( $monitor, 'shared_data:state' ) );
}
sub zmGetAlarmLocation {
my $monitor = shift;
return( zmMemRead( $monitor, [ "shared_data:alarm_x", "shared_data:alarm_y" ] ) );
return( zmMemRead( $monitor, [ 'shared_data:alarm_x', 'shared_data:alarm_y' ] ) );
}
sub zmSetControlState {
my $monitor = shift;
my $control_state = shift;
zmMemWrite( $monitor, { "shared_data:control_state" => $control_state } );
zmMemWrite( $monitor, { 'shared_data:control_state' => $control_state } );
}
sub zmGetControlState {
my $monitor = shift;
return( zmMemRead( $monitor, "shared_data:control_state" ) );
return( zmMemRead( $monitor, 'shared_data:control_state' ) );
}
sub zmSaveControlState {
@ -494,8 +493,8 @@ sub zmHasAlarmed {
my $monitor = shift;
my $last_event_id = shift;
my ( $state, $last_event ) = zmMemRead( $monitor, [ "shared_data:state"
,"shared_data:last_event"
my ( $state, $last_event ) = zmMemRead( $monitor, [ 'shared_data:state'
,'shared_data:last_event'
]
);
@ -510,63 +509,63 @@ sub zmHasAlarmed {
sub zmGetLastEvent {
my $monitor = shift;
return( zmMemRead( $monitor, "shared_data:last_event" ) );
return( zmMemRead( $monitor, 'shared_data:last_event' ) );
}
sub zmGetLastWriteTime {
my $monitor = shift;
return( zmMemRead( $monitor, "shared_data:last_write_time" ) );
return( zmMemRead( $monitor, 'shared_data:last_write_time' ) );
}
sub zmGetLastReadTime {
my $monitor = shift;
return( zmMemRead( $monitor, "shared_data:last_read_time" ) );
return( zmMemRead( $monitor, 'shared_data:last_read_time' ) );
}
sub zmGetMonitorActions {
my $monitor = shift;
return( zmMemRead( $monitor, "shared_data:action" ) );
return( zmMemRead( $monitor, 'shared_data:action' ) );
}
sub zmMonitorEnable {
my $monitor = shift;
my $action = zmMemRead( $monitor, "shared_data:action" );
my $action = zmMemRead( $monitor, 'shared_data:action' );
$action |= ACTION_SUSPEND;
zmMemWrite( $monitor, { "shared_data:action" => $action } );
zmMemWrite( $monitor, { 'shared_data:action' => $action } );
}
sub zmMonitorDisable {
my $monitor = shift;
my $action = zmMemRead( $monitor, "shared_data:action" );
my $action = zmMemRead( $monitor, 'shared_data:action' );
$action |= ACTION_RESUME;
zmMemWrite( $monitor, { "shared_data:action" => $action } );
zmMemWrite( $monitor, { 'shared_data:action' => $action } );
}
sub zmMonitorSuspend {
my $monitor = shift;
my $action = zmMemRead( $monitor, "shared_data:action" );
my $action = zmMemRead( $monitor, 'shared_data:action' );
$action |= ACTION_SUSPEND;
zmMemWrite( $monitor, { "shared_data:action" => $action } );
zmMemWrite( $monitor, { 'shared_data:action' => $action } );
}
sub zmMonitorResume {
my $monitor = shift;
my $action = zmMemRead( $monitor, "shared_data:action" );
my $action = zmMemRead( $monitor, 'shared_data:action' );
$action |= ACTION_RESUME;
zmMemWrite( $monitor, { "shared_data:action" => $action } );
zmMemWrite( $monitor, { 'shared_data:action' => $action } );
}
sub zmGetTriggerState {
my $monitor = shift;
return( zmMemRead( $monitor, "trigger_data:trigger_state" ) );
return( zmMemRead( $monitor, 'trigger_data:trigger_state' ) );
}
sub zmTriggerEventOn {
@ -577,12 +576,12 @@ sub zmTriggerEventOn {
my $showtext = shift;
my $values = {
"trigger_data:trigger_score" => $score,
"trigger_data:trigger_cause" => $cause,
'trigger_data:trigger_score' => $score,
'trigger_data:trigger_cause' => $cause,
};
$values->{"trigger_data:trigger_text"} = $text if ( defined($text) );
$values->{"trigger_data:trigger_showtext"} = $showtext if ( defined($showtext) );
$values->{"trigger_data:trigger_state"} = TRIGGER_ON; # Write state last so event not read incomplete
$values->{'trigger_data:trigger_text'} = $text if ( defined($text) );
$values->{'trigger_data:trigger_showtext'} = $showtext if ( defined($showtext) );
$values->{'trigger_data:trigger_state'} = TRIGGER_ON; # Write state last so event not read incomplete
zmMemWrite( $monitor, $values );
}
@ -591,11 +590,11 @@ sub zmTriggerEventOff {
my $monitor = shift;
my $values = {
"trigger_data:trigger_state" => TRIGGER_OFF,
"trigger_data:trigger_score" => 0,
"trigger_data:trigger_cause" => "",
"trigger_data:trigger_text" => "",
"trigger_data:trigger_showtext" => "",
'trigger_data:trigger_state' => TRIGGER_OFF,
'trigger_data:trigger_score' => 0,
'trigger_data:trigger_cause' => '',
'trigger_data:trigger_text' => '',
'trigger_data:trigger_showtext' => '',
};
zmMemWrite( $monitor, $values );
@ -605,11 +604,11 @@ sub zmTriggerEventCancel {
my $monitor = shift;
my $values = {
"trigger_data:trigger_state" => TRIGGER_CANCEL,
"trigger_data:trigger_score" => 0,
"trigger_data:trigger_cause" => "",
"trigger_data:trigger_text" => "",
"trigger_data:trigger_showtext" => "",
'trigger_data:trigger_state' => TRIGGER_CANCEL,
'trigger_data:trigger_score' => 0,
'trigger_data:trigger_cause' => '',
'trigger_data:trigger_text' => '',
'trigger_data:trigger_showtext' => '',
};
zmMemWrite( $monitor, $values );
@ -620,7 +619,7 @@ sub zmTriggerShowtext {
my $showtext = shift;
my $values = {
"trigger_data:trigger_showtext" => $showtext,
'trigger_data:trigger_showtext' => $showtext,
};
zmMemWrite( $monitor, $values );
@ -649,7 +648,7 @@ if ( zmMemVerify( $monitor ) ) {
"shared_data:last_write_index"
]
);
zmMemWrite( $monitor, { "trigger_data:trigger_showtext" => "Some Text" } );
zmMemWrite( $monitor, { 'trigger_data:trigger_showtext' => "Some Text" } );
=head1 DESCRIPTION

View File

@ -40,15 +40,15 @@ our @ISA = qw(Exporter ZoneMinder::Base);
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = (
'functions' => [ qw(
zmMemKey
zmMemAttach
zmMemDetach
zmMemGet
zmMemPut
zmMemClean
) ],
);
functions => [ qw(
zmMemKey
zmMemAttach
zmMemDetach
zmMemGet
zmMemPut
zmMemClean
) ],
);
push( @{$EXPORT_TAGS{all}}, @{$EXPORT_TAGS{$_}} ) foreach keys %EXPORT_TAGS;
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
@ -68,133 +68,119 @@ use ZoneMinder::Logger qw(:all);
use Sys::Mmap;
sub zmMemKey
{
my $monitor = shift;
return( defined($monitor->{MMapAddr})?$monitor->{MMapAddr}:undef );
sub zmMemKey {
my $monitor = shift;
return( defined($monitor->{MMapAddr})?$monitor->{MMapAddr}:undef );
}
sub zmMemAttach
{
my ( $monitor, $size ) = @_;
if ( ! $size ) {
Error( "No size passed to zmMemAttach for monitor $$monitor{Id}\n" );
return( undef );
sub zmMemAttach {
my ( $monitor, $size ) = @_;
if ( ! $size ) {
Error( "No size passed to zmMemAttach for monitor $$monitor{Id}\n" );
return( undef );
}
if ( !defined($monitor->{MMapAddr}) ) {
my $mmap_file = $Config{ZM_PATH_MAP}."/zm.mmap.".$monitor->{Id};
if ( ! -e $mmap_file ) {
Error( sprintf( "Memory map file '%s' does not exist. zmc might not be running."
, $mmap_file
)
);
return ( undef );
}
if ( !defined($monitor->{MMapAddr}) )
{
my $mmap_file = $Config{ZM_PATH_MAP}."/zm.mmap.".$monitor->{Id};
if ( ! -e $mmap_file ) {
Error( sprintf( "Memory map file '%s' does not exist. zmc might not be running."
, $mmap_file
)
);
return ( undef );
}
my $mmap_file_size = -s $mmap_file;
my $mmap_file_size = -s $mmap_file;
if ( $mmap_file_size < $size ) {
Error( sprintf( "Memory map file '%s' should have been %d but was instead %d"
, $mmap_file
, $size
, $mmap_file_size
)
);
return ( undef );
}
if ( !open( MMAP, "+<", $mmap_file ) )
{
Error( sprintf( "Can't open memory map file '%s': $!\n", $mmap_file ) );
return( undef );
}
my $mmap = undef;
my $mmap_addr = mmap( $mmap, $size, PROT_READ|PROT_WRITE, MAP_SHARED, \*MMAP );
if ( !$mmap_addr || !$mmap )
{
Error( sprintf( "Can't mmap to file '%s': $!\n", $mmap_file ) );
close( MMAP );
return( undef );
}
$monitor->{MMapHandle} = \*MMAP;
$monitor->{MMapAddr} = $mmap_addr;
$monitor->{MMap} = \$mmap;
if ( $mmap_file_size < $size ) {
Error( sprintf( "Memory map file '%s' should have been %d but was instead %d"
, $mmap_file
, $size
, $mmap_file_size
)
);
return ( undef );
}
return( !undef );
my $MMAP;
if ( !open( $MMAP, '+<', $mmap_file ) ) {
Error( sprintf( "Can't open memory map file '%s': $!\n", $mmap_file ) );
return( undef );
}
my $mmap = undef;
my $mmap_addr = mmap( $mmap, $size, PROT_READ|PROT_WRITE, MAP_SHARED, $MMAP );
if ( !$mmap_addr || !$mmap ) {
Error( sprintf( "Can't mmap to file '%s': $!\n", $mmap_file ) );
close( $MMAP );
return( undef );
}
$monitor->{MMapHandle} = $MMAP;
$monitor->{MMapAddr} = $mmap_addr;
$monitor->{MMap} = \$mmap;
}
return( !undef );
}
sub zmMemDetach
{
my $monitor = shift;
sub zmMemDetach {
my $monitor = shift;
if ( $monitor->{MMap} )
{
if ( ! munmap( ${$monitor->{MMap}} ) ) {
Warn( "Unable to munmap for monitor $$monitor{Id}\n");
}
delete $monitor->{MMap};
}
if ( $monitor->{MMapAddr} )
{
delete $monitor->{MMapAddr};
}
if ( $monitor->{MMapHandle} )
{
close( $monitor->{MMapHandle} );
delete $monitor->{MMapHandle};
if ( $monitor->{MMap} ) {
if ( ! munmap( ${$monitor->{MMap}} ) ) {
Warn( "Unable to munmap for monitor $$monitor{Id}\n");
}
delete $monitor->{MMap};
}
if ( $monitor->{MMapAddr} ) {
delete $monitor->{MMapAddr};
}
if ( $monitor->{MMapHandle} ) {
close( $monitor->{MMapHandle} );
delete $monitor->{MMapHandle};
}
}
sub zmMemGet
{
my $monitor = shift;
my $offset = shift;
my $size = shift;
sub zmMemGet {
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $mmap = $monitor->{MMap};
if ( !$mmap || !$$mmap )
{
Error( sprintf( "Can't read from mapped memory for monitor '%d', gone away?"
, $monitor->{Id}
)
my $mmap = $monitor->{MMap};
if ( !$mmap || !$$mmap ) {
Error( sprintf( "Can't read from mapped memory for monitor '%d', gone away?"
, $monitor->{Id}
)
);
return( undef );
}
my $data = substr( $$mmap, $offset, $size );
return( $data );
return( undef );
}
my $data = substr( $$mmap, $offset, $size );
return( $data );
}
sub zmMemPut
{
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $data = shift;
sub zmMemPut {
my $monitor = shift;
my $offset = shift;
my $size = shift;
my $data = shift;
my $mmap = $monitor->{MMap};
if ( !$mmap || !$$mmap )
{
Error( sprintf( "Can't write mapped memory for monitor '%d', gone away?"
, $monitor->{Id}
)
my $mmap = $monitor->{MMap};
if ( !$mmap || !$$mmap ) {
Error( sprintf( "Can't write mapped memory for monitor '%d', gone away?"
, $monitor->{Id}
)
);
return( undef );
}
substr( $$mmap, $offset, $size ) = $data;
return( !undef );
return( undef );
}
substr( $$mmap, $offset, $size ) = $data;
return( !undef );
}
sub zmMemClean
{
Debug( "Removing memory map files\n" );
my $mapPath = $Config{ZM_PATH_MAP}."/zm.mmap.*";
foreach my $mapFile( glob( $mapPath ) )
{
( $mapFile ) = $mapFile =~ /^(.+)$/;
Debug( "Removing memory map file '$mapFile'\n" );
unlink( $mapFile );
}
sub zmMemClean {
Debug( "Removing memory map files\n" );
my $mapPath = $Config{ZM_PATH_MAP}.'/zm.mmap.*';
foreach my $mapFile( glob( $mapPath ) ) {
( $mapFile ) = $mapFile =~ /^(.+)$/;
Debug( "Removing memory map file '$mapFile'\n" );
unlink( $mapFile );
}
}
1;

View File

@ -0,0 +1,85 @@
# ==========================================================================
#
# ZoneMinder Monitor Module, $Date$, $Revision$
# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This module contains the common definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::Monitor;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
require ZoneMinder::Object;
require ZoneMinder::Server;
#our @ISA = qw(Exporter ZoneMinder::Base);
use parent qw(ZoneMinder::Object);
# ==========================================================================
#
# General Utility Functions
#
# ==========================================================================
use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Database qw(:all);
use POSIX;
use vars qw/ $table $primary_key /;
$table = 'Monitors';
$primary_key = 'Id';
sub Server {
return new ZoneMinder::Server( $_[0]{ServerId} );
} # end sub Server
1;
__END__
=head1 NAME
ZoneMinder::Monitor - Perl Class for Monitors
=head1 SYNOPSIS
use ZoneMinder::Monitor;
=head1 DESCRIPTION
=head1 AUTHOR
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2001-2017 ZoneMinder LLC
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.3 or,
at your option, any later version of Perl 5 you may have available.
=cut

View File

@ -0,0 +1,150 @@
# ==========================================================================
#
# ZoneMinder Object Module, $Date$, $Revision$
# Copyright (C) 2001-2017 ZoneMinder LLC
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ==========================================================================
#
# This module contains the common definitions and functions used by the rest
# of the ZoneMinder scripts
#
package ZoneMinder::Object;
use 5.006;
use strict;
use warnings;
require ZoneMinder::Base;
our @ISA = qw(ZoneMinder::Base);
# ==========================================================================
#
# General Utility Functions
#
# ==========================================================================
use ZoneMinder::Config qw(:all);
use ZoneMinder::Logger qw(:all);
use ZoneMinder::Database qw(:all);
use vars qw/ $AUTOLOAD /;
sub new {
my ( $parent, $id, $data ) = @_;
my $self = {};
bless $self, $parent;
no strict 'refs';
my $primary_key = ${$parent.'::primary_key'};
if ( ! $primary_key ) {
Error( 'NO primary_key for type ' . $parent );
return;
} # end if
if ( ( $$self{$primary_key} = $id ) or $data ) {
#$log->debug("loading $parent $id") if $debug or DEBUG_ALL;
$self->load( $data );
}
return $self;
} # end sub new
sub load {
my ( $self, $data ) = @_;
my $type = ref $self;
if ( ! $data ) {
no strict 'refs';
my $table = ${$type.'::table'};
if ( ! $table ) {
Error( 'NO table for type ' . $type );
return;
} # end if
my $primary_key = ${$type.'::primary_key'};
if ( ! $primary_key ) {
Error( 'NO primary_key for type ' . $type );
return;
} # end if
if ( ! $$self{$primary_key} ) {
my ( $caller, undef, $line ) = caller;
Error( (ref $self) . "::load called without $primary_key from $caller:$line");
} else {
#$log->debug("Object::load Loading from db $type");
Debug("Loading $type from $table WHERE $primary_key = $$self{$primary_key}");
$data = $ZoneMinder::Database::dbh->selectrow_hashref( "SELECT * FROM $table WHERE $primary_key=?", {}, $$self{$primary_key} );
if ( ! $data ) {
if ( $ZoneMinder::Database::dbh->errstr ) {
Error( "Failure to load Object record for $$self{$primary_key}: Reason: " . $ZoneMinder::Database::dbh->errstr );
} else {
Debug("No Results Loading $type from $table WHERE $primary_key = $$self{$primary_key}");
} # end if
} # end if
} # end if
} # end if ! $data
if ( $data and %$data ) {
@$self{keys %$data} = values %$data;
} # end if
} # end sub load
sub AUTOLOAD {
my ( $self, $newvalue ) = @_;
my $type = ref($_[0]);
my $name = $AUTOLOAD;
$name =~ s/.*://;
if ( @_ > 1 ) {
return $_[0]{$name} = $_[1];
}
return $_[0]{$name};
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
ZoneMinder::Object
=head1 SYNOPSIS
use parent ZoneMinder::Object;
This package should likely not be used directly, as it is meant mainly to be a parent for all other ZoneMinder classes.
=head1 DESCRIPTION
A base Object to act as parent for other ZoneMinder Objects.
=head2 EXPORT
None by default.
=head1 AUTHOR
Isaac Connor, E<lt>isaac@zoneminder.comE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2001-2017 ZoneMinder LLC
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.3 or,
at your option, any later version of Perl 5 you may have available.
=cut

View File

@ -41,9 +41,9 @@ yet.
=head1 OPTIONS
-r, --report - Just report don't actually do anything
-i, --interactive - Ask before applying any changes
-c, --continuous - Run continuously
-i, --interactive - Ask before applying any changes
-r, --report - Just report don't actually do anything
-v, --version - Print the installed version of ZoneMinder
=cut
@ -57,8 +57,8 @@ use bytes;
# ==========================================================================
use constant MAX_AGED_DIRS => 10; # Number of event dirs to check age on
use constant RECOVER_TAG => "(r)"; # Tag to append to event name when recovered
use constant RECOVER_TEXT => "Recovered."; # Text to append to event notes when recovered
use constant RECOVER_TAG => '(r)'; # Tag to append to event name when recovered
use constant RECOVER_TEXT => 'Recovered.'; # Text to append to event notes when recovered
# ==========================================================================
#
@ -96,20 +96,23 @@ logInit();
logSetSignal();
GetOptions(
'report' =>\$report,
'interactive' =>\$interactive,
'continuous' =>\$continuous,
'version' =>\$version
) or pod2usage(-exitstatus => -1);
continuous =>\$continuous,
interactive =>\$interactive,
report =>\$report,
version =>\$version
) or pod2usage(-exitstatus => -1);
if ( $version ) {
print( ZoneMinder::Base::ZM_VERSION . "\n");
exit(0);
print( ZoneMinder::Base::ZM_VERSION . "\n");
exit(0);
}
if ( ($report + $interactive + $continuous) > 1 )
{
print( STDERR "Error, only one option may be specified\n" );
pod2usage(-exitstatus => -1);
if ( ($report + $interactive + $continuous) > 1 ) {
print( STDERR "Error, only one option may be specified\n" );
pod2usage(-exitstatus => -1);
}
if ( ! exists $Config{ZM_AUDIT_MIN_AGE} ) {
Fatal('ZM_AUDIT_MIN_AGE is not set in config.');
}
my $dbh = zmDbConnect();
@ -123,517 +126,439 @@ my $image_path = IMAGE_PATH;
my $loop = 1;
my $cleaned = 0;
MAIN: while( $loop ) {
while ( ! ( $dbh and $dbh->ping() ) ) {
$dbh = zmDbConnect();
while ( ! ( $dbh and $dbh->ping() ) ) {
$dbh = zmDbConnect();
if ( $continuous ) {
Error("Unable to connect to database");
# if we are running continuously, then just skip to the next
# interval, otherwise we are a one off run, so wait a second and
# retry until someone kills us.
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} );
} else {
Fatal("Unable to connect to database");
} # end if
} # end while can't connect to the db
if ( $continuous ) {
Error('Unable to connect to database');
# if we are running continuously, then just skip to the next
# interval, otherwise we are a one off run, so wait a second and
# retry until someone kills us.
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} );
} else {
Fatal('Unable to connect to database');
} # end if
} # end while can't connect to the db
if ( $continuous ) {
# if we are running continuously, then just skip to the next
# interval, otherwise we are a one off run, so wait a second and
# retry until someone kills us.
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} );
} else {
sleep 1;
} # end if
if ( $continuous ) {
# if we are running continuously, then just skip to the next
# interval, otherwise we are a one off run, so wait a second and
# retry until someone kills us.
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} );
} else {
sleep 1;
} # end if
if ( ! exists $Config{ZM_AUDIT_MIN_AGE} ) {
Fatal("ZM_AUDIT_MIN_AGE is not set in config.");
my $db_monitors;
my $monitorSelectSql = 'select Id from Monitors order by Id';
my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql )
or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() );
my $eventSelectSql = 'SELECT Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age
FROM Events WHERE MonitorId = ? ORDER BY Id';
my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql )
or Fatal( "Can't prepare '$eventSelectSql': ".$dbh->errstr() );
$cleaned = 0;
my $res = $monitorSelectSth->execute()
or Fatal( "Can't execute: ".$monitorSelectSth->errstr() );
while( my $monitor = $monitorSelectSth->fetchrow_hashref() ) {
Debug( "Found database monitor '$monitor->{Id}'" );
my $db_events = $db_monitors->{$monitor->{Id}} = {};
my $res = $eventSelectSth->execute( $monitor->{Id} )
or Fatal( "Can't execute: ".$eventSelectSth->errstr() );
while ( my $event = $eventSelectSth->fetchrow_hashref() ) {
$db_events->{$event->{Id}} = $event->{Age};
}
Debug( 'Got '.int(keys(%$db_events))." events\n" );
}
my $db_monitors;
my $monitorSelectSql = "select Id from Monitors order by Id";
my $monitorSelectSth = $dbh->prepare_cached( $monitorSelectSql )
or Fatal( "Can't prepare '$monitorSelectSql': ".$dbh->errstr() );
my $eventSelectSql = "SELECT Id, (unix_timestamp() - unix_timestamp(StartTime)) as Age
FROM Events WHERE MonitorId = ? ORDER BY Id";
my $eventSelectSth = $dbh->prepare_cached( $eventSelectSql )
or Fatal( "Can't prepare '$eventSelectSql': ".$dbh->errstr() );
my $fs_monitors;
foreach my $monitor ( glob('[0-9]*') ) {
# Thie glob above gives all files starting with a digit. So a monitor with a name starting with a digit will be in this list.
next if $monitor =~ /\D/;
Debug( "Found filesystem monitor '$monitor'" );
my $fs_events = $fs_monitors->{$monitor} = {};
( my $monitor_dir ) = ( $monitor =~ /^(.*)$/ ); # De-taint
$cleaned = 0;
my $res = $monitorSelectSth->execute()
or Fatal( "Can't execute: ".$monitorSelectSth->errstr() );
while( my $monitor = $monitorSelectSth->fetchrow_hashref() )
{
Debug( "Found database monitor '$monitor->{Id}'" );
my $db_events = $db_monitors->{$monitor->{Id}} = {};
my $res = $eventSelectSth->execute( $monitor->{Id} )
or Fatal( "Can't execute: ".$eventSelectSth->errstr() );
while ( my $event = $eventSelectSth->fetchrow_hashref() )
{
$db_events->{$event->{Id}} = $event->{Age};
if ( $Config{ZM_USE_DEEP_STORAGE} ) {
foreach my $day_dir ( glob("$monitor_dir/*/*/*") ) {
Debug( "Checking day dir $day_dir" );
( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint
if ( ! chdir( $day_dir ) ) {
Error( "Can't chdir to '$day_dir': $!" );
next;
}
Debug( "Got ".int(keys(%$db_events))." events\n" );
}
my $fs_monitors;
foreach my $monitor ( glob("[0-9]*") )
{
# Thie glob above gives all files starting with a digit. So a monitor with a name starting with a digit will be in this list.
next if $monitor =~ /\D/;
Debug( "Found filesystem monitor '$monitor'" );
my $fs_events = $fs_monitors->{$monitor} = {};
( my $monitor_dir ) = ( $monitor =~ /^(.*)$/ ); # De-taint
if ( $Config{ZM_USE_DEEP_STORAGE} )
{
foreach my $day_dir ( glob("$monitor_dir/*/*/*") )
{
Debug( "Checking $day_dir" );
( $day_dir ) = ( $day_dir =~ /^(.*)$/ ); # De-taint
chdir( $day_dir );
opendir( DIR, "." )
or Fatal( "Can't open directory '$day_dir': $!" );
my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR );
closedir( DIR );
my $count = 0;
foreach my $event_link ( @event_links )
{
Debug( "Checking link $event_link" );
( my $event = $event_link ) =~ s/^.*\.//;
my $event_path = readlink( $event_link );
if ( $count++ > MAX_AGED_DIRS )
{
$fs_events->{$event} = -1;
}
else
{
if ( !-e $event_path )
{
aud_print( "Event link $day_dir/$event_link does not point to valid target" );
if ( confirm() )
{
( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint
unlink( $event_link );
$cleaned = 1;
}
}
else
{
$fs_events->{$event} = (time() - ($^T - ((-M $event_path) * 24*60*60)));
}
}
}
chdir( EVENT_PATH );
}
if ( ! opendir( DIR, '.' ) ) {
Error( "Can't open directory '$day_dir': $!" );
next;
}
else
{
chdir( $monitor_dir );
opendir( DIR, "." ) or Fatal( "Can't open directory '$monitor_dir': $!" );
my @temp_events = sort { $b <=> $a } grep { -d $_ && $_ =~ /^\d+$/ } readdir( DIR );
closedir( DIR );
my $count = 0;
foreach my $event ( @temp_events )
{
if ( $count++ > MAX_AGED_DIRS )
{
$fs_events->{$event} = -1;
}
else
{
$fs_events->{$event} = (time() - ($^T - ((-M $event) * 24*60*60)));
}
}
chdir( EVENT_PATH );
}
Debug( "Got ".int(keys(%$fs_events))." events\n" );
}
redo MAIN if ( $cleaned );
$cleaned = 0;
while ( my ( $fs_monitor, $fs_events ) = each(%$fs_monitors) )
{
if ( my $db_events = $db_monitors->{$fs_monitor} )
{
if ( $fs_events )
{
while ( my ( $fs_event, $age ) = each(%$fs_events ) )
{
if ( !defined($db_events->{$fs_event}) && ($age < 0 || ($age > $Config{ZM_AUDIT_MIN_AGE})) )
{
aud_print( "Filesystem event '$fs_monitor/$fs_event' does not exist in database" );
if ( confirm() )
{
deleteEventFiles( $fs_event, $fs_monitor );
$cleaned = 1;
}
}
}
}
}
else
{
aud_print( "Filesystem monitor '$fs_monitor' does not exist in database" );
if ( confirm() )
{
my $command = "rm -rf $fs_monitor";
executeShellCommand( $command );
my @event_links = sort { $b <=> $a } grep { -l $_ } readdir( DIR );
closedir( DIR );
my $count = 0;
foreach my $event_link ( @event_links ) {
Debug( "Checking link $event_link" );
( my $event = $event_link ) =~ s/^.*\.//;
my $event_path = readlink( $event_link );
if ( $count++ > MAX_AGED_DIRS ) {
$fs_events->{$event} = -1;
} else {
if ( !-e $event_path ) {
aud_print( "Event link $day_dir/$event_link does not point to valid target" );
if ( confirm() ) {
( $event_link ) = ( $event_link =~ /^(.*)$/ ); # De-taint
unlink( $event_link );
$cleaned = 1;
}
} else {
$fs_events->{$event} = (time() - ($^T - ((-M $event_path) * 24*60*60)));
}
}
} # end foreach event_link
chdir( EVENT_PATH );
} # end foreach day_dir
} else {
chdir( $monitor_dir );
opendir( DIR, "." ) or Fatal( "Can't open directory '$monitor_dir': $!" );
my @temp_events = sort { $b <=> $a } grep { -d $_ && $_ =~ /^\d+$/ } readdir( DIR );
closedir( DIR );
my $count = 0;
foreach my $event ( @temp_events ) {
if ( $count++ > MAX_AGED_DIRS ) {
$fs_events->{$event} = -1;
} else {
$fs_events->{$event} = (time() - ($^T - ((-M $event) * 24*60*60)));
}
}
chdir( EVENT_PATH );
}
Debug( 'Got '.int(keys(%$fs_events))." events\n" );
} # end foreach monitor Id
redo MAIN if ( $cleaned );
my $monitor_links;
foreach my $link ( glob("*") )
{
next if ( !-l $link );
next if ( -e $link );
aud_print( "Filesystem monitor link '$link' does not point to valid monitor directory" );
if ( confirm() )
{
( $link ) = ( $link =~ /^(.*)$/ ); # De-taint
my $command = qq`rm "$link"`;
executeShellCommand( $command );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
$cleaned = 0;
my $deleteMonitorSql = "delete low_priority from Monitors where Id = ?";
my $deleteMonitorSth = $dbh->prepare_cached( $deleteMonitorSql )
or Fatal( "Can't prepare '$deleteMonitorSql': ".$dbh->errstr() );
my $deleteEventSql = "delete low_priority from Events where Id = ?";
my $deleteEventSth = $dbh->prepare_cached( $deleteEventSql )
or Fatal( "Can't prepare '$deleteEventSql': ".$dbh->errstr() );
my $deleteFramesSql = "delete low_priority from Frames where EventId = ?";
my $deleteFramesSth = $dbh->prepare_cached( $deleteFramesSql )
or Fatal( "Can't prepare '$deleteFramesSql': ".$dbh->errstr() );
my $deleteStatsSql = "delete low_priority from Stats where EventId = ?";
my $deleteStatsSth = $dbh->prepare_cached( $deleteStatsSql )
or Fatal( "Can't prepare '$deleteStatsSql': ".$dbh->errstr() );
while ( my ( $db_monitor, $db_events ) = each(%$db_monitors) )
{
if ( my $fs_events = $fs_monitors->{$db_monitor} )
{
if ( $db_events )
{
while ( my ( $db_event, $age ) = each(%$db_events ) )
{
if ( !defined($fs_events->{$db_event}) ) {
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem" );
if ( confirm() ) {
my $res = $deleteEventSth->execute( $db_event )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$res = $deleteFramesSth->execute( $db_event )
or Fatal( "Can't execute: ".$deleteFramesSth->errstr() );
$res = $deleteStatsSth->execute( $db_event )
or Fatal( "Can't execute: ".$deleteStatsSth->errstr() );
$cleaned = 1;
}
} else {
aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem but too young to delete." );
}
}
}
$cleaned = 0;
while ( my ( $fs_monitor, $fs_events ) = each(%$fs_monitors) ) {
if ( my $db_events = $db_monitors->{$fs_monitor} ) {
if ( $fs_events ) {
while ( my ( $fs_event, $age ) = each(%$fs_events ) ) {
if ( !defined($db_events->{$fs_event}) && ($age < 0 || ($age > $Config{ZM_AUDIT_MIN_AGE})) ) {
aud_print( "Filesystem event '$fs_monitor/$fs_event' does not exist in database" );
if ( confirm() ) {
deleteEventFiles( $fs_event, $fs_monitor );
$cleaned = 1;
}
}
}
else
{
aud_print( "Database monitor '$db_monitor' does not exist in filesystem" );
#if ( confirm() )
#{
# We don't actually do this in case it's new
#my $res = $deleteMonitorSth->execute( $db_monitor )
# or Fatal( "Can't execute: ".$deleteMonitorSth->errstr() );
#$cleaned = 1;
#}
}
}
} else {
aud_print( "Filesystem monitor '$fs_monitor' does not exist in database" );
if ( confirm() ) {
my $command = "rm -rf $fs_monitor";
executeShellCommand( $command );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
}
# Remove orphaned events (with no monitor)
$cleaned = 0;
my $selectOrphanedEventsSql = "SELECT Events.Id, Events.Name
FROM Events LEFT JOIN Monitors ON (Events.MonitorId = Monitors.Id)
WHERE isnull(Monitors.Id)";
my $selectOrphanedEventsSth = $dbh->prepare_cached( $selectOrphanedEventsSql )
or Fatal( "Can't prepare '$selectOrphanedEventsSql': ".$dbh->errstr() );
$res = $selectOrphanedEventsSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedEventsSth->errstr() );
while( my $event = $selectOrphanedEventsSth->fetchrow_hashref() )
{
aud_print( "Found orphaned event with no monitor '$event->{Id}'" );
if ( confirm() )
{
$res = $deleteEventSth->execute( $event->{Id} )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$cleaned = 1;
}
my $monitor_links;
foreach my $link ( glob('*') ) {
next if ( !-l $link );
next if ( -e $link );
aud_print( "Filesystem monitor link '$link' does not point to valid monitor directory" );
if ( confirm() ) {
( $link ) = ( $link =~ /^(.*)$/ ); # De-taint
my $command = qq`rm "$link"`;
executeShellCommand( $command );
$cleaned = 1;
}
redo MAIN if ( $cleaned );
}
redo MAIN if ( $cleaned );
# Remove empty events (with no frames)
$cleaned = 0;
my $selectEmptyEventsSql = "SELECT E.Id AS Id, E.StartTime, F.EventId FROM Events as E LEFT JOIN Frames as F ON (E.Id = F.EventId)
WHERE isnull(F.EventId) AND now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second > E.StartTime";
my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql )
or Fatal( "Can't prepare '$selectEmptyEventsSql': ".$dbh->errstr() );
$res = $selectEmptyEventsSth->execute()
or Fatal( "Can't execute: ".$selectEmptyEventsSth->errstr() );
while( my $event = $selectEmptyEventsSth->fetchrow_hashref() )
{
aud_print( "Found empty event with no frame records '$event->{Id}'" );
if ( confirm() )
{
$res = $deleteEventSth->execute( $event->{Id} )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
# Remove orphaned frame records
$cleaned = 0;
my $selectOrphanedFramesSql = "SELECT DISTINCT EventId FROM Frames
WHERE EventId NOT IN (SELECT Id FROM Events)";
my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql )
or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() );
$res = $selectOrphanedFramesSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedFramesSth->errstr() );
while( my $frame = $selectOrphanedFramesSth->fetchrow_hashref() )
{
aud_print( "Found orphaned frame records for event '$frame->{EventId}'" );
if ( confirm() )
{
$res = $deleteFramesSth->execute( $frame->{EventId} )
or Fatal( "Can't execute: ".$deleteFramesSth->errstr() );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
# Remove orphaned stats records
$cleaned = 0;
my $selectOrphanedStatsSql = "SELECT DISTINCT EventId FROM Stats
WHERE EventId NOT IN (SELECT Id FROM Events)";
my $selectOrphanedStatsSth = $dbh->prepare_cached( $selectOrphanedStatsSql )
or Fatal( "Can't prepare '$selectOrphanedStatsSql': ".$dbh->errstr() );
$res = $selectOrphanedStatsSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedStatsSth->errstr() );
while( my $stat = $selectOrphanedStatsSth->fetchrow_hashref() )
{
aud_print( "Found orphaned statistic records for event '$stat->{EventId}'" );
if ( confirm() )
{
$res = $deleteStatsSth->execute( $stat->{EventId} )
or Fatal( "Can't execute: ".$deleteStatsSth->errstr() );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
# New audit to close any events that were left open for longer than MIN_AGE seconds
my $selectUnclosedEventsSql =
"SELECT E.Id,
max(F.TimeStamp) as EndTime,
unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length,
max(F.FrameId) as Frames,
count(if(F.Score>0,1,NULL)) as AlarmFrames,
sum(F.Score) as TotScore,
max(F.Score) as MaxScore,
M.EventPrefix as Prefix
FROM Events as E
LEFT JOIN Monitors as M on E.MonitorId = M.Id
INNER JOIN Frames as F on E.Id = F.EventId
WHERE isnull(E.Frames) or isnull(E.EndTime)
GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)"
;
my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql )
or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() );
my $updateUnclosedEventsSql =
"UPDATE low_priority Events
SET Name = ?,
EndTime = ?,
Length = ?,
Frames = ?,
AlarmFrames = ?,
TotScore = ?,
AvgScore = ?,
MaxScore = ?,
Notes = concat_ws( ' ', Notes, ? )
WHERE Id = ?"
;
my $updateUnclosedEventsSth = $dbh->prepare_cached( $updateUnclosedEventsSql )
or Fatal( "Can't prepare '$updateUnclosedEventsSql': ".$dbh->errstr() );
$res = $selectUnclosedEventsSth->execute()
or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() );
while( my $event = $selectUnclosedEventsSth->fetchrow_hashref() )
{
aud_print( "Found open event '$event->{Id}'" );
if ( confirm( 'close', 'closing' ) )
{
$res = $updateUnclosedEventsSth->execute
(
sprintf("%s%d%s",
$event->{Prefix},
$event->{Id},
RECOVER_TAG
),
$event->{EndTime},
$event->{Length},
$event->{Frames},
$event->{AlarmFrames},
$event->{TotScore},
$event->{AlarmFrames}
? int($event->{TotScore} / $event->{AlarmFrames})
: 0
,
$event->{MaxScore},
RECOVER_TEXT,
$event->{Id}
) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
}
}
# Now delete any old image files
if ( my @old_files = grep { -M > $max_image_age } <$image_path/*.{jpg,gif,wbmp}> )
{
aud_print( "Deleting ".int(@old_files)." old images\n" );
my $untainted_old_files = join( ";", @old_files );
( $untainted_old_files ) = ( $untainted_old_files =~ /^(.*)$/ );
unlink( split( /;/, $untainted_old_files ) );
}
# Now delete any old swap files
( my $swap_image_root ) = ( $Config{ZM_PATH_SWAP} =~ /^(.*)$/ ); # De-taint
File::Find::find( { wanted=>\&deleteSwapImage, untaint=>1 }, $swap_image_root );
# Prune the Logs table if required
if ( $Config{ZM_LOG_DATABASE_LIMIT} )
{
if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^\d+$/ )
{
# Number of rows
my $selectLogRowCountSql = "SELECT count(*) as Rows from Logs";
my $selectLogRowCountSth = $dbh->prepare_cached( $selectLogRowCountSql )
or Fatal( "Can't prepare '$selectLogRowCountSql': ".$dbh->errstr() );
$res = $selectLogRowCountSth->execute()
or Fatal( "Can't execute: ".$selectLogRowCountSth->errstr() );
my $row = $selectLogRowCountSth->fetchrow_hashref();
my $logRows = $row->{Rows};
if ( $logRows > $Config{ZM_LOG_DATABASE_LIMIT} )
{
my $deleteLogByRowsSql = "DELETE low_priority FROM Logs ORDER BY TimeKey ASC LIMIT ?";
my $deleteLogByRowsSth = $dbh->prepare_cached( $deleteLogByRowsSql )
or Fatal( "Can't prepare '$deleteLogByRowsSql': ".$dbh->errstr() );
$res = $deleteLogByRowsSth->execute( $logRows - $Config{ZM_LOG_DATABASE_LIMIT} )
or Fatal( "Can't execute: ".$deleteLogByRowsSth->errstr() );
if ( $deleteLogByRowsSth->rows() )
{
aud_print( "Deleted ".$deleteLogByRowsSth->rows()
." log table entries by count\n" )
;
}
}
}
else
{
# Time of record
my $deleteLogByTimeSql =
"DELETE low_priority FROM Logs
WHERE TimeKey < unix_timestamp(now() - interval ".$Config{ZM_LOG_DATABASE_LIMIT}.")";
my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql )
or Fatal( "Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr() );
$res = $deleteLogByTimeSth->execute()
or Fatal( "Can't execute: ".$deleteLogByTimeSth->errstr() );
if ( $deleteLogByTimeSth->rows() ){
aud_print( "Deleted ".$deleteLogByTimeSth->rows()
." log table entries by time\n" )
;
$cleaned = 0;
my $deleteMonitorSql = 'delete low_priority from Monitors where Id = ?';
my $deleteMonitorSth = $dbh->prepare_cached( $deleteMonitorSql )
or Fatal( "Can't prepare '$deleteMonitorSql': ".$dbh->errstr() );
my $deleteEventSql = 'delete low_priority from Events where Id = ?';
my $deleteEventSth = $dbh->prepare_cached( $deleteEventSql )
or Fatal( "Can't prepare '$deleteEventSql': ".$dbh->errstr() );
my $deleteFramesSql = 'delete low_priority from Frames where EventId = ?';
my $deleteFramesSth = $dbh->prepare_cached( $deleteFramesSql )
or Fatal( "Can't prepare '$deleteFramesSql': ".$dbh->errstr() );
my $deleteStatsSql = 'delete low_priority from Stats where EventId = ?';
my $deleteStatsSth = $dbh->prepare_cached( $deleteStatsSql )
or Fatal( "Can't prepare '$deleteStatsSql': ".$dbh->errstr() );
while ( my ( $db_monitor, $db_events ) = each(%$db_monitors) ) {
if ( my $fs_events = $fs_monitors->{$db_monitor} ) {
if ( $db_events ) {
while ( my ( $db_event, $age ) = each(%$db_events ) ) {
if ( !defined($fs_events->{$db_event}) ) {
if ( $age > $Config{ZM_AUDIT_MIN_AGE} ) {
aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem" );
if ( confirm() ) {
my $res = $deleteEventSth->execute( $db_event )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$res = $deleteFramesSth->execute( $db_event )
or Fatal( "Can't execute: ".$deleteFramesSth->errstr() );
$res = $deleteStatsSth->execute( $db_event )
or Fatal( "Can't execute: ".$deleteStatsSth->errstr() );
$cleaned = 1;
}
} else {
aud_print( "Database event '$db_monitor/$db_event' does not exist in filesystem but too young to delete." );
}
}
}
} # end if db_events
} else {
aud_print( "Database monitor '$db_monitor' does not exist in filesystem" );
#if ( confirm() )
#{
# We don't actually do this in case it's new
#my $res = $deleteMonitorSth->execute( $db_monitor )
# or Fatal( "Can't execute: ".$deleteMonitorSth->errstr() );
#$cleaned = 1;
#}
}
$loop = $continuous;
} # end foreach db_monitor
redo MAIN if ( $cleaned );
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} ) if $continuous;
# Remove orphaned events (with no monitor)
$cleaned = 0;
my $selectOrphanedEventsSql = 'SELECT Events.Id, Events.Name
FROM Events LEFT JOIN Monitors ON (Events.MonitorId = Monitors.Id)
WHERE isnull(Monitors.Id)';
my $selectOrphanedEventsSth = $dbh->prepare_cached( $selectOrphanedEventsSql )
or Fatal( "Can't prepare '$selectOrphanedEventsSql': ".$dbh->errstr() );
$res = $selectOrphanedEventsSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedEventsSth->errstr() );
while( my $event = $selectOrphanedEventsSth->fetchrow_hashref() ) {
aud_print( "Found orphaned event with no monitor '$event->{Id}'" );
if ( confirm() ) {
$res = $deleteEventSth->execute( $event->{Id} )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
# Remove empty events (with no frames)
$cleaned = 0;
my $selectEmptyEventsSql = "SELECT E.Id AS Id, E.StartTime, F.EventId FROM Events as E LEFT JOIN Frames as F ON (E.Id = F.EventId)
WHERE isnull(F.EventId) AND now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second > E.StartTime";
my $selectEmptyEventsSth = $dbh->prepare_cached( $selectEmptyEventsSql )
or Fatal( "Can't prepare '$selectEmptyEventsSql': ".$dbh->errstr() );
$res = $selectEmptyEventsSth->execute()
or Fatal( "Can't execute: ".$selectEmptyEventsSth->errstr() );
while( my $event = $selectEmptyEventsSth->fetchrow_hashref() ) {
aud_print( "Found empty event with no frame records '$event->{Id}'" );
if ( confirm() ) {
$res = $deleteEventSth->execute( $event->{Id} )
or Fatal( "Can't execute: ".$deleteEventSth->errstr() );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
# Remove orphaned frame records
$cleaned = 0;
my $selectOrphanedFramesSql = 'SELECT DISTINCT EventId FROM Frames
WHERE EventId NOT IN (SELECT Id FROM Events)';
my $selectOrphanedFramesSth = $dbh->prepare_cached( $selectOrphanedFramesSql )
or Fatal( "Can't prepare '$selectOrphanedFramesSql': ".$dbh->errstr() );
$res = $selectOrphanedFramesSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedFramesSth->errstr() );
while( my $frame = $selectOrphanedFramesSth->fetchrow_hashref() ) {
aud_print( "Found orphaned frame records for event '$frame->{EventId}'" );
if ( confirm() ) {
$res = $deleteFramesSth->execute( $frame->{EventId} )
or Fatal( "Can't execute: ".$deleteFramesSth->errstr() );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
# Remove orphaned stats records
$cleaned = 0;
my $selectOrphanedStatsSql = 'SELECT DISTINCT EventId FROM Stats
WHERE EventId NOT IN (SELECT Id FROM Events)';
my $selectOrphanedStatsSth = $dbh->prepare_cached( $selectOrphanedStatsSql )
or Fatal( "Can't prepare '$selectOrphanedStatsSql': ".$dbh->errstr() );
$res = $selectOrphanedStatsSth->execute()
or Fatal( "Can't execute: ".$selectOrphanedStatsSth->errstr() );
while( my $stat = $selectOrphanedStatsSth->fetchrow_hashref() ) {
aud_print( "Found orphaned statistic records for event '$stat->{EventId}'" );
if ( confirm() ) {
$res = $deleteStatsSth->execute( $stat->{EventId} )
or Fatal( "Can't execute: ".$deleteStatsSth->errstr() );
$cleaned = 1;
}
}
redo MAIN if ( $cleaned );
# New audit to close any events that were left open for longer than MIN_AGE seconds
my $selectUnclosedEventsSql =
"SELECT E.Id,
max(F.TimeStamp) as EndTime,
unix_timestamp(max(F.TimeStamp)) - unix_timestamp(E.StartTime) as Length,
max(F.FrameId) as Frames,
count(if(F.Score>0,1,NULL)) as AlarmFrames,
sum(F.Score) as TotScore,
max(F.Score) as MaxScore,
M.EventPrefix as Prefix
FROM Events as E
LEFT JOIN Monitors as M on E.MonitorId = M.Id
INNER JOIN Frames as F on E.Id = F.EventId
WHERE isnull(E.Frames) or isnull(E.EndTime)
GROUP BY E.Id HAVING EndTime < (now() - interval ".$Config{ZM_AUDIT_MIN_AGE}." second)"
;
my $selectUnclosedEventsSth = $dbh->prepare_cached( $selectUnclosedEventsSql )
or Fatal( "Can't prepare '$selectUnclosedEventsSql': ".$dbh->errstr() );
my $updateUnclosedEventsSql =
"UPDATE low_priority Events
SET Name = ?,
EndTime = ?,
Length = ?,
Frames = ?,
AlarmFrames = ?,
TotScore = ?,
AvgScore = ?,
MaxScore = ?,
Notes = concat_ws( ' ', Notes, ? )
WHERE Id = ?"
;
my $updateUnclosedEventsSth = $dbh->prepare_cached( $updateUnclosedEventsSql )
or Fatal( "Can't prepare '$updateUnclosedEventsSql': ".$dbh->errstr() );
$res = $selectUnclosedEventsSth->execute()
or Fatal( "Can't execute: ".$selectUnclosedEventsSth->errstr() );
while( my $event = $selectUnclosedEventsSth->fetchrow_hashref() ) {
aud_print( "Found open event '$event->{Id}'" );
if ( confirm( 'close', 'closing' ) ) {
$res = $updateUnclosedEventsSth->execute(
sprintf('%s%d%s', $event->{Prefix}, $event->{Id}, RECOVER_TAG),
$event->{EndTime},
$event->{Length},
$event->{Frames},
$event->{AlarmFrames},
$event->{TotScore},
$event->{AlarmFrames} ? int($event->{TotScore} / $event->{AlarmFrames}) : 0,
$event->{MaxScore},
RECOVER_TEXT,
$event->{Id}
) or Fatal( "Can't execute: ".$updateUnclosedEventsSth->errstr() );
}
}
# Now delete any old image files
my @old_files = grep { -M > $max_image_age } <$image_path/*.{jpg,gif,wbmp}>;
if ( @old_files ) {
aud_print( 'Deleting '.( scalar @old_files )." old images\n" );
my $untainted_old_files = join( ';', @old_files );
( $untainted_old_files ) = ( $untainted_old_files =~ /^(.*)$/ );
unlink( split( /;/, $untainted_old_files ) );
}
# Now delete any old swap files
( my $swap_image_root ) = ( $Config{ZM_PATH_SWAP} =~ /^(.*)$/ ); # De-taint
File::Find::find( { wanted=>\&deleteSwapImage, untaint=>1 }, $swap_image_root );
# Prune the Logs table if required
if ( $Config{ZM_LOG_DATABASE_LIMIT} ) {
if ( $Config{ZM_LOG_DATABASE_LIMIT} =~ /^\d+$/ ) {
# Number of rows
my $selectLogRowCountSql = 'SELECT count(*) AS Rows FROM Logs';
my $selectLogRowCountSth = $dbh->prepare_cached( $selectLogRowCountSql )
or Fatal( "Can't prepare '$selectLogRowCountSql': ".$dbh->errstr() );
$res = $selectLogRowCountSth->execute()
or Fatal( "Can't execute: ".$selectLogRowCountSth->errstr() );
my $row = $selectLogRowCountSth->fetchrow_hashref();
my $logRows = $row->{Rows};
if ( $logRows > $Config{ZM_LOG_DATABASE_LIMIT} ) {
my $deleteLogByRowsSql = 'DELETE low_priority FROM Logs ORDER BY TimeKey ASC LIMIT ?';
my $deleteLogByRowsSth = $dbh->prepare_cached( $deleteLogByRowsSql )
or Fatal( "Can't prepare '$deleteLogByRowsSql': ".$dbh->errstr() );
$res = $deleteLogByRowsSth->execute( $logRows - $Config{ZM_LOG_DATABASE_LIMIT} )
or Fatal( "Can't execute: ".$deleteLogByRowsSth->errstr() );
if ( $deleteLogByRowsSth->rows() ) {
aud_print( 'Deleted '.$deleteLogByRowsSth->rows() ." log table entries by count\n" );
}
}
} else {
# Time of record
my $deleteLogByTimeSql =
'DELETE low_priority FROM Logs
WHERE TimeKey < unix_timestamp(now() - interval '.$Config{ZM_LOG_DATABASE_LIMIT}.')';
my $deleteLogByTimeSth = $dbh->prepare_cached( $deleteLogByTimeSql )
or Fatal( "Can't prepare '$deleteLogByTimeSql': ".$dbh->errstr() );
$res = $deleteLogByTimeSth->execute()
or Fatal( "Can't execute: ".$deleteLogByTimeSth->errstr() );
if ( $deleteLogByTimeSth->rows() ){
aud_print( "Deleted ".$deleteLogByTimeSth->rows() ." log table entries by time\n" );
}
}
} # end if ( $Config{ZM_LOG_DATABASE_LIMIT} )
$loop = $continuous;
sleep( $Config{ZM_AUDIT_CHECK_INTERVAL} ) if $continuous;
};
exit( 0 );
sub aud_print
{
my $string = shift;
if ( !$continuous )
{
print( $string );
}
else
{
Info( $string );
}
sub aud_print {
my $string = shift;
if ( ! $continuous ) {
print( $string );
} else {
Info( $string );
}
}
sub confirm
{
my $prompt = shift || "delete";
my $action = shift || "deleting";
sub confirm {
my $prompt = shift || 'delete';
my $action = shift || 'deleting';
my $yesno = 0;
if ( $report )
{
print( "\n" );
my $yesno = 0;
if ( $report ) {
print( "\n" );
} elsif ( $interactive ) {
print( ", $prompt y/n: " );
my $char = <>;
chomp( $char );
if ( $char eq 'q' ) {
exit( 0 );
}
elsif ( $interactive )
{
print( ", $prompt y/n: " );
my $char = <>;
chomp( $char );
if ( $char eq 'q' )
{
exit( 0 );
}
if ( !$char )
{
$char = 'y';
}
$yesno = ( $char =~ /[yY]/ );
if ( !$char ) {
$char = 'y';
}
else
{
if ( !$continuous )
{
print( ", $action\n" );
}
else
{
Info( $action );
}
$yesno = 1;
$yesno = ( $char =~ /[yY]/ );
} else {
if ( !$continuous ) {
print( ", $action\n" );
} else {
Info( $action );
}
return( $yesno );
$yesno = 1;
}
return( $yesno );
}
sub deleteSwapImage
{
my $file = $_;
sub deleteSwapImage {
my $file = $_;
if ( $file !~ /^zmswap-/ )
{
return;
}
if ( $file !~ /^zmswap-/ ) {
return;
}
# Ignore directories
if ( -d $file )
{
return;
}
# Ignore directories
if ( -d $file ) {
return;
}
if ( -M $file > $max_swap_age )
{
Debug( "Deleting $file" );
#unlink( $file );
}
if ( -M $file > $max_swap_age ) {
Debug( "Deleting $file" );
#unlink( $file );
}
}
1;
__END__

View File

@ -89,7 +89,6 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
my @daemons = (
'zmc',
'zma',
'zmf',
'zmfilter.pl',
'zmaudit.pl',
'zmtrigger.pl',

File diff suppressed because it is too large Load Diff

View File

@ -251,10 +251,6 @@ if ( $command =~ /^(?:start|restart)$/ )
}
if ( $monitor->{Function} ne 'Monitor' )
{
if ( $Config{ZM_OPT_FRAME_SERVER} )
{
runCommand( "zmdc.pl start zmf -m $monitor->{Id}" );
}
runCommand( "zmdc.pl start zma -m $monitor->{Id}" );
}
if ( $Config{ZM_OPT_CONTROL} )

View File

@ -196,8 +196,10 @@ foreach my $connection ( @in_select_connections )
my %spawned_connections;
my %monitors;
my $monitor_reload_time = 0;
my $needsReload = 0;
loadMonitors();
$! = undef;
my $rin = '';
@ -313,6 +315,14 @@ while( 1 )
my @out_messages;
foreach my $monitor ( values(%monitors) )
{
if ( ! zmMemVerify($monitor) ) {
# Our attempt to verify the memory handle failed. We should reload the monitors.
# Don't need to zmMemInvalidate because the monitor reload will do it.
$needsReload = 1;
next;
}
my ( $state, $last_event )
= zmMemRead( $monitor,
[ "shared_data:state",
@ -413,7 +423,7 @@ while( 1 )
}
# If necessary reload monitors
if ( (time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL )
if ( $needsReload || ((time() - $monitor_reload_time) > MONITOR_RELOAD_INTERVAL ))
{
foreach my $monitor ( values(%monitors) )
{
@ -421,6 +431,7 @@ while( 1 )
zmMemInvalidate( $monitor );
}
loadMonitors();
$needsReload = 0;
}
}
Info( "Trigger daemon exiting\n" );
@ -443,7 +454,11 @@ sub loadMonitors
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() )
{
next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok
# Check shared memory ok
if ( !zmMemVerify( $monitor ) ) {
zmMemInvalidate( $monitor );
next;
}
if ( defined($monitors{$monitor->{Id}}->{LastState}) )
{

File diff suppressed because it is too large Load Diff

View File

@ -87,24 +87,19 @@ while( 1 )
{
next if $monitor->{Function} eq 'None';
my $restart = 0;
# Prevent open handles building up if we have connect to shared memory
# Many of our error checks below do a next without closing the mem handle.
# zmMemInvalidate will just return of nothing is open, so we can just do this here.
zmMemInvalidate( $monitor );
if ( zmMemVerify( $monitor )
&& zmMemRead( $monitor, "shared_data:valid" )
)
{
if ( zmMemVerify( $monitor ) ) {
# Check we have got an image recently
my $image_time = zmGetLastWriteTime( $monitor );
if ( !defined($image_time) ) {
# Can't read from shared data
Debug( "LastWriteTime is not defined." );
zmMemInvalidate( $monitor );
next;
}
if ( !$image_time ) {
# We can't get the last capture time so can't be sure it's died.
Debug( "LastWriteTime is = $image_time." );
zmMemInvalidate( $monitor );
next;
}

View File

@ -474,7 +474,11 @@ sub loadTasks
or Fatal( "Can't execute: ".$sth->errstr() );
while( my $monitor = $sth->fetchrow_hashref() )
{
next if ( !zmMemVerify( $monitor ) ); # Check shared memory ok
# Check shared memory ok
if ( !zmMemVerify( $monitor ) ) {
zmMemInvalidate( $monitor );
next ;
}
$monitor_hash{$monitor->{Id}} = $monitor;

View File

@ -4,7 +4,7 @@
configure_file(zm_config.h.in "${CMAKE_CURRENT_BINARY_DIR}/zm_config.h" @ONLY)
# Group together all the source files that are used by all the binaries (zmc, zma, zmu, zms etc)
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_zone.cpp)
set(ZM_BIN_SRC_FILES zm_box.cpp zm_buffer.cpp zm_camera.cpp zm_comms.cpp zm_config.cpp zm_coord.cpp zm_curl_camera.cpp zm.cpp zm_db.cpp zm_logger.cpp zm_event.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_camera.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_ffmpeg.cpp zm_mpeg.cpp zm_packet.cpp zm_packetqueue.cpp zm_poly.cpp zm_regexp.cpp zm_remote_camera.cpp zm_remote_camera_http.cpp zm_remote_camera_rtsp.cpp zm_rtp.cpp zm_rtp_ctrl.cpp zm_rtp_data.cpp zm_rtp_source.cpp zm_rtsp.cpp zm_rtsp_auth.cpp zm_sdp.cpp zm_signal.cpp zm_stream.cpp zm_thread.cpp zm_time.cpp zm_timer.cpp zm_user.cpp zm_utils.cpp zm_video.cpp zm_videostore.cpp zm_zone.cpp)
# A fix for cmake recompiling the source files for every target.
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
@ -12,21 +12,19 @@ add_library(zm STATIC ${ZM_BIN_SRC_FILES})
add_executable(zmc zmc.cpp)
add_executable(zma zma.cpp)
add_executable(zmu zmu.cpp)
add_executable(zmf zmf.cpp)
add_executable(zms zms.cpp)
target_link_libraries(zmc zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zma zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zmu zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zmf zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
target_link_libraries(zms zm ${ZM_EXTRA_LIBS} ${ZM_BIN_LIBS})
# Generate man files for the binaries destined for the bin folder
FOREACH(CBINARY zma zmc zmf zmu)
FOREACH(CBINARY zma zmc zmu)
POD2MAN(${CMAKE_CURRENT_SOURCE_DIR}/${CBINARY}.cpp zoneminder-${CBINARY} 8)
ENDFOREACH(CBINARY zma zmc zmf zmu)
ENDFOREACH(CBINARY zma zmc zmu)
install(TARGETS zmc zma zmu zmf RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(TARGETS zmc zma zmu RUNTIME DESTINATION "${CMAKE_INSTALL_FULL_BINDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(TARGETS zms RUNTIME DESTINATION "${ZM_CGIDIR}" PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
install(CODE "execute_process(COMMAND ln -sf zms nph-zms WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})" )
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/nph-zms DESTINATION "${ZM_CGIDIR}")

25
src/snprintf.cpp Normal file
View File

@ -0,0 +1,25 @@
snprintf( swap_path, sizeof(swap_path), "%s/zmswap-m%d/zmswap-q%06d", config.path_swap, monitor->Id(), connkey );
int len = snprintf(NULL, 0, "/zmswap-m%d", monitor->Id());
int swap_path_length = strlen(config.path_swap) + snprintf(NULL, 0, "/zmswap-m%d", monitor->Id() ) + snprintf(NULL, 0, "/zmswap-q%06d", connkey ) + 1; // +1 for NULL terminator
if ( connkey && playback_buffer > 0 ) {
if ( swap_path_length + max_swap_len_suffix > PATH_MAX ) {
Error( "Swap Path is too long. %d > %d ", swap_path_length+max_swap_len_suffix, PATH_MAX );
} else {
swap_path = (char *)malloc( swap_path_length+max_swap_len_suffix );
Debug( 3, "Checking swap image path %s", config.path_swap );
strncpy( swap_path, config.path_swap, swap_path_length );
if ( checkSwapPath( swap_path, false ) ) {
snprintf( &(swap_path[swap_path_length]), max_swap_len_suffix, "/zmswap-m%d", monitor->Id() );
if ( checkSwapPath( swap_path, true ) ) {
snprintf( &(swap_path[swap_path_length]), max_swap_len_suffix, "/zmswap-q%06d", connkey );
if ( checkSwapPath( swap_path, true ) ) {
buffered_playback = true;
}
}
}

View File

@ -153,7 +153,7 @@ public:
mHead = mTail = mStorage;
else if ( level )
{
if ( (mHead-mStorage) > mSize )
if ( ((uintptr_t)mHead-(uintptr_t)mStorage) > mSize )
{
memcpy( mStorage, mHead, mSize );
mHead = mStorage;

View File

@ -20,33 +20,43 @@
#include "zm.h"
#include "zm_camera.h"
Camera::Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
id( p_id ),
type( p_type ),
width( p_width),
height( p_height ),
colours( p_colours ),
subpixelorder( p_subpixelorder ),
brightness( p_brightness ),
hue( p_hue ),
colour( p_colour ),
contrast( p_contrast ),
capture( p_capture )
Camera::Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio ) :
monitor_id( p_monitor_id ),
type( p_type ),
width( p_width),
height( p_height ),
colours( p_colours ),
subpixelorder( p_subpixelorder ),
brightness( p_brightness ),
hue( p_hue ),
colour( p_colour ),
contrast( p_contrast ),
capture( p_capture ),
record_audio( p_record_audio )
{
pixels = width * height;
imagesize = pixels * colours;
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",id,width,height,colours,subpixelorder,capture);
Debug(2,"New camera id: %d width: %d height: %d colours: %d subpixelorder: %d capture: %d",monitor_id,width,height,colours,subpixelorder,capture);
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 16) != 0) {
Fatal("Image size is not multiples of 16");
} else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 16) != 0 || (imagesize % 12) != 0)) {
Fatal("Image size is not multiples of 12 and 16");
if((colours == ZM_COLOUR_GRAY8 || colours == ZM_COLOUR_RGB32) && (imagesize % 64) != 0) {
Fatal("Image size is not multiples of 64");
} else if(colours == ZM_COLOUR_RGB24 && ((imagesize % 64) != 0 || (imagesize % 12) != 0)) {
Fatal("Image size is not multiples of 12 and 64");
}
}
Camera::~Camera()
{
Camera::~Camera() {
}
Monitor *Camera::getMonitor() {
if ( ! monitor )
monitor = Monitor::Load( monitor_id, false, Monitor::QUERY );
return monitor;
}
void Camera::setMonitor( Monitor *p_monitor ) {
monitor = p_monitor;
monitor_id = monitor->Id();
}

View File

@ -25,6 +25,10 @@
#include "zm_image.h"
class Camera;
#include "zm_monitor.h"
//
// Abstract base class for cameras. This is intended just to express
// common attributes
@ -34,7 +38,8 @@ class Camera
protected:
typedef enum { LOCAL_SRC, REMOTE_SRC, FILE_SRC, FFMPEG_SRC, LIBVLC_SRC, CURL_SRC } SourceType;
int id;
unsigned int monitor_id;
Monitor * monitor; // Null on instantiation, set as soon as possible.
SourceType type;
unsigned int width;
unsigned int height;
@ -42,17 +47,20 @@ protected:
unsigned int subpixelorder;
unsigned int pixels;
unsigned int imagesize;
int brightness;
int hue;
int colour;
int contrast;
bool capture;
int brightness;
int hue;
int colour;
int contrast;
bool capture;
bool record_audio;
public:
Camera( int p_id, SourceType p_type, int p_width, int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
Camera( unsigned int p_monitor_id, SourceType p_type, unsigned int p_width, unsigned int p_height, int p_colours, int p_subpixelorder, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
virtual ~Camera();
int getId() const { return( id ); }
unsigned int getId() const { return( monitor_id ); }
Monitor *getMonitor();
void setMonitor( Monitor *p_monitor );
SourceType Type() const { return( type ); }
bool IsLocal() const { return( type == LOCAL_SRC ); }
bool IsRemote() const { return( type == REMOTE_SRC ); }
@ -73,11 +81,14 @@ public:
virtual int Contrast( int/*p_contrast*/=-1 ) { return( -1 ); }
bool CanCapture() const { return( capture ); }
bool SupportsNativeVideo() const { return( (type == FFMPEG_SRC )||(type == REMOTE_SRC)); }
virtual int PrimeCapture() { return( 0 ); }
virtual int PreCapture()=0;
virtual int Capture( Image &image )=0;
virtual int PostCapture()=0;
virtual int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) = 0;
};
#endif // ZM_CAMERA_H

View File

@ -27,16 +27,13 @@
#include "zm_utils.h"
void zmLoadConfig()
{
void zmLoadConfig() {
FILE *cfg;
char line[512];
if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL )
{
if ( (cfg = fopen( ZM_CONFIG, "r")) == NULL ) {
Fatal( "Can't open %s: %s", ZM_CONFIG, strerror(errno) );
}
while ( fgets( line, sizeof(line), cfg ) != NULL )
{
while ( fgets( line, sizeof(line), cfg ) != NULL ) {
char *line_ptr = line;
// Trim off any cr/lf line endings
@ -53,16 +50,14 @@ void zmLoadConfig()
// Remove trailing white space
char *temp_ptr = line_ptr+strlen(line_ptr)-1;
while ( *temp_ptr == ' ' || *temp_ptr == '\t' )
{
while ( *temp_ptr == ' ' || *temp_ptr == '\t' ) {
*temp_ptr-- = '\0';
temp_ptr--;
}
// Now look for the '=' in the middle of the line
temp_ptr = strchr( line_ptr, '=' );
if ( !temp_ptr )
{
if ( !temp_ptr ) {
Warning( "Invalid data in %s: '%s'", ZM_CONFIG, line );
continue;
}
@ -72,12 +67,10 @@ void zmLoadConfig()
char *val_ptr = temp_ptr+1;
// Trim trailing space from the name part
do
{
do {
*temp_ptr = '\0';
temp_ptr--;
}
while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
} while ( *temp_ptr == ' ' || *temp_ptr == '\t' );
// Remove leading white space from the value part
white_len = strspn( val_ptr, " \t" );
@ -99,8 +92,7 @@ void zmLoadConfig()
staticConfig.SERVER_NAME = std::string(val_ptr);
else if ( strcasecmp( name_ptr, "ZM_SERVER_ID" ) == 0 )
staticConfig.SERVER_ID = atoi(val_ptr);
else
{
else {
// We ignore this now as there may be more parameters than the
// c/c++ binaries are bothered about
// Warning( "Invalid parameter '%s' in %s", name_ptr, ZM_CONFIG );
@ -143,8 +135,7 @@ void zmLoadConfig()
StaticConfig staticConfig;
ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type )
{
ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *const p_type ) {
name = new char[strlen(p_name)+1];
strcpy( name, p_name );
value = new char[strlen(p_value)+1];
@ -157,50 +148,37 @@ ConfigItem::ConfigItem( const char *p_name, const char *p_value, const char *con
accessed = false;
}
ConfigItem::~ConfigItem()
{
ConfigItem::~ConfigItem() {
delete[] name;
delete[] value;
delete[] type;
}
void ConfigItem::ConvertValue() const
{
if ( !strcmp( type, "boolean" ) )
{
void ConfigItem::ConvertValue() const {
if ( !strcmp( type, "boolean" ) ) {
cfg_type = CFG_BOOLEAN;
cfg_value.boolean_value = (bool)strtol( value, 0, 0 );
}
else if ( !strcmp( type, "integer" ) )
{
} else if ( !strcmp( type, "integer" ) ) {
cfg_type = CFG_INTEGER;
cfg_value.integer_value = strtol( value, 0, 10 );
}
else if ( !strcmp( type, "hexadecimal" ) )
{
} else if ( !strcmp( type, "hexadecimal" ) ) {
cfg_type = CFG_INTEGER;
cfg_value.integer_value = strtol( value, 0, 16 );
}
else if ( !strcmp( type, "decimal" ) )
{
} else if ( !strcmp( type, "decimal" ) ) {
cfg_type = CFG_DECIMAL;
cfg_value.decimal_value = strtod( value, 0 );
}
else
{
} else {
cfg_type = CFG_STRING;
cfg_value.string_value = value;
}
accessed = true;
}
bool ConfigItem::BooleanValue() const
{
bool ConfigItem::BooleanValue() const {
if ( !accessed )
ConvertValue();
if ( cfg_type != CFG_BOOLEAN )
{
if ( cfg_type != CFG_BOOLEAN ) {
Error( "Attempt to fetch boolean value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
exit( -1 );
}
@ -208,13 +186,11 @@ bool ConfigItem::BooleanValue() const
return( cfg_value.boolean_value );
}
int ConfigItem::IntegerValue() const
{
int ConfigItem::IntegerValue() const {
if ( !accessed )
ConvertValue();
if ( cfg_type != CFG_INTEGER )
{
if ( cfg_type != CFG_INTEGER ) {
Error( "Attempt to fetch integer value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
exit( -1 );
}
@ -222,13 +198,11 @@ int ConfigItem::IntegerValue() const
return( cfg_value.integer_value );
}
double ConfigItem::DecimalValue() const
{
double ConfigItem::DecimalValue() const {
if ( !accessed )
ConvertValue();
if ( cfg_type != CFG_DECIMAL )
{
if ( cfg_type != CFG_DECIMAL ) {
Error( "Attempt to fetch decimal value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
exit( -1 );
}
@ -236,13 +210,11 @@ double ConfigItem::DecimalValue() const
return( cfg_value.decimal_value );
}
const char *ConfigItem::StringValue() const
{
const char *ConfigItem::StringValue() const {
if ( !accessed )
ConvertValue();
if ( cfg_type != CFG_STRING )
{
if ( cfg_type != CFG_STRING ) {
Error( "Attempt to fetch string value for %s, actual type is %s. Try running 'zmupdate.pl -f' to reload config.", name, type );
exit( -1 );
}
@ -250,80 +222,66 @@ const char *ConfigItem::StringValue() const
return( cfg_value.string_value );
}
Config::Config()
{
Config::Config() {
n_items = 0;
items = 0;
}
Config::~Config()
{
if ( items )
{
for ( int i = 0; i < n_items; i++ )
{
Config::~Config() {
if ( items ) {
for ( int i = 0; i < n_items; i++ ) {
delete items[i];
}
delete[] items;
}
}
void Config::Load()
{
void Config::Load() {
static char sql[ZM_SQL_SML_BUFSIZ];
strncpy( sql, "select Name, Value, Type from Config order by Id", sizeof(sql) );
if ( mysql_query( &dbconn, sql ) )
{
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
MYSQL_RES *result = mysql_store_result( &dbconn );
if ( !result )
{
if ( !result ) {
Error( "Can't use query result: %s", mysql_error( &dbconn ) );
exit( mysql_errno( &dbconn ) );
}
n_items = mysql_num_rows( result );
if ( n_items <= ZM_MAX_CFG_ID )
{
if ( n_items <= ZM_MAX_CFG_ID ) {
Error( "Config mismatch, expected %d items, read %d. Try running 'zmupdate.pl -f' to reload config.", ZM_MAX_CFG_ID+1, n_items );
exit( -1 );
}
items = new ConfigItem *[n_items];
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ )
{
for( int i = 0; MYSQL_ROW dbrow = mysql_fetch_row( result ); i++ ) {
items[i] = new ConfigItem( dbrow[0], dbrow[1], dbrow[2] );
}
mysql_free_result( result );
}
void Config::Assign()
{
void Config::Assign() {
ZM_CFG_ASSIGN_LIST
}
const ConfigItem &Config::Item( int id )
{
if ( !n_items )
{
const ConfigItem &Config::Item( int id ) {
if ( !n_items ) {
Load();
Assign();
}
if ( id < 0 || id > ZM_MAX_CFG_ID )
{
if ( id < 0 || id > ZM_MAX_CFG_ID ) {
Error( "Attempt to access invalid config, id = %d. Try running 'zmupdate.pl -f' to reload config.", id );
exit( -1 );
}
ConfigItem *item = items[id];
if ( !item )
{
if ( !item ) {
Error( "Can't find config item %d", id );
exit( -1 );
}

View File

@ -18,8 +18,11 @@
//
#include "zm.h"
#include "zm_curl_camera.h"
#include "zm_packetqueue.h"
#if HAVE_LIBCURL
#define CURL_MAXRETRY 5
@ -30,28 +33,24 @@ const char* content_type_match = "Content-Type:";
size_t content_length_match_len;
size_t content_type_match_len;
cURLCamera::cURLCamera( int p_id, const std::string &p_path, const std::string &p_user, const std::string &p_pass, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
Camera( p_id, 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 ),
cURLCamera::cURLCamera( int p_id, 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( p_id, 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 )
{
cURLCamera::~cURLCamera() {
if ( capture ) {
Terminate();
}
}
void cURLCamera::Initialise()
{
void cURLCamera::Initialise() {
content_length_match_len = strlen(content_length_match);
content_type_match_len = strlen(content_type_match);
@ -88,8 +87,7 @@ void cURLCamera::Initialise()
}
}
void cURLCamera::Terminate()
{
void cURLCamera::Terminate() {
/* Signal the thread to terminate */
bTerminate = true;
@ -108,20 +106,17 @@ void cURLCamera::Terminate()
}
int cURLCamera::PrimeCapture()
{
int cURLCamera::PrimeCapture() {
//Info( "Priming capture from %s", mPath.c_str() );
return 0;
}
int cURLCamera::PreCapture()
{
// Nothing to do here
return( 0 );
int cURLCamera::PreCapture() {
// Nothing to do here
return( 0 );
}
int cURLCamera::Capture( Image &image )
{
int cURLCamera::Capture( Image &image ) {
bool frameComplete = false;
/* MODE_STREAM specific variables */
@ -305,14 +300,18 @@ int cURLCamera::Capture( Image &image )
return 0;
}
int cURLCamera::PostCapture()
{
int cURLCamera::PostCapture() {
// Nothing to do here
return( 0 );
}
int cURLCamera::CaptureAndRecord( Image &image, struct timeval recording, char* event_directory ) {
Error("Capture and Record not implemented for the cURL camera type");
// Nothing to do here
return( 0 );
}
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata)
{
size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *userdata) {
lock();
/* Append the data we just received to our buffer */
@ -331,10 +330,7 @@ size_t cURLCamera::data_callback(void *buffer, size_t size, size_t nmemb, void *
return size*nmemb;
}
size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, void *userdata)
{
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);
@ -374,8 +370,7 @@ size_t cURLCamera::header_callback( void *buffer, size_t size, size_t nmemb, voi
return size*nmemb;
}
void* cURLCamera::thread_func()
{
void* cURLCamera::thread_func() {
long tRet;
double dSize;
@ -521,8 +516,7 @@ int cURLCamera::unlock() {
return nRet;
}
int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
{
int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) {
/* Signal the curl thread to terminate */
if(bTerminate)
return -10;
@ -531,18 +525,15 @@ int cURLCamera::progress_callback(void *userdata, double dltotal, double dlnow,
}
/* These functions call the functions in the class for the correct object */
size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
{
size_t data_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata) {
return ((cURLCamera*)userdata)->data_callback(buffer,size,nmemb,userdata);
}
size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata)
{
size_t header_callback_dispatcher(void *buffer, size_t size, size_t nmemb, void *userdata) {
return ((cURLCamera*)userdata)->header_callback(buffer,size,nmemb,userdata);
}
int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow)
{
int progress_callback_dispatcher(void *userdata, double dltotal, double dlnow, double ultotal, double ulnow) {
return ((cURLCamera*)userdata)->progress_callback(userdata,dltotal,dlnow,ultotal,ulnow);
}
@ -550,6 +541,4 @@ void* thread_func_dispatcher(void* object) {
return ((cURLCamera*)object)->thread_func();
}
#endif // HAVE_LIBCURL

View File

@ -39,8 +39,7 @@
// Class representing 'curl' cameras, i.e. those which are
// accessed using the curl library
//
class cURLCamera : public Camera
{
class cURLCamera : public Camera {
protected:
typedef enum {MODE_UNSET, MODE_SINGLE, MODE_STREAM} mode_t;
@ -65,7 +64,7 @@ protected:
pthread_cond_t request_complete_cond;
public:
cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
cURLCamera( int p_id, const std::string &path, const std::string &username, const std::string &password, unsigned int p_width, unsigned int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture, bool p_record_audio );
~cURLCamera();
const std::string &Path() const { return( mPath ); }
@ -79,6 +78,7 @@ public:
int PreCapture();
int Capture( Image &image );
int PostCapture();
int CaptureAndRecord( Image &image, struct timeval recording, char* event_directory );
size_t data_callback(void *buffer, size_t size, size_t nmemb, void *userdata);
size_t header_callback(void *buffer, size_t size, size_t nmemb, void *userdata);

File diff suppressed because it is too large Load Diff

View File

@ -37,6 +37,7 @@
#include "zm.h"
#include "zm_image.h"
#include "zm_stream.h"
#include "zm_video.h"
class Zone;
class Monitor;
@ -46,8 +47,7 @@ class Monitor;
//
// Class describing events, i.e. captured periods of activity.
//
class Event
{
class Event {
friend class EventStream;
protected:
@ -55,6 +55,7 @@ protected:
static char capture_file_format[PATH_MAX];
static char analyse_file_format[PATH_MAX];
static char general_file_format[PATH_MAX];
static char video_file_format[PATH_MAX];
protected:
static int sd;
@ -66,8 +67,7 @@ public:
protected:
typedef enum { NORMAL, BULK, ALARM } FrameType;
struct PreAlarmData
{
struct PreAlarmData {
Image *image;
struct timeval timestamp;
unsigned int score;
@ -84,24 +84,31 @@ protected:
struct timeval end_time;
std::string cause;
StringSetMap noteSetMap;
bool videoEvent;
int frames;
int alarm_frames;
unsigned int tot_score;
unsigned int max_score;
char path[PATH_MAX];
VideoWriter* videowriter;
FILE* timecodes_fd;
char video_name[PATH_MAX];
char video_file[PATH_MAX];
char timecodes_name[PATH_MAX];
char timecodes_file[PATH_MAX];
protected:
int last_db_frame;
protected:
static void Initialise()
{
static void Initialise() {
if ( initialised )
return;
snprintf( capture_file_format, sizeof(capture_file_format), "%%s/%%0%dd-capture.jpg", config.event_image_digits );
snprintf( analyse_file_format, sizeof(analyse_file_format), "%%s/%%0%dd-analyse.jpg", config.event_image_digits );
snprintf( general_file_format, sizeof(general_file_format), "%%s/%%0%dd-%%s", config.event_image_digits );
snprintf( video_file_format, sizeof(video_file_format), "%%s/%%s");
initialised = true;
}
@ -113,7 +120,7 @@ public:
static bool ValidateFrameSocket( int );
public:
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap );
Event( Monitor *p_monitor, struct timeval p_start_time, const std::string &p_cause, const StringSetMap &p_noteSetMap, bool p_videoEvent=false );
~Event();
int Id() const { return( id ); }
@ -127,6 +134,7 @@ public:
bool SendFrameImage( const Image *image, bool alarm_frame=false );
bool WriteFrameImage( Image *image, struct timeval timestamp, const char *event_file, bool alarm_frame=false );
bool WriteFrameVideo( const Image *image, const struct timeval timestamp, VideoWriter* videow );
void updateNotes( const StringSetMap &stringSetMap );
@ -137,28 +145,26 @@ private:
void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps );
public:
static const char *getSubPath( struct tm *time )
{
static const char *getSubPath( struct tm *time ) {
static char subpath[PATH_MAX] = "";
snprintf( subpath, sizeof(subpath), "%02d/%02d/%02d/%02d/%02d/%02d", time->tm_year-100, time->tm_mon+1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec );
return( subpath );
}
static const char *getSubPath( time_t *time )
{
static const char *getSubPath( time_t *time ) {
return( Event::getSubPath( localtime( time ) ) );
}
char* getEventFile(void) {
return video_file;
}
public:
static int PreAlarmCount()
{
static int PreAlarmCount() {
return( pre_alarm_count );
}
static void EmptyPreAlarmFrames()
{
if ( pre_alarm_count > 0 )
{
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ )
{
static void EmptyPreAlarmFrames() {
if ( pre_alarm_count > 0 ) {
for ( int i = 0; i < MAX_PRE_ALARM_FRAMES; i++ ) {
delete pre_alarm_data[i].image;
delete pre_alarm_data[i].alarm_frame;
}
@ -166,29 +172,24 @@ public:
}
pre_alarm_count = 0;
}
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL )
{
static void AddPreAlarmFrame( Image *image, struct timeval timestamp, int score=0, Image *alarm_frame=NULL ) {
pre_alarm_data[pre_alarm_count].image = new Image( *image );
pre_alarm_data[pre_alarm_count].timestamp = timestamp;
pre_alarm_data[pre_alarm_count].score = score;
if ( alarm_frame )
{
if ( alarm_frame ) {
pre_alarm_data[pre_alarm_count].alarm_frame = new Image( *alarm_frame );
}
pre_alarm_count++;
}
void SavePreAlarmFrames()
{
for ( int i = 0; i < pre_alarm_count; i++ )
{
void SavePreAlarmFrames() {
for ( int i = 0; i < pre_alarm_count; i++ ) {
AddFrame( pre_alarm_data[i].image, pre_alarm_data[i].timestamp, pre_alarm_data[i].score, pre_alarm_data[i].alarm_frame );
}
EmptyPreAlarmFrames();
}
};
class EventStream : public StreamBase
{
class EventStream : public StreamBase {
public:
typedef enum { MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode;
@ -201,16 +202,16 @@ protected:
bool in_db;
};
struct EventData
{
struct EventData {
unsigned long event_id;
unsigned long monitor_id;
unsigned long frame_count;
time_t start_time;
double duration;
char path[PATH_MAX];
int n_frames;
FrameData *frames;
time_t start_time;
double duration;
char path[PATH_MAX];
int n_frames;
FrameData *frames;
char video_file[PATH_MAX];
};
protected:
@ -238,8 +239,7 @@ protected:
bool sendFrame( int delta_us );
public:
EventStream()
{
EventStream() {
mode = DEFAULT_MODE;
forceEventChange = false;
@ -249,18 +249,15 @@ public:
event_data = 0;
}
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 )
{
void setStreamStart( int init_event_id, unsigned int init_frame_id=0 ) {
loadInitialEventData( init_event_id, init_frame_id );
loadMonitor( event_data->monitor_id );
}
void setStreamStart( int monitor_id, time_t event_time )
{
void setStreamStart( int monitor_id, time_t event_time ) {
loadInitialEventData( monitor_id, event_time );
loadMonitor( monitor_id );
}
void setStreamMode( StreamMode p_mode )
{
void setStreamMode( StreamMode p_mode ) {
mode = p_mode;
}
void runStream();

View File

@ -23,6 +23,16 @@
#if HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE
void FFMPEGInit() {
static bool bInit = false;
if(!bInit) {
av_register_all();
av_log_set_level(AV_LOG_DEBUG);
bInit = true;
}
}
#if HAVE_LIBAVUTIL
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder) {
enum _AVPIXELFORMAT pf;
@ -31,40 +41,40 @@ enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subp
switch(p_colours) {
case ZM_COLOUR_RGB24:
{
if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
/* BGR subpixel order */
pf = AV_PIX_FMT_BGR24;
} else {
/* Assume RGB subpixel order */
pf = AV_PIX_FMT_RGB24;
}
break;
}
{
if(p_subpixelorder == ZM_SUBPIX_ORDER_BGR) {
/* BGR subpixel order */
pf = AV_PIX_FMT_BGR24;
} else {
/* Assume RGB subpixel order */
pf = AV_PIX_FMT_RGB24;
}
break;
}
case ZM_COLOUR_RGB32:
{
if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
/* ARGB subpixel order */
pf = AV_PIX_FMT_ARGB;
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
/* ABGR subpixel order */
pf = AV_PIX_FMT_ABGR;
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
/* BGRA subpixel order */
pf = AV_PIX_FMT_BGRA;
} else {
/* Assume RGBA subpixel order */
pf = AV_PIX_FMT_RGBA;
}
break;
}
{
if(p_subpixelorder == ZM_SUBPIX_ORDER_ARGB) {
/* ARGB subpixel order */
pf = AV_PIX_FMT_ARGB;
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_ABGR) {
/* ABGR subpixel order */
pf = AV_PIX_FMT_ABGR;
} else if(p_subpixelorder == ZM_SUBPIX_ORDER_BGRA) {
/* BGRA subpixel order */
pf = AV_PIX_FMT_BGRA;
} else {
/* Assume RGBA subpixel order */
pf = AV_PIX_FMT_RGBA;
}
break;
}
case ZM_COLOUR_GRAY8:
pf = AV_PIX_FMT_GRAY8;
break;
pf = AV_PIX_FMT_GRAY8;
break;
default:
Panic("Unexpected colours: %d",p_colours);
pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */
break;
Panic("Unexpected colours: %d",p_colours);
pf = AV_PIX_FMT_GRAY8; /* Just to shush gcc variable may be unused warning */
break;
}
return pf;
@ -148,25 +158,17 @@ SWScale::SWScale() : gotdefaults(false), swscale_ctx(NULL), input_avframe(NULL),
SWScale::~SWScale() {
/* Free up everything */
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
av_frame_free( &input_avframe );
#else
av_freep( &input_avframe );
#endif
//input_avframe = NULL;
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
av_frame_free( &output_avframe );
#else
av_freep( &output_avframe );
#endif
//output_avframe = NULL;
if(swscale_ctx) {
sws_freeContext(swscale_ctx);
swscale_ctx = NULL;
}
Debug(4,"SWScale object destroyed");
}
@ -189,11 +191,11 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
Error("NULL Input or output buffer");
return -1;
}
if(in_pf == 0 || out_pf == 0) {
Error("Invalid input or output pixel formats");
return -2;
}
if(!width || !height) {
// if(in_pf == 0 || out_pf == 0) {
// Error("Invalid input or output pixel formats");
// return -2;
// }
if (!width || !height) {
Error("Invalid width or height");
return -3;
}
@ -223,24 +225,37 @@ int SWScale::Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint
#else
size_t outsize = avpicture_get_size(out_pf, width, height);
#endif
if(outsize < out_buffer_size) {
Error("The output buffer is undersized for the output format. Required: %d Available: %d", outsize, out_buffer_size);
return -5;
}
/* Get the context */
swscale_ctx = sws_getCachedContext( NULL, width, height, in_pf, width, height, out_pf, 0, NULL, NULL, NULL );
swscale_ctx = sws_getCachedContext( swscale_ctx, width, height, in_pf, width, height, out_pf, SWS_FAST_BILINEAR, NULL, NULL, NULL );
if(swscale_ctx == NULL) {
Error("Failed getting swscale context");
return -6;
}
/* Fill in the buffers */
if(!avpicture_fill( (AVPicture*)input_avframe, (uint8_t*)in_buffer, in_pf, width, height ) ) {
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
if (av_image_fill_arrays(input_avframe->data, input_avframe->linesize,
(uint8_t*) in_buffer, in_pf, width, height, 1) <= 0) {
#else
if (avpicture_fill((AVPicture*) input_avframe, (uint8_t*) in_buffer,
in_pf, width, height) <= 0) {
#endif
Error("Failed filling input frame with input buffer");
return -7;
}
if(!avpicture_fill( (AVPicture*)output_avframe, out_buffer, out_pf, width, height ) ) {
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
if (av_image_fill_arrays(output_avframe->data, output_avframe->linesize,
out_buffer, out_pf, width, height, 1) <= 0) {
#else
if (avpicture_fill((AVPicture*) output_avframe, out_buffer, out_pf, width,
height) <= 0) {
#endif
Error("Failed filling output frame with output buffer");
return -8;
}
@ -291,3 +306,206 @@ int SWScale::ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_si
#endif // HAVE_LIBAVCODEC || HAVE_LIBAVUTIL || HAVE_LIBSWSCALE
#if HAVE_LIBAVUTIL
int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb){
int64_t a, b, this_thing;
av_assert0(in_ts != AV_NOPTS_VALUE);
av_assert0(duration >= 0);
if (*last == AV_NOPTS_VALUE || !duration || in_tb.num*(int64_t)out_tb.den <= out_tb.num*(int64_t)in_tb.den) {
simple_round:
*last = av_rescale_q(in_ts, in_tb, fs_tb) + duration;
return av_rescale_q(in_ts, in_tb, out_tb);
}
a = av_rescale_q_rnd(2*in_ts-1, in_tb, fs_tb, AV_ROUND_DOWN) >>1;
b = (av_rescale_q_rnd(2*in_ts+1, in_tb, fs_tb, AV_ROUND_UP )+1)>>1;
if (*last < 2*a - b || *last > 2*b - a)
goto simple_round;
this_thing = av_clip64(*last, a, b);
*last = this_thing + duration;
return av_rescale_q(this_thing, fs_tb, out_tb);
}
#endif
int hacked_up_context2_for_older_ffmpeg(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename) {
AVFormatContext *s = avformat_alloc_context();
int ret = 0;
*avctx = NULL;
if (!s) {
av_log(s, AV_LOG_ERROR, "Out of memory\n");
ret = AVERROR(ENOMEM);
return ret;
}
if (!oformat) {
if (format) {
oformat = av_guess_format(format, NULL, NULL);
if (!oformat) {
av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format);
ret = AVERROR(EINVAL);
}
} else {
oformat = av_guess_format(NULL, filename, NULL);
if (!oformat) {
ret = AVERROR(EINVAL);
av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n", filename);
}
}
}
if (!oformat) {
if (format) {
oformat = av_guess_format(format, NULL, NULL);
if (!oformat) {
av_log(s, AV_LOG_ERROR, "Requested output format '%s' is not a suitable output format\n", format);
ret = AVERROR(EINVAL);
}
} else {
oformat = av_guess_format(NULL, filename, NULL);
if (!oformat) {
ret = AVERROR(EINVAL);
av_log(s, AV_LOG_ERROR, "Unable to find a suitable output format for '%s'\n", filename);
}
}
}
if (ret) {
avformat_free_context(s);
return ret;
} else {
s->oformat = oformat;
if (s->oformat->priv_data_size > 0) {
s->priv_data = av_mallocz(s->oformat->priv_data_size);
if (s->priv_data) {
if (s->oformat->priv_class) {
*(const AVClass**)s->priv_data= s->oformat->priv_class;
av_opt_set_defaults(s->priv_data);
}
} else {
av_log(s, AV_LOG_ERROR, "Out of memory\n");
ret = AVERROR(ENOMEM);
return ret;
}
s->priv_data = NULL;
}
if (filename) strncpy(s->filename, filename, sizeof(s->filename));
*avctx = s;
return 0;
}
}
static void zm_log_fps(double d, const char *postfix) {
uint64_t v = lrintf(d * 100);
if (!v) {
Debug(1, "%1.4f %s", d, postfix);
} else if (v % 100) {
Debug(1, "%3.2f %s", d, postfix);
} else if (v % (100 * 1000)) {
Debug(1, "%1.0f %s", d, postfix);
} else
Debug(1, "%1.0fk %s", d / 1000, postfix);
}
/* "user interface" functions */
void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output) {
char buf[256];
Debug(1, "Dumping stream index i(%d) index(%d)", i, index );
int flags = (is_output ? ic->oformat->flags : ic->iformat->flags);
AVStream *st = ic->streams[i];
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
avcodec_string(buf, sizeof(buf), st->codec, is_output);
Debug(1, " Stream #%d:%d", index, i);
/* the pid is an important information, so we display it */
/* XXX: add a generic system */
if (flags & AVFMT_SHOW_IDS)
Debug(1, "[0x%x]", st->id);
if (lang)
Debug(1, "(%s)", lang->value);
Debug(1, ", %d, %d/%d", st->codec_info_nb_frames, st->time_base.num, st->time_base.den);
Debug(1, ": %s", buf);
if (st->sample_aspect_ratio.num && // default
av_cmp_q(st->sample_aspect_ratio, st->codec->sample_aspect_ratio)) {
AVRational display_aspect_ratio;
av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
st->codec->width * (int64_t)st->sample_aspect_ratio.num,
st->codec->height * (int64_t)st->sample_aspect_ratio.den,
1024 * 1024);
Debug(1, ", SAR %d:%d DAR %d:%d",
st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
display_aspect_ratio.num, display_aspect_ratio.den);
}
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
int fps = st->avg_frame_rate.den && st->avg_frame_rate.num;
int tbn = st->time_base.den && st->time_base.num;
int tbc = st->codec->time_base.den && st->codec->time_base.num;
if (fps || tbn || tbc)
Debug(3, "\n" );
if (fps)
zm_log_fps(av_q2d(st->avg_frame_rate), tbn || tbc ? "fps, " : "fps");
if (tbn)
zm_log_fps(1 / av_q2d(st->time_base), tbc ? "stream tb numerator , " : "stream tb numerator");
if (tbc)
zm_log_fps(1 / av_q2d(st->codec->time_base), "codec time base:");
}
if (st->disposition & AV_DISPOSITION_DEFAULT)
Debug(1, " (default)");
if (st->disposition & AV_DISPOSITION_DUB)
Debug(1, " (dub)");
if (st->disposition & AV_DISPOSITION_ORIGINAL)
Debug(1, " (original)");
if (st->disposition & AV_DISPOSITION_COMMENT)
Debug(1, " (comment)");
if (st->disposition & AV_DISPOSITION_LYRICS)
Debug(1, " (lyrics)");
if (st->disposition & AV_DISPOSITION_KARAOKE)
Debug(1, " (karaoke)");
if (st->disposition & AV_DISPOSITION_FORCED)
Debug(1, " (forced)");
if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED)
Debug(1, " (hearing impaired)");
if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED)
Debug(1, " (visual impaired)");
if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS)
Debug(1, " (clean effects)");
Debug(1, "\n");
//dump_metadata(NULL, st->metadata, " ");
//dump_sidedata(NULL, st, " ");
}
int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt) {
const enum AVSampleFormat *p = codec->sample_fmts;
while (*p != AV_SAMPLE_FMT_NONE) {
if (*p == sample_fmt)
return 1;
else Debug(2, "Not %s", av_get_sample_fmt_name( *p ) );
p++;
}
return 0;
}
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
#else
unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src ) {
dst->data = reinterpret_cast<uint8_t*>(new uint64_t[(src->size + FF_INPUT_BUFFER_PADDING_SIZE)/sizeof(uint64_t) + 1]);
memcpy(dst->data, src->data, src->size );
return 0;
}
#endif

View File

@ -29,6 +29,7 @@ extern "C" {
// AVUTIL
#if HAVE_LIBAVUTIL_AVUTIL_H
#include "libavutil/avassert.h"
#include <libavutil/avutil.h>
#include <libavutil/base64.h>
#include <libavutil/mathematics.h>
@ -40,8 +41,8 @@ extern "C" {
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVUTIL_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#if LIBAVUTIL_VERSION_CHECK(50, 29, 0, 29, 0)
#include <libavutil/opt.h>
@ -58,55 +59,55 @@ extern "C" {
#include <ffmpeg/mathematics.h>
#include <ffmpeg/opt.h>
#endif /* HAVE_LIBAVUTIL_AVUTIL_H */
#if defined(HAVE_LIBAVUTIL_AVUTIL_H)
#if LIBAVUTIL_VERSION_CHECK(51, 42, 0, 74, 100)
#define _AVPIXELFORMAT AVPixelFormat
#define _AVPIXELFORMAT AVPixelFormat
#else
#define _AVPIXELFORMAT PixelFormat
#define AV_PIX_FMT_NONE PIX_FMT_NONE
#define AV_PIX_FMT_RGB444 PIX_FMT_RGB444
#define AV_PIX_FMT_RGB555 PIX_FMT_RGB555
#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
#define AV_PIX_FMT_BGRA PIX_FMT_BGRA
#define AV_PIX_FMT_ARGB PIX_FMT_ARGB
#define AV_PIX_FMT_ABGR PIX_FMT_ABGR
#define AV_PIX_FMT_RGBA PIX_FMT_RGBA
#define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8
#define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422
#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
#define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P
#define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
#define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
#define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
#define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
#define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411
#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
#define AV_PIX_FMT_BGR555 PIX_FMT_BGR555
#define AV_PIX_FMT_BGR8 PIX_FMT_BGR8
#define AV_PIX_FMT_BGR4 PIX_FMT_BGR4
#define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE
#define AV_PIX_FMT_RGB8 PIX_FMT_RGB8
#define AV_PIX_FMT_RGB4 PIX_FMT_RGB4
#define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE
#define AV_PIX_FMT_NV12 PIX_FMT_NV12
#define AV_PIX_FMT_NV21 PIX_FMT_NV21
#define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1
#define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1
#define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE
#define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE
#define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P
#define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
#define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P
//#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264
//#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1
//#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2
#define _AVPIXELFORMAT PixelFormat
#define AV_PIX_FMT_NONE PIX_FMT_NONE
#define AV_PIX_FMT_RGB444 PIX_FMT_RGB444
#define AV_PIX_FMT_RGB555 PIX_FMT_RGB555
#define AV_PIX_FMT_RGB565 PIX_FMT_RGB565
#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
#define AV_PIX_FMT_BGRA PIX_FMT_BGRA
#define AV_PIX_FMT_ARGB PIX_FMT_ARGB
#define AV_PIX_FMT_ABGR PIX_FMT_ABGR
#define AV_PIX_FMT_RGBA PIX_FMT_RGBA
#define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8
#define AV_PIX_FMT_YUYV422 PIX_FMT_YUYV422
#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
#define AV_PIX_FMT_YUV411P PIX_FMT_YUV411P
#define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
#define AV_PIX_FMT_YUV410P PIX_FMT_YUV410P
#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
#define AV_PIX_FMT_YUVJ444P PIX_FMT_YUVJ444P
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
#define AV_PIX_FMT_YUVJ422P PIX_FMT_YUVJ422P
#define AV_PIX_FMT_UYVY422 PIX_FMT_UYVY422
#define AV_PIX_FMT_UYYVYY411 PIX_FMT_UYYVYY411
#define AV_PIX_FMT_BGR565 PIX_FMT_BGR565
#define AV_PIX_FMT_BGR555 PIX_FMT_BGR555
#define AV_PIX_FMT_BGR8 PIX_FMT_BGR8
#define AV_PIX_FMT_BGR4 PIX_FMT_BGR4
#define AV_PIX_FMT_BGR4_BYTE PIX_FMT_BGR4_BYTE
#define AV_PIX_FMT_RGB8 PIX_FMT_RGB8
#define AV_PIX_FMT_RGB4 PIX_FMT_RGB4
#define AV_PIX_FMT_RGB4_BYTE PIX_FMT_RGB4_BYTE
#define AV_PIX_FMT_NV12 PIX_FMT_NV12
#define AV_PIX_FMT_NV21 PIX_FMT_NV21
#define AV_PIX_FMT_RGB32_1 PIX_FMT_RGB32_1
#define AV_PIX_FMT_BGR32_1 PIX_FMT_BGR32_1
#define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE
#define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE
#define AV_PIX_FMT_YUV440P PIX_FMT_YUV440P
#define AV_PIX_FMT_YUVJ440P PIX_FMT_YUVJ440P
#define AV_PIX_FMT_YUVA420P PIX_FMT_YUVA420P
//#define AV_PIX_FMT_VDPAU_H264 PIX_FMT_VDPAU_H264
//#define AV_PIX_FMT_VDPAU_MPEG1 PIX_FMT_VDPAU_MPEG1
//#define AV_PIX_FMT_VDPAU_MPEG2 PIX_FMT_VDPAU_MPEG2
#endif
#endif /* HAVE_LIBAVUTIL_AVUTIL_H */
@ -121,8 +122,8 @@ extern "C" {
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVCODEC_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_AVCODEC_H
#include <ffmpeg/avcodec.h>
@ -130,9 +131,9 @@ extern "C" {
#if defined(HAVE_LIBAVCODEC_AVCODEC_H)
#if LIBAVCODEC_VERSION_CHECK(54, 25, 0, 51, 100)
#define _AVCODECID AVCodecID
#define _AVCODECID AVCodecID
#else
#define _AVCODECID CodecID
#define _AVCODECID CodecID
#endif
#endif /* HAVE_LIBAVCODEC_AVCODEC_H */
@ -146,8 +147,8 @@ extern "C" {
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVFORMAT_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_AVFORMAT_H
#include <ffmpeg/avformat.h>
@ -162,8 +163,8 @@ extern "C" {
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBAVDEVICE_VERSION_CHECK(a, b, c, d, e) \
( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
( (LIBAVDEVICE_VERSION_MICRO < 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBAVDEVICE_VERSION_MICRO >= 100 && LIBAVDEVICE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_AVDEVICE_H
#include <ffmpeg/avdevice.h>
@ -178,8 +179,8 @@ extern "C" {
* b and c the minor and micro versions of libav
* d and e the minor and micro versions of FFmpeg */
#define LIBSWSCALE_VERSION_CHECK(a, b, c, d, e) \
( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
( (LIBSWSCALE_VERSION_MICRO < 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, b, c) ) || \
(LIBSWSCALE_VERSION_MICRO >= 100 && LIBSWSCALE_VERSION_INT >= AV_VERSION_INT(a, d, e) ) )
#elif HAVE_FFMPEG_SWSCALE_H
#include <ffmpeg/swscale.h>
@ -199,6 +200,9 @@ extern "C" {
#endif
#endif
/* A single function to initialize ffmpeg, to avoid multiple initializations */
void FFMPEGInit();
#if HAVE_LIBAVUTIL
enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subpixelorder);
#endif // HAVE_LIBAVUTIL
@ -208,23 +212,23 @@ enum _AVPIXELFORMAT GetFFMPEGPixelFormat(unsigned int p_colours, unsigned p_subp
#if HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
class SWScale {
public:
SWScale();
~SWScale();
int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size);
int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size);
int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
SWScale();
~SWScale();
int SetDefaults(enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
int ConvertDefaults(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size);
int ConvertDefaults(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size);
int Convert(const Image* img, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
int Convert(const uint8_t* in_buffer, const size_t in_buffer_size, uint8_t* out_buffer, const size_t out_buffer_size, enum _AVPIXELFORMAT in_pf, enum _AVPIXELFORMAT out_pf, unsigned int width, unsigned int height);
protected:
bool gotdefaults;
struct SwsContext* swscale_ctx;
AVFrame* input_avframe;
AVFrame* output_avframe;
enum _AVPIXELFORMAT default_input_pf;
enum _AVPIXELFORMAT default_output_pf;
unsigned int default_width;
unsigned int default_height;
bool gotdefaults;
struct SwsContext* swscale_ctx;
AVFrame* input_avframe;
AVFrame* output_avframe;
enum _AVPIXELFORMAT default_input_pf;
enum _AVPIXELFORMAT default_output_pf;
unsigned int default_width;
unsigned int default_height;
};
#endif // HAVE_LIBSWSCALE && HAVE_LIBAVUTIL
@ -261,19 +265,19 @@ protected:
*/
#ifdef __cplusplus
inline static const std::string av_make_error_string(int errnum)
{
char errbuf[AV_ERROR_MAX_STRING_SIZE];
inline static const std::string av_make_error_string(int errnum)
{
char errbuf[AV_ERROR_MAX_STRING_SIZE];
#if LIBAVUTIL_VERSION_CHECK(50, 13, 0, 13, 0)
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
av_strerror(errnum, errbuf, AV_ERROR_MAX_STRING_SIZE);
#else
snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum);
snprintf(errbuf, AV_ERROR_MAX_STRING_SIZE, "libav error %d", errnum);
#endif
return (std::string)errbuf;
}
return (std::string)errbuf;
}
#undef av_err2str
#define av_err2str(errnum) av_make_error_string(errnum).c_str()
#undef av_err2str
#define av_err2str(errnum) av_make_error_string(errnum).c_str()
/* The following is copied directly from newer ffmpeg */
#if LIBAVUTIL_VERSION_CHECK(52, 7, 0, 17, 100)
@ -288,4 +292,60 @@ protected:
#endif // ( HAVE_LIBAVUTIL_AVUTIL_H || HAVE_LIBAVCODEC_AVCODEC_H || HAVE_LIBAVFORMAT_AVFORMAT_H || HAVE_LIBAVDEVICE_AVDEVICE_H )
#ifndef avformat_alloc_output_context2
int hacked_up_context2_for_older_ffmpeg(AVFormatContext **avctx, AVOutputFormat *oformat, const char *format, const char *filename);
#define avformat_alloc_output_context2(x,y,z,a) hacked_up_context2_for_older_ffmpeg(x,y,z,a)
#endif
#ifndef av_rescale_delta
/**
* Rescale a timestamp while preserving known durations.
*/
int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb);
#endif
#ifndef av_clip64
/**
* Clip a signed 64bit integer value into the amin-amax range.
* @param a value to clip
* @param amin minimum value of the clip range
* @param amax maximum value of the clip range
* @return clipped value
*/
static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax)
{
if (a < amin) return amin;
else if (a > amax) return amax;
else return a;
}
#define av_clip64 av_clip64_c
#endif
void zm_dump_stream_format(AVFormatContext *ic, int i, int index, int is_output);
#if LIBAVCODEC_VERSION_CHECK(56, 8, 0, 60, 100)
#define zm_av_packet_unref( packet ) av_packet_unref( packet )
#define zm_av_packet_ref( dst, src ) av_packet_ref( dst, src )
#else
#define zm_av_packet_unref( packet ) av_free_packet( packet )
unsigned int zm_av_packet_ref( AVPacket *dst, AVPacket *src );
#endif
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
#define zm_avcodec_decode_video( context, rawFrame, frameComplete, packet ) avcodec_decode_video2( context, rawFrame, frameComplete, packet )
#else
#define zm_avcodec_decode_video(context, rawFrame, frameComplete, packet ) avcodec_decode_video( context, rawFrame, frameComplete, packet->data, packet->size)
#endif
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
#define zm_av_frame_alloc() av_frame_alloc()
#else
#define zm_av_frame_alloc() avcodec_alloc_frame()
#endif
#if ! LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
#define av_frame_free( input_avframe ) av_freep( input_avframe )
#endif
int check_sample_fmt(AVCodec *codec, enum AVSampleFormat sample_fmt);
#endif // ZM_FFMPEG_H

View File

@ -23,6 +23,9 @@
#include "zm_ffmpeg_camera.h"
extern "C" {
#include "libavutil/time.h"
}
#ifndef AV_ERROR_MAX_STRING_SIZE
#define AV_ERROR_MAX_STRING_SIZE 64
#endif
@ -33,29 +36,34 @@
#include <pthread.h>
#endif
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture ) :
Camera( p_id, FFMPEG_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 ),
FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::string &p_method, const std::string &p_options, 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 ) :
Camera( p_id, FFMPEG_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 ),
mMethod( p_method ),
mOptions( p_options )
{
if ( capture )
{
if ( capture ) {
Initialise();
}
mFormatContext = NULL;
mVideoStreamId = -1;
mCodecContext = NULL;
mCodec = NULL;
mAudioStreamId = -1;
mVideoCodecContext = NULL;
mAudioCodecContext = NULL;
mVideoCodec = NULL;
mAudioCodec = NULL;
mRawFrame = NULL;
mFrame = NULL;
frameCount = 0;
startTime=0;
mIsOpening = false;
mCanCapture = false;
mOpenStart = 0;
mReopenThread = 0;
videoStore = NULL;
video_last_pts = 0;
#if HAVE_LIBSWSCALE
mConvertContext = NULL;
#endif
@ -72,35 +80,37 @@ FfmpegCamera::FfmpegCamera( int p_id, const std::string &p_path, const std::stri
} else {
Panic("Unexpected colours: %d",colours);
}
}
FfmpegCamera::~FfmpegCamera()
{
FfmpegCamera::~FfmpegCamera() {
if ( videoStore ) {
delete videoStore;
}
CloseFfmpeg();
if ( capture )
{
if ( capture ) {
Terminate();
}
}
void FfmpegCamera::Initialise()
{
void FfmpegCamera::Initialise() {
if ( logDebugging() )
av_log_set_level( AV_LOG_DEBUG );
else
av_log_set_level( AV_LOG_QUIET );
av_register_all();
avformat_network_init();
}
void FfmpegCamera::Terminate()
{
void FfmpegCamera::Terminate() {
}
int FfmpegCamera::PrimeCapture()
{
int FfmpegCamera::PrimeCapture() {
mVideoStreamId = -1;
mAudioStreamId = -1;
Info( "Priming capture from %s", mPath.c_str() );
if (OpenFfmpeg() != 0){
@ -115,109 +125,102 @@ int FfmpegCamera::PreCapture()
return( 0 );
}
int FfmpegCamera::Capture( Image &image )
{
int FfmpegCamera::Capture( Image &image ) {
if (!mCanCapture){
return -1;
}
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
if (mReopenThread != 0) {
void *retval = 0;
int ret;
ret = pthread_join(mReopenThread, &retval);
if (ret != 0){
Error("Could not join reopen thread.");
}
Info( "Successfully reopened stream." );
mReopenThread = 0;
}
AVPacket packet;
uint8_t* directbuffer;
/* Request a writeable buffer of the target image */
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
if(directbuffer == NULL) {
Error("Failed requesting writeable buffer for the captured image.");
return (-1);
}
int frameComplete = false;
while ( !frameComplete )
{
while ( !frameComplete ) {
int avResult = av_read_frame( mFormatContext, &packet );
if ( avResult < 0 )
{
if ( avResult < 0 ) {
char errbuf[AV_ERROR_MAX_STRING_SIZE];
av_strerror(avResult, errbuf, AV_ERROR_MAX_STRING_SIZE);
if (
// Check if EOF.
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
// Check for Connection failure.
(avResult == -110)
)
{
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
// Check if EOF.
(avResult == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
// Check for Connection failure.
(avResult == -110)
) {
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf );
ReopenFfmpeg();
}
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, avResult, errbuf );
return( -1 );
}
Debug( 5, "Got packet from stream %d", packet.stream_index );
if ( packet.stream_index == mVideoStreamId )
{
#if LIBAVCODEC_VERSION_CHECK(52, 23, 0, 23, 0)
if ( avcodec_decode_video2( mCodecContext, mRawFrame, &frameComplete, &packet ) < 0 )
#else
if ( avcodec_decode_video( mCodecContext, mRawFrame, &frameComplete, packet.data, packet.size ) < 0 )
#endif
Debug( 5, "Got packet from stream %d dts (%d) pts(%d)", packet.stream_index, packet.pts, packet.dts );
// What about audio stream? Maybe someday we could do sound detection...
if ( packet.stream_index == mVideoStreamId ) {
int ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
if ( ret < 0 )
Fatal( "Unable to decode frame at frame %d", frameCount );
Debug( 4, "Decoded video packet at frame %d", frameCount );
if ( frameComplete )
{
Debug( 3, "Got frame %d", frameCount );
if ( frameComplete ) {
Debug( 4, "Got frame %d", frameCount );
uint8_t* directbuffer;
/* Request a writeable buffer of the target image */
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
if(directbuffer == NULL) {
Error("Failed requesting writeable buffer for the captured image.");
return (-1);
}
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(mFrame->data, mFrame->linesize,
directbuffer, imagePixFormat, width, height, 1);
directbuffer, imagePixFormat, width, height, 1);
#else
avpicture_fill( (AVPicture *)mFrame, directbuffer,
imagePixFormat, width, height);
imagePixFormat, width, height);
#endif
#if HAVE_LIBSWSCALE
if(mConvertContext == NULL) {
mConvertContext = sws_getContext( mCodecContext->width, mCodecContext->height, mCodecContext->pix_fmt, width, height, imagePixFormat, SWS_BICUBIC, NULL, NULL, NULL );
if(mConvertContext == NULL) {
mConvertContext = sws_getContext(mVideoCodecContext->width,
mVideoCodecContext->height,
mVideoCodecContext->pix_fmt,
width, height, imagePixFormat,
SWS_BICUBIC, NULL, NULL, NULL);
if(mConvertContext == NULL)
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
}
if ( sws_scale( mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mCodecContext->height, mFrame->data, mFrame->linesize ) < 0 )
Fatal( "Unable to convert raw format %u to target format %u at frame %d", mCodecContext->pix_fmt, imagePixFormat, frameCount );
if(mConvertContext == NULL)
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
}
if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize, 0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0)
Fatal("Unable to convert raw format %u to target format %u at frame %d", mVideoCodecContext->pix_fmt, imagePixFormat, frameCount);
#else // HAVE_LIBSWSCALE
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
#endif // HAVE_LIBSWSCALE
frameCount++;
}
}
#if LIBAVCODEC_VERSION_CHECK(57, 8, 0, 12, 100)
av_packet_unref( &packet);
#else
av_free_packet( &packet );
#endif
}
return (0);
}
int FfmpegCamera::PostCapture()
{
frameCount++;
} // end if frameComplete
} else {
Debug( 4, "Different stream_index %d", packet.stream_index );
} // end if packet.stream_index == mVideoStreamId
zm_av_packet_unref( &packet );
} // end while ! frameComplete
return (0);
} // FfmpegCamera::Capture
int FfmpegCamera::PostCapture() {
// Nothing to do here
return( 0 );
}
@ -278,70 +281,111 @@ int FfmpegCamera::OpenFfmpeg() {
mIsOpening = false;
Debug ( 1, "Opened input" );
Info( "Stream open %s", mPath.c_str() );
//FIXME can speed up initial analysis but need sensible parameters...
//mFormatContext->probesize = 32;
//mFormatContext->max_analyze_duration = 32;
// Locate stream info from avformat_open_input
#if !LIBAVFORMAT_VERSION_CHECK(53, 6, 0, 6, 0)
Debug ( 1, "Calling av_find_stream_info" );
if ( av_find_stream_info( mFormatContext ) < 0 )
#else
Debug ( 1, "Calling avformat_find_stream_info" );
Debug ( 1, "Calling avformat_find_stream_info" );
if ( avformat_find_stream_info( mFormatContext, 0 ) < 0 )
#endif
Fatal( "Unable to find stream info from %s due to: %s", mPath.c_str(), strerror(errno) );
startTime = av_gettime();//FIXME here or after find_Stream_info
Debug ( 1, "Got stream info" );
// Find first video stream present
// The one we want Might not be the first
mVideoStreamId = -1;
for (unsigned int i=0; i < mFormatContext->nb_streams; i++ )
{
mAudioStreamId = -1;
for (unsigned int i=0; i < mFormatContext->nb_streams; i++ ) {
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO )
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) {
#else
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO )
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO ) {
#endif
{
mVideoStreamId = i;
break;
if ( mVideoStreamId == -1 ) {
mVideoStreamId = i;
// if we break, then we won't find the audio stream
continue;
} else {
Debug(2, "Have another video stream." );
}
}
}
#if (LIBAVCODEC_VERSION_CHECK(52, 64, 0, 64, 0) || LIBAVUTIL_VERSION_CHECK(50, 14, 0, 14, 0))
if ( mFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) {
#else
if ( mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO ) {
#endif
if ( mAudioStreamId == -1 ) {
mAudioStreamId = i;
} else {
Debug(2, "Have another audio stream." );
}
}
} // end foreach stream
if ( mVideoStreamId == -1 )
Fatal( "Unable to locate video stream in %s", mPath.c_str() );
if ( mAudioStreamId == -1 )
Debug( 3, "Unable to locate audio stream in %s", mPath.c_str() );
Debug ( 1, "Found video stream" );
Debug ( 3, "Found video stream at index %d", mVideoStreamId );
Debug ( 3, "Found audio stream at index %d", mAudioStreamId );
mCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
mVideoCodecContext = mFormatContext->streams[mVideoStreamId]->codec;
// STolen from ispy
//this fixes issues with rtsp streams!! woot.
//mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG2_CHUNKS | CODEC_FLAG_LOW_DELAY; // Enable faster H264 decode.
mVideoCodecContext->flags2 |= CODEC_FLAG2_FAST | CODEC_FLAG_LOW_DELAY;
// Try and get the codec from the codec context
if ( (mCodec = avcodec_find_decoder( mCodecContext->codec_id )) == NULL )
Fatal( "Can't find codec for video stream from %s", mPath.c_str() );
Debug ( 1, "Found decoder" );
if ((mVideoCodec = avcodec_find_decoder(mVideoCodecContext->codec_id)) == NULL) {
Fatal("Can't find codec for video stream from %s", mPath.c_str());
} else {
Debug(1, "Video Found decoder");
zm_dump_stream_format(mFormatContext, mVideoStreamId, 0, 0);
// Open the codec
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
Debug ( 1, "Calling avcodec_open" );
if ( avcodec_open( mCodecContext, mCodec ) < 0 )
if (avcodec_open(mVideoCodecContext, mVideoCodec) < 0)
#else
Debug ( 1, "Calling avcodec_open2" );
if ( avcodec_open2( mCodecContext, mCodec, 0 ) < 0 )
Debug ( 1, "Calling avcodec_open2" );
if (avcodec_open2(mVideoCodecContext, mVideoCodec, 0) < 0)
#endif
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() );
}
if ( mAudioStreamId >= 0 ) {
mAudioCodecContext = mFormatContext->streams[mAudioStreamId]->codec;
if ((mAudioCodec = avcodec_find_decoder(mAudioCodecContext->codec_id)) == NULL) {
Debug(1, "Can't find codec for audio stream from %s", mPath.c_str());
} else {
Debug(1, "Audio Found decoder");
zm_dump_stream_format(mFormatContext, mAudioStreamId, 0, 0);
// Open the codec
#if !LIBAVFORMAT_VERSION_CHECK(53, 8, 0, 8, 0)
Debug ( 1, "Calling avcodec_open" );
if (avcodec_open(mAudioCodecContext, mAudioCodec) < 0)
#else
Debug ( 1, "Calling avcodec_open2" );
if (avcodec_open2(mAudioCodecContext, mAudioCodec, 0) < 0)
#endif
Fatal( "Unable to open codec for video stream from %s", mPath.c_str() );
}
}
Debug ( 1, "Opened codec" );
// Allocate space for the native video frame
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
mRawFrame = av_frame_alloc();
#else
mRawFrame = avcodec_alloc_frame();
#endif
mRawFrame = zm_av_frame_alloc();
// Allocate space for the converted video frame
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
mFrame = av_frame_alloc();
#else
mFrame = avcodec_alloc_frame();
#endif
mFrame = zm_av_frame_alloc();
if(mRawFrame == NULL || mFrame == NULL)
Fatal( "Unable to allocate frame for %s", mPath.c_str() );
@ -359,25 +403,37 @@ int FfmpegCamera::OpenFfmpeg() {
}
Debug ( 1, "Validated imagesize" );
#if HAVE_LIBSWSCALE
Debug ( 1, "Calling sws_isSupportedInput" );
if(!sws_isSupportedInput(mCodecContext->pix_fmt)) {
Fatal("swscale does not support the codec format: %c%c%c%c",(mCodecContext->pix_fmt)&0xff,((mCodecContext->pix_fmt>>8)&0xff),((mCodecContext->pix_fmt>>16)&0xff),((mCodecContext->pix_fmt>>24)&0xff));
if (!sws_isSupportedInput(mVideoCodecContext->pix_fmt)) {
Fatal("swscale does not support the codec format: %c%c%c%c", (mVideoCodecContext->pix_fmt)&0xff, ((mVideoCodecContext->pix_fmt >> 8)&0xff), ((mVideoCodecContext->pix_fmt >> 16)&0xff), ((mVideoCodecContext->pix_fmt >> 24)&0xff));
}
if(!sws_isSupportedOutput(imagePixFormat)) {
Fatal("swscale does not support the target format: %c%c%c%c",(imagePixFormat)&0xff,((imagePixFormat>>8)&0xff),((imagePixFormat>>16)&0xff),((imagePixFormat>>24)&0xff));
}
mConvertContext = sws_getContext(mVideoCodecContext->width,
mVideoCodecContext->height,
mVideoCodecContext->pix_fmt,
width, height,
imagePixFormat, SWS_BICUBIC, NULL,
NULL, NULL);
if ( mConvertContext == NULL )
Fatal( "Unable to create conversion context for %s", mPath.c_str() );
#else // HAVE_LIBSWSCALE
Fatal( "You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras" );
#endif // HAVE_LIBSWSCALE
if ( (unsigned int)mVideoCodecContext->width != width || (unsigned int)mVideoCodecContext->height != height ) {
Warning( "Monitor dimensions are %dx%d but camera is sending %dx%d", width, height, mVideoCodecContext->width, mVideoCodecContext->height );
}
mCanCapture = true;
return 0;
}
} // int FfmpegCamera::OpenFfmpeg()
int FfmpegCamera::ReopenFfmpeg() {
@ -398,29 +454,26 @@ int FfmpegCamera::CloseFfmpeg(){
mCanCapture = false;
#if LIBAVCODEC_VERSION_CHECK(55, 28, 1, 45, 101)
av_frame_free( &mFrame );
av_frame_free( &mRawFrame );
#else
av_freep( &mFrame );
av_freep( &mRawFrame );
#endif
#if HAVE_LIBSWSCALE
if ( mConvertContext )
{
if ( mConvertContext ) {
sws_freeContext( mConvertContext );
mConvertContext = NULL;
}
#endif
if ( mCodecContext )
{
avcodec_close( mCodecContext );
mCodecContext = NULL; // Freed by av_close_input_file
if (mVideoCodecContext) {
avcodec_close(mVideoCodecContext);
mVideoCodecContext = NULL; // Freed by av_close_input_file
}
if ( mFormatContext )
{
if (mAudioCodecContext) {
avcodec_close(mAudioCodecContext);
mAudioCodecContext = NULL; // Freed by av_close_input_file
}
if ( mFormatContext ) {
#if !LIBAVFORMAT_VERSION_CHECK(53, 17, 0, 25, 0)
av_close_input_file( mFormatContext );
#else
@ -432,8 +485,7 @@ int FfmpegCamera::CloseFfmpeg(){
return 0;
}
int FfmpegCamera::FfmpegInterruptCallback(void *ctx)
{
int FfmpegCamera::FfmpegInterruptCallback(void *ctx) {
FfmpegCamera* camera = reinterpret_cast<FfmpegCamera*>(ctx);
if (camera->mIsOpening){
int now = time(NULL);
@ -469,4 +521,282 @@ void *FfmpegCamera::ReopenFfmpegThreadCallback(void *ctx){
}
}
//Function to handle capture and store
int FfmpegCamera::CaptureAndRecord( Image &image, timeval recording, char* event_file ) {
if ( ! mCanCapture ) {
return -1;
}
int ret;
static char errbuf[AV_ERROR_MAX_STRING_SIZE];
// If the reopen thread has a value, but mCanCapture != 0, then we have just reopened the connection to the ffmpeg device, and we can clean up the thread.
if ( mReopenThread != 0 ) {
void *retval = 0;
ret = pthread_join(mReopenThread, &retval);
if (ret != 0){
Error("Could not join reopen thread.");
}
Info( "Successfully reopened stream." );
mReopenThread = 0;
}
if ( mVideoCodecContext->codec_id != AV_CODEC_ID_H264 ) {
Error( "Input stream is not h264. The stored event file may not be viewable in browser." );
}
int frameComplete = false;
while ( ! frameComplete ) {
av_init_packet( &packet );
ret = av_read_frame( mFormatContext, &packet );
if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
if (
// Check if EOF.
(ret == AVERROR_EOF || (mFormatContext->pb && mFormatContext->pb->eof_reached)) ||
// Check for Connection failure.
(ret == -110)
) {
Info( "av_read_frame returned \"%s\". Reopening stream.", errbuf);
ReopenFfmpeg();
}
Error( "Unable to read packet from stream %d: error %d \"%s\".", packet.stream_index, ret, errbuf );
return( -1 );
}
int key_frame = packet.flags & AV_PKT_FLAG_KEY;
Debug( 4, "Got packet from stream %d packet pts (%d) dts(%d), key?(%d)",
packet.stream_index, packet.pts, packet.dts,
key_frame
);
//Video recording
if ( recording.tv_sec ) {
// The directory we are recording to is no longer tied to the current event.
// Need to re-init the videostore with the correct directory and start recording again
// for efficiency's sake, we should test for keyframe before we test for directory change...
if ( videoStore && key_frame && (strcmp(oldDirectory, event_file) != 0 ) ) {
// don't open new videostore until we're on a key frame..would this require an offset adjustment for the event as a result?...
// if we store our key frame location with the event will that be enough?
Info("Re-starting video storage module");
// I don't know if this is important or not... but I figure we might as well write this last packet out to the store before closing it.
// Also don't know how much it matters for audio.
if ( packet.stream_index == mVideoStreamId ) {
//Write the packet to our video store
int ret = videoStore->writeVideoFramePacket( &packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
Warning("Error writing last packet to videostore.");
}
} // end if video
delete videoStore;
videoStore = NULL;
} // end if end of recording
if ( ( ! videoStore ) && key_frame && ( packet.stream_index == mVideoStreamId ) ) {
//Instantiate the video storage module
if (record_audio) {
if (mAudioStreamId == -1) {
Debug(3, "Record Audio on but no audio stream found");
videoStore = new VideoStore((const char *) event_file, "mp4",
mFormatContext->streams[mVideoStreamId],
NULL,
startTime,
this->getMonitor());
} else {
Debug(3, "Video module initiated with audio stream");
videoStore = new VideoStore((const char *) event_file, "mp4",
mFormatContext->streams[mVideoStreamId],
mFormatContext->streams[mAudioStreamId],
startTime,
this->getMonitor());
}
} else {
Debug(3, "Record_audio is false so exclude audio stream");
videoStore = new VideoStore((const char *) event_file, "mp4",
mFormatContext->streams[mVideoStreamId],
NULL,
startTime,
this->getMonitor());
} // end if record_audio
strcpy(oldDirectory, event_file);
// Need to write out all the frames from the last keyframe?
// No... need to write out all frames from when the event began. Due to PreEventFrames, this could be more than since the last keyframe.
unsigned int packet_count = 0;
ZMPacket *queued_packet;
// Clear all packets that predate the moment when the recording began
packetqueue.clear_unwanted_packets( &recording, mVideoStreamId );
while ( ( queued_packet = packetqueue.popPacket() ) ) {
AVPacket *avp = queued_packet->av_packet();
packet_count += 1;
//Write the packet to our video store
Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", avp->stream_index, avp->flags & AV_PKT_FLAG_KEY, packetqueue.size() );
if ( avp->stream_index == mVideoStreamId ) {
ret = videoStore->writeVideoFramePacket( avp );
} else if ( avp->stream_index == mAudioStreamId ) {
ret = videoStore->writeAudioFramePacket( avp );
} else {
Warning("Unknown stream id in queued packet (%d)", avp->stream_index );
ret = -1;
}
if ( ret < 0 ) {
//Less than zero and we skipped a frame
}
delete queued_packet;
} // end while packets in the packetqueue
Debug(2, "Wrote %d queued packets", packet_count );
} // end if ! was recording
} else {
// Not recording
if ( videoStore ) {
Info("Deleting videoStore instance");
delete videoStore;
videoStore = NULL;
}
// Buffer video packets, since we are not recording.
// All audio packets are keyframes, so only if it's a video keyframe
if ( packet.stream_index == mVideoStreamId ) {
if ( key_frame ) {
Debug(3, "Clearing queue");
packetqueue.clearQueue( monitor->GetPreEventCount(), mVideoStreamId );
}
#if 0
// Not sure this is valid. While a camera will PROBABLY always have an increasing pts... it doesn't have to.
// Also, I think there are integer wrap-around issues.
else if ( packet.pts && video_last_pts > packet.pts ) {
Warning( "Clearing queue due to out of order pts packet.pts(%d) < video_last_pts(%d)");
packetqueue.clearQueue();
}
#endif
}
// The following lines should ensure that the queue always begins with a video keyframe
if ( packet.stream_index == mAudioStreamId ) {
//Debug(2, "Have audio packet, reocrd_audio is (%d) and packetqueue.size is (%d)", record_audio, packetqueue.size() );
if ( record_audio && packetqueue.size() ) {
// if it's audio, and we are doing audio, and there is already something in the queue
packetqueue.queuePacket( &packet );
}
} else if ( packet.stream_index == mVideoStreamId ) {
if ( key_frame || packetqueue.size() ) // it's a keyframe or we already have something in the queue
packetqueue.queuePacket( &packet );
}
} // end if recording or not
if ( packet.stream_index == mVideoStreamId ) {
if ( videoStore ) {
//Write the packet to our video store
int ret = videoStore->writeVideoFramePacket( &packet );
if ( ret < 0 ) { //Less than zero and we skipped a frame
zm_av_packet_unref( &packet );
return 0;
}
}
Debug(4, "about to decode video" );
#if LIBAVCODEC_VERSION_CHECK(58, 0, 0, 0, 0)
ret = avcodec_send_packet( mVideoCodecContext, &packet );
if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
zm_av_packet_unref( &packet );
continue;
}
ret = avcodec_receive_frame( mVideoCodecContext, mRawFrame );
if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
Error( "Unable to send packet at frame %d: %s, continuing", frameCount, errbuf );
zm_av_packet_unref( &packet );
continue;
}
frameComplete = 1;
# else
ret = zm_avcodec_decode_video( mVideoCodecContext, mRawFrame, &frameComplete, &packet );
if ( ret < 0 ) {
av_strerror( ret, errbuf, AV_ERROR_MAX_STRING_SIZE );
Error( "Unable to decode frame at frame %d: %s, continuing", frameCount, errbuf );
zm_av_packet_unref( &packet );
continue;
}
#endif
Debug( 4, "Decoded video packet at frame %d", frameCount );
if ( frameComplete ) {
Debug( 4, "Got frame %d", frameCount );
uint8_t* directbuffer;
/* Request a writeable buffer of the target image */
directbuffer = image.WriteBuffer(width, height, colours, subpixelorder);
if ( directbuffer == NULL ) {
Error("Failed requesting writeable buffer for the captured image.");
zm_av_packet_unref( &packet );
return (-1);
}
#if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
av_image_fill_arrays(mFrame->data, mFrame->linesize, directbuffer, imagePixFormat, width, height, 1);
#else
avpicture_fill( (AVPicture *)mFrame, directbuffer, imagePixFormat, width, height);
#endif
if (sws_scale(mConvertContext, mRawFrame->data, mRawFrame->linesize,
0, mVideoCodecContext->height, mFrame->data, mFrame->linesize) < 0) {
Fatal("Unable to convert raw format %u to target format %u at frame %d",
mVideoCodecContext->pix_fmt, imagePixFormat, frameCount);
}
frameCount++;
} else {
Debug( 3, "Not framecomplete after av_read_frame" );
} // end if frameComplete
} else if ( packet.stream_index == mAudioStreamId ) { //FIXME best way to copy all other streams
if ( videoStore ) {
if ( record_audio ) {
Debug(3, "Recording audio packet streamindex(%d) packetstreamindex(%d)", mAudioStreamId, packet.stream_index );
//Write the packet to our video store
//FIXME no relevance of last key frame
int ret = videoStore->writeAudioFramePacket( &packet );
if ( ret < 0 ) {//Less than zero and we skipped a frame
Warning("Failure to write audio packet.");
zm_av_packet_unref( &packet );
return 0;
}
} else {
Debug(4, "Not doing recording of audio packet" );
}
} else {
Debug(4, "Have audio packet, but not recording atm" );
}
} else {
#if LIBAVUTIL_VERSION_CHECK(56, 23, 0, 23, 0)
Debug( 3, "Some other stream index %d, %s", packet.stream_index, av_get_media_type_string( mFormatContext->streams[packet.stream_index]->codecpar->codec_type) );
#else
Debug( 3, "Some other stream index %d", packet.stream_index );
#endif
}
//if ( videoStore ) {
// the packet contents are ref counted... when queuing, we allocate another packet and reference it with that one, so we should always need to unref here, which should not affect the queued version.
zm_av_packet_unref( &packet );
//}
} // end while ! frameComplete
return (frameCount);
} // end FfmpegCamera::CaptureAndRecord
#endif // HAVE_LIBAVFORMAT

View File

@ -25,59 +25,82 @@
#include "zm_buffer.h"
//#include "zm_utils.h"
#include "zm_ffmpeg.h"
#include "zm_videostore.h"
#include "zm_packetqueue.h"
//
// Class representing 'ffmpeg' cameras, i.e. those which are
// accessed using ffmpeg multimedia framework
//
class FfmpegCamera : public Camera
{
protected:
std::string mPath;
std::string mMethod;
std::string mOptions;
class FfmpegCamera : public Camera {
protected:
std::string mPath;
std::string mMethod;
std::string mOptions;
int frameCount;
int frameCount;
#if HAVE_LIBAVFORMAT
AVFormatContext *mFormatContext;
int mVideoStreamId;
AVCodecContext *mCodecContext;
AVCodec *mCodec;
AVFrame *mRawFrame;
AVFrame *mFrame;
_AVPIXELFORMAT imagePixFormat;
AVFormatContext *mFormatContext;
int mVideoStreamId;
int mAudioStreamId;
AVCodecContext *mVideoCodecContext;
AVCodecContext *mAudioCodecContext;
AVCodec *mVideoCodec;
AVCodec *mAudioCodec;
AVFrame *mRawFrame;
AVFrame *mFrame;
_AVPIXELFORMAT imagePixFormat;
int OpenFfmpeg();
int ReopenFfmpeg();
int CloseFfmpeg();
static int FfmpegInterruptCallback(void *ctx);
static void* ReopenFfmpegThreadCallback(void *ctx);
bool mIsOpening;
bool mCanCapture;
int mOpenStart;
pthread_t mReopenThread;
// Need to keep track of these because apparently the stream can start with values for pts/dts and then subsequent packets start at zero.
int64_t audio_last_pts;
int64_t audio_last_dts;
int64_t video_last_pts;
int64_t video_last_dts;
// Used to store the incoming packet, it will get copied when queued.
// We only ever need one at a time, so instead of constantly allocating
// and freeing this structure, we will just make it a member of the object.
AVPacket packet;
int OpenFfmpeg();
int ReopenFfmpeg();
int CloseFfmpeg();
static int FfmpegInterruptCallback(void *ctx);
static void* ReopenFfmpegThreadCallback(void *ctx);
bool mIsOpening;
bool mCanCapture;
int mOpenStart;
pthread_t mReopenThread;
#endif // HAVE_LIBAVFORMAT
VideoStore *videoStore;
char oldDirectory[4096];
unsigned int old_event_id;
zm_packetqueue packetqueue;
#if HAVE_LIBSWSCALE
struct SwsContext *mConvertContext;
struct SwsContext *mConvertContext;
#endif
public:
FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, int p_width, int p_height, int p_colours, int p_brightness, int p_contrast, int p_hue, int p_colour, bool p_capture );
~FfmpegCamera();
int64_t startTime;
const std::string &Path() const { return( mPath ); }
const std::string &Options() const { return( mOptions ); }
const std::string &Method() const { return( mMethod ); }
public:
FfmpegCamera( int p_id, const std::string &path, const std::string &p_method, const std::string &p_options, 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 );
~FfmpegCamera();
void Initialise();
void Terminate();
const std::string &Path() const { return( mPath ); }
const std::string &Options() const { return( mOptions ); }
const std::string &Method() const { return( mMethod ); }
int PrimeCapture();
int PreCapture();
int Capture( Image &image );
int PostCapture();
void Initialise();
void Terminate();
int PrimeCapture();
int PreCapture();
int Capture( Image &image );
int CaptureAndRecord( Image &image, timeval recording, char* event_directory );
int PostCapture();
};
#endif // ZM_FFMPEG_CAMERA_H

View File

@ -34,7 +34,7 @@
#include "zm.h"
#include "zm_file_camera.h"
FileCamera::FileCamera( int p_id, 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 ) : Camera( p_id, FILE_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 )
FileCamera::FileCamera( int p_id, 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 ) : Camera( p_id, FILE_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 )
{
strncpy( path, p_path, sizeof(path) );
if ( capture )
@ -87,5 +87,5 @@ int FileCamera::Capture( Image &image )
int FileCamera::PostCapture()
{
return( 0 );
return( 0 );
}

View File

@ -23,6 +23,7 @@
#include "zm_camera.h"
#include "zm_buffer.h"
#include "zm_regexp.h"
#include "zm_packetqueue.h"
#include <sys/param.h>
@ -36,7 +37,7 @@ protected:
char path[PATH_MAX];
public:
FileCamera( int p_id, 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 );
FileCamera( int p_id, 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 ); }
@ -46,6 +47,7 @@ public:
int PreCapture();
int Capture( Image &image );
int PostCapture();
int CaptureAndRecord( Image &image, timeval recording, char* event_directory ) {return(0);};
};
#endif // ZM_FILE_CAMERA_H

File diff suppressed because it is too large Load Diff

View File

@ -54,7 +54,7 @@ extern imgbufcpy_fptr_t fptr_imgbufcpy;
/* Should be called from Image class functions */
inline static uint8_t* AllocBuffer(size_t p_bufsize) {
uint8_t* buffer = (uint8_t*)zm_mallocaligned(16,p_bufsize);
uint8_t* buffer = (uint8_t*)zm_mallocaligned(64,p_bufsize);
if(buffer == NULL)
Fatal("Memory allocation failed: %s",strerror(errno));
@ -122,8 +122,8 @@ protected:
}
public:
enum { CHAR_HEIGHT=11, CHAR_WIDTH=6 };
enum { LINE_HEIGHT=CHAR_HEIGHT+0 };
enum { ZM_CHAR_HEIGHT=11, ZM_CHAR_WIDTH=6 };
enum { LINE_HEIGHT=ZM_CHAR_HEIGHT+0 };
protected:
static bool initialised;
@ -264,6 +264,8 @@ public:
/* Blend functions */
void sse2_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent);
void std_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent);
void neon32_armv7_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent);
void neon64_armv8_fastblend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent);
void std_blend(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count, double blendpercent);
/* Delta functions */
@ -274,6 +276,16 @@ void std_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result,
void std_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void std_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void std_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon32_armv7_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon32_armv7_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon32_armv7_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon32_armv7_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon32_armv7_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon64_armv8_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon64_armv8_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon64_armv8_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon64_armv8_delta8_argb(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void neon64_armv8_delta8_abgr(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void sse2_delta8_gray8(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void sse2_delta8_rgba(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
void sse2_delta8_bgra(const uint8_t* col1, const uint8_t* col2, uint8_t* result, unsigned long count);
@ -293,6 +305,9 @@ void std_convert_argb_gray8(const uint8_t* col1, uint8_t* result, unsigned long
void std_convert_abgr_gray8(const uint8_t* col1, uint8_t* result, unsigned long count);
void std_convert_yuyv_gray8(const uint8_t* col1, uint8_t* result, unsigned long count);
void ssse3_convert_rgba_gray8(const uint8_t* col1, uint8_t* result, unsigned long count);
void ssse3_convert_bgra_gray8(const uint8_t* col1, uint8_t* result, unsigned long count);
void ssse3_convert_argb_gray8(const uint8_t* col1, uint8_t* result, unsigned long count);
void ssse3_convert_abgr_gray8(const uint8_t* col1, uint8_t* result, unsigned long count);
void ssse3_convert_yuyv_gray8(const uint8_t* col1, uint8_t* result, unsigned long count);
void zm_convert_yuyv_rgb(const uint8_t* col1, uint8_t* result, unsigned long count);
void zm_convert_yuyv_rgba(const uint8_t* col1, uint8_t* result, unsigned long count);
@ -309,8 +324,3 @@ void std_deinterlace_4field_rgba(uint8_t* col1, uint8_t* col2, unsigned int thre
void std_deinterlace_4field_bgra(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height);
void std_deinterlace_4field_argb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height);
void std_deinterlace_4field_abgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height);
void ssse3_deinterlace_4field_gray8(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height);
void ssse3_deinterlace_4field_rgba(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height);
void ssse3_deinterlace_4field_bgra(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height);
void ssse3_deinterlace_4field_argb(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height);
void ssse3_deinterlace_4field_abgr(uint8_t* col1, uint8_t* col2, unsigned int threshold, unsigned int width, unsigned int height);

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