mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-29 04:10:17 +08:00
Compare commits
120 Commits
av1
...
multi-wind
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
863070a8a7 | ||
|
|
44f9e6a8c9 | ||
|
|
087d5d7e52 | ||
|
|
26fa53f867 | ||
|
|
d18af6cbc6 | ||
|
|
b5bb62bd22 | ||
|
|
9ed3ab9929 | ||
|
|
dca18762e0 | ||
|
|
fed7c3b103 | ||
|
|
d246b7a04d | ||
|
|
a49ca813e0 | ||
|
|
0c688efaee | ||
|
|
be3561d46f | ||
|
|
c3af40a3f0 | ||
|
|
d493b9a131 | ||
|
|
4e4e84ae4d | ||
|
|
fea545e5e7 | ||
|
|
9096769a85 | ||
|
|
04ab157ecb | ||
|
|
2331f08283 | ||
|
|
9f8f99f21b | ||
|
|
56dadb6a49 | ||
|
|
59c9ca8d53 | ||
|
|
f16a4e8aa2 | ||
|
|
890615e13a | ||
|
|
2f72e3957e | ||
|
|
1292018f51 | ||
|
|
8ae9513104 | ||
|
|
c1efe2f4ac | ||
|
|
1210a0b631 | ||
|
|
39863c597e | ||
|
|
8a964f0030 | ||
|
|
74e29f25bf | ||
|
|
1e5bea2b1e | ||
|
|
d8297ebb74 | ||
|
|
93d7f71cf2 | ||
|
|
887a217828 | ||
|
|
89b12136e4 | ||
|
|
def7025abf | ||
|
|
35af5aab43 | ||
|
|
9ea67df0fd | ||
|
|
72fda8a728 | ||
|
|
070b48d7a7 | ||
|
|
6168009cef | ||
|
|
06a7243ac1 | ||
|
|
c8602b0d89 | ||
|
|
e3c730fd5f | ||
|
|
b252cb6ddd | ||
|
|
8ca1e8e5a1 | ||
|
|
a7d45b78c8 | ||
|
|
018231eee4 | ||
|
|
4704d494ec | ||
|
|
65927c2091 | ||
|
|
574b9d10ab | ||
|
|
ff510a3b44 | ||
|
|
4d3c864950 | ||
|
|
2cde54cf30 | ||
|
|
d1f3d11318 | ||
|
|
436228946b | ||
|
|
2b4083ee10 | ||
|
|
e3abb4e3de | ||
|
|
4b7cd1005b | ||
|
|
03ea96096d | ||
|
|
0ea8916426 | ||
|
|
43b36eb893 | ||
|
|
03b6a187b3 | ||
|
|
664412dd4e | ||
|
|
b37e08a202 | ||
|
|
a05d72ec67 | ||
|
|
f77e9fe6a8 | ||
|
|
1f9614e060 | ||
|
|
50d92a763a | ||
|
|
ec23656334 | ||
|
|
880c2949c3 | ||
|
|
07f5fe81c8 | ||
|
|
5a992b6589 | ||
|
|
8e03e8e79b | ||
|
|
ceb3d9fe20 | ||
|
|
0dc0b87bc4 | ||
|
|
3a4284fece | ||
|
|
502a90f121 | ||
|
|
88cd4aca4a | ||
|
|
3395004f93 | ||
|
|
e4c05e1f4d | ||
|
|
d17c70c2c8 | ||
|
|
7b42923418 | ||
|
|
5b6bdee25a | ||
|
|
05deb73c29 | ||
|
|
3685acc549 | ||
|
|
8f5a53937a | ||
|
|
b9c5db41ab | ||
|
|
a99a4230af | ||
|
|
f446154747 | ||
|
|
5a1e2c5ed9 | ||
|
|
ff6f295fac | ||
|
|
3111b3a641 | ||
|
|
20bb13ce85 | ||
|
|
5aa05f3a13 | ||
|
|
c911aa2eb1 | ||
|
|
d0cd2fe9ab | ||
|
|
9702805331 | ||
|
|
872152f1be | ||
|
|
b822221d7f | ||
|
|
95ad605b36 | ||
|
|
af32e25149 | ||
|
|
e63b384d1e | ||
|
|
7f25f7426c | ||
|
|
eed93ea953 | ||
|
|
b5f8e92526 | ||
|
|
af04b0571e | ||
|
|
75452a3e76 | ||
|
|
3f717f1df2 | ||
|
|
ad6f2c2c70 | ||
|
|
8076e7f662 | ||
|
|
be78496992 | ||
|
|
a3f745d441 | ||
|
|
e693d920d3 | ||
|
|
0f1b89eda9 | ||
|
|
172b8836fd | ||
|
|
71178ffa33 |
39
Info.plist
Normal file
39
Info.plist
Normal 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>
|
||||
2025
fonts/OPPOSans_Regular.h
Normal file
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
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
35031
fonts/fa_solid_900.h
Normal file
File diff suppressed because it is too large
Load Diff
1
icon/app.rc
Normal file
1
icon/app.rc
Normal file
@@ -0,0 +1 @@
|
||||
IDI_ICON1 ICON "app_icon.ico"
|
||||
BIN
icon/app_icon.ico
Normal file
BIN
icon/app_icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 687 B |
BIN
icon/捕获.PNG
BIN
icon/捕获.PNG
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
BIN
icon/捕获.ico
BIN
icon/捕获.ico
Binary file not shown.
|
Before Width: | Height: | Size: 84 KiB |
@@ -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;
|
||||
|
||||
@@ -25,6 +25,11 @@ int ConfigCenter::SetHardwareVideoCodec(bool 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() {
|
||||
@@ -36,3 +41,5 @@ ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() {
|
||||
}
|
||||
|
||||
bool ConfigCenter::IsHardwareVideoCodec() { return hardware_video_codec_; }
|
||||
|
||||
bool ConfigCenter::IsEnableTurn() { return enable_turn_; }
|
||||
@@ -22,12 +22,14 @@ class ConfigCenter {
|
||||
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
|
||||
@@ -35,6 +37,7 @@ class ConfigCenter {
|
||||
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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -49,8 +49,8 @@ int MouseController::Init(int screen_width, int screen_height) {
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "log.h"
|
||||
#include "main_window.h"
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
LOG_INFO("Remote desk");
|
||||
MainWindow main_window;
|
||||
Render render;
|
||||
|
||||
main_window.Run();
|
||||
render.Run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,874 +0,0 @@
|
||||
#include <SDL.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _WIN32
|
||||
#ifdef REMOTE_DESK_DEBUG
|
||||
#pragma comment(linker, "/subsystem:\"console\"")
|
||||
#else
|
||||
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "../../thirdparty/projectx/src/interface/x.h"
|
||||
#include "config_center.h"
|
||||
#include "device_controller_factory.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl2.h"
|
||||
#include "imgui_impl_sdlrenderer2.h"
|
||||
#include "log.h"
|
||||
#include "platform.h"
|
||||
#include "screen_capturer_factory.h"
|
||||
|
||||
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
||||
|
||||
#ifdef REMOTE_DESK_DEBUG
|
||||
#define MOUSE_CONTROL 0
|
||||
#else
|
||||
#define MOUSE_CONTROL 1
|
||||
#endif
|
||||
|
||||
#define CHINESE_FONT 1
|
||||
|
||||
int screen_w = 1280, screen_h = 720;
|
||||
int window_w = 1280, window_h = 720;
|
||||
const int pixel_w = 1280, pixel_h = 720;
|
||||
|
||||
unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2];
|
||||
unsigned char audio_buffer[960];
|
||||
SDL_Texture *sdlTexture = nullptr;
|
||||
SDL_Renderer *sdlRenderer = nullptr;
|
||||
SDL_Rect sdlRect;
|
||||
SDL_Window *window;
|
||||
static SDL_AudioDeviceID input_dev;
|
||||
static SDL_AudioDeviceID output_dev;
|
||||
|
||||
uint32_t start_time, end_time, elapsed_time;
|
||||
uint32_t frame_count = 0;
|
||||
int fps = 0;
|
||||
|
||||
static std::atomic<bool> audio_buffer_fresh{false};
|
||||
static uint32_t last_ts = 0;
|
||||
|
||||
int dst_bufsize;
|
||||
struct SwrContext *swr_ctx;
|
||||
|
||||
int ret;
|
||||
|
||||
int audio_len = 0;
|
||||
|
||||
std::string window_title = "Remote Desk Client";
|
||||
std::string connection_status_str = "-";
|
||||
std::string signal_status_str = "-";
|
||||
|
||||
std::atomic<SignalStatus> signal_status{SignalStatus::SignalClosed};
|
||||
std::atomic<ConnectionStatus> connection_status{ConnectionStatus::Closed};
|
||||
|
||||
// Refresh Event
|
||||
#define REFRESH_EVENT (SDL_USEREVENT + 1)
|
||||
#define QUIT_EVENT (SDL_USEREVENT + 2)
|
||||
|
||||
typedef struct {
|
||||
char password[7];
|
||||
} CDCache;
|
||||
|
||||
int thread_exit = 0;
|
||||
PeerPtr *peer_server = nullptr;
|
||||
// PeerPtr *peer_server = nullptr;
|
||||
bool joined = false;
|
||||
bool received_frame = false;
|
||||
bool menu_hovered = false;
|
||||
|
||||
static bool connect_button_pressed = false;
|
||||
static bool fullscreen_button_pressed = false;
|
||||
|
||||
#if CHINESE_FONT
|
||||
static const char *connect_label = u8"连接";
|
||||
static const char *fullscreen_label = u8"全屏";
|
||||
#else
|
||||
static const char *connect_label = "Connect";
|
||||
static const char *fullscreen_label = "FULLSCREEN";
|
||||
#endif
|
||||
static char input_password[7] = "";
|
||||
static FILE *cd_cache_file = nullptr;
|
||||
static CDCache cd_cache;
|
||||
|
||||
static bool is_create_connection = false;
|
||||
static bool done = false;
|
||||
|
||||
ScreenCapturerFactory *screen_capturer_factory = nullptr;
|
||||
ScreenCapturer *screen_capturer = nullptr;
|
||||
|
||||
DeviceControllerFactory *device_controller_factory = nullptr;
|
||||
MouseController *mouse_controller = nullptr;
|
||||
|
||||
ConfigCenter config_center;
|
||||
|
||||
char *nv12_buffer = nullptr;
|
||||
|
||||
#ifdef __linux__
|
||||
std::chrono::_V2::system_clock::time_point last_frame_time_;
|
||||
#else
|
||||
std::chrono::steady_clock::time_point last_frame_time_;
|
||||
#endif
|
||||
|
||||
inline int ProcessMouseKeyEven(SDL_Event &ev) {
|
||||
float ratio = (float)(1280.0 / window_w);
|
||||
|
||||
RemoteAction remote_action;
|
||||
remote_action.m.x = (size_t)(ev.button.x * ratio);
|
||||
remote_action.m.y = (size_t)(ev.button.y * ratio);
|
||||
|
||||
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
|
||||
{
|
||||
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
|
||||
if (SDLK_DOWN == ev.key.keysym.sym) {
|
||||
// printf("SDLK_DOWN \n");
|
||||
|
||||
} else if (SDLK_UP == ev.key.keysym.sym) {
|
||||
// printf("SDLK_UP \n");
|
||||
|
||||
} else if (SDLK_LEFT == ev.key.keysym.sym) {
|
||||
// printf("SDLK_LEFT \n");
|
||||
|
||||
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
|
||||
// printf("SDLK_RIGHT \n");
|
||||
}
|
||||
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_down;
|
||||
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_down;
|
||||
}
|
||||
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
} else if (SDL_MOUSEBUTTONUP == ev.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_up;
|
||||
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_up;
|
||||
}
|
||||
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
} else if (SDL_MOUSEMOTION == ev.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
} else if (SDL_QUIT == ev.type) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
printf("SDL_QUIT\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
||||
if (1) {
|
||||
if ("Connected" == connection_status_str) {
|
||||
SendData(peer_server, DATA_TYPE::AUDIO, (const char *)stream, len);
|
||||
}
|
||||
} else {
|
||||
memcpy(audio_buffer, stream, len);
|
||||
audio_len = len;
|
||||
SDL_Delay(10);
|
||||
audio_buffer_fresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
|
||||
if (!audio_buffer_fresh) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_memset(stream, 0, len);
|
||||
|
||||
if (audio_len == 0) {
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
|
||||
len = (len > audio_len ? audio_len : len);
|
||||
SDL_MixAudioFormat(stream, audio_buffer, AUDIO_S16LSB, len,
|
||||
SDL_MIX_MAXVOLUME);
|
||||
audio_buffer_fresh = false;
|
||||
}
|
||||
|
||||
void ServerReceiveVideoBuffer(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size) {
|
||||
if (joined) {
|
||||
memcpy(dst_buffer, data, size);
|
||||
|
||||
SDL_Event event;
|
||||
event.type = REFRESH_EVENT;
|
||||
SDL_PushEvent(&event);
|
||||
received_frame = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientReceiveVideoBuffer(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size) {
|
||||
// std::cout << "Receive: [" << user_id << "] " << std::endl;
|
||||
if (joined) {
|
||||
memcpy(dst_buffer, data, size);
|
||||
|
||||
SDL_Event event;
|
||||
event.type = REFRESH_EVENT;
|
||||
SDL_PushEvent(&event);
|
||||
received_frame = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ServerReceiveAudioBuffer(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size) {
|
||||
// memset(audio_buffer, 0, size);
|
||||
// memcpy(audio_buffer, data, size);
|
||||
// audio_len = size;
|
||||
audio_buffer_fresh = true;
|
||||
|
||||
SDL_QueueAudio(output_dev, data, (Uint32)size);
|
||||
// printf("queue audio\n");
|
||||
}
|
||||
|
||||
void ClientReceiveAudioBuffer(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size) {
|
||||
// std::cout << "Client receive audio, size " << size << ", user [" << user_id
|
||||
// << "] " << std::endl;
|
||||
SDL_QueueAudio(output_dev, data, (Uint32)size);
|
||||
}
|
||||
|
||||
void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id,
|
||||
size_t user_id_size) {
|
||||
std::string user(user_id, user_id_size);
|
||||
|
||||
RemoteAction remote_action;
|
||||
memcpy(&remote_action, data, sizeof(remote_action));
|
||||
|
||||
// std::cout << "remote_action: " << remote_action.type << " "
|
||||
// << remote_action.m.flag << " " << remote_action.m.x << " "
|
||||
// << remote_action.m.y << std::endl;
|
||||
#if MOUSE_CONTROL
|
||||
if (mouse_controller) {
|
||||
mouse_controller->SendCommand(remote_action);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClientReceiveDataBuffer(const char *data, size_t size, const char *user_id,
|
||||
size_t user_id_size) {}
|
||||
|
||||
void SignalStatus(SignalStatus status) {
|
||||
signal_status = status;
|
||||
if (SignalStatus::SignalConnecting == status) {
|
||||
signal_status_str = "SignalConnecting";
|
||||
} else if (SignalStatus::SignalConnected == status) {
|
||||
signal_status_str = "SignalConnected";
|
||||
} else if (SignalStatus::SignalFailed == status) {
|
||||
signal_status_str = "SignalFailed";
|
||||
} else if (SignalStatus::SignalClosed == status) {
|
||||
signal_status_str = "SignalClosed";
|
||||
} else if (SignalStatus::SignalReconnecting == status) {
|
||||
signal_status_str = "SignalReconnecting";
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionStatus(ConnectionStatus status) {
|
||||
connection_status = status;
|
||||
if (ConnectionStatus::Connecting == status) {
|
||||
connection_status_str = "Connecting";
|
||||
} else if (ConnectionStatus::Connected == status) {
|
||||
connection_status_str = "Connected";
|
||||
joined = true;
|
||||
} else if (ConnectionStatus::Disconnected == status) {
|
||||
connection_status_str = "Disconnected";
|
||||
} else if (ConnectionStatus::Failed == status) {
|
||||
connection_status_str = "Failed";
|
||||
} else if (ConnectionStatus::Closed == status) {
|
||||
connection_status_str = "Closed";
|
||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||
connection_status_str = "Incorrect password";
|
||||
if (connect_button_pressed) {
|
||||
connect_button_pressed = false;
|
||||
joined = false;
|
||||
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
||||
}
|
||||
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
|
||||
connection_status_str = "No such transmission id";
|
||||
if (connect_button_pressed) {
|
||||
connect_button_pressed = false;
|
||||
joined = false;
|
||||
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
LOG_INFO("Remote desk");
|
||||
|
||||
last_ts = static_cast<uint32_t>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count());
|
||||
|
||||
cd_cache_file = fopen("cache.cd", "r+");
|
||||
if (cd_cache_file) {
|
||||
fseek(cd_cache_file, 0, SEEK_SET);
|
||||
fread(&cd_cache.password, sizeof(cd_cache.password), 1, cd_cache_file);
|
||||
fclose(cd_cache_file);
|
||||
strncpy(input_password, cd_cache.password, sizeof(cd_cache.password));
|
||||
}
|
||||
|
||||
// Setup SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
|
||||
SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
printf("Error: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// From 2.0.18: Enable native IME.
|
||||
#ifdef SDL_HINT_IME_SHOW_UI
|
||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||
#endif
|
||||
|
||||
// Create window with SDL_Renderer graphics context
|
||||
SDL_WindowFlags window_flags =
|
||||
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
window = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED, window_w, window_h,
|
||||
window_flags);
|
||||
|
||||
SDL_DisplayMode DM;
|
||||
SDL_GetCurrentDisplayMode(0, &DM);
|
||||
screen_w = DM.w;
|
||||
screen_h = DM.h;
|
||||
|
||||
sdlRenderer = SDL_CreateRenderer(
|
||||
window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
||||
if (sdlRenderer == nullptr) {
|
||||
SDL_Log("Error creating SDL_Renderer!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Uint32 pixformat = 0;
|
||||
pixformat = SDL_PIXELFORMAT_NV12;
|
||||
|
||||
sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,
|
||||
SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
|
||||
|
||||
// Audio
|
||||
SDL_AudioSpec want_in, have_in, want_out, have_out;
|
||||
SDL_zero(want_in);
|
||||
want_in.freq = 48000;
|
||||
want_in.format = AUDIO_S16LSB;
|
||||
want_in.channels = 1;
|
||||
want_in.samples = 480;
|
||||
want_in.callback = SdlCaptureAudioIn;
|
||||
|
||||
input_dev = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0);
|
||||
if (input_dev == 0) {
|
||||
SDL_Log("Failed to open input: %s", SDL_GetError());
|
||||
// return 1;
|
||||
}
|
||||
|
||||
SDL_zero(want_out);
|
||||
want_out.freq = 48000;
|
||||
want_out.format = AUDIO_S16LSB;
|
||||
want_out.channels = 1;
|
||||
// want_out.silence = 0;
|
||||
want_out.samples = 480;
|
||||
want_out.callback = NULL;
|
||||
|
||||
output_dev = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out, 0);
|
||||
if (output_dev == 0) {
|
||||
SDL_Log("Failed to open input: %s", SDL_GetError());
|
||||
// return 1;
|
||||
}
|
||||
|
||||
SDL_PauseAudioDevice(input_dev, 0);
|
||||
SDL_PauseAudioDevice(output_dev, 0);
|
||||
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
io.ConfigFlags |=
|
||||
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
io.ConfigFlags |=
|
||||
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
|
||||
#if CHINESE_FONT
|
||||
// Load Fonts
|
||||
#ifdef _WIN32
|
||||
std::string default_font_path = "c:/windows/fonts/simhei.ttf";
|
||||
std::ifstream font_path_f(default_font_path.c_str());
|
||||
std::string font_path =
|
||||
font_path_f.good() ? "c:/windows/fonts/simhei.ttf" : "";
|
||||
if (!font_path.empty()) {
|
||||
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
|
||||
io.Fonts->GetGlyphRangesChineseFull());
|
||||
}
|
||||
#elif __APPLE__
|
||||
std::string default_font_path = "/System/Library/Fonts/PingFang.ttc";
|
||||
std::ifstream font_path_f(default_font_path.c_str());
|
||||
std::string font_path =
|
||||
font_path_f.good() ? "/System/Library/Fonts/PingFang.ttc" : "";
|
||||
if (!font_path.empty()) {
|
||||
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
|
||||
io.Fonts->GetGlyphRangesChineseFull());
|
||||
}
|
||||
#elif __linux__
|
||||
io.Fonts->AddFontFromFileTTF("c:/windows/fonts/msyh.ttc", 13.0f, NULL,
|
||||
io.Fonts->GetGlyphRangesChineseFull());
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Setup Dear ImGui style
|
||||
// ImGui::StyleColorsDark();
|
||||
ImGui::StyleColorsLight();
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(window, sdlRenderer);
|
||||
ImGui_ImplSDLRenderer2_Init(sdlRenderer);
|
||||
|
||||
// Our state
|
||||
bool show_demo_window = true;
|
||||
bool show_another_window = false;
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
|
||||
std::string mac_addr_str = GetMac();
|
||||
|
||||
std::thread rtc_thread(
|
||||
[](int screen_width, int screen_height) {
|
||||
std::string default_cfg_path = "../../../../config/config.ini";
|
||||
std::ifstream f(default_cfg_path.c_str());
|
||||
|
||||
std::string mac_addr_str = GetMac();
|
||||
|
||||
Params server_params;
|
||||
server_params.cfg_path =
|
||||
f.good() ? "../../../../config/config.ini" : "config.ini";
|
||||
server_params.on_receive_video_buffer = ServerReceiveVideoBuffer;
|
||||
server_params.on_receive_audio_buffer = ServerReceiveAudioBuffer;
|
||||
server_params.on_receive_data_buffer = ServerReceiveDataBuffer;
|
||||
server_params.on_signal_status = SignalStatus;
|
||||
server_params.on_connection_status = ConnectionStatus;
|
||||
|
||||
std::string transmission_id = "000001";
|
||||
|
||||
peer_server = CreatePeer(&server_params);
|
||||
LOG_INFO("Create peer_server");
|
||||
std::string server_user_id = "S-" + mac_addr_str;
|
||||
Init(peer_server, server_user_id.c_str());
|
||||
LOG_INFO("peer_server init finish");
|
||||
|
||||
{
|
||||
while (SignalStatus::SignalConnected != signal_status && !done) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string user_id = "S-" + mac_addr_str;
|
||||
is_create_connection =
|
||||
CreateConnection(peer_server, mac_addr_str.c_str(),
|
||||
input_password)
|
||||
? false
|
||||
: true;
|
||||
|
||||
nv12_buffer = new char[NV12_BUFFER_SIZE];
|
||||
|
||||
// Screen capture
|
||||
screen_capturer_factory = new ScreenCapturerFactory();
|
||||
screen_capturer = (ScreenCapturer *)screen_capturer_factory->Create();
|
||||
|
||||
last_frame_time_ = std::chrono::high_resolution_clock::now();
|
||||
ScreenCapturer::RECORD_DESKTOP_RECT rect;
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.right = screen_w;
|
||||
rect.bottom = screen_h;
|
||||
|
||||
int screen_capturer_init_ret = screen_capturer->Init(
|
||||
rect, 60,
|
||||
[](unsigned char *data, int size, int width, int height) -> void {
|
||||
auto now_time = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> duration =
|
||||
now_time - last_frame_time_;
|
||||
auto tc = duration.count() * 1000;
|
||||
|
||||
if (tc >= 0) {
|
||||
SendData(peer_server, DATA_TYPE::VIDEO, (const char *)data,
|
||||
NV12_BUFFER_SIZE);
|
||||
last_frame_time_ = now_time;
|
||||
}
|
||||
});
|
||||
|
||||
if (0 == screen_capturer_init_ret) {
|
||||
screen_capturer->Start();
|
||||
} else {
|
||||
screen_capturer->Destroy();
|
||||
screen_capturer = nullptr;
|
||||
}
|
||||
|
||||
// Mouse control
|
||||
device_controller_factory = new DeviceControllerFactory();
|
||||
mouse_controller =
|
||||
(MouseController *)device_controller_factory->Create(
|
||||
DeviceControllerFactory::Device::Mouse);
|
||||
int mouse_controller_init_ret =
|
||||
mouse_controller->Init(screen_w, screen_h);
|
||||
if (0 != mouse_controller_init_ret) {
|
||||
mouse_controller->Destroy();
|
||||
mouse_controller = nullptr;
|
||||
}
|
||||
}
|
||||
},
|
||||
screen_w, screen_h);
|
||||
|
||||
// Main loop
|
||||
while (!done) {
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplSDLRenderer2_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (joined && !menu_hovered) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
|
||||
}
|
||||
|
||||
{
|
||||
static float f = 0.0f;
|
||||
static int counter = 0;
|
||||
|
||||
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
|
||||
|
||||
// ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
|
||||
|
||||
#if CHINESE_FONT
|
||||
ImGui::SetNextWindowSize(ImVec2(160, 210));
|
||||
#else
|
||||
ImGui::SetNextWindowSize(ImVec2(180, 210));
|
||||
#endif
|
||||
|
||||
#if CHINESE_FONT
|
||||
if (!joined) {
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::Begin(u8"菜单", nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove);
|
||||
} else {
|
||||
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||
ImGui::Begin(u8"菜单", nullptr, ImGuiWindowFlags_None);
|
||||
}
|
||||
#else
|
||||
if (!joined) {
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::Begin("Menu", nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove);
|
||||
} else {
|
||||
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||
ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_None);
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
menu_hovered = ImGui::IsWindowHovered();
|
||||
#if CHINESE_FONT
|
||||
ImGui::Text(u8"本机ID:");
|
||||
#else
|
||||
ImGui::Text("LOCAL ID:");
|
||||
#endif
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(90);
|
||||
#if CHINESE_FONT
|
||||
ImGui::SetCursorPosX(60.0f);
|
||||
#else
|
||||
ImGui::SetCursorPosX(80.0f);
|
||||
#endif
|
||||
ImGui::InputText(
|
||||
"##local_id", (char *)mac_addr_str.c_str(),
|
||||
mac_addr_str.length() + 1,
|
||||
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
#if CHINESE_FONT
|
||||
ImGui::Text(u8"密码:");
|
||||
#else
|
||||
ImGui::Text("PASSWORD:");
|
||||
#endif
|
||||
ImGui::SameLine();
|
||||
char input_password_tmp[7] = "";
|
||||
std::string input_password_str = "123456";
|
||||
strncpy(input_password_tmp, input_password, sizeof(input_password));
|
||||
ImGui::SetNextItemWidth(90);
|
||||
#if CHINESE_FONT
|
||||
ImGui::SetCursorPosX(60.0f);
|
||||
ImGui::InputTextWithHint("##server_pwd", u8"最长6个字符",
|
||||
input_password, IM_ARRAYSIZE(input_password),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
#else
|
||||
ImGui::SetCursorPosX(80.0f);
|
||||
ImGui::InputTextWithHint("##server_pwd", "max 6 chars", input_password,
|
||||
IM_ARRAYSIZE(input_password),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
#endif
|
||||
if (strcmp(input_password_tmp, input_password)) {
|
||||
cd_cache_file = fopen("cache.cd", "w+");
|
||||
if (cd_cache_file) {
|
||||
fseek(cd_cache_file, 0, SEEK_SET);
|
||||
strncpy(cd_cache.password, input_password, sizeof(input_password));
|
||||
fwrite(&cd_cache.password, sizeof(cd_cache.password), 1,
|
||||
cd_cache_file);
|
||||
fclose(cd_cache_file);
|
||||
}
|
||||
LeaveConnection(peer_server);
|
||||
CreateConnection(peer_server, mac_addr_str.c_str(), input_password);
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
{
|
||||
{
|
||||
static char remote_id[20] = "";
|
||||
#if CHINESE_FONT
|
||||
ImGui::Text(u8"远端ID:");
|
||||
#else
|
||||
ImGui::Text("REMOTE ID:");
|
||||
#endif
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(90);
|
||||
#if CHINESE_FONT
|
||||
ImGui::SetCursorPosX(60.0f);
|
||||
#else
|
||||
ImGui::SetCursorPosX(80.0f);
|
||||
#endif
|
||||
ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(),
|
||||
remote_id, IM_ARRAYSIZE(remote_id),
|
||||
ImGuiInputTextFlags_CharsUppercase |
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
#if CHINESE_FONT
|
||||
ImGui::Text(u8"密码:");
|
||||
#else
|
||||
ImGui::Text("PASSWORD:");
|
||||
#endif
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(90);
|
||||
static char client_password[20] = "";
|
||||
#if CHINESE_FONT
|
||||
ImGui::SetCursorPosX(60.0f);
|
||||
ImGui::InputTextWithHint("##client_pwd", u8"最长6个字符",
|
||||
client_password,
|
||||
IM_ARRAYSIZE(client_password),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
#else
|
||||
ImGui::SetCursorPosX(80.0f);
|
||||
ImGui::InputTextWithHint("##client_pwd", "max 6 chars",
|
||||
client_password,
|
||||
IM_ARRAYSIZE(client_password),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
#endif
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button(connect_label)) {
|
||||
int ret = -1;
|
||||
if ("SignalConnected" == signal_status_str) {
|
||||
#if CHINESE_FONT
|
||||
if (strcmp(connect_label, u8"连接") == 0 && !joined) {
|
||||
#else
|
||||
if (strcmp(connect_label, "Connect") == 0 && !joined) {
|
||||
#endif
|
||||
std::string user_id = "C-" + mac_addr_str;
|
||||
ret = JoinConnection(peer_server, remote_id, client_password);
|
||||
if (0 == ret) {
|
||||
// joined = true;
|
||||
}
|
||||
#if CHINESE_FONT
|
||||
} else if (strcmp(connect_label, u8"断开连接") == 0 && joined) {
|
||||
#else
|
||||
} else if (strcmp(connect_label, "Disconnect") == 0 && joined) {
|
||||
#endif
|
||||
ret = LeaveConnection(peer_server);
|
||||
CreateConnection(peer_server, mac_addr_str.c_str(),
|
||||
input_password);
|
||||
memset(audio_buffer, 0, 960);
|
||||
if (0 == ret) {
|
||||
joined = false;
|
||||
received_frame = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == ret) {
|
||||
connect_button_pressed = !connect_button_pressed;
|
||||
#if CHINESE_FONT
|
||||
connect_label =
|
||||
connect_button_pressed ? u8"断开连接" : u8"连接";
|
||||
#else
|
||||
connect_label =
|
||||
connect_button_pressed ? "Disconnect" : "Connect";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
#if CHINESE_FONT
|
||||
if (ImGui::Button(fullscreen_label)) {
|
||||
if (strcmp(fullscreen_label, u8"全屏") == 0) {
|
||||
#else
|
||||
if (ImGui::Button(fullscreen_label)) {
|
||||
if (strcmp(fullscreen_label, "FULLSCREEN") == 0) {
|
||||
#endif
|
||||
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
} else {
|
||||
SDL_SetWindowFullscreen(window, SDL_FALSE);
|
||||
}
|
||||
fullscreen_button_pressed = !fullscreen_button_pressed;
|
||||
#if CHINESE_FONT
|
||||
fullscreen_label = fullscreen_button_pressed ? u8"退出全屏" : u8"全屏";
|
||||
#else
|
||||
fullscreen_label =
|
||||
fullscreen_button_pressed ? "EXIT FULLSCREEN" : "FULLSCREEN";
|
||||
#endif
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
SDL_RenderSetScale(sdlRenderer, io.DisplayFramebufferScale.x,
|
||||
io.DisplayFramebufferScale.y);
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
if (event.type == SDL_QUIT) {
|
||||
done = true;
|
||||
} else if (event.type == SDL_WINDOWEVENT &&
|
||||
event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
// SDL_GetWindowSize(window, &window_w, &window_h);
|
||||
|
||||
int window_w_last = window_w;
|
||||
int window_h_last = window_h;
|
||||
|
||||
SDL_GetWindowSize(window, &window_w, &window_h);
|
||||
|
||||
int w_change_ratio = abs(window_w - window_w_last) / 16;
|
||||
int h_change_ratio = abs(window_h - window_h_last) / 9;
|
||||
|
||||
if (w_change_ratio > h_change_ratio) {
|
||||
window_h = window_w * 9 / 16;
|
||||
} else {
|
||||
window_w = window_h * 16 / 9;
|
||||
}
|
||||
|
||||
SDL_SetWindowSize(window, window_w, window_h);
|
||||
} else if (event.type == SDL_WINDOWEVENT &&
|
||||
event.window.event == SDL_WINDOWEVENT_CLOSE &&
|
||||
event.window.windowID == SDL_GetWindowID(window)) {
|
||||
done = true;
|
||||
} else if (event.type == REFRESH_EVENT) {
|
||||
sdlRect.x = 0;
|
||||
sdlRect.y = 0;
|
||||
sdlRect.w = window_w;
|
||||
sdlRect.h = window_h;
|
||||
|
||||
SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w);
|
||||
} else {
|
||||
if (joined) {
|
||||
ProcessMouseKeyEven(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_RenderClear(sdlRenderer);
|
||||
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
|
||||
|
||||
if (!joined || !received_frame) {
|
||||
SDL_RenderClear(sdlRenderer);
|
||||
SDL_SetRenderDrawColor(
|
||||
sdlRenderer, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
|
||||
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
|
||||
}
|
||||
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
|
||||
SDL_RenderPresent(sdlRenderer);
|
||||
|
||||
frame_count++;
|
||||
end_time = SDL_GetTicks();
|
||||
elapsed_time = end_time - start_time;
|
||||
if (elapsed_time >= 1000) {
|
||||
fps = frame_count / (elapsed_time / 1000);
|
||||
frame_count = 0;
|
||||
window_title = "Remote Desk Client FPS [" + std::to_string(fps) +
|
||||
"] status [" + signal_status_str + "|" +
|
||||
connection_status_str + "]";
|
||||
// For MacOS, UI frameworks can only be called from the main thread
|
||||
SDL_SetWindowTitle(window, window_title.c_str());
|
||||
start_time = end_time;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
|
||||
if (is_create_connection) {
|
||||
LeaveConnection(peer_server);
|
||||
}
|
||||
|
||||
rtc_thread.join();
|
||||
SDL_CloseAudioDevice(output_dev);
|
||||
SDL_CloseAudioDevice(input_dev);
|
||||
|
||||
if (screen_capturer) {
|
||||
screen_capturer->Destroy();
|
||||
}
|
||||
|
||||
if (mouse_controller) {
|
||||
mouse_controller->Destroy();
|
||||
}
|
||||
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyRenderer(sdlRenderer);
|
||||
SDL_DestroyWindow(window);
|
||||
|
||||
SDL_CloseAudio();
|
||||
SDL_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-05-29
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
@@ -10,19 +10,26 @@
|
||||
#include <vector>
|
||||
namespace localization {
|
||||
|
||||
static std::vector<std::string> menu = {u8"菜单", "Menu"};
|
||||
static std::vector<std::string> local_id = {u8"本机ID:", "Local ID:"};
|
||||
static std::vector<std::string> password = {u8"密码:", "Password:"};
|
||||
static std::vector<std::string> 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_id = {u8"对端ID:", "Remote ID:"};
|
||||
|
||||
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"控制鼠标", "Mouse Control"};
|
||||
static std::vector<std::string> release_mouse = {u8"释放鼠标", "Release Mouse"};
|
||||
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"};
|
||||
@@ -38,10 +45,41 @@ 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
|
||||
@@ -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)) { \
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-06-14
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LAYOUT_STYLE_H_
|
||||
#define _LAYOUT_STYLE_H_
|
||||
|
||||
#ifdef _WIN32
|
||||
#define MENU_WINDOW_WIDTH_CN 160
|
||||
#define MENU_WINDOW_HEIGHT_CN 245
|
||||
#define MENU_WINDOW_WIDTH_EN 190
|
||||
#define MENU_WINDOW_HEIGHT_EN 245
|
||||
#define IPUT_WINDOW_WIDTH 86
|
||||
#define INPUT_WINDOW_PADDING_CN 66
|
||||
#define INPUT_WINDOW_PADDING_EN 96
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 181
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 228
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 190
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 190
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 147
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 147
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 147
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 154
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 73
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 55
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 78
|
||||
#elif __linux__
|
||||
#define MENU_WINDOW_WIDTH_CN 160
|
||||
#define MENU_WINDOW_HEIGHT_CN 245
|
||||
#define MENU_WINDOW_WIDTH_EN 190
|
||||
#define MENU_WINDOW_HEIGHT_EN 245
|
||||
#define IPUT_WINDOW_WIDTH 90
|
||||
#define INPUT_WINDOW_PADDING_CN 60
|
||||
#define INPUT_WINDOW_PADDING_EN 80
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 188
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 228
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 190
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 190
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 140
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 140
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 140
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 161
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 60
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 60
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 80
|
||||
#elif __APPLE__
|
||||
#define MENU_WINDOW_WIDTH_CN 148
|
||||
#define MENU_WINDOW_HEIGHT_CN 244
|
||||
#define MENU_WINDOW_WIDTH_EN 148
|
||||
#define MENU_WINDOW_HEIGHT_EN 244
|
||||
#define IPUT_WINDOW_WIDTH 77
|
||||
#define INPUT_WINDOW_PADDING_CN 63
|
||||
#define INPUT_WINDOW_PADDING_EN 63
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 160
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 220
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 190
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 190
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 90
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 150
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 90
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 150
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 90
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 150
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_CN 133
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_EN 193
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 62
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 50
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 80
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,888 +0,0 @@
|
||||
#include "main_window.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "device_controller_factory.h"
|
||||
#include "layout_style.h"
|
||||
#include "localization.h"
|
||||
#include "log.h"
|
||||
#include "platform.h"
|
||||
#include "screen_capturer_factory.h"
|
||||
|
||||
// Refresh Event
|
||||
#define REFRESH_EVENT (SDL_USEREVENT + 1)
|
||||
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
||||
|
||||
MainWindow::MainWindow() {}
|
||||
|
||||
MainWindow::~MainWindow() {}
|
||||
|
||||
int MainWindow::SaveSettingsIntoCacheFile() {
|
||||
cd_cache_file_ = fopen("cache.cd", "w+");
|
||||
if (!cd_cache_file_) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fseek(cd_cache_file_, 0, SEEK_SET);
|
||||
strncpy(cd_cache_.password, input_password_, sizeof(input_password_));
|
||||
memcpy(&cd_cache_.language, &language_button_value_,
|
||||
sizeof(language_button_value_));
|
||||
memcpy(&cd_cache_.video_quality, &video_quality_button_value_,
|
||||
sizeof(video_quality_button_value_));
|
||||
memcpy(&cd_cache_.video_encode_format, &video_encode_format_button_value_,
|
||||
sizeof(video_encode_format_button_value_));
|
||||
memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_,
|
||||
sizeof(enable_hardware_video_codec_));
|
||||
fwrite(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
|
||||
fclose(cd_cache_file_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MainWindow::LoadSettingsIntoCacheFile() {
|
||||
cd_cache_file_ = fopen("cache.cd", "r+");
|
||||
if (!cd_cache_file_) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fseek(cd_cache_file_, 0, SEEK_SET);
|
||||
fread(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
|
||||
fclose(cd_cache_file_);
|
||||
strncpy(input_password_, cd_cache_.password, sizeof(cd_cache_.password));
|
||||
language_button_value_ = cd_cache_.language;
|
||||
video_quality_button_value_ = cd_cache_.video_quality;
|
||||
video_encode_format_button_value_ = cd_cache_.video_encode_format;
|
||||
enable_hardware_video_codec_ = cd_cache_.enable_hardware_video_codec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MainWindow::StartScreenCapture() {
|
||||
screen_capturer_ = (ScreenCapturer *)screen_capturer_factory_->Create();
|
||||
ScreenCapturer::RECORD_DESKTOP_RECT rect;
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.right = screen_width_;
|
||||
rect.bottom = screen_height_;
|
||||
last_frame_time_ = std::chrono::high_resolution_clock::now();
|
||||
|
||||
int screen_capturer_init_ret = screen_capturer_->Init(
|
||||
rect, 60,
|
||||
[this](unsigned char *data, int size, int width, int height) -> void {
|
||||
auto now_time = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> duration = now_time - last_frame_time_;
|
||||
auto tc = duration.count() * 1000;
|
||||
|
||||
if (tc >= 0) {
|
||||
SendData(peer_, DATA_TYPE::VIDEO, (const char *)data,
|
||||
NV12_BUFFER_SIZE);
|
||||
last_frame_time_ = now_time;
|
||||
}
|
||||
});
|
||||
|
||||
if (0 == screen_capturer_init_ret) {
|
||||
screen_capturer_->Start();
|
||||
} else {
|
||||
screen_capturer_->Destroy();
|
||||
delete screen_capturer_;
|
||||
screen_capturer_ = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MainWindow::StopScreenCapture() {
|
||||
if (screen_capturer_) {
|
||||
LOG_INFO("Destroy screen capturer")
|
||||
screen_capturer_->Destroy();
|
||||
delete screen_capturer_;
|
||||
screen_capturer_ = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MainWindow::StartMouseControl() {
|
||||
device_controller_factory_ = new DeviceControllerFactory();
|
||||
mouse_controller_ = (MouseController *)device_controller_factory_->Create(
|
||||
DeviceControllerFactory::Device::Mouse);
|
||||
int mouse_controller_init_ret =
|
||||
mouse_controller_->Init(screen_width_, screen_height_);
|
||||
if (0 != mouse_controller_init_ret) {
|
||||
LOG_INFO("Destroy mouse controller")
|
||||
mouse_controller_->Destroy();
|
||||
mouse_controller_ = nullptr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MainWindow::StopMouseControl() {
|
||||
if (mouse_controller_) {
|
||||
mouse_controller_->Destroy();
|
||||
delete mouse_controller_;
|
||||
mouse_controller_ = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MainWindow::CreateConnectionPeer() {
|
||||
mac_addr_str_ = GetMac();
|
||||
|
||||
params_.use_cfg_file = false;
|
||||
params_.signal_server_ip = "150.158.81.30";
|
||||
params_.signal_server_port = 9099;
|
||||
params_.stun_server_ip = "150.158.81.30";
|
||||
params_.stun_server_port = 3478;
|
||||
params_.turn_server_ip = "150.158.81.30";
|
||||
params_.turn_server_port = 3478;
|
||||
params_.turn_server_username = "dijunkun";
|
||||
params_.turn_server_password = "dijunkunpw";
|
||||
params_.hardware_acceleration = config_center_.IsHardwareVideoCodec();
|
||||
params_.av1_encoding = config_center_.GetVideoEncodeFormat() ==
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1
|
||||
? true
|
||||
: false;
|
||||
params_.on_receive_video_buffer = OnReceiveVideoBufferCb;
|
||||
params_.on_receive_audio_buffer = OnReceiveAudioBufferCb;
|
||||
params_.on_receive_data_buffer = OnReceiveDataBufferCb;
|
||||
params_.on_signal_status = OnSignalStatusCb;
|
||||
params_.on_connection_status = OnConnectionStatusCb;
|
||||
params_.user_data = this;
|
||||
|
||||
peer_ = CreatePeer(¶ms_);
|
||||
if (peer_) {
|
||||
LOG_INFO("Create peer instance successful");
|
||||
local_id_ = mac_addr_str_;
|
||||
Init(peer_, local_id_.c_str());
|
||||
LOG_INFO("Peer init finish");
|
||||
} else {
|
||||
LOG_INFO("Create peer instance failed");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MainWindow::Run() {
|
||||
LoadSettingsIntoCacheFile();
|
||||
|
||||
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
|
||||
localization_language_index_ = language_button_value_;
|
||||
|
||||
// Setup SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
|
||||
SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
printf("Error: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// From 2.0.18: Enable native IME.
|
||||
#ifdef SDL_HINT_IME_SHOW_UI
|
||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||
#endif
|
||||
|
||||
// Create main window with SDL_Renderer graphics context
|
||||
SDL_WindowFlags window_flags =
|
||||
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
main_window_ = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED, main_window_width_,
|
||||
main_window_height_, window_flags);
|
||||
|
||||
SDL_DisplayMode DM;
|
||||
SDL_GetCurrentDisplayMode(0, &DM);
|
||||
screen_width_ = DM.w;
|
||||
screen_height_ = DM.h;
|
||||
|
||||
sdl_renderer_ = SDL_CreateRenderer(
|
||||
main_window_, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
||||
if (sdl_renderer_ == nullptr) {
|
||||
SDL_Log("Error creating SDL_Renderer!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pixformat_ = SDL_PIXELFORMAT_NV12;
|
||||
|
||||
sdl_texture_ =
|
||||
SDL_CreateTexture(sdl_renderer_, pixformat_, SDL_TEXTUREACCESS_STREAMING,
|
||||
texture_width_, texture_height_);
|
||||
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
io.ConfigFlags |=
|
||||
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
io.ConfigFlags |=
|
||||
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
|
||||
if (config_center_.GetLanguage() == ConfigCenter::LANGUAGE::CHINESE) {
|
||||
// Load Fonts
|
||||
#ifdef _WIN32
|
||||
std::string default_font_path = "c:/windows/fonts/simhei.ttf";
|
||||
std::ifstream font_path_f(default_font_path.c_str());
|
||||
std::string font_path =
|
||||
font_path_f.good() ? "c:/windows/fonts/simhei.ttf" : "";
|
||||
if (!font_path.empty()) {
|
||||
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
|
||||
io.Fonts->GetGlyphRangesChineseFull());
|
||||
}
|
||||
#elif __APPLE__
|
||||
std::string default_font_path = "/System/Library/Fonts/PingFang.ttc";
|
||||
std::ifstream font_path_f(default_font_path.c_str());
|
||||
std::string font_path =
|
||||
font_path_f.good() ? "/System/Library/Fonts/PingFang.ttc" : "";
|
||||
if (!font_path.empty()) {
|
||||
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
|
||||
io.Fonts->GetGlyphRangesChineseFull());
|
||||
}
|
||||
#elif __linux__
|
||||
io.Fonts->AddFontFromFileTTF("c:/windows/fonts/msyh.ttc", 13.0f, NULL,
|
||||
io.Fonts->GetGlyphRangesChineseFull());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Setup Dear ImGui style
|
||||
// ImGui::StyleColorsDark();
|
||||
ImGui::StyleColorsLight();
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(main_window_, sdl_renderer_);
|
||||
ImGui_ImplSDLRenderer2_Init(sdl_renderer_);
|
||||
|
||||
// Our state
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
|
||||
CreateConnectionPeer();
|
||||
|
||||
{
|
||||
nv12_buffer_ = new char[NV12_BUFFER_SIZE];
|
||||
|
||||
// Screen capture
|
||||
screen_capturer_factory_ = new ScreenCapturerFactory();
|
||||
|
||||
// Mouse control
|
||||
device_controller_factory_ = new DeviceControllerFactory();
|
||||
}
|
||||
|
||||
// Main loop
|
||||
while (!exit_) {
|
||||
if (SignalStatus::SignalConnected == signal_status_ &&
|
||||
!is_create_connection_) {
|
||||
is_create_connection_ =
|
||||
CreateConnection(peer_, mac_addr_str_.c_str(), input_password_)
|
||||
? false
|
||||
: true;
|
||||
LOG_INFO("Connected with signal server, create p2p connection");
|
||||
}
|
||||
|
||||
if (!inited_ ||
|
||||
localization_language_index_last_ != localization_language_index_) {
|
||||
connect_button_label_ =
|
||||
connect_button_pressed_
|
||||
? localization::disconnect[localization_language_index_]
|
||||
: localization::connect[localization_language_index_];
|
||||
fullscreen_button_label_ =
|
||||
fullscreen_button_pressed_
|
||||
? localization::exit_fullscreen[localization_language_index_]
|
||||
: localization::fullscreen[localization_language_index_];
|
||||
|
||||
mouse_control_button_label_ =
|
||||
mouse_control_button_pressed_
|
||||
? localization::release_mouse[localization_language_index_]
|
||||
: localization::control_mouse[localization_language_index_];
|
||||
|
||||
settings_button_label_ =
|
||||
localization::settings[localization_language_index_];
|
||||
inited_ = true;
|
||||
localization_language_index_last_ = localization_language_index_;
|
||||
}
|
||||
|
||||
if (start_screen_capture_ && !screen_capture_is_started_) {
|
||||
StartScreenCapture();
|
||||
screen_capture_is_started_ = true;
|
||||
} else if (!start_screen_capture_ && screen_capture_is_started_) {
|
||||
StopScreenCapture();
|
||||
screen_capture_is_started_ = false;
|
||||
}
|
||||
|
||||
if (start_mouse_control_ && !mouse_control_is_started_) {
|
||||
StartMouseControl();
|
||||
mouse_control_is_started_ = true;
|
||||
} else if (!start_mouse_control_ && mouse_control_is_started_) {
|
||||
StopMouseControl();
|
||||
mouse_control_is_started_ = false;
|
||||
}
|
||||
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplSDLRenderer2_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (connection_established_ && !subwindow_hovered_ && control_mouse_) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
|
||||
}
|
||||
|
||||
// main window layout
|
||||
{
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(MENU_WINDOW_WIDTH_CN, MENU_WINDOW_HEIGHT_CN));
|
||||
} else {
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(MENU_WINDOW_WIDTH_EN, MENU_WINDOW_HEIGHT_EN));
|
||||
}
|
||||
|
||||
if (!connection_established_) {
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::Begin(localization::menu[localization_language_index_].c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove);
|
||||
} else {
|
||||
// ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||
ImGui::Begin(localization::menu[localization_language_index_].c_str(),
|
||||
nullptr, ImGuiWindowFlags_None);
|
||||
}
|
||||
|
||||
{
|
||||
subwindow_hovered_ = ImGui::IsWindowHovered();
|
||||
|
||||
// local
|
||||
{
|
||||
ImGui::Text(
|
||||
localization::local_id[localization_language_index_].c_str());
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::InputText("##local_id", (char *)mac_addr_str_.c_str(),
|
||||
mac_addr_str_.length() + 1,
|
||||
ImGuiInputTextFlags_CharsUppercase |
|
||||
ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::Text(
|
||||
localization::password[localization_language_index_].c_str());
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
strncpy(input_password_tmp_, input_password_,
|
||||
sizeof(input_password_));
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::InputTextWithHint(
|
||||
"##server_pwd",
|
||||
localization::max_password_len[localization_language_index_]
|
||||
.c_str(),
|
||||
input_password_, IM_ARRAYSIZE(input_password_),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
if (strcmp(input_password_tmp_, input_password_)) {
|
||||
SaveSettingsIntoCacheFile();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
// remote
|
||||
{
|
||||
ImGui::Text(
|
||||
localization::remote_id[localization_language_index_].c_str());
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::InputTextWithHint("##remote_id_", mac_addr_str_.c_str(),
|
||||
remote_id_, IM_ARRAYSIZE(remote_id_),
|
||||
ImGuiInputTextFlags_CharsUppercase |
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Text(
|
||||
localization::password[localization_language_index_].c_str());
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH);
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(INPUT_WINDOW_PADDING_EN);
|
||||
}
|
||||
|
||||
ImGui::InputTextWithHint(
|
||||
"##client_pwd",
|
||||
localization::max_password_len[localization_language_index_]
|
||||
.c_str(),
|
||||
client_password_, IM_ARRAYSIZE(client_password_),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button(connect_button_label_.c_str()) || rejoin_) {
|
||||
int ret = -1;
|
||||
if ("SignalConnected" == signal_status_str_) {
|
||||
if (connect_button_label_ ==
|
||||
localization::connect[localization_language_index_] &&
|
||||
!connection_established_ && strlen(remote_id_)) {
|
||||
if (remote_id_ == local_id_ && !peer_reserved_) {
|
||||
peer_reserved_ = CreatePeer(¶ms_);
|
||||
if (peer_reserved_) {
|
||||
LOG_INFO("Create peer[reserved] instance successful");
|
||||
std::string local_id = "C-" + mac_addr_str_;
|
||||
Init(peer_reserved_, local_id.c_str());
|
||||
LOG_INFO("Peer[reserved] init finish");
|
||||
} else {
|
||||
LOG_INFO("Create peer[reserved] instance failed");
|
||||
}
|
||||
}
|
||||
ret = JoinConnection(peer_reserved_ ? peer_reserved_ : peer_,
|
||||
remote_id_, client_password_);
|
||||
if (0 == ret) {
|
||||
if (!peer_reserved_) {
|
||||
is_client_mode_ = true;
|
||||
}
|
||||
rejoin_ = false;
|
||||
} else {
|
||||
rejoin_ = true;
|
||||
}
|
||||
|
||||
} else if (connect_button_label_ ==
|
||||
localization::disconnect
|
||||
[localization_language_index_] &&
|
||||
connection_established_) {
|
||||
ret = LeaveConnection(peer_reserved_ ? peer_reserved_ : peer_);
|
||||
|
||||
if (0 == ret) {
|
||||
rejoin_ = false;
|
||||
memset(audio_buffer_, 0, 960);
|
||||
connection_established_ = false;
|
||||
received_frame_ = false;
|
||||
is_client_mode_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == ret) {
|
||||
connect_button_pressed_ = !connect_button_pressed_;
|
||||
connect_button_label_ =
|
||||
connect_button_pressed_
|
||||
? localization::disconnect[localization_language_index_]
|
||||
: localization::connect[localization_language_index_];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
// Mouse control
|
||||
if (ImGui::Button(mouse_control_button_label_.c_str())) {
|
||||
if (mouse_control_button_label_ ==
|
||||
localization::control_mouse[localization_language_index_] &&
|
||||
connection_established_) {
|
||||
mouse_control_button_pressed_ = true;
|
||||
control_mouse_ = true;
|
||||
mouse_control_button_label_ =
|
||||
localization::release_mouse[localization_language_index_];
|
||||
} else {
|
||||
control_mouse_ = false;
|
||||
mouse_control_button_label_ =
|
||||
localization::control_mouse[localization_language_index_];
|
||||
}
|
||||
mouse_control_button_pressed_ = !mouse_control_button_pressed_;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// Fullscreen
|
||||
if (ImGui::Button(fullscreen_button_label_.c_str())) {
|
||||
if (fullscreen_button_label_ ==
|
||||
localization::fullscreen[localization_language_index_]) {
|
||||
main_window_width_before_fullscreen_ = main_window_width_;
|
||||
main_window_height_before_fullscreen_ = main_window_height_;
|
||||
SDL_SetWindowFullscreen(main_window_, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||
fullscreen_button_label_ =
|
||||
localization::exit_fullscreen[localization_language_index_];
|
||||
} else {
|
||||
SDL_SetWindowFullscreen(main_window_, SDL_FALSE);
|
||||
SDL_SetWindowSize(main_window_, main_window_width_before_fullscreen_,
|
||||
main_window_height_before_fullscreen_);
|
||||
main_window_width_ = main_window_width_before_fullscreen_;
|
||||
main_window_height_ = main_window_height_before_fullscreen_;
|
||||
fullscreen_button_label_ =
|
||||
localization::fullscreen[localization_language_index_];
|
||||
}
|
||||
fullscreen_button_pressed_ = !fullscreen_button_pressed_;
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ImGui::Button(settings_button_label_.c_str())) {
|
||||
settings_button_pressed_ = !settings_button_pressed_;
|
||||
settings_window_pos_reset_ = true;
|
||||
}
|
||||
|
||||
if (settings_button_pressed_) {
|
||||
if (settings_window_pos_reset_) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
SETTINGS_WINDOW_WIDTH_CN) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
SETTINGS_WINDOW_HEIGHT_CN) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(SETTINGS_WINDOW_WIDTH_CN, SETTINGS_WINDOW_HEIGHT_CN));
|
||||
} else {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
SETTINGS_WINDOW_WIDTH_EN) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
SETTINGS_WINDOW_HEIGHT_EN) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(SETTINGS_WINDOW_WIDTH_EN, SETTINGS_WINDOW_HEIGHT_EN));
|
||||
}
|
||||
|
||||
settings_window_pos_reset_ = false;
|
||||
}
|
||||
|
||||
// Settings
|
||||
ImGui::Begin(
|
||||
localization::settings[localization_language_index_].c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
|
||||
{
|
||||
subwindow_hovered_ = ImGui::IsWindowHovered();
|
||||
|
||||
const char *language_items[] = {
|
||||
localization::language_zh[localization_language_index_].c_str(),
|
||||
localization::language_en[localization_language_index_].c_str()};
|
||||
|
||||
ImGui::SetCursorPosY(32);
|
||||
ImGui::Text(
|
||||
localization::language[localization_language_index_].c_str());
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(30);
|
||||
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::Combo("##language", &language_button_value_, language_items,
|
||||
IM_ARRAYSIZE(language_items));
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
const char *video_quality_items[] = {
|
||||
localization::video_quality_high[localization_language_index_]
|
||||
.c_str(),
|
||||
localization::video_quality_medium[localization_language_index_]
|
||||
.c_str(),
|
||||
localization::video_quality_low[localization_language_index_]
|
||||
.c_str()};
|
||||
|
||||
ImGui::SetCursorPosY(62);
|
||||
ImGui::Text(localization::video_quality[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(60);
|
||||
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::Combo("##video_quality", &video_quality_button_value_,
|
||||
video_quality_items, IM_ARRAYSIZE(video_quality_items));
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
const char *video_encode_format_items[] = {
|
||||
localization::av1[localization_language_index_].c_str(),
|
||||
localization::h264[localization_language_index_].c_str()};
|
||||
|
||||
ImGui::SetCursorPosY(92);
|
||||
ImGui::Text(
|
||||
localization::video_encode_format[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(90);
|
||||
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
|
||||
|
||||
ImGui::Combo("##video_encode_format",
|
||||
&video_encode_format_button_value_,
|
||||
video_encode_format_items,
|
||||
IM_ARRAYSIZE(video_encode_format_items));
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
ImGui::SetCursorPosY(122);
|
||||
ImGui::Text(localization::enable_hardware_video_codec
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(
|
||||
ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(
|
||||
ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(120);
|
||||
ImGui::Checkbox("##enable_hardware_video_codec",
|
||||
&enable_hardware_video_codec_);
|
||||
}
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(160.0f);
|
||||
|
||||
// OK
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str())) {
|
||||
settings_button_pressed_ = false;
|
||||
|
||||
// Language
|
||||
if (language_button_value_ == 0) {
|
||||
config_center_.SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
|
||||
} else {
|
||||
config_center_.SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
|
||||
}
|
||||
language_button_value_last_ = language_button_value_;
|
||||
localization_language_ =
|
||||
(ConfigCenter::LANGUAGE)language_button_value_;
|
||||
localization_language_index_ = language_button_value_;
|
||||
LOG_INFO("Set localization language: {}",
|
||||
localization_language_index_ == 0 ? "zh" : "en");
|
||||
|
||||
// Video quality
|
||||
if (video_quality_button_value_ == 0) {
|
||||
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
|
||||
} else if (video_quality_button_value_ == 1) {
|
||||
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
|
||||
} else {
|
||||
config_center_.SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
|
||||
}
|
||||
video_quality_button_value_last_ = video_quality_button_value_;
|
||||
|
||||
// Video encode format
|
||||
if (video_encode_format_button_value_ == 0) {
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
|
||||
} else if (video_encode_format_button_value_ == 1) {
|
||||
config_center_.SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::H264);
|
||||
}
|
||||
video_encode_format_button_value_last_ =
|
||||
video_encode_format_button_value_;
|
||||
|
||||
// Hardware video codec
|
||||
if (enable_hardware_video_codec_) {
|
||||
config_center_.SetHardwareVideoCodec(true);
|
||||
} else {
|
||||
config_center_.SetHardwareVideoCodec(false);
|
||||
}
|
||||
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
|
||||
|
||||
SaveSettingsIntoCacheFile();
|
||||
settings_window_pos_reset_ = true;
|
||||
|
||||
// Recreate peer instance
|
||||
LoadSettingsIntoCacheFile();
|
||||
|
||||
// Recreate peer instance
|
||||
{
|
||||
DestroyPeer(peer_);
|
||||
CreateConnectionPeer();
|
||||
LOG_INFO("Recreate peer instance successful");
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
// Cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
settings_button_pressed_ = false;
|
||||
if (language_button_value_ != language_button_value_last_) {
|
||||
language_button_value_ = language_button_value_last_;
|
||||
}
|
||||
|
||||
if (video_quality_button_value_ != video_quality_button_value_last_) {
|
||||
video_quality_button_value_ = video_quality_button_value_last_;
|
||||
}
|
||||
|
||||
if (video_encode_format_button_value_ !=
|
||||
video_encode_format_button_value_last_) {
|
||||
video_encode_format_button_value_ =
|
||||
video_encode_format_button_value_last_;
|
||||
}
|
||||
|
||||
if (enable_hardware_video_codec_ !=
|
||||
enable_hardware_video_codec_last_) {
|
||||
enable_hardware_video_codec_ = enable_hardware_video_codec_last_;
|
||||
}
|
||||
|
||||
settings_window_pos_reset_ = true;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
SDL_RenderSetScale(sdl_renderer_, io.DisplayFramebufferScale.x,
|
||||
io.DisplayFramebufferScale.y);
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
if (event.type == SDL_QUIT) {
|
||||
exit_ = true;
|
||||
} else if (event.type == SDL_WINDOWEVENT &&
|
||||
event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
int window_w_last = main_window_width_;
|
||||
int window_h_last = main_window_height_;
|
||||
|
||||
SDL_GetWindowSize(main_window_, &main_window_width_,
|
||||
&main_window_height_);
|
||||
|
||||
int w_change_ratio = abs(main_window_width_ - window_w_last) / 16;
|
||||
int h_change_ratio = abs(main_window_height_ - window_h_last) / 9;
|
||||
|
||||
if (w_change_ratio > h_change_ratio) {
|
||||
main_window_height_ = main_window_width_ * 9 / 16;
|
||||
} else {
|
||||
main_window_width_ = main_window_height_ * 16 / 9;
|
||||
}
|
||||
|
||||
SDL_SetWindowSize(main_window_, main_window_width_,
|
||||
main_window_height_);
|
||||
} else if (event.type == SDL_WINDOWEVENT &&
|
||||
event.window.event == SDL_WINDOWEVENT_CLOSE &&
|
||||
event.window.windowID == SDL_GetWindowID(main_window_)) {
|
||||
exit_ = true;
|
||||
} else if (event.type == REFRESH_EVENT) {
|
||||
sdl_rect_.x = 0;
|
||||
sdl_rect_.y = 0;
|
||||
sdl_rect_.w = main_window_width_;
|
||||
sdl_rect_.h = main_window_height_;
|
||||
|
||||
SDL_UpdateTexture(sdl_texture_, NULL, dst_buffer_, 1280);
|
||||
} else {
|
||||
if (connection_established_) {
|
||||
ProcessMouseKeyEven(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_RenderClear(sdl_renderer_);
|
||||
SDL_RenderCopy(sdl_renderer_, sdl_texture_, NULL, &sdl_rect_);
|
||||
|
||||
if (!connection_established_ || !received_frame_) {
|
||||
SDL_RenderClear(sdl_renderer_);
|
||||
SDL_SetRenderDrawColor(
|
||||
sdl_renderer_, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
|
||||
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
|
||||
}
|
||||
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
|
||||
SDL_RenderPresent(sdl_renderer_);
|
||||
|
||||
frame_count_++;
|
||||
end_time_ = SDL_GetTicks();
|
||||
elapsed_time_ = end_time_ - start_time_;
|
||||
if (elapsed_time_ >= 1000) {
|
||||
fps_ = frame_count_ / (elapsed_time_ / 1000);
|
||||
frame_count_ = 0;
|
||||
window_title = "Remote Desk Client FPS [" + std::to_string(fps_) +
|
||||
"] status [" + connection_status_str_ + "|" +
|
||||
connection_status_str_ + "]";
|
||||
// For MacOS, UI frameworks can only be called from the main thread
|
||||
SDL_SetWindowTitle(main_window_, window_title.c_str());
|
||||
start_time_ = end_time_;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (is_create_connection_) {
|
||||
LeaveConnection(peer_);
|
||||
is_client_mode_ = false;
|
||||
}
|
||||
|
||||
if (peer_) {
|
||||
DestroyPeer(peer_);
|
||||
}
|
||||
|
||||
if (peer_reserved_) {
|
||||
DestroyPeer(peer_reserved_);
|
||||
}
|
||||
|
||||
SDL_CloseAudioDevice(output_dev_);
|
||||
SDL_CloseAudioDevice(input_dev_);
|
||||
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyRenderer(sdl_renderer_);
|
||||
SDL_DestroyWindow(main_window_);
|
||||
|
||||
SDL_CloseAudio();
|
||||
SDL_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-05-29
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _MAIN_WINDOW_H_
|
||||
#define _MAIN_WINDOW_H_
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include "../../thirdparty/projectx/src/interface/x.h"
|
||||
#include "config_center.h"
|
||||
#include "device_controller_factory.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl2.h"
|
||||
#include "imgui_impl_sdlrenderer2.h"
|
||||
#include "screen_capturer_factory.h"
|
||||
|
||||
class MainWindow {
|
||||
public:
|
||||
MainWindow();
|
||||
~MainWindow();
|
||||
|
||||
public:
|
||||
int Run();
|
||||
|
||||
public:
|
||||
static void OnReceiveVideoBufferCb(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data);
|
||||
|
||||
static void OnReceiveAudioBufferCb(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data);
|
||||
|
||||
static void OnReceiveDataBufferCb(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data);
|
||||
|
||||
static void OnSignalStatusCb(SignalStatus status, void *user_data);
|
||||
|
||||
static void OnConnectionStatusCb(ConnectionStatus status, void *user_data);
|
||||
|
||||
private:
|
||||
int ProcessMouseKeyEven(SDL_Event &ev);
|
||||
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len);
|
||||
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len);
|
||||
|
||||
private:
|
||||
int SaveSettingsIntoCacheFile();
|
||||
int LoadSettingsIntoCacheFile();
|
||||
|
||||
int StartScreenCapture();
|
||||
int StopScreenCapture();
|
||||
|
||||
int StartMouseControl();
|
||||
int StopMouseControl();
|
||||
|
||||
int CreateConnectionPeer();
|
||||
|
||||
private:
|
||||
typedef struct {
|
||||
char password[7];
|
||||
int language;
|
||||
int video_quality;
|
||||
int video_encode_format;
|
||||
bool enable_hardware_video_codec;
|
||||
} CDCache;
|
||||
|
||||
private:
|
||||
FILE *cd_cache_file_ = nullptr;
|
||||
CDCache cd_cache_;
|
||||
|
||||
ConfigCenter config_center_;
|
||||
|
||||
ConfigCenter::LANGUAGE localization_language_ =
|
||||
ConfigCenter::LANGUAGE::CHINESE;
|
||||
|
||||
int localization_language_index_ = -1;
|
||||
int localization_language_index_last_ = -1;
|
||||
|
||||
private:
|
||||
std::string window_title = "Remote Desk Client";
|
||||
std::string mac_addr_str_ = "";
|
||||
std::string connect_button_label_ = "Connect";
|
||||
std::string fullscreen_button_label_ = "Fullscreen";
|
||||
std::string mouse_control_button_label_ = "Mouse Control";
|
||||
std::string settings_button_label_ = "Setting";
|
||||
char input_password_tmp_[7] = "";
|
||||
char input_password_[7] = "";
|
||||
std::string local_id_ = "";
|
||||
char remote_id_[20] = "";
|
||||
char client_password_[20] = "";
|
||||
bool is_client_mode_ = false;
|
||||
|
||||
private:
|
||||
int screen_width_ = 1280;
|
||||
int screen_height_ = 720;
|
||||
int main_window_width_ = 1280;
|
||||
int main_window_height_ = 720;
|
||||
int main_window_width_before_fullscreen_ = 1280;
|
||||
int main_window_height_before_fullscreen_ = 720;
|
||||
|
||||
int texture_width_ = 1280;
|
||||
int texture_height_ = 720;
|
||||
|
||||
SDL_Texture *sdl_texture_ = nullptr;
|
||||
SDL_Renderer *sdl_renderer_ = nullptr;
|
||||
SDL_Rect sdl_rect_;
|
||||
SDL_Window *main_window_;
|
||||
uint32_t pixformat_ = 0;
|
||||
|
||||
bool inited_ = false;
|
||||
bool exit_ = false;
|
||||
bool connection_established_ = false;
|
||||
bool subwindow_hovered_ = false;
|
||||
bool connect_button_pressed_ = false;
|
||||
bool fullscreen_button_pressed_ = false;
|
||||
bool mouse_control_button_pressed_ = false;
|
||||
bool settings_button_pressed_ = false;
|
||||
bool received_frame_ = false;
|
||||
bool is_create_connection_ = false;
|
||||
bool audio_buffer_fresh_ = false;
|
||||
bool rejoin_ = false;
|
||||
bool control_mouse_ = false;
|
||||
|
||||
int fps_ = 0;
|
||||
uint32_t start_time_;
|
||||
uint32_t end_time_;
|
||||
uint32_t elapsed_time_;
|
||||
uint32_t frame_count_ = 0;
|
||||
|
||||
private:
|
||||
ConnectionStatus connection_status_ = ConnectionStatus::Closed;
|
||||
SignalStatus signal_status_ = SignalStatus::SignalClosed;
|
||||
std::string signal_status_str_ = "";
|
||||
std::string connection_status_str_ = "";
|
||||
|
||||
private:
|
||||
PeerPtr *peer_ = nullptr;
|
||||
PeerPtr *peer_reserved_ = nullptr;
|
||||
Params params_;
|
||||
|
||||
private:
|
||||
SDL_AudioDeviceID input_dev_;
|
||||
SDL_AudioDeviceID output_dev_;
|
||||
unsigned char audio_buffer_[960];
|
||||
int audio_len_ = 0;
|
||||
char *nv12_buffer_ = nullptr;
|
||||
unsigned char *dst_buffer_ = new unsigned char[1280 * 720 * 3];
|
||||
|
||||
private:
|
||||
ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
|
||||
ScreenCapturer *screen_capturer_ = nullptr;
|
||||
DeviceControllerFactory *device_controller_factory_ = nullptr;
|
||||
MouseController *mouse_controller_ = nullptr;
|
||||
|
||||
#ifdef __linux__
|
||||
std::chrono::_V2::system_clock::time_point last_frame_time_;
|
||||
#else
|
||||
std::chrono::steady_clock::time_point last_frame_time_;
|
||||
#endif
|
||||
|
||||
private:
|
||||
int language_button_value_ = 0;
|
||||
int video_quality_button_value_ = 0;
|
||||
int video_encode_format_button_value_ = 0;
|
||||
bool enable_hardware_video_codec_ = false;
|
||||
|
||||
int language_button_value_last_ = 0;
|
||||
int video_quality_button_value_last_ = 0;
|
||||
int video_encode_format_button_value_last_ = 0;
|
||||
bool enable_hardware_video_codec_last_ = false;
|
||||
|
||||
private:
|
||||
std::atomic<bool> start_screen_capture_{false};
|
||||
std::atomic<bool> start_mouse_control_{false};
|
||||
std::atomic<bool> screen_capture_is_started_{false};
|
||||
std::atomic<bool> mouse_control_is_started_{false};
|
||||
|
||||
private:
|
||||
bool settings_window_pos_reset_ = true;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,216 +0,0 @@
|
||||
#include "device_controller.h"
|
||||
#include "localization.h"
|
||||
#include "main_window.h"
|
||||
|
||||
// Refresh Event
|
||||
#define REFRESH_EVENT (SDL_USEREVENT + 1)
|
||||
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
||||
|
||||
#ifdef REMOTE_DESK_DEBUG
|
||||
#else
|
||||
#define MOUSE_CONTROL 1
|
||||
#endif
|
||||
|
||||
int MainWindow::ProcessMouseKeyEven(SDL_Event &ev) {
|
||||
if (!control_mouse_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float ratio = (float)(1280.0 / main_window_width_);
|
||||
|
||||
RemoteAction remote_action;
|
||||
remote_action.m.x = (size_t)(ev.button.x * ratio);
|
||||
remote_action.m.y = (size_t)(ev.button.y * ratio);
|
||||
|
||||
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
|
||||
{
|
||||
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
|
||||
if (SDLK_DOWN == ev.key.keysym.sym) {
|
||||
// printf("SDLK_DOWN \n");
|
||||
|
||||
} else if (SDLK_UP == ev.key.keysym.sym) {
|
||||
// printf("SDLK_UP \n");
|
||||
|
||||
} else if (SDLK_LEFT == ev.key.keysym.sym) {
|
||||
// printf("SDLK_LEFT \n");
|
||||
|
||||
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
|
||||
// printf("SDLK_RIGHT \n");
|
||||
}
|
||||
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_down;
|
||||
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_down;
|
||||
}
|
||||
if (subwindow_hovered_) {
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
}
|
||||
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
} else if (SDL_MOUSEBUTTONUP == ev.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_up;
|
||||
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_up;
|
||||
}
|
||||
if (subwindow_hovered_) {
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
}
|
||||
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
} else if (SDL_MOUSEMOTION == ev.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
} else if (SDL_QUIT == ev.type) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
printf("SDL_QUIT\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MainWindow::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
||||
if (1) {
|
||||
if ("Connected" == connection_status_str_) {
|
||||
SendData(peer_, DATA_TYPE::AUDIO, (const char *)stream, len);
|
||||
}
|
||||
} else {
|
||||
memcpy(audio_buffer_, stream, len);
|
||||
audio_len_ = len;
|
||||
SDL_Delay(10);
|
||||
audio_buffer_fresh_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
|
||||
if (!audio_buffer_fresh_) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_memset(stream, 0, len);
|
||||
|
||||
if (audio_len_ == 0) {
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
|
||||
len = (len > audio_len_ ? audio_len_ : len);
|
||||
SDL_MixAudioFormat(stream, audio_buffer_, AUDIO_S16LSB, len,
|
||||
SDL_MIX_MAXVOLUME);
|
||||
audio_buffer_fresh_ = false;
|
||||
}
|
||||
|
||||
void MainWindow::OnReceiveVideoBufferCb(const char *data, size_t size,
|
||||
const char *user_id,
|
||||
size_t user_id_size, void *user_data) {
|
||||
MainWindow *main_window = (MainWindow *)user_data;
|
||||
if (main_window->connection_established_) {
|
||||
memcpy(main_window->dst_buffer_, data, size);
|
||||
SDL_Event event;
|
||||
event.type = REFRESH_EVENT;
|
||||
SDL_PushEvent(&event);
|
||||
main_window->received_frame_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnReceiveAudioBufferCb(const char *data, size_t size,
|
||||
const char *user_id,
|
||||
size_t user_id_size, void *user_data) {
|
||||
MainWindow *main_window = (MainWindow *)user_data;
|
||||
main_window->audio_buffer_fresh_ = true;
|
||||
SDL_QueueAudio(main_window->output_dev_, data, (uint32_t)size);
|
||||
}
|
||||
|
||||
void MainWindow::OnReceiveDataBufferCb(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size,
|
||||
void *user_data) {
|
||||
MainWindow *main_window = (MainWindow *)user_data;
|
||||
std::string user(user_id, user_id_size);
|
||||
RemoteAction remote_action;
|
||||
memcpy(&remote_action, data, sizeof(remote_action));
|
||||
|
||||
#if MOUSE_CONTROL
|
||||
if (main_window->mouse_controller_) {
|
||||
main_window->mouse_controller_->SendCommand(remote_action);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::OnSignalStatusCb(SignalStatus status, void *user_data) {
|
||||
MainWindow *main_window = (MainWindow *)user_data;
|
||||
main_window->signal_status_ = status;
|
||||
if (SignalStatus::SignalConnecting == status) {
|
||||
main_window->signal_status_str_ = "SignalConnecting";
|
||||
} else if (SignalStatus::SignalConnected == status) {
|
||||
main_window->signal_status_str_ = "SignalConnected";
|
||||
} else if (SignalStatus::SignalFailed == status) {
|
||||
main_window->signal_status_str_ = "SignalFailed";
|
||||
} else if (SignalStatus::SignalClosed == status) {
|
||||
main_window->signal_status_str_ = "SignalClosed";
|
||||
} else if (SignalStatus::SignalReconnecting == status) {
|
||||
main_window->signal_status_str_ = "SignalReconnecting";
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::OnConnectionStatusCb(ConnectionStatus status,
|
||||
void *user_data) {
|
||||
MainWindow *main_window = (MainWindow *)user_data;
|
||||
main_window->connection_status_ = status;
|
||||
if (ConnectionStatus::Connecting == status) {
|
||||
main_window->connection_status_str_ = "Connecting";
|
||||
} else if (ConnectionStatus::Connected == status) {
|
||||
main_window->connection_status_str_ = "Connected";
|
||||
main_window->connection_established_ = true;
|
||||
if (!main_window->is_client_mode_) {
|
||||
main_window->start_screen_capture_ = true;
|
||||
main_window->start_mouse_control_ = true;
|
||||
}
|
||||
} else if (ConnectionStatus::Disconnected == status) {
|
||||
main_window->connection_status_str_ = "Disconnected";
|
||||
} else if (ConnectionStatus::Failed == status) {
|
||||
main_window->connection_status_str_ = "Failed";
|
||||
} else if (ConnectionStatus::Closed == status) {
|
||||
main_window->connection_status_str_ = "Closed";
|
||||
main_window->start_screen_capture_ = false;
|
||||
main_window->start_mouse_control_ = false;
|
||||
main_window->connection_established_ = false;
|
||||
main_window->control_mouse_ = false;
|
||||
if (main_window->dst_buffer_) {
|
||||
memset(main_window->dst_buffer_, 0, 1280 * 720 * 3);
|
||||
SDL_UpdateTexture(main_window->sdl_texture_, NULL,
|
||||
main_window->dst_buffer_, 1280);
|
||||
}
|
||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||
main_window->connection_status_str_ = "Incorrect password";
|
||||
if (main_window->connect_button_pressed_) {
|
||||
main_window->connect_button_pressed_ = false;
|
||||
main_window->connection_established_ = false;
|
||||
main_window->connect_button_label_ =
|
||||
main_window->connect_button_pressed_
|
||||
? localization::disconnect[main_window
|
||||
->localization_language_index_]
|
||||
: localization::connect[main_window
|
||||
->localization_language_index_];
|
||||
}
|
||||
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
|
||||
main_window->connection_status_str_ = "No such transmission id";
|
||||
if (main_window->connect_button_pressed_) {
|
||||
main_window->connect_button_pressed_ = false;
|
||||
main_window->connection_established_ = false;
|
||||
main_window->connect_button_label_ =
|
||||
main_window->connect_button_pressed_
|
||||
? localization::disconnect[main_window
|
||||
->localization_language_index_]
|
||||
: localization::connect[main_window
|
||||
->localization_language_index_];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
|
||||
ScreenCapturerX11::ScreenCapturerX11() {}
|
||||
|
||||
ScreenCapturerX11::~ScreenCapturerX11() {
|
||||
if (inited_ && capture_thread_->joinable()) {
|
||||
capture_thread_->join();
|
||||
if (inited_ && capture_thread_.joinable()) {
|
||||
capture_thread_.join();
|
||||
inited_ = false;
|
||||
}
|
||||
}
|
||||
@@ -32,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
|
||||
@@ -101,11 +103,14 @@ int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Destroy() { return 0; }
|
||||
int ScreenCapturerX11::Destroy() {
|
||||
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_);
|
||||
@@ -128,12 +133,15 @@ int ScreenCapturerX11::Start() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Stop() { return 0; }
|
||||
int ScreenCapturerX11::Stop() {
|
||||
running_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Pause() { return 0; }
|
||||
|
||||
|
||||
@@ -77,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
|
||||
212
src/screen_capturer/macosx/avfoundation/screen_capturer_avf.cpp
Normal file
212
src/screen_capturer/macosx/avfoundation/screen_capturer_avf.cpp
Normal 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() {}
|
||||
@@ -32,8 +32,8 @@ 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();
|
||||
@@ -49,7 +49,6 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
||||
void CleanUp();
|
||||
|
||||
private:
|
||||
std::atomic_bool _running;
|
||||
std::atomic_bool _paused;
|
||||
std::atomic_bool _inited;
|
||||
|
||||
@@ -57,8 +56,6 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
||||
|
||||
std::string _device_name;
|
||||
|
||||
RECORD_DESKTOP_RECT _rect;
|
||||
|
||||
int _fps;
|
||||
|
||||
cb_desktop_data _on_data;
|
||||
@@ -77,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
|
||||
104
src/screen_capturer/macosx/core_graphics/screen_capturer_cg.cpp
Normal file
104
src/screen_capturer/macosx/core_graphics/screen_capturer_cg.cpp
Normal 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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,145 +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() {
|
||||
if (inited_ && capture_thread_->joinable()) {
|
||||
capture_thread_->join();
|
||||
inited_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
cb_desktop_data cb) {
|
||||
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);
|
||||
|
||||
inited_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Destroy() { 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::Stop() { return 0; }
|
||||
|
||||
int ScreenCapturerAvf::Pause() { return 0; }
|
||||
|
||||
int ScreenCapturerAvf::Resume() { return 0; }
|
||||
|
||||
void ScreenCapturerAvf::OnFrame() {}
|
||||
|
||||
void ScreenCapturerAvf::CleanUp() {}
|
||||
@@ -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() {}
|
||||
@@ -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
|
||||
@@ -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>();
|
||||
}
|
||||
@@ -11,20 +11,13 @@
|
||||
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -62,16 +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;
|
||||
|
||||
int r = rect.right;
|
||||
int b = rect.bottom;
|
||||
|
||||
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2];
|
||||
nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2];
|
||||
// nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2];
|
||||
// nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2];
|
||||
|
||||
_fps = fps;
|
||||
|
||||
@@ -147,14 +143,12 @@ void ConvertABGRtoBGRA(const uint8_t *abgr_data, uint8_t *bgra_data, int width,
|
||||
int height, int abgr_stride, int bgra_stride) {
|
||||
for (int i = 0; i < height; ++i) {
|
||||
for (int j = 0; j < width; ++j) {
|
||||
// ABGR<47><52>BGRA<52><41><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
|
||||
int abgr_index = (i * abgr_stride + j) * 4;
|
||||
int bgra_index = (i * bgra_stride + j) * 4;
|
||||
|
||||
// ֱ<>ӽ<EFBFBD><D3BD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD>ͺ<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
bgra_data[bgra_index + 0] = abgr_data[abgr_index + 2]; // <EFBFBD><EFBFBD>ɫ
|
||||
bgra_data[bgra_index + 1] = abgr_data[abgr_index + 1]; // <EFBFBD><EFBFBD>ɫ
|
||||
bgra_data[bgra_index + 2] = abgr_data[abgr_index + 0]; // <20><>ɫ
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -164,11 +158,9 @@ void ConvertBGRAtoABGR(const uint8_t *bgra_data, uint8_t *abgr_data, int width,
|
||||
int height, int bgra_stride, int abgr_stride) {
|
||||
for (int i = 0; i < height; ++i) {
|
||||
for (int j = 0; j < width; ++j) {
|
||||
// BGRA<52><41>ABGR<47><52><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
|
||||
int bgra_index = (i * bgra_stride + j) * 4;
|
||||
int abgr_index = (i * abgr_stride + j) * 4;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>
|
||||
abgr_data[abgr_index + 0] = bgra_data[bgra_index + 3]; // Alpha
|
||||
abgr_data[abgr_index + 1] = bgra_data[bgra_index + 0]; // Blue
|
||||
abgr_data[abgr_index + 2] = bgra_data[bgra_index + 1]; // Green
|
||||
@@ -179,22 +171,20 @@ void ConvertBGRAtoABGR(const uint8_t *bgra_data, uint8_t *abgr_data, int width,
|
||||
|
||||
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame) {
|
||||
if (_on_data) {
|
||||
int width = 1280;
|
||||
int height = 720;
|
||||
// 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);
|
||||
|
||||
libyuv::NV12Scale(
|
||||
(const uint8_t *)nv12_frame_, frame.width,
|
||||
(const uint8_t *)(nv12_frame_ + frame.width * frame.height),
|
||||
frame.width, frame.width, frame.height, (uint8_t *)nv12_frame_scaled_,
|
||||
width, (uint8_t *)(nv12_frame_scaled_ + width * height), width, width,
|
||||
height, libyuv::FilterMode::kFilterLinear);
|
||||
|
||||
_on_data(nv12_frame_scaled_, width * height * 3 / 2, width, height);
|
||||
_on_data(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
|
||||
frame.height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@ 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();
|
||||
@@ -45,8 +44,6 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
||||
|
||||
std::string _device_name;
|
||||
|
||||
RECORD_DESKTOP_RECT _rect;
|
||||
|
||||
int _fps;
|
||||
|
||||
cb_desktop_data _on_data = nullptr;
|
||||
|
||||
@@ -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;
|
||||
|
||||
1408
src/single_window/IconsFontAwesome6.h
Normal file
1408
src/single_window/IconsFontAwesome6.h
Normal file
File diff suppressed because it is too large
Load Diff
56
src/single_window/about_window.cpp
Normal file
56
src/single_window/about_window.cpp
Normal 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;
|
||||
}
|
||||
159
src/single_window/connection_status_window.cpp
Normal file
159
src/single_window/connection_status_window.cpp
Normal 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;
|
||||
}
|
||||
144
src/single_window/control_bar.cpp
Normal file
144
src/single_window/control_bar.cpp
Normal 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;
|
||||
}
|
||||
150
src/single_window/control_window.cpp
Normal file
150
src/single_window/control_window.cpp
Normal 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;
|
||||
}
|
||||
39
src/single_window/layout_style.h
Normal file
39
src/single_window/layout_style.h
Normal 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
|
||||
311
src/single_window/local_peer_window.cpp
Normal file
311
src/single_window/local_peer_window.cpp
Normal 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;
|
||||
}
|
||||
15
src/single_window/main_window.cpp
Normal file
15
src/single_window/main_window.cpp
Normal 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;
|
||||
}
|
||||
110
src/single_window/remote_peer_window.cpp
Normal file
110
src/single_window/remote_peer_window.cpp
Normal 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(¶ms_);
|
||||
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;
|
||||
}
|
||||
982
src/single_window/render.cpp
Normal file
982
src/single_window/render.cpp
Normal 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(¶ms_);
|
||||
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
321
src/single_window/render.h
Normal 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
|
||||
331
src/single_window/render_callback_func.cpp
Normal file
331
src/single_window/render_callback_func.cpp
Normal 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));
|
||||
}
|
||||
}
|
||||
277
src/single_window/setting_window.cpp
Normal file
277
src/single_window/setting_window.cpp
Normal 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;
|
||||
}
|
||||
39
src/single_window/status_bar.cpp
Normal file
39
src/single_window/status_bar.cpp
Normal 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;
|
||||
}
|
||||
166
src/single_window/title_bar.cpp
Normal file
166
src/single_window/title_bar.cpp
Normal 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;
|
||||
}
|
||||
13
src/speaker_capturer/linux/speaker_capturer_linux.cpp
Normal file
13
src/speaker_capturer/linux/speaker_capturer_linux.cpp
Normal 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; }
|
||||
38
src/speaker_capturer/linux/speaker_capturer_linux.h
Normal file
38
src/speaker_capturer/linux/speaker_capturer_linux.h
Normal 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
|
||||
14
src/speaker_capturer/macosx/speaker_capturer_macosx.cpp
Normal file
14
src/speaker_capturer/macosx/speaker_capturer_macosx.cpp
Normal 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; }
|
||||
38
src/speaker_capturer/macosx/speaker_capturer_macosx.h
Normal file
38
src/speaker_capturer/macosx/speaker_capturer_macosx.h
Normal 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
|
||||
26
src/speaker_capturer/speaker_capturer.h
Normal file
26
src/speaker_capturer/speaker_capturer.h
Normal 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
|
||||
36
src/speaker_capturer/speaker_capturer_factory.h
Normal file
36
src/speaker_capturer/speaker_capturer_factory.h
Normal 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
|
||||
100
src/speaker_capturer/windows/speaker_capturer_wasapi.cpp
Normal file
100
src/speaker_capturer/windows/speaker_capturer_wasapi.cpp
Normal 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; }
|
||||
35
src/speaker_capturer/windows/speaker_capturer_wasapi.h
Normal file
35
src/speaker_capturer/windows/speaker_capturer_wasapi.h
Normal 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
|
||||
303
test/audio_capture/audio_capture_wasapi.cpp
Normal file
303
test/audio_capture/audio_capture_wasapi.cpp
Normal 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(¤t_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;
|
||||
}
|
||||
96
test/audio_capture/miniaudio.cpp
Normal file
96
test/audio_capture/miniaudio.cpp
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
2
thirdparty/projectx
vendored
2
thirdparty/projectx
vendored
Submodule thirdparty/projectx updated: 28fb35faf5...9d2e6f0c2a
73
xmake.lua
73
xmake.lua
@@ -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")
|
||||
@@ -14,10 +17,11 @@ if is_mode("debug") then
|
||||
end
|
||||
|
||||
add_requires("spdlog 1.14.1", {system = false})
|
||||
add_requires("imgui 1.90.6", {configs = {sdl2 = true, sdl2_renderer = true}})
|
||||
add_requires("libyuv")
|
||||
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")
|
||||
@@ -37,46 +41,65 @@ elseif is_os("macosx") then
|
||||
add_packages("libxcb")
|
||||
add_links("SDL2", "SDL2main")
|
||||
add_ldflags("-Wl,-ld_classic")
|
||||
add_frameworks("OpenGL")
|
||||
add_frameworks("OpenGL", "IOSurface", "ScreenCaptureKit")
|
||||
end
|
||||
|
||||
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_packages("libyuv")
|
||||
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_packages("ffmpeg")
|
||||
add_files("src/screen_capturer/macosx/*.cpp")
|
||||
add_includedirs("src/screen_capturer/macosx", {public = true})
|
||||
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_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")
|
||||
@@ -91,7 +114,7 @@ target("device_controller")
|
||||
|
||||
target("config_center")
|
||||
set_kind("object")
|
||||
add_deps("log")
|
||||
add_deps("rd_log")
|
||||
add_files("src/config_center/*.cpp")
|
||||
add_includedirs("src/config_center", {public = true})
|
||||
|
||||
@@ -99,27 +122,39 @@ target("localization")
|
||||
set_kind("headeronly")
|
||||
add_includedirs("src/localization", {public = true})
|
||||
|
||||
target("main_window")
|
||||
target("single_window")
|
||||
set_kind("object")
|
||||
add_deps("log", "common", "localization", "config_center", "projectx", "screen_capturer", "device_controller")
|
||||
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/main_window/*.cpp")
|
||||
add_includedirs("src/main_window", {public = true})
|
||||
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", "main_window")
|
||||
if is_os("macosx") then
|
||||
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")
|
||||
|
||||
-- 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")
|
||||
-- add_packages("sdl2", "imgui", "ffmpeg", "openh264")
|
||||
|
||||
Reference in New Issue
Block a user