170 Commits

Author SHA1 Message Date
dijunkun
863070a8a7 [feat] enable window grab when mouse control enabled 2024-11-04 17:29:26 +08:00
dijunkun
44f9e6a8c9 [fix] fix crash due to multi-context fonts release 2024-11-04 16:13:20 +08:00
dijunkun
087d5d7e52 [feat] use an additional window to show video streams 2024-11-01 20:30:06 +08:00
dijunkun
26fa53f867 [fix] fix imgui layout error 2024-11-01 15:57:15 +08:00
dijunkun
d18af6cbc6 [fix] fix client id generation 2024-10-30 17:25:41 +08:00
dijunkun
b5bb62bd22 [feat] support new screen capture method by using ScreenCaptureKit on MacOSX 2024-10-18 17:20:52 +08:00
dijunkun
9ed3ab9929 [fix] fix the fullscreen error when closing the connection 2024-09-19 16:41:59 +08:00
dijunkun
dca18762e0 [fix] use original screen render resolution to capturing and fix cursor mapping error 2024-09-13 17:08:58 +08:00
dijunkun
fed7c3b103 [fix] fix cursor mapping error 2024-09-13 16:04:19 +08:00
dijunkun
d246b7a04d [fix] fix the issue where the title bar is displayed incorrectly when in fullscreen mode 2024-09-13 15:10:23 +08:00
dijunkun
a49ca813e0 [fix] fix black screen after close the connection 2024-09-13 10:35:53 +08:00
dijunkun
0c688efaee [fix] fix buttons position when control bar in the right 2024-09-12 17:22:13 +08:00
dijunkun
be3561d46f [fix] optimize the first graph rendering time when open this program 2024-09-12 17:17:07 +08:00
dijunkun
c3af40a3f0 [feat] add close button in control bar 2024-09-12 16:22:02 +08:00
dijunkun
d493b9a131 [feat] make the window centered on the screen after closed 2024-09-12 13:57:51 +08:00
dijunkun
4e4e84ae4d [fix] fix window size when closed after resized 2024-09-12 13:51:11 +08:00
dijunkun
fea545e5e7 [fix] do not forget to destroy the texture 2024-09-11 17:35:27 +08:00
dijunkun
9096769a85 [fix] fix render stream blurry problem 2024-09-11 11:22:17 +08:00
dijunkun
04ab157ecb [fix] fix crash due to invalid pointer 2024-09-10 17:33:55 +08:00
dijunkun
2331f08283 [fix] fix cursor mapping error due to the client render aspect ratio different from the server screen aspect ratio 2024-09-06 19:36:33 +08:00
dijunkun
9f8f99f21b [fix] fix cursor mapping error due to ffmpeg default screen capture resolution different from the real screen resolution 2024-09-06 19:32:30 +08:00
dijunkun
56dadb6a49 Merge branch 'rt_desk' of github.com:dijunkun/continuous-desk into rt_desk 2024-09-06 17:38:43 +08:00
dijunkun
59c9ca8d53 [fix] fix render area cannot fit the resolution of the receiving video stream 2024-09-06 17:38:09 +08:00
dijunkun
f16a4e8aa2 [feat] support original resolution screen capture on MacOSX 2024-09-06 15:20:06 +08:00
dijunkun
890615e13a [fix] fix crash during termination 2024-09-06 13:07:20 +08:00
dijunkun
2f72e3957e [feat] support dynamic resolution codec 2024-09-05 17:29:27 +08:00
dijunkun
1292018f51 [fix] fix crash when signal server close the connection actively 2024-09-04 17:03:40 +08:00
dijunkun
8ae9513104 [feat] only a six-char password will be accepted 2024-09-03 17:29:46 +08:00
dijunkun
c1efe2f4ac [feat] do not capture cursor 2024-09-03 17:00:57 +08:00
dijunkun
1210a0b631 [fix] fix crash caused by accessing invalid memory 2024-09-03 16:24:36 +08:00
dijunkun
39863c597e [feat] allow user to set customized password 2024-09-03 15:50:38 +08:00
dijunkun
8a964f0030 [feat] add option 'enable TURN service' in settings menu 2024-09-02 17:29:03 +08:00
dijunkun
74e29f25bf [fix] do not use smart pointer to manage std::thread objects 2024-09-02 16:33:01 +08:00
dijunkun
1e5bea2b1e [feat] put ice agent into ice worker thread and use message queue to handle events 2024-08-28 17:31:27 +08:00
dijunkun
d8297ebb74 [feat] use fix random password otherwise user regenerates one 2024-08-28 10:28:11 +08:00
dijunkun
93d7f71cf2 [feat] support use param to control enable TURN or not 2024-08-27 17:06:56 +08:00
dijunkun
887a217828 [fix] fix fonts missing 2024-08-27 17:04:11 +08:00
dijunkun
89b12136e4 [fix] Optimizing video encoding speed for software encoders 2024-08-20 17:18:29 +08:00
dijunkun
def7025abf [fix] fix connection status display 2024-08-20 10:21:34 +08:00
dijunkun
35af5aab43 [fix] fix control bar border color 2024-08-19 17:09:19 +08:00
dijunkun
9ea67df0fd [fix] fix title bar icon error when leaves maximized state 2024-08-19 14:16:21 +08:00
dijunkun
72fda8a728 [feat] let keyboard focus on input widget when needs to input password 2024-08-19 11:05:08 +08:00
dijunkun
070b48d7a7 [fix] fix mouse and audio capture buttons cannot be disabled once enabled 2024-08-16 17:30:44 +08:00
dijunkun
6168009cef [feat] support building xcode app on MacOSX 2024-08-16 17:19:17 +08:00
dijunkun
06a7243ac1 [fix] fix mouse click event sending when control bar hovered 2024-08-16 15:06:42 +08:00
dijunkun
c8602b0d89 [fix] fix cursor position error 2024-08-16 14:52:44 +08:00
dijunkun
e3c730fd5f [fix] fix screen capture error in client mode 2024-08-16 14:22:08 +08:00
dijunkun
b252cb6ddd [style] use self-draw icon for mouse and audio_capture disable buttons 2024-08-16 13:55:48 +08:00
dijunkun
8ca1e8e5a1 [style] reset menu bar line size 2024-08-16 09:35:09 +08:00
dijunkun
a7d45b78c8 [feat] use self-draw icon for title bar 2024-08-15 17:38:18 +08:00
dijunkun
018231eee4 [feat] add protocol to control audio capture 2024-08-15 14:50:47 +08:00
dijunkun
4704d494ec [feat] add audio capture button in control bar 2024-08-15 11:25:19 +08:00
dijunkun
65927c2091 [feat] support speaker capture on Windows 2024-08-15 11:04:06 +08:00
dijunkun
574b9d10ab [fix] let temproary setting values equal to setting values when loading from cache file 2024-08-14 16:50:49 +08:00
dijunkun
ff510a3b44 [fix] fix peer object delete 2024-08-14 16:39:08 +08:00
dijunkun
4d3c864950 [fix] fix client id cannot read from cache file 2024-08-13 17:17:35 +08:00
dijunkun
2cde54cf30 [fix] call memset() before using strncpy() 2024-08-13 17:07:19 +08:00
dijunkun
d1f3d11318 [fix] use SDL_RestoreWindow() to reset the window when closed during streaming 2024-08-13 16:32:17 +08:00
dijunkun
436228946b [feat] do not show menu button during streaming 2024-08-13 16:21:31 +08:00
dijunkun
2b4083ee10 [feat] expand control bar by default 2024-08-13 11:26:18 +08:00
dijunkun
e3abb4e3de [fix] fix recreate peer failed due to is_create_connection_ flag not reset 2024-08-13 11:15:28 +08:00
dijunkun
4b7cd1005b [fix] fix send error when ice state change from ready to connected 2024-08-09 16:54:14 +08:00
dijunkun
03ea96096d [feat] add space automaticlly for remote id input box 2024-08-09 11:33:13 +08:00
dijunkun
0ea8916426 [fix] fix settings error when load from cache file 2024-08-09 10:56:16 +08:00
dijunkun
43b36eb893 [fix] use proper std::chrono clock 2024-08-09 10:23:51 +08:00
dijunkun
03b6a187b3 [feat] split remote id into chunks of three characters separated by space 2024-08-08 16:34:55 +08:00
dijunkun
664412dd4e [feat] enable Enter key pressing for ImGui::InputText() method 2024-08-08 15:56:20 +08:00
dijunkun
b37e08a202 [fix] fix client id empty error when run the program firstly 2024-08-08 15:26:21 +08:00
dijunkun
a05d72ec67 [feat] lock the cache file when write/read it 2024-08-08 15:24:54 +08:00
dijunkun
f77e9fe6a8 [feat] remove 'I/l/O/o/0' from password generator 2024-08-08 15:22:51 +08:00
dijunkun
1f9614e060 [feat] use h264 codec by default 2024-08-08 15:15:58 +08:00
dijunkun
50d92a763a [fix] fix leave transmission error when exit program 2024-08-07 17:33:05 +08:00
dijunkun
ec23656334 [fix] fix client id error when connect to itself 2024-08-07 09:55:59 +08:00
dijunkun
880c2949c3 [feat] Use server to generate transmission id and client id 2024-08-06 17:27:40 +08:00
dijunkun
07f5fe81c8 [fix] fix control bar cannot stick to right side when out of y-axis region 2024-08-06 11:24:10 +08:00
dijunkun
5a992b6589 [fix] fix control bar width 2024-08-06 10:35:25 +08:00
dijunkun
8e03e8e79b [feat] make control bar stick to left/right border 2024-08-05 17:28:39 +08:00
dijunkun
ceb3d9fe20 [feat] make control bar can only move along left border of the main window 2024-08-02 17:31:01 +08:00
dijunkun
0dc0b87bc4 [fix] fix control bar initial postion 2024-08-02 16:46:39 +08:00
dijunkun
3a4284fece [fix] fix read cache file failed 2024-08-02 16:15:18 +08:00
dijunkun
502a90f121 [fix] fix program cannot exit when click close button due to screen capture thread is running 2024-08-02 14:38:31 +08:00
dijunkun
88cd4aca4a [fix] fix title bar display error when streaming 2024-08-02 13:57:12 +08:00
dijunkun
3395004f93 [fix] fix compile error on Macosx and Linux 2024-08-02 13:56:22 +08:00
dijunkun
e4c05e1f4d [feat] enable movement for control bar 2024-08-02 12:30:50 +08:00
dijunkun
d17c70c2c8 [feat] remove menu bar and move menu button to title bar 2024-08-01 17:28:39 +08:00
dijunkun
7b42923418 [feat] use self designed title bar instead of system default title bar 2024-07-31 17:43:02 +08:00
dijunkun
5b6bdee25a [feat] add callback to notify the travsesal mode 2024-07-30 17:32:12 +08:00
dijunkun
05deb73c29 [feat] Support trickle ice 2024-07-29 16:48:22 +08:00
dijunkun
3685acc549 [feat] set window resizable only in streaming state 2024-07-25 10:10:37 +08:00
dijunkun
8f5a53937a [fix] fix control bar display and button postion 2024-07-24 17:10:05 +08:00
dijunkun
b9c5db41ab [fix] show connection status windows when connection status changed 2024-07-24 16:27:58 +08:00
dijunkun
a99a4230af Add module: speaker capture 2024-07-24 16:16:13 +08:00
dijunkun
f446154747 1.Fix texture update crash; 2.Do not show control window when in server mode 2024-07-19 14:54:53 +08:00
dijunkun
5a1e2c5ed9 Use speaker as audio input 2024-07-19 14:10:35 +08:00
dijunkun
ff6f295fac Do not use rc file on MacOS 2024-07-18 16:06:35 +08:00
dijunkun
3111b3a641 Add icon 2024-07-18 15:58:28 +08:00
dijunkun
20bb13ce85 Chinese support in connection status window 2024-07-18 14:46:19 +08:00
dijunkun
5aa05f3a13 Redesign elements styles 2024-07-18 11:02:35 +08:00
dijunkun
c911aa2eb1 Add about window 2024-07-17 16:22:45 +08:00
dijunkun
d0cd2fe9ab Fix log instance mismatch 2024-07-17 14:45:30 +08:00
dijunkun
9702805331 Make control bar scrollable 2024-07-05 16:05:50 +08:00
dijunkun
872152f1be 1.Enable random password; 2.Request user to input password when password is incorrect 2024-07-03 15:07:40 +08:00
dijunkun
b822221d7f Add a button which can copy local id to clipboard 2024-07-03 11:03:04 +08:00
dijunkun
95ad605b36 Add signal and p2p connection status windows 2024-07-02 17:29:39 +08:00
dijunkun
af32e25149 Use minimized compressed fonts header file 2024-07-02 11:19:50 +08:00
dijunkun
e63b384d1e Set dpi scaling to solve display errot when using high dpi displayer 2024-07-01 16:31:39 +08:00
dijunkun
7f25f7426c Fix remote window height size error 2024-07-01 11:24:58 +08:00
dijunkun
eed93ea953 Set utf-8 encoding flag in xmake.lua 2024-07-01 10:20:32 +08:00
dijunkun
b5f8e92526 Solve the warning of method ImGui::Text() 2024-06-28 15:11:40 +08:00
dijunkun
af04b0571e Use UTF-8 to save files 2024-06-28 12:49:56 +08:00
dijunkun
75452a3e76 1.Use compressed OPPOSans-Regular[3500 chinese characters]; 2.Use same layout style on different platforms 2024-06-28 12:30:09 +08:00
dijunkun
3f717f1df2 Use binary font headerfile instead of ttf file 2024-06-28 10:01:34 +08:00
dijunkun
ad6f2c2c70 Implementation for menu bar 2024-06-27 17:36:13 +08:00
dijunkun
8076e7f662 Fix stream render with menu bar 2024-06-26 14:58:15 +08:00
dijunkun
be78496992 Separate stream window from main window 2024-06-25 14:47:54 +08:00
dijunkun
a3f745d441 Use fix ratio to render frame in window 2024-06-25 11:12:10 +08:00
dijunkun
e693d920d3 Use sub windows to render main window 2024-06-24 17:35:25 +08:00
dijunkun
0f1b89eda9 Test tabbar in ImGui 2024-06-19 17:33:44 +08:00
dijunkun
172b8836fd Use FontAwesome6 to render icons 2024-06-18 17:28:02 +08:00
dijunkun
71178ffa33 Separate render window from main window 2024-06-17 17:31:57 +08:00
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
67 changed files with 49132 additions and 1173 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

39
Info.plist Normal file
View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- 应用的Bundle identifier通常使用反向域名标记 -->
<key>CFBundleIdentifier</key>
<string>com.yourcompany.yourappname</string>
<!-- 应用的显示名称 -->
<key>CFBundleName</key>
<string>Your App Name</string>
<!-- 应用的版本号 -->
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<!-- 应用的构建版本号 -->
<key>CFBundleVersion</key>
<string>1</string>
<!-- 请求麦克风访问权限 -->
<key>NSMicrophoneUsageDescription</key>
<string>App requires access to the microphone for audio recording.</string>
<!-- 请求相机访问权限 -->
<key>NSCameraUsageDescription</key>
<string>App requires access to the camera for video recording.</string>
<!-- 请求使用连续相机设备 -->
<key>NSCameraUseContinuityCameraDeviceType</key>
<string>Your usage description here</string>
<!-- High DPI -->>
<key>NSHighResolutionCapable</key>
<true/>
<!-- 其他权限和配置可以在这里添加 -->
</dict>
</plist>

View File

@@ -11,7 +11,7 @@
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

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

2025
fonts/OPPOSans_Regular.h Normal file

File diff suppressed because it is too large Load Diff

5658
fonts/fa_regular_400.h Normal file

File diff suppressed because it is too large Load Diff

35031
fonts/fa_solid_900.h Normal file

File diff suppressed because it is too large Load Diff

1
icon/app.rc Normal file
View File

@@ -0,0 +1 @@
IDI_ICON1 ICON "app_icon.ico"

BIN
icon/app_icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 687 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -1,6 +1,6 @@
#include "platform.h"
#include "log.h"
#include "rd_log.h"
#ifdef _WIN32
#include <Winsock2.h>
@@ -55,7 +55,8 @@ std::string GetMac() {
const unsigned char *base =
(const unsigned char *)&dlAddr->sdl_data[dlAddr->sdl_nlen];
for (int i = 0; i < dlAddr->sdl_alen; i++) {
len += sprintf(mac_addr + len, "%.2X", base[i]);
len +=
snprintf(mac_addr + len, sizeof(mac_addr) - len, "%.2X", base[i]);
}
}
cursor = cursor->ifa_next;

View File

@@ -0,0 +1,45 @@
#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;
}
int ConfigCenter::SetTurn(bool enable_turn) {
enable_turn_ = enable_turn;
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_; }
bool ConfigCenter::IsEnableTurn() { return enable_turn_; }

View File

