From 60d2186ff4ddea373340dc61d56154205a2db785 Mon Sep 17 00:00:00 2001 From: Isaac Connor Date: Thu, 16 Dec 2021 16:35:20 -0500 Subject: [PATCH] Move from SystemTimePoint to SteadyTimePoint where possible. Implement MAXFPS command. Change the logic of calculating sleep time to make more sense. Get rid of frame_mod use in favour of calculating when then next frame should get sent and just waiting till then. --- src/zm_eventstream.cpp | 10 ++-- src/zm_eventstream.h | 2 +- src/zm_monitorstream.cpp | 98 +++++++++++++++++++++++++++------------- src/zm_stream.cpp | 2 +- src/zm_stream.h | 10 ++-- 5 files changed, 80 insertions(+), 42 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index af9dd8639..0c104ffd0 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -663,7 +663,7 @@ bool EventStream::checkEventLoaded() { else curr_frame_id = 1; Debug(2, "New frame id = %ld", curr_frame_id); - start = std::chrono::system_clock::now(); + start = std::chrono::steady_clock::now(); return true; } else { Debug(2, "No next event loaded using %s. Pausing", sql.c_str()); @@ -851,13 +851,13 @@ void EventStream::runStream() { } updateFrameRate(fps); - start = std::chrono::system_clock::now(); + start = std::chrono::steady_clock::now(); SystemTimePoint::duration last_frame_offset = Seconds(0); SystemTimePoint::duration time_to_event = Seconds(0); while ( !zm_terminate ) { - now = std::chrono::system_clock::now(); + now = std::chrono::steady_clock::now(); Microseconds delta = Microseconds(0); send_frame = false; @@ -904,7 +904,7 @@ void EventStream::runStream() { // time_to_event > 0 means that we are not in the event if (time_to_event > Seconds(0) and mode == MODE_ALL) { - SystemTimePoint::duration time_since_last_send = now - last_frame_sent; + TimePoint::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]; @@ -976,7 +976,7 @@ void EventStream::runStream() { // +/- 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 - now = std::chrono::system_clock::now(); + now = std::chrono::steady_clock::now(); // we incremented by replay_rate, so might have jumped past frame_count if ( (mode == MODE_SINGLE) && ( diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 387748b08..969e6725d 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -76,7 +76,7 @@ class EventStream : public StreamBase { long curr_frame_id; SystemTimePoint curr_stream_time; bool send_frame; - SystemTimePoint start; // clock time when started the event + TimePoint start; // clock time when started the event EventData *event_data; diff --git a/src/zm_monitorstream.cpp b/src/zm_monitorstream.cpp index d7b306fe0..4ce0eebeb 100644 --- a/src/zm_monitorstream.cpp +++ b/src/zm_monitorstream.cpp @@ -134,6 +134,18 @@ void MonitorStream::processCommand(const CmdMsg *msg) { break; } break; + case CMD_MAXFPS : + { + 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]; + + maxfps = (int_part + dec_part / 1000000.0); + + Debug(1, "Got MAXFPS %f", maxfps); + break; + } case CMD_SLOWFWD : Debug(1, "Got SLOW FWD command"); paused = true; @@ -268,7 +280,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) { } else { FPSeconds elapsed = now - last_fps_update; if (elapsed.count()) { - actual_fps = (frame_count - last_frame_count) / elapsed.count(); + actual_fps = (actual_fps + (frame_count - last_frame_count) / elapsed.count())/2; last_frame_count = frame_count; last_fps_update = now; } @@ -288,7 +300,7 @@ void MonitorStream::processCommand(const CmdMsg *msg) { status_data.delayed = delayed; status_data.paused = paused; status_data.rate = replay_rate; - status_data.delay = FPSeconds(now - last_frame_timestamp).count(); + status_data.delay = FPSeconds(now - last_frame_sent).count(); status_data.zoom = zoom; Debug(2, "viewing 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, @@ -382,6 +394,9 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) { Image *send_image = prepareImage(image); fputs("--" BOUNDARY "\r\n", stdout); + // Calculate how long it takes to actually send the frame + TimePoint send_start_time = std::chrono::steady_clock::now(); + if ( type == STREAM_MPEG ) { if ( !vid_stream ) { vid_stream = new VideoStream("pipe:", format, bitrate, effective_fps, send_image->Colours(), send_image->SubpixelOrder(), send_image->Width(), send_image->Height()); @@ -402,8 +417,6 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) { int img_buffer_size = 0; unsigned char *img_buffer = temp_img_buffer; - // Calculate how long it takes to actually send the frame - TimePoint send_start_time = std::chrono::steady_clock::now(); switch ( type ) { case STREAM_JPEG : @@ -445,21 +458,22 @@ bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) { fputs("\r\n", stdout); fflush(stdout); - TimePoint send_end_time = std::chrono::steady_clock::now(); - TimePoint::duration frame_send_time = send_end_time - send_start_time; - TimePoint::duration maxfps_milliseconds = Milliseconds(lround(Milliseconds::period::den / maxfps)); - - if (frame_send_time > maxfps_milliseconds) { - //maxfps /= 1.5; - Warning("Frame send time %" PRIi64 " msec too slow (> %" PRIi64 ", throttling maxfps to %.3f", - static_cast(std::chrono::duration_cast(frame_send_time).count()), - static_cast(std::chrono::duration_cast(maxfps_milliseconds).count()), - maxfps); - } } // Not mpeg - last_frame_sent = now; + + TimePoint send_end_time = std::chrono::steady_clock::now(); + TimePoint::duration frame_send_time = send_end_time - send_start_time; + TimePoint::duration maxfps_milliseconds = Milliseconds(lround(Milliseconds::period::den / maxfps)); + + if (frame_send_time > maxfps_milliseconds) { + //maxfps /= 1.5; + Warning("Frame send time %" PRIi64 " msec too slow (> %" PRIi64 ", throttling maxfps to %.3f", + static_cast(std::chrono::duration_cast(frame_send_time).count()), + static_cast(std::chrono::duration_cast(maxfps_milliseconds).count()), + maxfps); + } + last_frame_sent = send_end_time; return true; -} +} // end bool MonitorStream::sendFrame(Image *image, SystemTimePoint timestamp) void MonitorStream::runStream() { if (type == STREAM_SINGLE) { @@ -502,7 +516,8 @@ 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; - SystemTimePoint stream_start_time = std::chrono::system_clock::now(); + TimePoint stream_start_time = std::chrono::steady_clock::now(); + when_to_send_next_frame = stream_start_time; // initialize it to now so that we spit out a frame immediately frame_count = 0; @@ -571,7 +586,7 @@ void MonitorStream::runStream() { break; } - now = std::chrono::system_clock::now(); + now = std::chrono::steady_clock::now(); bool was_paused = paused; bool got_command = false; // commands like zoom should output a frame even if paused @@ -618,7 +633,7 @@ void MonitorStream::runStream() { temp_read_index = MOD_ADD(temp_read_index, (replay_rate>=0?-1:1), temp_image_buffer_count); } else { 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; + TimePoint::duration actual_delta_time = now - last_frame_sent; // If the next frame is due if (actual_delta_time > expected_delta_time) { @@ -684,7 +699,8 @@ void MonitorStream::runStream() { if (last_read_index != monitor->shared_data->last_write_index) { // have a new image to send int index = monitor->shared_data->last_write_index % monitor->image_buffer_count; - if ((frame_mod == 1) || ((frame_count%frame_mod) == 0)) { + //if ((frame_mod == 1) || ((frame_count%frame_mod) == 0)) { + if ( now >= when_to_send_next_frame ) { if (!paused && !delayed) { last_read_index = monitor->shared_data->last_write_index; Debug(2, "Sending frame index: %d: frame_mod: %d frame count: %d paused(%d) delayed(%d)", @@ -743,9 +759,9 @@ void MonitorStream::runStream() { } // end if actual_delta_time > 5 } // end if change in zoom } // end if paused or not - } else { - frame_count++; - } // end if should send frame + //} else { + //frame_count++; + } // end if should send frame now > when_to_send_next_frame if (buffered_playback && !paused) { if (monitor->shared_data->valid) { @@ -776,15 +792,35 @@ void MonitorStream::runStream() { } } // end if buffered playback } else { - Debug(3, "Waiting for capture last_write_index=%u", monitor->shared_data->last_write_index); + Debug(3, "Waiting for capture last_write_index=%u == last_read_index=%u", + monitor->shared_data->last_write_index, + last_read_index); } // end if ( (unsigned int)last_read_index != monitor->shared_data->last_write_index ) - FPSeconds sleep_time = - FPSeconds(1 / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate) : 1))); - if (last_frame_sent.time_since_epoch() != Seconds(0)) { - sleep_time -= (now - last_frame_sent); - if (sleep_time < Seconds(0)) - sleep_time = Seconds(0); + FPSeconds sleep_time; + if (now >= when_to_send_next_frame) { + // sent a frame, so update + + double capture_fps = monitor->GetFPS(); + double fps = (maxfps && (capture_fps > maxfps)) ? maxfps : capture_fps; + double sleep_time_seconds = (1 / ((fps ? fps : 1))) // 1 second / fps + * (replay_rate ? abs(replay_rate)/ZM_RATE_BASE : 1); // replay_rate is 100 for 1x + Debug(3, "Using %f for maxfps. capture_fps: %f maxfps %f * replay_rate: %d = %f", fps, capture_fps, maxfps, replay_rate, sleep_time_seconds); + + sleep_time = FPSeconds(sleep_time_seconds); + if (when_to_send_next_frame > now) + sleep_time -= when_to_send_next_frame - now; + + when_to_send_next_frame = now + std::chrono::duration_cast(sleep_time); + + if (last_frame_sent > now) { + FPSeconds elapsed = last_frame_sent - now; + if (sleep_time > elapsed) { + sleep_time -= elapsed; + } + } + } else { + sleep_time = when_to_send_next_frame - now; } if (sleep_time > MonitorStream::MAX_SLEEP) { diff --git a/src/zm_stream.cpp b/src/zm_stream.cpp index 88ccd11d1..aba98e0c3 100644 --- a/src/zm_stream.cpp +++ b/src/zm_stream.cpp @@ -386,7 +386,7 @@ void StreamBase::openComms() { strncpy(rem_addr.sun_path, rem_sock_path, sizeof(rem_addr.sun_path)); rem_addr.sun_family = AF_UNIX; - last_comm_update = std::chrono::system_clock::now(); + last_comm_update = std::chrono::steady_clock::now(); Debug(3, "comms open at %s", loc_sock_path); } // end if connKey > 0 } // end void StreamBase::openComms() diff --git a/src/zm_stream.h b/src/zm_stream.h index d33127332..09bd5bd80 100644 --- a/src/zm_stream.h +++ b/src/zm_stream.h @@ -88,6 +88,7 @@ protected: CMD_VARPLAY, CMD_GET_IMAGE, CMD_QUIT, + CMD_MAXFPS, CMD_QUERY=99 } MsgCommand; @@ -118,21 +119,22 @@ protected: bool paused; int step; - SystemTimePoint now; - SystemTimePoint last_comm_update; + TimePoint now; + TimePoint last_comm_update; double maxfps; double base_fps; // Should be capturing fps, hence a rough target double effective_fps; // Target fps after taking max_fps into account double actual_fps; // sliding calculated actual streaming fps achieved - SystemTimePoint last_fps_update; + TimePoint last_fps_update; int frame_count; // Count of frames sent int last_frame_count; // Used in calculating actual_fps from frame_count - last_frame_count int frame_mod; - SystemTimePoint last_frame_sent; + TimePoint last_frame_sent; SystemTimePoint last_frame_timestamp; + TimePoint when_to_send_next_frame; // When to send next frame so if now < send_next_frame, skip VideoStream *vid_stream;