merged fifo changes

pull/2616/head
Pliable Pixels 2019-05-16 16:14:06 -04:00
commit 915e9f05a9
23 changed files with 559 additions and 109 deletions

View File

@ -33,7 +33,6 @@ install:
env:
- SMPFLAGS=-j4 OS=el DIST=7
- SMPFLAGS=-j4 OS=fedora DIST=27 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=fedora DIST=28 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=fedora DIST=29 DOCKER_REPO=knnniggett/packpack
- SMPFLAGS=-j4 OS=ubuntu DIST=trusty

View File

@ -791,7 +791,7 @@ INSERT INTO `Controls` VALUES (NULL,'IOS Camera','Ffmpeg','IPCAMIOS',0,0,0,0,0,0
INSERT INTO `Controls` VALUES (NULL,'Dericam P2','Ffmpeg','DericamP2',0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,10,0,1,1,1,0,0,0,1,1,0,0,0,0,1,1,45,0,0,1,0,0,0,0,1,1,45,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Trendnet','Remote','Trendnet',1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'PSIA','Remote','PSIA',0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,0,1,1,1,0,0,1,0,1,0,0,0,0,1,-100,100,0,0,1,0,0,0,0,1,-100,100,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'Dahua','Ffmpeg','Dahua',0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,20,1,1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0);
INSERT INTO `Controls` VALUES (NULL,'FOSCAMR2C','Libvlc','FOSCAMR2C',1,1,1,0,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,0,0,0,0,0,NULL,NULL,NULL,NULL,0,NULL,NULL,1,12,0,1,1,1,0,0,0,1,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,1,NULL,NULL,NULL,NULL,1,0,4,0,NULL,0,0);
INSERT INTO `Controls` VALUES (NULL,'Amcrest HTTP API','Ffmpeg','Amcrest_HTTP',0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,5,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,5);

View File

@ -73,6 +73,7 @@ BuildRequires: libcurl-devel
BuildRequires: libv4l-devel
BuildRequires: desktop-file-utils
BuildRequires: gzip
BuildRequires: zlib-devel
# ZoneMinder looks for and records the location of the ffmpeg binary during build
BuildRequires: ffmpeg

View File

@ -197,8 +197,8 @@ our @options = (
name => 'ZM_BANDWIDTH_DEFAULT',
default => 'high',
description => 'Default setting for bandwidth profile used by web interface',
help => q`The classic skin for ZoneMinder has different
profiles to use for low medium or high bandwidth connections.
help => q`The classic skin for ZoneMinder has different
profiles to use for low, medium, or high bandwidth connections.
`,
type => $types{string},
category => 'system',
@ -558,7 +558,7 @@ our @options = (
higher quality setting than the ordinary file setting. If set
to a lower value then it is ignored. Thus leaving it at the
default of 0 effectively means to use the regular file quality
setting for all saved images. This is to prevent acccidentally
setting for all saved images. This is to prevent accidentally
saving important images at a worse quality setting.
`,
type => $types{integer},
@ -682,7 +682,7 @@ our @options = (
Internet Explorer that don't natively support this format. If
you use this browser it is highly recommended to install this
from the [cambozola project site](http://www.charliemouse.com/code/cambozola/).
However, if it is not installed still images at a lower refresh rate can
However, if it is not installed still images at a lower refresh rate can
still be viewed.
`,
type => $types{boolean},
@ -937,10 +937,10 @@ our @options = (
help => q`
Due to browsers only wanting to open 6 connections, if you have more
than 6 monitors, you can have trouble viewing more than 6. This setting
specified the beginning of a port range that will be used to contact ZM
specified the beginning of a port range that will be used to contact ZM
on. Each monitor will use this value plus the Monitor Id to stream
content. So a value of 2000 here will cause a stream for Monitor 1 to
hit port 2001. Please ensure that you configure apache appropriately
content. So a value of 2000 here will cause a stream for Monitor 1 to
hit port 2001. Please ensure that you configure apache appropriately
to respond on these ports.`,
type => $types{integer},
category => 'network',
@ -1076,12 +1076,12 @@ our @options = (
default => '0',
description => 'Save logging output to the system log',
help => q`
ZoneMinder logging is now more more integrated between
ZoneMinder logging is now more integrated between
components and allows you to specify the destination for
logging output and the individual levels for each. This option
lets you control the level of logging output that goes to the
system log. ZoneMinder binaries have always logged to the
system log but now scripts and web logging is also included. To
system log but script and web logging is now included. To
preserve the previous behaviour you should ensure this value is
set to Info or Warning. This option controls the maximum level
of logging that will be written, so Info includes Warnings and
@ -1103,7 +1103,7 @@ our @options = (
default => '-5',
description => 'Save logging output to component files',
help => q`
ZoneMinder logging is now more more integrated between
ZoneMinder logging is now more integrated between
components and allows you to specify the destination for
logging output and the individual levels for each. This option
lets you control the level of logging output that goes to
@ -1133,7 +1133,7 @@ our @options = (
default => '-5',
description => 'Save logging output to the weblog',
help => q`
ZoneMinder logging is now more more integrated between
ZoneMinder logging is now more integrated between
components and allows you to specify the destination for
logging output and the individual levels for each. This option
lets you control the level of logging output from the web
@ -1160,7 +1160,7 @@ our @options = (
default => '0',
description => 'Save logging output to the database',
help => q`
ZoneMinder logging is now more more integrated between
ZoneMinder logging is now more integrated between
components and allows you to specify the destination for
logging output and the individual levels for each. This option
lets you control the level of logging output that is written to
@ -1215,7 +1215,7 @@ our @options = (
help => q`
When enabled (default is on), this option will log FFMPEG messages.
FFMPEG messages can be useful when debugging streaming issues. However,
depending on your distro and FFMPEG version, this may also result in
depending on your distro and FFMPEG version, this may also result in
more logs than you'd typically like to see. If all your streams are working
well, you may choose to turn this off.
`,
@ -1230,7 +1230,7 @@ our @options = (
ZoneMinder components usually support debug logging available
to help with diagnosing problems. Binary components have
several levels of debug whereas more other components have only
one. Normally this is disabled to minimise performance
one. Normally this is disabled to minimize performance
penalties and avoid filling logs too quickly. This option lets
you switch on other options that allow you to configure
additional debug information to be output. Components will pick
@ -1491,8 +1491,8 @@ our @options = (
default => 'ZoneMinder',
description => 'The title displayed wherever the site references itself.',
help => q`
If you want the site to identify as something other than ZoneMinder, change this here.
It can be used to more accurately identify this installation from others.
If you want the site to identify as something other than ZoneMinder, change this here.
It can be used to more accurately identify this installation from others.
`,
type => $types{string},
category => 'web',
@ -1515,8 +1515,8 @@ our @options = (
default => 'http://zoneminder.com',
description => 'The url used in the home/logo area of the navigation bar.',
help => q`
By default this takes you to the zoneminder.com website,
but perhaps you would prefer it to take you somewhere else.
By default this takes you to the zoneminder.com website,
but perhaps you would prefer it to take you somewhere else.
`,
type => $types{string},
category => 'web',
@ -1526,7 +1526,7 @@ our @options = (
default => 'ZoneMinder',
description => 'The content of the home button.',
help => q`
You may wish to set this to empty if you are using css to put a background image on it.
You may wish to set this to empty if you are using css to put a background image on it.
`,
type => $types{string},
category => 'web',
@ -1549,7 +1549,8 @@ our @options = (
name => 'ZM_WEB_EVENT_DISK_SPACE',
default => 'no',
description => 'Whether to show disk space used by each event.',
help => q`Adds another column to the listing of events
help => q`
Adds another column to the listing of events
showing the disk space used by the event. This will impart a small
overhead as it will call du on the event directory. In practice
this overhead is fairly small but may be noticeable on IO-constrained
@ -1566,7 +1567,7 @@ our @options = (
Traditionally the main ZoneMinder web console window has
resized itself to shrink to a size small enough to list only
the monitors that are actually present. This is intended to
make the window more unobtrusize but may not be to everyones
make the window more unobtrusize but may not be to everyone's
tastes, especially if opened in a tab in browsers which support
this kind if layout. Switch this option off to have the console
window size left to the users preference
@ -2116,7 +2117,7 @@ our @options = (
a remote ftp server. This option indicates that ftp transfers
should be done in passive mode. This uses a single connection
for all ftp activity and, whilst slower than active transfers,
is more robust and likely to work from behind filewalls. This
is more robust and likely to work from behind firewalls. This
option is ignored for SFTP transfers.
`,
requires => [ { name => 'ZM_OPT_UPLOAD', value => 'yes' } ],
@ -2642,7 +2643,7 @@ our @options = (
help => q`
As event images are captured they are stored to the filesystem
with a numerical index. By default this index has three digits
so the numbers start 001, 002 etc. This works works for most
so the numbers start 001, 002 etc. This works for most
scenarios as events with more than 999 frames are rarely
captured. However if you have extremely long events and use
external applications then you may wish to increase this to
@ -2739,7 +2740,7 @@ our @options = (
it to the ZoneMinder development team. This data will be used to
determine things like who and where our customers are, how big their
systems are, the underlying hardware and operating system, etc.
This is being done for the sole purpoase of creating a better
This is being done for the sole purpose of creating a better
product for our target audience. This script is intended to be
completely transparent to the end user, and can be disabled from
the web console under Options. For more details on what information
@ -2763,7 +2764,7 @@ our @options = (
{
name => 'ZM_TELEMETRY_LAST_UPLOAD',
default => '',
description => 'When the last ZoneMinder telemetry upload ocurred',
description => 'When the last ZoneMinder telemetry upload occurred',
help => '',
type => $types{integer},
readonly => 1,
@ -2821,7 +2822,7 @@ our @options = (
default => 'javascript',
description => 'What method windows should use to refresh themselves',
help => q`
Many windows in Javascript need to refresh themselves to keep
Many windows in ZoneMinder need to refresh themselves to keep
their information current. This option determines what method
they should use to do this. Choosing 'javascript' means that
each window will have a short JavaScript statement in with a
@ -2955,7 +2956,7 @@ our @options = (
some indication of the type of content. However this is not a
standard part of HTML. The official method is to use OBJECT
tags which are able to give more information allowing the
correct media viewers etc to be loaded. However these are less
correct media viewers etc. to be loaded. However these are less
widely supported and content may be specifically tailored to a
particular platform or player. This option controls whether
media content is enclosed in EMBED tags only or whether, where
@ -2979,7 +2980,7 @@ our @options = (
browsers. When this condition occurs, ZoneMinder will write a
warning to the log file. To get around this, one can install a browser
plugin or extension to ignore X-Frame headers, and then the page will
display properly. Once the plugin or extenstion has ben installed,
display properly. Once the plugin or extension has ben installed,
the end user may choose to turn this warning off.
`,
type => $types{boolean},
@ -3093,7 +3094,7 @@ our @options = (
description => 'How often (in seconds) the event listing is refreshed in the watch window',
help => q`
The monitor window is actually made from several frames. The
lower framme contains a listing of the last few events for easy
lower frame contains a listing of the last few events for easy
access. This option determines how often this is refreshed.
`,
type => $types{integer},
@ -3223,12 +3224,12 @@ our @options = (
{
name => 'ZM_WEB_H_SCALE_THUMBS',
default => 'no',
description => 'Scale thumbnails in events, bandwidth versus cpu in rescaling',
description => 'Scale thumbnails in events, bandwidth versus CPU in rescaling',
help => q`
If unset, this option sends the whole image to the browser
which resizes it in the window. If set the image is scaled down
on the server before sending a reduced size image to the
browser to conserve bandwidth at the cost of cpu on the server.
browser to conserve bandwidth at the cost of CPU on the server.
Note that ZM can only perform the resizing if the appropriate
PHP graphics functionality is installed. This is usually
available in the php-gd package.
@ -3262,7 +3263,7 @@ our @options = (
help => q`
When viewing events an event navigation panel and progress bar
is shown below the event itself. This allows you to jump to
specific points in the event, but can can also dynamically
specific points in the event, but can also dynamically
update to display the current progress of the event replay
itself. This progress is calculated from the actual event
duration and is not directly linked to the replay itself, so on
@ -3366,7 +3367,7 @@ our @options = (
description => 'How often (in seconds) the event listing is refreshed in the watch window',
help => q`
The monitor window is actually made from several frames. The
lower framme contains a listing of the last few events for easy
lower frame contains a listing of the last few events for easy
access. This option determines how often this is refreshed.
`,
type => $types{integer},
@ -3496,12 +3497,12 @@ our @options = (
{
name => 'ZM_WEB_M_SCALE_THUMBS',
default => 'yes',
description => 'Scale thumbnails in events, bandwidth versus cpu in rescaling',
description => 'Scale thumbnails in events, bandwidth versus CPU in rescaling',
help => q`
If unset, this option sends the whole image to the browser
which resizes it in the window. If set the image is scaled down
on the server before sending a reduced size image to the
browser to conserve bandwidth at the cost of cpu on the server.
browser to conserve bandwidth at the cost of CPU on the server.
Note that ZM can only perform the resizing if the appropriate
PHP graphics functionality is installed. This is usually
available in the php-gd package.
@ -3535,7 +3536,7 @@ our @options = (
help => q`
When viewing events an event navigation panel and progress bar
is shown below the event itself. This allows you to jump to
specific points in the event, but can can also dynamically
specific points in the event, but can also dynamically
update to display the current progress of the event replay
itself. This progress is calculated from the actual event
duration and is not directly linked to the replay itself, so on
@ -3639,7 +3640,7 @@ our @options = (
description => 'How often (in seconds) the event listing is refreshed in the watch window',
help => q`
The monitor window is actually made from several frames. The
lower framme contains a listing of the last few events for easy
lower frame contains a listing of the last few events for easy
access. This option determines how often this is refreshed.
`,
type => $types{integer},
@ -3768,12 +3769,12 @@ our @options = (
{
name => 'ZM_WEB_L_SCALE_THUMBS',
default => 'yes',
description => 'Scale thumbnails in events, bandwidth versus cpu in rescaling',
description => 'Scale thumbnails in events, bandwidth versus CPU in rescaling',
help => q`
If unset, this option sends the whole image to the browser
which resizes it in the window. If set the image is scaled down
on the server before sending a reduced size image to the
browser to conserve bandwidth at the cost of cpu on the server.
browser to conserve bandwidth at the cost of CPU on the server.
Note that ZM can only perform the resizing if the appropriate
PHP graphics functionality is installed. This is usually
available in the php-gd package.
@ -3807,7 +3808,7 @@ our @options = (
help => q`
When viewing events an event navigation panel and progress bar
is shown below the event itself. This allows you to jump to
specific points in the event, but can can also dynamically
specific points in the event, but can also dynamically
update to display the current progress of the event replay
itself. This progress is calculated from the actual event
duration and is not directly linked to the replay itself, so on
@ -3955,7 +3956,15 @@ our @options = (
help => q`This will affect how long a session will be valid for since the last request. Keeping this short helps prevent session hijacking. Keeping it long allows you to stay logged in longer without refreshing the view.`,
type => $types{integer},
category => 'system',
}
},
{
name => 'ZM_RECORD_DIAG_IMAGES_FIFO',
default => 'no',
description => ' Recording intermediate alarm diagnostic use fifo instead of files (faster)',
help => 'This tries to lessen the load of recording diag images by sending them to a memory FIFO pipe instead of creating each file.',
type => $types{boolean},
category => 'logging',
},
);
our %options_hash = map { ( $_->{name}, $_ ) } @options;
@ -3999,7 +4008,7 @@ saveConfigToDB();
The ZoneMinder:ConfigData module contains the master definition of the
ZoneMinder configuration options as well as helper methods. This module is
intended for specialist confguration management and would not normally be
intended for specialist configuration management and would not normally be
used by end users.
The configuration held in this module, which was previously in zmconfig.pl,

View File

@ -189,7 +189,7 @@ sub moveAbs ## Up, Down, Left, Right, etc. ??? Doesn't make sense here...
my $tilt_degrees = shift || 0;
my $speed = shift || 1;
Debug( "Move ABS" );
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degress.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed );
$self->sendCmd( 'cgi-bin/ptz.cgi?action=start&code=PositionABS&channel=0&arg1='.$pan_degrees.'&arg2='.$tilt_degrees.'&arg3=0&arg4='.$speed );
}
sub moveConUp

View File

@ -4,8 +4,8 @@
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_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.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_nvsocket.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_swscale.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 zm_storage.cpp zm_crypt.cpp)
<<<<<<< HEAD
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_frame.cpp zm_eventstream.cpp zm_exception.cpp zm_file_camera.cpp zm_ffmpeg_input.cpp zm_ffmpeg_camera.cpp zm_group.cpp zm_image.cpp zm_jpeg.cpp zm_libvlc_camera.cpp zm_local_camera.cpp zm_monitor.cpp zm_monitorstream.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_nvsocket.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_swscale.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 zm_storage.cpp zm_fifo.cpp zm_crypt.cpp)
# A fix for cmake recompiling the source files for every target.

247
src/zm_fifo.cpp Normal file
View File

@ -0,0 +1,247 @@
#include <fcntl.h>
#include <getopt.h>
#include <sys/file.h>
#include <stdio.h>
#include <stdarg.h>
#include <signal.h>
#include "zm.h"
#include "zm_db.h"
#include "zm_time.h"
#include "zm_mpeg.h"
#include "zm_signal.h"
#include "zm_monitor.h"
#include "zm_fifo.h"
#define RAW_BUFFER 512
static bool zm_fifodbg_inited = false;
FILE *zm_fifodbg_log_fd = 0;
char zm_fifodbg_log[PATH_MAX] = "";
static bool zmFifoDbgOpen(){
if (zm_fifodbg_log_fd)
fclose(zm_fifodbg_log_fd);
zm_fifodbg_log_fd = NULL;
signal(SIGPIPE, SIG_IGN);
FifoStream::fifo_create_if_missing(zm_fifodbg_log);
int fd = open(zm_fifodbg_log,O_WRONLY|O_NONBLOCK|O_TRUNC);
if (fd < 0)
return ( false );
int res = flock(fd,LOCK_EX | LOCK_NB);
if (res < 0)
{
close(fd);
return ( false );
}
zm_fifodbg_log_fd = fdopen(fd,"wb");
if (zm_fifodbg_log_fd == NULL)
{
close(fd);
return ( false );
}
return ( true );
}
int zmFifoDbgInit(Monitor *monitor){
zm_fifodbg_inited = true;
snprintf( zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/%d/dbgpipe.log", monitor->getStorage()->Path(), monitor->Id() );
zmFifoDbgOpen();
return 1;
}
void zmFifoDbgOutput( int hex, const char * const file, const int line, const int level, const char *fstring, ... ){
char dbg_string[8192];
va_list arg_ptr;
if (! zm_fifodbg_inited)
return;
if (! zm_fifodbg_log_fd && ! zmFifoDbgOpen())
return;
char *dbg_ptr = dbg_string;
va_start( arg_ptr, fstring );
if ( hex )
{
unsigned char *data = va_arg( arg_ptr, unsigned char * );
int len = va_arg( arg_ptr, int );
int i;
dbg_ptr += snprintf( dbg_ptr, sizeof(dbg_string)-(dbg_ptr-dbg_string), "%d:", len );
for ( i = 0; i < len; i++ )
{
dbg_ptr += snprintf( dbg_ptr, sizeof(dbg_string)-(dbg_ptr-dbg_string), " %02x", data[i] );
}
}
else
{
dbg_ptr += vsnprintf( dbg_ptr, sizeof(dbg_string)-(dbg_ptr-dbg_string), fstring, arg_ptr );
}
va_end(arg_ptr);
strncpy( dbg_ptr++, "\n", 1);
int res = fwrite( dbg_string, dbg_ptr-dbg_string, 1, zm_fifodbg_log_fd );
if (res != 1){
fclose(zm_fifodbg_log_fd);
zm_fifodbg_log_fd = NULL;
}
else
{
fflush(zm_fifodbg_log_fd);
}
}
bool FifoStream::sendRAWFrames(){
static unsigned char buffer[RAW_BUFFER];
int fd = open(stream_path,O_RDONLY);
if (fd < 0)
{
Error( "Can't open %s: %s", stream_path, strerror(errno));
return( false );
}
while( (bytes_read = read(fd,buffer,RAW_BUFFER)) )
{
if (bytes_read == 0)
continue;
if (bytes_read < 0)
{
Error( "Problem during reading: %s", strerror(errno));
close(fd);
return( false );
}
if ( fwrite( buffer, bytes_read, 1, stdout ) != 1){
Error( "Problem during writing: %s", strerror(errno));
close(fd);
return( false );
}
fflush( stdout );
}
close(fd);
return ( true );
}
void FifoStream::file_create_if_missing(const char * path, bool is_fifo,bool delete_fake_fifo){
static struct stat st;
if(stat(path,&st) == 0){
if (! is_fifo || S_ISFIFO(st.st_mode) || ! delete_fake_fifo)
return;
Debug(5, "Supposed to be a fifo pipe but isn't, unlinking: %s", path);
unlink(path);
}
int fd;
if (! is_fifo){
Debug(5, "Creating non fifo file as requested: %s", path);
fd = open(path,O_CREAT|O_WRONLY,S_IRUSR|S_IWUSR);
close(fd);
return;
}
Debug(5, "Making fifo file of: %s", path);
mkfifo(path,S_IRUSR|S_IWUSR);
}
void FifoStream::fifo_create_if_missing(const char * path, bool delete_fake_fifo){
file_create_if_missing(path,true,delete_fake_fifo);
}
bool FifoStream::sendMJEGFrames(){
static unsigned char buffer[ZM_MAX_IMAGE_SIZE];
int fd = open(stream_path,O_RDONLY);
if (fd < 0)
{
Error( "Can't open %s: %s", stream_path, strerror(errno));
return( false );
}
total_read = 0;
while( (bytes_read = read(fd,buffer+total_read,ZM_MAX_IMAGE_SIZE-total_read)) )
{
if (bytes_read < 0)
{
Error( "Problem during reading: %s", strerror(errno));
close(fd);
return( false );
}
total_read+=bytes_read;
}
close(fd);
if (total_read == 0)
return( true );
if (frame_count%frame_mod != 0)
return (true );
if (fprintf( stdout, "--ZoneMinderFrame\r\n" ) < 0 )
{
Error( "Problem during writing: %s", strerror(errno));
return( false );
}
fprintf( stdout, "Content-Type: image/jpeg\r\n" );
fprintf( stdout, "Content-Length: %d\r\n\r\n", total_read );
if ( fwrite( buffer, total_read, 1, stdout ) != 1){
Error( "Problem during reading: %s", strerror(errno));
return( false );
}
fprintf( stdout, "\r\n\r\n" );
fflush( stdout);
last_frame_sent = TV_2_FLOAT( now );
frame_count++;
return( true );
}
void FifoStream::setStreamStart( const char * path ){
stream_path = strdup(path);
}
void FifoStream::setStreamStart( int monitor_id, const char * format ){
char diag_path[PATH_MAX];
const char * filename;
Monitor * monitor = Monitor::Load(monitor_id, false, Monitor::QUERY);
if (! strcmp(format,"reference") )
{
stream_type = MJPEG;
filename = "diagpipe-r.jpg";
}
else if (! strcmp(format,"delta"))
{
filename = "diagpipe-d.jpg";
stream_type = MJPEG;
}
else
{
stream_type = RAW;
filename = "dbgpipe.log";
}
snprintf( diag_path, sizeof(diag_path), "%s/%d/%s", monitor->getStorage()->Path(), monitor->Id(), filename );
setStreamStart(diag_path);
}
void FifoStream::runStream(){
if (stream_type == MJPEG)
fprintf( stdout, "Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n" );
else
fprintf( stdout, "Content-Type: text/html\r\n\r\n" );
char lock_file[PATH_MAX];
strcpy(lock_file,stream_path);
strcat(lock_file,".rlock");
file_create_if_missing(lock_file,false);
int fd_lock = open(lock_file,O_RDONLY);
if (fd_lock < 0)
{
Error( "Can't open %s: %s", lock_file, strerror(errno));
return;
}
int res = flock(fd_lock,LOCK_EX | LOCK_NB);
if (res < 0)
{
Error( "Flocking problem on %s: - %s", lock_file, strerror(errno) );
close(fd_lock);
return;
}
while( !zm_terminate )
{
gettimeofday( &now, NULL );
checkCommandQueue();
if (stream_type == MJPEG)
{
if (! sendMJEGFrames())
zm_terminate = true;
}
else
{
if (! sendRAWFrames())
zm_terminate = true;
}
}
close(fd_lock);
}

61
src/zm_fifo.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef ZM_FIFO_H
#define ZM_FIFO_H
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "zm.h"
#include "zm_image.h"
#include "zm_monitor.h"
#include "zm_stream.h"
#define zmFifoDbgPrintf(level,params...) {\
zmFifoDbgOutput( 0, __FILE__, __LINE__, level, ##params );\
}
#ifndef ZM_DBG_OFF
#define FifoDebug(level,params...) zmFifoDbgPrintf(level,##params)
#else
#define FifoDebug(level,params...)
#endif
void zmFifoDbgOutput( int hex, const char * const file, const int line, const int level, const char *fstring, ... ) __attribute__ ((format(printf, 5, 6)));
int zmFifoDbgInit(Monitor * monitor);
class FifoStream : public StreamBase
{
private:
char * stream_path;
int fd;
int total_read;
int bytes_read;
unsigned int frame_count;
static void file_create_if_missing(const char * path, bool is_fifo, bool delete_fake_fifo=true);
protected:
typedef enum { MJPEG, RAW } StreamType;
StreamType stream_type;
bool sendMJEGFrames( );
bool sendRAWFrames( );
void processCommand( const CmdMsg *msg ) {};
public:
FifoStream(){
}
static void fifo_create_if_missing(const char * path,bool delete_fake_fifo=true);
void setStreamStart( const char * path );
void setStreamStart( int monitor_id, const char * format );
void runStream();
};
#endif

View File

@ -24,6 +24,7 @@
#include "zm_rgb.h"
#include "zm_ffmpeg.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
@ -957,7 +958,7 @@ cinfo->out_color_space = JCS_RGB;
return( true );
}
// Multiple calling formats to permit inclusion (or not) of both quality_override and timestamp (exif), with suitable defaults.
// Multiple calling formats to permit inclusion (or not) of non blocking, quality_override and timestamp (exif), with suitable defaults.
// Note quality=zero means default
bool Image::WriteJpeg(const char *filename, int quality_override) const {
@ -966,33 +967,71 @@ bool Image::WriteJpeg(const char *filename, int quality_override) const {
bool Image::WriteJpeg(const char *filename) const {
return Image::WriteJpeg(filename, 0, (timeval){0,0});
}
bool Image::WriteJpeg(const char *filename, bool on_blocking_abort) const {
return Image::WriteJpeg(filename, 0, (timeval){0,0}, on_blocking_abort);
}
bool Image::WriteJpeg(const char *filename, struct timeval timestamp) const {
return Image::WriteJpeg(filename, 0, timestamp);
}
bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval timestamp) const {
return Image::WriteJpeg(filename, quality_override, timestamp, false);
}
bool Image::WriteJpeg(const char *filename, int quality_override, struct timeval timestamp, bool on_blocking_abort) const {
if ( config.colour_jpeg_files && colours == ZM_COLOUR_GRAY8 ) {
Image temp_image(*this);
temp_image.Colourise(ZM_COLOUR_RGB24, ZM_SUBPIX_ORDER_RGB);
return temp_image.WriteJpeg(filename, quality_override, timestamp);
return temp_image.WriteJpeg(filename, quality_override, timestamp, on_blocking_abort);
}
int quality = quality_override?quality_override:config.jpeg_file_quality;
struct jpeg_compress_struct *cinfo = writejpg_ccinfo[quality];
FILE *outfile =NULL;
static int raw_fd = 0;
bool need_create_comp = false;
raw_fd = 0;
if ( !cinfo ) {
cinfo = writejpg_ccinfo[quality] = new jpeg_compress_struct;
cinfo->err = jpeg_std_error( &jpg_err.pub );
jpg_err.pub.error_exit = zm_jpeg_error_exit;
jpg_err.pub.emit_message = zm_jpeg_emit_message;
jpeg_create_compress( cinfo );
need_create_comp=true;
}
if (! on_blocking_abort) {
jpg_err.pub.error_exit = zm_jpeg_error_exit;
jpg_err.pub.emit_message = zm_jpeg_emit_message;
} else {
jpg_err.pub.error_exit = zm_jpeg_error_silent;
jpg_err.pub.emit_message = zm_jpeg_emit_silence;
if (setjmp( jpg_err.setjmp_buffer ) ) {
jpeg_abort_compress( cinfo );
Debug( 5, "Aborted a write mid-stream and %s and %d", (outfile == NULL) ? "closing file" : "file not opened", raw_fd );
if (raw_fd)
close(raw_fd);
if (outfile)
fclose( outfile );
return ( false );
}
}
if (need_create_comp)
jpeg_create_compress( cinfo );
if (! on_blocking_abort) {
if ( (outfile = fopen(filename, "wb")) == NULL ) {
Error( "Can't open %s for writing: %s", filename, strerror(errno) );
return false;
}
} else {
raw_fd = open(filename,O_WRONLY|O_NONBLOCK|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (raw_fd < 0)
return ( false );
outfile = fdopen(raw_fd,"wb");
if (outfile == NULL) {
close(raw_fd);
return( false );
}
}
FILE *outfile;
if ( (outfile = fopen(filename, "wb")) == NULL ) {
Error("Can't open %s: %s", filename, strerror(errno));
return false;
}
jpeg_stdio_dest( cinfo, outfile );
cinfo->image_width = width; /* image width and height, in pixels */

View File

@ -218,9 +218,12 @@ public:
bool ReadJpeg( const char *filename, unsigned int p_colours, unsigned int p_subpixelorder);
bool WriteJpeg ( const char *filename) const;
bool WriteJpeg ( const char *filename, bool on_blocking_abort) const;
bool WriteJpeg ( const char *filename, int quality_override ) const;
bool WriteJpeg ( const char *filename, struct timeval timestamp ) const;
bool WriteJpeg ( const char *filename, int quality_override, struct timeval timestamp ) const;
bool WriteJpeg ( const char *filename, int quality_override, struct timeval timestamp, bool on_blocking_abort ) const;
bool DecodeJpeg( const JOCTET *inbuffer, int inbuffer_size, unsigned int p_colours, unsigned int p_subpixelorder);
bool EncodeJpeg( JOCTET *outbuffer, int *outbuffer_size, int quality_override=0 ) const;

View File

@ -30,6 +30,13 @@ extern "C"
static int jpeg_err_count = 0;
void zm_jpeg_error_silent( j_common_ptr cinfo ){
zm_error_ptr zmerr = (zm_error_ptr)cinfo->err;
longjmp( zmerr->setjmp_buffer, 1 );
}
void zm_jpeg_emit_silence( j_common_ptr cinfo, int msg_level ){
}
void zm_jpeg_error_exit( j_common_ptr cinfo )
{
static char buffer[JMSG_LENGTH_MAX];

View File

@ -38,6 +38,8 @@ struct zm_error_mgr
typedef struct zm_error_mgr *zm_error_ptr;
void zm_jpeg_error_silent( j_common_ptr cinfo );
void zm_jpeg_emit_silence( j_common_ptr cinfo, int msg_level );
void zm_jpeg_error_exit( j_common_ptr cinfo );
void zm_jpeg_emit_message( j_common_ptr cinfo, int msg_level );

View File

@ -44,6 +44,7 @@
#if HAVE_LIBAVFORMAT
#include "zm_ffmpeg_camera.h"
#endif // HAVE_LIBAVFORMAT
#include "zm_fifo.h"
#if HAVE_LIBVLC
#include "zm_libvlc_camera.h"
#endif // HAVE_LIBVLC
@ -517,8 +518,12 @@ Monitor::Monitor(
ReloadLinkedMonitors(p_linked_monitors);
if ( config.record_diag_images ) {
diag_path_r = stringtf("%s/%d/diag-r.jpg", storage->Path(), id);
diag_path_d = stringtf("%s/%d/diag-d.jpg", storage->Path(), id);
diag_path_r = stringtf(config.record_diag_images_fifo ? "%s/%d/diagpipe-r.jpg" : "%s/%d/diag-r.jpg", storage->Path(), id);
diag_path_d = stringtf(config.record_diag_images_fifo ? "%s/%d/diagpipe-d.jpg" : "%s/%d/diag-d.jpg", storage->Path(), id);
if (config.record_diag_images_fifo){
FifoStream::fifo_create_if_missing(diag_path_r.c_str());
FifoStream::fifo_create_if_missing(diag_path_d.c_str());
}
}
} // end if purpose == ANALYSIS
@ -2614,8 +2619,8 @@ unsigned int Monitor::DetectMotion(const Image &comp_image, Event::StringSet &zo
ref_image.Delta(comp_image, &delta_image);
if ( config.record_diag_images ) {
ref_image.WriteJpeg(diag_path_r.c_str());
delta_image.WriteJpeg(diag_path_d.c_str());
ref_image.WriteJpeg(diag_path_r.c_str(), config.record_diag_images_fifo);
delta_image.WriteJpeg(diag_path_d.c_str(), config.record_diag_images_fifo);
}
// Blank out all exclusion zones

View File

@ -394,10 +394,15 @@ bool MonitorStream::sendFrame(Image *image, struct timeval *timestamp) {
img_buffer_size = send_image->Size();
break;
case STREAM_ZIP :
#if HAVE_ZLIB_H
fputs("Content-Type: image/x-rgbz\r\n",stdout);
unsigned long zip_buffer_size;
send_image->Zip(img_buffer, &zip_buffer_size);
img_buffer_size = zip_buffer_size;
#else
Error("zlib is required for zipped images. Falling back to raw image");
type = STREAM_RAW;
#endif // HAVE_ZLIB_H
break;
default :
Error("Unexpected frame type %d", type);
@ -794,6 +799,8 @@ void MonitorStream::SingleImageRaw( int scale ) {
fwrite( snap_image->Buffer(), snap_image->Size(), 1, stdout );
}
#ifdef HAVE_ZLIB_H
void MonitorStream::SingleImageZip( int scale ) {
unsigned long img_buffer_size = 0;
static Bytef img_buffer[ZM_MAX_IMAGE_SIZE];
@ -816,3 +823,4 @@ void MonitorStream::SingleImageZip( int scale ) {
fprintf( stdout, "Content-Type: image/x-rgbz\r\n\r\n" );
fwrite( img_buffer, img_buffer_size, 1, stdout );
}
#endif // HAVE_ZLIB_H

View File

@ -55,7 +55,9 @@ class MonitorStream : public StreamBase {
void processCommand( const CmdMsg *msg );
void SingleImage( int scale=100 );
void SingleImageRaw( int scale=100 );
#ifdef HAVE_ZLIB_H
void SingleImageZip( int scale=100 );
#endif
public:
MonitorStream() :

View File

@ -1011,7 +1011,6 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
}
zm_dump_frame(out_frame, "Out frame after resample");
out_frame->pts = in_frame->pts;
// out_frame pts is in the input pkt pts... needs to be adjusted before sending to the encoder
if ( out_frame->pts != AV_NOPTS_VALUE ) {
if ( !audio_first_pts ) {
@ -1065,6 +1064,8 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
return 0;
}
#endif
#if 0
// These should be set by encoder. They may not directly relate to ipkt due to buffering in codec.
opkt.duration = av_rescale_q(opkt.duration,
audio_in_stream->time_base,
audio_out_stream->time_base);
@ -1074,6 +1075,7 @@ int VideoStore::writeAudioFramePacket(AVPacket *ipkt) {
opkt.dts = av_rescale_q(opkt.dts,
audio_in_stream->time_base,
audio_out_stream->time_base);
#endif
dumpPacket(audio_out_stream, &opkt, "raw opkt");
} else {
@ -1179,13 +1181,32 @@ int VideoStore::resample_audio() {
Debug(2, "Converting %d to %d samples using swresample",
in_frame->nb_samples, out_frame->nb_samples);
ret = swr_convert_frame(resample_ctx, out_frame, in_frame);
zm_dump_frame(out_frame, "Out frame after convert");
if ( ret < 0 ) {
Error("Could not resample frame (error '%s')",
av_make_error_string(ret).c_str());
return 0;
}
zm_dump_frame(out_frame, "Out frame after convert");
#if 0
// out_frame pts is in the input pkt pts... needs to be adjusted before sending to the encoder
if ( out_frame->pts != AV_NOPTS_VALUE ) {
if ( !audio_first_pts ) {
audio_first_pts = out_frame->pts;
Debug(1, "No audio_first_pts setting to %" PRId64, audio_first_pts);
out_frame->pts = 0;
} else {
out_frame->pts = out_frame->pts - audio_first_pts;
}
//
} else {
// sending AV_NOPTS_VALUE doesn't really work but we seem to get it in ffmpeg 2.8
out_frame->pts = audio_next_pts;
}
audio_next_pts = out_frame->pts + out_frame->nb_samples;
#endif
if ((ret = av_audio_fifo_realloc(fifo, av_audio_fifo_size(fifo) + out_frame->nb_samples)) < 0) {
Error("Could not reallocate FIFO");
return 0;
@ -1193,7 +1214,8 @@ int VideoStore::resample_audio() {
/** Store the new samples in the FIFO buffer. */
ret = av_audio_fifo_write(fifo, (void **)out_frame->data, out_frame->nb_samples);
if ( ret < out_frame->nb_samples ) {
Error("Could not write data to FIFO on %d written, expecting %d", ret, out_frame->nb_samples);
Error("Could not write data to FIFO. %d written, expecting %d. Reason %s",
ret, out_frame->nb_samples, av_make_error_string(ret).c_str());
return 0;
}
@ -1210,6 +1232,17 @@ int VideoStore::resample_audio() {
return 0;
}
out_frame->nb_samples = frame_size;
// resampling changes the duration because the timebase is 1/samples
if ( in_frame->pts != AV_NOPTS_VALUE ) {
out_frame->pkt_duration = av_rescale_q(
in_frame->pkt_duration,
audio_in_stream->time_base,
audio_out_stream->time_base);
out_frame->pts = av_rescale_q(
in_frame->pts,
audio_in_stream->time_base,
audio_out_stream->time_base);
}
#else
#if defined(HAVE_LIBAVRESAMPLE)
ret = avresample_convert(resample_ctx, NULL, 0, 0, in_frame->data,

View File

@ -24,6 +24,7 @@
#include "zm_zone.h"
#include "zm_image.h"
#include "zm_monitor.h"
#include "zm_fifo.h"
void Zone::Setup(
@ -112,8 +113,10 @@ void Zone::Setup(
}
if ( config.record_diag_images ) {
snprintf(diag_path, sizeof(diag_path), "%s/diag-%d-poly.jpg", monitor->getStorage()->Path(), id);
pg_image->WriteJpeg(diag_path);
snprintf(diag_path, sizeof(diag_path), config.record_diag_images_fifo ? "%s/diagpipe-%d-poly.jpg" : "%s/diag-%d-poly.jpg", monitor->getStorage()->Path(), id);
if (config.record_diag_images_fifo)
FifoStream::fifo_create_if_missing(diag_path);
pg_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
} else {
diag_path[0] = 0;
}
@ -232,7 +235,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
std_alarmedpixels(diff_image, pg_image, &alarm_pixels, &pixel_diff_count);
if ( config.record_diag_images )
diff_image->WriteJpeg(diag_path);
diff_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
if ( pixel_diff_count && alarm_pixels )
pixel_diff = pixel_diff_count/alarm_pixels;
@ -240,6 +243,10 @@ bool Zone::CheckAlarms(const Image *delta_image) {
Debug(5, "Got %d alarmed pixels, need %d -> %d, avg pixel diff %d",
alarm_pixels, min_alarm_pixels, max_alarm_pixels, pixel_diff);
if (config.record_diag_images_fifo)
FifoDebug( 5, "{\"zone\":%d,\"type\":\"ALRM\",\"pixels\":%d,\"avg_diff\":%d}", id,alarm_pixels, pixel_diff );
if ( alarm_pixels ) {
if ( min_alarm_pixels && (alarm_pixels < (unsigned int)min_alarm_pixels) ) {
/* Not enough pixels alarmed */
@ -316,11 +323,14 @@ bool Zone::CheckAlarms(const Image *delta_image) {
}
if ( config.record_diag_images )
diff_image->WriteJpeg(diag_path);
diff_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
Debug(5, "Got %d filtered pixels, need %d -> %d",
alarm_filter_pixels, min_filter_pixels, max_filter_pixels);
if (config.record_diag_images_fifo)
FifoDebug( 5, "{\"zone\":%d,\"type\":\"FILT\",\"pixels\":%d}", id, alarm_filter_pixels );
if ( alarm_filter_pixels ) {
if ( min_filter_pixels && (alarm_filter_pixels < min_filter_pixels) ) {
/* Not enough pixels alarmed */
@ -542,7 +552,7 @@ bool Zone::CheckAlarms(const Image *delta_image) {
}
}
if ( config.record_diag_images )
diff_image->WriteJpeg(diag_path);
diff_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
if ( !alarm_blobs ) {
return false;
@ -551,6 +561,9 @@ bool Zone::CheckAlarms(const Image *delta_image) {
Debug(5, "Got %d raw blob pixels, %d raw blobs, need %d -> %d, %d -> %d",
alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs);
if (config.record_diag_images_fifo)
FifoDebug( 5, "{\"zone\":%d,\"type\":\"RBLB\",\"pixels\":%d,\"blobs\":%d}", id, alarm_blob_pixels, alarm_blobs );
// Now eliminate blobs under the threshold
for ( int i = 1; i < WHITE; i++ ) {
BlobStats *bs = &blob_stats[i];
@ -587,10 +600,12 @@ bool Zone::CheckAlarms(const Image *delta_image) {
} // end if bs_count
} // end for i < WHITE
if ( config.record_diag_images )
diff_image->WriteJpeg(diag_path);
diff_image->WriteJpeg(diag_path, config.record_diag_images_fifo);
Debug(5, "Got %d blob pixels, %d blobs, need %d -> %d, %d -> %d",
alarm_blob_pixels, alarm_blobs, min_blob_pixels, max_blob_pixels, min_blobs, max_blobs);
if (config.record_diag_images_fifo)
FifoDebug( 5, "{\"zone\":%d,\"type\":\"FBLB\",\"pixels\":%d,\"blobs\":%d}", id, alarm_blob_pixels, alarm_blobs );
if ( alarm_blobs ) {
if ( min_blobs && (alarm_blobs < min_blobs) ) {

View File

@ -56,6 +56,7 @@ behind.
#include "zm_db.h"
#include "zm_signal.h"
#include "zm_monitor.h"
#include "zm_fifo.h"
void Usage() {
fprintf(stderr, "zma -m <monitor_id>\n");
@ -129,6 +130,7 @@ int main( int argc, char *argv[] ) {
hwcaps_detect();
Monitor *monitor = Monitor::Load(id, true, Monitor::ANALYSIS);
zmFifoDbgInit( monitor );
if ( monitor ) {
Info("In mode %d/%d, warming up", monitor->GetFunction(), monitor->Enabled());

View File

@ -28,6 +28,7 @@
#include "zm_monitor.h"
#include "zm_monitorstream.h"
#include "zm_eventstream.h"
#include "zm_fifo.h"
bool ValidateAccess( User *user, int mon_id ) {
bool allowed = true;
@ -54,7 +55,7 @@ int main( int argc, const char *argv[] ) {
srand( getpid() * time( 0 ) );
enum { ZMS_UNKNOWN, ZMS_MONITOR, ZMS_EVENT } source = ZMS_UNKNOWN;
enum { ZMS_UNKNOWN, ZMS_MONITOR, ZMS_EVENT, ZMS_FIFO } source = ZMS_UNKNOWN;
enum { ZMS_JPEG, ZMS_MPEG, ZMS_RAW, ZMS_ZIP, ZMS_SINGLE } mode = ZMS_JPEG;
char format[32] = "";
int monitor_id = 0;
@ -111,6 +112,8 @@ int main( int argc, const char *argv[] ) {
value = (char *)"";
if ( !strcmp(name, "source") ) {
source = !strcmp(value, "event")?ZMS_EVENT:ZMS_MONITOR;
if (! strcmp( value,"fifo") )
source = ZMS_FIFO;
} else if ( !strcmp(name, "mode") ) {
mode = !strcmp(value, "jpeg")?ZMS_JPEG:ZMS_MPEG;
mode = !strcmp(value, "raw")?ZMS_RAW:mode;
@ -284,6 +287,11 @@ int main( int argc, const char *argv[] ) {
#endif // HAVE_LIBAVCODEC
}
stream.runStream();
} else if (source == ZMS_FIFO ) {
FifoStream stream;
stream.setStreamMaxFPS( maxfps );
stream.setStreamStart( monitor_id, format );
stream.runStream();
} else if ( source == ZMS_EVENT ) {
if ( ! event_id ) {
Fatal( "Can't view an event without specifying an event_id." );

View File

@ -32,7 +32,7 @@
// a formatting string. If the dynamic element is a number you will usually need to use a variable
// replacement also as described below.
// c) Variable replacements are used in conjunction with complex replacements and involve the generation
// of a singular or plural noun depending on the number passed into the zmVlang function. See the
// of a singular or plural noun depending on the number passed into the zmVlang function. See the
// the zmVlang section below for a further description of this.
// d) Optional strings which can be used to replace the prompts and/or help text for the Options section
// of the web interface. These are not listed below as they are quite large and held in the database
@ -40,7 +40,7 @@
// quite easily from the Config table in the database if necessary.
// 3. The tokens listed below are not used to build up phrases or sentences from single words. Therefore
// you can safely assume that a single word token will only be used in that context.
// 4. In new language files, or if you are changing only a few words or phrases it makes sense from a
// 4. In new language files, or if you are changing only a few words or phrases it makes sense from a
// maintenance point of view to include the original language file and override the old definitions rather
// than copy all the language tokens across. To do this change the line below to whatever your base language
// is and uncomment it.
@ -57,10 +57,10 @@
// If you do need to change your locale, be aware that the format of this function
// is subtlely different in versions of PHP before and after 4.3.0, see
// http://uk2.php.net/manual/en/function.setlocale.php for details.
// Also be aware that changing the whole locale may affect some floating point or decimal
// Also be aware that changing the whole locale may affect some floating point or decimal
// arithmetic in the database, if this is the case change only the individual locale areas
// that don't affect this rather than all at once. See the examples below.
// Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared
// Finally, depending on your setup, PHP may not enjoy have multiple locales in a shared
// threaded environment, if you get funny errors it may be this.
//
// Examples
@ -771,7 +771,7 @@ $SLANG = array(
'Update' => 'Update',
'Upload' => 'Upload',
'Updated' => 'Updated',
'UsedPlugins' => 'Used Plugins',
'UsedPlugins' => 'Used Plugins',
'UseFilterExprsPost' => '&nbsp;filter&nbsp;expressions', // This is used at the end of the phrase 'use N filter expressions'
'UseFilterExprsPre' => 'Use&nbsp;', // This is used at the beginning of the phrase 'use N filter expressions'
'UseFilter' => 'Use Filter',
@ -850,7 +850,7 @@ $CLANG = array(
'VersionMismatch' => 'Version mismatch, system is version %1$s, database is %2$s.',
);
// The next section allows you to describe a series of word ending and counts used to
// The next section allows you to describe a series of word ending and counts used to
// generate the correctly conjugated forms of words depending on a count that is associated
// with that word.
// This intended to allow phrases such a '0 potatoes', '1 potato', '2 potatoes' etc to
@ -891,7 +891,7 @@ $VLANG = array(
// with variable counts. This is used to conjugate the Vlang arrays above with a number passed
// in to generate the correct noun form.
//
// In languages such as English this is fairly simple
// In languages such as English this is fairly simple
// Note this still has to be used with printf etc to get the right formatting
function zmVlang( $langVarArray, $count )
{
@ -909,9 +909,9 @@ function zmVlang( $langVarArray, $count )
// This is an version that could be used in the Russian example above
// The rules are that the first word form is used if the count ends in
// 0, 5-9 or 11-19. The second form is used then the count ends in 1
// (not including 11 as above) and the third form is used when the
// (not including 11 as above) and the third form is used when the
// count ends in 2-4, again excluding any values ending in 12-14.
//
//
// function zmVlang( $langVarArray, $count )
// {
// $secondlastdigit = substr( $count, -2, 1 );
@ -919,7 +919,7 @@ function zmVlang( $langVarArray, $count )
// // or
// // $secondlastdigit = ($count/10)%10;
// // $lastdigit = $count%10;
//
//
// // Get rid of the special cases first, the teens
// if ( $secondlastdigit == 1 && $lastdigit != 0 )
// {
@ -953,7 +953,7 @@ function zmVlang( $langVarArray, $count )
// die( 'Error, unable to correlate variable language string' );
// }
// This is an example of how the function is used in the code which you can uncomment and
// This is an example of how the function is used in the code which you can uncomment and
// use to test your custom function.
//$monitors = array();
//$monitors[] = 1; // Choose any number
@ -970,17 +970,17 @@ $OLANG = array(
"\"reorder_queue_size=nnn\" Set number of packets to buffer for handling of reordered packets~~~~".
"\"loglevel=debug\" Set verbosity of FFmpeg (quiet, panic, fatal, error, warning, info, verbose, debug)"
),
'OPTIONS_RTSPTrans' => array(
'OPTIONS_RTSPTrans' => array(
'Help' => "This sets the RTSP Transport Protocol for FFmpeg.~~ ".
"TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~".
"UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some 'smearing' while using UDP, if so try TCP~~".
"UDP Multicast - Use UDP Multicast as transport protocol~~".
"HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~"
"TCP - Use TCP (interleaving within the RTSP control channel) as transport protocol.~~".
"UDP - Use UDP as transport protocol. Higher resolution cameras have experienced some 'smearing' while using UDP, if so try TCP~~".
"UDP Multicast - Use UDP Multicast as transport protocol~~".
"HTTP - Use HTTP tunneling as transport protocol, which is useful for passing proxies.~~"
),
'OPTIONS_LIBVLC' => array(
'Help' => "Parameters in this field are passed on to libVLC. Multiple parameters can be separated by ,~~ ".
"Examples (do not enter quotes)~~~~".
"\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~".
"\"--rtp-client-port=nnn\" Set local port to use for rtp data~~~~".
"\"--verbose=2\" Set verbosity of libVLC"
),
'OPTIONS_EXIF' => array(
@ -989,7 +989,7 @@ $OLANG = array(
'OPTIONS_RTSPDESCRIBE' => array(
'Help' => "Sometimes, during the initial RTSP handshake, the camera will send an updated media URL. ".
"Enable this option to tell ZoneMinder to use this URL. Disable this option to ignore the ".
"value from the camera and use the value as entered in the monitor configuration~~~~".
"value from the camera and use the value as entered in the monitor configuration~~~~".
"Generally this should be enabled. However, there are cases where the camera can get its".
"own URL incorrect, such as when the camera is streaming through a firewall"),
'OPTIONS_MAXFPS' => array(
@ -997,12 +997,12 @@ $OLANG = array(
"Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, ".
"and missed events~~".
"For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the".
" camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ".
"In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~".
" camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ".
"In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~".
"Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera ".
"for new images. In this case, it is safe to use the field."
),
// 'LANG_DEFAULT' => array(
// 'Prompt' => "This is a new prompt for this option",
// 'Help' => "This is some new help for this option which will be displayed in the popup window when the ? is clicked"

View File

@ -233,7 +233,12 @@ function getNavBarHTML($reload = null) {
ob_start();
if ( $running == null )
$running = daemonCheck();
$status = $running?translate('Running'):translate('Stopped');
if ( $running ) {
$state = dbFetchOne('SELECT Name FROM States WHERE isActive=1', 'Name');
if ( $state == 'default' )
$state = '';
}
$status = $running ? ($state ? $state : translate('Running')) : translate('Stopped');
?>
<div class="navbar navbar-inverse navbar-static-top">
<div class="container-fluid">
@ -244,7 +249,9 @@ function getNavBarHTML($reload = null) {
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<div class="navbar-brand"><a href="<?php echo validHtmlStr(ZM_HOME_URL); ?>" target="<?php echo validHtmlStr(ZM_WEB_TITLE); ?>"><?php echo ZM_HOME_CONTENT ?></a></div>
<div class="navbar-brand">
<a href="<?php echo validHtmlStr(ZM_HOME_URL); ?>" target="<?php echo validHtmlStr(ZM_WEB_TITLE); ?>"><?php echo ZM_HOME_CONTENT ?></a>
</div>
</div>
<div class="collapse navbar-collapse" id="main-header-nav">
@ -326,7 +333,7 @@ if (isset($_REQUEST['filter']['Query']['terms']['attr'])) {
<button type="button" class="btn btn-default navbar-btn" data-toggle="modal" data-target="#modalState"><?php echo $status ?></button>
<?php } else if ( canView('System') ) { ?>
<p class="navbar-text"> <?php echo $status ?></p>
<p class="navbar-text"><?php echo $status ?></p>
<?php } ?>
</div>
<?php } # end if !$user or $user['Id'] meaning logged in ?>

View File

@ -39,7 +39,7 @@ if ( ! empty($_REQUEST['mid']) ) {
$monitor = new ZM\Monitor( $_REQUEST['mid'] );
if ( $monitor and ZM_OPT_X10 )
$x10Monitor = dbFetchOne('SELECT * FROM TriggersX10 WHERE MonitorId = ?', NULL, array($_REQUEST['mid']));
}
}
if ( ! $monitor ) {
$nextId = getTableAutoInc('Monitors');
@ -132,9 +132,9 @@ if ( ! $monitor ) {
if ( ZM_OPT_X10 && empty($x10Monitor) ) {
$x10Monitor = array(
'Activation' => '',
'AlarmInput' => '',
'AlarmOutput' => '',
'Activation' => '',
'AlarmInput' => '',
'AlarmOutput' => '',
);
}
@ -166,7 +166,7 @@ if ( $monitor->AlarmMaxFPS() == '0.00' )
if ( !empty($_REQUEST['preset']) ) {
$preset = dbFetchOne( 'SELECT Type, Device, Channel, Format, Protocol, Method, Host, Port, Path, Width, Height, Palette, MaxFPS, Controllable, ControlId, ControlDevice, ControlAddress, DefaultRate, DefaultScale FROM MonitorPresets WHERE Id = ?', NULL, array($_REQUEST['preset']) );
foreach ( $preset as $name=>$value ) {
# Does isset handle NULL's? I don't think this code is correct.
# Does isset handle NULL's? I don't think this code is correct.
if ( isset($value) ) {
$monitor->$name = $value;
}
@ -176,7 +176,7 @@ if ( !empty($_REQUEST['probe']) ) {
$probe = json_decode(base64_decode($_REQUEST['probe']));
foreach ( $probe as $name=>$value ) {
if ( isset($value) ) {
# Does isset handle NULL's? I don't think this code is correct.
# Does isset handle NULL's? I don't think this code is correct.
$monitor->$name = urldecode($value);
}
}
@ -389,7 +389,7 @@ $orientations = array(
'90' => translate('RotateRight'),
'180' => translate('Inverted'),
'270' => translate('RotateLeft'),
'horz' => translate('FlippedHori'),
'hori' => translate('FlippedHori'),
'vert' => translate('FlippedVert')
);
@ -683,7 +683,7 @@ switch ( $tab ) {
?>
<tr class="Name"><td><?php echo translate('Name') ?></td><td><input type="text" name="newMonitor[Name]" value="<?php echo validHtmlStr($monitor->Name()) ?>" /></td></tr>
<tr><td><?php echo translate('Server') ?></td><td>
<?php
<?php
$servers = array(''=>'None','auto'=>'Auto');
foreach ( ZM\Server::find(NULL, array('order'=>'lower(Name)')) as $Server ) {
$servers[$Server->Id()] = $Server->Name();
@ -763,7 +763,7 @@ echo htmlOptions(ZM\Group::get_dropdown_options( ), $monitor->GroupIds() );
<td><?php echo translate('RefImageBlendPct') ?></td>
<td><select name="newMonitor[RefBlendPerc]"><?php foreach ( $fastblendopts as $name => $value ) { ?><option value="<?php echo $value ?>"<?php if ( $value == $monitor->RefBlendPerc() ) { ?> selected="selected"<?php } ?>><?php echo $name ?></option><?php } ?></select></td>
</tr>
<tr>
<tr>
<td><?php echo translate('AlarmRefImageBlendPct') ?></td>
<td>
<select name="newMonitor[AlarmRefBlendPerc]">
@ -840,7 +840,7 @@ echo htmlOptions(ZM\Group::get_dropdown_options( ), $monitor->GroupIds() );
</td></tr>
<tr><td><?php echo translate('V4LCapturesPerFrame') ?></td><td><input type="number" name="newMonitor[V4LCapturesPerFrame]" value="<?php echo validHtmlStr($monitor->V4LCapturesPerFrame()); ?>"/></td></tr>
<?php
} else if ( $monitor->Type() == 'NVSocket' ) {
include('_monitor_source_nvsocket.php');
} else if ( $monitor->Type() == 'Remote' ) {
@ -894,10 +894,10 @@ if ( $monitor->Type() != 'NVSocket' && $monitor->Type() != 'WebSite' ) {
</td></tr>
<tr><td><?php echo translate('CaptureWidth') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Width]" value="<?php echo validHtmlStr($monitor->Width()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
<tr><td><?php echo translate('CaptureHeight') ?> (<?php echo translate('Pixels') ?>)</td><td><input type="text" name="newMonitor[Height]" value="<?php echo validHtmlStr($monitor->Height()) ?>" size="4" onkeyup="updateMonitorDimensions(this);"/></td></tr>
<tr><td><?php echo translate('PreserveAspect') ?></td><td><input type="checkbox" name="preserveAspectRatio" value="1"/></td></tr>
<tr><td><?php echo translate('PreserveAspect') ?></td><td><input type="checkbox" name="preserveAspectRatio" value="1"/></td></tr>
<tr><td><?php echo translate('Orientation') ?></td><td><?php echo htmlselect( 'newMonitor[Orientation]', $orientations, $monitor->Orientation() );?></td></tr>
<?php
}
}
if ( $monitor->Type() == 'Local' ) {
?>
<tr><td><?php echo translate('Deinterlacing') ?></td><td><select name="newMonitor[Deinterlacing]"><?php foreach ( $deinterlaceopts_v4l2 as $name => $value ) { ?><option value="<?php echo validHtmlStr($value); ?>"<?php if ( $value == $monitor->Deinterlacing()) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($name); ?></option><?php } ?></select></td></tr>
@ -920,7 +920,7 @@ if ( $monitor->Type() == 'Local' ) {
?>
<tr><td><?php echo translate('SaveJPEGs') ?></td><td><select name="newMonitor[SaveJPEGs]"><?php foreach ( $savejpegopts as $name => $value ) { ?><option value="<?php echo validHtmlStr($value); ?>"<?php if ( $value == $monitor->SaveJPEGs() ) { ?> selected="selected"<?php } ?>><?php echo validHtmlStr($name); ?></option><?php } ?></select></td></tr>
<tr><td><?php echo translate('VideoWriter') ?></td><td>
<?php
<?php
$videowriteropts = array(
0 => 'Disabled',
);

View File

@ -54,7 +54,9 @@ if ( $running ) {
$states = dbFetchAll('SELECT * FROM States');
foreach ( $states as $state ) {
?>
<option value="<?php echo validHtmlStr($state['Name']) ?>"><?php echo validHtmlStr($state['Name']); ?></option>
<option value="<?php echo validHtmlStr($state['Name']) ?>" <?php echo $state['IsActive'] ? 'selected="selected"' : '' ?>>
<?php echo validHtmlStr($state['Name']); ?>
</option>
<?php
}
?>