嵌入式Qt-简易网络监控摄像头

2023-11-07

本编利用Qt实现一个网络摄像头功能,包含一个服务端和一个客户端,服务端用于将USB摄像头转换为一个IP摄像头,当有客户端连接时,将其捕获到的图像通过TCP发送出去;客户端运行在Linux板子上,用于查看摄像头的实时画面。

1 必备基础知识

本篇需要编写一个服务器和客户端,关于TCP服务器/客户端的基础知识,可参考这篇:Linux进程间通信详解(七) socket套接字基础

注意,Qt中对Socket的操作进行了进一步的封装,其基本思想还是一样的。

下面就来看一下Qt中如何实现TCP Socket通信。

1.1 QTcpSocket与QTcpServer

  • QTcpSocket,在Qt中,Socket被封装成了QTcpSocket,可以用它实现TCP客户端的功能,以及服务端接收到客户端后,对客户端的处理。

  • QTcpServer,对于TCP服务端的功能,可以使用QTcpServer来完成。

这里整理Qt中TCP Socket的使用方法,配合Qt的信号与槽机制,即可实现服务端/客户端数据的收发处理。

1.2 QCamera相关

  • QCamer,获取当前系统可用的摄像头 类似获取串口

  • QCamerInfo,获取当前系统可用的摄像头 类似获取串口

  • QCameraViewfinder,取景框类,摄像头的实时画面显示到这个里面

  • QCameraImageCapture,图像录制类,与QCamer 配合使用可进行拍照

2 Win平台上测试

首先在Windows平台上用Qt Creator编写服务端和客户端程序,并运行测试。

2.1 服务器端

先来看下服务器端的最终效果:

  • 左侧是摄像头的显示界面
  • 可以切换不同的摄像头作为视频源(笔记本自带的摄像头与USB外接的摄像头)
  • 可以切换摄像头的显示分辨率
  • 可以选择开启或关闭摄像头的IP服务

2.1.1 摄像头画面显示

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    QComboBox *pCamType = new QComboBox();
    m_pComboBox = ui->cbBox_resolution;
    pCamType = ui->cbBox_cameras;
    pCamType->clear();

    cameraList = QCameraInfo::availableCameras();
    foreach(const QCameraInfo &cameraInfo, cameraList)
    {
        qDebug() << "CameraInfo:" << cameraInfo;
        pCamType->addItem(cameraInfo.description());
    }

    m_pCamViewFind = new QCameraViewfinder(this);
    m_pCamViewFind->setGeometry(10, 10, W, H);
    m_pCamViewFind->show();

    m_pCam = new QCamera(this);
    m_pCam->setViewfinder(m_pCamViewFind);
    m_pCam->start();
}

2.1.2 创建Socket服务

void Widget::on_btn_IPServer_toggled(bool checked)
{
    if (checked)
    {
        m_pServer = new QTcpServer(this);
        if (!m_pServer->listen(QHostAddress::Any, 12345))
        {
            QMessageBox::critical(this, "error", "listen port failed");
            exit(0);
        }
        qDebug() << "start IP server";

        m_pTimer = new QTimer(this);
        connect(m_pServer, SIGNAL(newConnection()), this, SLOT(new_client()));
        connect(m_pTimer, SIGNAL(timeout()), this, SLOT(timer_slot()));
        m_pTimer->start(100);

        ui->btn_IPServer->setText("关闭IP服务");
    }
    else
    {
        qDebug() << "stop IP server";
        m_pServer->close();
        delete m_pServer;

        ui->btn_IPServer->setText("开启IP服务");
    }
}

2.1.3 读取图像并发送给客户端

先定义一下图像传送结构体和传送状态:

enum TransStatus{
    TS_IDLE,       //空闲(图像数据可以更新)
    TS_RUNNING,    //图像数据传输中(还不可以更新图像数据)
    TS_FIRST_DATA, //需要发出图像数据的第一部分
};

class ImgData {
public:
    char data[LEN] = {0}; //图像数据
    int  totalLen = 0; //图像大小
    int  hasSentLen = 0; //已发出的数据长度
    TransStatus  stats = TS_IDLE; //工作状态
};

具体的实现过程:

