Feature(web): Add toolbar audio toggle button
This commit is contained in:
@@ -9651,6 +9651,25 @@ void CMy2015RemoteDlg::CloseRemoteDesktopByClientID(uint64_t clientID)
|
||||
}
|
||||
}
|
||||
|
||||
bool CMy2015RemoteDlg::PostWebAudioToggle(uint64_t clientID)
|
||||
{
|
||||
HWND hWnd = NULL;
|
||||
EnterCriticalSection(&m_cs);
|
||||
for (auto& pair : m_RemoteWnds) {
|
||||
CScreenSpyDlg* dlg = dynamic_cast<CScreenSpyDlg*>(pair.second);
|
||||
if (dlg && dlg->GetClientID() == clientID && dlg->IsWebSession()) {
|
||||
hWnd = dlg->GetSafeHwnd();
|
||||
break;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&m_cs);
|
||||
if (hWnd && ::IsWindow(hWnd)) {
|
||||
// PostMessage 把活儿丢到对话框的 UI 线程,避免 WS 线程动 waveOut 句柄
|
||||
return ::PostMessage(hWnd, WM_AUDIO_TOGGLE_FROM_WEB, 0, 0) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CMy2015RemoteDlg::CloseWebRemoteDesktopByClientID(uint64_t clientID)
|
||||
{
|
||||
CScreenSpyDlg* targetDlg = nullptr;
|
||||
|
||||
@@ -373,6 +373,7 @@ public:
|
||||
void RemoveRemoteWindow(HWND wnd);
|
||||
void CloseRemoteDesktopByClientID(uint64_t clientID);
|
||||
void CloseWebRemoteDesktopByClientID(uint64_t clientID); // Only close Web session dialog
|
||||
bool PostWebAudioToggle(uint64_t clientID); // 给 Web 会话 ScreenSpy 投递音频开关消息
|
||||
CDialogBase* m_pActiveSession = nullptr; // 当前活动会话窗口指针 / NULL 表示无
|
||||
void UpdateActiveRemoteSession(CDialogBase* sess);
|
||||
CDialogBase* GetActiveRemoteSession();
|
||||
|
||||
@@ -224,6 +224,8 @@ CScreenSpyDlg::CScreenSpyDlg(CMy2015RemoteDlg* Parent, Server* IOCPServer, CONTE
|
||||
int width = m_BitmapInfor_Full->bmiHeader.biWidth;
|
||||
int height = abs(m_BitmapInfor_Full->bmiHeader.biHeight);
|
||||
WebService().NotifyResolutionChange(m_ClientID, width, height);
|
||||
// 透传客户端初始的音频开/关状态给 web,让前端按钮显示正确
|
||||
WebService().NotifyAudioState(m_ClientID, m_Settings.AudioEnabled != 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,6 +570,7 @@ BEGIN_MESSAGE_MAP(CScreenSpyDlg, CDialog)
|
||||
ON_MESSAGE(MM_WOM_DONE, &CScreenSpyDlg::OnWaveOutDone)
|
||||
ON_MESSAGE(WM_RECVFILEV2_CHUNK, &CScreenSpyDlg::OnRecvFileV2Chunk)
|
||||
ON_MESSAGE(WM_RECVFILEV2_COMPLETE, &CScreenSpyDlg::OnRecvFileV2Complete)
|
||||
ON_MESSAGE(WM_AUDIO_TOGGLE_FROM_WEB, &CScreenSpyDlg::OnAudioToggleFromWeb)
|
||||
ON_WM_DROPFILES()
|
||||
ON_WM_CAPTURECHANGED()
|
||||
END_MESSAGE_MAP()
|
||||
@@ -3521,6 +3524,11 @@ void CScreenSpyDlg::DisableAudio()
|
||||
}
|
||||
|
||||
Mprintf("[Audio Web] 禁用音频(来自 web 命令)\n");
|
||||
|
||||
// 广播状态给所有正在观看本设备的 web 客户端
|
||||
if (WebService().IsRunning()) {
|
||||
WebService().NotifyAudioState(m_ClientID, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3539,9 +3547,24 @@ void CScreenSpyDlg::EnableAudio()
|
||||
}
|
||||
|
||||
Mprintf("[Audio Web] 启用音频(来自 web 命令)\n");
|
||||
|
||||
if (WebService().IsRunning()) {
|
||||
WebService().NotifyAudioState(m_ClientID, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 由 PostMessage 从 WS 线程派发到 UI 线程;根据当前状态翻转
|
||||
LRESULT CScreenSpyDlg::OnAudioToggleFromWeb(WPARAM /*wParam*/, LPARAM /*lParam*/)
|
||||
{
|
||||
if (m_Settings.AudioEnabled) {
|
||||
DisableAudio();
|
||||
} else {
|
||||
EnableAudio();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CScreenSpyDlg::OnAudioData(BYTE* pData, UINT32 len)
|
||||
{
|
||||
if (len < 1) return;
|
||||
|
||||
@@ -93,6 +93,9 @@ extern "C"
|
||||
// 文件接收消息(用于将工作线程的文件数据转发到主线程处理)
|
||||
#define WM_RECVFILEV2_CHUNK (WM_USER + 0x200)
|
||||
#define WM_RECVFILEV2_COMPLETE (WM_USER + 0x201)
|
||||
// 来自 web 命令的音频开关,PostMessage 到对话框的 UI 线程,避免 WS 线程
|
||||
// 直接动 waveOut 句柄
|
||||
#define WM_AUDIO_TOGGLE_FROM_WEB (WM_USER + 0x202)
|
||||
|
||||
// ScreenSpyDlg 系统菜单命令 ID
|
||||
enum {
|
||||
@@ -363,6 +366,7 @@ public:
|
||||
void StopAudioPlayback(); // 停止音频播放
|
||||
void DisableAudio(); // 禁用音频(从网页命令)
|
||||
void EnableAudio(); // 启用音频(从网页命令)
|
||||
LRESULT OnAudioToggleFromWeb(WPARAM wParam, LPARAM lParam); // PostMessage 处理器
|
||||
void SendAudioCtrl(BYTE enable, BYTE persist); // 发送音频控制命令
|
||||
void FeedAudioBuffers(); // 填充音频缓冲区
|
||||
void SendAudioToWeb(const BYTE* pAudioData, UINT32 len, const WAVEFORMATEX* pFormat, BYTE compression); // 发送音频到网页 (compression=AudioCompression)
|
||||
|
||||
@@ -396,6 +396,8 @@ void CWebService::ServerThread(int port) {
|
||||
HandleKey(ws_ptr, msg);
|
||||
} else if (cmd == "rdp_reset") {
|
||||
HandleRdpReset(ws_ptr, token);
|
||||
} else if (cmd == "audio_toggle") {
|
||||
HandleAudioToggle(ws_ptr, token);
|
||||
} else if (cmd == "get_salt") {
|
||||
HandleGetSalt(ws_ptr, msg);
|
||||
} else if (cmd == "create_user") {
|
||||
@@ -689,14 +691,16 @@ void CWebService::HandleConnect(void* ws_ptr, const std::string& token, uint64_t
|
||||
}
|
||||
}
|
||||
|
||||
// Get screen dimensions from device info cache (may not be available yet)
|
||||
// Get screen dimensions + audio state from device info cache (may not be ready)
|
||||
int width = 0, height = 0;
|
||||
int audio_enabled = -1; // -1 = unknown yet (前端走 audio_state 事件兜底)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_DeviceCacheMutex);
|
||||
auto it = m_DeviceCache.find(device_id);
|
||||
if (it != m_DeviceCache.end()) {
|
||||
width = it->second->screen_width;
|
||||
height = it->second->screen_height;
|
||||
audio_enabled = it->second->audio_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,6 +714,9 @@ void CWebService::HandleConnect(void* ws_ptr, const std::string& token, uint64_t
|
||||
res["width"] = width;
|
||||
res["height"] = height;
|
||||
}
|
||||
if (audio_enabled >= 0) {
|
||||
res["audio_enabled"] = (audio_enabled != 0);
|
||||
}
|
||||
res["algorithm"] = "h264";
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
@@ -1002,6 +1009,33 @@ void CWebService::HandleRdpReset(void* ws_ptr, const std::string& token) {
|
||||
}
|
||||
}
|
||||
|
||||
void CWebService::HandleAudioToggle(void* ws_ptr, const std::string& token) {
|
||||
std::string username, role;
|
||||
if (!ValidateToken(token, username, role)) {
|
||||
SendText(ws_ptr, BuildJsonResponse("audio_toggle_result", false, "Invalid token"));
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t device_id = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ClientsMutex);
|
||||
auto it = m_Clients.find(ws_ptr);
|
||||
if (it != m_Clients.end()) device_id = it->second.watch_device_id;
|
||||
}
|
||||
if (device_id == 0) {
|
||||
SendText(ws_ptr, BuildJsonResponse("audio_toggle_result", false, "No device connected"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 投递到 ScreenSpyDlg 的 UI 线程;那边会调用 Enable/DisableAudio 并通过
|
||||
// NotifyAudioState 把新状态广播给所有 watching 的 web 客户端
|
||||
if (!m_pParentDlg || !m_pParentDlg->PostWebAudioToggle(device_id)) {
|
||||
SendText(ws_ptr, BuildJsonResponse("audio_toggle_result", false, "No active screen session"));
|
||||
return;
|
||||
}
|
||||
SendText(ws_ptr, BuildJsonResponse("audio_toggle_result", true));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// User Management Handlers
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -1699,6 +1733,37 @@ void CWebService::NotifyResolutionChange(uint64_t device_id, int width, int heig
|
||||
}
|
||||
}
|
||||
|
||||
void CWebService::NotifyAudioState(uint64_t device_id, bool enabled) {
|
||||
if (m_bStopping) return;
|
||||
|
||||
// 缓存最新状态,新加入的 web 客户端通过 connect_result 取到初值
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_DeviceCacheMutex);
|
||||
auto it = m_DeviceCache.find(device_id);
|
||||
if (it == m_DeviceCache.end()) {
|
||||
m_DeviceCache[device_id] = std::make_shared<WebDeviceInfo>();
|
||||
it = m_DeviceCache.find(device_id);
|
||||
}
|
||||
it->second->audio_enabled = enabled ? 1 : 0;
|
||||
}
|
||||
|
||||
Json::Value res;
|
||||
res["cmd"] = "audio_state";
|
||||
res["id"] = device_id;
|
||||
res["enabled"] = enabled;
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = "";
|
||||
std::string json = Json::writeString(builder, res);
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_ClientsMutex);
|
||||
for (auto& [ws_ptr, client] : m_Clients) {
|
||||
if (client.watch_device_id == device_id) {
|
||||
SendText(ws_ptr, json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CWebService::BroadcastCursor(uint64_t device_id, uint8_t cursor_index) {
|
||||
if (m_bStopping) return;
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ struct WebDeviceInfo {
|
||||
int screen_width;
|
||||
int screen_height;
|
||||
bool online;
|
||||
// 当前会话的音频开关。-1=未知(客户端 BITMAPINFO 还没回来),0=关,1=开
|
||||
int audio_enabled = -1;
|
||||
|
||||
// Keyframe cache for new web clients
|
||||
std::vector<uint8_t> keyframe_cache;
|
||||
@@ -98,6 +100,10 @@ public:
|
||||
// Resolution change notification
|
||||
void NotifyResolutionChange(uint64_t device_id, int width, int height);
|
||||
|
||||
// Audio enable/disable notification — pushes current state to all web
|
||||
// clients watching this device and caches it for newcomers.
|
||||
void NotifyAudioState(uint64_t device_id, bool enabled);
|
||||
|
||||
// Cursor change notification (called from ScreenSpyDlg)
|
||||
void BroadcastCursor(uint64_t device_id, uint8_t cursor_index);
|
||||
|
||||
@@ -129,6 +135,7 @@ private:
|
||||
void HandleMouse(void* ws_ptr, const std::string& msg);
|
||||
void HandleKey(void* ws_ptr, const std::string& msg);
|
||||
void HandleRdpReset(void* ws_ptr, const std::string& token);
|
||||
void HandleAudioToggle(void* ws_ptr, const std::string& token);
|
||||
|
||||
// Token management
|
||||
std::string GenerateToken(const std::string& username, const std::string& role);
|
||||
|
||||
Reference in New Issue
Block a user