#include "camera_streamer.h" #include #include #include #include CameraStreamer::CameraStreamer(QObject *parent) : QObject(parent) { } CameraStreamer::~CameraStreamer() { stopStreaming(); } bool CameraStreamer::startStreaming(const QString &remoteUser, const QString &remoteHost, const QString &remoteCommand, int localPort) { if (m_running.load()) return true; // already running // 启动 ssh 进程,在远端开始推流 if (!m_sshProcess) { m_sshProcess = new QProcess(this); // 使 ssh 保持会话 & 不读取stdin,-T 禁用伪终端 QStringList args; args << QString("%1@%2").arg(remoteUser, remoteHost) << "-T" << remoteCommand; m_sshProcess->start("ssh", args); if (!m_sshProcess->waitForStarted(3000)) { qWarning() << "Failed to start ssh process:" << m_sshProcess->errorString(); delete m_sshProcess; m_sshProcess = nullptr; return false; } } // 启动本地接收线程 m_running = true; m_captureThread = std::thread(&CameraStreamer::captureLoop, this, localPort); return true; } void CameraStreamer::stopStreaming() { if (!m_running.load()) return; // 停止读取线程 m_running = false; if (m_captureThread.joinable()) m_captureThread.join(); // 结束远端 ssh 进程 if (m_sshProcess) { m_sshProcess->terminate(); if (!m_sshProcess->waitForFinished(3000)) { m_sshProcess->kill(); m_sshProcess->waitForFinished(); } delete m_sshProcess; m_sshProcess = nullptr; } } void CameraStreamer::captureLoop(int localPort) { // GStreamer UDP pipeline QString pipeline = QString("udpsrc address=0.0.0.0 port=%1 ! application/x-rtp,media=video,encoding-name=H264 ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! appsink max-buffers=1 drop=true").arg(localPort); cv::VideoCapture cap(pipeline.toStdString(), cv::CAP_GSTREAMER); if (!cap.isOpened()) { qWarning() << "Failed to open capture pipeline" << pipeline; return; } cv::Mat frame; while (m_running.load()) { if (!cap.read(frame) || frame.empty()) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); continue; } cv::Mat rgb; if (frame.channels() == 3) { cv::cvtColor(frame, rgb, cv::COLOR_BGR2RGB); } else if (frame.channels() == 4) { cv::cvtColor(frame, rgb, cv::COLOR_BGRA2RGB); } else { rgb = frame; } QImage image(rgb.data, rgb.cols, rgb.rows, static_cast(rgb.step), QImage::Format_RGB888); emit newFrame(image.copy()); // copy to detach from cv::Mat memory // 控制帧率:根据需要调整 std::this_thread::sleep_for(std::chrono::milliseconds(1)); } cap.release(); }