mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-29 12:32:58 +08:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95a014a601 | ||
|
|
34d6bac345 | ||
|
|
399785409c | ||
|
|
5f1d9b6912 | ||
|
|
d963a0cf38 | ||
|
|
f9c1bc48b4 | ||
|
|
2906d05a4b | ||
|
|
053a0f86ad | ||
|
|
6c2363b239 | ||
|
|
167514fed8 | ||
|
|
342eb0c386 | ||
|
|
52c7099dbe | ||
|
|
12faf7cd2d | ||
|
|
6d921a3309 | ||
|
|
5a690ebbb6 | ||
|
|
4b3839aa34 | ||
|
|
efb165b56f | ||
|
|
0047b4ecc5 | ||
|
|
844710af7c | ||
|
|
562d54090a | ||
|
|
f7fd37651e | ||
|
|
280f59f97d | ||
|
|
0683ad9d27 | ||
|
|
e061e3b4d7 | ||
|
|
eaedcb8d06 | ||
|
|
e7e6380adc | ||
|
|
1f50483b50 | ||
|
|
6f703c8267 | ||
|
|
d150c374b5 | ||
|
|
f29b2ee09d | ||
|
|
0a934e8c01 | ||
|
|
2163aa87d4 | ||
|
|
5d8408d892 | ||
|
|
93d0e3a5d0 | ||
|
|
b4a5e91bc9 | ||
|
|
759078ef7f | ||
|
|
905539a6eb | ||
|
|
f1512812ad | ||
|
|
5f1cf89649 | ||
|
|
f291ad189a | ||
|
|
8807636372 | ||
|
|
70be1d8afc | ||
|
|
1f76aa427d | ||
|
|
134cbf8b75 | ||
|
|
669b944cfd | ||
|
|
9962829885 | ||
|
|
1393615f01 | ||
|
|
d58ae3a6b1 | ||
|
|
a188729af6 | ||
|
|
422478bd9a |
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
|||||||
[submodule "thirdparty/projectx"]
|
[submodule "thirdparty/projectx"]
|
||||||
path = thirdparty/projectx
|
path = thirdparty/projectx
|
||||||
url = git@github.com:dijunkun/projectx.git
|
url = https://github.com/dijunkun/projectx.git
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
Continuous Desk is a lightweight cross-platform remote desktop. It allows multiple users to remotely control the same computer at the same time. In addition to desktop image transmission, it also supports end-to-end voice transmission, providing collaboration capabilities on the basis of remote desktop.
|
Continuous Desk is a lightweight cross-platform remote desktop. It allows multiple users to remotely control the same computer at the same time. In addition to desktop image transmission, it also supports end-to-end voice transmission, providing collaboration capabilities on the basis of remote desktop.
|
||||||
|
|
||||||
Continuous Desk is an experimental application of [Projectx](https://github.com/dijunkun/projectx) real-time communications library. Projectx is a lightweight cross-platform real-time communications library. It has basic capabilities such as network traversal ([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)), video softwar/hardware encoding/decoding (H264), audio encoding/decoding ([Opus](https://github.com/xiph/opus)), signaling interaction, and network congestion control ([TCP over UDP](https://libnice.freedesktop.org/)).
|
Continuous Desk is an experimental application of [Projectx](https://github.com/dijunkun/projectx) real-time communications library. Projectx is a lightweight cross-platform real-time communications library. It has basic capabilities such as network traversal ([RFC5245](https://datatracker.ietf.org/doc/html/rfc5245)), video software/hardware encoding/decoding (H264), audio encoding/decoding ([Opus](https://github.com/xiph/opus)), signaling interaction, and network congestion control ([TCP over UDP](https://libnice.freedesktop.org/)).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
[signal server]
|
[signal server]
|
||||||
ip = 120.77.216.215
|
ip = 150.158.81.30
|
||||||
port = 9099
|
port = 9099
|
||||||
|
|
||||||
[stun server]
|
[stun server]
|
||||||
ip = 120.77.216.215
|
ip = 150.158.81.30
|
||||||
port = 3478
|
port = 3478
|
||||||
|
|
||||||
[turn server]
|
[turn server]
|
||||||
ip = 120.77.216.215
|
ip = 150.158.81.30
|
||||||
port = 3478
|
port = 3478
|
||||||
username = dijunkun
|
username = dijunkun
|
||||||
password = dijunkunpw
|
password = dijunkunpw
|
||||||
|
|
||||||
[hardware acceleration]
|
[hardware acceleration]
|
||||||
|
turn_on = false
|
||||||
|
|
||||||
|
[av1 encoding]
|
||||||
turn_on = true
|
turn_on = true
|
||||||
38
src/config_center/config_center.cpp
Normal file
38
src/config_center/config_center.cpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#include "config_center.h"
|
||||||
|
|
||||||
|
ConfigCenter::ConfigCenter() {}
|
||||||
|
|
||||||
|
ConfigCenter::~ConfigCenter() {}
|
||||||
|
|
||||||
|
int ConfigCenter::SetLanguage(LANGUAGE language) {
|
||||||
|
language_ = language;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConfigCenter::SetVideoQuality(VIDEO_QUALITY video_quality) {
|
||||||
|
video_quality_ = video_quality;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConfigCenter::SetVideoEncodeFormat(
|
||||||
|
VIDEO_ENCODE_FORMAT video_encode_format) {
|
||||||
|
video_encode_format_ = video_encode_format;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConfigCenter::SetHardwareVideoCodec(bool hardware_video_codec) {
|
||||||
|
hardware_video_codec_ = hardware_video_codec;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() { return language_; }
|
||||||
|
|
||||||
|
ConfigCenter::VIDEO_QUALITY ConfigCenter::GetVideoQuality() {
|
||||||
|
return video_quality_;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() {
|
||||||
|
return video_encode_format_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigCenter::IsHardwareVideoCodec() { return hardware_video_codec_; }
|
||||||
40
src/config_center/config_center.h
Normal file
40
src/config_center/config_center.h
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2024-05-29
|
||||||
|
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CONFIG_CENTER_H_
|
||||||
|
#define _CONFIG_CENTER_H_
|
||||||
|
|
||||||
|
class ConfigCenter {
|
||||||
|
public:
|
||||||
|
enum class LANGUAGE { CHINESE = 0, ENGLISH = 1 };
|
||||||
|
enum class VIDEO_QUALITY { LOW = 0, MEDIUM = 1, HIGH = 2 };
|
||||||
|
enum class VIDEO_ENCODE_FORMAT { AV1 = 0, H264 = 1 };
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConfigCenter();
|
||||||
|
~ConfigCenter();
|
||||||
|
|
||||||
|
public:
|
||||||
|
int SetLanguage(LANGUAGE language);
|
||||||
|
int SetVideoQuality(VIDEO_QUALITY video_quality);
|
||||||
|
int SetVideoEncodeFormat(VIDEO_ENCODE_FORMAT video_encode_format);
|
||||||
|
int SetHardwareVideoCodec(bool hardware_video_codec);
|
||||||
|
|
||||||
|
public:
|
||||||
|
LANGUAGE GetLanguage();
|
||||||
|
VIDEO_QUALITY GetVideoQuality();
|
||||||
|
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat();
|
||||||
|
bool IsHardwareVideoCodec();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Default value should be same with parameters in localization.h
|
||||||
|
LANGUAGE language_ = LANGUAGE::CHINESE;
|
||||||
|
VIDEO_QUALITY video_quality_ = VIDEO_QUALITY::MEDIUM;
|
||||||
|
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::AV1;
|
||||||
|
bool hardware_video_codec_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -4,7 +4,12 @@
|
|||||||
|
|
||||||
MouseController::MouseController() {}
|
MouseController::MouseController() {}
|
||||||
|
|
||||||
MouseController::~MouseController() {}
|
MouseController::~MouseController() {
|
||||||
|
if (uinput_fd_) {
|
||||||
|
ioctl(uinput_fd_, UI_DEV_DESTROY);
|
||||||
|
close(uinput_fd_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int MouseController::Init(int screen_width, int screen_height) {
|
int MouseController::Init(int screen_width, int screen_height) {
|
||||||
screen_width_ = screen_width;
|
screen_width_ = screen_width;
|
||||||
@@ -13,6 +18,7 @@ int MouseController::Init(int screen_width, int screen_height) {
|
|||||||
uinput_fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
|
uinput_fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
|
||||||
if (uinput_fd_ < 0) {
|
if (uinput_fd_ < 0) {
|
||||||
LOG_ERROR("Cannot open device: /dev/uinput");
|
LOG_ERROR("Cannot open device: /dev/uinput");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ioctl(uinput_fd_, UI_SET_EVBIT, EV_KEY);
|
ioctl(uinput_fd_, UI_SET_EVBIT, EV_KEY);
|
||||||
@@ -35,15 +41,12 @@ int MouseController::Init(int screen_width, int screen_height) {
|
|||||||
uidev.absmin[ABS_Y] = 0;
|
uidev.absmin[ABS_Y] = 0;
|
||||||
uidev.absmax[ABS_Y] = screen_height_;
|
uidev.absmax[ABS_Y] = screen_height_;
|
||||||
|
|
||||||
write(uinput_fd_, &uidev, sizeof(uidev));
|
int res_uidev = write(uinput_fd_, &uidev, sizeof(uidev));
|
||||||
ioctl(uinput_fd_, UI_DEV_CREATE);
|
ioctl(uinput_fd_, UI_DEV_CREATE);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MouseController::Destroy() {
|
int MouseController::Destroy() { return 0; }
|
||||||
ioctl(uinput_fd_, UI_DEV_DESTROY);
|
|
||||||
close(uinput_fd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
int MouseController::SendCommand(RemoteAction remote_action) {
|
int MouseController::SendCommand(RemoteAction remote_action) {
|
||||||
int mouse_pos_x = remote_action.m.x * screen_width_ / 1280;
|
int mouse_pos_x = remote_action.m.x * screen_width_ / 1280;
|
||||||
@@ -71,6 +74,7 @@ int MouseController::SendCommand(RemoteAction remote_action) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MouseController::SimulateKeyDown(int fd, int kval) {
|
void MouseController::SimulateKeyDown(int fd, int kval) {
|
||||||
|
int res_ev = 0;
|
||||||
struct input_event event;
|
struct input_event event;
|
||||||
memset(&event, 0, sizeof(event));
|
memset(&event, 0, sizeof(event));
|
||||||
gettimeofday(&event.time, 0);
|
gettimeofday(&event.time, 0);
|
||||||
@@ -78,15 +82,16 @@ void MouseController::SimulateKeyDown(int fd, int kval) {
|
|||||||
event.type = EV_KEY;
|
event.type = EV_KEY;
|
||||||
event.value = 1;
|
event.value = 1;
|
||||||
event.code = kval;
|
event.code = kval;
|
||||||
write(fd, &event, sizeof(event));
|
res_ev = write(fd, &event, sizeof(event));
|
||||||
|
|
||||||
event.type = EV_SYN;
|
event.type = EV_SYN;
|
||||||
event.value = 0;
|
event.value = 0;
|
||||||
event.code = SYN_REPORT;
|
event.code = SYN_REPORT;
|
||||||
write(fd, &event, sizeof(event));
|
res_ev = write(fd, &event, sizeof(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MouseController::SimulateKeyUp(int fd, int kval) {
|
void MouseController::SimulateKeyUp(int fd, int kval) {
|
||||||
|
int res_ev = 0;
|
||||||
struct input_event event;
|
struct input_event event;
|
||||||
memset(&event, 0, sizeof(event));
|
memset(&event, 0, sizeof(event));
|
||||||
gettimeofday(&event.time, 0);
|
gettimeofday(&event.time, 0);
|
||||||
@@ -94,12 +99,12 @@ void MouseController::SimulateKeyUp(int fd, int kval) {
|
|||||||
event.type = EV_KEY;
|
event.type = EV_KEY;
|
||||||
event.value = 0;
|
event.value = 0;
|
||||||
event.code = kval;
|
event.code = kval;
|
||||||
write(fd, &event, sizeof(event));
|
res_ev = write(fd, &event, sizeof(event));
|
||||||
|
|
||||||
event.type = EV_SYN;
|
event.type = EV_SYN;
|
||||||
event.value = 0;
|
event.value = 0;
|
||||||
event.code = SYN_REPORT;
|
event.code = SYN_REPORT;
|
||||||
write(fd, &event, sizeof(event));
|
res_ev = write(fd, &event, sizeof(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MouseController::SetMousePosition(int fd, int x, int y) {
|
void MouseController::SetMousePosition(int fd, int x, int y) {
|
||||||
|
|||||||
861
src/gui/main.cpp
861
src/gui/main.cpp
@@ -1,5 +1,3 @@
|
|||||||
#include <SDL.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#ifdef REMOTE_DESK_DEBUG
|
#ifdef REMOTE_DESK_DEBUG
|
||||||
#pragma comment(linker, "/subsystem:\"console\"")
|
#pragma comment(linker, "/subsystem:\"console\"")
|
||||||
@@ -8,865 +6,14 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstring>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "platform.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <libavdevice/avdevice.h>
|
|
||||||
#include <libswresample/swresample.h>
|
|
||||||
#include <libswscale/swscale.h>
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "../../thirdparty/projectx/src/interface/x.h"
|
|
||||||
#include "device_controller_factory.h"
|
|
||||||
#include "imgui.h"
|
|
||||||
#include "imgui_impl_sdl2.h"
|
|
||||||
#include "imgui_impl_sdlrenderer2.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "screen_capturer_factory.h"
|
#include "main_window.h"
|
||||||
|
|
||||||
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
#ifdef REMOTE_DESK_DEBUG
|
|
||||||
#define MOUSE_CONTROL 0
|
|
||||||
#else
|
|
||||||
#define MOUSE_CONTROL 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int screen_w = 1280, screen_h = 720;
|
|
||||||
int window_w = 1280, window_h = 720;
|
|
||||||
const int pixel_w = 1280, pixel_h = 720;
|
|
||||||
|
|
||||||
unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2];
|
|
||||||
unsigned char audio_buffer[960];
|
|
||||||
SDL_Texture *sdlTexture = nullptr;
|
|
||||||
SDL_Renderer *sdlRenderer = nullptr;
|
|
||||||
SDL_Rect sdlRect;
|
|
||||||
SDL_Window *window;
|
|
||||||
static SDL_AudioDeviceID input_dev;
|
|
||||||
static SDL_AudioDeviceID output_dev;
|
|
||||||
|
|
||||||
uint32_t start_time, end_time, elapsed_time;
|
|
||||||
uint32_t frame_count = 0;
|
|
||||||
int fps = 0;
|
|
||||||
|
|
||||||
static std::atomic<bool> audio_buffer_fresh{false};
|
|
||||||
static uint32_t last_ts = 0;
|
|
||||||
|
|
||||||
int64_t src_ch_layout = AV_CH_LAYOUT_MONO;
|
|
||||||
int src_rate = 48000;
|
|
||||||
enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_S16;
|
|
||||||
int src_nb_channels = 0;
|
|
||||||
uint8_t **src_data = NULL;
|
|
||||||
int src_linesize;
|
|
||||||
int src_nb_samples = 480;
|
|
||||||
|
|
||||||
int64_t dst_ch_layout = AV_CH_LAYOUT_MONO;
|
|
||||||
int dst_rate = 48000;
|
|
||||||
enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;
|
|
||||||
int dst_nb_channels = 0;
|
|
||||||
uint8_t **dst_data = NULL;
|
|
||||||
int dst_linesize;
|
|
||||||
int dst_nb_samples;
|
|
||||||
int max_dst_nb_samples;
|
|
||||||
|
|
||||||
int dst_bufsize;
|
|
||||||
struct SwrContext *swr_ctx;
|
|
||||||
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
int audio_len = 0;
|
|
||||||
|
|
||||||
std::string window_title = "Remote Desk Client";
|
|
||||||
std::string server_connection_status_str = "-";
|
|
||||||
std::string client_connection_status_str = "-";
|
|
||||||
std::string server_signal_status_str = "-";
|
|
||||||
std::string client_signal_status_str = "-";
|
|
||||||
|
|
||||||
std::atomic<ConnectionStatus> server_connection_status{
|
|
||||||
ConnectionStatus::Closed};
|
|
||||||
std::atomic<ConnectionStatus> client_connection_status{
|
|
||||||
ConnectionStatus::Closed};
|
|
||||||
std::atomic<SignalStatus> server_signal_status{SignalStatus::SignalClosed};
|
|
||||||
std::atomic<SignalStatus> client_signal_status{SignalStatus::SignalClosed};
|
|
||||||
|
|
||||||
// Refresh Event
|
|
||||||
#define REFRESH_EVENT (SDL_USEREVENT + 1)
|
|
||||||
#define QUIT_EVENT (SDL_USEREVENT + 2)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char password[7];
|
|
||||||
} CDCache;
|
|
||||||
|
|
||||||
int thread_exit = 0;
|
|
||||||
PeerPtr *peer_server = nullptr;
|
|
||||||
PeerPtr *peer_client = nullptr;
|
|
||||||
bool joined = false;
|
|
||||||
bool received_frame = false;
|
|
||||||
bool menu_hovered = false;
|
|
||||||
|
|
||||||
static bool connect_button_pressed = false;
|
|
||||||
static const char *connect_label = "Connect";
|
|
||||||
static char input_password[7] = "";
|
|
||||||
static FILE *cd_cache_file = nullptr;
|
|
||||||
static CDCache cd_cache;
|
|
||||||
|
|
||||||
static bool is_create_connection = false;
|
|
||||||
static bool done = false;
|
|
||||||
|
|
||||||
ScreenCapturerFactory *screen_capturer_factory = nullptr;
|
|
||||||
ScreenCapturer *screen_capturer = nullptr;
|
|
||||||
|
|
||||||
DeviceControllerFactory *device_controller_factory = nullptr;
|
|
||||||
MouseController *mouse_controller = nullptr;
|
|
||||||
|
|
||||||
char *nv12_buffer = nullptr;
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
std::chrono::_V2::system_clock::time_point last_frame_time_;
|
|
||||||
#else
|
|
||||||
std::chrono::steady_clock::time_point last_frame_time_;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inline int ProcessMouseKeyEven(SDL_Event &ev) {
|
|
||||||
float ratio = (float)(1280.0 / window_w);
|
|
||||||
|
|
||||||
RemoteAction remote_action;
|
|
||||||
remote_action.m.x = (size_t)(ev.button.x * ratio);
|
|
||||||
remote_action.m.y = (size_t)(ev.button.y * ratio);
|
|
||||||
|
|
||||||
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
|
|
||||||
{
|
|
||||||
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
|
|
||||||
if (SDLK_DOWN == ev.key.keysym.sym) {
|
|
||||||
// printf("SDLK_DOWN \n");
|
|
||||||
|
|
||||||
} else if (SDLK_UP == ev.key.keysym.sym) {
|
|
||||||
// printf("SDLK_UP \n");
|
|
||||||
|
|
||||||
} else if (SDLK_LEFT == ev.key.keysym.sym) {
|
|
||||||
// printf("SDLK_LEFT \n");
|
|
||||||
|
|
||||||
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
|
|
||||||
// printf("SDLK_RIGHT \n");
|
|
||||||
}
|
|
||||||
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
|
|
||||||
remote_action.type = ControlType::mouse;
|
|
||||||
if (SDL_BUTTON_LEFT == ev.button.button) {
|
|
||||||
remote_action.m.flag = MouseFlag::left_down;
|
|
||||||
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
|
||||||
remote_action.m.flag = MouseFlag::right_down;
|
|
||||||
}
|
|
||||||
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
|
|
||||||
sizeof(remote_action));
|
|
||||||
} else if (SDL_MOUSEBUTTONUP == ev.type) {
|
|
||||||
remote_action.type = ControlType::mouse;
|
|
||||||
if (SDL_BUTTON_LEFT == ev.button.button) {
|
|
||||||
remote_action.m.flag = MouseFlag::left_up;
|
|
||||||
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
|
||||||
remote_action.m.flag = MouseFlag::right_up;
|
|
||||||
}
|
|
||||||
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
|
|
||||||
sizeof(remote_action));
|
|
||||||
} else if (SDL_MOUSEMOTION == ev.type) {
|
|
||||||
remote_action.type = ControlType::mouse;
|
|
||||||
remote_action.m.flag = MouseFlag::move;
|
|
||||||
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
|
|
||||||
sizeof(remote_action));
|
|
||||||
} else if (SDL_QUIT == ev.type) {
|
|
||||||
SDL_Event event;
|
|
||||||
event.type = SDL_QUIT;
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
printf("SDL_QUIT\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
|
||||||
int64_t delay = swr_get_delay(swr_ctx, src_rate);
|
|
||||||
dst_nb_samples = (int)av_rescale_rnd(delay + src_nb_samples, dst_rate,
|
|
||||||
src_rate, AV_ROUND_UP);
|
|
||||||
if (dst_nb_samples > max_dst_nb_samples) {
|
|
||||||
av_freep(&dst_data[0]);
|
|
||||||
ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,
|
|
||||||
dst_nb_samples, dst_sample_fmt, 1);
|
|
||||||
if (ret < 0) return;
|
|
||||||
max_dst_nb_samples = dst_nb_samples;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples,
|
|
||||||
(const uint8_t **)&stream, src_nb_samples);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Error while converting\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, ret,
|
|
||||||
dst_sample_fmt, 1);
|
|
||||||
if (dst_bufsize < 0) {
|
|
||||||
fprintf(stderr, "Could not get sample buffer size\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (1) {
|
|
||||||
if ("ClientConnected" == client_connection_status_str) {
|
|
||||||
SendData(peer_client, DATA_TYPE::AUDIO, (const char *)dst_data[0],
|
|
||||||
dst_bufsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("ServerConnected" == server_connection_status_str) {
|
|
||||||
SendData(peer_server, DATA_TYPE::AUDIO, (const char *)dst_data[0],
|
|
||||||
dst_bufsize);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
memcpy(audio_buffer, dst_data[0], dst_bufsize);
|
|
||||||
audio_len = dst_bufsize;
|
|
||||||
SDL_Delay(10);
|
|
||||||
audio_buffer_fresh = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
|
|
||||||
// if ("ClientConnected" != client_connection_status_str) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (!audio_buffer_fresh) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_memset(stream, 0, len);
|
|
||||||
|
|
||||||
if (audio_len == 0) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
|
|
||||||
len = (len > audio_len ? audio_len : len);
|
|
||||||
SDL_MixAudioFormat(stream, audio_buffer, AUDIO_S16LSB, len,
|
|
||||||
SDL_MIX_MAXVOLUME);
|
|
||||||
audio_buffer_fresh = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerReceiveVideoBuffer(const char *data, size_t size,
|
|
||||||
const char *user_id, size_t user_id_size) {}
|
|
||||||
|
|
||||||
void ClientReceiveVideoBuffer(const char *data, size_t size,
|
|
||||||
const char *user_id, size_t user_id_size) {
|
|
||||||
// std::cout << "Receive: [" << user_id << "] " << std::endl;
|
|
||||||
if (joined) {
|
|
||||||
memcpy(dst_buffer, data, size);
|
|
||||||
|
|
||||||
SDL_Event event;
|
|
||||||
event.type = REFRESH_EVENT;
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
received_frame = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerReceiveAudioBuffer(const char *data, size_t size,
|
|
||||||
const char *user_id, size_t user_id_size) {
|
|
||||||
// memset(audio_buffer, 0, size);
|
|
||||||
// memcpy(audio_buffer, data, size);
|
|
||||||
// audio_len = size;
|
|
||||||
audio_buffer_fresh = true;
|
|
||||||
|
|
||||||
SDL_QueueAudio(output_dev, data, (Uint32)size);
|
|
||||||
// printf("queue audio\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientReceiveAudioBuffer(const char *data, size_t size,
|
|
||||||
const char *user_id, size_t user_id_size) {
|
|
||||||
// std::cout << "Client receive audio, size " << size << ", user [" << user_id
|
|
||||||
// << "] " << std::endl;
|
|
||||||
SDL_QueueAudio(output_dev, data, (Uint32)size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id,
|
|
||||||
size_t user_id_size) {
|
|
||||||
std::string user(user_id, user_id_size);
|
|
||||||
|
|
||||||
RemoteAction remote_action;
|
|
||||||
memcpy(&remote_action, data, sizeof(remote_action));
|
|
||||||
|
|
||||||
// std::cout << "remote_action: " << remote_action.type << " "
|
|
||||||
// << remote_action.m.flag << " " << remote_action.m.x << " "
|
|
||||||
// << remote_action.m.y << std::endl;
|
|
||||||
#if MOUSE_CONTROL
|
|
||||||
mouse_controller->SendCommand(remote_action);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientReceiveDataBuffer(const char *data, size_t size, const char *user_id,
|
|
||||||
size_t user_id_size) {}
|
|
||||||
|
|
||||||
void ServerSignalStatus(SignalStatus status) {
|
|
||||||
server_signal_status = status;
|
|
||||||
if (SignalStatus::SignalConnecting == status) {
|
|
||||||
server_signal_status_str = "ServerSignalConnecting";
|
|
||||||
} else if (SignalStatus::SignalConnected == status) {
|
|
||||||
server_signal_status_str = "ServerSignalConnected";
|
|
||||||
} else if (SignalStatus::SignalFailed == status) {
|
|
||||||
server_signal_status_str = "ServerSignalFailed";
|
|
||||||
} else if (SignalStatus::SignalClosed == status) {
|
|
||||||
server_signal_status_str = "ServerSignalClosed";
|
|
||||||
} else if (SignalStatus::SignalReconnecting == status) {
|
|
||||||
server_signal_status_str = "ServerSignalReconnecting";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientSignalStatus(SignalStatus status) {
|
|
||||||
client_signal_status = status;
|
|
||||||
if (SignalStatus::SignalConnecting == status) {
|
|
||||||
client_signal_status_str = "ClientSignalConnecting";
|
|
||||||
} else if (SignalStatus::SignalConnected == status) {
|
|
||||||
client_signal_status_str = "ClientSignalConnected";
|
|
||||||
} else if (SignalStatus::SignalFailed == status) {
|
|
||||||
client_signal_status_str = "ClientSignalFailed";
|
|
||||||
} else if (SignalStatus::SignalClosed == status) {
|
|
||||||
client_signal_status_str = "ClientSignalClosed";
|
|
||||||
} else if (SignalStatus::SignalReconnecting == status) {
|
|
||||||
client_signal_status_str = "ClientSignalReconnecting";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerConnectionStatus(ConnectionStatus status) {
|
|
||||||
server_connection_status = status;
|
|
||||||
if (ConnectionStatus::Connecting == status) {
|
|
||||||
server_connection_status_str = "ServerConnecting";
|
|
||||||
} else if (ConnectionStatus::Connected == status) {
|
|
||||||
server_connection_status_str = "ServerConnected";
|
|
||||||
} else if (ConnectionStatus::Disconnected == status) {
|
|
||||||
server_connection_status_str = "ServerDisconnected";
|
|
||||||
} else if (ConnectionStatus::Failed == status) {
|
|
||||||
server_connection_status_str = "ServerFailed";
|
|
||||||
} else if (ConnectionStatus::Closed == status) {
|
|
||||||
server_connection_status_str = "ServerClosed";
|
|
||||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
|
||||||
server_connection_status_str = "Incorrect password";
|
|
||||||
if (connect_button_pressed) {
|
|
||||||
connect_button_pressed = false;
|
|
||||||
joined = false;
|
|
||||||
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientConnectionStatus(ConnectionStatus status) {
|
|
||||||
client_connection_status = status;
|
|
||||||
if (ConnectionStatus::Connecting == status) {
|
|
||||||
client_connection_status_str = "ClientConnecting";
|
|
||||||
} else if (ConnectionStatus::Connected == status) {
|
|
||||||
client_connection_status_str = "ClientConnected";
|
|
||||||
joined = true;
|
|
||||||
} else if (ConnectionStatus::Disconnected == status) {
|
|
||||||
client_connection_status_str = "ClientDisconnected";
|
|
||||||
} else if (ConnectionStatus::Failed == status) {
|
|
||||||
client_connection_status_str = "ClientFailed";
|
|
||||||
} else if (ConnectionStatus::Closed == status) {
|
|
||||||
client_connection_status_str = "ClientClosed";
|
|
||||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
|
||||||
client_connection_status_str = "Incorrect password";
|
|
||||||
if (connect_button_pressed) {
|
|
||||||
connect_button_pressed = false;
|
|
||||||
joined = false;
|
|
||||||
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
|
||||||
}
|
|
||||||
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
|
|
||||||
client_connection_status_str = "No such transmission id";
|
|
||||||
if (connect_button_pressed) {
|
|
||||||
connect_button_pressed = false;
|
|
||||||
joined = false;
|
|
||||||
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int initResampler() {
|
|
||||||
/* create resampler context */
|
|
||||||
swr_ctx = swr_alloc();
|
|
||||||
if (!swr_ctx) {
|
|
||||||
fprintf(stderr, "Could not allocate resampler context\n");
|
|
||||||
ret = AVERROR(ENOMEM);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set options */
|
|
||||||
av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);
|
|
||||||
av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0);
|
|
||||||
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0);
|
|
||||||
|
|
||||||
av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0);
|
|
||||||
av_opt_set_int(swr_ctx, "out_sample_rate", dst_rate, 0);
|
|
||||||
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);
|
|
||||||
|
|
||||||
/* initialize the resampling context */
|
|
||||||
if ((ret = swr_init(swr_ctx)) < 0) {
|
|
||||||
fprintf(stderr, "Failed to initialize the resampling context\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate source and destination samples buffers */
|
|
||||||
src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);
|
|
||||||
|
|
||||||
ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize,
|
|
||||||
src_nb_channels, src_nb_samples,
|
|
||||||
src_sample_fmt, 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Could not allocate source samples\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
max_dst_nb_samples = dst_nb_samples =
|
|
||||||
av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
|
|
||||||
|
|
||||||
/* buffer is going to be directly written to a rawaudio file, no alignment */
|
|
||||||
dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
|
|
||||||
|
|
||||||
ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize,
|
|
||||||
dst_nb_channels, dst_nb_samples,
|
|
||||||
dst_sample_fmt, 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
fprintf(stderr, "Could not allocate destination samples\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
LOG_INFO("Remote desk");
|
LOG_INFO("Remote desk");
|
||||||
|
MainWindow main_window;
|
||||||
|
|
||||||
last_ts = static_cast<uint32_t>(
|
main_window.Run();
|
||||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
|
||||||
.count());
|
|
||||||
|
|
||||||
initResampler();
|
|
||||||
|
|
||||||
cd_cache_file = fopen("cache.cd", "r+");
|
|
||||||
if (cd_cache_file) {
|
|
||||||
fseek(cd_cache_file, 0, SEEK_SET);
|
|
||||||
fread(&cd_cache.password, sizeof(cd_cache.password), 1, cd_cache_file);
|
|
||||||
fclose(cd_cache_file);
|
|
||||||
strncpy(input_password, cd_cache.password, sizeof(cd_cache.password));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup SDL
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
|
|
||||||
SDL_INIT_GAMECONTROLLER) != 0) {
|
|
||||||
printf("Error: %s\n", SDL_GetError());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// From 2.0.18: Enable native IME.
|
|
||||||
#ifdef SDL_HINT_IME_SHOW_UI
|
|
||||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Create window with SDL_Renderer graphics context
|
|
||||||
SDL_WindowFlags window_flags =
|
|
||||||
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
|
||||||
window = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
|
|
||||||
SDL_WINDOWPOS_CENTERED, window_w, window_h,
|
|
||||||
window_flags);
|
|
||||||
|
|
||||||
SDL_DisplayMode DM;
|
|
||||||
SDL_GetCurrentDisplayMode(0, &DM);
|
|
||||||
screen_w = DM.w;
|
|
||||||
screen_h = DM.h;
|
|
||||||
|
|
||||||
sdlRenderer = SDL_CreateRenderer(
|
|
||||||
window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
|
||||||
if (sdlRenderer == nullptr) {
|
|
||||||
SDL_Log("Error creating SDL_Renderer!");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint32 pixformat = 0;
|
|
||||||
pixformat = SDL_PIXELFORMAT_NV12;
|
|
||||||
|
|
||||||
sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,
|
|
||||||
SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
|
|
||||||
|
|
||||||
// Audio
|
|
||||||
SDL_AudioSpec want_in, have_in, want_out, have_out;
|
|
||||||
SDL_zero(want_in);
|
|
||||||
want_in.freq = 48000;
|
|
||||||
want_in.format = AUDIO_S16LSB;
|
|
||||||
want_in.channels = 1;
|
|
||||||
want_in.samples = 480;
|
|
||||||
want_in.callback = SdlCaptureAudioIn;
|
|
||||||
|
|
||||||
input_dev = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0);
|
|
||||||
if (input_dev == 0) {
|
|
||||||
SDL_Log("Failed to open input: %s", SDL_GetError());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_zero(want_out);
|
|
||||||
want_out.freq = 48000;
|
|
||||||
want_out.format = AUDIO_S16LSB;
|
|
||||||
want_out.channels = 1;
|
|
||||||
// want_out.silence = 0;
|
|
||||||
want_out.samples = 480;
|
|
||||||
want_out.callback = NULL;
|
|
||||||
|
|
||||||
output_dev = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out, 0);
|
|
||||||
if (output_dev == 0) {
|
|
||||||
SDL_Log("Failed to open input: %s", SDL_GetError());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_PauseAudioDevice(input_dev, 0);
|
|
||||||
SDL_PauseAudioDevice(output_dev, 0);
|
|
||||||
|
|
||||||
// Setup Dear ImGui context
|
|
||||||
IMGUI_CHECKVERSION();
|
|
||||||
ImGui::CreateContext();
|
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
|
||||||
|
|
||||||
io.ConfigFlags |=
|
|
||||||
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
|
||||||
io.ConfigFlags |=
|
|
||||||
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
|
||||||
|
|
||||||
// Setup Dear ImGui style
|
|
||||||
ImGui::StyleColorsDark();
|
|
||||||
// ImGui::StyleColorsLight();
|
|
||||||
|
|
||||||
// Setup Platform/Renderer backends
|
|
||||||
ImGui_ImplSDL2_InitForSDLRenderer(window, sdlRenderer);
|
|
||||||
ImGui_ImplSDLRenderer2_Init(sdlRenderer);
|
|
||||||
|
|
||||||
// Our state
|
|
||||||
bool show_demo_window = true;
|
|
||||||
bool show_another_window = false;
|
|
||||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
|
||||||
|
|
||||||
std::string mac_addr_str = GetMac();
|
|
||||||
|
|
||||||
std::thread rtc_thread(
|
|
||||||
[](int screen_width, int screen_height) {
|
|
||||||
std::string default_cfg_path = "../../../../config/config.ini";
|
|
||||||
std::ifstream f(default_cfg_path.c_str());
|
|
||||||
|
|
||||||
std::string mac_addr_str = GetMac();
|
|
||||||
|
|
||||||
Params server_params;
|
|
||||||
server_params.cfg_path =
|
|
||||||
f.good() ? "../../../../config/config.ini" : "config.ini";
|
|
||||||
server_params.on_receive_video_buffer = ServerReceiveVideoBuffer;
|
|
||||||
server_params.on_receive_audio_buffer = ServerReceiveAudioBuffer;
|
|
||||||
server_params.on_receive_data_buffer = ServerReceiveDataBuffer;
|
|
||||||
server_params.on_signal_status = ServerSignalStatus;
|
|
||||||
server_params.on_connection_status = ServerConnectionStatus;
|
|
||||||
|
|
||||||
Params client_params;
|
|
||||||
client_params.cfg_path =
|
|
||||||
f.good() ? "../../../../config/config.ini" : "config.ini";
|
|
||||||
client_params.on_receive_video_buffer = ClientReceiveVideoBuffer;
|
|
||||||
client_params.on_receive_audio_buffer = ClientReceiveAudioBuffer;
|
|
||||||
client_params.on_receive_data_buffer = ClientReceiveDataBuffer;
|
|
||||||
client_params.on_signal_status = ClientSignalStatus;
|
|
||||||
client_params.on_connection_status = ClientConnectionStatus;
|
|
||||||
|
|
||||||
std::string transmission_id = "000001";
|
|
||||||
|
|
||||||
peer_server = CreatePeer(&server_params);
|
|
||||||
LOG_INFO("Create peer_server");
|
|
||||||
std::string server_user_id = "S-" + mac_addr_str;
|
|
||||||
Init(peer_server, server_user_id.c_str());
|
|
||||||
LOG_INFO("peer_server init finish");
|
|
||||||
|
|
||||||
peer_client = CreatePeer(&client_params);
|
|
||||||
LOG_INFO("Create peer_client");
|
|
||||||
std::string client_user_id = "C-" + mac_addr_str;
|
|
||||||
Init(peer_client, client_user_id.c_str());
|
|
||||||
LOG_INFO("peer_client init finish");
|
|
||||||
|
|
||||||
{
|
|
||||||
while (SignalStatus::SignalConnected != server_signal_status &&
|
|
||||||
!done) {
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (done) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string user_id = "S-" + mac_addr_str;
|
|
||||||
is_create_connection =
|
|
||||||
CreateConnection(peer_server, mac_addr_str.c_str(),
|
|
||||||
input_password)
|
|
||||||
? false
|
|
||||||
: true;
|
|
||||||
|
|
||||||
nv12_buffer = new char[NV12_BUFFER_SIZE];
|
|
||||||
|
|
||||||
// Screen capture
|
|
||||||
screen_capturer_factory = new ScreenCapturerFactory();
|
|
||||||
screen_capturer = (ScreenCapturer *)screen_capturer_factory->Create();
|
|
||||||
|
|
||||||
last_frame_time_ = std::chrono::high_resolution_clock::now();
|
|
||||||
ScreenCapturer::RECORD_DESKTOP_RECT rect;
|
|
||||||
rect.left = 0;
|
|
||||||
rect.top = 0;
|
|
||||||
rect.right = screen_w;
|
|
||||||
rect.bottom = screen_h;
|
|
||||||
|
|
||||||
screen_capturer->Init(
|
|
||||||
rect, 60,
|
|
||||||
[](unsigned char *data, int size, int width, int height) -> void {
|
|
||||||
auto now_time = std::chrono::high_resolution_clock::now();
|
|
||||||
std::chrono::duration<double> duration =
|
|
||||||
now_time - last_frame_time_;
|
|
||||||
auto tc = duration.count() * 1000;
|
|
||||||
|
|
||||||
if (tc >= 0) {
|
|
||||||
SendData(peer_server, DATA_TYPE::VIDEO, (const char *)data,
|
|
||||||
NV12_BUFFER_SIZE);
|
|
||||||
last_frame_time_ = now_time;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
screen_capturer->Start();
|
|
||||||
|
|
||||||
// Mouse control
|
|
||||||
device_controller_factory = new DeviceControllerFactory();
|
|
||||||
mouse_controller =
|
|
||||||
(MouseController *)device_controller_factory->Create(
|
|
||||||
DeviceControllerFactory::Device::Mouse);
|
|
||||||
mouse_controller->Init(screen_w, screen_h);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
screen_w, screen_h);
|
|
||||||
|
|
||||||
// Main loop
|
|
||||||
while (!done) {
|
|
||||||
// Start the Dear ImGui frame
|
|
||||||
ImGui_ImplSDLRenderer2_NewFrame();
|
|
||||||
ImGui_ImplSDL2_NewFrame();
|
|
||||||
ImGui::NewFrame();
|
|
||||||
|
|
||||||
if (joined && !menu_hovered) {
|
|
||||||
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
static float f = 0.0f;
|
|
||||||
static int counter = 0;
|
|
||||||
|
|
||||||
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
|
|
||||||
ImGui::SetNextWindowSize(ImVec2(190, 200));
|
|
||||||
|
|
||||||
ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_NoResize);
|
|
||||||
|
|
||||||
{
|
|
||||||
menu_hovered = ImGui::IsWindowHovered();
|
|
||||||
ImGui::Text(" LOCAL ID:");
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::SetNextItemWidth(95);
|
|
||||||
ImGui::InputText(
|
|
||||||
"##local_id", (char *)mac_addr_str.c_str(),
|
|
||||||
mac_addr_str.length() + 1,
|
|
||||||
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
|
|
||||||
|
|
||||||
ImGui::Text(" PASSWORD:");
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::SetNextItemWidth(95);
|
|
||||||
|
|
||||||
char input_password_tmp[7] = "";
|
|
||||||
strncpy(input_password_tmp, input_password, sizeof(input_password));
|
|
||||||
|
|
||||||
ImGui::InputTextWithHint("##server_pwd", "max 6 chars", input_password,
|
|
||||||
IM_ARRAYSIZE(input_password),
|
|
||||||
ImGuiInputTextFlags_CharsNoBlank);
|
|
||||||
if (strcmp(input_password_tmp, input_password)) {
|
|
||||||
cd_cache_file = fopen("cache.cd", "w+");
|
|
||||||
if (cd_cache_file) {
|
|
||||||
fseek(cd_cache_file, 0, SEEK_SET);
|
|
||||||
strncpy(cd_cache.password, input_password, sizeof(input_password));
|
|
||||||
fwrite(&cd_cache.password, sizeof(cd_cache.password), 1,
|
|
||||||
cd_cache_file);
|
|
||||||
fclose(cd_cache_file);
|
|
||||||
}
|
|
||||||
LeaveConnection(peer_server);
|
|
||||||
CreateConnection(peer_server, mac_addr_str.c_str(), input_password);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
|
||||||
{
|
|
||||||
{
|
|
||||||
static char remote_id[20] = "";
|
|
||||||
ImGui::Text("REMOTE ID:");
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::SetNextItemWidth(95);
|
|
||||||
ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(),
|
|
||||||
remote_id, IM_ARRAYSIZE(remote_id),
|
|
||||||
ImGuiInputTextFlags_CharsUppercase |
|
|
||||||
ImGuiInputTextFlags_CharsNoBlank);
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
ImGui::Text(" PASSWORD:");
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::SetNextItemWidth(95);
|
|
||||||
static char client_password[20] = "";
|
|
||||||
ImGui::InputTextWithHint("##client_pwd", "max 6 chars",
|
|
||||||
client_password,
|
|
||||||
IM_ARRAYSIZE(client_password),
|
|
||||||
ImGuiInputTextFlags_CharsNoBlank);
|
|
||||||
|
|
||||||
if (ImGui::Button(connect_label)) {
|
|
||||||
int ret = -1;
|
|
||||||
if ("ClientSignalConnected" == client_signal_status_str) {
|
|
||||||
if (strcmp(connect_label, "Connect") == 0 && !joined) {
|
|
||||||
std::string user_id = "C-" + mac_addr_str;
|
|
||||||
ret = JoinConnection(peer_client, remote_id, client_password);
|
|
||||||
if (0 == ret) {
|
|
||||||
// joined = true;
|
|
||||||
}
|
|
||||||
} else if (strcmp(connect_label, "Disconnect") == 0 && joined) {
|
|
||||||
ret = LeaveConnection(peer_client);
|
|
||||||
memset(audio_buffer, 0, 960);
|
|
||||||
if (0 == ret) {
|
|
||||||
joined = false;
|
|
||||||
received_frame = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 == ret) {
|
|
||||||
connect_button_pressed = !connect_button_pressed;
|
|
||||||
connect_label =
|
|
||||||
connect_button_pressed ? "Disconnect" : "Connect";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
ImGui::Separator();
|
|
||||||
|
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
{
|
|
||||||
if (ImGui::Button("Resize Window")) {
|
|
||||||
SDL_GetWindowSize(window, &window_w, &window_h);
|
|
||||||
|
|
||||||
if (window_h != window_w * 9 / 16) {
|
|
||||||
window_w = window_h * 16 / 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_SetWindowSize(window, window_w, window_h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rendering
|
|
||||||
ImGui::Render();
|
|
||||||
SDL_RenderSetScale(sdlRenderer, io.DisplayFramebufferScale.x,
|
|
||||||
io.DisplayFramebufferScale.y);
|
|
||||||
|
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
|
||||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
|
||||||
if (event.type == SDL_QUIT) {
|
|
||||||
done = true;
|
|
||||||
} else if (event.type == SDL_WINDOWEVENT &&
|
|
||||||
event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
|
||||||
SDL_GetWindowSize(window, &window_w, &window_h);
|
|
||||||
} else if (event.type == SDL_WINDOWEVENT &&
|
|
||||||
event.window.event == SDL_WINDOWEVENT_CLOSE &&
|
|
||||||
event.window.windowID == SDL_GetWindowID(window)) {
|
|
||||||
done = true;
|
|
||||||
} else if (event.type == REFRESH_EVENT) {
|
|
||||||
sdlRect.x = 0;
|
|
||||||
sdlRect.y = 0;
|
|
||||||
sdlRect.w = window_w;
|
|
||||||
sdlRect.h = window_h;
|
|
||||||
|
|
||||||
SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w);
|
|
||||||
} else {
|
|
||||||
if (joined) {
|
|
||||||
ProcessMouseKeyEven(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_RenderClear(sdlRenderer);
|
|
||||||
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
|
|
||||||
|
|
||||||
if (!joined || !received_frame) {
|
|
||||||
SDL_RenderClear(sdlRenderer);
|
|
||||||
SDL_SetRenderDrawColor(
|
|
||||||
sdlRenderer, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
|
|
||||||
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
|
|
||||||
SDL_RenderPresent(sdlRenderer);
|
|
||||||
|
|
||||||
frame_count++;
|
|
||||||
end_time = SDL_GetTicks();
|
|
||||||
elapsed_time = end_time - start_time;
|
|
||||||
if (elapsed_time >= 1000) {
|
|
||||||
fps = frame_count / (elapsed_time / 1000);
|
|
||||||
frame_count = 0;
|
|
||||||
window_title = "Remote Desk Client FPS [" + std::to_string(fps) +
|
|
||||||
"] status [" + server_signal_status_str + "|" +
|
|
||||||
client_signal_status_str + "|" +
|
|
||||||
server_connection_status_str + "|" +
|
|
||||||
client_connection_status_str + "]";
|
|
||||||
// For MacOS, UI frameworks can only be called from the main thread
|
|
||||||
SDL_SetWindowTitle(window, window_title.c_str());
|
|
||||||
start_time = end_time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
|
|
||||||
if (is_create_connection) {
|
|
||||||
LeaveConnection(peer_server);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (joined) {
|
|
||||||
LeaveConnection(peer_client);
|
|
||||||
}
|
|
||||||
|
|
||||||
rtc_thread.join();
|
|
||||||
SDL_CloseAudioDevice(output_dev);
|
|
||||||
SDL_CloseAudioDevice(input_dev);
|
|
||||||
|
|
||||||
mouse_controller->Destroy();
|
|
||||||
|
|
||||||
ImGui_ImplSDLRenderer2_Shutdown();
|
|
||||||
ImGui_ImplSDL2_Shutdown();
|
|
||||||
ImGui::DestroyContext();
|
|
||||||
|
|
||||||
SDL_DestroyRenderer(sdlRenderer);
|
|
||||||
SDL_DestroyWindow(window);
|
|
||||||
|
|
||||||
SDL_CloseAudio();
|
|
||||||
SDL_Quit();
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
874
src/gui/main_single_peer.cpp
Normal file
874
src/gui/main_single_peer.cpp
Normal file
@@ -0,0 +1,874 @@
|
|||||||
|
#include <SDL.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef REMOTE_DESK_DEBUG
|
||||||
|
#pragma comment(linker, "/subsystem:\"console\"")
|
||||||
|
#else
|
||||||
|
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstring>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "../../thirdparty/projectx/src/interface/x.h"
|
||||||
|
#include "config_center.h"
|
||||||
|
#include "device_controller_factory.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_sdl2.h"
|
||||||
|
#include "imgui_impl_sdlrenderer2.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "screen_capturer_factory.h"
|
||||||
|
|
||||||
|
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
||||||
|
|
||||||
|
#ifdef REMOTE_DESK_DEBUG
|
||||||
|
#define MOUSE_CONTROL 0
|
||||||
|
#else
|
||||||
|
#define MOUSE_CONTROL 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CHINESE_FONT 1
|
||||||
|
|
||||||
|
int screen_w = 1280, screen_h = 720;
|
||||||
|
int window_w = 1280, window_h = 720;
|
||||||
|
const int pixel_w = 1280, pixel_h = 720;
|
||||||
|
|
||||||
|
unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2];
|
||||||
|
unsigned char audio_buffer[960];
|
||||||
|
SDL_Texture *sdlTexture = nullptr;
|
||||||
|
SDL_Renderer *sdlRenderer = nullptr;
|
||||||
|
SDL_Rect sdlRect;
|
||||||
|
SDL_Window *window;
|
||||||
|
static SDL_AudioDeviceID input_dev;
|
||||||
|
static SDL_AudioDeviceID output_dev;
|
||||||
|
|
||||||
|
uint32_t start_time, end_time, elapsed_time;
|
||||||
|
uint32_t frame_count = 0;
|
||||||
|
int fps = 0;
|
||||||
|
|
||||||
|
static std::atomic<bool> audio_buffer_fresh{false};
|
||||||
|
static uint32_t last_ts = 0;
|
||||||
|
|
||||||
|
int dst_bufsize;
|
||||||
|
struct SwrContext *swr_ctx;
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
int audio_len = 0;
|
||||||
|
|
||||||
|
std::string window_title = "Remote Desk Client";
|
||||||
|
std::string connection_status_str = "-";
|
||||||
|
std::string signal_status_str = "-";
|
||||||
|
|
||||||
|
std::atomic<SignalStatus> signal_status{SignalStatus::SignalClosed};
|
||||||
|
std::atomic<ConnectionStatus> connection_status{ConnectionStatus::Closed};
|
||||||
|
|
||||||
|
// Refresh Event
|
||||||
|
#define REFRESH_EVENT (SDL_USEREVENT + 1)
|
||||||
|
#define QUIT_EVENT (SDL_USEREVENT + 2)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char password[7];
|
||||||
|
} CDCache;
|
||||||
|
|
||||||
|
int thread_exit = 0;
|
||||||
|
PeerPtr *peer_server = nullptr;
|
||||||
|
// PeerPtr *peer_server = nullptr;
|
||||||
|
bool joined = false;
|
||||||
|
bool received_frame = false;
|
||||||
|
bool menu_hovered = false;
|
||||||
|
|
||||||
|
static bool connect_button_pressed = false;
|
||||||
|
static bool fullscreen_button_pressed = false;
|
||||||
|
|
||||||
|
#if CHINESE_FONT
|
||||||
|
static const char *connect_label = u8"连接";
|
||||||
|
static const char *fullscreen_label = u8"全屏";
|
||||||
|
#else
|
||||||
|
static const char *connect_label = "Connect";
|
||||||
|
static const char *fullscreen_label = "FULLSCREEN";
|
||||||
|
#endif
|
||||||
|
static char input_password[7] = "";
|
||||||
|
static FILE *cd_cache_file = nullptr;
|
||||||
|
static CDCache cd_cache;
|
||||||
|
|
||||||
|
static bool is_create_connection = false;
|
||||||
|
static bool done = false;
|
||||||
|
|
||||||
|
ScreenCapturerFactory *screen_capturer_factory = nullptr;
|
||||||
|
ScreenCapturer *screen_capturer = nullptr;
|
||||||
|
|
||||||
|
DeviceControllerFactory *device_controller_factory = nullptr;
|
||||||
|
MouseController *mouse_controller = nullptr;
|
||||||
|
|
||||||
|
ConfigCenter config_center;
|
||||||
|
|
||||||
|
char *nv12_buffer = nullptr;
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
std::chrono::_V2::system_clock::time_point last_frame_time_;
|
||||||
|
#else
|
||||||
|
std::chrono::steady_clock::time_point last_frame_time_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline int ProcessMouseKeyEven(SDL_Event &ev) {
|
||||||
|
float ratio = (float)(1280.0 / window_w);
|
||||||
|
|
||||||
|
RemoteAction remote_action;
|
||||||
|
remote_action.m.x = (size_t)(ev.button.x * ratio);
|
||||||
|
remote_action.m.y = (size_t)(ev.button.y * ratio);
|
||||||
|
|
||||||
|
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
|
||||||
|
{
|
||||||
|
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
|
||||||
|
if (SDLK_DOWN == ev.key.keysym.sym) {
|
||||||
|
// printf("SDLK_DOWN \n");
|
||||||
|
|
||||||
|
} else if (SDLK_UP == ev.key.keysym.sym) {
|
||||||
|
// printf("SDLK_UP \n");
|
||||||
|
|
||||||
|
} else if (SDLK_LEFT == ev.key.keysym.sym) {
|
||||||
|
// printf("SDLK_LEFT \n");
|
||||||
|
|
||||||
|
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
|
||||||
|
// printf("SDLK_RIGHT \n");
|
||||||
|
}
|
||||||
|
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
|
||||||
|
remote_action.type = ControlType::mouse;
|
||||||
|
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||||
|
remote_action.m.flag = MouseFlag::left_down;
|
||||||
|
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||||
|
remote_action.m.flag = MouseFlag::right_down;
|
||||||
|
}
|
||||||
|
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||||
|
sizeof(remote_action));
|
||||||
|
} else if (SDL_MOUSEBUTTONUP == ev.type) {
|
||||||
|
remote_action.type = ControlType::mouse;
|
||||||
|
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||||
|
remote_action.m.flag = MouseFlag::left_up;
|
||||||
|
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||||
|
remote_action.m.flag = MouseFlag::right_up;
|
||||||
|
}
|
||||||
|
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||||
|
sizeof(remote_action));
|
||||||
|
} else if (SDL_MOUSEMOTION == ev.type) {
|
||||||
|
remote_action.type = ControlType::mouse;
|
||||||
|
remote_action.m.flag = MouseFlag::move;
|
||||||
|
SendData(peer_server, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||||
|
sizeof(remote_action));
|
||||||
|
} else if (SDL_QUIT == ev.type) {
|
||||||
|
SDL_Event event;
|
||||||
|
event.type = SDL_QUIT;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
printf("SDL_QUIT\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
||||||
|
if (1) {
|
||||||
|
if ("Connected" == connection_status_str) {
|
||||||
|
SendData(peer_server, DATA_TYPE::AUDIO, (const char *)stream, len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(audio_buffer, stream, len);
|
||||||
|
audio_len = len;
|
||||||
|
SDL_Delay(10);
|
||||||
|
audio_buffer_fresh = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
|
||||||
|
if (!audio_buffer_fresh) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_memset(stream, 0, len);
|
||||||
|
|
||||||
|
if (audio_len == 0) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
len = (len > audio_len ? audio_len : len);
|
||||||
|
SDL_MixAudioFormat(stream, audio_buffer, AUDIO_S16LSB, len,
|
||||||
|
SDL_MIX_MAXVOLUME);
|
||||||
|
audio_buffer_fresh = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerReceiveVideoBuffer(const char *data, size_t size,
|
||||||
|
const char *user_id, size_t user_id_size) {
|
||||||
|
if (joined) {
|
||||||
|
memcpy(dst_buffer, data, size);
|
||||||
|
|
||||||
|
SDL_Event event;
|
||||||
|
event.type = REFRESH_EVENT;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
received_frame = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientReceiveVideoBuffer(const char *data, size_t size,
|
||||||
|
const char *user_id, size_t user_id_size) {
|
||||||
|
// std::cout << "Receive: [" << user_id << "] " << std::endl;
|
||||||
|
if (joined) {
|
||||||
|
memcpy(dst_buffer, data, size);
|
||||||
|
|
||||||
|
SDL_Event event;
|
||||||
|
event.type = REFRESH_EVENT;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
received_frame = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerReceiveAudioBuffer(const char *data, size_t size,
|
||||||
|
const char *user_id, size_t user_id_size) {
|
||||||
|
// memset(audio_buffer, 0, size);
|
||||||
|
// memcpy(audio_buffer, data, size);
|
||||||
|
// audio_len = size;
|
||||||
|
audio_buffer_fresh = true;
|
||||||
|
|
||||||
|
SDL_QueueAudio(output_dev, data, (Uint32)size);
|
||||||
|
// printf("queue audio\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientReceiveAudioBuffer(const char *data, size_t size,
|
||||||
|
const char *user_id, size_t user_id_size) {
|
||||||
|
// std::cout << "Client receive audio, size " << size << ", user [" << user_id
|
||||||
|
// << "] " << std::endl;
|
||||||
|
SDL_QueueAudio(output_dev, data, (Uint32)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id,
|
||||||
|
size_t user_id_size) {
|
||||||
|
std::string user(user_id, user_id_size);
|
||||||
|
|
||||||
|
RemoteAction remote_action;
|
||||||
|
memcpy(&remote_action, data, sizeof(remote_action));
|
||||||
|
|
||||||
|
// std::cout << "remote_action: " << remote_action.type << " "
|
||||||
|
// << remote_action.m.flag << " " << remote_action.m.x << " "
|
||||||
|
// << remote_action.m.y << std::endl;
|
||||||
|
#if MOUSE_CONTROL
|
||||||
|
if (mouse_controller) {
|
||||||
|
mouse_controller->SendCommand(remote_action);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientReceiveDataBuffer(const char *data, size_t size, const char *user_id,
|
||||||
|
size_t user_id_size) {}
|
||||||
|
|
||||||
|
void SignalStatus(SignalStatus status) {
|
||||||
|
signal_status = status;
|
||||||
|
if (SignalStatus::SignalConnecting == status) {
|
||||||
|
signal_status_str = "SignalConnecting";
|
||||||
|
} else if (SignalStatus::SignalConnected == status) {
|
||||||
|
signal_status_str = "SignalConnected";
|
||||||
|
} else if (SignalStatus::SignalFailed == status) {
|
||||||
|
signal_status_str = "SignalFailed";
|
||||||
|
} else if (SignalStatus::SignalClosed == status) {
|
||||||
|
signal_status_str = "SignalClosed";
|
||||||
|
} else if (SignalStatus::SignalReconnecting == status) {
|
||||||
|
signal_status_str = "SignalReconnecting";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionStatus(ConnectionStatus status) {
|
||||||
|
connection_status = status;
|
||||||
|
if (ConnectionStatus::Connecting == status) {
|
||||||
|
connection_status_str = "Connecting";
|
||||||
|
} else if (ConnectionStatus::Connected == status) {
|
||||||
|
connection_status_str = "Connected";
|
||||||
|
joined = true;
|
||||||
|
} else if (ConnectionStatus::Disconnected == status) {
|
||||||
|
connection_status_str = "Disconnected";
|
||||||
|
} else if (ConnectionStatus::Failed == status) {
|
||||||
|
connection_status_str = "Failed";
|
||||||
|
} else if (ConnectionStatus::Closed == status) {
|
||||||
|
connection_status_str = "Closed";
|
||||||
|
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||||
|
connection_status_str = "Incorrect password";
|
||||||
|
if (connect_button_pressed) {
|
||||||
|
connect_button_pressed = false;
|
||||||
|
joined = false;
|
||||||
|
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
||||||
|
}
|
||||||
|
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
|
||||||
|
connection_status_str = "No such transmission id";
|
||||||
|
if (connect_button_pressed) {
|
||||||
|
connect_button_pressed = false;
|
||||||
|
joined = false;
|
||||||
|
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
LOG_INFO("Remote desk");
|
||||||
|
|
||||||
|
last_ts = static_cast<uint32_t>(
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||||
|
.count());
|
||||||
|
|
||||||
|
cd_cache_file = fopen("cache.cd", "r+");
|
||||||
|
if (cd_cache_file) {
|
||||||
|
fseek(cd_cache_file, 0, SEEK_SET);
|
||||||
|
fread(&cd_cache.password, sizeof(cd_cache.password), 1, cd_cache_file);
|
||||||
|
fclose(cd_cache_file);
|
||||||
|
strncpy(input_password, cd_cache.password, sizeof(cd_cache.password));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup SDL
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
|
||||||
|
SDL_INIT_GAMECONTROLLER) != 0) {
|
||||||
|
printf("Error: %s\n", SDL_GetError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From 2.0.18: Enable native IME.
|
||||||
|
#ifdef SDL_HINT_IME_SHOW_UI
|
||||||
|
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Create window with SDL_Renderer graphics context
|
||||||
|
SDL_WindowFlags window_flags =
|
||||||
|
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||||
|
window = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
|
||||||
|
SDL_WINDOWPOS_CENTERED, window_w, window_h,
|
||||||
|
window_flags);
|
||||||
|
|
||||||
|
SDL_DisplayMode DM;
|
||||||
|
SDL_GetCurrentDisplayMode(0, &DM);
|
||||||
|
screen_w = DM.w;
|
||||||
|
screen_h = DM.h;
|
||||||
|
|
||||||
|
sdlRenderer = SDL_CreateRenderer(
|
||||||
|
window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
||||||
|
if (sdlRenderer == nullptr) {
|
||||||
|
SDL_Log("Error creating SDL_Renderer!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 pixformat = 0;
|
||||||
|
pixformat = SDL_PIXELFORMAT_NV12;
|
||||||
|
|
||||||
|
sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,
|
||||||
|
SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
SDL_AudioSpec want_in, have_in, want_out, have_out;
|
||||||
|
SDL_zero(want_in);
|
||||||
|
want_in.freq = 48000;
|
||||||
|
want_in.format = AUDIO_S16LSB;
|
||||||
|
want_in.channels = 1;
|
||||||
|
want_in.samples = 480;
|
||||||
|
want_in.callback = SdlCaptureAudioIn;
|
||||||
|
|
||||||
|
input_dev = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0);
|
||||||
|
if (input_dev == 0) {
|
||||||
|
SDL_Log("Failed to open input: %s", SDL_GetError());
|
||||||
|
// return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_zero(want_out);
|
||||||
|
want_out.freq = 48000;
|
||||||
|
want_out.format = AUDIO_S16LSB;
|
||||||
|
want_out.channels = 1;
|
||||||
|
// want_out.silence = 0;
|
||||||
|
want_out.samples = 480;
|
||||||
|
want_out.callback = NULL;
|
||||||
|
|
||||||
|
output_dev = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out, 0);
|
||||||
|
if (output_dev == 0) {
|
||||||
|
SDL_Log("Failed to open input: %s", SDL_GetError());
|
||||||
|
// return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_PauseAudioDevice(input_dev, 0);
|
||||||
|
SDL_PauseAudioDevice(output_dev, 0);
|
||||||
|
|
||||||
|
// Setup Dear ImGui context
|
||||||
|
IMGUI_CHECKVERSION();
|
||||||
|
ImGui::CreateContext();
|
||||||
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
|
|
||||||
|
io.ConfigFlags |=
|
||||||
|
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||||
|
io.ConfigFlags |=
|
||||||
|
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||||
|
|
||||||
|
#if CHINESE_FONT
|
||||||
|
// Load Fonts
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::string default_font_path = "c:/windows/fonts/simhei.ttf";
|
||||||
|
std::ifstream font_path_f(default_font_path.c_str());
|
||||||
|
std::string font_path =
|
||||||
|
font_path_f.good() ? "c:/windows/fonts/simhei.ttf" : "";
|
||||||
|
if (!font_path.empty()) {
|
||||||
|
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
|
||||||
|
io.Fonts->GetGlyphRangesChineseFull());
|
||||||
|
}
|
||||||
|
#elif __APPLE__
|
||||||
|
std::string default_font_path = "/System/Library/Fonts/PingFang.ttc";
|
||||||
|
std::ifstream font_path_f(default_font_path.c_str());
|
||||||
|
std::string font_path =
|
||||||
|
font_path_f.good() ? "/System/Library/Fonts/PingFang.ttc" : "";
|
||||||
|
if (!font_path.empty()) {
|
||||||
|
io.Fonts->AddFontFromFileTTF(font_path.c_str(), 13.0f, NULL,
|
||||||
|
io.Fonts->GetGlyphRangesChineseFull());
|
||||||
|
}
|
||||||
|
#elif __linux__
|
||||||
|
io.Fonts->AddFontFromFileTTF("c:/windows/fonts/msyh.ttc", 13.0f, NULL,
|
||||||
|
io.Fonts->GetGlyphRangesChineseFull());
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Setup Dear ImGui style
|
||||||
|
// ImGui::StyleColorsDark();
|
||||||
|
ImGui::StyleColorsLight();
|
||||||
|
|
||||||
|
// Setup Platform/Renderer backends
|
||||||
|
ImGui_ImplSDL2_InitForSDLRenderer(window, sdlRenderer);
|
||||||
|
ImGui_ImplSDLRenderer2_Init(sdlRenderer);
|
||||||
|
|
||||||
|
// Our state
|
||||||
|
bool show_demo_window = true;
|
||||||
|
bool show_another_window = false;
|
||||||
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||||
|
|
||||||
|
std::string mac_addr_str = GetMac();
|
||||||
|
|
||||||
|
std::thread rtc_thread(
|
||||||
|
[](int screen_width, int screen_height) {
|
||||||
|
std::string default_cfg_path = "../../../../config/config.ini";
|
||||||
|
std::ifstream f(default_cfg_path.c_str());
|
||||||
|
|
||||||
|
std::string mac_addr_str = GetMac();
|
||||||
|
|
||||||
|
Params server_params;
|
||||||
|
server_params.cfg_path =
|
||||||
|
f.good() ? "../../../../config/config.ini" : "config.ini";
|
||||||
|
server_params.on_receive_video_buffer = ServerReceiveVideoBuffer;
|
||||||
|
server_params.on_receive_audio_buffer = ServerReceiveAudioBuffer;
|
||||||
|
server_params.on_receive_data_buffer = ServerReceiveDataBuffer;
|
||||||
|
server_params.on_signal_status = SignalStatus;
|
||||||
|
server_params.on_connection_status = ConnectionStatus;
|
||||||
|
|
||||||
|
std::string transmission_id = "000001";
|
||||||
|
|
||||||
|
peer_server = CreatePeer(&server_params);
|
||||||
|
LOG_INFO("Create peer_server");
|
||||||
|
std::string server_user_id = "S-" + mac_addr_str;
|
||||||
|
Init(peer_server, server_user_id.c_str());
|
||||||
|
LOG_INFO("peer_server init finish");
|
||||||
|
|
||||||
|
{
|
||||||
|
while (SignalStatus::SignalConnected != signal_status && !done) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string user_id = "S-" + mac_addr_str;
|
||||||
|
is_create_connection =
|
||||||
|
CreateConnection(peer_server, mac_addr_str.c_str(),
|
||||||
|
input_password)
|
||||||
|
? false
|
||||||
|
: true;
|
||||||
|
|
||||||
|
nv12_buffer = new char[NV12_BUFFER_SIZE];
|
||||||
|
|
||||||
|
// Screen capture
|
||||||
|
screen_capturer_factory = new ScreenCapturerFactory();
|
||||||
|
screen_capturer = (ScreenCapturer *)screen_capturer_factory->Create();
|
||||||
|
|
||||||
|
last_frame_time_ = std::chrono::high_resolution_clock::now();
|
||||||
|
ScreenCapturer::RECORD_DESKTOP_RECT rect;
|
||||||
|
rect.left = 0;
|
||||||
|
rect.top = 0;
|
||||||
|
rect.right = screen_w;
|
||||||
|
rect.bottom = screen_h;
|
||||||
|
|
||||||
|
int screen_capturer_init_ret = screen_capturer->Init(
|
||||||
|
rect, 60,
|
||||||
|
[](unsigned char *data, int size, int width, int height) -> void {
|
||||||
|
auto now_time = std::chrono::high_resolution_clock::now();
|
||||||
|
std::chrono::duration<double> duration =
|
||||||
|
now_time - last_frame_time_;
|
||||||
|
auto tc = duration.count() * 1000;
|
||||||
|
|
||||||
|
if (tc >= 0) {
|
||||||
|
SendData(peer_server, DATA_TYPE::VIDEO, (const char *)data,
|
||||||
|
NV12_BUFFER_SIZE);
|
||||||
|
last_frame_time_ = now_time;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (0 == screen_capturer_init_ret) {
|
||||||
|
screen_capturer->Start();
|
||||||
|
} else {
|
||||||
|
screen_capturer->Destroy();
|
||||||
|
screen_capturer = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mouse control
|
||||||
|
device_controller_factory = new DeviceControllerFactory();
|
||||||
|
mouse_controller =
|
||||||
|
(MouseController *)device_controller_factory->Create(
|
||||||
|
DeviceControllerFactory::Device::Mouse);
|
||||||
|
int mouse_controller_init_ret =
|
||||||
|
mouse_controller->Init(screen_w, screen_h);
|
||||||
|
if (0 != mouse_controller_init_ret) {
|
||||||
|
mouse_controller->Destroy();
|
||||||
|
mouse_controller = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
screen_w, screen_h);
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
while (!done) {
|
||||||
|
// Start the Dear ImGui frame
|
||||||
|
ImGui_ImplSDLRenderer2_NewFrame();
|
||||||
|
ImGui_ImplSDL2_NewFrame();
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
if (joined && !menu_hovered) {
|
||||||
|
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
static float f = 0.0f;
|
||||||
|
static int counter = 0;
|
||||||
|
|
||||||
|
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
|
||||||
|
|
||||||
|
// ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
|
||||||
|
|
||||||
|
#if CHINESE_FONT
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(160, 210));
|
||||||
|
#else
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(180, 210));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CHINESE_FONT
|
||||||
|
if (!joined) {
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||||
|
ImGui::Begin(u8"菜单", nullptr,
|
||||||
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||||
|
ImGuiWindowFlags_NoMove);
|
||||||
|
} else {
|
||||||
|
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||||
|
ImGui::Begin(u8"菜单", nullptr, ImGuiWindowFlags_None);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!joined) {
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||||
|
ImGui::Begin("Menu", nullptr,
|
||||||
|
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||||
|
ImGuiWindowFlags_NoMove);
|
||||||
|
} else {
|
||||||
|
ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
|
||||||
|
ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_None);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{
|
||||||
|
menu_hovered = ImGui::IsWindowHovered();
|
||||||
|
#if CHINESE_FONT
|
||||||
|
ImGui::Text(u8"本机ID:");
|
||||||
|
#else
|
||||||
|
ImGui::Text("LOCAL ID:");
|
||||||
|
#endif
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(90);
|
||||||
|
#if CHINESE_FONT
|
||||||
|
ImGui::SetCursorPosX(60.0f);
|
||||||
|
#else
|
||||||
|
ImGui::SetCursorPosX(80.0f);
|
||||||
|
#endif
|
||||||
|
ImGui::InputText(
|
||||||
|
"##local_id", (char *)mac_addr_str.c_str(),
|
||||||
|
mac_addr_str.length() + 1,
|
||||||
|
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
|
||||||
|
|
||||||
|
#if CHINESE_FONT
|
||||||
|
ImGui::Text(u8"密码:");
|
||||||
|
#else
|
||||||
|
ImGui::Text("PASSWORD:");
|
||||||
|
#endif
|
||||||
|
ImGui::SameLine();
|
||||||
|
char input_password_tmp[7] = "";
|
||||||
|
std::string input_password_str = "123456";
|
||||||
|
strncpy(input_password_tmp, input_password, sizeof(input_password));
|
||||||
|
ImGui::SetNextItemWidth(90);
|
||||||
|
#if CHINESE_FONT
|
||||||
|
ImGui::SetCursorPosX(60.0f);
|
||||||
|
ImGui::InputTextWithHint("##server_pwd", u8"最长6个字符",
|
||||||
|
input_password, IM_ARRAYSIZE(input_password),
|
||||||
|
ImGuiInputTextFlags_CharsNoBlank);
|
||||||
|
#else
|
||||||
|
ImGui::SetCursorPosX(80.0f);
|
||||||
|
ImGui::InputTextWithHint("##server_pwd", "max 6 chars", input_password,
|
||||||
|
IM_ARRAYSIZE(input_password),
|
||||||
|
ImGuiInputTextFlags_CharsNoBlank);
|
||||||
|
#endif
|
||||||
|
if (strcmp(input_password_tmp, input_password)) {
|
||||||
|
cd_cache_file = fopen("cache.cd", "w+");
|
||||||
|
if (cd_cache_file) {
|
||||||
|
fseek(cd_cache_file, 0, SEEK_SET);
|
||||||
|
strncpy(cd_cache.password, input_password, sizeof(input_password));
|
||||||
|
fwrite(&cd_cache.password, sizeof(cd_cache.password), 1,
|
||||||
|
cd_cache_file);
|
||||||
|
fclose(cd_cache_file);
|
||||||
|
}
|
||||||
|
LeaveConnection(peer_server);
|
||||||
|
CreateConnection(peer_server, mac_addr_str.c_str(), input_password);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
{
|
||||||
|
{
|
||||||
|
static char remote_id[20] = "";
|
||||||
|
#if CHINESE_FONT
|
||||||
|
ImGui::Text(u8"远端ID:");
|
||||||
|
#else
|
||||||
|
ImGui::Text("REMOTE ID:");
|
||||||
|
#endif
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(90);
|
||||||
|
#if CHINESE_FONT
|
||||||
|
ImGui::SetCursorPosX(60.0f);
|
||||||
|
#else
|
||||||
|
ImGui::SetCursorPosX(80.0f);
|
||||||
|
#endif
|
||||||
|
ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(),
|
||||||
|
remote_id, IM_ARRAYSIZE(remote_id),
|
||||||
|
ImGuiInputTextFlags_CharsUppercase |
|
||||||
|
ImGuiInputTextFlags_CharsNoBlank);
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
#if CHINESE_FONT
|
||||||
|
ImGui::Text(u8"密码:");
|
||||||
|
#else
|
||||||
|
ImGui::Text("PASSWORD:");
|
||||||
|
#endif
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(90);
|
||||||
|
static char client_password[20] = "";
|
||||||
|
#if CHINESE_FONT
|
||||||
|
ImGui::SetCursorPosX(60.0f);
|
||||||
|
ImGui::InputTextWithHint("##client_pwd", u8"最长6个字符",
|
||||||
|
client_password,
|
||||||
|
IM_ARRAYSIZE(client_password),
|
||||||
|
ImGuiInputTextFlags_CharsNoBlank);
|
||||||
|
#else
|
||||||
|
ImGui::SetCursorPosX(80.0f);
|
||||||
|
ImGui::InputTextWithHint("##client_pwd", "max 6 chars",
|
||||||
|
client_password,
|
||||||
|
IM_ARRAYSIZE(client_password),
|
||||||
|
ImGuiInputTextFlags_CharsNoBlank);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
if (ImGui::Button(connect_label)) {
|
||||||
|
int ret = -1;
|
||||||
|
if ("SignalConnected" == signal_status_str) {
|
||||||
|
#if CHINESE_FONT
|
||||||
|
if (strcmp(connect_label, u8"连接") == 0 && !joined) {
|
||||||
|
#else
|
||||||
|
if (strcmp(connect_label, "Connect") == 0 && !joined) {
|
||||||
|
#endif
|
||||||
|
std::string user_id = "C-" + mac_addr_str;
|
||||||
|
ret = JoinConnection(peer_server, remote_id, client_password);
|
||||||
|
if (0 == ret) {
|
||||||
|
// joined = true;
|
||||||
|
}
|
||||||
|
#if CHINESE_FONT
|
||||||
|
} else if (strcmp(connect_label, u8"断开连接") == 0 && joined) {
|
||||||
|
#else
|
||||||
|
} else if (strcmp(connect_label, "Disconnect") == 0 && joined) {
|
||||||
|
#endif
|
||||||
|
ret = LeaveConnection(peer_server);
|
||||||
|
CreateConnection(peer_server, mac_addr_str.c_str(),
|
||||||
|
input_password);
|
||||||
|
memset(audio_buffer, 0, 960);
|
||||||
|
if (0 == ret) {
|
||||||
|
joined = false;
|
||||||
|
received_frame = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == ret) {
|
||||||
|
connect_button_pressed = !connect_button_pressed;
|
||||||
|
#if CHINESE_FONT
|
||||||
|
connect_label =
|
||||||
|
connect_button_pressed ? u8"断开连接" : u8"连接";
|
||||||
|
#else
|
||||||
|
connect_label =
|
||||||
|
connect_button_pressed ? "Disconnect" : "Connect";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
#if CHINESE_FONT
|
||||||
|
if (ImGui::Button(fullscreen_label)) {
|
||||||
|
if (strcmp(fullscreen_label, u8"全屏") == 0) {
|
||||||
|
#else
|
||||||
|
if (ImGui::Button(fullscreen_label)) {
|
||||||
|
if (strcmp(fullscreen_label, "FULLSCREEN") == 0) {
|
||||||
|
#endif
|
||||||
|
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
||||||
|
} else {
|
||||||
|
SDL_SetWindowFullscreen(window, SDL_FALSE);
|
||||||
|
}
|
||||||
|
fullscreen_button_pressed = !fullscreen_button_pressed;
|
||||||
|
#if CHINESE_FONT
|
||||||
|
fullscreen_label = fullscreen_button_pressed ? u8"退出全屏" : u8"全屏";
|
||||||
|
#else
|
||||||
|
fullscreen_label =
|
||||||
|
fullscreen_button_pressed ? "EXIT FULLSCREEN" : "FULLSCREEN";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rendering
|
||||||
|
ImGui::Render();
|
||||||
|
SDL_RenderSetScale(sdlRenderer, io.DisplayFramebufferScale.x,
|
||||||
|
io.DisplayFramebufferScale.y);
|
||||||
|
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||||
|
if (event.type == SDL_QUIT) {
|
||||||
|
done = true;
|
||||||
|
} else if (event.type == SDL_WINDOWEVENT &&
|
||||||
|
event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
|
// SDL_GetWindowSize(window, &window_w, &window_h);
|
||||||
|
|
||||||
|
int window_w_last = window_w;
|
||||||
|
int window_h_last = window_h;
|
||||||
|
|
||||||
|
SDL_GetWindowSize(window, &window_w, &window_h);
|
||||||
|
|
||||||
|
int w_change_ratio = abs(window_w - window_w_last) / 16;
|
||||||
|
int h_change_ratio = abs(window_h - window_h_last) / 9;
|
||||||
|
|
||||||
|
if (w_change_ratio > h_change_ratio) {
|
||||||
|
window_h = window_w * 9 / 16;
|
||||||
|
} else {
|
||||||
|
window_w = window_h * 16 / 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_SetWindowSize(window, window_w, window_h);
|
||||||
|
} else if (event.type == SDL_WINDOWEVENT &&
|
||||||
|
event.window.event == SDL_WINDOWEVENT_CLOSE &&
|
||||||
|
event.window.windowID == SDL_GetWindowID(window)) {
|
||||||
|
done = true;
|
||||||
|
} else if (event.type == REFRESH_EVENT) {
|
||||||
|
sdlRect.x = 0;
|
||||||
|
sdlRect.y = 0;
|
||||||
|
sdlRect.w = window_w;
|
||||||
|
sdlRect.h = window_h;
|
||||||
|
|
||||||
|
SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w);
|
||||||
|
} else {
|
||||||
|
if (joined) {
|
||||||
|
ProcessMouseKeyEven(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderClear(sdlRenderer);
|
||||||
|
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
|
||||||
|
|
||||||
|
if (!joined || !received_frame) {
|
||||||
|
SDL_RenderClear(sdlRenderer);
|
||||||
|
SDL_SetRenderDrawColor(
|
||||||
|
sdlRenderer, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
|
||||||
|
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
SDL_RenderPresent(sdlRenderer);
|
||||||
|
|
||||||
|
frame_count++;
|
||||||
|
end_time = SDL_GetTicks();
|
||||||
|
elapsed_time = end_time - start_time;
|
||||||
|
if (elapsed_time >= 1000) {
|
||||||
|
fps = frame_count / (elapsed_time / 1000);
|
||||||
|
frame_count = 0;
|
||||||
|
window_title = "Remote Desk Client FPS [" + std::to_string(fps) +
|
||||||
|
"] status [" + signal_status_str + "|" +
|
||||||
|
connection_status_str + "]";
|
||||||
|
// For MacOS, UI frameworks can only be called from the main thread
|
||||||
|
SDL_SetWindowTitle(window, window_title.c_str());
|
||||||
|
start_time = end_time;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
|
||||||
|
if (is_create_connection) {
|
||||||
|
LeaveConnection(peer_server);
|
||||||
|
}
|
||||||
|
|
||||||
|
rtc_thread.join();
|
||||||
|
SDL_CloseAudioDevice(output_dev);
|
||||||
|
SDL_CloseAudioDevice(input_dev);
|
||||||
|
|
||||||
|
if (screen_capturer) {
|
||||||
|
screen_capturer->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mouse_controller) {
|
||||||
|
mouse_controller->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplSDLRenderer2_Shutdown();
|
||||||
|
ImGui_ImplSDL2_Shutdown();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
|
SDL_DestroyRenderer(sdlRenderer);
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
|
||||||
|
SDL_CloseAudio();
|
||||||
|
SDL_Quit();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
47
src/localization/localization.h
Normal file
47
src/localization/localization.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2024-05-29
|
||||||
|
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
#ifndef _LOCALIZATION_H_
|
||||||
|
#define _LOCALIZATION_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
namespace localization {
|
||||||
|
|
||||||
|
static std::vector<std::string> menu = {u8"菜单", "Menu"};
|
||||||
|
static std::vector<std::string> local_id = {u8"本机ID:", "Local ID:"};
|
||||||
|
static std::vector<std::string> password = {u8"密码:", "Password:"};
|
||||||
|
static std::vector<std::string> max_password_len = {u8"最大6个字符",
|
||||||
|
"Max 6 chars"};
|
||||||
|
static std::vector<std::string> remote_id = {u8"对端ID:", "Remote ID:"};
|
||||||
|
static std::vector<std::string> connect = {u8"连接", "Connect"};
|
||||||
|
static std::vector<std::string> disconnect = {u8"断开连接", "Disconnect"};
|
||||||
|
static std::vector<std::string> fullscreen = {u8"全屏", "Fullscreen"};
|
||||||
|
static std::vector<std::string> exit_fullscreen = {u8"退出全屏",
|
||||||
|
"Exit fullscreen"};
|
||||||
|
static std::vector<std::string> control_mouse = {u8"控制鼠标", "Mouse Control"};
|
||||||
|
static std::vector<std::string> release_mouse = {u8"释放鼠标", "Release Mouse"};
|
||||||
|
static std::vector<std::string> settings = {u8"设置", "Settings"};
|
||||||
|
static std::vector<std::string> language = {u8"语言:", "Language:"};
|
||||||
|
static std::vector<std::string> language_zh = {u8"中文", "Chinese"};
|
||||||
|
static std::vector<std::string> language_en = {u8"英文", "English"};
|
||||||
|
static std::vector<std::string> video_quality = {u8"视频质量:",
|
||||||
|
"Video Quality:"};
|
||||||
|
static std::vector<std::string> video_quality_high = {u8"高", "High"};
|
||||||
|
static std::vector<std::string> video_quality_medium = {u8"中", "Medium"};
|
||||||
|
static std::vector<std::string> video_quality_low = {u8"低", "Low"};
|
||||||
|
static std::vector<std::string> video_encode_format = {u8"视频编码格式:",
|
||||||
|
"Video Encode Format:"};
|
||||||
|
static std::vector<std::string> av1 = {u8"AV1", "AV1"};
|
||||||
|
static std::vector<std::string> h264 = {u8"H.264", "H.264"};
|
||||||
|
static std::vector<std::string> enable_hardware_video_codec = {
|
||||||
|
u8"启用硬件编解码器:", "Enable Hardware Video Codec:"};
|
||||||
|
|
||||||
|
static std::vector<std::string> ok = {u8"确认", "OK"};
|
||||||
|
static std::vector<std::string> cancel = {u8"取消", "Cancel"};
|
||||||
|
|
||||||
|
} // namespace localization
|
||||||
|
|
||||||
|
#endif
|
||||||
81
src/main_window/layout_style.h
Normal file
81
src/main_window/layout_style.h
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2024-06-14
|
||||||
|
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _LAYOUT_STYLE_H_
|
||||||
|
#define _LAYOUT_STYLE_H_
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define MENU_WINDOW_WIDTH_CN 160
|
||||||
|
#define MENU_WINDOW_HEIGHT_CN 245
|
||||||
|
#define MENU_WINDOW_WIDTH_EN 190
|
||||||
|
#define MENU_WINDOW_HEIGHT_EN 245
|
||||||
|
#define IPUT_WINDOW_WIDTH 86
|
||||||
|
#define INPUT_WINDOW_PADDING_CN 66
|
||||||
|
#define INPUT_WINDOW_PADDING_EN 96
|
||||||
|
#define SETTINGS_WINDOW_WIDTH_CN 181
|
||||||
|
#define SETTINGS_WINDOW_WIDTH_EN 228
|
||||||
|
#define SETTINGS_WINDOW_HEIGHT_CN 190
|
||||||
|
#define SETTINGS_WINDOW_HEIGHT_EN 190
|
||||||
|
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
|
||||||
|
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 147
|
||||||
|
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
|
||||||
|
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 147
|
||||||
|
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
|
||||||
|
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 147
|
||||||
|
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 154
|
||||||
|
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201
|
||||||
|
#define SETTINGS_SELECT_WINDOW_WIDTH 73
|
||||||
|
#define SETTINGS_OK_BUTTON_PADDING_CN 55
|
||||||
|
#define SETTINGS_OK_BUTTON_PADDING_EN 78
|
||||||
|
#elif __linux__
|
||||||
|
#define MENU_WINDOW_WIDTH_CN 160
|
||||||
|
#define MENU_WINDOW_HEIGHT_CN 245
|
||||||
|
#define MENU_WINDOW_WIDTH_EN 190
|
||||||
|
#define MENU_WINDOW_HEIGHT_EN 245
|
||||||
|
#define IPUT_WINDOW_WIDTH 90
|
||||||
|
#define INPUT_WINDOW_PADDING_CN 60
|
||||||
|
#define INPUT_WINDOW_PADDING_EN 80
|
||||||
|
#define SETTINGS_WINDOW_WIDTH_CN 188
|
||||||
|
#define SETTINGS_WINDOW_WIDTH_EN 228
|
||||||
|
#define SETTINGS_WINDOW_HEIGHT_CN 190
|
||||||
|
#define SETTINGS_WINDOW_HEIGHT_EN 190
|
||||||
|
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 100
|
||||||
|
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 140
|
||||||
|
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 100
|
||||||
|
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 140
|
||||||
|
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 100
|
||||||
|
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 140
|
||||||
|
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 161
|
||||||
|
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 201
|
||||||
|
#define SETTINGS_SELECT_WINDOW_WIDTH 60
|
||||||
|
#define SETTINGS_OK_BUTTON_PADDING_CN 60
|
||||||
|
#define SETTINGS_OK_BUTTON_PADDING_EN 80
|
||||||
|
#elif __APPLE__
|
||||||
|
#define MENU_WINDOW_WIDTH_CN 148
|
||||||
|
#define MENU_WINDOW_HEIGHT_CN 244
|
||||||
|
#define MENU_WINDOW_WIDTH_EN 148
|
||||||
|
#define MENU_WINDOW_HEIGHT_EN 244
|
||||||
|
#define IPUT_WINDOW_WIDTH 77
|
||||||
|
#define INPUT_WINDOW_PADDING_CN 63
|
||||||
|
#define INPUT_WINDOW_PADDING_EN 63
|
||||||
|
#define SETTINGS_WINDOW_WIDTH_CN 160
|
||||||
|
#define SETTINGS_WINDOW_WIDTH_EN 220
|
||||||
|
#define SETTINGS_WINDOW_HEIGHT_CN 190
|
||||||
|
#define SETTINGS_WINDOW_HEIGHT_EN 190
|
||||||
|
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 90
|
||||||
|
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 150
|
||||||
|
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 90
|
||||||
|
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 150
|
||||||
|
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 90
|
||||||
|
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 150
|
||||||
|
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_CN 133
|
||||||
|
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECK_WINDOW_PADDING_EN 193
|
||||||
|
#define SETTINGS_SELECT_WINDOW_WIDTH 62
|
||||||
|
#define SETTINGS_OK_BUTTON_PADDING_CN 50
|
||||||
|
#define SETTINGS_OK_BUTTON_PADDING_EN 80
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
888
src/main_window/main_window.cpp
Normal file
888
src/main_window/main_window.cpp
Normal file
@@ -0,0 +1,888 @@
|
|||||||
|
#include "main_window.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "device_controller_factory.h"
|
||||||
|
#include "layout_style.h"
|
||||||
|
#include "localization.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "screen_capturer_factory.h"
|
||||||
|
|
||||||
|
// Refresh Event
|
||||||
|
#define REFRESH_EVENT (SDL_USEREVENT + 1)
|
||||||
|
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
||||||
|
|
||||||
|
MainWindow::MainWindow() {}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow() {}
|
||||||
|
|
||||||
|
int MainWindow::SaveSettingsIntoCacheFile() {
|
||||||
|
cd_cache_file_ = fopen("cache.cd", "w+");
|
||||||
|
if (!cd_cache_file_) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(cd_cache_file_, 0, SEEK_SET);
|
||||||
|
strncpy(cd_cache_.password, input_password_, sizeof(input_password_));
|
||||||
|
memcpy(&cd_cache_.language, &language_button_value_,
|
||||||
|
sizeof(language_button_value_));
|
||||||
|
memcpy(&cd_cache_.video_quality, &video_quality_button_value_,
|
||||||
|
sizeof(video_quality_button_value_));
|
||||||
|
memcpy(&cd_cache_.video_encode_format, &video_encode_format_button_value_,
|
||||||
|
sizeof(video_encode_format_button_value_));
|
||||||
|
memcpy(&cd_cache_.enable_hardware_video_codec, &enable_hardware_video_codec_,
|
||||||
|
sizeof(enable_hardware_video_codec_));
|
||||||
|
fwrite(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
|
||||||
|
fclose(cd_cache_file_);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainWindow::LoadSettingsIntoCacheFile() {
|
||||||
|
cd_cache_file_ = fopen("cache.cd", "r+");
|
||||||
|
if (!cd_cache_file_) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(cd_cache_file_, 0, SEEK_SET);
|
||||||
|
fread(&cd_cache_, sizeof(cd_cache_), 1, cd_cache_file_);
|
||||||
|
fclose(cd_cache_file_);
|
||||||
|
strncpy(input_password_, cd_cache_.password, sizeof(cd_cache_.password));
|
||||||
|
language_button_value_ = cd_cache_.language;
|
||||||
|
video_quality_button_value_ = cd_cache_.video_quality;
|
||||||
|
video_encode_format_button_value_ = cd_cache_.video_encode_format;
|
||||||
|
enable_hardware_video_codec_ = cd_cache_.enable_hardware_video_codec;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainWindow::StartScreenCapture() {
|
||||||
|
screen_capturer_ = (ScreenCapturer *)screen_capturer_factory_->Create();
|
||||||
|
ScreenCapturer::RECORD_DESKTOP_RECT rect;
|
||||||
|
rect.left = 0;
|
||||||
|
rect.top = 0;
|
||||||
|
rect.right = screen_width_;
|
||||||
|
rect.bottom = screen_height_;
|
||||||
|
last_frame_time_ = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
int screen_capturer_init_ret = screen_capturer_->Init(
|
||||||
|
rect, 60,
|
||||||
|
[this](unsigned char *data, int size, int width, int height) -> void {
|
||||||
|
auto now_time = std::chrono::high_resolution_clock::now();
|
||||||
|
std::chrono::duration<double> duration = now_time - last_frame_time_;
|
||||||
|
auto tc = duration.count() * 1000;
|
||||||
|
|
||||||
|
if (tc >= 0) {
|
||||||
|
SendData(peer_, DATA_TYPE::VIDEO, (const char *)data,
|
||||||
|
NV12_BUFFER_SIZE);
|
||||||
|
last_frame_time_ = now_time;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (0 == screen_capturer_init_ret) {
|
||||||
|
screen_capturer_->Start();
|
||||||
|
} else {
|
||||||
|
screen_capturer_->Destroy();
|
||||||
|
delete screen_capturer_;
|
||||||
|
screen_capturer_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainWindow::StopScreenCapture() {
|
||||||
|
if (screen_capturer_) {
|
||||||
|
LOG_INFO("Destroy screen capturer")
|
||||||
|
screen_capturer_->Destroy();
|
||||||
|
delete screen_capturer_;
|
||||||
|
screen_capturer_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainWindow::StartMouseControl() {
|
||||||
|
device_controller_factory_ = new DeviceControllerFactory();
|
||||||
|
mouse_controller_ = (MouseController *)device_controller_factory_->Create(
|
||||||
|
DeviceControllerFactory::Device::Mouse);
|
||||||
|
int mouse_controller_init_ret =
|
||||||
|
mouse_controller_->Init(screen_width_, screen_height_);
|
||||||
|
if (0 != mouse_controller_init_ret) {
|
||||||
|
LOG_INFO("Destroy mouse controller")
|
||||||
|
mouse_controller_->Destroy();
|
||||||
|
mouse_controller_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainWindow::StopMouseControl() {
|
||||||
|
if (mouse_controller_) {
|
||||||
|
mouse_controller_->Destroy();
|
||||||
|
delete mouse_controller_;
|
||||||
|
mouse_controller_ = nullptr;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainWindow::CreateConnectionPeer() {
|
||||||
|
mac_addr_str_ = GetMac();
|
||||||
|
|
||||||
|
params_.use_cfg_file = false;
|
||||||
|
params_.signal_server_ip = "150.158.81.30";
|
||||||
|
params_.signal_server_port = 9099;
|
||||||
|
params_.stun_server_ip = "150.158.81.30";
|
||||||
|
params_.stun_server_port = 3478;
|
||||||
|
params_.turn_server_ip = "150.158.81.30";
|
||||||
|
params_.turn_server_port = 3478;
|
||||||
|
params_.turn_server_username = "dijunkun";
|
||||||
|
params_.turn_server_password = "dijunkunpw";
|
||||||
|
params_.hardware_acceleration = config_center_.IsHardwareVideoCodec();
|
||||||
|
params_.av1_encoding = config_center_.GetVideoEncodeFormat() ==
|
||||||
|
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1
|
||||||
|
? true
|
||||||
|
: false;
|
||||||
|
params_.on_receive_video_buffer = OnReceiveVideoBufferCb;
|
||||||
|
params_.on_receive_audio_buffer = OnReceiveAudioBufferCb;
|
||||||
|
params_.on_receive_data_buffer = OnReceiveDataBufferCb;
|
||||||
|
params_.on_signal_status = OnSignalStatusCb;
|
||||||
|
params_.on_connection_status = OnConnectionStatusCb;
|
||||||
|
params_.user_data = this;
|
||||||
|
|
||||||
|
peer_ = CreatePeer(¶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;
|
||||||
|
}
|
||||||
190
src/main_window/main_window.h
Normal file
190
src/main_window/main_window.h
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* @Author: DI JUNKUN
|
||||||
|
* @Date: 2024-05-29
|
||||||
|
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _MAIN_WINDOW_H_
|
||||||
|
#define _MAIN_WINDOW_H_
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "../../thirdparty/projectx/src/interface/x.h"
|
||||||
|
#include "config_center.h"
|
||||||
|
#include "device_controller_factory.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_sdl2.h"
|
||||||
|
#include "imgui_impl_sdlrenderer2.h"
|
||||||
|
#include "screen_capturer_factory.h"
|
||||||
|
|
||||||
|
class MainWindow {
|
||||||
|
public:
|
||||||
|
MainWindow();
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
public:
|
||||||
|
int Run();
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void OnReceiveVideoBufferCb(const char *data, size_t size,
|
||||||
|
const char *user_id, size_t user_id_size,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
static void OnReceiveAudioBufferCb(const char *data, size_t size,
|
||||||
|
const char *user_id, size_t user_id_size,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
static void OnReceiveDataBufferCb(const char *data, size_t size,
|
||||||
|
const char *user_id, size_t user_id_size,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
static void OnSignalStatusCb(SignalStatus status, void *user_data);
|
||||||
|
|
||||||
|
static void OnConnectionStatusCb(ConnectionStatus status, void *user_data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int ProcessMouseKeyEven(SDL_Event &ev);
|
||||||
|
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len);
|
||||||
|
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int SaveSettingsIntoCacheFile();
|
||||||
|
int LoadSettingsIntoCacheFile();
|
||||||
|
|
||||||
|
int StartScreenCapture();
|
||||||
|
int StopScreenCapture();
|
||||||
|
|
||||||
|
int StartMouseControl();
|
||||||
|
int StopMouseControl();
|
||||||
|
|
||||||
|
int CreateConnectionPeer();
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef struct {
|
||||||
|
char password[7];
|
||||||
|
int language;
|
||||||
|
int video_quality;
|
||||||
|
int video_encode_format;
|
||||||
|
bool enable_hardware_video_codec;
|
||||||
|
} CDCache;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FILE *cd_cache_file_ = nullptr;
|
||||||
|
CDCache cd_cache_;
|
||||||
|
|
||||||
|
ConfigCenter config_center_;
|
||||||
|
|
||||||
|
ConfigCenter::LANGUAGE localization_language_ =
|
||||||
|
ConfigCenter::LANGUAGE::CHINESE;
|
||||||
|
|
||||||
|
int localization_language_index_ = -1;
|
||||||
|
int localization_language_index_last_ = -1;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string window_title = "Remote Desk Client";
|
||||||
|
std::string mac_addr_str_ = "";
|
||||||
|
std::string connect_button_label_ = "Connect";
|
||||||
|
std::string fullscreen_button_label_ = "Fullscreen";
|
||||||
|
std::string mouse_control_button_label_ = "Mouse Control";
|
||||||
|
std::string settings_button_label_ = "Setting";
|
||||||
|
char input_password_tmp_[7] = "";
|
||||||
|
char input_password_[7] = "";
|
||||||
|
std::string local_id_ = "";
|
||||||
|
char remote_id_[20] = "";
|
||||||
|
char client_password_[20] = "";
|
||||||
|
bool is_client_mode_ = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int screen_width_ = 1280;
|
||||||
|
int screen_height_ = 720;
|
||||||
|
int main_window_width_ = 1280;
|
||||||
|
int main_window_height_ = 720;
|
||||||
|
int main_window_width_before_fullscreen_ = 1280;
|
||||||
|
int main_window_height_before_fullscreen_ = 720;
|
||||||
|
|
||||||
|
int texture_width_ = 1280;
|
||||||
|
int texture_height_ = 720;
|
||||||
|
|
||||||
|
SDL_Texture *sdl_texture_ = nullptr;
|
||||||
|
SDL_Renderer *sdl_renderer_ = nullptr;
|
||||||
|
SDL_Rect sdl_rect_;
|
||||||
|
SDL_Window *main_window_;
|
||||||
|
uint32_t pixformat_ = 0;
|
||||||
|
|
||||||
|
bool inited_ = false;
|
||||||
|
bool exit_ = false;
|
||||||
|
bool connection_established_ = false;
|
||||||
|
bool subwindow_hovered_ = false;
|
||||||
|
bool connect_button_pressed_ = false;
|
||||||
|
bool fullscreen_button_pressed_ = false;
|
||||||
|
bool mouse_control_button_pressed_ = false;
|
||||||
|
bool settings_button_pressed_ = false;
|
||||||
|
bool received_frame_ = false;
|
||||||
|
bool is_create_connection_ = false;
|
||||||
|
bool audio_buffer_fresh_ = false;
|
||||||
|
bool rejoin_ = false;
|
||||||
|
bool control_mouse_ = false;
|
||||||
|
|
||||||
|
int fps_ = 0;
|
||||||
|
uint32_t start_time_;
|
||||||
|
uint32_t end_time_;
|
||||||
|
uint32_t elapsed_time_;
|
||||||
|
uint32_t frame_count_ = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConnectionStatus connection_status_ = ConnectionStatus::Closed;
|
||||||
|
SignalStatus signal_status_ = SignalStatus::SignalClosed;
|
||||||
|
std::string signal_status_str_ = "";
|
||||||
|
std::string connection_status_str_ = "";
|
||||||
|
|
||||||
|
private:
|
||||||
|
PeerPtr *peer_ = nullptr;
|
||||||
|
PeerPtr *peer_reserved_ = nullptr;
|
||||||
|
Params params_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL_AudioDeviceID input_dev_;
|
||||||
|
SDL_AudioDeviceID output_dev_;
|
||||||
|
unsigned char audio_buffer_[960];
|
||||||
|
int audio_len_ = 0;
|
||||||
|
char *nv12_buffer_ = nullptr;
|
||||||
|
unsigned char *dst_buffer_ = new unsigned char[1280 * 720 * 3];
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScreenCapturerFactory *screen_capturer_factory_ = nullptr;
|
||||||
|
ScreenCapturer *screen_capturer_ = nullptr;
|
||||||
|
DeviceControllerFactory *device_controller_factory_ = nullptr;
|
||||||
|
MouseController *mouse_controller_ = nullptr;
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
std::chrono::_V2::system_clock::time_point last_frame_time_;
|
||||||
|
#else
|
||||||
|
std::chrono::steady_clock::time_point last_frame_time_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
int language_button_value_ = 0;
|
||||||
|
int video_quality_button_value_ = 0;
|
||||||
|
int video_encode_format_button_value_ = 0;
|
||||||
|
bool enable_hardware_video_codec_ = false;
|
||||||
|
|
||||||
|
int language_button_value_last_ = 0;
|
||||||
|
int video_quality_button_value_last_ = 0;
|
||||||
|
int video_encode_format_button_value_last_ = 0;
|
||||||
|
bool enable_hardware_video_codec_last_ = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<bool> start_screen_capture_{false};
|
||||||
|
std::atomic<bool> start_mouse_control_{false};
|
||||||
|
std::atomic<bool> screen_capture_is_started_{false};
|
||||||
|
std::atomic<bool> mouse_control_is_started_{false};
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool settings_window_pos_reset_ = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
216
src/main_window/main_window_callback_func.cpp
Normal file
216
src/main_window/main_window_callback_func.cpp
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
#include "device_controller.h"
|
||||||
|
#include "localization.h"
|
||||||
|
#include "main_window.h"
|
||||||
|
|
||||||
|
// Refresh Event
|
||||||
|
#define REFRESH_EVENT (SDL_USEREVENT + 1)
|
||||||
|
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
||||||
|
|
||||||
|
#ifdef REMOTE_DESK_DEBUG
|
||||||
|
#else
|
||||||
|
#define MOUSE_CONTROL 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int MainWindow::ProcessMouseKeyEven(SDL_Event &ev) {
|
||||||
|
if (!control_mouse_) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ratio = (float)(1280.0 / main_window_width_);
|
||||||
|
|
||||||
|
RemoteAction remote_action;
|
||||||
|
remote_action.m.x = (size_t)(ev.button.x * ratio);
|
||||||
|
remote_action.m.y = (size_t)(ev.button.y * ratio);
|
||||||
|
|
||||||
|
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
|
||||||
|
{
|
||||||
|
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
|
||||||
|
if (SDLK_DOWN == ev.key.keysym.sym) {
|
||||||
|
// printf("SDLK_DOWN \n");
|
||||||
|
|
||||||
|
} else if (SDLK_UP == ev.key.keysym.sym) {
|
||||||
|
// printf("SDLK_UP \n");
|
||||||
|
|
||||||
|
} else if (SDLK_LEFT == ev.key.keysym.sym) {
|
||||||
|
// printf("SDLK_LEFT \n");
|
||||||
|
|
||||||
|
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
|
||||||
|
// printf("SDLK_RIGHT \n");
|
||||||
|
}
|
||||||
|
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
|
||||||
|
remote_action.type = ControlType::mouse;
|
||||||
|
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||||
|
remote_action.m.flag = MouseFlag::left_down;
|
||||||
|
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||||
|
remote_action.m.flag = MouseFlag::right_down;
|
||||||
|
}
|
||||||
|
if (subwindow_hovered_) {
|
||||||
|
remote_action.m.flag = MouseFlag::move;
|
||||||
|
}
|
||||||
|
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||||
|
sizeof(remote_action));
|
||||||
|
} else if (SDL_MOUSEBUTTONUP == ev.type) {
|
||||||
|
remote_action.type = ControlType::mouse;
|
||||||
|
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||||
|
remote_action.m.flag = MouseFlag::left_up;
|
||||||
|
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||||
|
remote_action.m.flag = MouseFlag::right_up;
|
||||||
|
}
|
||||||
|
if (subwindow_hovered_) {
|
||||||
|
remote_action.m.flag = MouseFlag::move;
|
||||||
|
}
|
||||||
|
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||||
|
sizeof(remote_action));
|
||||||
|
} else if (SDL_MOUSEMOTION == ev.type) {
|
||||||
|
remote_action.type = ControlType::mouse;
|
||||||
|
remote_action.m.flag = MouseFlag::move;
|
||||||
|
SendData(peer_, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||||
|
sizeof(remote_action));
|
||||||
|
} else if (SDL_QUIT == ev.type) {
|
||||||
|
SDL_Event event;
|
||||||
|
event.type = SDL_QUIT;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
printf("SDL_QUIT\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
||||||
|
if (1) {
|
||||||
|
if ("Connected" == connection_status_str_) {
|
||||||
|
SendData(peer_, DATA_TYPE::AUDIO, (const char *)stream, len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(audio_buffer_, stream, len);
|
||||||
|
audio_len_ = len;
|
||||||
|
SDL_Delay(10);
|
||||||
|
audio_buffer_fresh_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
|
||||||
|
if (!audio_buffer_fresh_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_memset(stream, 0, len);
|
||||||
|
|
||||||
|
if (audio_len_ == 0) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
len = (len > audio_len_ ? audio_len_ : len);
|
||||||
|
SDL_MixAudioFormat(stream, audio_buffer_, AUDIO_S16LSB, len,
|
||||||
|
SDL_MIX_MAXVOLUME);
|
||||||
|
audio_buffer_fresh_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnReceiveVideoBufferCb(const char *data, size_t size,
|
||||||
|
const char *user_id,
|
||||||
|
size_t user_id_size, void *user_data) {
|
||||||
|
MainWindow *main_window = (MainWindow *)user_data;
|
||||||
|
if (main_window->connection_established_) {
|
||||||
|
memcpy(main_window->dst_buffer_, data, size);
|
||||||
|
SDL_Event event;
|
||||||
|
event.type = REFRESH_EVENT;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
main_window->received_frame_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnReceiveAudioBufferCb(const char *data, size_t size,
|
||||||
|
const char *user_id,
|
||||||
|
size_t user_id_size, void *user_data) {
|
||||||
|
MainWindow *main_window = (MainWindow *)user_data;
|
||||||
|
main_window->audio_buffer_fresh_ = true;
|
||||||
|
SDL_QueueAudio(main_window->output_dev_, data, (uint32_t)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnReceiveDataBufferCb(const char *data, size_t size,
|
||||||
|
const char *user_id, size_t user_id_size,
|
||||||
|
void *user_data) {
|
||||||
|
MainWindow *main_window = (MainWindow *)user_data;
|
||||||
|
std::string user(user_id, user_id_size);
|
||||||
|
RemoteAction remote_action;
|
||||||
|
memcpy(&remote_action, data, sizeof(remote_action));
|
||||||
|
|
||||||
|
#if MOUSE_CONTROL
|
||||||
|
if (main_window->mouse_controller_) {
|
||||||
|
main_window->mouse_controller_->SendCommand(remote_action);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnSignalStatusCb(SignalStatus status, void *user_data) {
|
||||||
|
MainWindow *main_window = (MainWindow *)user_data;
|
||||||
|
main_window->signal_status_ = status;
|
||||||
|
if (SignalStatus::SignalConnecting == status) {
|
||||||
|
main_window->signal_status_str_ = "SignalConnecting";
|
||||||
|
} else if (SignalStatus::SignalConnected == status) {
|
||||||
|
main_window->signal_status_str_ = "SignalConnected";
|
||||||
|
} else if (SignalStatus::SignalFailed == status) {
|
||||||
|
main_window->signal_status_str_ = "SignalFailed";
|
||||||
|
} else if (SignalStatus::SignalClosed == status) {
|
||||||
|
main_window->signal_status_str_ = "SignalClosed";
|
||||||
|
} else if (SignalStatus::SignalReconnecting == status) {
|
||||||
|
main_window->signal_status_str_ = "SignalReconnecting";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::OnConnectionStatusCb(ConnectionStatus status,
|
||||||
|
void *user_data) {
|
||||||
|
MainWindow *main_window = (MainWindow *)user_data;
|
||||||
|
main_window->connection_status_ = status;
|
||||||
|
if (ConnectionStatus::Connecting == status) {
|
||||||
|
main_window->connection_status_str_ = "Connecting";
|
||||||
|
} else if (ConnectionStatus::Connected == status) {
|
||||||
|
main_window->connection_status_str_ = "Connected";
|
||||||
|
main_window->connection_established_ = true;
|
||||||
|
if (!main_window->is_client_mode_) {
|
||||||
|
main_window->start_screen_capture_ = true;
|
||||||
|
main_window->start_mouse_control_ = true;
|
||||||
|
}
|
||||||
|
} else if (ConnectionStatus::Disconnected == status) {
|
||||||
|
main_window->connection_status_str_ = "Disconnected";
|
||||||
|
} else if (ConnectionStatus::Failed == status) {
|
||||||
|
main_window->connection_status_str_ = "Failed";
|
||||||
|
} else if (ConnectionStatus::Closed == status) {
|
||||||
|
main_window->connection_status_str_ = "Closed";
|
||||||
|
main_window->start_screen_capture_ = false;
|
||||||
|
main_window->start_mouse_control_ = false;
|
||||||
|
main_window->connection_established_ = false;
|
||||||
|
main_window->control_mouse_ = false;
|
||||||
|
if (main_window->dst_buffer_) {
|
||||||
|
memset(main_window->dst_buffer_, 0, 1280 * 720 * 3);
|
||||||
|
SDL_UpdateTexture(main_window->sdl_texture_, NULL,
|
||||||
|
main_window->dst_buffer_, 1280);
|
||||||
|
}
|
||||||
|
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||||
|
main_window->connection_status_str_ = "Incorrect password";
|
||||||
|
if (main_window->connect_button_pressed_) {
|
||||||
|
main_window->connect_button_pressed_ = false;
|
||||||
|
main_window->connection_established_ = false;
|
||||||
|
main_window->connect_button_label_ =
|
||||||
|
main_window->connect_button_pressed_
|
||||||
|
? localization::disconnect[main_window
|
||||||
|
->localization_language_index_]
|
||||||
|
: localization::connect[main_window
|
||||||
|
->localization_language_index_];
|
||||||
|
}
|
||||||
|
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
|
||||||
|
main_window->connection_status_str_ = "No such transmission id";
|
||||||
|
if (main_window->connect_button_pressed_) {
|
||||||
|
main_window->connect_button_pressed_ = false;
|
||||||
|
main_window->connection_established_ = false;
|
||||||
|
main_window->connect_button_label_ =
|
||||||
|
main_window->connect_button_pressed_
|
||||||
|
? localization::disconnect[main_window
|
||||||
|
->localization_language_index_]
|
||||||
|
: localization::connect[main_window
|
||||||
|
->localization_language_index_];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,12 @@ unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
|
|||||||
|
|
||||||
ScreenCapturerX11::ScreenCapturerX11() {}
|
ScreenCapturerX11::ScreenCapturerX11() {}
|
||||||
|
|
||||||
ScreenCapturerX11::~ScreenCapturerX11() {}
|
ScreenCapturerX11::~ScreenCapturerX11() {
|
||||||
|
if (inited_ && capture_thread_->joinable()) {
|
||||||
|
capture_thread_->join();
|
||||||
|
inited_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||||
cb_desktop_data cb) {
|
cb_desktop_data cb) {
|
||||||
@@ -91,17 +96,13 @@ int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
|||||||
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
|
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
|
||||||
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
|
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
|
||||||
|
|
||||||
return 0;
|
inited_ = true;
|
||||||
}
|
|
||||||
|
|
||||||
int ScreenCapturerX11::Destroy() {
|
|
||||||
if (capture_thread_->joinable()) {
|
|
||||||
capture_thread_->join();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ScreenCapturerX11::Destroy() { return 0; }
|
||||||
|
|
||||||
int ScreenCapturerX11::Start() {
|
int ScreenCapturerX11::Start() {
|
||||||
capture_thread_.reset(new std::thread([this]() {
|
capture_thread_.reset(new std::thread([this]() {
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -132,12 +133,12 @@ int ScreenCapturerX11::Start() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ScreenCapturerX11::Stop() { return 0; }
|
||||||
|
|
||||||
int ScreenCapturerX11::Pause() { return 0; }
|
int ScreenCapturerX11::Pause() { return 0; }
|
||||||
|
|
||||||
int ScreenCapturerX11::Resume() { return 0; }
|
int ScreenCapturerX11::Resume() { return 0; }
|
||||||
|
|
||||||
int ScreenCapturerX11::Stop() { return 0; }
|
|
||||||
|
|
||||||
void ScreenCapturerX11::OnFrame() {}
|
void ScreenCapturerX11::OnFrame() {}
|
||||||
|
|
||||||
void ScreenCapturerX11::CleanUp() {}
|
void ScreenCapturerX11::CleanUp() {}
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
|||||||
|
|
||||||
virtual int Start();
|
virtual int Start();
|
||||||
|
|
||||||
|
virtual int Stop();
|
||||||
|
|
||||||
int Pause();
|
int Pause();
|
||||||
int Resume();
|
int Resume();
|
||||||
int Stop();
|
|
||||||
|
|
||||||
void OnFrame();
|
void OnFrame();
|
||||||
|
|
||||||
@@ -61,6 +62,8 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
|||||||
int videoindex_ = 0;
|
int videoindex_ = 0;
|
||||||
int got_picture_ = 0;
|
int got_picture_ = 0;
|
||||||
int fps_ = 0;
|
int fps_ = 0;
|
||||||
|
bool inited_ = false;
|
||||||
|
|
||||||
// ffmpeg
|
// ffmpeg
|
||||||
AVFormatContext *pFormatCtx_ = nullptr;
|
AVFormatContext *pFormatCtx_ = nullptr;
|
||||||
AVCodecContext *pCodecCtx_ = nullptr;
|
AVCodecContext *pCodecCtx_ = nullptr;
|
||||||
|
|||||||
@@ -9,7 +9,12 @@ unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
|
|||||||
|
|
||||||
ScreenCapturerAvf::ScreenCapturerAvf() {}
|
ScreenCapturerAvf::ScreenCapturerAvf() {}
|
||||||
|
|
||||||
ScreenCapturerAvf::~ScreenCapturerAvf() {}
|
ScreenCapturerAvf::~ScreenCapturerAvf() {
|
||||||
|
if (inited_ && capture_thread_->joinable()) {
|
||||||
|
capture_thread_->join();
|
||||||
|
inited_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||||
cb_desktop_data cb) {
|
cb_desktop_data cb) {
|
||||||
@@ -92,17 +97,13 @@ int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
|||||||
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
|
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
|
||||||
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
|
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
|
||||||
|
|
||||||
return 0;
|
inited_ = true;
|
||||||
}
|
|
||||||
|
|
||||||
int ScreenCapturerAvf::Destroy() {
|
|
||||||
if (capture_thread_->joinable()) {
|
|
||||||
capture_thread_->join();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ScreenCapturerAvf::Destroy() { return 0; }
|
||||||
|
|
||||||
int ScreenCapturerAvf::Start() {
|
int ScreenCapturerAvf::Start() {
|
||||||
capture_thread_.reset(new std::thread([this]() {
|
capture_thread_.reset(new std::thread([this]() {
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -133,12 +134,12 @@ int ScreenCapturerAvf::Start() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ScreenCapturerAvf::Stop() { return 0; }
|
||||||
|
|
||||||
int ScreenCapturerAvf::Pause() { return 0; }
|
int ScreenCapturerAvf::Pause() { return 0; }
|
||||||
|
|
||||||
int ScreenCapturerAvf::Resume() { return 0; }
|
int ScreenCapturerAvf::Resume() { return 0; }
|
||||||
|
|
||||||
int ScreenCapturerAvf::Stop() { return 0; }
|
|
||||||
|
|
||||||
void ScreenCapturerAvf::OnFrame() {}
|
void ScreenCapturerAvf::OnFrame() {}
|
||||||
|
|
||||||
void ScreenCapturerAvf::CleanUp() {}
|
void ScreenCapturerAvf::CleanUp() {}
|
||||||
|
|||||||
@@ -38,9 +38,10 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
|||||||
|
|
||||||
virtual int Start();
|
virtual int Start();
|
||||||
|
|
||||||
|
virtual int Stop();
|
||||||
|
|
||||||
int Pause();
|
int Pause();
|
||||||
int Resume();
|
int Resume();
|
||||||
int Stop();
|
|
||||||
|
|
||||||
void OnFrame();
|
void OnFrame();
|
||||||
|
|
||||||
@@ -66,6 +67,8 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
|||||||
int i_ = 0;
|
int i_ = 0;
|
||||||
int videoindex_ = 0;
|
int videoindex_ = 0;
|
||||||
int got_picture_ = 0;
|
int got_picture_ = 0;
|
||||||
|
bool inited_ = false;
|
||||||
|
|
||||||
// ffmpeg
|
// ffmpeg
|
||||||
AVFormatContext *pFormatCtx_ = nullptr;
|
AVFormatContext *pFormatCtx_ = nullptr;
|
||||||
AVCodecContext *pCodecCtx_ = nullptr;
|
AVCodecContext *pCodecCtx_ = nullptr;
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ class ScreenCapturer {
|
|||||||
virtual int Destroy() = 0;
|
virtual int Destroy() = 0;
|
||||||
|
|
||||||
virtual int Start() = 0;
|
virtual int Start() = 0;
|
||||||
|
|
||||||
|
virtual int Stop() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -5,36 +5,9 @@
|
|||||||
#include <winrt/Windows.Foundation.Metadata.h>
|
#include <winrt/Windows.Foundation.Metadata.h>
|
||||||
#include <winrt/Windows.Graphics.Capture.h>
|
#include <winrt/Windows.Graphics.Capture.h>
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <libavutil/imgutils.h>
|
|
||||||
#include <libswscale/swscale.h>
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int BGRAToNV12FFmpeg(unsigned char *src_buffer, int width, int height,
|
#include "libyuv.h"
|
||||||
unsigned char *dst_buffer) {
|
|
||||||
AVFrame *Input_pFrame = av_frame_alloc();
|
|
||||||
AVFrame *Output_pFrame = av_frame_alloc();
|
|
||||||
struct SwsContext *img_convert_ctx =
|
|
||||||
sws_getContext(width, height, AV_PIX_FMT_BGRA, 1280, 720, AV_PIX_FMT_NV12,
|
|
||||||
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
|
|
||||||
|
|
||||||
av_image_fill_arrays(Input_pFrame->data, Input_pFrame->linesize, src_buffer,
|
|
||||||
AV_PIX_FMT_BGRA, width, height, 1);
|
|
||||||
av_image_fill_arrays(Output_pFrame->data, Output_pFrame->linesize, dst_buffer,
|
|
||||||
AV_PIX_FMT_NV12, 1280, 720, 1);
|
|
||||||
|
|
||||||
sws_scale(img_convert_ctx, (uint8_t const **)Input_pFrame->data,
|
|
||||||
Input_pFrame->linesize, 0, height, Output_pFrame->data,
|
|
||||||
Output_pFrame->linesize);
|
|
||||||
|
|
||||||
if (Input_pFrame) av_free(Input_pFrame);
|
|
||||||
if (Output_pFrame) av_free(Output_pFrame);
|
|
||||||
if (img_convert_ctx) sws_freeContext(img_convert_ctx);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc,
|
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc,
|
||||||
LPARAM data) {
|
LPARAM data) {
|
||||||
@@ -62,7 +35,20 @@ HMONITOR GetPrimaryMonitor() {
|
|||||||
|
|
||||||
ScreenCapturerWgc::ScreenCapturerWgc() {}
|
ScreenCapturerWgc::ScreenCapturerWgc() {}
|
||||||
|
|
||||||
ScreenCapturerWgc::~ScreenCapturerWgc() {}
|
ScreenCapturerWgc::~ScreenCapturerWgc() {
|
||||||
|
Stop();
|
||||||
|
CleanUp();
|
||||||
|
|
||||||
|
if (nv12_frame_) {
|
||||||
|
delete nv12_frame_;
|
||||||
|
nv12_frame_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nv12_frame_scaled_) {
|
||||||
|
delete nv12_frame_scaled_;
|
||||||
|
nv12_frame_scaled_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ScreenCapturerWgc::IsWgcSupported() {
|
bool ScreenCapturerWgc::IsWgcSupported() {
|
||||||
try {
|
try {
|
||||||
@@ -81,7 +67,11 @@ int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
|||||||
int error = 0;
|
int error = 0;
|
||||||
if (_inited == true) return error;
|
if (_inited == true) return error;
|
||||||
|
|
||||||
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 4];
|
int r = rect.right;
|
||||||
|
int b = rect.bottom;
|
||||||
|
|
||||||
|
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2];
|
||||||
|
nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2];
|
||||||
|
|
||||||
_fps = fps;
|
_fps = fps;
|
||||||
|
|
||||||
@@ -114,17 +104,7 @@ int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ScreenCapturerWgc::Destroy() {
|
int ScreenCapturerWgc::Destroy() { return 0; }
|
||||||
if (nv12_frame_) {
|
|
||||||
delete nv12_frame_;
|
|
||||||
nv12_frame_ = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stop();
|
|
||||||
CleanUp();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ScreenCapturerWgc::Start() {
|
int ScreenCapturerWgc::Start() {
|
||||||
if (_running == true) {
|
if (_running == true) {
|
||||||
@@ -163,12 +143,59 @@ int ScreenCapturerWgc::Stop() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConvertABGRtoBGRA(const uint8_t *abgr_data, uint8_t *bgra_data, int width,
|
||||||
|
int height, int abgr_stride, int bgra_stride) {
|
||||||
|
for (int i = 0; i < height; ++i) {
|
||||||
|
for (int j = 0; j < width; ++j) {
|
||||||
|
// ABGR<47><52>BGRA<52><41><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
|
||||||
|
int abgr_index = (i * abgr_stride + j) * 4;
|
||||||
|
int bgra_index = (i * bgra_stride + j) * 4;
|
||||||
|
|
||||||
|
// ֱ<>ӽ<EFBFBD><D3BD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD>ͺ<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||||
|
bgra_data[bgra_index + 0] = abgr_data[abgr_index + 2]; // <20><>ɫ
|
||||||
|
bgra_data[bgra_index + 1] = abgr_data[abgr_index + 1]; // <20><>ɫ
|
||||||
|
bgra_data[bgra_index + 2] = abgr_data[abgr_index + 0]; // <20><>ɫ
|
||||||
|
bgra_data[bgra_index + 3] = abgr_data[abgr_index + 3]; // Alpha
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConvertBGRAtoABGR(const uint8_t *bgra_data, uint8_t *abgr_data, int width,
|
||||||
|
int height, int bgra_stride, int abgr_stride) {
|
||||||
|
for (int i = 0; i < height; ++i) {
|
||||||
|
for (int j = 0; j < width; ++j) {
|
||||||
|
// BGRA<52><41>ABGR<47><52><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
|
||||||
|
int bgra_index = (i * bgra_stride + j) * 4;
|
||||||
|
int abgr_index = (i * abgr_stride + j) * 4;
|
||||||
|
|
||||||
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>
|
||||||
|
abgr_data[abgr_index + 0] = bgra_data[bgra_index + 3]; // Alpha
|
||||||
|
abgr_data[abgr_index + 1] = bgra_data[bgra_index + 0]; // Blue
|
||||||
|
abgr_data[abgr_index + 2] = bgra_data[bgra_index + 1]; // Green
|
||||||
|
abgr_data[abgr_index + 3] = bgra_data[bgra_index + 2]; // Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame) {
|
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame) {
|
||||||
if (_on_data)
|
if (_on_data) {
|
||||||
BGRAToNV12FFmpeg((unsigned char *)frame.data, frame.width, frame.height,
|
int width = 1280;
|
||||||
nv12_frame_);
|
int height = 720;
|
||||||
_on_data(nv12_frame_, frame.width * frame.height * 4, frame.width,
|
|
||||||
frame.height);
|
libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4,
|
||||||
|
(uint8_t *)nv12_frame_, frame.width,
|
||||||
|
(uint8_t *)(nv12_frame_ + frame.width * frame.height),
|
||||||
|
frame.width, frame.width, frame.height);
|
||||||
|
|
||||||
|
libyuv::NV12Scale(
|
||||||
|
(const uint8_t *)nv12_frame_, frame.width,
|
||||||
|
(const uint8_t *)(nv12_frame_ + frame.width * frame.height),
|
||||||
|
frame.width, frame.width, frame.height, (uint8_t *)nv12_frame_scaled_,
|
||||||
|
width, (uint8_t *)(nv12_frame_scaled_ + width * height), width, width,
|
||||||
|
height, libyuv::FilterMode::kFilterLinear);
|
||||||
|
|
||||||
|
_on_data(nv12_frame_scaled_, width * height * 3 / 2, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenCapturerWgc::CleanUp() {
|
void ScreenCapturerWgc::CleanUp() {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
|||||||
|
|
||||||
int Pause();
|
int Pause();
|
||||||
int Resume();
|
int Resume();
|
||||||
int Stop();
|
virtual int Stop();
|
||||||
|
|
||||||
void OnFrame(const WgcSession::wgc_session_frame &frame);
|
void OnFrame(const WgcSession::wgc_session_frame &frame);
|
||||||
|
|
||||||
@@ -49,9 +49,10 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
|||||||
|
|
||||||
int _fps;
|
int _fps;
|
||||||
|
|
||||||
cb_desktop_data _on_data;
|
cb_desktop_data _on_data = nullptr;
|
||||||
|
|
||||||
unsigned char *nv12_frame_ = nullptr;
|
unsigned char *nv12_frame_ = nullptr;
|
||||||
|
unsigned char *nv12_frame_scaled_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
2
thirdparty/projectx
vendored
2
thirdparty/projectx
vendored
Submodule thirdparty/projectx updated: a309627ca3...28fb35faf5
10
thirdparty/sdl2/xmake.lua
vendored
10
thirdparty/sdl2/xmake.lua
vendored
@@ -1,10 +0,0 @@
|
|||||||
package("sdl2")
|
|
||||||
add_urls("https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.28.3.tar.gz", {alias = "github"})
|
|
||||||
add_versions("github:2.28.3", "c17455d6e0c484bfe634b8de6af4c608e86ee449c28e40af04064aa6643fe382")
|
|
||||||
|
|
||||||
on_install(function (package)
|
|
||||||
local configs = {}
|
|
||||||
table.insert(configs, "-DSDL_SHARED_ENABLED_BY_DEFAULT=OFF -DSDL_TEST_ENABLED_BY_DEFAULT=OFF")
|
|
||||||
import("package.tools.cmake").install(package, configs)
|
|
||||||
end)
|
|
||||||
package_end()
|
|
||||||
2
thirdparty/xmake.lua
vendored
2
thirdparty/xmake.lua
vendored
@@ -1,4 +1,4 @@
|
|||||||
includes("sdl2", "projectx")
|
includes("projectx")
|
||||||
if is_plat("windows") then
|
if is_plat("windows") then
|
||||||
elseif is_plat("linux") then
|
elseif is_plat("linux") then
|
||||||
includes("ffmpeg")
|
includes("ffmpeg")
|
||||||
|
|||||||
68
xmake.lua
68
xmake.lua
@@ -13,19 +13,16 @@ if is_mode("debug") then
|
|||||||
add_defines("REMOTE_DESK_DEBUG")
|
add_defines("REMOTE_DESK_DEBUG")
|
||||||
end
|
end
|
||||||
|
|
||||||
add_requires("sdl2", {system = false})
|
add_requires("spdlog 1.14.1", {system = false})
|
||||||
add_requires("spdlog 1.11.0", {system = false})
|
add_requires("imgui 1.90.6", {configs = {sdl2 = true, sdl2_renderer = true}})
|
||||||
add_requires("imgui 1.89.9", {configs = {sdl2 = true, sdl2_renderer = true}})
|
add_requires("libyuv")
|
||||||
|
|
||||||
if is_os("windows") then
|
if is_os("windows") then
|
||||||
add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32",
|
add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32",
|
||||||
"SDL2-static", "SDL2main", "gdi32", "winmm", "setupapi", "version",
|
"SDL2-static", "SDL2main", "gdi32", "winmm", "setupapi", "version",
|
||||||
"Imm32", "iphlpapi")
|
"Imm32", "iphlpapi")
|
||||||
add_requires("vcpkg::ffmpeg 5.1.2", {configs = {shared = false}})
|
|
||||||
add_packages("vcpkg::ffmpeg")
|
|
||||||
elseif is_os("linux") then
|
elseif is_os("linux") then
|
||||||
add_requires("ffmpeg 5.1.2", {system = false})
|
add_requires("ffmpeg 5.1.2", {system = false})
|
||||||
add_packages("ffmpeg")
|
|
||||||
add_syslinks("pthread", "dl")
|
add_syslinks("pthread", "dl")
|
||||||
add_linkdirs("thirdparty/projectx/thirdparty/nvcodec/Lib/x64")
|
add_linkdirs("thirdparty/projectx/thirdparty/nvcodec/Lib/x64")
|
||||||
add_links("SDL2", "cuda", "nvidia-encode", "nvcuvid")
|
add_links("SDL2", "cuda", "nvidia-encode", "nvcuvid")
|
||||||
@@ -37,13 +34,13 @@ elseif is_os("linux") then
|
|||||||
elseif is_os("macosx") then
|
elseif is_os("macosx") then
|
||||||
add_requires("ffmpeg 5.1.2", {system = false})
|
add_requires("ffmpeg 5.1.2", {system = false})
|
||||||
add_requires("libxcb", {system = false})
|
add_requires("libxcb", {system = false})
|
||||||
add_packages("ffmpeg", "libxcb")
|
add_packages("libxcb")
|
||||||
add_links("SDL2", "SDL2main")
|
add_links("SDL2", "SDL2main")
|
||||||
add_ldflags("-Wl,-ld_classic")
|
add_ldflags("-Wl,-ld_classic")
|
||||||
add_frameworks("OpenGL")
|
add_frameworks("OpenGL")
|
||||||
end
|
end
|
||||||
|
|
||||||
add_packages("spdlog", "sdl2", "imgui")
|
add_packages("spdlog", "imgui")
|
||||||
|
|
||||||
includes("thirdparty")
|
includes("thirdparty")
|
||||||
|
|
||||||
@@ -62,16 +59,19 @@ target("common")
|
|||||||
target("screen_capturer")
|
target("screen_capturer")
|
||||||
set_kind("object")
|
set_kind("object")
|
||||||
add_deps("log")
|
add_deps("log")
|
||||||
|
add_packages("libyuv")
|
||||||
add_includedirs("src/screen_capturer", {public = true})
|
add_includedirs("src/screen_capturer", {public = true})
|
||||||
if is_os("windows") then
|
if is_os("windows") then
|
||||||
add_files("src/screen_capturer/windows/*.cpp")
|
add_files("src/screen_capturer/windows/*.cpp")
|
||||||
add_includedirs("src/screen_capturer/windows", {public = true})
|
add_includedirs("src/screen_capturer/windows", {public = true})
|
||||||
elseif is_os("macosx") then
|
elseif is_os("macosx") then
|
||||||
add_files("src/screen_capturer/macosx/*.cpp")
|
add_packages("ffmpeg")
|
||||||
add_includedirs("src/screen_capturer/macosx", {public = true})
|
add_files("src/screen_capturer/macosx/*.cpp")
|
||||||
|
add_includedirs("src/screen_capturer/macosx", {public = true})
|
||||||
elseif is_os("linux") then
|
elseif is_os("linux") then
|
||||||
add_files("src/screen_capturer/linux/*.cpp")
|
add_packages("ffmpeg")
|
||||||
add_includedirs("src/screen_capturer/linux", {public = true})
|
add_files("src/screen_capturer/linux/*.cpp")
|
||||||
|
add_includedirs("src/screen_capturer/linux", {public = true})
|
||||||
end
|
end
|
||||||
|
|
||||||
target("device_controller")
|
target("device_controller")
|
||||||
@@ -89,19 +89,37 @@ target("device_controller")
|
|||||||
add_includedirs("src/device_controller/mouse/linux", {public = true})
|
add_includedirs("src/device_controller/mouse/linux", {public = true})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
target("config_center")
|
||||||
|
set_kind("object")
|
||||||
|
add_deps("log")
|
||||||
|
add_files("src/config_center/*.cpp")
|
||||||
|
add_includedirs("src/config_center", {public = true})
|
||||||
|
|
||||||
|
target("localization")
|
||||||
|
set_kind("headeronly")
|
||||||
|
add_includedirs("src/localization", {public = true})
|
||||||
|
|
||||||
|
target("main_window")
|
||||||
|
set_kind("object")
|
||||||
|
add_deps("log", "common", "localization", "config_center", "projectx", "screen_capturer", "device_controller")
|
||||||
|
if is_os("macosx") then
|
||||||
|
add_packages("ffmpeg")
|
||||||
|
elseif is_os("linux") then
|
||||||
|
add_packages("ffmpeg")
|
||||||
|
end
|
||||||
|
add_files("src/main_window/*.cpp")
|
||||||
|
add_includedirs("src/main_window", {public = true})
|
||||||
|
|
||||||
target("remote_desk")
|
target("remote_desk")
|
||||||
set_kind("binary")
|
set_kind("binary")
|
||||||
add_deps("log", "common", "screen_capturer", "device_controller", "projectx")
|
add_deps("log", "common", "main_window")
|
||||||
|
if is_os("macosx") then
|
||||||
|
add_packages("ffmpeg")
|
||||||
|
elseif is_os("linux") then
|
||||||
|
add_packages("ffmpeg")
|
||||||
|
end
|
||||||
add_files("src/gui/main.cpp")
|
add_files("src/gui/main.cpp")
|
||||||
|
|
||||||
-- after_install(function (target)
|
|
||||||
-- os.cp("$(projectdir)/thirdparty/nvcodec/Lib/x64/*.so", "$(projectdir)/out/bin")
|
|
||||||
-- os.cp("$(projectdir)/thirdparty/nvcodec/Lib/x64/*.so.1", "$(projectdir)/out/bin")
|
|
||||||
-- os.cp("$(projectdir)/out/lib/*.so", "$(projectdir)/out/bin")
|
|
||||||
-- os.rm("$(projectdir)/out/include")
|
|
||||||
-- os.rm("$(projectdir)/out/lib")
|
|
||||||
-- end)
|
|
||||||
|
|
||||||
-- target("screen_capturer")
|
-- target("screen_capturer")
|
||||||
-- set_kind("binary")
|
-- set_kind("binary")
|
||||||
-- add_packages("sdl2", "imgui", "ffmpeg", "openh264")
|
-- add_packages("sdl2", "imgui", "ffmpeg", "openh264")
|
||||||
@@ -157,7 +175,7 @@ target("remote_desk")
|
|||||||
-- "-lxcb-shm", "-lXext", "-lX11", "-lXv", "-lpthread", "-lSDL2", "-lopenh264",
|
-- "-lxcb-shm", "-lXext", "-lX11", "-lXv", "-lpthread", "-lSDL2", "-lopenh264",
|
||||||
-- "-ldl", {force = true})
|
-- "-ldl", {force = true})
|
||||||
|
|
||||||
target("mouse_control")
|
-- target("mouse_control")
|
||||||
set_kind("binary")
|
-- set_kind("binary")
|
||||||
add_files("test/linux_mouse_control/mouse_control.cpp")
|
-- add_files("test/linux_mouse_control/mouse_control.cpp")
|
||||||
add_includedirs("test/linux_mouse_control")
|
-- add_includedirs("test/linux_mouse_control")
|
||||||
Reference in New Issue
Block a user