diff --git a/src/zm_event.cpp b/src/zm_event.cpp index 109df5dd3..e564479f3 100644 --- a/src/zm_event.cpp +++ b/src/zm_event.cpp @@ -256,6 +256,20 @@ Event::~Event() { if ( frame_data.size() ) WriteDbFrames(); + // update frame deltas to refer to video start time which may be a few frames before event start + struct timeval video_offset = {0}; + struct timeval video_start_time = monitor->GetVideoWriterStartTime(); + if (video_start_time.tv_sec > 0) { + timersub(&video_start_time, &start_time, &video_offset); + Debug(1, "Updating frames delta by %d sec %d usec", + video_offset.tv_sec, video_offset.tv_usec); + UpdateFramesDelta(video_offset.tv_sec + video_offset.tv_usec*1e-6); + } + else { + Debug(3, "Video start_time %d sec %d usec not valid -- frame deltas not updated", + video_start_time.tv_sec, video_start_time.tv_usec); + } + // Should not be static because we might be multi-threaded char sql[ZM_SQL_LGE_BUFSIZ]; snprintf(sql, sizeof(sql), @@ -554,6 +568,27 @@ void Event::WriteDbFrames() { db_mutex.unlock(); } // end void Event::WriteDbFrames() +// Subtract an offset time from frames deltas to match with video start time +void Event::UpdateFramesDelta(double offset) { + char sql[ZM_SQL_MED_BUFSIZ]; + + if (offset == 0.0) return; + // the table is set to auto update timestamp so we force it to keep current value + snprintf(sql, sizeof(sql), + "UPDATE Frames SET timestamp = timestamp, Delta = Delta - (%.4f) WHERE EventId = %" PRIu64, + offset, id); + + db_mutex.lock(); + if (mysql_query(&dbconn, sql)) { + db_mutex.unlock(); + Error("Can't update frames: %s, sql was %s", mysql_error(&dbconn), sql); + return; + } + db_mutex.unlock(); + Info("Updating frames delta by %0.2f sec to match video file", offset); +} + + void Event::AddFrame(Image *image, struct timeval timestamp, int score, Image *alarm_image) { if ( !timestamp.tv_sec ) { Debug(1, "Not adding new frame, zero timestamp"); diff --git a/src/zm_event.h b/src/zm_event.h index 2035b6af6..77199509c 100644 --- a/src/zm_event.h +++ b/src/zm_event.h @@ -132,6 +132,7 @@ class Event { private: void AddFramesInternal( int n_frames, int start_frame, Image **images, struct timeval **timestamps ); void WriteDbFrames(); + void UpdateFramesDelta(double offset); public: static const char *getSubPath( struct tm *time ) { diff --git a/src/zm_ffmpeg_camera.cpp b/src/zm_ffmpeg_camera.cpp index 029a2de0d..ef4dfc057 100644 --- a/src/zm_ffmpeg_camera.cpp +++ b/src/zm_ffmpeg_camera.cpp @@ -872,6 +872,7 @@ int FfmpegCamera::CaptureAndRecord( // since the last keyframe. unsigned int packet_count = 0; ZMPacket *queued_packet; + struct timeval video_offset = {0}; // Clear all packets that predate the moment when the recording began packetqueue->clear_unwanted_packets( @@ -880,6 +881,14 @@ int FfmpegCamera::CaptureAndRecord( while ( (queued_packet = packetqueue->popPacket()) ) { AVPacket *avp = queued_packet->av_packet(); + // compute time offset between event start and first frame in video + if (packet_count == 0){ + monitor->SetVideoWriterStartTime(queued_packet->timestamp); + timersub(&queued_packet->timestamp, &recording, &video_offset); + Info("Event video offset is %.3f sec (<0 means video starts early)", + video_offset.tv_sec + video_offset.tv_usec*1e-6); + } + packet_count += 1; // Write the packet to our video store Debug(2, "Writing queued packet stream: %d KEY %d, remaining (%d)", diff --git a/src/zm_monitor.h b/src/zm_monitor.h index 97f173845..9109c9c72 100644 --- a/src/zm_monitor.h +++ b/src/zm_monitor.h @@ -466,6 +466,8 @@ public: const std::vector* GetOptEncoderParams() const { return &encoderparamsvec; } uint64_t GetVideoWriterEventId() const { return video_store_data->current_event; } void SetVideoWriterEventId( unsigned long long p_event_id ) { video_store_data->current_event = p_event_id; } + struct timeval GetVideoWriterStartTime() const { return video_store_data->recording; } + void SetVideoWriterStartTime(struct timeval &t) { video_store_data->recording = t; } unsigned int GetPreEventCount() const { return pre_event_count; }; struct timeval GetVideoBufferDuration() const { return video_buffer_duration; };