60 Commits

Author SHA1 Message Date
dijunkun
95a014a601 Not allow clicking connect button if remote id is empty 2024-06-14 17:01:58 +08:00
dijunkun
34d6bac345 Update Windows platform layout style 2024-06-14 16:02:55 +08:00
dijunkun
399785409c Use element style header file to control layout 2024-06-14 15:37:16 +08:00
dijunkun
5f1d9b6912 Fix language localization error 2024-06-14 13:55:05 +08:00
dijunkun
d963a0cf38 1.The mouse control button allowed to be clicked Only when connection established; 2.Fix display resolution error after exit fullscreen 2024-06-14 10:48:35 +08:00
dijunkun
f9c1bc48b4 Add control/release mouse button 2024-06-13 17:36:02 +08:00
dijunkun
2906d05a4b Do not send mouse click event when cursor hovers over subwindows 2024-06-13 16:56:03 +08:00
dijunkun
053a0f86ad Add mouse control flag 2024-06-13 16:36:11 +08:00
dijunkun
6c2363b239 Clear render buffer when connection closed 2024-06-13 16:30:25 +08:00
dijunkun
167514fed8 Do not collapse menu window when connection established 2024-06-13 16:07:22 +08:00
dijunkun
342eb0c386 Reset connection_established_ flag when connection closed 2024-06-13 16:06:02 +08:00
dijunkun
52c7099dbe Fix crash when connecting to local desk 2024-06-13 15:49:26 +08:00
dijunkun
12faf7cd2d Do not reset is_create_connection_ when click disconnect button 2024-06-07 18:13:09 +08:00
dijunkun
6d921a3309 Fix server mode screen capture error 2024-06-07 16:30:18 +08:00
dijunkun
5a690ebbb6 Do not use 'S-' or 'C-' as the prefix for the user id of a peer 2024-06-07 16:27:05 +08:00
dijunkun
4b3839aa34 Only server can capture screen and control mouse 2024-06-07 14:07:22 +08:00
dijunkun
efb165b56f 1.Add CreateConnectionPeer method in order to recreate peer instance; 2.Fix settings OK/Cancel button position 2024-06-06 17:19:16 +08:00
dijunkun
0047b4ecc5 Recreate peer instance after settings changed 2024-06-06 15:11:02 +08:00
dijunkun
844710af7c Fix settings button value mismatch 2024-06-06 14:34:16 +08:00
dijunkun
562d54090a Use 'ImGuiWindowFlags_NoSavedSettings' for settings window 2024-06-06 09:57:37 +08:00
dijunkun
f7fd37651e Reset settings window position before it is opened 2024-06-06 09:55:08 +08:00
dijunkun
280f59f97d Enable movement of settings window 2024-06-05 17:33:42 +08:00
dijunkun
0683ad9d27 Use Combo instead of RadioButton for settings 2024-06-05 17:30:23 +08:00
dijunkun
e061e3b4d7 Support read configure params from input directly 2024-06-04 17:38:55 +08:00
dijunkun
eaedcb8d06 Fix crash caused by the release of screen capturer 2024-06-04 16:27:34 +08:00
dijunkun
e7e6380adc Start capturing screen when connection established 2024-06-03 23:48:53 +08:00
dijunkun
1f50483b50 Save settings into cache file 2024-06-03 17:02:20 +08:00
dijunkun
6f703c8267 Use OOP to refactor main function 2024-06-03 15:23:37 +08:00
dijunkun
d150c374b5 Reset main window width/height ratio automatically when width/height is changed 2024-06-03 11:30:55 +08:00
dijunkun
f29b2ee09d Test user date in peer instance and callback functions 2024-05-30 17:27:49 +08:00
dijunkun
0a934e8c01 Fix LNK1561 error on Winodws 2024-05-30 16:33:50 +08:00
dijunkun
2163aa87d4 The connection can use only one peer to realize server and client 2024-05-30 16:12:53 +08:00
dijunkun
5d8408d892 Use abstraction to refactor remote desk gui 2024-05-29 17:33:41 +08:00
dijunkun
93d0e3a5d0 Auto collapse menu bar when connection established 2024-05-28 17:37:06 +08:00
dijunkun
b4a5e91bc9 Support fullscreen 2024-05-28 16:27:04 +08:00
dijunkun
759078ef7f 1.Use PingFang.ttc as Chinese default fonts on MacOS;2.Fix link error for FFmpeg 2024-05-28 15:25:16 +08:00
dijunkun
905539a6eb 1.Use Windows font file 'simhei.ttf'; 2.Add test button for fullscreen 2024-05-27 17:06:09 +08:00
dijunkun
f1512812ad Support localization(Simplified Chinese[source-han-sans-regular]) 2024-05-24 17:15:44 +08:00
dijunkun
5f1cf89649 Fix crash during termination on windows 2024-05-24 15:48:37 +08:00
dijunkun
f291ad189a 1.Remove sdl2 from thirdparty since it is already required by imgui; 2.Update imgui to v1.90.6; 3.Update spdlog to v 1.14.1 2024-05-24 15:10:58 +08:00
dijunkun
8807636372 Fix crash caused by screen capturer or mouse controller init failed on Linux and MacOS 2024-05-23 15:48:10 +08:00
dijunkun
70be1d8afc Update submodule projectx 2024-05-22 16:39:27 +08:00
dijunkun
1f76aa427d Change submodule projectx url 2024-05-22 16:35:30 +08:00
dijunkun
134cbf8b75 Restore ffmpeg dependency for Linux and MacOS due to screen capture needs ffmpeg on these platforms 2024-05-22 14:06:12 +08:00
dijunkun
669b944cfd Remove resampling process during SDL2 capture audio stream 2024-05-21 17:01:08 +08:00
dijunkun
9962829885 Fix ARGB to NV12 error caused by uv stride 2024-05-20 10:47:44 +08:00
dijunkun
1393615f01 Remove dependency on FFmpeg 2024-05-17 17:55:57 +08:00
dijunkun
d58ae3a6b1 Fix AV1 codec on MacOS 2024-05-10 14:45:12 +08:00
dijunkun
a188729af6 Support AV1 stream transmitting over RTP 2024-05-09 17:05:38 +08:00
dijunkun
422478bd9a Support AV1 codec 2024-04-19 17:37:00 +08:00
dijunkun
d8980f0082 WGC screen capturer needs c++17 or above 2023-12-22 16:07:32 +08:00
dijunkun
e88bb017fa 1.Using c++14; 2.Using {} to initialize std::atomic 2023-12-22 15:46:54 +08:00
dijunkun
87466d6074 Update thirdparty library 2023-12-22 14:14:08 +08:00
Di Junkun
fbbbfc5e6a Update README_CN.md 2023-12-21 15:02:12 +08:00
Di Junkun
d2cefd1827 Update README.md 2023-12-21 14:59:44 +08:00
Di Junkun
a350e06529 Update README.md 2023-12-21 14:56:18 +08:00
Di Junkun
8c742ffa08 Update README.md 2023-12-21 14:34:11 +08:00
Di Junkun
475005b8a4 Update README.md 2023-12-20 15:54:54 +08:00
dijunkun
4da5188759 Update README.md 2023-12-20 15:19:44 +08:00
Di Junkun
d8df4df5ae Update README.md 2023-12-20 15:17:53 +08:00
25 changed files with 2584 additions and 1009 deletions

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "thirdparty/projectx"]
path = thirdparty/projectx
url = git@github.com:dijunkun/projectx.git
url = https://github.com/dijunkun/projectx.git

View File

