IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> FPGA UDP视频/图片数据传输(QT实现) -> 正文阅读

[网络协议]FPGA UDP视频/图片数据传输(QT实现)

一、目标功能

1、可以让pc通过udp与FPGA之间双向传输视频/图片。

2、udp传图的数据不经过压缩按:R(8bit)、G(8bit)、B(8bit)一个像素点一个像素点传输。

3、每帧图片得有帧标记(类似vga的hs vs de)。

二、实现过程

1、实现计划使用opencv库,qt+opencv的环境搭建

错误1:opencv编译中ffmpeg插件问题。

? ? ? ? ? ?videocapture无法打开视频(时间最长,卡了4、5天)

错误2:版本问题(opencv版本和ffmpeg下载的版本不不对应会出现)

? ? ? ? ? ? Qt版本:mingw730 64bit ?opencv:4.1.0 ?64bit

2、发送程序调试

1、实现了udp通讯的搭建

???实现了opencv提取图像像素矩阵数据?

???实现了像素数据到udp数据类型的转换

? ? ??代码:

2、测试图片:(画图软件绘制的12x12的图)

?3、Mat类输出矩阵显示:

?4、Mat转换为数组输出:

?代码:(待补充)

5、发送较大的图片文件测试

? ? ?上面测试的小数据量的,但超过一个udp包(65535byte)就会出错

AbstractSocket::SocketError

?6、分包发送。图像数据分多次发送。

?代码:(待补充)

7、数据包的设置

????????图片和视频都能发送了。

? ? ? ? 需要用于区分图片数据的开头结尾。(设置安排如下)

Head+每包数据:

Head ??: 帧标志+图像长度

(例:一帧数据的第一个包,图像行长为12)

01 00 0C???(3byte)

??????????????????????????????????(当前帧第二包及之后)

??????????????????????????????????????00 00 0C????(3byte)

??????????????第1byte: 一帧第一个包的为1,其他包为0。

??????????????后2byte: 为图像的行长。

??????????????Data ??: 图像数据(len=包长)

??????????????????????数据格式:第一个像素点R(8bit)+G(8bit)+B(8bit)

??代码:(待补充)

注意:frame.cols为int型,要转换

? ? ? ? ? 且?int存储方式为高位在后低位在前!

?代码:(待补充)

?3、接收程序调试

? ? ?目标:数据接收并转为图片,并窗口显示

? ? ?涉及: 多包数据整合

? ? ? ? ? ? ? ? ?需要利用head标记区分图片数据接收开始结束。

? ? ? ? ? ? ? ? ?Data与head分离

? ? ? ? ? ? ? ? 数据类型的转换

1、测试1:单包发送情况

???发送数据:(用的上面12x12的图的数据)

?结果可行!

QT的debug输出:

?效果展示:

2、一帧数据分多包发送的情况

? ? ?用flag判断一幅图片开始

?网络助手手发数据测试:

?基本功能完成。

?4、其他功能

? ? ? ? ?在实际FPGA上板测试后,又添加了一些功能

?①保存接收到的图片(save文件里)

②程序运行日志添加(save文件里log.txt),可用于检查软件运行日志(检查错误)

③ 两个图片显示窗口需要清空按钮。

?④添加本机的接收端口设置(不设置为固定端口)。

⑤应为有接收丢包的情况,尝试了多线程处理(实现把发送放到了子线程处理)

5、目前问题

? ? ? ? 1、在测试时,使用自发自收,接收时还是有可能丢数据,发送不丢包

? ? ? ? ? ? ? 可能原因:收发数据要分不同线程?(没有专门学过qt c++,不了解)

? ? ?

?6、程序

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QTextStream>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    QString title ="FPGA   UDP传图  本机端口:"+ ui->rxport->text();
    setWindowTitle(title);

    udpsocket=new QUdpSocket(this);
    udpsocket->abort();
    portStr = ui->rxport->text().toInt();
    udpsocket->bind(QHostAddress::Any,portStr);//绑定当前网卡所有的ip,端口
    timer=new QTimer(this);

    connect(ui->ptn_quit,SIGNAL(clicked(bool)),this,SLOT(quit())); //退出按钮
    connect(udpsocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));
    connect(ui->ptn_openfile,SIGNAL(clicked(bool)),this,SLOT(openfile()) );// 打开文件
    connect(ui->ptn_send,SIGNAL(clicked(bool)),this,SLOT(ptn_send_clicked()));  //发送按钮按下
    connect(timer,SIGNAL(timeout()),this,SLOT(PlayAndSendData()));//按下按钮后

    //当收到数据时readyRead()有效,进而触发跳转到UDPReceive()函数;
    connect(udpsocket,SIGNAL(readyRead()),this,SLOT(UDPReceive()));

    //新建文件夹保存接收到的图片及log
    path=QDir::currentPath();
    path=path.replace("/","\\")+"/save";
    createFile(path,"log.txt");

}

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

