#include "tools/cabana/streams/livestream.h" #include #include struct LiveStream::Logger { Logger() : start_ts(seconds_since_epoch()), segment_num(-1) {} void write(const char *data, const size_t size) { int n = (seconds_since_epoch() - start_ts) / 60.0; if (std::exchange(segment_num, n) != segment_num) { QString dir = QString("%1/%2--%3") .arg(settings.log_path) .arg(QDateTime::fromSecsSinceEpoch(start_ts).toString("yyyy-MM-dd--hh-mm-ss")) .arg(n); util::create_directories(dir.toStdString(), 0755); fs.reset(new std::ofstream((dir + "/rlog").toStdString(), std::ios::binary | std::ios::out)); } fs->write(data, size); } std::unique_ptr fs; int segment_num; uint64_t start_ts; }; LiveStream::LiveStream(QObject *parent) : AbstractStream(parent) { if (settings.log_livestream) { logger = std::make_unique(); } stream_thread = new QThread(this); QObject::connect(&settings, &Settings::changed, this, &LiveStream::startUpdateTimer); QObject::connect(stream_thread, &QThread::started, [=]() { streamThread(); }); QObject::connect(stream_thread, &QThread::finished, stream_thread, &QThread::deleteLater); } void LiveStream::startUpdateTimer() { update_timer.stop(); update_timer.start(1000.0 / settings.fps, this); timer_id = update_timer.timerId(); } void LiveStream::start() { emit streamStarted(); stream_thread->start(); startUpdateTimer(); } LiveStream::~LiveStream() { update_timer.stop(); stream_thread->requestInterruption(); stream_thread->quit(); stream_thread->wait(); } // called in streamThread void LiveStream::handleEvent(const char *data, const size_t size) { if (logger) { logger->write(data, size); } std::lock_guard lk(lock); auto &msg = receivedMessages.emplace_back(data, size); receivedEvents.push_back(msg.event); } void LiveStream::timerEvent(QTimerEvent *event) { if (event->timerId() == timer_id) { { // merge events received from live stream thread. std::lock_guard lk(lock); mergeEvents(receivedEvents.cbegin(), receivedEvents.cend()); receivedEvents.clear(); receivedMessages.clear(); } if (!all_events_.empty()) { begin_event_ts = all_events_.front()->mono_time; updateEvents(); return; } } QObject::timerEvent(event); } void LiveStream::updateEvents() { static double prev_speed = 1.0; if (first_update_ts == 0) { first_update_ts = nanos_since_boot(); first_event_ts = current_event_ts = all_events_.back()->mono_time; } if (paused_ || prev_speed != speed_) { prev_speed = speed_; first_update_ts = nanos_since_boot(); first_event_ts = current_event_ts; return; } uint64_t last_ts = post_last_event && speed_ == 1.0 ? all_events_.back()->mono_time : first_event_ts + (nanos_since_boot() - first_update_ts) * speed_; auto first = std::upper_bound(all_events_.cbegin(), all_events_.cend(), current_event_ts, [](uint64_t ts, auto e) { return ts < e->mono_time; }); auto last = std::upper_bound(first, all_events_.cend(), last_ts, [](uint64_t ts, auto e) { return ts < e->mono_time; }); for (auto it = first; it != last; ++it) { const CanEvent *e = *it; MessageId id = {.source = e->src, .address = e->address}; updateEvent(id, (e->mono_time - begin_event_ts) / 1e9, e->dat, e->size); current_event_ts = e->mono_time; } postEvents(); } void LiveStream::seekTo(double sec) { sec = std::max(0.0, sec); first_update_ts = nanos_since_boot(); current_event_ts = first_event_ts = std::min(sec * 1e9 + begin_event_ts, lastEventMonoTime()); post_last_event = (first_event_ts == lastEventMonoTime()); emit seekedTo((current_event_ts - begin_event_ts) / 1e9); } void LiveStream::pause(bool pause) { paused_ = pause; emit(pause ? paused() : resume()); }