Linux和Windows基于V4L2和TCP的QT監控

最近工作需要用QT做一個網絡攝像頭測試,簡單記錄:

服務端,主機配置為Ubuntu,通過端口12345采集傳輸MJPEG格式圖片

windows客戶端,QT Creator通過ip地址連接訪問

提前準備

服務端需要安裝QT5

sudo apt-get install qt5-default

g++

qmake

客戶端需要安裝Qt Creator

服務端代碼

cameraserver.cpp

#include "cameraserver.h"CameraServer::CameraServer(QObject *parent) : QObject(parent), camera_fd(-1), buffer(nullptr), buffer_length(0)
{server = new QTcpServer(this);connect(server, &QTcpServer::newConnection, this, &CameraServer::newConnection);if (!server->listen(QHostAddress::Any, 12345)) {qDebug() << "Server could not start!";} else {qDebug() << "Server started on port 12345";}// 打開攝像頭camera_fd = open("/dev/video0", O_RDWR | O_NONBLOCK, 0);if (camera_fd == -1) {qDebug() << "Failed to open camera:" << strerror(errno);return;}// 設置攝像頭格式struct v4l2_format format;memset(&format, 0, sizeof(format));format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;format.fmt.pix.width = 640;format.fmt.pix.height = 480;format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;format.fmt.pix.field = V4L2_FIELD_NONE;if (ioctl(camera_fd, VIDIOC_S_FMT, &format) == -1) {qDebug() << "Failed to set camera format:" << strerror(errno);close(camera_fd);camera_fd = -1;return;}// 請求緩沖區struct v4l2_requestbuffers req;memset(&req, 0, sizeof(req));req.count = 1;req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;req.memory = V4L2_MEMORY_MMAP;if (ioctl(camera_fd, VIDIOC_REQBUFS, &req) == -1) {qDebug() << "Failed to request buffers:" << strerror(errno);close(camera_fd);camera_fd = -1;return;}// 映射緩沖區struct v4l2_buffer buf;memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = 0;if (ioctl(camera_fd, VIDIOC_QUERYBUF, &buf) == -1) {qDebug() << "Failed to query buffer:" << strerror(errno);close(camera_fd);camera_fd = -1;return;}buffer = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, camera_fd, buf.m.offset);if (buffer == MAP_FAILED) {qDebug() << "Failed to mmap buffer:" << strerror(errno);close(camera_fd);camera_fd = -1;return;}buffer_length = buf.length;// 將緩沖區加入隊列if (ioctl(camera_fd, VIDIOC_QBUF, &buf) == -1) {qDebug() << "Failed to queue buffer:" << strerror(errno);close(camera_fd);camera_fd = -1;return;}// 開啟視頻流enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(camera_fd, VIDIOC_STREAMON, &type) == -1) {qDebug() << "Failed to start streaming:" << strerror(errno);close(camera_fd);camera_fd = -1;return;}// 設置定時器timer = new QTimer(this);connect(timer, &QTimer::timeout, this, &CameraServer::captureFrame);timer->start(33); // ~30 FPS
}CameraServer::~CameraServer()
{if (camera_fd != -1) {// 停止視頻流enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(camera_fd, VIDIOC_STREAMOFF, &type);if (buffer != MAP_FAILED && buffer != nullptr) {munmap(buffer, buffer_length);}close(camera_fd);}
}void CameraServer::newConnection()
{QTcpSocket *socket = server->nextPendingConnection();qDebug() << "Client connected:" << socket->peerAddress().toString();clients.append(socket);connect(socket, &QTcpSocket::disconnected, this, [this, socket]() {qDebug() << "Client disconnected";clients.removeOne(socket);socket->deleteLater();});
}void CameraServer::captureFrame()
{if (camera_fd == -1 || clients.isEmpty()) return;struct v4l2_buffer buf;memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;// 出隊緩沖區if (ioctl(camera_fd, VIDIOC_DQBUF, &buf) == -1) {if (errno != EAGAIN) {qDebug() << "Failed to dequeue buffer:" << strerror(errno);}return;}// 發送幀數據for (QTcpSocket *client : clients) {if (client->state() == QAbstractSocket::ConnectedState) {// 發送幀大小quint32 size = buf.bytesused;client->write(reinterpret_cast<const char*>(&size), sizeof(size));// 發送幀數據client->write(reinterpret_cast<const char*>(buffer), size);}}// 重新入隊緩沖區if (ioctl(camera_fd, VIDIOC_QBUF, &buf) == -1) {qDebug() << "Failed to queue buffer:" << strerror(errno);}
}

cameraserver.h

#ifndef CAMERASERVER_H
#define CAMERASERVER_H#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
#include <QList>
#include <QDebug>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <cstring>
#include <cerrno>class CameraServer : public QObject
{Q_OBJECT
public:explicit CameraServer(QObject *parent = nullptr);~CameraServer();private slots:void newConnection();void captureFrame();private:QTcpServer *server;QList<QTcpSocket*> clients;int camera_fd;QTimer *timer;void *buffer;size_t buffer_length;
};#endif // CAMERASERVER_H

緩沖區增加聲明

客戶端QT代碼

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QBuffer>
#include <QMessageBox>
#include <QHostAddress> // 添加頭文件MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);setWindowTitle("Camera Stream Client");socket = new QTcpSocket(this);connect(socket, &QTcpSocket::connected, this, &MainWindow::on_socketConnected);connect(socket, &QTcpSocket::disconnected, this, &MainWindow::on_socketDisconnected);// 修復1: 使用兼容Qt 5.14的錯誤處理方式connect(socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),this, &MainWindow::on_socketError);connect(socket, &QTcpSocket::readyRead, this, &MainWindow::on_socketReadyRead);// Set placeholder imageQImage placeholder(640, 480, QImage::Format_RGB888);placeholder.fill(Qt::darkGray);ui->imageLabel->setPixmap(QPixmap::fromImage(placeholder));ui->imageLabel->setScaledContents(true);ui->ipLineEdit->setText("192.168.1.100"); // Default IP
}MainWindow::~MainWindow() {delete ui;
}void MainWindow::on_connectButton_clicked() {if (socket->state() == QAbstractSocket::ConnectedState) {socket->disconnectFromHost();ui->connectButton->setText("Connect");ui->statusLabel->setText("Disconnected");return;}QString ip = ui->ipLineEdit->text();if (ip.isEmpty()) {QMessageBox::warning(this, "Error", "Please enter server IP address");return;}ui->connectButton->setText("Connecting...");ui->connectButton->setEnabled(false);socket->connectToHost(ip, 12345);
}void MainWindow::on_socketConnected() {ui->connectButton->setText("Disconnect");ui->connectButton->setEnabled(true);// 修復2: 使用正確的QHostAddress方法ui->statusLabel->setText("Connected to " + socket->peerAddress().toString());
}void MainWindow::on_socketDisconnected() {ui->connectButton->setText("Connect");ui->statusLabel->setText("Disconnected");
}// 修改錯誤處理函數的簽名
void MainWindow::on_socketError(QAbstractSocket::SocketError error) {Q_UNUSED(error);ui->connectButton->setText("Connect");ui->connectButton->setEnabled(true);ui->statusLabel->setText("Error: " + socket->errorString());
}void MainWindow::on_socketReadyRead() {while (socket->bytesAvailable() > 0) {if (!readingFrame) {// Start reading a new frameif (socket->bytesAvailable() < static_cast<qint64>(sizeof(quint32))) {return; // Wait for more data}socket->read(reinterpret_cast<char*>(&frameSize), sizeof(quint32));readingFrame = true;}if (socket->bytesAvailable() < frameSize) {return; // Wait for the complete frame}// Read the complete frameQByteArray frameData = socket->read(frameSize);readingFrame = false;// Decode JPEG to QImagecurrentFrame = QImage::fromData(frameData, "JPEG");if (!currentFrame.isNull()) {ui->imageLabel->setPixmap(QPixmap::fromImage(currentFrame));ui->statusLabel->setText(QString("Received frame: %1x%2").arg(currentFrame.width()).arg(currentFrame.height()));}}
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QTcpSocket>
#include <QImage>
#include <QLabel>
#include <QHostAddress> // 添加頭文件QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_connectButton_clicked();void on_socketConnected();void on_socketDisconnected();void on_socketError(QAbstractSocket::SocketError error); // 保持原簽名void on_socketReadyRead();private:Ui::MainWindow *ui;QTcpSocket *socket;QImage currentFrame;bool readingFrame = false;quint32 frameSize = 0;
};
#endif // MAINWINDOW_H

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>MainWindow</class><widget class="QMainWindow" name="MainWindow"><property name="geometry"><rect><x>0</x><y>0</y><width>800</width><height>600</height></rect></property><property name="windowTitle"><string>Camera Stream Client</string></property><widget class="QWidget" name="centralwidget"><layout class="QVBoxLayout" name="verticalLayout"><item><widget class="QFrame" name="frame"><property name="frameShape"><enum>QFrame::StyledPanel</enum></property><property name="frameShadow"><enum>QFrame::Raised</enum></property><layout class="QHBoxLayout" name="horizontalLayout"><item><widget class="QLabel" name="label"><property name="text"><string>Server IP:</string></property></widget></item><item><widget class="QLineEdit" name="ipLineEdit"/></item><item><widget class="QPushButton" name="connectButton"><property name="text"><string>Connect</string></property></widget></item></layout></widget></item><item><widget class="QLabel" name="imageLabel"><property name="minimumSize"><size><width>640</width><height>480</height></size></property><property name="frameShape"><enum>QFrame::Box</enum></property><property name="text"><string>No Image</string></property><property name="alignment"><set>Qt::AlignCenter</set></property></widget></item><item><widget class="QLabel" name="statusLabel"><property name="text"><string>Disconnected</string></property></widget></item></layout></widget></widget><resources/><connections/>
</ui>

整體框架基于V4L2

演示效果

后續打算在RK3576上面跑一下,雖說arch64架構的Ubuntu,移植QT應該會遇到一些阻力,不過可以期待一下

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93121.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93121.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93121.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

yolo格式

labelimg中的格式yolo格式id 框中心點X對于總圖片的比例 框中心點Y對于總圖片的比例 框X總長度對于總圖片的比例 框Y總長度對于總圖片的比例

Day 8-zhou R包批量安裝小補充!!!

BiocManager::install(c(“S4Vectors”, “BiocGenerics”)) 以下是使用BiocManager安裝S4Vectors和BiocGenerics包的詳細步驟。這些步驟基于最新的Bioconductor和R版本&#xff08;R 4.5&#xff09;。 安裝步驟安裝BiocManager 如果你還沒有安裝BiocManager&#xff0c;可以使…

電商項目_核心業務_數據歸檔

無論采用哪種存儲系統&#xff0c;數據查詢的耗時取決于兩個因素查找的時間復雜度數據總量查找的時間復雜度又取決于查找算法數據存儲結構以Mysql存儲的訂單數據為例&#xff0c;隨著業務的發展&#xff0c;數據量越來越大&#xff0c;對一些歷史歸檔數據的查詢&#xff0c;如果…

第十講:stack、queue、priority_queue以及deque

目錄 1、stack 1.1、stack的使用 1.2、stack的OJ題 1.2.1、最小棧 1.2.2、棧的壓入彈出序列 1.2.3、逆波蘭表達式求值 1.3、stack的模擬實現 2、queue 2.1、queue的使用 2.2、queue的OJ題 2.2.1、二叉樹的層序遍歷 2.3、queue的模擬實現 3、priority_queue 3.1、…

如何思考一個動態規劃問題需要幾個狀態?

如何思考一個動態規劃問題需要幾個狀態&#xff1f;第一步&#xff1a;思考 角色第二步&#xff1a;考慮 過去的影響第三步&#xff1a;畫出狀態轉移圖第四步&#xff1a;寫出狀態轉移方程第五步&#xff1a;驗證是否能覆蓋所有路徑 邊界幾個常見題目總結&#xff1a;第一步&a…

【每天一個知識點】生成對抗聚類(Generative Adversarial Clustering, GAC)

&#x1f4d8; 生成對抗聚類&#xff08;Generative Adversarial Clustering, GAC&#xff09; 一、研究背景與動機 聚類是無監督學習中的核心任務。傳統方法如 K-means、GMM、DBSCAN 等難以適應高維、非線性、復雜結構數據。 生成對抗聚類&#xff08;GAC&#xff09; 融合…

Qt 窗口 工具欄QToolBar、狀態欄StatusBar

每日激勵&#xff1a;“不設限和自我肯定的心態&#xff1a;I can do all things。 — Stephen Curry” 緒論?&#xff1a; 一段時間沒有更新&#xff0c;這段時間一直在忙各種事情&#xff0c;后續將再次上路持續更新C相關知識 本章將繼續前面的QT篇章&#xff0c;本章主要講…

FFmpeg——參數詳解

FFmpeg參數詳解一、基本命令結構1.1、查詢參數1.1.1、version1.1.2、buildconf1.1.3、devices1.1.4、formats1.1.5、muxers1.1.6、demuxers1.1.7、codecs1.1.8、decoders1.1.9、encoders1.1.10、bsfs1.1.11、protocols1.1.12、filters1.1.13、pix_fmts1.1.14、layouts1.1.15、s…

流媒體傳輸:RTSP傳輸詳解(包含RTP,RTCP,RTSP詳解)

一、什么是 RTSP?協議 1.1 RTSP 協議簡介? RTSP&#xff0c;全稱實時流傳輸協議&#xff08;Real Time Streaming Protocol&#xff09;&#xff0c;是一種位于應用層的網絡協議。它主要用于在流媒體系統中控制實時數據&#xff08;如音頻、視頻等&#xff09;的傳輸&#…

Python學習-----1.認識Python

目錄 前言 1.關于Python博客前期的內容 2.計算機基礎概念 2.1.什么是計算機? 2.2.什么是編程&#xff1f; 2.3.編程語言有哪些&#xff1f; 3.Python背景知識 3.1.Python是怎么來的&#xff1f; 3.2.Python都可以用來干什么&#xff1f; 3.3.Python的優缺點 3.4.Py…

MongoDB頻繁掉線頻繁斷開服務的核心原因以及解決方案-卓伊凡|貝貝|莉莉|糖果

MongoDB頻繁掉線頻繁斷開服務的核心原因以及解決方案-卓伊凡|貝貝|莉莉|糖果查看日志內容 &#xff1a;2025-07-22T17:05:20.2160800 I CONTROL [initandlisten] MongoDB starting : pid34231 port28018 dbpath/data/mongodb 64-bit hostVM-0-17-centos 2025-07-22T17:05:20.21…

VUE懶加載(4種方式)

第一種 使用 Webpack 的動態導入&#xff08;Dynamic Imports&#xff09;第二種 Vue Router 中的懶加載第三種 使用第三方庫第四種 使用 Vuex 進行異步數據加載雖然不是直接的懶加載&#xff0c;但你可以在組件內部或 Vuex store 中使用異步 action 來加載數據&#xff0c;確保…

【ROS1】09-ROS通信機制——參數服務器

目錄 一、參數服務器概念 二、參數操作 2.1 C實現 2.1.1 新增參數 2.1.2 修改參數 2.1.3 查詢參數 2.1.4 刪除參數 2.2 python實現 2.2.1 新增參數 2.2.2 修改參數 2.2.3 查詢參數 2.2.4 刪除參數 一、參數服務器概念 假設正在開發一個復雜的機器人應用&#xff0…

C#.NET dapper 詳解

簡介 Dapper 是由 Stack Overflow 團隊開發的一個簡單、高性能的微型 ORM&#xff08;Object?Relational Mapper&#xff09;&#xff0c;僅幾千行代碼&#xff0c;依賴于 ADO.NET 的 IDbConnection&#xff0c;通過動態生成 IL 來映射結果到實體對象。 與 EF、NHibernate 這類…

【LeetCode 熱題 100】35. 搜索插入位置——二分查找(左閉右開)

Problem: 35. 搜索插入位置 給定一個排序數組和一個目標值&#xff0c;在數組中找到目標值&#xff0c;并返回其索引。如果目標值不存在于數組中&#xff0c;返回它將會被按順序插入的位置。 請必須使用時間復雜度為 O(log n) 的算法。 文章目錄整體思路完整代碼時空復雜度時間…

Python-初學openCV——圖像預處理(四)——濾波器

目錄 一、圖像噪點消除噪聲&#xff1a; 1、概念 2、均值濾波 3、方框濾波 4 、高斯濾波 5、中值濾波 6、雙邊濾波 7、總結 一、圖像噪點消除噪聲&#xff1a; 1、概念 指圖像中的一些干擾因素&#xff0c;通常是由圖像采集設備、傳輸信道等因素造成的&#xff0c;表現…

嵌入式系統可靠性設計

嵌入式系統可靠性設計硬件件可靠性設計1. 硬件設計原則2. 硬件設計注意問題2.1 引腳布局和走線2.2 元器件選擇和布局2.3 電源和地線分離2.4 EMI/EMC設計2.5 系統可靠性2.6 資源利用和擴展性軟件可靠性設計1. 設計原則1.1 模塊化設計1.2 冗余設計1.3 容錯設計1.4 實時性保障1.5 …

cJSON在STM32單片機上使用遇到解析數據失敗問題

我們在單片機上解析JSON格式時&#xff08;比如在用云平臺物聯網開發時&#xff09;&#xff0c;可以直接使用cJson庫來完成自己的操作&#xff0c;而不需要單獨實現&#xff0c;具體使用方法可以搜一下。 cJson&#xff1a;一個基于 C 語言的 Json 庫&#xff0c;它是一個開源…

python3基礎語法梳理(三)

接上一篇博客 &#x1f3ae; 猜數字小游戲 - Python版 &#x1f9e0; 游戲規則&#xff1a; 系統隨機生成一個 1 到 10 的整數玩家輸入猜測的數字使用 if 語句判斷玩家猜得是否正確提示“猜對了”或“太大/太小了” import randomsecret_number random.randint(1, 10) att…

【docker】將已有mysql腳本導入鏡像內使用

準備SQL腳本將SQL腳本&#xff08;如init.sql&#xff09;放在宿主機目錄下&#xff0c;例如&#xff1a;/path/to/sql-scripts/init.sql啟動MySQL容器并掛載腳本使用 -v 參數將SQL腳本掛載到容器的初始化目錄&#xff1a;docker run --name mysql-container \-e MYSQL_ROOT_PA…