use std::
{
time::Instant,
collections::HashMap,
sync::{ Arc, LazyLock },
};
use tokio::
{
task::AbortHandle,
net::tcp::OwnedWriteHalf,
sync::
{
Mutex,
mpsc::{ self, Sender },
},
};
use crate::
{
crypto,
consts::SharedKeys,
network::
{
self,
Streams,
codes::PacketCode,
server::
{
self,
connection::Connection,
},
screen::{ self, ScreenPacketCode },
},
};
static MUTED_ANIMATION: LazyLock<Vec<Vec<u8>>> = LazyLock::new(|| split_access_units(include_bytes!("./assets/muted.h264")));
struct ScreenTransferGuard
{
id: usize,
}
struct Viewer {
token: [u8; 32], tx: Sender<ScreenPacketCode>, task: AbortHandle, needs_key: bool, }
impl Drop for ScreenTransferGuard
{
fn drop(&mut self)
{
if let Some(mut conn) = server::CONNECTIONS.iter_mut().find(|c| c.id() == Some(&self.id))
{
conn.remove_screen_stream();
}
}
}
impl Drop for Viewer
{
fn drop(&mut self)
{
self.task.abort();
}
}
fn split_access_units(bitstream: &[u8]) -> Vec<Vec<u8>> {
let mut units = Vec::new();
let mut start = None;
let mut index = 0;
while index + 3 < bitstream.len()
{
if bitstream[index..index + 3] != [0, 0, 1] { index += 1; continue; }
if bitstream[index + 3] & 0x1f == 7
{
let mut boundary = index;
while boundary > 0 && bitstream[boundary - 1] == 0 { boundary -= 1; }
if let Some(start) = start.replace(boundary)
{
units.push(bitstream[start..boundary].to_vec());
}
}
index += 3;
}
if let Some(start) = start { units.push(bitstream[start..].to_vec()); }
units
}
fn is_keyframe(bitstream: &[u8]) -> bool {
let mut index = 0;
while index + 3 < bitstream.len()
{
if bitstream[index..index + 3] != [0, 0, 1] { index += 1; continue; }
if matches!(bitstream[index + 3] & 0x1f, 5 | 7) { return true; }
index += 3;
}
false
}
fn spawn_viewer (
stream: Arc<Mutex<OwnedWriteHalf>>,
keys: &SharedKeys,
token: [u8; 32],
) -> Option<Viewer>
{
let mut rex_stream = crypto::init_rex_stream(keys, &token)?;
let (tx, mut rx) = mpsc::channel(screen::consts::VIEWER_CHANNEL_BOUND);
let task = tokio::spawn(async move
{
let mut seq = 0usize;
while let Some(code) = rx.recv().await
{
screen::send_frame(&mut *stream.lock().await, code, &mut rex_stream, Some(&mut seq)).await;
}
}).abort_handle();
Some(Viewer { token, tx, task, needs_key: false })
}
fn muted_frame(started: &Instant) -> Option<usize> {
let frames = MUTED_ANIMATION.len();
if frames == 0 { return None; }
Some((started.elapsed().as_millis() / screen::consts::MUTED_FRAME_INTERVAL.as_millis()) as usize % frames)
}
async fn end_share(id: usize) {
let (write_stream, keys, username) =
{
let mut conn = match server::CONNECTIONS.iter_mut().find(|c| c.id() == Some(&id))
{
Some(c) => c,
None => return
};
if conn.take_screen_stream().is_none() { return; }
(conn.write_stream().clone(), conn.keys().cloned(), conn.username().cloned())
};
if let Some(username) = username
{
server::deattach(id, &username).await;
}
network::send(&mut *write_stream.lock().await, PacketCode::Screen { token: None }, keys.as_ref()).await;
}
pub async fn screen(token: [u8; 32], id: usize, streams: &mut Streams<'_>, task: AbortHandle)
{
let keys =
{
let conn = server::CONNECTIONS.iter_mut()
.find(|e| e.value().id() == Some(&id));
match conn
{
Some(mut c) =>
{
let keys = match c.keys()
{
Some(k) => k.clone(),
None => return
};
c.set_screen_stream(task);
keys
},
None => return
}
};
let _guard = ScreenTransferGuard { id };
let mut seq = 0usize;
let mut viewers = HashMap::<usize, Viewer>::new();
let mut rex_stream = crypto::init_rex_stream(&keys, &token).unwrap();
let started = Instant::now();
let mut sent_muted_frame = None;
loop
{
let read = match screen::receive_frame(streams, &mut rex_stream, &mut seq).await
{
Some(r) => r,
None => break
};
let muted = server::CONNECTIONS.iter()
.find(|c| c.id() == Some(&id))
.map(|c| *c.muted())
.unwrap_or(false);
let read = match (muted, read)
{
(false, read) =>
{
sent_muted_frame = None;
read
},
(true, ScreenPacketCode::Audio { .. }) => continue,
(true, ScreenPacketCode::Video { .. }) =>
{
let Some(frame) = muted_frame(&started) else { continue; };
if sent_muted_frame == Some(frame) { continue; }
sent_muted_frame = Some(frame);
ScreenPacketCode::Video { data: MUTED_ANIMATION[frame].clone() }
},
};
let entries: Vec<(usize, Arc<Mutex<OwnedWriteHalf>>, [u8; 32], Option<SharedKeys>)> = server::CONNECTIONS.iter().filter_map(|entry|
{
match entry.value()
{
Connection::Authenticated { id: client_id, attached_screen, keys, .. } =>
{
if let Some(attached_screen) = attached_screen && attached_screen.target_id == id
{
let known = viewers.get(client_id).is_some_and(|v| v.token == attached_screen.token);
Some((*client_id, attached_screen.stream.clone(), attached_screen.token,
if known { None } else { Some(keys.clone()) }))
} else { None }
},
_ => None,
}
}).collect();
viewers.retain(|client_id, _| entries.iter().any(|(e, ..)| e == client_id));
let keyframe = matches!(&read, ScreenPacketCode::Video { data } if is_keyframe(data));
for (client_id, stream, token, keys) in entries
{
if let Some(keys) = keys
{
let Some(viewer) = spawn_viewer(stream, &keys, token) else { continue; };
viewers.insert(client_id, viewer);
}
let Some(viewer) = viewers.get_mut(&client_id) else { continue; };
if client_id == id && matches!(read, ScreenPacketCode::Audio { .. }) { continue; }
if viewer.needs_key && matches!(read, ScreenPacketCode::Video { .. })
{
if !keyframe { continue; }
viewer.needs_key = false;
}
if viewer.tx.try_send(read.clone()).is_err() && matches!(read, ScreenPacketCode::Video { .. })
{
viewer.needs_key = true;
}
}
}
end_share(id).await;
}