Qt网络编程之UDP通信(二)视频传输

9 篇文章 0 订阅
2 篇文章 1 订阅

上一篇博客中介绍了Qt下UDP传输流程与文本数据的传输过程,根据UDP的特点而言(与TCP对比),它注重的是数据传输的效率而不是可靠性,因此在很多对于实时性要求较高而可靠性要求不是那么高的场景下,如视频、语音传输,多采用UDP传输方式,故本文介绍一下基于UDP的视频传输过程。

一、基本流程

我们知道,UDP通信无需建立连接,每一个应用程序端绑定IP和端口之后便可以实现读写操作,我么们只要在发送端获取视频信息,将获取到的每一帧画面转换为可传输的数据发送到接收端,然后接收端再将数据转换为图片,放到窗口显示,即可实现视频的传输。

之前的博客中我们知道,使用OpenCV打开视频设备获取的原始图像是Mat格式的,如果要显示在窗口中则要将其转换为QImage格式;而UDP的读写操作对象都是QByteArray类型,所以将QImage转为QByteArray便可发送;接收端收到数据后再将QByteArray转为QImage即可显示画面,所以总体流程大致如下:

发送端:

Created with Raphaël 2.2.0 开始 创建通信套接字对 象并绑定IP与端口 连接定时器槽函数 是否点击开始? 获取目的IP和端口号 开启视频设备 开启定时器 进入定时器槽函数: { 读取Mat数据 Mat转为QImage并显示 QImage转为QByteArray并发送 } 是否点击停止? 关闭定时器 关闭视频设备 关闭窗口 结束 yes no yes no

接收端的处理相对简单一些,它只需要绑定好自己的IP和端口,当发送端向该端口发送数据时便会触发它(接收端)的readyRead()信号,所以只需在该信号对应的槽函数中将QByteArray转为QImage并显示即可。

Created with Raphaël 2.2.0 开始 创建通信套接字对 象并绑定IP和端口号 连接readyRead()信号 与数据接收槽函数 是否有数据 发送过来? 读取数据 转为QImage 窗口显示 yes no

可以看到流程图并不复杂,其中关键的地方在于数据类型的转换,主要有以下几个地方:

  1. OpenCV读取的Mat类型图像其默认颜色通道为BGR,首先要使用cvtColor(InputArray src, OutputArray dst, int code);将其转为我们常用的RGB通道,否则视频的颜色会看起来怪怪的。函数中src表示源数据,dst表示输出数据,code转换方式,在本例中按照如下方式转换:
     Mat frame;
     camera.read(frame);
     cvtColor(frame,frame,CV_BGR2RGB);
    
  2. MatQImage:
    QImage image((unsigned char *)(frame.data),frame.cols,frame.rows,QImage::Format_RGB888);
    
  3. QImageQByteArray,这个要借助QBuffer类:
    QByteArray byte;
    QBuffer buff(&byte);
    buff.open(QIODevice::WriteOnly);
    image.save(&buff,"JPEG");
    
  4. 为了方便传输,可以选择数据压缩:
    QByteArray ss = qCompress(byte,5);
    
    参数5代表压缩比。

在接收端要按照从后往前的顺序,先解压缩,再将QByteArray转为QImage:

  1. 解压缩
    QByteArray buff;
    receiver.readDatagram(buff.data(),buff.size(),&adrr,&port); 
    buff = qUncompress(buff);
    
  2. QByteArray转为QImage:
    QBuffer buffer(&buff);
    QImageReader reader(&buffer,"JPEG");
    QImage image = reader.read();
    

二、项目创建

发送端:
在这里插入图片描述
在这里插入图片描述
widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <opencv/cv.hpp>
#include <QUdpSocket>
#include <QTimer>
#include <QBuffer>
using namespace cv;
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

private slots:
    void on_pushButton_open_clicked();

    void on_pushButton_close_clicked();

    void VideoSend();
private:
    Ui::Widget *ui;
    QUdpSocket *udpSocket;
    VideoCapture camera;
    QTimer fps_timer;
};

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    udpSocket = new QUdpSocket(this);
    udpSocket->bind(QHostAddress::Any,8888);
    connect(&fps_timer,SIGNAL(timeout()),this,SLOT(VideoSend()));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_open_clicked()
{
    camera.open(0);
    fps_timer.start(33);
}

void Widget::on_pushButton_close_clicked()
{
    fps_timer.stop();
    camera.release();
    this->close();
}
void Widget::VideoSend()
{
    QHostAddress dstip = (QHostAddress)(ui->lineEdit_ip->text());
    quint16 dstport = ui->lineEdit_port->text().toInt();
    Mat frame;
    camera.read(frame);
    cvtColor(frame,frame,CV_BGR2RGB);
    QImage image((unsigned char *)(frame.data),frame.cols,frame.rows,QImage::Format_RGB888);
    ui->label_video->setPixmap(QPixmap::fromImage(image));
    ui->label_video->resize(image.width(),image.height());
    QByteArray byte;
    QBuffer buff(&byte);
    buff.open(QIODevice::WriteOnly);
    image.save(&buff,"JPEG");
    QByteArray ss = qCompress(byte,5);
    udpSocket->writeDatagram(ss,dstip,dstport);
}

接收端:
在这里插入图片描述
在这里插入图片描述
mainwindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QUdpSocket>
namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
public slots:
  void video_receive_show();
private:
    Ui::MainWindow *ui;
    QUdpSocket receiver;
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QHostAddress>
#include<QPixmap>
#include<QImageReader>
#include<QBuffer>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    receiver.bind(QHostAddress::Any,6666);
    connect(&receiver,SIGNAL(readyRead()),this,SLOT(video_receive_show()));
}

MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::video_receive_show()
{
    quint64 size = receiver.pendingDatagramSize();    
    QByteArray buff;    
    buff.resize(size);
    QHostAddress adrr ;
    quint16 port;
    receiver.readDatagram(buff.data(),buff.size(),&adrr,&port);    
    buff = qUncompress(buff);
    QBuffer buffer(&buff);
    QImageReader reader(&buffer,"JPEG");//可读入磁盘文件、设备文件中的图像、以及其他图像数据如pixmap和image,相比较更加专业。
    //buffer属于设备文件一类,
    QImage image = reader.read();//read()方法用来读取设备图像,也可读取视频,读取成功返回QImage*,否则返回NULL    
    ui->label->setPixmap(QPixmap::fromImage(image));
    ui->label->resize(image.width(),image.height());
}

三、测试效果:

在这里插入图片描述
测试时还是发送端在主机上,接收端在虚拟机中的Linux系统中,通过主机打开视频设备将画面传送到虚拟机中。至此,整个项目完成。
项目源码:https://gitee.com/Mr-Yslf/BlogResources.git
由于项目中使用了外部库(OpenCV),所以源码编译可能不会直接通过,请修改外部库的地址后再尝试编译。

评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值