2 Commits

Author SHA1 Message Date
yuanyuanxiang
91d4c0a523 Fix: eliminate extra screen restarts on connection init
Two changes to reduce unnecessary CScreenSpy restarts when connecting:

1. Client (ScreenManager.cpp): Initialize CScreenSpy with bitrate from
   the locally-saved quality profile, so CMD_QUALITY_LEVEL arriving from
   the server (same bitrate as default) hits SetBitRate(3000)==3000 and
   skips the restart instead of comparing against the hard-coded 0.
   Also fixes QualityLevel init to use the already-computed `quality`
   variable (which honours the QUALITY_DISABLED override when algo!=NUL)
   rather than re-reading the cfg key a second time.

2. Server (ScreenSpyDlg.cpp): Only send CMD_SCREEN_SIZE strategy=2 when
   the session is in QUALITY_ADAPTIVE mode and a cached maxWidth exists.
   Fixed quality levels already carry resolution via CMD_QUALITY_PROFILES,
   so unconditionally sending CMD_SCREEN_SIZE caused a second restart when
   the screen spy was still rebuilding from the first one.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 22:14:06 +02:00
yuanyuanxiang
abafd673a2 Opt: skip 8MB raw first-frame in H264 mode; server unlocks on first IDR instead
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:29:59 +02:00
3 changed files with 25 additions and 6 deletions

View File

@@ -153,7 +153,7 @@ CScreenManager::CScreenManager(IOCPClient* ClientObject, int n, void* user, BOOL
m_ScreenSettings.FullScreen = cfg.GetInt("settings", "FullScreen", priv); m_ScreenSettings.FullScreen = cfg.GetInt("settings", "FullScreen", priv);
m_ScreenSettings.RemoteCursor = cfg.GetInt("settings", "RemoteCursor", 0); m_ScreenSettings.RemoteCursor = cfg.GetInt("settings", "RemoteCursor", 0);
m_ScreenSettings.ScrollDetectInterval = cfg.GetInt("settings", "ScrollDetectInterval", 2); // 默认每2帧 m_ScreenSettings.ScrollDetectInterval = cfg.GetInt("settings", "ScrollDetectInterval", 2); // 默认每2帧
m_ScreenSettings.QualityLevel = cfg.GetInt("settings", "QualityLevel", quality); m_ScreenSettings.QualityLevel = quality;
m_ScreenSettings.CpuSpeedup = cfg.GetInt("settings", "CpuSpeedup", 0); m_ScreenSettings.CpuSpeedup = cfg.GetInt("settings", "CpuSpeedup", 0);
m_ScreenSettings.AudioEnabled = cfg.GetInt("settings", "AudioEnabled", 0); // 默认禁用音频 m_ScreenSettings.AudioEnabled = cfg.GetInt("settings", "AudioEnabled", 0); // 默认禁用音频
m_ScreenSettings.EncodeLevel = cfg.GetInt("settings", "EncodeLevel", LEVEL_H264_SOFT); m_ScreenSettings.EncodeLevel = cfg.GetInt("settings", "EncodeLevel", LEVEL_H264_SOFT);
@@ -556,6 +556,10 @@ void CScreenManager::InitScreenSpy()
m_isGDI = TRUE; m_isGDI = TRUE;
m_ScreenSpyObject = new CScreenSpy(32, algo, DXGI == USING_VIRTUAL, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel, rect, switchScreen, m_hTargetWnd, m_bDynamicForeground); m_ScreenSpyObject = new CScreenSpy(32, algo, DXGI == USING_VIRTUAL, DEFAULT_GOP, all, m_ScreenSettings.EncodeLevel, rect, switchScreen, m_hTargetWnd, m_bDynamicForeground);
} }
// 用已保存的质量配置初始化码率,避免 CMD_QUALITY_LEVEL 到达时 0→3000 触发不必要重启
if (m_ScreenSpyObject && level >= 0 && level < QUALITY_COUNT) {
m_ScreenSpyObject->SetBitRate(m_QualityProfiles[level].bitRate);
}
} }
BOOL IsRunningAsSystem() BOOL IsRunningAsSystem()

View File

