Qt Demo:基于TCP协议的视频传输Demo

目录

        1.设计思路

        2.Pro文件配置

        3.头文件引入

        4.界面设计

        5.初始化设备函数

        6.发起视频链接函数

        7.初始化定时器模块函数

        8.TCP链接模块函数

        9.处理接收的数据线程函数

        10.实现功能展示


设计思路

        基于TCP协议的视频传输Demo,设计要实现的功能主要是TCP传输还有视频,可参考以下开发思路:

        1.视频本质都是通过照片组成,由于连续的照片数量多了,画面就由单纯的静态图片变成动态的视频,所以要实现视频传输,实现得把摄像头录制到的画面分成帧使用TCP协议发送,具体开发细节如下:

                1.设置QTimer类对象,实现定时器功能,定期捕获摄像头录制的画面

                2.为了实现捕获画面功能,需要定义QImageCapture对象,并使用QMediaCaptureSession类对象统一管理摄像头和QImageCapture对象

        2.TCP协议的链接,主要通过三次握手形成可靠的链接。所以我们需要一个QTcpSocket对象来进行链接,并对该对象发出的readyRead信号(读取到数据)进行相对应的槽函数绑定

        3.由于要同时录制视频,还有捕获视频画面发送至其他用户,所以要使用线程,单独使用线程往往管理不是很方便,所以Demo中使用了QThradPool类(线程池)来管理线程,并使用QThreadPool::globalInstance()->start()函数将线程放置全局线程池统一管理启动

        4.有时候不仅仅满足于视频传输的需求(现实生活中或许只有倒车视频,监控等功能不需要实现音频录制),所以该Demo使用了QAudioDevice访问音频设备,并录制


Pro文件配置

        为了实现基于TCP协议的视频传输Demo,我们需要Qt中自带的两个模块,分别为multimedia 模块和multimediawidgets模块:

        1.multimedia模块:该模块提供了音频和视频播放、录制以及处理的基本功能。支持多种音频和视频格式,能实现流媒体播放等功能

        2.multimediawidgets模块:该模块提供了一组预构建的多媒体小部件,使得开发者可以快速地在应用程序中添加多媒体播放和控制功能

        所以开发前Pro文件中应该添加以下字段

QT += multimedia multimediawidgets

头文件引入

        以下为项目main文件中需要引入的头文件,我会把为什么要引入该头文件列在备注中,以供参考:

头文件备注
QDir用于获取操作文件系统目录
QFile用于文件操作的类
QTimer用于实现定时器功能
QCamera用于访问和控制摄像头设备
QHostInfo用于获取主机信息
QTcpSocket用于实现TCP套接字的类
QTcpServer用于实现TCP服务器端的类
QMessageBox用于显示消息框,向用户显示信息、警告或错误
QAudioInput用于音频输入设备的类
QThreadPool用于管理线程池
QAudioDevice用于访问和控制音频设备
QMediaDevices提供对媒体设备信息的访问
QCameraDevice用于访问和控制相机设备
QImageCapture用于捕获摄像头录制的图片
QMediaRecorder用于录制媒体内容
QMediaCaptureSession管理媒体捕获会话的类

表1.头文件列表


界面设计

        设计思想是为了实现多个用户同时视频的功能,但是没实现服务端,于是把服务端使用的QTcpServer转到客户端实现,写Demo时本人将更多考虑的是提供更强的可扩展性


初始化设备函数

        以下函数为初始化设备的函数,针对Ui文件中控件名称的不同可以修改对应的指针,完成效果

/*  初始化MainWindow界面信息
 *  device:当前摄像头
 *  返回值:void
*/
void MainWindow::init_MainWindowInfo()
{
    session = new QMediaCaptureSession(this);
    session->setVideoOutput(ui->MyvideoPreview);      //设置视频输出控件

    QAudioInput* audioInput = new QAudioInput(this);
    audioInput->setDevice(QMediaDevices::defaultAudioInput());  //获取默认的音频输入设备
    session->setAudioInput(audioInput);     //设置音频输入设备

    QCameraDevice  defaultCameraDevice = QMediaDevices::defaultVideoInput();//获取当前摄像头    
    if(defaultCameraDevice.isNull()){
        QMessageBox::critical(this,"提示","没有发现摄像头");
        return;
    }
    init_CameraDevice(defaultCameraDevice);
}

