Skip to content

Commit

Permalink
FIFO support for zoneminder zone debugging (ZoneMinder#2594)
Browse files Browse the repository at this point in the history
Adds fifo options for diagnostic images for much lower impact diagnostics mode.  Diagnostic images are only written when there is a client listening for them (otherwise they are skipped).  Also added a json stream for the detection data so you can see in real time the pixels or blobs detected for the motion.  This allows for easy real time stream of both delta and reference images (as video streams) along with the detection numbers.
  • Loading branch information
mitchcapper authored and connortechnology committed May 16, 2019
1 parent 4f44db8 commit eb005e8
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 21 deletions.
10 changes: 9 additions & 1 deletion scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in
Original file line number Diff line number Diff line change
Expand Up @@ -3945,7 +3945,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;
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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_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)
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_fifo.cpp zm_storage.cpp)

# A fix for cmake recompiling the source files for every target.
add_library(zm STATIC ${ZM_BIN_SRC_FILES})
Expand Down
247 changes: 247 additions & 0 deletions src/zm_fifo.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions src/zm_fifo.h
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit eb005e8

Please sign in to comment.