#pragma once #include #include #include "tools/replay/camera.h" #include "tools/replay/route.h" // one segment uses about 100M of memory constexpr int FORWARD_SEGS = 5; enum REPLAY_FLAGS { REPLAY_FLAG_NONE = 0x0000, REPLAY_FLAG_DCAM = 0x0002, REPLAY_FLAG_ECAM = 0x0004, REPLAY_FLAG_NO_LOOP = 0x0010, REPLAY_FLAG_NO_FILE_CACHE = 0x0020, REPLAY_FLAG_QCAMERA = 0x0040, REPLAY_FLAG_NO_HW_DECODER = 0x0100, REPLAY_FLAG_FULL_SPEED = 0x0200, REPLAY_FLAG_NO_VIPC = 0x0400, }; enum class FindFlag { nextEngagement, nextDisEngagement, nextUserFlag, }; enum class TimelineType { None, Engaged, AlertInfo, AlertWarning, AlertCritical, UserFlag }; class Replay : public QObject { Q_OBJECT public: Replay(QString route, QStringList allow, QStringList block, SubMaster *sm = nullptr, uint32_t flags = REPLAY_FLAG_NONE, QString data_dir = "", QObject *parent = 0); ~Replay(); bool load(); void start(int seconds = 0); void stop(); void pause(bool pause); void seekToFlag(FindFlag flag); void seekTo(int seconds, bool relative); inline bool isPaused() const { return paused_; } inline bool hasFlag(REPLAY_FLAGS flag) const { return flags_ & flag; } inline void addFlag(REPLAY_FLAGS flag) { flags_ |= flag; } inline void removeFlag(REPLAY_FLAGS flag) { flags_ &= ~flag; } inline const Route* route() const { return route_.get(); } inline int currentSeconds() const { return (cur_mono_time_ - route_start_ts_) / 1e9; } inline int toSeconds(uint64_t mono_time) const { return (mono_time - route_start_ts_) / 1e9; } inline int totalSeconds() const { return segments_.size() * 60; } inline const std::string &carFingerprint() const { return car_fingerprint_; } inline const std::vector> getTimeline() { std::lock_guard lk(timeline_lock); return timeline; } signals: void streamStarted(); protected slots: void segmentLoadFinished(bool sucess); protected: typedef std::map> SegmentMap; std::optional find(FindFlag flag); void startStream(const Segment *cur_segment); void stream(); void setCurrentSegment(int n); void queueSegment(); void mergeSegments(const SegmentMap::iterator &begin, const SegmentMap::iterator &end); void updateEvents(const std::function& lambda); void publishMessage(const Event *e); void publishFrame(const Event *e); void buildTimeline(); inline bool isSegmentMerged(int n) { return std::find(segments_merged_.begin(), segments_merged_.end(), n) != segments_merged_.end(); } QThread *stream_thread_ = nullptr; // logs std::mutex stream_lock_; std::condition_variable stream_cv_; std::atomic updating_events_ = false; std::atomic current_segment_ = 0; SegmentMap segments_; // the following variables must be protected with stream_lock_ std::atomic exit_ = false; bool paused_ = false; bool events_updated_ = false; uint64_t route_start_ts_ = 0; uint64_t cur_mono_time_ = 0; std::unique_ptr> events_; std::unique_ptr> new_events_; std::vector segments_merged_; // messaging SubMaster *sm = nullptr; std::unique_ptr pm; std::vector sockets_; std::unique_ptr route_; std::unique_ptr camera_server_; std::atomic flags_ = REPLAY_FLAG_NONE; std::mutex timeline_lock; QFuture timeline_future; std::vector> timeline; std::string car_fingerprint_; };