mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-03-28 07:31:01 +03:00
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:
@@ -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()
|
||||
|
||||
@@ -404,6 +404,7 @@ fn no_displays(displays: &Vec<Display>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(windows))]
|
||||
pub fn try_get_displays() -> ResultType<Vec<Display>> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user