Selaa lähdekoodia

添加: 开播自动链接

zhuizhu 8 kuukautta sitten
vanhempi
säilyke
260fc1244d

+ 8 - 0
AvPlayer2/PlayWidget.cpp

@@ -30,6 +30,14 @@ void PlayWidget::startToPlay(const QString& url)
     }
 }
 
+bool PlayWidget::isPlaying() const
+{
+    if (m_playerController) {
+        return m_playerController->isPlaying();
+    }
+    return false;
+}
+
 void PlayWidget::setupUi()
 {
     m_videoWidget = std::make_unique<OpenGLVideoWidget>(this);

+ 2 - 0
AvPlayer2/PlayWidget.h

@@ -20,6 +20,8 @@ public:
 
     void startToPlay(const QString& url);
 
+    bool isPlaying() const;
+
 private:
     std::unique_ptr<OpenGLVideoWidget> m_videoWidget;
     QSlider* m_sliderProgress;

+ 2 - 3
AvRecorder/ui/av_recorder.cpp

@@ -9,14 +9,13 @@ using namespace avrecorder::video;
 AvRecorder::AvRecorder(QWidget* parent)
     : QWidget(parent)
 {
-    setWindowTitle("Recorder");
     m_settingsParam.audioParam.bitRate = 160'000;
     m_settingsParam.videoParam.bitRate = 8'000'000;
     m_settingsParam.videoParam.fps = 30;
     m_settingsParam.videoParam.name = Encoder<MediaType::VIDEO>::GetUsableEncoders().front();
     m_settingsParam.outputDir = ".";
-    m_settingsParam.liveUrl = "rtmp://192.168.3.76:1935/stream/V1";
-    m_settingsParam.liveName = "stream";
+    // m_settingsParam.liveUrl = "rtmp://192.168.3.76:1935/stream/V1";
+    // m_settingsParam.liveName = "stream";
 
     // 1. 视频预览区
     m_glWidget = new OpenGLVideoWidget(this);

+ 128 - 16
MainPanel.cpp

@@ -2,15 +2,24 @@
 #include <QSplitter>
 #include <QVBoxLayout>
 #include <QListWidget>
-#include "widgets/userprofilewidget.h"
-#include "widgets/chatView/chatwindow.h"
+
+#include <util/jsonmapper.h>
+
+#include <QDebug>
+
+#include <qtpromise/qpromise.h>
+#include <qtpromise/qpromisefuture.h>
+#include <qtpromise/qpromisehelpers.h>
+
 #include "widgets/bubbletip.h"
+#include "widgets/chatView/chatwindow.h"
+#include "widgets/maskoverlay.h"
+#include "widgets/userprofilewidget.h"
+
+#include "AvPlayer2/PlayWidget.h"
 #include "api/roomapi.h"
-#include "api/userapi.h"
 #include "appevent.h"
 #include "ui/av_recorder.h"
-#include "AvPlayer2/PlayWidget.h"
-#include <QDebug>
 
 MainPanel::MainPanel(QWidget *parent)
     : QWidget(parent)
@@ -19,7 +28,8 @@ MainPanel::MainPanel(QWidget *parent)
 {
     // setupUI
     userProfile = new UserProfileWidget(this);
-    chatView = new ChatWindow();
+    webSocketClient = new WebSocketClient(this);
+    chatView = new ChatWindow(webSocketClient);
     chatView->setMinimumWidth(400);
 
     QWidget *rightWidget = new QWidget;
@@ -28,10 +38,10 @@ MainPanel::MainPanel(QWidget *parent)
     vbox->addWidget(userProfile, 0);
     vbox->addWidget(chatView, 1);
 
-    roomListWidget = new QListWidget;
+    m_roomListWidget = new QListWidget;
     splitter = new QSplitter(Qt::Horizontal, this);
     playerContainer = new QWidget(this);
-    splitter->addWidget(roomListWidget);
+    splitter->addWidget(m_roomListWidget);
     splitter->addWidget(playerContainer);
     splitter->addWidget(rightWidget);
     splitter->setStretchFactor(0, 10);
@@ -49,6 +59,37 @@ MainPanel::MainPanel(QWidget *parent)
             userProfile->setStatus(connected ? "在线" : "离线");
         }
     });
