use std::
{
collections::HashMap,
time::{ Duration, Instant },
sync::
{
Arc,
atomic::{ AtomicBool, Ordering },
}
};
use tokio::sync::mpsc::{ Receiver, UnboundedSender };
use winit::
{
application::ApplicationHandler,
dpi::PhysicalSize,
event::WindowEvent,
event_loop::{ ActiveEventLoop, ControlFlow },
window::
{
Window,
WindowAttributes,
WindowId,
},
};
use openh264::decoder::Decoder;
use crate::network::screen::
{
consts,
client::{ ScreenShareRequest, UserEvent, video::VideoSurface },
};
struct Session
{
window: Arc<Window>,
surface: VideoSurface,
frame_rx: Receiver<Vec<u8>>,
decoder: Decoder,
frame_dirty: bool,
running: Arc<AtomicBool>,
deattach: UnboundedSender<()>,
frame_count: u32,
last_fps_time: Instant,
close_requested_at: Option<Instant>,
}
impl Session
{
fn process_pending_frames(&mut self) -> bool
{
let mut pending: Vec<Vec<u8>> = Vec::new();
while let Ok(compressed) = self.frame_rx.try_recv()
{
pending.push(compressed);
}
if pending.is_empty() { return false; }
let newest = pending.len() - 1;
for (index, compressed) in pending.iter().enumerate()
{
let Ok(Some(yuv)) = self.decoder.decode(compressed) else { continue; };
if index != newest { continue; }
self.surface.upload(&yuv);
self.frame_dirty = true;
}
true
}
fn redraw(&mut self)
{
let size = self.window.inner_size();
self.surface.resize(size.width, size.height);
self.surface.render();
}
}
pub struct ScreenShareApp {
sessions: HashMap<WindowId, Session>,
}
impl ScreenShareApp
{
pub fn new() -> Self
{
Self
{
sessions: HashMap::new(),
}
}
fn create_session(&mut self, event_loop: &ActiveEventLoop, request: ScreenShareRequest)
{
if let Some(session) = self.sessions.values_mut().next()
{
let old_running = session.running.clone();
session.frame_rx = request.rx;
session.running = request.running;
session.deattach = request.deattach;
session.decoder = Decoder::new().expect("Failed to create H.264 decoder");
session.frame_dirty = false;
session.frame_count = 0;
session.last_fps_time = Instant::now();
session.close_requested_at = None;
session.window.request_redraw();
old_running.store(false, Ordering::Relaxed);
return;
}
let attrs = WindowAttributes::default()
.with_title("WHY2 ScreenShare")
.with_inner_size(PhysicalSize::new(consts::WINIT_SIZE.0, consts::WINIT_SIZE.1));
let Ok(window) = event_loop.create_window(attrs) else { return; };
let window = Arc::new(window);
let size = window.inner_size();
let Ok(surface) = VideoSurface::new(window.clone(), size.width, size.height) else { return; };
let window_id = window.id();
window.request_redraw();
self.sessions.insert(window_id, Session
{
window,
surface,
frame_rx: request.rx,
decoder: Decoder::new().expect("Failed to create H.264 decoder"),
frame_dirty: false,
running: request.running,
deattach: request.deattach,
frame_count: 0,
last_fps_time: Instant::now(),
close_requested_at: None,
});
}
}
impl ApplicationHandler<UserEvent> for ScreenShareApp
{
fn resumed(&mut self, event_loop: &ActiveEventLoop)
{
event_loop.set_control_flow(ControlFlow::WaitUntil(Instant::now() + consts::FRAME_POLL_INTERVAL));
}
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent)
{
match event
{
UserEvent::NewSession(request) =>
{
self.create_session(event_loop, request);
},
UserEvent::NewFrame => {}, }
}
fn window_event(&mut self, _event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent)
{
match event
{
WindowEvent::RedrawRequested =>
{
if let Some(session) = self.sessions.get_mut(&window_id)
{
session.redraw();
}
},
WindowEvent::Resized(size) =>
{
if let Some(session) = self.sessions.get_mut(&window_id)
{
session.surface.resize(size.width, size.height);
session.window.request_redraw();
}
},
WindowEvent::CloseRequested =>
{
if let Some(session) = self.sessions.remove(&window_id)
{
session.running.store(false, Ordering::Relaxed);
session.deattach.send(()).ok();
}
},
_ => {}
}
}
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop)
{
self.sessions.retain(|_, session|
{
if session.running.load(Ordering::Relaxed)
{
session.close_requested_at = None;
true
} else
{
if session.close_requested_at.is_none()
{
session.close_requested_at = Some(Instant::now());
}
session.close_requested_at.unwrap().elapsed() < Duration::from_millis(250)
}
});
for session in self.sessions.values_mut()
{
if session.process_pending_frames()
{
session.frame_count += 1;
if session.last_fps_time.elapsed() >= Duration::from_secs(1)
{
session.window.set_title(&format!("WHY2 Screenshare ({} FPS)", session.frame_count));
session.frame_count = 0;
session.last_fps_time = Instant::now();
}
session.window.request_redraw();
}
}
event_loop.set_control_flow(ControlFlow::WaitUntil(Instant::now() + consts::FRAME_POLL_INTERVAL));
}
}