Add restream Rust executable
This commit is contained in:
		
							
								
								
									
										14
									
								
								.cargo/config
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								.cargo/config
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | [target.armv7-unknown-linux-gnueabihf] | ||||||
|  | linker = "/usr/local/oecore-x86_64/sysroots/x86_64-oesdk-linux/usr/bin/arm-oe-linux-gnueabi/arm-oe-linux-gnueabi-gcc" | ||||||
|  | rustflags = [ | ||||||
|  |   "-C", "link-arg=-march=armv7-a", | ||||||
|  |   "-C", "link-arg=-marm", | ||||||
|  |   "-C", "link-arg=-mfpu=neon", | ||||||
|  |   "-C", "link-arg=-mfloat-abi=hard", | ||||||
|  |   "-C", "link-arg=-mcpu=cortex-a9", | ||||||
|  |   "-C", "link-arg=--sysroot=/usr/local/oecore-x86_64/sysroots/cortexa9hf-neon-oe-linux-gnueabi", | ||||||
|  | ] | ||||||
|  |  | ||||||
|  | [build] | ||||||
|  | # Set the default --target flag | ||||||
|  | target = "armv7-unknown-linux-gnueabihf" | ||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | /target | ||||||
							
								
								
									
										9
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | [package] | ||||||
|  | name = "restream" | ||||||
|  | version = "0.1.0" | ||||||
|  | authors = ["Rien Maertens <rien.maertens@posteo.be>"] | ||||||
|  | edition = "2018" | ||||||
|  |  | ||||||
|  | [dependencies] | ||||||
|  | anyhow = "1.0" | ||||||
|  | lz-fear = "0.1" | ||||||
							
								
								
									
										133
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,133 @@ | |||||||
|  | #[macro_use] | ||||||
|  | extern crate anyhow; | ||||||
|  | extern crate lz_fear; | ||||||
|  |  | ||||||
|  | use anyhow::{Context, Result}; | ||||||
|  | use lz_fear::CompressionSettings; | ||||||
|  |  | ||||||
|  | use std::default::Default; | ||||||
|  | use std::fs::File; | ||||||
|  | use std::io::{BufRead, BufReader, Read, Seek, SeekFrom}; | ||||||
|  | use std::process::Command; | ||||||
|  |  | ||||||
|  | fn main() -> Result<()> { | ||||||
|  |     let version = remarkable_version()?; | ||||||
|  |     let streamer = if version == "reMarkable 1.0\n" { | ||||||
|  |         let width = 1408; | ||||||
|  |         let height = 1872; | ||||||
|  |         let bytes_per_pixel = 2; | ||||||
|  |         ReStreamer::init("/dev/fb0", 0, width, height, bytes_per_pixel)? | ||||||
|  |     } else if version == "reMarkable 2.0\n" { | ||||||
|  |         let width = 1404; | ||||||
|  |         let height = 1872; | ||||||
|  |         let bytes_per_pixel = 1; | ||||||
|  |  | ||||||
|  |         let pid = xochitl_pid()?; | ||||||
|  |         let offset = fb_start(pid)?; | ||||||
|  |         let mem = format!("/proc/{}/mem", pid); | ||||||
|  |         ReStreamer::init(&mem, offset, width, height, bytes_per_pixel)? | ||||||
|  |     } else { | ||||||
|  |         Err(anyhow!( | ||||||
|  |             "Unknown reMarkable version: {}\nPlease open a feature request to support your device.", | ||||||
|  |             version | ||||||
|  |         ))? | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     let lz4: CompressionSettings = Default::default(); | ||||||
|  |     lz4.compress(streamer, std::io::stdout().lock()) | ||||||
|  |         .context("Error while compressing framebuffer stream") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn remarkable_version() -> Result<String> { | ||||||
|  |     let content = std::fs::read("/sys/devices/soc0/machine") | ||||||
|  |         .context("Failed to read /sys/devices/soc0/machine")?; | ||||||
|  |     Ok(String::from_utf8(content)?) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn xochitl_pid() -> Result<usize> { | ||||||
|  |     let output = Command::new("/bin/pidof") | ||||||
|  |         .args(&["xochitl"]) | ||||||
|  |         .output() | ||||||
|  |         .context("Failed to run `/bin/pidof xochitl`")?; | ||||||
|  |     if output.status.success() { | ||||||
|  |         let pid = &output.stdout; | ||||||
|  |         std::str::from_utf8(pid)? | ||||||
|  |             .parse() | ||||||
|  |             .with_context(|| format!("Failed to parse xochitl's pid: {:?}", pid)) | ||||||
|  |     } else { | ||||||
|  |         Err(anyhow!( | ||||||
|  |             "Could not find pid of xochitl, is xochitl running?" | ||||||
|  |         )) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn fb_start(pid: usize) -> Result<usize> { | ||||||
|  |     let file = File::open(format!("/proc/{}/maps", &pid))?; | ||||||
|  |     let line = BufReader::new(file) | ||||||
|  |         .lines() | ||||||
|  |         .skip_while(|line| matches!(line, Ok(l) if l.ends_with("/dev/fb0"))) | ||||||
|  |         //.skip(1) | ||||||
|  |         .next() | ||||||
|  |         .with_context(|| format!("No line containing /dev/fb0 in /proc/{}/maps file", pid))? | ||||||
|  |         .with_context(|| format!("Error reading file /proc/{}/maps", pid))?; | ||||||
|  |  | ||||||
|  |     let addr = line | ||||||
|  |         .split("-") | ||||||
|  |         .next() | ||||||
|  |         .with_context(|| format!("Error parsing line in /proc/{}/maps", pid))?; | ||||||
|  |  | ||||||
|  |     usize::from_str_radix(addr, 16).context("Error parsing framebuffer address") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub struct ReStreamer { | ||||||
|  |     file: File, | ||||||
|  |     start: u64, | ||||||
|  |     cursor: usize, | ||||||
|  |     size: usize, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl ReStreamer { | ||||||
|  |     pub fn init( | ||||||
|  |         path: &str, | ||||||
|  |         offset: usize, | ||||||
|  |         width: usize, | ||||||
|  |         height: usize, | ||||||
|  |         bytes_per_pixel: usize, | ||||||
|  |     ) -> Result<ReStreamer> { | ||||||
|  |         let start = offset as u64; | ||||||
|  |         let size = width * height * bytes_per_pixel; | ||||||
|  |         let cursor = 0; | ||||||
|  |         let file = File::open(path)?; | ||||||
|  |         let mut streamer = ReStreamer { | ||||||
|  |             file, | ||||||
|  |             start: start, | ||||||
|  |             cursor, | ||||||
|  |             size, | ||||||
|  |         }; | ||||||
|  |         streamer.next_frame()?; | ||||||
|  |         Ok(streamer) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn next_frame(&mut self) -> std::io::Result<()> { | ||||||
|  |         self.file.seek(SeekFrom::Start(self.start))?; | ||||||
|  |         self.cursor = 0; | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Read for ReStreamer { | ||||||
|  |     fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { | ||||||
|  |         let requested = buf.len(); | ||||||
|  |         let bytes_read = if self.cursor + requested < self.size { | ||||||
|  |             self.file.read(buf)? | ||||||
|  |         } else { | ||||||
|  |             let rest = self.size - self.cursor; | ||||||
|  |             self.file.read(&mut buf[0..rest])? | ||||||
|  |         }; | ||||||
|  |         self.cursor += bytes_read; | ||||||
|  |         if self.cursor == self.size { | ||||||
|  |             self.next_frame()?; | ||||||
|  |         } | ||||||
|  |         Ok(bytes_read) | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Rien Maertens
					Rien Maertens