+    connect(m_roomListWidget, &QListWidget::itemDoubleClicked, this, &MainPanel::roomItemChanged);
+    connect(userProfile, &UserProfileWidget::logoutClicked, this, &MainPanel::logoutClicked);
+    connect(webSocketClient, &WebSocketClient::liveStatus, this, [this](const QString &msg) {
+        // 这里可以处理 liveStatus 相关逻辑
+        QJsonParseError err;
+        QJsonDocument doc = QJsonDocument::fromJson(msg.toUtf8(), &err);
+        if (err.error != QJsonParseError::NoError || !doc.isObject()) {
+            qDebug() << "[MainPanel] liveStatus: 解析失败" << err.errorString();
+            return;
+        }
+        QJsonObject obj = doc.object();
+        int liveStatus = obj.value("liveStatus").toInt(0); // 默认-1
+        if (liveStatus == 1) {
+            qDebug() << "[MainPanel] liveStatus: 直播中";
+            if (chatView) {
+                const QString id = webSocketClient->roomId();
+                if (PlayWidget *playWidget = qobject_cast<PlayWidget *>(playerWidget)) {
+                    if (!playWidget->isPlaying()) {
+                        playWidget->startToPlay("rtmp://106.55.186.74:1935/stream/V1/" + id);
+                    }
+                }
+            }
+
+            // 你的处理逻辑
+        } else if (liveStatus == 2) {
+            qDebug() << "[MainPanel] liveStatus: 未开播";
+            // 你的处理逻辑
+        } else {
+            qDebug() << "[MainPanel] liveStatus: 未知状态" << liveStatus;
+        }
+    });
 }
 
 MainPanel::~MainPanel()
@@ -58,7 +99,43 @@ MainPanel::~MainPanel()
         userProfile = nullptr;
     }
 }
+void MainPanel::setRole(const QStringList &roleList)
+{
+    QWidget *newPlayer = nullptr;
+    if (roleList.contains("role.admin")) {
+        newPlayer = new AvRecorder(this);
+    } else {
+        newPlayer = new PlayWidget(this);
+    }
+    setPlayerWidget(newPlayer);
+
+    // 如果是管理也就是教师 直接过滤当前教师的选项
+    if (AppEvent::instance()->hasRole("role.admin")) {
+        // roomInfo.ownerId = AppEvent::instance()->userId();
+        m_roomListWidget->hide();
+
+    } else {
+        m_roomListWidget->show();
+    }
+
+    // 设置初始化信息
+    const QString &name = AppEvent::instance()->userName();
+    userProfile->setUsername(name);
+}
 
+void MainPanel::setPushRoomId(const QString &id)
+{
+    // 推流配置
+    if (AvRecorder *avRecorder = qobject_cast<AvRecorder *>(playerWidget)) {
+        SettingsPage::Param param;
+        param.liveUrl = "rtmp://106.55.186.74:1935/stream/V1";
+        param.liveName = id.toStdString();
+        avRecorder->setSettings(param);
+    }
+
+    // 重新进入房间
+    chatView->initWebsocket(id);
+}
 void MainPanel::setPlayerWidget(QWidget *newPlayer)
 {
     if (playerWidget) {
@@ -82,13 +159,48 @@ void MainPanel::setPlayerWidget(QWidget *newPlayer)
     vbox->addWidget(playerWidget);
 }
 
-void MainPanel::setRole(const QStringList &roleList)
+void MainPanel::roomItemChanged(QListWidgetItem *item)
 {
-    QWidget *newPlayer = nullptr;
-    if (roleList.contains("role.admin")) {
-        newPlayer = new AvRecorder(this);
-    } else {
-        newPlayer = new PlayWidget(this);
+    if (item) {
+        const QString id = item->data(Qt::UserRole + 100).value<QString>();
+
+        if (!playerWidget) {
+            return;
+        }
+
+        // // 推流配置
+        // if (AvRecorder *avRecorder = qobject_cast<AvRecorder *>(playerWidget)) {
+        //     SettingsPage::Param param;
+        //     param.liveUrl = "rtmp://106.55.186.74:1935/stream/V1";
+        //     param.liveName = id.toStdString();
+        //     avRecorder->setSettings(param);
+        // }
+        // 拉取视频流程
+        if (PlayWidget *playWidget = qobject_cast<PlayWidget *>(playerWidget)) {
+            MaskOverlay::instance()->show(nullptr, 0, MaskOverlay::ActiveWindow);
+
+            QFuture<HttpResponse> getRoomFuture = getRoomApi(id);
+            QtPromise::QPromise<HttpResponse> roomListPromise = QtPromise::resolve(getRoomFuture);
+
+            roomListPromise
+                .then([this, playWidget, id](const HttpResponse &response) {
+                    qDebug() << response.code << response.data << response.message;
+                    if (response.code != 0) {
+                        BubbleTip::showTip(this, response.message, BubbleTip::Top, 3000);
+                        return;
+                    }
+                    RoomInfo roomInfo = JsonMapper::formJsonEx<RoomInfo>(response.data.toObject());
+
+                    qDebug() << "roomInfo.liveStatus.has_value()"
+                             << roomInfo.liveStatus.has_value();
+
+                    int status = roomInfo.liveStatus.value_or(0);
+                    if (status == 1) {
+                        qDebug() << "open" << ("rtmp://106.55.186.74:1935/stream/V1/" + id);
+                        playWidget->startToPlay("rtmp://106.55.186.74:1935/stream/V1/" + id);
+                    }
+                })
+                .finally([]() { MaskOverlay::instance()->hide(); });
+        }
     }
-    setPlayerWidget(newPlayer);
-} 
+}

+ 19 - 4
MainPanel.h

@@ -1,10 +1,13 @@
 #pragma once
-#include <QWidget>
 #include <QStringList>
+#include <QWidget>
+#include "qobjectdefs.h"
 class QSplitter;
 class QListWidget;
 class UserProfileWidget;
 class ChatWindow;
+class QListWidgetItem;
+class WebSocketClient;
 
 class MainPanel : public QWidget
 {
@@ -12,13 +15,25 @@ class MainPanel : public QWidget
 public:
     explicit MainPanel(QWidget *parent = nullptr);
     ~MainPanel();
-    void setPlayerWidget(QWidget *newPlayer);
     void setRole(const QStringList &roleList);
 
+    void setPushRoomId(const QString &room);
+
+    QListWidget *roomListWidget() { return m_roomListWidget; }
+
+signals:
+    void logoutClicked();
+
+private:
+    void setPlayerWidget(QWidget *newPlayer);
+    void roomItemChanged(QListWidgetItem *item);
+
+private:
     QSplitter *splitter = nullptr;
     QWidget *playerContainer = nullptr;
     QWidget *playerWidget = nullptr;
     UserProfileWidget *userProfile = nullptr;
     ChatWindow *chatView = nullptr;
-    QListWidget *roomListWidget = nullptr;
-}; 
+    QListWidget *m_roomListWidget = nullptr;
+    WebSocketClient *webSocketClient = nullptr;
+};

