#include "tools/cabana/cameraview.h" #include #include #include #include #include #include #include "common/yuv.h" CameraWidget::CameraWidget(std::string stream_name, VisionStreamType type, QWidget* parent) : stream_name(stream_name), active_stream_type(type), requested_stream_type(type), QWidget(parent) { setAttribute(Qt::WA_OpaquePaintEvent); qRegisterMetaType>("availableStreams"); QObject::connect(this, &CameraWidget::vipcThreadFrameReceived, this, &CameraWidget::vipcFrameReceived, Qt::QueuedConnection); QObject::connect(this, &CameraWidget::vipcAvailableStreamsUpdated, this, &CameraWidget::availableStreamsUpdated, Qt::QueuedConnection); QObject::connect(QApplication::instance(), &QCoreApplication::aboutToQuit, this, &CameraWidget::stopVipcThread); } CameraWidget::~CameraWidget() { stopVipcThread(); } void CameraWidget::showEvent(QShowEvent *event) { if (!vipc_thread.joinable()) { clearFrames(); vipc_exit = false; vipc_thread = std::thread(&CameraWidget::vipcThread, this); } } void CameraWidget::stopVipcThread() { vipc_exit = true; if (vipc_thread.joinable()) { vipc_thread.join(); } } void CameraWidget::availableStreamsUpdated(std::set streams) { available_streams = streams; } void CameraWidget::paintEvent(QPaintEvent *event) { QPainter p(this); p.fillRect(rect(), bg); std::lock_guard lk(frame_lock); if (rgb_frame.isNull()) return; // Scale for aspect ratio float widget_ratio = (float)width() / height(); float frame_ratio = (float)rgb_frame.width() / rgb_frame.height(); int w = std::lround(width() * std::min(frame_ratio / widget_ratio, 1.0f)); int h = std::lround(height() * std::min(widget_ratio / frame_ratio, 1.0f)); QRect video_rect((width() - w) / 2, (height() - h) / 2, w, h); p.setRenderHint(QPainter::SmoothPixmapTransform); if (active_stream_type == VISION_STREAM_DRIVER) { // mirror driver camera horizontally const qreal cx = video_rect.x() + video_rect.width() / 2.0; p.translate(cx, 0); p.scale(-1, 1); p.translate(-cx, 0); } p.drawImage(video_rect, rgb_frame); } void CameraWidget::vipcFrameReceived() { update(); } void CameraWidget::vipcThread() { VisionStreamType cur_stream = requested_stream_type; std::unique_ptr vipc_client; VisionIpcBufExtra frame_meta = {}; while (!vipc_exit) { if (!vipc_client || cur_stream != requested_stream_type) { clearFrames(); fprintf(stderr, "connecting to stream %d, was connected to %d\n", (int)requested_stream_type, (int)cur_stream); cur_stream = requested_stream_type; vipc_client.reset(new VisionIpcClient(stream_name, cur_stream, false)); } active_stream_type = cur_stream; if (!vipc_client->connected) { clearFrames(); auto streams = VisionIpcClient::getAvailableStreams(stream_name, false); if (streams.empty()) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); continue; } emit vipcAvailableStreamsUpdated(streams); if (!vipc_client->connect(false)) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); continue; } } if (VisionBuf *buf = vipc_client->recv(&frame_meta, 100)) { // NV12 -> RGBA once per frame on the receive thread; paint just draws the image if (rgb_back.width() != (int)buf->width || rgb_back.height() != (int)buf->height) { rgb_back = QImage(buf->width, buf->height, QImage::Format_RGBA8888); } yuv::nv12_to_rgba(buf->y, buf->stride, buf->uv, buf->stride, rgb_back.bits(), rgb_back.bytesPerLine(), buf->width, buf->height); { std::lock_guard lk(frame_lock); rgb_frame.swap(rgb_back); } emit vipcThreadFrameReceived(); } } } void CameraWidget::clearFrames() { std::lock_guard lk(frame_lock); rgb_frame = QImage(); rgb_back = QImage(); available_streams.clear(); }