打开摄像头,服务器和客户端传输摄像头图像数据

1:Camera Server

主要功能,打开摄像头,接收客户端请求

接收到客户端请求“R”字符后开始传输摄像头图像。

#include "mainwindow.h"
#include "ui_mainwindow.h"#include<QDebug>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);const QList<QCameraInfo> availableCameras = QCameraInfo::availableCameras();for (const QCameraInfo &cameraInfo : availableCameras){ui->comboxCamera->addItem(cameraInfo.description());cameras.append(cameraInfo);}ca = new QCamera();//    foreach (const QCameraInfo &cameraInfo, cameras) {
//         if (cameraInfo.deviceName() == "mycamera")
//             ca = new QCamera(cameraInfo);
//     }foreach(const QCameraInfo &info, cameras){qDebug() << info.deviceName();}timer = new QTimer();connect(timer, &QTimer::timeout, [&](){cap->capture();});server = new QTcpServer(this);server->listen(QHostAddress::Any, 8888);connect(server, &QTcpServer::newConnection, this, &MainWindow::newConnect);//ServerListenSocket = new QTcpServer(this);	//服务器端监听套接字 ServerListenSocketclientRequestSize = 0;socket = nullptr;}MainWindow::~MainWindow()
{delete ui;}void MainWindow::on_pushButton_open_clicked()
{//构造一下摄像头对象ca = new QCamera(cameras[ui->comboxCamera->currentIndex()], this);//创建截图对象的内存  截图软件要和摄像头对象相互关联cap = new QCameraImageCapture(ca);//将截图信号与显示截图的槽函数关联一下//连接  截图信号和显示截图的槽函数  一旦发出imageCaptured截图信号,就触发截图的槽函数connect(cap, &QCameraImageCapture::imageCaptured, this, &MainWindow::To_client);//将QCameraViewfinder绑定到一个控件上QCameraViewfinder *v = new QCameraViewfinder(ui->label);v->resize(ui->label->size());ca->setViewfinder(v);v->show();//启动摄像头ca->start();}void MainWindow::on_pushButton_start_clicked()
{//启动时间循环timer->start(20);}void MainWindow::To_client(int index, QImage ima)
{//如果没有链接的情况,就直接退出,不至于闪退if(socket == nullptr){return;}QByteArray byte;	//The QByteArray class provides an array of bytes.QBuffer buf(&byte);		//缓存区域//QString imageSize = "image size is:" + QString::number(frame.cols*frame.rows * 3) + " Bytes";//ui.info->addItem(imageSize);//图像的大小(字节数)ima.save(&buf, "JPEG");	//将图像以jpeg的压缩方式压缩了以后保存在 buf当中//QString jpegImageSize =  "jpeg image size is " + QString::number(buf.size()) + " Bytes";//ui.info->addItem(jpegImageSize);	//压缩后的jpg图像的大小(字节数)QByteArray ss = qCompress(byte, 1);//将压缩后的jpg图像 再用qCompress 压缩 ,第二个参数1-9,9是最大压缩率//QString ssSize="ss's size is "+ QString::number(ss.size()) + " Bytes";//ui.info->addItem(ssSize);//用qCompress 压缩后的数据大小(字节数)//将压缩后的字节串数据编码成Base64方式,字节数会比压缩前稍微变多一些QByteArray vv = ss.toBase64();  // QByteArray QByteArray::toBase64() const : Returns a copy of the byte array, encoded as Base64.//QString vvSize = "vv's size is "  + QString::number(vv.size()) + " Bytes";//ui.info->addItem(vvSize);	//编码后的数据的大小QByteArray ba;QDataStream out(&ba, QIODevice::WriteOnly);	//二进制只写输出流out.setVersion(QDataStream::Qt_5_10);	//输出流的版本/* 当操作复杂数据类型时,我们就要确保读取和写入时的QDataStream版本是一样的,简单类型,比如char,short,int,char* 等不需要指定版本也行 *//* 上面这些编解码的过程肯定是会影响 时效性的,可以考虑只使用jpeg 压缩后就进行发送 。 */out << (quint64)0;	//写入套接字的经压缩-编码后的图像数据的大小out << vv;			//写入套接字的经压缩-编码后的图像数据out.device()->seek(0);out << (quint64)(ba.size() - sizeof(quint64));//写入套接字的经压缩-编码后的图像数据的大小socket->write(ba);	//将整块数据写入套接字//update();	//更新界面}void MainWindow::newConnect()
{//ui->textEdit->append("An new client is connected!");socket = server->nextPendingConnection();	//返回已连接套接字对象connect(socket, SIGNAL(readyRead()), this, SLOT(readClientRequest()));	//将已连接套接字对象的准备好可读信号readyRead与 readClientRequest()槽函数连接//connect(socket, SIGNAL(disconnected()), socket,SLOT(deleterLater()));	//已连接套接字的断开信号与自身的稍后删除信号相连接}void MainWindow::readClientRequest()
{QDataStream in(socket);      //绑定套接字in.setVersion(QDataStream::Qt_5_10);        //指定版本//如果客户端发送过来的第一段数据块的大小为0,说明确实是第一次交互if (clientRequestSize == 0){//客户端发送过来的第一段数据块的大小如果小于 64bit ,则说明:还未收到客户端发送过来的前64bit的数据,这64bit的数据存储了客户端第一次请求包的大小(字节数)if (socket->bytesAvailable() < sizeof(quint16)){return;	//返回,继续等待 接收数据,数据还在套接字缓存当中}else//如果 客户端发送过来的第一段数据块的大小>= 64bit 了{in >> clientRequestSize;//将数据的前64bit提取出来,存储到quint64 类型的clientRequestSize}}if (socket->bytesAvailable() < clientRequestSize)//当前套接字缓冲区中存储的数据如果小于clientRequestSize个字节{return;//返回,继续等待 接收数据,数据还在套接字缓存当中}quint8 requestType;in >> requestType;//从套接字缓冲区中读取 8bit的数据解释为quint8类型,存储到requestType中if (requestType == 'R')  //如果requestType是 'R'  字符R的ASCII值{connect(timer, &QTimer::timeout, [&](){cap->capture();});//将30ms时间到与发送数据的 SendData() 连接}}void MainWindow::on_pushButton_close_clicked()
{}void MainWindow::on_benSendImage_clicked()
{//启动时间循环timer->start(20);
}

2客户端

连接服务器,发送请求,接收数据后展示到界面上

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);imageBlockSize = 0;		//一次接收的图像数据的大小(字节数)connect(&tcpSocket, SIGNAL(disconnect()), this, SLOT(connectionCloseByServer()));//套接字的断开信号connect(&tcpSocket, SIGNAL(readyRead()), this, SLOT(ReceiveData()));//套接字一次可读的触发信号connect(&tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error()));//套接字的错误消息信号}MainWindow::~MainWindow()
{delete ui;
}//连接
void MainWindow::on_pushButton_clicked()
{connectToServer();bool k = connect(&tcpSocket, SIGNAL(connected()), this, SLOT(tcpConnected()));}//请求
void MainWindow::on_benRequest_clicked()
{sendRequest();
}void MainWindow::connectToServer()
{//连接到本机的8888端口tcpSocket.connectToHost(QHostAddress::LocalHost, 8888);//connectToHost是异步连接函数(不阻塞),一经调用结束立刻返回ui->textEdit->append("connecting to LocalHost port 8888...");//ui.connectToServer->setEnabled(false);}void MainWindow::sendRequest()//发送请求,请求视频图像序列
{QByteArray requestMessage;	//请求消息(字节数组)QDataStream out(&requestMessage, QIODevice::WriteOnly);//只读输出流out.setVersion(QDataStream::Qt_5_10);out << quint16(0) << quint8('R');//将请求消息的大小、长度(字节数)与请求消息 'R'写入 输出数据流outout.device()->seek(0);out << quint16(requestMessage.size() - sizeof(quint16));tcpSocket.write(requestMessage);//将输出数据流中的数据写入套接字ui->textEdit->append("Sending request...");//ui.requestVideo->setEnabled(false);}static int receiveCount = 0;//接收 readyRead()信号的触发计数
static int imageCount = 0;	//接收到的图像数据的计数void MainWindow::ReceiveData()
{receiveCount++;QString rCount = QString::number(receiveCount);//ui.receiveCount->setText(rCount);QByteArray message;//存放从服务器接收到的字节流数据QDataStream in(&tcpSocket);	//将客户端套接字与输入数据流对象in绑定in.setVersion(QDataStream::Qt_5_10);//设置数据流的版本/* 接收端的 这部分控制逻辑很重要 */if (imageBlockSize == 0){//如果imageBlockSize == 0 则说明,一幅图像的大小信息还未传输过来//uint64是8字节的8 Bytes  64bit//判断接收的数据是否有8字节(文件大小信息)//如果有则保存到basize变量中,没有则返回,继续接收数据if (tcpSocket.bytesAvailable() < (int)sizeof(quint64)){//一幅图像的大小信息还未传输过来return;}in >> imageBlockSize;//一幅图像的大小信息    //先接受图片的大小if (imageBlockSize == (quint64)0xFFFFFFFFFFFFFFFF)//视频结束的标注符{tcpSocket.close();QMessageBox::information(this, tr("warning"), tr("the video is end!"));return;}qDebug() << "imageBlockSize  is " << imageBlockSize;QString imageBlockS = "imageBlockSize  is " + QString::number(imageBlockSize) + "Bytes!";ui->textEdit->append(imageBlockS);message.resize(imageBlockSize);}//如果没有得到一幅图像的全部数据,则返回继续接收数据if (tcpSocket.bytesAvailable() < imageBlockSize){return;}in >> message;//一幅图像所有像素的完整字节流imageBlockSize = 0;//已经收到一幅完整的图像,将imageBlockSize置0,等待接收下一幅图像imageCount++;	//已接收的图像计数QString iCount = QString::number(imageCount);//ui.imageCount->setText(iCount);ShowImage(message);	//显示当前接收到的这一幅图像}void MainWindow::connectionCloseByServer()//服务端主动断开了已连接套接字
{ui->textEdit->append("Error:Connection closed by server!");tcpSocket.close();//关闭客户端套接字//ui.connectToServer->setEnabled(true);
}void MainWindow::error()
{ui->textEdit->append(tcpSocket.errorString());tcpSocket.close();//ui.connectToServer->setEnabled(true);
}void MainWindow::tcpConnected()//套接字已经建立连接信号的处理槽函数
{//ui.requestVideo->setEnabled(true);
}void MainWindow::ShowImage(QByteArray ba)	//从接收到了字节流中,执行与服务器断相反的操作:解压缩、解释为图像数据
{QString ss = QString::fromLatin1(ba.data(), ba.size());QByteArray rc;rc = QByteArray::fromBase64(ss.toLatin1());QByteArray rdc = qUncompress(rc);QImage img;//img.loadFromData(rdc,"JPEG");//解释为jpg格式的图像img.loadFromData(rdc);//解释为jpg格式的图像ui->label->setPixmap(QPixmap::fromImage(img));ui->label->resize(img.size());update();
}

代码下载连接

https://download.csdn.net/download/qianshanxue11/91264982

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/87586.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/87586.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android实现获取前台应用信息

Android实现获取前台应用信息 1.前言&#xff1a; 之前需要获取在后台运行的App信息&#xff0c;比如包名、版本这些常规的&#xff0c;今天是讲解获取在前台的App信息&#xff0c;虽然App在前台&#xff0c;但是具体的信息可能不知道&#xff0c;今天就尝试获取一下&#xf…

快讯|美团即时零售日订单已突破1.2亿,餐饮订单占比过亿

据美团内网公布信息显示&#xff0c;截至22时54分&#xff0c;美团即时零售当日订单已经突破了1.2亿单&#xff0c;其中&#xff0c;餐饮订单已超过1亿单。 值得注意的是&#xff0c;就在当晚20时45分&#xff0c;美团内网曾显示即时零售日订单突破了1亿。这也意味着&#xff…

pycharm2018配置gitee操作

一、gitee介绍及下载安装 gitee介绍&#xff1a; gitee别名码云&#xff0c;是中国的一个代码托管平台&#xff0c;类似于GitHub&#xff0c;基于Git技术&#xff0c;提供远程仓库托管、协作功能和开源社区服务&#xff0c;优势包括访问速度快、本地化服务和政策合规git和gite…

数据结构——栈的讲解(超详细)

数据结构——栈的讲解&#xff08;超详细&#xff09;-腾讯云开发者社区-腾讯云 #include"Stack.h" void STInit(ST* ps) {ps->arr NULL;ps->capacity ps->top 0; //总空间个数和有用空间个数都初始化为0 }void STDestroy(ST* ps) {if (ps -> arr) …

MySQL允许root用户远程连接

注意&#xff1a;在实际生产环境中&#xff0c;允许root用户从任意主机&#xff08;‘%’&#xff09;连接存在安全风险&#xff0c;建议使用强密码并限制访问IP&#xff0c;或者创建具有必要权限的单独用户用于远程连接。MySQL 配置远程连接指南 1. 登录 MySQL 服务器 mysql -…

STM32的 syscalls.c 和 sysmem.c

syscalls.c 是 STM32CubeIDE 自动生成的标准系统调用适配文件&#xff0c;用于裸机环境下支持 newlib 标准库&#xff08;如 printf, scanf, malloc&#xff09;的运行。这份文件提供了标准库运行所需的最小系统调用实现。现在我来逐段解析其作用&#xff0c;并补充你可能需要修…

Java零基础笔记01(JKD及开发工具IDEA安装配置)

1.Java简介 Java是一种广泛使用的计算机编程语言&#xff0c;由美国的Sun Microsystems公司&#xff08;Stanford University Network&#xff09;在1995年推出。Java以其跨平台、面向对象、安全性高等特点&#xff0c;广泛应用于企业级应用开发、移动应用开发等领域。2009年&a…

Spark SQL架构及高级用法

Spark SQL 架构概述 架构核心组件 API层&#xff08;用户接口&#xff09; 输入方式&#xff1a;SQL查询&#xff1b;DataFrame/Dataset API。统一性&#xff1a; 所有接口最终转换为逻辑计划树&#xff08;Logical Plan&#xff09;&#xff0c;进入优化流程。 编译器层&…

【机器学习深度学习】什么是下游任务模型?

目录 前言 一、什么是下游任务模型&#xff1f; 二、为什么需要下游任务模型&#xff1f; 三、下游任务模型都在干嘛&#xff1f; 四、下游模型怎么训练出来的&#xff1f; 五、图解理解&#xff1a;上游 vs 下游 六、一个现实案例&#xff1a;BERT做情感分析 原始数据…

补充:问题:CORS ,前后端访问跨域问题

补充&#xff1a;问题&#xff1a;CORS &#xff0c;前后端访问跨域问题 我这边的解决方法是&#xff1a; myAxios.defaults.withCredentials true; // 配置为true&#xff0c;表示前端向后端发送请求的时候&#xff0c;需要携带上凭证cookie整体的&#xff1a; import axio…

洛谷 P13014 [GESP202506 五级] 最大公因数-普及-

题目描述 对于两个正整数 a,ba,ba,b&#xff0c;他们的最大公因数记为 gcd⁡(a,b)\gcd(a,b)gcd(a,b)。对于 k>3k > 3k>3 个正整数 c1,c2,…,ckc_1,c_2,\dots,c_kc1​,c2​,…,ck​&#xff0c;他们的最大公因数为&#xff1a; gcd⁡(c1,c2,…,ck)gcd⁡(gcd⁡(c1,c2,……

前端-CSS-day1

目录 1、初识CSS 2、CSS引入方式 3、标签选择器 4、类选择器 5、id选择器 6、通配符选择器 7、画盒子 8、字体大小 9、字体粗细 10、字体倾斜 11、行高 12、行高-垂直居中 13、字体族 14、font属性 15、文本缩进 16、文本对齐方式 17、图片对齐方式 18、文本…

解锁万能文件内容提取器:Apache Tika

01 引言 在日常工作中&#xff0c;你是否曾为这些场景头疼过&#xff1f; 堆积如山的PDF、Word、Excel文档&#xff0c;如何快速提取关键信息&#xff1f;用户上传的文件五花八门&#xff0c;如何自动识别类型并安全处理&#xff1f;构建搜索引擎时&#xff0c;如何让系统“读懂…

gemini-cli初体验

目录 准备配置环境变量运行使用基础使用配置MCP调用MCP 参考 准备 NodeJS 18版本 配置环境变量 设置GEMINI_API_KEY 变量&#xff0c;在https://aistudio.google.com/apikey创建key 设置代理&#xff08;可选&#xff0c;取决于您的网络&#xff09;,不配置可能会报错 api e…

Java --类变量和类方法--main语句

1. 类变量和类方法 介绍&#xff1a; 类变量也叫静态变量/静态属性&#xff0c;是该类的所有对象共享的变量&#xff0c;任何一个该类的对象去访问它时&#xff0c;取到的都是相同的值&#xff0c;同样任何一个该类的对象去修改它时&#xff0c;修改的也是同一个变量。 语法…

spring boot项目配置使用minion

一. Minio概述 Minio是一款开源的高性能对象存储服务,兼容Amazon S3 API,适用于私有云、混合云及边缘计算场景。它采用分布式架构设计,支持水平扩展,提供数据加密、版本控制、生命周期管理等企业级功能,适用于存储非结构化数据(如图片、视频、日志等)。 核心特性 S3兼…

<5>_Linux进程控制

目录 一&#xff0c;进程创建&#xff0c;fork/vfork 1&#xff0c;fork创建子进程&#xff0c;操作系统都做了什么 2&#xff0c;写时拷贝的做了什么 二&#xff0c;进程终止&#xff0c;echo $&#xff1f; 1&#xff0c;进程终止时&#xff0c;操作系统做了什么 2&…

阿里云服务器正确配置 Docker 国内镜像的方法

&#x1f4e6; 原理说明&#xff1a;什么是“Docker 镜像加速器”&#xff1f; Docker 默认会从官方仓库 registry-1.docker.io 拉取镜像。由于网络原因&#xff0c;在中国大陆访问这个地址较慢甚至失败。 镜像加速器的作用是&#xff1a; 在国内部署一个缓存服务器&#xf…

PH热榜 | 2025-07-05

1. todai 标语&#xff1a;你的第一份个性化快乐生活指数 介绍&#xff1a;Todai 是你个人的人工智能助手&#xff0c;帮助你获得心理清晰和情感平衡。你可以随时随地记录自己的情绪&#xff0c;发现情绪变化的规律&#xff0c;并获取基于科学的工具。 产品网站&#xff1a;…

c++ duiLib环境集成

duiLib的Github链接&#xff1a;https://github.com/duilib/duilib 使用vcpkg快速安装duilib以及配置。步骤如下&#xff1a; 1、用git下载vcpkg&#xff0c;下载报错&#xff0c;这个错误通常表明在Git克隆过程中&#xff0c;与GitHub服务器的SSL连接被意外重置。改用http下…