1.简单流程

首先通过opencv打开视频流,获取到数据帧,然后将数据帧转换为QT可识别的图像,显示到QT界面上。

2.opencv解码线程源码

//DecodeOpencv.h

#pragma once

#include <QThread>
#include <QObject>

#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

class DecodeOpencv : public QThread
{
Q_OBJECT

Q_SIGNALS:
void sigSendFrame(cv::Mat mat);

public:
DecodeOpencv(QObject *parent = nullptr);
~DecodeOpencv();

public:
void setUrl(QString url);

void setStoped(bool stop);

protected:
void run();

private:
QString m_rtsp;

bool m_isStop = false;
};
//DecodeOpencv.cpp

#include "DecodeOpencv.h"
#include <QDebug>

DecodeOpencv::DecodeOpencv(QObject *parent)
: QThread(parent)
{
qRegisterMetaType<cv::Mat>("cv::Mat");
}

DecodeOpencv::~DecodeOpencv()
{
m_isStop = true;
}

void DecodeOpencv::setUrl(QString url)
{
m_rtsp = url;
}

void DecodeOpencv::setStoped(bool stop)
{
m_isStop = stop;
}

void DecodeOpencv::run()
{
cv::VideoCapture cap;

if (m_rtsp.isEmpty())
{
qDebug() << "未找到视频流地址";
return;
}
bool suc = false;
if (m_rtsp.contains("localhost:camera"))
{
suc = cap.open(0); //打开本地摄像头
}
else
{
suc = cap.open(m_rtsp.toStdString()); //打开网络流地址
}

if (!suc)
{
qDebug() << "播放失败";
return;
}

while (true)
{
if (!m_isStop)
{
cv::Mat frame;
cap >> frame;
if (frame.empty())
{
qDebug() << "播放帧解析失败";
continue;
}
emit sigSendFrame(frame);
}
else
{
qDebug() << "停止播放";
break;
}
}
}

3.QT界面

QT+Opencv实现视频流播放_#pragma

//ShowMatWidget.h

#pragma once

#include <QWidget>
#include "ui_ShowMatWidget.h"
#include "DecodeOpencv.h"

class ShowMatWidget : public QWidget
{
Q_OBJECT

public:
ShowMatWidget(QWidget *parent = Q_NULLPTR);
~ShowMatWidget();

private Q_SLOTS:
void slotBtnConnected();

void slotGetFrame(cv::Mat mat);

QImage cvMat2QImage(const cv::Mat& mat);

private:
Ui::ShowMatWidget ui;

DecodeOpencv *m_thread = nullptr;
};
//ShowMatWidget.cpp

#include "ShowMatWidget.h"

#include <QDebug>

ShowMatWidget::ShowMatWidget(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);

m_thread = new DecodeOpencv(this);

connect(m_thread, &DecodeOpencv::sigSendFrame, this, &ShowMatWidget::slotGetFrame);

connect(ui.pushButton, &QPushButton::clicked, this, &ShowMatWidget::slotBtnConnected);
}

ShowMatWidget::~ShowMatWidget()
{
}

void ShowMatWidget::slotBtnConnected()
{
m_thread->setUrl(ui.lineEdit->text());
m_thread->start();
}

void ShowMatWidget::slotGetFrame(cv::Mat mat)
{
QImage image = cvMat2QImage(mat);

ui.label->setPixmap(QPixmap::fromImage(image));
}

QImage ShowMatWidget::cvMat2QImage(const cv::Mat& mat)
{
//8位,通道数为1
if (mat.type() == CV_8UC1)
{
QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
image.setColorCount(256);
for (int i = 0; i < 256; i++)
{
image.setColor(i, qRgb(i, i, i));
}

uchar *pSrc = mat.data;
for (int row = 0; row < mat.rows; row++)
{
uchar *pDest = image.scanLine(row);
memcpy(pDest, pSrc, mat.cols);
pSrc += mat.step;
}
return image;
}
//8位,通道数为3
else if (mat.type() == CV_8UC3)
{
const uchar *pSrc = (const uchar*)mat.data;
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
return image.rgbSwapped();
}
//8位,通道数为4
else if (mat.type() == CV_8UC4)
{
qDebug() << "CV_8UC4";

const uchar *pSrc = (const uchar*)mat.data;
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
return image.copy();
}
//32位,通道数为3
else if (mat.type() == CV_32FC3)
{
cv::Mat temp;
mat.convertTo(temp, CV_8UC3);

const uchar *pSrc = (const uchar*)temp.data;
QImage image(pSrc, temp.cols, temp.rows, temp.step, QImage::Format_RGB888);
return image.rgbSwapped();
}
else
{
qDebug() << "ERROR: Mat could not be converted to QImage.";
return QImage();
}
}