/*  初始化摄像头
 *  device:当前摄像头
 *  返回值:void
*/
void MainWindow::init_CameraDevice(const QCameraDevice& device)
{
    QByteArray defaultCameraID = device.id();
    int index=0;
    for(int i=0; i<QMediaDevices::videoInputs().size(); i++){
        QCameraDevice device =QMediaDevices::videoInputs().at(i);
        if (device.id() == defaultCameraID){
            ui->comboCam_List->addItem(device.description()+"(默认)",QVariant::fromValue(device));
            index=i;
        }else{
            ui->comboCam_List->addItem(device.description(),QVariant::fromValue(device));
        }
    }
    if (ui->comboCam_List->currentIndex() != index){
        ui->comboCam_List->setCurrentIndex(index);
    }

    camera_Obj = new QCamera(this);       //初始化摄像头对象
    camera_Obj->setCameraDevice(device);
    session->setCamera(camera_Obj);       //为session设置摄像头
    capture_Obj = new QImageCapture(this);//创建图像捕获对象
    session->setImageCapture(capture_Obj);//设置画面捕获对象

    //捕获到画面时发送画面
    connect(capture_Obj, &QImageCapture::imageCaptured, this, &MainWindow::handle_SendingImg);
    //设置摄像头信息
    connect(ui->comboCam_List,&QComboBox::currentIndexChanged,this, &MainWindow::handle_ShowCameraDevice);
    handle_ShowCameraDevice(ui->comboCam_List->currentIndex());
}

/*  初始化摄像头设备信息
 *  device:当前摄像头
 *  返回值:void
*/
void MainWindow::init_CameraDeviceInfo(const QCameraDevice& device)
{
    ui->comboCam_VideoRes->clear();     //支持的视频分辨率
    ui->comboCam_FrameRate->clear();    //支持的帧率范围
    foreach(QCameraFormat format, device.videoFormats()){
        QSize size=format.resolution();
        QString str=QString::asprintf("%d X %d",size.width(),size.height());
        ui->comboCam_VideoRes->addItem(str);
        str=QString::asprintf("%.1f ~ %.1f",format.minFrameRate(),format.maxFrameRate());
        ui->comboCam_FrameRate->addItem(str);
    }
}

/*  显示摄像头信息
 *  index:项下标
 *  返回值:void
*/
void MainWindow::handle_ShowCameraDevice(int index)
{
    QVariant var=ui->comboCam_List->itemData(index);
    QCameraDevice device = var.value<QCameraDevice>();
    init_CameraDeviceInfo(device);      //显示摄像头设备信息
    camera_Obj->setCameraDevice(device);    //重新设置摄像头设备
}

发起视频链接函数

//发起视频链接
void MainWindow::on_Camclose_Btn_2_clicked()
{
    QString ip = ui->lineEdit->text();
    QString port = ui->lineEdit_2->text();
    if(port.isEmpty() || ip.isEmpty()){
        QMessageBox::information(nullptr,"提示","未输入目标端口或IPv4地址",QMessageBox::Ok);
    }

    if(tcpSocket != nullptr){
        tcpSocket->deleteLater();
    }
    if(video_ThreadObj != nullptr){
        video_ThreadObj->handle_CloseThread();
    }
    tcpSocket = new QTcpSocket(this);
    qDebug() << "发起连接,目标地址:" << ip  << "目标端口" << port.toUShort();
    //连接成功
    connect(tcpSocket,&QAbstractSocket::connected,this,&MainWindow::handle_Connect);
    //显示接收的图片
    connect(tcpSocket,&QTcpSocket::readyRead,this,&MainWindow::hanlde_RelayinThread);

    tcpSocket->connectToHost(ip,port.toUShort());    //链接服务器
    video_ThreadObj = new VideoThread(ui->show_Img);
}


void MainWindow::handle_Connect()
{
    qDebug() << "连接成功";
    init_TimerModel();
}

