view camera (#11040)

* view camera

Signed-off-by: 21pages <sunboeasy@gmail.com>

* `No cameras` prompt if no cameras available,  `peerGetSessionsCount` use
connType as parameter

Signed-off-by: 21pages <sunboeasy@gmail.com>

* fix, use video_service_name rather than display_idx as key in qos,etc

Signed-off-by: 21pages <sunboeasy@gmail.com>

---------

Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: Adwin White <adwinw01@gmail.com>
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
This commit is contained in:
21pages
2025-03-10 21:06:53 +08:00
committed by GitHub
parent df4a101316
commit f0f999dc27
96 changed files with 3999 additions and 458 deletions

View File

@@ -1389,14 +1389,14 @@ impl VideoHandler {
}
/// Start or stop screen record.
pub fn record_screen(&mut self, start: bool, id: String, display: usize) {
pub fn record_screen(&mut self, start: bool, id: String, video_service_name: String) {
self.record = false;
if start {
self.recorder = Recorder::new(RecorderContext {
server: false,
id,
dir: crate::ui_interface::video_save_directory(false),
display,
video_service_name,
tx: None,
})
.map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r))));
@@ -2349,6 +2349,7 @@ impl LoginConfigHandler {
show_hidden: !self.get_option("remote_show_hidden").is_empty(),
..Default::default()
}),
ConnType::VIEW_CAMERA => lr.set_view_camera(Default::default()),
ConnType::PORT_FORWARD | ConnType::RDP => lr.set_port_forward(PortForward {
host: self.port_forward.0.clone(),
port: self.port_forward.1,
@@ -2436,6 +2437,14 @@ pub fn start_video_thread<F, T>(
{
let mut video_callback = video_callback;
let mut last_chroma = None;
let video_service_name = crate::video_service::get_service_name(
if session.is_view_camera() {
crate::video_service::VideoSource::Camera
} else {
crate::video_service::VideoSource::Monitor
},
display,
);
std::thread::spawn(move || {
#[cfg(windows)]
@@ -2478,7 +2487,7 @@ pub fn start_video_thread<F, T>(
let record_permission = session.lc.read().unwrap().record_permission;
let id = session.lc.read().unwrap().id.clone();
if record_state && record_permission {
handler.record_screen(true, id, display);
handler.record_screen(true, id, video_service_name.clone());
}
video_handler = Some(handler);
}
@@ -2559,7 +2568,7 @@ pub fn start_video_thread<F, T>(
MediaData::RecordScreen(start) => {
let id = session.lc.read().unwrap().id.clone();
if let Some(handler) = video_handler.as_mut() {
handler.record_screen(start, id, display);
handler.record_screen(start, id, video_service_name.clone());
}
}
_ => {}

View File

@@ -83,6 +83,7 @@ struct ParsedPeerInfo {
platform: String,
is_installed: bool,
idd_impl: String,
support_view_camera: bool,
}
impl ParsedPeerInfo {
@@ -129,7 +130,10 @@ impl<T: InvokeUiSession> Remote<T> {
#[cfg(target_os = "windows")]
let _file_clip_context_holder = {
// `is_port_forward()` will not reach here, but we still check it for clarity.
if !self.handler.is_file_transfer() && !self.handler.is_port_forward() {
if !self.handler.is_file_transfer()
&& !self.handler.is_port_forward()
&& !self.handler.is_view_camera()
{
// It is ok to call this function multiple times.
ContextSend::enable(true);
Some(crate::SimpleCallOnReturn {
@@ -152,6 +156,8 @@ impl<T: InvokeUiSession> Remote<T> {
let mut received = false;
let conn_type = if self.handler.is_file_transfer() {
ConnType::FILE_TRANSFER
} else if self.handler.is_view_camera() {
ConnType::VIEW_CAMERA
} else {
ConnType::default()
};
@@ -173,7 +179,7 @@ impl<T: InvokeUiSession> Remote<T> {
.set_connected();
self.handler.set_connection_type(peer.is_secured(), direct); // flutter -> connection_ready
self.handler.update_direct(Some(direct));
if conn_type == ConnType::DEFAULT_CONN {
if conn_type == ConnType::DEFAULT_CONN || conn_type == ConnType::VIEW_CAMERA {
self.handler
.set_fingerprint(crate::common::pk_to_fingerprint(pk.unwrap_or_default()));
}
@@ -190,7 +196,8 @@ impl<T: InvokeUiSession> Remote<T> {
{
let is_conn_not_default = self.handler.is_file_transfer()
|| self.handler.is_port_forward()
|| self.handler.is_rdp();
|| self.handler.is_rdp()
|| self.handler.is_view_camera();
if !is_conn_not_default {
(self.client_conn_id, rx_clip_client_holder.0) =
clipboard::get_rx_cliprdr_client(&self.handler.get_id());
@@ -330,12 +337,12 @@ impl<T: InvokeUiSession> Remote<T> {
.set_disconnected(round);
#[cfg(not(target_os = "ios"))]
if _set_disconnected_ok {
if !self.handler.is_view_camera() && _set_disconnected_ok {
Client::try_stop_clipboard();
}
#[cfg(any(target_os = "windows", feature = "unix-file-copy-paste"))]
if _set_disconnected_ok {
if !self.handler.is_view_camera() && _set_disconnected_ok {
crate::clipboard::try_empty_clipboard_files(ClipboardSide::Client, self.client_conn_id);
}
}
@@ -1176,6 +1183,25 @@ impl<T: InvokeUiSession> Remote<T> {
}
}
fn check_view_camera_support(&self, peer_version: &str, peer_platform: &str) -> bool {
if self.peer_info.support_view_camera {
return true;
}
if hbb_common::get_version_number(&peer_version) < hbb_common::get_version_number("1.3.9")
&& (peer_platform == "Windows" || peer_platform == "Linux")
{
self.handler.msgbox(
"error",
"Download new version",
"upgrade_remote_rustdesk_client_to_{1.3.9}_tip",
"",
);
} else {
self.handler.on_error("view_camera_unsupported_tip");
}
return false;
}
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
match msg_in.union {
@@ -1230,10 +1256,19 @@ impl<T: InvokeUiSession> Remote<T> {
let peer_version = pi.version.clone();
let peer_platform = pi.platform.clone();
self.set_peer_info(&pi);
if self.handler.is_view_camera() {
if !self.check_view_camera_support(&peer_version, &peer_platform) {
self.handler.lc.write().unwrap().handle_peer_info(&pi);
return false;
}
}
self.handler.handle_peer_info(pi);
#[cfg(all(target_os = "windows", not(feature = "flutter")))]
self.check_clipboard_file_context();
if !(self.handler.is_file_transfer() || self.handler.is_port_forward()) {
if !(self.handler.is_file_transfer()
|| self.handler.is_port_forward()
|| self.handler.is_view_camera())
{
#[cfg(feature = "flutter")]
#[cfg(not(target_os = "ios"))]
let rx = Client::try_start_clipboard(None);
@@ -1532,6 +1567,9 @@ impl<T: InvokeUiSession> Remote<T> {
);
}
}
Ok(Permission::Camera) => {
self.handler.set_permission("camera", p.enabled);
}
Ok(Permission::Restart) => {
self.handler.set_permission("restart", p.enabled);
}
@@ -1773,6 +1811,11 @@ impl<T: InvokeUiSession> Remote<T> {
.flatten()
.unwrap_or_default()
.to_string();
self.peer_info.support_view_camera = platform_additions
.get("support_view_camera")
.map(|v| v.as_bool())
.flatten()
.unwrap_or(false);
}
}

View File

@@ -53,6 +53,7 @@ pub fn core_main() -> Option<Vec<String>> {
"--connect",
"--play",
"--file-transfer",
"--view-camera",
"--port-forward",
"--rdp",
]
@@ -99,7 +100,7 @@ pub fn core_main() -> Option<Vec<String>> {
}
}
#[cfg(windows)]
if args.contains(&"--connect".to_string()) {
if args.contains(&"--connect".to_string()) || args.contains(&"--view-camera".to_string()) {
hbb_common::platform::windows::start_cpu_performance_monitor();
}
#[cfg(feature = "flutter")]
@@ -589,7 +590,7 @@ fn core_main_invoke_new_connection(mut args: std::env::Args) -> Option<Vec<Strin
let mut param_array = vec![];
while let Some(arg) = args.next() {
match arg.as_str() {
"--connect" | "--play" | "--file-transfer" | "--port-forward" | "--rdp" => {
"--connect" | "--play" | "--file-transfer" | "--view-camera" | "--port-forward" | "--rdp" => {
authority = Some((&arg.to_string()[2..]).to_owned());
id = args.next();
}

View File

@@ -1149,8 +1149,14 @@ pub fn session_add_existed(
peer_id: String,
session_id: SessionID,
displays: Vec<i32>,
is_view_camera: bool,
) -> ResultType<()> {
sessions::insert_peer_session_id(peer_id, ConnType::DEFAULT_CONN, session_id, displays);
let conn_type = if is_view_camera {
ConnType::VIEW_CAMERA
} else {
ConnType::DEFAULT_CONN
};
sessions::insert_peer_session_id(peer_id, conn_type, session_id, displays);
Ok(())
}
@@ -1160,11 +1166,13 @@ pub fn session_add_existed(
///
/// * `id` - The identifier of the remote session with prefix. Regex: [\w]*[\_]*[\d]+
/// * `is_file_transfer` - If the session is used for file transfer.
/// * `is_view_camera` - If the session is used for view camera.
/// * `is_port_forward` - If the session is used for port forward.
pub fn session_add(
session_id: &SessionID,
id: &str,
is_file_transfer: bool,
is_view_camera: bool,
is_port_forward: bool,
is_rdp: bool,
switch_uuid: &str,
@@ -1175,6 +1183,8 @@ pub fn session_add(
) -> ResultType<FlutterSession> {
let conn_type = if is_file_transfer {
ConnType::FILE_TRANSFER
} else if is_view_camera {
ConnType::VIEW_CAMERA
} else if is_port_forward {
if is_rdp {
ConnType::RDP

View File

@@ -92,16 +92,28 @@ pub fn host_stop_system_key_propagate(_stopped: bool) {
}
// This function is only used to count the number of control sessions.
pub fn peer_get_default_sessions_count(id: String) -> SyncReturn<usize> {
SyncReturn(sessions::get_session_count(id, ConnType::DEFAULT_CONN))
pub fn peer_get_sessions_count(id: String, conn_type: i32) -> SyncReturn<usize> {
let conn_type = if conn_type == ConnType::VIEW_CAMERA as i32 {
ConnType::VIEW_CAMERA
} else if conn_type == ConnType::FILE_TRANSFER as i32 {
ConnType::FILE_TRANSFER
} else if conn_type == ConnType::PORT_FORWARD as i32 {
ConnType::PORT_FORWARD
} else if conn_type == ConnType::RDP as i32 {
ConnType::RDP
} else {
ConnType::DEFAULT_CONN
};
SyncReturn(sessions::get_session_count(id, conn_type))
}
pub fn session_add_existed_sync(
id: String,
session_id: SessionID,
displays: Vec<i32>,
is_view_camera: bool,
) -> SyncReturn<String> {
if let Err(e) = session_add_existed(id.clone(), session_id, displays) {
if let Err(e) = session_add_existed(id.clone(), session_id, displays, is_view_camera) {
SyncReturn(format!("Failed to add session with id {}, {}", &id, e))
} else {
SyncReturn("".to_owned())
@@ -112,6 +124,7 @@ pub fn session_add_sync(
session_id: SessionID,
id: String,
is_file_transfer: bool,
is_view_camera: bool,
is_port_forward: bool,
is_rdp: bool,
switch_uuid: String,
@@ -124,6 +137,7 @@ pub fn session_add_sync(
&session_id,
&id,
is_file_transfer,
is_view_camera,
is_port_forward,
is_rdp,
&switch_uuid,

View File

@@ -188,6 +188,7 @@ pub enum Data {
Login {
id: i32,
is_file_transfer: bool,
is_view_camera: bool,
peer_id: String,
name: String,
authorized: bool,
@@ -1280,6 +1281,6 @@ mod test {
#[test]
fn verify_ffi_enum_data_size() {
println!("{}", std::mem::size_of::<Data>());
assert!(std::mem::size_of::<Data>() < 96);
assert!(std::mem::size_of::<Data>() <= 96);
}
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "عرض الكاميرا"),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Прагляд камеры"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Калі ласка, абнавіце кліент RustDesk да версіі {} або навейшай на аддаленым баку!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Преглед на камерата"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Моля, надстройте клиента RustDesk до версия {} или по-нова от отдалечената страна!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Sense etiquetar"),
("new-version-of-{}-tip", ""),
("Accessible devices", "Dispositius accessibles"),
("View camera", "Mostra la càmera"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Veuillez mettre à niveau le client RustDesk vers la version {} ou plus récente du côté distant !"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "无标签"),
("new-version-of-{}-tip", "{} 版本更新"),
("Accessible devices", "可访问的设备"),
("View camera", "查看摄像头"),
("upgrade_remote_rustdesk_client_to_{}_tip", "请在远程端将 RustDesk 客户端升级至版本 {} 或更新版本!"),
("view_camera_unsupported_tip", "您的远程端不支持查看摄像头。"),
("Enable camera", "允许查看摄像头"),
("No cameras", "没有摄像头"),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Zobrazit kameru"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Upgradujte prosím klienta RustDesk na verzi {} nebo novější na vzdálené straně!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Se kamera"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Opgrader venligst RustDesk-klienten til version {} eller nyere på fjernsiden!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Unmarkiert"),
("new-version-of-{}-tip", "Es ist eine neue Version von {} verfügbar"),
("Accessible devices", "Erreichbare Geräte"),
("View camera", "Kamera anzeigen"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Bitte aktualisieren Sie den RustDesk-Client auf der Remote-Seite auf Version {} oder neuer!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Χωρίς ετικέτα"),
("new-version-of-{}-tip", "Υπάρχει διαθέσιμη νέα έκδοση του {}"),
("Accessible devices", "Προσβάσιμες συσκευές"),
("View camera", "Προβολή κάμερας"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Αναβαθμίστε τον πελάτη RustDesk στην έκδοση {} ή νεότερη στην απομακρυσμένη πλευρά!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -237,5 +237,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("one-way-file-transfer-tip", "One-way file transfer is enabled on the controlled side."),
("web_id_input_tip", "You can input an ID in the same server, direct IP access is not supported in web client.\nIf you want to access a device on another server, please append the server address (<id>@<server_address>?key=<key_value>), for example,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nIf you want to access a device on a public server, please input \"<id>@public\", the key is not needed for public server."),
("new-version-of-{}-tip", "There is a new version of {} available"),
("View camera", "View camera"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Please upgrade the RustDesk client to version {} or newer on the remote side!"),
("view_camera_unsupported_tip", "The remote device does not support viewing the camera."),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Rigardi kameron"),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Sin itiquetar"),
("new-version-of-{}-tip", "Hay una nueva versión de {} disponible"),
("Accessible devices", ""),
("View camera", "Ver cámara"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Por favor, actualiza el cliente RustDesk a la versión {} o superior en el lado remoto"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -600,7 +600,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Everyone", "Igaüks"),
("ab_web_console_tip", "Rohkem leiad veebikonsoolist"),
("allow-only-conn-window-open-tip", "Luba ühendus ainult siis, kui RustDeski aken on avatud."),
("no_need_privacy_mode_no_physical_displays_tip", "Füüsilisi ekraane pole, privaatsusrežiimi kasutamine pole vajalik."),
("no_need_privacy_mode_no_physical_displays_tip", "Füüsilisi ekraane pole, privaatsusrežiimi kasutamine pole vajalik."),
("Follow remote cursor", "Jälgi kaugkursorit"),
("Follow remote window focus", "Jälgi kaugakna fookust"),
("default_proxy_tip", "Vaikimisi protokoll ja port on Socks5 ja 1080."),
@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Sildistamata"),
("new-version-of-{}-tip", "Saadaval on {} uus versioon"),
("Accessible devices", "Ligipääsetavad seadmed"),
("View camera", "Vaata kaamerat"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Täiendage RustDeski klient kaugküljel versioonile {} või uuemale!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Ikusi kamera"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Mesedez, eguneratu RustDesk bezeroa {} bertsiora edo berriagoa urruneko aldean!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "نمایش دوربین"),
("upgrade_remote_rustdesk_client_to_{}_tip", "لطفاً مشتری RustDesk را به نسخه {} یا جدیدتر در سمت راه دور ارتقا دهید!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Sans étiquette"),
("new-version-of-{}-tip", "Une nouvelle version de {} est disponible"),
("Accessible devices", "Appareils accessibles"),
("View camera", "Voir la caméra"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Veuillez mettre à jour le client RustDesk avec la version {} ou une version plus récente sur l'appareil distant"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", ""),
("upgrade_remote_rustdesk_client_to_{}_tip", "אנא שדרג את לקוח RustDesk לגרסה {} או חדשה יותר בצד המרוחק!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Pregled kamere"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Molimo ažurirajte RustDesk klijent na verziju {} ili noviju na udaljenoj strani!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Címkézetlen"),
("new-version-of-{}-tip", "A(z) {} új verziója"),
("Accessible devices", "Hozzáférhető eszközök"),
("View camera", "Kamera megtekintése"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Kérjük, frissítse a RustDesk kliens {} vagy újabb verziójára a távoli oldalon!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Lihat Kamera"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Silakan perbarui klien RustDesk ke versi {} atau lebih baru di sisi remote!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Senza tag"),
("new-version-of-{}-tip", "È disponibile una nuova versione di {}"),
("Accessible devices", "Dispositivi accessibili"),
("View camera", "Visualizza telecamera"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Aggiorna il client RustDesk remoto alla versione {} o successiva!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "カメラを表示"),
("upgrade_remote_rustdesk_client_to_{}_tip", "リモート側のRustDeskクライアントをバージョン{}以上にアップグレードしてください!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "태그 없음"),
("new-version-of-{}-tip", "{} 의 새로운 버전이 출시되었습니다."),
("Accessible devices", ""),
("View camera", "카메라 보기"),
("upgrade_remote_rustdesk_client_to_{}_tip", "원격 측의 RustDesk 클라이언트를 {} 버전 이상으로 업그레이드하십시오!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Камераны Көру"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Қашықтағы жақтағы RustDesk клиентін {} немесе одан жоғары нұсқаға жаңартуды өтінеміз!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Peržiūrėti kamerą"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Prašome atnaujinti nuotolinės pusės RustDesk klientą į {} ar naujesnę versiją!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Neatzīmēts"),
("new-version-of-{}-tip", "Ir pieejama jauna {} versija"),
("Accessible devices", "Pieejamas ierīces"),
("View camera", "Skatīt kameru"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Lūdzu, jauniniet attālās puses RustDesk klientu uz versiju {} vai jaunāku!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Vis kamera"),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Ongemarkeerd"),
("new-version-of-{}-tip", "Er is een nieuwe versie van {} beschikbaar"),
("Accessible devices", "Toegankelijke apparaten"),
("View camera", "Camera bekijken"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Upgrade de RustDesk client naar versie {} of nieuwer op de externe computer!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Bez etykiety"),
("new-version-of-{}-tip", "Dostępna jest nowa wersja {}"),
("Accessible devices", "Dostępne urządzenia"),
("View camera", "Podgląd kamery"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Proszę zaktualizować zdalny klient RustDesk do wersji {} lub nowszej!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Ver câmara"),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Visualizar Câmera"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Atualize o cliente RustDesk para a versão {} ou superior no lado remoto."),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Vezi camera"),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Без метки"),
("new-version-of-{}-tip", "Доступна новая версия {}"),
("Accessible devices", "Доступные устройства"),
("View camera", "Просмотр камеры"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Обновите клиент RustDesk до версии {} или новее на удаленной стороне!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Chene tag"),
("new-version-of-{}-tip", "B'at una versione noa de {} a disponimentu"),
("Accessible devices", "Dispositivos atzessìbiles"),
("View camera", "Mustra càmera"),
("upgrade_remote_rustdesk_client_to_{}_tip", "¡Actualice el cliente RustDesk a la versión {} o más reciente en el lado remoto!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Zobraziť kameru"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Aktualizujte klienta RustDesk na verziu {} alebo novšiu na vzdialenej strane!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Neoznačeno"),
("new-version-of-{}-tip", "Na voljo je nova različica {}"),
("Accessible devices", ""),
("View camera", "Pogled kamere"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Prosimo, nadgradite RustDesk odjemalec na različico {} ali novejšo na oddaljeni strani."),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", ""),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Pregled kamere"),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Visa kamera"),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", ""),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "ดูกล้อง"),
("upgrade_remote_rustdesk_client_to_{}_tip", "กรุณาอัปเดต RustDesk ไคลเอนต์ไปยังเวอร์ชัน {} หรือใหม่กว่าที่ฝั่งปลายทาง!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Kamerayı görüntüle"),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "無標籤"),
("new-version-of-{}-tip", "有新版本的 {} 可用"),
("Accessible devices", "可存取的裝置"),
("View camera", "檢視相機"),
("upgrade_remote_rustdesk_client_to_{}_tip", "請將遠端 RustDesk 用戶端升級到 {} 或更新版本!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", "Без міток"),
("new-version-of-{}-tip", "Доступна нова версія {}"),
("Accessible devices", ""),
("View camera", "Перегляд камери"),
("upgrade_remote_rustdesk_client_to_{}_tip", "Будь ласка, оновіть RustDesk клієнт на віддаленому пристрої до версії {} чи новіше!"),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -657,5 +657,10 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Untagged", ""),
("new-version-of-{}-tip", ""),
("Accessible devices", ""),
("View camera", "Xem camera"),
("upgrade_remote_rustdesk_client_to_{}_tip", ""),
("view_camera_unsupported_tip", ""),
("Enable camera", ""),
("No cameras", ""),
].iter().cloned().collect();
}

View File

@@ -24,9 +24,11 @@ use hbb_common::{
sodiumoxide::crypto::{box_, sign},
timeout, tokio, ResultType, Stream,
};
use scrap::camera;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use service::ServiceTmpl;
use service::{EmptyExtraFieldService, GenericService, Service, Subscriber};
use video_service::VideoSource;
use crate::ipc::Data;
@@ -76,7 +78,6 @@ const CONFIG_SYNC_INTERVAL_SECS: f32 = 0.3;
lazy_static::lazy_static! {
pub static ref CHILD_PROCESS: Childs = Default::default();
pub static ref CONN_COUNT: Arc<Mutex<usize>> = Default::default();
// A client server used to provide local services(audio, video, clipboard, etc.)
// for all initiative connections.
//
@@ -279,22 +280,53 @@ async fn create_relay_connection_(
impl Server {
fn is_video_service_name(name: &str) -> bool {
name.starts_with(video_service::NAME)
name.starts_with(VideoSource::Monitor.service_name_prefix())
|| name.starts_with(VideoSource::Camera.service_name_prefix())
}
pub fn try_add_primary_camera_service(&mut self) {
if !camera::primary_camera_exists() {
return;
}
let primary_camera_name =
video_service::get_service_name(VideoSource::Camera, camera::PRIMARY_CAMERA_IDX);
if !self.contains(&primary_camera_name) {
self.add_service(Box::new(video_service::new(
VideoSource::Camera,
camera::PRIMARY_CAMERA_IDX,
)));
}
}
pub fn try_add_primay_video_service(&mut self) {
let primary_video_service_name =
video_service::get_service_name(*display_service::PRIMARY_DISPLAY_IDX);
let primary_video_service_name = video_service::get_service_name(
VideoSource::Monitor,
*display_service::PRIMARY_DISPLAY_IDX,
);
if !self.contains(&primary_video_service_name) {
self.add_service(Box::new(video_service::new(
VideoSource::Monitor,
*display_service::PRIMARY_DISPLAY_IDX,
)));
}
}
pub fn add_camera_connection(&mut self, conn: ConnInner) {
if camera::primary_camera_exists() {
let primary_camera_name =
video_service::get_service_name(VideoSource::Camera, camera::PRIMARY_CAMERA_IDX);
if let Some(s) = self.services.get(&primary_camera_name) {
s.on_subscribe(conn.clone());
}
}
self.connections.insert(conn.id(), conn);
}
pub fn add_connection(&mut self, conn: ConnInner, noperms: &Vec<&'static str>) {
let primary_video_service_name =
video_service::get_service_name(*display_service::PRIMARY_DISPLAY_IDX);
let primary_video_service_name = video_service::get_service_name(
VideoSource::Monitor,
*display_service::PRIMARY_DISPLAY_IDX,
);
for s in self.services.values() {
let name = s.name();
if Self::is_video_service_name(&name) && name != primary_video_service_name {
@@ -307,7 +339,6 @@ impl Server {
#[cfg(target_os = "macos")]
self.update_enable_retina();
self.connections.insert(conn.id(), conn);
*CONN_COUNT.lock().unwrap() = self.connections.len();
}
pub fn remove_connection(&mut self, conn: &ConnInner) {
@@ -315,7 +346,6 @@ impl Server {
s.on_unsubscribe(conn.id());
}
self.connections.remove(&conn.id());
*CONN_COUNT.lock().unwrap() = self.connections.len();
#[cfg(target_os = "macos")]
self.update_enable_retina();
}
@@ -361,10 +391,15 @@ impl Server {
self.id_count
}
pub fn set_video_service_opt(&self, display: Option<usize>, opt: &str, value: &str) {
pub fn set_video_service_opt(
&self,
display: Option<(VideoSource, usize)>,
opt: &str,
value: &str,
) {
for (k, v) in self.services.iter() {
if let Some(display) = display {
if k != &video_service::get_service_name(display) {
if let Some((source, display)) = display {
if k != &video_service::get_service_name(source, display) {
continue;
}
}
@@ -392,13 +427,14 @@ impl Server {
fn capture_displays(
&mut self,
conn: ConnInner,
source: VideoSource,
displays: &[usize],
include: bool,
exclude: bool,
) {
let displays = displays
.iter()
.map(|d| video_service::get_service_name(*d))
.map(|d| video_service::get_service_name(source, *d))
.collect::<Vec<_>>();
let keys = self.services.keys().cloned().collect::<Vec<_>>();
for name in keys.iter() {

View File

@@ -44,6 +44,7 @@ use hbb_common::{
};
#[cfg(any(target_os = "android", target_os = "ios"))]
use scrap::android::{call_main_service_key_event, call_main_service_pointer_input};
use scrap::camera;
use serde_derive::Serialize;
use serde_json::{json, value::Value};
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@@ -167,6 +168,7 @@ pub enum AuthConnType {
Remote,
FileTransfer,
PortForward,
ViewCamera,
}
pub struct Connection {
@@ -179,6 +181,7 @@ pub struct Connection {
timer: crate::RustDeskInterval,
file_timer: crate::RustDeskInterval,
file_transfer: Option<(String, bool)>,
view_camera: bool,
port_forward_socket: Option<Framed<TcpStream, BytesCodec>>,
port_forward_address: String,
tx_to_cm: mpsc::UnboundedSender<ipc::Data>,
@@ -222,6 +225,7 @@ pub struct Connection {
portable: PortableState,
from_switch: bool,
voice_call_request_timestamp: Option<NonZeroI64>,
voice_calling: bool,
options_in_login: Option<OptionMessage>,
#[cfg(not(any(target_os = "ios")))]
pressed_modifiers: HashSet<rdev::Key>,
@@ -331,6 +335,7 @@ impl Connection {
timer: crate::rustdesk_interval(time::interval(SEC30)),
file_timer: crate::rustdesk_interval(time::interval(SEC30)),
file_transfer: None,
view_camera: false,
port_forward_socket: None,
port_forward_address: "".to_owned(),
tx_to_cm,
@@ -369,6 +374,7 @@ impl Connection {
from_switch: false,
audio_sender: None,
voice_call_request_timestamp: None,
voice_calling: false,
options_in_login: None,
#[cfg(not(any(target_os = "ios")))]
pressed_modifiers: Default::default(),
@@ -533,9 +539,17 @@ impl Connection {
conn.send_permission(Permission::Audio, enabled).await;
if conn.authorized {
if let Some(s) = conn.server.upgrade() {
s.write().unwrap().subscribe(
super::audio_service::NAME,
conn.inner.clone(), conn.audio_enabled());
if conn.is_authed_view_camera_conn() {
if conn.voice_calling || !conn.audio_enabled() {
s.write().unwrap().subscribe(
super::audio_service::NAME,
conn.inner.clone(), conn.audio_enabled());
}
} else {
s.write().unwrap().subscribe(
super::audio_service::NAME,
conn.inner.clone(), conn.audio_enabled());
}
}
}
} else if &name == "file" {
@@ -774,7 +788,7 @@ impl Connection {
});
conn.send(msg_out.into()).await;
}
if conn.is_authed_remote_conn() {
if conn.is_authed_remote_conn() || conn.view_camera {
if let Some(last_test_delay) = conn.last_test_delay {
video_service::VIDEO_QOS.lock().unwrap().user_delay_response_elapsed(id, last_test_delay.elapsed().as_millis());
}
@@ -1189,6 +1203,8 @@ impl Connection {
(1, AuthConnType::FileTransfer)
} else if self.port_forward_socket.is_some() {
(2, AuthConnType::PortForward)
} else if self.view_camera {
(3, AuthConnType::ViewCamera)
} else {
(0, AuthConnType::Remote)
};
@@ -1277,6 +1293,11 @@ impl Connection {
platform_additions.insert("has_file_clipboard".into(), json!(has_file_clipboard));
}
#[cfg(any(target_os = "windows", target_os = "linux"))]
{
platform_additions.insert("support_view_camera".into(), json!(true));
}
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
if !platform_additions.is_empty() {
pi.platform_additions = serde_json::to_string(&platform_additions).unwrap_or("".into());
@@ -1290,7 +1311,8 @@ impl Connection {
return;
}
#[cfg(target_os = "linux")]
if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() {
if !self.file_transfer.is_some() && !self.port_forward_socket.is_some() && !self.view_camera
{
let mut msg = "".to_string();
if crate::platform::linux::is_login_screen_wayland() {
msg = crate::client::LOGIN_SCREEN_WAYLAND.to_owned()
@@ -1347,6 +1369,29 @@ impl Connection {
self.handle_windows_specific_session(&mut pi, &mut wait_session_id_confirm);
if self.file_transfer.is_some() {
res.set_peer_info(pi);
} else if self.view_camera {
let supported_encoding = scrap::codec::Encoder::supported_encoding();
self.last_supported_encoding = Some(supported_encoding.clone());
log::info!("peer info supported_encoding: {:?}", supported_encoding);
pi.encoding = Some(supported_encoding).into();
pi.displays = camera::Cameras::all_info().unwrap_or(Vec::new());
pi.current_display = camera::PRIMARY_CAMERA_IDX as _;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
pi.resolutions = Some(SupportedResolutions {
resolutions: camera::Cameras::get_camera_resolution(
pi.current_display as usize,
)
.ok()
.into_iter()
.collect(),
..Default::default()
})
.into();
}
res.set_peer_info(pi);
self.update_codec_on_login();
} else {
let supported_encoding = scrap::codec::Encoder::supported_encoding();
self.last_supported_encoding = Some(supported_encoding.clone());
@@ -1414,15 +1459,31 @@ impl Connection {
} else {
self.delayed_read_dir = Some((dir.to_owned(), show_hidden));
}
} else if self.view_camera {
if !wait_session_id_confirm {
self.try_sub_camera_displays();
}
self.keyboard = false;
self.send_permission(Permission::Keyboard, false).await;
} else if sub_service {
if !wait_session_id_confirm {
self.try_sub_services();
self.try_sub_monitor_services();
}
}
}
fn try_sub_services(&mut self) {
let is_remote = self.file_transfer.is_none() && self.port_forward_socket.is_none();
fn try_sub_camera_displays(&mut self) {
if let Some(s) = self.server.upgrade() {
let mut s = s.write().unwrap();
s.try_add_primary_camera_service();
s.add_camera_connection(self.inner.clone());
}
}
fn try_sub_monitor_services(&mut self) {
let is_remote =
self.file_transfer.is_none() && self.port_forward_socket.is_none() && !self.view_camera;
if is_remote && !self.services_subed {
self.services_subed = true;
if let Some(s) = self.server.upgrade() {
@@ -1466,7 +1527,7 @@ impl Connection {
if let Some(current_sid) = crate::platform::get_current_process_session_id() {
if crate::platform::is_installed()
&& crate::platform::is_share_rdp()
&& raii::AuthedConnID::remote_and_file_conn_count() == 1
&& raii::AuthedConnID::non_port_forward_conn_count() == 1
&& sessions.len() > 1
&& sessions.iter().any(|e| e.sid == current_sid)
&& get_version_number(&self.lr.version) >= get_version_number("1.2.4")
@@ -1539,6 +1600,7 @@ impl Connection {
self.send_to_cm(ipc::Data::Login {
id: self.inner.id(),
is_file_transfer: self.file_transfer.is_some(),
is_view_camera: self.view_camera,
port_forward: self.port_forward_address.clone(),
peer_id,
name,
@@ -1781,6 +1843,15 @@ impl Connection {
}
self.file_transfer = Some((ft.dir, ft.show_hidden));
}
Some(login_request::Union::ViewCamera(_vc)) => {
if !Connection::permission(keys::OPTION_ENABLE_CAMERA) {
self.send_login_error("No permission of viewing camera")
.await;
sleep(1.).await;
return false;
}
self.view_camera = true;
}
Some(login_request::Union::PortForward(mut pf)) => {
if !Connection::permission("enable-tunnel") {
self.send_login_error("No permission of IP tunneling").await;
@@ -1987,6 +2058,9 @@ impl Connection {
match msg.union {
#[allow(unused_mut)]
Some(message::Union::MouseEvent(mut me)) => {
if self.is_authed_view_camera_conn() {
return true;
}
#[cfg(any(target_os = "android", target_os = "ios"))]
if let Err(e) = call_main_service_pointer_input("mouse", me.mask, me.x, me.y) {
log::debug!("call_main_service_pointer_input fail:{}", e);
@@ -2005,6 +2079,9 @@ impl Connection {
self.update_auto_disconnect_timer();
}
Some(message::Union::PointerDeviceEvent(pde)) => {
if self.is_authed_view_camera_conn() {
return true;
}
#[cfg(any(target_os = "android", target_os = "ios"))]
if let Err(e) = match pde.union {
Some(pointer_device_event::Union::TouchEvent(touch)) => match touch.union {
@@ -2044,6 +2121,9 @@ impl Connection {
Some(message::Union::KeyEvent(..)) => {}
#[cfg(any(target_os = "android"))]
Some(message::Union::KeyEvent(mut me)) => {
if self.is_authed_view_camera_conn() {
return true;
}
let key = match me.mode.enum_value() {
Ok(KeyboardMode::Map) => {
Some(crate::keyboard::keycode_to_rdev_key(me.chr()))
@@ -2096,6 +2176,9 @@ impl Connection {
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Some(message::Union::KeyEvent(me)) => {
if self.is_authed_view_camera_conn() {
return true;
}
if self.peer_keyboard_enabled() {
if is_enter(&me) {
CLICK_TIME.store(get_time(), Ordering::SeqCst);
@@ -2592,7 +2675,7 @@ impl Connection {
let sessions = crate::platform::get_available_sessions(false);
if crate::platform::is_installed()
&& crate::platform::is_share_rdp()
&& raii::AuthedConnID::remote_and_file_conn_count() == 1
&& raii::AuthedConnID::non_port_forward_conn_count() == 1
&& sessions.len() > 1
&& current_process_sid != sid
&& sessions.iter().any(|e| e.sid == sid)
@@ -2606,15 +2689,19 @@ impl Connection {
if let Some((dir, show_hidden)) = self.delayed_read_dir.take() {
self.read_dir(&dir, show_hidden);
}
} else if self.view_camera {
self.try_sub_camera_displays();
} else {
self.try_sub_services();
self.try_sub_monitor_services();
}
}
}
Some(misc::Union::MessageQuery(mq)) => {
if let Some(msg_out) =
video_service::make_display_changed_msg(mq.switch_display as _, None)
{
if let Some(msg_out) = video_service::make_display_changed_msg(
mq.switch_display as _,
None,
self.video_source(),
) {
self.send(msg_out).await;
}
}
@@ -2713,7 +2800,7 @@ impl Connection {
video_service::refresh();
self.server.upgrade().map(|s| {
s.read().unwrap().set_video_service_opt(
display,
display.map(|d| (self.video_source(), d)),
video_service::OPTION_REFRESH,
super::service::SERVICE_OPTION_VALUE_TRUE,
);
@@ -2743,19 +2830,33 @@ impl Connection {
// 1. For compatibility with old versions ( < 1.2.4 ).
// 2. Sciter version.
// 3. Update `SupportedResolutions`.
if let Some(msg_out) = video_service::make_display_changed_msg(self.display_idx, None) {
if let Some(msg_out) =
video_service::make_display_changed_msg(self.display_idx, None, self.video_source())
{
self.send(msg_out).await;
}
}
}
fn video_source(&self) -> VideoSource {
if self.view_camera {
VideoSource::Camera
} else {
VideoSource::Monitor
}
}
fn switch_display_to(&mut self, display_idx: usize, server: Arc<RwLock<Server>>) {
let new_service_name = video_service::get_service_name(display_idx);
let old_service_name = video_service::get_service_name(self.display_idx);
let new_service_name = video_service::get_service_name(self.video_source(), display_idx);
let old_service_name =
video_service::get_service_name(self.video_source(), self.display_idx);
let mut lock = server.write().unwrap();
if display_idx != *display_service::PRIMARY_DISPLAY_IDX {
if !lock.contains(&new_service_name) {
lock.add_service(Box::new(video_service::new(display_idx)));
lock.add_service(Box::new(video_service::new(
self.video_source(),
display_idx,
)));
}
}
// For versions greater than 1.2.4, a `CaptureDisplays` message will be sent immediately.
@@ -2790,26 +2891,27 @@ impl Connection {
}
async fn capture_displays(&mut self, add: &[usize], sub: &[usize], set: &[usize]) {
let video_source = self.video_source();
if let Some(sever) = self.server.upgrade() {
let mut lock = sever.write().unwrap();
for display in add.iter() {
let service_name = video_service::get_service_name(*display);
let service_name = video_service::get_service_name(video_source, *display);
if !lock.contains(&service_name) {
lock.add_service(Box::new(video_service::new(*display)));
lock.add_service(Box::new(video_service::new(video_source, *display)));
}
}
for display in set.iter() {
let service_name = video_service::get_service_name(*display);
let service_name = video_service::get_service_name(video_source, *display);
if !lock.contains(&service_name) {
lock.add_service(Box::new(video_service::new(*display)));
lock.add_service(Box::new(video_service::new(video_source, *display)));
}
}
if !add.is_empty() {
lock.capture_displays(self.inner.clone(), add, true, false);
lock.capture_displays(self.inner.clone(), video_source, add, true, false);
} else if !sub.is_empty() {
lock.capture_displays(self.inner.clone(), sub, false, true);
lock.capture_displays(self.inner.clone(), video_source, sub, false, true);
} else {
lock.capture_displays(self.inner.clone(), set, true, true);
lock.capture_displays(self.inner.clone(), video_source, set, true, true);
}
self.multi_ui_session = lock.get_subbed_displays_count(self.inner.id()) > 1;
if self.follow_remote_window {
@@ -2931,6 +3033,16 @@ impl Connection {
self.send_to_cm(Data::CloseVoiceCall("".to_owned()));
}
self.send(msg).await;
self.voice_calling = accepted;
if self.is_authed_view_camera_conn() {
if let Some(s) = self.server.upgrade() {
s.write().unwrap().subscribe(
super::audio_service::NAME,
self.inner.clone(),
self.audio_enabled() && accepted,
);
}
}
} else {
log::warn!("Possible a voice call attack.");
}
@@ -2940,6 +3052,14 @@ impl Connection {
crate::audio_service::set_voice_call_input_device(None, true);
// Notify the connection manager that the voice call has been closed.
self.send_to_cm(Data::CloseVoiceCall("".to_owned()));
self.voice_calling = false;
if self.is_authed_view_camera_conn() {
if let Some(s) = self.server.upgrade() {
s.write()
.unwrap()
.subscribe(super::audio_service::NAME, self.inner.clone(), false);
}
}
}
async fn update_options(&mut self, o: &OptionMessage) {
@@ -3016,11 +3136,21 @@ impl Connection {
if q != BoolOption::NotSet {
self.disable_audio = q == BoolOption::Yes;
if let Some(s) = self.server.upgrade() {
s.write().unwrap().subscribe(
super::audio_service::NAME,
self.inner.clone(),
self.audio_enabled(),
);
if self.is_authed_view_camera_conn() {
if self.voice_calling || !self.audio_enabled() {
s.write().unwrap().subscribe(
super::audio_service::NAME,
self.inner.clone(),
self.audio_enabled(),
);
}
} else {
s.write().unwrap().subscribe(
super::audio_service::NAME,
self.inner.clone(),
self.audio_enabled(),
);
}
}
}
}
@@ -3316,6 +3446,7 @@ impl Connection {
fn portable_check(&mut self) {
if self.portable.is_installed
|| self.file_transfer.is_some()
|| self.view_camera
|| self.port_forward_socket.is_some()
|| !self.keyboard
{
@@ -3463,6 +3594,13 @@ impl Connection {
false
}
fn is_authed_view_camera_conn(&self) -> bool {
if let Some(id) = self.authed_conn_id.as_ref() {
return id.conn_type() == AuthConnType::ViewCamera;
}
false
}
#[cfg(feature = "unix-file-copy-paste")]
async fn handle_file_clip(&mut self, clip: clipboard::ClipboardFile) {
let is_stopping_allowed = clip.is_stopping_allowed();
@@ -3966,7 +4104,6 @@ impl Retina {
}
mod raii {
// CONN_COUNT: remote connection count in fact
// ALIVE_CONNS: all connections, including unauthorized connections
// AUTHED_CONNS: all authorized connections
@@ -4001,7 +4138,7 @@ mod raii {
_ONCE.call_once(|| {
shutdown_hooks::add_shutdown_hook(connection_shutdown_hook);
});
if conn_type == AuthConnType::Remote {
if conn_type == AuthConnType::Remote || conn_type == AuthConnType::ViewCamera {
video_service::VIDEO_QOS
.lock()
.unwrap()
@@ -4024,12 +4161,12 @@ mod raii {
.send((conn_count, remote_count)));
}
pub fn remote_and_file_conn_count() -> usize {
pub fn non_port_forward_conn_count() -> usize {
AUTHED_CONNS
.lock()
.unwrap()
.iter()
.filter(|c| c.1 == AuthConnType::Remote || c.1 == AuthConnType::FileTransfer)
.filter(|c| c.1 != AuthConnType::PortForward)
.count()
}
@@ -4112,7 +4249,7 @@ mod raii {
impl Drop for AuthedConnID {
fn drop(&mut self) {
if self.1 == AuthConnType::Remote {
if self.1 == AuthConnType::Remote || self.1 == AuthConnType::ViewCamera {
scrap::codec::Encoder::update(scrap::codec::EncodingUpdate::Remove(self.0));
video_service::VIDEO_QOS
.lock()

View File

@@ -404,6 +404,7 @@ fn no_displays(displays: &Vec<Display>) -> bool {
}
}
#[inline]
#[cfg(not(windows))]
pub fn try_get_displays() -> ResultType<Vec<Display>> {

View File

@@ -501,8 +501,13 @@ pub fn try_start_record_cursor_pos() -> Option<thread::JoinHandle<()>> {
}
pub fn try_stop_record_cursor_pos() {
let count_lock = CONN_COUNT.lock().unwrap();
if *count_lock > 0 {
let remote_count = AUTHED_CONNS
.lock()
.unwrap()
.iter()
.filter(|c| c.1 == AuthConnType::Remote)
.count();
if remote_count > 0 {
return;
}
RECORD_CURSOR_POS_RUNNING.store(false, Ordering::SeqCst);

View File

@@ -717,7 +717,7 @@ pub mod client {
}
let frame_ptr = base.add(ADDR_CAPTURE_FRAME);
let data = slice::from_raw_parts(frame_ptr, (*frame_info).length);
Ok(Frame::PixelBuffer(PixelBuffer::new(
Ok(Frame::PixelBuffer(PixelBuffer::with_BGRA(
data,
self.width,
self.height,
@@ -808,8 +808,13 @@ pub mod client {
},
ConnCount(None) => {
if !quick_support {
let cnt = crate::server::CONN_COUNT.lock().unwrap().clone();
stream.send(&Data::DataPortableService(ConnCount(Some(cnt)))).await.ok();
let remote_count = crate::server::AUTHED_CONNS
.lock()
.unwrap()
.iter()
.filter(|c| c.1 == crate::server::AuthConnType::Remote)
.count();
stream.send(&Data::DataPortableService(ConnCount(Some(remote_count)))).await.ok();
}
},
WillClose => {

View File

@@ -106,7 +106,7 @@ pub struct VideoQoS {
fps: u32,
ratio: f32,
users: HashMap<i32, UserData>,
displays: HashMap<usize, DisplayData>,
displays: HashMap<String, DisplayData>,
bitrate_store: u32,
adjust_ratio_instant: Instant,
abr_config: bool,
@@ -168,8 +168,8 @@ impl VideoQoS {
self.users.iter().any(|u| u.1.record)
}
pub fn set_support_changing_quality(&mut self, display_idx: usize, support: bool) {
if let Some(display) = self.displays.get_mut(&display_idx) {
pub fn set_support_changing_quality(&mut self, video_service_name: &str, support: bool) {
if let Some(display) = self.displays.get_mut(video_service_name) {
display.support_changing_quality = support;
}
}
@@ -346,16 +346,17 @@ impl VideoQoS {
// Common adjust functions
impl VideoQoS {
pub fn new_display(&mut self, display_idx: usize) {
self.displays.insert(display_idx, DisplayData::default());
pub fn new_display(&mut self, video_service_name: String) {
self.displays
.insert(video_service_name, DisplayData::default());
}
pub fn remove_display(&mut self, display_idx: usize) {
self.displays.remove(&display_idx);
pub fn remove_display(&mut self, video_service_name: &str) {
self.displays.remove(video_service_name);
}
pub fn update_display_data(&mut self, display_idx: usize, send_counter: usize) {
if let Some(display) = self.displays.get_mut(&display_idx) {
pub fn update_display_data(&mut self, video_service_name: &str, send_counter: usize) {
if let Some(display) = self.displays.get_mut(video_service_name) {
display.send_counter += send_counter;
}
self.adjust_fps();

View File

@@ -18,12 +18,7 @@
// to-do:
// https://slhck.info/video/2017/03/01/rate-control.html
use super::{
display_service::{check_display_changed, get_display_info},
service::ServiceTmpl,
video_qos::VideoQoS,
*,
};
use super::{display_service::check_display_changed, service::ServiceTmpl, video_qos::VideoQoS, *};
#[cfg(target_os = "linux")]
use crate::common::SimpleCallOnReturn;
#[cfg(target_os = "linux")]
@@ -65,7 +60,6 @@ use std::{
time::{self, Duration, Instant},
};
pub const NAME: &'static str = "video";
pub const OPTION_REFRESH: &'static str = "refresh";
lazy_static::lazy_static! {
@@ -133,10 +127,34 @@ impl VideoFrameController {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum VideoSource {
Monitor,
Camera,
}
impl VideoSource {
pub fn service_name_prefix(&self) -> &'static str {
match self {
VideoSource::Monitor => "monitor",
VideoSource::Camera => "camera",
}
}
pub fn is_monitor(&self) -> bool {
matches!(self, VideoSource::Monitor)
}
pub fn is_camera(&self) -> bool {
matches!(self, VideoSource::Camera)
}
}
#[derive(Clone)]
pub struct VideoService {
sp: GenericService,
idx: usize,
source: VideoSource,
}
impl Deref for VideoService {
@@ -153,14 +171,15 @@ impl DerefMut for VideoService {
}
}
pub fn get_service_name(idx: usize) -> String {
format!("{}{}", NAME, idx)
pub fn get_service_name(source: VideoSource, idx: usize) -> String {
format!("{}{}", source.service_name_prefix(), idx)
}
pub fn new(idx: usize) -> GenericService {
pub fn new(source: VideoSource, idx: usize) -> GenericService {
let vs = VideoService {
sp: GenericService::new(get_service_name(idx), true),
sp: GenericService::new(get_service_name(source, idx), true),
idx,
source,
};
GenericService::run(&vs, run);
vs.sp
@@ -292,7 +311,10 @@ impl DerefMut for CapturerInfo {
}
}
fn get_capturer(current: usize, portable_service_running: bool) -> ResultType<CapturerInfo> {
fn get_capturer_monitor(
current: usize,
portable_service_running: bool,
) -> ResultType<CapturerInfo> {
#[cfg(target_os = "linux")]
{
if !is_x11() {
@@ -309,6 +331,7 @@ fn get_capturer(current: usize, portable_service_running: bool) -> ResultType<Ca
ndisplay
);
}
let display = displays.remove(current);
#[cfg(target_os = "linux")]
@@ -382,8 +405,59 @@ fn get_capturer(current: usize, portable_service_running: bool) -> ResultType<Ca
})
}
fn get_capturer_camera(current: usize) -> ResultType<CapturerInfo> {
let cameras = camera::Cameras::get_sync_cameras();
let ncamera = cameras.len();
if ncamera <= current {
bail!("Failed to get camera {}, cameras len: {}", current, ncamera,);
}
let Some(camera) = cameras.get(current) else {
bail!(
"Camera of index {} doesn't exist or platform not supported",
current
);
};
let capturer = camera::Cameras::get_capturer(current)?;
let (width, height) = (camera.width as usize, camera.height as usize);
let origin = (camera.x as i32, camera.y as i32);
let name = &camera.name;
let privacy_mode_id = get_privacy_mode_conn_id().unwrap_or(INVALID_PRIVACY_MODE_CONN_ID);
let _capturer_privacy_mode_id = privacy_mode_id;
log::debug!(
"#cameras={}, current={}, origin: {:?}, width={}, height={}, cpus={}/{}, name:{}",
ncamera,
current,
&origin,
width,
height,
num_cpus::get_physical(),
num_cpus::get(),
name,
);
return Ok(CapturerInfo {
origin,
width,
height,
ndisplay: ncamera,
current,
privacy_mode_id,
_capturer_privacy_mode_id: privacy_mode_id,
capturer,
});
}
fn get_capturer(
source: VideoSource,
current: usize,
portable_service_running: bool,
) -> ResultType<CapturerInfo> {
match source {
VideoSource::Monitor => get_capturer_monitor(current, portable_service_running),
VideoSource::Camera => get_capturer_camera(current),
}
}
fn run(vs: VideoService) -> ResultType<()> {
let _raii = Raii::new(vs.idx);
let _raii = Raii::new(vs.sp.name());
// Wayland only support one video capturer for now. It is ok to call ensure_inited() here.
//
// ensure_inited() is needed because clear() may be called.
@@ -406,7 +480,7 @@ fn run(vs: VideoService) -> ResultType<()> {
let display_idx = vs.idx;
let sp = vs.sp;
let mut c = get_capturer(display_idx, last_portable_service_running)?;
let mut c = get_capturer(vs.source, display_idx, last_portable_service_running)?;
#[cfg(windows)]
if !scrap::codec::enable_directx_capture() && !c.is_gdi() {
log::info!("disable dxgi with option, fall back to gdi");
@@ -423,11 +497,12 @@ fn run(vs: VideoService) -> ResultType<()> {
drop(video_qos);
let (mut encoder, encoder_cfg, codec_format, use_i444, recorder) = match setup_encoder(
&c,
display_idx,
sp.name(),
quality,
client_record,
record_incoming,
last_portable_service_running,
vs.source,
) {
Ok(result) => result,
Err(err) => {
@@ -441,26 +516,29 @@ fn run(vs: VideoService) -> ResultType<()> {
}));
setup_encoder(
&c,
display_idx,
sp.name(),
quality,
client_record,
record_incoming,
last_portable_service_running,
vs.source,
)?
}
};
#[cfg(feature = "vram")]
c.set_output_texture(encoder.input_texture());
#[cfg(target_os = "android")]
if let Err(e) = check_change_scale(encoder.is_hardware()) {
try_broadcast_display_changed(&sp, display_idx, &c, true).ok();
bail!(e);
if vs.source.is_monitor() {
if let Err(e) = check_change_scale(encoder.is_hardware()) {
try_broadcast_display_changed(&sp, display_idx, &c, true).ok();
bail!(e);
}
}
VIDEO_QOS.lock().unwrap().store_bitrate(encoder.bitrate());
VIDEO_QOS
.lock()
.unwrap()
.set_support_changing_quality(display_idx, encoder.support_changing_quality());
.set_support_changing_quality(&sp.name(), encoder.support_changing_quality());
log::info!("initial quality: {quality:?}");
if sp.is_option_true(OPTION_REFRESH) {
@@ -500,10 +578,12 @@ fn run(vs: VideoService) -> ResultType<()> {
client_record,
&mut send_counter,
&mut second_instant,
display_idx,
&sp.name(),
)?;
if sp.is_option_true(OPTION_REFRESH) {
let _ = try_broadcast_display_changed(&sp, display_idx, &c, true);
if vs.source.is_monitor() {
let _ = try_broadcast_display_changed(&sp, display_idx, &c, true);
}
log::info!("switch to refresh");
bail!("SWITCH");
}
@@ -527,10 +607,12 @@ fn run(vs: VideoService) -> ResultType<()> {
#[cfg(all(windows, feature = "vram"))]
if c.is_gdi() && encoder.input_texture() {
log::info!("changed to gdi when using vram");
VRamEncoder::set_fallback_gdi(display_idx, true);
VRamEncoder::set_fallback_gdi(sp.name(), true);
bail!("SWITCH");
}
check_privacy_mode_changed(&sp, display_idx, &c)?;
if vs.source.is_monitor() {
check_privacy_mode_changed(&sp, display_idx, &c)?;
}
#[cfg(windows)]
{
if crate::platform::windows::desktop_changed()
@@ -540,7 +622,7 @@ fn run(vs: VideoService) -> ResultType<()> {
}
}
let now = time::Instant::now();
if last_check_displays.elapsed().as_millis() > 1000 {
if vs.source.is_monitor() && last_check_displays.elapsed().as_millis() > 1000 {
last_check_displays = now;
// This check may be redundant, but it is better to be safe.
// The previous check in `sp.is_option_true(OPTION_REFRESH)` block may be enough.
@@ -575,7 +657,7 @@ fn run(vs: VideoService) -> ResultType<()> {
{
#[cfg(feature = "vram")]
if try_gdi == 1 && !c.is_gdi() {
VRamEncoder::set_fallback_gdi(display_idx, false);
VRamEncoder::set_fallback_gdi(sp.name(), false);
}
try_gdi = 0;
}
@@ -635,7 +717,9 @@ fn run(vs: VideoService) -> ResultType<()> {
Err(err) => {
// This check may be redundant, but it is better to be safe.
// The previous check in `sp.is_option_true(OPTION_REFRESH)` block may be enough.
try_broadcast_display_changed(&sp, display_idx, &c, true)?;
if vs.source.is_monitor() {
try_broadcast_display_changed(&sp, display_idx, &c, true)?;
}
#[cfg(windows)]
if !c.is_gdi() {
@@ -657,7 +741,9 @@ fn run(vs: VideoService) -> ResultType<()> {
let timeout_millis = 3_000u64;
let wait_begin = Instant::now();
while wait_begin.elapsed().as_millis() < timeout_millis as _ {
check_privacy_mode_changed(&sp, display_idx, &c)?;
if vs.source.is_monitor() {
check_privacy_mode_changed(&sp, display_idx, &c)?;
}
frame_controller.try_wait_next(&mut fetched_conn_ids, 300);
// break if all connections have received current frame
if fetched_conn_ids.len() >= frame_controller.send_conn_ids.len() {
@@ -676,32 +762,35 @@ fn run(vs: VideoService) -> ResultType<()> {
Ok(())
}
struct Raii(usize);
struct Raii(String);
impl Raii {
fn new(display_idx: usize) -> Self {
VIDEO_QOS.lock().unwrap().new_display(display_idx);
Raii(display_idx)
fn new(name: String) -> Self {
log::info!("new video service: {}", name);
VIDEO_QOS.lock().unwrap().new_display(name.clone());
Raii(name)
}
}
impl Drop for Raii {
fn drop(&mut self) {
log::info!("stop video service: {}", self.0);
#[cfg(feature = "vram")]
VRamEncoder::set_not_use(self.0, false);
VRamEncoder::set_not_use(self.0.clone(), false);
#[cfg(feature = "vram")]
Encoder::update(scrap::codec::EncodingUpdate::Check);
VIDEO_QOS.lock().unwrap().remove_display(self.0);
VIDEO_QOS.lock().unwrap().remove_display(&self.0);
}
}
fn setup_encoder(
c: &CapturerInfo,
display_idx: usize,
name: String,
quality: f32,
client_record: bool,
record_incoming: bool,
last_portable_service_running: bool,
source: VideoSource,
) -> ResultType<(
Encoder,
EncoderCfg,
@@ -711,14 +800,15 @@ fn setup_encoder(
)> {
let encoder_cfg = get_encoder_config(
&c,
display_idx,
name.to_string(),
quality,
client_record || record_incoming,
last_portable_service_running,
source,
);
Encoder::set_fallback(&encoder_cfg);
let codec_format = Encoder::negotiated_codec();
let recorder = get_recorder(record_incoming, display_idx);
let recorder = get_recorder(record_incoming, name);
let use_i444 = Encoder::use_i444(&encoder_cfg);
let encoder = Encoder::new(encoder_cfg.clone(), use_i444)?;
Ok((encoder, encoder_cfg, codec_format, use_i444, recorder))
@@ -726,15 +816,16 @@ fn setup_encoder(
fn get_encoder_config(
c: &CapturerInfo,
_display_idx: usize,
_name: String,
quality: f32,
record: bool,
_portable_service: bool,
_source: VideoSource,
) -> EncoderCfg {
#[cfg(all(windows, feature = "vram"))]
if _portable_service || c.is_gdi() {
if _portable_service || c.is_gdi() || _source == VideoSource::Camera {
log::info!("gdi:{}, portable:{}", c.is_gdi(), _portable_service);
VRamEncoder::set_not_use(_display_idx, true);
VRamEncoder::set_not_use(_name, true);
}
#[cfg(feature = "vram")]
Encoder::update(scrap::codec::EncodingUpdate::Check);
@@ -800,7 +891,7 @@ fn get_encoder_config(
}
}
fn get_recorder(record_incoming: bool, display: usize) -> Arc<Mutex<Option<Recorder>>> {
fn get_recorder(record_incoming: bool, video_service_name: String) -> Arc<Mutex<Option<Recorder>>> {
#[cfg(windows)]
let root = crate::platform::is_root();
#[cfg(not(windows))]
@@ -819,7 +910,7 @@ fn get_recorder(record_incoming: bool, display: usize) -> Arc<Mutex<Option<Recor
server: true,
id: Config::get_id(),
dir: crate::ui_interface::video_save_directory(root),
display,
video_service_name,
tx,
})
.map_or(Default::default(), |r| Arc::new(Mutex::new(Some(r))))
@@ -1004,7 +1095,9 @@ fn try_broadcast_display_changed(
(cap.origin.0, cap.origin.1, cap.width, cap.height),
) {
log::info!("Display {} changed", display);
if let Some(msg_out) = make_display_changed_msg(display_idx, Some(display)) {
if let Some(msg_out) =
make_display_changed_msg(display_idx, Some(display), VideoSource::Monitor)
{
let msg_out = Arc::new(msg_out);
sp.send_shared(msg_out.clone());
// switch display may occur before the first video frame, add snapshot to send to new subscribers
@@ -1021,10 +1114,16 @@ fn try_broadcast_display_changed(
pub fn make_display_changed_msg(
display_idx: usize,
opt_display: Option<DisplayInfo>,
source: VideoSource,
) -> Option<Message> {
let display = match opt_display {
Some(d) => d,
None => get_display_info(display_idx)?,
None => match source {
VideoSource::Monitor => display_service::get_display_info(display_idx)?,
VideoSource::Camera => camera::Cameras::get_sync_cameras()
.get(display_idx)?
.clone(),
},
};
let mut misc = Misc::new();
misc.set_switch_display(SwitchDisplay {
@@ -1033,13 +1132,24 @@ pub fn make_display_changed_msg(
y: display.y,
width: display.width,
height: display.height,
cursor_embedded: display_service::capture_cursor_embedded(),
cursor_embedded: match source {
VideoSource::Monitor => display_service::capture_cursor_embedded(),
VideoSource::Camera => false,
},
#[cfg(not(target_os = "android"))]
resolutions: Some(SupportedResolutions {
resolutions: if display.name.is_empty() {
vec![]
} else {
crate::platform::resolutions(&display.name)
resolutions: match source {
VideoSource::Monitor => {
if display.name.is_empty() {
vec![]
} else {
crate::platform::resolutions(&display.name)
}
}
VideoSource::Camera => camera::Cameras::get_camera_resolution(display_idx)
.ok()
.into_iter()
.collect(),
},
..SupportedResolutions::default()
})
@@ -1059,7 +1169,7 @@ fn check_qos(
client_record: bool,
send_counter: &mut usize,
second_instant: &mut Instant,
display_idx: usize,
name: &str,
) -> ResultType<()> {
let mut video_qos = VIDEO_QOS.lock().unwrap();
*spf = video_qos.spf();
@@ -1082,7 +1192,7 @@ fn check_qos(
}
if second_instant.elapsed() > Duration::from_secs(1) {
*second_instant = Instant::now();
video_qos.update_display_data(display_idx, *send_counter);
video_qos.update_display_data(&name, *send_counter);
*send_counter = 0;
}
drop(video_qos);

View File

@@ -19,6 +19,7 @@ impl InvokeUiCM for SciterHandler {
&make_args!(
client.id,
client.is_file_transfer,
client.is_view_camera,
client.port_forward.clone(),
client.peer_id.clone(),
client.name.clone(),

View File

@@ -356,7 +356,7 @@ function bring_to_top(idx=-1) {
}
}
handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input) {
handler.addConnection = function(id, is_file_transfer, is_view_camera, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input) {
stdout.println("new connection #" + id + ": " + peer_id);
var conn;
connections.map(function(c) {
@@ -550,7 +550,7 @@ function adjustHeader() {
view.on("size", adjustHeader);
// handler.addConnection(0, false, 0, "", "test1", true, false, false, true, true);
// handler.addConnection(1, false, 0, "", "test2--------", true, false, false, false, false);
// handler.addConnection(2, false, 0, "", "test3", true, false, false, false, false);
// handler.addConnection(0, false, false, 0, "", "test1", true, false, false, true, true);
// handler.addConnection(1, false, false, 0, "", "test2--------", true, false, false, false, false);
// handler.addConnection(2, false, false, 0, "", "test3", true, false, false, false, false);
// handler.newMessage(0, 'h');

View File

@@ -316,6 +316,7 @@ impl InvokeUiSession for SciterHandler {
ConnType::RDP => {}
ConnType::PORT_FORWARD => {}
ConnType::FILE_TRANSFER => {}
ConnType::VIEW_CAMERA => {}
ConnType::DEFAULT_CONN => {
crate::keyboard::client::start_grab_loop();
}
@@ -557,6 +558,8 @@ impl SciterSession {
let conn_type = if cmd.eq("--file-transfer") {
ConnType::FILE_TRANSFER
} else if cmd.eq("--view-camera") {
ConnType::VIEW_CAMERA
} else if cmd.eq("--port-forward") {
ConnType::PORT_FORWARD
} else if cmd.eq("--rdp") {

View File

@@ -47,6 +47,7 @@ pub struct Client {
pub authorized: bool,
pub disconnected: bool,
pub is_file_transfer: bool,
pub is_view_camera: bool,
pub port_forward: String,
pub name: String,
pub peer_id: String,
@@ -128,6 +129,7 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
&self,
id: i32,
is_file_transfer: bool,
is_view_camera: bool,
port_forward: String,
peer_id: String,
name: String,
@@ -147,6 +149,7 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
authorized,
disconnected: false,
is_file_transfer,
is_view_camera,
port_forward,
name: name.clone(),
peer_id: peer_id.clone(),
@@ -402,9 +405,9 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
}
Ok(Some(data)) => {
match data {
Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, block_input, from_switch} => {
Data::Login{id, is_file_transfer, is_view_camera, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, block_input, from_switch} => {
log::debug!("conn_id: {}", id);
self.cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, from_switch, self.tx.clone());
self.cm.add_connection(id, is_file_transfer, is_view_camera, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, from_switch, self.tx.clone());
self.conn_id = id;
#[cfg(target_os = "windows")]
{
@@ -672,6 +675,7 @@ pub async fn start_listen<T: InvokeUiCM>(
Some(Data::Login {
id,
is_file_transfer,
is_view_camera,
port_forward,
peer_id,
name,
@@ -690,6 +694,7 @@ pub async fn start_listen<T: InvokeUiCM>(
cm.add_connection(
id,
is_file_transfer,
is_view_camera,
port_forward,
peer_id,
name,

View File

@@ -190,6 +190,10 @@ impl<T: InvokeUiSession> Session<T> {
.eq(&ConnType::FILE_TRANSFER)
}
pub fn is_view_camera(&self) -> bool {
self.lc.read().unwrap().conn_type.eq(&ConnType::VIEW_CAMERA)
}
pub fn is_port_forward(&self) -> bool {
let conn_type = self.lc.read().unwrap().conn_type;
conn_type == ConnType::PORT_FORWARD || conn_type == ConnType::RDP
@@ -1630,7 +1634,12 @@ impl<T: InvokeUiSession> Interface for Session<T> {
if pi.displays.is_empty() {
self.lc.write().unwrap().handle_peer_info(&pi);
self.update_privacy_mode();
self.msgbox("error", "Remote Error", "No Displays", "");
let msg = if self.is_view_camera() {
"No cameras"
} else {
"No displays"
};
self.msgbox("error", "Error", msg, "");
return;
}
self.try_change_init_resolution(pi.current_display);