+ 15 - 0
api/roomapi.cpp

@@ -49,10 +49,14 @@ void initRoomType()
 {
     QtJsonSerializer::registerTypes();
     QtJsonSerializer::JsonSerializer::registerOptionalConverters<QString>();
+    QtJsonSerializer::JsonSerializer::registerOptionalConverters<int>();
+    QtJsonSerializer::JsonSerializer::registerOptionalConverters<qint64>();
     QtJsonSerializer::JsonSerializer::registerListConverters<RoomInfo>();
     qRegisterMetaType<RoomInfo>("RoomInfo");
     qRegisterMetaType<RoomListData>("RoomListData");
     qRegisterMetaType<std::optional<QString>>("std::optional<QString>");
+    qRegisterMetaType<std::optional<int>>("std::optional<int>");
+    qRegisterMetaType<std::optional<qint64>>("std::optional<qint64>");
 }
 #endif
 
@@ -98,3 +102,14 @@ QFuture<HttpResponse> getRoomListApi(const RoomInfo &roomInfo, int page, int pag
 
     return TC::RequestClient::globalInstance()->postAsync("/room/list", doc);
 }
+
+QFuture<HttpResponse> getRoomApi(const QString &id)
+{
+    QJsonObject jsonData;
+    jsonData["id"] = id;
+    // 活驴
+    qDebug() << jsonData;
+    QJsonDocument doc(jsonData);
+
+    return TC::RequestClient::globalInstance()->postAsync("/room", doc);
+}

+ 10 - 4
api/roomapi.h

@@ -17,7 +17,8 @@ class RoomInfo
     Q_PROPERTY(std::optional<QString> name MEMBER name)
     Q_PROPERTY(std::optional<QString> description MEMBER description)
     Q_PROPERTY(std::optional<int> maxUsers MEMBER maxUsers)
-    Q_PROPERTY(std::optional<qint64> status MEMBER status)
+    Q_PROPERTY(std::optional<int> status MEMBER status)
+    Q_PROPERTY(std::optional<int> liveStatus MEMBER liveStatus)
     Q_PROPERTY(std::optional<qint64> createdAt MEMBER createdAt)
     Q_PROPERTY(std::optional<qint64> updatedAt MEMBER updatedAt)
     Q_PROPERTY(std::optional<QString> createdId MEMBER createdId)
@@ -29,7 +30,8 @@ public:
     std::optional<QString> name;        // 房间名
     std::optional<QString> description; // 房间的描述信息
     std::optional<int> maxUsers;        // 最大用户数
