[feat] prefer hardware video codecs by default

This commit is contained in:
dijunkun
2026-10-22 17:20:48 +08:00
parent f8f4aa3cea
commit 193dcc4be0
22 changed files with 29 additions and 98 deletions
-2
View File
@@ -208,8 +208,6 @@ int HandleServiceCliCommand(const std::string& command) {
destination.SetTurnMode(source.GetTurnMode()) == 0 &&
destination.SetVideoEncodeFormat(source.GetVideoEncodeFormat()) ==
0 &&
destination.SetHardwareVideoCodec(source.IsHardwareVideoCodec()) ==
0 &&
destination.SetScreenCaptureMethod(
crossdesk::ScreenCaptureMethod::Auto) == 0 &&
destination.SetPrivacyScreen(false) == 0 &&
@@ -41,6 +41,8 @@ int ConfigCenter::Load() {
}
bool persist_config_migration = false;
// Hardware codecs are always preferred, with software fallback in MiniRTC.
persist_config_migration |= ini_->Delete(section_, "hardware_video_codec");
// These options now belong only to the active remote connection. Discard
// legacy local defaults so they cannot affect this host or new connections.
persist_config_migration |= ini_->Delete(section_, "video_quality");
@@ -78,8 +80,6 @@ int ConfigCenter::Load() {
ini_->GetLongValue(section_, "video_encode_format",
static_cast<long>(video_encode_format_)));
hardware_video_codec_ = ini_->GetBoolValue(section_, "hardware_video_codec",
hardware_video_codec_);
const char* turn_mode_value = ini_->GetValue(section_, "turn_mode", nullptr);
if (turn_mode_value != nullptr && strlen(turn_mode_value) > 0) {
const long parsed_turn_mode = ini_->GetLongValue(
@@ -205,7 +205,6 @@ int ConfigCenter::Save() {
static_cast<long>(screen_capture_method_));
ini_->SetLongValue(section_, "video_encode_format",
static_cast<long>(video_encode_format_));
ini_->SetBoolValue(section_, "hardware_video_codec", hardware_video_codec_);
ini_->SetLongValue(section_, "turn_mode", static_cast<long>(turn_mode_));
ini_->SetBoolValue(section_, "enable_turn",
turn_mode_ != TURN_MODE::DISABLED);
@@ -271,13 +270,6 @@ int ConfigCenter::SetVideoEncodeFormat(VIDEO_ENCODE_FORMAT format) {
return 0;
}
int ConfigCenter::SetHardwareVideoCodec(bool enabled) {
if (enabled && !IsHardwareVideoCodecAvailable()) return -1;
if (StoreValues({{"hardware_video_codec", enabled ? "true" : "false"}}) != 0) return -1;
hardware_video_codec_ = enabled;
return 0;
}
int ConfigCenter::SetTurnMode(TURN_MODE mode) {
if (!IsValidTurnModeValue(static_cast<long>(mode))) return -1;
if (StoreValues({{"turn_mode", std::to_string(static_cast<int>(mode))},
@@ -425,19 +417,6 @@ ConfigCenter::VIDEO_ENCODE_FORMAT ConfigCenter::GetVideoEncodeFormat() const {
return video_encode_format_;
}
bool ConfigCenter::IsHardwareVideoCodecAvailable() {
#if defined(_WIN32) || defined(__APPLE__) || \
(defined(__linux__) && !defined(__aarch64__) && !defined(__arm__) && USE_CUDA)
return true;
#else
return false;
#endif
}
bool ConfigCenter::IsHardwareVideoCodec() const {
return IsHardwareVideoCodecAvailable() && hardware_video_codec_;
}
ConfigCenter::TURN_MODE ConfigCenter::GetTurnMode() const {
return turn_mode_;
}
@@ -37,7 +37,6 @@ class ConfigCenter {
int SetLanguage(LANGUAGE language);
int SetScreenCaptureMethod(ScreenCaptureMethod method);
int SetVideoEncodeFormat(VIDEO_ENCODE_FORMAT video_encode_format);
int SetHardwareVideoCodec(bool hardware_video_codec);
int SetTurnMode(TURN_MODE turn_mode);
int SetTurn(bool enable_turn, bool force_relay);
int SetServerHost(const std::string& signal_server_host);
@@ -57,9 +56,6 @@ class ConfigCenter {
LANGUAGE GetLanguage() const;
ScreenCaptureMethod GetScreenCaptureMethod() const;
VIDEO_ENCODE_FORMAT GetVideoEncodeFormat() const;
// Whether this build includes a hardware video codec backend.
static bool IsHardwareVideoCodecAvailable();
bool IsHardwareVideoCodec() const;
TURN_MODE GetTurnMode() const;
bool IsEnableTurn() const;
bool IsForceRelay() const;
@@ -92,7 +88,6 @@ class ConfigCenter {
LANGUAGE language_ = LANGUAGE::CHINESE;
ScreenCaptureMethod screen_capture_method_ = ScreenCaptureMethod::Auto;
VIDEO_ENCODE_FORMAT video_encode_format_ = VIDEO_ENCODE_FORMAT::H264;
bool hardware_video_codec_ = false;
TURN_MODE turn_mode_ = TURN_MODE::AUTO_UDP_TCP;
std::string signal_server_host_ = "";
std::string signal_server_host_default_ = "api.crossdesk.cn";
@@ -1201,7 +1201,7 @@ void GuiApplication::InitializeSettings() {
bool GuiApplication::InitializeSDL() {
#if (defined(__linux__) && !defined(__OHOS__)) && !defined(__APPLE__)
ConfigureLinuxCudaGraphics(config_center_->IsHardwareVideoCodec());
ConfigureLinuxCudaGraphics();
// Standard Wayland top-level windows cannot choose an absolute screen
// position. Use the session's XWayland compatibility server for the GUI so
// auxiliary windows such as the controlled-side status window can be
@@ -1477,11 +1477,6 @@ void GuiApplication::ResetSettingsUi(bool gallery_window) {
auto& main = ui_->Window(gallery_window);
main->set_language_index(language_button_value_);
main->set_codec_index(video_encode_format_button_value_);
const bool hardware_codec_available =
ConfigCenter::IsHardwareVideoCodecAvailable();
main->set_hardware_codec_available(hardware_codec_available);
main->set_hardware_codec_enabled(hardware_codec_available &&
enable_hardware_video_codec_);
main->set_turn_enabled(enable_turn_);
main->set_force_relay_enabled(config_center_->IsForceRelay());
main->set_self_hosted_enabled(enable_self_hosted_);
@@ -4293,8 +4288,6 @@ bool GuiApplication::SaveSettingsFromUi(bool gallery_window) {
language_button_value_ =
localization::detail::ClampLanguageIndex(main->get_language_index());
video_encode_format_button_value_ = std::clamp(main->get_codec_index(), 0, 1);
enable_hardware_video_codec_ = ConfigCenter::IsHardwareVideoCodecAvailable() &&
main->get_hardware_codec_enabled();
enable_turn_ = main->get_turn_enabled();
enable_self_hosted_ = main->get_self_hosted_enabled();
#if !defined(__OHOS__)
@@ -4311,7 +4304,6 @@ bool GuiApplication::SaveSettingsFromUi(bool gallery_window) {
config_center_->SetVideoEncodeFormat(
static_cast<ConfigCenter::VIDEO_ENCODE_FORMAT>(
video_encode_format_button_value_));
config_center_->SetHardwareVideoCodec(enable_hardware_video_codec_);
config_center_->SetTurn(enable_turn_, main->get_force_relay_enabled());
main->set_force_relay_enabled(config_center_->IsForceRelay());
#if !defined(__OHOS__)
@@ -4378,7 +4370,6 @@ void GuiApplication::ApplySettingsFromConfig(bool reconnect) {
localization_language_index_ = localization::detail::ClampLanguageIndex(language_button_value_);
localization_language_ = config_center_->GetLanguage();
video_encode_format_button_value_ = static_cast<int>(config_center_->GetVideoEncodeFormat());
enable_hardware_video_codec_ = config_center_->IsHardwareVideoCodec();
enable_turn_ = config_center_->IsEnableTurn();
enable_self_hosted_ = config_center_->IsSelfHosted();
enable_autostart_ = config_center_->IsEnableAutostart();
@@ -4393,7 +4384,6 @@ void GuiApplication::ApplySettingsFromConfig(bool reconnect) {
std::snprintf(signal_server_port_self_, sizeof(signal_server_port_self_), "%s", port_text.c_str());
language_button_value_last_ = language_button_value_;
video_encode_format_button_value_last_ = video_encode_format_button_value_;
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
enable_turn_last_ = enable_turn_;
enable_self_hosted_last_ = enable_self_hosted_;
enable_autostart_last_ = enable_autostart_;
@@ -4412,17 +4402,14 @@ void GuiApplication::ApplySettingsFromConfig(bool reconnect) {
// configuration. Keep the application and its console running throughout.
CloseAllRemoteSessions();
} else {
const bool hardware = config_center_->IsHardwareVideoCodec();
const bool av1 = config_center_->GetVideoEncodeFormat() ==
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1;
const auto turn = static_cast<TurnMode>(config_center_->GetTurnMode());
if (hardware != params_.hardware_acceleration || av1 != params_.av1_encoding ||
turn != params_.turn_mode) {
if (av1 != params_.av1_encoding || turn != params_.turn_mode) {
// Update the listening peer for incoming connections and the template
// copied by ConnectTo for outgoing connections. Existing sessions retain
// the codec/ICE snapshot with which they negotiated.
if (!peer_ || UpdateConnectionSettings(peer_, hardware, av1, turn) == 0) {
params_.hardware_acceleration = hardware;
if (!peer_ || UpdateConnectionSettings(peer_, true, av1, turn) == 0) {
params_.av1_encoding = av1;
params_.turn_mode = turn;
} else {
@@ -78,13 +78,11 @@ struct TranslationRow {
X(console_settings_next_file, u8"已保存,将用于后续接收的文件。", "Saved; applies to subsequently received files.", u8"Сохранено. Будет применено к следующим получаемым файлам.") \
X(console_settings_autostart, u8"已保存,在下次图形桌面登录时生效。", "Saved; applies at the next graphical desktop login.", u8"Сохранено. Будет применено при следующем входе в графический сеанс.") \
X(console_settings_daemon, u8"已保存,普通模式重启后生效;无头控制台仍保持前台运行。", "Saved for the next normal-mode start; the headless console stays in the foreground.", u8"Сохранено для следующего запуска в обычном режиме. Консоль остаётся на переднем плане.") \
X(console_settings_hardware_unavailable, u8"当前构建不支持硬件编解码,未保存。", "Hardware codecs are unavailable in this build; not saved.", u8"Аппаратные кодеки недоступны в этой сборке. Не сохранено.") \
X(console_settings_server_required, u8"请先设置有效的服务器地址和端口,再启用自托管。", "Set a valid server host and port before enabling self-hosting.", u8"Укажите адрес и порт сервера перед включением собственного сервера.") \
X(console_settings_path_invalid, u8"请选择已存在且可写的绝对目录路径;default 使用默认目录。", "Use an existing writable absolute directory, or default for the default location.", u8"Укажите существующий доступный для записи абсолютный путь к каталогу или default.") \
X(console_settings_readonly, u8"该选项由系统或控制端管理,不可在此修改。", "This option is managed by the system or controller and is read-only here.", u8"Этот параметр управляется системой или клиентом и здесь недоступен для изменения.") \
X(console_setting_language, u8"语言", "Language", u8"Язык") \
X(console_setting_codec, u8"视频编码", "Video codec", u8"Видеокодек") \
X(console_setting_hardware, u8"硬件编解码", "Hardware codecs", u8"Аппаратные кодеки") \
X(console_setting_turn, u8"中继模式", "TURN relay mode", u8"Режим ретрансляции TURN") \
X(console_setting_self_hosted, u8"自托管", "Self-hosted server", u8"Собственный сервер") \
X(console_setting_host, u8"服务器地址", "Server host", u8"Адрес сервера") \
@@ -234,8 +232,6 @@ struct TranslationRow {
"Video Encode Format:", u8"Формат кодека видео:") \
X(av1, u8"AV1", "AV1", "AV1") \
X(h264, u8"H.264", "H.264", "H.264") \
X(enable_hardware_video_codec, u8"启用硬件编解码器:", \
"Enable Hardware Video Codec:", u8"Использовать аппаратный кодек:") \
X(enable_turn, u8"启用中继服务:", \
"Enable TURN Service:", u8"Включить TURN-сервис:") \
X(force_relay, u8"强制中继连接:", "Force Relay:", u8"Только через реле:") \
@@ -465,8 +465,6 @@ int SettingsManager::Load() {
static_cast<int>(owner_.config_center_->GetLanguage()));
owner_.video_encode_format_button_value_ =
static_cast<int>(owner_.config_center_->GetVideoEncodeFormat());
owner_.enable_hardware_video_codec_ =
owner_.config_center_->IsHardwareVideoCodec();
owner_.enable_turn_ = owner_.config_center_->IsEnableTurn();
owner_.enable_self_hosted_ = owner_.config_center_->IsSelfHosted();
owner_.enable_autostart_ = owner_.config_center_->IsEnableAutostart();
@@ -486,8 +484,6 @@ int SettingsManager::Load() {
owner_.language_button_value_last_ = owner_.language_button_value_;
owner_.video_encode_format_button_value_last_ =
owner_.video_encode_format_button_value_;
owner_.enable_hardware_video_codec_last_ =
owner_.enable_hardware_video_codec_;
owner_.enable_turn_last_ = owner_.enable_turn_;
owner_.enable_self_hosted_last_ = owner_.enable_self_hosted_;
owner_.enable_autostart_last_ = owner_.enable_autostart_;
@@ -63,7 +63,7 @@ private:
int legacy_video_quality;
int legacy_video_frame_rate;
int video_encode_format;
bool enable_hardware_video_codec;
bool legacy_enable_hardware_video_codec;
bool enable_turn;
bool enable_srtp;
unsigned char key[16];
@@ -77,7 +77,7 @@ private:
int legacy_video_quality;
int legacy_video_frame_rate;
int video_encode_format;
bool enable_hardware_video_codec;
bool legacy_enable_hardware_video_codec;
bool enable_turn;
bool enable_srtp;
unsigned char key[16];
+2 -1
View File
@@ -237,7 +237,8 @@ int GuiRuntime::CreateConnectionPeer() {
strncpy(params_.log_path, dll_log_path_.c_str(),
sizeof(params_.log_path) - 1);
params_.log_path[sizeof(params_.log_path) - 1] = '\0';
params_.hardware_acceleration = config_center_->IsHardwareVideoCodec();
// MiniRTC selects the available hardware backend and falls back to software.
params_.hardware_acceleration = true;
#if defined(_WIN32) || (defined(__linux__) && !defined(__OHOS__)) || defined(__APPLE__)
// Windows and Linux retain pooled CPU NV12 frames or CUDA device frames;
// macOS retains VideoToolbox CVPixelBuffers for direct Metal sampling. All
@@ -149,14 +149,12 @@ struct UserSettingsState {
char connection_login_identity_[17] = "";
int language_button_value_ = 0;
int video_encode_format_button_value_ = 0;
bool enable_hardware_video_codec_ = true;
bool enable_turn_ = true;
char signal_server_ip_[256] = "api.crossdesk.cn";
char signal_server_port_[6] = "9099";
bool enable_self_hosted_ = false;
int language_button_value_last_ = 0;
int video_encode_format_button_value_last_ = 0;
bool enable_hardware_video_codec_last_ = false;
bool enable_turn_last_ = true;
bool enable_self_hosted_last_ = false;
bool enable_autostart_ = false;
+2 -8
View File
@@ -69,7 +69,6 @@ export global UiStrings {
in-out property <string> capture-method: "Capture method";
in-out property <string> capture-method-auto: "Auto";
in-out property <string> codec: "Video codec";
in-out property <string> hardware-codec: "Hardware video codec";
in-out property <string> turn-relay: "TURN relay";
in-out property <string> force-relay: "Force relay";
in-out property <string> self-hosted: "Self-hosted server";
@@ -456,7 +455,6 @@ export component MainWindow inherits Window {
in property <bool> capture-method-visible: false;
in-out property <int> capture-method-index: 0;
in-out property <int> codec-index: 0;
in-out property <bool> hardware-codec-enabled: true;
in-out property <bool> turn-enabled: true;
in-out property <bool> force-relay-enabled: false;
changed turn-enabled => {
@@ -484,9 +482,6 @@ export component MainWindow inherits Window {
in-out property <string> server-port: "";
in-out property <string> self-host-settings-error: "";
in property <bool> settings-session-active: false;
// Keep the setting visible on every platform. Unsupported builds expose it
// as disabled so the fixed settings layout never turns into a blank row.
in property <bool> hardware-codec-available: true;
callback copy-local-id() -> bool;
callback toggle-password-visibility;
@@ -2175,16 +2170,15 @@ export component MainWindow inherits Window {
}
SettingsGroup {
title: GalleryStrings.tr("视频", "Video", "Видео");
rows: root.capture-method-visible ? 3 : 2;
rows: root.capture-method-visible ? 2 : 1;
if root.capture-method-visible: SettingsRow {
label: UiStrings.capture-method; enabled: !root.settings-session-active;
SettingsSelect { x: parent.width - 116px; y: 4px; width: 104px; enabled: !root.settings-session-active; model: [UiStrings.capture-method-auto, "DXGI", "WGC", "GDI"]; current-index <=> root.capture-method-index; }
}
SettingsRow {
label: UiStrings.codec; enabled: !root.settings-session-active;
label: UiStrings.codec; enabled: !root.settings-session-active; divider: false;
SettingsSelect { x: parent.width - 116px; y: 4px; width: 104px; enabled: !root.settings-session-active; model: [UiStrings.codec-h264, UiStrings.codec-av1]; current-index <=> root.codec-index; }
}
SettingsToggleRow { label: UiStrings.hardware-codec; checked <=> root.hardware-codec-enabled; enabled: root.hardware-codec-available && !root.settings-session-active; divider: false; }
}
SettingsGroup {
title: GalleryStrings.tr("网络连接", "Network", "Сеть"); rows: 3;
@@ -93,8 +93,6 @@ inline int ApplyMainWindowStrings(
strings.set_capture_method_auto(
Text(localization::screen_capture_method_auto[language]));
strings.set_codec(Text(localization::video_encode_format[language]));
strings.set_hardware_codec(
Text(localization::enable_hardware_video_codec[language]));
strings.set_turn_relay(Text(localization::enable_turn[language]));
strings.set_force_relay(Text(localization::force_relay[language]));
strings.set_self_hosted(
@@ -9,12 +9,8 @@
namespace crossdesk {
void ConfigureLinuxCudaGraphics(bool hardware_acceleration) {
void ConfigureLinuxCudaGraphics() {
#if USE_CUDA && (defined(__x86_64__) || defined(__amd64__))
if (!hardware_acceleration) {
return;
}
// Respect explicit GPU/driver selection, including a request to use Mesa
// or software rendering. Native frames can still use the CPU fallback.
for (const char* name : {"__NV_PRIME_RENDER_OFFLOAD",
@@ -45,8 +41,6 @@ void ConfigureLinuxCudaGraphics(bool hardware_acceleration) {
return;
}
LOG_INFO("Selected NVIDIA PRIME rendering for CUDA/OpenGL video interop");
#else
(void)hardware_acceleration;
#endif
}
@@ -11,7 +11,7 @@ namespace crossdesk {
// Call before SDL or Slint initializes EGL/GLX. Hybrid laptops otherwise
// create the GUI context on the integrated GPU while NVDEC uses NVIDIA.
void ConfigureLinuxCudaGraphics(bool hardware_acceleration);
void ConfigureLinuxCudaGraphics();
} // namespace crossdesk
@@ -35,7 +35,6 @@ std::vector<ConsoleSetting> GetConsoleSettings(const ConfigCenter& config) {
return {
{"language", "console_setting_language", languages[language], "zh-CN / en-US / ru-RU"},
{"codec", "console_setting_codec", config.GetVideoEncodeFormat() == ConfigCenter::VIDEO_ENCODE_FORMAT::AV1 ? "av1" : "h264", "h264 / av1"},
{"hardware", "console_setting_hardware", OnOff(config.IsHardwareVideoCodec()), "on / off"},
{"turn", "console_setting_turn", turn_modes[turn], "off / auto / udp / tcp"},
{"self_hosted", "console_setting_self_hosted", OnOff(config.IsSelfHosted()), "on / off"},
{"server_host", "console_setting_host", config.GetSignalServerHost(), "example.com"},
@@ -54,7 +53,7 @@ SettingResult ApplyConsoleSetting(ConfigCenter& config, const std::string& key,
result.message = "console_settings_readonly";
return result;
}
const bool network = key == "codec" || key == "hardware" || key == "turn" ||
const bool network = key == "codec" || key == "turn" ||
key == "self_hosted" || key == "server_host" || key == "server_port";
if (network && connection_busy) {
result.message = "console_settings_busy";
@@ -111,13 +110,7 @@ SettingResult ApplyConsoleSetting(ConfigCenter& config, const std::string& key,
if (!boolean) return result;
const bool enabled = *boolean;
canonical = OnOff(enabled);
if (key == "hardware") {
if (enabled && !ConfigCenter::IsHardwareVideoCodecAvailable()) {
result.message = "console_settings_hardware_unavailable";
return result;
}
save = [&config, enabled] { return config.SetHardwareVideoCodec(enabled); };
} else if (key == "self_hosted") {
if (key == "self_hosted") {
if (enabled && (config.GetSignalServerHost().empty() || config.GetSignalServerPort() <= 0 || config.GetSignalServerPort() > 65535)) {
result.message = "console_settings_server_required";
return result;
@@ -138,7 +131,7 @@ SettingResult ApplyConsoleSetting(ConfigCenter& config, const std::string& key,
} else return result;
}
for (const auto& setting : GetConsoleSettings(config)) {
if (key != "autostart" && key != "hardware" && setting.key == key && setting.value == canonical) {
if (key != "autostart" && setting.key == key && setting.value == canonical) {
return {true, SettingEffect::none, "console_settings_unchanged"};
}
}
@@ -148,7 +141,7 @@ SettingResult ApplyConsoleSetting(ConfigCenter& config, const std::string& key,
if (previous_server != GetSignalServerSettings(config)) {
effect = SettingEffect::reconnect;
message = "console_settings_reconnect";
} else if (key == "codec" || key == "hardware" || key == "turn") {
} else if (key == "codec" || key == "turn") {
effect = SettingEffect::next_connection;
message = "console_settings_next_connection";
}
@@ -160,7 +160,6 @@ bool ConfigureDocumentationDemo(
std::make_shared<slint::VectorModel<crossdesk::ui::RecentConnection>>(
std::move(connections)));
window->set_signal_connected(true);
window->set_hardware_codec_available(true);
#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
window->set_privacy_setting_available(true);
#endif
@@ -937,7 +936,6 @@ int main() {
window->set_custom_titlebar(true);
window->set_wayland_titlebar(true);
window->set_settings_session_active(true);
window->set_hardware_codec_available(false);
window->set_portable_service_settings_visible(true);
assert(std::string(window->get_local_id()) == "123 456 789");
assert(window->get_connection_dialog_open());
@@ -945,7 +943,6 @@ int main() {
assert(window->get_custom_titlebar());
assert(window->get_wayland_titlebar());
assert(window->get_settings_session_active());
assert(!window->get_hardware_codec_available());
assert(window->get_portable_service_settings_visible());
if (const char *snapshot_path =
std::getenv("CROSSDESK_MAIN_UI_SNAPSHOT")) {
+2 -1
View File
@@ -55,11 +55,12 @@ xmake r crossdesk --headless
设置菜单与 GUI 共用 `config.ini`,每项修改校验通过后立即保存,无需另执行保存命令。输入 `cancel` 放弃正在编辑的值。控制台状态、菜单、密码提示和帮助跟随语言设置,支持中文、英文和俄文;命令名称保持不变。
H.264 自动优先使用硬件编解码,不可用或初始化失败时回退到软件;AV1 使用软件编解码。硬件加速不再提供独立开关,旧配置中的 `hardware_video_codec` 会自动清理。
| 设置名称 | 可选值 / 格式 | 生效方式 |
| --- | --- | --- |
| `language` | `zh-CN` / `en-US` / `ru-RU` | 立即切换控制台和 GUI 语言,下次启动保留 |
| `codec` | `h264` / `av1` | 保存后重新连接 |
| `hardware` | `on` / `off` | 保存后重新连接;构建不支持时拒绝开启 |
| `turn` | `off` / `auto` / `udp` / `tcp` | 保存后重新连接;`udp` / `tcp` 表示强制中继 |
| `server_host` | 主机名、IP 或 HTTPS/WSS 信令地址,支持路径和显式端口 | 自托管未启用时保存备用,已启用时重连 |
| `server_port` | `1`–`65535` | 同上 |
+2 -1
View File
@@ -54,11 +54,12 @@ Enter commands in that terminal while CrossDesk is running:
The menu shares the GUI's `config.ini`. Each valid change is saved immediately; `cancel` discards the value being edited. Console status, menus, password messages and help follow the saved Chinese, English or Russian language setting. Command names remain the same.
H.264 automatically prefers hardware codecs, with software fallback when unavailable or initialization fails; AV1 uses software codecs. There is no separate hardware acceleration switch, and the legacy `hardware_video_codec` setting is removed automatically.
| Key | Values / format | When applied |
| --- | --- | --- |
| `language` | `zh-CN` / `en-US` / `ru-RU` | Immediately, including the GUI; retained on restart |
| `codec` | `h264` / `av1` | Reconnect after saving |
| `hardware` | `on` / `off` | Reconnect; enabling requires a build with hardware codec support |
| `turn` | `off` / `auto` / `udp` / `tcp` | Reconnect; UDP/TCP force relay use |
| `server_host` | Hostname, IP, or HTTPS/WSS signaling URL, with optional path and explicit port | Reconnect if self-hosting is enabled; otherwise saved for later |
| `server_port` | `1`–`65535` | Same as server host |
+2 -2
View File
@@ -117,14 +117,14 @@ sudo apt install "./crossdesk-virtual-desktop-xfce-linux-amd64-<version>.deb"
点击主窗口右上角 **☰ → 设置**,顶部切换 **常规 / 桌面墙**,修改后点击 **保存**。切换选项卡会保留未保存的修改,点击“取消”放弃修改。“常规”按界面、视频、网络连接等类别组织选项,可向下滚动查看其余设置。编码与连接选项建议在建立连接前调整;会话进行中部分选项不可修改。普通会话的画质、帧率和画面偏好在远程会话工具栏中调整。
<p align="center">
<img src="images/desktop-settings-zh.png" width="720" alt="新版常规设置:常规与桌面墙选项卡、语言、视频编码、硬件编解码及网络连接设置" />
<img src="images/desktop-settings-zh.png" width="720" alt="新版常规设置:常规与桌面墙选项卡、语言、视频编码及网络连接设置" />
</p>
| 设置 | 说明 |
| --- | --- |
| 语言 | 中文、English、Русский |
| 采集方式(Windows) | 自动 / DXGI / WGC / GDI;无显示器主机使用“自动”启用自动适配 |
| 视频编码格式 | H.264 / AV1;硬件编解码是否可用取决于平台、硬件与构建选项 |
| 视频编码格式 | H.264 / AV1;H.264 自动优先使用硬件编解码,不可用或初始化失败时回退到软件;桌面端 AV1 使用软件编解码 |
| 启用中继服务 | 允许借助 TURN 中继建立连接;P2P 连接失败时先检查此项 |
| 强制中继连接 | 启用中继服务后可勾选,让连接通过中继建立;关闭中继服务会同时关闭此项 |
| 保存远程画面预览 | 默认开启,保存远程画面预览;关闭后缩略图区域显示被控端平台,并清除已有预览,保留连接记录和密码 |
+2 -2
View File
@@ -117,14 +117,14 @@ After connecting, open **Video settings** (the sliders icon) in the toolbar. Cho
Open **☰ → Settings** in the top-right corner, choose **General / DeskHub**, and click **Save** to apply changes. Switching tabs preserves pending changes; **Cancel** discards them. General settings are grouped into interface, video, networking, and further sections available by scrolling. Configure codecs and connections before connecting; some settings are disabled during an active session. Adjust ordinary session quality, frame rate, and video preference from its toolbar.
<p align="center">
<img src="images/desktop-settings-en.png" width="720" alt="Updated General settings with General and DeskHub tabs, language, video codec, hardware codecs, and networking options" />
<img src="images/desktop-settings-en.png" width="720" alt="Updated General settings with General and DeskHub tabs, language, video codec, and networking options" />
</p>
| Setting | Purpose |
| --- | --- |
| Language | 中文, English, Русский |
| Capture Method (Windows) | Auto / DXGI / WGC / GDI; choose Auto for automatic headless adaptation |
| Video Encode Format | H.264 / AV1; hardware codec availability depends on the platform, device, and build options |
| Video Encode Format | H.264 / AV1; H.264 automatically prefers hardware codecs, with software fallback when unavailable or initialization fails; desktop AV1 uses software codecs |
| Enable TURN Service | Allow relay-assisted connections; check this setting if P2P fails |
| Force Relay | Require relay connections when TURN is enabled; turning TURN off also disables this option |
| Save remote screen previews | On by default. When disabled, the thumbnail area shows the remote device’s platform and existing previews are cleared; connection records and passwords are preserved |
+4 -1
View File
@@ -4,6 +4,7 @@
## 来源 / Sources
- 两张常规设置截图(`desktop-settings-zh.png` / `desktop-settings-en.png`)于 **2026-10-22** 从 `f8f4aa3c` 加当前工作区修改重新渲染,移除硬件编解码开关;使用现有 `slint_ui_smoke_test`、macOS arm64 和 2× 软件渲染,PPM 中间文件存放在工程外临时目录。
- 16 张中英文桌面截图更新于 **2026-10-19**,UI 源代码为 `1ef372d2`,使用 Slint 1.17.1、macOS arm64 和软件渲染器。图片直接渲染仓库中的 Slint 组件。
- 捕获程序来自现有 [`slint_ui_smoke_test`](../../apps/desktop/tests/slint_ui_smoke_test.cpp),在**工程外的临时目录**中复制源文件,仅为桌面墙调整演示设备数量、单元格宽度和网格窗口高度。Slint 组件没有改动,工程内没有新增或修改测试文件。
- 主窗口展示桌面墙入口、连接区、近期设备卡片与分组筛选;设置展示“常规 / 桌面墙”选项卡,关于窗口展示三个赞助入口。桌面墙网格展示六台演示设备;放大图展示只读预览、群控和接管入口,没有建立真实连接。
@@ -12,7 +13,9 @@
- 缩略图沿用既有公开页面截图:`fixtures/source.png` 来自 [CrossDesk Xmake 源码页](https://github.com/kunkundi/crossdesk/blob/ios-support/xmake.lua),`fixtures/website.png` 来自 [CrossDesk 官网](https://www.crossdesk.cn/),两者采集于 2026-09-07。它们只用于展示预览效果,不代表这些页面正在对应设备上运行,页面中的版本信息也不代表最新发布。
- `web-client.png` 保留 **2026-09-25** 从 [Web 客户端](https://web.crossdesk.cn/) 采集的 1280 × 720 连接首页,页面版本为 `2026.09.24.2`,ID 与密码为空。本次未重新采集 Web 页面;桌面设备预览继续复用这张图片。
All 16 desktop images were refreshed on **2026-10-19** from UI source `1ef372d2`, using Slint 1.17.1 on macOS arm64. They render the actual components with demonstration data. The capture program is an external temporary copy of the existing source, adjusted only for DeskHub's six sample devices, 300 logical-pixel cells, and shorter grid window; no UI components or repository tests were changed. The About view includes all three sponsorship links. IDs, credentials, connection states, versions, and update results are illustrative. No remote sessions were opened. Public-page fixtures remain from 2026-09-07, and the Web screenshot remains from 2026-09-25. This refresh does not include native mobile screenshots.
The two General settings images were refreshed again on **2026-10-22** from `f8f4aa3c` plus the current working-tree changes to remove the hardware codec switch, using the existing `slint_ui_smoke_test` on macOS arm64 at 2× software rendering scale. Intermediate PPM files were kept outside the repository.
All 16 desktop images were previously refreshed on **2026-10-19** from UI source `1ef372d2`, using Slint 1.17.1 on macOS arm64. They render the actual components with demonstration data. The capture program is an external temporary copy of the existing source, adjusted only for DeskHub's six sample devices, 300 logical-pixel cells, and shorter grid window; no UI components or repository tests were changed in that refresh. The About view includes all three sponsorship links. IDs, credentials, connection states, versions, and update results are illustrative. No remote sessions were opened. Public-page fixtures remain from 2026-09-07, and the Web screenshot remains from 2026-09-25. This refresh does not include native mobile screenshots.
## 文件 / Files
Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 145 KiB