Bläddra i källkod

修复: 鼠标绘制

zhuizhu 9 månader sedan
förälder
incheckning
c6f20ab6d8

+ 29 - 3
AvRecorder/capturer/video/DxgiCapturer.cpp

@@ -116,7 +116,9 @@ void DxgiCapturerPrivate::Close() {
     Free(_hContext, [this] { _hContext->Release(); });
 }
 
-AVFrame* DxgiCapturerPrivate::GetFrame() {
+AVFrame* DxgiCapturerPrivate::GetFrame(
+    bool shouldDrawCursor, int left, int top, int right, int bottom)
+{
     if (!_bInit) return nullptr;
     _isCaptureSuccess = false;
     IDXGIResource* hDesktopResource = nullptr;
@@ -164,6 +166,10 @@ AVFrame* DxgiCapturerPrivate::GetFrame() {
     _isCaptureSuccess = true;
     HDC hdc = nullptr;
     _hStagingSurf->GetDC(FALSE, &hdc);
+    // 合成鼠标指针
+    if (hdc && shouldDrawCursor) {
+        drawCursor(hdc, left, top, right, bottom);
+    }
     // 释放 DC 并转换为 AVFrame
     if (_isCaptureSuccess) {
         _isCaptureSuccess = false;
@@ -192,6 +198,25 @@ AVFrame* DxgiCapturerPrivate::GetFrame() {
     return nullptr;
 }
 
+void DxgiCapturerPrivate::drawCursor(HDC hdc, int left, int top, int right, int bottom)
+{
+    CURSORINFO ci;
+    ci.cbSize = sizeof(CURSORINFO);
+    __CheckNo(GetCursorInfo(&ci));
+    int cursorX = ci.ptScreenPos.x;
+    int cursorY = ci.ptScreenPos.y;
+
+    if (cursorX > right || cursorX < left || cursorY > bottom || cursorY < top) {
+        return; // 超出显示范围
+    }
+
+    if (ci.flags == CURSOR_SHOWING) {
+        // 将光标画到屏幕所在位置
+        int x = cursorX - left;
+        int y = cursorY - top;
+        DrawIconEx(hdc, x, y, ci.hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);
+    }
+}
 // --- DxgiCapturer 实现 ---
 DxgiCapturer::DxgiCapturer() : d(new DxgiCapturerPrivate) {}
 DxgiCapturer::~DxgiCapturer() { close(); delete d; }
@@ -207,6 +232,8 @@ bool DxgiCapturer::open(const CaptureTarget& target, int width, int height) {
     m_top = monitorInfo.rect.top;
     m_width = monitorInfo.rect.right - monitorInfo.rect.left;
     m_height = monitorInfo.rect.bottom - monitorInfo.rect.top;
+    m_right = monitorInfo.rect.right;
+    m_bottom = monitorInfo.rect.bottom;
     return d->Open(m_left, m_top, m_width, m_height);
 #else
     return false;
@@ -221,11 +248,10 @@ void DxgiCapturer::close() {
 
 AVFrame* DxgiCapturer::getFrame() {
 #ifdef PLATFORM_WINDOWS
-    return d->GetFrame();
+    return d->GetFrame(m_drawCursor, m_left, m_top, m_right, m_bottom);
 #else
     return nullptr;
 #endif
 }
-
 } // namespace video
 } // namespace avrecorder 

+ 4 - 1
AvRecorder/capturer/video/DxgiCapturer.h

@@ -17,7 +17,7 @@ public:
     ~DxgiCapturerPrivate() = default;
     bool Open(int left, int top, int width, int height);
     void Close();
-    AVFrame* GetFrame();
+    AVFrame* GetFrame(bool drawCursor, int left, int top, int right, int bottom);
     bool _bInit = false;
     bool _isCaptureSuccess = false;
     ID3D11Device* _hDevice = nullptr;
@@ -32,6 +32,8 @@ public:
     BufferFiller _xrgbBuffers;
     BufferFiller _nv12Buffers;
     D3dConverter _rgbToNv12;
+private:
+    void drawCursor(HDC hdc, int left, int top, int right, int bottom);
 };
 
 class DxgiCapturer : public IVideoCapturer {
@@ -46,6 +48,7 @@ private:
     DxgiCapturerPrivate* d;
     bool m_drawCursor = true;
     int m_left = 0, m_top = 0, m_width = 0, m_height = 0;
+    int m_right = 0, m_bottom = 0;
 };
 
 } // namespace video

+ 18 - 3
AvRecorder/capturer/video/GdiCapturer.cpp

@@ -48,13 +48,28 @@ AVFrame* GdiCapturer::getFrame() {
     for (int row = 0; row < m_height; ++row) {
         GetDIBits(_dstHdc, _bitmap, m_height - 1 - row, 1, _frame->data[0] + row * linesize, &_bitmapInfo, DIB_RGB_COLORS);
     }
-    // 可选:绘制鼠标
-    // ...
+    if (m_drawCursor) {
+        drawCursor(_dstHdc);
+    }
     return _frame;
 #else
     return nullptr;
 #endif
 }
 
+void GdiCapturer::drawCursor(HDC hdc)
+{
+    CURSORINFO ci = {sizeof(CURSORINFO)};
+    if (!GetCursorInfo(&ci))
+        return;
+    if (ci.flags != CURSOR_SHOWING)
+        return;
+
+    RECT rect;
+    GetWindowRect(m_hwnd, &rect);
+    POINT pos = {ci.ptScreenPos.x - rect.left, ci.ptScreenPos.y - rect.top};
+
+    DrawIconEx(hdc, pos.x, pos.y, ci.hCursor, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT);
+}
 } // namespace video
-} // namespace avrecorder 
+} // namespace avrecorder

