stream screen to display
This commit is contained in:
parent
9e299f69f6
commit
ef19ab8b3f
8 changed files with 1775 additions and 15 deletions
11
src/cli.rs
11
src/cli.rs
|
@ -1,3 +1,5 @@
|
|||
use crate::stream_window::StreamScreenOptions;
|
||||
|
||||
#[derive(clap::Parser, std::fmt::Debug)]
|
||||
#[clap(version, arg_required_else_help = true)]
|
||||
pub struct Cli {
|
||||
|
@ -38,8 +40,12 @@ pub enum Mode {
|
|||
},
|
||||
StreamStdin {
|
||||
#[arg(long, short, default_value_t = false)]
|
||||
slow: bool
|
||||
}
|
||||
slow: bool,
|
||||
},
|
||||
StreamScreen {
|
||||
#[command(flatten)]
|
||||
options: StreamScreenOptions,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(clap::Parser, std::fmt::Debug)]
|
||||
|
@ -66,3 +72,4 @@ pub enum Protocol {
|
|||
WebSocket,
|
||||
Fake,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::cli::{BrightnessCommand, Mode, PixelCommand};
|
||||
use crate::stream_stdin::stream_stdin;
|
||||
use crate::stream_window::stream_window;
|
||||
use log::info;
|
||||
use servicepoint::{Brightness, Command, Connection};
|
||||
use crate::stream_stdin::stream_stdin;
|
||||
|
||||
pub fn execute_mode(mode: Mode, connection: Connection) {
|
||||
match mode {
|
||||
|
@ -11,7 +12,8 @@ pub fn execute_mode(mode: Mode, connection: Connection) {
|
|||
}
|
||||
Mode::Pixels { pixel_command } => pixels(&connection, pixel_command),
|
||||
Mode::Brightness { brightness_command } => brightness(&connection, brightness_command),
|
||||
Mode::StreamStdin{slow} => stream_stdin(&connection, slow),
|
||||
Mode::StreamStdin { slow } => stream_stdin(&connection, slow),
|
||||
Mode::StreamScreen { options } => stream_window(&connection, options),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use servicepoint::Connection;
|
|||
mod cli;
|
||||
mod execute;
|
||||
mod stream_stdin;
|
||||
mod stream_window;
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::thread::sleep;
|
||||
use log::warn;
|
||||
use servicepoint::*;
|
||||
use std::thread::sleep;
|
||||
|
||||
pub(crate) fn stream_stdin(connection: &Connection, slow: bool) {
|
||||
warn!("This mode will break when using multi-byte characters and does not support ANSI escape sequences yet.");
|
||||
|
@ -8,7 +8,7 @@ pub(crate) fn stream_stdin(connection: &Connection, slow: bool) {
|
|||
connection,
|
||||
mirror: CharGrid::new(TILE_WIDTH, TILE_HEIGHT),
|
||||
y: 0,
|
||||
slow
|
||||
slow,
|
||||
};
|
||||
app.run()
|
||||
}
|
||||
|
|
115
src/stream_window.rs
Normal file
115
src/stream_window.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
use image::{
|
||||
imageops::{dither, resize, BiLevel, FilterType},
|
||||
DynamicImage, ImageBuffer, Rgb, Rgba,
|
||||
};
|
||||
use log::{debug, error, warn};
|
||||
use scap::{
|
||||
capturer::{Capturer, Options},
|
||||
frame::convert_bgra_to_rgb,
|
||||
frame::Frame,
|
||||
};
|
||||
use servicepoint::{
|
||||
Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH,
|
||||
};
|
||||
use std::ops::Div;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(clap::Parser, std::fmt::Debug, Clone)]
|
||||
pub struct StreamScreenOptions {
|
||||
#[arg(long, short, default_value_t = true)]
|
||||
pub dither: bool,
|
||||
}
|
||||
|
||||
pub fn stream_window(connection: &Connection, options: StreamScreenOptions) {
|
||||
let capturer = match start_capture() {
|
||||
Some(value) => value,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let mut bitmap = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
|
||||
loop {
|
||||
let frame = capturer.get_next_frame().expect("failed to capture frame");
|
||||
let frame = frame_to_image(frame);
|
||||
let frame = frame.grayscale().to_luma8();
|
||||
let mut frame = resize(
|
||||
&frame,
|
||||
PIXEL_WIDTH as u32,
|
||||
PIXEL_HEIGHT as u32,
|
||||
FilterType::Nearest,
|
||||
);
|
||||
|
||||
if options.dither {
|
||||
dither(&mut frame, &BiLevel);
|
||||
}
|
||||
|
||||
for (mut dest, src) in bitmap.iter_mut().zip(frame.pixels()) {
|
||||
*dest = src.0[0] > u8::MAX / 2;
|
||||
}
|
||||
|
||||
connection
|
||||
.send(Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
bitmap.clone(),
|
||||
CompressionCode::Uncompressed,
|
||||
))
|
||||
.expect("failed to send frame to display");
|
||||
}
|
||||
}
|
||||
|
||||
fn start_capture() -> Option<Capturer> {
|
||||
if !scap::is_supported() {
|
||||
error!("platform not supported by scap");
|
||||
return None;
|
||||
}
|
||||
|
||||
if !scap::has_permission() {
|
||||
warn!("requesting screen recording permission");
|
||||
if !scap::request_permission() {
|
||||
error!("screen recording ermission denied");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let mut capturer = Capturer::build(Options {
|
||||
fps: FRAME_PACING
|
||||
.div_duration_f32(Duration::from_secs(1))
|
||||
.div(2f32) as u32,
|
||||
target: None,
|
||||
show_cursor: true,
|
||||
show_highlight: true,
|
||||
excluded_targets: None,
|
||||
output_type: scap::frame::FrameType::BGR0,
|
||||
..Default::default()
|
||||
})
|
||||
.expect("failed to create screen capture");
|
||||
capturer.start_capture();
|
||||
Some(capturer)
|
||||
}
|
||||
|
||||
fn frame_to_image(frame: Frame) -> DynamicImage {
|
||||
match frame {
|
||||
Frame::BGRx(frame) => bgrx_to_rgb(frame.width, frame.height, frame.data),
|
||||
Frame::RGBx(frame) => DynamicImage::from(
|
||||
ImageBuffer::<Rgba<_>, _>::from_raw(
|
||||
frame.width as u32,
|
||||
frame.height as u32,
|
||||
frame.data,
|
||||
)
|
||||
.unwrap(),
|
||||
),
|
||||
Frame::BGR0(frame) => bgrx_to_rgb(frame.width, frame.height, frame.data),
|
||||
Frame::RGB(frame) => DynamicImage::from(
|
||||
ImageBuffer::<Rgb<_>, _>::from_raw(frame.width as u32, frame.height as u32, frame.data)
|
||||
.unwrap(),
|
||||
),
|
||||
Frame::BGRA(frame) => bgrx_to_rgb(frame.width, frame.height, frame.data),
|
||||
Frame::YUVFrame(_) | Frame::XBGR(_) => panic!("unsupported frame format"),
|
||||
}
|
||||
}
|
||||
|
||||
fn bgrx_to_rgb(width: i32, height: i32, data: Vec<u8>) -> DynamicImage {
|
||||
DynamicImage::from(
|
||||
ImageBuffer::<Rgb<_>, _>::from_raw(width as u32, height as u32, convert_bgra_to_rgb(data))
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue