2017-06-23 05:58:32 +08:00
|
|
|
//
|
|
|
|
// ZoneMinder Event Class Implementation, $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.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <sys/ipc.h>
|
|
|
|
#include <sys/msg.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <glob.h>
|
2018-04-25 01:16:19 +08:00
|
|
|
#include <cinttypes>
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
#include "zm.h"
|
|
|
|
#include "zm_db.h"
|
|
|
|
#include "zm_time.h"
|
|
|
|
#include "zm_mpeg.h"
|
|
|
|
#include "zm_signal.h"
|
|
|
|
#include "zm_event.h"
|
|
|
|
#include "zm_eventstream.h"
|
2017-06-27 04:55:49 +08:00
|
|
|
#include "zm_storage.h"
|
2017-06-22 22:12:04 +08:00
|
|
|
#include "zm_monitor.h"
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
#include "zm_sendfile.h"
|
|
|
|
|
2019-07-26 22:53:48 +08:00
|
|
|
bool EventStream::loadInitialEventData(int monitor_id, time_t event_time) {
|
2017-06-23 05:58:32 +08:00
|
|
|
static char sql[ZM_SQL_SML_BUFSIZ];
|
|
|
|
|
2019-08-15 04:18:21 +08:00
|
|
|
snprintf(sql, sizeof(sql), "SELECT `Id` FROM `Events` WHERE "
|
|
|
|
"`MonitorId` = %d AND unix_timestamp(`EndTime`) > %ld "
|
|
|
|
"ORDER BY `Id` ASC LIMIT 1", monitor_id, event_time);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-04-13 01:14:00 +08:00
|
|
|
if ( mysql_query(&dbconn, sql) ) {
|
|
|
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-04-13 01:14:00 +08:00
|
|
|
MYSQL_RES *result = mysql_store_result(&dbconn);
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( !result ) {
|
2018-04-13 01:14:00 +08:00
|
|
|
Error("Can't use query result: %s", mysql_error(&dbconn));
|
2019-01-17 00:20:10 +08:00
|
|
|
exit(mysql_errno(&dbconn));
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
2018-04-13 01:14:00 +08:00
|
|
|
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-04-13 01:14:00 +08:00
|
|
|
if ( mysql_errno(&dbconn) ) {
|
|
|
|
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
2019-01-17 00:20:10 +08:00
|
|
|
exit(mysql_errno(&dbconn));
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-04-18 01:51:20 +08:00
|
|
|
uint64_t init_event_id = atoll(dbrow[0]);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-04-13 01:14:00 +08:00
|
|
|
mysql_free_result(result);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-04-13 01:14:00 +08:00
|
|
|
loadEventData(init_event_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
if ( event_time ) {
|
|
|
|
curr_stream_time = event_time;
|
2018-11-20 05:45:56 +08:00
|
|
|
curr_frame_id = 1; // curr_frame_id is 1-based
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( event_time >= event_data->start_time ) {
|
2018-11-20 05:45:56 +08:00
|
|
|
Debug(2, "event time is after event start");
|
2020-03-27 00:06:07 +08:00
|
|
|
for ( unsigned int i = 0; i < event_data->frame_count; i++ ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
//Info( "eft %d > et %d", event_data->frames[i].timestamp, event_time );
|
|
|
|
if ( event_data->frames[i].timestamp >= event_time ) {
|
|
|
|
curr_frame_id = i+1;
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(3, "Set curr_stream_time:%.2f, curr_frame_id:%d", curr_stream_time, curr_frame_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
}
|
2019-05-28 05:15:46 +08:00
|
|
|
} // end foreach frame
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(3, "Skipping %ld frames", event_data->frame_count);
|
2019-05-28 05:15:46 +08:00
|
|
|
} else {
|
|
|
|
Warning("Requested an event time less than the start of the event. event_time %.2f < start_time %.2f",
|
|
|
|
event_time, event_data->start_time);
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
2019-05-28 05:15:46 +08:00
|
|
|
} // end if have a start time
|
2018-04-13 04:40:11 +08:00
|
|
|
return true;
|
2019-05-28 05:15:46 +08:00
|
|
|
} // bool EventStream::loadInitialEventData( int monitor_id, time_t event_time )
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-07-26 22:53:48 +08:00
|
|
|
bool EventStream::loadInitialEventData(uint64_t init_event_id, unsigned int init_frame_id) {
|
2018-03-03 10:24:39 +08:00
|
|
|
loadEventData(init_event_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
if ( init_frame_id ) {
|
2018-03-03 10:24:39 +08:00
|
|
|
if ( init_frame_id >= event_data->frame_count ) {
|
2019-01-17 00:20:10 +08:00
|
|
|
Error("Invalid frame id specified. %d > %d", init_frame_id, event_data->frame_count);
|
2018-03-03 10:24:39 +08:00
|
|
|
curr_stream_time = event_data->start_time;
|
2020-01-08 03:49:58 +08:00
|
|
|
curr_frame_id = 1;
|
2018-03-03 10:24:39 +08:00
|
|
|
} else {
|
|
|
|
curr_stream_time = event_data->frames[init_frame_id-1].timestamp;
|
|
|
|
curr_frame_id = init_frame_id;
|
|
|
|
}
|
2017-06-23 05:58:32 +08:00
|
|
|
} else {
|
|
|
|
curr_stream_time = event_data->start_time;
|
|
|
|
}
|
|
|
|
|
2018-03-03 10:24:39 +08:00
|
|
|
return true;
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-04-18 01:51:20 +08:00
|
|
|
bool EventStream::loadEventData(uint64_t event_id) {
|
2017-06-23 05:58:32 +08:00
|
|
|
static char sql[ZM_SQL_MED_BUFSIZ];
|
|
|
|
|
2018-11-07 00:45:34 +08:00
|
|
|
snprintf(sql, sizeof(sql),
|
2019-08-15 04:18:21 +08:00
|
|
|
"SELECT `MonitorId`, `StorageId`, `Frames`, unix_timestamp( `StartTime` ) AS StartTimestamp, "
|
2019-08-16 22:24:51 +08:00
|
|
|
"(SELECT max(`Delta`)-min(`Delta`) FROM `Frames` WHERE `EventId`=`Events`.`Id`) AS Duration, "
|
2020-02-11 05:22:01 +08:00
|
|
|
"`DefaultVideo`, `Scheme`, `SaveJPEGs`, `Orientation`+0 FROM `Events` WHERE `Id` = %" PRIu64, event_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-03-03 10:24:39 +08:00
|
|
|
if ( mysql_query(&dbconn, sql) ) {
|
|
|
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-03-03 10:24:39 +08:00
|
|
|
MYSQL_RES *result = mysql_store_result(&dbconn);
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( !result ) {
|
2018-03-03 10:24:39 +08:00
|
|
|
Error("Can't use query result: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-03-03 10:24:39 +08:00
|
|
|
if ( !mysql_num_rows(result) ) {
|
|
|
|
Fatal("Unable to load event %d, not found in DB", event_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-03-03 10:24:39 +08:00
|
|
|
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-03-03 10:24:39 +08:00
|
|
|
if ( mysql_errno(&dbconn) ) {
|
|
|
|
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
delete event_data;
|
|
|
|
event_data = new EventData;
|
|
|
|
event_data->event_id = event_id;
|
2017-06-26 23:20:10 +08:00
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
event_data->monitor_id = atoi(dbrow[0]);
|
|
|
|
event_data->storage_id = dbrow[1] ? atoi(dbrow[1]) : 0;
|
2017-06-27 04:55:49 +08:00
|
|
|
event_data->frame_count = dbrow[2] == NULL ? 0 : atoi(dbrow[2]);
|
2017-06-23 05:58:32 +08:00
|
|
|
event_data->start_time = atoi(dbrow[3]);
|
2019-01-16 00:25:22 +08:00
|
|
|
event_data->duration = dbrow[4] ? atof(dbrow[4]) : 0.0;
|
2019-01-17 00:20:10 +08:00
|
|
|
strncpy(event_data->video_file, dbrow[5], sizeof(event_data->video_file)-1);
|
2017-12-20 00:01:03 +08:00
|
|
|
std::string scheme_str = std::string(dbrow[6]);
|
|
|
|
if ( scheme_str == "Deep" ) {
|
|
|
|
event_data->scheme = Storage::DEEP;
|
|
|
|
} else if ( scheme_str == "Medium" ) {
|
|
|
|
event_data->scheme = Storage::MEDIUM;
|
|
|
|
} else {
|
|
|
|
event_data->scheme = Storage::SHALLOW;
|
|
|
|
}
|
2018-11-07 00:45:34 +08:00
|
|
|
event_data->SaveJPEGs = dbrow[7] == NULL ? 0 : atoi(dbrow[7]);
|
2020-02-11 05:22:01 +08:00
|
|
|
event_data->Orientation = (Monitor::Orientation)(dbrow[8] == NULL ? 0 : atoi(dbrow[8]));
|
2019-07-26 22:53:48 +08:00
|
|
|
mysql_free_result(result);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2020-04-27 06:19:30 +08:00
|
|
|
if ( !monitor ) {
|
|
|
|
monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY);
|
|
|
|
} else if ( monitor->Id() != event_data->monitor_id ) {
|
|
|
|
delete monitor;
|
|
|
|
monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY);
|
|
|
|
}
|
|
|
|
if ( !monitor ) {
|
|
|
|
Fatal("Unable to load monitor id %d for streaming", event_data->monitor_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !storage ) {
|
|
|
|
storage = new Storage(event_data->storage_id);
|
|
|
|
} else if ( storage->Id() != event_data->storage_id ) {
|
|
|
|
delete storage;
|
|
|
|
storage = new Storage(event_data->storage_id);
|
|
|
|
}
|
2017-06-23 05:58:32 +08:00
|
|
|
const char *storage_path = storage->Path();
|
|
|
|
|
2017-12-20 00:01:03 +08:00
|
|
|
if ( event_data->scheme == Storage::DEEP ) {
|
2018-04-04 03:23:38 +08:00
|
|
|
struct tm *event_time = localtime(&event_data->start_time);
|
2017-06-26 23:20:10 +08:00
|
|
|
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( storage_path[0] == '/' )
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(event_data->path, sizeof(event_data->path),
|
|
|
|
"%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d",
|
2018-09-12 01:20:41 +08:00
|
|
|
storage_path, event_data->monitor_id,
|
|
|
|
event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday,
|
2019-01-17 00:20:10 +08:00
|
|
|
event_time->tm_hour, event_time->tm_min, event_time->tm_sec);
|
2017-06-23 05:58:32 +08:00
|
|
|
else
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(event_data->path, sizeof(event_data->path),
|
|
|
|
"%s/%s/%ld/%02d/%02d/%02d/%02d/%02d/%02d",
|
2018-09-12 01:20:41 +08:00
|
|
|
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
|
|
|
event_time->tm_year-100, event_time->tm_mon+1, event_time->tm_mday,
|
2019-01-17 00:20:10 +08:00
|
|
|
event_time->tm_hour, event_time->tm_min, event_time->tm_sec);
|
2017-12-20 00:01:03 +08:00
|
|
|
} else if ( event_data->scheme == Storage::MEDIUM ) {
|
2019-01-17 00:20:10 +08:00
|
|
|
struct tm *event_time = localtime(&event_data->start_time);
|
2017-12-20 00:01:03 +08:00
|
|
|
if ( storage_path[0] == '/' )
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(event_data->path, sizeof(event_data->path),
|
|
|
|
"%s/%ld/%04d-%02d-%02d/%" PRIu64,
|
2018-09-12 01:20:41 +08:00
|
|
|
storage_path, event_data->monitor_id,
|
|
|
|
event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday,
|
2019-01-17 00:20:10 +08:00
|
|
|
event_data->event_id);
|
2017-12-20 00:01:03 +08:00
|
|
|
else
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(event_data->path, sizeof(event_data->path),
|
|
|
|
"%s/%s/%ld/%04d-%02d-%02d/%" PRIu64,
|
2018-09-12 01:20:41 +08:00
|
|
|
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
|
|
|
event_time->tm_year+1900, event_time->tm_mon+1, event_time->tm_mday,
|
2019-01-17 00:20:10 +08:00
|
|
|
event_data->event_id);
|
2017-06-26 23:20:10 +08:00
|
|
|
|
2017-06-23 05:58:32 +08:00
|
|
|
} else {
|
|
|
|
if ( storage_path[0] == '/' )
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(event_data->path, sizeof(event_data->path), "%s/%ld/%" PRIu64,
|
|
|
|
storage_path, event_data->monitor_id, event_data->event_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
else
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(event_data->path, sizeof(event_data->path), "%s/%s/%ld/%" PRIu64,
|
|
|
|
staticConfig.PATH_WEB.c_str(), storage_path, event_data->monitor_id,
|
|
|
|
event_data->event_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
updateFrameRate((double)event_data->frame_count/event_data->duration);
|
2019-07-26 22:53:48 +08:00
|
|
|
Debug(3, "fps set by frame_count(%d)/duration(%f)",
|
|
|
|
event_data->frame_count, event_data->duration);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-08-15 04:18:21 +08:00
|
|
|
snprintf(sql, sizeof(sql), "SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` "
|
|
|
|
"FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id);
|
2018-04-04 03:23:38 +08:00
|
|
|
if ( mysql_query(&dbconn, sql) ) {
|
|
|
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-04-04 03:23:38 +08:00
|
|
|
result = mysql_store_result(&dbconn);
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( !result ) {
|
2018-04-04 03:23:38 +08:00
|
|
|
Error("Can't use query result: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-04-04 03:23:38 +08:00
|
|
|
event_data->n_frames = mysql_num_rows(result);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
event_data->frames = new FrameData[event_data->frame_count];
|
2017-11-17 20:52:26 +08:00
|
|
|
int last_id = 0;
|
2018-09-12 01:20:41 +08:00
|
|
|
double last_timestamp = event_data->start_time;
|
2017-11-17 20:52:26 +08:00
|
|
|
double last_delta = 0.0;
|
2018-09-12 01:20:41 +08:00
|
|
|
|
2019-07-26 22:53:48 +08:00
|
|
|
while ( ( dbrow = mysql_fetch_row(result) ) ) {
|
2017-11-17 20:52:26 +08:00
|
|
|
int id = atoi(dbrow[0]);
|
2018-09-12 01:20:41 +08:00
|
|
|
//timestamp = atof(dbrow[1]);
|
2017-11-17 20:52:26 +08:00
|
|
|
double delta = atof(dbrow[2]);
|
2017-06-23 05:58:32 +08:00
|
|
|
int id_diff = id - last_id;
|
2019-03-07 03:50:36 +08:00
|
|
|
double frame_delta = id_diff ? (delta-last_delta)/id_diff : (delta-last_delta);
|
2018-09-12 01:20:41 +08:00
|
|
|
// Fill in data between bulk frames
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( id_diff > 1 ) {
|
|
|
|
for ( int i = last_id+1; i < id; i++ ) {
|
2018-09-12 01:20:41 +08:00
|
|
|
// Delta is the time since last frame, no since beginning of Event
|
2017-06-23 05:58:32 +08:00
|
|
|
event_data->frames[i-1].delta = frame_delta;
|
2018-09-12 01:20:41 +08:00
|
|
|
event_data->frames[i-1].timestamp = last_timestamp + ((i-last_id)*frame_delta);
|
|
|
|
event_data->frames[i-1].offset = event_data->frames[i-1].timestamp - event_data->start_time;
|
2017-06-23 05:58:32 +08:00
|
|
|
event_data->frames[i-1].in_db = false;
|
2019-12-03 04:51:08 +08:00
|
|
|
Debug(3, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)",
|
2019-05-28 05:15:46 +08:00
|
|
|
i,
|
|
|
|
event_data->frames[i-1].timestamp,
|
|
|
|
event_data->frames[i-1].offset,
|
|
|
|
event_data->frames[i-1].delta,
|
|
|
|
event_data->frames[i-1].in_db
|
|
|
|
);
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
}
|
2018-09-12 01:20:41 +08:00
|
|
|
event_data->frames[id-1].timestamp = event_data->start_time + delta;
|
|
|
|
event_data->frames[id-1].offset = delta;
|
|
|
|
event_data->frames[id-1].delta = frame_delta;
|
2017-06-23 05:58:32 +08:00
|
|
|
event_data->frames[id-1].in_db = true;
|
|
|
|
last_id = id;
|
|
|
|
last_delta = delta;
|
2018-09-12 01:20:41 +08:00
|
|
|
last_timestamp = event_data->frames[id-1].timestamp;
|
2019-12-03 04:51:08 +08:00
|
|
|
Debug(3, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)",
|
2018-09-12 01:20:41 +08:00
|
|
|
id,
|
|
|
|
event_data->frames[id-1].timestamp,
|
|
|
|
event_data->frames[id-1].offset,
|
|
|
|
event_data->frames[id-1].delta,
|
|
|
|
event_data->frames[id-1].in_db
|
|
|
|
);
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
2019-07-26 22:53:48 +08:00
|
|
|
if ( mysql_errno(&dbconn) ) {
|
2019-03-07 03:50:36 +08:00
|
|
|
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-04-13 04:40:11 +08:00
|
|
|
mysql_free_result(result);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2020-04-27 06:19:30 +08:00
|
|
|
if ( event_data->video_file[0] || (monitor->GetOptVideoWriter() > 0) ) {
|
|
|
|
if ( !event_data->video_file[0] ) {
|
|
|
|
snprintf(event_data->video_file, sizeof(event_data->video_file), "%" PRIu64 "-%s", event_data->event_id, "video.mp4");
|
|
|
|
}
|
2019-07-26 22:53:48 +08:00
|
|
|
std::string filepath = std::string(event_data->path) + "/" + std::string(event_data->video_file);
|
|
|
|
//char filepath[PATH_MAX];
|
|
|
|
//snprintf(filepath, sizeof(filepath), "%s/%s", event_data->path, event_data->video_file);
|
|
|
|
Debug(1, "Loading video file from %s", filepath.c_str());
|
2017-06-23 05:58:32 +08:00
|
|
|
ffmpeg_input = new FFmpeg_Input();
|
2019-07-26 22:53:48 +08:00
|
|
|
if ( 0 > ffmpeg_input->Open(filepath.c_str()) ) {
|
|
|
|
Warning("Unable to open ffmpeg_input %s", filepath.c_str());
|
2017-06-23 05:58:32 +08:00
|
|
|
delete ffmpeg_input;
|
|
|
|
ffmpeg_input = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( forceEventChange || mode == MODE_ALL_GAPLESS ) {
|
|
|
|
if ( replay_rate > 0 )
|
|
|
|
curr_stream_time = event_data->frames[0].timestamp;
|
|
|
|
else
|
|
|
|
curr_stream_time = event_data->frames[event_data->frame_count-1].timestamp;
|
|
|
|
}
|
2019-05-28 05:15:46 +08:00
|
|
|
Debug(2, "Event:%" PRIu64 ", Frames:%ld, Duration: %.2f",
|
|
|
|
event_data->event_id, event_data->frame_count, event_data->duration);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-04-04 03:23:38 +08:00
|
|
|
return true;
|
2017-08-24 03:05:44 +08:00
|
|
|
} // bool EventStream::loadEventData( int event_id )
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-04-04 03:23:38 +08:00
|
|
|
void EventStream::processCommand(const CmdMsg *msg) {
|
|
|
|
Debug(2, "Got message, type %d, msg %d", msg->msg_type, msg->msg_data[0]);
|
2017-06-23 05:58:32 +08:00
|
|
|
// Check for incoming command
|
2019-12-03 04:51:08 +08:00
|
|
|
switch ( (MsgCommand)msg->msg_data[0] ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
case CMD_PAUSE :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got PAUSE command");
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
// Set paused flag
|
|
|
|
paused = true;
|
|
|
|
replay_rate = ZM_RATE_BASE;
|
2019-01-17 00:20:10 +08:00
|
|
|
last_frame_sent = TV_2_FLOAT(now);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case CMD_PLAY :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got PLAY command");
|
2017-06-22 22:46:32 +08:00
|
|
|
if ( paused ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
paused = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we are in single event mode and at the last frame, replay the current event
|
2019-07-26 22:53:48 +08:00
|
|
|
if (
|
|
|
|
(mode == MODE_SINGLE || mode == MODE_NONE)
|
|
|
|
&&
|
|
|
|
((unsigned int)curr_frame_id == event_data->frame_count)
|
|
|
|
) {
|
2018-09-29 00:31:53 +08:00
|
|
|
Debug(1, "Was in single or no replay mode, and at last frame, so jumping to 1st frame");
|
2017-06-23 05:58:32 +08:00
|
|
|
curr_frame_id = 1;
|
2017-06-22 22:46:32 +08:00
|
|
|
} else {
|
2019-07-26 22:53:48 +08:00
|
|
|
Debug(1, "mode is %s, current frame is %d, frame count is %d",
|
|
|
|
(mode == MODE_SINGLE ? "single" : "not single"),
|
|
|
|
curr_frame_id, event_data->frame_count );
|
2017-06-22 22:46:32 +08:00
|
|
|
}
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
replay_rate = ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
case CMD_VARPLAY :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got VARPLAY command");
|
2017-10-25 07:10:16 +08:00
|
|
|
if ( paused ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
paused = false;
|
|
|
|
}
|
|
|
|
replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768;
|
|
|
|
break;
|
|
|
|
case CMD_STOP :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got STOP command");
|
2017-06-23 05:58:32 +08:00
|
|
|
paused = false;
|
|
|
|
break;
|
|
|
|
case CMD_FASTFWD :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got FAST FWD command");
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( paused ) {
|
|
|
|
paused = false;
|
|
|
|
}
|
|
|
|
// Set play rate
|
|
|
|
switch ( replay_rate ) {
|
|
|
|
case 2 * ZM_RATE_BASE :
|
|
|
|
replay_rate = 5 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
case 5 * ZM_RATE_BASE :
|
|
|
|
replay_rate = 10 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
case 10 * ZM_RATE_BASE :
|
|
|
|
replay_rate = 25 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
case 25 * ZM_RATE_BASE :
|
|
|
|
case 50 * ZM_RATE_BASE :
|
|
|
|
replay_rate = 50 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
default :
|
2019-02-27 00:41:02 +08:00
|
|
|
Debug(1,"Defaulting replay_rate to 2*ZM_RATE_BASE because it is %d", replay_rate);
|
2017-06-23 05:58:32 +08:00
|
|
|
replay_rate = 2 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CMD_SLOWFWD :
|
|
|
|
paused = true;
|
|
|
|
replay_rate = ZM_RATE_BASE;
|
|
|
|
step = 1;
|
2020-03-27 00:06:07 +08:00
|
|
|
if ( (unsigned int)curr_frame_id < event_data->frame_count )
|
|
|
|
curr_frame_id += 1;
|
2020-03-27 03:08:32 +08:00
|
|
|
Debug(1, "Got SLOWFWD command new frame id %d", curr_frame_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case CMD_SLOWREV :
|
|
|
|
paused = true;
|
|
|
|
replay_rate = ZM_RATE_BASE;
|
|
|
|
step = -1;
|
2020-01-08 06:07:35 +08:00
|
|
|
curr_frame_id -= 1;
|
|
|
|
if ( curr_frame_id < 1 ) curr_frame_id = 1;
|
2020-03-27 03:08:32 +08:00
|
|
|
Debug(1, "Got SLOWREV command new frame id %d", curr_frame_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case CMD_FASTREV :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got FAST REV command");
|
2018-04-13 04:40:11 +08:00
|
|
|
paused = false;
|
2017-06-23 05:58:32 +08:00
|
|
|
// Set play rate
|
|
|
|
switch ( replay_rate ) {
|
|
|
|
case -2 * ZM_RATE_BASE :
|
|
|
|
replay_rate = -5 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
case -5 * ZM_RATE_BASE :
|
|
|
|
replay_rate = -10 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
case -10 * ZM_RATE_BASE :
|
|
|
|
replay_rate = -25 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
case -25 * ZM_RATE_BASE :
|
|
|
|
case -50 * ZM_RATE_BASE :
|
|
|
|
replay_rate = -50 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
default :
|
|
|
|
replay_rate = -2 * ZM_RATE_BASE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CMD_ZOOMIN :
|
|
|
|
x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
|
|
|
|
y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got ZOOM IN command, to %d,%d", x, y);
|
2017-06-23 05:58:32 +08:00
|
|
|
switch ( zoom ) {
|
|
|
|
case 100:
|
|
|
|
zoom = 150;
|
|
|
|
break;
|
|
|
|
case 150:
|
|
|
|
zoom = 200;
|
|
|
|
break;
|
|
|
|
case 200:
|
|
|
|
zoom = 300;
|
|
|
|
break;
|
|
|
|
case 300:
|
|
|
|
zoom = 400;
|
|
|
|
break;
|
|
|
|
case 400:
|
|
|
|
default :
|
|
|
|
zoom = 500;
|
|
|
|
break;
|
|
|
|
}
|
2017-06-22 22:46:32 +08:00
|
|
|
send_frame = true;
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case CMD_ZOOMOUT :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got ZOOM OUT command");
|
2017-06-23 05:58:32 +08:00
|
|
|
switch ( zoom ) {
|
|
|
|
case 500:
|
|
|
|
zoom = 400;
|
|
|
|
break;
|
|
|
|
case 400:
|
|
|
|
zoom = 300;
|
|
|
|
break;
|
|
|
|
case 300:
|
|
|
|
zoom = 200;
|
|
|
|
break;
|
|
|
|
case 200:
|
|
|
|
zoom = 150;
|
|
|
|
break;
|
|
|
|
case 150:
|
|
|
|
default :
|
|
|
|
zoom = 100;
|
|
|
|
break;
|
|
|
|
}
|
2017-06-22 22:46:32 +08:00
|
|
|
send_frame = true;
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case CMD_PAN :
|
|
|
|
x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
|
|
|
|
y = ((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got PAN command, to %d,%d", x, y);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case CMD_SCALE :
|
|
|
|
scale = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2];
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got SCALE command, to %d", scale);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case CMD_PREV :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got PREV command");
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( replay_rate >= 0 )
|
|
|
|
curr_frame_id = 0;
|
|
|
|
else
|
|
|
|
curr_frame_id = event_data->frame_count+1;
|
|
|
|
paused = false;
|
|
|
|
forceEventChange = true;
|
|
|
|
break;
|
|
|
|
case CMD_NEXT :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got NEXT command");
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( replay_rate >= 0 )
|
|
|
|
curr_frame_id = event_data->frame_count+1;
|
|
|
|
else
|
|
|
|
curr_frame_id = 0;
|
|
|
|
paused = false;
|
|
|
|
forceEventChange = true;
|
|
|
|
break;
|
|
|
|
case CMD_SEEK :
|
|
|
|
{
|
2019-05-28 05:15:46 +08:00
|
|
|
// offset is in seconds
|
2017-06-23 05:58:32 +08:00
|
|
|
int offset = ((unsigned char)msg->msg_data[1]<<24)|((unsigned char)msg->msg_data[2]<<16)|((unsigned char)msg->msg_data[3]<<8)|(unsigned char)msg->msg_data[4];
|
|
|
|
curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration);
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got SEEK command, to %d (new cfid: %d)", offset, curr_frame_id);
|
2017-06-22 22:46:32 +08:00
|
|
|
send_frame = true;
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CMD_QUERY :
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(1, "Got QUERY command, sending STATUS");
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case CMD_QUIT :
|
2018-04-13 04:40:11 +08:00
|
|
|
Info("User initiated exit - CMD_QUIT");
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
default :
|
|
|
|
// Do nothing, for now
|
2018-04-13 04:40:11 +08:00
|
|
|
break;
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
2019-12-03 04:51:08 +08:00
|
|
|
|
2017-06-23 05:58:32 +08:00
|
|
|
struct {
|
2018-04-18 01:51:20 +08:00
|
|
|
uint64_t event_id;
|
2017-06-23 05:58:32 +08:00
|
|
|
int progress;
|
|
|
|
int rate;
|
|
|
|
int zoom;
|
|
|
|
bool paused;
|
|
|
|
} status_data;
|
|
|
|
|
2018-04-13 04:40:11 +08:00
|
|
|
status_data.event_id = event_data->event_id;
|
2017-06-23 05:58:32 +08:00
|
|
|
status_data.progress = (int)event_data->frames[curr_frame_id-1].offset;
|
|
|
|
status_data.rate = replay_rate;
|
|
|
|
status_data.zoom = zoom;
|
|
|
|
status_data.paused = paused;
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(2, "Event:%" PRIu64 ", Paused:%d, progress:%d Rate:%d, Zoom:%d",
|
2018-04-13 04:40:11 +08:00
|
|
|
status_data.event_id,
|
2017-06-22 22:12:04 +08:00
|
|
|
status_data.paused,
|
|
|
|
status_data.progress,
|
|
|
|
status_data.rate,
|
|
|
|
status_data.zoom
|
|
|
|
);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
DataMsg status_msg;
|
|
|
|
status_msg.msg_type = MSG_DATA_EVENT;
|
2018-04-13 04:40:11 +08:00
|
|
|
memcpy(&status_msg.msg_data, &status_data, sizeof(status_data));
|
2019-07-26 22:53:48 +08:00
|
|
|
Debug(1, "Size of msg %d", sizeof(status_data));
|
2018-04-13 04:40:11 +08:00
|
|
|
if ( sendto(sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr)) < 0 ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
//if ( errno != EAGAIN )
|
|
|
|
{
|
2018-04-13 04:40:11 +08:00
|
|
|
Error("Can't sendto on sd %d: %s", sd, strerror(errno));
|
2018-12-05 07:23:08 +08:00
|
|
|
//exit(-1);
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// quit after sending a status, if this was a quit request
|
2019-07-26 22:53:48 +08:00
|
|
|
if ( (MsgCommand)msg->msg_data[0] == CMD_QUIT )
|
2017-06-23 05:58:32 +08:00
|
|
|
exit(0);
|
|
|
|
|
2018-04-13 04:40:11 +08:00
|
|
|
updateFrameRate((double)event_data->frame_count/event_data->duration);
|
2019-12-03 04:51:08 +08:00
|
|
|
} // void EventStream::processCommand(const CmdMsg *msg)
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-12-03 04:51:08 +08:00
|
|
|
bool EventStream::checkEventLoaded() {
|
2017-06-23 05:58:32 +08:00
|
|
|
static char sql[ZM_SQL_SML_BUFSIZ];
|
|
|
|
|
|
|
|
if ( curr_frame_id <= 0 ) {
|
2019-07-26 22:53:48 +08:00
|
|
|
snprintf(sql, sizeof(sql),
|
2019-08-15 04:18:21 +08:00
|
|
|
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %ld AND `Id` < %" PRIu64 " ORDER BY `Id` DESC LIMIT 1",
|
2019-07-26 22:53:48 +08:00
|
|
|
event_data->monitor_id, event_data->event_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
} else if ( (unsigned int)curr_frame_id > event_data->frame_count ) {
|
2019-07-26 22:53:48 +08:00
|
|
|
snprintf(sql, sizeof(sql),
|
2019-08-15 04:18:21 +08:00
|
|
|
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %ld AND `Id` > %" PRIu64 " ORDER BY `Id` ASC LIMIT 1",
|
2019-07-26 22:53:48 +08:00
|
|
|
event_data->monitor_id, event_data->event_id);
|
2019-01-17 00:20:10 +08:00
|
|
|
} else {
|
|
|
|
// No event change required
|
2019-07-26 22:53:48 +08:00
|
|
|
Debug(3, "No event change required, as curr frame %d <=> event frames %d",
|
|
|
|
curr_frame_id, event_data->frame_count);
|
2019-12-03 04:51:08 +08:00
|
|
|
return false;
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
// Event change required.
|
2019-07-26 22:53:48 +08:00
|
|
|
if ( forceEventChange || ( (mode != MODE_SINGLE) && (mode != MODE_NONE) ) ) {
|
2019-01-17 00:20:10 +08:00
|
|
|
if ( mysql_query(&dbconn, sql) ) {
|
|
|
|
Error("Can't run query: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
|
|
|
}
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
MYSQL_RES *result = mysql_store_result(&dbconn);
|
|
|
|
if ( !result ) {
|
|
|
|
Error("Can't use query result: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
|
|
|
}
|
|
|
|
MYSQL_ROW dbrow = mysql_fetch_row(result);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
if ( mysql_errno(&dbconn)) {
|
|
|
|
Error("Can't fetch row: %s", mysql_error(&dbconn));
|
|
|
|
exit(mysql_errno(&dbconn));
|
|
|
|
}
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
if ( dbrow ) {
|
|
|
|
uint64_t event_id = atoll(dbrow[0]);
|
|
|
|
Debug(1, "Loading new event %" PRIu64, event_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
loadEventData(event_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-07-26 22:53:48 +08:00
|
|
|
if ( replay_rate < 0 ) // rewind
|
2019-01-17 00:20:10 +08:00
|
|
|
curr_frame_id = event_data->frame_count;
|
|
|
|
else
|
|
|
|
curr_frame_id = 1;
|
|
|
|
Debug(2, "New frame id = %d", curr_frame_id);
|
2019-12-03 04:51:08 +08:00
|
|
|
return true;
|
2017-06-23 05:58:32 +08:00
|
|
|
} else {
|
2019-07-26 22:53:48 +08:00
|
|
|
Debug(2, "No next event loaded using %s. Pausing", sql);
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( curr_frame_id <= 0 )
|
|
|
|
curr_frame_id = 1;
|
|
|
|
else
|
|
|
|
curr_frame_id = event_data->frame_count;
|
|
|
|
paused = true;
|
2019-07-27 00:21:15 +08:00
|
|
|
sendTextFrame("No more event data found");
|
2019-12-03 04:51:08 +08:00
|
|
|
} // end if found a new event or not
|
2019-01-17 00:20:10 +08:00
|
|
|
mysql_free_result(result);
|
|
|
|
forceEventChange = false;
|
|
|
|
} else {
|
2019-07-26 22:53:48 +08:00
|
|
|
Debug(2, "Pausing because mode is %d", mode);
|
2019-01-17 00:20:10 +08:00
|
|
|
if ( curr_frame_id <= 0 )
|
|
|
|
curr_frame_id = 1;
|
|
|
|
else
|
|
|
|
curr_frame_id = event_data->frame_count;
|
|
|
|
paused = true;
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
2019-12-03 04:51:08 +08:00
|
|
|
return false;
|
|
|
|
} // void EventStream::checkEventLoaded()
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
Image * EventStream::getImage( ) {
|
|
|
|
static char filepath[PATH_MAX];
|
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
|
2019-05-28 05:15:46 +08:00
|
|
|
Debug(2, "EventStream::getImage path(%s) from %s frame(%d) ", filepath, event_data->path, curr_frame_id);
|
2018-04-04 03:23:38 +08:00
|
|
|
Image *image = new Image(filepath);
|
2017-06-23 05:58:32 +08:00
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
2019-05-28 05:15:46 +08:00
|
|
|
bool EventStream::sendFrame(int delta_us) {
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(2, "Sending frame %d", curr_frame_id);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
static char filepath[PATH_MAX];
|
|
|
|
static struct stat filestat;
|
|
|
|
FILE *fdj = NULL;
|
|
|
|
|
2019-07-26 22:53:48 +08:00
|
|
|
// This needs to be abstracted. If we are saving jpgs, then load the capture file.
|
|
|
|
// If we are only saving analysis frames, then send that.
|
2018-11-07 00:45:34 +08:00
|
|
|
if ( event_data->SaveJPEGs & 1 ) {
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
|
2018-11-07 00:45:34 +08:00
|
|
|
} else if ( event_data->SaveJPEGs & 2 ) {
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(filepath, sizeof(filepath), staticConfig.analyse_file_format, event_data->path, curr_frame_id);
|
2019-07-26 22:53:48 +08:00
|
|
|
if ( stat(filepath, &filestat) < 0 ) {
|
2017-08-24 03:05:44 +08:00
|
|
|
Debug(1, "analyze file %s not found will try to stream from other", filepath);
|
2019-01-17 00:20:10 +08:00
|
|
|
snprintf(filepath, sizeof(filepath), staticConfig.capture_file_format, event_data->path, curr_frame_id);
|
2019-07-26 22:53:48 +08:00
|
|
|
if ( stat(filepath, &filestat) < 0 ) {
|
2018-11-07 00:45:34 +08:00
|
|
|
Debug(1, "capture file %s not found either", filepath);
|
|
|
|
filepath[0] = 0;
|
|
|
|
}
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
} else if ( !ffmpeg_input ) {
|
|
|
|
Fatal("JPEGS not saved. zms is not capable of streaming jpegs from mp4 yet");
|
2017-06-23 05:58:32 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_LIBAVCODEC
|
|
|
|
if ( type == STREAM_MPEG ) {
|
2019-01-17 00:20:10 +08:00
|
|
|
Image image(filepath);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-04-04 03:23:38 +08:00
|
|
|
Image *send_image = prepareImage(&image);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
if ( !vid_stream ) {
|
2019-12-03 04:51:08 +08:00
|
|
|
vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps,
|
|
|
|
send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height());
|
2018-04-04 03:23:38 +08:00
|
|
|
fprintf(stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType());
|
2017-06-23 05:58:32 +08:00
|
|
|
vid_stream->OpenStream();
|
|
|
|
}
|
2019-01-17 00:20:10 +08:00
|
|
|
/* double pts = */ vid_stream->EncodeFrame(send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000);
|
2017-06-23 05:58:32 +08:00
|
|
|
} else
|
|
|
|
#endif // HAVE_LIBAVCODEC
|
|
|
|
{
|
|
|
|
static unsigned char temp_img_buffer[ZM_MAX_IMAGE_SIZE];
|
|
|
|
|
|
|
|
int img_buffer_size = 0;
|
|
|
|
uint8_t *img_buffer = temp_img_buffer;
|
|
|
|
|
2017-08-24 03:05:44 +08:00
|
|
|
bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)) && filepath[0];
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
fprintf(stdout, "--ZoneMinderFrame\r\n");
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-04-04 03:23:38 +08:00
|
|
|
if ( (type != STREAM_JPEG) || (!filepath[0]) )
|
2017-06-23 05:58:32 +08:00
|
|
|
send_raw = false;
|
|
|
|
|
|
|
|
if ( send_raw ) {
|
2018-04-13 04:40:11 +08:00
|
|
|
fdj = fopen(filepath, "rb");
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( !fdj ) {
|
2018-04-13 04:40:11 +08:00
|
|
|
Error("Can't open %s: %s", filepath, strerror(errno));
|
2019-08-16 22:24:51 +08:00
|
|
|
return true; // returning false will cause us to terminate.
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
#if HAVE_SENDFILE
|
2019-01-17 00:20:10 +08:00
|
|
|
if ( fstat(fileno(fdj),&filestat) < 0 ) {
|
|
|
|
Error("Failed getting information about file %s: %s", filepath, strerror(errno));
|
2018-04-13 04:40:11 +08:00
|
|
|
return false;
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
#else
|
2018-04-13 04:40:11 +08:00
|
|
|
img_buffer_size = fread(img_buffer, 1, sizeof(temp_img_buffer), fdj);
|
2017-06-23 05:58:32 +08:00
|
|
|
#endif
|
|
|
|
} else {
|
2017-08-24 03:05:44 +08:00
|
|
|
Image *image = NULL;
|
|
|
|
|
|
|
|
if ( filepath[0] ) {
|
2018-04-04 03:23:38 +08:00
|
|
|
Debug(1, "Loading image");
|
2018-04-13 04:40:11 +08:00
|
|
|
image = new Image(filepath);
|
2017-08-24 03:05:44 +08:00
|
|
|
} else if ( ffmpeg_input ) {
|
|
|
|
// Get the frame from the mp4 input
|
|
|
|
Debug(1,"Getting frame from ffmpeg");
|
2018-09-12 01:20:41 +08:00
|
|
|
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
2020-04-27 06:19:30 +08:00
|
|
|
AVFrame *frame = ffmpeg_input->get_frame(
|
|
|
|
ffmpeg_input->get_video_stream_id(),
|
|
|
|
frame_data->offset);
|
2017-08-24 03:05:44 +08:00
|
|
|
if ( frame ) {
|
2018-04-04 03:23:38 +08:00
|
|
|
image = new Image(frame);
|
2018-11-20 05:45:56 +08:00
|
|
|
//av_frame_free(&frame);
|
2017-08-24 03:05:44 +08:00
|
|
|
} else {
|
|
|
|
Error("Failed getting a frame.");
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-11 05:22:01 +08:00
|
|
|
|
|
|
|
// when stored as an mp4, we just have the rotation as a flag in the headers
|
|
|
|
// so we need to rotate it before outputting
|
2020-02-25 03:12:10 +08:00
|
|
|
if (
|
|
|
|
(monitor->GetOptVideoWriter() == Monitor::H264PASSTHROUGH)
|
|
|
|
and
|
|
|
|
(event_data->Orientation != Monitor::ROTATE_0)
|
|
|
|
) {
|
2020-02-11 05:22:01 +08:00
|
|
|
Debug(2, "Rotating image %d", event_data->Orientation);
|
|
|
|
switch ( event_data->Orientation ) {
|
|
|
|
case Monitor::ROTATE_0 :
|
|
|
|
// No action required
|
|
|
|
break;
|
|
|
|
case Monitor::ROTATE_90 :
|
|
|
|
case Monitor::ROTATE_180 :
|
|
|
|
case Monitor::ROTATE_270 :
|
|
|
|
image->Rotate((event_data->Orientation-1)*90);
|
|
|
|
break;
|
|
|
|
case Monitor::FLIP_HORI :
|
|
|
|
case Monitor::FLIP_VERT :
|
|
|
|
image->Flip(event_data->Orientation==Monitor::FLIP_HORI);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
Error("Invalid Orientation: %d", event_data->Orientation);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Debug(2, "Not Rotating image %d", event_data->Orientation);
|
|
|
|
} // end if have rotation
|
2017-08-24 03:05:44 +08:00
|
|
|
} else {
|
|
|
|
Error("Unable to get a frame");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-04-04 03:23:38 +08:00
|
|
|
Image *send_image = prepareImage(image);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
switch ( type ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
case STREAM_JPEG :
|
2018-04-04 03:23:38 +08:00
|
|
|
send_image->EncodeJpeg(img_buffer, &img_buffer_size);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case STREAM_ZIP :
|
|
|
|
#if HAVE_ZLIB_H
|
|
|
|
unsigned long zip_buffer_size;
|
2018-04-04 03:23:38 +08:00
|
|
|
send_image->Zip(img_buffer, &zip_buffer_size);
|
2017-06-23 05:58:32 +08:00
|
|
|
img_buffer_size = zip_buffer_size;
|
|
|
|
break;
|
|
|
|
#else
|
|
|
|
Error("zlib is required for zipped images. Falling back to raw image");
|
|
|
|
type = STREAM_RAW;
|
|
|
|
#endif // HAVE_ZLIB_H
|
|
|
|
case STREAM_RAW :
|
|
|
|
img_buffer = (uint8_t*)(send_image->Buffer());
|
|
|
|
img_buffer_size = send_image->Size();
|
|
|
|
break;
|
|
|
|
default:
|
2019-01-17 00:20:10 +08:00
|
|
|
Fatal("Unexpected frame type %d", type);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
}
|
2020-04-27 06:19:30 +08:00
|
|
|
if ( send_image != image ) {
|
|
|
|
delete send_image;
|
|
|
|
send_image = NULL;
|
|
|
|
}
|
2017-08-24 03:05:44 +08:00
|
|
|
delete image;
|
|
|
|
image = NULL;
|
2019-01-17 00:20:10 +08:00
|
|
|
} // end if send_raw or not
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
switch ( type ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
case STREAM_JPEG :
|
2019-01-17 00:20:10 +08:00
|
|
|
fputs("Content-Type: image/jpeg\r\n", stdout);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case STREAM_RAW :
|
2019-01-17 00:20:10 +08:00
|
|
|
fputs("Content-Type: image/x-rgb\r\n", stdout);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
case STREAM_ZIP :
|
2019-01-17 00:20:10 +08:00
|
|
|
fputs("Content-Type: image/x-rgbz\r\n", stdout);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
default :
|
2018-04-13 04:40:11 +08:00
|
|
|
Fatal("Unexpected frame type %d", type);
|
2017-06-23 05:58:32 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-08-24 03:05:44 +08:00
|
|
|
if ( send_raw ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
#if HAVE_SENDFILE
|
2019-01-17 00:20:10 +08:00
|
|
|
fprintf(stdout, "Content-Length: %d\r\n\r\n", (int)filestat.st_size);
|
2017-06-26 23:20:10 +08:00
|
|
|
if ( zm_sendfile(fileno(stdout), fileno(fdj), 0, (int)filestat.st_size) != (int)filestat.st_size ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
/* sendfile() failed, use standard way instead */
|
|
|
|
img_buffer_size = fread( img_buffer, 1, sizeof(temp_img_buffer), fdj );
|
2019-01-17 00:20:10 +08:00
|
|
|
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
|
2017-12-13 02:38:31 +08:00
|
|
|
fclose(fdj); /* Close the file handle */
|
2019-07-26 22:53:48 +08:00
|
|
|
Error("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
|
2019-01-17 00:20:10 +08:00
|
|
|
return false;
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
2019-01-17 00:20:10 +08:00
|
|
|
fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size);
|
|
|
|
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
|
2017-12-13 02:38:31 +08:00
|
|
|
fclose(fdj); /* Close the file handle */
|
2019-07-26 22:53:48 +08:00
|
|
|
Error("Unable to send raw frame %u: %s", curr_frame_id, strerror(errno));
|
2019-01-17 00:20:10 +08:00
|
|
|
return false;
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
2017-06-22 22:12:04 +08:00
|
|
|
#endif
|
2017-06-23 05:58:32 +08:00
|
|
|
fclose(fdj); /* Close the file handle */
|
|
|
|
} else {
|
2019-01-17 00:20:10 +08:00
|
|
|
Debug(3, "Content length: %d", img_buffer_size);
|
|
|
|
fprintf(stdout, "Content-Length: %d\r\n\r\n", img_buffer_size);
|
|
|
|
if ( fwrite(img_buffer, img_buffer_size, 1, stdout) != 1 ) {
|
|
|
|
Error("Unable to send stream frame: %s", strerror(errno));
|
|
|
|
return false;
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
2019-07-26 22:53:48 +08:00
|
|
|
} // end if send_raw or not
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2018-04-13 04:40:11 +08:00
|
|
|
fputs("\r\n\r\n", stdout);
|
|
|
|
fflush(stdout);
|
2019-07-26 22:53:48 +08:00
|
|
|
} // end if stream MPEG or other
|
2018-04-13 04:40:11 +08:00
|
|
|
last_frame_sent = TV_2_FLOAT(now);
|
|
|
|
return true;
|
2019-07-26 22:53:48 +08:00
|
|
|
} // bool EventStream::sendFrame( int delta_us )
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
void EventStream::runStream() {
|
|
|
|
openComms();
|
|
|
|
|
|
|
|
checkInitialised();
|
|
|
|
|
|
|
|
if ( type == STREAM_JPEG )
|
2018-04-13 01:14:00 +08:00
|
|
|
fputs("Content-Type: multipart/x-mixed-replace;boundary=ZoneMinderFrame\r\n\r\n", stdout);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
if ( !event_data ) {
|
2018-04-13 01:14:00 +08:00
|
|
|
sendTextFrame("No event data found");
|
|
|
|
exit(0);
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
|
2018-04-13 01:14:00 +08:00
|
|
|
Debug(3, "frame rate is: (%f)", (double)event_data->frame_count/event_data->duration);
|
|
|
|
updateFrameRate((double)event_data->frame_count/event_data->duration);
|
2019-03-07 03:50:36 +08:00
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
uint64_t start_usec = start.tv_sec * 1000000 + start.tv_usec;
|
2019-12-03 04:51:08 +08:00
|
|
|
uint64_t last_frame_offset = 0;
|
|
|
|
|
|
|
|
bool in_event = true;
|
|
|
|
double time_to_event = 0;
|
2017-12-13 02:30:43 +08:00
|
|
|
|
2019-01-17 00:20:10 +08:00
|
|
|
while ( !zm_terminate ) {
|
2018-04-13 01:14:00 +08:00
|
|
|
gettimeofday(&now, NULL);
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-01-16 00:25:22 +08:00
|
|
|
int delta_us = 0;
|
2017-06-22 22:46:32 +08:00
|
|
|
send_frame = false;
|
|
|
|
|
2019-02-26 23:08:07 +08:00
|
|
|
if ( connkey ) {
|
|
|
|
// commands may set send_frame to true
|
|
|
|
while ( checkCommandQueue() && !zm_terminate ) {
|
|
|
|
// The idea is to loop here processing all commands before proceeding.
|
|
|
|
}
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-02-26 23:08:07 +08:00
|
|
|
// Update modified time of the socket .lock file so that we can tell which ones are stale.
|
|
|
|
if ( now.tv_sec - last_comm_update.tv_sec > 3600 ) {
|
|
|
|
touch(sock_path_lock);
|
|
|
|
last_comm_update = now;
|
|
|
|
}
|
2018-09-22 03:58:14 +08:00
|
|
|
}
|
|
|
|
|
2017-06-23 05:58:32 +08:00
|
|
|
// Get current frame data
|
|
|
|
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
|
|
|
|
|
|
|
if ( !paused ) {
|
2017-06-22 22:46:32 +08:00
|
|
|
// Figure out if we should send this frame
|
2019-12-03 04:51:08 +08:00
|
|
|
Debug(3, "not paused at cur_frame_id (%d-1) mod frame_mod(%d)", curr_frame_id, frame_mod);
|
2017-06-23 05:58:32 +08:00
|
|
|
// If we are streaming and this frame is due to be sent
|
2018-09-12 01:20:41 +08:00
|
|
|
// frame mod defaults to 1 and if we are going faster than max_fps will get multiplied by 2
|
|
|
|
// so if it is 2, then we send every other frame, if is it 4 then every fourth frame, etc.
|
2019-12-03 04:51:08 +08:00
|
|
|
|
2018-09-12 01:20:41 +08:00
|
|
|
if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
send_frame = true;
|
|
|
|
}
|
|
|
|
} else if ( step != 0 ) {
|
2020-01-08 06:07:35 +08:00
|
|
|
Debug(2, "Paused with step %d", step);
|
2017-06-23 05:58:32 +08:00
|
|
|
// We are paused and are just stepping forward or backward one frame
|
|
|
|
step = 0;
|
|
|
|
send_frame = true;
|
2017-08-14 22:41:04 +08:00
|
|
|
} else if ( !send_frame ) {
|
2019-12-03 04:51:08 +08:00
|
|
|
// We are paused, not stepping and doing nothing, meaning that comms didn't set send_frame to true
|
2018-04-13 01:14:00 +08:00
|
|
|
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
2017-06-23 05:58:32 +08:00
|
|
|
if ( actual_delta_time > MAX_STREAM_DELAY ) {
|
|
|
|
// Send keepalive
|
2018-04-13 01:14:00 +08:00
|
|
|
Debug(2, "Sending keepalive frame");
|
2017-06-23 05:58:32 +08:00
|
|
|
send_frame = true;
|
|
|
|
}
|
2019-12-03 04:51:08 +08:00
|
|
|
} // end if streaming stepping or doing nothing
|
|
|
|
|
|
|
|
// time_to_event > 0 means that we are not in the event
|
|
|
|
if ( time_to_event > 0 ) {
|
|
|
|
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
|
|
|
Debug(1, "Actual delta time = %f = %f - %f", actual_delta_time, TV_2_FLOAT(now), last_frame_sent);
|
|
|
|
// > 1 second
|
|
|
|
if ( actual_delta_time > 1 ) {
|
|
|
|
Debug(1, "Sending time to next event frame");
|
|
|
|
static char frame_text[64];
|
|
|
|
snprintf(frame_text, sizeof(frame_text), "Time to next event = %d seconds", (int)time_to_event);
|
|
|
|
if ( !sendTextFrame(frame_text) )
|
|
|
|
zm_terminate = true;
|
|
|
|
} else {
|
|
|
|
Debug(1, "Not Sending time to next event frame because actual delta time is %f", actual_delta_time);
|
|
|
|
}
|
|
|
|
//else
|
|
|
|
//{
|
|
|
|
// FIXME ICON But we are not paused. We are somehow still in the event?
|
|
|
|
double sleep_time = (replay_rate>0?1:-1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000));
|
|
|
|
//double sleep_time = (replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
|
|
|
|
//// ZM_RATE_BASE == 100, and 1x replay_rate is 100
|
|
|
|
//double sleep_time = ((replay_rate/ZM_RATE_BASE) * STREAM_PAUSE_WAIT)/1000000;
|
|
|
|
if ( !sleep_time ) {
|
|
|
|
sleep_time += STREAM_PAUSE_WAIT/1000000;
|
|
|
|
}
|
|
|
|
curr_stream_time += sleep_time;
|
|
|
|
time_to_event -= sleep_time;
|
|
|
|
Debug(2, "Sleeping (%dus) because we are not at the next event yet, adding %f", STREAM_PAUSE_WAIT, sleep_time);
|
|
|
|
usleep(STREAM_PAUSE_WAIT);
|
|
|
|
|
|
|
|
//curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
|
|
|
|
//}
|
|
|
|
continue;
|
|
|
|
} // end if !in_event
|
2017-06-23 05:58:32 +08:00
|
|
|
|
2019-02-27 00:41:02 +08:00
|
|
|
if ( send_frame ) {
|
2019-08-20 23:13:38 +08:00
|
|
|
if ( !sendFrame(delta_us) ) {
|
2017-06-23 05:58:32 +08:00
|
|
|
zm_terminate = true;
|
2019-08-20 23:13:38 +08:00
|
|
|
break;
|
|
|
|
}
|
2019-02-27 00:41:02 +08:00
|
|
|
}
|
2017-06-23 05:58:32 +08:00
|
|
|
|
|
|
|
curr_stream_time = frame_data->timestamp;
|
|
|
|
|
|
|
|
if ( !paused ) {
|
2019-12-03 04:51:08 +08:00
|
|
|
|
|
|
|
// delta is since the last frame
|
|
|
|
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
|
|
|
Debug(3, "frame delta %uus ", delta_us);
|
|
|
|
// if effective > base we should speed up frame delivery
|
|
|
|
delta_us = (unsigned int)((delta_us * base_fps)/effective_fps);
|
|
|
|
Debug(3, "delta %u = base_fps(%f)/effective fps(%f)", delta_us, base_fps, effective_fps);
|
|
|
|
// but must not exceed maxfps
|
|
|
|
delta_us = max(delta_us, 1000000/maxfps);
|
|
|
|
Debug(3, "delta %u = base_fps(%f)/effective fps(%f) from 30fps", delta_us, base_fps, effective_fps);
|
|
|
|
|
2019-03-07 03:50:36 +08:00
|
|
|
// +/- 1? What if we are skipping frames?
|
|
|
|
curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod;
|
2019-05-25 01:45:48 +08:00
|
|
|
// sending the frame may have taken some time, so reload now
|
|
|
|
gettimeofday(&now, NULL);
|
|
|
|
uint64_t now_usec = (now.tv_sec * 1000000 + now.tv_usec);
|
2019-01-16 00:25:22 +08:00
|
|
|
|
2019-07-26 22:53:48 +08:00
|
|
|
// we incremented by replay_rate, so might have jumped past frame_count
|
2019-12-03 04:51:08 +08:00
|
|
|
if ( (mode == MODE_SINGLE) && (
|
|
|
|
(curr_frame_id < 1 )
|
|
|
|
||
|
|
|
|
((unsigned int)curr_frame_id >= event_data->frame_count)
|
|
|
|
)
|
|
|
|
) {
|
2018-09-29 00:31:53 +08:00
|
|
|
Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start");
|
2017-06-23 05:58:32 +08:00
|
|
|
curr_frame_id = 1;
|
2019-05-25 01:45:48 +08:00
|
|
|
// Have to reset start_usec to now when replaying
|
|
|
|
start_usec = now_usec;
|
2018-09-29 00:31:53 +08:00
|
|
|
}
|
2019-01-16 00:25:22 +08:00
|
|
|
frame_data = &event_data->frames[curr_frame_id-1];
|
|
|
|
|
2019-03-07 03:50:36 +08:00
|
|
|
// frame_data->delta is the time since last frame as a float in seconds
|
|
|
|
// but what if we are skipping frames? We need the distance from the last frame sent
|
|
|
|
// Also, what about reverse? needs to be absolute value
|
|
|
|
|
|
|
|
// There are two ways to go about this, not sure which is correct.
|
|
|
|
// you can calculate the relationship between now and the start
|
|
|
|
// or calc the relationship from the last frame. I think from the start is better as it self-corrects
|
2019-12-03 04:51:08 +08:00
|
|
|
//
|
|
|
|
if ( last_frame_offset ) {
|
|
|
|
// We assume that we are going forward and the next frame is in the future.
|
|
|
|
delta_us = frame_data->offset * 1000000 - (now_usec-start_usec);
|
|
|
|
// - (now_usec - start_usec);
|
|
|
|
Debug(2, "New delta_us now %" PRIu64 " - start %" PRIu64 " = %d offset %" PRId64 " - elapsed = %dusec",
|
|
|
|
now_usec, start_usec, now_usec-start_usec, frame_data->offset * 1000000, delta_us);
|
|
|
|
} else {
|
|
|
|
Debug(2, "No last frame_offset, no sleep");
|
|
|
|
delta_us = 0;
|
|
|
|
}
|
|
|
|
last_frame_offset = frame_data->offset * 1000000;
|
2019-03-07 03:50:36 +08:00
|
|
|
|
2019-12-03 04:51:08 +08:00
|
|
|
if ( send_frame && (type != STREAM_MPEG) ) {
|
|
|
|
if ( delta_us > 0 ) {
|
2019-08-16 22:24:51 +08:00
|
|
|
if ( delta_us > MAX_SLEEP_USEC ) {
|
|
|
|
Debug(1, "Limiting sleep to %d because calculated sleep is too long %d", MAX_SLEEP_USEC, delta_us);
|
|
|
|
delta_us = MAX_SLEEP_USEC;
|
|
|
|
}
|
2019-01-17 00:20:10 +08:00
|
|
|
usleep(delta_us);
|
2019-02-27 00:41:02 +08:00
|
|
|
Debug(3, "Done sleeping: %d usec", delta_us);
|
2019-01-16 00:25:22 +08:00
|
|
|
}
|
2017-06-23 05:58:32 +08:00
|
|
|
}
|
|
|
|
} else {
|
2019-02-27 00:41:02 +08:00
|
|
|
delta_us = ((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*(replay_rate?abs(replay_rate*2):2)));
|
|
|
|
|
2019-07-26 22:53:48 +08:00
|
|
|
Debug(2, "Sleeping %d because 1000000 * ZM_RATE_BASE(%d) / ( base_fps (%f), replay_rate(%d)",
|
2019-02-27 00:41:02 +08:00
|
|
|
(unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2))),
|
|
|
|
ZM_RATE_BASE,
|
|
|
|
(base_fps?base_fps:1),
|
2019-08-16 22:24:51 +08:00
|
|
|
(replay_rate?abs(replay_rate*2):0)
|
2019-02-27 00:41:02 +08:00
|
|
|
);
|
2019-08-16 22:24:51 +08:00
|
|
|
if ( delta_us > 0 ) {
|
|
|
|
if ( delta_us > MAX_SLEEP_USEC ) {
|
|
|
|
Debug(1, "Limiting sleep to %d because calculated sleep is too long %d", MAX_SLEEP_USEC, delta_us);
|
|
|
|
delta_us = MAX_SLEEP_USEC;
|
|
|
|
}
|
2019-07-26 22:53:48 +08:00
|
|
|
usleep(delta_us);
|
2019-02-27 00:41:02 +08:00
|
|
|
}
|
2019-12-03 04:51:08 +08:00
|
|
|
// We are paused, so might be stepping
|
|
|
|
//if ( step != 0 )// Adding 0 is cheaper than an if 0
|
|
|
|
// curr_frame_id starts at 1 though, so we might skip the first frame?
|
2019-08-16 22:24:51 +08:00
|
|
|
curr_frame_id += step;
|
|
|
|
|
2020-04-27 06:19:30 +08:00
|
|
|
// Detects when we hit end of event and will load the next event or previous event
|
|
|
|
if ( checkEventLoaded() ) {
|
|
|
|
// Have change of event
|
|
|
|
|
|
|
|
// This next bit is to determine if we are in the current event time wise
|
|
|
|
// and whether to show an image saying how long until the next event.
|
|
|
|
if ( replay_rate > 0 ) {
|
|
|
|
// This doesn't make sense unless we have hit the end of the event.
|
|
|
|
time_to_event = event_data->frames[0].timestamp - curr_stream_time;
|
|
|
|
Debug(1, "replay rate(%d) time_to_event(%f)=frame timestamp:%f - curr_stream_time(%f)",
|
|
|
|
replay_rate, time_to_event,
|
|
|
|
event_data->frames[0].timestamp,
|
|
|
|
curr_stream_time);
|
|
|
|
|
|
|
|
} else if ( replay_rate < 0 ) {
|
|
|
|
time_to_event = curr_stream_time - event_data->frames[event_data->frame_count-1].timestamp;
|
|
|
|
Debug(1, "replay rate(%d) time_to_event(%f)=curr_stream_time(%f)-frame timestamp:%f",
|
|
|
|
replay_rate, time_to_event, curr_stream_time, event_data->frames[event_data->frame_count-1].timestamp);
|
|
|
|
} // end if forward or reverse
|
|
|
|
|
|
|
|
} // end if checkEventLoaded
|
|
|
|
} // end if !paused
|
|
|
|
} // end while ! zm_terminate
|
2017-06-23 05:58:32 +08:00
|
|
|
#if HAVE_LIBAVCODEC
|
|
|
|
if ( type == STREAM_MPEG )
|
|
|
|
delete vid_stream;
|
|
|
|
#endif // HAVE_LIBAVCODEC
|
|
|
|
|
|
|
|
closeComms();
|
2019-01-17 00:20:10 +08:00
|
|
|
} // void EventStream::runStream()
|
|
|
|
|
2020-04-27 06:19:30 +08:00
|
|
|
void EventStream::setStreamStart(
|
|
|
|
uint64_t init_event_id,
|
|
|
|
unsigned int init_frame_id=0) {
|
2019-01-17 00:20:10 +08:00
|
|
|
loadInitialEventData(init_event_id, init_frame_id);
|
2020-04-27 06:19:30 +08:00
|
|
|
} // end void EventStream::setStreamStart(init_event_id,init_frame_id=0)
|
2019-01-17 00:20:10 +08:00
|
|
|
|
|
|
|
void EventStream::setStreamStart(int monitor_id, time_t event_time) {
|
2018-04-13 04:40:11 +08:00
|
|
|
loadInitialEventData(monitor_id, event_time);
|
2018-03-03 10:24:39 +08:00
|
|
|
}
|