zoneminder/src/zmc.cpp

361 lines
11 KiB
C++
Raw Normal View History

//
// ZoneMinder Capture Daemon, $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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
2015-06-21 06:11:20 +08:00
/*
=head1 NAME
zmc - The ZoneMinder Capture daemon
=head1 SYNOPSIS
zmc -d <device_path>
zmc --device <device_path>
zmc -f <file_path>
zmc --file <file_path>
zmc -m <monitor_id>
zmc --monitor <monitor_id>
zmc -h
zmc --help
zmc -v
zmc --version
=head1 DESCRIPTION
This binary's job is to sit on a video device and suck frames off it as fast as
possible, this should run at more or less constant speed.
=head1 OPTIONS
-d, --device <device_path> - For local cameras, device to access. e.g /dev/video0 etc
-f, --file <file_path> - For local images, jpg file to access.
-m, --monitor_id - ID of the monitor to analyse
-h, --help - Display usage information
-v, --version - Print the installed version of ZoneMinder
2015-06-21 06:11:20 +08:00
=cut
*/
#include <getopt.h>
#include <signal.h>
2015-02-24 22:22:55 +08:00
#if defined(__FreeBSD__)
#include <limits.h>
2015-01-04 03:07:18 +08:00
#else
#include <values.h>
#endif
2015-02-24 22:22:55 +08:00
#if !defined(MAXINT)
#define MAXINT INT_MAX
#endif
#include "zm.h"
#include "zm_db.h"
#include "zm_time.h"
#include "zm_signal.h"
#include "zm_monitor.h"
2017-03-31 23:12:52 +08:00
void Usage() {
2017-09-05 03:28:23 +08:00
fprintf(stderr, "zmc -d <device_path> or -r <proto> -H <host> -P <port> -p <path> or -f <file_path> or -m <monitor_id>\n");
2017-09-05 03:28:23 +08:00
fprintf(stderr, "Options:\n");
#if defined(BSD)
2017-09-05 03:28:23 +08:00
fprintf(stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/bktr0 etc\n");
#else
2017-09-05 03:28:23 +08:00
fprintf(stderr, " -d, --device <device_path> : For local cameras, device to access. E.g /dev/video0 etc\n");
#endif
2017-09-05 03:28:23 +08:00
fprintf(stderr, " -f, --file <file_path> : For local images, jpg file to access.\n");
fprintf(stderr, " -m, --monitor <monitor_id> : For sources associated with a single monitor\n");
fprintf(stderr, " -h, --help : This screen\n");
fprintf(stderr, " -v, --version : Report the installed version of ZoneMinder\n");
exit(0);
}
2017-09-05 03:28:23 +08:00
int main(int argc, char *argv[]) {
self = argv[0];
2017-09-05 03:28:23 +08:00
srand(getpid() * time(0));
const char *device = "";
const char *protocol = "";
const char *host = "";
const char *port = "";
const char *path = "";
const char *file = "";
int monitor_id = -1;
static struct option long_options[] = {
{"device", 1, 0, 'd'},
{"protocol", 1, 0, 'r'},
{"host", 1, 0, 'H'},
{"port", 1, 0, 'P'},
2017-03-31 23:12:52 +08:00
{"path", 1, 0, 'p'},
{"file", 1, 0, 'f'},
{"monitor", 1, 0, 'm'},
{"help", 0, 0, 'h'},
{"version", 0, 0, 'v'},
{0, 0, 0, 0}
};
2017-03-31 23:12:52 +08:00
while (1) {
int option_index = 0;
2017-09-05 03:28:23 +08:00
int c = getopt_long(argc, argv, "d:H:P:p:f:m:h:v", long_options, &option_index);
if ( c == -1 ) {
break;
}
2017-09-05 03:28:23 +08:00
switch (c) {
case 'd':
device = optarg;
break;
case 'H':
host = optarg;
break;
case 'P':
port = optarg;
break;
case 'p':
path = optarg;
break;
case 'f':
file = optarg;
break;
case 'm':
monitor_id = atoi(optarg);
break;
case 'h':
case '?':
Usage();
break;
case 'v':
std::cout << ZM_VERSION << "\n";
exit(0);
default:
//fprintf( stderr, "?? getopt returned character code 0%o ??\n", c );
break;
}
}
2017-09-05 03:28:23 +08:00
if ( optind < argc ) {
fprintf(stderr, "Extraneous options, ");
while ( optind < argc )
printf("%s ", argv[optind++]);
printf("\n");
Usage();
}
2017-11-13 04:18:39 +08:00
int modes = ( (device[0]?1:0) + (host[0]?1:0) + (file[0]?1:0) + (monitor_id > 0 ? 1 : 0));
2017-03-31 23:12:52 +08:00
if ( modes > 1 ) {
2017-09-05 03:28:23 +08:00
fprintf(stderr, "Only one of device, host/port/path, file or monitor id allowed\n");
Usage();
2017-09-05 03:28:23 +08:00
exit(0);
}
2017-03-31 23:12:52 +08:00
if ( modes < 1 ) {
2017-09-05 03:28:23 +08:00
fprintf(stderr, "One of device, host/port/path, file or monitor id must be specified\n");
Usage();
2017-09-05 03:28:23 +08:00
exit(0);
}
char log_id_string[32] = "";
2017-03-31 23:12:52 +08:00
if ( device[0] ) {
2017-09-05 03:28:23 +08:00
const char *slash_ptr = strrchr(device, '/');
snprintf(log_id_string, sizeof(log_id_string), "zmc_d%s", slash_ptr?slash_ptr+1:device);
2017-03-31 23:12:52 +08:00
} else if ( host[0] ) {
2017-09-05 03:28:23 +08:00
snprintf(log_id_string, sizeof(log_id_string), "zmc_h%s", host);
2017-03-31 23:12:52 +08:00
} else if ( file[0] ) {
2017-09-05 03:28:23 +08:00
const char *slash_ptr = strrchr(file, '/');
snprintf(log_id_string, sizeof(log_id_string), "zmc_f%s", slash_ptr?slash_ptr+1:file);
2017-03-31 23:12:52 +08:00
} else {
2017-09-05 03:28:23 +08:00
snprintf(log_id_string, sizeof(log_id_string), "zmc_m%d", monitor_id);
}
zmLoadConfig();
2017-09-05 03:28:23 +08:00
logInit(log_id_string);
hwcaps_detect();
Monitor **monitors = 0;
int n_monitors = 0;
#if ZM_HAS_V4L
2017-03-31 23:12:52 +08:00
if ( device[0] ) {
2017-09-05 03:28:23 +08:00
n_monitors = Monitor::LoadLocalMonitors(device, monitors, Monitor::CAPTURE);
2017-03-31 23:12:52 +08:00
} else
#endif // ZM_HAS_V4L
2017-03-31 23:12:52 +08:00
if ( host[0] ) {
if ( !port )
port = "80";
2017-09-05 03:28:23 +08:00
n_monitors = Monitor::LoadRemoteMonitors(protocol, host, port, path, monitors, Monitor::CAPTURE);
2017-03-31 23:12:52 +08:00
} else if ( file[0] ) {
2017-09-05 03:28:23 +08:00
n_monitors = Monitor::LoadFileMonitors(file, monitors, Monitor::CAPTURE);
2017-03-31 23:12:52 +08:00
} else {
2017-09-05 03:28:23 +08:00
Monitor *monitor = Monitor::Load(monitor_id, true, Monitor::CAPTURE);
2017-03-31 23:12:52 +08:00
if ( monitor ) {
monitors = new Monitor *[1];
monitors[0] = monitor;
n_monitors = 1;
}
}
2017-03-31 23:12:52 +08:00
if ( !n_monitors ) {
2017-09-05 03:28:23 +08:00
Error("No monitors found");
exit(-1);
}
2017-09-05 03:28:23 +08:00
Info("Starting Capture version %s", ZM_VERSION);
zmSetDefaultTermHandler();
zmSetDefaultDieHandler();
sigset_t block_set;
2017-09-05 03:28:23 +08:00
sigemptyset(&block_set);
2017-09-05 03:28:23 +08:00
sigaddset(&block_set, SIGUSR1);
sigaddset(&block_set, SIGUSR2);
int result = 0;
while( ! zm_terminate ) {
2018-02-03 05:07:13 +08:00
result = 0;
static char sql[ZM_SQL_SML_BUFSIZ];
for ( int i = 0; i < n_monitors; i ++ ) {
time_t now = (time_t)time(NULL);
monitors[i]->setStartupTime(now);
snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Running')", monitors[i]->Id() );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
}
}
// Outer primary loop, handles connection to camera
if ( monitors[0]->PrimeCapture() < 0 ) {
Error("Failed to prime capture of initial monitor");
sleep(10);
continue;
}
for ( int i = 0; i < n_monitors; i ++ ) {
snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','Connected')", monitors[i]->Id() );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
}
}
long *capture_delays = new long[n_monitors];
long *alarm_capture_delays = new long[n_monitors];
long *next_delays = new long[n_monitors];
struct timeval * last_capture_times = new struct timeval[n_monitors];
2017-03-31 23:12:52 +08:00
for ( int i = 0; i < n_monitors; i++ ) {
last_capture_times[i].tv_sec = last_capture_times[i].tv_usec = 0;
capture_delays[i] = monitors[i]->GetCaptureDelay();
alarm_capture_delays[i] = monitors[i]->GetAlarmCaptureDelay();
}
struct timeval now;
struct DeltaTimeval delta_time;
while ( !zm_terminate ) {
sigprocmask(SIG_BLOCK, &block_set, 0);
for ( int i = 0; i < n_monitors; i++ ) {
long min_delay = MAXINT;
gettimeofday(&now, NULL);
for ( int j = 0; j < n_monitors; j++ ) {
if ( last_capture_times[j].tv_sec ) {
DELTA_TIMEVAL(delta_time, now, last_capture_times[j], DT_PREC_3);
if ( monitors[i]->GetState() == Monitor::ALARM )
next_delays[j] = alarm_capture_delays[j]-delta_time.delta;
else
next_delays[j] = capture_delays[j]-delta_time.delta;
if ( next_delays[j] < 0 )
next_delays[j] = 0;
} else {
next_delays[j] = 0;
}
if ( next_delays[j] <= min_delay ) {
min_delay = next_delays[j];
}
} // end foreach monitor
if ( next_delays[i] <= min_delay || next_delays[i] <= 0 ) {
if ( monitors[i]->PreCapture() < 0 ) {
Error("Failed to pre-capture monitor %d %d (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
result = -1;
break;
}
if ( monitors[i]->Capture() < 0 ) {
Error("Failed to capture image from monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
result = -1;
break;
}
if ( monitors[i]->PostCapture() < 0 ) {
Error("Failed to post-capture monitor %d %s (%d/%d)", monitors[i]->Id(), monitors[i]->Name(), i+1, n_monitors);
result = -1;
break;
}
if ( next_delays[i] > 0 ) {
gettimeofday(&now, NULL);
DELTA_TIMEVAL(delta_time, now, last_capture_times[i], DT_PREC_3);
long sleep_time = next_delays[i]-delta_time.delta;
if ( sleep_time > 0 ) {
usleep(sleep_time*(DT_MAXGRAN/DT_PREC_3));
}
}
gettimeofday(&(last_capture_times[i]), NULL);
} // end if next_delay <= min_delay || next_delays[i] <= 0 )
} // end foreach n_monitors
sigprocmask(SIG_UNBLOCK, &block_set, 0);
if ( zm_reload ) {
for ( int i = 0; i < n_monitors; i++ ) {
monitors[i]->Reload();
}
logTerm();
logInit( log_id_string );
zm_reload = false;
2018-01-11 01:59:42 +08:00
}
if ( result < 0 ) {
// Failure, try reconnecting
break;
}
} // end while ! zm_terminate
delete [] alarm_capture_delays;
delete [] capture_delays;
delete [] next_delays;
delete [] last_capture_times;
sleep(10);
} // end while ! zm_terminate outer connection loop
2017-03-31 23:12:52 +08:00
for ( int i = 0; i < n_monitors; i++ ) {
static char sql[ZM_SQL_SML_BUFSIZ];
snprintf( sql, sizeof(sql), "REPLACE INTO Monitor_Status (MonitorId, Status) VALUES ('%d','NotRunning')", monitors[i]->Id() );
if ( mysql_query( &dbconn, sql ) ) {
Error( "Can't run query: %s", mysql_error( &dbconn ) );
}
delete monitors[i];
}
delete [] monitors;
Image::Deinitialise();
logTerm();
zmDbClose();
return result;
}