@@ -0,0 +1,43 @@
/*
* @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);
int SetTurn(bool enable_turn);
public:
LANGUAGE GetLanguage();
VIDEO_QUALITY GetVideoQuality();
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat();
bool IsHardwareVideoCodec();
bool IsEnableTurn();
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;
bool enable_turn_ = false;
};
#endif

View File

@@ -9,7 +9,7 @@
#include <stdio.h>
typedef enum { mouse = 0, keyboard } ControlType;
typedef enum { mouse = 0, keyboard, audio_capture } ControlType;
typedef enum { move = 0, left_down, left_up, right_down, right_up } MouseFlag;
typedef enum { key_down = 0, key_up } KeyFlag;
typedef struct {
@@ -28,6 +28,7 @@ typedef struct {
union {
Mouse m;
Key k;
bool a;
};
} RemoteAction;

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,19 +41,16 @@ 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;
int mouse_pos_y = remote_action.m.y * screen_height_ / 720;
int mouse_pos_x = remote_action.m.x;
int mouse_pos_y = remote_action.m.y;
if (remote_action.type == ControlType::mouse) {
struct input_event event;
@@ -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

@@ -2,7 +2,7 @@
#include <ApplicationServices/ApplicationServices.h>
#include "log.h"
#include "rd_log.h"
MouseController::MouseController() {}
@@ -12,14 +12,19 @@ int MouseController::Init(int screen_width, int screen_height) {
screen_width_ = screen_width;
screen_height_ = screen_height;
pixel_width_ =
CGDisplayModeGetPixelWidth(CGDisplayCopyDisplayMode(CGMainDisplayID()));
pixel_height_ =
CGDisplayModeGetPixelHeight(CGDisplayCopyDisplayMode(CGMainDisplayID()));
return 0;
}
int MouseController::Destroy() { return 0; }
int MouseController::SendCommand(RemoteAction remote_action) {
int mouse_pos_x = remote_action.m.x * screen_width_ / 1280;
int mouse_pos_y = remote_action.m.y * screen_height_ / 720;
int mouse_pos_x = remote_action.m.x * screen_width_ / pixel_width_;
int mouse_pos_y = remote_action.m.y * screen_height_ / pixel_height_;
if (remote_action.type == ControlType::mouse) {
CGEventRef mouse_event;

View File

@@ -22,6 +22,9 @@ class MouseController : public DeviceController {
private:
int screen_width_ = 0;
int screen_height_ = 0;
int pixel_width_ = 0;
int pixel_height_ = 0;
};
#endif

View File

@@ -1,6 +1,6 @@
#include "mouse_controller.h"
#include "log.h"
#include "rd_log.h"
MouseController::MouseController() {}
@@ -20,8 +20,8 @@ int MouseController::SendCommand(RemoteAction remote_action) {
if (remote_action.type == ControlType::mouse) {
ip.type = INPUT_MOUSE;
ip.mi.dx = remote_action.m.x * screen_width_ / 1280;
ip.mi.dy = remote_action.m.y * screen_height_ / 720;
ip.mi.dx = remote_action.m.x;
ip.mi.dy = remote_action.m.y;
if (remote_action.m.flag == MouseFlag::left_down) {
ip.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE;
} else if (remote_action.m.flag == MouseFlag::left_up) {

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 "rd_log.h"
#include "render.h"
#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"
#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[7];
} 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");
Render render;
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();
render.Run();
return 0;
}

View File

@@ -0,0 +1,85 @@
/*
* @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> local_desktop = {u8"本桌面", "Local Desktop"};
static std::vector<std::string> local_id = {u8"本机ID", "Local ID"};
static std::vector<std::string> local_id_copied_to_clipboard = {
u8"已复制到剪贴板", "Copied to clipboard"};
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_desktop = {u8"控制远程桌面",
"Control Remote Desktop"};
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" 控制", " Control"};
static std::vector<std::string> release_mouse = {u8" 释放", " Release"};
static std::vector<std::string> audio_capture = {u8" 声音", " Audio"};
static std::vector<std::string> mute = {u8" 静音", " Mute"};
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> enable_turn = {u8"启用中继服务:",
"Enable TURN Service:"};
static std::vector<std::string> ok = {u8"确认", "OK"};
static std::vector<std::string> cancel = {u8"取消", "Cancel"};
static std::vector<std::string> new_password = {
u8"请输入六位密码:", "Please input a six-char password:"};
static std::vector<std::string> input_password = {u8"请输入密码:",
"Please input password:"};
static std::vector<std::string> validate_password = {u8"验证密码中...",
"Validate password ..."};
static std::vector<std::string> reinput_password = {
u8"请重新输入密码", "Please input password again"};
static std::vector<std::string> signal_connected = {u8"已连接服务器",
"Connected"};
static std::vector<std::string> signal_disconnected = {u8"未连接服务器",
"Disconnected"};
static std::vector<std::string> p2p_connected = {u8"对等连接已建立",
"P2P Connected"};
static std::vector<std::string> p2p_disconnected = {u8"对等连接已断开",
"P2P Disconnected"};
static std::vector<std::string> p2p_connecting = {u8"正在建立对等连接...",
"P2P Connecting ..."};
static std::vector<std::string> p2p_failed = {u8"对等连接失败", "P2P Failed"};
static std::vector<std::string> p2p_closed = {u8"对等连接已关闭", "P2P closed"};
static std::vector<std::string> no_such_id = {u8"无此ID", "No such ID"};
static std::vector<std::string> about = {u8"关于", "About"};
static std::vector<std::string> version = {u8"版本", "Version"};
} // namespace localization
#endif

View File

@@ -1,5 +1,11 @@
#ifndef _LOG_H_
#define _LOG_H_
/*
* @Author: DI JUNKUN
* @Date: 2024-07-17
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _RD_LOG_H_
#define _RD_LOG_H_
#include <chrono>
#include <iomanip>
@@ -18,18 +24,7 @@ using namespace std::chrono;
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
// SPDLOG_TRACE(...)
// SPDLOG_DEBUG(...)
// SPDLOG_INFO(...)
// SPDLOG_WARN(...)
// SPDLOG_ERROR(...)
// SPDLOG_CRITICAL(...)
#ifdef SIGNAL_LOGGER
constexpr auto LOGGER_NAME = "siganl_server";
#else
constexpr auto LOGGER_NAME = "remote_desk";
#endif
constexpr auto LOGGER_NAME = "rd";
#define LOG_INFO(...) \
if (nullptr == spdlog::get(LOGGER_NAME)) { \
@@ -124,4 +119,4 @@ constexpr auto LOGGER_NAME = "remote_desk";
SPDLOG_LOGGER_CRITICAL(spdlog::get(LOGGER_NAME), __VA_ARGS__); \
}
#endif
#endif

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) {
@@ -27,6 +32,8 @@ int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
// grabbing frame rate
av_dict_set(&options_, "framerate", "30", 0);
// show remote cursor
av_dict_set(&options_, "capture_cursor", "0", 0);
// Make the grabbed area follow the mouse
// av_dict_set(&options_, "follow_mouse", "centered", 0);
// Video frame size. The default is to capture the full screen
@@ -91,20 +98,19 @@ 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);
inited_ = true;
return 0;
}
int ScreenCapturerX11::Destroy() {
if (capture_thread_->joinable()) {
capture_thread_->join();
}
running_ = false;
return 0;
}
int ScreenCapturerX11::Start() {
capture_thread_.reset(new std::thread([this]() {
while (1) {
capture_thread_ = std::thread([this]() {
while (running_) {
if (av_read_frame(pFormatCtx_, packet_) >= 0) {
if (packet_->stream_index == videoindex_) {
avcodec_send_packet(pCodecCtx_, packet_);
@@ -127,17 +133,20 @@ int ScreenCapturerX11::Start() {
}
}
}
}));
});
return 0;
}
int ScreenCapturerX11::Stop() {
running_ = false;
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;
@@ -74,7 +77,8 @@ class ScreenCapturerX11 : public ScreenCapturer {
struct SwsContext *img_convert_ctx_ = nullptr;
// thread
std::unique_ptr<std::thread> capture_thread_ = nullptr;
std::thread capture_thread_;
std::atomic_bool running_;
};
#endif

View File

@@ -0,0 +1,212 @@
#include "screen_capturer_avf.h"
#include <ApplicationServices/ApplicationServices.h>
#include <iostream>
#include "rd_log.h"
#define USE_SCALE_FACTOR 0
ScreenCapturerAvf::ScreenCapturerAvf() {}
ScreenCapturerAvf::~ScreenCapturerAvf() {
if (inited_ && capture_thread_.joinable()) {
capture_thread_.join();
inited_ = false;
}
if (nv12_frame_) {
delete[] nv12_frame_;
nv12_frame_ = nullptr;
}
if (pFormatCtx_) {
avformat_close_input(&pFormatCtx_);
pFormatCtx_ = nullptr;
}
if (pCodecCtx_) {
avcodec_free_context(&pCodecCtx_);
pCodecCtx_ = nullptr;
}
if (options_) {
av_dict_free(&options_);
options_ = nullptr;
}
if (pFrame_) {
av_frame_free(&pFrame_);
pFrame_ = nullptr;
}
if (packet_) {
av_packet_free(&packet_);
packet_ = nullptr;
}
#if USE_SCALE_FACTOR
if (img_convert_ctx_) {
sws_freeContext(img_convert_ctx_);
img_convert_ctx_ = nullptr;
}
#endif
}
int ScreenCapturerAvf::Init(const int fps, cb_desktop_data cb) {
if (cb) {
_on_data = cb;
}
av_log_set_level(AV_LOG_QUIET);
pFormatCtx_ = avformat_alloc_context();
avdevice_register_all();
// grabbing frame rate
av_dict_set(&options_, "framerate", "60", 0);
av_dict_set(&options_, "pixel_format", "nv12", 0);
// show remote cursor
av_dict_set(&options_, "capture_cursor", "0", 0);
// Make the grabbed area follow the mouse
// av_dict_set(&options_, "follow_mouse", "centered", 0);
// Video frame size. The default is to capture the full screen
// av_dict_set(&options_, "video_size", "1440x900", 0);
ifmt_ = (AVInputFormat *)av_find_input_format("avfoundation");
if (!ifmt_) {
printf("Couldn't find_input_format\n");
}
// Grab at position 10,20
if (avformat_open_input(&pFormatCtx_, "Capture screen 0", ifmt_, &options_) !=
0) {
printf("Couldn't open input stream.\n");
return -1;
}
if (avformat_find_stream_info(pFormatCtx_, NULL) < 0) {
printf("Couldn't find stream information.\n");
return -1;
}
videoindex_ = -1;
for (i_ = 0; i_ < pFormatCtx_->nb_streams; i_++)
if (pFormatCtx_->streams[i_]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoindex_ = i_;
break;
}
if (videoindex_ == -1) {
printf("Didn't find a video stream.\n");
return -1;
}
pCodecParam_ = pFormatCtx_->streams[videoindex_]->codecpar;
pCodecCtx_ = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(pCodecCtx_, pCodecParam_);
pCodec_ = const_cast<AVCodec *>(avcodec_find_decoder(pCodecCtx_->codec_id));
if (pCodec_ == NULL) {
printf("Codec not found.\n");
return -1;
}
if (avcodec_open2(pCodecCtx_, pCodec_, NULL) < 0) {
printf("Could not open codec.\n");
return -1;
}
const int screen_w = pFormatCtx_->streams[videoindex_]->codecpar->width;
const int screen_h = pFormatCtx_->streams[videoindex_]->codecpar->height;
pFrame_ = av_frame_alloc();
pFrame_->width = screen_w;
pFrame_->height = screen_h;
#if USE_SCALE_FACTOR
pFrame_resized_ = av_frame_alloc();
pFrame_resized_->width = CGDisplayPixelsWide(CGMainDisplayID());
pFrame_resized_->height = CGDisplayPixelsHigh(CGMainDisplayID());
img_convert_ctx_ =
sws_getContext(pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt,
pFrame_resized_->width, pFrame_resized_->height,
AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
#endif
if (!nv12_frame_) {
nv12_frame_ = new unsigned char[screen_w * screen_h * 3 / 2];
}
packet_ = (AVPacket *)av_malloc(sizeof(AVPacket));
inited_ = true;
return 0;
}
int ScreenCapturerAvf::Destroy() {
running_ = false;
return 0;
}
int ScreenCapturerAvf::Start() {
if (running_) {
return 0;
}
running_ = true;
capture_thread_ = std::thread([this]() {
while (running_) {
if (av_read_frame(pFormatCtx_, packet_) >= 0) {
if (packet_->stream_index == videoindex_) {
avcodec_send_packet(pCodecCtx_, packet_);
av_packet_unref(packet_);
got_picture_ = avcodec_receive_frame(pCodecCtx_, pFrame_);
if (!got_picture_) {
#if USE_SCALE_FACTOR
av_image_fill_arrays(pFrame_resized_->data,
pFrame_resized_->linesize, nv12_frame_,
AV_PIX_FMT_NV12, pFrame_resized_->width,
pFrame_resized_->height, 1);
sws_scale(img_convert_ctx_, pFrame_->data, pFrame_->linesize, 0,
pFrame_->height, pFrame_resized_->data,
pFrame_resized_->linesize);
_on_data((unsigned char *)nv12_frame_,
pFrame_resized_->width * pFrame_resized_->height * 3 / 2,
pFrame_resized_->width, pFrame_resized_->height);
#else
memcpy(nv12_frame_, pFrame_->data[0],
pFrame_->linesize[0] * pFrame_->height);
memcpy(nv12_frame_ + pFrame_->linesize[0] * pFrame_->height,
pFrame_->data[1],
pFrame_->linesize[1] * pFrame_->height / 2);
_on_data((unsigned char *)nv12_frame_,
pFrame_->width * pFrame_->height * 3 / 2, pFrame_->width,
pFrame_->height);
#endif
}
}
}
}
});
return 0;
}
int ScreenCapturerAvf::Stop() {
running_ = false;
return 0;
}
int ScreenCapturerAvf::Pause() { return 0; }
int ScreenCapturerAvf::Resume() { return 0; }
void ScreenCapturerAvf::OnFrame() {}
void ScreenCapturerAvf::CleanUp() {}

View File

@@ -32,15 +32,16 @@ class ScreenCapturerAvf : public ScreenCapturer {
~ScreenCapturerAvf();
public:
virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb);
virtual int Init(const int fps, cb_desktop_data cb);
virtual int Destroy();
virtual int Start();
virtual int Stop();
int Pause();
int Resume();
int Stop();
void OnFrame();
@@ -48,7 +49,6 @@ class ScreenCapturerAvf : public ScreenCapturer {
void CleanUp();
private:
std::atomic_bool _running;
std::atomic_bool _paused;
std::atomic_bool _inited;
@@ -56,8 +56,6 @@ class ScreenCapturerAvf : public ScreenCapturer {
std::string _device_name;
RECORD_DESKTOP_RECT _rect;
int _fps;
cb_desktop_data _on_data;
@@ -66,6 +64,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;
@@ -74,12 +74,14 @@ class ScreenCapturerAvf : public ScreenCapturer {
AVDictionary *options_ = nullptr;
AVInputFormat *ifmt_ = nullptr;
AVFrame *pFrame_ = nullptr;
AVFrame *pFrameNv12_ = nullptr;
AVFrame *pFrame_resized_ = nullptr;
AVPacket *packet_ = nullptr;
struct SwsContext *img_convert_ctx_ = nullptr;
unsigned char *nv12_frame_ = nullptr;
// thread
std::unique_ptr<std::thread> capture_thread_ = nullptr;
std::thread capture_thread_;
std::atomic_bool running_;
};
#endif

View File

@@ -0,0 +1,104 @@
#include <IOSurface/IOSurface.h>
#include <utility>
#include "rd_log.h"
#include "screen_capturer_cgd.h"
ScreenCapturerCg::ScreenCapturerCg() {}
ScreenCapturerCg::~ScreenCapturerCg() {}
int ScreenCapturerCg::Init(const int fps, cb_desktop_data cb) {
if (cb) {
_on_data = cb;
}
size_t pixel_width = 1280;
size_t pixel_height = 720;
CGDirectDisplayID display_id = 0;
CGDisplayStreamFrameAvailableHandler handler =
^(CGDisplayStreamFrameStatus status, uint64_t display_time,
IOSurfaceRef frame_surface, CGDisplayStreamUpdateRef updateRef) {
if (status == kCGDisplayStreamFrameStatusStopped) return;
// Only pay attention to frame updates.
if (status != kCGDisplayStreamFrameStatusFrameComplete) return;
// size_t count = 0;
// const CGRect* rects = CGDisplayStreamUpdateGetRects(
// updateRef, kCGDisplayStreamUpdateDirtyRects, &count);
// 获取帧数据
void* frameData = IOSurfaceGetBaseAddressOfPlane(frame_surface, 0);
size_t width = IOSurfaceGetWidthOfPlane(frame_surface, 0);
size_t height = IOSurfaceGetHeightOfPlane(frame_surface, 0);
};
CFDictionaryRef properties_dictionary = CFDictionaryCreate(
kCFAllocatorDefault, (const void*[]){kCGDisplayStreamShowCursor},
(const void*[]){kCFBooleanFalse}, 1, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CGDisplayStreamRef display_stream =
CGDisplayStreamCreate(display_id, pixel_width, pixel_height, 'BGRA',
properties_dictionary, handler);
if (display_stream) {
CGError error = CGDisplayStreamStart(display_stream);
if (error != kCGErrorSuccess) return -1;
CFRunLoopSourceRef source = CGDisplayStreamGetRunLoopSource(display_stream);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
display_streams_.push_back(display_stream);
}
CFRelease(properties_dictionary);
return 0;
}
int ScreenCapturerCg::Destroy() {
running_ = false;
return 0;
}
int ScreenCapturerCg::Start() {
if (_running) {
return 0;
}
running_ = true;
capture_thread_ = std::thread([this]() {
while (running_) {
CFRunLoopRun();
}
});
return 0;
}
int ScreenCapturerCg::Stop() {
running_ = false;
return 0;
}
int ScreenCapturerCg::Pause() { return 0; }
int ScreenCapturerCg::Resume() { return 0; }
void ScreenCapturerCg::OnFrame() {}
void ScreenCapturerCg::CleanUp() {}
//
void ScreenCapturerCg::UnregisterRefreshAndMoveHandlers() {
for (CGDisplayStreamRef stream : display_streams_) {
CFRunLoopSourceRef source = CGDisplayStreamGetRunLoopSource(stream);
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
CGDisplayStreamStop(stream);
CFRelease(stream);
}
display_streams_.clear();
}

View File

@@ -0,0 +1,56 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-10-16
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SCREEN_CAPTURER_CGD_H_
#define _SCREEN_CAPTURER_CGD_H_
#include <CoreGraphics/CoreGraphics.h>
#include <atomic>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include "screen_capturer.h"
class ScreenCapturerCg : public ScreenCapturer {
public:
ScreenCapturerCg();
~ScreenCapturerCg();
public:
virtual int Init(const int fps, cb_desktop_data cb);
virtual int Destroy();
virtual int Start();
virtual int Stop();
int Pause();
int Resume();
void OnFrame();
protected:
void CleanUp();
private:
int _fps;
cb_desktop_data _on_data;
// thread
std::thread capture_thread_;
std::atomic_bool running_;
private:
};
#endif

View File

@@ -1,144 +0,0 @@
#include "screen_capturer_avf.h"
#include <iostream>
#include "log.h"
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
ScreenCapturerAvf::ScreenCapturerAvf() {}
ScreenCapturerAvf::~ScreenCapturerAvf() {}
int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
if (cb) {
_on_data = cb;
}
av_log_set_level(AV_LOG_QUIET);
pFormatCtx_ = avformat_alloc_context();
avdevice_register_all();
// grabbing frame rate
av_dict_set(&options_, "framerate", "60", 0);
av_dict_set(&options_, "pixel_format", "nv12", 0);
// show remote cursor
av_dict_set(&options_, "capture_cursor", "1", 0);
// Make the grabbed area follow the mouse
// av_dict_set(&options_, "follow_mouse", "centered", 0);
// Video frame size. The default is to capture the full screen
// av_dict_set(&options_, "video_size", "1280x720", 0);
ifmt_ = (AVInputFormat *)av_find_input_format("avfoundation");
if (!ifmt_) {
printf("Couldn't find_input_format\n");
}
// Grab at position 10,20
if (avformat_open_input(&pFormatCtx_, "Capture screen 0", ifmt_, &options_) !=
0) {
printf("Couldn't open input stream.\n");
return -1;
}
if (avformat_find_stream_info(pFormatCtx_, NULL) < 0) {
printf("Couldn't find stream information.\n");
return -1;
}
videoindex_ = -1;
for (i_ = 0; i_ < pFormatCtx_->nb_streams; i_++)
if (pFormatCtx_->streams[i_]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoindex_ = i_;
break;
}
if (videoindex_ == -1) {
printf("Didn't find a video stream.\n");
return -1;
}
pCodecParam_ = pFormatCtx_->streams[videoindex_]->codecpar;
pCodecCtx_ = avcodec_alloc_context3(NULL);
avcodec_parameters_to_context(pCodecCtx_, pCodecParam_);
pCodec_ = const_cast<AVCodec *>(avcodec_find_decoder(pCodecCtx_->codec_id));
if (pCodec_ == NULL) {
printf("Codec not found.\n");
return -1;
}
if (avcodec_open2(pCodecCtx_, pCodec_, NULL) < 0) {
printf("Could not open codec.\n");
return -1;
}
const int screen_w = pFormatCtx_->streams[videoindex_]->codecpar->width;
const int screen_h = pFormatCtx_->streams[videoindex_]->codecpar->height;
pFrame_ = av_frame_alloc();
pFrameNv12_ = av_frame_alloc();
pFrame_->width = screen_w;
pFrame_->height = screen_h;
pFrameNv12_->width = 1280;
pFrameNv12_->height = 720;
packet_ = (AVPacket *)av_malloc(sizeof(AVPacket));
img_convert_ctx_ = sws_getContext(
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();
}
return 0;
}
int ScreenCapturerAvf::Start() {
capture_thread_.reset(new std::thread([this]() {
while (1) {
if (av_read_frame(pFormatCtx_, packet_) >= 0) {
if (packet_->stream_index == videoindex_) {
avcodec_send_packet(pCodecCtx_, packet_);
av_packet_unref(packet_);
got_picture_ = avcodec_receive_frame(pCodecCtx_, pFrame_);
if (!got_picture_) {
av_image_fill_arrays(pFrameNv12_->data, pFrameNv12_->linesize,
nv12_buffer_, AV_PIX_FMT_NV12,
pFrameNv12_->width, pFrameNv12_->height, 1);
sws_scale(img_convert_ctx_, pFrame_->data, pFrame_->linesize, 0,
pFrame_->height, pFrameNv12_->data,
pFrameNv12_->linesize);
_on_data((unsigned char *)nv12_buffer_,
pFrameNv12_->width * pFrameNv12_->height * 3 / 2,
pFrameNv12_->width, pFrameNv12_->height);
}
}
}
}
}));
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

@@ -0,0 +1,51 @@
#include "screen_capturer_sck.h"
#include "rd_log.h"
ScreenCapturerSck::ScreenCapturerSck() {}
ScreenCapturerSck::~ScreenCapturerSck() {
// if (inited_ && capture_thread_.joinable()) {
// capture_thread_.join();
// inited_ = false;
// }
}
int ScreenCapturerSck::Init(const int fps, cb_desktop_data cb) {
if (cb) {
on_data_ = cb;
} else {
LOG_ERROR("cb is null");
return -1;
}
screen_capturer_sck_impl_ = CreateScreenCapturerSck();
screen_capturer_sck_impl_->Init(fps, on_data_);
return 0;
}
int ScreenCapturerSck::Destroy() { return 0; }
int ScreenCapturerSck::Start() {
// if (running_) {
// return 0;
// }
// running_ = true;
// capture_thread_ = std::thread([this]() {
// while (running_) {
// }
// });
return 0;
}
int ScreenCapturerSck::Stop() { return 0; }
int ScreenCapturerSck::Pause() { return 0; }
int ScreenCapturerSck::Resume() { return 0; }
void ScreenCapturerSck::OnFrame() {}
void ScreenCapturerSck::CleanUp() {}

View File

@@ -0,0 +1,59 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-10-17
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SCREEN_CAPTURER_SCK_H_
#define _SCREEN_CAPTURER_SCK_H_
#include <atomic>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include "screen_capturer.h"
class ScreenCapturerSck : public ScreenCapturer {
public:
ScreenCapturerSck();
~ScreenCapturerSck();
public:
virtual int Init(const int fps, cb_desktop_data cb);
virtual int Destroy();
virtual int Start();
virtual int Stop();
int Pause();
int Resume();
void OnFrame();
protected:
void CleanUp();
private:
std::unique_ptr<ScreenCapturer> CreateScreenCapturerSck();
private:
int _fps;
cb_desktop_data on_data_;
unsigned char* nv12_frame_ = nullptr;
bool inited_ = false;
// thread
std::thread capture_thread_;
std::atomic_bool running_;
private:
std::unique_ptr<ScreenCapturer> screen_capturer_sck_impl_;
};
#endif

View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2024 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "screen_capturer_sck.h"
#include "rd_log.h"
#include <CoreGraphics/CoreGraphics.h>
#include <IOSurface/IOSurface.h>
#include <ScreenCaptureKit/ScreenCaptureKit.h>
#include <atomic>
#include <mutex>
class ScreenCapturerSckImpl;
// The ScreenCaptureKit API was available in macOS 12.3, but full-screen capture
// was reported to be broken before macOS 13 - see http://crbug.com/40234870.
// Also, the `SCContentFilter` fields `contentRect` and `pointPixelScale` were
// introduced in macOS 14.
API_AVAILABLE(macos(14.0))
@interface SckHelper : NSObject <SCStreamDelegate, SCStreamOutput>
- (instancetype)initWithCapturer:(ScreenCapturerSckImpl *)capturer;
- (void)onShareableContentCreated:(SCShareableContent *)content;
// Called just before the capturer is destroyed. This avoids a dangling pointer,
// and prevents any new calls into a deleted capturer. If any method-call on the
// capturer is currently running on a different thread, this blocks until it
// completes.
- (void)releaseCapturer;
@end
class API_AVAILABLE(macos(14.0)) ScreenCapturerSckImpl : public ScreenCapturer {
public:
explicit ScreenCapturerSckImpl();
ScreenCapturerSckImpl(const ScreenCapturerSckImpl &) = delete;
ScreenCapturerSckImpl &operator=(const ScreenCapturerSckImpl &) = delete;
~ScreenCapturerSckImpl();
public:
int Init(const int fps, cb_desktop_data cb);
void OnReceiveContent(SCShareableContent *content);
void OnNewIOSurface(IOSurfaceRef io_surface, CFDictionaryRef attachment);
virtual int Destroy() { return 0; }
virtual int Start() { return 0; }
virtual int Stop() { return 0; }
private:
SckHelper *__strong helper_;
SCStream *__strong stream_;
cb_desktop_data _on_data;
unsigned char *nv12_frame_ = nullptr;
bool permanent_error_ = false;
CGDirectDisplayID current_display_ = -1;
std::mutex mtx_;
};
@implementation SckHelper {
// This lock is to prevent the capturer being destroyed while an instance
// method is still running on another thread.
std::mutex helper_mtx_;
ScreenCapturerSckImpl *_capturer;
}
- (instancetype)initWithCapturer:(ScreenCapturerSckImpl *)capturer {
self = [super init];
if (self) {
_capturer = capturer;
}
return self;
}
- (void)onShareableContentCreated:(SCShareableContent *)content {
std::lock_guard<std::mutex> lock(helper_mtx_);
if (_capturer) {
_capturer->OnReceiveContent(content);
} else {
LOG_ERROR("Invalid capturer");
}
}
- (void)stream:(SCStream *)stream
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
ofType:(SCStreamOutputType)type {
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
if (!pixelBuffer) {
return;
}
IOSurfaceRef ioSurface = CVPixelBufferGetIOSurface(pixelBuffer);
if (!ioSurface) {
return;
}
CFArrayRef attachmentsArray = CMSampleBufferGetSampleAttachmentsArray(
sampleBuffer, /*createIfNecessary=*/false);
if (!attachmentsArray || CFArrayGetCount(attachmentsArray) <= 0) {
LOG_ERROR("Discarding frame with no attachments");
return;
}
CFDictionaryRef attachment =
static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(attachmentsArray, 0));
std::lock_guard<std::mutex> lock(helper_mtx_);
if (_capturer) {
_capturer->OnNewIOSurface(ioSurface, attachment);
}
}
- (void)releaseCapturer {
std::lock_guard<std::mutex> lock(helper_mtx_);
_capturer = nullptr;
}
@end
ScreenCapturerSckImpl::ScreenCapturerSckImpl() {
helper_ = [[SckHelper alloc] initWithCapturer:this];
}
ScreenCapturerSckImpl::~ScreenCapturerSckImpl() {
[stream_ stopCaptureWithCompletionHandler:nil];
[helper_ releaseCapturer];
}
int ScreenCapturerSckImpl::Init(const int fps, cb_desktop_data cb) {
_on_data = cb;
SckHelper *local_helper = helper_;
auto handler = ^(SCShareableContent *content, NSError *error) {
[local_helper onShareableContentCreated:content];
};
[SCShareableContent getShareableContentWithCompletionHandler:handler];
return 0;
}
void ScreenCapturerSckImpl::OnReceiveContent(SCShareableContent *content) {
if (!content) {
LOG_ERROR("getShareableContent failed");
permanent_error_ = true;
return;
}
if (!content.displays.count) {
LOG_ERROR("getShareableContent returned no displays");
permanent_error_ = true;
return;
}
SCDisplay *captured_display;
{
std::lock_guard<std::mutex> lock(mtx_);
for (SCDisplay *display in content.displays) {
if (current_display_ == display.displayID) {
captured_display = display;
break;
}
}
if (!captured_display) {
if (-1 == current_display_) {
LOG_ERROR("Full screen capture is not supported, falling back to first "
"display");
} else {
LOG_ERROR("Display [{}] not found, falling back to first display",
current_display_);
}
captured_display = content.displays.firstObject;
}
}
SCContentFilter *filter =
[[SCContentFilter alloc] initWithDisplay:captured_display
excludingWindows:@[]];
SCStreamConfiguration *config = [[SCStreamConfiguration alloc] init];
config.pixelFormat = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
config.showsCursor = false;
config.width = filter.contentRect.size.width * filter.pointPixelScale;
config.height = filter.contentRect.size.height * filter.pointPixelScale;
config.captureResolution = SCCaptureResolutionNominal;
std::lock_guard<std::mutex> lock(mtx_);
if (stream_) {
LOG_INFO("Updating stream configuration");
[stream_ updateContentFilter:filter completionHandler:nil];
[stream_ updateConfiguration:config completionHandler:nil];
} else {
stream_ = [[SCStream alloc] initWithFilter:filter
configuration:config
delegate:helper_];
// TODO: crbug.com/327458809 - Choose an appropriate sampleHandlerQueue for
// best performance.
NSError *add_stream_output_error;
bool add_stream_output_result =
[stream_ addStreamOutput:helper_
type:SCStreamOutputTypeScreen
sampleHandlerQueue:nil
error:&add_stream_output_error];
if (!add_stream_output_result) {
stream_ = nil;
LOG_ERROR("addStreamOutput failed");
permanent_error_ = true;
return;
}
auto handler = ^(NSError *error) {
if (error) {
// It should be safe to access `this` here, because the C++ destructor
// calls stopCaptureWithCompletionHandler on the stream, which cancels
// this handler.
permanent_error_ = true;
LOG_ERROR("startCaptureWithCompletionHandler failed");
} else {
LOG_INFO("Capture started");
}
};
[stream_ startCaptureWithCompletionHandler:handler];
}
}
void ScreenCapturerSckImpl::OnNewIOSurface(IOSurfaceRef io_surface,
CFDictionaryRef attachment) {
size_t width = IOSurfaceGetWidth(io_surface);
size_t height = IOSurfaceGetHeight(io_surface);
uint32_t aseed;
IOSurfaceLock(io_surface, kIOSurfaceLockReadOnly, &aseed);
nv12_frame_ =
static_cast<unsigned char *>(IOSurfaceGetBaseAddress(io_surface));
_on_data(nv12_frame_, width * height * 3 / 2, width, height);
IOSurfaceUnlock(io_surface, kIOSurfaceLockReadOnly, &aseed);
}
std::unique_ptr<ScreenCapturer> ScreenCapturerSck::CreateScreenCapturerSck() {
return std::make_unique<ScreenCapturerSckImpl>();
}

