|
|
|
@ -27,6 +27,13 @@ static const QColor timeline_colors[] = { |
|
|
|
|
[(int)TimelineType::AlertCritical] = QColor(199, 0, 57), |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static Replay *getReplay() { |
|
|
|
|
auto stream = qobject_cast<ReplayStream *>(can); |
|
|
|
|
if (!stream) return nullptr; |
|
|
|
|
|
|
|
|
|
return stream->getReplay(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
VideoWidget::VideoWidget(QWidget *parent) : QFrame(parent) { |
|
|
|
|
setFrameStyle(QFrame::StyledPanel | QFrame::Plain); |
|
|
|
|
auto main_layout = new QVBoxLayout(this); |
|
|
|
@ -76,7 +83,7 @@ QHBoxLayout *VideoWidget::createPlaybackController() { |
|
|
|
|
// set speed to 1.0
|
|
|
|
|
speed_btn->menu()->actions()[7]->setChecked(true); |
|
|
|
|
can->pause(false); |
|
|
|
|
can->seekTo(can->totalSeconds() + 1); |
|
|
|
|
can->seekTo(can->maxSeconds() + 1); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -141,7 +148,7 @@ QWidget *VideoWidget::createCameraWidget() { |
|
|
|
|
|
|
|
|
|
QStackedLayout *stacked = new QStackedLayout(); |
|
|
|
|
stacked->setStackingMode(QStackedLayout::StackAll); |
|
|
|
|
stacked->addWidget(cam_widget = new CameraWidget("camerad", VISION_STREAM_ROAD, false)); |
|
|
|
|
stacked->addWidget(cam_widget = new StreamCameraView("camerad", VISION_STREAM_ROAD, false)); |
|
|
|
|
cam_widget->setMinimumHeight(MIN_VIDEO_HEIGHT); |
|
|
|
|
cam_widget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); |
|
|
|
|
stacked->addWidget(alert_label = new InfoLabel(this)); |
|
|
|
@ -149,9 +156,11 @@ QWidget *VideoWidget::createCameraWidget() { |
|
|
|
|
|
|
|
|
|
l->addWidget(slider = new Slider(w)); |
|
|
|
|
slider->setSingleStep(0); |
|
|
|
|
slider->setTimeRange(can->minSeconds(), can->maxSeconds()); |
|
|
|
|
|
|
|
|
|
setMaximumTime(can->totalSeconds()); |
|
|
|
|
QObject::connect(slider, &QSlider::sliderReleased, [this]() { can->seekTo(slider->currentSecond()); }); |
|
|
|
|
QObject::connect(can, &AbstractStream::paused, cam_widget, [c = cam_widget]() { c->showPausedOverlay(); }); |
|
|
|
|
QObject::connect(can, &AbstractStream::resume, cam_widget, [c = cam_widget]() { c->update(); }); |
|
|
|
|
QObject::connect(can, &AbstractStream::eventsMerged, this, [this]() { slider->update(); }); |
|
|
|
|
QObject::connect(cam_widget, &CameraWidget::clicked, []() { can->pause(!can->isPaused()); }); |
|
|
|
|
QObject::connect(cam_widget, &CameraWidget::vipcAvailableStreamsUpdated, this, &VideoWidget::vipcAvailableStreamsUpdated); |
|
|
|
@ -161,7 +170,7 @@ QWidget *VideoWidget::createCameraWidget() { |
|
|
|
|
|
|
|
|
|
auto replay = static_cast<ReplayStream*>(can)->getReplay(); |
|
|
|
|
QObject::connect(replay, &Replay::qLogLoaded, slider, &Slider::parseQLog, Qt::QueuedConnection); |
|
|
|
|
QObject::connect(replay, &Replay::totalSecondsUpdated, this, &VideoWidget::setMaximumTime, Qt::QueuedConnection); |
|
|
|
|
QObject::connect(replay, &Replay::minMaxTimeChanged, this, &VideoWidget::timeRangeChanged, Qt::QueuedConnection); |
|
|
|
|
return w; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -185,7 +194,7 @@ void VideoWidget::vipcAvailableStreamsUpdated(std::set<VisionStreamType> streams |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void VideoWidget::loopPlaybackClicked() { |
|
|
|
|
auto replay = qobject_cast<ReplayStream *>(can)->getReplay(); |
|
|
|
|
auto replay = getReplay(); |
|
|
|
|
if (!replay) return; |
|
|
|
|
|
|
|
|
|
if (replay->hasFlag(REPLAY_FLAG_NO_LOOP)) { |
|
|
|
@ -197,18 +206,15 @@ void VideoWidget::loopPlaybackClicked() { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void VideoWidget::setMaximumTime(double sec) { |
|
|
|
|
maximum_time = sec; |
|
|
|
|
slider->setTimeRange(0, sec); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void VideoWidget::timeRangeChanged(const std::optional<std::pair<double, double>> &time_range) { |
|
|
|
|
void VideoWidget::timeRangeChanged() { |
|
|
|
|
const auto time_range = can->timeRange(); |
|
|
|
|
if (can->liveStreaming()) { |
|
|
|
|
skip_to_end_btn->setEnabled(!time_range.has_value()); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
time_range ? slider->setTimeRange(time_range->first, time_range->second) |
|
|
|
|
: slider->setTimeRange(0, maximum_time); |
|
|
|
|
: slider->setTimeRange(can->minSeconds(), can->maxSeconds()); |
|
|
|
|
updateState(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
QString VideoWidget::formatTime(double sec, bool include_milliseconds) { |
|
|
|
@ -219,8 +225,9 @@ QString VideoWidget::formatTime(double sec, bool include_milliseconds) { |
|
|
|
|
|
|
|
|
|
void VideoWidget::updateState() { |
|
|
|
|
if (slider) { |
|
|
|
|
if (!slider->isSliderDown()) |
|
|
|
|
if (!slider->isSliderDown()) { |
|
|
|
|
slider->setCurrentSecond(can->currentSec()); |
|
|
|
|
} |
|
|
|
|
alert_label->showAlert(slider->alertInfo(can->currentSec())); |
|
|
|
|
time_btn->setText(QString("%1 / %2").arg(formatTime(can->currentSec(), true), |
|
|
|
|
formatTime(slider->maximum() / slider->factor))); |
|
|
|
@ -242,15 +249,14 @@ Slider::Slider(QWidget *parent) : QSlider(Qt::Horizontal, parent) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
AlertInfo Slider::alertInfo(double seconds) { |
|
|
|
|
uint64_t mono_time = (seconds + can->routeStartTime()) * 1e9; |
|
|
|
|
uint64_t mono_time = can->toMonoTime(seconds); |
|
|
|
|
auto alert_it = alerts.lower_bound(mono_time); |
|
|
|
|
bool has_alert = (alert_it != alerts.end()) && ((alert_it->first - mono_time) <= 1e8); |
|
|
|
|
return has_alert ? alert_it->second : AlertInfo{}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
QPixmap Slider::thumbnail(double seconds) { |
|
|
|
|
uint64_t mono_time = (seconds + can->routeStartTime()) * 1e9; |
|
|
|
|
auto it = thumbnails.lowerBound(mono_time); |
|
|
|
|
auto it = thumbnails.lowerBound(can->toMonoTime(seconds)); |
|
|
|
|
return it != thumbnails.end() ? it.value() : QPixmap(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -298,16 +304,18 @@ void Slider::paintEvent(QPaintEvent *ev) { |
|
|
|
|
p.fillRect(r, color); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const auto replay = qobject_cast<ReplayStream *>(can)->getReplay(); |
|
|
|
|
for (auto [begin, end, type] : replay->getTimeline()) { |
|
|
|
|
fillRange(begin, end, timeline_colors[(int)type]); |
|
|
|
|
} |
|
|
|
|
auto replay = getReplay(); |
|
|
|
|
if (replay) { |
|
|
|
|
for (auto [begin, end, type] : replay->getTimeline()) { |
|
|
|
|
fillRange(begin, end, timeline_colors[(int)type]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
QColor empty_color = palette().color(QPalette::Window); |
|
|
|
|
empty_color.setAlpha(160); |
|
|
|
|
for (const auto &[n, seg] : replay->segments()) { |
|
|
|
|
if (!(seg && seg->isLoaded())) |
|
|
|
|
fillRange(n * 60.0, (n + 1) * 60.0, empty_color); |
|
|
|
|
QColor empty_color = palette().color(QPalette::Window); |
|
|
|
|
empty_color.setAlpha(160); |
|
|
|
|
for (const auto &[n, seg] : replay->segments()) { |
|
|
|
|
if (!(seg && seg->isLoaded())) |
|
|
|
|
fillRange(n * 60.0, (n + 1) * 60.0, empty_color); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
QStyleOptionSlider opt; |
|
|
|
@ -411,3 +419,22 @@ void InfoLabel::paintEvent(QPaintEvent *event) { |
|
|
|
|
p.drawText(text_rect, Qt::AlignTop | Qt::AlignHCenter | Qt::TextWordWrap, text); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
StreamCameraView::StreamCameraView(std::string stream_name, VisionStreamType stream_type, bool zoom, QWidget *parent) |
|
|
|
|
: CameraWidget(stream_name, stream_type, zoom, parent) { |
|
|
|
|
fade_animation = new QPropertyAnimation(this, "overlayOpacity"); |
|
|
|
|
fade_animation->setDuration(500); |
|
|
|
|
fade_animation->setStartValue(0.2f); |
|
|
|
|
fade_animation->setEndValue(0.7f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void StreamCameraView::paintGL() { |
|
|
|
|
CameraWidget::paintGL(); |
|
|
|
|
|
|
|
|
|
if (can->isPaused()) { |
|
|
|
|
QPainter p(this); |
|
|
|
|
p.setPen(QColor(200, 200, 200, static_cast<int>(255 * overlay_opacity))); |
|
|
|
|
p.setFont(QFont(font().family(), 16, QFont::Bold)); |
|
|
|
|
p.drawText(rect(), Qt::AlignCenter, tr("PAUSED")); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|