From 3e54af9db8d44ec8a36c6de624a42f8a4ca4953b Mon Sep 17 00:00:00 2001 From: Trey Moen <50057480+greatgitsby@users.noreply.github.com> Date: Mon, 7 Sep 2026 14:47:20 -0700 Subject: [PATCH] cabana: playback controls for live streaming (#38802) * Hide Cabana playback controls during live streaming * cabana: retain pause and go-live controls for live streams --- openpilot/tools/cabana/ui/mainwin.cc | 8 +++--- .../tools/cabana/ui/widgets/videowidget.cc | 25 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/openpilot/tools/cabana/ui/mainwin.cc b/openpilot/tools/cabana/ui/mainwin.cc index d720e87c83..eef3a9b54a 100644 --- a/openpilot/tools/cabana/ui/mainwin.cc +++ b/openpilot/tools/cabana/ui/mainwin.cc @@ -903,8 +903,8 @@ void MainWindow::drawVideoPanel() { video_h = avail.y - splitter_h - charts_min_h; } } - // The splitter provides the gap; extra ItemSpacing would leave an undraggable strip. - if (!charts_floating_) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); + // Replay uses a splitter for the gap; live streams use normal item spacing. + if (!charts_floating_ && !live) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(ImGui::GetStyle().ItemSpacing.x, 0.0f)); if (video_h > 0.0f) { ImGui::BeginChild("video", ImVec2(0, video_h), ImGuiChildFlags_Borders); help_overlay_.add(video_widget_->whatsThis(), ImGui::GetCurrentWindow()->Rect()); @@ -913,7 +913,7 @@ void MainWindow::drawVideoPanel() { } else { video_widget_->setVisible(false); // the splitter collapsed the video: stop the vipc thread } - if (!charts_floating_) { + if (!charts_floating_ && !live) { ImGui::InvisibleButton("##splitter", ImVec2(-1.0f, splitter_h)); const bool splitter_hovered = ImGui::IsItemHovered() && !live, splitter_active = ImGui::IsItemActive() && !live; if (splitter_active) { @@ -927,6 +927,8 @@ void MainWindow::drawVideoPanel() { ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(splitter.Min.x, line_y), ImVec2(splitter.Max.x, line_y + 2.0f), ImGui::GetColorU32(splitter_active ? ImGuiCol_SeparatorActive : splitter_hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Border)); ImGui::PopStyleVar(); + } + if (!charts_floating_) { if (!charts_collapsed) { // the chart list scrolls in its own child, the container itself never scrolls ImGui::BeginChild("charts", ImVec2(0, 0), ImGuiChildFlags_Borders, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); diff --git a/openpilot/tools/cabana/ui/widgets/videowidget.cc b/openpilot/tools/cabana/ui/widgets/videowidget.cc index ce8fb64c04..9d80c1a2bd 100644 --- a/openpilot/tools/cabana/ui/widgets/videowidget.cc +++ b/openpilot/tools/cabana/ui/widgets/videowidget.cc @@ -141,7 +141,8 @@ std::string VideoWidget::whatsThis() const { static float toolbarHeight() { return TOOLBAR_MARGIN_Y + ImGui::GetFrameHeight(); } void VideoWidget::drawPlaybackController() { - ImGui::SetCursorPosY(ImGui::GetCursorPosY() + TOOLBAR_MARGIN_Y); + if (!can->liveStreaming()) + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + TOOLBAR_MARGIN_Y); const float speed_width = menuButtonWidth("0.05x", true); const char *play_icon = can->isPaused() ? icon::PLAY : icon::PAUSE; @@ -151,13 +152,15 @@ void VideoWidget::drawPlaybackController() { : formatTime(can->currentSec(), true); const char *time_tooltip = settings.absolute_time ? "Elapsed time" : "Absolute time"; - std::vector items = { - toolbarAction("rewind", icon::REWIND, "Seek backward", []() { can->seekTo(can->currentSec() - 1); }), - toolbarAction("play", play_icon, play_tooltip, []() { can->pause(!can->isPaused()); }, true, true), - toolbarAction("fast-forward", icon::FAST_FORWARD, "Seek forward", []() { can->seekTo(can->currentSec() + 1); }, true, true), - }; + std::vector items; + if (!can->liveStreaming()) { + items.push_back(toolbarAction("rewind", icon::REWIND, "Seek backward", []() { can->seekTo(can->currentSec() - 1); })); + } + items.push_back(toolbarAction("play", play_icon, play_tooltip, []() { can->pause(!can->isPaused()); }, true, true)); if (can->liveStreaming()) { - items.push_back(toolbarAction("skip-end", icon::SKIP_END, "Skip to the end", [this]() { skipToEnd(); }, skip_to_end_enabled_, true)); + items.push_back(toolbarAction("skip-end", icon::SKIP_END, "Go live", [this]() { skipToEnd(); }, skip_to_end_enabled_, true)); + } else { + items.push_back(toolbarAction("fast-forward", icon::FAST_FORWARD, "Seek forward", []() { can->seekTo(can->currentSec() + 1); }, true, true)); } if (slider_ || msgs_received_) { // a mono font: with proportional digits the time changed width as it ticked and the items after it moved @@ -190,13 +193,11 @@ void VideoWidget::drawPlaybackController() { return item; }; const char *aspect_ratio_icon = settings.crop_video ? icon::ASPECT_RATIO_FILL : icon::ASPECT_RATIO; - items.push_back(toolbarAction("crop_video", aspect_ratio_icon, "Crop to fill", [this]() { cropVideoClicked(); })); if (!can->liveStreaming()) { + items.push_back(toolbarAction("crop_video", aspect_ratio_icon, "Crop to fill", [this]() { cropVideoClicked(); })); items.push_back(separator()); items.push_back(toolbarAction("loop", loop_icon, "Loop playback", [this]() { loopPlaybackClicked(); }, true, true)); - } - items.push_back(toolbarMenu("speed_btn", speed_text_, "Speed", [this]() { drawSpeedMenuItems(); }, true, true, speed_width)); - if (!can->liveStreaming()) { + items.push_back(toolbarMenu("speed_btn", speed_text_, "Speed", [this]() { drawSpeedMenuItems(); }, true, true, speed_width)); items.push_back(separator()); items.push_back(toolbarAction("route_info", icon::INFO_CIRCLE, "View route details", [this]() { showRouteInfo(); }, true, true)); } @@ -356,7 +357,7 @@ float VideoWidget::sizeHintHeight() const { // Keep the pane's default proportions stable as frames arrive or cameras change. float VideoWidget::defaultHeight(float width) const { - if (!cam_widget_) return toolbarHeight(); // live streams have no camera or slider + if (!cam_widget_) return ImGui::GetFrameHeight(); // live streams have no camera or slider const float cam_height = std::max((float)MIN_VIDEO_HEIGHT, width / DEFAULT_CAMERA_ASPECT_RATIO); const float tab_height = camera_tab_->count() >= 2 ? ImGui::GetFrameHeight() : 0.0f; return cam_height + tab_height + SLIDER_HEIGHT + toolbarHeight();