@@ -257,10 +257,17 @@ void StreamHistoryTracker::on_frame(std::shared_ptr<const VideoFrame> frame){
257257 // don't save every frame. only save frames as per m_target_fps
258258 // Only save when we've crossed the next sampling boundary
259259 if (frame->timestamp < m_next_frame_time){
260- return ; // skip
260+ return ; // skip frame
261261 }
262262
263263 // Advance by fixed intervals
264+ // Next frame time is anchored relative to the first frame's time, with increments by a multiple of m_frame_interval,
265+ // instead of being relative to the current frame's time. This prevents timing drift.
266+
267+ // If there is a massive jump in time (e.g. the stream pauses for 5 seconds),
268+ // the while loop advances the schedule multiple times until it is once again ahead of the
269+ // current timestamp. If this happens, there will be a matching gap in the saved frames.
270+ // We handle this gap by duplicating frames in the save() function, so that we maintain a constant frame rate.
264271 while (m_next_frame_time <= frame->timestamp ){
265272 m_next_frame_time += std::chrono::microseconds (m_frame_interval);
266273 }
@@ -362,7 +369,7 @@ bool StreamHistoryTracker::save(const std::string& filename) const{
362369
363370 // 2. Loop through frames
364371 for (CompressedVideoFrame frame : frames) {
365- // Insert dummy frames if there is a gap due to dropping frames.
372+ // Insert duplicate frames if there is a gap due to dropping frames.
366373 // Because VideoWriter can only handle a fixed frame rate.
367374 while (current_timeline + m_frame_interval < frame.timestamp ) {
368375 // Decompress last known good frame and write again
0 commit comments