微信
投稿

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

2022-09-01 08:38 来源:码农爱学习 作者:

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

 

1 必备基础知识

 

本篇需要编写一个服务器和客户端,关于TCP服务器/客户端的基础知识,可参考这篇:socket套接字基础

 

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

 

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

 

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

 

1.1 QTcpSocket与QTcpServer

 

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

 

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

 

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

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

     

    1.2 QCamera相关

     

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

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

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

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

     

      2 Win平台上测试

       

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

       

      2.1 服务器端

       

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

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

       

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

       

      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地址可以选择开启或关闭网络摄像头

       

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

       

      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灯

       

      4 总结

       

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


      免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      热门作者

      东方

      简介: 天马行空的文字之旅。

      邮箱: liutingting03@hczyw.com

      简介: 保持期待,奔赴山海。

      邮箱: zhuangjiaxin@hczyw.com

      松月

      简介: 脚踏实地,仰望星空。

      邮箱: wuxiaqing@hczyw.com

      合作咨询:15889679808               媒体咨询:13650668942

      广州地址: 广州市越秀区东风东路745号紫园商务大厦19楼

      深圳地址: 广东省深圳市龙华区五和大道星河WORDC座5F506

      北京地址: 北京市朝阳区小关东里10号院润宇大厦2层

      慧聪电子网微信公众号
      慧聪电子网微信视频号

      Copyright?2000-2020 hczyw.com. All Rights Reserved
      慧聪电子网    粤ICP备2021157007号