+ 3 - 1
AvRecorder/capturer/video/GdiCapturer.h

@@ -16,6 +16,8 @@ public:
     AVFrame* getFrame() override;
     void setDrawCursor(bool enable) override { m_drawCursor = enable; }
 private:
+    void drawCursor(HDC hdc);
+
     HDC _srcHdc = nullptr;
     HDC _dstHdc = nullptr;
     HBITMAP _bitmap = nullptr;
@@ -30,4 +32,4 @@ private:
 } // namespace video
 } // namespace avrecorder
 
-#endif // GDICAPTURER_H 
+#endif // GDICAPTURER_H 

+ 4 - 4
AvRecorder/ui/audio_render.cpp

@@ -41,10 +41,10 @@ RenderArea::RenderArea(QWidget* parent)
     setBackgroundRole(QPalette::Base);
     setAutoFillBackground(true);
 
-    setMinimumHeight(10);
-    setMaximumHeight(30);
-    setMinimumWidth(100);
-    setMaximumWidth(200);
+    // setMinimumHeight(10);
+    // setMaximumHeight(30);
+    // setMinimumWidth(100);
+    // setMaximumWidth(200);
 }
 
 void RenderArea::paintEvent(QPaintEvent* /* event */)

+ 34 - 18
AvRecorder/ui/av_recorder.cpp

@@ -31,13 +31,13 @@ AvRecorder::AvRecorder(QWidget* parent)
     m_captureComboBox = new QComboBox;
     m_updateListBtn = new QPushButton("刷新窗口列表");
     QGroupBox* captureGroup = new QGroupBox("捕获源");
-    QVBoxLayout* captureLayout = new QVBoxLayout;
     QHBoxLayout* captureRow = new QHBoxLayout;
     captureRow->addWidget(m_captureComboBox);
     captureRow->addWidget(m_updateListBtn);
-    captureLayout->addLayout(captureRow);
-    captureGroup->setLayout(captureLayout);
-    captureGroup->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
+    captureGroup->setLayout(captureRow);
+
+    // m_captureComboBox->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
+    m_captureComboBox->setMinimumWidth(20);
 
     // 4. 音频区分组
     QGroupBox* audioGroup = new QGroupBox("音频");
@@ -45,7 +45,7 @@ AvRecorder::AvRecorder(QWidget* parent)
     audioLayout->addWidget(m_microphoneWidget);
     audioLayout->addWidget(m_speakerWidget);
     audioGroup->setLayout(audioLayout);
-    audioGroup->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
+    //audioGroup->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
 
     // 5. 操作区
     m_isDrawCursorBox = new QCheckBox("绘制鼠标指针");
@@ -69,7 +69,7 @@ AvRecorder::AvRecorder(QWidget* parent)
     actionLayout->addWidget(m_recordBtn);
     actionLayout->addWidget(m_liveBtn);
     actionGroup->setLayout(actionLayout);