void Widget::read_data()
{
    QString str = m_pClient->readAll();
    ImgData *pData = (ImgData*)m_pClient->userData(0);
    QString s("newImage:%1");

    if (str == "new_request")
    {
        qDebug() << "read_data, new_request, d->len:" << pData->totalLen << "d->stats:" << pData->stats;
        if ((pData->totalLen > 0) && (pData->stats==TS_IDLE)) //图像大小不为0,表示已更新图像数据了
        {
            pData->stats = TS_RUNNING;
            m_pClient->write(s.arg(pData->totalLen).toUtf8());
            pData->hasSentLen = 0;
        }
        else //图像数据还没有更新
        {
            pData->stats = TS_FIRST_DATA; //在定时器的槽函数里发出"newImage..."
        }
    }
    else if (str == "ack")
    {
        int len_send = P_LEN; //本次需要发送的长度

        if (pData->hasSentLen >= pData->totalLen) //如果图像已传输完毕
        {
            qDebug() << "read_data, send done! lenSent:" << pData->hasSentLen << "len" << pData->totalLen;
            return;
        }

        // 最后1包数据(不满P_LEN)
        if ((pData->hasSentLen + P_LEN) > pData->totalLen)
        {
            len_send = pData->totalLen - pData->hasSentLen;
        }

        qDebug() << "read_data, ack, write len:" << len_send;

        // 发送数据
        pData->hasSentLen += m_pClient->write(pData->data + pData->hasSentLen, len_send);
        if (pData->hasSentLen >= pData->totalLen)
        {
            pData->stats = TS_IDLE; //传输完毕后,把状态改为可更新
            pData->totalLen = 0;
        }
    }
}

需要注意的是,图像是需要分包传送的,最后一包一般都不是设定的最大长度,需要计算一下最后一包的数据长度。

2.2 客户端

先来看下客户端的最终效果:

  • 右侧是摄像头画面的显示框
  • 可以修改要连接的服务端的IP地址
  • 可以选择开启或关闭网络摄像头

2.2.1 创建Socket连接

void Widget::on_pushButton_toggled(bool checked)
{
    if (checked)
    {
        QString ip = ui->lineEdit->text();
        m_pSocket->connectToHost(ip, 12345);
        if (!m_pSocket->waitForConnected(1000))
        {
            QMessageBox::critical(this, "error", "server connection failed");
            return;
        }

        ui->pushButton->setText("关闭");
        m_iRecvLen = 0;
        m_pSocket->write("new_request");
        qDebug("on_bnt_connect_clicked, new_request");
    }
    else
    {
        m_pSocket->close();
        ui->pushButton->setText("打开");
    }
}

2.3.2 接收服务端的图像

void Widget::read_data()
{
    int ret;
    QTime qTime;
    static int i = 0;

    ret = m_pSocket->read(m_pData + m_iRecvLen, P_LEN);
    if (0 == strncmp("newImage", m_pData + m_iRecvLen, 8))
    {
        m_iImgLen = atoi(m_pData + m_iRecvLen + 9);
        i++;
    }
    else
    {
        m_iRecvLen += ret;
        if (m_iRecvLen >= m_iImgLen)
        {
            QString timestamp = QString::number(QDateTime::currentMSecsSinceEpoch());
            update();

            return;
        }
    }

    //图像传输完毕
    m_pSocket->write("ack");
}

2.3.3 将图像显示出来

void Widget::paintEvent(QPaintEvent *event)
{
    QPixmap map;

    if ((m_iRecvLen >= m_iImgLen) && (m_iImgLen > 0))
    {
        map.loadFromData((uchar *)m_pData, m_iImgLen);
        QPainter p(this);
        p.drawPixmap(140, 0, 640, 480, map);
        m_pSocket->write("new_request");
        m_iRecvLen = 0;
    }
}

3 嵌入式Linux平台上测试

3.1 交叉编译

将客户端程序的源代码拷贝到Ubunu中进行交叉编译,具体编译过程可参考之前的文章:

嵌入式Qt-动手编写并运行自己的第1个ARM-Qt程序

本篇的实验环境,继续使用的是烧录了野火i.MX6ULL自带的系统固件Linux板子,需要通过SSH的方式将编译的程序再发送到板子中,SSH传输文件的操作可参考上篇文章:

