mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-28 20:06:14 +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"]
|
||||
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 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
|
||||
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
[signal server]
|
||||
ip = 120.77.216.215
|
||||
ip = 150.158.81.30
|
||||
port = 9099
|
||||
|
||||
[stun server]
|
||||
ip = 120.77.216.215
|
||||
ip = 150.158.81.30
|
||||
port = 3478
|
||||
|
||||
[turn server]
|
||||
ip = 120.77.216.215
|
||||
ip = 150.158.81.30
|
||||
port = 3478
|
||||
username = dijunkun
|
||||
password = dijunkunpw
|
||||
|
||||
[hardware acceleration]
|
||||
turn_on = false
|
||||
|
||||
[av1 encoding]
|
||||
turn_on = true
|
||||
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() {
|
||||
if (uinput_fd_) {
|
||||
ioctl(uinput_fd_, UI_DEV_DESTROY);
|
||||
close(uinput_fd_);
|
||||
}
|
||||
}
|
||||
|
||||
int MouseController::Init(int screen_width, int screen_height) {
|
||||
screen_width_ = screen_width;
|
||||
@@ -13,6 +18,7 @@ int MouseController::Init(int screen_width, int screen_height) {
|
||||
uinput_fd_ = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
|
||||
if (uinput_fd_ < 0) {
|
||||
LOG_ERROR("Cannot open device: /dev/uinput");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ioctl(uinput_fd_, UI_SET_EVBIT, EV_KEY);
|
||||
@@ -35,15 +41,12 @@ int MouseController::Init(int screen_width, int screen_height) {
|
||||
uidev.absmin[ABS_Y] = 0;
|
||||
uidev.absmax[ABS_Y] = screen_height_;
|
||||
|
||||
write(uinput_fd_, &uidev, sizeof(uidev));
|
||||
int res_uidev = write(uinput_fd_, &uidev, sizeof(uidev));
|
||||
ioctl(uinput_fd_, UI_DEV_CREATE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MouseController::Destroy() {
|
||||
ioctl(uinput_fd_, UI_DEV_DESTROY);
|
||||
close(uinput_fd_);
|
||||
}
|
||||
int MouseController::Destroy() { return 0; }
|
||||
|
||||
int MouseController::SendCommand(RemoteAction remote_action) {
|
||||
int mouse_pos_x = remote_action.m.x * screen_width_ / 1280;
|
||||
@@ -71,6 +74,7 @@ int MouseController::SendCommand(RemoteAction remote_action) {
|
||||
}
|
||||
|
||||
void MouseController::SimulateKeyDown(int fd, int kval) {
|
||||
int res_ev = 0;
|
||||
struct input_event event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
gettimeofday(&event.time, 0);
|
||||
@@ -78,15 +82,16 @@ void MouseController::SimulateKeyDown(int fd, int kval) {
|
||||
event.type = EV_KEY;
|
||||
event.value = 1;
|
||||
event.code = kval;
|
||||
write(fd, &event, sizeof(event));
|
||||
res_ev = write(fd, &event, sizeof(event));
|
||||
|
||||
event.type = EV_SYN;
|
||||
event.value = 0;
|
||||
event.code = SYN_REPORT;
|
||||
write(fd, &event, sizeof(event));
|
||||
res_ev = write(fd, &event, sizeof(event));
|
||||
}
|
||||
|
||||
void MouseController::SimulateKeyUp(int fd, int kval) {
|
||||
int res_ev = 0;
|
||||
struct input_event event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
gettimeofday(&event.time, 0);
|
||||
@@ -94,12 +99,12 @@ void MouseController::SimulateKeyUp(int fd, int kval) {
|
||||
event.type = EV_KEY;
|
||||
event.value = 0;
|
||||
event.code = kval;
|
||||
write(fd, &event, sizeof(event));
|
||||
res_ev = write(fd, &event, sizeof(event));
|
||||
|
||||
event.type = EV_SYN;
|
||||
event.value = 0;
|
||||
event.code = SYN_REPORT;
|
||||
write(fd, &event, sizeof(event));
|
||||
res_ev = write(fd, &event, sizeof(event));
|
||||
}
|
||||
|
||||
void MouseController::SetMousePosition(int fd, int x, int y) {
|
||||
|
||||
861
src/gui/main.cpp
861
src/gui/main.cpp
@@ -1,5 +1,3 @@
|
||||
#include <SDL.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _WIN32
|
||||
#ifdef REMOTE_DESK_DEBUG
|
||||
#pragma comment(linker, "/subsystem:\"console\"")
|
||||
@@ -8,865 +6,14 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavdevice/avdevice.h>
|
||||
#include <libswresample/swresample.h>
|
||||
#include <libswscale/swscale.h>
|
||||
};
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../../thirdparty/projectx/src/interface/x.h"
|
||||
#include "device_controller_factory.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl2.h"
|
||||
#include "imgui_impl_sdlrenderer2.h"
|
||||
#include "log.h"
|
||||
#include "screen_capturer_factory.h"
|
||||
#include "main_window.h"
|
||||
|
||||
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
|
||||
|
||||
#ifdef REMOTE_DESK_DEBUG
|
||||
#define MOUSE_CONTROL 0
|
||||
#else
|
||||
#define MOUSE_CONTROL 1
|
||||
#endif
|
||||
|
||||
int screen_w = 1280, screen_h = 720;
|
||||
int window_w = 1280, window_h = 720;
|
||||
const int pixel_w = 1280, pixel_h = 720;
|
||||
|
||||
unsigned char dst_buffer[pixel_w * pixel_h * 3 / 2];
|
||||
unsigned char audio_buffer[960];
|
||||
SDL_Texture *sdlTexture = nullptr;
|
||||
SDL_Renderer *sdlRenderer = nullptr;
|
||||
SDL_Rect sdlRect;
|
||||
SDL_Window *window;
|
||||
static SDL_AudioDeviceID input_dev;
|
||||
static SDL_AudioDeviceID output_dev;
|
||||
|
||||
uint32_t start_time, end_time, elapsed_time;
|
||||
uint32_t frame_count = 0;
|
||||
int fps = 0;
|
||||
|
||||
static std::atomic<bool> audio_buffer_fresh{false};
|
||||
static uint32_t last_ts = 0;
|
||||
|
||||
int64_t src_ch_layout = AV_CH_LAYOUT_MONO;
|
||||
int src_rate = 48000;
|
||||
enum AVSampleFormat src_sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
int src_nb_channels = 0;
|
||||
uint8_t **src_data = NULL;
|
||||
int src_linesize;
|
||||
int src_nb_samples = 480;
|
||||
|
||||
int64_t dst_ch_layout = AV_CH_LAYOUT_MONO;
|
||||
int dst_rate = 48000;
|
||||
enum AVSampleFormat dst_sample_fmt = AV_SAMPLE_FMT_S16;
|
||||
int dst_nb_channels = 0;
|
||||
uint8_t **dst_data = NULL;
|
||||
int dst_linesize;
|
||||
int dst_nb_samples;
|
||||
int max_dst_nb_samples;
|
||||
|
||||
int dst_bufsize;
|
||||
struct SwrContext *swr_ctx;
|
||||
|
||||
int ret;
|
||||
|
||||
int audio_len = 0;
|
||||
|
||||
std::string window_title = "Remote Desk Client";
|
||||
std::string server_connection_status_str = "-";
|
||||
std::string client_connection_status_str = "-";
|
||||
std::string server_signal_status_str = "-";
|
||||
std::string client_signal_status_str = "-";
|
||||
|
||||
std::atomic<ConnectionStatus> server_connection_status{
|
||||
ConnectionStatus::Closed};
|
||||
std::atomic<ConnectionStatus> client_connection_status{
|
||||
ConnectionStatus::Closed};
|
||||
std::atomic<SignalStatus> server_signal_status{SignalStatus::SignalClosed};
|
||||
std::atomic<SignalStatus> client_signal_status{SignalStatus::SignalClosed};
|
||||
|
||||
// Refresh Event
|
||||
#define REFRESH_EVENT (SDL_USEREVENT + 1)
|
||||
#define QUIT_EVENT (SDL_USEREVENT + 2)
|
||||
|
||||
typedef struct {
|
||||
char password[7];
|
||||
} CDCache;
|
||||
|
||||
int thread_exit = 0;
|
||||
PeerPtr *peer_server = nullptr;
|
||||
PeerPtr *peer_client = nullptr;
|
||||
bool joined = false;
|
||||
bool received_frame = false;
|
||||
bool menu_hovered = false;
|
||||
|
||||
static bool connect_button_pressed = false;
|
||||
static const char *connect_label = "Connect";
|
||||
static char input_password[7] = "";
|
||||
static FILE *cd_cache_file = nullptr;
|
||||
static CDCache cd_cache;
|
||||
|
||||
static bool is_create_connection = false;
|
||||
static bool done = false;
|
||||
|
||||
ScreenCapturerFactory *screen_capturer_factory = nullptr;
|
||||
ScreenCapturer *screen_capturer = nullptr;
|
||||
|
||||
DeviceControllerFactory *device_controller_factory = nullptr;
|
||||
MouseController *mouse_controller = nullptr;
|
||||
|
||||
char *nv12_buffer = nullptr;
|
||||
|
||||
#ifdef __linux__
|
||||
std::chrono::_V2::system_clock::time_point last_frame_time_;
|
||||
#else
|
||||
std::chrono::steady_clock::time_point last_frame_time_;
|
||||
#endif
|
||||
|
||||
inline int ProcessMouseKeyEven(SDL_Event &ev) {
|
||||
float ratio = (float)(1280.0 / window_w);
|
||||
|
||||
RemoteAction remote_action;
|
||||
remote_action.m.x = (size_t)(ev.button.x * ratio);
|
||||
remote_action.m.y = (size_t)(ev.button.y * ratio);
|
||||
|
||||
if (SDL_KEYDOWN == ev.type) // SDL_KEYUP
|
||||
{
|
||||
// printf("SDLK_DOWN: %d\n", SDL_KeyCode(ev.key.keysym.sym));
|
||||
if (SDLK_DOWN == ev.key.keysym.sym) {
|
||||
// printf("SDLK_DOWN \n");
|
||||
|
||||
} else if (SDLK_UP == ev.key.keysym.sym) {
|
||||
// printf("SDLK_UP \n");
|
||||
|
||||
} else if (SDLK_LEFT == ev.key.keysym.sym) {
|
||||
// printf("SDLK_LEFT \n");
|
||||
|
||||
} else if (SDLK_RIGHT == ev.key.keysym.sym) {
|
||||
// printf("SDLK_RIGHT \n");
|
||||
}
|
||||
} else if (SDL_MOUSEBUTTONDOWN == ev.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_down;
|
||||
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_down;
|
||||
}
|
||||
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
} else if (SDL_MOUSEBUTTONUP == ev.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_up;
|
||||
} else if (SDL_BUTTON_RIGHT == ev.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_up;
|
||||
}
|
||||
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
} else if (SDL_MOUSEMOTION == ev.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
SendData(peer_client, DATA_TYPE::DATA, (const char *)&remote_action,
|
||||
sizeof(remote_action));
|
||||
} else if (SDL_QUIT == ev.type) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
printf("SDL_QUIT\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SdlCaptureAudioIn(void *userdata, Uint8 *stream, int len) {
|
||||
int64_t delay = swr_get_delay(swr_ctx, src_rate);
|
||||
dst_nb_samples = (int)av_rescale_rnd(delay + src_nb_samples, dst_rate,
|
||||
src_rate, AV_ROUND_UP);
|
||||
if (dst_nb_samples > max_dst_nb_samples) {
|
||||
av_freep(&dst_data[0]);
|
||||
ret = av_samples_alloc(dst_data, &dst_linesize, dst_nb_channels,
|
||||
dst_nb_samples, dst_sample_fmt, 1);
|
||||
if (ret < 0) return;
|
||||
max_dst_nb_samples = dst_nb_samples;
|
||||
}
|
||||
|
||||
ret = swr_convert(swr_ctx, dst_data, dst_nb_samples,
|
||||
(const uint8_t **)&stream, src_nb_samples);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Error while converting\n");
|
||||
return;
|
||||
}
|
||||
dst_bufsize = av_samples_get_buffer_size(&dst_linesize, dst_nb_channels, ret,
|
||||
dst_sample_fmt, 1);
|
||||
if (dst_bufsize < 0) {
|
||||
fprintf(stderr, "Could not get sample buffer size\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (1) {
|
||||
if ("ClientConnected" == client_connection_status_str) {
|
||||
SendData(peer_client, DATA_TYPE::AUDIO, (const char *)dst_data[0],
|
||||
dst_bufsize);
|
||||
}
|
||||
|
||||
if ("ServerConnected" == server_connection_status_str) {
|
||||
SendData(peer_server, DATA_TYPE::AUDIO, (const char *)dst_data[0],
|
||||
dst_bufsize);
|
||||
}
|
||||
} else {
|
||||
memcpy(audio_buffer, dst_data[0], dst_bufsize);
|
||||
audio_len = dst_bufsize;
|
||||
SDL_Delay(10);
|
||||
audio_buffer_fresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SdlCaptureAudioOut(void *userdata, Uint8 *stream, int len) {
|
||||
// if ("ClientConnected" != client_connection_status_str) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (!audio_buffer_fresh) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_memset(stream, 0, len);
|
||||
|
||||
if (audio_len == 0) {
|
||||
return;
|
||||
} else {
|
||||
}
|
||||
|
||||
len = (len > audio_len ? audio_len : len);
|
||||
SDL_MixAudioFormat(stream, audio_buffer, AUDIO_S16LSB, len,
|
||||
SDL_MIX_MAXVOLUME);
|
||||
audio_buffer_fresh = false;
|
||||
}
|
||||
|
||||
void ServerReceiveVideoBuffer(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size) {}
|
||||
|
||||
void ClientReceiveVideoBuffer(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size) {
|
||||
// std::cout << "Receive: [" << user_id << "] " << std::endl;
|
||||
if (joined) {
|
||||
memcpy(dst_buffer, data, size);
|
||||
|
||||
SDL_Event event;
|
||||
event.type = REFRESH_EVENT;
|
||||
SDL_PushEvent(&event);
|
||||
received_frame = true;
|
||||
}
|
||||
}
|
||||
|
||||
void ServerReceiveAudioBuffer(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size) {
|
||||
// memset(audio_buffer, 0, size);
|
||||
// memcpy(audio_buffer, data, size);
|
||||
// audio_len = size;
|
||||
audio_buffer_fresh = true;
|
||||
|
||||
SDL_QueueAudio(output_dev, data, (Uint32)size);
|
||||
// printf("queue audio\n");
|
||||
}
|
||||
|
||||
void ClientReceiveAudioBuffer(const char *data, size_t size,
|
||||
const char *user_id, size_t user_id_size) {
|
||||
// std::cout << "Client receive audio, size " << size << ", user [" << user_id
|
||||
// << "] " << std::endl;
|
||||
SDL_QueueAudio(output_dev, data, (Uint32)size);
|
||||
}
|
||||
|
||||
void ServerReceiveDataBuffer(const char *data, size_t size, const char *user_id,
|
||||
size_t user_id_size) {
|
||||
std::string user(user_id, user_id_size);
|
||||
|
||||
RemoteAction remote_action;
|
||||
memcpy(&remote_action, data, sizeof(remote_action));
|
||||
|
||||
// std::cout << "remote_action: " << remote_action.type << " "
|
||||
// << remote_action.m.flag << " " << remote_action.m.x << " "
|
||||
// << remote_action.m.y << std::endl;
|
||||
#if MOUSE_CONTROL
|
||||
mouse_controller->SendCommand(remote_action);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClientReceiveDataBuffer(const char *data, size_t size, const char *user_id,
|
||||
size_t user_id_size) {}
|
||||
|
||||
void ServerSignalStatus(SignalStatus status) {
|
||||
server_signal_status = status;
|
||||
if (SignalStatus::SignalConnecting == status) {
|
||||
server_signal_status_str = "ServerSignalConnecting";
|
||||
} else if (SignalStatus::SignalConnected == status) {
|
||||
server_signal_status_str = "ServerSignalConnected";
|
||||
} else if (SignalStatus::SignalFailed == status) {
|
||||
server_signal_status_str = "ServerSignalFailed";
|
||||
} else if (SignalStatus::SignalClosed == status) {
|
||||
server_signal_status_str = "ServerSignalClosed";
|
||||
} else if (SignalStatus::SignalReconnecting == status) {
|
||||
server_signal_status_str = "ServerSignalReconnecting";
|
||||
}
|
||||
}
|
||||
|
||||
void ClientSignalStatus(SignalStatus status) {
|
||||
client_signal_status = status;
|
||||
if (SignalStatus::SignalConnecting == status) {
|
||||
client_signal_status_str = "ClientSignalConnecting";
|
||||
} else if (SignalStatus::SignalConnected == status) {
|
||||
client_signal_status_str = "ClientSignalConnected";
|
||||
} else if (SignalStatus::SignalFailed == status) {
|
||||
client_signal_status_str = "ClientSignalFailed";
|
||||
} else if (SignalStatus::SignalClosed == status) {
|
||||
client_signal_status_str = "ClientSignalClosed";
|
||||
} else if (SignalStatus::SignalReconnecting == status) {
|
||||
client_signal_status_str = "ClientSignalReconnecting";
|
||||
}
|
||||
}
|
||||
|
||||
void ServerConnectionStatus(ConnectionStatus status) {
|
||||
server_connection_status = status;
|
||||
if (ConnectionStatus::Connecting == status) {
|
||||
server_connection_status_str = "ServerConnecting";
|
||||
} else if (ConnectionStatus::Connected == status) {
|
||||
server_connection_status_str = "ServerConnected";
|
||||
} else if (ConnectionStatus::Disconnected == status) {
|
||||
server_connection_status_str = "ServerDisconnected";
|
||||
} else if (ConnectionStatus::Failed == status) {
|
||||
server_connection_status_str = "ServerFailed";
|
||||
} else if (ConnectionStatus::Closed == status) {
|
||||
server_connection_status_str = "ServerClosed";
|
||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||
server_connection_status_str = "Incorrect password";
|
||||
if (connect_button_pressed) {
|
||||
connect_button_pressed = false;
|
||||
joined = false;
|
||||
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientConnectionStatus(ConnectionStatus status) {
|
||||
client_connection_status = status;
|
||||
if (ConnectionStatus::Connecting == status) {
|
||||
client_connection_status_str = "ClientConnecting";
|
||||
} else if (ConnectionStatus::Connected == status) {
|
||||
client_connection_status_str = "ClientConnected";
|
||||
joined = true;
|
||||
} else if (ConnectionStatus::Disconnected == status) {
|
||||
client_connection_status_str = "ClientDisconnected";
|
||||
} else if (ConnectionStatus::Failed == status) {
|
||||
client_connection_status_str = "ClientFailed";
|
||||
} else if (ConnectionStatus::Closed == status) {
|
||||
client_connection_status_str = "ClientClosed";
|
||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||
client_connection_status_str = "Incorrect password";
|
||||
if (connect_button_pressed) {
|
||||
connect_button_pressed = false;
|
||||
joined = false;
|
||||
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
||||
}
|
||||
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
|
||||
client_connection_status_str = "No such transmission id";
|
||||
if (connect_button_pressed) {
|
||||
connect_button_pressed = false;
|
||||
joined = false;
|
||||
connect_label = connect_button_pressed ? "Disconnect" : "Connect";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int initResampler() {
|
||||
/* create resampler context */
|
||||
swr_ctx = swr_alloc();
|
||||
if (!swr_ctx) {
|
||||
fprintf(stderr, "Could not allocate resampler context\n");
|
||||
ret = AVERROR(ENOMEM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* set options */
|
||||
av_opt_set_int(swr_ctx, "in_channel_layout", src_ch_layout, 0);
|
||||
av_opt_set_int(swr_ctx, "in_sample_rate", src_rate, 0);
|
||||
av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", src_sample_fmt, 0);
|
||||
|
||||
av_opt_set_int(swr_ctx, "out_channel_layout", dst_ch_layout, 0);
|
||||
av_opt_set_int(swr_ctx, "out_sample_rate", dst_rate, 0);
|
||||
av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", dst_sample_fmt, 0);
|
||||
|
||||
/* initialize the resampling context */
|
||||
if ((ret = swr_init(swr_ctx)) < 0) {
|
||||
fprintf(stderr, "Failed to initialize the resampling context\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* allocate source and destination samples buffers */
|
||||
src_nb_channels = av_get_channel_layout_nb_channels(src_ch_layout);
|
||||
|
||||
ret = av_samples_alloc_array_and_samples(&src_data, &src_linesize,
|
||||
src_nb_channels, src_nb_samples,
|
||||
src_sample_fmt, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not allocate source samples\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
max_dst_nb_samples = dst_nb_samples =
|
||||
av_rescale_rnd(src_nb_samples, dst_rate, src_rate, AV_ROUND_UP);
|
||||
|
||||
/* buffer is going to be directly written to a rawaudio file, no alignment */
|
||||
dst_nb_channels = av_get_channel_layout_nb_channels(dst_ch_layout);
|
||||
|
||||
ret = av_samples_alloc_array_and_samples(&dst_data, &dst_linesize,
|
||||
dst_nb_channels, dst_nb_samples,
|
||||
dst_sample_fmt, 0);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Could not allocate destination samples\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
int main(int argc, char *argv[]) {
|
||||
LOG_INFO("Remote desk");
|
||||
MainWindow main_window;
|
||||
|
||||
last_ts = static_cast<uint32_t>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||
.count());
|
||||
|
||||
initResampler();
|
||||
|
||||
cd_cache_file = fopen("cache.cd", "r+");
|
||||
if (cd_cache_file) {
|
||||
fseek(cd_cache_file, 0, SEEK_SET);
|
||||
fread(&cd_cache.password, sizeof(cd_cache.password), 1, cd_cache_file);
|
||||
fclose(cd_cache_file);
|
||||
strncpy(input_password, cd_cache.password, sizeof(cd_cache.password));
|
||||
}
|
||||
|
||||
// Setup SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER |
|
||||
SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
printf("Error: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// From 2.0.18: Enable native IME.
|
||||
#ifdef SDL_HINT_IME_SHOW_UI
|
||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||
#endif
|
||||
|
||||
// Create window with SDL_Renderer graphics context
|
||||
SDL_WindowFlags window_flags =
|
||||
(SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
window = SDL_CreateWindow("Remote Desk", SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED, window_w, window_h,
|
||||
window_flags);
|
||||
|
||||
SDL_DisplayMode DM;
|
||||
SDL_GetCurrentDisplayMode(0, &DM);
|
||||
screen_w = DM.w;
|
||||
screen_h = DM.h;
|
||||
|
||||
sdlRenderer = SDL_CreateRenderer(
|
||||
window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);
|
||||
if (sdlRenderer == nullptr) {
|
||||
SDL_Log("Error creating SDL_Renderer!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Uint32 pixformat = 0;
|
||||
pixformat = SDL_PIXELFORMAT_NV12;
|
||||
|
||||
sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,
|
||||
SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
|
||||
|
||||
// Audio
|
||||
SDL_AudioSpec want_in, have_in, want_out, have_out;
|
||||
SDL_zero(want_in);
|
||||
want_in.freq = 48000;
|
||||
want_in.format = AUDIO_S16LSB;
|
||||
want_in.channels = 1;
|
||||
want_in.samples = 480;
|
||||
want_in.callback = SdlCaptureAudioIn;
|
||||
|
||||
input_dev = SDL_OpenAudioDevice(NULL, 1, &want_in, &have_in, 0);
|
||||
if (input_dev == 0) {
|
||||
SDL_Log("Failed to open input: %s", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_zero(want_out);
|
||||
want_out.freq = 48000;
|
||||
want_out.format = AUDIO_S16LSB;
|
||||
want_out.channels = 1;
|
||||
// want_out.silence = 0;
|
||||
want_out.samples = 480;
|
||||
want_out.callback = NULL;
|
||||
|
||||
output_dev = SDL_OpenAudioDevice(NULL, 0, &want_out, &have_out, 0);
|
||||
if (output_dev == 0) {
|
||||
SDL_Log("Failed to open input: %s", SDL_GetError());
|
||||
return 1;
|
||||
}
|
||||
|
||||
SDL_PauseAudioDevice(input_dev, 0);
|
||||
SDL_PauseAudioDevice(output_dev, 0);
|
||||
|
||||
// Setup Dear ImGui context
|
||||
IMGUI_CHECKVERSION();
|
||||
ImGui::CreateContext();
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
io.ConfigFlags |=
|
||||
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
io.ConfigFlags |=
|
||||
ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
// ImGui::StyleColorsLight();
|
||||
|
||||
// Setup Platform/Renderer backends
|
||||
ImGui_ImplSDL2_InitForSDLRenderer(window, sdlRenderer);
|
||||
ImGui_ImplSDLRenderer2_Init(sdlRenderer);
|
||||
|
||||
// Our state
|
||||
bool show_demo_window = true;
|
||||
bool show_another_window = false;
|
||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
|
||||
std::string mac_addr_str = GetMac();
|
||||
|
||||
std::thread rtc_thread(
|
||||
[](int screen_width, int screen_height) {
|
||||
std::string default_cfg_path = "../../../../config/config.ini";
|
||||
std::ifstream f(default_cfg_path.c_str());
|
||||
|
||||
std::string mac_addr_str = GetMac();
|
||||
|
||||
Params server_params;
|
||||
server_params.cfg_path =
|
||||
f.good() ? "../../../../config/config.ini" : "config.ini";
|
||||
server_params.on_receive_video_buffer = ServerReceiveVideoBuffer;
|
||||
server_params.on_receive_audio_buffer = ServerReceiveAudioBuffer;
|
||||
server_params.on_receive_data_buffer = ServerReceiveDataBuffer;
|
||||
server_params.on_signal_status = ServerSignalStatus;
|
||||
server_params.on_connection_status = ServerConnectionStatus;
|
||||
|
||||
Params client_params;
|
||||
client_params.cfg_path =
|
||||
f.good() ? "../../../../config/config.ini" : "config.ini";
|
||||
client_params.on_receive_video_buffer = ClientReceiveVideoBuffer;
|
||||
client_params.on_receive_audio_buffer = ClientReceiveAudioBuffer;
|
||||
client_params.on_receive_data_buffer = ClientReceiveDataBuffer;
|
||||
client_params.on_signal_status = ClientSignalStatus;
|
||||
client_params.on_connection_status = ClientConnectionStatus;
|
||||
|
||||
std::string transmission_id = "000001";
|
||||
|
||||
peer_server = CreatePeer(&server_params);
|
||||
LOG_INFO("Create peer_server");
|
||||
std::string server_user_id = "S-" + mac_addr_str;
|
||||
Init(peer_server, server_user_id.c_str());
|
||||
LOG_INFO("peer_server init finish");
|
||||
|
||||
peer_client = CreatePeer(&client_params);
|
||||
LOG_INFO("Create peer_client");
|
||||
std::string client_user_id = "C-" + mac_addr_str;
|
||||
Init(peer_client, client_user_id.c_str());
|
||||
LOG_INFO("peer_client init finish");
|
||||
|
||||
{
|
||||
while (SignalStatus::SignalConnected != server_signal_status &&
|
||||
!done) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string user_id = "S-" + mac_addr_str;
|
||||
is_create_connection =
|
||||
CreateConnection(peer_server, mac_addr_str.c_str(),
|
||||
input_password)
|
||||
? false
|
||||
: true;
|
||||
|
||||
nv12_buffer = new char[NV12_BUFFER_SIZE];
|
||||
|
||||
// Screen capture
|
||||
screen_capturer_factory = new ScreenCapturerFactory();
|
||||
screen_capturer = (ScreenCapturer *)screen_capturer_factory->Create();
|
||||
|
||||
last_frame_time_ = std::chrono::high_resolution_clock::now();
|
||||
ScreenCapturer::RECORD_DESKTOP_RECT rect;
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.right = screen_w;
|
||||
rect.bottom = screen_h;
|
||||
|
||||
screen_capturer->Init(
|
||||
rect, 60,
|
||||
[](unsigned char *data, int size, int width, int height) -> void {
|
||||
auto now_time = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> duration =
|
||||
now_time - last_frame_time_;
|
||||
auto tc = duration.count() * 1000;
|
||||
|
||||
if (tc >= 0) {
|
||||
SendData(peer_server, DATA_TYPE::VIDEO, (const char *)data,
|
||||
NV12_BUFFER_SIZE);
|
||||
last_frame_time_ = now_time;
|
||||
}
|
||||
});
|
||||
|
||||
screen_capturer->Start();
|
||||
|
||||
// Mouse control
|
||||
device_controller_factory = new DeviceControllerFactory();
|
||||
mouse_controller =
|
||||
(MouseController *)device_controller_factory->Create(
|
||||
DeviceControllerFactory::Device::Mouse);
|
||||
mouse_controller->Init(screen_w, screen_h);
|
||||
}
|
||||
},
|
||||
screen_w, screen_h);
|
||||
|
||||
// Main loop
|
||||
while (!done) {
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplSDLRenderer2_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
if (joined && !menu_hovered) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_None);
|
||||
}
|
||||
|
||||
{
|
||||
static float f = 0.0f;
|
||||
static int counter = 0;
|
||||
|
||||
const ImGuiViewport *main_viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Once);
|
||||
ImGui::SetNextWindowSize(ImVec2(190, 200));
|
||||
|
||||
ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_NoResize);
|
||||
|
||||
{
|
||||
menu_hovered = ImGui::IsWindowHovered();
|
||||
ImGui::Text(" LOCAL ID:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(95);
|
||||
ImGui::InputText(
|
||||
"##local_id", (char *)mac_addr_str.c_str(),
|
||||
mac_addr_str.length() + 1,
|
||||
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::Text(" PASSWORD:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(95);
|
||||
|
||||
char input_password_tmp[7] = "";
|
||||
strncpy(input_password_tmp, input_password, sizeof(input_password));
|
||||
|
||||
ImGui::InputTextWithHint("##server_pwd", "max 6 chars", input_password,
|
||||
IM_ARRAYSIZE(input_password),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
if (strcmp(input_password_tmp, input_password)) {
|
||||
cd_cache_file = fopen("cache.cd", "w+");
|
||||
if (cd_cache_file) {
|
||||
fseek(cd_cache_file, 0, SEEK_SET);
|
||||
strncpy(cd_cache.password, input_password, sizeof(input_password));
|
||||
fwrite(&cd_cache.password, sizeof(cd_cache.password), 1,
|
||||
cd_cache_file);
|
||||
fclose(cd_cache_file);
|
||||
}
|
||||
LeaveConnection(peer_server);
|
||||
CreateConnection(peer_server, mac_addr_str.c_str(), input_password);
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
{
|
||||
{
|
||||
static char remote_id[20] = "";
|
||||
ImGui::Text("REMOTE ID:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(95);
|
||||
ImGui::InputTextWithHint("##remote_id", mac_addr_str.c_str(),
|
||||
remote_id, IM_ARRAYSIZE(remote_id),
|
||||
ImGuiInputTextFlags_CharsUppercase |
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Text(" PASSWORD:");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(95);
|
||||
static char client_password[20] = "";
|
||||
ImGui::InputTextWithHint("##client_pwd", "max 6 chars",
|
||||
client_password,
|
||||
IM_ARRAYSIZE(client_password),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
if (ImGui::Button(connect_label)) {
|
||||
int ret = -1;
|
||||
if ("ClientSignalConnected" == client_signal_status_str) {
|
||||
if (strcmp(connect_label, "Connect") == 0 && !joined) {
|
||||
std::string user_id = "C-" + mac_addr_str;
|
||||
ret = JoinConnection(peer_client, remote_id, client_password);
|
||||
if (0 == ret) {
|
||||
// joined = true;
|
||||
}
|
||||
} else if (strcmp(connect_label, "Disconnect") == 0 && joined) {
|
||||
ret = LeaveConnection(peer_client);
|
||||
memset(audio_buffer, 0, 960);
|
||||
if (0 == ret) {
|
||||
joined = false;
|
||||
received_frame = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == ret) {
|
||||
connect_button_pressed = !connect_button_pressed;
|
||||
connect_label =
|
||||
connect_button_pressed ? "Disconnect" : "Connect";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
{
|
||||
if (ImGui::Button("Resize Window")) {
|
||||
SDL_GetWindowSize(window, &window_w, &window_h);
|
||||
|
||||
if (window_h != window_w * 9 / 16) {
|
||||
window_w = window_h * 16 / 9;
|
||||
}
|
||||
|
||||
SDL_SetWindowSize(window, window_w, window_h);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
SDL_RenderSetScale(sdlRenderer, io.DisplayFramebufferScale.x,
|
||||
io.DisplayFramebufferScale.y);
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event)) {
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
if (event.type == SDL_QUIT) {
|
||||
done = true;
|
||||
} else if (event.type == SDL_WINDOWEVENT &&
|
||||
event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
SDL_GetWindowSize(window, &window_w, &window_h);
|
||||
} else if (event.type == SDL_WINDOWEVENT &&
|
||||
event.window.event == SDL_WINDOWEVENT_CLOSE &&
|
||||
event.window.windowID == SDL_GetWindowID(window)) {
|
||||
done = true;
|
||||
} else if (event.type == REFRESH_EVENT) {
|
||||
sdlRect.x = 0;
|
||||
sdlRect.y = 0;
|
||||
sdlRect.w = window_w;
|
||||
sdlRect.h = window_h;
|
||||
|
||||
SDL_UpdateTexture(sdlTexture, NULL, dst_buffer, pixel_w);
|
||||
} else {
|
||||
if (joined) {
|
||||
ProcessMouseKeyEven(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_RenderClear(sdlRenderer);
|
||||
SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);
|
||||
|
||||
if (!joined || !received_frame) {
|
||||
SDL_RenderClear(sdlRenderer);
|
||||
SDL_SetRenderDrawColor(
|
||||
sdlRenderer, (Uint8)(clear_color.x * 0), (Uint8)(clear_color.y * 0),
|
||||
(Uint8)(clear_color.z * 0), (Uint8)(clear_color.w * 0));
|
||||
}
|
||||
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
|
||||
SDL_RenderPresent(sdlRenderer);
|
||||
|
||||
frame_count++;
|
||||
end_time = SDL_GetTicks();
|
||||
elapsed_time = end_time - start_time;
|
||||
if (elapsed_time >= 1000) {
|
||||
fps = frame_count / (elapsed_time / 1000);
|
||||
frame_count = 0;
|
||||
window_title = "Remote Desk Client FPS [" + std::to_string(fps) +
|
||||
"] status [" + server_signal_status_str + "|" +
|
||||
client_signal_status_str + "|" +
|
||||
server_connection_status_str + "|" +
|
||||
client_connection_status_str + "]";
|
||||
// For MacOS, UI frameworks can only be called from the main thread
|
||||
SDL_SetWindowTitle(window, window_title.c_str());
|
||||
start_time = end_time;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
|
||||
if (is_create_connection) {
|
||||
LeaveConnection(peer_server);
|
||||
}
|
||||
|
||||
if (joined) {
|
||||
LeaveConnection(peer_client);
|
||||
}
|
||||
|
||||
rtc_thread.join();
|
||||
SDL_CloseAudioDevice(output_dev);
|
||||
SDL_CloseAudioDevice(input_dev);
|
||||
|
||||
mouse_controller->Destroy();
|
||||
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
|
||||
SDL_DestroyRenderer(sdlRenderer);
|
||||
SDL_DestroyWindow(window);
|
||||
|
||||
SDL_CloseAudio();
|
||||
SDL_Quit();
|
||||
main_window.Run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
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() {
|
||||
if (inited_ && capture_thread_->joinable()) {
|
||||
capture_thread_->join();
|
||||
inited_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
cb_desktop_data cb) {
|
||||
@@ -91,17 +96,13 @@ int ScreenCapturerX11::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
|
||||
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Destroy() {
|
||||
if (capture_thread_->joinable()) {
|
||||
capture_thread_->join();
|
||||
}
|
||||
inited_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Destroy() { return 0; }
|
||||
|
||||
int ScreenCapturerX11::Start() {
|
||||
capture_thread_.reset(new std::thread([this]() {
|
||||
while (1) {
|
||||
@@ -132,12 +133,12 @@ int ScreenCapturerX11::Start() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerX11::Stop() { return 0; }
|
||||
|
||||
int ScreenCapturerX11::Pause() { return 0; }
|
||||
|
||||
int ScreenCapturerX11::Resume() { return 0; }
|
||||
|
||||
int ScreenCapturerX11::Stop() { return 0; }
|
||||
|
||||
void ScreenCapturerX11::OnFrame() {}
|
||||
|
||||
void ScreenCapturerX11::CleanUp() {}
|
||||
|
||||
@@ -32,9 +32,10 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
||||
|
||||
virtual int Start();
|
||||
|
||||
virtual int Stop();
|
||||
|
||||
int Pause();
|
||||
int Resume();
|
||||
int Stop();
|
||||
|
||||
void OnFrame();
|
||||
|
||||
@@ -61,6 +62,8 @@ class ScreenCapturerX11 : public ScreenCapturer {
|
||||
int videoindex_ = 0;
|
||||
int got_picture_ = 0;
|
||||
int fps_ = 0;
|
||||
bool inited_ = false;
|
||||
|
||||
// ffmpeg
|
||||
AVFormatContext *pFormatCtx_ = nullptr;
|
||||
AVCodecContext *pCodecCtx_ = nullptr;
|
||||
|
||||
@@ -9,7 +9,12 @@ unsigned char nv12_buffer_[NV12_BUFFER_SIZE];
|
||||
|
||||
ScreenCapturerAvf::ScreenCapturerAvf() {}
|
||||
|
||||
ScreenCapturerAvf::~ScreenCapturerAvf() {}
|
||||
ScreenCapturerAvf::~ScreenCapturerAvf() {
|
||||
if (inited_ && capture_thread_->joinable()) {
|
||||
capture_thread_->join();
|
||||
inited_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
cb_desktop_data cb) {
|
||||
@@ -92,17 +97,13 @@ int ScreenCapturerAvf::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
pFrame_->width, pFrame_->height, pCodecCtx_->pix_fmt, pFrameNv12_->width,
|
||||
pFrameNv12_->height, AV_PIX_FMT_NV12, SWS_BICUBIC, NULL, NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Destroy() {
|
||||
if (capture_thread_->joinable()) {
|
||||
capture_thread_->join();
|
||||
}
|
||||
inited_ = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Destroy() { return 0; }
|
||||
|
||||
int ScreenCapturerAvf::Start() {
|
||||
capture_thread_.reset(new std::thread([this]() {
|
||||
while (1) {
|
||||
@@ -133,12 +134,12 @@ int ScreenCapturerAvf::Start() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ScreenCapturerAvf::Stop() { return 0; }
|
||||
|
||||
int ScreenCapturerAvf::Pause() { return 0; }
|
||||
|
||||
int ScreenCapturerAvf::Resume() { return 0; }
|
||||
|
||||
int ScreenCapturerAvf::Stop() { return 0; }
|
||||
|
||||
void ScreenCapturerAvf::OnFrame() {}
|
||||
|
||||
void ScreenCapturerAvf::CleanUp() {}
|
||||
|
||||
@@ -38,9 +38,10 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
||||
|
||||
virtual int Start();
|
||||
|
||||
virtual int Stop();
|
||||
|
||||
int Pause();
|
||||
int Resume();
|
||||
int Stop();
|
||||
|
||||
void OnFrame();
|
||||
|
||||
@@ -66,6 +67,8 @@ class ScreenCapturerAvf : public ScreenCapturer {
|
||||
int i_ = 0;
|
||||
int videoindex_ = 0;
|
||||
int got_picture_ = 0;
|
||||
bool inited_ = false;
|
||||
|
||||
// ffmpeg
|
||||
AVFormatContext *pFormatCtx_ = nullptr;
|
||||
AVCodecContext *pCodecCtx_ = nullptr;
|
||||
|
||||
@@ -28,6 +28,8 @@ class ScreenCapturer {
|
||||
virtual int Destroy() = 0;
|
||||
|
||||
virtual int Start() = 0;
|
||||
|
||||
virtual int Stop() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -5,36 +5,9 @@
|
||||
#include <winrt/Windows.Foundation.Metadata.h>
|
||||
#include <winrt/Windows.Graphics.Capture.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
};
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int BGRAToNV12FFmpeg(unsigned char *src_buffer, int width, int height,
|
||||
unsigned char *dst_buffer) {
|
||||
AVFrame *Input_pFrame = av_frame_alloc();
|
||||
AVFrame *Output_pFrame = av_frame_alloc();
|
||||
struct SwsContext *img_convert_ctx =
|
||||
sws_getContext(width, height, AV_PIX_FMT_BGRA, 1280, 720, AV_PIX_FMT_NV12,
|
||||
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
|
||||
|
||||
av_image_fill_arrays(Input_pFrame->data, Input_pFrame->linesize, src_buffer,
|
||||
AV_PIX_FMT_BGRA, width, height, 1);
|
||||
av_image_fill_arrays(Output_pFrame->data, Output_pFrame->linesize, dst_buffer,
|
||||
AV_PIX_FMT_NV12, 1280, 720, 1);
|
||||
|
||||
sws_scale(img_convert_ctx, (uint8_t const **)Input_pFrame->data,
|
||||
Input_pFrame->linesize, 0, height, Output_pFrame->data,
|
||||
Output_pFrame->linesize);
|
||||
|
||||
if (Input_pFrame) av_free(Input_pFrame);
|
||||
if (Output_pFrame) av_free(Output_pFrame);
|
||||
if (img_convert_ctx) sws_freeContext(img_convert_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#include "libyuv.h"
|
||||
|
||||
BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, HDC hdc, LPRECT lprc,
|
||||
LPARAM data) {
|
||||
@@ -62,7 +35,20 @@ HMONITOR GetPrimaryMonitor() {
|
||||
|
||||
ScreenCapturerWgc::ScreenCapturerWgc() {}
|
||||
|
||||
ScreenCapturerWgc::~ScreenCapturerWgc() {}
|
||||
ScreenCapturerWgc::~ScreenCapturerWgc() {
|
||||
Stop();
|
||||
CleanUp();
|
||||
|
||||
if (nv12_frame_) {
|
||||
delete nv12_frame_;
|
||||
nv12_frame_ = nullptr;
|
||||
}
|
||||
|
||||
if (nv12_frame_scaled_) {
|
||||
delete nv12_frame_scaled_;
|
||||
nv12_frame_scaled_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScreenCapturerWgc::IsWgcSupported() {
|
||||
try {
|
||||
@@ -81,7 +67,11 @@ int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
int error = 0;
|
||||
if (_inited == true) return error;
|
||||
|
||||
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 4];
|
||||
int r = rect.right;
|
||||
int b = rect.bottom;
|
||||
|
||||
nv12_frame_ = new unsigned char[rect.right * rect.bottom * 3 / 2];
|
||||
nv12_frame_scaled_ = new unsigned char[1280 * 720 * 3 / 2];
|
||||
|
||||
_fps = fps;
|
||||
|
||||
@@ -114,17 +104,7 @@ int ScreenCapturerWgc::Init(const RECORD_DESKTOP_RECT &rect, const int fps,
|
||||
return error;
|
||||
}
|
||||
|
||||
int ScreenCapturerWgc::Destroy() {
|
||||
if (nv12_frame_) {
|
||||
delete nv12_frame_;
|
||||
nv12_frame_ = nullptr;
|
||||
}
|
||||
|
||||
Stop();
|
||||
CleanUp();
|
||||
|
||||
return 0;
|
||||
}
|
||||
int ScreenCapturerWgc::Destroy() { return 0; }
|
||||
|
||||
int ScreenCapturerWgc::Start() {
|
||||
if (_running == true) {
|
||||
@@ -163,12 +143,59 @@ int ScreenCapturerWgc::Stop() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ConvertABGRtoBGRA(const uint8_t *abgr_data, uint8_t *bgra_data, int width,
|
||||
int height, int abgr_stride, int bgra_stride) {
|
||||
for (int i = 0; i < height; ++i) {
|
||||
for (int j = 0; j < width; ++j) {
|
||||
// ABGR<47><52>BGRA<52><41><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
|
||||
int abgr_index = (i * abgr_stride + j) * 4;
|
||||
int bgra_index = (i * bgra_stride + j) * 4;
|
||||
|
||||
// ֱ<>ӽ<EFBFBD><D3BD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD>ͺ<EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||||
bgra_data[bgra_index + 0] = abgr_data[abgr_index + 2]; // <20><>ɫ
|
||||
bgra_data[bgra_index + 1] = abgr_data[abgr_index + 1]; // <20><>ɫ
|
||||
bgra_data[bgra_index + 2] = abgr_data[abgr_index + 0]; // <20><>ɫ
|
||||
bgra_data[bgra_index + 3] = abgr_data[abgr_index + 3]; // Alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertBGRAtoABGR(const uint8_t *bgra_data, uint8_t *abgr_data, int width,
|
||||
int height, int bgra_stride, int abgr_stride) {
|
||||
for (int i = 0; i < height; ++i) {
|
||||
for (int j = 0; j < width; ++j) {
|
||||
// BGRA<52><41>ABGR<47><52><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ӳ<EFBFBD><D3B3>
|
||||
int bgra_index = (i * bgra_stride + j) * 4;
|
||||
int abgr_index = (i * abgr_stride + j) * 4;
|
||||
|
||||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͬʱ<CDAC><CAB1><EFBFBD><EFBFBD>Alphaͨ<61><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>
|
||||
abgr_data[abgr_index + 0] = bgra_data[bgra_index + 3]; // Alpha
|
||||
abgr_data[abgr_index + 1] = bgra_data[bgra_index + 0]; // Blue
|
||||
abgr_data[abgr_index + 2] = bgra_data[bgra_index + 1]; // Green
|
||||
abgr_data[abgr_index + 3] = bgra_data[bgra_index + 2]; // Red
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame) {
|
||||
if (_on_data)
|
||||
BGRAToNV12FFmpeg((unsigned char *)frame.data, frame.width, frame.height,
|
||||
nv12_frame_);
|
||||
_on_data(nv12_frame_, frame.width * frame.height * 4, frame.width,
|
||||
frame.height);
|
||||
if (_on_data) {
|
||||
int width = 1280;
|
||||
int height = 720;
|
||||
|
||||
libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4,
|
||||
(uint8_t *)nv12_frame_, frame.width,
|
||||
(uint8_t *)(nv12_frame_ + frame.width * frame.height),
|
||||
frame.width, frame.width, frame.height);
|
||||
|
||||
libyuv::NV12Scale(
|
||||
(const uint8_t *)nv12_frame_, frame.width,
|
||||
(const uint8_t *)(nv12_frame_ + frame.width * frame.height),
|
||||
frame.width, frame.width, frame.height, (uint8_t *)nv12_frame_scaled_,
|
||||
width, (uint8_t *)(nv12_frame_scaled_ + width * height), width, width,
|
||||
height, libyuv::FilterMode::kFilterLinear);
|
||||
|
||||
_on_data(nv12_frame_scaled_, width * height * 3 / 2, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenCapturerWgc::CleanUp() {
|
||||
|
||||
@@ -27,7 +27,7 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
||||
|
||||
int Pause();
|
||||
int Resume();
|
||||
int Stop();
|
||||
virtual int Stop();
|
||||
|
||||
void OnFrame(const WgcSession::wgc_session_frame &frame);
|
||||
|
||||
@@ -49,9 +49,10 @@ class ScreenCapturerWgc : public ScreenCapturer,
|
||||
|
||||
int _fps;
|
||||
|
||||
cb_desktop_data _on_data;
|
||||
cb_desktop_data _on_data = nullptr;
|
||||
|
||||
unsigned char *nv12_frame_ = nullptr;
|
||||
unsigned char *nv12_frame_scaled_ = nullptr;
|
||||
};
|
||||
|
||||
#endif
|
||||
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
|
||||
elseif is_plat("linux") then
|
||||
includes("ffmpeg")
|
||||
|
||||
68
xmake.lua
68
xmake.lua
@@ -13,19 +13,16 @@ if is_mode("debug") then
|
||||
add_defines("REMOTE_DESK_DEBUG")
|
||||
end
|
||||
|
||||
add_requires("sdl2", {system = false})
|
||||
add_requires("spdlog 1.11.0", {system = false})
|
||||
add_requires("imgui 1.89.9", {configs = {sdl2 = true, sdl2_renderer = true}})
|
||||
add_requires("spdlog 1.14.1", {system = false})
|
||||
add_requires("imgui 1.90.6", {configs = {sdl2 = true, sdl2_renderer = true}})
|
||||
add_requires("libyuv")
|
||||
|
||||
if is_os("windows") then
|
||||
add_links("Shell32", "windowsapp", "dwmapi", "User32", "kernel32",
|
||||
"SDL2-static", "SDL2main", "gdi32", "winmm", "setupapi", "version",
|
||||
"Imm32", "iphlpapi")
|
||||
add_requires("vcpkg::ffmpeg 5.1.2", {configs = {shared = false}})
|
||||
add_packages("vcpkg::ffmpeg")
|
||||
elseif is_os("linux") then
|
||||
add_requires("ffmpeg 5.1.2", {system = false})
|
||||
add_packages("ffmpeg")
|
||||
add_syslinks("pthread", "dl")
|
||||
add_linkdirs("thirdparty/projectx/thirdparty/nvcodec/Lib/x64")
|
||||
add_links("SDL2", "cuda", "nvidia-encode", "nvcuvid")
|
||||
@@ -37,13 +34,13 @@ elseif is_os("linux") then
|
||||
elseif is_os("macosx") then
|
||||
add_requires("ffmpeg 5.1.2", {system = false})
|
||||
add_requires("libxcb", {system = false})
|
||||
add_packages("ffmpeg", "libxcb")
|
||||
add_packages("libxcb")
|
||||
add_links("SDL2", "SDL2main")
|
||||
add_ldflags("-Wl,-ld_classic")
|
||||
add_frameworks("OpenGL")
|
||||
end
|
||||
|
||||
add_packages("spdlog", "sdl2", "imgui")
|
||||
add_packages("spdlog", "imgui")
|
||||
|
||||
includes("thirdparty")
|
||||
|
||||
@@ -62,16 +59,19 @@ target("common")
|
||||
target("screen_capturer")
|
||||
set_kind("object")
|
||||
add_deps("log")
|
||||
add_packages("libyuv")
|
||||
add_includedirs("src/screen_capturer", {public = true})
|
||||
if is_os("windows") then
|
||||
add_files("src/screen_capturer/windows/*.cpp")
|
||||
add_includedirs("src/screen_capturer/windows", {public = true})
|
||||
elseif is_os("macosx") then
|
||||
add_files("src/screen_capturer/macosx/*.cpp")
|
||||
add_includedirs("src/screen_capturer/macosx", {public = true})
|
||||
add_packages("ffmpeg")
|
||||
add_files("src/screen_capturer/macosx/*.cpp")
|
||||
add_includedirs("src/screen_capturer/macosx", {public = true})
|
||||
elseif is_os("linux") then
|
||||
add_files("src/screen_capturer/linux/*.cpp")
|
||||
add_includedirs("src/screen_capturer/linux", {public = true})
|
||||
add_packages("ffmpeg")
|
||||
add_files("src/screen_capturer/linux/*.cpp")
|
||||
add_includedirs("src/screen_capturer/linux", {public = true})
|
||||
end
|
||||
|
||||
target("device_controller")
|
||||
@@ -89,19 +89,37 @@ target("device_controller")
|
||||
add_includedirs("src/device_controller/mouse/linux", {public = true})
|
||||
end
|
||||
|
||||
target("config_center")
|
||||
set_kind("object")
|
||||
add_deps("log")
|
||||
add_files("src/config_center/*.cpp")
|
||||
add_includedirs("src/config_center", {public = true})
|
||||
|
||||
target("localization")
|
||||
set_kind("headeronly")
|
||||
add_includedirs("src/localization", {public = true})
|
||||
|
||||
target("main_window")
|
||||
set_kind("object")
|
||||
add_deps("log", "common", "localization", "config_center", "projectx", "screen_capturer", "device_controller")
|
||||
if is_os("macosx") then
|
||||
add_packages("ffmpeg")
|
||||
elseif is_os("linux") then
|
||||
add_packages("ffmpeg")
|
||||
end
|
||||
add_files("src/main_window/*.cpp")
|
||||
add_includedirs("src/main_window", {public = true})
|
||||
|
||||
target("remote_desk")
|
||||
set_kind("binary")
|
||||
add_deps("log", "common", "screen_capturer", "device_controller", "projectx")
|
||||
add_deps("log", "common", "main_window")
|
||||
if is_os("macosx") then
|
||||
add_packages("ffmpeg")
|
||||
elseif is_os("linux") then
|
||||
add_packages("ffmpeg")
|
||||
end
|
||||
add_files("src/gui/main.cpp")
|
||||
|
||||
-- after_install(function (target)
|
||||
-- os.cp("$(projectdir)/thirdparty/nvcodec/Lib/x64/*.so", "$(projectdir)/out/bin")
|
||||
-- os.cp("$(projectdir)/thirdparty/nvcodec/Lib/x64/*.so.1", "$(projectdir)/out/bin")
|
||||
-- os.cp("$(projectdir)/out/lib/*.so", "$(projectdir)/out/bin")
|
||||
-- os.rm("$(projectdir)/out/include")
|
||||
-- os.rm("$(projectdir)/out/lib")
|
||||
-- end)
|
||||
|
||||
-- target("screen_capturer")
|
||||
-- set_kind("binary")
|
||||
-- add_packages("sdl2", "imgui", "ffmpeg", "openh264")
|
||||
@@ -157,7 +175,7 @@ target("remote_desk")
|
||||
-- "-lxcb-shm", "-lXext", "-lX11", "-lXv", "-lpthread", "-lSDL2", "-lopenh264",
|
||||
-- "-ldl", {force = true})
|
||||
|
||||
target("mouse_control")
|
||||
set_kind("binary")
|
||||
add_files("test/linux_mouse_control/mouse_control.cpp")
|
||||
add_includedirs("test/linux_mouse_control")
|
||||
-- target("mouse_control")
|
||||
-- set_kind("binary")
|
||||
-- add_files("test/linux_mouse_control/mouse_control.cpp")
|
||||
-- add_includedirs("test/linux_mouse_control")
|
||||
Reference in New Issue
Block a user