1use std::collections::VecDeque;
5use std::future::Future;
6use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
7use std::sync::mpsc::Sender;
8use std::sync::{Arc, Mutex, PoisonError};
9use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
10
11use jpeg_encoder::{ColorType, Encoder};
12use mirror::backend::{BackendError, EncodeRequest, FrameMeta, FrameSource, StreamItem, Timed};
13use protocol::code;
14use protocol::message::Rotation;
15
16use crate::emulator::{
17 self, BYTES_PER_PIXEL, Controller, Endpoint, Frame, FrameHasher, Options, Unavailable,
18};
19
20pub const INPUT_QUIET: Duration = Duration::from_millis(200);
24
25pub(crate) const KEPT_FRAMES: usize = 4;
27
28const MAX_LAG: Duration = Duration::from_secs(1);
31
32const FRAME_WAIT: Duration = Duration::from_millis(250);
35
36const RESUBSCRIBE_AFTER: Duration = Duration::from_millis(500);
39
40pub struct ControllerLink {
42 runtime: tokio::runtime::Runtime,
43 controller: Controller,
44 endpoint: String,
45}
46
47impl std::fmt::Debug for ControllerLink {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("ControllerLink")
50 .field("controller", &self.controller)
51 .finish_non_exhaustive()
52 }
53}
54
55impl ControllerLink {
56 pub fn connect(endpoint: &Endpoint, options: Options) -> Result<ControllerLink, Unavailable> {
58 let runtime = tokio::runtime::Builder::new_multi_thread()
59 .worker_threads(2)
60 .thread_name("android-controller")
61 .enable_all()
62 .build()
63 .map_err(|e| {
64 Unavailable::new(
65 &endpoint.serial,
66 endpoint.console_port,
67 format!("cannot start the controller's runtime: {e}"),
68 )
69 })?;
70 let controller = runtime.block_on(Controller::connect(endpoint, options))?;
71 Ok(ControllerLink {
72 runtime,
73 controller,
74 endpoint: endpoint.uri.clone(),
75 })
76 }
77
78 pub fn controller(&self) -> &Controller {
79 &self.controller
80 }
81
82 pub fn endpoint(&self) -> &str {
84 &self.endpoint
85 }
86
87 pub fn keyboard(&self) -> bool {
89 self.controller.keys_supported()
90 }
91
92 pub(crate) fn block_on<F: Future>(&self, future: F) -> F::Output {
94 self.runtime.block_on(future)
95 }
96
97 pub(crate) fn start_frames(&self, width: u32, pump: FramePump) {
100 let controller = self.controller.clone();
101 self.runtime.spawn(pump.run(controller, width));
102 }
103}
104
105pub fn frame_meta(
109 frame: &Frame,
110 seq: u64,
111 hasher: &FrameHasher,
112 arrived: Instant,
113 now_epoch_us: u64,
114) -> FrameMeta {
115 let lag = match frame.timestamp_us() {
116 0 => Duration::ZERO,
117 produced => Duration::from_micros(now_epoch_us.saturating_sub(produced)).min(MAX_LAG),
118 };
119 FrameMeta {
120 seq,
121 at: arrived.checked_sub(lag).unwrap_or(arrived),
122 hash: hasher.key(frame).hash,
123 width: frame.width(),
124 height: frame.height(),
125 rotation: Rotation::R0,
126 }
127}
128
129fn epoch_us() -> u64 {
131 SystemTime::now()
132 .duration_since(UNIX_EPOCH)
133 .map_or(0, |d| u64::try_from(d.as_micros()).unwrap_or(u64::MAX))
134}
135
136pub fn rotate_rgb(
139 pixels: &[u8],
140 width: u32,
141 height: u32,
142 rotation: Rotation,
143) -> (Vec<u8>, u32, u32) {
144 if !matches!(rotation, Rotation::R90 | Rotation::R180 | Rotation::R270) {
145 return (pixels.to_vec(), width, height);
146 }
147 let (w, h) = (width as usize, height as usize);
148 let (out_w, out_h) = if rotation == Rotation::R180 {
149 (w, h)
150 } else {
151 (h, w)
152 };
153 let mut out = vec![0u8; out_w * out_h * BYTES_PER_PIXEL];
154 for y in 0..out_h {
155 for x in 0..out_w {
156 let (sx, sy) = match rotation {
157 Rotation::R90 => (y, h - 1 - x),
158 Rotation::R180 => (w - 1 - x, h - 1 - y),
159 _ => (w - 1 - y, x),
160 };
161 let from = (sy * w + sx) * BYTES_PER_PIXEL;
162 let to = (y * out_w + x) * BYTES_PER_PIXEL;
163 out[to..to + BYTES_PER_PIXEL].copy_from_slice(&pixels[from..from + BYTES_PER_PIXEL]);
164 }
165 }
166 (out, out_w as u32, out_h as u32)
167}
168
169fn span(o: u32, from: u32, to: u32) -> (usize, usize) {
172 let (o, from, to) = (u64::from(o), u64::from(from), u64::from(to));
173 let start = o * from / to;
174 let end = ((o + 1) * from).div_ceil(to).max(start + 1).min(from);
175 (start as usize, end as usize)
176}
177
178pub fn resize_rgb(
182 pixels: &[u8],
183 width: u32,
184 height: u32,
185 to_width: u32,
186 to_height: u32,
187) -> Vec<u8> {
188 let (w, to_w) = (width as usize, to_width as usize);
189 let mut out = vec![0u8; to_w * to_height as usize * BYTES_PER_PIXEL];
190 let columns: Vec<(usize, usize)> = (0..to_width).map(|x| span(x, width, to_width)).collect();
191 for oy in 0..to_height {
192 let (y0, y1) = span(oy, height, to_height);
193 for (ox, &(x0, x1)) in columns.iter().enumerate() {
194 let mut sum = [0u64; BYTES_PER_PIXEL];
195 for y in y0..y1 {
196 let row = &pixels[(y * w + x0) * BYTES_PER_PIXEL..(y * w + x1) * BYTES_PER_PIXEL];
197 for px in row.as_chunks::<BYTES_PER_PIXEL>().0 {
198 for (total, &channel) in sum.iter_mut().zip(px) {
199 *total += u64::from(channel);
200 }
201 }
202 }
203 let count = ((y1 - y0) * (x1 - x0)) as u64;
204 let at = (oy as usize * to_w + ox) * BYTES_PER_PIXEL;
205 for (c, total) in sum.iter().enumerate() {
206 out[at + c] = ((total + count / 2) / count) as u8;
207 }
208 }
209 }
210 out
211}
212
213pub fn encode_jpeg(frame: &Frame, request: &EncodeRequest) -> Result<Vec<u8>, BackendError> {
216 let edge = |pixels: u32| u16::try_from(pixels).ok().filter(|&e| e > 0);
217 let (Some(width), Some(height)) = (edge(request.width), edge(request.height)) else {
218 return Err(BackendError::new(
219 code::INVALID_PARAMS,
220 format!(
221 "a JPEG of {}x{} pixels cannot be encoded",
222 request.width, request.height
223 ),
224 ));
225 };
226 let (turned, w, h) = rotate_rgb(
227 frame.pixels(),
228 frame.width(),
229 frame.height(),
230 request.rotation,
231 );
232 let pixels = if (w, h) == (request.width, request.height) {
233 turned
234 } else {
235 resize_rgb(&turned, w, h, request.width, request.height)
236 };
237 let mut out = Vec::new();
238 Encoder::new(&mut out, request.quality.clamp(1, 100))
239 .encode(&pixels, width, height, ColorType::Rgb)
240 .map_err(|e| BackendError::new(code::INTERNAL, format!("JPEG encoding failed: {e}")))?;
241 Ok(out)
242}
243
244#[derive(Default)]
246pub(crate) struct FrameStore {
247 frames: VecDeque<(FrameMeta, Frame)>,
248}
249
250impl FrameStore {
251 pub(crate) fn push(&mut self, meta: FrameMeta, frame: Frame) {
252 self.frames.push_back((meta, frame));
253 while self.frames.len() > KEPT_FRAMES {
254 self.frames.pop_front();
255 }
256 }
257
258 pub(crate) fn latest(&self) -> Option<FrameMeta> {
259 self.frames.back().map(|(meta, _)| *meta)
260 }
261
262 pub(crate) fn get(&self, seq: u64) -> Option<&Frame> {
263 self.frames
264 .iter()
265 .find(|(meta, _)| meta.seq == seq)
266 .map(|(_, frame)| frame)
267 }
268}
269
270#[derive(Clone)]
272pub(crate) struct FramePump {
273 pub(crate) store: Arc<Mutex<FrameStore>>,
274 pub(crate) items: Sender<Timed<StreamItem>>,
275 pub(crate) received: Arc<AtomicU64>,
276 pub(crate) stop: Arc<AtomicBool>,
277 pub(crate) error: Arc<Mutex<Option<String>>>,
279}
280
281impl FramePump {
282 async fn run(self, controller: Controller, width: u32) {
283 let hasher = FrameHasher::default();
284 let mut seq = 0u64;
285 while !self.stop.load(Ordering::SeqCst) {
286 let mut stream = match controller.frames(width).await {
287 Ok(stream) => stream,
288 Err(error) => {
289 self.fail(&error);
290 tokio::time::sleep(RESUBSCRIBE_AFTER).await;
291 continue;
292 }
293 };
294 while !self.stop.load(Ordering::SeqCst) {
295 match stream.next(FRAME_WAIT).await {
296 Ok(Some(frame)) => {
297 seq += 1;
298 let arrived = Instant::now();
299 let meta = frame_meta(&frame, seq, &hasher, arrived, epoch_us());
300 self.store
301 .lock()
302 .unwrap_or_else(PoisonError::into_inner)
303 .push(meta, frame);
304 self.received.fetch_add(1, Ordering::SeqCst);
305 let item = Timed {
306 at: arrived,
307 item: StreamItem::Frame(meta),
308 };
309 if self.items.send(item).is_err() {
310 return;
311 }
312 }
313 Ok(None) => {}
314 Err(emulator::Error::Frame { .. }) => {}
316 Err(error) => {
317 self.fail(&error);
318 break;
319 }
320 }
321 }
322 tokio::time::sleep(RESUBSCRIBE_AFTER).await;
323 }
324 }
325
326 fn fail(&self, error: &emulator::Error) {
327 *self.error.lock().unwrap_or_else(PoisonError::into_inner) = Some(error.to_string());
328 }
329}
330
331pub(crate) struct ControllerFrames {
333 pub(crate) store: Arc<Mutex<FrameStore>>,
334}
335
336impl FrameSource for ControllerFrames {
337 fn latest(&self) -> Option<FrameMeta> {
338 self.store
339 .lock()
340 .unwrap_or_else(PoisonError::into_inner)
341 .latest()
342 }
343
344 fn encode(&mut self, request: &EncodeRequest) -> Result<Vec<u8>, BackendError> {
345 let frame = self
346 .store
347 .lock()
348 .unwrap_or_else(PoisonError::into_inner)
349 .get(request.seq)
350 .cloned()
351 .ok_or_else(|| BackendError {
352 code: "no_frame".into(),
353 message: format!(
354 "frame {} is no longer held; ask for the latest frame again",
355 request.seq
356 ),
357 retryable: true,
358 })?;
359 encode_jpeg(&frame, request)
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366 use prost::bytes::Bytes;
367
368 fn frame(width: u32, height: u32, fill: impl Fn(u32, u32) -> [u8; 3]) -> Frame {
369 let mut pixels = Vec::with_capacity((width * height * 3) as usize);
370 for y in 0..height {
371 for x in 0..width {
372 pixels.extend_from_slice(&fill(x, y));
373 }
374 }
375 Frame::new(width, height, Bytes::from(pixels)).unwrap()
376 }
377
378 fn pixel(pixels: &[u8], width: u32, x: u32, y: u32) -> [u8; 3] {
379 let at = ((y * width + x) * 3) as usize;
380 [pixels[at], pixels[at + 1], pixels[at + 2]]
381 }
382
383 #[test]
384 fn frame_hashes_leave_the_status_bar_out() {
385 let hasher = FrameHasher::default();
386 let now = Instant::now();
387 let base = frame(10, 100, |_, _| [9, 9, 9]);
388 let clock = frame(10, 100, |x, y| {
389 if y < 3 && x == 1 {
390 [200, 0, 0]
391 } else {
392 [9, 9, 9]
393 }
394 });
395 let content = frame(10, 100, |x, y| {
396 if y == 50 && x == 1 {
397 [200, 0, 0]
398 } else {
399 [9, 9, 9]
400 }
401 });
402 let a = frame_meta(&base, 1, &hasher, now, 0);
403 let b = frame_meta(&clock, 2, &hasher, now, 0);
404 let c = frame_meta(&content, 3, &hasher, now, 0);
405 assert_eq!(a.hash, b.hash, "a status bar change is no change");
406 assert_ne!(a.hash, c.hash);
407 assert_eq!((c.seq, c.width, c.height), (3, 10, 100));
408 assert_eq!(c.rotation, Rotation::R0);
409 }
410
411 #[test]
412 fn the_capture_time_is_the_arrival_minus_the_emulators_lag() {
413 let hasher = FrameHasher::default();
414 let arrived = Instant::now();
415 let produced = frame(2, 2, |_, _| [0, 0, 0]).with_timing(7, 1_000_000);
416 let meta = frame_meta(&produced, 1, &hasher, arrived, 1_015_000);
417 assert_eq!(arrived - meta.at, Duration::from_millis(15));
418 assert_eq!(
421 frame_meta(&produced, 1, &hasher, arrived, 900_000).at,
422 arrived
423 );
424 let untimed = frame(2, 2, |_, _| [0, 0, 0]);
425 assert_eq!(
426 frame_meta(&untimed, 1, &hasher, arrived, 5_000_000).at,
427 arrived
428 );
429 let late = frame_meta(&produced, 1, &hasher, arrived, 9_000_000);
430 assert_eq!(arrived - late.at, Duration::from_secs(1));
431 }
432
433 fn letters() -> Vec<u8> {
435 b"ABCDEF".iter().flat_map(|&c| [c, c, c]).collect()
436 }
437
438 fn rows(pixels: &[u8], width: u32, height: u32) -> Vec<String> {
439 (0..height)
440 .map(|y| {
441 (0..width)
442 .map(|x| pixel(pixels, width, x, y)[0] as char)
443 .collect()
444 })
445 .collect()
446 }
447
448 #[test]
449 fn rotation_turns_the_pixels_clockwise() {
450 let (r0, w, h) = rotate_rgb(&letters(), 3, 2, Rotation::R0);
451 assert_eq!(rows(&r0, w, h), ["ABC", "DEF"]);
452 let (r90, w, h) = rotate_rgb(&letters(), 3, 2, Rotation::R90);
453 assert_eq!((w, h), (2, 3));
454 assert_eq!(rows(&r90, w, h), ["DA", "EB", "FC"]);
455 let (r180, w, h) = rotate_rgb(&letters(), 3, 2, Rotation::R180);
456 assert_eq!(rows(&r180, w, h), ["FED", "CBA"]);
457 let (r270, w, h) = rotate_rgb(&letters(), 3, 2, Rotation::R270);
458 assert_eq!(rows(&r270, w, h), ["CF", "BE", "AD"]);
459 }
460
461 #[test]
462 fn shrinking_averages_and_enlarging_repeats() {
463 let halves = frame(4, 2, |x, _| if x < 2 { [200, 0, 0] } else { [0, 0, 100] });
465 let small = resize_rgb(halves.pixels(), 4, 2, 2, 1);
466 assert_eq!(small, [200, 0, 0, 0, 0, 100]);
467 let checker = [0, 0, 0, 255, 255, 255];
469 assert_eq!(resize_rgb(&checker, 2, 1, 1, 1), [128, 128, 128]);
470 let one = [10, 20, 30];
471 assert_eq!(resize_rgb(&one, 1, 1, 2, 2), [10, 20, 30].repeat(4));
472 }
473
474 fn jpeg_size(data: &[u8]) -> (u32, u32) {
476 assert_eq!(&data[..2], [0xFF, 0xD8], "no start of image");
477 assert_eq!(&data[data.len() - 2..], [0xFF, 0xD9], "no end of image");
478 let mut at = 2;
479 while at + 9 < data.len() {
480 assert_eq!(data[at], 0xFF);
481 let marker = data[at + 1];
482 let len = u16::from_be_bytes([data[at + 2], data[at + 3]]) as usize;
483 if (0xC0..=0xC2).contains(&marker) {
484 let h = u16::from_be_bytes([data[at + 5], data[at + 6]]);
485 let w = u16::from_be_bytes([data[at + 7], data[at + 8]]);
486 return (u32::from(w), u32::from(h));
487 }
488 at += 2 + len;
489 }
490 panic!("no start of frame");
491 }
492
493 #[test]
494 fn frames_encode_as_jpeg_turned_and_scaled_to_the_request() {
495 let wide = frame(160, 80, |x, y| [(x % 256) as u8, (y % 256) as u8, 7]);
496 let request = EncodeRequest {
497 seq: 1,
498 width: 40,
499 height: 80,
500 quality: 80,
501 rotation: Rotation::R90,
502 };
503 let data = encode_jpeg(&wide, &request).unwrap();
504 assert_eq!(jpeg_size(&data), (40, 80));
505 }
506
507 #[test]
508 fn the_store_keeps_the_latest_frames_by_sequence() {
509 let hasher = FrameHasher::default();
510 let now = Instant::now();
511 let mut store = FrameStore::default();
512 assert_eq!(store.latest(), None);
513 for seq in 1..=6u64 {
514 let f = frame(2, 2, |_, _| [seq as u8, 0, 0]);
515 store.push(frame_meta(&f, seq, &hasher, now, 0), f);
516 }
517 assert_eq!(store.latest().map(|m| m.seq), Some(6));
518 assert!(store.get(6).is_some());
519 assert!(store.get(3).is_some());
520 assert!(store.get(2).is_none(), "only {KEPT_FRAMES} frames are kept");
521 assert_eq!(store.get(5).unwrap().pixels()[0], 5);
522 }
523
524 #[test]
529 #[ignore = "a measurement; run with --release --ignored --nocapture on a quiet machine"]
530 fn measure_the_rgb_resize() {
531 use std::hint::black_box;
532 const WARMUP: usize = 10;
533 const RUNS: usize = 200;
534 let (width, height, to_width, to_height) = (1080, 2400, 450, 1000);
535 let source = frame(width, height, |x, y| {
536 let v = (x * 31 + y * 17 + (x * y) % 97) as u8;
537 [v, v.wrapping_mul(3), v ^ 0x5A]
538 });
539 let mut samples = Vec::with_capacity(RUNS);
540 let mut small = Vec::new();
541 for run in 0..WARMUP + RUNS {
542 let started = Instant::now();
543 small = resize_rgb(
544 black_box(source.pixels()),
545 width,
546 height,
547 to_width,
548 to_height,
549 );
550 let elapsed = started.elapsed();
551 if run >= WARMUP {
552 samples.push(elapsed.as_secs_f64() * 1e3);
553 }
554 }
555 samples.sort_by(f64::total_cmp);
556 let percentile = |percent: usize| samples[(samples.len() * percent).div_ceil(100) - 1];
557 let checksum = small.iter().fold(0xcbf2_9ce4_8422_2325_u64, |hash, &byte| {
558 (hash ^ u64::from(byte)).wrapping_mul(0x0000_0100_0000_01b3)
559 });
560 eprintln!(
561 "resize_rgb {width}x{height} to {to_width}x{to_height}, {RUNS} runs: p50 {:.3} ms, p95 {:.3} ms, checksum {checksum:016x}",
562 percentile(50),
563 percentile(95)
564 );
565 }
566}