void Widget::openfile()
{
    QString path=QDir::currentPath();
    path.replace("/","\\");
    filename = QFileDialog::getOpenFileName(this,"选择需要播放的视频",path,
     "allfiles(*.*);;"
     "MP4(*.mp4);;"
     "AVI(*.avi)");
}

// ========================== udp发送  ===========================================================
void Widget::SendData()   // 每帧执行一次
{
    cvtColor(frame,frame,CV_BGR2RGB);
//-------- 分包处理 -------
    QByteArray sendbyte;
// 定义包头
    QByteArray head;
    head.append(1);//一帧第一包的标志
    int l=frame.cols; //int的存储是低位在前!! frame.cols是int32(而QByteArray用char16?)
    int h=frame.rows;
    head.append(dec2hex2(l),2);//写入一行长度(.append为追加行长数据)(char型,直接添加丢失精度)(这里int转hex且取2byte)
    head.append(dec2hex2(h),2);
    qDebug()<<"第 "<<n<<"frame 帧";
    qDebug()<<"head = "<<head.toHex(); 
    qDebug()<<"The size of head is "<<head.size(); 
// 定义将要发送的数据
    QByteArray sendbyte1;
    sendbyte1.append((char*)frame.data,frame.rows*frame.cols*3);

    QByteArray byte;
    QBuffer buffer(&byte);//缓冲区绑定数据源
    buffer.open(QIODevice::WriteOnly);

    buffer.write(sendbyte1);
    //每帧图像分多次发送,总大小为ByteLen,每次发送的大小为sendLen,比较可知是否完整发完本帧的数据。
    int ByteLen = byte.size();
    int sendLen = 0;
    qDebug()  << "byte.size :" << ByteLen;

  while(ByteLen > sendLen )
    {
     // 填充要发送的数据
        sendbyte.append (head);//加上每包数据的3byte
        sendbyte.append (byte.mid(sendLen,ui->lineEdit_bao->text().toUInt()));      // !!!后设置分包大小:frame.cols*3
     //发送
        int len = udpsocket->writeDatagram(sendbyte,sendbyte.size(),QHostAddress(ip),port);
        sendLen += len-head.size();//并减去head的长度
        qDebug() <<"本次发送出数据长度为 this time:" << len-head.size() << "已发送数据长度为 Has been sent:" << sendLen << " 此帧像素数据总长度为 The total length:" << ByteLen ;
        sendbyte.clear();
        udpsocket->flush();
        head[0]=0x00;//一帧非第一包标志
    }
    byte.clear();
    //QThread::msleep(delay);
}


// ===================================================================
void Widget::displayError(QAbstractSocket::SocketError)
{
    qDebug()<<"Failed to open video";
    QString string=udpsocket->errorString();
    qDebug()<<string;
}

void Widget::quit()
{
    stop = true;
}
//=========================================================
// int转为hex(但这里高位在后)
QByteArray Widget::dec2hex(int a)
{
    QByteArray array;
    array.resize(sizeof(int));
    memcpy(array.data(), &a, sizeof(int));
    return array;
}
//但是下位机通信经常采用大端模式,即高字节数据在前!!
QByteArray Widget::dec2hex2(int a)
{
    QByteArray array;
//    array[0] = (uchar)(a >> 24 & 0xff);
//    array[1] = (uchar)(a >> 16 & 0xff);
    array[0] = (uchar)(a>>8 & 0xff);
    array[1] = (uchar)(a    & 0xff);
    return array;
}



