1use wall::Frame;
9use std::io::{self, Read, Write};
10
11pub struct RawSource<R> {
13 r: R,
14 width: u32,
15 height: u32,
16 filled: usize,
19}
20
21impl<R: Read> RawSource<R> {
22 pub fn new(r: R, width: u32, height: u32) -> Self {
23 Self {
24 r,
25 width,
26 height,
27 filled: 0,
28 }
29 }
30
31 pub fn read_frame(&mut self, frame: &mut Frame) -> io::Result<bool> {
40 if (frame.width, frame.height) != (self.width, self.height) {
41 *frame = Frame::black(self.width, self.height);
42 self.filled = 0;
43 }
44 let buf = frame.as_bytes_mut();
45 while self.filled < buf.len() {
46 match self.r.read(&mut buf[self.filled..]) {
47 Ok(0) => {
48 self.filled = 0;
49 return Ok(false);
50 }
51 Ok(n) => self.filled += n,
52 Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
53 Err(e) => return Err(e),
54 }
55 }
56 self.filled = 0;
57 Ok(true)
58 }
59}
60
61impl<R: Read> crate::FrameSource for RawSource<R> {
62 fn next_frame(&mut self, frame: &mut Frame) -> anyhow::Result<bool> {
63 self.read_frame(frame).map_err(Into::into)
64 }
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub struct Header {
73 pub width: u16,
74 pub height: u16,
75 pub fps: u16,
76}
77
78impl Header {
79 pub const LEN: usize = 12;
80 const MAGIC: [u8; 4] = *b"RXP\0";
81 const VERSION: u8 = 1;
82
83 #[must_use]
84 pub fn to_bytes(self) -> [u8; Self::LEN] {
85 let mut b = [0u8; Self::LEN];
86 b[..4].copy_from_slice(&Self::MAGIC);
87 b[4] = Self::VERSION;
88 b[6..8].copy_from_slice(&self.width.to_le_bytes());
89 b[8..10].copy_from_slice(&self.height.to_le_bytes());
90 b[10..12].copy_from_slice(&self.fps.to_le_bytes());
91 b
92 }
93
94 pub fn from_bytes(b: &[u8; Self::LEN]) -> io::Result<Self> {
97 if b[..4] != Self::MAGIC {
98 return Err(io::Error::new(
99 io::ErrorKind::InvalidData,
100 "not a receiverproxy stream header",
101 ));
102 }
103 if b[4] != Self::VERSION {
104 return Err(io::Error::new(
105 io::ErrorKind::InvalidData,
106 format!("stream header version {} (want {})", b[4], Self::VERSION),
107 ));
108 }
109 let h = Self {
110 width: u16::from_le_bytes([b[6], b[7]]),
111 height: u16::from_le_bytes([b[8], b[9]]),
112 fps: u16::from_le_bytes([b[10], b[11]]),
113 };
114 if h.width == 0 || h.height == 0 {
115 return Err(io::Error::new(
116 io::ErrorKind::InvalidInput,
117 "zero-sized stream",
118 ));
119 }
120 Ok(h)
121 }
122
123 pub fn write(self, w: &mut impl Write) -> io::Result<()> {
126 w.write_all(&self.to_bytes())
127 }
128
129 pub fn read(r: &mut impl Read) -> io::Result<Self> {
132 let mut b = [0u8; Self::LEN];
133 r.read_exact(&mut b)?;
134 Self::from_bytes(&b)
135 }
136
137 #[must_use]
139 pub fn frame_len(self) -> usize {
140 usize::from(self.width) * usize::from(self.height) * 3
141 }
142}
143
144pub struct Writer<W> {
155 w: W,
156 frame_len: usize,
157}
158
159impl<W: Write> Writer<W> {
160 pub fn new(mut w: W, width: u16, height: u16, fps: u16) -> io::Result<Self> {
165 let header = Header { width, height, fps };
166 header.write(&mut w)?;
167 w.flush()?;
168 Ok(Self {
169 w,
170 frame_len: header.frame_len(),
171 })
172 }
173
174 pub fn frame(&mut self, rgb: &[u8]) -> io::Result<()> {
179 if rgb.len() != self.frame_len {
180 return Err(io::Error::new(
181 io::ErrorKind::InvalidInput,
182 format!("frame is {} bytes, want {}", rgb.len(), self.frame_len),
183 ));
184 }
185 self.w.write_all(rgb)?;
186 self.w.flush()
187 }
188
189 pub fn into_inner(self) -> W {
190 self.w
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use crate::{pattern, Pattern};
198 use std::io::Cursor;
199
200 #[test]
201 fn frames_are_read_back_to_back_and_a_short_tail_is_dropped() {
202 let (w, h) = (4, 2);
203 let a = pattern(Pattern::Rgb, w, h);
204 let b = pattern(Pattern::Gradient, w, h);
205 let c = pattern(Pattern::White, w, h);
206 let mut bytes = a.as_bytes().to_vec();
207 bytes.extend_from_slice(b.as_bytes());
208 bytes.extend_from_slice(c.as_bytes());
209 bytes.extend_from_slice(&[7; 5]);
210 let mut src = RawSource::new(Cursor::new(bytes), w, h);
211 let mut f = Frame::black(1, 1);
213 for want in [&a, &b, &c] {
214 assert!(src.read_frame(&mut f).unwrap());
215 assert_eq!(&f, want);
216 }
217 assert!(!src.read_frame(&mut f).unwrap());
218 assert!(!src.read_frame(&mut f).unwrap());
219 }
220
221 struct Dribble {
223 data: Vec<u8>,
224 at: usize,
225 timeouts: usize,
226 }
227
228 impl Read for Dribble {
229 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
230 if self.at == 3 && self.timeouts > 0 {
231 self.timeouts -= 1;
232 return Err(io::ErrorKind::TimedOut.into());
233 }
234 if self.at == self.data.len() || buf.is_empty() {
235 return Ok(0);
236 }
237 buf[0] = self.data[self.at];
238 self.at += 1;
239 Ok(1)
240 }
241 }
242
243 #[test]
244 fn a_timeout_mid_frame_resumes_on_the_next_call() {
245 let a = pattern(Pattern::Gradient, 2, 2);
246 let mut src = RawSource::new(
247 Dribble {
248 data: a.as_bytes().to_vec(),
249 at: 0,
250 timeouts: 1,
251 },
252 2,
253 2,
254 );
255 let mut f = Frame::black(2, 2);
256 assert_eq!(
257 src.read_frame(&mut f).unwrap_err().kind(),
258 io::ErrorKind::TimedOut
259 );
260 assert!(src.read_frame(&mut f).unwrap());
261 assert_eq!(f, a);
262 }
263
264 #[test]
265 fn header_round_trips_and_rejects_strangers() {
266 let h = Header {
267 width: 128,
268 height: 64,
269 fps: 30,
270 };
271 let b = h.to_bytes();
272 assert_eq!(&b, b"RXP\0\x01\x00\x80\x00\x40\x00\x1e\x00");
273 assert_eq!(Header::read(&mut Cursor::new(b)).unwrap(), h);
274 assert_eq!(h.frame_len(), 128 * 64 * 3);
275
276 let mut bad = b;
277 bad[0] = b'X';
278 assert_eq!(
279 Header::from_bytes(&bad).unwrap_err().kind(),
280 io::ErrorKind::InvalidData
281 );
282 let mut v2 = b;
283 v2[4] = 2;
284 assert!(Header::from_bytes(&v2).is_err());
285 let mut zero = b;
286 zero[6..8].fill(0);
287 assert_eq!(
288 Header::from_bytes(&zero).unwrap_err().kind(),
289 io::ErrorKind::InvalidInput
290 );
291 assert!(Header::read(&mut Cursor::new(&b[..5])).is_err());
292 }
293
294 #[test]
295 fn writer_output_reads_back_through_a_raw_source() {
296 let (w, h) = (6, 3);
297 let a = pattern(Pattern::Border, w, h);
298 let b = pattern(Pattern::Rows, w, h);
299 let mut writer = Writer::new(Vec::new(), w as u16, h as u16, 25).unwrap();
300 writer.frame(a.as_bytes()).unwrap();
301 writer.frame(b.as_bytes()).unwrap();
302 assert_eq!(
303 writer.frame(&[0; 4]).unwrap_err().kind(),
304 io::ErrorKind::InvalidInput
305 );
306 let bytes = writer.into_inner();
307 assert_eq!(bytes.len(), Header::LEN + 2 * (w * h * 3) as usize);
308
309 let mut r = Cursor::new(bytes);
310 let header = Header::read(&mut r).unwrap();
311 assert_eq!((header.width, header.height, header.fps), (6, 3, 25));
312 let mut src = RawSource::new(r, u32::from(header.width), u32::from(header.height));
313 let mut f = Frame::black(w, h);
314 assert!(src.read_frame(&mut f).unwrap());
315 assert_eq!(f, a);
316 assert!(src.read_frame(&mut f).unwrap());
317 assert_eq!(f, b);
318 assert!(!src.read_frame(&mut f).unwrap());
319 }
320}