初始化定时器模块函数

//初始化定时器
void MainWindow::init_TimerModel()
{
    timer_Obj = new QTimer;
    timer_Obj->start(100);
    //定时截取摄像头拍摄的图片
    connect(timer_Obj,&QTimer::timeout,this,&MainWindow::handle_InterceptImg);
}

//定时截取摄像头图片
void MainWindow::handle_InterceptImg()
{
    QDir().mkdir("images");
    QString path = QDir::currentPath() + "/images/image.jpg";   //捕获图像的保存路径
    qDebug() << "定时截取摄像头图片" << path;
    capture_Obj->captureToFile(path);
}

//发送截取的图片
void MainWindow::handle_SendingImg(int id, const QImage &image)
{
    QDir().mkdir("images");
    QString path = QDir::currentPath() + "/images/image.jpg";   //捕获图像的保存路径
    if (!image.isNull()) {  //截取到图片
        image.save(path,"jpg",30);   //保存图像并压缩,质量为30
        QFile file(path);
        file.open(QIODevice::ReadOnly);
        tcpSocket->write(file.readAll());   //发送图片
        tcpSocket->flush();     //确保消息被发送
        file.close();
        qDebug() << "发送图片";
    }
}

TCP链接模块函数

//初始化Tcp模块
void MainWindow::init_TcpModule()
{
    tcpSocket = new QTcpSocket;
    tcpServer = new QTcpServer(this);
    connect(tcpServer,&QTcpServer::newConnection,this,&MainWindow::init_TcpConnection);

    get_LocalIPv4();    //获取IPv4地址
    QString port = ui->lineEdit_3->text();//监听端口
    qDebug() << "监听端口" << port.toUShort();
    tcpServer->listen(QHostAddress::Any,port.toUShort());  //开始监听
}

//初始化Tcp链接
void MainWindow::init_TcpConnection()
{
    qDebug() << "获取到连接";
    //获取第一个链接的客户端
    tcpSocket = tcpServer->nextPendingConnection();
    video_ThreadObj = new VideoThread(ui->show_Img);
    //显示接收的图片
    connect(tcpSocket,&QAbstractSocket::readyRead,this,&MainWindow::hanlde_RelayinThread);
    //连接成功
    connect(tcpSocket,&QAbstractSocket::connected,this,&MainWindow::handle_Connect);

    QThreadPool::globalInstance()->start(video_ThreadObj);//启动线程
}

//转发至线程处理读取到的数据
void MainWindow::hanlde_RelayinThread()
{
    qDebug() << "处理接收的函数";
    video_ThreadObj->handle_ShowReceiveImg(tcpSocket->readAll());
}

//  获取本机IPV4地址
int MainWindow::get_LocalIPv4()
{
    QString hostName = QHostInfo::localHostName();     //获取主机设备名
    QHostInfo hostInfo = QHostInfo::fromName(hostName);//获取主机信息
    QList<QHostAddress> addList = hostInfo.addresses();//获取主机地址列表
    if(addList.isEmpty()){  //未获取到地址
        return 0;
    }
    else{
        foreach(const QHostAddress& item,addList){
            if(item.protocol() == QAbstractSocket::IPv4Protocol){
                ui->udpIpv4_Box->addItem(item.toString());
            }
        }
        return 1;
    }
    return -1;
}

处理接收的数据线程函数

VideoThread::VideoThread(QObject *parent)
    : QRunnable{}
    , QObject{parent}
{
    setAutoDelete(true);  //设置线程自动析构
}

VideoThread::VideoThread(QLabel *widget, QObject *parent)
    : QRunnable{}
    , QObject{parent}
    , video_Label{widget}
{
    ok = true;   //初始化线程状态
    setAutoDelete(true);  //设置线程自动析构
}

void VideoThread::run()
{
    while(ok);
}