-    std::optional<qint64> status;       // 1: normal 2: ban | 状态 1 正常 2 禁用
+    std::optional<int> status;          // 1: normal 2: ban | 状态 1 正常 2 禁用
+    std::optional<int> liveStatus;      // 直播状态 状态 0 未使用 1 在线 2 不在线
     std::optional<qint64> createdAt;    // 创建日期
     std::optional<qint64> updatedAt;    // 修改日期
     std::optional<QString> createdId;   // 创建者
@@ -39,8 +41,9 @@ public:
     {
         return lhs.id == rhs.id && lhs.name == rhs.name && lhs.description == rhs.description
                && lhs.maxUsers == rhs.maxUsers && lhs.status == rhs.status
-               && lhs.createdAt == rhs.createdAt && lhs.updatedAt == rhs.updatedAt
-               && lhs.createdId == rhs.createdId && lhs.ownerId == rhs.ownerId;
+               && lhs.liveStatus == rhs.liveStatus && lhs.createdAt == rhs.createdAt
+               && lhs.updatedAt == rhs.updatedAt && lhs.createdId == rhs.createdId
+               && lhs.ownerId == rhs.ownerId;
     }
 };
 Q_DECLARE_METATYPE(RoomInfo)
@@ -69,4 +72,7 @@ Q_DECLARE_METATYPE(RoomListData)
 QFuture<HttpResponse> getRoomListApi(const RoomInfo &roomInfo = RoomInfo(),
                                      int page = 1,
                                      int pageSize = 100);
+
+QFuture<HttpResponse> getRoomApi(const QString &id);
+
 #endif // ROOMAPI_H

+ 17 - 52
mainwindow.cpp

@@ -84,45 +84,15 @@ void MainWindow::createMainWindow()
     stackedWidget->addWidget(mainWidget);
 
     // 退出处理