View File

@@ -11,23 +11,18 @@
class ScreenCapturer {
public:
typedef struct {
int left;
int top;
int right;
int bottom;
} RECORD_DESKTOP_RECT;
typedef std::function<void(unsigned char *, int, int, int)> cb_desktop_data;
public:
virtual ~ScreenCapturer() {}
public:
virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) = 0;
virtual int Init(const int fps, cb_desktop_data cb) = 0;
virtual int Destroy() = 0;
virtual int Start() = 0;
virtual int Stop() = 0;
};
#endif

View File

@@ -8,12 +8,12 @@
#define _SCREEN_CAPTURER_FACTORY_H_
#ifdef _WIN32
#include "screen_capturer_wgc.h"
#elif __linux__
#include "screen_capturer_x11.h"
#elif __APPLE__
#include "screen_capturer_avf.h"
// #include "screen_capturer_avf.h"
#include "screen_capturer_sck.h"
#endif
class ScreenCapturerFactory {
@@ -27,7 +27,8 @@ class ScreenCapturerFactory {
#elif __linux__
return new ScreenCapturerX11();
#elif __APPLE__
return new ScreenCapturerAvf();
// return new ScreenCapturerAvf();
return new ScreenCapturerSck();
#else
return nullptr;
#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 {
@@ -76,12 +62,12 @@ bool ScreenCapturerWgc::IsWgcSupported() {
}
}
int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb) {
int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
int error = 0;
if (_inited == true) return error;
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 4];
// 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 +100,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 +139,53 @@ 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) {
int abgr_index = (i * abgr_stride + j) * 4;
int bgra_index = (i * bgra_stride + j) * 4;
bgra_data[bgra_index + 0] = abgr_data[abgr_index + 2]; // 蓝色
bgra_data[bgra_index + 1] = abgr_data[abgr_index + 1]; // 绿色
bgra_data[bgra_index + 2] = abgr_data[abgr_index + 0]; // 红色
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) {
int bgra_index = (i * bgra_stride + j) * 4;
int abgr_index = (i * abgr_stride + j) * 4;
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;
if (!nv12_frame_) {
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
}
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);
_on_data(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
frame.height);
}
}
void ScreenCapturerWgc::CleanUp() {

View File

@@ -19,15 +19,14 @@ class ScreenCapturerWgc : public ScreenCapturer,
public:
bool IsWgcSupported();
virtual int Init(const RECORD_DESKTOP_RECT &rect, const int fps,
cb_desktop_data cb);
virtual int Init(const int fps, cb_desktop_data cb);
virtual int Destroy();
virtual int Start();
int Pause();
int Resume();
int Stop();
virtual int Stop();
void OnFrame(const WgcSession::wgc_session_frame &frame);
@@ -45,13 +44,12 @@ class ScreenCapturerWgc : public ScreenCapturer,
std::string _device_name;
RECORD_DESKTOP_RECT _rect;
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

@@ -89,6 +89,8 @@ int WgcSessionImpl::Start() {
capture_session_.StartCapture();
capture_session_.IsCursorCaptureEnabled(false);
error = 0;
} catch (winrt::hresult_error) {
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
#include "IconsFontAwesome6.h"
#include "layout_style.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::AboutWindow() {
if (show_about_window_) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2(
(viewport->WorkSize.x - viewport->WorkPos.x - about_window_width_) / 2,
(viewport->WorkSize.y - viewport->WorkPos.y - about_window_height_) /
2));
ImGui::SetNextWindowSize(ImVec2(about_window_width_, about_window_height_));
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::Begin(
localization::about[localization_language_index_].c_str(), nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings);
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
std::string version;
#ifdef RD_VERSION
version = RD_VERSION;
#else
version = "Unknown";
#endif
std::string text =
localization::version[localization_language_index_] + ": " + version;
ImGui::Text("%s", text.c_str());
ImGui::SetCursorPosX(about_window_width_ * 0.42f);
ImGui::SetCursorPosY(about_window_height_ * 0.75f);
// OK
if (ImGui::Button(localization::ok[localization_language_index_].c_str())) {
show_about_window_ = false;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::End();
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleVar(3);
ImGui::PopStyleColor();
}
return 0;
}

View File

