1use serde::{Deserialize, Serialize};
6
7use super::wire::{Tracks, VideoCodec, VideoHeader, VideoKind};
8
9pub const KIND_FRAME: u8 = 1;
12pub const KIND_CONTROL: u8 = 2;
14pub const KIND_PCM: u8 = 3;
17pub const PCM_HEADER_LEN: usize = 5;
18
19#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
21#[serde(tag = "t", rename_all = "snake_case")]
22pub enum ToLink {
23 Rate { kind: VideoKind, kbps: u32, width: u32, height: u32, fps: u32 },
25 Skip { kind: VideoKind },
27 Keyframe { kind: VideoKind },
29 Peer { tracks: Tracks },
31 Pause { on: bool },
33 Codec { codec: VideoCodec },
35}
36
37#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
39#[serde(tag = "t", rename_all = "snake_case")]
40pub enum FromLink {
41 Caps { encode: Vec<String>, decode: Vec<String> },
43 Stats { kind: VideoKind, fps: u32, kbps: u32, width: u32, height: u32 },
45 Lost { kind: VideoKind },
47 Unsupported { codec: VideoCodec },
49 #[serde(other)]
50 Unknown,
51}
52
53pub enum LinkMsg<'a> {
55 Frame { header: VideoHeader, frame: &'a [u8] },
56 Control(FromLink),
57 Pcm { rate: u32, channels: u8, samples: &'a [u8] },
58}
59
60pub fn parse(msg: &[u8]) -> Option<LinkMsg<'_>> {
63 match msg.first()? {
64 &KIND_FRAME => {
65 let frame = &msg[1..];
66 let header = VideoHeader::parse(frame)?;
67 Some(LinkMsg::Frame { header, frame })
68 }
69 &KIND_CONTROL => serde_json::from_slice(&msg[1..]).ok().map(LinkMsg::Control),
70 &KIND_PCM => {
71 let body = &msg[1..];
72 if body.len() < PCM_HEADER_LEN {
73 return None;
74 }
75 let rate = u32::from_le_bytes([body[0], body[1], body[2], body[3]]);
76 let channels = body[4];
77 if !(1..=2).contains(&channels) || !(8000..=192_000).contains(&rate) {
78 return None;
79 }
80 Some(LinkMsg::Pcm { rate, channels, samples: &body[PCM_HEADER_LEN..] })
81 }
82 _ => None,
83 }
84}
85
86pub fn control(msg: &ToLink) -> Vec<u8> {
88 let mut out = vec![KIND_CONTROL];
89 if let Ok(body) = serde_json::to_vec(msg) {
90 out.extend_from_slice(&body);
91 }
92 out
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use crate::wire::{VIDEO_HEADER_LEN, VIDEO_KEY};
99
100 #[test]
101 fn frames_and_controls_parse_and_garbage_does_not() {
102 let mut msg = vec![KIND_FRAME];
103 let mut hdr = [0u8; VIDEO_HEADER_LEN];
104 VideoHeader { seq: 3, ts_us: 9, flags: VIDEO_KEY, codec: VideoCodec::H264 }.write(&mut hdr);
105 msg.extend_from_slice(&hdr);
106 msg.extend_from_slice(&[7, 7, 7]);
107 match parse(&msg) {
108 Some(LinkMsg::Frame { header, frame }) => {
109 assert_eq!(header.seq, 3);
110 assert_eq!(frame.len(), VIDEO_HEADER_LEN + 3);
111 }
112 _ => panic!("frame expected"),
113 }
114 let c = control(&ToLink::Rate { kind: VideoKind::Camera, kbps: 800, width: 640, height: 360, fps: 30 });
115 assert_eq!(c[0], KIND_CONTROL);
116 let mut lost = vec![KIND_CONTROL];
117 lost.extend_from_slice(br#"{"t":"lost","kind":"screen"}"#);
118 assert!(matches!(parse(&lost), Some(LinkMsg::Control(FromLink::Lost { kind: VideoKind::Screen }))));
119 let mut odd = vec![KIND_CONTROL];
120 odd.extend_from_slice(br#"{"t":"teleport"}"#);
121 assert!(matches!(parse(&odd), Some(LinkMsg::Control(FromLink::Unknown))));
122 assert!(parse(&[9, 9]).is_none());
123 assert!(parse(&[]).is_none());
124 let mut pcm = vec![KIND_PCM];
125 pcm.extend_from_slice(&48_000u32.to_le_bytes());
126 pcm.push(2);
127 pcm.extend_from_slice(&1.0f32.to_le_bytes());
128 match parse(&pcm) {
129 Some(LinkMsg::Pcm { rate, channels, samples }) => assert_eq!((rate, channels, samples.len()), (48_000, 2, 4)),
130 _ => panic!("pcm expected"),
131 }
132 pcm[5] = 7;
133 assert!(parse(&pcm).is_none(), "an impossible channel count is refused");
134 }
135}