Feature: Support recording video in remote desktop control

This commit is contained in:
yuanyuanxiang
2025-11-23 10:57:52 +01:00
parent 1e3aa8ff63
commit 9a640c0a1d
11 changed files with 3614 additions and 150 deletions

View File

@@ -24,11 +24,14 @@ enum {
IDM_GET_CLIPBOARD, // 获取剪贴板
IDM_SET_CLIPBOARD, // 设置剪贴板
IDM_ADAPTIVE_SIZE,
IDM_SAVEAVI,
IDM_SAVEAVI_H264,
};
IMPLEMENT_DYNAMIC(CScreenSpyDlg, CDialog)
#define ALGORITHM_DIFF 1
#define TIMER_ID 132
#ifdef _WIN64
#ifdef _DEBUG
@@ -170,6 +173,7 @@ BEGIN_MESSAGE_MAP(CScreenSpyDlg, CDialog)
ON_WM_SIZE()
ON_WM_LBUTTONDBLCLK()
ON_WM_ACTIVATE()
ON_WM_TIMER()
END_MESSAGE_MAP()
@@ -220,7 +224,10 @@ BOOL CScreenSpyDlg::OnInitDialog()
SysMenu->AppendMenu(MF_STRING, IDM_ADAPTIVE_SIZE, "自适应窗口大小(&A)");
SysMenu->AppendMenu(MF_STRING, IDM_TRACE_CURSOR, "跟踪被控端鼠标(&T)");
SysMenu->AppendMenu(MF_STRING, IDM_BLOCK_INPUT, "锁定被控端鼠标和键盘(&L)");
SysMenu->AppendMenu(MF_SEPARATOR);
SysMenu->AppendMenu(MF_STRING, IDM_SAVEDIB, "保存快照(&S)");
SysMenu->AppendMenu(MF_STRING, IDM_SAVEAVI, _T("录像(MJPEG)"));
SysMenu->AppendMenu(MF_STRING, IDM_SAVEAVI_H264, _T("录像(H264)"));
SysMenu->AppendMenu(MF_SEPARATOR);
SysMenu->AppendMenu(MF_STRING, IDM_GET_CLIPBOARD, "获取剪贴板(&R)");
SysMenu->AppendMenu(MF_STRING, IDM_SET_CLIPBOARD, "设置剪贴板(&L)");
@@ -253,6 +260,11 @@ BOOL CScreenSpyDlg::OnInitDialog()
VOID CScreenSpyDlg::OnClose()
{
if (!m_aviFile.IsEmpty()) {
KillTimer(TIMER_ID);
m_aviFile = "";
m_aviStream.Close();
}
CancelIO();
// 恢复鼠标状态
SetClassLongPtr(m_hWnd, GCLP_HCURSOR, (LONG_PTR)LoadCursor(NULL, IDC_ARROW));
@@ -266,6 +278,7 @@ VOID CScreenSpyDlg::OnClose()
// 等待数据处理完毕
if (IsProcessing()) {
m_bHide = true;
ShowWindow(SW_HIDE);
return;
}
@@ -429,7 +442,7 @@ VOID CScreenSpyDlg::DrawNextScreenDiff(bool keyFrame)
}
#endif
if (bChange) {
if (bChange && !m_bHide) {
PostMessage(WM_PAINT);
}
}
@@ -519,6 +532,35 @@ VOID CScreenSpyDlg::DrawTipString(CString strString)
SetTextColor(m_hFullDC, OldBackgroundColor);
}
bool DirectoryExists(const char* path) {
DWORD attr = GetFileAttributesA(path);
return (attr != INVALID_FILE_ATTRIBUTES &&
(attr & FILE_ATTRIBUTE_DIRECTORY));
}
std::string GetScreenShotPath(CWnd *parent, const CString& ip, const CString &filter, const CString& suffix) {
std::string path;
std::string folder = THIS_CFG.GetStr("settings", "ScreenShot", "");
if (folder.empty() || !DirectoryExists(folder.c_str())) {
CString strFileName = ip + CTime::GetCurrentTime().Format(_T("_%Y%m%d%H%M%S.")) + suffix;
CFileDialog dlg(FALSE, suffix, strFileName, OFN_OVERWRITEPROMPT, filter, parent);
if (dlg.DoModal() != IDOK)
return "";
folder = dlg.GetFolderPath();
if (!folder.empty() && folder.back() != '\\') {
folder += '\\';
}
path = dlg.GetPathName();
THIS_CFG.SetStr("settings", "ScreenShot", folder);
}
else {
if (!folder.empty() && folder.back() != '\\') {
folder += '\\';
}
path = folder + std::string(ip) + "_" + ToPekingDateTime(0) + "." + std::string(suffix);
}
return path;
}
void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
@@ -541,6 +583,33 @@ void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
SaveSnapshot();
break;
}
case IDM_SAVEAVI: case IDM_SAVEAVI_H264: {
if (SysMenu->GetMenuState(nID, MF_BYCOMMAND) & MF_CHECKED) {
KillTimer(TIMER_ID);
SysMenu->CheckMenuItem(nID, MF_UNCHECKED);
SysMenu->EnableMenuItem(IDM_SAVEAVI, MF_ENABLED);
SysMenu->EnableMenuItem(IDM_SAVEAVI_H264, MF_ENABLED);
m_aviFile = "";
m_aviStream.Close();
return;
}
m_aviFile = GetScreenShotPath(this, m_IPAddress, "Video(*.avi)|*.avi|", "avi").c_str();
const int duration = 250, rate = 1000 / duration;
FCCHandler handler = nID == IDM_SAVEAVI ? ENCODER_MJPEG : ENCODER_H264;
int code;
if (code = m_aviStream.Open(m_aviFile, m_BitmapInfor_Full, rate, handler)) {
MessageBox(CString("Create Video(*.avi) Failed:\n") + m_aviFile + "\r\n错误代码: " +
CBmpToAvi::GetErrMsg(code).c_str(), "提示");
m_aviFile = _T("");
}
else {
::SetTimer(m_hWnd, TIMER_ID, duration, NULL);
SysMenu->CheckMenuItem(nID, MF_CHECKED);
SysMenu->EnableMenuItem(nID == IDM_SAVEAVI ? IDM_SAVEAVI_H264 : IDM_SAVEAVI, MF_DISABLED);
}
break;
}
case IDM_TRACE_CURSOR: { // 跟踪被控端鼠标
m_bIsTraceCursor = !m_bIsTraceCursor; //这里在改变数据
SysMenu->CheckMenuItem(IDM_TRACE_CURSOR, m_bIsTraceCursor ? MF_CHECKED : MF_UNCHECKED);//在菜单打钩不打钩
@@ -580,6 +649,19 @@ void CScreenSpyDlg::OnSysCommand(UINT nID, LPARAM lParam)
CDialog::OnSysCommand(nID, lParam);
}
void CScreenSpyDlg::OnTimer(UINT_PTR nIDEvent)
{
if (!m_aviFile.IsEmpty()) {
LPCTSTR lpTipsString = _T("");
m_aviStream.Write((BYTE*)m_BitmapData_Full);
// 提示正在录像
SetTextColor(m_hFullDC, RGB(0xff, 0x00, 0x00));
TextOut(m_hFullDC, 0, 0, lpTipsString, lstrlen(lpTipsString));
}
CDialog::OnTimer(nIDEvent);
}
BOOL CScreenSpyDlg::PreTranslateMessage(MSG* pMsg)
{
@@ -660,12 +742,11 @@ VOID CScreenSpyDlg::SendCommand(const MYMSG* Msg)
BOOL CScreenSpyDlg::SaveSnapshot(void)
{
CString strFileName = m_IPAddress + CTime::GetCurrentTime().Format("_%Y-%m-%d_%H-%M-%S.bmp");
CFileDialog Dlg(FALSE, "bmp", strFileName, OFN_OVERWRITEPROMPT, "位图文件(*.bmp)|*.bmp|", this);
if(Dlg.DoModal () != IDOK)
auto path = GetScreenShotPath(this, m_IPAddress, "位图文件(*.bmp)|*.bmp|", "bmp");
if (path.empty())
return FALSE;
WriteBitmap(m_BitmapInfor_Full, m_BitmapData_Full, Dlg.GetPathName().GetBuffer());
WriteBitmap(m_BitmapInfor_Full, m_BitmapData_Full, path.c_str());
return true;
}