cabana: add a slider to adjust chart range from 1 sec to max cached seconds. (#27005)

* add slider to set chart range

* set minumu to 1 sec

* make chart work in any range

* cleanup

* rename to max_cached_minitues

* more

* dont draw out of plotarea

* cleanup

* updateLayout after chart removed

* cleanup

* fix clamp

* usea same cur_sec to update all charts
pull/27020/head
Dean Lee 2 years ago committed by GitHub
parent 3efb22e82b
commit f0d22f88e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 187
      tools/cabana/chartswidget.cc
  2. 17
      tools/cabana/chartswidget.h
  3. 29
      tools/cabana/settings.cc
  4. 7
      tools/cabana/settings.h
  5. 2
      tools/cabana/streams/livestream.h
  6. 4
      tools/cabana/streams/replaystream.cc

@ -2,10 +2,11 @@
#include <QApplication>
#include <QCompleter>
#include <QDialogButtonBox>
#include <QDrag>
#include <QLineEdit>
#include <QFutureSynchronizer>
#include <QGraphicsLayout>
#include <QLineEdit>
#include <QRubberBand>
#include <QToolBar>
#include <QToolButton>
@ -24,10 +25,9 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
toolbar->setIconSize({16, 16});
toolbar->addWidget(title_label = new QLabel());
title_label->setContentsMargins(0 ,0, 12, 0);
title_label->setContentsMargins(0, 0, 12, 0);
columns_cb = new QComboBox(this);
columns_cb->addItems({"1", "2", "3", "4"});
columns_cb->setCurrentIndex(std::clamp(settings.chart_column_count - 1, 0, 3));
toolbar->addWidget(new QLabel(tr("Columns:")));
toolbar->addWidget(columns_cb);
@ -35,8 +35,16 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
stretch_label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
toolbar->addWidget(stretch_label);
show_all_values_btn = toolbar->addAction("");
toolbar->addWidget(range_label = new QLabel());
toolbar->addWidget(new QLabel(tr("Range:")));
toolbar->addWidget(range_lb = new QLabel(this));
range_slider = new QSlider(Qt::Horizontal, this);
range_slider->setToolTip(tr("Set the chart range"));
range_slider->setRange(1, settings.max_cached_minutes * 60);
range_slider->setSingleStep(1);
range_slider->setPageStep(60); // 1 min
toolbar->addWidget(range_slider);
toolbar->addWidget(zoom_range_lb = new QLabel());
reset_zoom_btn = toolbar->addAction(bootstrapPixmap("arrow-counterclockwise"), "");
reset_zoom_btn->setToolTip(tr("Reset zoom (drag on chart to zoom X-Axis)"));
remove_all_btn = toolbar->addAction(bootstrapPixmap("x"), "");
@ -48,8 +56,7 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
QWidget *charts_container = new QWidget(this);
QVBoxLayout *charts_main_layout = new QVBoxLayout(charts_container);
charts_main_layout->setContentsMargins(0, 0, 0, 0);
charts_layout = new QGridLayout(charts_container);
charts_main_layout->addLayout(charts_layout);
charts_main_layout->addLayout(charts_layout = new QGridLayout);
charts_main_layout->addStretch(0);
QScrollArea *charts_scroll = new QScrollArea(this);
@ -58,20 +65,24 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
charts_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
main_layout->addWidget(charts_scroll);
max_chart_range = settings.max_chart_x_range;
use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() >
QApplication::style()->standardPalette().color(QPalette::Background).value();
updateToolBar();
align_charts_timer = new QTimer(this);
align_charts_timer->setSingleShot(true);
align_charts_timer->callOnTimeout(this, &ChartsWidget::alignCharts);
column_count = settings.chart_column_count;
// init settings
use_dark_theme = QApplication::style()->standardPalette().color(QPalette::WindowText).value() >
QApplication::style()->standardPalette().color(QPalette::Background).value();
column_count = std::clamp(settings.chart_column_count, 1, columns_cb->count());
max_chart_range = std::clamp(settings.chart_range, 1, settings.max_cached_minutes * 60);
display_range = {0, max_chart_range};
columns_cb->setCurrentIndex(column_count);
range_slider->setValue(max_chart_range);
updateToolBar();
QObject::connect(dbc(), &DBCManager::DBCFileChanged, this, &ChartsWidget::removeAll);
QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::updateState);
QObject::connect(can, &AbstractStream::eventsMerged, this, &ChartsWidget::eventsMerged);
QObject::connect(can, &AbstractStream::updated, this, &ChartsWidget::updateState);
QObject::connect(show_all_values_btn, &QAction::triggered, this, &ChartsWidget::showAllData);
QObject::connect(range_slider, &QSlider::valueChanged, this, &ChartsWidget::setMaxChartRange);
QObject::connect(remove_all_btn, &QAction::triggered, this, &ChartsWidget::removeAll);
QObject::connect(reset_zoom_btn, &QAction::triggered, this, &ChartsWidget::zoomReset);
QObject::connect(columns_cb, SIGNAL(activated(int)), SLOT(setColumnCount(int)));
@ -83,34 +94,16 @@ ChartsWidget::ChartsWidget(QWidget *parent) : QWidget(parent) {
});
}
void ChartsWidget::updateDisplayRange() {
auto events = can->events();
double min_event_sec = (events->front()->mono_time / (double)1e9) - can->routeStartTime();
double max_event_sec = (events->back()->mono_time / (double)1e9) - can->routeStartTime();
const double cur_sec = can->currentSec();
if (!can->liveStreaming()) {
auto prev_range = display_range;
if (cur_sec < display_range.first || cur_sec >= (display_range.second - 5)) {
// reached the end, or seeked to a timestamp out of range.
display_range.first = cur_sec - 5;
}
display_range.first = std::floor(std::max(min_event_sec, display_range.first));
display_range.second = std::floor(std::min(display_range.first + max_chart_range, max_event_sec));
if (prev_range != display_range) {
QFutureSynchronizer<void> future_synchronizer;
for (auto c : charts) {
future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::setEventsRange, display_range));
}
}
} else {
if (cur_sec >= (display_range.second - 5)) {
display_range.first = std::floor(std::max(min_event_sec, cur_sec - settings.max_chart_x_range / 2.0));
}
display_range.second = std::floor(display_range.first + settings.max_chart_x_range);
void ChartsWidget::eventsMerged() {
{
assert(!can->liveStreaming());
QFutureSynchronizer<void> future_synchronizer;
const auto events = can->events();
for (auto c : charts) {
c->updateSeries(nullptr, events, false);
future_synchronizer.addFuture(QtConcurrent::run(c, &ChartView::updateSeries, nullptr, events, true));
}
}
updateState();
}
void ChartsWidget::zoomIn(double min, double max) {
@ -128,49 +121,61 @@ void ChartsWidget::zoomReset() {
void ChartsWidget::updateState() {
if (charts.isEmpty()) return;
if (can->liveStreaming()) {
// appends incoming events to the end of series
const auto events = can->events();
for (auto c : charts) {
c->updateSeries(nullptr, events, false);
}
}
const double cur_sec = can->currentSec();
if (!is_zoomed) {
updateDisplayRange();
} else if (can->currentSec() < zoomed_range.first || can->currentSec() >= zoomed_range.second) {
double pos = (cur_sec - display_range.first) / max_chart_range;
if (pos > 0.8) {
const double min_event_sec = (can->events()->front()->mono_time / (double)1e9) - can->routeStartTime();
display_range.first = std::floor(std::max(min_event_sec, cur_sec - max_chart_range * 0.2));
}
display_range.second = std::floor(display_range.first + max_chart_range);
} else if (cur_sec < zoomed_range.first || cur_sec >= zoomed_range.second) {
// loop in zoommed range
can->seekTo(zoomed_range.first);
}
const auto &range = is_zoomed ? zoomed_range : display_range;
setUpdatesEnabled(false);
const auto &range = is_zoomed ? zoomed_range : display_range;
for (auto c : charts) {
c->setDisplayRange(range.first, range.second);
c->scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
c->updatePlot(cur_sec, range.first, range.second);
}
setUpdatesEnabled(true);
}
void ChartsWidget::showAllData() {
bool switch_to_show_all = max_chart_range == settings.max_chart_x_range;
max_chart_range = switch_to_show_all ? settings.cached_segment_limit * 60
: settings.max_chart_x_range;
max_chart_range = std::min(max_chart_range, (uint32_t)can->totalSeconds());
void ChartsWidget::setMaxChartRange(int value) {
max_chart_range = settings.chart_range = value;
double current_sec = can->currentSec();
const double min_event_sec = (can->events()->front()->mono_time / (double)1e9) - can->routeStartTime();
// keep current_sec's pos
double pos = (current_sec - display_range.first) / (display_range.second - display_range.first);
display_range.first = std::floor(std::max(min_event_sec, current_sec - max_chart_range * (1.0 - pos)));
display_range.second = std::floor(display_range.first + max_chart_range);
updateToolBar();
updateState();
}
void ChartsWidget::updateToolBar() {
int min_range = std::min(settings.max_chart_x_range, (int)can->totalSeconds());
bool displaying_all = max_chart_range != min_range;
show_all_values_btn->setText(tr("%1 minutes").arg(max_chart_range / 60));
show_all_values_btn->setToolTip(tr("Click to display %1 data").arg(displaying_all ? tr("%1 minutes").arg(min_range / 60) : tr("ALL cached")));
show_all_values_btn->setVisible(!is_zoomed && !can->liveStreaming());
remove_all_btn->setEnabled(!charts.isEmpty());
reset_zoom_btn->setEnabled(is_zoomed);
range_label->setText(is_zoomed ? tr("%1 - %2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : "");
title_label->setText(charts.size() > 0 ? tr("Charts (%1)").arg(charts.size()) : tr("Charts"));
range_lb->setText(QString(" %1:%2 ").arg(max_chart_range / 60, 2, 10, QLatin1Char('0')).arg(max_chart_range % 60, 2, 10, QLatin1Char('0')));
zoom_range_lb->setText(is_zoomed ? tr("Zooming: %1 - %2").arg(zoomed_range.first, 0, 'f', 2).arg(zoomed_range.second, 0, 'f', 2) : "");
title_label->setText(tr("Charts: %1").arg(charts.size()));
dock_btn->setIcon(bootstrapPixmap(docking ? "arrow-up-right" : "arrow-down-left"));
dock_btn->setToolTip(docking ? tr("Undock charts") : tr("Dock charts"));
remove_all_btn->setEnabled(!charts.isEmpty());
reset_zoom_btn->setEnabled(is_zoomed);
}
void ChartsWidget::settingChanged() {
range_slider->setRange(1, settings.max_cached_minutes * 60);
for (auto c : charts) {
c->setFixedHeight(settings.chart_height);
columns_cb->setCurrentIndex(std::clamp(settings.chart_column_count - 1, 0, columns_cb->count() - 1));
setColumnCount(settings.chart_column_count);
}
}
@ -189,11 +194,8 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo
chart = new ChartView(this);
chart->setFixedHeight(settings.chart_height);
chart->setMinimumWidth(CHART_MIN_WIDTH);
chart->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
chart->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
chart->chart()->setTheme(use_dark_theme ? QChart::QChart::ChartThemeDark : QChart::ChartThemeLight);
chart->setEventsRange(display_range);
auto range = is_zoomed ? zoomed_range : display_range;
chart->setDisplayRange(range.first, range.second);
QObject::connect(chart, &ChartView::remove, [=]() { removeChart(chart); });
QObject::connect(chart, &ChartView::zoomIn, this, &ChartsWidget::zoomIn);
QObject::connect(chart, &ChartView::zoomReset, this, &ChartsWidget::zoomReset);
@ -204,6 +206,7 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo
updateLayout();
}
chart->addSeries(id, sig);
updateState();
} else if (!show && chart) {
chart->removeSeries(id, sig);
}
@ -214,7 +217,7 @@ void ChartsWidget::showChart(const QString &id, const Signal *sig, bool show, bo
void ChartsWidget::setColumnCount(int n) {
n = std::clamp(n + 1, 1, columns_cb->count());
if (column_count != n) {
column_count = n;
column_count = settings.chart_column_count = n;
updateLayout();
}
}
@ -239,6 +242,7 @@ void ChartsWidget::removeChart(ChartView *chart) {
chart->deleteLater();
updateToolBar();
alignCharts();
updateLayout();
emit seriesChanged();
}
@ -440,36 +444,28 @@ void ChartView::updateTitle() {
}
}
void ChartView::setEventsRange(const std::pair<double, double> &range) {
if (range != events_range) {
events_range = range;
updateSeries();
}
}
void ChartView::setDisplayRange(double min, double max) {
void ChartView::updatePlot(double cur, double min, double max) {
cur_sec = cur;
if (min != axis_x->min() || max != axis_x->max()) {
axis_x->setRange(min, max);
updateAxisY();
}
scene()->invalidate({}, QGraphicsScene::ForegroundLayer);
}
void ChartView::updateSeries(const Signal *sig, const std::vector<Event*> *events, bool clear) {
if (!events) events = can->events();
if (!events || sigs.isEmpty()) return;
void ChartView::updateSeries(const Signal *sig, const std::vector<Event *> *events, bool clear) {
events = events ? events : can->events();
for (auto &s : sigs) {
if (!sig || s.sig == sig) {
if (clear) {
s.vals.clear();
s.vals.reserve(settings.max_cached_minutes * 60 * 100); // [n]seconds * 100hz
s.last_value_mono_time = 0;
}
double route_start_time = can->routeStartTime();
uint64_t begin_ts = can->liveStreaming() ? s.last_value_mono_time : (route_start_time + events_range.first) * 1e9;
Event begin_event(cereal::Event::Which::INIT_DATA, begin_ts);
Event begin_event(cereal::Event::Which::INIT_DATA, s.last_value_mono_time);
auto begin = std::upper_bound(events->begin(), events->end(), &begin_event, Event::lessThan());
uint64_t end_ns = can->liveStreaming() ? events->back()->mono_time : (route_start_time + events_range.second) * 1e9;
for (auto it = begin; it != events->end() && (*it)->mono_time <= end_ns; ++it) {
for (auto it = begin; it != events->end(); ++it) {
if ((*it)->which == cereal::Event::Which::CAN) {
for (const auto &c : (*it)->event.getCan()) {
if (s.source == c.getSrc() && s.address == c.getAddress()) {
@ -481,13 +477,8 @@ void ChartView::updateSeries(const Signal *sig, const std::vector<Event*> *event
}
}
}
if (!s.vals.isEmpty()) {
auto [min_v, max_v] = std::minmax_element(s.vals.begin(), s.vals.end(), [](auto &l, auto &r) { return l.y() < r.y(); });
s.min_y = min_v->y();
s.max_y = max_v->y();
if (events->size()) {
s.last_value_mono_time = events->back()->mono_time;
} else {
s.min_y = s.max_y = 0;
}
s.series->replace(s.vals);
updateAxisY();
@ -501,18 +492,11 @@ void ChartView::updateAxisY() {
double min_y = std::numeric_limits<double>::max();
double max_y = std::numeric_limits<double>::lowest();
if (can->liveStreaming() || events_range == std::pair{axis_x->min(), axis_x->max()}) {
for (auto &s : sigs) {
if (s.min_y < min_y) min_y = s.min_y;
if (s.max_y > max_y) max_y = s.max_y;
}
} else {
for (auto &s : sigs) {
auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; });
for (auto it = begin; it != s.vals.end() && it->x() <= axis_x->max(); ++it) {
if (it->y() < min_y) min_y = it->y();
if (it->y() > max_y) max_y = it->y();
}
for (auto &s : sigs) {
auto begin = std::lower_bound(s.vals.begin(), s.vals.end(), axis_x->min(), [](auto &p, double x) { return p.x() < x; });
for (auto it = begin; it != s.vals.end() && it->x() <= axis_x->max(); ++it) {
if (it->y() < min_y) min_y = it->y();
if (it->y() > max_y) max_y = it->y();
}
}
@ -664,7 +648,8 @@ void ChartView::dropEvent(QDropEvent *event) {
}
void ChartView::drawForeground(QPainter *painter, const QRectF &rect) {
qreal x = chart()->mapToPosition(QPointF{can->currentSec(), 0}).x();
qreal x = chart()->mapToPosition(QPointF{cur_sec, 0}).x();
x = std::clamp(x, chart()->plotArea().left(), chart()->plotArea().right());
qreal y1 = chart()->plotArea().top() - 2;
qreal y2 = chart()->plotArea().bottom() + 2;
painter->setPen(QPen(chart()->titleBrush().color(), 2));

@ -1,12 +1,12 @@
#pragma once
#include <QComboBox>
#include <QDialogButtonBox>
#include <QDragEnterEvent>
#include <QGridLayout>
#include <QLabel>
#include <QListWidget>
#include <QGraphicsProxyWidget>
#include <QSlider>
#include <QTimer>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
@ -26,8 +26,7 @@ public:
void removeSeries(const QString &msg_id, const Signal *sig);
bool hasSeries(const QString &msg_id, const Signal *sig) const;
void updateSeries(const Signal *sig = nullptr, const std::vector<Event*> *events = nullptr, bool clear = true);
void setEventsRange(const std::pair<double, double> &range);
void setDisplayRange(double min, double max);
void updatePlot(double cur, double min, double max);
void setPlotAreaLeftPosition(int pos);
qreal getYAsixLabelWidth() const;
@ -37,8 +36,6 @@ public:
uint32_t address = 0;
const Signal *sig = nullptr;
QLineSeries *series = nullptr;
double min_y = 0;
double max_y = 0;
QVector<QPointF> vals;
uint64_t last_value_mono_time = 0;
};
@ -79,8 +76,8 @@ private:
QPointF track_pt;
QGraphicsProxyWidget *close_btn_proxy;
QGraphicsProxyWidget *manage_btn_proxy;
std::pair<double, double> events_range = {0, 0};
QList<SigItem> sigs;
double cur_sec = 0;
const QString mime_type = "application/x-cabanachartview";
};
@ -106,21 +103,21 @@ private:
void removeChart(ChartView *chart);
void eventsMerged();
void updateState();
void updateDisplayRange();
void zoomIn(double min, double max);
void zoomReset();
void updateToolBar();
void removeAll();
void showAllData();
void setMaxChartRange(int value);
void updateLayout();
void settingChanged();
bool eventFilter(QObject *obj, QEvent *event) override;
ChartView *findChart(const QString &id, const Signal *sig);
QLabel *title_label;
QLabel *range_label;
QLabel *zoom_range_lb;
QLabel *range_lb;
QSlider *range_slider;
bool docking = true;
QAction *show_all_values_btn;
QAction *dock_btn;
QAction *reset_zoom_btn;
QAction *remove_all_btn;

@ -15,9 +15,9 @@ Settings::Settings() {
void Settings::save() {
QSettings s("settings", QSettings::IniFormat);
s.setValue("fps", fps);
s.setValue("cached_segment", cached_segment_limit);
s.setValue("max_cached_minutes", max_cached_minutes);
s.setValue("chart_height", chart_height);
s.setValue("max_chart_x_range", max_chart_x_range);
s.setValue("chart_range", chart_range);
s.setValue("chart_column_count", chart_column_count);
s.setValue("last_dir", last_dir);
s.setValue("window_state", window_state);
@ -28,9 +28,9 @@ void Settings::save() {
void Settings::load() {
QSettings s("settings", QSettings::IniFormat);
fps = s.value("fps", 10).toInt();
cached_segment_limit = s.value("cached_segment", 5).toInt();
max_cached_minutes = s.value("cached_minutes", 5).toInt();
chart_height = s.value("chart_height", 200).toInt();
max_chart_x_range = s.value("max_chart_x_range", 3 * 60).toInt();
chart_range = s.value("chart_range", 3 * 60).toInt();
chart_column_count = s.value("chart_column_count", 1).toInt();
last_dir = s.value("last_dir", QDir::homePath()).toString();
window_state = s.value("window_state").toByteArray();
@ -50,23 +50,17 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) {
fps->setValue(settings.fps);
form_layout->addRow("FPS", fps);
cached_segment = new QSpinBox(this);
cached_segment->setRange(5, 60);
cached_segment->setSingleStep(1);
cached_segment->setValue(settings.cached_segment_limit);
form_layout->addRow(tr("Cached segments limit"), cached_segment);
max_chart_x_range = new QSpinBox(this);
max_chart_x_range->setRange(3, 60);
max_chart_x_range->setSingleStep(1);
max_chart_x_range->setValue(settings.max_chart_x_range / 60);
form_layout->addRow(tr("Chart range (minutes)"), max_chart_x_range);
cached_minutes = new QSpinBox(this);
cached_minutes->setRange(5, 60);
cached_minutes->setSingleStep(1);
cached_minutes->setValue(settings.max_cached_minutes);
form_layout->addRow(tr("Max Cached Minutes"), cached_minutes);
chart_height = new QSpinBox(this);
chart_height->setRange(100, 500);
chart_height->setSingleStep(10);
chart_height->setValue(settings.chart_height);
form_layout->addRow(tr("Chart height"), chart_height);
form_layout->addRow(tr("Chart Height"), chart_height);
auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
form_layout->addRow(buttonBox);
@ -78,9 +72,8 @@ SettingsDlg::SettingsDlg(QWidget *parent) : QDialog(parent) {
void SettingsDlg::save() {
settings.fps = fps->value();
settings.cached_segment_limit = cached_segment->value();
settings.max_cached_minutes = cached_minutes->value();
settings.chart_height = chart_height->value();
settings.max_chart_x_range = max_chart_x_range->value() * 60;
settings.save();
accept();
emit settings.changed();

@ -14,10 +14,10 @@ public:
void load();
int fps = 10;
int cached_segment_limit = 5;
int max_cached_minutes = 5;
int chart_height = 200;
int chart_column_count = 1;
int max_chart_x_range = 3 * 60; // 3 minutes
int chart_range = 3 * 60; // e minutes
QString last_dir;
QByteArray geometry;
QByteArray video_splitter_state;
@ -34,9 +34,8 @@ public:
SettingsDlg(QWidget *parent);
void save();
QSpinBox *fps;
QSpinBox *cached_segment;
QSpinBox *cached_minutes;
QSpinBox *chart_height;
QSpinBox *max_chart_x_range;
};
extern Settings settings;

@ -17,7 +17,7 @@ public:
protected:
void streamThread();
void updateCachedNS() { cache_ns = (settings.cached_segment_limit * 60) * 1e9; }
void updateCachedNS() { cache_ns = (settings.max_cached_minutes * 60) * 1e9; }
mutable std::mutex lock;
mutable std::vector<Event *> events_vector;

@ -4,7 +4,7 @@
ReplayStream::ReplayStream(QObject *parent) : AbstractStream(parent, false) {
QObject::connect(&settings, &Settings::changed, [this]() {
if (replay) replay->setSegmentCacheLimit(settings.cached_segment_limit);
if (replay) replay->setSegmentCacheLimit(settings.max_cached_minutes);
});
}
@ -18,7 +18,7 @@ static bool event_filter(const Event *e, void *opaque) {
bool ReplayStream::loadRoute(const QString &route, const QString &data_dir, uint32_t replay_flags) {
replay = new Replay(route, {"can", "roadEncodeIdx", "wideRoadEncodeIdx", "carParams"}, {}, nullptr, replay_flags, data_dir, this);
replay->setSegmentCacheLimit(settings.cached_segment_limit);
replay->setSegmentCacheLimit(settings.max_cached_minutes);
replay->installEventFilter(event_filter, this);
QObject::connect(replay, &Replay::seekedTo, this, &AbstractStream::seekedTo);
QObject::connect(replay, &Replay::segmentsMerged, this, &AbstractStream::eventsMerged);

Loading…
Cancel
Save