From 17df9713b1e0c9ca2a4d78354cb85d4b0bfa2f91 Mon Sep 17 00:00:00 2001 From: Isaac Connor <isaac@zoneminder.com> Date: Mon, 12 Sep 2022 11:55:09 -0400 Subject: [PATCH 1/3] Replace Function with Capturing --- src/zm_rtsp_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zm_rtsp_server.cpp b/src/zm_rtsp_server.cpp index 34c036f37..7883eb743 100644 --- a/src/zm_rtsp_server.cpp +++ b/src/zm_rtsp_server.cpp @@ -136,7 +136,7 @@ int main(int argc, char *argv[]) { HwCapsDetect(); - std::string where = "`Function` != 'None' AND `RTSPServer` != false"; + std::string where = "`Capturing` != 'None' AND `RTSPServer` != false"; if (staticConfig.SERVER_ID) where += stringtf(" AND `ServerId`=%d", staticConfig.SERVER_ID); if (monitor_id > 0) From 8549cdf317635f0b14b89a89db18fecbdc1f7e82 Mon Sep 17 00:00:00 2001 From: Isaac Connor <isaac@zoneminder.com> Date: Mon, 12 Sep 2022 16:17:42 -0400 Subject: [PATCH 2/3] Implement a mutex/locks around changing event data, curr_frame_id, etc. also contains a fix for seeking due to > instead of <. In general fixes seeking --- src/zm_eventstream.cpp | 513 ++++++++++++++++++++++------------------- src/zm_eventstream.h | 3 + 2 files changed, 278 insertions(+), 238 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 9ea1d074a..1ec7d6551 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -122,13 +122,13 @@ bool EventStream::loadEventData(uint64_t event_id) { exit(-1); } - if ( !mysql_num_rows(result) ) { + if (!mysql_num_rows(result)) { Fatal("Unable to load event %" PRIu64 ", not found in DB", event_id); } MYSQL_ROW dbrow = mysql_fetch_row(result); - if ( mysql_errno(&dbconn) ) { + if (mysql_errno(&dbconn)) { Error("Can't fetch row: %s", mysql_error(&dbconn)); exit(mysql_errno(&dbconn)); } @@ -136,7 +136,6 @@ bool EventStream::loadEventData(uint64_t event_id) { delete event_data; event_data = new EventData; event_data->event_id = 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]); @@ -158,24 +157,24 @@ bool EventStream::loadEventData(uint64_t event_id) { event_data->Orientation = (Monitor::Orientation)(dbrow[9] == nullptr ? 0 : atoi(dbrow[9])); mysql_free_result(result); - if ( !monitor ) { + if (!monitor) { monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY); - } else if ( monitor->Id() != event_data->monitor_id ) { + } else if (monitor->Id() != event_data->monitor_id) { monitor = Monitor::Load(event_data->monitor_id, false, Monitor::QUERY); } - if ( !monitor ) { + if (!monitor) { Fatal("Unable to load monitor id %d for streaming", event_data->monitor_id); } - if ( !storage ) { + if (!storage) { storage = new Storage(event_data->storage_id); - } else if ( storage->Id() != event_data->storage_id ) { + } else if (storage->Id() != event_data->storage_id) { delete storage; storage = new Storage(event_data->storage_id); } const char *storage_path = storage->Path(); - if ( event_data->scheme == Storage::DEEP ) { + if (event_data->scheme == Storage::DEEP) { tm 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); @@ -333,93 +332,108 @@ void EventStream::processCommand(const CmdMsg *msg) { // Set paused flag paused = true; - replay_rate = ZM_RATE_BASE; - last_frame_sent = now; + //replay_rate = ZM_RATE_BASE; + //last_frame_sent = now; break; case CMD_PLAY : - Debug(1, "Got PLAY command"); - if ( paused ) { - paused = false; - } + { + std::scoped_lock lck{mutex}; + Debug(1, "Got PLAY command"); + if ( paused ) { + paused = false; + } - // If we are in single event mode and at the last frame, replay the current event - if ( - (mode == MODE_SINGLE || mode == MODE_NONE) - && - (curr_frame_id == event_data->last_frame_id) - ) { - Debug(1, "Was in single or no replay mode, and at last frame, so jumping to 1st frame"); - curr_frame_id = 1; - } else { - Debug(1, "mode is %s, current frame is %d, frame count is %d, last frame id is %d", + // If we are in single event mode and at the last frame, replay the current event + if ( + (mode == MODE_SINGLE || mode == MODE_NONE) + && + (curr_frame_id == event_data->last_frame_id) + ) { + Debug(1, "Was in single or no replay mode, and at last frame, so jumping to 1st frame"); + curr_frame_id = 1; + } else { + Debug(1, "mode is %s, current frame is %d, frame count is %d, last frame id is %d", StreamMode_Strings[(int) mode].c_str(), curr_frame_id, event_data->frame_count, event_data->last_frame_id); - } + } - replay_rate = ZM_RATE_BASE; - break; + replay_rate = ZM_RATE_BASE; + break; + } case CMD_VARPLAY : - Debug(1, "Got VARPLAY command"); - if ( paused ) { - paused = false; + { + std::scoped_lock lck{mutex}; + Debug(1, "Got VARPLAY command"); + if ( paused ) { + paused = false; + } + replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; + if ( replay_rate > 50 * ZM_RATE_BASE ) { + Warning("requested replay rate (%d) is too high. We only support up to 50x", replay_rate); + replay_rate = 50 * ZM_RATE_BASE; + } else if ( replay_rate < -50*ZM_RATE_BASE ) { + Warning("requested replay rate (%d) is too low. We only support up to -50x", replay_rate); + replay_rate = -50 * ZM_RATE_BASE; + } + break; } - replay_rate = ntohs(((unsigned char)msg->msg_data[2]<<8)|(unsigned char)msg->msg_data[1])-32768; - if ( replay_rate > 50 * ZM_RATE_BASE ) { - Warning("requested replay rate (%d) is too high. We only support up to 50x", replay_rate); - replay_rate = 50 * ZM_RATE_BASE; - } else if ( replay_rate < -50*ZM_RATE_BASE ) { - Warning("requested replay rate (%d) is too low. We only support up to -50x", replay_rate); - replay_rate = -50 * ZM_RATE_BASE; - } - break; case CMD_STOP : Debug(1, "Got STOP command"); paused = false; break; case CMD_FASTFWD : - Debug(1, "Got FAST FWD command"); - if ( paused ) { - paused = false; + { + Debug(1, "Got FAST FWD command"); + std::scoped_lock lck{mutex}; + 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 : + Debug(1,"Defaulting replay_rate to 2*ZM_RATE_BASE because it is %d", replay_rate); + replay_rate = 2 * ZM_RATE_BASE; + break; + } + break; } - // 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 : - Debug(1,"Defaulting replay_rate to 2*ZM_RATE_BASE because it is %d", replay_rate); - replay_rate = 2 * ZM_RATE_BASE; - break; - } - break; case CMD_SLOWFWD : - paused = true; - replay_rate = ZM_RATE_BASE; - step = 1; - if (curr_frame_id < event_data->last_frame_id) - curr_frame_id += 1; - Debug(1, "Got SLOWFWD command new frame id %d", curr_frame_id); - break; + { + std::scoped_lock lck{mutex}; + paused = true; + replay_rate = ZM_RATE_BASE; + step = 1; + if (curr_frame_id < event_data->last_frame_id) + curr_frame_id += 1; + Debug(1, "Got SLOWFWD command new frame id %d", curr_frame_id); + break; + } case CMD_SLOWREV : - paused = true; - replay_rate = ZM_RATE_BASE; - step = -1; - curr_frame_id -= 1; - if ( curr_frame_id < 1 ) curr_frame_id = 1; - Debug(1, "Got SLOWREV command new frame id %d", curr_frame_id); - break; + { + std::scoped_lock lck{mutex}; + paused = true; + replay_rate = ZM_RATE_BASE; + step = -1; + curr_frame_id -= 1; + if ( curr_frame_id < 1 ) curr_frame_id = 1; + Debug(1, "Got SLOWREV command new frame id %d", curr_frame_id); + break; + } case CMD_FASTREV : Debug(1, "Got FAST REV command"); paused = false; @@ -493,6 +507,7 @@ void EventStream::processCommand(const CmdMsg *msg) { break; case CMD_SEEK : { + std::scoped_lock lck{mutex}; 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) @@ -506,13 +521,37 @@ void EventStream::processCommand(const CmdMsg *msg) { // 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 (curr_frame_id < 1) { + Debug(1, "curr_frame_id = %d, so setting to 1", curr_frame_id); + curr_frame_id = 1; + } + // TODO Replace this with a binary search if (event_data->frames[curr_frame_id - 1].offset > offset) { - while ((curr_frame_id--) && (event_data->frames[curr_frame_id - 1].offset > offset)) {} + Debug(1, "Searching for frame at %.2f, offset of frame %d is %.2f", + FPSeconds(offset).count(), + curr_frame_id, + FPSeconds(event_data->frames[curr_frame_id - 1].offset).count() + ); + while ((curr_frame_id--) && (event_data->frames[curr_frame_id - 1].offset > offset)) { + Debug(1, "Searching for frame at %.2f, offset of frame %d is %.2f", + FPSeconds(offset).count(), + curr_frame_id, + FPSeconds(event_data->frames[curr_frame_id - 1].offset).count() + ); + + } } else if (event_data->frames[curr_frame_id - 1].offset < offset) { - while ((curr_frame_id++) && (event_data->frames[curr_frame_id - 1].offset > offset)) {} + while ((curr_frame_id++ < event_data->last_frame_id) && (event_data->frames[curr_frame_id - 1].offset < offset)) { + Debug(1, "Searching for frame at %.2f, offset of frame %d is %.2f", + FPSeconds(offset).count(), + curr_frame_id, + FPSeconds(event_data->frames[curr_frame_id - 1].offset).count() + ); + } } if ( curr_frame_id < 1 ) { + Debug(1, "curr_frame_id = %d, so setting to 1", curr_frame_id); curr_frame_id = 1; } else if (curr_frame_id > event_data->last_frame_id) { curr_frame_id = event_data->last_frame_id; @@ -841,78 +880,79 @@ void EventStream::runStream() { command_processor = std::thread(&EventStream::checkCommandQueue, this); } - while ( !zm_terminate ) { - now = std::chrono::steady_clock::now(); - + while (!zm_terminate) { Microseconds delta = Microseconds(0); - send_frame = false; + { + std::scoped_lock lck{mutex}; + now = std::chrono::steady_clock::now(); + send_frame = false; - // Get current frame data - FrameData *frame_data = &event_data->frames[curr_frame_id-1]; + // Get current frame data + FrameData *frame_data = &event_data->frames[curr_frame_id-1]; - if ( !paused ) { - // Figure out if we should send this frame - Debug(3, "not paused at cur_frame_id (%d-1) mod frame_mod(%d)", curr_frame_id, frame_mod); - // If we are streaming and this frame is due to be sent - // 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. + if (!paused) { + // Figure out if we should send this frame + Debug(3, "not paused at cur_frame_id (%d-1) mod frame_mod(%d)", curr_frame_id, frame_mod); + // If we are streaming and this frame is due to be sent + // 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. - if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) { + if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) { + send_frame = true; + } + } else if (step != 0) { + Debug(2, "Paused with step %d", step); + // We are paused and are just stepping forward or backward one frame + step = 0; send_frame = true; - } - } else if ( step != 0 ) { - Debug(2, "Paused with step %d", step); - // We are paused and are just stepping forward or backward one frame - step = 0; - 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 - if (now - last_frame_sent > MAX_STREAM_DELAY) { - // Send keepalive - Debug(2, "Sending keepalive frame"); - send_frame = true; - } - } // end if streaming stepping or doing nothing + } else if (!send_frame) { + // We are paused, not stepping and doing nothing, meaning that comms didn't set send_frame to true + if (now - last_frame_sent > MAX_STREAM_DELAY) { + // Send keepalive + Debug(2, "Sending keepalive frame"); + send_frame = true; + } + } // end if streaming stepping or doing nothing - // time_to_event > 0 means that we are not in the event - if (time_to_event > Seconds(0) and mode == MODE_ALL) { - 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]; + // time_to_event > 0 means that we are not in the event + if (time_to_event > Seconds(0) and mode == MODE_ALL) { + 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]; - snprintf(frame_text, sizeof(frame_text), "Time to %s event = %f s", - (replay_rate > 0 ? "next" : "previous"), - FPSeconds(time_to_event).count()); + 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; + if (!sendTextFrame(frame_text)) { + zm_terminate = true; + } + + send_frame = false; // In case keepalive was set } - send_frame = false; // In case keepalive was set - } + // FIXME ICON But we are not paused. We are somehow still in the event? + 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 == Seconds(0)) { + sleep_time += STREAM_PAUSE_WAIT; + } - // FIXME ICON But we are not paused. We are somehow still in the event? - 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 == Seconds(0)) { - sleep_time += STREAM_PAUSE_WAIT; - } - - curr_stream_time += sleep_time; - time_to_event -= sleep_time; - Debug(2, "Sleeping (%" PRIi64 " ms) because we are not at the next event yet, adding %" PRIi64 " ms", + curr_stream_time += sleep_time; + time_to_event -= sleep_time; + 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); + 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 + //curr_stream_time += (1.0L * replay_rate * STREAM_PAUSE_WAIT)/(ZM_RATE_BASE * 1000000); + continue; + } // end if !in_event + } // end scope for mutex lock if (send_frame) { if (!sendFrame(delta)) { @@ -921,141 +961,138 @@ void EventStream::runStream() { } } - curr_stream_time = frame_data->timestamp; + { + std::scoped_lock lck{mutex}; - if (!paused) { - // delta is since the last frame - delta = std::chrono::duration_cast<Microseconds>(frame_data->delta); - Debug(3, "frame delta %" PRIi64 "us ", + // Get current frame data, curr_frame_id may have changed + FrameData *frame_data = &event_data->frames[curr_frame_id-1]; + curr_stream_time = frame_data->timestamp; + + if (!paused) { + // delta is since the last frame + 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 - if (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); + // if effective > base we should speed up frame delivery + if (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 = 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); - } + // but must not exceed maxfps + 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 - now = std::chrono::steady_clock::now(); + // +/- 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::steady_clock::now(); - // we incremented by replay_rate, so might have jumped past frame_count - if ( (mode == MODE_SINGLE) && ( - (curr_frame_id < 1 ) - || - (curr_frame_id >= event_data->frame_count) - ) - ) { - Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start"); - curr_frame_id = 1; - // Have to reset start to now when replaying - start = now; - } + // we incremented by replay_rate, so might have jumped past frame_count + if ( (mode == MODE_SINGLE) && ( + (curr_frame_id < 1 ) + || + (curr_frame_id >= event_data->frame_count) + ) + ) { + Debug(2, "Have mode==MODE_SINGLE and at end of event, looping back to start"); + curr_frame_id = 1; + // Have to reset start to now when replaying + start = now; + } - if (curr_frame_id <= event_data->frame_count) { - frame_data = &event_data->frames[curr_frame_id-1]; + if (curr_frame_id <= event_data->frame_count) { + frame_data = &event_data->frames[curr_frame_id-1]; - // 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 + // 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 - // - if (last_frame_offset != Seconds(0)) { - // We assume that we are going forward and the next frame is in the future. - delta = std::chrono::duration_cast<Microseconds>(frame_data->offset - (now - start)); + // 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 + // + if (last_frame_offset != Seconds(0)) { + // We assume that we are going forward and the next frame is in the future. + 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", + 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 = Seconds(0); - } - last_frame_offset = frame_data->offset; - - 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; - } - - std::this_thread::sleep_for(delta); - Debug(3, "Done sleeping: %" PRIi64 " us", - static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count())); + } else { + Debug(2, "No last frame_offset, no sleep"); + delta = Seconds(0); } - } // end if need to sleep + last_frame_offset = frame_data->offset; + } else { + Debug(1, "invalid curr_frame_id %d !< %d", curr_frame_id, event_data->frame_count); + } // end if not at end of event } else { - Debug(1, "invalid curr_frame_id %d !< %d", curr_frame_id, event_data->frame_count); - } // end if not at end of event - } else { - // Paused - delta = std::chrono::duration_cast<Microseconds>(FPSeconds( - ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2)))); + // Paused + 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 %" 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)); + // 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? + curr_frame_id += step; + } // end if !paused + } // end scope for mutex lock + 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 (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, - static_cast<int64>(std::chrono::duration_cast<Milliseconds>(MAX_SLEEP).count()), - static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count())); + static_cast<int64>(std::chrono::duration_cast<Milliseconds>(MAX_SLEEP).count()), + static_cast<int64>(std::chrono::duration_cast<Microseconds>(delta).count())); delta = MAX_SLEEP; } std::this_thread::sleep_for(delta); - } - // 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? - curr_frame_id += step; - } // end if !paused + } // end if need to sleep + } - // Detects when we hit end of event and will load the next event or previous event - if ( checkEventLoaded() ) { - // Have change of event + { + std::scoped_lock lck{mutex}; + // 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 s) = frame timestamp (%f s) - curr_stream_time (%f s)", + // 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 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 s) = curr_stream_time (%f s) - frame timestamp (%f s)", + } 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 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 if forward or reverse + } // end if checkEventLoaded + } // end scope for lock } // end while ! zm_terminate if (type == STREAM_MPEG) { delete vid_stream; diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 093bcd0eb..7780975a6 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -32,6 +32,8 @@ extern "C" { #include <libavcodec/avcodec.h> } +#include <mutex> + class EventStream : public StreamBase { public: typedef enum { MODE_NONE, MODE_SINGLE, MODE_ALL, MODE_ALL_GAPLESS } StreamMode; @@ -73,6 +75,7 @@ class EventStream : public StreamBase { StreamMode mode; bool forceEventChange; + std::mutex mutex; int curr_frame_id; SystemTimePoint curr_stream_time; bool send_frame; From 4668ffcaa9870c773341eae3691f62dbe9d7f181 Mon Sep 17 00:00:00 2001 From: Isaac Connor <isaac@zoneminder.com> Date: Tue, 13 Sep 2022 16:16:54 -0400 Subject: [PATCH 3/3] rework event stream. Fixes seeking. Fixes event not completing due to not have Frame records for every frame. Adds send_twice to deal with browsers not showing the last sent image. Use locking around anything to do with curr_frame_id as commandQueue is it's own thread now. --- src/zm_eventstream.cpp | 248 +++++++++++++++++++++++------------------ src/zm_eventstream.h | 36 +++--- src/zm_stream.h | 1 + 3 files changed, 156 insertions(+), 129 deletions(-) diff --git a/src/zm_eventstream.cpp b/src/zm_eventstream.cpp index 1ec7d6551..fdbd5c681 100644 --- a/src/zm_eventstream.cpp +++ b/src/zm_eventstream.cpp @@ -230,56 +230,87 @@ bool EventStream::loadEventData(uint64_t event_id) { } event_data->n_frames = mysql_num_rows(result); - event_data->frames = new FrameData[event_data->frame_count]; if (event_data->frame_count < event_data->n_frames) { event_data->frame_count = event_data->n_frames; Warning("Event %" PRId64 " has more frames in the Frames table (%d) than in the Event record (%d)", event_data->event_id, event_data->n_frames, event_data->frame_count); } + event_data->frames.clear(); + event_data->frames.reserve(event_data->frame_count); int last_id = 0; SystemTimePoint last_timestamp = event_data->start_time; - Microseconds last_delta = Seconds(0); + Microseconds last_offset = Seconds(0); + const FrameData *last_frame; + // Here are the issues: if showing jpegs, need FrameId. + // Delta is the time since last frame, not since beginning of Event while ((dbrow = mysql_fetch_row(result))) { int id = atoi(dbrow[0]); - //timestamp = atof(dbrow[1]); - Microseconds delta = std::chrono::duration_cast<Microseconds>(FPSeconds(atof(dbrow[2]))); + //timestamp = atof(dbrow[1]); // timestamp is useless because it's just seconds. + // What is in the Delta column is distance from StartTime. We will call that offset. + Microseconds offset = std::chrono::duration_cast<Microseconds>(FPSeconds(atof(dbrow[2]))); + SystemTimePoint timestamp = event_data->start_time + offset; + int id_diff = id - last_id; - Microseconds frame_delta = - std::chrono::duration_cast<Microseconds>(id_diff ? (delta - last_delta) / id_diff : (delta - last_delta)); + Microseconds delta = + std::chrono::duration_cast<Microseconds>(id_diff ? (offset - last_offset) / id_diff : (offset - last_offset)); + Debug(1, "New delta %f from id_diff %d = id %d - last_id %d offset %f - last)_offset %f", + FPSeconds(delta).count(), id_diff, id, last_id, FPSeconds(offset).count(), FPSeconds(last_offset).count()); // Fill in data between bulk frames 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 = - 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); + auto frame = event_data->frames.emplace_back( + i, + last_timestamp + ((i - last_id) * delta), + std::chrono::duration_cast<Microseconds>((last_frame->timestamp - event_data->start_time) + delta), + delta, + false + ); + last_frame = &frame; + Debug(3, "Frame %d %d timestamp (%f s), offset (%f s) delta (%f s), in_db (%d)", + i, frame.id, + FPSeconds(frame.timestamp.time_since_epoch()).count(), + FPSeconds(frame.offset).count(), + FPSeconds(frame.delta).count(), + frame.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; + auto frame = event_data->frames.emplace_back(id, timestamp, offset, delta, true); + last_frame = &frame; last_id = id; - last_delta = delta; - last_timestamp = event_data->frames[id-1].timestamp; + last_offset = offset; + last_timestamp = timestamp; 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); + FPSeconds(frame.timestamp.time_since_epoch()).count(), + FPSeconds(frame.offset).count(), + FPSeconds(frame.delta).count(), + frame.in_db); } + if (event_data->end_time.time_since_epoch() != Seconds(0)) { + while (event_data->end_time > last_timestamp) { + last_timestamp += last_frame->delta; + last_id ++; + + auto frame = event_data->frames.emplace_back( + last_id, + last_timestamp, + last_frame->offset + last_frame->delta, + last_frame->delta, + false + ); + last_frame = &frame; + Debug(3, "Trailing Frame %d timestamp (%f s), offset (%f s), delta(%f s), in_db(%d)", + last_id, + FPSeconds(frame.timestamp.time_since_epoch()).count(), + FPSeconds(frame.offset).count(), + FPSeconds(frame.delta).count(), + frame.in_db); + } // end while + } // end if have endtime + // Incomplete events might not have any frame data event_data->last_frame_id = last_id; @@ -414,7 +445,7 @@ void EventStream::processCommand(const CmdMsg *msg) { } case CMD_SLOWFWD : { - std::scoped_lock lck{mutex}; + std::scoped_lock lck{mutex}; paused = true; replay_rate = ZM_RATE_BASE; step = 1; @@ -425,7 +456,7 @@ void EventStream::processCommand(const CmdMsg *msg) { } case CMD_SLOWREV : { - std::scoped_lock lck{mutex}; + std::scoped_lock lck{mutex}; paused = true; replay_rate = ZM_RATE_BASE; step = -1; @@ -466,17 +497,29 @@ void EventStream::processCommand(const CmdMsg *msg) { Debug(1, "Got ZOOM IN command, to %d,%d", x, y); zoom += 10; send_frame = true; + if (paused) { + step = 1; + send_twice = true; + } break; case CMD_ZOOMOUT : Debug(1, "Got ZOOM OUT command"); zoom -= 10; if (zoom < 100) zoom = 100; send_frame = true; + if (paused) { + step = 1; + send_twice = true; + } break; case CMD_ZOOMSTOP : Debug(1, "Got ZOOM STOP command"); zoom = 100; send_frame = true; + if (paused) { + step = 1; + send_twice = true; + } break; case CMD_PAN : x = ((unsigned char)msg->msg_data[1]<<8)|(unsigned char)msg->msg_data[2]; @@ -538,7 +581,6 @@ void EventStream::processCommand(const CmdMsg *msg) { curr_frame_id, FPSeconds(event_data->frames[curr_frame_id - 1].offset).count() ); - } } else if (event_data->frames[curr_frame_id - 1].offset < offset) { while ((curr_frame_id++ < event_data->last_frame_id) && (event_data->frames[curr_frame_id - 1].offset < offset)) { @@ -548,9 +590,10 @@ void EventStream::processCommand(const CmdMsg *msg) { FPSeconds(event_data->frames[curr_frame_id - 1].offset).count() ); } + curr_frame_id--; } - if ( curr_frame_id < 1 ) { + if (curr_frame_id < 1) { Debug(1, "curr_frame_id = %d, so setting to 1", curr_frame_id); curr_frame_id = 1; } else if (curr_frame_id > event_data->last_frame_id) { @@ -562,6 +605,10 @@ void EventStream::processCommand(const CmdMsg *msg) { FPSeconds(offset).count(), curr_frame_id, FPSeconds(event_data->frames[curr_frame_id - 1].offset).count()); + if (paused) { + step = 1; // if we are paused, we won't send a frame except a keepalive. + send_twice = true; + } send_frame = true; break; } @@ -606,8 +653,7 @@ void EventStream::processCommand(const CmdMsg *msg) { DataMsg status_msg; status_msg.msg_type = MSG_DATA_EVENT; memcpy(&status_msg.msg_data, &status_data, sizeof(status_data)); - Debug(1, "Size of msg %zu", sizeof(status_data)); - if ( sendto(sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr)) < 0 ) { + if (sendto(sd, &status_msg, sizeof(status_msg), MSG_DONTWAIT, (sockaddr *)&rem_addr, sizeof(rem_addr)) < 0) { //if ( errno != EAGAIN ) { Error("Can't sendto on sd %d: %s", sd, strerror(errno)); @@ -769,7 +815,7 @@ bool EventStream::sendFrame(Microseconds delta_us) { image = new Image(filepath.c_str()); } else if ( ffmpeg_input ) { // Get the frame from the mp4 input - FrameData *frame_data = &event_data->frames[curr_frame_id-1]; + const FrameData *frame_data = &event_data->frames[curr_frame_id-1]; AVFrame *frame = ffmpeg_input->get_frame(ffmpeg_input->get_video_stream_id(), FPSeconds(frame_data->offset).count()); if (frame) { @@ -870,9 +916,6 @@ void EventStream::runStream() { } updateFrameRate(fps); - start = std::chrono::steady_clock::now(); - - SystemTimePoint::duration last_frame_offset = Seconds(0); SystemTimePoint::duration time_to_event = Seconds(0); std::thread command_processor; @@ -880,15 +923,16 @@ void EventStream::runStream() { command_processor = std::thread(&EventStream::checkCommandQueue, this); } + // Has to go here, at the moment, for sendFrame(delta). + Microseconds delta = Microseconds(0); + while (!zm_terminate) { - Microseconds delta = Microseconds(0); + start = std::chrono::steady_clock::now(); + { std::scoped_lock lck{mutex}; - now = std::chrono::steady_clock::now(); - send_frame = false; - // Get current frame data - FrameData *frame_data = &event_data->frames[curr_frame_id-1]; + send_frame = false; if (!paused) { // Figure out if we should send this frame @@ -897,9 +941,9 @@ void EventStream::runStream() { // 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. - if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) { + //if ( (frame_mod == 1) || (((curr_frame_id-1)%frame_mod) == 0) ) { send_frame = true; - } + //} } else if (step != 0) { Debug(2, "Paused with step %d", step); // We are paused and are just stepping forward or backward one frame @@ -935,9 +979,6 @@ void EventStream::runStream() { // FIXME ICON But we are not paused. We are somehow still in the event? 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 == Seconds(0)) { sleep_time += STREAM_PAUSE_WAIT; } @@ -949,7 +990,6 @@ void EventStream::runStream() { 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 } // end scope for mutex lock @@ -959,41 +999,20 @@ void EventStream::runStream() { zm_terminate = true; break; } + if (send_twice and !sendFrame(delta)) { + zm_terminate = true; + break; + } } { std::scoped_lock lck{mutex}; - // Get current frame data, curr_frame_id may have changed - FrameData *frame_data = &event_data->frames[curr_frame_id-1]; - curr_stream_time = frame_data->timestamp; - if (!paused) { - // delta is since the last frame - 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 - if (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 = 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 - now = std::chrono::steady_clock::now(); + // Get current frame data, curr_frame_id may have changed + FrameData *last_frame_data = &event_data->frames[curr_frame_id-1]; + curr_stream_time = last_frame_data->timestamp; + curr_frame_id += (replay_rate > 0 ? frame_mod : -1*frame_mod); // we incremented by replay_rate, so might have jumped past frame_count if ( (mode == MODE_SINGLE) && ( @@ -1004,57 +1023,67 @@ 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 to now when replaying - start = now; } if (curr_frame_id <= event_data->frame_count) { - frame_data = &event_data->frames[curr_frame_id-1]; + const FrameData *next_frame_data = &event_data->frames[curr_frame_id-1]; + Debug(3, "Have Frame %d %d timestamp (%f s), offset (%f s) delta (%f s), in_db (%d)", + curr_frame_id, next_frame_data->id, + FPSeconds(next_frame_data->timestamp.time_since_epoch()).count(), + FPSeconds(next_frame_data->offset).count(), + FPSeconds(next_frame_data->delta).count(), + next_frame_data->in_db); // 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 - // - if (last_frame_offset != Seconds(0)) { - // We assume that we are going forward and the next frame is in the future. - delta = std::chrono::duration_cast<Microseconds>(frame_data->offset - (now - start)); + delta = abs(next_frame_data->offset - last_frame_data->offset); + Debug(2, "New delta: %fs from last frame offset %fs - next_frame_offset %fs", + FPSeconds(delta).count(), + FPSeconds(last_frame_data->offset).count(), + FPSeconds(next_frame_data->offset).count()); + // if effective > base we should speed up frame delivery + if (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); - 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 = Seconds(0); + // but must not exceed maxfps + 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); } - last_frame_offset = frame_data->offset; + now = std::chrono::steady_clock::now(); + TimePoint::duration elapsed = now - start; + delta -= std::chrono::duration_cast<Milliseconds>(elapsed); // sending frames takes time, so remove it from the sleep time + + Debug(2, "New delta: %fs from last frame offset %fs - next_frame_offset %fs - elapsed %fs", + FPSeconds(delta).count(), + FPSeconds(last_frame_data->offset).count(), + FPSeconds(next_frame_data->offset).count(), + FPSeconds(elapsed).count() + ); } else { Debug(1, "invalid curr_frame_id %d !< %d", curr_frame_id, event_data->frame_count); } // end if not at end of event } else { // Paused - delta = std::chrono::duration_cast<Microseconds>(FPSeconds( - ZM_RATE_BASE / ((base_fps ? base_fps : 1) * (replay_rate ? abs(replay_rate * 2) : 2)))); + delta = MAX_SLEEP; // 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? curr_frame_id += step; } // end if !paused - } // end scope for mutex lock - - 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)); + } // end scope for mutex lock if (send_frame && type != STREAM_MPEG) { - if (delta != Seconds(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()), @@ -1069,12 +1098,12 @@ void EventStream::runStream() { { std::scoped_lock lck{mutex}; // Detects when we hit end of event and will load the next event or previous event - if ( checkEventLoaded() ) { + 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 ) { + 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 s) = frame timestamp (%f s) - curr_stream_time (%f s)", @@ -1083,7 +1112,7 @@ void EventStream::runStream() { FPSeconds(event_data->frames[0].timestamp.time_since_epoch()).count(), FPSeconds(curr_stream_time.time_since_epoch()).count()); - } else if ( replay_rate < 0 ) { + } 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 s) = curr_stream_time (%f s) - frame timestamp (%f s)", replay_rate, @@ -1094,6 +1123,7 @@ void EventStream::runStream() { } // end if checkEventLoaded } // end scope for lock } // end while ! zm_terminate + if (type == STREAM_MPEG) { delete vid_stream; } diff --git a/src/zm_eventstream.h b/src/zm_eventstream.h index 7780975a6..00f11441f 100644 --- a/src/zm_eventstream.h +++ b/src/zm_eventstream.h @@ -41,11 +41,20 @@ class EventStream : public StreamBase { protected: struct FrameData { - //unsigned long id; + unsigned int id; SystemTimePoint timestamp; - Microseconds offset; - Microseconds delta; + Microseconds offset; // distance from event->starttime + Microseconds delta; // distance from last frame bool in_db; + public: + FrameData(unsigned int p_id, SystemTimePoint p_timestamp, Microseconds p_offset, Microseconds p_delta, bool p_in_db) : + id(p_id), + timestamp(p_timestamp), + offset(p_offset), + delta(p_delta), + in_db(p_in_db) + { + } }; struct EventData { @@ -60,7 +69,7 @@ class EventStream : public StreamBase { Microseconds frames_duration; std::string path; int n_frames; // # of frame rows returned from database - FrameData *frames; + std::vector<FrameData> frames; std::string video_file; Storage::Schemes scheme; int SaveJPEGs; @@ -104,22 +113,9 @@ class EventStream : public StreamBase { {} ~EventStream() { - if ( event_data ) { - if ( event_data->frames ) { - delete[] event_data->frames; - event_data->frames = nullptr; - } - delete event_data; - event_data = nullptr; - } - if ( storage ) { - delete storage; - storage = nullptr; - } - if ( ffmpeg_input ) { - delete ffmpeg_input; - ffmpeg_input = nullptr; - } + delete event_data; + delete storage; + delete ffmpeg_input; } void setStreamStart(uint64_t init_event_id, int init_frame_id); void setStreamStart(int monitor_id, time_t event_time); diff --git a/src/zm_stream.h b/src/zm_stream.h index a4b03a3a3..536b73e94 100644 --- a/src/zm_stream.h +++ b/src/zm_stream.h @@ -125,6 +125,7 @@ protected: int lock_fd; bool paused; int step; + bool send_twice; // flag to send the same frame twice TimePoint now; TimePoint last_comm_update;