333 lines
12 KiB
C++
333 lines
12 KiB
C++
#pragma once
|
||
#include <imm.h>
|
||
#include <map>
|
||
#include "IOCPServer.h"
|
||
#include "..\..\client\CursorInfo.h"
|
||
#include "VideoDlg.h"
|
||
#include "ToolbarDlg.h"
|
||
#include "2015RemoteDlg.h"
|
||
|
||
extern "C"
|
||
{
|
||
#include "libavcodec\avcodec.h"
|
||
#include "libavutil\avutil.h"
|
||
#include "libyuv\libyuv.h"
|
||
}
|
||
|
||
#ifndef _WIN64
|
||
// https://github.com/Terodee/FFMpeg-windows-static-build/releases
|
||
#pragma comment(lib,"ffmpeg/libavcodec.lib")
|
||
#pragma comment(lib,"ffmpeg/libavutil.lib")
|
||
#pragma comment(lib,"ffmpeg/libswresample.lib")
|
||
|
||
#pragma comment(lib,"libyuv/libyuv.lib")
|
||
#else
|
||
#pragma comment(lib,"x264/libx264_x64.lib")
|
||
#pragma comment(lib,"libyuv/libyuv_x64.lib")
|
||
// https://github.com/ShiftMediaProject/FFmpeg
|
||
#pragma comment(lib,"ffmpeg/libavcodec_x64.lib")
|
||
#pragma comment(lib,"ffmpeg/libavutil_x64.lib")
|
||
#pragma comment(lib,"ffmpeg/libswresample_x64.lib")
|
||
#endif
|
||
|
||
#pragma comment(lib, "Mfplat.lib")
|
||
#pragma comment(lib, "Mfuuid.lib")
|
||
#pragma comment(lib, "Bcrypt.lib")
|
||
#pragma comment(lib, "Strmiids.lib")
|
||
|
||
// 文件接收消息(用于将工作线程的文件数据转发到主线程处理)
|
||
#define WM_RECVFILEV2_CHUNK (WM_USER + 0x200)
|
||
#define WM_RECVFILEV2_COMPLETE (WM_USER + 0x201)
|
||
|
||
// ScreenSpyDlg 系统菜单命令 ID
|
||
enum {
|
||
IDM_CONTROL = 0x1010,
|
||
IDM_FULLSCREEN,
|
||
IDM_SEND_CTRL_ALT_DEL,
|
||
IDM_TRACE_CURSOR, // 跟踪显示远程鼠标
|
||
IDM_BLOCK_INPUT, // 锁定远程计算机输入
|
||
IDM_SAVEDIB, // 保存图片
|
||
IDM_GET_CLIPBOARD, // 获取剪贴板
|
||
IDM_SET_CLIPBOARD, // 设置剪贴板
|
||
IDM_ADAPTIVE_SIZE,
|
||
IDM_SAVEAVI,
|
||
IDM_SAVEAVI_H264,
|
||
IDM_SWITCHSCREEN,
|
||
IDM_MULTITHREAD_COMPRESS,
|
||
IDM_FPS_10,
|
||
IDM_FPS_15,
|
||
IDM_FPS_20,
|
||
IDM_FPS_25,
|
||
IDM_FPS_30,
|
||
IDM_FPS_UNLIMITED,
|
||
IDM_ORIGINAL_SIZE,
|
||
IDM_SCREEN_1080P,
|
||
IDM_REMOTE_CURSOR,
|
||
IDM_SCROLL_DETECT_OFF, // 滚动检测:关闭(局域网)
|
||
IDM_SCROLL_DETECT_2, // 滚动检测:跨网推荐
|
||
IDM_SCROLL_DETECT_4, // 滚动检测:标准模式
|
||
IDM_SCROLL_DETECT_8, // 滚动检测:省CPU模式
|
||
IDM_QUALITY_OFF, // 关闭质量控制(使用原有算法)
|
||
IDM_ADAPTIVE_QUALITY, // 自适应质量
|
||
IDM_QUALITY_ULTRA, // 手动质量:Ultra
|
||
IDM_QUALITY_HIGH, // 手动质量:High
|
||
IDM_QUALITY_GOOD, // 手动质量:Good
|
||
IDM_QUALITY_MEDIUM, // 手动质量:Medium
|
||
IDM_QUALITY_LOW, // 手动质量:Low
|
||
IDM_QUALITY_MINIMAL, // 手动质量:Minimal
|
||
IDM_ENABLE_SSE2,
|
||
IDM_FAST_STRETCH, // 快速缩放模式(降低CPU占用)
|
||
IDM_CUSTOM_CURSOR, // 使用自定义光标
|
||
IDM_RESTORE_CONSOLE, // RDP会话归位
|
||
IDM_RESET_VIRTUAL_DESKTOP, // 重置虚拟桌面
|
||
IDM_AUDIO_TOGGLE, // 音频开关
|
||
};
|
||
|
||
// 状态信息窗口 - 全屏时显示帧率/速度/质量
|
||
class CStatusInfoWnd : public CWnd
|
||
{
|
||
public:
|
||
CStatusInfoWnd() : m_nOpacityLevel(0), m_bVisible(false), m_bDragging(false) {}
|
||
|
||
BOOL Create(CWnd* pParent);
|
||
void UpdateInfo(double fps, double kbps, const CString& quality);
|
||
void Show();
|
||
void Hide();
|
||
void SetOpacityLevel(int level);
|
||
void UpdatePosition(const RECT& rcMonitor);
|
||
void LoadSettings();
|
||
void SaveSettings();
|
||
|
||
bool IsVisible() const { return m_bVisible; }
|
||
bool IsParentInControlMode(); // 检查父窗口是否处于控制模式
|
||
|
||
protected:
|
||
afx_msg void OnPaint();
|
||
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
|
||
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
|
||
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
|
||
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
|
||
DECLARE_MESSAGE_MAP()
|
||
|
||
private:
|
||
CString m_strInfo;
|
||
int m_nOpacityLevel;
|
||
bool m_bVisible;
|
||
|
||
// 拖动支持
|
||
bool m_bDragging;
|
||
CPoint m_ptDragStart;
|
||
|
||
// 位置保存
|
||
double m_dOffsetXRatio = 0.5;
|
||
int m_nOffsetY = 50;
|
||
bool m_bHasCustomPosition = false;
|
||
};
|
||
|
||
// CScreenSpyDlg 对话框
|
||
|
||
class CScreenSpyDlg : public DialogBase
|
||
{
|
||
DECLARE_DYNAMIC(CScreenSpyDlg)
|
||
CToolbarDlg* m_pToolbar = nullptr;
|
||
CMy2015RemoteDlg* m_pParent = nullptr;
|
||
|
||
public:
|
||
CStatusInfoWnd* m_pStatusInfoWnd = nullptr;
|
||
// MaxFPS=20, ScrollDetectInterval=2, Reserved={}, Capabilities=0
|
||
// MaxFPS=20, CompressThread=0, ScreenStrategy=0, ScreenWidth=0, ScreenHeight=0,
|
||
// FullScreen=0, RemoteCursor=0, ScrollDetectInterval=2, QualityLevel=-1,
|
||
// CpuSpeedup=0, ScreenType=0, AudioEnabled=0, Reserved={}, Capabilities=0
|
||
ScreenSettings m_Settings = { 20, 0, 0, 0, 0, 0, 0, 2, -1, 0, 0, 0, {}, 0 };
|
||
|
||
public:
|
||
// 快速缩放模式(全局配置,所有实例共享)
|
||
static int s_nFastStretch; // -1=未初始化, 0=关闭, 1=开启
|
||
static bool GetFastStretchMode();
|
||
static void SetFastStretchMode(bool bFast);
|
||
|
||
CScreenSpyDlg(CMy2015RemoteDlg* Parent, Server* IOCPServer=NULL, CONTEXT_OBJECT *ContextObject=NULL);
|
||
virtual ~CScreenSpyDlg();
|
||
virtual BOOL ShouldReconnect()
|
||
{
|
||
return TRUE;
|
||
}
|
||
|
||
VOID SendNext(void);
|
||
VOID OnReceiveComplete();
|
||
HDC m_hFullDC;
|
||
HDC m_hFullMemDC;
|
||
HBITMAP m_BitmapHandle;
|
||
PVOID m_BitmapData_Full;
|
||
LPBITMAPINFO m_BitmapInfor_Full;
|
||
VOID DrawFirstScreen(void);
|
||
VOID DrawNextScreenDiff(bool keyFrame);
|
||
VOID DrawScrollFrame(void);
|
||
BOOL m_bIsFirst;
|
||
bool m_bQualitySwitch = false; // 质量切换中,不显示"请等待"
|
||
ULONG m_ulHScrollPos;
|
||
ULONG m_ulVScrollPos;
|
||
// fillMode: 0=不填充, 1=全黑, 2=半透明
|
||
VOID DrawTipString(CString strString, int fillMode=1);
|
||
|
||
POINT m_ClientCursorPos;
|
||
BYTE m_bCursorIndex;
|
||
BOOL m_bIsTraceCursor;
|
||
CCursorInfo m_CursorInfo; //自定义的一个系统的光标类
|
||
VOID SendCommand(const MYMSG* Msg);
|
||
void SendScaledMouseMessage(MSG* pMsg, bool makeLP);
|
||
VOID UpdateServerClipboard(char *szBuffer,ULONG ulLength);
|
||
VOID SendServerClipboard(void);
|
||
|
||
BOOL m_bIsCtrl;
|
||
LPBYTE m_szData;
|
||
BOOL m_bSend;
|
||
ULONG m_ulMsgCount;
|
||
int m_FrameID;
|
||
HIMC m_hOldIMC = NULL; // 保存原始 IME 上下文,控制模式切换时使用
|
||
bool m_bHide = false;
|
||
std::string m_strSaveNotice; // 截图保存路径提示
|
||
ULONGLONG m_nSaveNoticeTime = 0; // 截图提示开始时间
|
||
BOOL m_bUsingFRP = FALSE;
|
||
|
||
// 文件接收进度对话框(用于 Linux Ctrl+C -> 服务端 Ctrl+V)
|
||
// 按 transferID 管理多个并发传输
|
||
std::map<uint64_t, class CDlgFileSend*> m_FileRecvDlgs;
|
||
|
||
void SaveSnapshot(void);
|
||
// 对话框数据
|
||
enum { IDD = IDD_DIALOG_SCREEN_SPY };
|
||
|
||
WINDOWPLACEMENT m_struOldWndpl;
|
||
|
||
const AVCodec* m_pCodec;
|
||
AVCodecContext* m_pCodecContext;
|
||
AVPacket m_AVPacket;
|
||
AVFrame m_AVFrame;
|
||
|
||
clock_t m_lastMouseMove; // 鼠标移动时间
|
||
POINT m_lastMousePoint;// 上次鼠标位置
|
||
BOOL m_bAdaptiveSize = TRUE;
|
||
HCURSOR m_hRemoteCursor = NULL;
|
||
HCURSOR m_hCustomCursor = NULL; // 缓存的自定义光标
|
||
DWORD m_dwCustomCursorHash = 0; // 当前自定义光标哈希
|
||
BOOL m_bUseCustomCursor = TRUE; // 是否使用自定义光标
|
||
CRect m_CRect;
|
||
double m_wZoom=1, m_hZoom=1;
|
||
bool m_bMouseTracking = false;
|
||
|
||
CString m_aviFile;
|
||
CBmpToAvi m_aviStream;
|
||
|
||
// 传输速率统计
|
||
ULONG m_ulBytesThisSecond = 0; // 本秒累计字节
|
||
double m_dTransferRate = 0; // 当前速率 (KB/s)
|
||
// 帧率统计 (使用EMA平滑)
|
||
ULONG m_ulFramesThisSecond = 0; // 本秒累计帧数
|
||
double m_dFrameRate = 0; // 平滑后的帧率 (FPS)
|
||
|
||
// 自适应质量
|
||
struct {
|
||
bool enabled = false; // 是否启用自适应 (默认关闭)
|
||
int currentLevel = QUALITY_HIGH; // 当前质量等级
|
||
int currentMaxWidth = 0; // 当前分辨率限制 (0=原始)
|
||
int lastRTT = 0; // 上次RTT值
|
||
ULONGLONG lastChangeTime = 0; // 上次切换时间
|
||
ULONGLONG lastResChangeTime = 0; // 上次分辨率变化时间
|
||
ULONGLONG startTime = 0; // 启动时间 (用于延迟启动自适应)
|
||
int stableCount = 0; // 稳定计数 (用于防抖)
|
||
} m_AdaptiveQuality;
|
||
|
||
volatile bool m_bResolutionChanging = false; // 分辨率切换中,阻止解码
|
||
|
||
// ========== 音频播放 ==========
|
||
// m_Settings.AudioEnabled 表示是否启用音频(与客户端同步)
|
||
BOOL m_bAudioPlaying = FALSE; // 音频是否正在播放
|
||
HWAVEOUT m_hWaveOut = NULL; // 波形输出设备句柄
|
||
WAVEFORMATEX m_AudioFormat = {}; // 音频格式
|
||
static const int AUDIO_BUFFER_COUNT = 8; // 缓冲区数量(增加到8个)
|
||
WAVEHDR m_WaveHdr[AUDIO_BUFFER_COUNT] = {}; // 波形头
|
||
LPBYTE m_pAudioBuf[AUDIO_BUFFER_COUNT] = {};// 音频缓冲区
|
||
int m_nAudioBufIndex = 0; // 当前缓冲区索引
|
||
static const DWORD AUDIO_BUF_SIZE = 8192; // 8KB 每个缓冲区(更小更频繁)
|
||
|
||
// 环形缓冲区(吸收网络抖动)
|
||
static const DWORD RING_BUF_SIZE = 65536; // 64KB 环形缓冲区
|
||
BYTE* m_pRingBuf = nullptr; // 环形缓冲区
|
||
DWORD m_nRingHead = 0; // 写入位置
|
||
DWORD m_nRingTail = 0; // 读取位置
|
||
DWORD m_nRingDataLen = 0; // 缓冲数据量
|
||
int m_nPrebufferCount = 0; // 预缓冲计数
|
||
static const int PREBUFFER_TARGET = 5; // 预缓冲目标(积累5个包再开始播放,约50ms)
|
||
|
||
BYTE m_nAudioCompression = 0; // 音频压缩类型 (AudioCompression)
|
||
#if USING_OPUS
|
||
void* m_pOpusDecoder = nullptr; // Opus 解码器
|
||
short* m_pOpusDecodeBuffer = nullptr; // Opus 解码输出缓冲区
|
||
#endif
|
||
|
||
void OnAudioData(BYTE* pData, UINT32 len); // 处理音频数据
|
||
BOOL InitAudioPlayback(const AudioFormat* fmt); // 初始化音频播放
|
||
void StopAudioPlayback(); // 停止音频播放
|
||
void SendAudioCtrl(BYTE enable, BYTE persist); // 发送音频控制命令
|
||
void FeedAudioBuffers(); // 填充音频缓冲区
|
||
|
||
int GetClientRTT(); // 获取客户端RTT(ms)
|
||
void EvaluateQuality(); // 评估并调整质量
|
||
void ApplyQualityLevel(int level, bool persist = false); // 应用质量等级
|
||
const char* GetQualityName(int level); // 获取质量等级名称
|
||
void UpdateQualityMenuCheck(CMenu* SysMenu = nullptr); // 更新质量菜单勾选状态
|
||
|
||
void OnTimer(UINT_PTR nIDEvent);
|
||
void UpdateWindowTitle();
|
||
|
||
bool Decode(LPBYTE Buffer, int size);
|
||
void EnterFullScreen();
|
||
bool LeaveFullScreen();
|
||
void UpdateCtrlStatus(BOOL ctrl);
|
||
void OnDropFiles(HDROP hDropInfo);
|
||
|
||
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
|
||
afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
|
||
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
|
||
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
|
||
afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
|
||
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
|
||
afx_msg void OnMouseLeave();
|
||
afx_msg void OnKillFocus(CWnd* pNewWnd);
|
||
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||
afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
|
||
afx_msg LRESULT OnDisconnect(WPARAM wParam, LPARAM lParam);
|
||
afx_msg LRESULT OnWaveOutDone(WPARAM wParam, LPARAM lParam);
|
||
afx_msg LRESULT OnRecvFileV2Chunk(WPARAM wParam, LPARAM lParam);
|
||
afx_msg LRESULT OnRecvFileV2Complete(WPARAM wParam, LPARAM lParam);
|
||
afx_msg void OnExitFullscreen()
|
||
{
|
||
BYTE cmd[4] = { CMD_FULL_SCREEN, m_Settings.FullScreen = FALSE };
|
||
m_ContextObject->Send2Client(cmd, sizeof(cmd));
|
||
LeaveFullScreen();
|
||
}
|
||
afx_msg void OnShowStatusInfo()
|
||
{
|
||
if (m_pStatusInfoWnd) m_pStatusInfoWnd->Show();
|
||
}
|
||
afx_msg void OnHideStatusInfo()
|
||
{
|
||
if (m_pStatusInfoWnd) m_pStatusInfoWnd->Hide();
|
||
}
|
||
|
||
protected:
|
||
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
|
||
|
||
DECLARE_MESSAGE_MAP()
|
||
public:
|
||
void PrepareDrawing(const LPBITMAPINFO bmp);
|
||
virtual BOOL OnInitDialog();
|
||
afx_msg void OnClose();
|
||
afx_msg void OnPaint();
|
||
BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
|
||
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
|
||
virtual BOOL PreTranslateMessage(MSG* pMsg);
|
||
void OnLButtonDblClk(UINT nFlags, CPoint point);
|
||
};
|