Merge pull request #3292 from Carbenium/time-eventstream
BaseStream+EventStream: Convert internals to std::chrono
This commit is contained in:
commit
1fe30c848d
|
@ -578,17 +578,16 @@ void Event::AddFrame(
|
|||
SystemTimePoint timestamp_us = SystemTimePoint(zm::chrono::duration_cast<Microseconds>(timestamp));
|
||||
|
||||
if (db_frame) {
|
||||
FPSeconds delta_time = timestamp_us - start_time;
|
||||
Debug(1, "Frame delta is %.2f - %.2f = %.2f, score %u zone_stats.size %zu",
|
||||
std::chrono::duration_cast<FPSeconds>(timestamp_us.time_since_epoch()).count(),
|
||||
std::chrono::duration_cast<FPSeconds>(start_time.time_since_epoch()).count(),
|
||||
delta_time.count(),
|
||||
Microseconds delta_time = std::chrono::duration_cast<Microseconds>(timestamp_us - start_time);
|
||||
Debug(1, "Frame delta is %.2f s - %.2f s = %.2f s, score %u zone_stats.size %zu",
|
||||
FPSeconds(timestamp_us.time_since_epoch()).count(),
|
||||
FPSeconds(start_time.time_since_epoch()).count(),
|
||||
FPSeconds(delta_time).count(),
|
||||
score,
|
||||
zone_stats.size());
|
||||
|
||||
Milliseconds delta_time_ms = std::chrono::duration_cast<Milliseconds>(delta_time);
|
||||
// The idea is to write out 1/sec
|
||||
frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time_ms, score, zone_stats));
|
||||
frame_data.push(new Frame(id, frames, frame_type, timestamp, delta_time, score, zone_stats));
|
||||
double fps = monitor->get_capture_fps();
|
||||
if (write_to_db
|
||||
or
|
||||
|
@ -604,7 +603,7 @@ void Event::AddFrame(
|
|||
|
||||
std::string sql = stringtf(
|
||||
"UPDATE Events SET Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64,
|
||||
delta_time.count(),
|
||||
FPSeconds(delta_time).count(),
|
||||
frames,
|
||||
alarm_frames,
|
||||
tot_score,
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include "zm_storage.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <netinet/in.h>
|
||||
|
@ -40,7 +39,9 @@ const std::string EventStream::StreamMode_Strings[4] = {
|
|||
"Gapless"
|
||||
};
|
||||
|
||||
bool EventStream::loadInitialEventData(int monitor_id, time_t event_time) {
|
||||
constexpr Milliseconds EventStream::STREAM_PAUSE_WAIT;
|
||||
|
||||
bool EventStream::loadInitialEventData(int monitor_id, SystemTimePoint event_time) {
|
||||
std::string sql = stringtf("SELECT `Id` FROM `Events` WHERE "
|
||||
"`MonitorId` = %d AND unix_timestamp(`EndDateTime`) > %ld "
|
||||
"ORDER BY `Id` ASC LIMIT 1", monitor_id, event_time);
|
||||
|
@ -62,23 +63,26 @@ bool EventStream::loadInitialEventData(int monitor_id, time_t event_time) {
|
|||
|
||||
loadEventData(init_event_id);
|
||||
|
||||
if ( event_time ) {
|
||||
if (event_time.time_since_epoch() == Seconds(0)) {
|
||||
curr_stream_time = event_time;
|
||||
curr_frame_id = 1; // curr_frame_id is 1-based
|
||||
if ( event_time >= event_data->start_time ) {
|
||||
if (event_time >= event_data->start_time) {
|
||||
Debug(2, "event time is after event start");
|
||||
for ( unsigned int i = 0; i < event_data->frame_count; i++ ) {
|
||||
for (unsigned int i = 0; i < event_data->frame_count; i++) {
|
||||
//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;
|
||||
Debug(3, "Set curr_stream_time:%.2f, curr_frame_id:%ld", curr_stream_time, curr_frame_id);
|
||||
if (event_data->frames[i].timestamp >= event_time) {
|
||||
curr_frame_id = i + 1;
|
||||
Debug(3, "Set curr_stream_time: %.2f, curr_frame_id: %ld",
|
||||
FPSeconds(curr_stream_time.time_since_epoch()).count(),
|
||||
curr_frame_id);
|
||||
break;
|
||||
}
|
||||
} // end foreach frame
|
||||
Debug(3, "Skipping %ld frames", event_data->frame_count);
|
||||
} else {
|
||||
Warning("Requested an event time less than the start of the event. event_time %" PRIi64 " < start_time %" PRIi64,
|
||||
static_cast<int64>(event_time), static_cast<int64>(event_data->start_time));
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(event_time.time_since_epoch()).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Seconds>(event_data->start_time.time_since_epoch()).count()));
|
||||
}
|
||||
} // end if have a start time
|
||||
return true;
|
||||
|
@ -136,11 +140,12 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
event_data->monitor_id = atoi(dbrow[0]);
|
||||
event_data->storage_id = dbrow[1] ? atoi(dbrow[1]) : 0;
|
||||
event_data->frame_count = dbrow[2] == nullptr ? 0 : atoi(dbrow[2]);
|
||||
event_data->start_time = atoi(dbrow[3]);
|
||||
event_data->end_time = dbrow[4] ? atoi(dbrow[4]) : 0;
|
||||
event_data->duration = event_data->end_time - event_data->start_time;
|
||||
event_data->frames_duration = dbrow[5] ? atof(dbrow[5]) : 0.0;
|
||||
strncpy(event_data->video_file, dbrow[6], sizeof(event_data->video_file)-1);
|
||||
event_data->start_time = SystemTimePoint(Seconds(atoi(dbrow[3])));
|
||||
event_data->end_time = dbrow[4] ? SystemTimePoint(Seconds(atoi(dbrow[4]))) : SystemTimePoint();
|
||||
event_data->duration = std::chrono::duration_cast<Microseconds>(event_data->end_time - event_data->start_time);
|
||||
event_data->frames_duration =
|
||||
std::chrono::duration_cast<Microseconds>(dbrow[5] ? FPSeconds(atof(dbrow[5])) : FPSeconds(0.0));
|
||||
strncpy(event_data->video_file, dbrow[6], sizeof(event_data->video_file) - 1);
|
||||
std::string scheme_str = std::string(dbrow[7]);
|
||||
if ( scheme_str == "Deep" ) {
|
||||
event_data->scheme = Storage::DEEP;
|
||||
|
@ -172,7 +177,8 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
|
||||
if ( event_data->scheme == Storage::DEEP ) {
|
||||
tm event_time = {};
|
||||
localtime_r(&event_data->start_time, &event_time);
|
||||
time_t start_time_t = std::chrono::system_clock::to_time_t(event_data->start_time);
|
||||
localtime_r(&start_time_t, &event_time);
|
||||
|
||||
if ( storage_path[0] == '/' )
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
|
@ -188,7 +194,9 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
event_time.tm_hour, event_time.tm_min, event_time.tm_sec);
|
||||
} else if ( event_data->scheme == Storage::MEDIUM ) {
|
||||
tm event_time = {};
|
||||
localtime_r(&event_data->start_time, &event_time);
|
||||
time_t start_time_t = std::chrono::system_clock::to_time_t(event_data->start_time);
|
||||
localtime_r(&start_time_t, &event_time);
|
||||
|
||||
if ( storage_path[0] == '/' )
|
||||
snprintf(event_data->path, sizeof(event_data->path),
|
||||
"%s/%u/%04d-%02d-%02d/%" PRIu64,
|
||||
|
@ -212,7 +220,11 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
event_data->event_id);
|
||||
}
|
||||
|
||||
updateFrameRate((event_data->frame_count and event_data->duration) ? (double)event_data->frame_count/event_data->duration : 1);
|
||||
double fps = 1.0;
|
||||
if ((event_data->frame_count and event_data->duration != Seconds(0))) {
|
||||
fps = static_cast<double>(event_data->frame_count) / FPSeconds(event_data->duration).count();
|
||||
}
|
||||
updateFrameRate(fps);
|
||||
|
||||
sql = stringtf("SELECT `FrameId`, unix_timestamp(`TimeStamp`), `Delta` "
|
||||
"FROM `Frames` WHERE `EventId` = %" PRIu64 " ORDER BY `FrameId` ASC", event_id);
|
||||
|
@ -226,46 +238,47 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
|
||||
event_data->frames = new FrameData[event_data->frame_count];
|
||||
int last_id = 0;
|
||||
double last_timestamp = event_data->start_time;
|
||||
double last_delta = 0.0;
|
||||
SystemTimePoint last_timestamp = event_data->start_time;
|
||||
Microseconds last_delta = Seconds(0);
|
||||
|
||||
while ( ( dbrow = mysql_fetch_row(result) ) ) {
|
||||
int id = atoi(dbrow[0]);
|
||||
//timestamp = atof(dbrow[1]);
|
||||
double delta = atof(dbrow[2]);
|
||||
Microseconds delta = std::chrono::duration_cast<Microseconds>(FPSeconds(atof(dbrow[2])));
|
||||
int id_diff = id - last_id;
|
||||
double frame_delta = id_diff ? (delta-last_delta)/id_diff : (delta-last_delta);
|
||||
Microseconds frame_delta =
|
||||
std::chrono::duration_cast<Microseconds>(id_diff ? (delta - last_delta) / id_diff : (delta - last_delta));
|
||||
|
||||
// Fill in data between bulk frames
|
||||
if ( id_diff > 1 ) {
|
||||
for ( int i = last_id+1; i < id; i++ ) {
|
||||
if (id_diff > 1) {
|
||||
for (int i = last_id + 1; i < id; i++) {
|
||||
// Delta is the time since last frame, no since beginning of Event
|
||||
event_data->frames[i-1].delta = frame_delta;
|
||||
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;
|
||||
event_data->frames[i-1].in_db = false;
|
||||
Debug(3, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)",
|
||||
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
|
||||
);
|
||||
event_data->frames[i - 1].delta = frame_delta;
|
||||
event_data->frames[i - 1].timestamp = last_timestamp + ((i - last_id) * frame_delta);
|
||||
event_data->frames[i - 1].offset =
|
||||
std::chrono::duration_cast<Microseconds>(event_data->frames[i - 1].timestamp - event_data->start_time);
|
||||
event_data->frames[i - 1].in_db = false;
|
||||
Debug(3, "Frame %d timestamp (%f s), offset (%f s) delta (%f s), in_db (%d)",
|
||||
i,
|
||||
FPSeconds(event_data->frames[i - 1].timestamp.time_since_epoch()).count(),
|
||||
FPSeconds(event_data->frames[i - 1].offset).count(),
|
||||
FPSeconds(event_data->frames[i - 1].delta).count(),
|
||||
event_data->frames[i - 1].in_db);
|
||||
}
|
||||
}
|
||||
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;
|
||||
event_data->frames[id-1].in_db = true;
|
||||
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;
|
||||
event_data->frames[id - 1].in_db = true;
|
||||
last_id = id;
|
||||
last_delta = delta;
|
||||
last_timestamp = event_data->frames[id-1].timestamp;
|
||||
Debug(3, "Frame %d timestamp:(%f), offset(%f) delta(%f), in_db(%d)",
|
||||
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
|
||||
);
|
||||
Debug(3, "Frame %d timestamp (%f s), offset (%f s), delta(%f s), in_db(%d)",
|
||||
id,
|
||||
FPSeconds(event_data->frames[id - 1].timestamp.time_since_epoch()).count(),
|
||||
FPSeconds(event_data->frames[id - 1].offset).count(),
|
||||
FPSeconds(event_data->frames[id - 1].delta).count(),
|
||||
event_data->frames[id - 1].in_db);
|
||||
}
|
||||
// Incomplete events might not have any frame data
|
||||
event_data->last_frame_id = last_id;
|
||||
|
@ -300,8 +313,12 @@ bool EventStream::loadEventData(uint64_t event_id) {
|
|||
else
|
||||
curr_stream_time = event_data->frames[event_data->last_frame_id-1].timestamp;
|
||||
}
|
||||
Debug(2, "Event:%" PRIu64 ", Frames:%ld, Last Frame ID(%ld, Duration: %.2f Frames Duration: %.2f",
|
||||
event_data->event_id, event_data->frame_count, event_data->last_frame_id, event_data->duration, event_data->frames_duration);
|
||||
Debug(2, "Event: %" PRIu64 ", Frames: %ld, Last Frame ID (%ld, Duration: %.2f s Frames Duration: %.2f s",
|
||||
event_data->event_id,
|
||||
event_data->frame_count,
|
||||
event_data->last_frame_id,
|
||||
FPSeconds(event_data->duration).count(),
|
||||
FPSeconds(event_data->frames_duration).count());
|
||||
|
||||
return true;
|
||||
} // bool EventStream::loadEventData( int event_id )
|
||||
|
@ -316,7 +333,7 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
// Set paused flag
|
||||
paused = true;
|
||||
replay_rate = ZM_RATE_BASE;
|
||||
last_frame_sent = TV_2_FLOAT(now);
|
||||
last_frame_sent = now;
|
||||
break;
|
||||
case CMD_PLAY :
|
||||
Debug(1, "Got PLAY command");
|
||||
|
@ -503,25 +520,25 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
break;
|
||||
case CMD_SEEK :
|
||||
{
|
||||
// offset is in seconds
|
||||
double int_part = ((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];
|
||||
double dec_part = ((unsigned char) msg->msg_data[5] << 24) | ((unsigned char) msg->msg_data[6] << 16)
|
||||
| ((unsigned char) msg->msg_data[7] << 8) | (unsigned char) msg->msg_data[8];
|
||||
|
||||
int int_part = ((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];
|
||||
int dec_part = ((unsigned char)msg->msg_data[5]<<24)|((unsigned char)msg->msg_data[6]<<16)|((unsigned char)msg->msg_data[7]<<8)|(unsigned char)msg->msg_data[8];
|
||||
|
||||
double offset = (double)int_part + (double)(dec_part / (double)1000000);
|
||||
if ( offset < 0.0 ) {
|
||||
FPSeconds offset = FPSeconds(int_part + dec_part / 1000000.0);
|
||||
if (offset < Seconds(0)) {
|
||||
Warning("Invalid offset, not seeking");
|
||||
break;
|
||||
}
|
||||
|
||||
// This should get us close, but not all frames will have the same duration
|
||||
curr_frame_id = (int)(event_data->frame_count*offset/event_data->duration)+1;
|
||||
if ( event_data->frames[curr_frame_id-1].offset > offset ) {
|
||||
while ( (curr_frame_id --) && ( event_data->frames[curr_frame_id-1].offset > offset ) ) {
|
||||
}
|
||||
} else if ( event_data->frames[curr_frame_id-1].offset < offset ) {
|
||||
while ( (curr_frame_id ++) && ( event_data->frames[curr_frame_id-1].offset > offset ) ) {
|
||||
}
|
||||
curr_frame_id = (int) (event_data->frame_count * offset / event_data->duration) + 1;
|
||||
if (event_data->frames[curr_frame_id - 1].offset > offset) {
|
||||
while ((curr_frame_id--) && (event_data->frames[curr_frame_id - 1].offset > offset)) {}
|
||||
} else if (event_data->frames[curr_frame_id - 1].offset < offset) {
|
||||
while ((curr_frame_id++) && (event_data->frames[curr_frame_id - 1].offset > offset)) {}
|
||||
}
|
||||
|
||||
if ( curr_frame_id < 1 ) {
|
||||
curr_frame_id = 1;
|
||||
} else if ( (unsigned long)curr_frame_id > event_data->last_frame_id ) {
|
||||
|
@ -529,8 +546,10 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
}
|
||||
|
||||
curr_stream_time = event_data->frames[curr_frame_id-1].timestamp;
|
||||
Debug(1, "Got SEEK command, to %f (new current frame id: %ld offset %f)",
|
||||
offset, curr_frame_id, event_data->frames[curr_frame_id-1].offset);
|
||||
Debug(1, "Got SEEK command, to %f s (new current frame id: %ld offset %f s)",
|
||||
FPSeconds(offset).count(),
|
||||
curr_frame_id,
|
||||
FPSeconds(event_data->frames[curr_frame_id - 1].offset).count());
|
||||
send_frame = true;
|
||||
break;
|
||||
}
|
||||
|
@ -547,12 +566,12 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
|
||||
struct {
|
||||
uint64_t event_id;
|
||||
double duration;
|
||||
double progress;
|
||||
Microseconds duration;
|
||||
Microseconds progress;
|
||||
int rate;
|
||||
int zoom;
|
||||
bool paused;
|
||||
} status_data;
|
||||
} status_data = {};
|
||||
|
||||
status_data.event_id = event_data->event_id;
|
||||
status_data.duration = event_data->duration;
|
||||
|
@ -561,13 +580,12 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
status_data.zoom = zoom;
|
||||
status_data.paused = paused;
|
||||
Debug(2, "Event:%" PRIu64 ", Duration %f, Paused:%d, progress:%f Rate:%d, Zoom:%d",
|
||||
status_data.event_id,
|
||||
status_data.duration,
|
||||
status_data.paused,
|
||||
status_data.progress,
|
||||
status_data.rate,
|
||||
status_data.zoom
|
||||
);
|
||||
status_data.event_id,
|
||||
FPSeconds(status_data.duration).count(),
|
||||
status_data.paused,
|
||||
FPSeconds(status_data.progress).count(),
|
||||
status_data.rate,
|
||||
status_data.zoom);
|
||||
|
||||
DataMsg status_msg;
|
||||
status_msg.msg_type = MSG_DATA_EVENT;
|
||||
|
@ -580,11 +598,17 @@ void EventStream::processCommand(const CmdMsg *msg) {
|
|||
//exit(-1);
|
||||
}
|
||||
}
|
||||
// quit after sending a status, if this was a quit request
|
||||
if ( (MsgCommand)msg->msg_data[0] == CMD_QUIT )
|
||||
exit(0);
|
||||
|
||||
updateFrameRate((event_data->frame_count and event_data->duration) ? (double)event_data->frame_count/event_data->duration : 1);
|
||||
// quit after sending a status, if this was a quit request
|
||||
if (static_cast<MsgCommand>(msg->msg_data[0]) == CMD_QUIT) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
double fps = 1.0;
|
||||
if ((event_data->frame_count and event_data->duration != Seconds(0))) {
|
||||
fps = static_cast<double>(event_data->frame_count) / FPSeconds(event_data->duration).count();
|
||||
}
|
||||
updateFrameRate(fps);
|
||||
} // void EventStream::processCommand(const CmdMsg *msg)
|
||||
|
||||
bool EventStream::checkEventLoaded() {
|
||||
|
@ -595,7 +619,7 @@ bool EventStream::checkEventLoaded() {
|
|||
"SELECT `Id` FROM `Events` WHERE `MonitorId` = %d AND `Id` < %" PRIu64 " ORDER BY `Id` DESC LIMIT 1",
|
||||
event_data->monitor_id, event_data->event_id);
|
||||
} else if ( (unsigned int)curr_frame_id > event_data->last_frame_id ) {
|
||||
if ( !event_data->end_time ) {
|
||||
if (event_data->end_time.time_since_epoch() == Seconds(0)) {
|
||||
// We are viewing an in-process event, so just reload it.
|
||||
loadEventData(event_data->event_id);
|
||||
if ( (unsigned int)curr_frame_id > event_data->last_frame_id )
|
||||
|
@ -674,7 +698,7 @@ Image * EventStream::getImage( ) {
|
|||
return image;
|
||||
}
|
||||
|
||||
bool EventStream::sendFrame(int delta_us) {
|
||||
bool EventStream::sendFrame(Microseconds delta_us) {
|
||||
Debug(2, "Sending frame %ld", curr_frame_id);
|
||||
|
||||
static char filepath[PATH_MAX];
|
||||
|
@ -710,7 +734,10 @@ bool EventStream::sendFrame(int delta_us) {
|
|||
fprintf(stdout, "Content-type: %s\r\n\r\n", vid_stream->MimeType());
|
||||
vid_stream->OpenStream();
|
||||
}
|
||||
/* double pts = */ vid_stream->EncodeFrame(send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_us*1000);
|
||||
vid_stream->EncodeFrame(send_image->Buffer(),
|
||||
send_image->Size(),
|
||||
config.mpeg_timed_frames,
|
||||
delta_us.count() * 1000);
|
||||
} else {
|
||||
bool send_raw = (type == STREAM_JPEG) && ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE)) && filepath[0];
|
||||
|
||||
|
@ -729,9 +756,8 @@ bool EventStream::sendFrame(int delta_us) {
|
|||
} else if ( ffmpeg_input ) {
|
||||
// Get the frame from the mp4 input
|
||||
FrameData *frame_data = &event_data->frames[curr_frame_id-1];
|
||||
AVFrame *frame = ffmpeg_input->get_frame(
|
||||
ffmpeg_input->get_video_stream_id(),
|
||||
frame_data->offset);
|
||||
AVFrame *frame =
|
||||
ffmpeg_input->get_frame(ffmpeg_input->get_video_stream_id(), FPSeconds(frame_data->offset).count());
|
||||
if ( frame ) {
|
||||
image = new Image(frame);
|
||||
//av_frame_free(&frame);
|
||||
|
@ -806,7 +832,7 @@ bool EventStream::sendFrame(int delta_us) {
|
|||
|
||||
fputs("\r\n", stdout);
|
||||
fflush(stdout);
|
||||
last_frame_sent = TV_2_FLOAT(now);
|
||||
last_frame_sent = now;
|
||||
return true;
|
||||
} // bool EventStream::sendFrame( int delta_us )
|
||||
|
||||
|
@ -823,17 +849,21 @@ void EventStream::runStream() {
|
|||
exit(0);
|
||||
}
|
||||
|
||||
updateFrameRate((event_data->frame_count and event_data->duration) ? (double)event_data->frame_count/event_data->duration : 1);
|
||||
gettimeofday(&start, nullptr);
|
||||
uint64_t start_usec = start.tv_sec * 1000000 + start.tv_usec;
|
||||
uint64_t last_frame_offset = 0;
|
||||
double fps = 1.0;
|
||||
if ((event_data->frame_count and event_data->duration != Seconds(0))) {
|
||||
fps = static_cast<double>(event_data->frame_count) / FPSeconds(event_data->duration).count();
|
||||
}
|
||||
updateFrameRate(fps);
|
||||
|
||||
double time_to_event = 0;
|
||||
start = std::chrono::system_clock::now();
|
||||
|
||||
SystemTimePoint::duration last_frame_offset = Seconds(0);
|
||||
SystemTimePoint::duration time_to_event = Seconds(0);
|
||||
|
||||
while ( !zm_terminate ) {
|
||||
gettimeofday(&now, nullptr);
|
||||
now = std::chrono::system_clock::now();
|
||||
|
||||
int delta_us = 0;
|
||||
Microseconds delta = Microseconds(0);
|
||||
send_frame = false;
|
||||
|
||||
if ( connkey ) {
|
||||
|
@ -843,7 +873,7 @@ void EventStream::runStream() {
|
|||
}
|
||||
|
||||
// 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 ) {
|
||||
if (now - last_comm_update > Hours(1)) {
|
||||
touch(sock_path_lock);
|
||||
last_comm_update = now;
|
||||
}
|
||||
|
@ -869,8 +899,7 @@ void EventStream::runStream() {
|
|||
send_frame = true;
|
||||
} else if ( !send_frame ) {
|
||||
// We are paused, not stepping and doing nothing, meaning that comms didn't set send_frame to true
|
||||
double time_since_last_send = TV_2_FLOAT(now) - last_frame_sent;
|
||||
if ( time_since_last_send > MAX_STREAM_DELAY ) {
|
||||
if (now - last_frame_sent > MAX_STREAM_DELAY) {
|
||||
// Send keepalive
|
||||
Debug(2, "Sending keepalive frame");
|
||||
send_frame = true;
|
||||
|
@ -878,39 +907,47 @@ void EventStream::runStream() {
|
|||
} // end if streaming stepping or doing nothing
|
||||
|
||||
// time_to_event > 0 means that we are not in the event
|
||||
if ( ( time_to_event > 0 ) and ( mode == MODE_ALL ) ) {
|
||||
double time_since_last_send = TV_2_FLOAT(now) - last_frame_sent;
|
||||
Debug(1, "Time since last send = %f = %f - %f", time_since_last_send, TV_2_FLOAT(now), last_frame_sent);
|
||||
if ( time_since_last_send > 1 /* second */ ) {
|
||||
static char frame_text[64];
|
||||
if (time_to_event > Seconds(0) and mode == MODE_ALL) {
|
||||
SystemTimePoint::duration time_since_last_send = now - last_frame_sent;
|
||||
Debug(1, "Time since last send = %.2f s", FPSeconds(time_since_last_send).count());
|
||||
if (time_since_last_send > Seconds(1)) {
|
||||
char frame_text[64];
|
||||
|
||||
snprintf(frame_text, sizeof(frame_text), "Time to %s event = %d seconds",
|
||||
(replay_rate > 0 ? "next" : "previous" ), (int)time_to_event);
|
||||
if ( !sendTextFrame(frame_text) )
|
||||
snprintf(frame_text, sizeof(frame_text), "Time to %s event = %f s",
|
||||
(replay_rate > 0 ? "next" : "previous"),
|
||||
FPSeconds(time_to_event).count());
|
||||
|
||||
if (!sendTextFrame(frame_text)) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
|
||||
send_frame = false; // In case keepalive was set
|
||||
}
|
||||
|
||||
// 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));
|
||||
Milliseconds sleep_time = std::chrono::duration_cast<Milliseconds>(
|
||||
(replay_rate > 0 ? 1 : -1) * ((1.0L * replay_rate * STREAM_PAUSE_WAIT) / ZM_RATE_BASE));
|
||||
//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;
|
||||
if (sleep_time == Seconds(0)) {
|
||||
sleep_time += STREAM_PAUSE_WAIT;
|
||||
}
|
||||
|
||||
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);
|
||||
Debug(2, "Sleeping (%" PRIi64 " ms) because we are not at the next event yet, adding %" PRIi64 " ms",
|
||||
static_cast<int64>(Milliseconds(STREAM_PAUSE_WAIT).count()),
|
||||
static_cast<int64>(Milliseconds(sleep_time).count()));
|
||||
std::this_thread::sleep_for(STREAM_PAUSE_WAIT);
|
||||
|
||||
//curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000);
|
||||
//}
|
||||
continue;
|
||||
} // end if !in_event
|
||||
|
||||
if ( send_frame ) {
|
||||
if ( !sendFrame(delta_us) ) {
|
||||
if (send_frame) {
|
||||
if (!sendFrame(delta)) {
|
||||
zm_terminate = true;
|
||||
break;
|
||||
}
|
||||
|
@ -918,23 +955,30 @@ void EventStream::runStream() {
|
|||
|
||||
curr_stream_time = frame_data->timestamp;
|
||||
|
||||
if ( !paused ) {
|
||||
|
||||
if (!paused) {
|
||||
// delta is since the last frame
|
||||
delta_us = (unsigned int)(frame_data->delta * 1000000);
|
||||
Debug(3, "frame delta %uus ", delta_us);
|
||||
delta = std::chrono::duration_cast<Microseconds>(frame_data->delta);
|
||||
Debug(3, "frame delta %" PRIi64 "us ",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||
|
||||
// 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);
|
||||
delta = std::chrono::duration_cast<Microseconds>((delta * base_fps) / effective_fps);
|
||||
Debug(3, "delta %" PRIi64 " us = base_fps (%f) / effective_fps (%f)",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()),
|
||||
base_fps,
|
||||
effective_fps);
|
||||
|
||||
// but must not exceed maxfps
|
||||
delta_us = std::max(delta_us, int(1000000/maxfps));
|
||||
Debug(3, "delta %u = base_fps(%f)/effective fps(%f) from 30fps", delta_us, base_fps, effective_fps);
|
||||
delta = std::max(delta, Microseconds(lround(Microseconds::period::den / maxfps)));
|
||||
Debug(3, "delta %" PRIi64 " us = base_fps (%f) /effective_fps (%f) from 30fps",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()),
|
||||
base_fps,
|
||||
effective_fps);
|
||||
|
||||
// +/- 1? What if we are skipping frames?
|
||||
curr_frame_id += (replay_rate>0) ? frame_mod : -1*frame_mod;
|
||||
// sending the frame may have taken some time, so reload now
|
||||
gettimeofday(&now, nullptr);
|
||||
uint64_t now_usec = (now.tv_sec * 1000000 + now.tv_usec);
|
||||
now = std::chrono::system_clock::now();
|
||||
|
||||
// we incremented by replay_rate, so might have jumped past frame_count
|
||||
if ( (mode == MODE_SINGLE) && (
|
||||
|
@ -945,8 +989,8 @@ void EventStream::runStream() {
|
|||
) {
|
||||
Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start");
|
||||
curr_frame_id = 1;
|
||||
// Have to reset start_usec to now when replaying
|
||||
start_usec = now_usec;
|
||||
// Have to reset start to now when replaying
|
||||
start = now;
|
||||
}
|
||||
frame_data = &event_data->frames[curr_frame_id-1];
|
||||
|
||||
|
@ -958,43 +1002,53 @@ void EventStream::runStream() {
|
|||
// 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
|
||||
//
|
||||
if ( last_frame_offset ) {
|
||||
if (last_frame_offset != Seconds(0)) {
|
||||
// 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 " = %" PRIu64 " offset %f - elapsed = %dusec",
|
||||
now_usec, start_usec, static_cast<uint64>(now_usec - start_usec), frame_data->offset * 1000000, delta_us);
|
||||
delta = std::chrono::duration_cast<Microseconds>(frame_data->offset - (now - start));
|
||||
|
||||
Debug(2, "New delta: now - start = %" PRIu64 " us offset %" PRIi64 " us- elapsed = %" PRIu64 " us",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(now - start).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(frame_data->offset).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||
} else {
|
||||
Debug(2, "No last frame_offset, no sleep");
|
||||
delta_us = 0;
|
||||
delta = Seconds(0);
|
||||
}
|
||||
last_frame_offset = frame_data->offset * 1000000;
|
||||
last_frame_offset = frame_data->offset;
|
||||
|
||||
if ( send_frame && (type != STREAM_MPEG) ) {
|
||||
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;
|
||||
if (send_frame && type != STREAM_MPEG) {
|
||||
if (delta != Seconds(0)) {
|
||||
if (delta > MAX_SLEEP) {
|
||||
Debug(1, "Limiting sleep to %" PRIi64 " ms because calculated sleep is too long: %" PRIi64" us",
|
||||
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(MAX_SLEEP).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||
delta = MAX_SLEEP;
|
||||
}
|
||||
usleep(delta_us);
|
||||
Debug(3, "Done sleeping: %d usec", delta_us);
|
||||
|
||||
std::this_thread::sleep_for(delta);
|
||||
Debug(3, "Done sleeping: %" PRIi64 " us",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
delta_us = ((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*(replay_rate?abs(replay_rate*2):2)));
|
||||
delta = std::chrono::duration_cast<Microseconds>(FPSeconds(
|
||||
ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2))));
|
||||
|
||||
Debug(2, "Sleeping %d because 1000000 * ZM_RATE_BASE(%d) / ( base_fps (%f), replay_rate(%d)",
|
||||
delta_us,
|
||||
ZM_RATE_BASE,
|
||||
(base_fps ? base_fps : 1),
|
||||
(replay_rate ? abs(replay_rate*2) : 0)
|
||||
);
|
||||
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;
|
||||
Debug(2, "Sleeping %" PRIi64 " us because ZM_RATE_BASE (%d) / ( base_fps (%f) * replay_rate (%d)",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()),
|
||||
ZM_RATE_BASE,
|
||||
(base_fps ? base_fps : 1),
|
||||
(replay_rate ? abs(replay_rate * 2) : 0));
|
||||
|
||||
if (delta != Seconds(0)) {
|
||||
if (delta > MAX_SLEEP) {
|
||||
Debug(1, "Limiting sleep to %" PRIi64 " ms because calculated sleep is too long %" PRIi64,
|
||||
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(MAX_SLEEP).count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count()));
|
||||
delta = MAX_SLEEP;
|
||||
}
|
||||
usleep(delta_us);
|
||||
|
||||
std::this_thread::sleep_for(delta);
|
||||
}
|
||||
// We are paused, so might be stepping
|
||||
//if ( step != 0 )// Adding 0 is cheaper than an if 0
|
||||
|
@ -1011,15 +1065,19 @@ void EventStream::runStream() {
|
|||
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);
|
||||
Debug(1, "replay rate (%d) time_to_event (%f s) = frame timestamp (%f s) - curr_stream_time (%f s)",
|
||||
replay_rate,
|
||||
FPSeconds(time_to_event).count(),
|
||||
FPSeconds(event_data->frames[0].timestamp.time_since_epoch()).count(),
|
||||
FPSeconds(curr_stream_time.time_since_epoch()).count());
|
||||
|
||||
} 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);
|
||||
Debug(1, "replay rate (%d), time_to_event(%f s) = curr_stream_time (%f s) - frame timestamp (%f s)",
|
||||
replay_rate,
|
||||
FPSeconds(time_to_event).count(),
|
||||
FPSeconds(curr_stream_time.time_since_epoch()).count(),
|
||||
FPSeconds(event_data->frames[event_data->frame_count - 1].timestamp.time_since_epoch()).count());
|
||||
} // end if forward or reverse
|
||||
} // end if checkEventLoaded
|
||||
} // end while ! zm_terminate
|
||||
|
@ -1036,7 +1094,7 @@ bool EventStream::send_file(const char *filepath) {
|
|||
int img_buffer_size = 0;
|
||||
uint8_t *img_buffer = temp_img_buffer;
|
||||
|
||||
FILE *fdj = NULL;
|
||||
FILE *fdj = nullptr;
|
||||
fdj = fopen(filepath, "rb");
|
||||
if ( !fdj ) {
|
||||
Error("Can't open %s: %s", filepath, strerror(errno));
|
||||
|
|
|
@ -40,10 +40,10 @@ class EventStream : public StreamBase {
|
|||
protected:
|
||||
struct FrameData {
|
||||
//unsigned long id;
|
||||
double timestamp;
|
||||
double offset;
|
||||
double delta;
|
||||
bool in_db;
|
||||
SystemTimePoint timestamp;
|
||||
Microseconds offset;
|
||||
Microseconds delta;
|
||||
bool in_db;
|
||||
};
|
||||
|
||||
struct EventData {
|
||||
|
@ -52,10 +52,10 @@ class EventStream : public StreamBase {
|
|||
unsigned long storage_id;
|
||||
unsigned long frame_count; // Value of Frames column in Event
|
||||
unsigned long last_frame_id; // Highest frame id known about. Can be < frame_count in incomplete events
|
||||
time_t start_time;
|
||||
time_t end_time;
|
||||
double duration;
|
||||
double frames_duration;
|
||||
SystemTimePoint start_time;
|
||||
SystemTimePoint end_time;
|
||||
Microseconds duration;
|
||||
Microseconds frames_duration;
|
||||
char path[PATH_MAX];
|
||||
int n_frames; // # of frame rows returned from database
|
||||
FrameData *frames;
|
||||
|
@ -66,7 +66,7 @@ class EventStream : public StreamBase {
|
|||
};
|
||||
|
||||
protected:
|
||||
static const int STREAM_PAUSE_WAIT = 250000; // Microseconds
|
||||
static constexpr Milliseconds STREAM_PAUSE_WAIT = Milliseconds(250);
|
||||
|
||||
static const StreamMode DEFAULT_MODE = MODE_SINGLE;
|
||||
|
||||
|
@ -74,32 +74,32 @@ class EventStream : public StreamBase {
|
|||
bool forceEventChange;
|
||||
|
||||
long curr_frame_id;
|
||||
double curr_stream_time;
|
||||
SystemTimePoint curr_stream_time;
|
||||
bool send_frame;
|
||||
struct timeval start; // clock time when started the event
|
||||
SystemTimePoint start; // clock time when started the event
|
||||
|
||||
EventData *event_data;
|
||||
|
||||
protected:
|
||||
bool loadEventData(uint64_t event_id);
|
||||
bool loadInitialEventData(uint64_t init_event_id, unsigned int init_frame_id);
|
||||
bool loadInitialEventData(int monitor_id, time_t event_time);
|
||||
bool loadInitialEventData(int monitor_id, SystemTimePoint event_time);
|
||||
|
||||
bool checkEventLoaded();
|
||||
void processCommand(const CmdMsg *msg) override;
|
||||
bool sendFrame(int delta_us);
|
||||
bool sendFrame(Microseconds delta);
|
||||
|
||||
public:
|
||||
EventStream() :
|
||||
mode(DEFAULT_MODE),
|
||||
forceEventChange(false),
|
||||
curr_frame_id(0),
|
||||
curr_stream_time(0.0),
|
||||
send_frame(false),
|
||||
event_data(nullptr),
|
||||
storage(nullptr),
|
||||
ffmpeg_input(nullptr)
|
||||
{}
|
||||
|
||||
~EventStream() {
|
||||
if ( event_data ) {
|
||||
if ( event_data->frames ) {
|
||||
|
|
|
@ -92,7 +92,7 @@ bool FifoStream::sendMJEGFrames() {
|
|||
}
|
||||
fprintf(stdout, "\r\n\r\n");
|
||||
fflush(stdout);
|
||||
last_frame_sent = TV_2_FLOAT(now);
|
||||
last_frame_sent = now;
|
||||
frame_count++;
|
||||
return true;
|
||||
}
|
||||
|
@ -156,8 +156,9 @@ void FifoStream::runStream() {
|
|||
}
|
||||
|
||||
while ( !zm_terminate ) {
|
||||
gettimeofday(&now, nullptr);
|
||||
now = std::chrono::system_clock::now();
|
||||
checkCommandQueue();
|
||||
|
||||
if ( stream_type == MJPEG ) {
|
||||
if ( !sendMJEGFrames() )
|
||||
zm_terminate = true;
|
||||
|
|
|
@ -4,7 +4,7 @@ Frame::Frame(event_id_t p_event_id,
|
|||
int p_frame_id,
|
||||
FrameType p_type,
|
||||
struct timeval p_timestamp,
|
||||
Milliseconds p_delta,
|
||||
Microseconds p_delta,
|
||||
int p_score,
|
||||
std::vector<ZoneStats> p_stats)
|
||||
: event_id(p_event_id),
|
||||
|
|
|
@ -42,7 +42,7 @@ class Frame {
|
|||
int p_frame_id,
|
||||
FrameType p_type,
|
||||
struct timeval p_timestamp,
|
||||
Milliseconds p_delta,
|
||||
Microseconds p_delta,
|
||||
int p_score,
|
||||
std::vector<ZoneStats> p_stats
|
||||
);
|
||||
|
@ -51,7 +51,7 @@ class Frame {
|
|||
int frame_id;
|
||||
FrameType type;
|
||||
struct timeval timestamp;
|
||||
Milliseconds delta;
|
||||
Microseconds delta;
|
||||
int score;
|
||||
std::vector<ZoneStats> zone_stats;
|
||||
};
|
||||
|
|
|
@ -85,7 +85,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
Debug(1, "Got PAUSE command");
|
||||
paused = true;
|
||||
delayed = true;
|
||||
last_frame_sent = TV_2_FLOAT(now);
|
||||
last_frame_sent = now;
|
||||
break;
|
||||
case CMD_PLAY :
|
||||
Debug(1, "Got PLAY command");
|
||||
|
@ -280,7 +280,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
status_data.delayed = delayed;
|
||||
status_data.paused = paused;
|
||||
status_data.rate = replay_rate;
|
||||
status_data.delay = TV_2_FLOAT(now) - TV_2_FLOAT(last_frame_timestamp);
|
||||
status_data.delay = FPSeconds(now - last_frame_timestamp).count();
|
||||
status_data.zoom = zoom;
|
||||
Debug(2, "fps: %.2f capture_fps: %.2f analysis_fps: %.2f Buffer Level:%d, Delayed:%d, Paused:%d, Rate:%d, delay:%.3f, Zoom:%d, Enabled:%d Forced:%d",
|
||||
status_data.fps,
|
||||
|
@ -320,7 +320,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) {
|
|||
//updateFrameRate(monitor->GetFPS());
|
||||
} // end void MonitorStream::processCommand(const CmdMsg *msg)
|
||||
|
||||
bool MonitorStream::sendFrame(const char *filepath, const timeval ×tamp) {
|
||||
bool MonitorStream::sendFrame(const char *filepath, SystemTimePoint timestamp) {
|
||||
bool send_raw = ((scale>=ZM_SCALE_BASE)&&(zoom==ZM_SCALE_BASE));
|
||||
|
||||
if (
|
||||
|
@ -350,8 +350,8 @@ bool MonitorStream::sendFrame(const char *filepath, const timeval ×tamp) {
|
|||
TimePoint send_start_time = std::chrono::steady_clock::now();
|
||||
|
||||
if (
|
||||
(0 > fprintf(stdout, "Content-Length: %d\r\nX-Timestamp: %d.%06d\r\n\r\n",
|
||||
img_buffer_size, (int)timestamp.tv_sec, (int)timestamp.tv_usec))
|
||||
(0 > fprintf(stdout, "Content-Length: %d\r\nX-Timestamp: %.6f\r\n\r\n",
|
||||
img_buffer_size, std::chrono::duration_cast<FPSeconds>(timestamp.time_since_epoch()).count()))
|
||||
||
|
||||
(fwrite(img_buffer, img_buffer_size, 1, stdout) != 1)
|
||||
) {
|
||||
|
@ -363,26 +363,27 @@ bool MonitorStream::sendFrame(const char *filepath, const timeval ×tamp) {
|
|||
fflush(stdout);
|
||||
|
||||
TimePoint send_end_time = std::chrono::steady_clock::now();
|
||||
Milliseconds frame_send_time = std::chrono::duration_cast<Milliseconds>(send_end_time - send_start_time);
|
||||
TimePoint::duration frame_send_time = send_end_time - send_start_time;
|
||||
|
||||
if (frame_send_time > Milliseconds(lround(Milliseconds::period::den / maxfps))) {
|
||||
maxfps /= 2;
|
||||
Info("Frame send time %" PRIi64 " msec too slow, throttling maxfps to %.2f",
|
||||
static_cast<int64>(frame_send_time.count()),
|
||||
Info("Frame send time %" PRIi64 " ms too slow, throttling maxfps to %.2f",
|
||||
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
|
||||
maxfps);
|
||||
}
|
||||
|
||||
last_frame_sent = TV_2_FLOAT(now);
|
||||
last_frame_sent = now;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} // end bool MonitorStream::sendFrame(const char *filepath, struct timeval *timestamp)
|
||||
|
||||
bool MonitorStream::sendFrame(Image *image, const timeval ×tamp) {
|
||||
bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) {
|
||||
Image *send_image = prepareImage(image);
|
||||
if (!config.timestamp_on_capture)
|
||||
monitor->TimestampImage(send_image, timestamp);
|
||||
if (!config.timestamp_on_capture) {
|
||||
monitor->TimestampImage(send_image, zm::chrono::duration_cast<timeval>(timestamp.time_since_epoch()));
|
||||
}
|
||||
|
||||
fputs("--" BOUNDARY "\r\n", stdout);
|
||||
if ( type == STREAM_MPEG ) {
|
||||
|
@ -392,13 +393,11 @@ bool MonitorStream::sendFrame(Image *image, const timeval ×tamp) {
|
|||
vid_stream->OpenStream();
|
||||
}
|
||||
|
||||
static struct timeval base_time;
|
||||
Milliseconds delta_time =
|
||||
zm::chrono::duration_cast<Milliseconds>(timestamp) - zm::chrono::duration_cast<Milliseconds>(base_time);
|
||||
|
||||
static SystemTimePoint base_time;
|
||||
if (!frame_count) {
|
||||
base_time = timestamp;
|
||||
}
|
||||
SystemTimePoint::duration delta_time = timestamp - base_time;
|
||||
|
||||
/* double pts = */ vid_stream->EncodeFrame(send_image->Buffer(), send_image->Size(), config.mpeg_timed_frames, delta_time.count());
|
||||
} else {
|
||||
|
@ -436,8 +435,8 @@ bool MonitorStream::sendFrame(Image *image, const timeval ×tamp) {
|
|||
return false;
|
||||
}
|
||||
if (
|
||||
( 0 > fprintf(stdout, "Content-Length: %d\r\nX-Timestamp: %d.%06d\r\n\r\n",
|
||||
img_buffer_size, (int)timestamp.tv_sec, (int)timestamp.tv_usec) )
|
||||
(0 > fprintf(stdout, "Content-Length: %d\r\nX-Timestamp: %.6f\r\n\r\n",
|
||||
img_buffer_size, std::chrono::duration_cast<FPSeconds>(timestamp.time_since_epoch()).count()))
|
||||
||
|
||||
(fwrite(img_buffer, img_buffer_size, 1, stdout) != 1)
|
||||
) {
|
||||
|
@ -451,16 +450,16 @@ bool MonitorStream::sendFrame(Image *image, const timeval ×tamp) {
|
|||
fflush(stdout);
|
||||
|
||||
TimePoint send_end_time = std::chrono::steady_clock::now();
|
||||
Milliseconds frame_send_time = std::chrono::duration_cast<Milliseconds>(send_end_time - send_start_time);
|
||||
TimePoint::duration frame_send_time = send_end_time - send_start_time;
|
||||
|
||||
if (frame_send_time > Milliseconds(lround(Milliseconds::period::den / maxfps))) {
|
||||
maxfps /= 1.5;
|
||||
Warning("Frame send time %" PRIi64 " msec too slow, throttling maxfps to %.2f",
|
||||
static_cast<int64>(frame_send_time.count()),
|
||||
static_cast<int64>(std::chrono::duration_cast<Milliseconds>(frame_send_time).count()),
|
||||
maxfps);
|
||||
}
|
||||
} // Not mpeg
|
||||
last_frame_sent = TV_2_FLOAT(now);
|
||||
last_frame_sent = now;
|
||||
return true;
|
||||
} // end bool MonitorStream::sendFrame( Image *image, const timeval ×tamp )
|
||||
|
||||
|
@ -503,8 +502,7 @@ void MonitorStream::runStream() {
|
|||
// point to end which is theoretically not a valid value because all indexes are % image_buffer_count
|
||||
int32_t last_read_index = monitor->image_buffer_count;
|
||||
|
||||
time_t stream_start_time;
|
||||
time(&stream_start_time);
|
||||
SystemTimePoint stream_start_time = std::chrono::system_clock::now();
|
||||
|
||||
frame_count = 0;
|
||||
|
||||
|
@ -518,7 +516,7 @@ void MonitorStream::runStream() {
|
|||
|
||||
// Last image and timestamp when paused, will be resent occasionally to prevent timeout
|
||||
Image *paused_image = nullptr;
|
||||
struct timeval paused_timestamp;
|
||||
SystemTimePoint paused_timestamp;
|
||||
|
||||
if ( connkey && ( playback_buffer > 0 ) ) {
|
||||
// 15 is the max length for the swap path suffix, /zmswap-whatever, assuming max 6 digits for monitor id
|
||||
|
@ -554,7 +552,6 @@ void MonitorStream::runStream() {
|
|||
} else {
|
||||
Debug(2, "Assigning temporary buffer");
|
||||
temp_image_buffer = new SwapImage[temp_image_buffer_count];
|
||||
memset(temp_image_buffer, 0, sizeof(*temp_image_buffer)*temp_image_buffer_count);
|
||||
Debug(2, "Assigned temporary buffer");
|
||||
}
|
||||
}
|
||||
|
@ -575,7 +572,7 @@ void MonitorStream::runStream() {
|
|||
break;
|
||||
}
|
||||
|
||||
gettimeofday(&now, nullptr);
|
||||
now = std::chrono::system_clock::now();
|
||||
|
||||
bool was_paused = paused;
|
||||
if ( connkey ) {
|
||||
|
@ -585,7 +582,7 @@ void MonitorStream::runStream() {
|
|||
got_command = true;
|
||||
}
|
||||
// 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 ) {
|
||||
if (now - last_comm_update > Hours(1)) {
|
||||
touch(sock_path_lock);
|
||||
last_comm_update = now;
|
||||
}
|
||||
|
@ -596,7 +593,7 @@ void MonitorStream::runStream() {
|
|||
int index = monitor->shared_data->last_write_index % monitor->image_buffer_count;
|
||||
Debug(1, "Saving paused image from index %d",index);
|
||||
paused_image = new Image(*monitor->image_buffer[index]);
|
||||
paused_timestamp = monitor->shared_timestamps[index];
|
||||
paused_timestamp = SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
|
||||
}
|
||||
} else if ( paused_image ) {
|
||||
Debug(1, "Clearing paused_image");
|
||||
|
@ -624,55 +621,46 @@ void MonitorStream::runStream() {
|
|||
delayed = true;
|
||||
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count);
|
||||
} else {
|
||||
// Debug( 3, "siT: %f, lfT: %f", TV_2_FLOAT( swap_image->timestamp ), TV_2_FLOAT( last_frame_timestamp ) );
|
||||
double expected_delta_time = ((TV_2_FLOAT(swap_image->timestamp) - TV_2_FLOAT(last_frame_timestamp)) * ZM_RATE_BASE)/replay_rate;
|
||||
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
||||
FPSeconds expected_delta_time = ((FPSeconds(swap_image->timestamp - last_frame_timestamp)) * ZM_RATE_BASE) / replay_rate;
|
||||
SystemTimePoint::duration actual_delta_time = now - last_frame_sent;
|
||||
|
||||
// Debug( 3, "eDT: %.3lf, aDT: %.3f, lFS:%.3f, NOW:%.3f", expected_delta_time, actual_delta_time, last_frame_sent, TV_2_FLOAT( now ) );
|
||||
// If the next frame is due
|
||||
if ( actual_delta_time > expected_delta_time ) {
|
||||
if (actual_delta_time > expected_delta_time) {
|
||||
// Debug( 2, "eDT: %.3lf, aDT: %.3f", expected_delta_time, actual_delta_time );
|
||||
if ( temp_index%frame_mod == 0 ) {
|
||||
if (temp_index % frame_mod == 0) {
|
||||
Debug(2, "Sending delayed frame %d", temp_index);
|
||||
// Send the next frame
|
||||
if (!sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp)) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
memcpy(&last_frame_timestamp, &(swap_image->timestamp), sizeof(last_frame_timestamp));
|
||||
last_frame_timestamp = swap_image->timestamp;
|
||||
// frame_sent = true;
|
||||
}
|
||||
temp_read_index = MOD_ADD(temp_read_index, (replay_rate>0?1:-1), temp_image_buffer_count);
|
||||
temp_read_index = MOD_ADD(temp_read_index, (replay_rate > 0 ? 1 : -1), temp_image_buffer_count);
|
||||
}
|
||||
}
|
||||
} else if ( step != 0 ) {
|
||||
} else if (step != 0) {
|
||||
temp_read_index = MOD_ADD(temp_read_index, (step>0?1:-1), temp_image_buffer_count);
|
||||
|
||||
SwapImage *swap_image = &temp_image_buffer[temp_read_index];
|
||||
|
||||
// Send the next frame
|
||||
if ( !sendFrame(
|
||||
temp_image_buffer[temp_read_index].file_name,
|
||||
temp_image_buffer[temp_read_index].timestamp
|
||||
) ) {
|
||||
if (!sendFrame(temp_image_buffer[temp_read_index].file_name, temp_image_buffer[temp_read_index].timestamp)) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
memcpy(
|
||||
&last_frame_timestamp,
|
||||
&(swap_image->timestamp),
|
||||
sizeof(last_frame_timestamp)
|
||||
);
|
||||
|
||||
last_frame_timestamp = swap_image->timestamp;
|
||||
// frame_sent = true;
|
||||
step = 0;
|
||||
} else {
|
||||
//paused?
|
||||
int temp_index = MOD_ADD(temp_read_index, 0, temp_image_buffer_count);
|
||||
|
||||
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
||||
if ( got_command || (actual_delta_time > 5) ) {
|
||||
if (got_command || (now - last_frame_sent > Seconds(5))) {
|
||||
// Send keepalive
|
||||
Debug(2, "Sending keepalive frame %d", temp_index);
|
||||
// Send the next frame
|
||||
if ( !sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp) ) {
|
||||
if (!sendFrame(temp_image_buffer[temp_index].file_name, temp_image_buffer[temp_index].timestamp)) {
|
||||
zm_terminate = true;
|
||||
}
|
||||
// frame_sent = true;
|
||||
|
@ -702,20 +690,21 @@ void MonitorStream::runStream() {
|
|||
// Send the next frame
|
||||
//
|
||||
// Perhaps we should use NOW instead.
|
||||
last_frame_timestamp = monitor->shared_timestamps[index];
|
||||
last_frame_timestamp =
|
||||
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
|
||||
Image *image = monitor->image_buffer[index];
|
||||
|
||||
if ( !sendFrame(image, last_frame_timestamp) ) {
|
||||
if (!sendFrame(image, last_frame_timestamp)) {
|
||||
Debug(2, "sendFrame failed, quiting.");
|
||||
zm_terminate = true;
|
||||
break;
|
||||
}
|
||||
//frame_sent = true;
|
||||
//
|
||||
if ( frame_count == 0 ) {
|
||||
if (frame_count == 0) {
|
||||
// Chrome will not display the first frame until it receives another.
|
||||
// Firefox is fine. So just send the first frame twice.
|
||||
if ( !sendFrame(image, last_frame_timestamp) ) {
|
||||
if (!sendFrame(image, last_frame_timestamp)) {
|
||||
Debug(2, "sendFrame failed, quiting.");
|
||||
zm_terminate = true;
|
||||
break;
|
||||
|
@ -735,12 +724,12 @@ void MonitorStream::runStream() {
|
|||
if (!sendFrame(paused_image, paused_timestamp))
|
||||
zm_terminate = true;
|
||||
} else {
|
||||
double actual_delta_time = TV_2_FLOAT(now) - last_frame_sent;
|
||||
if ( actual_delta_time > 5 ) {
|
||||
if ( paused_image ) {
|
||||
SystemTimePoint::duration actual_delta_time = now - last_frame_sent;
|
||||
if (actual_delta_time > Seconds(5)) {
|
||||
if (paused_image) {
|
||||
// Send keepalive
|
||||
Debug(2, "Sending keepalive frame because delta time %.2f > 5",
|
||||
actual_delta_time);
|
||||
Debug(2, "Sending keepalive frame because delta time %.2f s > 5 s",
|
||||
FPSeconds(actual_delta_time).count());
|
||||
// Send the next frame
|
||||
if (!sendFrame(paused_image, paused_timestamp))
|
||||
zm_terminate = true;
|
||||
|
@ -766,7 +755,9 @@ void MonitorStream::runStream() {
|
|||
temp_index);
|
||||
temp_image_buffer[temp_index].valid = true;
|
||||
}
|
||||
temp_image_buffer[temp_index].timestamp = monitor->shared_timestamps[index];
|
||||
|
||||
temp_image_buffer[temp_index].timestamp =
|
||||
SystemTimePoint(zm::chrono::duration_cast<Microseconds>(monitor->shared_timestamps[index]));
|
||||
monitor->image_buffer[index]->WriteJpeg(
|
||||
temp_image_buffer[temp_index].file_name,
|
||||
config.jpeg_file_quality
|
||||
|
@ -791,27 +782,29 @@ void MonitorStream::runStream() {
|
|||
Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index);
|
||||
} // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index )
|
||||
|
||||
unsigned long sleep_time = (unsigned long)((1000000 * ZM_RATE_BASE)/((base_fps?base_fps:1)*abs(replay_rate*2)));
|
||||
if ( sleep_time > MonitorStream::MAX_SLEEP_USEC ) {
|
||||
FPSeconds sleep_time =
|
||||
FPSeconds(ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2)));
|
||||
|
||||
if (sleep_time > MonitorStream::MAX_SLEEP) {
|
||||
// Shouldn't sleep for long because we need to check command queue, etc.
|
||||
sleep_time = MonitorStream::MAX_SLEEP_USEC;
|
||||
Debug(3, "Sleeping for MAX_SLEEP_USEC %luus", sleep_time);
|
||||
sleep_time = MonitorStream::MAX_SLEEP;
|
||||
Debug(3, "Sleeping for MAX_SLEEP_USEC %" PRIi64 " us",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count()));
|
||||
} else {
|
||||
Debug(3, "Sleeping for %luus", sleep_time);
|
||||
Debug(3, "Sleeping for %" PRIi64 " us",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(sleep_time).count()));
|
||||
}
|
||||
usleep(sleep_time);
|
||||
if ( ttl ) {
|
||||
if ( (now.tv_sec - stream_start_time) > ttl ) {
|
||||
Debug(2, "now(%" PRIi64 ") - start(%" PRIi64 " ) > ttl(%" PRIi64 ") break",
|
||||
static_cast<int64>(now.tv_sec),
|
||||
static_cast<int64>(stream_start_time),
|
||||
static_cast<int64>(ttl));
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(sleep_time);
|
||||
|
||||
if (ttl > Seconds(0) && (now - stream_start_time) > ttl) {
|
||||
Debug(2, "now - start > ttl (%" PRIi64 " us). break",
|
||||
static_cast<int64>(std::chrono::duration_cast<Microseconds>(ttl).count()));
|
||||
break;
|
||||
}
|
||||
if ( !last_frame_sent ) {
|
||||
|
||||
if (last_frame_sent.time_since_epoch() == Seconds(0)) {
|
||||
// If we didn't capture above, because frame_mod was bad? Then last_frame_sent will not have a value.
|
||||
last_frame_sent = now.tv_sec;
|
||||
last_frame_sent = now;
|
||||
Warning("no last_frame_sent. Shouldn't happen. frame_mod was (%d) frame_count (%d)",
|
||||
frame_mod, frame_count);
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@
|
|||
|
||||
class MonitorStream : public StreamBase {
|
||||
protected:
|
||||
typedef struct SwapImage {
|
||||
bool valid;
|
||||
struct timeval timestamp;
|
||||
char file_name[PATH_MAX];
|
||||
} SwapImage;
|
||||
struct SwapImage {
|
||||
bool valid = false;
|
||||
SystemTimePoint timestamp;
|
||||
char file_name[PATH_MAX] = "";
|
||||
};
|
||||
|
||||
private:
|
||||
SwapImage *temp_image_buffer;
|
||||
|
@ -38,15 +38,15 @@ class MonitorStream : public StreamBase {
|
|||
int temp_write_index;
|
||||
|
||||
protected:
|
||||
time_t ttl;
|
||||
Microseconds ttl;
|
||||
int playback_buffer;
|
||||
bool delayed;
|
||||
int frame_count;
|
||||
|
||||
protected:
|
||||
bool checkSwapPath(const char *path, bool create_path);
|
||||
bool sendFrame(const char *filepath, const timeval ×tamp);
|
||||
bool sendFrame(Image *image, const timeval ×tamp);
|
||||
bool sendFrame(const char *filepath, SystemTimePoint timestamp);
|
||||
bool sendFrame(Image *image, SystemTimePoint timestamp);
|
||||
void processCommand(const CmdMsg *msg) override;
|
||||
void SingleImage(int scale=100);
|
||||
void SingleImageRaw(int scale=100);
|
||||
|
@ -63,13 +63,13 @@ class MonitorStream : public StreamBase {
|
|||
ttl(0),
|
||||
playback_buffer(0),
|
||||
delayed(false),
|
||||
frame_count(0) {
|
||||
}
|
||||
frame_count(0) {}
|
||||
|
||||
void setStreamBuffer(int p_playback_buffer) {
|
||||
playback_buffer = p_playback_buffer;
|
||||
}
|
||||
void setStreamTTL(time_t p_ttl) {
|
||||
ttl = p_ttl;
|
||||
ttl = Seconds(p_ttl);
|
||||
}
|
||||
bool setStreamStart(int monitor_id) {
|
||||
return loadMonitor(monitor_id);
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
constexpr Seconds StreamBase::MAX_STREAM_DELAY;
|
||||
constexpr Milliseconds StreamBase::MAX_SLEEP;
|
||||
|
||||
StreamBase::~StreamBase() {
|
||||
if (vid_stream) {
|
||||
delete vid_stream;
|
||||
|
@ -283,7 +286,7 @@ bool StreamBase::sendTextFrame(const char *frame_text) {
|
|||
fputs("\r\n\r\n", stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
last_frame_sent = TV_2_FLOAT(now);
|
||||
last_frame_sent = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -368,7 +371,7 @@ void StreamBase::openComms() {
|
|||
strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path));
|
||||
rem_addr.sun_family = AF_UNIX;
|
||||
|
||||
gettimeofday(&last_comm_update, nullptr);
|
||||
last_comm_update = std::chrono::system_clock::now();
|
||||
} // end if connKey > 0
|
||||
Debug(3, "comms open at %s", loc_sock_path);
|
||||
} // end void StreamBase::openComms()
|
||||
|
|
|
@ -22,13 +22,13 @@
|
|||
|
||||
#include "zm_logger.h"
|
||||
#include "zm_mpeg.h"
|
||||
#include "zm_time.h"
|
||||
#include <memory>
|
||||
#include <sys/un.h>
|
||||
|
||||
class Image;
|
||||
class Monitor;
|
||||
|
||||
#define TV_2_FLOAT( tv ) ( double((tv).tv_sec) + (double((tv).tv_usec) / 1000000.0) )
|
||||
#define BOUNDARY "ZoneMinderFrame"
|
||||
|
||||
class StreamBase {
|
||||
|
@ -42,8 +42,8 @@ public:
|
|||
} StreamType;
|
||||
|
||||
protected:
|
||||
static const int MAX_STREAM_DELAY = 5; // Seconds
|
||||
static const int MAX_SLEEP_USEC = 500000; // .5 Seconds
|
||||
static constexpr Seconds MAX_STREAM_DELAY = Seconds(5);
|
||||
static constexpr Milliseconds MAX_SLEEP = Milliseconds(500);
|
||||
|
||||
static const StreamType DEFAULT_TYPE = STREAM_JPEG;
|
||||
enum { DEFAULT_RATE=ZM_RATE_BASE };
|
||||
|
@ -119,15 +119,15 @@ protected:
|
|||
bool paused;
|
||||
int step;
|
||||
|
||||
struct timeval now;
|
||||
struct timeval last_comm_update;
|
||||
SystemTimePoint now;
|
||||
SystemTimePoint last_comm_update;
|
||||
|
||||
double base_fps;
|
||||
double effective_fps;
|
||||
int frame_mod;
|
||||
|
||||
double last_frame_sent;
|
||||
struct timeval last_frame_timestamp;
|
||||
SystemTimePoint last_frame_sent;
|
||||
SystemTimePoint last_frame_timestamp;
|
||||
|
||||
VideoStream *vid_stream;
|
||||
|
||||
|
@ -176,9 +176,7 @@ public:
|
|||
effective_fps = 0.0;
|
||||
frame_mod = 1;
|
||||
|
||||
vid_stream = 0;
|
||||
last_frame_sent = 0.0;
|
||||
last_frame_timestamp = {};
|
||||
vid_stream = nullptr;
|
||||
msg = { 0, { 0 } };
|
||||
}
|
||||
virtual ~StreamBase();
|
||||
|
|
Loading…
Reference in New Issue