嵌入式Qt-控制硬件:滑动条控制RGB灯

3.2 实验演示

https://www.bilibili.com/video/BV12G4y1a7za
在这里插入图片描述

4 总结

本篇介绍了如何用Qt实现一个网络摄像头功能,通过服务端将USB摄像头转换为一个IP摄像头,Linux板子中的客户端来连接服务器,将摄像头的实时画面显示出来。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式Qt-简易网络监控摄像头 的相关文章

  • 如何在 Qt Creator 中编辑 QtWebKit 的右键上下文菜单?

    好吧 这是我的困境 我正在使用 Qt Creator 制作一个使用 Webkit 的简单应用程序 我认为 Qt Creator 会有一种简单的方法来使用信号和槽编辑器编辑右键单击上下文菜单 但事实证明这不是真的 我知道 webkit 有与上
  • 有没有办法向 QListView 添加部分?

    我正在使用 Qt5 2 和 C 来实现一个应用程序 需要显示一个列表 其中包含类似于下面的示例图像的部分 source ngo hung com http www ngo hung com files images contact list
  • 在 Qt 服务器上验证用户身份

    我正在尝试使用 C QtTcpSocket 为个人项目 多人国际象棋游戏 实现身份验证系统 我的朋友建议了一种验证用户的方法 但我想问是否有更简单或更好的方法 来自 Python 背景 做这个项目主要是为了加深对 C 的理解 我将发布我朋友
  • QComboBox 下拉项边距

    我想设计我的风格QComboBox为下拉项目留出边距 现在是这样的 我想要这样的东西 我尝试过 QComboBox QAbstractItemView item margin 3px 但它不起作用 你能帮我解决这个问题吗 您想在项目之间设置
  • QStyledItemDelegate 绘制自定义小部件失败

    在我的一个项目中 我使用的是QTableWidget为了显示一些复杂的计算结果 为了提高表格的可读性 我需要在单个表格单元格内显示两个对齐的值 后来我想通过使用颜色或箭头等来进一步自定义小部件 为此我源自QStyledItemDelegat
  • 获取 QListView 中所有可见项目的简单方法

    我正在尝试使用 Qt Framework 开发一个图像库应用程序 应用程序从所选文件夹加载所有图像 并使用 QListView 控件显示这些图像 但现在我想通过仅加载用户可见的图像来减少内存消耗 由于没有直接函数来获取视图中的所有可见项目
  • 如何doxygen注释Qt属性?

    我想将 Doxygen 注释附加到我的 Q PROPERTY 例如 song h class Song public QObject Q OBJECT private Q PROPERTY QString title READ title
  • Qt中用于线程间通信的类设计

    问题陈述 用相机跟踪物体并相应地移动相机的方位角和仰角 Process 相机获取物体的图像 处理相机的每一帧以查找物体 应该被跟踪 并将每帧中生成的信息传递给机械设备 万向节 以平移和倾斜方式移动摄像机 Design 主 Gui 在一个线程
  • 覆盖 QWebView 中的页面回复

    我试图在 Qt 的 QWebView 中拦截页面 表单请求 并在某些情况下使用替代内容进行响应 QNetworkReply ngcBrowser createRequest Operation operation const QNetwor
  • Qt 5.1.1 与 Visual Studio 2012 - 这些 QT 版本无法访问

    打开 Visual Studio 时出现此错误 我安装自http qt project org downloads http qt project org downloads 适用于 Windows 64 位的 Qt 5 1 1 VS 20
  • 如何将自定义 Qt 类型与 QML 信号一起使用?

    我在 Qt 5 2 qml 应用程序中创建了一个自定义类型 class Setting public QObject Q OBJECT Q PROPERTY QString key READ key WRITE setKey Q PROPE
  • 如何在Android中使用QML - QWebView

    我想在 Android 中部署一个 YouTube 应用程序 但它只能在我的电脑上运行 在安卓上不起作用 它不加载任何视频 问题仅出在 QWebView 上 我使用了与此类似的代码 http doc qt io archives qt 5
  • 无法运行 Qt 应用程序:找不到版本“Qt_5”

    我运行 Ubuntu 16 04 LTS 我的问题是我无法运行可以编译的 Qt5 应用程序 这是我尝试运行它时得到的结果 home user Desktop sconfig dist Release GNU Linux SCongif us
  • Qt QML 数据模型似乎不适用于 C++

    我一直在使用中的示例http doc qt digia com 4 7 qdeclarativemodels html http doc qt digia com 4 7 qdeclarativemodels html这是 QML 声明性数
  • Qt:更改 Mac OS X 上的应用程序 QMenuBar 内容

    我的应用程序对多个 页面 使用 QTabWidget 其中顶级菜单根据用户所在的页面而变化 我的问题是 尝试重新创建菜单栏的内容会导致严重的显示问题 它在除 Mac OS X 之外的所有平台上按预期使用第一种和第三种样式 尚未测试第二种 但
  • 在 Qt GraphicsView 中创建长线(或十字线)光标的最佳方法

    创建长十字线光标 与视口一样长 的简单方法是创建一条十字线graphicsItem 当鼠标移动时 设置该项目的pos财产 但是当场景复杂时这种方式会很慢 因为它要更新整个视口来更新光标的pos 另一种简单的方法是setCursor QCur
  • 使用 QGraphicsScene 和 QGraphicsView 在 Qt 中开始基于 Tile 的游戏

    我将开始在 Qt 中编写基于 2D 图块的游戏 并阅读 QGraphicsScene 和 QGraphicsView 类 这些类旨在显示和处理大量 2D 对象 我的问题是 使用 QGraphicsScene 创建一个包含大量图块的世界是否可
  • CMake AUTOMOC,文件位于不同文件夹中

    我有一个简单的 CMake 项目 proj project folder a h a cpp CMakeLists txt CMakeLists txt cmake minimum required VERSION 3 2 set CMAK
  • QByteArray 到整数

    正如您可能从标题中看出的那样 我在转换QByteArray为一个整数 QByteArray buffer server gt read 8192 QByteArray q size buffer mid 0 2 int size q siz
  • 如何在不声明 32 个插槽的情况下将 32 个按钮的 pressed() 信号连接到单个函数?

    我有一个小部件 里面有 32 个按钮 我需要将每个按钮的 Pressed 信号连接到一个插槽 以便调用一个函数 该函数的参数取决于我按下的按钮 现在我通过以 on QPushButtonName pressed 的形式添加 32 个插槽来做

