| 
						
						
							
								
							
						
						
					 | 
					 | 
					@ -2,6 +2,7 @@ | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include <QApplication> | 
					 | 
					 | 
					 | 
					#include <QApplication> | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include <QDebug> | 
					 | 
					 | 
					 | 
					#include <QDebug> | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "cereal/services.h" | 
					 | 
					 | 
					 | 
					#include "cereal/services.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "selfdrive/camerad/cameras/camera_common.h" | 
					 | 
					 | 
					 | 
					#include "selfdrive/camerad/cameras/camera_common.h" | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					#include "selfdrive/common/timing.h" | 
					 | 
					 | 
					 | 
					#include "selfdrive/common/timing.h" | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -19,13 +20,13 @@ inline void precise_nano_sleep(long sleep_ns) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // spin wait
 | 
					 | 
					 | 
					 | 
					  // spin wait
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if (sleep_ns > 0) { | 
					 | 
					 | 
					 | 
					  if (sleep_ns > 0) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    while ((nanos_since_boot() - start_sleep) <= sleep_ns) {/**/} | 
					 | 
					 | 
					 | 
					    while ((nanos_since_boot() - start_sleep) <= sleep_ns) { usleep(0); } | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					Replay::Replay(QString route, QStringList allow, QStringList block, SubMaster *sm_, bool dcam, bool ecam, QObject *parent) | 
					 | 
					 | 
					 | 
					Replay::Replay(QString route, QStringList allow, QStringList block, SubMaster *sm_, bool dcam, bool ecam, QObject *parent) | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    : sm(sm_), load_dcam(dcam), load_ecam(ecam), QObject(parent) { | 
					 | 
					 | 
					 | 
					    : sm(sm_), load_dcam(dcam), load_ecam(ecam), QObject(parent) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  std::vector<const char*> s; | 
					 | 
					 | 
					 | 
					  std::vector<const char *> s; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  for (const auto &it : services) { | 
					 | 
					 | 
					 | 
					  for (const auto &it : services) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if ((allow.size() == 0 || allow.contains(it.name)) && | 
					 | 
					 | 
					 | 
					    if ((allow.size() == 0 || allow.contains(it.name)) && | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        !block.contains(it.name)) { | 
					 | 
					 | 
					 | 
					        !block.contains(it.name)) { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -49,28 +50,30 @@ Replay::~Replay() { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // TODO: quit stream thread and free resources.
 | 
					 | 
					 | 
					 | 
					  // TODO: quit stream thread and free resources.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					void Replay::start(int seconds){ | 
					 | 
					 | 
					 | 
					bool Replay::load() { | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // load route
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if (!route_->load() || route_->size() == 0) { | 
					 | 
					 | 
					 | 
					  if (!route_->load() || route_->size() == 0) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    qDebug() << "failed load route" << route_->name() << "from server"; | 
					 | 
					 | 
					 | 
					    qDebug() << "failed load route" << route_->name() << "from server"; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return; | 
					 | 
					 | 
					 | 
					    return false; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  qDebug() << "load route" << route_->name() << route_->size() << "segments, start from" << seconds; | 
					 | 
					 | 
					 | 
					  qDebug() << "load route" << route_->name() << route_->size() << "segments"; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  segments_.resize(route_->size()); | 
					 | 
					 | 
					 | 
					  segments_.resize(route_->size()); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  seekTo(seconds); | 
					 | 
					 | 
					 | 
					  return true; | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					void Replay::start(int seconds) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  seekTo(seconds); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // start stream thread
 | 
					 | 
					 | 
					 | 
					  // start stream thread
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  thread = new QThread; | 
					 | 
					 | 
					 | 
					  thread = new QThread; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  QObject::connect(thread, &QThread::started, [=]() { stream(); }); | 
					 | 
					 | 
					 | 
					  QObject::connect(thread, &QThread::started, [=]() { stream(); }); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  thread->start(); | 
					 | 
					 | 
					 | 
					  thread->start(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					void Replay::updateEvents(const std::function<bool()>& lambda) { | 
					 | 
					 | 
					 | 
					void Replay::updateEvents(const std::function<bool()> &lambda) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // set updating_events to true to force stream thread relase the lock and wait for evnets_udpated.
 | 
					 | 
					 | 
					 | 
					  // set updating_events to true to force stream thread relase the lock and wait for evnets_udpated.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  updating_events_ = true; | 
					 | 
					 | 
					 | 
					  updating_events_ = true; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  { | 
					 | 
					 | 
					 | 
					  { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    std::unique_lock lk(lock_); | 
					 | 
					 | 
					 | 
					    std::unique_lock lk(stream_lock_); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    events_updated_ = lambda(); | 
					 | 
					 | 
					 | 
					    events_updated_ = lambda(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    updating_events_ = false; | 
					 | 
					 | 
					 | 
					    updating_events_ = false; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -80,22 +83,24 @@ void Replay::updateEvents(const std::function<bool()>& lambda) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					void Replay::seekTo(int seconds, bool relative) { | 
					 | 
					 | 
					 | 
					void Replay::seekTo(int seconds, bool relative) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if (segments_.empty()) return; | 
					 | 
					 | 
					 | 
					  if (segments_.empty()) return; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  bool segment_loaded = false; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  updateEvents([&]() { | 
					 | 
					 | 
					 | 
					  updateEvents([&]() { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (relative) { | 
					 | 
					 | 
					 | 
					    if (relative) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      seconds += ((cur_mono_time_ - route_start_ts_) * 1e-9); | 
					 | 
					 | 
					 | 
					      seconds += ((cur_mono_time_ - route_start_ts_) * 1e-9); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    seconds = std::clamp(seconds, 0, (int)segments_.size() * 60 - 1); | 
					 | 
					 | 
					 | 
					    qInfo() << "seeking to" << seconds; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    qInfo() << "seeking to " << seconds; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    int segment = seconds / 60; | 
					 | 
					 | 
					 | 
					    cur_mono_time_ = route_start_ts_ + std::clamp(seconds, 0, (int)segments_.size() * 60) * 1e9; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    bool segment_changed = (segment != current_segment_); | 
					 | 
					 | 
					 | 
					    current_segment_ = std::min(seconds / 60, (int)segments_.size() - 1); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					    segment_loaded = std::find(segments_merged_.begin(), segments_merged_.end(), current_segment_) != segments_merged_.end(); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    cur_mono_time_ = route_start_ts_ + seconds * 1e9; | 
					 | 
					 | 
					 | 
					    return segment_loaded; | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    setCurrentSegment(segment); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    bool segment_loaded = std::find(segments_merged_.begin(), segments_merged_.end(), segment) != segments_merged_.end(); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // return false if segment changed and not loaded yet
 | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    return !segment_changed || segment_loaded; | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  }); | 
					 | 
					 | 
					 | 
					  }); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  if (!segment_loaded) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    // always emit segmentChanged if segment is not loaded.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    // the current_segment_ may not valid when seeking cross boundary or seeking to an invalid segment.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    emit segmentChanged(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					void Replay::pause(bool pause) { | 
					 | 
					 | 
					 | 
					void Replay::pause(bool pause) { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -108,7 +113,7 @@ void Replay::pause(bool pause) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					void Replay::setCurrentSegment(int n) { | 
					 | 
					 | 
					 | 
					void Replay::setCurrentSegment(int n) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  if (current_segment_.exchange(n) != n) { | 
					 | 
					 | 
					 | 
					  if (current_segment_.exchange(n) != n) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    emit segmentChanged(n); | 
					 | 
					 | 
					 | 
					    emit segmentChanged(); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -124,11 +129,14 @@ void Replay::queueSegment() { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    end_idx = i; | 
					 | 
					 | 
					 | 
					    end_idx = i; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // skip invalid segment
 | 
					 | 
					 | 
					 | 
					    // skip invalid segment
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    fwd += segments_[i]->isValid(); | 
					 | 
					 | 
					 | 
					    if (segments_[i]->isValid()) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      ++fwd; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    } else if (i == cur_seg) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					      ++cur_seg; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // merge segments
 | 
					 | 
					 | 
					 | 
					  mergeSegments(std::min(cur_seg, (int)segments_.size() - 1), end_idx); | 
				
			
			
				
				
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  mergeSegments(cur_seg, end_idx); | 
					 | 
					 | 
					 | 
					 | 
				
			
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					} | 
					 | 
					 | 
					 | 
					} | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					void Replay::mergeSegments(int cur_seg, int end_idx) { | 
					 | 
					 | 
					 | 
					void Replay::mergeSegments(int cur_seg, int end_idx) { | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -138,7 +146,7 @@ void Replay::mergeSegments(int cur_seg, int end_idx) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  for (int i = begin_idx; i <= end_idx; ++i) { | 
					 | 
					 | 
					 | 
					  for (int i = begin_idx; i <= end_idx; ++i) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    if (segments_[i] && segments_[i]->isLoaded()) { | 
					 | 
					 | 
					 | 
					    if (segments_[i] && segments_[i]->isLoaded()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      segments_need_merge.push_back(i); | 
					 | 
					 | 
					 | 
					      segments_need_merge.push_back(i); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    } else if (i >= cur_seg) { | 
					 | 
					 | 
					 | 
					    } else if (i >= cur_seg && segments_[i] && segments_[i]->isValid()) { | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      // segment is valid,but still loading. can't skip it to merge the next one.
 | 
					 | 
					 | 
					 | 
					      // segment is valid,but still loading. can't skip it to merge the next one.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      // otherwise the stream thread may jump to the next segment.
 | 
					 | 
					 | 
					 | 
					      // otherwise the stream thread may jump to the next segment.
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      break; | 
					 | 
					 | 
					 | 
					      break; | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -150,6 +158,8 @@ void Replay::mergeSegments(int cur_seg, int end_idx) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    // merge & sort events
 | 
					 | 
					 | 
					 | 
					    // merge & sort events
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    std::vector<Event *> *new_events = new std::vector<Event *>(); | 
					 | 
					 | 
					 | 
					    std::vector<Event *> *new_events = new std::vector<Event *>(); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    new_events->reserve(std::accumulate(segments_need_merge.begin(), segments_need_merge.end(), 0, | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					                                        [=](int v, int n) { return v + segments_[n]->log->events.size(); })); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    for (int n : segments_need_merge) { | 
					 | 
					 | 
					 | 
					    for (int n : segments_need_merge) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      auto &log = segments_[n]->log; | 
					 | 
					 | 
					 | 
					      auto &log = segments_[n]->log; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      auto middle = new_events->insert(new_events->end(), log->events.begin(), log->events.end()); | 
					 | 
					 | 
					 | 
					      auto middle = new_events->insert(new_events->end(), log->events.begin(), log->events.end()); | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -164,7 +174,8 @@ void Replay::mergeSegments(int cur_seg, int end_idx) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        auto it = std::find_if(new_events->begin(), new_events->end(), [=](auto e) { return e->which == cereal::Event::Which::INIT_DATA; }); | 
					 | 
					 | 
					 | 
					        auto it = std::find_if(new_events->begin(), new_events->end(), [=](auto e) { return e->which == cereal::Event::Which::INIT_DATA; }); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        if (it != new_events->end()) { | 
					 | 
					 | 
					 | 
					        if (it != new_events->end()) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          route_start_ts_ = (*it)->mono_time; | 
					 | 
					 | 
					 | 
					          route_start_ts_ = (*it)->mono_time; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					          cur_mono_time_ = route_start_ts_; | 
					 | 
					 | 
					 | 
					          // cur_mono_time_ is set by seekTo int start() before get route_start_ts_
 | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					          cur_mono_time_ += route_start_ts_; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					        } | 
					 | 
					 | 
					 | 
					        } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      } | 
					 | 
					 | 
					 | 
					      } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -173,6 +184,8 @@ void Replay::mergeSegments(int cur_seg, int end_idx) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					      return true; | 
					 | 
					 | 
					 | 
					      return true; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    }); | 
					 | 
					 | 
					 | 
					    }); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    delete prev_events; | 
					 | 
					 | 
					 | 
					    delete prev_events; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					  } else { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					 | 
					    updateEvents([]() { return true; }); | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  } | 
					 | 
					 | 
					 | 
					  } | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  // free segments out of current semgnt window.
 | 
					 | 
					 | 
					 | 
					  // free segments out of current semgnt window.
 | 
				
			
			
		
	
	
		
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
					 | 
					@ -187,7 +200,7 @@ void Replay::stream() { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  float last_print = 0; | 
					 | 
					 | 
					 | 
					  float last_print = 0; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  cereal::Event::Which cur_which = cereal::Event::Which::INIT_DATA; | 
					 | 
					 | 
					 | 
					  cereal::Event::Which cur_which = cereal::Event::Which::INIT_DATA; | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  std::unique_lock lk(lock_); | 
					 | 
					 | 
					 | 
					  std::unique_lock lk(stream_lock_); | 
				
			
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					
 | 
					 | 
					 | 
					 | 
					
 | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					  while (true) { | 
					 | 
					 | 
					 | 
					  while (true) { | 
				
			
			
		
	
		
		
			
				
					
					 | 
					 | 
					 | 
					    stream_cv_.wait(lk, [=]() { return exit_ || (events_updated_ && !paused_); }); | 
					 | 
					 | 
					 | 
					    stream_cv_.wait(lk, [=]() { return exit_ || (events_updated_ && !paused_); }); | 
				
			
			
		
	
	
		
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
					 | 
					
  |