You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Software_Architecture/mediamodule/camera_streamer.cpp

105 lines
3.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "camera_streamer.h"
#include <QDebug>
#include <QHostAddress>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
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<int>(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();
}