@@ -0,0 +1,159 @@
#include "layout_style.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::ConnectionStatusWindow() {
if (show_connection_status_window_) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
connection_status_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
connection_status_window_height_) /
2));
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
connection_status_window_height_));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::Begin("ConnectionStatusWindow", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoSavedSettings);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(0.5f);
std::string text;
if (ConnectionStatus::Connecting == connection_status_) {
text = localization::p2p_connecting[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
} else if (ConnectionStatus::Connected == connection_status_) {
text = localization::p2p_connected[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// ok
if (ImGui::Button(
localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
show_connection_status_window_ = false;
}
} else if (ConnectionStatus::Disconnected == connection_status_) {
text = localization::p2p_disconnected[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// ok
if (ImGui::Button(
localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
show_connection_status_window_ = false;
}
} else if (ConnectionStatus::Failed == connection_status_) {
text = localization::p2p_failed[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// ok
if (ImGui::Button(
localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
show_connection_status_window_ = false;
}
} else if (ConnectionStatus::Closed == connection_status_) {
text = localization::p2p_closed[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// ok
if (ImGui::Button(
localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
show_connection_status_window_ = false;
}
} else if (ConnectionStatus::IncorrectPassword == connection_status_) {
if (!password_validating_) {
if (password_validating_time_ == 1) {
text = localization::input_password[localization_language_index_];
} else {
text = localization::reinput_password[localization_language_index_];
}
auto window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.4f);
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
if (focus_on_input_widget_) {
ImGui::SetKeyboardFocusHere();
focus_on_input_widget_ = false;
}
ImGui::InputText("##password", remote_password_,
IM_ARRAYSIZE(remote_password_),
ImGuiInputTextFlags_CharsNoBlank);
ImGui::PopStyleVar();
ImGui::SetCursorPosX(window_width * 0.315f);
ImGui::SetCursorPosY(window_height * 0.75f);
// ok
if (ImGui::Button(
localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
show_connection_status_window_ = true;
password_validating_ = true;
rejoin_ = true;
focus_on_input_widget_ = true;
}
ImGui::SameLine();
// cancel
if (ImGui::Button(
localization::cancel[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
memset(remote_password_, 0, sizeof(remote_password_));
show_connection_status_window_ = false;
focus_on_input_widget_ = true;
}
} else if (password_validating_) {
text = localization::validate_password[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
}
} else if (ConnectionStatus::NoSuchTransmissionId == connection_status_) {
text = localization::no_such_id[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// ok
if (ImGui::Button(
localization::ok[localization_language_index_].c_str()) ||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
show_connection_status_window_ = false;
}
}
auto window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.2f);
ImGui::Text("%s", text.c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::End();
ImGui::PopStyleVar();
}
return 0;
}

View File

@@ -0,0 +1,144 @@
#include "IconsFontAwesome6.h"
#include "layout_style.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::ControlBar() {
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
if (control_bar_expand_) {
ImGui::SetCursorPosX(
is_control_bar_in_left_ ? (control_window_width_ + 5.0f) : 41.0f);
// mouse control button
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (is_control_bar_in_left_) {
draw_list->AddLine(
ImVec2(ImGui::GetCursorScreenPos().x - 5.0f,
ImGui::GetCursorScreenPos().y - 7.0f),
ImVec2(ImGui::GetCursorScreenPos().x - 5.0f,
ImGui::GetCursorScreenPos().y - 7.0f + control_window_height_),
IM_COL32(178, 178, 178, 255), 1.0f);
}
float disable_mouse_x = ImGui::GetCursorScreenPos().x + 4.0f;
float disable_mouse_y = ImGui::GetCursorScreenPos().y + 4.0f;
std::string mouse = mouse_control_button_pressed_ ? ICON_FA_COMPUTER_MOUSE
: ICON_FA_COMPUTER_MOUSE;
if (ImGui::Button(mouse.c_str(), ImVec2(25, 25))) {
if (connection_established_) {
control_mouse_ = !control_mouse_;
mouse_control_button_pressed_ = !mouse_control_button_pressed_;
mouse_control_button_label_ =
mouse_control_button_pressed_
? localization::release_mouse[localization_language_index_]
: localization::control_mouse[localization_language_index_];
}
}
if (!mouse_control_button_pressed_) {
draw_list->AddLine(
ImVec2(disable_mouse_x, disable_mouse_y),
ImVec2(disable_mouse_x + 16.0f, disable_mouse_y + 14.2f),
IM_COL32(0, 0, 0, 255), 2.0f);
draw_list->AddLine(
ImVec2(disable_mouse_x - 1.2f, disable_mouse_y + 1.2f),
ImVec2(disable_mouse_x + 15.3f, disable_mouse_y + 15.4f),
ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255)
: IM_COL32(179, 213, 253, 255),
2.0f);
}
ImGui::SameLine();
// audio capture button
float disable_audio_x = ImGui::GetCursorScreenPos().x + 4;
float disable_audio_y = ImGui::GetCursorScreenPos().y + 4.0f;
// std::string audio = audio_capture_button_pressed_ ? ICON_FA_VOLUME_HIGH
// : ICON_FA_VOLUME_XMARK;
std::string audio = audio_capture_button_pressed_ ? ICON_FA_VOLUME_HIGH
: ICON_FA_VOLUME_HIGH;
if (ImGui::Button(audio.c_str(), ImVec2(25, 25))) {
if (connection_established_) {
audio_capture_ = !audio_capture_;
audio_capture_button_pressed_ = !audio_capture_button_pressed_;
audio_capture_button_label_ =
audio_capture_button_pressed_
? localization::audio_capture[localization_language_index_]
: localization::mute[localization_language_index_];
RemoteAction remote_action;
remote_action.type = ControlType::audio_capture;
remote_action.a = audio_capture_button_pressed_;
SendData(peer_, DATA_TYPE::DATA, (const char*)&remote_action,
sizeof(remote_action));
}
}
if (!audio_capture_button_pressed_) {
draw_list->AddLine(
ImVec2(disable_audio_x, disable_audio_y),
ImVec2(disable_audio_x + 16.0f, disable_audio_y + 14.2f),
IM_COL32(0, 0, 0, 255), 2.0f);
draw_list->AddLine(
ImVec2(disable_audio_x - 1.2f, disable_audio_y + 1.2f),
ImVec2(disable_audio_x + 15.3f, disable_audio_y + 15.4f),
ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255)
: IM_COL32(179, 213, 253, 255),
2.0f);
}
ImGui::SameLine();
// fullscreen button
std::string fullscreen =
fullscreen_button_pressed_ ? ICON_FA_COMPRESS : ICON_FA_EXPAND;
if (ImGui::Button(fullscreen.c_str(), ImVec2(25, 25))) {
fullscreen_button_pressed_ = !fullscreen_button_pressed_;
fullscreen_button_label_ =
fullscreen_button_pressed_
? localization::exit_fullscreen[localization_language_index_]
: localization::fullscreen[localization_language_index_];
if (fullscreen_button_pressed_) {
SDL_SetWindowFullscreen(main_window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
} else {
SDL_SetWindowFullscreen(main_window_, SDL_FALSE);
}
}
ImGui::SameLine();
// close button
std::string close_button = ICON_FA_XMARK;
if (ImGui::Button(close_button.c_str(), ImVec2(25, 25))) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
}
ImGui::SameLine();
if (!is_control_bar_in_left_) {
draw_list->AddLine(
ImVec2(ImGui::GetCursorScreenPos().x - 3.0f,
ImGui::GetCursorScreenPos().y - 7.0f),
ImVec2(ImGui::GetCursorScreenPos().x - 3.0f,
ImGui::GetCursorScreenPos().y - 7.0f + control_window_height_),
IM_COL32(178, 178, 178, 255), 1.0f);
}
}
ImGui::SetCursorPosX(
is_control_bar_in_left_ ? (control_window_width_ * 2 - 18.0f) : 3.0f);
std::string control_bar =
control_bar_expand_
? (is_control_bar_in_left_ ? ICON_FA_ANGLE_LEFT : ICON_FA_ANGLE_RIGHT)
: (is_control_bar_in_left_ ? ICON_FA_ANGLE_RIGHT
: ICON_FA_ANGLE_LEFT);
if (ImGui::Button(control_bar.c_str(), ImVec2(15, 25))) {
control_bar_expand_ = !control_bar_expand_;
control_bar_button_pressed_time_ = ImGui::GetTime();
control_window_width_is_changing_ = true;
}
ImGui::PopStyleVar();
return 0;
}

View File

@@ -0,0 +1,150 @@
#include "rd_log.h"
#include "render.h"
int Render::ControlWindow() {
auto time_duration = ImGui::GetTime() - control_bar_button_pressed_time_;
if (control_window_width_is_changing_) {
if (control_bar_expand_) {
control_window_width_ =
control_window_min_width_ +
(control_window_max_width_ - control_window_min_width_) * 4 *
time_duration;
} else {
control_window_width_ =
control_window_max_width_ -
(control_window_max_width_ - control_window_min_width_) * 4 *
time_duration;
}
}
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1, 1, 1, 1));
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0);
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::SetNextWindowSize(
ImVec2(control_window_width_, control_window_height_), ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Once);
if (ImGui::IsMouseReleased(ImGuiPopupFlags_MouseButtonLeft) ||
control_window_width_is_changing_) {
if (control_winodw_pos_.x <= stream_window_width_ / 2) {
int pos_x = 0;
int pos_y =
(control_winodw_pos_.y >=
(fullscreen_button_pressed_ ? 0 : title_bar_height_) &&
control_winodw_pos_.y <=
stream_window_height_ - control_window_height_)
? control_winodw_pos_.y
: (control_winodw_pos_.y <
(fullscreen_button_pressed_ ? 0 : title_bar_height_)
? (fullscreen_button_pressed_ ? 0 : title_bar_height_)
: (stream_window_height_ - control_window_height_));
if (control_bar_expand_) {
if (control_window_width_ >= control_window_max_width_) {
control_window_width_ = control_window_max_width_;
control_window_width_is_changing_ = false;
} else {
control_window_width_is_changing_ = true;
}
} else {
if (control_window_width_ <= control_window_min_width_) {
control_window_width_ = control_window_min_width_;
control_window_width_is_changing_ = false;
} else {
control_window_width_is_changing_ = true;
}
}
ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always);
is_control_bar_in_left_ = true;
} else if (control_winodw_pos_.x > stream_window_width_ / 2) {
int pos_x = 0;
int pos_y =
(control_winodw_pos_.y >=
(fullscreen_button_pressed_ ? 0 : title_bar_height_) &&
control_winodw_pos_.y <=
stream_window_height_ - control_window_height_)
? control_winodw_pos_.y
: (control_winodw_pos_.y <
(fullscreen_button_pressed_ ? 0 : title_bar_height_)
? (fullscreen_button_pressed_ ? 0 : title_bar_height_)
: (stream_window_height_ - control_window_height_));
if (control_bar_expand_) {
if (control_window_width_ >= control_window_max_width_) {
control_window_width_ = control_window_max_width_;
control_window_width_is_changing_ = false;
pos_x = stream_window_width_ - control_window_max_width_;
} else {
control_window_width_is_changing_ = true;
pos_x = stream_window_width_ - control_window_width_;
}
} else {
if (control_window_width_ <= control_window_min_width_) {
control_window_width_ = control_window_min_width_;
control_window_width_is_changing_ = false;
pos_x = stream_window_width_ - control_window_min_width_;
} else {
control_window_width_is_changing_ = true;
pos_x = stream_window_width_ - control_window_width_;
}
}
ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always);
is_control_bar_in_left_ = false;
}
}
ImGui::Begin("ControlWindow", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
control_winodw_pos_ = ImGui::GetWindowPos();
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
static bool a, b, c, d, e;
ImGui::SetNextWindowPos(
ImVec2(is_control_bar_in_left_
? control_winodw_pos_.x - control_window_width_
: control_winodw_pos_.x,
control_winodw_pos_.y),
ImGuiCond_Always);
ImGui::SetWindowFontScale(0.5f);
ImGui::BeginChild("ControlBar",
ImVec2(control_window_width_ * 2, control_window_height_),
ImGuiChildFlags_Border, ImGuiWindowFlags_NoDecoration);
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor();
ControlBar();
control_bar_hovered_ = ImGui::IsWindowHovered();
ImGui::EndChild();
ImGui::End();
ImGui::PopStyleVar(4);
ImGui::PopStyleColor();
ImGui::SetNextWindowPos(
ImVec2(0, fullscreen_button_pressed_ ? 0 : title_bar_height_),
ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(stream_window_width_,
stream_window_height_ -
(fullscreen_button_pressed_ ? 0 : title_bar_height_)),
ImGuiCond_Always);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_Border,
ImVec4(178 / 255.0f, 178 / 255.0f, 178 / 255.0f,
fullscreen_button_pressed_ ? 0 : 1.0f));
ImGui::Begin("VideoBg", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor(2);
ImGui::End();
return 0;
}

View File

@@ -0,0 +1,39 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-06-14
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LAYOUT_STYLE_H_
#define _LAYOUT_STYLE_H_
#define MENU_WINDOW_WIDTH_CN 300
#define MENU_WINDOW_HEIGHT_CN 280
#define LOCAL_WINDOW_WIDTH_CN 300
#define LOCAL_WINDOW_HEIGHT_CN 280
#define REMOTE_WINDOW_WIDTH_CN 300
#define REMOTE_WINDOW_HEIGHT_CN 280
#define MENU_WINDOW_WIDTH_EN 190
#define MENU_WINDOW_HEIGHT_EN 245
#define IPUT_WINDOW_WIDTH 160
#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 220
#define SETTINGS_WINDOW_HEIGHT_EN 220
#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 151
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 198
#define ENABLE_TURN_CHECKBOX_PADDING_CN 151
#define ENABLE_TURN_CHECKBOX_PADDING_EN 198
#define SETTINGS_SELECT_WINDOW_WIDTH 73
#define SETTINGS_OK_BUTTON_PADDING_CN 55
#define SETTINGS_OK_BUTTON_PADDING_EN 78
#endif

View File

@@ -0,0 +1,311 @@
#include <random>
#include "IconsFontAwesome6.h"
#include "layout_style.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::LocalWindow() {
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_), ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::BeginChild(
"LocalDesktopWindow",
ImVec2(local_window_width_, main_window_height_default_ -
title_bar_height_ - status_bar_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
ImGui::Text(
"%s", localization::local_desktop[localization_language_index_].c_str());
ImGui::Spacing();
{
ImGui::PushStyleColor(ImGuiCol_ChildBg,
ImVec4(239.0 / 255, 240.0 / 255, 242.0 / 255, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::BeginChild("LocalDesktopWindow_1", ImVec2(330, 180),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
{
ImGui::SetWindowFontScale(0.5f);
ImGui::Text("%s",
localization::local_id[localization_language_index_].c_str());
ImGui::Spacing();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
ImGui::SetWindowFontScale(1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
char client_id_display[12] = "";
for (int i = 0, j = 0; i < sizeof(client_id_); i++, j++) {
client_id_display[j] = client_id_[i];
if (i == 2 || i == 5) {
client_id_display[++j] = ' ';
}
}
ImGui::InputText(
"##local_id", client_id_display, IM_ARRAYSIZE(client_id_display),
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleVar();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(ICON_FA_COPY, ImVec2(35, 38))) {
local_id_copied_ = true;
ImGui::SetClipboardText(client_id_);
copy_start_time_ = ImGui::GetTime();
}
ImGui::PopStyleColor(3);
auto time_duration = ImGui::GetTime() - copy_start_time_;
if (local_id_copied_ && time_duration < 1.0f) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
notification_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
notification_window_height_) /
2));
ImGui::SetNextWindowSize(
ImVec2(notification_window_width_, notification_window_height_));
ImGui::PushStyleColor(ImGuiCol_WindowBg,
ImVec4(1.0, 1.0, 1.0, 1.0 - time_duration));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::Begin("ConnectionStatusWindow", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
auto window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
ImGui::SetWindowFontScale(0.8f);
std::string text = localization::local_id_copied_to_clipboard
[localization_language_index_];
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.5f);
ImGui::PushStyleColor(ImGuiCol_Text,
ImVec4(0, 0, 0, 1.0 - time_duration));
ImGui::Text("%s", text.c_str());
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
ImGui::End();
}
ImGui::SetWindowFontScale(1.0f);
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
ImGui::SetWindowFontScale(0.5f);
ImGui::Text("%s",
localization::password[localization_language_index_].c_str());
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
ImGui::Spacing();
if (!password_inited_) {
char a[] = {
"123456789QWERTYUPASDFGHJKLZXCVBNMqwertyupasdfghijkzxcvbnm"};
std::mt19937 generator(
std::chrono::system_clock::now().time_since_epoch().count());
std::uniform_int_distribution<int> distribution(0, strlen(a) - 1);
random_password_.clear();
for (int i = 0, len = strlen(a); i < 6; i++) {
random_password_ += a[distribution(generator)];
}
password_inited_ = true;
if (0 != strcmp(random_password_.c_str(), password_saved_)) {
strncpy(password_saved_, random_password_.c_str(),
sizeof(password_saved_));
LOG_INFO("Generate new password and save into cache file");
SaveSettingsIntoCacheFile();
}
}
ImGui::SetWindowFontScale(1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
ImGui::InputTextWithHint(
"##server_pwd",
localization::max_password_len[localization_language_index_].c_str(),
password_saved_, IM_ARRAYSIZE(password_saved_),
show_password_
? ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_ReadOnly
: ImGuiInputTextFlags_CharsNoBlank |
ImGuiInputTextFlags_Password |
ImGuiInputTextFlags_ReadOnly);
ImGui::PopStyleVar();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
ImGui::SetWindowFontScale(0.5f);
auto l_x = ImGui::GetCursorScreenPos().x;
auto l_y = ImGui::GetCursorScreenPos().y;
if (ImGui::Button(ICON_FA_EYE, ImVec2(22, 38))) {
show_password_ = !show_password_;
}
if (!show_password_) {
ImDrawList *draw_list = ImGui::GetWindowDrawList();
draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 12.5f),
ImVec2(l_x + 20.3f, l_y + 26.5f),
IM_COL32(239, 240, 242, 255), 2.0f);
draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 11.0f),
ImVec2(l_x + 20.3f, l_y + 25.0f),
IM_COL32(0, 0, 0, 255), 1.5f);
}
ImGui::SameLine();
if (ImGui::Button(
regenerate_password_ ? ICON_FA_SPINNER : ICON_FA_ARROWS_ROTATE,
ImVec2(22, 38))) {
regenerate_password_ = true;
password_inited_ = false;
regenerate_password_start_time_ = ImGui::GetTime();
LeaveConnection(peer_, client_id_);
is_create_connection_ = false;
}
if (ImGui::GetTime() - regenerate_password_start_time_ > 0.3f) {
regenerate_password_ = false;
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_PEN, ImVec2(22, 38))) {
show_reset_password_window_ = true;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor(3);
if (show_reset_password_window_) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
connection_status_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
connection_status_window_height_) /
2));
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
connection_status_window_height_));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::Begin("ResetPasswordWindow", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoSavedSettings);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
auto window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
std::string text =
localization::new_password[localization_language_index_];
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
ImGui::SetWindowFontScale(0.5f);
ImGui::SetCursorPosX((window_width - text_width / 2) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.2f);
ImGui::Text("%s", text.c_str());
ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.4f);
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
if (focus_on_input_widget_) {
ImGui::SetKeyboardFocusHere();
focus_on_input_widget_ = false;
}
bool enter_pressed = ImGui::InputText(
"##new_password", new_password_, IM_ARRAYSIZE(new_password_),
ImGuiInputTextFlags_CharsNoBlank |
ImGuiInputTextFlags_EnterReturnsTrue);
ImGui::PopStyleVar();
ImGui::SetCursorPosX(window_width * 0.315f);
ImGui::SetCursorPosY(window_height * 0.75f);
// OK
if (ImGui::Button(
localization::ok[localization_language_index_].c_str()) ||
enter_pressed) {
if (6 != strlen(new_password_)) {
LOG_ERROR("Invalid password length");
show_reset_password_window_ = true;
focus_on_input_widget_ = true;
} else {
show_reset_password_window_ = false;
LOG_INFO("Generate new password and save into cache file");
strncpy(password_saved_, new_password_, sizeof(password_saved_));
memset(new_password_, 0, sizeof(new_password_));
SaveSettingsIntoCacheFile();
LeaveConnection(peer_, client_id_);
is_create_connection_ = false;
focus_on_input_widget_ = true;
}
}
ImGui::SameLine();
if (ImGui::Button(
localization::cancel[localization_language_index_].c_str())) {
show_reset_password_window_ = false;
focus_on_input_widget_ = true;
memset(new_password_, 0, sizeof(new_password_));
}
ImGui::SetWindowFontScale(1.0f);
ImGui::End();
ImGui::PopStyleVar();
}
}
ImGui::EndChild();
}
ImGui::EndChild();
ImGui::PopStyleVar();
return 0;
}