随机推荐

  • STM32F407单片机读取USR-WIFI232-B2模块的MAC地址

    最近工程项目需要单片机读取USR WIFI232 B2 WIFI 模块的MAC地址 研究了一下 并成功获取了MAC地址 步骤如下 1 单片机上电 USR WIFI232 B2 WIFI 模块先延时12秒 等待模块准备好 2 单片机给USR
  • 面试小结-那些求职路上的经验分享与感受

    世界那么大 我想去看看 世界那么大 我也想去看看 这个月 小吕完成了一次工作上的跳槽 在这种全民跳槽的月份 小吕的心情显的稍有些浮躁 工作上也怠慢了很多 虽然这个月的工作量也不大 没有心思钻研技术 也不能好好静下心来学习 我不喜欢现在这状态
  • flask昌平区安防摄像头可视化

    flask昌平区安防摄像头可视化 此系统有详细的录屏 下面只是部分截图 需要看完整录屏联系博主 系统开发语言python 框架为flask 数据库mysql 分为爬虫和可视化分析
  • 获取当月天数

    获取当月天数 需要导入import java util Calendar String time 2023 2 14 Calendar calendar Calendar getInstance Date data new SimpleDa
  • osgEarth的Rex引擎原理分析(十)earth文件中都有哪些options

    目标 九 中问题9 通过在earth文件中搜索options 发现主要有这么几种
  • 添加和修改docker容器端口映射的方法

    一 添加docker容器端口映射 以tomcat容器为例 root localhost docker run name mytomcat d p 8888 8080 tomcat name 创建的tomcat镜像名称 d 后台运行 p 将主
  • CTF中phpinfo应注意什么

    CTF中 phpinfo应注意什么 1 allow url fopen和allow url include 这个配置选项可以知道在PHP文件包含中可以使用哪些伪协议 2 PHP版本 3 open basedir 这个配置选项可以知道PHP将
  • eclipse打开之后没有界面

    自己工作的电脑上的eclipse经常打开之后没有界面显示出来 但是后台有eclipse和java的进程在运行 这个时候要进入到当前eclipse选择的工作目录 也就是打不开eclipse界面的那个工作目录 中的 gt metadata pl
  • 深入理解原码、反码和补码及其在计算机中的应用【附代码】

    目录 引言 一 原码 Sign Magnitude 二 反码 One s Complement 三 补码 Two s Complement 四 代码实例 结论 参考资料 引言 在计算机科学中 原码 反码和补码是表示有符号整数的三种常见编码方
  • JSON解析器之Gson、FastJson、Jackson

    文章目录 1 Gson 谷歌 2 FastJson 阿里 3 JackSon JSON解析器 常见的解析器 Jsonlib Gson fastjson jackson 1 Gson 谷歌 最好使用最新jar包 百度搜索Gson然后去GitH
  • NFT相关的常见术语

    NFT 爱好者在谈论这个东西时候总是会有一堆让人眼花缭乱的术语 对非圈内人来说会感觉难以理解 这里整理了23个术语解释来帮助你理解 1 10k 项目 一个 10k 项目是一个由大约 10 000 个头像组成的 集邮 项目 这种类型的 NFT
  • 如何在GitHub上传大文件(≥100M)

    学习工作中 有时会遇到需要将一些资源上传到GitHub的仓库 repository 上去 比如一些训练完的模型 但GitHub对于直接上传的文件有大小限制 超过100M的文件需要使用Git LFS才能上传 以下是我根据各位大神和自己的实操经
  • switch 语句 -- 超详解

    目录 语法结构 在switch 语句中的break default 子句 编程好习惯 语法结构 switch 整型表达式 语句项 那么语句项是什么呢 是一些case语句 如下 case 整形常量表达式 语句 在switch 语句中的brea
  • 论文研读 —— 9. DensePose From WiFi

    文章目录 Authors Bibtex 0 ABSTRACT 1 INTRODUCTION 2 RELATED WORK 3 METHODS 3 1 Phase Sanitization 3 2 Modality Translation N
  • linux编译新内核放在哪,将新文件系统编译到Linux内核中

    我正在努力在Debian上构建一个新的文件系统作为原型 因为现有的文件系统不符合我的某些要求 它旨在成为评估我们所拥有的某些要求的原型 到目前为止的步骤 我正在尝试使用provided MakeFile进行项目 但这是我得到的错误 make
  • 夜深人静学32系列5——STM32MAP文件浅析&启动过程

    STM32MAP文件浅析 启动过程 MAP文件浅析 1 MDK中间文件 2 MAP文件浅析 实际的MAP文件 你找不到你的MAP文件 STM32启动过程 启动过程 启动文件介绍 上期我们学习了STM32的寄存器映射相关内容 本期我们一起来学
  • xenserver使用cli进行主机池添加

    使用 CLI 将 XenServer 主机 host1 和 host2 加入到资源池 1 在 XenServer 主机 host2 中打开控制台 2 运行以下命令 指示 XenServer 主机 host2 加入位于 XenServer 主
  • win10如何校验文件哈希值

    转自 https jingyan baidu com article 67662997a9b06654d51b84a1 html 文件的哈希值可以用软件计算 算法一样 无须多讲 本文讲述如何用win10自带命令计算 右击开始 点击windo
  • vue3实现没有误差的时间 moment

    使用requestAnimationFrame 方法实现 window requestAnimationFrame 告诉浏览器 你希望执行一个动画 并且要求浏览器在下次重绘之前调用指定的回调函数更新动画 该方法需要传入一个回调函数作为参数
  • 嵌入式Qt-简易网络监控摄像头

    本编利用Qt实现一个网络摄像头功能 包含一个服务端和一个客户端 服务端用于将USB摄像头转换为一个IP摄像头 当有客户端连接时 将其捕获到的图像通过TCP发送出去 客户端运行在Linux板子上 用于查看摄像头的实时画面 1 必备基础知识 本