#pragma once #include #include #include #include #include #include #include #include "tools/replay/camera.h" #include "tools/replay/seg_mgr.h" #include "tools/replay/timeline.h" #define DEMO_ROUTE "a2a0ccea32023010|2023-07-27--13-01-19" 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_NO_VIPC = 0x0400, REPLAY_FLAG_ALL_SERVICES = 0x0800, }; class Replay { public: Replay(const std::string &route, std::vector allow, std::vector block, SubMaster *sm = nullptr, uint32_t flags = REPLAY_FLAG_NONE, const std::string &data_dir = "", bool auto_source = false); ~Replay(); bool load(); RouteLoadError lastRouteError() const { return route().lastError(); } void start(int seconds = 0) { seekTo(min_seconds_ + seconds, false); } void pause(bool pause); void seekToFlag(FindFlag flag); void seekTo(double seconds, bool relative); inline bool isPaused() const { return user_paused_; } inline int segmentCacheLimit() const { return seg_mgr_->segment_cache_limit_; } inline void setSegmentCacheLimit(int n) { seg_mgr_->segment_cache_limit_ = std::max(MIN_SEGMENTS_CACHE, n); } inline bool hasFlag(REPLAY_FLAGS flag) const { return flags_ & flag; } void setLoop(bool loop) { loop ? flags_ &= ~REPLAY_FLAG_NO_LOOP : flags_ |= REPLAY_FLAG_NO_LOOP; } bool loop() const { return !(flags_ & REPLAY_FLAG_NO_LOOP); } const Route &route() const { return seg_mgr_->route_; } inline double currentSeconds() const { return double(cur_mono_time_ - route_start_ts_) / 1e9; } inline std::time_t routeDateTime() const { return route_date_time_; } inline uint64_t routeStartNanos() const { return route_start_ts_; } inline double toSeconds(uint64_t mono_time) const { return (mono_time - route_start_ts_) / 1e9; } inline double minSeconds() const { return min_seconds_; } inline double maxSeconds() const { return max_seconds_; } inline void setSpeed(float speed) { speed_ = speed; } inline float getSpeed() const { return speed_; } inline const std::string &carFingerprint() const { return car_fingerprint_; } inline const std::shared_ptr> getTimeline() const { return timeline_.getEntries(); } inline const std::optional findAlertAtTime(double sec) const { return timeline_.findAlertAtTime(sec); } const std::shared_ptr getEventData() const { return seg_mgr_->getEventData(); } void installEventFilter(std::function filter) { event_filter_ = filter; } // Event callback functions std::function onSegmentsMerged = nullptr; std::function onSeeking = nullptr; std::function onSeekedTo = nullptr; std::function)> onQLogLoaded = nullptr; private: void setupServices(const std::vector &allow, const std::vector &block); void setupSegmentManager(bool has_filters); void startStream(const std::shared_ptr segment); void streamThread(); void handleSegmentMerge(); void interruptStream(const std::function& update_fn); std::vector::const_iterator publishEvents(std::vector::const_iterator first, std::vector::const_iterator last); void publishMessage(const Event *e); void publishFrame(const Event *e); void checkSeekProgress(); std::unique_ptr seg_mgr_; Timeline timeline_; pthread_t stream_thread_id = 0; std::thread stream_thread_; std::mutex stream_lock_; bool user_paused_ = false; std::condition_variable stream_cv_; std::atomic current_segment_ = 0; std::atomic seeking_to_ = -1.0; std::atomic exit_ = false; std::atomic interrupt_requested_ = false; bool events_ready_ = false; std::time_t route_date_time_; uint64_t route_start_ts_ = 0; std::atomic cur_mono_time_ = 0; cereal::Event::Which cur_which_ = cereal::Event::Which::INIT_DATA; double min_seconds_ = 0; double max_seconds_ = 0; SubMaster *sm_ = nullptr; std::unique_ptr pm_; std::vector sockets_; std::unique_ptr camera_server_; std::atomic flags_ = REPLAY_FLAG_NONE; std::string car_fingerprint_; std::atomic speed_ = 1.0; std::function event_filter_ = nullptr; std::shared_ptr event_data_ = std::make_shared(); };