-    actionGroup->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
+    //actionGroup->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
 
     // 6. 设置区
     QHBoxLayout* utilLayout = new QHBoxLayout;
@@ -96,7 +96,7 @@ AvRecorder::AvRecorder(QWidget* parent)
     initStatusBarUi();
 
     // 让视频区尽量大
-    m_glWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    // m_glWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
 
     // 11. 总体布局
     QVBoxLayout* mainLayout = new QVBoxLayout(this);
@@ -316,29 +316,45 @@ void AvRecorder::stopCapture()
     m_videoRecorder.Close();
     m_audioRecorder.Close();
 }
-
+void AvRecorder::renderFrame()
+{
+    auto frame = m_videoRecorder.GetRenderFrame();
+    m_glWidget->Render(frame);
+}
 void AvRecorder::startPreview()
 {
     m_glWidget->Open(m_settingsParam.videoParam.width, m_settingsParam.videoParam.height);
 
     // 视频需要做到和帧率一样的渲染速度,QTimer 达不到要求
     // 需要自己封装一个计时器
-    m_videoRenderTimer.Start(m_settingsParam.videoParam.fps, [this] {
-        if (windowState() == Qt::WindowMinimized) {
-            return;
-        }
-        // 视频
-        auto frame = m_videoRecorder.GetRenderFrame();
-        m_glWidget->Render(frame);
-    });
-
+    // m_videoRenderTimer.Start(m_settingsParam.videoParam.fps, [this] {
+    //     if (windowState() == Qt::WindowMinimized) {
+    //         return;
+    //     }
+    //     QMetaObject::invokeMethod(this, "renderFrame", Qt::QueuedConnection);
+    //     // // 视频
+    //     // auto frame = m_videoRecorder.GetRenderFrame();
+    //     // m_glWidget->Render(frame);
+    // });
+
+    if (!m_videoRenderTimer) {
+        m_videoRenderTimer = new QTimer(this);
+        connect(m_videoRenderTimer, &QTimer::timeout, this, [this] {
+            if (windowState() == Qt::WindowMinimized)
+                return;
+            renderFrame();
+        });
+    }
+    m_videoRenderTimer->start(1000 / m_settingsParam.videoParam.fps);
     // 刷新率设置为 25
     m_otherTimer.start(40);
 }
 
 void AvRecorder::stopPreview()
 {
-    m_videoRenderTimer.Stop();
+    if (m_videoRenderTimer) {
+        m_videoRenderTimer->stop();
+    }
     m_otherTimer.stop();
 }
 

+ 5 - 5
AvRecorder/ui/av_recorder.h

@@ -8,13 +8,14 @@
 #include <QWidget>
 
 #include "audio_widget.h"
+#include "avrecorder/capturer/video/VideoCaptureManager.h"
 #include "qcombobox.h"
+#include "qobjectdefs.h"
 #include "qstatusbar.h"
 #include "recorder/audio_recorder.h"
 #include "recorder/video_recorder.h"
 #include "ui/opengl_video_widget.h"
 #include "ui/settings_page.h"
-#include "avrecorder/capturer/video/VideoCaptureManager.h"
 using namespace avrecorder::video;
 
 class AvRecorder : public QWidget
@@ -29,9 +30,8 @@ public:
     QWidget* statusBar() const { return m_statusBar; }
 
     void setSettings(const SettingsPage::Param& param);
-
-signals:
-    void renderFrame(AVFrame* frame);
+public slots:
+    void renderFrame();
 
 private:
     SettingsPage::Param m_settingsParam;
@@ -62,7 +62,7 @@ private:
     QPushButton* m_settingsBtn = nullptr;
     QCheckBox* m_isDrawCursorBox = nullptr;
     QCheckBox* m_syncRecordBox = nullptr; // 添加同步录像的复选框
-    Timer m_videoRenderTimer;
+    QTimer* m_videoRenderTimer = nullptr;
     QTimer m_otherTimer;
     QComboBox* m_captureComboBox = nullptr;
 

+ 1 - 1
network/networkaccessmanager.h

@@ -57,7 +57,7 @@ public:
     using Ptr = QSharedPointer<RequestClient>;
 
     explicit RequestClient(const RequestClientOptions& options = RequestClientOptions());
-    virtual ~RequestClient() = default;
+    ~RequestClient() {};
 
     // 禁用拷贝和赋值
     RequestClient(const RequestClient&) = delete;