diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 14b0fc9a3..442033c50 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -60,8 +60,8 @@ Event::Event( //snapshit_file(), //alarm_file(""), videoStore(nullptr), - //video_name(""), //video_file(""), + //video_path(""), last_db_frame(0), have_video_keyframe(false), //scheme @@ -104,6 +104,13 @@ Event::Event( // Copy it in case opening the mp4 doesn't work we can set it to another value save_jpegs = monitor->GetOptSaveJPEGs(); Storage * storage = monitor->getStorage(); + if (monitor->GetOptVideoWriter() != 0) { + container = monitor->OutputContainer(); + if ( container == "auto" || container == "" ) { + container = "mp4"; + } + video_incomplete_file = "incomplete."+container; + } std::string sql = stringtf( "INSERT INTO `Events` " @@ -120,7 +127,7 @@ Event::Event( state_id, monitor->getOrientation(), 0, - "", + video_incomplete_file.c_str(), save_jpegs, storage->SchemeString().c_str() ); @@ -178,24 +185,16 @@ Event::Event( } // end if ! setPath(Storage) Debug(1, "Using storage area at %s", path.c_str()); - video_name = ""; - snapshot_file = path + "/snapshot.jpg"; alarm_file = path + "/alarm.jpg"; - /* Save as video */ + video_incomplete_path = path + "/" + video_incomplete_file; - if ( monitor->GetOptVideoWriter() != 0 ) { - std::string container = monitor->OutputContainer(); - if ( container == "auto" || container == "" ) { - container = "mp4"; - } + if (monitor->GetOptVideoWriter() != 0) { + /* Save as video */ - video_name = stringtf("%" PRIu64 "-%s.%s", id, "video", container.c_str()); - video_file = path + "/" + video_name; - Debug(1, "Writing video file to %s", video_file.c_str()); videoStore = new VideoStore( - video_file.c_str(), + video_incomplete_path.c_str(), container.c_str(), monitor->GetVideoStream(), monitor->GetVideoCodecContext(), @@ -213,8 +212,10 @@ Event::Event( zmDbDo(sql); } } else { - sql = stringtf("UPDATE Events SET Videoed=1, DefaultVideo = '%s' WHERE Id=%" PRIu64, video_name.c_str(), id); - zmDbDo(sql); + std::string codec = videoStore->get_codec(); + video_file = stringtf("%" PRIu64 "-%s.%s.%s", id, "video", codec.c_str(), container.c_str()); + video_path = path + "/" + video_file; + Debug(1, "Video file is %s", video_file.c_str()); } } // end if GetOptVideoWriter } @@ -223,10 +224,18 @@ Event::~Event() { // We close the videowriter first, because if we finish the event, we might try to view the file, but we aren't done writing it yet. /* Close the video file */ - if ( videoStore != nullptr ) { + if (videoStore != nullptr) { Debug(4, "Deleting video store"); delete videoStore; videoStore = nullptr; + int result = rename(video_incomplete_path.c_str(), video_path.c_str()); + if (result == 0) { + Debug(1, "File successfully renamed"); + } else { + Error("Failed renaming %s to %s", video_incomplete_path.c_str(), video_path.c_str()); + // So that we don't update the event record + video_file = video_incomplete_file; + } } // endtime is set in AddFrame, so SHOULD be set to the value of the last frame timestamp. @@ -245,21 +254,23 @@ Event::~Event() { } std::string sql = stringtf( - "UPDATE Events SET Name='%s%" PRIu64 "', EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64 " AND Name='New Event'", + "UPDATE Events SET Name='%s%" PRIu64 "', EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo='%s' WHERE Id = %" PRIu64 " AND Name='New Event'", monitor->EventPrefix(), id, std::chrono::system_clock::to_time_t(end_time), delta_time.count(), frames, alarm_frames, tot_score, static_cast(alarm_frames ? (tot_score / alarm_frames) : 0), max_score, + video_file.c_str(), // defaults to "" id); if (!zmDbDoUpdate(sql)) { // Name might have been changed during recording, so just do the update without changing the name. sql = stringtf( - "UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d WHERE Id = %" PRIu64, + "UPDATE Events SET EndDateTime = from_unixtime(%ld), Length = %.2f, Frames = %d, AlarmFrames = %d, TotScore = %d, AvgScore = %d, MaxScore = %d, DefaultVideo='%s' WHERE Id = %" PRIu64, std::chrono::system_clock::to_time_t(end_time), delta_time.count(), frames, alarm_frames, tot_score, static_cast(alarm_frames ? (tot_score / alarm_frames) : 0), max_score, + video_file.c_str(), // defaults to "" id); zmDbDoUpdate(sql); } // end if no changed rows due to Name change during recording diff --git a/src/zm_event.h b/src/zm_event.h index 8b8e49322..611b2f716 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -84,8 +84,13 @@ class Event { std::string alarm_file; VideoStore *videoStore; - std::string video_name; + std::string container; + std::string codec; std::string video_file; + std::string video_path; + std::string video_incomplete_file; + std::string video_incomplete_path; + int last_db_frame; bool have_video_keyframe; // a flag to tell us if we have had a video keyframe when writing an mp4. The first frame SHOULD be a video keyframe. Storage::Schemes scheme; diff --git a/src/zm_image.cpp b/src/zm_image.cpp index 2b7bc69e2..295426254 100644 --- a/src/zm_image.cpp +++ b/src/zm_image.cpp @@ -270,7 +270,6 @@ int Image::PopulateFrame(AVFrame *frame) { frame->width = width; frame->height = height; frame->format = imagePixFormat; - Debug(1, "PopulateFrame: width %d height %d linesize %d colours %d imagesize %d", width, height, linesize, colours, size); zm_dump_video_frame(frame, "Image.Populate(frame)"); return 1; } // int Image::PopulateFrame(AVFrame *frame) diff --git a/src/zm_monitor.cpp b/src/zm_monitor.cpp index 93ae2d026..679ea27b7 100644 --- a/src/zm_monitor.cpp +++ b/src/zm_monitor.cpp @@ -1656,7 +1656,7 @@ void Monitor::CheckAction() { } } -void Monitor::UpdateCaptureFPS() { +void Monitor::UpdateFPS() { if ( fps_report_interval and ( !(image_count%fps_report_interval) @@ -1675,82 +1675,35 @@ void Monitor::UpdateCaptureFPS() { uint32 new_camera_bytes = camera->Bytes(); uint32 new_capture_bandwidth = static_cast((new_camera_bytes - last_camera_bytes) / elapsed.count()); - last_camera_bytes = new_camera_bytes; + double new_analysis_fps = (motion_frame_count - last_motion_frame_count) / elapsed.count(); - Debug(4, "%s: %d - last %d = %d now:%lf, last %lf, elapsed %lf = %lffps", - "Capturing", + Debug(4, "FPS: capture count %d - last capture count %d = %d now:%lf, last %lf, elapsed %lf = capture: %lf fps analysis: %lf fps", image_count, last_capture_image_count, image_count - last_capture_image_count, FPSeconds(now.time_since_epoch()).count(), - FPSeconds(last_analysis_fps_time.time_since_epoch()).count(), + FPSeconds(last_fps_time.time_since_epoch()).count(), elapsed.count(), - new_capture_fps); + new_capture_fps, + new_analysis_fps); - Info("%s: %d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec", - name.c_str(), image_count, new_capture_fps, new_capture_bandwidth); + Info("%s: %d - Capturing at %.2lf fps, capturing bandwidth %ubytes/sec Analysing at %.2lf fps", + name.c_str(), image_count, new_capture_fps, new_capture_bandwidth, new_analysis_fps); shared_data->capture_fps = new_capture_fps; last_fps_time = now; last_capture_image_count = image_count; + shared_data->analysis_fps = new_analysis_fps; + last_motion_frame_count = motion_frame_count; + last_camera_bytes = new_camera_bytes; std::string sql = stringtf( - "UPDATE LOW_PRIORITY Monitor_Status SET CaptureFPS = %.2lf, CaptureBandwidth=%u WHERE MonitorId=%u", - new_capture_fps, new_capture_bandwidth, id); + "UPDATE LOW_PRIORITY Monitor_Status SET CaptureFPS = %.2lf, CaptureBandwidth=%u, AnalysisFPS = %.2lf WHERE MonitorId=%u", + new_capture_fps, new_capture_bandwidth, new_analysis_fps, id); dbQueue.push(std::move(sql)); } // now != last_fps_time } // end if report fps -} // void Monitor::UpdateCaptureFPS() - -void Monitor::UpdateAnalysisFPS() { - Debug(1, "analysis_image_count(%d) motion_count(%d) fps_report_interval(%d) mod%d", - analysis_image_count, motion_frame_count, fps_report_interval, - ((analysis_image_count && fps_report_interval) ? !(analysis_image_count%fps_report_interval) : -1 ) ); - - if ( - ( analysis_image_count and fps_report_interval and !(analysis_image_count%fps_report_interval) ) - or - // In startup do faster updates - ( (analysis_image_count < fps_report_interval) and !(analysis_image_count%10) ) - ) { - SystemTimePoint now = std::chrono::system_clock::now(); - - FPSeconds elapsed = now - last_analysis_fps_time; - Debug(4, "%s: %d - now: %.2f, last %lf, diff %lf", - name.c_str(), - analysis_image_count, - FPSeconds(now.time_since_epoch()).count(), - FPSeconds(last_analysis_fps_time.time_since_epoch()).count(), - elapsed.count()); - - if (elapsed > Seconds(1)) { - double new_analysis_fps = (motion_frame_count - last_motion_frame_count) / elapsed.count(); - Info("%s: %d - Analysing at %.2lf fps from %d - %d=%d / %lf - %lf = %lf", - name.c_str(), - analysis_image_count, - new_analysis_fps, - motion_frame_count, - last_motion_frame_count, - (motion_frame_count - last_motion_frame_count), - FPSeconds(now.time_since_epoch()).count(), - FPSeconds(last_analysis_fps_time.time_since_epoch()).count(), - elapsed.count()); - - if (new_analysis_fps != shared_data->analysis_fps) { - shared_data->analysis_fps = new_analysis_fps; - - std::string sql = stringtf("UPDATE LOW_PRIORITY Monitor_Status SET AnalysisFPS = %.2lf WHERE MonitorId=%u", - new_analysis_fps, id); - dbQueue.push(std::move(sql)); - last_analysis_fps_time = now; - last_motion_frame_count = motion_frame_count; - } else { - Debug(4, "No change in fps"); - } // end if change in fps - } // end if at least 1 second has passed since last update - - } // end if time to do an update -} // end void Monitor::UpdateAnalysisFPS +} // void Monitor::UpdateFPS() // Would be nice if this JUST did analysis // This idea is that we should be analysing as close to the capture frame as possible. @@ -2299,8 +2252,6 @@ bool Monitor::Analyse() { // Only do these if it's a video packet. shared_data->last_read_index = snap->image_index; analysis_image_count++; - if (function == MODECT or function == MOCORD) - UpdateAnalysisFPS(); } packetqueue.increment_it(analysis_it); packetqueue.unlock(packet_lock); @@ -2585,7 +2536,6 @@ int Monitor::Capture() { // Will only be queued if there are iterators allocated in the queue. packetqueue.queuePacket(packet); - UpdateCaptureFPS(); } else { // result == 0 // Question is, do we update last_write_index etc? return 0; @@ -2625,7 +2575,7 @@ bool Monitor::Decode() { // //capture_image = packet->image = new Image(width, height, camera->Colours(), camera->SubpixelOrder()); int ret = packet->decode(camera->getVideoCodecContext()); - if (ret > 0) { + if (ret > 0 and !zm_terminate) { if (packet->in_frame and !packet->image) { packet->image = new Image(camera_width, camera_height, camera->Colours(), camera->SubpixelOrder()); AVFrame *input_frame = packet->in_frame; diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 92b724e65..56785405f 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -546,8 +546,7 @@ public: unsigned int GetLastWriteIndex() const; uint64_t GetLastEventId() const; double GetFPS() const; - void UpdateAnalysisFPS(); - void UpdateCaptureFPS(); + void UpdateFPS(); void ForceAlarmOn( int force_score, const char *force_case, const char *force_text="" ); void ForceAlarmOff(); void CancelForced(); diff --git a/src/zm_signal.cpp b/src/zm_signal.cpp index 9a92b5eac..4107e96c8 100644 --- a/src/zm_signal.cpp +++ b/src/zm_signal.cpp @@ -46,6 +46,7 @@ RETSIGTYPE zm_die_handler(int signal, siginfo_t * info, void *context) RETSIGTYPE zm_die_handler(int signal) #endif { + zm_terminate = true; Error("Got signal %d (%s), crashing", signal, strsignal(signal)); #if (defined(__i386__) || defined(__x86_64__)) // Get more information if available diff --git a/src/zm_videostore.h b/src/zm_videostore.h index 682fc147b..c73ed19db 100644 --- a/src/zm_videostore.h +++ b/src/zm_videostore.h @@ -111,6 +111,13 @@ class VideoStore { int writePacket(const std::shared_ptr &pkt); int write_packets(PacketQueue &queue); void flush_codecs(); + const char *get_codec() { + if (chosen_codec_data) + return chosen_codec_data->codec_codec; + if (video_out_stream) + return avcodec_get_name(video_out_stream->codecpar->codec_id); + return ""; + } }; #endif // ZM_VIDEOSTORE_H diff --git a/src/zmc.cpp b/src/zmc.cpp index d974d1bec..68c6227af 100644 --- a/src/zmc.cpp +++ b/src/zmc.cpp @@ -268,7 +268,7 @@ int main(int argc, char *argv[]) { std::this_thread::sleep_for(sleep_time); } - if (zm_terminate){ + if (zm_terminate) { break; } @@ -308,6 +308,7 @@ int main(int argc, char *argv[]) { result = -1; break; } + monitors[i]->UpdateFPS(); // capture_delay is the amount of time we should sleep in useconds to achieve the desired framerate. Microseconds delay = (monitors[i]->GetState() == Monitor::ALARM) ? monitors[i]->GetAlarmCaptureDelay()