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的图)

FPGA UDP视频/图片数据传输(QT实现)_第1张图片

 3、Mat类输出矩阵显示:

 4、Mat转换为数组输出:

 代码:(待补充)

FPGA UDP视频/图片数据传输(QT实现)_第2张图片

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

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

AbstractSocket::SocketError

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

 代码:(待补充)

FPGA UDP视频/图片数据传输(QT实现)_第3张图片

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的图的数据)

FPGA UDP视频/图片数据传输(QT实现)_第4张图片

 结果可行!

QT的debug输出:

FPGA UDP视频/图片数据传输(QT实现)_第5张图片

 效果展示:

FPGA UDP视频/图片数据传输(QT实现)_第6张图片

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

     用flag判断一幅图片开始

FPGA UDP视频/图片数据传输(QT实现)_第7张图片

FPGA UDP视频/图片数据传输(QT实现)_第8张图片

 网络助手手发数据测试:

FPGA UDP视频/图片数据传输(QT实现)_第9张图片

 基本功能完成。

 4、其他功能

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

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

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

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

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

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

最终效果:

FPGA UDP视频/图片数据传输(QT实现)_第10张图片

5、目前问题

        1、目标功能实现了,

               在测试时,使用自发自收时,大图接收时还是有可能丢数据,发送不丢包。(wireshark显示正常)

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

  

     

 6、程序

#include "widget.h"
#include "ui_widget.h"
#include 
#include 


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()<<"第 "< 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()<> 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:"<(receivebyte.data())+5; //+5:跳过head
             //如果数据只有一帧,显示
             qDebug()<<"(第1包)receivebyte.size:"<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()<<"本包数据:"<(datareg.data()); //无head
             //如果是最后一包数据,显示并重置
             qDebug()<<"本次接受数据长度 Length of data accepted this time:"<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()<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();
}
https://download.csdn.net/download/Iloadingl/86248945

你可能感兴趣的:(FPGA,QT,其他,嵌入式硬件)