//============================================  UDP接收图片  =============================================================================
void Widget::UDPReceive()   //测试:广播(255.255.255.255)发
{
    qDebug()<<"start udp rx";
    QByteArray receivebyte;//本次data
    //QByteArray datareg;//累积的data

    // 让datagram的大小为等待处理的数据报的大小,这样才能接收到完整的数据
    receivebyte.resize(udpsocket->pendingDatagramSize());
    // 接收数据报,存到datagram
    qint64 len = udpsocket->readDatagram(receivebyte.data(), receivebyte.size());

    if(len>0)//如果>0就接收处理
     {
 // -----------------------------
        // 解析出每次的head
        int8_t flag =(receivebyte.at(0));
        qDebug()<<"帧标记 flag:"<<flag;

         bool ok;
         QByteArray headL ,headH;
         headL.resize(0);
         headH.resize(0);
         headL.append(receivebyte.at(1));
         headL.append(receivebyte.at(2));
         headH.append(receivebyte.at(3));
         headH.append(receivebyte.at(4));
//         head[0]=0x00;
//         head[1]=0xB4;
         int L = headL.toHex().toInt(&ok, 16);
         int H = headH.toHex().toInt(&ok, 16);
         //qDebug()<<"ok:"<<ok;
         //qDebug()<<"head="<<headL;
         //qDebug()<<"head="<<headH;
         qDebug()<<"L="<<L;
         qDebug()<<"H="<<H;
 // -------------------------
         int sum = H*L*3;
         //qDebug()<<"H*L*3 sum="<<sum;
         if(flag==1)//如果接收到一帧开始
         {
             //清空
             datareg.remove(0,datareg.size());
             qDebug()<<"清空 head refrish:datareg.size="<<datareg.size();
             //取出本包数据
             //qDebug()<<"(本包数据:)"<<receivebyte.right(receivebyte.size()-5).toHex();//5跳过head
             unsigned char *rxdata;
             rxdata = reinterpret_cast<unsigned char*>(receivebyte.data())+5; //+5:跳过head
             //如果数据只有一帧,显示
             qDebug()<<"(第1包)receivebyte.size:"<<receivebyte.size();

             if(sum==receivebyte.size()-5)
             {
                 qDebug()<<"only 1(只有一包数据)";
                 //Mat img(h,l,rxdata);
                 // 图片显示准备
                 QImage image_rx(rxdata,L,H,QImage::Format_RGB888);
                 // 同时显示当前图片
                 ui->label_rx->setPixmap(QPixmap::fromImage(image_rx));
                 ui->label_rx->resize(image_rx.size());
                 // 图像保存
                 imageSave(image_rx );
                 //清空  再重新 累计data
                 datareg.remove(0,datareg.size());//head.resize(0);???
             }
             else//如果不够一帧,累计
             {
                 datareg.append(receivebyte.right(receivebyte.size()-5));//只存图像到datareg
             }

         }
         else//不是帧头,累积datar
         {
             datareg.append(receivebyte.right(receivebyte.size()-5));//只存图像到datareg
             //qDebug()<<"本包数据:"<<receivebyte.right(receivebyte.size()-5).toHex();//5跳过head

             unsigned char *rxdata;
             rxdata = reinterpret_cast<unsigned char*>(datareg.data()); //无head
             //如果是最后一包数据,显示并重置
             qDebug()<<"本次接受数据长度 Length of data accepted this time:"<<receivebyte.size()-5<<"已接收总量 Total received:"<<datareg.size()<<"此帧像素数据总长度为 The total length:"<<sum;
             //qDebug()<<"h*l*3 sum="<<sum<<endl;
             if(sum==datareg.size())//或者可以多数据?xx
             {
                 //Mat img(h,l,rxdata);
                 // 图片显示准备
                 QImage image_rx(rxdata,L,H,QImage::Format_RGB888);
                 // 同时显示当前图片
                 ui->label_rx->setPixmap(QPixmap::fromImage(image_rx));
                 ui->label_rx->resize(image_rx.size());
                 // 图像保存
                 imageSave(image_rx);
                 //清空
                 datareg.remove(0,datareg.size());
             }
         }

      }
}

 // 图像保存=============================
void Widget::imageSave(QImage image_rx )
{

    static int i = 0;
    QString imageName = path+(QString("/%1.png").arg(i));
    image_rx.save(imageName);
    i++;
    qDebug() << "image save ok";
}

// 新建文件==============================
void Widget::createFile(QString filePath,QString fileName)
 {
     QDir tempDir;
     //临时保存程序当前路径
     QString currentDir = tempDir.currentPath();
     //如果filePath路径不存在,创建它
     if(!tempDir.exists(filePath))
     {
         qDebug()<<"不存在该路径 no path!"<<endl;
         tempDir.mkpath(filePath);
     }
     QFile *tempFile = new QFile;
     //将程序的执行路径设置到filePath下
     tempDir.setCurrent(filePath);
     qDebug()<<tempDir.currentPath();
     //检查filePath路径下是否存在文件fileName,如果停止操作。
     if(tempFile->exists(fileName))
     {
         qDebug()<<"文件存在 File exists";
         return ;
     }
     //此时,路径下没有fileName文件,使用下面代码在当前路径下创建文件
     tempFile->setFileName(fileName);
     if(!tempFile->open(QIODevice::WriteOnly|QIODevice::Text))
     {
         qDebug()<<"Open fail";
     }
     tempFile->close();
     //将程序当前路径设置为原来的路径
     tempDir.setCurrent(currentDir);
     qDebug()<<tempDir.currentPath();
 }

void Widget::on_pushButton_rxset_clicked()
{
    udpsocket->abort();
    QString title ="FPGA   UDP传图  本机端口:"+ ui->rxport->text();
    setWindowTitle(title);
    portStr = ui->rxport->text().toInt();
    udpsocket->bind(QHostAddress::Any,portStr);//绑定当前网卡所有的ip,端口
}

void Widget::on_pushButton_clicked()
{
    ui->label_rx->clear();
}

void Widget::on_pushButton_2_clicked()
{
    ui->label->clear();
}

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-17 15:45:03  更:2021-08-17 15:45:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:34:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码