2019-06-27 22:20:23 +08:00
|
|
|
//
|
|
|
|
// ZoneMinder Fifo Debug
|
|
|
|
// Copyright (C) 2019 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
//
|
|
|
|
|
2019-05-17 03:37:03 +08:00
|
|
|
#include <fcntl.h>
|
2019-06-27 22:20:23 +08:00
|
|
|
#include <sys/file.h>
|
2019-05-17 03:37:03 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <signal.h>
|
2019-06-27 22:20:23 +08:00
|
|
|
|
2019-05-17 03:37:03 +08:00
|
|
|
#include "zm.h"
|
|
|
|
#include "zm_time.h"
|
|
|
|
#include "zm_signal.h"
|
|
|
|
#include "zm_monitor.h"
|
|
|
|
#include "zm_fifo.h"
|
|
|
|
#define RAW_BUFFER 512
|
|
|
|
static bool zm_fifodbg_inited = false;
|
2020-08-26 07:45:48 +08:00
|
|
|
FILE *zm_fifodbg_log_fd = nullptr;
|
2019-05-17 03:37:03 +08:00
|
|
|
char zm_fifodbg_log[PATH_MAX] = "";
|
|
|
|
|
2019-06-27 22:20:23 +08:00
|
|
|
static bool zmFifoDbgOpen() {
|
|
|
|
if ( zm_fifodbg_log_fd )
|
|
|
|
fclose(zm_fifodbg_log_fd);
|
2020-08-26 07:45:48 +08:00
|
|
|
zm_fifodbg_log_fd = nullptr;
|
2019-06-27 22:20:23 +08:00
|
|
|
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");
|
2020-08-26 07:45:48 +08:00
|
|
|
if ( zm_fifodbg_log_fd == nullptr ) {
|
2019-06-27 22:20:23 +08:00
|
|
|
close(fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|
2019-06-27 22:20:23 +08:00
|
|
|
|
|
|
|
int zmFifoDbgInit(Monitor *monitor) {
|
|
|
|
zm_fifodbg_inited = true;
|
2020-11-14 06:05:03 +08:00
|
|
|
snprintf(zm_fifodbg_log, sizeof(zm_fifodbg_log), "%s/dbgpipe-%d.log",
|
|
|
|
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
2019-06-27 22:20:23 +08:00
|
|
|
zmFifoDbgOpen();
|
|
|
|
return 1;
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|
2019-06-27 22:20:23 +08:00
|
|
|
|
|
|
|
void zmFifoDbgOutput(
|
|
|
|
int hex,
|
|
|
|
const char * const file,
|
|
|
|
const int line,
|
|
|
|
const int level,
|
|
|
|
const char *fstring,
|
|
|
|
...
|
|
|
|
) {
|
|
|
|
char dbg_string[8192];
|
|
|
|
int str_size = sizeof(dbg_string);
|
|
|
|
|
|
|
|
va_list arg_ptr;
|
|
|
|
if ( (!zm_fifodbg_inited) || ( !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);
|
|
|
|
dbg_ptr += snprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), "%d:", len);
|
|
|
|
for ( int i = 0; i < len; i++ ) {
|
|
|
|
dbg_ptr += snprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), " %02x", data[i]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dbg_ptr += vsnprintf(dbg_ptr, str_size-(dbg_ptr-dbg_string), fstring, arg_ptr);
|
|
|
|
}
|
|
|
|
va_end(arg_ptr);
|
2019-06-27 22:24:51 +08:00
|
|
|
strncpy(dbg_ptr++, "\n", 2);
|
2019-06-27 22:20:23 +08:00
|
|
|
int res = fwrite(dbg_string, dbg_ptr-dbg_string, 1, zm_fifodbg_log_fd);
|
|
|
|
if ( res != 1 ) {
|
|
|
|
fclose(zm_fifodbg_log_fd);
|
2020-08-26 07:45:48 +08:00
|
|
|
zm_fifodbg_log_fd = nullptr;
|
2019-06-27 22:20:23 +08:00
|
|
|
} else {
|
|
|
|
fflush(zm_fifodbg_log_fd);
|
|
|
|
}
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|
2019-06-27 22:20:23 +08:00
|
|
|
|
|
|
|
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;
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|
|
|
|
|
2019-06-27 22:20:23 +08:00
|
|
|
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);
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|
2019-06-27 22:20:23 +08:00
|
|
|
|
|
|
|
void FifoStream::fifo_create_if_missing(
|
|
|
|
const char * path,
|
|
|
|
bool delete_fake_fifo
|
|
|
|
) {
|
|
|
|
file_create_if_missing(path, true, delete_fake_fifo);
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|
2019-06-27 22:20:23 +08:00
|
|
|
|
|
|
|
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) || (frame_count%frame_mod != 0) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if ( fprintf(stdout,
|
2020-11-21 05:29:10 +08:00
|
|
|
"--" BOUNDARY "\r\n"
|
2019-06-27 22:20:23 +08:00
|
|
|
"Content-Type: image/jpeg\r\n"
|
|
|
|
"Content-Length: %d\r\n\r\n",
|
|
|
|
total_read) < 0 ) {
|
|
|
|
Error("Problem during writing: %s", strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|
|
|
|
|
2019-06-27 22:20:23 +08:00
|
|
|
void FifoStream::setStreamStart(const char * path) {
|
|
|
|
stream_path = strdup(path);
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|
2019-06-27 22:20:23 +08:00
|
|
|
|
|
|
|
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") ) {
|
2020-11-14 06:05:03 +08:00
|
|
|
snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-r-%d.jpg",
|
|
|
|
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
2020-11-21 05:29:10 +08:00
|
|
|
stream_type = MJPEG;
|
2019-06-27 22:20:23 +08:00
|
|
|
} else if ( !strcmp(format, "delta") ) {
|
2020-11-14 06:05:03 +08:00
|
|
|
snprintf(diag_path, sizeof(diag_path), "%s/diagpipe-d-%d.jpg",
|
|
|
|
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
2019-06-27 22:20:23 +08:00
|
|
|
stream_type = MJPEG;
|
|
|
|
} else {
|
2020-11-14 06:05:03 +08:00
|
|
|
snprintf(diag_path, sizeof(diag_path), "%s/dbgpipe-%d.log",
|
|
|
|
staticConfig.PATH_SOCKS.c_str(), monitor->Id());
|
2019-06-27 22:20:23 +08:00
|
|
|
stream_type = RAW;
|
|
|
|
}
|
|
|
|
|
|
|
|
setStreamStart(diag_path);
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|
2019-06-27 22:20:23 +08:00
|
|
|
|
|
|
|
void FifoStream::runStream() {
|
|
|
|
if ( stream_type == MJPEG ) {
|
2020-11-21 05:29:10 +08:00
|
|
|
fprintf(stdout, "Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n\r\n");
|
2019-06-27 22:20:23 +08:00
|
|
|
} else {
|
|
|
|
fprintf(stdout, "Content-Type: text/html\r\n\r\n");
|
|
|
|
}
|
|
|
|
|
2020-11-21 05:29:10 +08:00
|
|
|
/* only 1 person can read from a fifo at a time, so use a lock */
|
2019-06-27 22:20:23 +08:00
|
|
|
char lock_file[PATH_MAX];
|
|
|
|
snprintf(lock_file, sizeof(lock_file), "%s.rlock", stream_path);
|
|
|
|
file_create_if_missing(lock_file, false);
|
2020-11-21 05:29:10 +08:00
|
|
|
Debug(1, "Locking %s", lock_file);
|
2019-06-27 22:20:23 +08:00
|
|
|
|
|
|
|
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);
|
2020-11-21 05:29:10 +08:00
|
|
|
while ( (res == EAGAIN) and (! zm_terminate) ) {
|
|
|
|
Warning("Flocking problem on %s: - %s", lock_file, strerror(errno));
|
|
|
|
sleep(1);
|
|
|
|
res = flock(fd_lock, LOCK_EX | LOCK_NB);
|
|
|
|
}
|
2019-06-27 22:20:23 +08:00
|
|
|
if ( res < 0 ) {
|
|
|
|
Error("Flocking problem on %s: - %s", lock_file, strerror(errno));
|
|
|
|
close(fd_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ( !zm_terminate ) {
|
2020-08-26 07:45:48 +08:00
|
|
|
gettimeofday(&now, nullptr);
|
2019-06-27 22:20:23 +08:00
|
|
|
checkCommandQueue();
|
|
|
|
if ( stream_type == MJPEG ) {
|
|
|
|
if ( !sendMJEGFrames() )
|
|
|
|
zm_terminate = true;
|
|
|
|
} else {
|
|
|
|
if ( !sendRAWFrames() )
|
|
|
|
zm_terminate = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(fd_lock);
|
2019-05-17 03:37:03 +08:00
|
|
|
}
|