-    connect(mainWidget->userProfile, &UserProfileWidget::logoutClicked, this, [this]() {
+    connect(mainWidget, &MainPanel::logoutClicked, this, [this]() {
         AppEvent *appEvent = AppEvent::instance();
         appEvent->setJwtToken(QString()); // 清理token
         appEvent->setRefreshTime(0);      // 清理token刷新时间
         appEvent->setRoles({});           // 清理角色
         appEvent->setUserId({});          // 清理用户id
 
-        if (mainWidget->userProfile) {
-            stackedWidget->setCurrentWidget(loginWidget);
-        }
+        stackedWidget->setCurrentWidget(loginWidget);
     });
-
-    connect(mainWidget->roomListWidget,
-            &QListWidget::itemClicked,
-            this,
-            [this](QListWidgetItem *item) {
-                if (item) {
-                    const QString id = item->data(Qt::UserRole + 100).value<QString>();
-
-                    // 重新进入房间
-                    mainWidget->chatView->initWebsocket(id);
-                    if (!mainWidget->playerWidget) {
-                        return;
-                    }
-                    // 推流配置
-                    if (AvRecorder *avRecorder = qobject_cast<AvRecorder *>(
-                            mainWidget->playerWidget)) {
-                        SettingsPage::Param param;
-                        param.liveUrl = "rtmp://192.168.3.76:1935/stream/V1";
-                        param.liveName = id.toStdString();
-                        avRecorder->setSettings(param);
-                    }
-                    // 拉取视频流程
-                    if (PlayWidget *playWidget = qobject_cast<PlayWidget *>(
-                            mainWidget->playerWidget)) {
-                        playWidget->startToPlay("rtmp://192.168.3.76:1935/stream/V1/" + id);
-                    }
-                }
-            });
 }
 
 void MainWindow::onLoginSuccess(const QString &username, const QString &password)
@@ -197,16 +167,16 @@ void MainWindow::authLogin()
             AppEvent::instance()->setRoles(user.roleName);
             AppEvent::instance()->setUserId(user.id);
             AppEvent::instance()->setUserName(user.username);
-            createMainWindow();
 
-            checkRoom();
             // 创建主界面部件
-            mainWidget->userProfile->setUsername(user.username);
+            createMainWindow();
 
             mainWidget->setRole(user.roleName);
 
             stackedWidget->addWidget(mainWidget);
             stackedWidget->setCurrentWidget(mainWidget);
+
+            checkRoom();
         })
         .fail([this](const std::exception &e) {
             // 处理错误
@@ -225,14 +195,7 @@ void MainWindow::checkRoom()
 {
     // 创建两个异步操作的Promise
     RoomInfo roomInfo;
-    // 如果是管理也就是教师 直接过滤当前教师的选项
-    if (AppEvent::instance()->hasRole("role.admin")) {
-        roomInfo.ownerId = AppEvent::instance()->userId();
-        mainWidget->roomListWidget->hide();
 
-    } else {
-        mainWidget->roomListWidget->show();
-    }
     QFuture<HttpResponse> getRoomListFuture = getRoomListApi(roomInfo);
 
     // 使用fromQFuture将QFuture转换为QPromise
@@ -254,20 +217,22 @@ void MainWindow::checkRoom()
                 const RoomInfo room = RoomInfos.at(var);
                 if (room.name) {
                     QListWidgetItem *item = new QListWidgetItem(room.name.value_or(""),
-                                                                mainWidget->roomListWidget);
+                                                                mainWidget->roomListWidget());
                     item->setData(Qt::UserRole + 100, room.id.value_or(""));
-                    mainWidget->roomListWidget->addItem(item);
+                    mainWidget->roomListWidget()->addItem(item);
                 }
             }
             if (RoomInfos.size() > 0) {
-                mainWidget->chatView->initWebsocket(RoomInfos[0].id.value());
-
-                if (AvRecorder *avRecorder = qobject_cast<AvRecorder *>(mainWidget->playerWidget)) {
-                    SettingsPage::Param param;
-                    param.liveUrl = "rtmp://192.168.3.76:1935/stream/V1";
-                    param.liveName = RoomInfos[0].id.value_or("").toStdString();
-                    avRecorder->setSettings(param);
-                }
+                mainWidget->roomListWidget()->setCurrentRow(0);
+                mainWidget->setPushRoomId(RoomInfos[0].id.value_or(""));
+                // mainWidget->chatView->initWebsocket(RoomInfos[0].id.value());
+
+                // if (AvRecorder *avRecorder = qobject_cast<AvRecorder *>(mainWidget->playerWidget)) {
+                //     SettingsPage::Param param;
+                //     param.liveUrl = "rtmp://192.168.3.76:1935/stream/V1";
+                //     param.liveName = RoomInfos[0].id.value_or("").toStdString();
+                //     avRecorder->setSettings(param);
+                // }
             }
         })
         .fail([this](const std::exception &e) {

+ 1 - 1
network/networkaccessmanager.cpp

@@ -11,7 +11,7 @@
 #include <QtConcurrent>
 
 // 基础URL配置
-static QString base_url("http://127.0.0.1:8200");
+static QString base_url("http://106.55.186.74:8200");
 static const QString messageVal = "msg";
 
 static NetworkAccessManager* namInstance = nullptr;

+ 3 - 0
network/websocketclient.cpp

@@ -161,6 +161,9 @@ void WebSocketClient::onTextMessageReceived(const QString& message)
             chatMessage.type = MessageType::System;
         } else if (type == "private") {
             chatMessage.type = MessageType::Right;
+        } else if (type == "room_live_status") {
+            emit liveStatus(content);
+            return;
         }
 
         // 设置消息内容

+ 4 - 0
network/websocketclient.h

@@ -33,9 +33,13 @@ public:
     // 设置最大重连尝试次数
     void setMaxReconnectAttempts(int maxAttempts);
 
+    QString roomId() const { return m_roomId; }
+
 signals:
     // 收到新消息信号
     void messageReceived(const ChatMessage& message);
+    // 直播状态
+    void liveStatus(const QString& message);
 
     // 连接状态变化信号
     void connectionStateChanged(bool connected);

+ 3 - 2
widgets/chatView/chatwindow.cpp

@@ -3,9 +3,9 @@
 #include "appevent.h"
 #include "widgets/chatView/chat1/chatview.h"
 
-ChatWindow::ChatWindow(QWidget *parent)
+ChatWindow::ChatWindow(WebSocketClient *webSocketClient, QWidget *parent)
     : QWidget(parent)
-    , m_webSocketClient(new WebSocketClient(this))
+    , m_webSocketClient(webSocketClient)
 {
     setWindowTitle("气泡消息示例");
     resize(800, 600);
@@ -99,6 +99,7 @@ void ChatWindow::initWebsocket(const QString &roomId)
     }
 }
 
+
 void ChatWindow::onSendClicked()
 {
     QString text = m_inputEdit->text().trimmed();

+ 2 - 1
widgets/chatView/chatwindow.h

@@ -15,9 +15,10 @@ class ChatView;
 class ChatWindow : public QWidget
 {
 public:
-    ChatWindow(QWidget *parent = nullptr);
+    ChatWindow(WebSocketClient *webSocketClient, QWidget *parent = nullptr);
 
     void initWebsocket(const QString &roomId);
+
 private slots:
     void onSendClicked();