mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-17 10:21:28 +03:00
fix: video service, wait timeout (#13208)
Use multiple frame fetched notifiers. Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -776,7 +776,9 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
Some((instant, value)) = rx_video.recv() => {
|
Some((instant, value)) = rx_video.recv() => {
|
||||||
if !conn.video_ack_required {
|
if !conn.video_ack_required {
|
||||||
video_service::notify_video_frame_fetched(id, Some(instant.into()));
|
if let Some(message::Union::VideoFrame(vf)) = &value.union {
|
||||||
|
video_service::notify_video_frame_fetched(vf.display as usize, id, Some(instant.into()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Err(err) = conn.stream.send(&value as &Message).await {
|
if let Err(err) = conn.stream.send(&value as &Message).await {
|
||||||
conn.on_close(&err.to_string(), false).await;
|
conn.on_close(&err.to_string(), false).await;
|
||||||
@@ -924,7 +926,7 @@ impl Connection {
|
|||||||
crate::plugin::EVENT_ON_CONN_CLOSE_SERVER.to_owned(),
|
crate::plugin::EVENT_ON_CONN_CLOSE_SERVER.to_owned(),
|
||||||
conn.lr.my_id.clone(),
|
conn.lr.my_id.clone(),
|
||||||
);
|
);
|
||||||
video_service::notify_video_frame_fetched(id, None);
|
video_service::notify_video_frame_fetched_by_conn_id(id, None);
|
||||||
if conn.authorized {
|
if conn.authorized {
|
||||||
password::update_temporary_password();
|
password::update_temporary_password();
|
||||||
}
|
}
|
||||||
@@ -2909,7 +2911,7 @@ impl Connection {
|
|||||||
self.update_auto_disconnect_timer();
|
self.update_auto_disconnect_timer();
|
||||||
}
|
}
|
||||||
Some(misc::Union::VideoReceived(_)) => {
|
Some(misc::Union::VideoReceived(_)) => {
|
||||||
video_service::notify_video_frame_fetched(
|
video_service::notify_video_frame_fetched_by_conn_id(
|
||||||
self.inner.id,
|
self.inner.id,
|
||||||
Some(Instant::now().into()),
|
Some(Instant::now().into()),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -62,11 +62,18 @@ use std::{
|
|||||||
|
|
||||||
pub const OPTION_REFRESH: &'static str = "refresh";
|
pub const OPTION_REFRESH: &'static str = "refresh";
|
||||||
|
|
||||||
|
type FrameFetchedNotifierSender = UnboundedSender<(i32, Option<Instant>)>;
|
||||||
|
type FrameFetchedNotifierReceiver = Arc<TokioMutex<UnboundedReceiver<(i32, Option<Instant>)>>>;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref FRAME_FETCHED_NOTIFIER: (UnboundedSender<(i32, Option<Instant>)>, Arc<TokioMutex<UnboundedReceiver<(i32, Option<Instant>)>>>) = {
|
static ref FRAME_FETCHED_NOTIFIERS: Mutex<HashMap<usize, (FrameFetchedNotifierSender, FrameFetchedNotifierReceiver)>> = Mutex::new(HashMap::default());
|
||||||
let (tx, rx) = unbounded_channel();
|
|
||||||
(tx, Arc::new(TokioMutex::new(rx)))
|
// display_idx -> set of conn id.
|
||||||
};
|
// Used to record which connections need to be notified when
|
||||||
|
// 1. A new frame is received from a web client.
|
||||||
|
// Because web client does not send the display index in message `VideoReceived`.
|
||||||
|
// 2. The client is closing.
|
||||||
|
static ref DISPLAY_CONN_IDS: Arc<Mutex<HashMap<usize, HashSet<i32>>>> = Default::default();
|
||||||
pub static ref VIDEO_QOS: Arc<Mutex<VideoQoS>> = Default::default();
|
pub static ref VIDEO_QOS: Arc<Mutex<VideoQoS>> = Default::default();
|
||||||
pub static ref IS_UAC_RUNNING: Arc<Mutex<bool>> = Default::default();
|
pub static ref IS_UAC_RUNNING: Arc<Mutex<bool>> = Default::default();
|
||||||
pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc<Mutex<bool>> = Default::default();
|
pub static ref IS_FOREGROUND_WINDOW_ELEVATED: Arc<Mutex<bool>> = Default::default();
|
||||||
@@ -80,18 +87,45 @@ struct Screenshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn notify_video_frame_fetched(conn_id: i32, frame_tm: Option<Instant>) {
|
pub fn notify_video_frame_fetched(display_idx: usize, conn_id: i32, frame_tm: Option<Instant>) {
|
||||||
FRAME_FETCHED_NOTIFIER.0.send((conn_id, frame_tm)).ok();
|
if let Some(notifier) = FRAME_FETCHED_NOTIFIERS.lock().unwrap().get(&display_idx) {
|
||||||
|
notifier.0.send((conn_id, frame_tm)).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn notify_video_frame_fetched_by_conn_id(conn_id: i32, frame_tm: Option<Instant>) {
|
||||||
|
let vec_display_idx: Vec<usize> = {
|
||||||
|
let display_conn_ids = DISPLAY_CONN_IDS.lock().unwrap();
|
||||||
|
display_conn_ids
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(display_idx, conn_ids)| {
|
||||||
|
if conn_ids.contains(&conn_id) {
|
||||||
|
Some(*display_idx)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
let notifiers = FRAME_FETCHED_NOTIFIERS.lock().unwrap();
|
||||||
|
for display_idx in vec_display_idx {
|
||||||
|
if let Some(notifier) = notifiers.get(&display_idx) {
|
||||||
|
notifier.0.send((conn_id, frame_tm)).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VideoFrameController {
|
struct VideoFrameController {
|
||||||
|
display_idx: usize,
|
||||||
cur: Instant,
|
cur: Instant,
|
||||||
send_conn_ids: HashSet<i32>,
|
send_conn_ids: HashSet<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VideoFrameController {
|
impl VideoFrameController {
|
||||||
fn new() -> Self {
|
fn new(display_idx: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
display_idx,
|
||||||
cur: Instant::now(),
|
cur: Instant::now(),
|
||||||
send_conn_ids: HashSet::new(),
|
send_conn_ids: HashSet::new(),
|
||||||
}
|
}
|
||||||
@@ -105,6 +139,10 @@ impl VideoFrameController {
|
|||||||
if !conn_ids.is_empty() {
|
if !conn_ids.is_empty() {
|
||||||
self.cur = tm;
|
self.cur = tm;
|
||||||
self.send_conn_ids = conn_ids;
|
self.send_conn_ids = conn_ids;
|
||||||
|
DISPLAY_CONN_IDS
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(self.display_idx, self.send_conn_ids.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +153,20 @@ impl VideoFrameController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let timeout_dur = Duration::from_millis(timeout_millis as u64);
|
let timeout_dur = Duration::from_millis(timeout_millis as u64);
|
||||||
match tokio::time::timeout(timeout_dur, FRAME_FETCHED_NOTIFIER.1.lock().await.recv()).await
|
let receiver = {
|
||||||
{
|
match FRAME_FETCHED_NOTIFIERS
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get(&self.display_idx)
|
||||||
|
{
|
||||||
|
Some(notifier) => notifier.1.clone(),
|
||||||
|
None => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut receiver_guard = receiver.lock().await;
|
||||||
|
match tokio::time::timeout(timeout_dur, receiver_guard.recv()).await {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// break if timeout
|
// break if timeout
|
||||||
// log::error!("blocking wait frame receiving timeout {}", timeout_millis);
|
// log::error!("blocking wait frame receiving timeout {}", timeout_millis);
|
||||||
@@ -131,6 +181,14 @@ impl VideoFrameController {
|
|||||||
// this branch would never be reached
|
// this branch would never be reached
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
while !receiver_guard.is_empty() {
|
||||||
|
if let Some((id, instant)) = receiver_guard.recv().await {
|
||||||
|
if let Some(tm) = instant {
|
||||||
|
log::trace!("Channel recv latency: {}", tm.elapsed().as_secs_f32());
|
||||||
|
}
|
||||||
|
fetched_conn_ids.insert(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +241,14 @@ pub fn get_service_name(source: VideoSource, idx: usize) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(source: VideoSource, idx: usize) -> GenericService {
|
pub fn new(source: VideoSource, idx: usize) -> GenericService {
|
||||||
|
let _ = FRAME_FETCHED_NOTIFIERS
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.entry(idx)
|
||||||
|
.or_insert_with(|| {
|
||||||
|
let (tx, rx) = unbounded_channel();
|
||||||
|
(tx, Arc::new(TokioMutex::new(rx)))
|
||||||
|
});
|
||||||
let vs = VideoService {
|
let vs = VideoService {
|
||||||
sp: GenericService::new(get_service_name(source, idx), true),
|
sp: GenericService::new(get_service_name(source, idx), true),
|
||||||
idx,
|
idx,
|
||||||
@@ -464,7 +530,7 @@ fn get_capturer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(vs: VideoService) -> ResultType<()> {
|
fn run(vs: VideoService) -> ResultType<()> {
|
||||||
let mut _raii = Raii::new(vs.sp.name());
|
let mut _raii = Raii::new(vs.idx, vs.sp.name());
|
||||||
// Wayland only support one video capturer for now. It is ok to call ensure_inited() here.
|
// 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.
|
// ensure_inited() is needed because clear() may be called.
|
||||||
@@ -476,7 +542,7 @@ fn run(vs: VideoService) -> ResultType<()> {
|
|||||||
let _wayland_call_on_ret = {
|
let _wayland_call_on_ret = {
|
||||||
// Increment active display count when starting
|
// Increment active display count when starting
|
||||||
let _display_count = super::wayland::increment_active_display_count();
|
let _display_count = super::wayland::increment_active_display_count();
|
||||||
|
|
||||||
SimpleCallOnReturn {
|
SimpleCallOnReturn {
|
||||||
b: true,
|
b: true,
|
||||||
f: Box::new(|| {
|
f: Box::new(|| {
|
||||||
@@ -563,7 +629,7 @@ fn run(vs: VideoService) -> ResultType<()> {
|
|||||||
sp.set_option_bool(OPTION_REFRESH, false);
|
sp.set_option_bool(OPTION_REFRESH, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut frame_controller = VideoFrameController::new();
|
let mut frame_controller = VideoFrameController::new(display_idx);
|
||||||
|
|
||||||
let start = time::Instant::now();
|
let start = time::Instant::now();
|
||||||
let mut last_check_displays = time::Instant::now();
|
let mut last_check_displays = time::Instant::now();
|
||||||
@@ -811,6 +877,7 @@ fn run(vs: VideoService) -> ResultType<()> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DISPLAY_CONN_IDS.lock().unwrap().remove(&display_idx);
|
||||||
|
|
||||||
let elapsed = now.elapsed();
|
let elapsed = now.elapsed();
|
||||||
// may need to enable frame(timeout)
|
// may need to enable frame(timeout)
|
||||||
@@ -824,15 +891,17 @@ fn run(vs: VideoService) -> ResultType<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct Raii {
|
struct Raii {
|
||||||
|
display_idx: usize,
|
||||||
name: String,
|
name: String,
|
||||||
try_vram: bool,
|
try_vram: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Raii {
|
impl Raii {
|
||||||
fn new(name: String) -> Self {
|
fn new(display_idx: usize, name: String) -> Self {
|
||||||
log::info!("new video service: {}", name);
|
log::info!("new video service: {}", name);
|
||||||
VIDEO_QOS.lock().unwrap().new_display(name.clone());
|
VIDEO_QOS.lock().unwrap().new_display(name.clone());
|
||||||
Raii {
|
Raii {
|
||||||
|
display_idx,
|
||||||
name,
|
name,
|
||||||
try_vram: true,
|
try_vram: true,
|
||||||
}
|
}
|
||||||
@@ -849,6 +918,7 @@ impl Drop for Raii {
|
|||||||
#[cfg(feature = "vram")]
|
#[cfg(feature = "vram")]
|
||||||
Encoder::update(scrap::codec::EncodingUpdate::Check);
|
Encoder::update(scrap::codec::EncodingUpdate::Check);
|
||||||
VIDEO_QOS.lock().unwrap().remove_display(&self.name);
|
VIDEO_QOS.lock().unwrap().remove_display(&self.name);
|
||||||
|
DISPLAY_CONN_IDS.lock().unwrap().remove(&self.display_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user