@@ -3,44 +3,44 @@
#### More than remote desktop
----
[Chinese](README_CN.md) / [English](README.md)
[中文](README_CN.md) / [English](README.md)
![example](https://github.com/dijunkun/continuous-desk/assets/29698109/f3153e28-751e-477c-b4e3-ac2384d3370f)
![sup_example](https://github.com/dijunkun/continuous-desk/assets/29698109/46536bc8-3ddd-438d-bf52-dccf143f1c20)
# Intro
Continuous Desk is a lightweight cross-platform remote desktop. It allows multiple users to remotely control the same computer at the same time. In addition to desktop image transmission, it also supports end-to-end voice transmission, providing collaboration capabilities on the basis of remote desktop.
Continuous Desk is an experimental application of [Projectx](https://github.com/dijunkun/projectx) real-time communications library. Projectx is a lightweight cross-platform real-time communications library. It has basic capabilities such as network traversal ([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)), video softwar/hardware encoding/decoding (H264), audio encoding/decoding ([Opus](https://github.com/xiph/opus)), signaling interaction, and network congestion control ([TCP over UDP](https://libnice.freedesktop.org/)).
Continuous Desk is an experimental application of [Projectx](https://github.com/dijunkun/projectx) real-time communications library. Projectx is a lightweight cross-platform real-time communications library. It has basic capabilities such as network traversal ([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)), video software/hardware encoding/decoding (H264), audio encoding/decoding ([Opus](https://github.com/xiph/opus)), signaling interaction, and network congestion control ([TCP over UDP](https://libnice.freedesktop.org/)).
## Usage
Enter the remote desktop ID in the 'REMOTE ID' field on the menu bar, and click 'Connect' button to initiate the remote connection.
![usage1](https://github.com/dijunkun/continuous-desk/assets/29698109/80099485-f2db-4f09-9fb2-e811d87265dc)
![usage1](https://github.com/dijunkun/continuous-desk/assets/29698109/2ad59e6d-bdba-46d0-90cf-cbc9c06c2278)
If the remote desktop is set with a connection password, the local end needs to enter the correct password to initiate the remote connection. If the password is incorrect, an "Incorrect password" alert will appear in the status bar.
![incorrect password](https://github.com/dijunkun/continuous-desk/assets/29698109/0b16d21a-baa6-49c1-9d87-5e80978dd345)
![incorrect password](https://github.com/dijunkun/continuous-desk/assets/29698109/cb05501c-ec4e-4adf-952d-7a55ef770a97)
After connection successfully established, the status bar will display the message "ClientConnected."
![success](https://github.com/dijunkun/continuous-desk/assets/29698109/9c4f6604-2b1b-47b6-9c73-21c5026d1f26)
![success](https://github.com/dijunkun/continuous-desk/assets/29698109/0cca21f7-48fe-44a5-b83d-eafeb8a81eb1)
## How to build
Requirements<EFBFBD><EFBFBD>
Requirements:
- [xmake](https://xmake.io/#/guide/installation)
- [cmake](https://cmake.org/download/)
- [vcpkg](https://vcpkg.io/en/getting-started)
Following packages need to be installed on Linux<EFBFBD><EFBFBD>
Following packages need to be installed on Linux:
```
sudo apt-get install -y nvidia-cuda-toolkit libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxcb-shm0-dev libxv-dev libasound2-dev libsndio-dev libasound2-dev libpulse-dev
```
Commands
Commands:
```
git clone https://github.com/dijunkun/continuous-desk
@@ -52,7 +52,7 @@ git submodule update
xmake b remote_desk
```
Run
Run:
```
# Windows/MacOS
xmake r remote_desk

View File

@@ -1,47 +1,47 @@
# Continuous Desk
#### <EFBFBD><EFBFBD>ֹԶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#### 不止远程桌面
----
[English](README.md) / [<EFBFBD><EFBFBD><EFBFBD><EFBFBD>](README_CN.md)
[English](README.md) / [中文](README_CN.md)
![example](https://github.com/dijunkun/continuous-desk/assets/29698109/f3153e28-751e-477c-b4e3-ac2384d3370f)
![sup_example](https://github.com/dijunkun/continuous-desk/assets/29698109/46536bc8-3ddd-438d-bf52-dccf143f1c20)
## <EFBFBD><EFBFBD><EFBFBD><EFBFBD>
## 简介
Continuous Desk <EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD>ƽ̨Զ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD><EFBFBD><EFBFBD>ͬһʱ<EFBFBD><EFBFBD>Զ<EFBFBD>̲ٿ<EFBFBD>ͬһ̨<EFBFBD><EFBFBD><EFBFBD>ԡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֧<EFBFBD>ֶ˵<EFBFBD><EFBFBD>˵<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Э<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Continuous Desk 是一个轻量级的跨平台远程桌面软件。它允许多个用户在同一时间远程操控同一台电脑。除桌面图像传输外,它还支持端到端的语音传输,在远程桌面基础上提供额外的协作能力。
Continuous Desk <EFBFBD><EFBFBD> [Projectx](https://github.com/dijunkun/projectx) ʵʱ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ӧ<EFBFBD>á<EFBFBD>Projectx <20><>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ<EFBFBD>ƽ̨ʵʱ<CAB5><CAB1><EFBFBD><EFBFBD>Ƶ<EFBFBD><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E2A1A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>͸<EFBFBD><CDB8><EFBFBD><EFBFBD>[RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><EFBFBD>Ӳ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>루H264<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƶ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>[Opus](https://github.com/xiph/opus)<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƣ<EFBFBD>[TCP over UDP](https://libnice.freedesktop.org/)<EFBFBD><EFBFBD><EFBFBD>Ȼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
Continuous Desk [Projectx](https://github.com/dijunkun/projectx) 实时音视频传输库的实验性应用。Projectx 是一个轻量级的跨平台实时音视频传输库。它具有网络透传([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)视频软硬编解码H264音频编解码[Opus](https://github.com/xiph/opus)),信令交互,网络拥塞控制([TCP over UDP](https://libnice.freedesktop.org/))等基础能力。
## ʹ<EFBFBD><EFBFBD>
## 使用
<EFBFBD>ڲ˵<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>REMOTE ID<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ID<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Connect<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɷ<EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӡ<EFBFBD>
在菜单栏“REMOTE ID”处输入远端桌面的ID点击“Connect”即可发起远程连接。
![usage1](https://github.com/dijunkun/continuous-desk/assets/29698109/80099485-f2db-4f09-9fb2-e811d87265dc)
![usage1](https://github.com/dijunkun/continuous-desk/assets/29698109/2ad59e6d-bdba-46d0-90cf-cbc9c06c2278)
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>򱾶<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д<EFBFBD><EFBFBD>ȷ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܳɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӡ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><EFBFBD>״̬<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֡<EFBFBD>Incorrect password<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><EFBFBD>
如果远端桌面设置了连接密码,则本端需填写正确的连接密码才能成功发起远程连接。密码错误时,状态栏会出现“Incorrect password”告警提示。
![incorrect password](https://github.com/dijunkun/continuous-desk/assets/29698109/0b16d21a-baa6-49c1-9d87-5e80978dd345)
![incorrect password](https://github.com/dijunkun/continuous-desk/assets/29698109/cb05501c-ec4e-4adf-952d-7a55ef770a97)
<EFBFBD><EFBFBD><EFBFBD>ӳɹ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>״̬<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С<EFBFBD>ClientConnected<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
连接成功建立后,状态栏会有“ClientConnected”相关字样。
![success](https://github.com/dijunkun/continuous-desk/assets/29698109/9c4f6604-2b1b-47b6-9c73-21c5026d1f26)
![success](https://github.com/dijunkun/continuous-desk/assets/29698109/0cca21f7-48fe-44a5-b83d-eafeb8a81eb1)
## <EFBFBD><EFBFBD><EFBFBD><EFBFBD>
## 编译
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
依赖:
- [xmake](https://xmake.io/#/guide/installation)
- [cmake](https://cmake.org/download/)
- [vcpkg](https://vcpkg.io/en/getting-started)
Linux<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>谲װ<EFBFBD><EFBFBD><EFBFBD>°<EFBFBD><EFBFBD><EFBFBD>
Linux环境下需安装以下包:
```
sudo apt-get install -y nvidia-cuda-toolkit libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev libxcb-xfixes0-dev libxcb-shm0-dev libxv-dev libasound2-dev libsndio-dev libasound2-dev libpulse-dev
```
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
编译命令
```
git clone https://github.com/dijunkun/continuous-desk
@@ -53,15 +53,15 @@ git submodule update
xmake b remote_desk
```
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
运行
```
# Windows/MacOS
xmake r remote_desk
# Linux<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD><EFBFBD>rootȨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
# Linux下需使用root权限运行
./remote_desk
```
## <EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤
## 许可证
Continuous Desk ʹ<EFBFBD><EFBFBD> MIT <EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʹ<EFBFBD>õ<EFBFBD><EFBFBD>ĵ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD>зַ<EFBFBD><EFBFBD><EFBFBD>
Continuous Desk 使用 MIT 许可证,其中使用到的第三方库根据自身许可证进行分发。

View File

@@ -1,16 +1,19 @@
[signal server]
ip = 120.77.216.215
ip = 150.158.81.30
port = 9099
[stun server]
ip = 120.77.216.215
ip = 150.158.81.30
port = 3478
[turn server]
ip = 120.77.216.215
ip = 150.158.81.30
port = 3478
username = dijunkun
password = dijunkunpw
[hardware acceleration]
turn_on = false
[av1 encoding]
turn_on = true

View File

@@ -0,0 +1,38 @@
#include "config_center.h"
ConfigCenter::ConfigCenter() {}
ConfigCenter::~ConfigCenter() {}
int ConfigCenter::SetLanguage(LANGUAGE language) {
language_ = language;
return 0;
}
int ConfigCenter::SetVideoQuality(VIDEO_QUALITY video_quality) {
video_quality_ = video_quality;
return 0;
}
int ConfigCenter::SetVideoEncodeFormat(
VIDEO_ENCODE_FORMAT video_encode_format) {
video_encode_format_ = video_encode_format;
return 0;
}
int ConfigCenter::SetHardwareVideoCodec(bool hardware_video_codec) {
hardware_video_codec_ = hardware_video_codec;
return 0;
}
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() { return language_; }
ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() {
return video_quality_;
}
ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() {
return video_encode_format_;
}
bool ConfigCenter::IsHardwareVideoCodec() { return hardware_video_codec_; }

View File

@@ -0,0 +1,40 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _CONFIG_CENTER_H_
#define _CONFIG_CENTER_H_
class ConfigCenter {
public:
enum class LANGUAGE { CHINESE = 0, ENGLISH = 1 };
enum class VIDEO_QUALITY { LOW = 0, MEDIUM = 1, HIGH = 2 };
enum class VIDEO_ENCODE_FORMAT { AV1 = 0, H264 = 1 };
public:
ConfigCenter();
~ConfigCenter();
public:
int SetLanguage(LANGUAGE language);
int SetVideoQuality(VIDEO_QUALITY video_quality);
int SetVideoEncodeFormat(VIDEO_ENCODE_FORMAT video_encode_format);
int SetHardwareVideoCodec(bool hardware_video_codec);
public:
LANGUAGE GetLanguage();
VIDEO_QUALITY GetVideoQuality();
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat();
bool IsHardwareVideoCodec();
private:
// Default value should be same with parameters in localization.h
LANGUAGE language_ = LANGUAGE::CHINESE;
VIDEO_QUALITY video_quality_ = VIDEO_QUALITY::MEDIUM;
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::AV1;
bool hardware_video_codec_ = false;
};
#endif

View File

@@ -4,7 +4,12 @@
MouseController::MouseController() {}
MouseController::~MouseController() {}
MouseController::~MouseController() {
if (uinput_fd_) {
ioctl(uinput_fd_, UI_DEV_DESTROY);
close(uinput_fd_);
}
}
int MouseController::Init(int screen_width, int screen_height) {
screen_width_ = screen_width;
@@ -13,6 +18,7 @@ int MouseController::Init(int screen_width, int screen_height) {
uinput_fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (uinput_fd_ < 0) {
LOG_ERROR("Cannot open device: /dev/uinput");
return -1;
}
ioctl(uinput_fd_, UI_SET_EVBIT, EV_KEY);
@@ -35,15 +41,12 @@ int MouseController::Init(int screen_width, int screen_height) {
uidev.absmin[ABS_Y] = 0;
uidev.absmax[ABS_Y] = screen_height_;
write(uinput_fd_, &uidev, sizeof(uidev));
int res_uidev = write(uinput_fd_, &uidev, sizeof(uidev));
ioctl(uinput_fd_, UI_DEV_CREATE);
return 0;
}
int MouseController::Destroy() {
ioctl(uinput_fd_, UI_DEV_DESTROY);
close(uinput_fd_);
}
int MouseController::Destroy() { return 0; }
int MouseController::SendCommand(RemoteAction remote_action) {
int mouse_pos_x = remote_action.m.x * screen_width_ / 1280;
@@ -71,6 +74,7 @@ int MouseController::SendCommand(RemoteAction remote_action) {
}
void MouseController::SimulateKeyDown(int fd, int kval) {
int res_ev = 0;
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, 0);
@@ -78,15 +82,16 @@ void MouseController::SimulateKeyDown(int fd, int kval) {
event.type = EV_KEY;
event.value = 1;
event.code = kval;
write(fd, &event, sizeof(event));
res_ev = write(fd, &event, sizeof(event));
event.type = EV_SYN;
event.value = 0;
event.code = SYN_REPORT;
write(fd, &event, sizeof(event));
res_ev = write(fd, &event, sizeof(event));
}
void MouseController::SimulateKeyUp(int fd, int kval) {
int res_ev = 0;
struct input_event event;
memset(&event, 0, sizeof(event));
gettimeofday(&event.time, 0);
@@ -94,12 +99,12 @@ void MouseController::SimulateKeyUp(int fd, int kval) {
event.type = EV_KEY;
event.value = 0;
event.code = kval;
write(fd, &event, sizeof(event));
res_ev = write(fd, &event, sizeof(event));
event.type = EV_SYN;
event.value = 0;
event.code = SYN_REPORT;
write(fd, &event, sizeof(event));
res_ev = write(fd, &event, sizeof(event));
}
void MouseController::SetMousePosition(int fd, int x, int y) {

View File

@@ -1,5 +1,3 @@
#include <SDL.h>
#include <stdio.h>
#ifdef _WIN32
#ifdef REMOTE_DESK_DEBUG
#pragma comment(linker, "/subsystem:\"console\"")
@@ -8,865 +6,14 @@
#endif
#endif
#include <atomic>
#include <chrono>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include "platform.h"
extern "C" {
#include <libavdevice/avdevice.h>
#include <libswresample/swresample.h>
#include <libswscale/swscale.h>
};
#include <stdio.h>
#include "../../thirdparty/projectx/src/interface/x.h"
#include "device_controller_factory.h"
#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_sdlrenderer2.h"
#include "log.h"
#include "screen_capturer_factory.h"
#include "main_window.h"
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
#ifdef REMOTE_DESK_DEBUG
#define MOUSE_CONTROL 0
#else
#define MOUSE_CONTROL 1
#endif
int screen_w = 1280, screen_h = 720;
int window_w = 1280, window_h = 720;
const int pixel_w = 1280, pixel_h = 720;
unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2];
unsigned char audio_buffer[960];
SDL_Texture *sdlTexture = nullptr;
SDL_Renderer *sdlRenderer = nullptr;
SDL_Rect sdlRect;
SDL_Window *window;
static SDL_AudioDeviceID input_dev;
static SDL_AudioDeviceID output_dev;
uint32_t start_time, end_time, elapsed_time;
uint32_t frame_count = 0;
int fps = 0;
static std::atomic<bool> audio_buffer_fresh = false;
static uint32_t last_ts = 0;
int64_t src_ch_layout = AV_CH_LAYOUT_MONO;
int src_rate = 48000;
enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_S16;
int src_nb_channels = 0;
uint8_t **src_data = NULL;
int src_linesize;
int src_nb_samples = 480;
int64_t dst_ch_layout = AV_CH_LAYOUT_MONO;
int dst_rate = 48000;
enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;
int dst_nb_channels = 0;
uint8_t **dst_data = NULL;
int dst_linesize;
int dst_nb_samples;
int max_dst_nb_samples;
int dst_bufsize;
struct SwrContext *swr_ctx;
int ret;
int audio_len = 0;
std::string window_title = "Remote Desk Client";
std::string server_connection_status_str = "-";
std::string client_connection_status_str = "-";
std::string server_signal_status_str = "-";
std::string client_signal_status_str = "-";
std::atomic<ConnectionStatus> server_connection_status =
ConnectionStatus::Closed;
std::atomic<ConnectionStatus> client_connection_status =
ConnectionStatus::Closed;
std::atomic<SignalStatus> server_signal_status = SignalStatus::SignalClosed;
std::atomic<SignalStatus> client_signal_status = SignalStatus::SignalClosed;
// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define QUIT_EVENT (SDL_USEREVENT + 2)
typedef struct {
char password[16];
} CDCache;
int thread_exit = 0;
PeerPtr *peer_server = nullptr;
PeerPtr *peer_client = nullptr;
bool joined = false;
bool received_frame = false;
bool menu_hovered = false;
static bool connect_button_pressed = false;
static const char *connect_label = "Connect";
static char input_password[7] = "";
static FILE *cd_cache_file = nullptr;
static CDCache cd_cache;
static bool is_create_connection = false;
static bool done = false;
ScreenCapturerFactory *screen_capturer_factory = nullptr;
ScreenCapturer *screen_capturer = nullptr;
DeviceControllerFactory *device_controller_factory = nullptr;
MouseController *mouse_controller = nullptr;
char *nv12_buffer = nullptr;
#ifdef __linux__
std::chrono::_V2::system_clock::time_point last_frame_time_;
#else
std::chrono::steady_clock::time_point last_frame_time_;
#endif
inline int ProcessMouseKeyEven(SDL_Event &ev) {
float ratio = (float)(1280.0 / window_w);
RemoteAction remote_action;
remote_action.m.x = (size_t)(ev.button.x * ratio);
remote_action.m.y = (size_t)(ev.button.y * ratio);
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
{
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
if (SDLK_DOWN == ev.key.keysym.sym) {
// printf("SDLK_DOWN \n");
} else if (SDLK_UP == ev.key.keysym.sym) {
// printf("SDLK_UP \n");
} else if (SDLK_LEFT == ev.key.keysym.sym) {
// printf("SDLK_LEFT \n");
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
// printf("SDLK_RIGHT \n");
}
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_down;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_down;
}
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEBUTTONUP == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_up;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_up;
}
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEMOTION == ev.type) {
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::move;
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_QUIT == ev.type) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
printf("SDL_QUIT\n");
return 0;
}
return 0;
}
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
int64_t delay = swr_get_delay(swr_ctx, src_rate);
dst_nb_samples = (int)av_rescale_rnd(delay + src_nb_samples, dst_rate,
src_rate, AV_ROUND_UP);
if (dst_nb_samples > max_dst_nb_samples) {
av_freep(&dst_data[0]);
ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,
dst_nb_samples, dst_sample_fmt, 1);
if (ret < 0) return;
max_dst_nb_samples = dst_nb_samples;
}
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples,
(const uint8_t **)&stream, src_nb_samples);
if (ret < 0) {
fprintf(stderr, "Error while converting\n");
return;
}
dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, ret,
dst_sample_fmt, 1);
if (dst_bufsize < 0) {
fprintf(stderr, "Could not get sample buffer size\n");
return;
}
if (1) {
if ("ClientConnected" == client_connection_status_str) {
SendData(peer_client, DATA_TYPE::AUDIO, (const char *)dst_data[0],
dst_bufsize);
}
if ("ServerConnected" == server_connection_status_str) {
SendData(peer_server, DATA_TYPE::AUDIO, (const char *)dst_data[0],
dst_bufsize);
}
} else {
memcpy(audio_buffer, dst_data[0], dst_bufsize);
audio_len = dst_bufsize;
SDL_Delay(10);
audio_buffer_fresh = true;
}
}
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
// if ("ClientConnected" != client_connection_status_str) {
// return;
// }
if (!audio_buffer_fresh) {
return;
}
SDL_memset(stream, 0, len);
if (audio_len == 0) {
return;
} else {
}
len = (len > audio_len ? audio_len : len);
SDL_MixAudioFormat(stream, audio_buffer, AUDIO_S16LSB, len,
SDL_MIX_MAXVOLUME);
audio_buffer_fresh = false;
}
void ServerReceiveVideoBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {}
void ClientReceiveVideoBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// std::cout << "Receive: [" << user_id << "] " << std::endl;
if (joined) {
memcpy(dst_buffer, data, size);
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
received_frame = true;
}
}
void ServerReceiveAudioBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// memset(audio_buffer, 0, size);
// memcpy(audio_buffer, data, size);
// audio_len = size;
audio_buffer_fresh = true;
SDL_QueueAudio(output_dev, data, (Uint32)size);
// printf("queue audio\n");
}
void ClientReceiveAudioBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// std::cout << "Client receive audio, size " << size << ", user [" << user_id
// << "] " << std::endl;
SDL_QueueAudio(output_dev, data, (Uint32)size);
}
void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id,
size_t user_id_size) {
std::string user(user_id, user_id_size);
RemoteAction remote_action;
memcpy(&remote_action, data, sizeof(remote_action));
// std::cout << "remote_action: " << remote_action.type << " "
// << remote_action.m.flag << " " << remote_action.m.x << " "
// << remote_action.m.y << std::endl;
#if MOUSE_CONTROL
mouse_controller->SendCommand(remote_action);
#endif
}
void ClientReceiveDataBuffer(const char *data, size_t size, const char *user_id,
size_t user_id_size) {}
void ServerSignalStatus(SignalStatus status) {
server_signal_status = status;
if (SignalStatus::SignalConnecting == status) {
server_signal_status_str = "ServerSignalConnecting";
} else if (SignalStatus::SignalConnected == status) {
server_signal_status_str = "ServerSignalConnected";
} else if (SignalStatus::SignalFailed == status) {
server_signal_status_str = "ServerSignalFailed";
} else if (SignalStatus::SignalClosed == status) {
server_signal_status_str = "ServerSignalClosed";
} else if (SignalStatus::SignalReconnecting == status) {
server_signal_status_str = "ServerSignalReconnecting";
}
}
void ClientSignalStatus(SignalStatus status) {
client_signal_status = status;
if (SignalStatus::SignalConnecting == status) {
client_signal_status_str = "ClientSignalConnecting";
} else if (SignalStatus::SignalConnected == status) {
client_signal_status_str = "ClientSignalConnected";
} else if (SignalStatus::SignalFailed == status) {
client_signal_status_str = "ClientSignalFailed";
} else if (SignalStatus::SignalClosed == status) {
client_signal_status_str = "ClientSignalClosed";
} else if (SignalStatus::SignalReconnecting == status) {
client_signal_status_str = "ClientSignalReconnecting";
}
}
void ServerConnectionStatus(ConnectionStatus status) {
server_connection_status = status;
if (ConnectionStatus::Connecting == status) {
server_connection_status_str = "ServerConnecting";
} else if (ConnectionStatus::Connected == status) {
server_connection_status_str = "ServerConnected";
} else if (ConnectionStatus::Disconnected == status) {
server_connection_status_str = "ServerDisconnected";
} else if (ConnectionStatus::Failed == status) {
server_connection_status_str = "ServerFailed";
} else if (ConnectionStatus::Closed == status) {
server_connection_status_str = "ServerClosed";
} else if (ConnectionStatus::IncorrectPassword == status) {
server_connection_status_str = "Incorrect password";
if (connect_button_pressed) {
connect_button_pressed = false;
joined = false;
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
}
}
}
void ClientConnectionStatus(ConnectionStatus status) {
client_connection_status = status;
if (ConnectionStatus::Connecting == status) {
client_connection_status_str = "ClientConnecting";
} else if (ConnectionStatus::Connected == status) {
client_connection_status_str = "ClientConnected";
joined = true;
} else if (ConnectionStatus::Disconnected == status) {
client_connection_status_str = "ClientDisconnected";
} else if (ConnectionStatus::Failed == status) {
client_connection_status_str = "ClientFailed";
} else if (ConnectionStatus::Closed == status) {
client_connection_status_str = "ClientClosed";
} else if (ConnectionStatus::IncorrectPassword == status) {
client_connection_status_str = "Incorrect password";
if (connect_button_pressed) {
connect_button_pressed = false;
joined = false;
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
}
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
client_connection_status_str = "No such transmission id";
if (connect_button_pressed) {
connect_button_pressed = false;
joined = false;
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
}
}
}
int initResampler() {
/* create resampler context */
swr_ctx = swr_alloc();
if (!swr_ctx) {
fprintf(stderr, "Could not allocate resampler context\n");
ret = AVERROR(ENOMEM);
return -1;
}
/* set options */
av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);
av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0);
av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0);
av_opt_set_int(swr_ctx, "out_sample_rate", dst_rate, 0);
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);
/* initialize the resampling context */
if ((ret = swr_init(swr_ctx)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
return -1;
}
/* allocate source and destination samples buffers */
src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);
ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize,
src_nb_channels, src_nb_samples,
src_sample_fmt, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate source samples\n");
return -1;
}
max_dst_nb_samples = dst_nb_samples =
av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
/* buffer is going to be directly written to a rawaudio file, no alignment */
dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize,
dst_nb_channels, dst_nb_samples,
dst_sample_fmt, 0);
if (ret < 0) {
fprintf(stderr, "Could not allocate destination samples\n");
return -1;
}
}
int main() {
int main(int argc, char *argv[]) {
LOG_INFO("Remote desk");
MainWindow main_window;
last_ts = static_cast<uint32_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count());
initResampler();
cd_cache_file = fopen("cache.cd", "r+");
if (cd_cache_file) {
fseek(cd_cache_file, 0, SEEK_SET);
fread(&cd_cache.password, sizeof(cd_cache.password), 1, cd_cache_file);
fclose(cd_cache_file);
strncpy(input_password, cd_cache.password, sizeof(cd_cache.password));
}
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
SDL_INIT_GAMECONTROLLER) != 0) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
// From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
// Create window with SDL_Renderer graphics context
SDL_WindowFlags window_flags =
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
window = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, window_w, window_h,
window_flags);
SDL_DisplayMode DM;
SDL_GetCurrentDisplayMode(0, &DM);
screen_w = DM.w;
screen_h = DM.h;
sdlRenderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
if (sdlRenderer == nullptr) {
SDL_Log("Error creating SDL_Renderer!");
return 0;
}
Uint32 pixformat = 0;
pixformat = SDL_PIXELFORMAT_NV12;
sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,
SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
// Audio
SDL_AudioSpec want_in, have_in, want_out, have_out;
SDL_zero(want_in);
want_in.freq = 48000;
want_in.format = AUDIO_S16LSB;
want_in.channels = 1;
want_in.samples = 480;
want_in.callback = SdlCaptureAudioIn;
input_dev = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0);
if (input_dev == 0) {
SDL_Log("Failed to open input: %s", SDL_GetError());
return 1;
}
SDL_zero(want_out);
want_out.freq = 48000;
want_out.format = AUDIO_S16LSB;
want_out.channels = 1;
// want_out.silence = 0;
want_out.samples = 480;
want_out.callback = NULL;
output_dev = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out, 0);
if (output_dev == 0) {
SDL_Log("Failed to open input: %s", SDL_GetError());
return 1;
}
SDL_PauseAudioDevice(input_dev, 0);
SDL_PauseAudioDevice(output_dev, 0);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Dear ImGui style
ImGui::StyleColorsDark();
// ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForSDLRenderer(window, sdlRenderer);
ImGui_ImplSDLRenderer2_Init(sdlRenderer);
// Our state
bool show_demo_window = true;
bool show_another_window = false;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
std::string mac_addr_str = GetMac();
std::thread rtc_thread(
[](int screen_width, int screen_height) {
std::string default_cfg_path = "../../../../config/config.ini";
std::ifstream f(default_cfg_path.c_str());
std::string mac_addr_str = GetMac();
Params server_params;
server_params.cfg_path =
f.good() ? "../../../../config/config.ini" : "config.ini";
server_params.on_receive_video_buffer = ServerReceiveVideoBuffer;
server_params.on_receive_audio_buffer = ServerReceiveAudioBuffer;
server_params.on_receive_data_buffer = ServerReceiveDataBuffer;
server_params.on_signal_status = ServerSignalStatus;
server_params.on_connection_status = ServerConnectionStatus;
Params client_params;
client_params.cfg_path =
f.good() ? "../../../../config/config.ini" : "config.ini";
client_params.on_receive_video_buffer = ClientReceiveVideoBuffer;
client_params.on_receive_audio_buffer = ClientReceiveAudioBuffer;
client_params.on_receive_data_buffer = ClientReceiveDataBuffer;
client_params.on_signal_status = ClientSignalStatus;
client_params.on_connection_status = ClientConnectionStatus;
std::string transmission_id = "000001";
peer_server = CreatePeer(&server_params);
LOG_INFO("Create peer_server");
std::string server_user_id = "S-" + mac_addr_str;
Init(peer_server, server_user_id.c_str());
LOG_INFO("peer_server init finish");
peer_client = CreatePeer(&client_params);
LOG_INFO("Create peer_client");
std::string client_user_id = "C-" + mac_addr_str;
Init(peer_client, client_user_id.c_str());
LOG_INFO("peer_client init finish");
{
while (SignalStatus::SignalConnected != server_signal_status &&
!done) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (done) {
return;
}
std::string user_id = "S-" + mac_addr_str;
is_create_connection =
CreateConnection(peer_server, mac_addr_str.c_str(),
input_password)
? false
: true;
nv12_buffer = new char[NV12_BUFFER_SIZE];
// Screen capture
screen_capturer_factory = new ScreenCapturerFactory();
screen_capturer = (ScreenCapturer *)screen_capturer_factory->Create();
last_frame_time_ = std::chrono::high_resolution_clock::now();
ScreenCapturer::RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = screen_w;
rect.bottom = screen_h;
screen_capturer->Init(
rect, 60,
[](unsigned char *data, int size, int width, int height) -> void {
auto now_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration =
now_time - last_frame_time_;
auto tc = duration.count() * 1000;
if (tc >= 0) {
SendData(peer_server, DATA_TYPE::VIDEO, (const char *)data,
NV12_BUFFER_SIZE);
last_frame_time_ = now_time;
}
});
screen_capturer->Start();
// Mouse control
device_controller_factory = new DeviceControllerFactory();
mouse_controller =
(MouseController *)device_controller_factory->Create(
DeviceControllerFactory::Device::Mouse);
mouse_controller->Init(screen_w, screen_h);
}
},
screen_w, screen_h);
// Main loop
while (!done) {
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
if (joined && !menu_hovered) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
}
{
static float f = 0.0f;
static int counter = 0;
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
ImGui::SetNextWindowSize(ImVec2(190, 200));
ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_NoResize);
{
menu_hovered = ImGui::IsWindowHovered();
ImGui::Text(" LOCAL ID:");
ImGui::SameLine();
ImGui::SetNextItemWidth(95);
ImGui::InputText(
"##local_id", (char *)mac_addr_str.c_str(),
mac_addr_str.length() + 1,
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
ImGui::Text(" PASSWORD:");
ImGui::SameLine();
ImGui::SetNextItemWidth(95);
char input_password_tmp[7] = "";
strncpy(input_password_tmp, input_password, sizeof(input_password));
ImGui::InputTextWithHint("##server_pwd", "max 6 chars", input_password,
IM_ARRAYSIZE(input_password),
ImGuiInputTextFlags_CharsNoBlank);
if (strcmp(input_password_tmp, input_password)) {
cd_cache_file = fopen("cache.cd", "w+");
if (cd_cache_file) {
fseek(cd_cache_file, 0, SEEK_SET);
strncpy(cd_cache.password, input_password, sizeof(input_password));
fwrite(&cd_cache.password, sizeof(cd_cache.password), 1,
cd_cache_file);
fclose(cd_cache_file);
}
LeaveConnection(peer_server);
CreateConnection(peer_server, mac_addr_str.c_str(), input_password);
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
{
{
static char remote_id[20] = "";
ImGui::Text("REMOTE ID:");
ImGui::SameLine();
ImGui::SetNextItemWidth(95);
ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(),
remote_id, IM_ARRAYSIZE(remote_id),
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_CharsNoBlank);
ImGui::Spacing();
ImGui::Text(" PASSWORD:");
ImGui::SameLine();
ImGui::SetNextItemWidth(95);
static char client_password[20] = "";
ImGui::InputTextWithHint("##client_pwd", "max 6 chars",
client_password,
IM_ARRAYSIZE(client_password),
ImGuiInputTextFlags_CharsNoBlank);
if (ImGui::Button(connect_label)) {
int ret = -1;
if ("ClientSignalConnected" == client_signal_status_str) {
if (strcmp(connect_label, "Connect") == 0 && !joined) {
std::string user_id = "C-" + mac_addr_str;
ret = JoinConnection(peer_client, remote_id, client_password);
if (0 == ret) {
// joined = true;
}
} else if (strcmp(connect_label, "Disconnect") == 0 && joined) {
ret = LeaveConnection(peer_client);
memset(audio_buffer, 0, 960);
if (0 == ret) {
joined = false;
received_frame = false;
}
}
if (0 == ret) {
connect_button_pressed = !connect_button_pressed;
connect_label =
connect_button_pressed ? "Disconnect" : "Connect";
}
}
}
}
}
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
{
if (ImGui::Button("Resize Window")) {
SDL_GetWindowSize(window, &window_w, &window_h);
if (window_h != window_w * 9 / 16) {
window_w = window_h * 16 / 9;
}
SDL_SetWindowSize(window, window_w, window_h);
}
}
ImGui::End();
}
// Rendering
ImGui::Render();
SDL_RenderSetScale(sdlRenderer, io.DisplayFramebufferScale.x,
io.DisplayFramebufferScale.y);
SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT) {
done = true;
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_RESIZED) {
SDL_GetWindowSize(window, &window_w, &window_h);
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_CLOSE &&
event.window.windowID == SDL_GetWindowID(window)) {
done = true;
} else if (event.type == REFRESH_EVENT) {
sdlRect.x = 0;
sdlRect.y = 0;
sdlRect.w = window_w;
sdlRect.h = window_h;
SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w);
} else {
if (joined) {
ProcessMouseKeyEven(event);
}
}
}
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
if (!joined || !received_frame) {
SDL_RenderClear(sdlRenderer);
SDL_SetRenderDrawColor(
sdlRenderer, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
}
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
SDL_RenderPresent(sdlRenderer);
frame_count++;
end_time = SDL_GetTicks();
elapsed_time = end_time - start_time;
if (elapsed_time >= 1000) {
fps = frame_count / (elapsed_time / 1000);
frame_count = 0;
window_title = "Remote Desk Client FPS [" + std::to_string(fps) +
"] status [" + server_signal_status_str + "|" +
client_signal_status_str + "|" +
server_connection_status_str + "|" +
client_connection_status_str + "]";
// For MacOS, UI frameworks can only be called from the main thread
SDL_SetWindowTitle(window, window_title.c_str());
start_time = end_time;
}
}
// Cleanup
if (is_create_connection) {
LeaveConnection(peer_server);
}
if (joined) {
LeaveConnection(peer_client);
}
rtc_thread.join();
SDL_CloseAudioDevice(output_dev);
SDL_CloseAudioDevice(input_dev);
mouse_controller->Destroy();
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow(window);
SDL_CloseAudio();
SDL_Quit();
main_window.Run();
return 0;
}

View File

@@ -0,0 +1,874 @@
#include <SDL.h>
#include <stdio.h>
#ifdef _WIN32
#ifdef REMOTE_DESK_DEBUG
#pragma comment(linker, "/subsystem:\"console\"")
#else
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#endif
#endif
#include <stdio.h>
#include <atomic>
#include <chrono>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include "../../thirdparty/projectx/src/interface/x.h"
#include "config_center.h"
#include "device_controller_factory.h"
#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_sdlrenderer2.h"
#include "log.h"
#include "platform.h"
#include "screen_capturer_factory.h"
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
#ifdef REMOTE_DESK_DEBUG
#define MOUSE_CONTROL 0
#else
#define MOUSE_CONTROL 1
#endif
#define CHINESE_FONT 1
int screen_w = 1280, screen_h = 720;
int window_w = 1280, window_h = 720;
const int pixel_w = 1280, pixel_h = 720;
unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2];
unsigned char audio_buffer[960];
SDL_Texture *sdlTexture = nullptr;
SDL_Renderer *sdlRenderer = nullptr;
SDL_Rect sdlRect;
SDL_Window *window;
static SDL_AudioDeviceID input_dev;
static SDL_AudioDeviceID output_dev;
uint32_t start_time, end_time, elapsed_time;
uint32_t frame_count = 0;
int fps = 0;
static std::atomic<bool> audio_buffer_fresh{false};
static uint32_t last_ts = 0;
int dst_bufsize;
struct SwrContext *swr_ctx;
int ret;
int audio_len = 0;
std::string window_title = "Remote Desk Client";
std::string connection_status_str = "-";
std::string signal_status_str = "-";
std::atomic<SignalStatus> signal_status{SignalStatus::SignalClosed};
std::atomic<ConnectionStatus> connection_status{ConnectionStatus::Closed};
// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define QUIT_EVENT (SDL_USEREVENT + 2)
typedef struct {
char password[7];
} CDCache;
int thread_exit = 0;
PeerPtr *peer_server = nullptr;
// PeerPtr *peer_server = nullptr;
bool joined = false;
bool received_frame = false;
bool menu_hovered = false;
static bool connect_button_pressed = false;
static bool fullscreen_button_pressed = false;
#if CHINESE_FONT
static const char *connect_label = u8"连接";
static const char *fullscreen_label = u8"全屏";
#else
static const char *connect_label = "Connect";
static const char *fullscreen_label = "FULLSCREEN";
#endif
static char input_password[7] = "";
static FILE *cd_cache_file = nullptr;
static CDCache cd_cache;
static bool is_create_connection = false;
static bool done = false;
ScreenCapturerFactory *screen_capturer_factory = nullptr;
ScreenCapturer *screen_capturer = nullptr;
DeviceControllerFactory *device_controller_factory = nullptr;
MouseController *mouse_controller = nullptr;
ConfigCenter config_center;
char *nv12_buffer = nullptr;
#ifdef __linux__
std::chrono::_V2::system_clock::time_point last_frame_time_;
#else
std::chrono::steady_clock::time_point last_frame_time_;
#endif
inline int ProcessMouseKeyEven(SDL_Event &ev) {
float ratio = (float)(1280.0 / window_w);
RemoteAction remote_action;
remote_action.m.x = (size_t)(ev.button.x * ratio);
remote_action.m.y = (size_t)(ev.button.y * ratio);
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
{
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
if (SDLK_DOWN == ev.key.keysym.sym) {
// printf("SDLK_DOWN \n");
} else if (SDLK_UP == ev.key.keysym.sym) {
// printf("SDLK_UP \n");
} else if (SDLK_LEFT == ev.key.keysym.sym) {
// printf("SDLK_LEFT \n");
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
// printf("SDLK_RIGHT \n");
}
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_down;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_down;
}
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEBUTTONUP == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_up;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_up;
}
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEMOTION == ev.type) {
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::move;
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_QUIT == ev.type) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
printf("SDL_QUIT\n");
return 0;
}
return 0;
}
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
if (1) {
if ("Connected" == connection_status_str) {
SendData(peer_server, DATA_TYPE::AUDIO, (const char *)stream, len);
}
} else {
memcpy(audio_buffer, stream, len);
audio_len = len;
SDL_Delay(10);
audio_buffer_fresh = true;
}
}
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
if (!audio_buffer_fresh) {
return;
}
SDL_memset(stream, 0, len);
if (audio_len == 0) {
return;
} else {
}
len = (len > audio_len ? audio_len : len);
SDL_MixAudioFormat(stream, audio_buffer, AUDIO_S16LSB, len,
SDL_MIX_MAXVOLUME);
audio_buffer_fresh = false;
}
void ServerReceiveVideoBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
if (joined) {
memcpy(dst_buffer, data, size);
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
received_frame = true;
}
}
void ClientReceiveVideoBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// std::cout << "Receive: [" << user_id << "] " << std::endl;
if (joined) {
memcpy(dst_buffer, data, size);
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
received_frame = true;
}
}
void ServerReceiveAudioBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// memset(audio_buffer, 0, size);
// memcpy(audio_buffer, data, size);
// audio_len = size;
audio_buffer_fresh = true;
SDL_QueueAudio(output_dev, data, (Uint32)size);
// printf("queue audio\n");
}
void ClientReceiveAudioBuffer(const char *data, size_t size,
const char *user_id, size_t user_id_size) {
// std::cout << "Client receive audio, size " << size << ", user [" << user_id
// << "] " << std::endl;
SDL_QueueAudio(output_dev, data, (Uint32)size);
}
void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id,
size_t user_id_size) {
std::string user(user_id, user_id_size);
RemoteAction remote_action;
memcpy(&remote_action, data, sizeof(remote_action));
// std::cout << "remote_action: " << remote_action.type << " "
// << remote_action.m.flag << " " << remote_action.m.x << " "
// << remote_action.m.y << std::endl;
#if MOUSE_CONTROL
if (mouse_controller) {
mouse_controller->SendCommand(remote_action);
}
#endif
}
void ClientReceiveDataBuffer(const char *data, size_t size, const char *user_id,
size_t user_id_size) {}
void SignalStatus(SignalStatus status) {
signal_status = status;
if (SignalStatus::SignalConnecting == status) {
signal_status_str = "SignalConnecting";
} else if (SignalStatus::SignalConnected == status) {
signal_status_str = "SignalConnected";
} else if (SignalStatus::SignalFailed == status) {
signal_status_str = "SignalFailed";
} else if (SignalStatus::SignalClosed == status) {
signal_status_str = "SignalClosed";
} else if (SignalStatus::SignalReconnecting == status) {
signal_status_str = "SignalReconnecting";
}
}
void ConnectionStatus(ConnectionStatus status) {
connection_status = status;
if (ConnectionStatus::Connecting == status) {
connection_status_str = "Connecting";
} else if (ConnectionStatus::Connected == status) {
connection_status_str = "Connected";
joined = true;
} else if (ConnectionStatus::Disconnected == status) {
connection_status_str = "Disconnected";
} else if (ConnectionStatus::Failed == status) {
connection_status_str = "Failed";
} else if (ConnectionStatus::Closed == status) {
connection_status_str = "Closed";
} else if (ConnectionStatus::IncorrectPassword == status) {
connection_status_str = "Incorrect password";
if (connect_button_pressed) {
connect_button_pressed = false;
joined = false;
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
}
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
connection_status_str = "No such transmission id";
if (connect_button_pressed) {
connect_button_pressed = false;
joined = false;
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
}
}
}
int main(int argc, char *argv[]) {
LOG_INFO("Remote desk");
last_ts = static_cast<uint32_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count());
cd_cache_file = fopen("cache.cd", "r+");
if (cd_cache_file) {
fseek(cd_cache_file, 0, SEEK_SET);
fread(&cd_cache.password, sizeof(cd_cache.password), 1, cd_cache_file);
fclose(cd_cache_file);
strncpy(input_password, cd_cache.password, sizeof(cd_cache.password));
}
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
SDL_INIT_GAMECONTROLLER) != 0) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
// From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
// Create window with SDL_Renderer graphics context
SDL_WindowFlags window_flags =
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
window = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, window_w, window_h,
window_flags);
SDL_DisplayMode DM;
SDL_GetCurrentDisplayMode(0, &DM);
screen_w = DM.w;
screen_h = DM.h;
sdlRenderer = SDL_CreateRenderer(
window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
if (sdlRenderer == nullptr) {
SDL_Log("Error creating SDL_Renderer!");
return 0;
}
Uint32 pixformat = 0;
pixformat = SDL_PIXELFORMAT_NV12;
sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,
SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
// Audio
SDL_AudioSpec want_in, have_in, want_out, have_out;
SDL_zero(want_in);
want_in.freq = 48000;
want_in.format = AUDIO_S16LSB;
want_in.channels = 1;
want_in.samples = 480;
want_in.callback = SdlCaptureAudioIn;
input_dev = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0);
if (input_dev == 0) {
SDL_Log("Failed to open input: %s", SDL_GetError());
// return 1;
}
SDL_zero(want_out);
want_out.freq = 48000;
want_out.format = AUDIO_S16LSB;
want_out.channels = 1;
// want_out.silence = 0;
want_out.samples = 480;
want_out.callback = NULL;
output_dev = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out, 0);
if (output_dev == 0) {
SDL_Log("Failed to open input: %s", SDL_GetError());
// return 1;
}
SDL_PauseAudioDevice(input_dev, 0);
SDL_PauseAudioDevice(output_dev, 0);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
#if CHINESE_FONT
// Load Fonts
#ifdef _WIN32
std::string default_font_path = "c:/windows/fonts/simhei.ttf";
std::ifstream font_path_f(default_font_path.c_str());
std::string font_path =
font_path_f.good() ? "c:/windows/fonts/simhei.ttf" : "";
if (!font_path.empty()) {
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
}
#elif __APPLE__
std::string default_font_path = "/System/Library/Fonts/PingFang.ttc";
std::ifstream font_path_f(default_font_path.c_str());
std::string font_path =
font_path_f.good() ? "/System/Library/Fonts/PingFang.ttc" : "";
if (!font_path.empty()) {
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
}
#elif __linux__
io.Fonts->AddFontFromFileTTF("c:/windows/fonts/msyh.ttc", 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
#endif
#endif
// Setup Dear ImGui style
// ImGui::StyleColorsDark();
ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForSDLRenderer(window, sdlRenderer);
ImGui_ImplSDLRenderer2_Init(sdlRenderer);
// Our state
bool show_demo_window = true;
bool show_another_window = false;
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
std::string mac_addr_str = GetMac();
std::thread rtc_thread(
[](int screen_width, int screen_height) {
std::string default_cfg_path = "../../../../config/config.ini";
std::ifstream f(default_cfg_path.c_str());
std::string mac_addr_str = GetMac();
Params server_params;
server_params.cfg_path =
f.good() ? "../../../../config/config.ini" : "config.ini";
server_params.on_receive_video_buffer = ServerReceiveVideoBuffer;
server_params.on_receive_audio_buffer = ServerReceiveAudioBuffer;
server_params.on_receive_data_buffer = ServerReceiveDataBuffer;
server_params.on_signal_status = SignalStatus;
server_params.on_connection_status = ConnectionStatus;
std::string transmission_id = "000001";
peer_server = CreatePeer(&server_params);
LOG_INFO("Create peer_server");
std::string server_user_id = "S-" + mac_addr_str;
Init(peer_server, server_user_id.c_str());
LOG_INFO("peer_server init finish");
{
while (SignalStatus::SignalConnected != signal_status && !done) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
if (done) {
return;
}
std::string user_id = "S-" + mac_addr_str;
is_create_connection =
CreateConnection(peer_server, mac_addr_str.c_str(),
input_password)
? false
: true;
nv12_buffer = new char[NV12_BUFFER_SIZE];
// Screen capture
screen_capturer_factory = new ScreenCapturerFactory();
screen_capturer = (ScreenCapturer *)screen_capturer_factory->Create();
last_frame_time_ = std::chrono::high_resolution_clock::now();
ScreenCapturer::RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = screen_w;
rect.bottom = screen_h;
int screen_capturer_init_ret = screen_capturer->Init(
rect, 60,
[](unsigned char *data, int size, int width, int height) -> void {
auto now_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration =
now_time - last_frame_time_;
auto tc = duration.count() * 1000;
if (tc >= 0) {
SendData(peer_server, DATA_TYPE::VIDEO, (const char *)data,
NV12_BUFFER_SIZE);
last_frame_time_ = now_time;
}
});
if (0 == screen_capturer_init_ret) {
screen_capturer->Start();
} else {
screen_capturer->Destroy();
screen_capturer = nullptr;
}
// Mouse control
device_controller_factory = new DeviceControllerFactory();
mouse_controller =
(MouseController *)device_controller_factory->Create(
DeviceControllerFactory::Device::Mouse);
int mouse_controller_init_ret =
mouse_controller->Init(screen_w, screen_h);
if (0 != mouse_controller_init_ret) {
mouse_controller->Destroy();
mouse_controller = nullptr;
}
}
},
screen_w, screen_h);
// Main loop
while (!done) {
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
if (joined && !menu_hovered) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
}
{
static float f = 0.0f;
static int counter = 0;
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
// ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
#if CHINESE_FONT
ImGui::SetNextWindowSize(ImVec2(160, 210));
#else
ImGui::SetNextWindowSize(ImVec2(180, 210));
#endif
#if CHINESE_FONT
if (!joined) {
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::Begin(u8"菜单", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove);
} else {
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
ImGui::Begin(u8"菜单", nullptr, ImGuiWindowFlags_None);
}
#else
if (!joined) {
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::Begin("Menu", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove);
} else {
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_None);
}
#endif
{
menu_hovered = ImGui::IsWindowHovered();
#if CHINESE_FONT
ImGui::Text(u8"本机ID:");
#else
ImGui::Text("LOCAL ID:");
#endif
ImGui::SameLine();
ImGui::SetNextItemWidth(90);
#if CHINESE_FONT
ImGui::SetCursorPosX(60.0f);
#else
ImGui::SetCursorPosX(80.0f);
#endif
ImGui::InputText(
"##local_id", (char *)mac_addr_str.c_str(),
mac_addr_str.length() + 1,
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
#if CHINESE_FONT
ImGui::Text(u8"密码:");
#else
ImGui::Text("PASSWORD:");
#endif
ImGui::SameLine();
char input_password_tmp[7] = "";
std::string input_password_str = "123456";
strncpy(input_password_tmp, input_password, sizeof(input_password));
ImGui::SetNextItemWidth(90);
#if CHINESE_FONT
ImGui::SetCursorPosX(60.0f);
ImGui::InputTextWithHint("##server_pwd", u8"最长6个字符",
input_password, IM_ARRAYSIZE(input_password),
ImGuiInputTextFlags_CharsNoBlank);
#else
ImGui::SetCursorPosX(80.0f);
ImGui::InputTextWithHint("##server_pwd", "max 6 chars", input_password,
IM_ARRAYSIZE(input_password),
ImGuiInputTextFlags_CharsNoBlank);
#endif
if (strcmp(input_password_tmp, input_password)) {
cd_cache_file = fopen("cache.cd", "w+");
if (cd_cache_file) {
fseek(cd_cache_file, 0, SEEK_SET);
strncpy(cd_cache.password, input_password, sizeof(input_password));
fwrite(&cd_cache.password, sizeof(cd_cache.password), 1,
cd_cache_file);
fclose(cd_cache_file);
}
LeaveConnection(peer_server);
CreateConnection(peer_server, mac_addr_str.c_str(), input_password);
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
{
{
static char remote_id[20] = "";
#if CHINESE_FONT
ImGui::Text(u8"远端ID:");
#else
ImGui::Text("REMOTE ID:");
#endif
ImGui::SameLine();
ImGui::SetNextItemWidth(90);
#if CHINESE_FONT
ImGui::SetCursorPosX(60.0f);
#else
ImGui::SetCursorPosX(80.0f);
#endif
ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(),
remote_id, IM_ARRAYSIZE(remote_id),
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_CharsNoBlank);
ImGui::Spacing();
#if CHINESE_FONT
ImGui::Text(u8"密码:");
#else
ImGui::Text("PASSWORD:");
#endif
ImGui::SameLine();
ImGui::SetNextItemWidth(90);
static char client_password[20] = "";
#if CHINESE_FONT
ImGui::SetCursorPosX(60.0f);
ImGui::InputTextWithHint("##client_pwd", u8"最长6个字符",
client_password,
IM_ARRAYSIZE(client_password),
ImGuiInputTextFlags_CharsNoBlank);
#else
ImGui::SetCursorPosX(80.0f);
ImGui::InputTextWithHint("##client_pwd", "max 6 chars",
client_password,
IM_ARRAYSIZE(client_password),
ImGuiInputTextFlags_CharsNoBlank);
#endif
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button(connect_label)) {
int ret = -1;
if ("SignalConnected" == signal_status_str) {
#if CHINESE_FONT
if (strcmp(connect_label, u8"连接") == 0 && !joined) {
#else
if (strcmp(connect_label, "Connect") == 0 && !joined) {
#endif
std::string user_id = "C-" + mac_addr_str;
ret = JoinConnection(peer_server, remote_id, client_password);
if (0 == ret) {
// joined = true;
}
#if CHINESE_FONT
} else if (strcmp(connect_label, u8"断开连接") == 0 && joined) {
#else
} else if (strcmp(connect_label, "Disconnect") == 0 && joined) {
#endif
ret = LeaveConnection(peer_server);
CreateConnection(peer_server, mac_addr_str.c_str(),
input_password);
memset(audio_buffer, 0, 960);
if (0 == ret) {
joined = false;
received_frame = false;
}
}
if (0 == ret) {
connect_button_pressed = !connect_button_pressed;
#if CHINESE_FONT
connect_label =
connect_button_pressed ? u8"断开连接" : u8"连接";
#else
connect_label =
connect_button_pressed ? "Disconnect" : "Connect";
#endif
}
}
}
}
}
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
#if CHINESE_FONT
if (ImGui::Button(fullscreen_label)) {
if (strcmp(fullscreen_label, u8"全屏") == 0) {
#else
if (ImGui::Button(fullscreen_label)) {
if (strcmp(fullscreen_label, "FULLSCREEN") == 0) {
#endif
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
} else {
SDL_SetWindowFullscreen(window, SDL_FALSE);
}
fullscreen_button_pressed = !fullscreen_button_pressed;
#if CHINESE_FONT
fullscreen_label = fullscreen_button_pressed ? u8"退出全屏" : u8"全屏";
#else
fullscreen_label =
fullscreen_button_pressed ? "EXIT FULLSCREEN" : "FULLSCREEN";
#endif
}
ImGui::End();
}
// Rendering
ImGui::Render();
SDL_RenderSetScale(sdlRenderer, io.DisplayFramebufferScale.x,
io.DisplayFramebufferScale.y);
SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT) {
done = true;
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_RESIZED) {
// SDL_GetWindowSize(window, &window_w, &window_h);
int window_w_last = window_w;
int window_h_last = window_h;
SDL_GetWindowSize(window, &window_w, &window_h);
int w_change_ratio = abs(window_w - window_w_last) / 16;
int h_change_ratio = abs(window_h - window_h_last) / 9;
if (w_change_ratio > h_change_ratio) {
window_h = window_w * 9 / 16;
} else {
window_w = window_h * 16 / 9;
}
SDL_SetWindowSize(window, window_w, window_h);
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_CLOSE &&
event.window.windowID == SDL_GetWindowID(window)) {
done = true;
} else if (event.type == REFRESH_EVENT) {
sdlRect.x = 0;
sdlRect.y = 0;
sdlRect.w = window_w;
sdlRect.h = window_h;
SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w);
} else {
if (joined) {
ProcessMouseKeyEven(event);
}
}
}
SDL_RenderClear(sdlRenderer);
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
if (!joined || !received_frame) {
SDL_RenderClear(sdlRenderer);
SDL_SetRenderDrawColor(
sdlRenderer, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
}
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
SDL_RenderPresent(sdlRenderer);
frame_count++;
end_time = SDL_GetTicks();
elapsed_time = end_time - start_time;
if (elapsed_time >= 1000) {
fps = frame_count / (elapsed_time / 1000);
frame_count = 0;
window_title = "Remote Desk Client FPS [" + std::to_string(fps) +
"] status [" + signal_status_str + "|" +
connection_status_str + "]";
// For MacOS, UI frameworks can only be called from the main thread
SDL_SetWindowTitle(window, window_title.c_str());
start_time = end_time;
}
}
// Cleanup
if (is_create_connection) {
LeaveConnection(peer_server);
}
rtc_thread.join();
SDL_CloseAudioDevice(output_dev);
SDL_CloseAudioDevice(input_dev);
if (screen_capturer) {
screen_capturer->Destroy();
}
if (mouse_controller) {
mouse_controller->Destroy();
}
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(sdlRenderer);
SDL_DestroyWindow(window);
SDL_CloseAudio();
SDL_Quit();
return 0;
}

View File

@@ -0,0 +1,47 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LOCALIZATION_H_
#define _LOCALIZATION_H_
#include <string>
#include <vector>
namespace localization {
static std::vector<std::string> menu = {u8"菜单", "Menu"};
static std::vector<std::string> local_id = {u8"本机ID:", "Local ID:"};
static std::vector<std::string> password = {u8"密码:", "Password:"};
static std::vector<std::string> max_password_len = {u8"最大6个字符",
"Max 6 chars"};
static std::vector<std::string> remote_id = {u8"对端ID:", "Remote ID:"};
static std::vector<std::string> connect = {u8"连接", "Connect"};
static std::vector<std::string> disconnect = {u8"断开连接", "Disconnect"};
static std::vector<std::string> fullscreen = {u8"全屏", "Fullscreen"};
static std::vector<std::string> exit_fullscreen = {u8"退出全屏",
"Exit fullscreen"};
static std::vector<std::string> control_mouse = {u8"控制鼠标", "Mouse Control"};
static std::vector<std::string> release_mouse = {u8"释放鼠标", "Release Mouse"};
static std::vector<std::string> settings = {u8"设置", "Settings"};
static std::vector<std::string> language = {u8"语言:", "Language:"};
static std::vector<std::string> language_zh = {u8"中文", "Chinese"};
static std::vector<std::string> language_en = {u8"英文", "English"};
static std::vector<std::string> video_quality = {u8"视频质量:",
"Video Quality:"};
static std::vector<std::string> video_quality_high = {u8"", "High"};
static std::vector<std::string> video_quality_medium = {u8"", "Medium"};
static std::vector<std::string> video_quality_low = {u8"", "Low"};
static std::vector<std::string> video_encode_format = {u8"视频编码格式:",
"Video Encode Format:"};
static std::vector<std::string> av1 = {u8"AV1", "AV1"};
static std::vector<std::string> h264 = {u8"H.264", "H.264"};
static std::vector<std::string> enable_hardware_video_codec = {
u8"启用硬件编解码器:", "Enable Hardware Video Codec:"};
static std::vector<std::string> ok = {u8"确认", "OK"};
static std::vector<std::string> cancel = {u8"取消", "Cancel"};
} // namespace localization
#endif

View File

@@ -0,0 +1,81 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-06-14
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LAYOUT_STYLE_H_
#define _LAYOUT_STYLE_H_
#ifdef _WIN32
#define MENU_WINDOW_WIDTH_CN 160
#define MENU_WINDOW_HEIGHT_CN 245
#define MENU_WINDOW_WIDTH_EN 190
#define MENU_WINDOW_HEIGHT_EN 245
#define IPUT_WINDOW_WIDTH 86
#define INPUT_WINDOW_PADDING_CN 66
#define INPUT_WINDOW_PADDING_EN 96
#define SETTINGS_WINDOW_WIDTH_CN 181
#define SETTINGS_WINDOW_WIDTH_EN 228
#define SETTINGS_WINDOW_HEIGHT_CN 190
#define SETTINGS_WINDOW_HEIGHT_EN 190
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 147
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 147
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 147
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 154
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201
#define SETTINGS_SELECT_WINDOW_WIDTH 73
#define SETTINGS_OK_BUTTON_PADDING_CN 55
#define SETTINGS_OK_BUTTON_PADDING_EN 78
#elif __linux__
#define MENU_WINDOW_WIDTH_CN 160
#define MENU_WINDOW_HEIGHT_CN 245
#define MENU_WINDOW_WIDTH_EN 190
#define MENU_WINDOW_HEIGHT_EN 245
#define IPUT_WINDOW_WIDTH 90
#define INPUT_WINDOW_PADDING_CN 60
#define INPUT_WINDOW_PADDING_EN 80
#define SETTINGS_WINDOW_WIDTH_CN 188
#define SETTINGS_WINDOW_WIDTH_EN 228
#define SETTINGS_WINDOW_HEIGHT_CN 190
#define SETTINGS_WINDOW_HEIGHT_EN 190
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 140
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 140
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 140
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 161
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201
#define SETTINGS_SELECT_WINDOW_WIDTH 60
#define SETTINGS_OK_BUTTON_PADDING_CN 60
#define SETTINGS_OK_BUTTON_PADDING_EN 80
#elif __APPLE__
#define MENU_WINDOW_WIDTH_CN 148
#define MENU_WINDOW_HEIGHT_CN 244
#define MENU_WINDOW_WIDTH_EN 148
#define MENU_WINDOW_HEIGHT_EN 244
#define IPUT_WINDOW_WIDTH 77
#define INPUT_WINDOW_PADDING_CN 63
#define INPUT_WINDOW_PADDING_EN 63
#define SETTINGS_WINDOW_WIDTH_CN 160
#define SETTINGS_WINDOW_WIDTH_EN 220
#define SETTINGS_WINDOW_HEIGHT_CN 190
#define SETTINGS_WINDOW_HEIGHT_EN 190
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 90
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 150
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 90
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 150
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 90
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 150
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_CN 133
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_EN 193
#define SETTINGS_SELECT_WINDOW_WIDTH 62
#define SETTINGS_OK_BUTTON_PADDING_CN 50
#define SETTINGS_OK_BUTTON_PADDING_EN 80
#endif
#endif

View File

@@ -0,0 +1,888 @@
#include "main_window.h"
#include <fstream>
#include <iostream>
#include <string>
#include "device_controller_factory.h"
#include "layout_style.h"
#include "localization.h"
#include "log.h"
#include "platform.h"
#include "screen_capturer_factory.h"
// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
MainWindow::MainWindow() {}
MainWindow::~MainWindow() {}
int MainWindow::SaveSettingsIntoCacheFile() {
cd_cache_file_ = fopen("cache.cd", "w+");
if (!cd_cache_file_) {
return -1;
}
fseek(cd_cache_file_, 0, SEEK_SET);
strncpy(cd_cache_.password, input_password_, sizeof(input_password_));
memcpy(&cd_cache_.language, &language_button_value_,
sizeof(language_button_value_));
memcpy(&cd_cache_.video_quality, &video_quality_button_value_,
sizeof(video_quality_button_value_));
memcpy(&cd_cache_.video_encode_format, &video_encode_format_button_value_,
sizeof(video_encode_format_button_value_));
memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_,
sizeof(enable_hardware_video_codec_));
fwrite(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
fclose(cd_cache_file_);
return 0;
}
int MainWindow::LoadSettingsIntoCacheFile() {
cd_cache_file_ = fopen("cache.cd", "r+");
if (!cd_cache_file_) {
return -1;
}
fseek(cd_cache_file_, 0, SEEK_SET);
fread(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
fclose(cd_cache_file_);
strncpy(input_password_, cd_cache_.password, sizeof(cd_cache_.password));
language_button_value_ = cd_cache_.language;
video_quality_button_value_ = cd_cache_.video_quality;
video_encode_format_button_value_ = cd_cache_.video_encode_format;
enable_hardware_video_codec_ = cd_cache_.enable_hardware_video_codec;
return 0;
}
int MainWindow::StartScreenCapture() {
screen_capturer_ = (ScreenCapturer *)screen_capturer_factory_->Create();
ScreenCapturer::RECORD_DESKTOP_RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = screen_width_;
rect.bottom = screen_height_;
last_frame_time_ = std::chrono::high_resolution_clock::now();
int screen_capturer_init_ret = screen_capturer_->Init(
rect, 60,
[this](unsigned char *data, int size, int width, int height) -> void {
auto now_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> duration = now_time - last_frame_time_;
auto tc = duration.count() * 1000;
if (tc >= 0) {
SendData(peer_, DATA_TYPE::VIDEO, (const char *)data,
NV12_BUFFER_SIZE);
last_frame_time_ = now_time;
}
});
if (0 == screen_capturer_init_ret) {
screen_capturer_->Start();
} else {
screen_capturer_->Destroy();
delete screen_capturer_;
screen_capturer_ = nullptr;
}
return 0;
}
int MainWindow::StopScreenCapture() {
if (screen_capturer_) {
LOG_INFO("Destroy screen capturer")
screen_capturer_->Destroy();
delete screen_capturer_;
screen_capturer_ = nullptr;
}
return 0;
}
int MainWindow::StartMouseControl() {
device_controller_factory_ = new DeviceControllerFactory();
mouse_controller_ = (MouseController *)device_controller_factory_->Create(
DeviceControllerFactory::Device::Mouse);
int mouse_controller_init_ret =
mouse_controller_->Init(screen_width_, screen_height_);
if (0 != mouse_controller_init_ret) {
LOG_INFO("Destroy mouse controller")
mouse_controller_->Destroy();
mouse_controller_ = nullptr;
}
return 0;
}
int MainWindow::StopMouseControl() {
if (mouse_controller_) {
mouse_controller_->Destroy();
delete mouse_controller_;
mouse_controller_ = nullptr;
}
return 0;
}
int MainWindow::CreateConnectionPeer() {
mac_addr_str_ = GetMac();
params_.use_cfg_file = false;
params_.signal_server_ip = "150.158.81.30";
params_.signal_server_port = 9099;
params_.stun_server_ip = "150.158.81.30";
params_.stun_server_port = 3478;
params_.turn_server_ip = "150.158.81.30";
params_.turn_server_port = 3478;
params_.turn_server_username = "dijunkun";
params_.turn_server_password = "dijunkunpw";
params_.hardware_acceleration = config_center_.IsHardwareVideoCodec();
params_.av1_encoding = config_center_.GetVideoEncodeFormat() ==
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1
? true
: false;
params_.on_receive_video_buffer = OnReceiveVideoBufferCb;
params_.on_receive_audio_buffer = OnReceiveAudioBufferCb;
params_.on_receive_data_buffer = OnReceiveDataBufferCb;
params_.on_signal_status = OnSignalStatusCb;
params_.on_connection_status = OnConnectionStatusCb;
params_.user_data = this;
peer_ = CreatePeer(&params_);
if (peer_) {
LOG_INFO("Create peer instance successful");
local_id_ = mac_addr_str_;
Init(peer_, local_id_.c_str());
LOG_INFO("Peer init finish");
} else {
LOG_INFO("Create peer instance failed");
}
return 0;
}
int MainWindow::Run() {
LoadSettingsIntoCacheFile();
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
localization_language_index_ = language_button_value_;
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
SDL_INIT_GAMECONTROLLER) != 0) {
printf("Error: %s\n", SDL_GetError());
return -1;
}
// From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
// Create main window with SDL_Renderer graphics context
SDL_WindowFlags window_flags =
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
main_window_ = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, main_window_width_,
main_window_height_, window_flags);
SDL_DisplayMode DM;
SDL_GetCurrentDisplayMode(0, &DM);
screen_width_ = DM.w;
screen_height_ = DM.h;
sdl_renderer_ = SDL_CreateRenderer(
main_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
if (sdl_renderer_ == nullptr) {
SDL_Log("Error creating SDL_Renderer!");
return 0;
}
pixformat_ = SDL_PIXELFORMAT_NV12;
sdl_texture_ =
SDL_CreateTexture(sdl_renderer_, pixformat_, SDL_TEXTUREACCESS_STREAMING,
texture_width_, texture_height_);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
if (config_center_.GetLanguage() == ConfigCenter::LANGUAGE::CHINESE) {
// Load Fonts
#ifdef _WIN32
std::string default_font_path = "c:/windows/fonts/simhei.ttf";
std::ifstream font_path_f(default_font_path.c_str());
std::string font_path =
font_path_f.good() ? "c:/windows/fonts/simhei.ttf" : "";
if (!font_path.empty()) {
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
}
#elif __APPLE__
std::string default_font_path = "/System/Library/Fonts/PingFang.ttc";
std::ifstream font_path_f(default_font_path.c_str());
std::string font_path =
font_path_f.good() ? "/System/Library/Fonts/PingFang.ttc" : "";
if (!font_path.empty()) {
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
}
#elif __linux__
io.Fonts->AddFontFromFileTTF("c:/windows/fonts/msyh.ttc", 13.0f, NULL,
io.Fonts->GetGlyphRangesChineseFull());
#endif
}
// Setup Dear ImGui style
// ImGui::StyleColorsDark();
ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForSDLRenderer(main_window_, sdl_renderer_);
ImGui_ImplSDLRenderer2_Init(sdl_renderer_);
// Our state
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
CreateConnectionPeer();
{
nv12_buffer_ = new char[NV12_BUFFER_SIZE];
// Screen capture
screen_capturer_factory_ = new ScreenCapturerFactory();
// Mouse control
device_controller_factory_ = new DeviceControllerFactory();
}
// Main loop
while (!exit_) {
if (SignalStatus::SignalConnected == signal_status_ &&
!is_create_connection_) {
is_create_connection_ =
CreateConnection(peer_, mac_addr_str_.c_str(), input_password_)
? false
: true;
LOG_INFO("Connected with signal server, create p2p connection");
}
if (!inited_ ||
localization_language_index_last_ != localization_language_index_) {
connect_button_label_ =
connect_button_pressed_
? localization::disconnect[localization_language_index_]
: localization::connect[localization_language_index_];
fullscreen_button_label_ =
fullscreen_button_pressed_
? localization::exit_fullscreen[localization_language_index_]
: localization::fullscreen[localization_language_index_];
mouse_control_button_label_ =
mouse_control_button_pressed_
? localization::release_mouse[localization_language_index_]
: localization::control_mouse[localization_language_index_];
settings_button_label_ =
localization::settings[localization_language_index_];
inited_ = true;
localization_language_index_last_ = localization_language_index_;
}
if (start_screen_capture_ && !screen_capture_is_started_) {
StartScreenCapture();
screen_capture_is_started_ = true;
} else if (!start_screen_capture_ && screen_capture_is_started_) {
StopScreenCapture();
screen_capture_is_started_ = false;
}
if (start_mouse_control_ && !mouse_control_is_started_) {
StartMouseControl();
mouse_control_is_started_ = true;
} else if (!start_mouse_control_ && mouse_control_is_started_) {
StopMouseControl();
mouse_control_is_started_ = false;
}
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
if (connection_established_ && !subwindow_hovered_ && control_mouse_) {
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
}
// main window layout
{
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetNextWindowSize(
ImVec2(MENU_WINDOW_WIDTH_CN, MENU_WINDOW_HEIGHT_CN));
} else {
ImGui::SetNextWindowSize(
ImVec2(MENU_WINDOW_WIDTH_EN, MENU_WINDOW_HEIGHT_EN));
}
if (!connection_established_) {
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::Begin(localization::menu[localization_language_index_].c_str(),
nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove);
} else {
// ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
ImGui::Begin(localization::menu[localization_language_index_].c_str(),
nullptr, ImGuiWindowFlags_None);
}
{
subwindow_hovered_ = ImGui::IsWindowHovered();
// local
{
ImGui::Text(
localization::local_id[localization_language_index_].c_str());
ImGui::SameLine();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
}
ImGui::InputText("##local_id", (char *)mac_addr_str_.c_str(),
mac_addr_str_.length() + 1,
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_ReadOnly);
ImGui::Text(
localization::password[localization_language_index_].c_str());
ImGui::SameLine();
strncpy(input_password_tmp_, input_password_,
sizeof(input_password_));
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
}
ImGui::InputTextWithHint(
"##server_pwd",
localization::max_password_len[localization_language_index_]
.c_str(),
input_password_, IM_ARRAYSIZE(input_password_),
ImGuiInputTextFlags_CharsNoBlank);
if (strcmp(input_password_tmp_, input_password_)) {
SaveSettingsIntoCacheFile();
}
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// remote
{
ImGui::Text(
localization::remote_id[localization_language_index_].c_str());
ImGui::SameLine();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
}
ImGui::InputTextWithHint("##remote_id_", mac_addr_str_.c_str(),
remote_id_, IM_ARRAYSIZE(remote_id_),
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_CharsNoBlank);
ImGui::Spacing();
ImGui::Text(
localization::password[localization_language_index_].c_str());
ImGui::SameLine();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
}
ImGui::InputTextWithHint(
"##client_pwd",
localization::max_password_len[localization_language_index_]
.c_str(),
client_password_, IM_ARRAYSIZE(client_password_),
ImGuiInputTextFlags_CharsNoBlank);
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button(connect_button_label_.c_str()) || rejoin_) {
int ret = -1;
if ("SignalConnected" == signal_status_str_) {
if (connect_button_label_ ==
localization::connect[localization_language_index_] &&
!connection_established_ && strlen(remote_id_)) {
if (remote_id_ == local_id_ && !peer_reserved_) {
peer_reserved_ = CreatePeer(&params_);
if (peer_reserved_) {
LOG_INFO("Create peer[reserved] instance successful");
std::string local_id = "C-" + mac_addr_str_;
Init(peer_reserved_, local_id.c_str());
LOG_INFO("Peer[reserved] init finish");
} else {
LOG_INFO("Create peer[reserved] instance failed");
}
}
ret = JoinConnection(peer_reserved_ ? peer_reserved_ : peer_,
remote_id_, client_password_);
if (0 == ret) {
if (!peer_reserved_) {
is_client_mode_ = true;
}
rejoin_ = false;
} else {
rejoin_ = true;
}
} else if (connect_button_label_ ==
localization::disconnect
[localization_language_index_] &&
connection_established_) {
ret = LeaveConnection(peer_reserved_ ? peer_reserved_ : peer_);
if (0 == ret) {
rejoin_ = false;
memset(audio_buffer_, 0, 960);
connection_established_ = false;
received_frame_ = false;
is_client_mode_ = false;
}
}
if (0 == ret) {
connect_button_pressed_ = !connect_button_pressed_;
connect_button_label_ =
connect_button_pressed_
? localization::disconnect[localization_language_index_]
: localization::connect[localization_language_index_];
}
}
}
}
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
// Mouse control
if (ImGui::Button(mouse_control_button_label_.c_str())) {
if (mouse_control_button_label_ ==
localization::control_mouse[localization_language_index_] &&
connection_established_) {
mouse_control_button_pressed_ = true;
control_mouse_ = true;
mouse_control_button_label_ =
localization::release_mouse[localization_language_index_];
} else {
control_mouse_ = false;
mouse_control_button_label_ =
localization::control_mouse[localization_language_index_];
}
mouse_control_button_pressed_ = !mouse_control_button_pressed_;
}
ImGui::SameLine();
// Fullscreen
if (ImGui::Button(fullscreen_button_label_.c_str())) {
if (fullscreen_button_label_ ==
localization::fullscreen[localization_language_index_]) {
main_window_width_before_fullscreen_ = main_window_width_;
main_window_height_before_fullscreen_ = main_window_height_;
SDL_SetWindowFullscreen(main_window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
fullscreen_button_label_ =
localization::exit_fullscreen[localization_language_index_];
} else {
SDL_SetWindowFullscreen(main_window_, SDL_FALSE);
SDL_SetWindowSize(main_window_, main_window_width_before_fullscreen_,
main_window_height_before_fullscreen_);
main_window_width_ = main_window_width_before_fullscreen_;
main_window_height_ = main_window_height_before_fullscreen_;
fullscreen_button_label_ =
localization::fullscreen[localization_language_index_];
}
fullscreen_button_pressed_ = !fullscreen_button_pressed_;
}
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
if (ImGui::Button(settings_button_label_.c_str())) {
settings_button_pressed_ = !settings_button_pressed_;
settings_window_pos_reset_ = true;
}
if (settings_button_pressed_) {
if (settings_window_pos_reset_) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SETTINGS_WINDOW_WIDTH_CN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SETTINGS_WINDOW_HEIGHT_CN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SETTINGS_WINDOW_WIDTH_CN, SETTINGS_WINDOW_HEIGHT_CN));
} else {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SETTINGS_WINDOW_WIDTH_EN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SETTINGS_WINDOW_HEIGHT_EN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SETTINGS_WINDOW_WIDTH_EN, SETTINGS_WINDOW_HEIGHT_EN));
}
settings_window_pos_reset_ = false;
}
// Settings
ImGui::Begin(
localization::settings[localization_language_index_].c_str(),
nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoSavedSettings);
{
subwindow_hovered_ = ImGui::IsWindowHovered();
const char *language_items[] = {
localization::language_zh[localization_language_index_].c_str(),
localization::language_en[localization_language_index_].c_str()};
ImGui::SetCursorPosY(32);
ImGui::Text(
localization::language[localization_language_index_].c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(30);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##language", &language_button_value_, language_items,
IM_ARRAYSIZE(language_items));
}
ImGui::Separator();
{
const char *video_quality_items[] = {
localization::video_quality_high[localization_language_index_]
.c_str(),
localization::video_quality_medium[localization_language_index_]
.c_str(),
localization::video_quality_low[localization_language_index_]
.c_str()};
ImGui::SetCursorPosY(62);
ImGui::Text(localization::video_quality[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(60);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##video_quality", &video_quality_button_value_,
video_quality_items, IM_ARRAYSIZE(video_quality_items));
}
ImGui::Separator();
{
const char *video_encode_format_items[] = {
localization::av1[localization_language_index_].c_str(),
localization::h264[localization_language_index_].c_str()};
ImGui::SetCursorPosY(92);
ImGui::Text(
localization::video_encode_format[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(90);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##video_encode_format",
&video_encode_format_button_value_,
video_encode_format_items,
IM_ARRAYSIZE(video_encode_format_items));
}
ImGui::Separator();
{
ImGui::SetCursorPosY(122);
ImGui::Text(localization::enable_hardware_video_codec
[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(
ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(
ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN);
}
ImGui::SetCursorPosY(120);
ImGui::Checkbox("##enable_hardware_video_codec",
&enable_hardware_video_codec_);
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN);
} else {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
}
ImGui::SetCursorPosY(160.0f);
// OK
if (ImGui::Button(
localization::ok[localization_language_index_].c_str())) {
settings_button_pressed_ = false;
// Language
if (language_button_value_ == 0) {
config_center_.SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
} else {
config_center_.SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
}
language_button_value_last_ = language_button_value_;
localization_language_ =
(ConfigCenter::LANGUAGE)language_button_value_;
localization_language_index_ = language_button_value_;
LOG_INFO("Set localization language: {}",
localization_language_index_ == 0 ? "zh" : "en");
// Video quality
if (video_quality_button_value_ == 0) {
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
} else if (video_quality_button_value_ == 1) {
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
} else {
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
}
video_quality_button_value_last_ = video_quality_button_value_;
// Video encode format
if (video_encode_format_button_value_ == 0) {
config_center_.SetVideoEncodeFormat(
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
} else if (video_encode_format_button_value_ == 1) {
config_center_.SetVideoEncodeFormat(
ConfigCenter::VIDEO_ENCODE_FORMAT::H264);
}
video_encode_format_button_value_last_ =
video_encode_format_button_value_;
// Hardware video codec
if (enable_hardware_video_codec_) {
config_center_.SetHardwareVideoCodec(true);
} else {
config_center_.SetHardwareVideoCodec(false);
}
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
SaveSettingsIntoCacheFile();
settings_window_pos_reset_ = true;
// Recreate peer instance
LoadSettingsIntoCacheFile();
// Recreate peer instance
{
DestroyPeer(peer_);
CreateConnectionPeer();
LOG_INFO("Recreate peer instance successful");
}
}
ImGui::SameLine();
// Cancel
if (ImGui::Button(
localization::cancel[localization_language_index_].c_str())) {
settings_button_pressed_ = false;
if (language_button_value_ != language_button_value_last_) {
language_button_value_ = language_button_value_last_;
}
if (video_quality_button_value_ != video_quality_button_value_last_) {
video_quality_button_value_ = video_quality_button_value_last_;
}
if (video_encode_format_button_value_ !=
video_encode_format_button_value_last_) {
video_encode_format_button_value_ =
video_encode_format_button_value_last_;
}
if (enable_hardware_video_codec_ !=
enable_hardware_video_codec_last_) {
enable_hardware_video_codec_ = enable_hardware_video_codec_last_;
}
settings_window_pos_reset_ = true;
}
ImGui::End();
}
ImGui::End();
}
// Rendering
ImGui::Render();
SDL_RenderSetScale(sdl_renderer_, io.DisplayFramebufferScale.x,
io.DisplayFramebufferScale.y);
SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT) {
exit_ = true;
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_RESIZED) {
int window_w_last = main_window_width_;
int window_h_last = main_window_height_;
SDL_GetWindowSize(main_window_, &main_window_width_,
&main_window_height_);
int w_change_ratio = abs(main_window_width_ - window_w_last) / 16;
int h_change_ratio = abs(main_window_height_ - window_h_last) / 9;
if (w_change_ratio > h_change_ratio) {
main_window_height_ = main_window_width_ * 9 / 16;
} else {
main_window_width_ = main_window_height_ * 16 / 9;
}
SDL_SetWindowSize(main_window_, main_window_width_,
main_window_height_);
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_CLOSE &&
event.window.windowID == SDL_GetWindowID(main_window_)) {
exit_ = true;
} else if (event.type == REFRESH_EVENT) {
sdl_rect_.x = 0;
sdl_rect_.y = 0;
sdl_rect_.w = main_window_width_;
sdl_rect_.h = main_window_height_;
SDL_UpdateTexture(sdl_texture_, NULL, dst_buffer_, 1280);
} else {
if (connection_established_) {
ProcessMouseKeyEven(event);
}
}
}
SDL_RenderClear(sdl_renderer_);
SDL_RenderCopy(sdl_renderer_, sdl_texture_, NULL, &sdl_rect_);
if (!connection_established_ || !received_frame_) {
SDL_RenderClear(sdl_renderer_);
SDL_SetRenderDrawColor(
sdl_renderer_, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
}
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
SDL_RenderPresent(sdl_renderer_);
frame_count_++;
end_time_ = SDL_GetTicks();
elapsed_time_ = end_time_ - start_time_;
if (elapsed_time_ >= 1000) {
fps_ = frame_count_ / (elapsed_time_ / 1000);
frame_count_ = 0;
window_title = "Remote Desk Client FPS [" + std::to_string(fps_) +
"] status [" + connection_status_str_ + "|" +
connection_status_str_ + "]";
// For MacOS, UI frameworks can only be called from the main thread
SDL_SetWindowTitle(main_window_, window_title.c_str());
start_time_ = end_time_;
}
}
// Cleanup
if (is_create_connection_) {
LeaveConnection(peer_);
is_client_mode_ = false;
}
if (peer_) {
DestroyPeer(peer_);
}
if (peer_reserved_) {
DestroyPeer(peer_reserved_);
}
SDL_CloseAudioDevice(output_dev_);
SDL_CloseAudioDevice(input_dev_);
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(sdl_renderer_);
SDL_DestroyWindow(main_window_);
SDL_CloseAudio();
SDL_Quit();
return 0;
}

View File

@@ -0,0 +1,190 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _MAIN_WINDOW_H_
#define _MAIN_WINDOW_H_
#include <SDL.h>
#include <atomic>
#include <chrono>
#include <string>
#include "../../thirdparty/projectx/src/interface/x.h"
#include "config_center.h"
#include "device_controller_factory.h"
#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_sdlrenderer2.h"
#include "screen_capturer_factory.h"
class MainWindow {
public:
MainWindow();
~MainWindow();
public:
int Run();
public:
static void OnReceiveVideoBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data);
static void OnReceiveAudioBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data);
static void OnReceiveDataBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data);
static void OnSignalStatusCb(SignalStatus status, void *user_data);
static void OnConnectionStatusCb(ConnectionStatus status, void *user_data);
private:
int ProcessMouseKeyEven(SDL_Event &ev);
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len);
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len);
private:
int SaveSettingsIntoCacheFile();
int LoadSettingsIntoCacheFile();
int StartScreenCapture();
int StopScreenCapture();
int StartMouseControl();
int StopMouseControl();
int CreateConnectionPeer();
private:
typedef struct {
char password[7];
int language;
int video_quality;
int video_encode_format;
bool enable_hardware_video_codec;
} CDCache;
private:
FILE *cd_cache_file_ = nullptr;
CDCache cd_cache_;
ConfigCenter config_center_;
ConfigCenter::LANGUAGE localization_language_ =
ConfigCenter::LANGUAGE::CHINESE;
int localization_language_index_ = -1;
int localization_language_index_last_ = -1;
private:
std::string window_title = "Remote Desk Client";
std::string mac_addr_str_ = "";
std::string connect_button_label_ = "Connect";
std::string fullscreen_button_label_ = "Fullscreen";
std::string mouse_control_button_label_ = "Mouse Control";
std::string settings_button_label_ = "Setting";
char input_password_tmp_[7] = "";
char input_password_[7] = "";
std::string local_id_ = "";
char remote_id_[20] = "";
char client_password_[20] = "";
bool is_client_mode_ = false;
private:
int screen_width_ = 1280;
int screen_height_ = 720;
int main_window_width_ = 1280;
int main_window_height_ = 720;
int main_window_width_before_fullscreen_ = 1280;
int main_window_height_before_fullscreen_ = 720;
int texture_width_ = 1280;
int texture_height_ = 720;
SDL_Texture *sdl_texture_ = nullptr;
SDL_Renderer *sdl_renderer_ = nullptr;
SDL_Rect sdl_rect_;
SDL_Window *main_window_;
uint32_t pixformat_ = 0;
bool inited_ = false;
bool exit_ = false;
bool connection_established_ = false;
bool subwindow_hovered_ = false;
bool connect_button_pressed_ = false;
bool fullscreen_button_pressed_ = false;
bool mouse_control_button_pressed_ = false;
bool settings_button_pressed_ = false;
bool received_frame_ = false;
bool is_create_connection_ = false;
bool audio_buffer_fresh_ = false;
bool rejoin_ = false;
bool control_mouse_ = false;
int fps_ = 0;
uint32_t start_time_;
uint32_t end_time_;
uint32_t elapsed_time_;
uint32_t frame_count_ = 0;
private:
ConnectionStatus connection_status_ = ConnectionStatus::Closed;
SignalStatus signal_status_ = SignalStatus::SignalClosed;
std::string signal_status_str_ = "";
std::string connection_status_str_ = "";
private:
PeerPtr *peer_ = nullptr;
PeerPtr *peer_reserved_ = nullptr;
Params params_;
private:
SDL_AudioDeviceID input_dev_;
SDL_AudioDeviceID output_dev_;
unsigned char audio_buffer_[960];
int audio_len_ = 0;
char *nv12_buffer_ = nullptr;
unsigned char *dst_buffer_ = new unsigned char[1280 * 720 * 3];
private:
ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
ScreenCapturer *screen_capturer_ = nullptr;
DeviceControllerFactory *device_controller_factory_ = nullptr;
MouseController *mouse_controller_ = nullptr;
#ifdef __linux__
std::chrono::_V2::system_clock::time_point last_frame_time_;
#else
std::chrono::steady_clock::time_point last_frame_time_;
#endif
private:
int language_button_value_ = 0;
int video_quality_button_value_ = 0;
int video_encode_format_button_value_ = 0;
bool enable_hardware_video_codec_ = false;
int language_button_value_last_ = 0;
int video_quality_button_value_last_ = 0;
int video_encode_format_button_value_last_ = 0;
bool enable_hardware_video_codec_last_ = false;
private:
std::atomic<bool> start_screen_capture_{false};
std::atomic<bool> start_mouse_control_{false};
std::atomic<bool> screen_capture_is_started_{false};
std::atomic<bool> mouse_control_is_started_{false};
private:
bool settings_window_pos_reset_ = true;
};
#endif

View File

@@ -0,0 +1,216 @@
#include "device_controller.h"
#include "localization.h"
#include "main_window.h"
// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
#ifdef REMOTE_DESK_DEBUG
#else
#define MOUSE_CONTROL 1
#endif
int MainWindow::ProcessMouseKeyEven(SDL_Event &ev) {
if (!control_mouse_) {
return 0;
}
float ratio = (float)(1280.0 / main_window_width_);
RemoteAction remote_action;
remote_action.m.x = (size_t)(ev.button.x * ratio);
remote_action.m.y = (size_t)(ev.button.y * ratio);
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
{
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
if (SDLK_DOWN == ev.key.keysym.sym) {
// printf("SDLK_DOWN \n");
} else if (SDLK_UP == ev.key.keysym.sym) {
// printf("SDLK_UP \n");
} else if (SDLK_LEFT == ev.key.keysym.sym) {
// printf("SDLK_LEFT \n");
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
// printf("SDLK_RIGHT \n");
}
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_down;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_down;
}
if (subwindow_hovered_) {
remote_action.m.flag = MouseFlag::move;
}
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEBUTTONUP == ev.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == ev.button.button) {
remote_action.m.flag = MouseFlag::left_up;
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
remote_action.m.flag = MouseFlag::right_up;
}
if (subwindow_hovered_) {
remote_action.m.flag = MouseFlag::move;
}
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEMOTION == ev.type) {
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::move;
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_QUIT == ev.type) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
printf("SDL_QUIT\n");
return 0;
}
return 0;
}
void MainWindow::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
if (1) {
if ("Connected" == connection_status_str_) {
SendData(peer_, DATA_TYPE::AUDIO, (const char *)stream, len);
}
} else {
memcpy(audio_buffer_, stream, len);
audio_len_ = len;
SDL_Delay(10);
audio_buffer_fresh_ = true;
}
}
void MainWindow::SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
if (!audio_buffer_fresh_) {
return;
}
SDL_memset(stream, 0, len);
if (audio_len_ == 0) {
return;
} else {
}
len = (len > audio_len_ ? audio_len_ : len);
SDL_MixAudioFormat(stream, audio_buffer_, AUDIO_S16LSB, len,
SDL_MIX_MAXVOLUME);
audio_buffer_fresh_ = false;
}
void MainWindow::OnReceiveVideoBufferCb(const char *data, size_t size,
const char *user_id,
size_t user_id_size, void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
if (main_window->connection_established_) {
memcpy(main_window->dst_buffer_, data, size);
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
main_window->received_frame_ = true;
}
}
void MainWindow::OnReceiveAudioBufferCb(const char *data, size_t size,
const char *user_id,
size_t user_id_size, void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
main_window->audio_buffer_fresh_ = true;
SDL_QueueAudio(main_window->output_dev_, data, (uint32_t)size);
}
void MainWindow::OnReceiveDataBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
std::string user(user_id, user_id_size);
RemoteAction remote_action;
memcpy(&remote_action, data, sizeof(remote_action));
#if MOUSE_CONTROL
if (main_window->mouse_controller_) {
main_window->mouse_controller_->SendCommand(remote_action);
}
#endif
}
void MainWindow::OnSignalStatusCb(SignalStatus status, void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
main_window->signal_status_ = status;
if (SignalStatus::SignalConnecting == status) {
main_window->signal_status_str_ = "SignalConnecting";
} else if (SignalStatus::SignalConnected == status) {
main_window->signal_status_str_ = "SignalConnected";
} else if (SignalStatus::SignalFailed == status) {
main_window->signal_status_str_ = "SignalFailed";
} else if (SignalStatus::SignalClosed == status) {
main_window->signal_status_str_ = "SignalClosed";
} else if (SignalStatus::SignalReconnecting == status) {
main_window->signal_status_str_ = "SignalReconnecting";
}
}
void MainWindow::OnConnectionStatusCb(ConnectionStatus status,
void *user_data) {
MainWindow *main_window = (MainWindow *)user_data;
main_window->connection_status_ = status;
if (ConnectionStatus::Connecting == status) {
main_window->connection_status_str_ = "Connecting";
} else if (ConnectionStatus::Connected == status) {
main_window->connection_status_str_ = "Connected";
main_window->connection_established_ = true;
if (!main_window->is_client_mode_) {
main_window->start_screen_capture_ = true;
main_window->start_mouse_control_ = true;
}
} else if (ConnectionStatus::Disconnected == status) {
main_window->connection_status_str_ = "Disconnected";
} else if (ConnectionStatus::Failed == status) {
main_window->connection_status_str_ = "Failed";
} else if (ConnectionStatus::Closed == status) {
main_window->connection_status_str_ = "Closed";
main_window->start_screen_capture_ = false;
main_window->start_mouse_control_ = false;
main_window->connection_established_ = false;
main_window->control_mouse_ = false;
if (main_window->dst_buffer_) {
memset(main_window->dst_buffer_, 0, 1280 * 720 * 3);
SDL_UpdateTexture(main_window->sdl_texture_, NULL,
main_window->dst_buffer_, 1280);
}
} else if (ConnectionStatus::IncorrectPassword == status) {
main_window->connection_status_str_ = "Incorrect password";
if (main_window->connect_button_pressed_) {
main_window->connect_button_pressed_ = false;
main_window->connection_established_ = false;
main_window->connect_button_label_ =
main_window->connect_button_pressed_
? localization::disconnect[main_window
->localization_language_index_]
: localization::connect[main_window
->localization_language_index_];
}
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
main_window->connection_status_str_ = "No such transmission id";
if (main_window->connect_button_pressed_) {
main_window->connect_button_pressed_ = false;
main_window->connection_established_ = false;
main_window->connect_button_label_ =
main_window->connect_button_pressed_
? localization::disconnect[main_window
->localization_language_index_]
: localization::connect[main_window
->localization_language_index_];
}
}
}

View File

@@ -9,7 +9,12 @@ unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
ScreenCapturerX11::ScreenCapturerX11() {}
ScreenCapturerX11::~ScreenCapturerX11() {}
ScreenCapturerX11::~ScreenCapturerX11() {
if (inited_ && capture_thread_->joinable()) {
capture_thread_->join();
inited_ = false;
}
}
int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
@@ -91,17 +96,13 @@ int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
return 0;
}
int ScreenCapturerX11::Destroy() {
if (capture_thread_->joinable()) {
capture_thread_->join();
}
inited_ = true;
return 0;
}
int ScreenCapturerX11::Destroy() { return 0; }
int ScreenCapturerX11::Start() {
capture_thread_.reset(new std::thread([this]() {
while (1) {
@@ -132,12 +133,12 @@ int ScreenCapturerX11::Start() {
return 0;
}
int ScreenCapturerX11::Stop() { return 0; }
int ScreenCapturerX11::Pause() { return 0; }
int ScreenCapturerX11::Resume() { return 0; }
int ScreenCapturerX11::Stop() { return 0; }
void ScreenCapturerX11::OnFrame() {}
void ScreenCapturerX11::CleanUp() {}

View File

@@ -32,9 +32,10 @@ class ScreenCapturerX11 : public ScreenCapturer {
virtual int Start();
virtual int Stop();
int Pause();
int Resume();
int Stop();
void OnFrame();
@@ -61,6 +62,8 @@ class ScreenCapturerX11 : public ScreenCapturer {
int videoindex_ = 0;
int got_picture_ = 0;
int fps_ = 0;
bool inited_ = false;
// ffmpeg
AVFormatContext *pFormatCtx_ = nullptr;
AVCodecContext *pCodecCtx_ = nullptr;

View File

@@ -9,7 +9,12 @@ unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
ScreenCapturerAvf::ScreenCapturerAvf() {}
ScreenCapturerAvf::~ScreenCapturerAvf() {}
ScreenCapturerAvf::~ScreenCapturerAvf() {
if (inited_ && capture_thread_->joinable()) {
capture_thread_->join();
inited_ = false;
}
}
int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
@@ -92,17 +97,13 @@ int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
return 0;
}
int ScreenCapturerAvf::Destroy() {
if (capture_thread_->joinable()) {
capture_thread_->join();
}
inited_ = true;
return 0;
}
int ScreenCapturerAvf::Destroy() { return 0; }
int ScreenCapturerAvf::Start() {
capture_thread_.reset(new std::thread([this]() {
while (1) {
@@ -133,12 +134,12 @@ int ScreenCapturerAvf::Start() {
return 0;
}
int ScreenCapturerAvf::Stop() { return 0; }
int ScreenCapturerAvf::Pause() { return 0; }
int ScreenCapturerAvf::Resume() { return 0; }
int ScreenCapturerAvf::Stop() { return 0; }
void ScreenCapturerAvf::OnFrame() {}
void ScreenCapturerAvf::CleanUp() {}

View File

@@ -38,9 +38,10 @@ class ScreenCapturerAvf : public ScreenCapturer {
virtual int Start();
virtual int Stop();
int Pause();
int Resume();
int Stop();
void OnFrame();
@@ -66,6 +67,8 @@ class ScreenCapturerAvf : public ScreenCapturer {
int i_ = 0;
int videoindex_ = 0;
int got_picture_ = 0;
bool inited_ = false;
// ffmpeg
AVFormatContext *pFormatCtx_ = nullptr;
AVCodecContext *pCodecCtx_ = nullptr;

View File

@@ -28,6 +28,8 @@ class ScreenCapturer {
virtual int Destroy() = 0;
virtual int Start() = 0;
virtual int Stop() = 0;
};
#endif

View File

@@ -5,36 +5,9 @@
#include <winrt/Windows.Foundation.Metadata.h>
#include <winrt/Windows.Graphics.Capture.h>
extern "C" {
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
};
#include <iostream>
int BGRAToNV12FFmpeg(unsigned char *src_buffer, int width, int height,
unsigned char *dst_buffer) {
AVFrame *Input_pFrame = av_frame_alloc();
AVFrame *Output_pFrame = av_frame_alloc();
struct SwsContext *img_convert_ctx =
sws_getContext(width, height, AV_PIX_FMT_BGRA, 1280, 720, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
av_image_fill_arrays(Input_pFrame->data, Input_pFrame->linesize, src_buffer,
AV_PIX_FMT_BGRA, width, height, 1);
av_image_fill_arrays(Output_pFrame->data, Output_pFrame->linesize, dst_buffer,
AV_PIX_FMT_NV12, 1280, 720, 1);
sws_scale(img_convert_ctx, (uint8_t const **)Input_pFrame->data,
Input_pFrame->linesize, 0, height, Output_pFrame->data,
Output_pFrame->linesize);
if (Input_pFrame) av_free(Input_pFrame);
if (Output_pFrame) av_free(Output_pFrame);
if (img_convert_ctx) sws_freeContext(img_convert_ctx);
return 0;
}
#include "libyuv.h"
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc,
LPARAM data) {
@@ -62,7 +35,20 @@ HMONITOR GetPrimaryMonitor() {
ScreenCapturerWgc::ScreenCapturerWgc() {}
ScreenCapturerWgc::~ScreenCapturerWgc() {}
ScreenCapturerWgc::~ScreenCapturerWgc() {
Stop();
CleanUp();
if (nv12_frame_) {
delete nv12_frame_;
nv12_frame_ = nullptr;
}
if (nv12_frame_scaled_) {
delete nv12_frame_scaled_;
nv12_frame_scaled_ = nullptr;
}
}
bool ScreenCapturerWgc::IsWgcSupported() {
try {
@@ -81,7 +67,11 @@ int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
int error = 0;
if (_inited == true) return error;
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 4];
int r = rect.right;
int b = rect.bottom;
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2];
nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2];
_fps = fps;
@@ -114,17 +104,7 @@ int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
return error;
}
int ScreenCapturerWgc::Destroy() {
if (nv12_frame_) {
delete nv12_frame_;
nv12_frame_ = nullptr;
}
Stop();
CleanUp();
return 0;
}
int ScreenCapturerWgc::Destroy() { return 0; }
int ScreenCapturerWgc::Start() {
if (_running == true) {
@@ -163,12 +143,59 @@ int ScreenCapturerWgc::Stop() {
return 0;
}
void ConvertABGRtoBGRA(const uint8_t *abgr_data, uint8_t *bgra_data, int width,
int height, int abgr_stride, int bgra_stride) {
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
// ABGR<47><52>BGRA<52><41><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
int abgr_index = (i * abgr_stride + j) * 4;
int bgra_index = (i * bgra_stride + j) * 4;
// ֱ<>ӽ<EFBFBD><D3BD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD>ͺ<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
bgra_data[bgra_index + 0] = abgr_data[abgr_index + 2]; // <20><>ɫ
bgra_data[bgra_index + 1] = abgr_data[abgr_index + 1]; // <20><>ɫ
bgra_data[bgra_index + 2] = abgr_data[abgr_index + 0]; // <20><>ɫ
bgra_data[bgra_index + 3] = abgr_data[abgr_index + 3]; // Alpha
}
}
}
void ConvertBGRAtoABGR(const uint8_t *bgra_data, uint8_t *abgr_data, int width,
int height, int bgra_stride, int abgr_stride) {
for (int i = 0; i < height; ++i) {
for (int j = 0; j < width; ++j) {
// BGRA<52><41>ABGR<47><52><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
int bgra_index = (i * bgra_stride + j) * 4;
int abgr_index = (i * abgr_stride + j) * 4;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>
abgr_data[abgr_index + 0] = bgra_data[bgra_index + 3]; // Alpha
abgr_data[abgr_index + 1] = bgra_data[bgra_index + 0]; // Blue
abgr_data[abgr_index + 2] = bgra_data[bgra_index + 1]; // Green
abgr_data[abgr_index + 3] = bgra_data[bgra_index + 2]; // Red
}
}
}
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame) {
if (_on_data)
BGRAToNV12FFmpeg((unsigned char *)frame.data, frame.width, frame.height,
nv12_frame_);
_on_data(nv12_frame_, frame.width * frame.height * 4, frame.width,
frame.height);
if (_on_data) {
int width = 1280;
int height = 720;
libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4,
(uint8_t *)nv12_frame_, frame.width,
(uint8_t *)(nv12_frame_ + frame.width * frame.height),
frame.width, frame.width, frame.height);
libyuv::NV12Scale(
(const uint8_t *)nv12_frame_, frame.width,
(const uint8_t *)(nv12_frame_ + frame.width * frame.height),
frame.width, frame.width, frame.height, (uint8_t *)nv12_frame_scaled_,
width, (uint8_t *)(nv12_frame_scaled_ + width * height), width, width,
height, libyuv::FilterMode::kFilterLinear);
_on_data(nv12_frame_scaled_, width * height * 3 / 2, width, height);
}
}
void ScreenCapturerWgc::CleanUp() {

View File

@@ -27,7 +27,7 @@ class ScreenCapturerWgc : public ScreenCapturer,
int Pause();
int Resume();
int Stop();
virtual int Stop();
void OnFrame(const WgcSession::wgc_session_frame &frame);
@@ -49,9 +49,10 @@ class ScreenCapturerWgc : public ScreenCapturer,
int _fps;
cb_desktop_data _on_data;
cb_desktop_data _on_data = nullptr;
unsigned char *nv12_frame_ = nullptr;
unsigned char *nv12_frame_scaled_ = nullptr;
};
#endif

View File

@@ -1,10 +0,0 @@
package("sdl2")
add_urls("https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.28.3.tar.gz", {alias = "github"})
add_versions("github:2.28.3", "c17455d6e0c484bfe634b8de6af4c608e86ee449c28e40af04064aa6643fe382")
on_install(function (package)
local configs = {}
table.insert(configs, "-DSDL_SHARED_ENABLED_BY_DEFAULT=OFF -DSDL_TEST_ENABLED_BY_DEFAULT=OFF")
import("package.tools.cmake").install(package, configs)
end)
package_end()

View File

@@ -1,4 +1,4 @@
includes("sdl2", "projectx")
includes("projectx")
if is_plat("windows") then
elseif is_plat("linux") then
includes("ffmpeg")

View File

@@ -13,19 +13,16 @@ if is_mode("debug") then
add_defines("REMOTE_DESK_DEBUG")
end
add_requires("sdl2", {system = false})
add_requires("spdlog 1.11.0", {system = false})
add_requires("imgui 1.89.9", {configs = {sdl2 = true, sdl2_renderer = true}})
add_requires("spdlog 1.14.1", {system = false})
add_requires("imgui 1.90.6", {configs = {sdl2 = true, sdl2_renderer = true}})
add_requires("libyuv")
if is_os("windows") then
add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32",
"SDL2-static", "SDL2main", "gdi32", "winmm", "setupapi", "version",
"Imm32", "iphlpapi")
add_requires("vcpkg::ffmpeg 5.1.2", {configs = {shared = false}})
add_packages("vcpkg::ffmpeg")
elseif is_os("linux") then
add_requires("ffmpeg 5.1.2", {system = false})
add_packages("ffmpeg")
add_syslinks("pthread", "dl")
add_linkdirs("thirdparty/projectx/thirdparty/nvcodec/Lib/x64")
add_links("SDL2", "cuda", "nvidia-encode", "nvcuvid")
@@ -37,13 +34,13 @@ elseif is_os("linux") then
elseif is_os("macosx") then
add_requires("ffmpeg 5.1.2", {system = false})
add_requires("libxcb", {system = false})
add_packages("ffmpeg", "libxcb")
add_packages("libxcb")
add_links("SDL2", "SDL2main")
add_ldflags("-Wl,-ld_classic")
add_frameworks("OpenGL")
end
add_packages("spdlog", "sdl2", "imgui")
add_packages("spdlog", "imgui")
includes("thirdparty")
@@ -62,16 +59,19 @@ target("common")
target("screen_capturer")
set_kind("object")
add_deps("log")
add_packages("libyuv")
add_includedirs("src/screen_capturer", {public = true})
if is_os("windows") then
add_files("src/screen_capturer/windows/*.cpp")
add_includedirs("src/screen_capturer/windows", {public = true})
elseif is_os("macosx") then
add_files("src/screen_capturer/macosx/*.cpp")
add_includedirs("src/screen_capturer/macosx", {public = true})
add_packages("ffmpeg")
add_files("src/screen_capturer/macosx/*.cpp")
add_includedirs("src/screen_capturer/macosx", {public = true})
elseif is_os("linux") then
add_files("src/screen_capturer/linux/*.cpp")
add_includedirs("src/screen_capturer/linux", {public = true})
add_packages("ffmpeg")
add_files("src/screen_capturer/linux/*.cpp")
add_includedirs("src/screen_capturer/linux", {public = true})
end
target("device_controller")
@@ -89,19 +89,37 @@ target("device_controller")
add_includedirs("src/device_controller/mouse/linux", {public = true})
end
target("config_center")
set_kind("object")
add_deps("log")
add_files("src/config_center/*.cpp")
add_includedirs("src/config_center", {public = true})
target("localization")
set_kind("headeronly")
add_includedirs("src/localization", {public = true})
target("main_window")
set_kind("object")
add_deps("log", "common", "localization", "config_center", "projectx", "screen_capturer", "device_controller")
if is_os("macosx") then
add_packages("ffmpeg")
elseif is_os("linux") then
add_packages("ffmpeg")
end
add_files("src/main_window/*.cpp")
add_includedirs("src/main_window", {public = true})
target("remote_desk")
set_kind("binary")
add_deps("log", "common", "screen_capturer", "device_controller", "projectx")
add_deps("log", "common", "main_window")
if is_os("macosx") then
add_packages("ffmpeg")
elseif is_os("linux") then
add_packages("ffmpeg")
end
add_files("src/gui/main.cpp")
-- after_install(function (target)
-- os.cp("$(projectdir)/thirdparty/nvcodec/Lib/x64/*.so", "$(projectdir)/out/bin")
-- os.cp("$(projectdir)/thirdparty/nvcodec/Lib/x64/*.so.1", "$(projectdir)/out/bin")
-- os.cp("$(projectdir)/out/lib/*.so", "$(projectdir)/out/bin")
-- os.rm("$(projectdir)/out/include")
-- os.rm("$(projectdir)/out/lib")
-- end)
-- target("screen_capturer")
-- set_kind("binary")
-- add_packages("sdl2", "imgui", "ffmpeg", "openh264")
@@ -157,7 +175,7 @@ target("remote_desk")
-- "-lxcb-shm", "-lXext", "-lX11", "-lXv", "-lpthread", "-lSDL2", "-lopenh264",
-- "-ldl", {force = true})
target("mouse_control")
set_kind("binary")
add_files("test/linux_mouse_control/mouse_control.cpp")
add_includedirs("test/linux_mouse_control")
-- target("mouse_control")
-- set_kind("binary")
-- add_files("test/linux_mouse_control/mouse_control.cpp")
-- add_includedirs("test/linux_mouse_control")