//显示接收的图像
void VideoThread::handle_ShowReceiveImg(QByteArray data)
{
    QPixmap pic = QPixmap::fromImage(QImage::fromData(data));
    video_Label->setPixmap(pic.scaled(video_Label->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
}

//关闭线程
void VideoThread::handle_CloseThread()
{
    if(ok){
        ok = false;
    }
}

实现功能展示


        PS:由于本机只有一个摄像头,就不能开另一边应用的摄像头了,后续会把Demo的github地址发布在评论区。该Demo还需要更多的完善才能真正的实现一个视频通话功能,后续会更新优化后的Demo

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

带DSP音效处理D类数字功放TAS5805M中文资料

国产替代D类数字功放中文资料访问下方链接 ACM8628 241W立体声182W单通道数字功放中文寄存器表 内置DSP多种音频处理效果ACM8628M-241W立体声或182W单通道数字功放 1 特性 具有增强处理能力和低功率损耗的 TAS5805M 23W、无电感器、数字输入、立体声、闭环 D 类音频放大器 …

测试:ollama加载羊驼版本llama-3中文大模型

找了一个晚上各种模型&#xff0c;像极了当初找各种操作系统的镜像&#xff0c;雨林木风&#xff0c;深蓝、老毛桃…… 主要是官方的默认7B版本回答好多英文&#xff0c;而且回复的很慢&#xff0c;所以我是在ollama上搜索"chinese"找到了这个羊驼版本的&#xff0c…

代码随想录-Day25

216.组合总和III 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff0c;组合可以以任何顺序返回。 示例 1: 输入: k 3, n 7 输…

Maven打包错误:无效的源发行版:17

1. 报错问题 在用maven进行打包时&#xff08;clean & install&#xff09;&#xff0c;报如下错误&#xff1a; 一开始让我很摸不着头脑&#xff0c;我确定我的pom.xml&#xff0c;还有IDEA中的Project Settings是正确的。 2. 排查 尽管确定&#xff0c;但还是一个个排…

升级笔记本

笔记本型号参数&#xff1a;Acer V5-573G CPU:I5 4200U 1.6GHz 最高2.6GHz 双核四线程 内存&#xff1a;4G 1600M DDR3L SO-DIMM 显卡&#xff1a;独立显卡NVIDIA GeForce GT 750M 硬盘&#xff1a;1T 5400转 屏幕&#xff1a;15.6英寸 1920*1080 【Acer V5-573G-54204G1…

安装zookeeper

一、搭建前准备 192.168.1.99 sdw1 192.168.1.98 sdw2 192.168.1.97 sdw3 二、搭建 1、各主机修改/etc/hosts&#xff0c;/etc/hostname文件 /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhos…

牛客网刷题 | BC106 K形图案

目前主要分为三个专栏&#xff0c;后续还会添加&#xff1a; 专栏如下&#xff1a; C语言刷题解析 C语言系列文章 我的成长经历 感谢阅读&#xff01; 初来乍到&#xff0c;如有错误请指出&#xff0c;感谢&#xff01; 描述 KiKi学习了循环&am…

今日好料推荐(大数据湖体系规划)

今日好料推荐&#xff08;大数据湖体系规划&#xff09; 参考资料在文末获取&#xff0c;关注我&#xff0c;获取优质资源。 大数据湖体系规划 一、大数据湖简介 大数据湖&#xff08;Data Lake&#xff09;是一个集中式的存储库&#xff0c;用于存储来自各种来源的结构化和…

墨天轮《2023年中国数据库行业年度分析报告》正式发布!

为明晰发展脉络&#xff0c;把握未来趋势&#xff0c;墨天轮于5月29日正式发布 《2023年中国数据库年度行业分析报告》。该报告由墨天轮联合业界专家学者共同编写&#xff0c;共330页&#xff0c;旨在梳理和洞察中国数据库行业的发展趋势、技术创新、市场动态以及面临的挑战&am…

微信群活码生成系统网源码

微信群二维码活码工具&#xff0c;生成微信群活码&#xff0c;随时可以切换二维码&#xff01;微信官方群二维码有效期是7天&#xff0c;过期后无法扫码进群&#xff0c;或者是群人数满200人就无法扫码进群&#xff0c;如果我们在推广的时候&#xff0c;群满人或者过期了&#…

M-G364PD惯性测量单元:相机及微小层面的革命性应用

在现代科技飞速发展的今天&#xff0c;精准控制和精确测量是众多高端设备实现卓越性能的关键。爱普生推出的M-G364PD惯性测量单元&#xff08;IMU&#xff09;&#xff0c;因其卓越的性能和微小尺寸&#xff0c;成为相机以及其他微小层面应用的理想选择&#xff0c;为科技创新提…

IDEA中,MybatisPlus整合Spring项目的基础用法

一、本文涉及的知识点【重点】 IDEA中使用MybatisPlus生成代码&#xff0c;并使用。 Spring整合了Mybatis框架后&#xff0c;开发变得方便了很多&#xff0c;然而&#xff0c;Mapper、Service和XML文件&#xff0c;在Spring开发中常常会重复地使用&#xff0c;每一次的创建、修…

pytorch学习笔记4

开启tensorboard 在terminal中输入tensorboard --logdir文件名 文件名中不能含有空格 tensorboard --logdirlogs --port6007#将端口调整为6007tensorboard --logdirlogs --port 0 自动分配一个端口&#xff0c;成功访问打开的时候如果发现没数据可以把logs换成文件夹的绝对路径…

[无监督学习] 10.详细图解PCA

PCA 在众多降维算法中&#xff0c;PCA&#xff08;Principal Component Analysis&#xff0c;主成分分析&#xff09;历史悠久&#xff0c;被广泛应用于各个领域。 使用 PCA 可以将相关的多变量数据以主成分简洁地表现出来。 概述 PCA 是一种用于减少数据中的变量的算法。它对…

11.3 指针和函数

11.3 指针和函数 本节必须掌握的知识点&#xff1a; 指针作为函数的参数 数组作为函数的参数 指针作为函数的返回值 在C语言中&#xff0c;指针的一个重要作用就是作为函数参数使用&#xff0c;本节将介绍这一重要作用。 11.3.1 指针作为函数的参数 实验一百一十三&#xff…

从功能性磁共振成像(fMRI)数据重建音频

听觉是人类最重要的感官之一&#xff0c;它负责接收外部的听觉刺激&#xff0c;并将这些信息传递给大脑进行处理和理解。研究人员正致力于从神经科学和计算机科学两个领域探索人脑的听觉感知机制。一个关键目标是从人脑中解码神经信息&#xff0c;并重建原始的刺激。常见的大脑…

深入解析 YOLOv8 中的 `conv.py`(代码图文全解析-下)

&#x1f60e; 作者介绍&#xff1a;我是程序员行者孙&#xff0c;一个热爱分享技术的制能工人。计算机本硕&#xff0c;人工制能研究生。公众号&#xff1a;AI Sun&#xff0c;视频号&#xff1a;AI-行者Sun &#x1f388; 本文专栏&#xff1a;本文收录于《yolov8》系列专栏&…

快速排序详讲(两种方法)

目录 原理 实现方式 正常实现 理由 先从右到左&#xff0c;在从左到右 先从左到右&#xff0c;先从右到左 挖坑法 效率 优化 测试 代码 原理 快速排序是将最左侧的数字当作关键数字&#xff0c;将关键数字放在对应位置&#xff0c;且关键数字左侧均大于它&#xff…

【深度学习】【STWave】时空图预测,车流量预测,Efficient Spectral Graph Attention Network

Spatio-Temporal meets Wavelet: Disentangled Traffic Flow Forecasting via Efficient Spectral Graph Attention Network 代码&#xff1a;https://github.com/LMissher/STWave 论文&#xff1a;https://arxiv.org/abs/2112.02740 帮助&#xff1a; https://docs.qq.com/s…

使用pycharm+opencv进行视频抽帧(可以用来扩充数据集)+ labelimg的使用(数据标准)

一.视频抽帧 1.新创建一个空Pycharm项目文件&#xff0c;命名为streach zhen 注&#xff1a;然后要做一个前期工作 创建opencv环境 &#xff08;1&#xff09;我们在这个pycharm项目的终端里面输入下面的命令&#xff1a; pip install opencv-python --user -i https://pypi.t…