@@ -145,10 +145,19 @@ CScreenSpy::~CScreenSpy()
LPBYTE CScreenSpy::GetFirstScreenData(ULONG* ulFirstScreenLength) LPBYTE CScreenSpy::GetFirstScreenData(ULONG* ulFirstScreenLength)
{ {
if (m_hTargetWnd && IsIconic(m_hTargetWnd)) {
*ulFirstScreenLength = 0;
return nullptr;
}
ScanScreen(m_hFullMemDC, m_hDeskTopDC, m_ulFullWidth, m_ulFullHeight); ScanScreen(m_hFullMemDC, m_hDeskTopDC, m_ulFullWidth, m_ulFullHeight);
m_RectBuffer[0] = TOKEN_FIRSTSCREEN; m_RectBuffer[0] = TOKEN_FIRSTSCREEN;
LPBYTE bmp = scaleBitmap(m_BmpZoomBuffer, (LPBYTE)m_BitmapData_Full); LPBYTE bmp = scaleBitmap(m_BmpZoomBuffer, (LPBYTE)m_BitmapData_Full);
memcpy(m_FirstBuffer, bmp, m_BitmapInfor_Send->bmiHeader.biSizeImage); memcpy(m_FirstBuffer, bmp, m_BitmapInfor_Send->bmiHeader.biSizeImage);
// H264/AV1不发原始位图IDR 到达后服务端自行解锁;节省每次切窗口的流量峰值
if (m_bAlgorithm == ALGORITHM_H264) {
*ulFirstScreenLength = 0;
return nullptr;
}
memcpy(1 + m_RectBuffer, bmp, m_BitmapInfor_Send->bmiHeader.biSizeImage); memcpy(1 + m_RectBuffer, bmp, m_BitmapInfor_Send->bmiHeader.biSizeImage);
if (m_bAlgorithm == ALGORITHM_GRAY) { if (m_bAlgorithm == ALGORITHM_GRAY) {
ToGray(1 + m_RectBuffer, 1 + m_RectBuffer, m_BitmapInfor_Send->bmiHeader.biSizeImage); ToGray(1 + m_RectBuffer, 1 + m_RectBuffer, m_BitmapInfor_Send->bmiHeader.biSizeImage);

View File

@@ -856,12 +856,13 @@ BOOL CScreenSpyDlg::OnInitDialog()
BYTE cmd[4] = { CMD_QUALITY_LEVEL, (BYTE)m_AdaptiveQuality.currentLevel, 0 }; BYTE cmd[4] = { CMD_QUALITY_LEVEL, (BYTE)m_AdaptiveQuality.currentLevel, 0 };
m_ContextObject->Send2Client(cmd, sizeof(cmd)); m_ContextObject->Send2Client(cmd, sizeof(cmd));
// 始终发送 CMD_SCREEN_SIZE让客户端同步分辨率策略 // 仅在自适应模式且有缓存宽度时同步分辨率,固定等级分辨率由 profile 决定无需重发
// maxWidth=0 表示使用默认分辨率1080p 限制) if (m_Settings.QualityLevel == QUALITY_ADAPTIVE && m_AdaptiveQuality.currentMaxWidth > 0) {
BYTE sizeCmd[16] = { CMD_SCREEN_SIZE, 2 }; // strategy=2 表示自适应质量 BYTE sizeCmd[16] = { CMD_SCREEN_SIZE, 2 }; // strategy=2 = 自适应质量
memcpy(sizeCmd + 2, &m_AdaptiveQuality.currentMaxWidth, sizeof(int)); memcpy(sizeCmd + 2, &m_AdaptiveQuality.currentMaxWidth, sizeof(int));
m_ContextObject->Send2Client(sizeCmd, 10); m_ContextObject->Send2Client(sizeCmd, 10);
} }
}
SendNext(); SendNext();
@@ -1478,6 +1479,11 @@ VOID CScreenSpyDlg::DrawNextScreenDiff(bool keyFrame)
if (!m_bHide) { if (!m_bHide) {
if (Decode((LPBYTE)NextScreenData, NextScreenLength)) { if (Decode((LPBYTE)NextScreenData, NextScreenLength)) {
bChange = TRUE; bChange = TRUE;
// 解码成功即代表 IDR 参考帧已建立(编码器首帧强制 IDR解除门控。
// 不依赖 IsAnyKeyframe某些硬编输出格式或解码器首帧 EAGAIN 延迟会导致
// 关键帧检测误判,进而让 m_bIsFirst 永久为 TRUE"请等待"卡死)。
if (m_bIsFirst)
m_bIsFirst = FALSE;
} }
} }
// Broadcast video frame to web clients (only for Web session dialogs) // Broadcast video frame to web clients (only for Web session dialogs)