#include "realtimeplayer.h"
|
#include "ui_realtimeplayer.h"
|
|
|
// 静态常量定义
|
const QString RealTimePlayer::DEFAULT_SERVER_HOST = "127.0.0.1"; // 本地测试服务器
|
const quint16 RealTimePlayer::DEFAULT_SERVER_PORT = 8888; // 默认端口
|
|
RealTimePlayer::RealTimePlayer(QWidget *parent)
|
: QMainWindow(parent)
|
, ui(new Ui::RealTimePlayer)
|
, m_tcpSocket(nullptr)
|
, m_serverHost(DEFAULT_SERVER_HOST)
|
, m_serverPort(DEFAULT_SERVER_PORT)
|
, m_connectionStatus(Disconnected)
|
, m_timeTimer(nullptr)
|
, m_heartbeatTimer(nullptr)
|
, m_heartbeatTimeoutTimer(nullptr)
|
, m_currentLayout(static_cast<LayoutType>(0))
|
, m_videoGridLayout(nullptr)
|
, m_heartbeatRetryCount(0)
|
{
|
ui->setupUi(this);
|
|
// 初始化各个模块
|
initializeUI(); // 初始化界面
|
initializeNetwork(); // 初始化网络
|
initializeTimers(); // 初始化定时器
|
setupConnections(); // 设置信号槽连接
|
|
// 设置窗口标题
|
setWindowTitle("实时播放模块 v1.0");
|
|
// 设置默认布局
|
setLayout(Layout_1x1);
|
|
// 启动时间更新定时器
|
m_timeTimer->start(1000); // 每秒更新一次时间
|
|
// 自动连接服务器(可选)
|
connectToServer();
|
|
qDebug() << "实时播放模块初始化完成";
|
}
|
|
RealTimePlayer::~RealTimePlayer()
|
{
|
// 停止所有播放
|
stopAllCameras();
|
|
// 断开网络连接
|
disconnectFromServer();
|
|
// 清理界面资源
|
clearVideoLayout();
|
|
// 释放UI资源
|
delete ui;
|
|
qDebug() << "实时播放模块已清理资源";
|
}
|
|
|
// 初始化界面
|
void RealTimePlayer::initializeUI()
|
{
|
// 获取视频显示区域并设置网格布局
|
m_videoGridLayout = ui->videoGridLayout;
|
m_videoGridLayout->setSpacing(2); // 设置网格间距
|
m_videoGridLayout->setContentsMargins(5, 5, 5, 5); // 设置边距
|
|
// 设置窗口最小尺寸
|
setMinimumSize(800, 600);
|
|
// 初始化连接状态
|
updateConnectionStatus(Disconnected);
|
|
qDebug() << "界面初始化完成";
|
}
|
|
// 初始化网络
|
void RealTimePlayer::initializeNetwork()
|
{
|
// 创建TCP套接字
|
m_tcpSocket = new QTcpSocket(this);
|
|
// 设置接收缓冲区
|
m_receiveBuffer.clear();
|
|
qDebug() << "网络模块初始化完成";
|
}
|
|
// 初始化定时器
|
void RealTimePlayer::initializeTimers()
|
{
|
// 创建时间更新定时器
|
m_timeTimer = new QTimer(this);
|
|
// 创建心跳相关定时器
|
m_heartbeatTimer = new QTimer(this);
|
m_heartbeatTimeoutTimer = new QTimer(this);
|
|
// 设置心跳定时器参数
|
m_heartbeatTimer->setInterval(30000); // 30秒发送一次心跳
|
m_heartbeatTimeoutTimer->setInterval(10000); // 10秒心跳超时
|
m_heartbeatTimeoutTimer->setSingleShot(true); // 单次触发
|
|
qDebug() << "定时器初始化完成";
|
}
|
|
// 设置信号槽连接
|
void RealTimePlayer::setupConnections()
|
{
|
// 界面按钮信号槽连接
|
connect(ui->btn1x1, &QPushButton::clicked, this, &RealTimePlayer::onBtn1x1Clicked);
|
connect(ui->btn2x2, &QPushButton::clicked, this, &RealTimePlayer::onBtn2x2Clicked);
|
connect(ui->btn3x3, &QPushButton::clicked, this, &RealTimePlayer::onBtn3x3Clicked);
|
connect(ui->btn4x4, &QPushButton::clicked, this, &RealTimePlayer::onBtn4x4Clicked);
|
connect(ui->btnPlayAll, &QPushButton::clicked, this, &RealTimePlayer::onBtnPlayAllClicked);
|
connect(ui->btnStopAll, &QPushButton::clicked, this, &RealTimePlayer::onBtnStopAllClicked);
|
|
// TCP套接字信号槽连接
|
connect(m_tcpSocket, &QTcpSocket::connected, this, &RealTimePlayer::onConnected);
|
connect(m_tcpSocket, &QTcpSocket::disconnected, this, &RealTimePlayer::onDisconnected);
|
connect(m_tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
|
this, &RealTimePlayer::onSocketError);
|
connect(m_tcpSocket, &QTcpSocket::readyRead, this, &RealTimePlayer::onDataReceived);
|
|
// 定时器信号槽连接
|
connect(m_timeTimer, &QTimer::timeout, this, &RealTimePlayer::updateCurrentTime);
|
connect(m_heartbeatTimer, &QTimer::timeout, this, &RealTimePlayer::sendHeartbeat);
|
connect(m_heartbeatTimeoutTimer, &QTimer::timeout, this, &RealTimePlayer::onHeartbeatTimeout);
|
|
qDebug() << "信号槽连接设置完成";
|
}
|
|
// === 界面按钮槽函数实现 ===
|
|
void RealTimePlayer::onBtn1x1Clicked()
|
{
|
qDebug() << "切换到1画面布局";
|
setLayout(Layout_1x1);
|
}
|
|
void RealTimePlayer::onBtn2x2Clicked()
|
{
|
qDebug() << "切换到4画面布局";
|
setLayout(Layout_2x2);
|
}
|
|
void RealTimePlayer::onBtn3x3Clicked()
|
{
|
qDebug() << "切换到9画面布局";
|
setLayout(Layout_3x3);
|
}
|
|
void RealTimePlayer::onBtn4x4Clicked()
|
{
|
qDebug() << "切换到16画面布局";
|
setLayout(Layout_4x4);
|
}
|
|
void RealTimePlayer::onBtnPlayAllClicked()
|
{
|
qDebug() << "开始播放所有摄像头";
|
playAllCameras();
|
}
|
|
void RealTimePlayer::onBtnStopAllClicked()
|
{
|
qDebug() << "停止播放所有摄像头";
|
stopAllCameras();
|
}
|
|
void RealTimePlayer::onVideoWindowClicked(int windowIndex)
|
{
|
qDebug() << "点击了视频窗口:" << windowIndex;
|
|
// 检查窗口索引有效性
|
if (windowIndex < 0 || windowIndex >= m_videoWidgets.size()) {
|
return;
|
}
|
|
ClickableLabel* videoLabel = qobject_cast<ClickableLabel*>(m_videoWidgets[windowIndex]);
|
if (!videoLabel) {
|
return;
|
}
|
|
// 🎯 切换播放状态
|
QString currentText = videoLabel->text();
|
if (currentText.contains("点击开始播放")) {
|
// 开始播放
|
QString cameraId = QString("camera_%1").arg(windowIndex + 1);
|
startPlayCamera(cameraId, windowIndex);
|
} else if (currentText.contains("正在播放")) {
|
// 停止播放
|
stopPlayCamera(windowIndex);
|
|
// 从播放列表中移除
|
QString cameraId = QString("camera_%1").arg(windowIndex + 1);
|
m_playingCameras.removeOne(cameraId);
|
updateStatusBar();
|
}
|
}
|
|
|
// === 网络相关槽函数实现 ===
|
void RealTimePlayer::onConnected()
|
{
|
qDebug() << "TCP连接成功";
|
updateConnectionStatus(Connected);
|
|
// 连接成功后启动心跳
|
m_heartbeatTimer->start();
|
|
// 请求摄像头列表
|
requestCameraList();
|
}
|
|
void RealTimePlayer::onDisconnected()
|
{
|
qDebug() << "TCP连接断开";
|
updateConnectionStatus(Disconnected);
|
|
// 停止心跳
|
m_heartbeatTimer->stop();
|
m_heartbeatTimeoutTimer->stop();
|
|
// 清空接收缓冲区
|
m_receiveBuffer.clear();
|
|
// 停止所有播放
|
stopAllCameras();
|
}
|
|
void RealTimePlayer::onSocketError(QAbstractSocket::SocketError error)
|
{
|
QString errorString = m_tcpSocket->errorString();
|
qWarning() << "TCP连接错误:" << error << errorString;
|
|
updateConnectionStatus(Disconnected);
|
|
// 可以在这里添加自动重连逻辑
|
}
|
|
void RealTimePlayer::onDataReceived()
|
{
|
// 读取所有可用数据并追加到缓冲区
|
m_receiveBuffer.append(m_tcpSocket->readAll());
|
|
// 处理接收到的数据
|
processReceivedData();
|
}
|
|
|
// === 定时器相关槽函数实现 ===
|
void RealTimePlayer::updateCurrentTime()
|
{
|
// 更新当前时间显示
|
QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
|
ui->labelCurrentTime->setText(QString("当前时间: %1").arg(currentTime));
|
}
|
|
void RealTimePlayer::sendHeartbeat()
|
{
|
if (m_connectionStatus != Connected) {
|
return;
|
}
|
|
// 这里暂时发送一个简单的心跳字符串
|
// 后续会替换为二进制协议
|
QString heartbeatMsg = "HEARTBEAT";
|
m_tcpSocket->write(heartbeatMsg.toUtf8());
|
|
// 启动心跳超时检测
|
m_heartbeatTimeoutTimer->start();
|
|
qDebug() << "发送心跳包";
|
}
|
|
void RealTimePlayer::onHeartbeatTimeout()
|
{
|
qWarning() << "心跳超时";
|
m_heartbeatRetryCount++;
|
|
if (m_heartbeatRetryCount >= MAX_HEARTBEAT_RETRY) {
|
qCritical() << "心跳重试次数超限,断开连接";
|
disconnectFromServer();
|
m_heartbeatRetryCount = 0;
|
|
// 可以在这里触发重连
|
} else {
|
qDebug() << "心跳重试,次数:" << m_heartbeatRetryCount;
|
// 立即发送心跳重试
|
sendHeartbeat();
|
}
|
}
|
|
|
// === 界面相关函数实现 ===
|
void RealTimePlayer::setLayout(LayoutType layoutType)
|
{
|
// 如果布局类型相同,直接返回
|
if (m_currentLayout == layoutType) {
|
return;
|
}
|
|
// 清空当前布局
|
clearVideoLayout();
|
|
// 记录新的布局类型
|
m_currentLayout = layoutType;
|
|
// 计算网格行列数
|
int gridSize = 0;
|
switch (layoutType) {
|
case Layout_1x1: gridSize = 1; break;
|
case Layout_2x2: gridSize = 2; break;
|
case Layout_3x3: gridSize = 3; break;
|
case Layout_4x4: gridSize = 4; break;
|
}
|
|
// 创建视频显示组件
|
int totalWindows = gridSize * gridSize;
|
for (int i = 0; i < totalWindows; i++) {
|
// 创建可点击的视频标签
|
ClickableLabel* videoLabel = new ClickableLabel();
|
//videoLabel->setStyleSheet("QLabel { border: 1px solid gray; background-color: black; color: white; }");
|
videoLabel->setStyleSheet(
|
"QLabel {"
|
" border: 2px solid #555555;"
|
" background-color: #1e1e1e;"
|
" color: #ffffff;"
|
" border-radius: 5px;"
|
" font-size: 12px;"
|
"}"
|
"QLabel:hover {"
|
" border-color: #2196F3;"
|
" background-color: #2e2e2e;"
|
"}"
|
);
|
|
videoLabel->setAlignment(Qt::AlignCenter);
|
videoLabel->setText(QString("视频窗口 %1\n[点击开始播放]").arg(i + 1));
|
videoLabel->setMinimumSize(160, 120);
|
|
// 连接点击信号
|
connect(videoLabel, &ClickableLabel::clicked,
|
this, [this, i](){ onVideoWindowClicked(i); });
|
|
// 计算在网格中的位置
|
int row = i / gridSize;
|
int col = i % gridSize;
|
|
// 添加到网格布局中
|
m_videoGridLayout->addWidget(videoLabel, row, col);
|
|
// 添加到组件列表
|
m_videoWidgets.append(videoLabel);
|
}
|
|
// 更新状态栏
|
updateStatusBar();
|
|
qDebug() << "设置视频布局:" << totalWindows << "画面";
|
}
|
|
void RealTimePlayer::clearVideoLayout()
|
{
|
// 清理所有视频组件
|
for (QWidget* widget : m_videoWidgets) {
|
m_videoGridLayout->removeWidget(widget);
|
widget->deleteLater();
|
}
|
m_videoWidgets.clear();
|
|
// 清空播放列表
|
m_playingCameras.clear();
|
}
|
|
void RealTimePlayer::updateStatusBar()
|
{
|
// 更新播放路数显示
|
int totalWindows = m_videoWidgets.size();
|
int playingCount = m_playingCameras.size();
|
ui->labelPlayCount->setText(QString("播放路数: %1/%2").arg(playingCount).arg(totalWindows));
|
}
|
|
void RealTimePlayer::updateConnectionStatus(ConnectionStatus status)
|
{
|
m_connectionStatus = status;
|
|
QString statusText;
|
QString statusStyle;
|
|
switch (status) {
|
case Disconnected:
|
statusText = "连接状态: 未连接";
|
statusStyle = "color: red;";
|
ui->labelNetworkDelay->setText("网络延迟: --ms");
|
break;
|
case Connecting:
|
statusText = "连接状态: 连接中...";
|
statusStyle = "color: orange;";
|
break;
|
case Connected:
|
statusText = "连接状态: 已连接";
|
statusStyle = "color: green;";
|
break;
|
case Reconnecting:
|
statusText = "连接状态: 重连中...";
|
statusStyle = "color: orange;";
|
break;
|
}
|
|
ui->labelConnectionStatus->setText(statusText);
|
ui->labelConnectionStatus->setStyleSheet(statusStyle);
|
}
|
|
|
// === 网络相关函数实现 ===
|
void RealTimePlayer::connectToServer()
|
{
|
if (m_connectionStatus == Connected || m_connectionStatus == Connecting) {
|
qDebug() << "已经连接或正在连接中";
|
return;
|
}
|
|
qDebug() << "开始连接服务器:" << m_serverHost << ":" << m_serverPort;
|
updateConnectionStatus(Connecting);
|
|
// 连接到服务器
|
m_tcpSocket->connectToHost(m_serverHost, m_serverPort);
|
}
|
|
void RealTimePlayer::disconnectFromServer()
|
{
|
if (m_connectionStatus == Disconnected) {
|
return;
|
}
|
|
qDebug() << "断开服务器连接";
|
|
// 停止心跳
|
m_heartbeatTimer->stop();
|
m_heartbeatTimeoutTimer->stop();
|
|
// 断开连接
|
m_tcpSocket->disconnectFromHost();
|
|
updateConnectionStatus(Disconnected);
|
}
|
|
void RealTimePlayer::requestCameraList()
|
{
|
if (m_connectionStatus != Connected) {
|
qWarning() << "未连接到服务器,无法请求摄像头列表";
|
return;
|
}
|
|
// 这里暂时发送一个简单的请求字符串
|
// 后续会替换为二进制协议
|
QString request = "GET_CAMERA_LIST";
|
m_tcpSocket->write(request.toUtf8());
|
|
qDebug() << "请求摄像头列表";
|
}
|
|
void RealTimePlayer::processReceivedData()
|
{
|
// 这里是处理接收到数据的地方
|
// 目前简单处理,后续会实现完整的协议解析
|
|
if (m_receiveBuffer.contains("HEARTBEAT_RESPONSE")) {
|
// 收到心跳响应,停止超时定时器
|
m_heartbeatTimeoutTimer->stop();
|
m_heartbeatRetryCount = 0;
|
|
// 更新网络延迟显示(这里是模拟数据)
|
ui->labelNetworkDelay->setText("网络延迟: 25ms");
|
|
qDebug() << "收到心跳响应";
|
|
// 移除已处理的数据
|
m_receiveBuffer = m_receiveBuffer.mid(m_receiveBuffer.indexOf("HEARTBEAT_RESPONSE") + 18);
|
}
|
|
// 在这里ke添加其他协议数据的处理
|
}
|
|
|
// === 视频相关函数实现 ===
|
void RealTimePlayer::startPlayCamera(const QString& cameraId, int windowIndex)
|
{
|
// 检查窗口索引有效性
|
if (windowIndex < 0 || windowIndex >= m_videoWidgets.size()) {
|
qWarning() << "无效的窗口索引:" << windowIndex;
|
return;
|
}
|
|
// 这里暂时只是更新显示文本,后续会实现真正的视频播放
|
ClickableLabel* videoLabel = qobject_cast<ClickableLabel*>(m_videoWidgets[windowIndex]);
|
if (videoLabel) {
|
videoLabel->setText(QString("正在播放\n摄像头: %1\n[点击停止]").arg(cameraId));
|
videoLabel->setStyleSheet(
|
"QLabel {"
|
" border: 2px solid #4CAF50;"
|
" background-color: #1b5e20;"
|
" color: white;"
|
" border-radius: 5px;"
|
" font-size: 12px;"
|
"}"
|
"QLabel:hover {"
|
" border-color: #66BB6A;"
|
" background-color: #2e7d32;"
|
"}"
|
);
|
}
|
|
// 添加到播放列表
|
if (!m_playingCameras.contains(cameraId)) {
|
m_playingCameras.append(cameraId);
|
}
|
|
// 更新状态栏
|
updateStatusBar();
|
qDebug() << "开始播放摄像头:" << cameraId << "窗口:" << windowIndex;
|
}
|
|
void RealTimePlayer::stopPlayCamera(int windowIndex)
|
{
|
// 检查窗口索引有效性
|
if (windowIndex < 0 || windowIndex >= m_videoWidgets.size()) {
|
qWarning() << "无效的窗口索引:" << windowIndex;
|
return;
|
}
|
|
// 更新显示
|
ClickableLabel* videoLabel = qobject_cast<ClickableLabel*>(m_videoWidgets[windowIndex]);
|
if (videoLabel) {
|
videoLabel->setText(QString("视频窗口 %1\n[点击开始播放]").arg(windowIndex + 1));
|
videoLabel->setStyleSheet(
|
"QLabel {"
|
" border: 2px solid #555555;"
|
" background-color: #1e1e1e;"
|
" color: #ffffff;"
|
" border-radius: 5px;"
|
" font-size: 12px;"
|
"}"
|
"QLabel:hover {"
|
" border-color: #2196F3;"
|
" background-color: #2e2e2e;"
|
"}"
|
);
|
}
|
|
updateStatusBar();
|
qDebug() << "停止播放窗口:" << windowIndex;
|
}
|
|
void RealTimePlayer::playAllCameras()
|
{
|
// 这里是模拟播放所有摄像头的功能
|
// 实际实现需要根据摄像头列表来播放
|
|
for (int i = 0; i < m_videoWidgets.size() && i < 4; i++) { // 暂时最多播放4个
|
QString cameraId = QString("camera_%1").arg(i + 1);
|
startPlayCamera(cameraId, i);
|
}
|
|
qDebug() << "开始播放所有摄像头";
|
}
|
|
void RealTimePlayer::stopAllCameras()
|
{
|
// 停止所有窗口的播放
|
for (int i = 0; i < m_videoWidgets.size(); i++) {
|
stopPlayCamera(i);
|
}
|
|
// 清空播放列表
|
m_playingCameras.clear();
|
|
// 更新状态栏
|
updateStatusBar();
|
|
qDebug() << "停止所有摄像头播放";
|
}
|