添加OpenCv
This commit is contained in:
191
PublicFunctions/RtspPlayer.cpp
Normal file
191
PublicFunctions/RtspPlayer.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
#include "RtspPlayer.h"
|
||||
#include <QDebug>
|
||||
#include <QMutexLocker>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
RtspPlayer::RtspPlayer(QObject *parent) : QThread(parent)
|
||||
{
|
||||
// 设置线程退出时自动释放资源
|
||||
setTerminationEnabled(true);
|
||||
}
|
||||
|
||||
RtspPlayer::~RtspPlayer()
|
||||
{
|
||||
stopPlay(); // 停止播放
|
||||
wait(1000); // 等待线程退出(最多1秒)
|
||||
if (isRunning()) {
|
||||
terminate(); // 若仍未退出,强制终止(极端情况)
|
||||
}
|
||||
}
|
||||
|
||||
bool RtspPlayer::init(const QString &rtspUrl)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex); // 加锁保护资源
|
||||
|
||||
// 释放已有连接
|
||||
if (m_cap.isOpened()) {
|
||||
m_cap.release();
|
||||
}
|
||||
|
||||
m_rtspUrl = rtspUrl;
|
||||
m_needReconnect = false;
|
||||
|
||||
// 强制使用FFmpeg后端(兼容性最佳)
|
||||
bool opened = m_cap.open(rtspUrl.toStdString(), cv::CAP_FFMPEG);
|
||||
|
||||
// 若失败,尝试GStreamer后端(Linux常用)
|
||||
if (!opened) {
|
||||
opened = m_cap.open(rtspUrl.toStdString(), cv::CAP_GSTREAMER);
|
||||
}
|
||||
|
||||
if (!opened) {
|
||||
emit errorOccurred("初始化失败:无法打开RTSP流");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 配置低延迟参数
|
||||
m_cap.set(cv::CAP_PROP_BUFFERSIZE, 1); // 缓冲区仅1帧(最低延迟)
|
||||
m_cap.set(cv::CAP_PROP_FPS, 25); // 固定帧率
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RtspPlayer::startPlay()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_isPaused = false;
|
||||
m_isPlaying = true;
|
||||
|
||||
// 若线程未运行,则启动
|
||||
if (!m_isRunning) {
|
||||
m_isRunning = true;
|
||||
start(NormalPriority); // 正常优先级启动线程
|
||||
}
|
||||
}
|
||||
|
||||
void RtspPlayer::pausePlay()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_isPaused = true;
|
||||
m_isPlaying = false;
|
||||
}
|
||||
|
||||
void RtspPlayer::stopPlay()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_isPlaying = false;
|
||||
m_isPaused = false;
|
||||
m_isRunning = false;
|
||||
m_needReconnect = false;
|
||||
|
||||
// 强制释放OpenCV资源
|
||||
if (m_cap.isOpened()) {
|
||||
m_cap.release();
|
||||
}
|
||||
}
|
||||
|
||||
bool RtspPlayer::isPlaying() const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex); // const函数加锁需用mutable mutex
|
||||
return m_isPlaying;
|
||||
}
|
||||
|
||||
void RtspPlayer::run()
|
||||
{
|
||||
cv::Mat frame;
|
||||
int reconnectCount = 0; // 重连计数器,避免无限重试
|
||||
|
||||
while (m_isRunning) {
|
||||
// 检查是否需要暂停
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (m_isPaused) {
|
||||
locker.unlock(); // 解锁后再休眠,避免阻塞其他操作
|
||||
msleep(50);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查连接是否有效,无效则重连
|
||||
if (!m_cap.isOpened() || m_needReconnect) {
|
||||
emit errorOccurred(QString("连接断开,尝试重连(%1次)...").arg(reconnectCount));
|
||||
|
||||
// 重连逻辑(加锁保护)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_cap.release(); // 彻底释放旧连接
|
||||
m_cap.open(m_rtspUrl.toStdString(), cv::CAP_FFMPEG);
|
||||
m_needReconnect = !m_cap.isOpened();
|
||||
}
|
||||
|
||||
if (m_cap.isOpened()) {
|
||||
emit errorOccurred("重连成功");
|
||||
reconnectCount = 0; // 重置计数器
|
||||
} else {
|
||||
reconnectCount++;
|
||||
if (reconnectCount >= 5) { // 最多重试5次
|
||||
emit errorOccurred("重连失败次数过多,请检查设备");
|
||||
stopPlay(); // 停止播放
|
||||
break;
|
||||
}
|
||||
msleep(1000); // 1秒后重试
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 读取一帧(非阻塞方式)
|
||||
bool readSuccess = m_cap.grab(); // 先抓取帧(快速)
|
||||
if (readSuccess) {
|
||||
readSuccess = m_cap.retrieve(frame); // 再解码(耗时)
|
||||
}
|
||||
|
||||
if (!readSuccess || frame.empty()) {
|
||||
m_needReconnect = true; // 标记需要重连
|
||||
msleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 转换为QImage并发送
|
||||
QImage image = cvMatToQImage(frame);
|
||||
if (!image.isNull()) {
|
||||
emit frameReady(image);
|
||||
}
|
||||
|
||||
// 控制帧率(约25fps)
|
||||
msleep(40);
|
||||
}
|
||||
|
||||
// 线程退出前释放资源
|
||||
frame.release();
|
||||
m_cap.release();
|
||||
qDebug() << "RtspPlayer线程已退出";
|
||||
}
|
||||
|
||||
QImage RtspPlayer::cvMatToQImage(const cv::Mat &mat)
|
||||
{
|
||||
if (mat.empty()) {
|
||||
return QImage();
|
||||
}
|
||||
|
||||
QImage image;
|
||||
if (mat.channels() == 3) {
|
||||
// BGR转RGB(OpenCV默认BGR格式)
|
||||
cv::Mat rgbMat;
|
||||
cv::cvtColor(mat, rgbMat, cv::COLOR_BGR2RGB);
|
||||
// 复制数据,避免Mat释放后QImage访问无效内存
|
||||
image = QImage(
|
||||
rgbMat.data, rgbMat.cols, rgbMat.rows, rgbMat.step,
|
||||
QImage::Format_RGB888
|
||||
).copy();
|
||||
} else if (mat.channels() == 1) {
|
||||
// 灰度图
|
||||
image = QImage(
|
||||
mat.data, mat.cols, mat.rows, mat.step,
|
||||
QImage::Format_Grayscale8
|
||||
).copy();
|
||||
} else {
|
||||
emit errorOccurred(QString("不支持的图像格式(通道数:%1)").arg(mat.channels()));
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
Reference in New Issue
Block a user