|
|
@ -1,8 +1,6 @@ |
|
|
|
#include "tools/cabana/videowidget.h" |
|
|
|
#include "tools/cabana/videowidget.h" |
|
|
|
|
|
|
|
|
|
|
|
#include <algorithm> |
|
|
|
#include <algorithm> |
|
|
|
#include <memory> |
|
|
|
|
|
|
|
#include <string> |
|
|
|
|
|
|
|
#include <utility> |
|
|
|
#include <utility> |
|
|
|
|
|
|
|
|
|
|
|
#include <QButtonGroup> |
|
|
|
#include <QButtonGroup> |
|
|
@ -11,7 +9,6 @@ |
|
|
|
#include <QPainter> |
|
|
|
#include <QPainter> |
|
|
|
#include <QStackedLayout> |
|
|
|
#include <QStackedLayout> |
|
|
|
#include <QStyleOptionSlider> |
|
|
|
#include <QStyleOptionSlider> |
|
|
|
#include <QTimer> |
|
|
|
|
|
|
|
#include <QVBoxLayout> |
|
|
|
#include <QVBoxLayout> |
|
|
|
#include <QtConcurrent> |
|
|
|
#include <QtConcurrent> |
|
|
|
|
|
|
|
|
|
|
@ -125,6 +122,7 @@ QWidget *VideoWidget::createCameraWidget() { |
|
|
|
QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(utils::formatSeconds(slider->currentSecond())); }); |
|
|
|
QObject::connect(slider, &QSlider::valueChanged, [=](int value) { time_label->setText(utils::formatSeconds(slider->currentSecond())); }); |
|
|
|
QObject::connect(slider, &Slider::updateMaximumTime, this, &VideoWidget::setMaximumTime, Qt::QueuedConnection); |
|
|
|
QObject::connect(slider, &Slider::updateMaximumTime, this, &VideoWidget::setMaximumTime, Qt::QueuedConnection); |
|
|
|
QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); |
|
|
|
QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); |
|
|
|
|
|
|
|
QObject::connect(static_cast<ReplayStream*>(can), &ReplayStream::qLogLoaded, slider, &Slider::parseQLog); |
|
|
|
QObject::connect(can, &AbstractStream::updated, this, &VideoWidget::updateState); |
|
|
|
QObject::connect(can, &AbstractStream::updated, this, &VideoWidget::updateState); |
|
|
|
return w; |
|
|
|
return w; |
|
|
|
} |
|
|
|
} |
|
|
@ -165,29 +163,9 @@ void VideoWidget::updatePlayBtnState() { |
|
|
|
|
|
|
|
|
|
|
|
Slider::Slider(QWidget *parent) : thumbnail_label(parent), QSlider(Qt::Horizontal, parent) { |
|
|
|
Slider::Slider(QWidget *parent) : thumbnail_label(parent), QSlider(Qt::Horizontal, parent) { |
|
|
|
setMouseTracking(true); |
|
|
|
setMouseTracking(true); |
|
|
|
auto timer = new QTimer(this); |
|
|
|
|
|
|
|
timer->callOnTimeout([this]() { |
|
|
|
|
|
|
|
timeline = can->getTimeline(); |
|
|
|
|
|
|
|
std::sort(timeline.begin(), timeline.end(), [](auto &l, auto &r) { return std::get<2>(l) < std::get<2>(r); }); |
|
|
|
|
|
|
|
update(); |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
timer->start(2000); |
|
|
|
|
|
|
|
QObject::connect(can, &AbstractStream::eventsMerged, [this]() { |
|
|
|
|
|
|
|
if (!qlog_future) { |
|
|
|
|
|
|
|
qlog_future = std::make_unique<QFuture<void>>(QtConcurrent::run(this, &Slider::parseQLog)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
QObject::connect(qApp, &QApplication::aboutToQuit, [this]() { |
|
|
|
|
|
|
|
abort_parse_qlog = true; |
|
|
|
|
|
|
|
if (qlog_future && qlog_future->isRunning()) { |
|
|
|
|
|
|
|
qDebug() << "stopping thumbnail thread"; |
|
|
|
|
|
|
|
qlog_future->waitForFinished(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
AlertInfo Slider::alertInfo(double seconds) { |
|
|
|
AlertInfo Slider::alertInfo(double seconds) { |
|
|
|
std::lock_guard lk(thumbnail_lock); |
|
|
|
|
|
|
|
uint64_t mono_time = (seconds + can->routeStartTime()) * 1e9; |
|
|
|
uint64_t mono_time = (seconds + can->routeStartTime()) * 1e9; |
|
|
|
auto alert_it = alerts.lower_bound(mono_time); |
|
|
|
auto alert_it = alerts.lower_bound(mono_time); |
|
|
|
bool has_alert = (alert_it != alerts.end()) && ((alert_it->first - mono_time) <= 1e8); |
|
|
|
bool has_alert = (alert_it != alerts.end()) && ((alert_it->first - mono_time) <= 1e8); |
|
|
@ -195,7 +173,6 @@ AlertInfo Slider::alertInfo(double seconds) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
QPixmap Slider::thumbnail(double seconds) { |
|
|
|
QPixmap Slider::thumbnail(double seconds) { |
|
|
|
std::lock_guard lk(thumbnail_lock); |
|
|
|
|
|
|
|
uint64_t mono_time = (seconds + can->routeStartTime()) * 1e9; |
|
|
|
uint64_t mono_time = (seconds + can->routeStartTime()) * 1e9; |
|
|
|
auto it = thumbnails.lowerBound(mono_time); |
|
|
|
auto it = thumbnails.lowerBound(mono_time); |
|
|
|
return it != thumbnails.end() ? it.value() : QPixmap(); |
|
|
|
return it != thumbnails.end() ? it.value() : QPixmap(); |
|
|
@ -206,36 +183,32 @@ void Slider::setTimeRange(double min, double max) { |
|
|
|
setRange(min * factor, max * factor); |
|
|
|
setRange(min * factor, max * factor); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Slider::parseQLog() { |
|
|
|
void Slider::parseQLog(int segnum, std::shared_ptr<LogReader> qlog) { |
|
|
|
const auto &segments = can->route()->segments(); |
|
|
|
const auto &segments = qobject_cast<ReplayStream *>(can)->route()->segments(); |
|
|
|
for (auto it = segments.rbegin(); it != segments.rend() && !abort_parse_qlog; ++it) { |
|
|
|
if (segments.size() > 0 && segnum == segments.rbegin()->first && !qlog->events.empty()) { |
|
|
|
LogReader log; |
|
|
|
emit updateMaximumTime(qlog->events.back()->mono_time / 1e9 - can->routeStartTime()); |
|
|
|
std::string qlog = it->second.qlog.toStdString(); |
|
|
|
} |
|
|
|
if (!qlog.empty() && log.load(qlog, &abort_parse_qlog, {cereal::Event::Which::THUMBNAIL, cereal::Event::Which::CONTROLS_STATE}, true, 0, 3)) { |
|
|
|
|
|
|
|
if (it == segments.rbegin() && !log.events.empty()) { |
|
|
|
std::mutex mutex; |
|
|
|
double max_time = log.events.back()->mono_time / 1e9 - can->routeStartTime(); |
|
|
|
QtConcurrent::blockingMap(qlog->events.cbegin(), qlog->events.cend(), [&mutex, this](const Event *e) { |
|
|
|
emit updateMaximumTime(max_time); |
|
|
|
if (e->which == cereal::Event::Which::THUMBNAIL) { |
|
|
|
|
|
|
|
auto thumb = e->event.getThumbnail(); |
|
|
|
|
|
|
|
auto data = thumb.getThumbnail(); |
|
|
|
|
|
|
|
if (QPixmap pm; pm.loadFromData(data.begin(), data.size(), "jpeg")) { |
|
|
|
|
|
|
|
QPixmap scaled = pm.scaledToHeight(MIN_VIDEO_HEIGHT - THUMBNAIL_MARGIN * 2, Qt::SmoothTransformation); |
|
|
|
|
|
|
|
std::lock_guard lk(mutex); |
|
|
|
|
|
|
|
thumbnails[thumb.getTimestampEof()] = scaled; |
|
|
|
} |
|
|
|
} |
|
|
|
for (auto ev = log.events.cbegin(); ev != log.events.cend() && !abort_parse_qlog; ++ev) { |
|
|
|
} else if (e->which == cereal::Event::Which::CONTROLS_STATE) { |
|
|
|
if ((*ev)->which == cereal::Event::Which::THUMBNAIL) { |
|
|
|
auto cs = e->event.getControlsState(); |
|
|
|
auto thumb = (*ev)->event.getThumbnail(); |
|
|
|
if (cs.getAlertType().size() > 0 && cs.getAlertText1().size() > 0 && |
|
|
|
auto data = thumb.getThumbnail(); |
|
|
|
cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE) { |
|
|
|
if (QPixmap pm; pm.loadFromData(data.begin(), data.size(), "jpeg")) { |
|
|
|
std::lock_guard lk(mutex); |
|
|
|
pm = pm.scaledToHeight(MIN_VIDEO_HEIGHT - THUMBNAIL_MARGIN * 2, Qt::SmoothTransformation); |
|
|
|
alerts.emplace(e->mono_time, AlertInfo{cs.getAlertStatus(), cs.getAlertText1().cStr(), cs.getAlertText2().cStr()}); |
|
|
|
std::lock_guard lk(thumbnail_lock); |
|
|
|
|
|
|
|
thumbnails[thumb.getTimestampEof()] = pm; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else if ((*ev)->which == cereal::Event::Which::CONTROLS_STATE) { |
|
|
|
|
|
|
|
auto cs = (*ev)->event.getControlsState(); |
|
|
|
|
|
|
|
if (cs.getAlertType().size() > 0 && cs.getAlertText1().size() > 0 && |
|
|
|
|
|
|
|
cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE) { |
|
|
|
|
|
|
|
std::lock_guard lk(thumbnail_lock); |
|
|
|
|
|
|
|
alerts.emplace((*ev)->mono_time, AlertInfo{cs.getAlertStatus(), cs.getAlertText1().cStr(), cs.getAlertText2().cStr()}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
update(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Slider::paintEvent(QPaintEvent *ev) { |
|
|
|
void Slider::paintEvent(QPaintEvent *ev) { |
|
|
@ -245,7 +218,7 @@ void Slider::paintEvent(QPaintEvent *ev) { |
|
|
|
double min = minimum() / factor; |
|
|
|
double min = minimum() / factor; |
|
|
|
double max = maximum() / factor; |
|
|
|
double max = maximum() / factor; |
|
|
|
|
|
|
|
|
|
|
|
for (auto [begin, end, type] : timeline) { |
|
|
|
for (auto [begin, end, type] : qobject_cast<ReplayStream *>(can)->getTimeline()) { |
|
|
|
if (begin > max || end < min) |
|
|
|
if (begin > max || end < min) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
r.setLeft(((std::max(min, begin) - min) / (max - min)) * width()); |
|
|
|
r.setLeft(((std::max(min, begin) - min) / (max - min)) * width()); |
|
|
|