View File

@@ -0,0 +1,15 @@
#include "render.h"
int Render::MainWindow() {
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(main_window_width_default_, main_window_height_default_),
ImGuiCond_Always);
LocalWindow();
RemoteWindow();
StatusBar();
ConnectionStatusWindow();
return 0;
}

View File

@@ -0,0 +1,110 @@
#include "IconsFontAwesome6.h"
#include "layout_style.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
static int InputTextCallback(ImGuiInputTextCallbackData *data) {
if (data->BufTextLen > 3 && data->Buf[3] != ' ') {
data->InsertChars(3, " ");
}
if (data->BufTextLen > 7 && data->Buf[7] != ' ') {
data->InsertChars(7, " ");
}
return 0;
}
int Render::RemoteWindow() {
ImGui::SetNextWindowPos(ImVec2(local_window_width_ - 1, title_bar_height_),
ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::BeginChild("RemoteDesktopWindow",
ImVec2(main_window_width_ - local_window_width_ + 1,
main_window_height_default_ - title_bar_height_ -
status_bar_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
ImGui::Text(
"%s", localization::remote_desktop[localization_language_index_].c_str());
ImGui::Spacing();
ImGui::PushStyleColor(ImGuiCol_ChildBg,
ImVec4(239.0 / 255, 240.0 / 255, 242.0 / 255, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::BeginChild("RemoteDesktopWindow_1", ImVec2(330, 180),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
{
ImGui::SetWindowFontScale(0.5f);
ImGui::Text("%s",
localization::remote_id[localization_language_index_].c_str());
ImGui::Spacing();
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
ImGui::SetWindowFontScale(1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
bool enter_pressed = ImGui::InputText(
"##remote_id_", remote_id_display_, IM_ARRAYSIZE(remote_id_display_),
ImGuiInputTextFlags_CharsUppercase |
ImGuiInputTextFlags_EnterReturnsTrue |
ImGuiInputTextFlags_CallbackEdit | ImGuiInputTextFlags_CharsNoBlank,
InputTextCallback);
ImGui::PopStyleVar();
ImGui::SameLine();
if (ImGui::Button(ICON_FA_ARROW_RIGHT_LONG, ImVec2(55, 38)) ||
enter_pressed || rejoin_) {
connect_button_pressed_ = true;
connection_status_ = ConnectionStatus::Connecting;
int ret = -1;
if (signal_connected_) {
if (!connection_established_) {
remote_id_ = remote_id_display_;
remote_id_.erase(remove_if(remote_id_.begin(), remote_id_.end(),
static_cast<int (*)(int)>(&isspace)),
remote_id_.end());
if (0 == strcmp(remote_id_.c_str(), client_id_) && !peer_reserved_) {
peer_reserved_ = CreatePeer(&params_);
if (peer_reserved_) {
LOG_INFO("Create peer[reserved] instance successful");
std::string client_id = "C-";
client_id += client_id_;
Init(peer_reserved_, client_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_.c_str(), remote_password_);
if (0 == ret) {
is_client_mode_ = true;
rejoin_ = false;
} else {
rejoin_ = true;
}
}
}
}
}
ImGui::EndChild();
ImGui::EndChild();
ImGui::PopStyleVar();
return 0;
}

View File

@@ -0,0 +1,982 @@
#include "render.h"
#include <fstream>
#include <iostream>
#include <string>
#include "IconsFontAwesome6.h"
#include "OPPOSans_Regular.h"
#include "device_controller_factory.h"
#include "fa_regular_400.h"
#include "fa_solid_900.h"
#include "layout_style.h"
#include "localization.h"
#include "platform.h"
#include "rd_log.h"
#include "screen_capturer_factory.h"
// Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
#define MOUSE_GRAB_PADDING 5
SDL_HitTestResult Render::HitTestCallback(SDL_Window *window,
const SDL_Point *area, void *data) {
Render *render = (Render *)data;
if (!render) {
return SDL_HITTEST_NORMAL;
}
if (render->fullscreen_button_pressed_) {
return SDL_HITTEST_NORMAL;
}
int window_width, window_height;
SDL_GetWindowSize(window, &window_width, &window_height);
if (area->y < 30 && area->y > MOUSE_GRAB_PADDING &&
area->x < window_width - 120 && area->x > MOUSE_GRAB_PADDING) {
return SDL_HITTEST_DRAGGABLE;
}
if (!render->streaming_) {
return SDL_HITTEST_NORMAL;
}
if (area->y < MOUSE_GRAB_PADDING) {
if (area->x < MOUSE_GRAB_PADDING) {
return SDL_HITTEST_RESIZE_TOPLEFT;
} else if (area->x > window_width - MOUSE_GRAB_PADDING) {
return SDL_HITTEST_RESIZE_TOPRIGHT;
} else {
return SDL_HITTEST_RESIZE_TOP;
}
} else if (area->y > window_height - MOUSE_GRAB_PADDING) {
if (area->x < MOUSE_GRAB_PADDING) {
return SDL_HITTEST_RESIZE_BOTTOMLEFT;
} else if (area->x > window_width - MOUSE_GRAB_PADDING) {
return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
} else {
return SDL_HITTEST_RESIZE_BOTTOM;
}
} else if (area->x < MOUSE_GRAB_PADDING) {
return SDL_HITTEST_RESIZE_LEFT;
} else if (area->x > window_width - MOUSE_GRAB_PADDING) {
return SDL_HITTEST_RESIZE_RIGHT;
}
return SDL_HITTEST_NORMAL;
}
Render::Render() {}
Render::~Render() {}
int Render::SaveSettingsIntoCacheFile() {
std::lock_guard<std::mutex> lock(cd_cache_mutex_);
cd_cache_file_ = fopen("cache.cd", "w+");
if (!cd_cache_file_) {
return -1;
}
fseek(cd_cache_file_, 0, SEEK_SET);
memset(&cd_cache_.client_id, 0, sizeof(cd_cache_.client_id));
strncpy(cd_cache_.client_id, client_id_, sizeof(client_id_));
memset(&cd_cache_.password, 0, sizeof(cd_cache_.password));
strncpy(cd_cache_.password, password_saved_, sizeof(password_saved_));
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_);
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
config_center_.SetVideoQuality(
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
config_center_.SetVideoEncodeFormat(
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
LOG_INFO("Save settings into cache file success");
return 0;
}
int Render::LoadSettingsFromCacheFile() {
std::lock_guard<std::mutex> lock(cd_cache_mutex_);
cd_cache_file_ = fopen("cache.cd", "r+");
if (!cd_cache_file_) {
LOG_INFO("Init cache file by using default settings");
memset(password_saved_, 0, sizeof(password_saved_));
language_button_value_ = 0;
video_quality_button_value_ = 0;
video_encode_format_button_value_ = 1;
enable_hardware_video_codec_ = false;
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
config_center_.SetVideoQuality(
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
config_center_.SetVideoEncodeFormat(
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
return -1;
}
fseek(cd_cache_file_, 0, SEEK_SET);
fread(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
fclose(cd_cache_file_);
memset(&client_id_, 0, sizeof(client_id_));
strncpy(client_id_, cd_cache_.client_id, sizeof(client_id_));
strncpy(password_saved_, cd_cache_.password, sizeof(password_saved_));
if (0 != strcmp(password_saved_, "") && 7 == sizeof(password_saved_)) {
password_inited_ = true;
}
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;
language_button_value_last_ = language_button_value_;
video_quality_button_value_last_ = video_quality_button_value_;
video_encode_format_button_value_last_ = video_encode_format_button_value_;
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
config_center_.SetLanguage((ConfigCenter::LANGUAGE)language_button_value_);
config_center_.SetVideoQuality(
(ConfigCenter::VIDEO_QUALITY)video_quality_button_value_);
config_center_.SetVideoEncodeFormat(
(ConfigCenter::VIDEO_ENCODE_FORMAT)video_encode_format_button_value_);
config_center_.SetHardwareVideoCodec(enable_hardware_video_codec_);
LOG_INFO("Load settings from cache file");
return 0;
}
int Render::StartScreenCapture() {
screen_capturer_ = (ScreenCapturer *)screen_capturer_factory_->Create();
last_frame_time_ = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
int screen_capturer_init_ret = screen_capturer_->Init(
60, [this](unsigned char *data, int size, int width, int height) -> void {
auto now_time = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count();
auto duration = now_time - last_frame_time_;
if (duration >= 0 && connection_established_) {
// SendData(peer_, DATA_TYPE::VIDEO, (const char *)data,
// NV12_BUFFER_SIZE);
XVideoFrame frame;
frame.data = (const char *)data;
frame.size = size;
frame.width = width;
frame.height = height;
SendVideoFrame(peer_, &frame);
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 Render::StopScreenCapture() {
if (screen_capturer_) {
LOG_INFO("Stop screen capturer")
screen_capturer_->Stop();
screen_capturer_->Destroy();
delete screen_capturer_;
screen_capturer_ = nullptr;
}
return 0;
}
int Render::StartSpeakerCapture() {
if (!speaker_capturer_) {
speaker_capturer_ = (SpeakerCapturer *)speaker_capturer_factory_->Create();
int speaker_capturer_init_ret = speaker_capturer_->Init(
[this](unsigned char *data, size_t size) -> void {
if (connection_established_) {
SendData(peer_, DATA_TYPE::AUDIO, (const char *)data, size);
}
});
if (0 != speaker_capturer_init_ret) {
speaker_capturer_->Destroy();
delete speaker_capturer_;
speaker_capturer_ = nullptr;
}
}
if (speaker_capturer_) {
speaker_capturer_->Start();
}
return 0;
}
int Render::StopSpeakerCapture() {
if (speaker_capturer_) {
speaker_capturer_->Stop();
}
return 0;
}
int Render::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 Render::StopMouseControl() {
if (mouse_controller_) {
mouse_controller_->Destroy();
delete mouse_controller_;
mouse_controller_ = nullptr;
}
return 0;
}
int Render::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_.enable_turn = config_center_.IsEnableTurn();
params_.on_receive_video_buffer = nullptr;
params_.on_receive_audio_buffer = OnReceiveAudioBufferCb;
params_.on_receive_data_buffer = OnReceiveDataBufferCb;
params_.on_receive_video_frame = OnReceiveVideoBufferCb;
params_.on_signal_status = OnSignalStatusCb;
params_.on_connection_status = OnConnectionStatusCb;
params_.net_status_report = NetStatusReport;
params_.user_data = this;
peer_ = CreatePeer(&params_);
if (peer_) {
LOG_INFO("[{}] Create peer instance successful", client_id_);
Init(peer_, client_id_);
LOG_INFO("[{}] Peer init finish", client_id_);
} else {
LOG_INFO("Create peer instance failed");
}
return 0;
}
int Render::AudioDeviceInit() {
// 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;
want_in.userdata = this;
// 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 = nullptr;
want_out.userdata = this;
output_dev_ = SDL_OpenAudioDevice(nullptr, 0, &want_out, NULL, 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);
return 0;
}
int Render::AudioDeviceDestroy() {
SDL_CloseAudioDevice(output_dev_);
// SDL_CloseAudioDevice(input_dev_);
return 0;
}
int Render::CreateRtcConnection() {
// create connection
if (SignalStatus::SignalConnected == signal_status_ &&
!is_create_connection_ && password_inited_) {
LOG_INFO("Connected with signal server, create p2p connection");
is_create_connection_ =
CreateConnection(peer_, client_id_, password_saved_) ? false : true;
}
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;
}
return 0;
}
int Render::CreateMainWindow() {
main_ctx_ = ImGui::CreateContext();
if (!main_ctx_) {
LOG_ERROR("Main context is null");
return -1;
}
ImGui::SetCurrentContext(main_ctx_);
SDL_WindowFlags window_flags =
(SDL_WindowFlags)(SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_BORDERLESS);
main_window_ = SDL_CreateWindow(
"Remote Desk", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
main_window_width_default_, main_window_height_default_, window_flags);
main_renderer_ = SDL_CreateRenderer(
main_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
if (main_renderer_ == nullptr) {
LOG_ERROR("Error creating SDL_Renderer");
return 0;
}
SDL_SetWindowResizable(main_window_, SDL_FALSE);
// for window region action
SDL_SetWindowHitTest(main_window_, HitTestCallback, this);
return 0;
}
int Render::DestroyMainWindow() {
if (main_ctx_) {
ImGui::SetCurrentContext(main_ctx_);
}
if (main_renderer_) {
SDL_DestroyRenderer(main_renderer_);
}
if (main_window_) {
SDL_DestroyWindow(main_window_);
}
return 0;
}
int Render::CreateStreamWindow() {
if (stream_window_created_) {
return 0;
}
stream_ctx_ = ImGui::CreateContext();
if (!stream_ctx_) {
LOG_ERROR("Stream context is null");
return -1;
}
ImGui::SetCurrentContext(stream_ctx_);
SDL_WindowFlags window_flags =
(SDL_WindowFlags)(SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_BORDERLESS);
stream_window_ =
SDL_CreateWindow("Stream window", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, stream_window_width_default_,
stream_window_height_default_, window_flags);
stream_renderer_ = SDL_CreateRenderer(
stream_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
if (stream_renderer_ == nullptr) {
LOG_ERROR("Error creating SDL_Renderer");
return 0;
}
stream_pixformat_ = SDL_PIXELFORMAT_NV12;
stream_texture_ = SDL_CreateTexture(stream_renderer_, stream_pixformat_,
SDL_TEXTUREACCESS_STREAMING,
texture_width_, texture_height_);
SDL_SetWindowResizable(stream_window_, SDL_TRUE);
// for window region action
SDL_SetWindowHitTest(stream_window_, HitTestCallback, this);
stream_window_created_ = true;
return 0;
}
int Render::DestroyStreamWindow() {
if (stream_ctx_) {
ImGui::SetCurrentContext(stream_ctx_);
}
if (stream_renderer_) {
SDL_DestroyRenderer(stream_renderer_);
}
if (stream_window_) {
SDL_DestroyWindow(stream_window_);
}
stream_window_created_ = false;
return 0;
}
int Render::SetupFontAndStyle() {
// Setup Dear ImGui style
ImGuiIO &io = ImGui::GetIO();
// Master keyboard navigation enable flag. Enable full Tabbing + directional
// arrows + space/enter to activate.
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |=
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
// Load Fonts
ImFontConfig config;
config.FontDataOwnedByAtlas = false;
io.Fonts->AddFontFromMemoryTTF(OPPOSans_Regular_ttf,
sizeof(OPPOSans_Regular_ttf), 32.0f, &config,
io.Fonts->GetGlyphRangesChineseFull());
config.MergeMode = true;
config.GlyphMinAdvanceX =
13.0f; // Use if you want to make the icon monospaced
static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0};
// io.Fonts->AddFontFromMemoryTTF(fa_regular_400_ttf,
// sizeof(fa_regular_400_ttf),
// 30.0f, &config, icon_ranges);
io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, sizeof(fa_solid_900_ttf),
30.0f, &config, icon_ranges);
io.Fonts->Build();
// Setup Dear ImGui style
// ImGui::StyleColorsDark();
ImGui::StyleColorsLight();
return 0;
}
int Render::SetupMainWindow() {
if (!main_ctx_) {
LOG_ERROR("Main context is null");
return -1;
}
ImGui::SetCurrentContext(main_ctx_);
SetupFontAndStyle();
SDL_GL_GetDrawableSize(main_window_, &main_window_width_real_,
&main_window_height_real_);
main_window_dpi_scaling_w_ =
(float)main_window_width_real_ / (float)main_window_width_;
main_window_dpi_scaling_h_ =
(float)main_window_width_real_ / (float)main_window_width_;
SDL_RenderSetScale(main_renderer_, main_window_dpi_scaling_w_,
main_window_dpi_scaling_h_);
LOG_INFO("Use dpi scaling [{}x{}] for main window",
main_window_dpi_scaling_w_, main_window_dpi_scaling_h_);
ImGui_ImplSDL2_InitForSDLRenderer(main_window_, main_renderer_);
ImGui_ImplSDLRenderer2_Init(main_renderer_);
return 0;
}
int Render::DestroyMainWindowContext() {
ImGui::SetCurrentContext(main_ctx_);
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext(main_ctx_);
return 0;
}
int Render::SetupStreamWindow() {
if (stream_window_inited_) {
return 0;
}
if (!stream_ctx_) {
LOG_ERROR("Stream context is null");
return -1;
}
ImGui::SetCurrentContext(stream_ctx_);
SetupFontAndStyle();
SDL_GL_GetDrawableSize(stream_window_, &stream_window_width_real_,
&stream_window_height_real_);
stream_window_dpi_scaling_w_ =
(float)stream_window_width_real_ / (float)stream_window_width_;
stream_window_dpi_scaling_h_ =
(float)stream_window_width_real_ / (float)stream_window_width_;
SDL_RenderSetScale(stream_renderer_, stream_window_dpi_scaling_w_,
stream_window_dpi_scaling_h_);
LOG_INFO("Use dpi scaling [{}x{}] for stream window",
stream_window_dpi_scaling_w_, stream_window_dpi_scaling_h_);
ImGui_ImplSDL2_InitForSDLRenderer(stream_window_, stream_renderer_);
ImGui_ImplSDLRenderer2_Init(stream_renderer_);
stream_window_inited_ = true;
LOG_INFO("Stream window inited");
return 0;
}
int Render::DestroyStreamWindowContext() {
stream_window_inited_ = false;
ImGui::SetCurrentContext(stream_ctx_);
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext(stream_ctx_);
return 0;
}
int Render::DrawMainWindow() {
if (!main_ctx_) {
LOG_ERROR("Main context is null");
return -1;
}
ImGui::SetCurrentContext(main_ctx_);
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(main_window_width_, main_window_height_default_),
ImGuiCond_Always);
ImGui::Begin("MainRender", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
TitleBar(true);
MainWindow();
ImGui::End();
// Rendering
ImGui::Render();
SDL_RenderClear(main_renderer_);
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), main_renderer_);
SDL_RenderPresent(main_renderer_);
return 0;
}
int Render::DrawStreamWindow() {
if (!stream_ctx_) {
LOG_ERROR("Stream context is null");
return -1;
}
ImGui::SetCurrentContext(stream_ctx_);
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(
ImVec2(stream_window_width_,
fullscreen_button_pressed_ ? 0 : title_bar_height_),
ImGuiCond_Always);
ImGui::Begin("StreamRender", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
TitleBar(false);
ControlWindow();
ImGui::End();
// Rendering
ImGui::Render();
SDL_RenderClear(stream_renderer_);
SDL_RenderCopy(stream_renderer_, stream_texture_, NULL, &stream_render_rect_);
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), stream_renderer_);
SDL_RenderPresent(stream_renderer_);
return 0;
}
int Render::Run() {
LoadSettingsFromCacheFile();
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;
}
// get screen resolution
SDL_DisplayMode DM;
SDL_GetCurrentDisplayMode(0, &DM);
screen_width_ = DM.w;
screen_height_ = DM.h;
stream_render_rect_.x = 0;
stream_render_rect_.y = title_bar_height_;
stream_render_rect_.w = stream_window_width_;
stream_render_rect_.h = stream_window_height_ - title_bar_height_;
// use linear filtering to render textures otherwise the graphics will be
// blurry
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
// init modules
if (!modules_inited_) {
// audio
AudioDeviceInit();
// screen capture init
screen_capturer_factory_ = new ScreenCapturerFactory();
// speaker capture init
speaker_capturer_factory_ = new SpeakerCapturerFactory();
// mouse control
device_controller_factory_ = new DeviceControllerFactory();
// RTC
CreateConnectionPeer();
modules_inited_ = true;
}
// create window
CreateMainWindow();
SetupMainWindow();
// Main loop
while (!exit_) {
if (!label_inited_ ||
localization_language_index_last_ != localization_language_index_) {
connect_button_label_ =
connect_button_pressed_
? localization::disconnect[localization_language_index_]
: localization::connect[localization_language_index_];
mouse_control_button_label_ =
mouse_control_button_pressed_
? localization::release_mouse[localization_language_index_]
: localization::control_mouse[localization_language_index_];
audio_capture_button_label_ =
audio_capture_button_pressed_
? localization::mute[localization_language_index_]
: localization::audio_capture[localization_language_index_];
fullscreen_button_label_ =
fullscreen_button_pressed_
? localization::exit_fullscreen[localization_language_index_]
: localization::fullscreen[localization_language_index_];
settings_button_label_ =
localization::settings[localization_language_index_];
label_inited_ = true;
localization_language_index_last_ = localization_language_index_;
}
SDL_Event event;
while (SDL_PollEvent(&event)) {
{
if (!main_ctx_) {
LOG_ERROR("Main context is null");
return -1;
}
ImGui::SetCurrentContext(main_ctx_);
ImGui_ImplSDL2_ProcessEvent(&event);
}
if (stream_window_inited_) {
if (!stream_ctx_) {
LOG_ERROR("Stream context is null");
return -1;
}
ImGui::SetCurrentContext(stream_ctx_);
ImGui_ImplSDL2_ProcessEvent(&event);
}
if (event.type == SDL_QUIT) {
if (streaming_) {
LOG_INFO("Destroy stream window");
DestroyStreamWindow();
DestroyStreamWindowContext();
LOG_INFO("[{}] Leave connection [{}]", client_id_, remote_id_);
LeaveConnection(peer_reserved_ ? peer_reserved_ : peer_,
remote_id_.c_str());
if (peer_reserved_) {
LOG_INFO("Destroy peer[reserved]");
DestroyPeer(&peer_reserved_);
}
streaming_ = false;
rejoin_ = false;
connection_established_ = false;
received_frame_ = false;
is_client_mode_ = false;
audio_capture_button_pressed_ = false;
fullscreen_button_pressed_ = false;
SDL_SetWindowFullscreen(main_window_, SDL_FALSE);
memset(audio_buffer_, 0, 960);
SDL_SetWindowSize(main_window_, main_window_width_default_,
main_window_height_default_);
// SDL_Rect display_bounds;
// SDL_GetDisplayBounds(0, &display_bounds);
// int center_x = (display_bounds.w - main_window_width_default_) / 2;
// int center_y = (display_bounds.h - main_window_height_default_) /
// 2; SDL_SetWindowPosition(main_window_, center_x, center_y);
continue;
} else {
LOG_INFO("Quit program");
exit_ = true;
}
} else if (event.window.event == SDL_WINDOWEVENT_MAXIMIZED) {
} else if (event.window.event == SDL_WINDOWEVENT_MINIMIZED) {
} else if (event.window.event == SDL_WINDOWEVENT_RESTORED) {
window_maximized_ = false;
} else if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
SDL_GetWindowSize(stream_window_, &stream_window_width_,
&stream_window_height_);
float video_ratio = (float)video_width_ / (float)video_height_;
float video_ratio_reverse = (float)video_height_ / (float)video_width_;
int render_area_width = stream_window_width_;
int render_area_height =
stream_window_height_ -
(fullscreen_button_pressed_ ? 0 : title_bar_height_);
if (render_area_width < render_area_height * video_ratio) {
stream_render_rect_.x = 0;
stream_render_rect_.y =
abs(render_area_height -
render_area_width * video_ratio_reverse) /
2 +
(fullscreen_button_pressed_ ? 0 : title_bar_height_);
stream_render_rect_.w = render_area_width;
stream_render_rect_.h = render_area_width * video_ratio_reverse;
} else if (render_area_width > render_area_height * video_ratio) {
stream_render_rect_.x =
abs(render_area_width - render_area_height * video_ratio) / 2;
stream_render_rect_.y =
fullscreen_button_pressed_ ? 0 : title_bar_height_;
stream_render_rect_.w = render_area_height * video_ratio;
stream_render_rect_.h = render_area_height;
} else {
stream_render_rect_.x = 0;
stream_render_rect_.y =
fullscreen_button_pressed_ ? 0 : title_bar_height_;
stream_render_rect_.w = render_area_width;
stream_render_rect_.h = render_area_height;
}
} else if (event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_CLOSE) {
if (connection_established_) {
continue;
} else {
exit_ = true;
}
} else if (event.type == REFRESH_EVENT) {
if (stream_texture_)
if (video_width_ != texture_width_ ||
video_height_ != texture_height_) {
texture_width_ = video_width_;
texture_height_ = video_height_;
SDL_DestroyTexture(stream_texture_);
stream_texture_ = SDL_CreateTexture(
stream_renderer_, stream_pixformat_,
SDL_TEXTUREACCESS_STREAMING, texture_width_, texture_height_);
}
SDL_UpdateTexture(stream_texture_, NULL, dst_buffer_, texture_width_);
} else {
if (connection_established_) {
ProcessMouseKeyEvent(event);
}
}
}
if (connection_established_ && streaming_) {
CreateStreamWindow();
SetupStreamWindow();
if (!stream_window_grabbed_ && control_mouse_) {
SDL_SetWindowGrab(stream_window_, SDL_TRUE);
stream_window_grabbed_ = true;
LOG_INFO("Grabbing input events");
} else if (stream_window_grabbed_ && !control_mouse_) {
SDL_SetWindowGrab(stream_window_, SDL_FALSE);
stream_window_grabbed_ = false;
}
}
DrawMainWindow();
if (stream_window_inited_) {
DrawStreamWindow();
}
// create connection
CreateRtcConnection();
// 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 (screen_capturer_) {
screen_capturer_->Destroy();
delete screen_capturer_;
screen_capturer_ = nullptr;
}
if (speaker_capturer_) {
speaker_capturer_->Destroy();
delete speaker_capturer_;
speaker_capturer_ = nullptr;
}
if (mouse_controller_) {
mouse_controller_->Destroy();
delete mouse_controller_;
mouse_controller_ = nullptr;
}
if (screen_capturer_factory_) {
delete screen_capturer_factory_;
screen_capturer_factory_ = nullptr;
}
if (speaker_capturer_factory_) {
delete speaker_capturer_factory_;
speaker_capturer_factory_ = nullptr;
}
if (device_controller_factory_) {
delete device_controller_factory_;
device_controller_factory_ = nullptr;
}
if (peer_) {
LOG_INFO("[{}] Leave connection [{}]", client_id_, client_id_);
LeaveConnection(peer_, client_id_);
is_client_mode_ = false;
LOG_INFO("Destroy peer");
DestroyPeer(&peer_);
}
if (peer_reserved_) {
LOG_INFO("Destroy peer[reserved]");
DestroyPeer(&peer_reserved_);
}
AudioDeviceDestroy();
DestroyMainWindow();
DestroyMainWindowContext();
SDL_Quit();
return 0;
}

321
src/single_window/render.h Normal file
View File

@@ -0,0 +1,321 @@
/*
* @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"
#include "speaker_capturer_factory.h"
class Render {
public:
Render();
~Render();
public:
int Run();
private:
int CreateStreamRenderWindow();
int TitleBar(bool main_window);
int MainWindow();
int LocalWindow();
int RemoteWindow();
int SettingWindow();
int ControlWindow();
int ControlBar();
int AboutWindow();
int StatusBar();
int ConnectionStatusWindow();
private:
int CreateRtcConnection();
int CreateMainWindow();
int DestroyMainWindow();
int CreateStreamWindow();
int DestroyStreamWindow();
int SetupFontAndStyle();
int SetupMainWindow();
int DestroyMainWindowContext();
int SetupStreamWindow();
int DestroyStreamWindowContext();
int DrawMainWindow();
int DrawStreamWindow();
public:
static void OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
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, const char *user_id,
size_t user_id_size, void *user_data);
static void NetStatusReport(const char *client_id, size_t client_id_size,
TraversalMode mode, const unsigned short send,
const unsigned short receive, void *user_data);
static SDL_HitTestResult HitTestCallback(SDL_Window *window,
const SDL_Point *area, void *data);
private:
int ProcessMouseKeyEvent(SDL_Event &event);
int ProcessKeyEvent(SDL_Event &event);
int ProcessMouseEvent(SDL_Event &event);
static void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len);
static void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len);
private:
int SaveSettingsIntoCacheFile();
int LoadSettingsFromCacheFile();
int StartScreenCapture();
int StopScreenCapture();
int StartSpeakerCapture();
int StopSpeakerCapture();
int StartMouseControl();
int StopMouseControl();
int CreateConnectionPeer();
int AudioDeviceInit();
int AudioDeviceDestroy();
private:
typedef struct {
char client_id[10];
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_;
std::mutex cd_cache_mutex_;
ConfigCenter config_center_;
ConfigCenter::LANGUAGE localization_language_ =
ConfigCenter::LANGUAGE::CHINESE;
int localization_language_index_ = -1;
int localization_language_index_last_ = -1;
bool modules_inited_ = false;
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 audio_capture_button_label_ = "Audio Capture";
std::string settings_button_label_ = "Setting";
char input_password_tmp_[7] = "";
char input_password_[7] = "";
std::string random_password_ = "";
char remote_password_[7] = "";
char new_password_[7] = "";
char remote_id_display_[12] = "";
std::string remote_id_ = "";
char client_password_[20] = "";
private:
int title_bar_width_ = 960;
int title_bar_height_ = 30;
int screen_width_ = 1280;
int screen_height_ = 720;
int main_window_width_default_ = 960;
int main_window_height_default_ = 570;
int main_window_width_ = 960;
int main_window_height_ = 570;
int main_window_width_last_ = 960;
int main_window_height_last_ = 540;
int stream_window_width_default_ = 1280;
int stream_window_height_default_ = 720;
int stream_window_width_ = 1280;
int stream_window_height_ = 720;
int stream_window_width_last_ = 1280;
int stream_window_height_last_ = 720;
int main_window_width_before_maximized_ = 960;
int main_window_height_before_maximized_ = 570;
int control_window_min_width_ = 20;
int control_window_max_width_ = 170;
int control_window_width_ = 170;
int control_window_height_ = 40;
int local_window_width_ = 350;
int status_bar_height_ = 20;
int connection_status_window_width_ = 200;
int connection_status_window_height_ = 150;
int notification_window_width_ = 200;
int notification_window_height_ = 80;
int about_window_width_ = 200;
int about_window_height_ = 150;
int control_bar_pos_x_ = 0;
int control_bar_pos_y_ = 30;
int main_window_width_real_ = 960;
int main_window_height_real_ = 540;
float main_window_dpi_scaling_w_ = 1.0f;
float main_window_dpi_scaling_h_ = 1.0f;
int stream_window_width_real_ = 1280;
int stream_window_height_real_ = 720;
float stream_window_dpi_scaling_w_ = 1.0f;
float stream_window_dpi_scaling_h_ = 1.0f;
int texture_width_ = 1280;
int texture_height_ = 720;
int video_width_ = 1280;
int video_height_ = 720;
int video_size_ = 1280 * 720 * 3;
SDL_Window *main_window_ = nullptr;
SDL_Renderer *main_renderer_ = nullptr;
ImGuiContext *main_ctx_ = nullptr;
SDL_Window *stream_window_ = nullptr;
SDL_Renderer *stream_renderer_ = nullptr;
ImGuiContext *stream_ctx_ = nullptr;
bool stream_window_created_ = false;
bool stream_window_inited_ = false;
// video window
SDL_Texture *stream_texture_ = nullptr;
SDL_Rect stream_render_rect_;
uint32_t stream_pixformat_ = 0;
bool resizable_ = false;
bool label_inited_ = false;
bool exit_ = false;
bool exit_video_window_ = false;
bool connection_established_ = false;
bool control_bar_hovered_ = false;
bool connect_button_pressed_ = false;
bool password_validating_ = false;
uint32_t password_validating_time_ = 0;
bool control_bar_expand_ = true;
bool fullscreen_button_pressed_ = false;
bool mouse_control_button_pressed_ = false;
bool audio_capture_button_pressed_ = false;
bool show_settings_window_ = false;
bool received_frame_ = false;
bool is_create_connection_ = false;
bool audio_buffer_fresh_ = false;
bool rejoin_ = false;
bool control_mouse_ = false;
bool stream_window_grabbed_ = false;
bool audio_capture_ = true;
bool local_id_copied_ = false;
bool show_password_ = true;
bool password_inited_ = false;
bool regenerate_password_ = false;
bool show_about_window_ = false;
bool show_connection_status_window_ = false;
bool show_reset_password_window_ = false;
bool focus_on_input_widget_ = true;
bool window_maximized_ = false;
bool streaming_ = false;
bool is_client_mode_ = false;
bool is_control_bar_in_left_ = true;
bool control_window_width_is_changing_ = false;
double copy_start_time_ = 0;
double regenerate_password_start_time_ = 0;
double control_bar_button_pressed_time_ = 0;
ImVec2 control_winodw_pos_;
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_ = "";
bool signal_connected_ = false;
bool p2p_mode_ = true;
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;
unsigned char *dst_buffer_ = nullptr;
int dst_buffer_capacity_ = 0;
private:
ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
ScreenCapturer *screen_capturer_ = nullptr;
SpeakerCapturerFactory *speaker_capturer_factory_ = nullptr;
SpeakerCapturer *speaker_capturer_ = nullptr;
DeviceControllerFactory *device_controller_factory_ = nullptr;
MouseController *mouse_controller_ = nullptr;
uint32_t last_frame_time_;
private:
char client_id_[10] = "";
char password_saved_[7] = "";
int language_button_value_ = 0;
int video_quality_button_value_ = 0;
int video_encode_format_button_value_ = 0;
bool enable_hardware_video_codec_ = false;
bool enable_turn_ = 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;
bool enable_turn_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,331 @@
#include "device_controller.h"
#include "localization.h"
#include "rd_log.h"
#include "render.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 Render::ProcessMouseKeyEvent(SDL_Event &event) {
if (!control_mouse_ || !connection_established_) {
return 0;
}
if (SDL_KEYDOWN == event.type || SDL_KEYUP == event.type) {
ProcessKeyEvent(event);
} else {
ProcessMouseEvent(event);
}
return 0;
}
int Render::ProcessMouseEvent(SDL_Event &event) {
float ratio_x = (float)video_width_ / (float)stream_render_rect_.w;
float ratio_y = (float)video_height_ / (float)stream_render_rect_.h;
if (event.button.x <= stream_render_rect_.x) {
event.button.x = 0;
} else if (event.button.x > stream_render_rect_.x &&
event.button.x < stream_render_rect_.x + stream_render_rect_.w) {
event.button.x -= stream_render_rect_.x;
} else if (event.button.x >= stream_render_rect_.x + stream_render_rect_.w) {
event.button.x = stream_render_rect_.w;
}
if (event.button.y <= stream_render_rect_.y) {
event.button.y = 0;
} else if (event.button.y > stream_render_rect_.y &&
event.button.y < stream_render_rect_.y + stream_render_rect_.h) {
event.button.y -= stream_render_rect_.y;
} else if (event.button.y >= stream_render_rect_.y + stream_render_rect_.h) {
event.button.y = stream_render_rect_.h;
}
RemoteAction remote_action;
remote_action.m.x = (size_t)(event.button.x * ratio_x);
remote_action.m.y = (size_t)(event.button.y * ratio_y);
if (SDL_MOUSEBUTTONDOWN == event.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == event.button.button) {
remote_action.m.flag = MouseFlag::left_down;
} else if (SDL_BUTTON_RIGHT == event.button.button) {
remote_action.m.flag = MouseFlag::right_down;
}
if (control_bar_hovered_) {
remote_action.m.flag = MouseFlag::move;
}
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEBUTTONUP == event.type) {
remote_action.type = ControlType::mouse;
if (SDL_BUTTON_LEFT == event.button.button) {
remote_action.m.flag = MouseFlag::left_up;
} else if (SDL_BUTTON_RIGHT == event.button.button) {
remote_action.m.flag = MouseFlag::right_up;
}
if (control_bar_hovered_) {
remote_action.m.flag = MouseFlag::move;
}
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
} else if (SDL_MOUSEMOTION == event.type) {
remote_action.type = ControlType::mouse;
remote_action.m.flag = MouseFlag::move;
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
sizeof(remote_action));
}
return 0;
}
int Render::ProcessKeyEvent(SDL_Event &event) {
RemoteAction remote_action;
SDL_Keycode key = event.key.keysym.sym;
if (SDL_KEYDOWN == event.type) {
std::cout << "Key pressed: " << SDL_GetKeyName(key) << std::endl;
} else if (SDL_KEYUP == event.type) {
std::cout << "Key released: " << SDL_GetKeyName(key) << std::endl;
}
return 0;
}
void Render::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
Render *render = (Render *)userdata;
if (!render) {
return;
}
if (1) {
if ("Connected" == render->connection_status_str_) {
SendData(render->peer_, DATA_TYPE::AUDIO, (const char *)stream, len);
}
} else {
memcpy(render->audio_buffer_, stream, len);
render->audio_len_ = len;
SDL_Delay(10);
render->audio_buffer_fresh_ = true;
}
}
void Render::SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
// Render *render = (Render *)userdata;
// if ("Connected" == render->connection_status_str_) {
// SendData(render->peer_, DATA_TYPE::AUDIO, (const char *)stream, len);
// }
// if (!render->audio_buffer_fresh_) {
// return;
// }
// SDL_memset(stream, 0, len);
// if (render->audio_len_ == 0) {
// return;
// } else {
// }
// len = (len > render->audio_len_ ? render->audio_len_ : len);
// SDL_MixAudioFormat(stream, render->audio_buffer_, AUDIO_S16LSB, len,
// SDL_MIX_MAXVOLUME);
// render->audio_buffer_fresh_ = false;
}
void Render::OnReceiveVideoBufferCb(const XVideoFrame *video_frame,
const char *user_id, size_t user_id_size,
void *user_data) {
Render *render = (Render *)user_data;
if (!render) {
return;
}
if (render->connection_established_) {
if (!render->dst_buffer_) {
render->dst_buffer_capacity_ = video_frame->size;
render->dst_buffer_ = new unsigned char[video_frame->size];
// Adapt stream_render_rect_ to the video resolution
SDL_Event event;
event.type = SDL_WINDOWEVENT;
event.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
SDL_PushEvent(&event);
}
if (render->dst_buffer_capacity_ < video_frame->size) {
delete render->dst_buffer_;
render->dst_buffer_capacity_ = video_frame->size;
render->dst_buffer_ = new unsigned char[video_frame->size];
}
memcpy(render->dst_buffer_, video_frame->data, video_frame->size);
render->video_width_ = video_frame->width;
render->video_height_ = video_frame->height;
render->video_size_ = video_frame->size;
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
render->received_frame_ = true;
render->streaming_ = true;
}
}
void Render::OnReceiveAudioBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data) {
Render *render = (Render *)user_data;
if (!render) {
return;
}
render->audio_buffer_fresh_ = true;
SDL_QueueAudio(render->output_dev_, data, (uint32_t)size);
}
void Render::OnReceiveDataBufferCb(const char *data, size_t size,
const char *user_id, size_t user_id_size,
void *user_data) {
Render *render = (Render *)user_data;
if (!render) {
return;
}
std::string user(user_id, user_id_size);
RemoteAction remote_action;
memcpy(&remote_action, data, sizeof(remote_action));
if (ControlType::mouse == remote_action.type && render->mouse_controller_) {
render->mouse_controller_->SendCommand(remote_action);
} else if (ControlType::audio_capture == remote_action.type) {
if (remote_action.a) {
render->StartSpeakerCapture();
} else {
render->StopSpeakerCapture();
}
}
}
void Render::OnSignalStatusCb(SignalStatus status, void *user_data) {
Render *render = (Render *)user_data;
if (!render) {
return;
}
render->signal_status_ = status;
if (SignalStatus::SignalConnecting == status) {
render->signal_status_str_ = "SignalConnecting";
render->signal_connected_ = false;
} else if (SignalStatus::SignalConnected == status) {
render->signal_status_str_ = "SignalConnected";
render->signal_connected_ = true;
} else if (SignalStatus::SignalFailed == status) {
render->signal_status_str_ = "SignalFailed";
render->signal_connected_ = false;
} else if (SignalStatus::SignalClosed == status) {
render->signal_status_str_ = "SignalClosed";
render->signal_connected_ = false;
} else if (SignalStatus::SignalReconnecting == status) {
render->signal_status_str_ = "SignalReconnecting";
render->signal_connected_ = false;
} else if (SignalStatus::SignalServerClosed == status) {
render->signal_status_str_ = "SignalServerClosed";
render->signal_connected_ = false;
render->is_create_connection_ = false;
}
}
void Render::OnConnectionStatusCb(ConnectionStatus status, const char *user_id,
const size_t user_id_size, void *user_data) {
Render *render = (Render *)user_data;
if (!render) {
return;
}
render->connection_status_ = status;
render->show_connection_status_window_ = true;
if (ConnectionStatus::Connecting == status) {
render->connection_status_str_ = "Connecting";
} else if (ConnectionStatus::Gathering == status) {
render->connection_status_str_ = "Gathering";
} else if (ConnectionStatus::Connected == status) {
render->connection_status_str_ = "Connected";
render->connection_established_ = true;
if (render->peer_reserved_ || !render->is_client_mode_) {
render->start_screen_capture_ = true;
render->start_mouse_control_ = true;
}
} else if (ConnectionStatus::Disconnected == status) {
render->connection_status_str_ = "Disconnected";
render->password_validating_time_ = 0;
} else if (ConnectionStatus::Failed == status) {
render->connection_status_str_ = "Failed";
render->password_validating_time_ = 0;
} else if (ConnectionStatus::Closed == status) {
render->connection_status_str_ = "Closed";
render->password_validating_time_ = 0;
render->start_screen_capture_ = false;
render->start_mouse_control_ = false;
render->connection_established_ = false;
render->control_mouse_ = false;
if (render->audio_capture_) {
render->StopSpeakerCapture();
render->audio_capture_ = false;
render->audio_capture_button_pressed_ = false;
}
render->exit_video_window_ = false;
if (!render->rejoin_) {
memset(render->remote_password_, 0, sizeof(render->remote_password_));
}
if (render->dst_buffer_) {
memset(render->dst_buffer_, 0, render->dst_buffer_capacity_);
SDL_UpdateTexture(render->stream_texture_, NULL, render->dst_buffer_,
render->texture_width_);
}
} else if (ConnectionStatus::IncorrectPassword == status) {
render->connection_status_str_ = "Incorrect password";
render->password_validating_ = false;
render->password_validating_time_++;
if (render->connect_button_pressed_) {
render->connection_established_ = false;
render->connect_button_label_ =
render->connect_button_pressed_
? localization::disconnect[render->localization_language_index_]
: localization::connect[render->localization_language_index_];
}
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
render->connection_status_str_ = "No such transmission id";
if (render->connect_button_pressed_) {
render->connection_established_ = false;
render->connect_button_label_ =
render->connect_button_pressed_
? localization::disconnect[render->localization_language_index_]
: localization::connect[render->localization_language_index_];
}
}
}
void Render::NetStatusReport(const char *client_id, size_t client_id_size,
TraversalMode mode, const unsigned short send,
const unsigned short receive, void *user_data) {
Render *render = (Render *)user_data;
if (!render) {
return;
}
if (0 == strcmp(render->client_id_, "")) {
memset(&render->client_id_, 0, sizeof(render->client_id_));
strncpy(render->client_id_, client_id, client_id_size);
LOG_INFO("Use client id [{}] and save id into cache file", client_id);
render->SaveSettingsIntoCacheFile();
}
if (mode != TraversalMode::UnknownMode) {
LOG_INFO("Net mode: [{}]", int(mode));
}
}

View File

@@ -0,0 +1,277 @@
#include "IconsFontAwesome6.h"
#include "layout_style.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::SettingWindow() {
if (show_settings_window_) {
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::SetWindowFontScale(0.5f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::Begin(localization::settings[localization_language_index_].c_str(),
nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoSavedSettings);
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
{
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(
"%s", 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(
"%s",
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(
"%s",
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("%s", 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_);
}
ImGui::Separator();
{
ImGui::SetCursorPosY(152);
ImGui::Text(
"%s",
localization::enable_turn[localization_language_index_].c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_EN);
}
ImGui::SetCursorPosY(150);
ImGui::Checkbox("##enable_turn", &enable_turn_);
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN);
} else {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
}
ImGui::SetCursorPosY(190.0f);
ImGui::PopStyleVar();
// OK
if (ImGui::Button(
localization::ok[localization_language_index_].c_str())) {
show_settings_window_ = 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_;
// TURN mode
if (enable_turn_) {
config_center_.SetTurn(true);
} else {
config_center_.SetTurn(false);
}
enable_turn_last_ = enable_turn_;
SaveSettingsIntoCacheFile();
settings_window_pos_reset_ = true;
// Recreate peer instance
LoadSettingsFromCacheFile();
// Recreate peer instance
{
LOG_INFO("Recreate peer instance");
DestroyPeer(&peer_);
is_create_connection_ = false;
CreateConnectionPeer();
}
}
ImGui::SameLine();
// Cancel
if (ImGui::Button(
localization::cancel[localization_language_index_].c_str())) {
show_settings_window_ = 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_;
}
if (enable_turn_ != enable_turn_last_) {
enable_turn_ = enable_turn_last_;
}
settings_window_pos_reset_ = true;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::End();
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
}
}
return 0;
}

View File

@@ -0,0 +1,39 @@
#include "localization.h"
#include "render.h"
int Render::StatusBar() {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
static bool a, b, c, d, e;
ImGui::SetNextWindowPos(
ImVec2(0, main_window_height_default_ - status_bar_height_ - 1),
ImGuiCond_Always);
ImGui::BeginChild(
"StatusBar", ImVec2(main_window_width_, status_bar_height_ + 1),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddCircleFilled(
ImVec2(15, main_window_height_default_ - status_bar_height_ + 9.0f), 5,
ImColor(signal_connected_ ? 0.0f : 1.0f, signal_connected_ ? 1.0f : 0.0f,
0.0f),
100);
draw_list->AddCircle(
ImVec2(15, main_window_height_default_ - status_bar_height_ + 10.0f), 6,
ImColor(1.0f, 1.0f, 1.0f), 100);
ImGui::SetWindowFontScale(0.5f);
draw_list->AddText(
ImVec2(25, main_window_height_default_ - status_bar_height_ + 3.0f),
ImColor(0.0f, 0.0f, 0.0f),
signal_connected_
? localization::signal_connected[localization_language_index_].c_str()
: localization::signal_disconnected[localization_language_index_]
.c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor();
ImGui::EndChild();
return 0;
}

View File

@@ -0,0 +1,166 @@
#include "IconsFontAwesome6.h"
#include "localization.h"
#include "render.h"
#define BUTTON_PADDING 36.0f
int Render::TitleBar(bool main_window) {
ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4(1, 1, 1, 0.0f));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetWindowFontScale(0.8f);
ImGui::BeginChild(
"TitleBar",
ImVec2(main_window ? main_window_width_ : stream_window_width_,
title_bar_height_),
ImGuiChildFlags_None,
ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (ImGui::BeginMenuBar()) {
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
(streaming_ ? BUTTON_PADDING * 4 - 3 : BUTTON_PADDING * 3 - 3));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (!streaming_) {
float bar_pos_x = ImGui::GetCursorPosX() + 6;
float bar_pos_y = ImGui::GetCursorPosY() + 15;
std::string menu_button = " "; // ICON_FA_BARS;
if (ImGui::BeginMenu(menu_button.c_str())) {
ImGui::SetWindowFontScale(0.5f);
if (ImGui::MenuItem(
localization::settings[localization_language_index_].c_str())) {
show_settings_window_ = true;
}
if (ImGui::MenuItem(
localization::about[localization_language_index_].c_str())) {
show_about_window_ = true;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::EndMenu();
}
float menu_bar_line_size = 15.0f;
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y - 6),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y - 6),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y + 6),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y + 6),
IM_COL32(0, 0, 0, 255));
{
SettingWindow();
AboutWindow();
}
}
ImGui::PopStyleColor(2);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
(streaming_ ? BUTTON_PADDING * 3 : BUTTON_PADDING * 2));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
float minimize_pos_x = ImGui::GetCursorPosX() + 12;
float minimize_pos_y = ImGui::GetCursorPosY() + 15;
std::string window_minimize_button = "##minimize"; // ICON_FA_MINUS;
if (ImGui::Button(window_minimize_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_MinimizeWindow(main_window ? main_window_ : stream_window_);
}
draw_list->AddLine(ImVec2(minimize_pos_x, minimize_pos_y),
ImVec2(minimize_pos_x + 12, minimize_pos_y),
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(2);
if (streaming_) {
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
BUTTON_PADDING * 2);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (window_maximized_) {
float pos_x_top = ImGui::GetCursorPosX() + 11;
float pos_y_top = ImGui::GetCursorPosY() + 11;
float pos_x_bottom = ImGui::GetCursorPosX() + 13;
float pos_y_bottom = ImGui::GetCursorPosY() + 9;
std::string window_restore_button =
"##restore"; // ICON_FA_WINDOW_RESTORE;
if (ImGui::Button(window_restore_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_RestoreWindow(main_window ? main_window_ : stream_window_);
window_maximized_ = false;
}
draw_list->AddRect(ImVec2(pos_x_top, pos_y_top),
ImVec2(pos_x_top + 12, pos_y_top + 12),
IM_COL32(0, 0, 0, 255));
draw_list->AddRect(ImVec2(pos_x_bottom, pos_y_bottom),
ImVec2(pos_x_bottom + 12, pos_y_bottom + 12),
IM_COL32(0, 0, 0, 255));
draw_list->AddRectFilled(ImVec2(pos_x_top + 1, pos_y_top + 1),
ImVec2(pos_x_top + 11, pos_y_top + 11),
IM_COL32(255, 255, 255, 255));
} else {
float maximize_pos_x = ImGui::GetCursorPosX() + 12;
float maximize_pos_y = ImGui::GetCursorPosY() + 10;
std::string window_maximize_button =
"##maximize"; // ICON_FA_SQUARE_FULL;
if (ImGui::Button(window_maximize_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_MaximizeWindow(main_window ? main_window_ : stream_window_);
window_maximized_ = !window_maximized_;
}
draw_list->AddRect(ImVec2(maximize_pos_x, maximize_pos_y),
ImVec2(maximize_pos_x + 12, maximize_pos_y + 12),
IM_COL32(0, 0, 0, 255));
}
ImGui::PopStyleColor(2);
}
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
BUTTON_PADDING);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0, 0, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0, 0, 0.5f));
float xmark_pos_x = ImGui::GetCursorPosX() + 18;
float xmark_pos_y = ImGui::GetCursorPosY() + 16;
float xmark_size = 12.0f;
std::string close_button = "##xmark"; // ICON_FA_XMARK;
if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
}
draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
xmark_pos_y - xmark_size / 2 + 0.75f),
ImVec2(xmark_pos_x + xmark_size / 2 - 1.5f,
xmark_pos_y + xmark_size / 2 - 0.5f),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(ImVec2(xmark_pos_x + xmark_size / 2 - 1.75f,
xmark_pos_y - xmark_size / 2 + 0.75f),
ImVec2(xmark_pos_x - xmark_size / 2,
xmark_pos_y + xmark_size / 2 - 1.0f),
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(2);
ImGui::PopStyleColor();
}
ImGui::EndMenuBar();
ImGui::SetWindowFontScale(1.0f);
ImGui::EndChild();
ImGui::PopStyleColor();
return 0;
}

View File

@@ -0,0 +1,13 @@
#include "speaker_capturer_linux.h"
SpeakerCapturerLinux::SpeakerCapturerLinux() {}
SpeakerCapturerLinux::~SpeakerCapturerLinux() {}
int SpeakerCapturerLinux::Init(speaker_data_cb cb) { return 0; }
int SpeakerCapturerLinux::Destroy() { return 0; }
int SpeakerCapturerLinux::Start() { return 0; }
int SpeakerCapturerLinux::Stop() { return 0; }
int SpeakerCapturerLinux::Pause() { return 0; }
int SpeakerCapturerLinux::Resume() { return 0; }

View File

@@ -0,0 +1,38 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-08-02
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SPEAKER_CAPTURER_LINUX_H_
#define _SPEAKER_CAPTURER_LINUX_H_
#include <thread>
#include <vector>
#include "speaker_capturer.h"
class SpeakerCapturerLinux : public SpeakerCapturer {
public:
SpeakerCapturerLinux();
~SpeakerCapturerLinux();
public:
virtual int Init(speaker_data_cb cb);
virtual int Destroy();
virtual int Start();
virtual int Stop();
int Pause();
int Resume();
private:
speaker_data_cb cb_ = nullptr;
private:
bool inited_ = false;
// thread
std::thread capture_thread_;
};
#endif

View File

@@ -0,0 +1,14 @@
#include "speaker_capturer_macosx.h"
SpeakerCapturerMacosx::SpeakerCapturerMacosx() {}
SpeakerCapturerMacosx::~SpeakerCapturerMacosx() {}
int SpeakerCapturerMacosx::Init(speaker_data_cb cb) { return 0; }
int SpeakerCapturerMacosx::Destroy() { return 0; }
int SpeakerCapturerMacosx::Start() { return 0; }
int SpeakerCapturerMacosx::Stop() { return 0; }
int SpeakerCapturerMacosx::Pause() { return 0; }
int SpeakerCapturerMacosx::Resume() { return 0; }

View File

@@ -0,0 +1,38 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-08-02
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SPEAKER_CAPTURER_MACOSX_H_
#define _SPEAKER_CAPTURER_MACOSX_H_
#include <thread>
#include <vector>
#include "speaker_capturer.h"
class SpeakerCapturerMacosx : public SpeakerCapturer {
public:
SpeakerCapturerMacosx();
~SpeakerCapturerMacosx();
public:
virtual int Init(speaker_data_cb cb);
virtual int Destroy();
virtual int Start();
virtual int Stop();
int Pause();
int Resume();
private:
speaker_data_cb cb_ = nullptr;
private:
bool inited_ = false;
// thread
std::thread capture_thread_;
};
#endif

View File

@@ -0,0 +1,26 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-07-22
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SPEAKER_CAPTURER_H_
#define _SPEAKER_CAPTURER_H_
#include <functional>
class SpeakerCapturer {
public:
typedef std::function<void(unsigned char *, size_t)> speaker_data_cb;
public:
virtual ~SpeakerCapturer() {}
public:
virtual int Init(speaker_data_cb cb) = 0;
virtual int Destroy() = 0;
virtual int Start() = 0;
virtual int Stop() = 0;
};
#endif

View File

@@ -0,0 +1,36 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-07-22
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SPEAKER_CAPTURER_FACTORY_H_
#define _SPEAKER_CAPTURER_FACTORY_H_
#ifdef _WIN32
#include "speaker_capturer_wasapi.h"
#elif __linux__
#include "speaker_capturer_linux.h"
#elif __APPLE__
#include "speaker_capturer_macosx.h"
#endif
class SpeakerCapturerFactory {
public:
virtual ~SpeakerCapturerFactory() {}
public:
SpeakerCapturer* Create() {
#ifdef _WIN32
return new SpeakerCapturerWasapi();
#elif __linux__
return new SpeakerCapturerLinux();
#elif __APPLE__
return new SpeakerCapturerMacosx();
#else
return nullptr;
#endif
}
};
#endif

View File

@@ -0,0 +1,100 @@
#include "speaker_capturer_wasapi.h"
#include "rd_log.h"
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#define SAVE_AUDIO_FILE 0
static ma_device_config device_config_;
static ma_device device_;
static ma_format format_ = ma_format_s16;
static ma_uint32 sample_rate_ = ma_standard_sample_rate_48000;
static ma_uint32 channels_ = 1;
static FILE* fp_ = nullptr;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
ma_uint32 frameCount) {
SpeakerCapturerWasapi* ptr = (SpeakerCapturerWasapi*)pDevice->pUserData;
if (ptr) {
if (SAVE_AUDIO_FILE) {
fwrite(pInput, frameCount * ma_get_bytes_per_frame(format_, channels_), 1,
fp_);
}
ptr->GetCallback()((unsigned char*)pInput,
frameCount * ma_get_bytes_per_frame(format_, channels_));
}
(void)pOutput;
}
SpeakerCapturerWasapi::speaker_data_cb SpeakerCapturerWasapi::GetCallback() {
return cb_;
}
SpeakerCapturerWasapi::SpeakerCapturerWasapi() {}
SpeakerCapturerWasapi::~SpeakerCapturerWasapi() {
if (SAVE_AUDIO_FILE) {
fclose(fp_);
}
}
int SpeakerCapturerWasapi::Init(speaker_data_cb cb) {
if (inited_) {
return 0;
}
cb_ = cb;
if (SAVE_AUDIO_FILE) {
fopen_s(&fp_, "system_audio.pcm", "wb");
}
ma_result result;
ma_backend backends[] = {ma_backend_wasapi};
device_config_ = ma_device_config_init(ma_device_type_loopback);
device_config_.capture.pDeviceID = NULL;
device_config_.capture.format = format_;
device_config_.capture.channels = channels_;
device_config_.sampleRate = sample_rate_;
device_config_.dataCallback = data_callback;
device_config_.pUserData = this;
result = ma_device_init_ex(backends, sizeof(backends) / sizeof(backends[0]),
NULL, &device_config_, &device_);
if (result != MA_SUCCESS) {
LOG_ERROR("Failed to initialize loopback device");
return -1;
}
inited_ = true;
return 0;
}
int SpeakerCapturerWasapi::Start() {
ma_result result = ma_device_start(&device_);
if (result != MA_SUCCESS) {
ma_device_uninit(&device_);
LOG_ERROR("Failed to start device");
return -1;
}
return 0;
}
int SpeakerCapturerWasapi::Stop() {
ma_device_stop(&device_);
return 0;
}
int SpeakerCapturerWasapi::Destroy() {
ma_device_uninit(&device_);
return 0;
}
int SpeakerCapturerWasapi::Pause() { return 0; }

View File

@@ -0,0 +1,35 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-08-15
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SPEAKER_CAPTURER_WASAPI_H_
#define _SPEAKER_CAPTURER_WASAPI_H_
#include "speaker_capturer.h"
class SpeakerCapturerWasapi : public SpeakerCapturer {
public:
SpeakerCapturerWasapi();
~SpeakerCapturerWasapi();
public:
virtual int Init(speaker_data_cb cb);
virtual int Destroy();
virtual int Start();
virtual int Stop();
int Pause();
int Resume();
speaker_data_cb GetCallback();
private:
speaker_data_cb cb_ = nullptr;
private:
bool inited_ = false;
};
#endif

View File

@@ -0,0 +1,303 @@
// MyAudioSink.cpp : 定义控制台应用程序的入口点。
//
// #define _CRT_SECURE_NO_WARNINGS
#include <Audioclient.h>
#include <Devicetopology.h>
#include <Endpointvolume.h>
#include <Mmdeviceapi.h>
#include <tchar.h>
#include <iostream>
//-----------------------------------------------------------
// Record an audio stream from the default audio capture
// device. The RecordAudioStream function allocates a shared
// buffer big enough to hold one second of PCM audio data.
// The function uses this buffer to stream data from the
// capture device. The main loop runs every 1/2 second.
//-----------------------------------------------------------
// REFERENCE_TIME time units per second and per millisecond
#define REFTIMES_PER_SEC 10000000
#define REFTIMES_PER_MILLISEC 10000
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { \
goto Exit; \
}
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) { \
(punk)->Release(); \
(punk) = NULL; \
}
#define IS_INPUT_DEVICE 0 // 切换输入和输出音频设备
#define BUFFER_TIME_100NS (5 * 10000000)
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient);
const IID IID_IDeviceTopology = __uuidof(IDeviceTopology);
const IID IID_IAudioVolumeLevel = __uuidof(IAudioVolumeLevel);
const IID IID_IPart = __uuidof(IPart);
const IID IID_IConnector = __uuidof(IConnector);
const IID IID_IAudioEndpointVolume = __uuidof(IAudioEndpointVolume);
class MyAudioSink {
public:
// WAVEFORMATEX *pwfx = NULL;
int SetFormat(WAVEFORMATEX *pwfx);
int CopyData(SHORT *pData, UINT32 numFramesAvailable, BOOL *pbDone);
};
int MyAudioSink::SetFormat(WAVEFORMATEX *pwfx) {
printf("wFormatTag is %x\n", pwfx->wFormatTag);
printf("nChannels is %x\n", pwfx->nChannels);
printf("nSamplesPerSec is %d\n", pwfx->nSamplesPerSec);
printf("nAvgBytesPerSec is %d\n", pwfx->nAvgBytesPerSec);
printf("wBitsPerSample is %d\n", pwfx->wBitsPerSample);
return 0;
}
FILE *fp;
int MyAudioSink::CopyData(SHORT *pData, UINT32 numFramesAvailable,
BOOL *pbDone) {
if (pData != NULL) {
size_t t = sizeof(SHORT);
for (int i = 0; i < numFramesAvailable / t; i++) {
double dbVal = pData[i];
pData[i] = dbVal; // 可以通过不同的分母来控制声音大小
}
fwrite(pData, numFramesAvailable, 1, fp);
}
return 0;
}
/// pwfx->nSamplesPerSec = 44100;
/// 不支持修改采样率, 看来只能等得到数据之后再 swr 转换了
BOOL AdjustFormatTo16Bits(WAVEFORMATEX *pwfx) {
BOOL bRet(FALSE);
if (pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
pwfx->wFormatTag = WAVE_FORMAT_PCM;
pwfx->wBitsPerSample = 16;
pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
bRet = TRUE;
} else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast<PWAVEFORMATEXTENSIBLE>(pwfx);
if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) {
pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
pEx->Samples.wValidBitsPerSample = 16;
pwfx->wBitsPerSample = 16;
pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
bRet = TRUE;
}
}
return bRet;
}
typedef unsigned long long uint64_t;
static bool have_clockfreq = false;
static LARGE_INTEGER clock_freq;
static inline uint64_t get_clockfreq(void) {
if (!have_clockfreq) QueryPerformanceFrequency(&clock_freq);
return clock_freq.QuadPart;
}
uint64_t os_gettime_ns(void) {
LARGE_INTEGER current_time;
double time_val;
QueryPerformanceCounter(&current_time);
time_val = (double)current_time.QuadPart;
time_val *= 1000000000.0;
time_val /= (double)get_clockfreq();
return (uint64_t)time_val;
}
HRESULT RecordAudioStream(MyAudioSink *pMySink) {
HRESULT hr;
REFERENCE_TIME hnsActualDuration;
UINT32 bufferFrameCount;
UINT32 numFramesAvailable;
BYTE *pData;
DWORD flags;
REFERENCE_TIME hnsDefaultDevicePeriod(0);
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
WAVEFORMATEX *pwfx = NULL;
UINT32 packetLength = 0;
BOOL bDone = FALSE;
HANDLE hTimerWakeUp = NULL;
UINT64 pos, ts;
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
IID_IMMDeviceEnumerator, (void **)&pEnumerator);
EXIT_ON_ERROR(hr)
if (IS_INPUT_DEVICE)
hr = pEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications,
&pDevice); // 输入
else
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole,
&pDevice); // 输出
// wchar_t *w_id;
// os_utf8_to_wcs_ptr(device_id.c_str(), device_id.size(), &w_id);
// hr = pEnumerator->GetDevice(w_id, &pDevice);
// bfree(w_id);
EXIT_ON_ERROR(hr)
hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL,
(void **)&pAudioClient);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetMixFormat(&pwfx);
EXIT_ON_ERROR(hr)
// The GetDevicePeriod method retrieves the length of the periodic interval
// separating successive processing passes by the audio engine on the data in
// the endpoint buffer.
hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL);
EXIT_ON_ERROR(hr)
AdjustFormatTo16Bits(pwfx);
// 平时创建定时器使用的是WINAPI SetTimer不过该函数一般用于有界面的时候。
// 无界面的情况下可以选择微软提供的CreateWaitableTimer和SetWaitableTimer
// API。
hTimerWakeUp = CreateWaitableTimer(NULL, FALSE, NULL);
DWORD flag;
if (IS_INPUT_DEVICE)
flag = 0;
else
flag = AUDCLNT_STREAMFLAGS_LOOPBACK;
if (IS_INPUT_DEVICE)
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flag /*0*/, 0, 0,
pwfx, NULL); // 输入
else
hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, flag /*0*/, 0, 0,
pwfx, NULL); // 输出
EXIT_ON_ERROR(hr)
// Get the size of the allocated buffer.
hr = pAudioClient->GetBufferSize(&bufferFrameCount);
EXIT_ON_ERROR(hr)
hr = pAudioClient->GetService(IID_IAudioCaptureClient,
(void **)&pCaptureClient);
EXIT_ON_ERROR(hr)
LARGE_INTEGER liFirstFire;
liFirstFire.QuadPart =
-hnsDefaultDevicePeriod / 2; // negative means relative time
LONG lTimeBetweenFires = (LONG)hnsDefaultDevicePeriod / 2 /
(10 * 1000); // convert to milliseconds
BOOL bOK = SetWaitableTimer(hTimerWakeUp, &liFirstFire, lTimeBetweenFires,
NULL, NULL, FALSE);
// Notify the audio sink which format to use.
hr = pMySink->SetFormat(pwfx);
EXIT_ON_ERROR(hr)
// Calculate the actual duration of the allocated buffer.
hnsActualDuration =
(double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
/*************************************************************/
hr = pAudioClient->Start(); // Start recording.
EXIT_ON_ERROR(hr)
HANDLE waitArray[1] = {/*htemp hEventStop,*/ hTimerWakeUp};
// Each loop fills about half of the shared buffer.
while (bDone == FALSE) {
// Sleep for half the buffer duration.
// Sleep(hnsActualDuration/REFTIMES_PER_MILLISEC/2);//这句貌似不加也可以
// WaitForSingleObject(hTimerWakeUp,INFINITE);
int a = sizeof(waitArray);
int aa = sizeof(waitArray[0]);
WaitForMultipleObjects(sizeof(waitArray) / sizeof(waitArray[0]), waitArray,
FALSE, INFINITE);
// WaitForMultipleObjects(sizeof(waitArray) / sizeof(waitArray[0]),
// waitArray, FALSE, INFINITE);
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
while (packetLength != 0) {
// Get the available data in the shared buffer.
hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL,
&ts);
ts = ts * 100;
uint64_t timestamp =
os_gettime_ns(); // ts是设备时间timestamp是系统时间
EXIT_ON_ERROR(hr)
// 位运算flags的标志符为2静音状态将pData置为NULL
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
pData = NULL; // Tell CopyData to write silence.
}
// Copy the available capture data to the audio sink.
hr = pMySink->CopyData((SHORT *)pData,
numFramesAvailable * pwfx->nBlockAlign, &bDone);
EXIT_ON_ERROR(hr)
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
EXIT_ON_ERROR(hr)
hr = pCaptureClient->GetNextPacketSize(&packetLength);
EXIT_ON_ERROR(hr)
}
}
hr = pAudioClient->Stop(); // Stop recording.
EXIT_ON_ERROR(hr)
Exit:
CoTaskMemFree(pwfx);
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pDevice)
SAFE_RELEASE(pAudioClient)
SAFE_RELEASE(pCaptureClient)
return hr;
}
int _tmain(int argc, _TCHAR *argv[]) {
fopen_s(&fp, "record.pcm", "wb");
CoInitialize(NULL);
MyAudioSink test;
RecordAudioStream(&test);
return 0;
}

View File

@@ -0,0 +1,96 @@
/*
Demonstrates how to implement loopback recording.
This example simply captures data from your default playback device until you
press Enter. The output is saved to the file specified on the command line.
Loopback mode is when you record audio that is played from a given speaker. It
is only supported on WASAPI, but can be used indirectly with PulseAudio by
choosing the appropriate loopback device after enumeration.
To use loopback mode you just need to set the device type to
ma_device_type_loopback and set the capture device config properties. The output
buffer in the callback will be null whereas the input buffer will be valid.
*/
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#include <stdio.h>
#include <stdlib.h>
FILE* fp;
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
ma_uint32 frameCount) {
// ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData;
// MA_ASSERT(pEncoder != NULL);
// ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL);
fwrite(pInput, frameCount * ma_get_bytes_per_frame(ma_format_s16, 1), 1, fp);
(void)pOutput;
}
int main(int argc, char** argv) {
ma_result result;
ma_encoder_config encoderConfig;
ma_encoder encoder;
ma_device_config deviceConfig;
ma_device device;
fopen_s(&fp, "miniaudio.pcm", "wb");
/* Loopback mode is currently only supported on WASAPI. */
ma_backend backends[] = {ma_backend_wasapi};
// if (argc < 2) {
// printf("No output file.\n");
// return -1;
// }
// encoderConfig =
// ma_encoder_config_init(ma_encoding_format_wav, ma_format_s16, 1,
// 48000);
// if (ma_encoder_init_file(argv[1], &encoderConfig, &encoder) != MA_SUCCESS)
// {
// printf("Failed to initialize output file.\n");
// return -1;
// }
deviceConfig = ma_device_config_init(ma_device_type_loopback);
deviceConfig.capture.pDeviceID =
NULL; /* Use default device for this example. Set this to the ID of a
_playback_ device if you want to capture from a specific device.
*/
deviceConfig.capture.format = ma_format_s16;
deviceConfig.capture.channels = 1;
deviceConfig.sampleRate = 48000;
deviceConfig.dataCallback = data_callback;
deviceConfig.pUserData = nullptr;
result = ma_device_init_ex(backends, sizeof(backends) / sizeof(backends[0]),
NULL, &deviceConfig, &device);
if (result != MA_SUCCESS) {
printf("Failed to initialize loopback device.\n");
return -2;
}
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
ma_device_uninit(&device);
printf("Failed to start device.\n");
return -3;
}
printf("Press Enter to stop recording...\n");
getchar();
fclose(fp);
ma_device_uninit(&device);
// ma_encoder_uninit(&encoder);
return 0;
}

View File

@@ -228,7 +228,7 @@ int main(int argc, char *argv[]) {
// Event Loop
SDL_Event event;
last_frame_time = std::chrono::high_resolution_clock::now();
last_frame_time = std::chrono::steady_clock::now();
for (;;) {
// Wait
@@ -249,7 +249,7 @@ int main(int argc, char *argv[]) {
}
printf("xxxxxxxxxxxxxxxxxxx\n");
if (!got_picture) {
auto now_time = std::chrono::high_resolution_clock::now();
auto now_time = std::chrono::steady_clock::now();
std::chrono::duration<double> duration = now_time - last_frame_time;
auto tc = duration.count() * 1000;
printf("duration: %f\n", tc);

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")

115
xmake.lua
View File

@@ -1,9 +1,12 @@
set_project("remote_desk")
set_version("0.0.1")
set_license("LGPL-3.0")
set_version("0.0.1")
add_defines("RD_VERSION=\"0.0.1\"");
add_rules("mode.release", "mode.debug")
set_languages("c++17")
set_encodings("utf-8")
-- set_policy("build.warning", true)
-- set_warnings("all", "extra")
@@ -13,19 +16,17 @@ 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 v1.91.4-docking", {configs = {sdl2 = true, sdl2_renderer = true}})
add_requires("miniaudio 0.11.21")
if is_os("windows") then
add_requires("libyuv")
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,46 +38,68 @@ 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")
add_frameworks("OpenGL", "IOSurface", "ScreenCaptureKit")
end
add_packages("spdlog", "sdl2", "imgui")
add_packages("spdlog", "imgui")
includes("thirdparty")
target("log")
target("rd_log")
set_kind("object")
add_packages("spdlog")
add_headerfiles("src/log/log.h")
add_headerfiles("src/log/rd_log.h")
add_includedirs("src/log", {public = true})
target("common")
set_kind("object")
add_deps("log")
add_deps("rd_log")
add_files("src/common/*.cpp")
add_includedirs("src/common", {public = true})
target("screen_capturer")
set_kind("object")
add_deps("log")
add_deps("rd_log")
add_includedirs("src/screen_capturer", {public = true})
if is_os("windows") then
add_packages("libyuv")
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/avfoundation/*.cpp",
"src/screen_capturer/macosx/screen_capturer_kit/*.cpp",
"src/screen_capturer/macosx/screen_capturer_kit/*.mm")
add_includedirs("src/screen_capturer/macosx/avfoundation",
"src/screen_capturer/macosx/screen_capturer_kit", {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("speaker_capturer")
set_kind("object")
add_deps("rd_log")
add_includedirs("src/speaker_capturer", {public = true})
if is_os("windows") then
add_packages("miniaudio")
add_files("src/speaker_capturer/windows/*.cpp")
add_includedirs("src/speaker_capturer/windows", {public = true})
elseif is_os("macosx") then
add_files("src/speaker_capturer/macosx/*.cpp")
add_includedirs("src/speaker_capturer/macosx", {public = true})
elseif is_os("linux") then
add_files("src/speaker_capturer/linux/*.cpp")
add_includedirs("src/speaker_capturer/linux", {public = true})
end
target("device_controller")
set_kind("object")
add_deps("log")
add_deps("rd_log")
add_includedirs("src/device_controller", {public = true})
if is_os("windows") then
add_files("src/device_controller/mouse/windows/*.cpp")
@@ -89,18 +112,48 @@ target("device_controller")
add_includedirs("src/device_controller/mouse/linux", {public = true})
end
target("config_center")
set_kind("object")
add_deps("rd_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("single_window")
set_kind("object")
add_deps("rd_log", "common", "localization", "config_center", "projectx", "screen_capturer", "speaker_capturer", "device_controller")
if is_os("macosx") then
add_packages("ffmpeg")
elseif is_os("linux") then
add_packages("ffmpeg")
end
add_files("src/single_window/*.cpp")
add_includedirs("src/single_window", {public = true})
add_includedirs("fonts", {public = true})
target("remote_desk")
set_kind("binary")
add_deps("log", "common", "screen_capturer", "device_controller", "projectx")
add_deps("rd_log", "common", "single_window")
if is_os("windows") then
add_files("icon/app.rc")
elseif is_os("macosx") then
add_packages("ffmpeg")
-- add_rules("xcode.application")
-- add_files("Info.plist")
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("miniaudio_capture")
-- set_kind("binary")
-- add_packages("miniaudio")
-- if is_os("windows") then
-- add_files("test/audio_capture/miniaudio.cpp")
-- end
-- target("screen_capturer")
-- set_kind("binary")
@@ -157,7 +210,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")