commit
3fcf753096
7 changed files with 181 additions and 0 deletions
@ -0,0 +1,14 @@
@@ -0,0 +1,14 @@
|
||||
# This file is automatically @generated by Cargo. |
||||
# It is not intended for manual editing. |
||||
[[package]] |
||||
name = "byteorder" |
||||
version = "1.4.3" |
||||
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" |
||||
|
||||
[[package]] |
||||
name = "showtime" |
||||
version = "0.1.0" |
||||
dependencies = [ |
||||
"byteorder", |
||||
] |
@ -0,0 +1,10 @@
@@ -0,0 +1,10 @@
|
||||
[package] |
||||
name = "showtime" |
||||
version = "0.1.0" |
||||
authors = ["eta <eta@theta.eu.org>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
byteorder = "1.4" |
Binary file not shown.
@ -0,0 +1,156 @@
@@ -0,0 +1,156 @@
|
||||
use std::net::{UdpSocket, Ipv4Addr, SocketAddr}; |
||||
use byteorder::{BigEndian, WriteBytesExt, ByteOrder}; |
||||
use std::io::Write; |
||||
|
||||
struct FrameChunker<'a> { |
||||
inner: &'a [u8], |
||||
frame_no: u16, |
||||
seq_no: u16 |
||||
} |
||||
|
||||
impl<'a> FrameChunker<'a> { |
||||
fn new(frame_no: u16, inner: &'a [u8]) -> Self { |
||||
Self { |
||||
inner, |
||||
frame_no, |
||||
seq_no: 0 |
||||
} |
||||
} |
||||
fn empty() -> Self { |
||||
Self { |
||||
inner: &[], |
||||
frame_no: 0, |
||||
seq_no: 0 |
||||
} |
||||
} |
||||
fn next_chunk(&mut self) -> Vec<u8> { |
||||
let mut ret = Vec::with_capacity(1024); |
||||
let (remaining, last) = if self.inner.len() > 1020 { |
||||
(1020, false) |
||||
} |
||||
else { |
||||
(self.inner.len(), true) |
||||
}; |
||||
ret.write_u16::<BigEndian>(self.frame_no).unwrap(); |
||||
ret.write_u16::<BigEndian>(self.seq_no).unwrap(); |
||||
if last { |
||||
ret[2] |= 0b1000_0000; |
||||
} |
||||
ret.extend(&self.inner[..remaining]); |
||||
self.inner = &self.inner[remaining..]; |
||||
self.seq_no += 1; |
||||
ret |
||||
} |
||||
fn is_empty(&self) -> bool { |
||||
self.inner.is_empty() |
||||
} |
||||
} |
||||
|
||||
const MAGIC_ADDRESS: Ipv4Addr = Ipv4Addr::new(226, 2, 2, 2); |
||||
|
||||
fn make_vsync(frame_no: u16) -> Vec<u8> { |
||||
let mut ret = Vec::with_capacity(20); |
||||
ret.write_all(&[0, 0, 0, 0]).unwrap(); |
||||
ret.write_u16::<BigEndian>(frame_no).unwrap(); |
||||
for _ in 0..14 { |
||||
ret.write_all(&[0]).unwrap(); |
||||
} |
||||
ret |
||||
} |
||||
|
||||
fn make_heartbeat(seq_no: u16, cur_ts: u16, init: bool) -> Vec<u8> { |
||||
let mut ret = Vec::with_capacity(1024); |
||||
ret.write_all(&[0x54, 0x46, 0x36, 0x7a, 0x63, 0x01, 0x00]).unwrap(); |
||||
ret.write_u16::<BigEndian>(seq_no).unwrap(); |
||||
ret.write_all(&[0x00, 0x00, 0x03, 0x03, 0x03, 0x00, 0x24, 0x00, 0x00]).unwrap(); |
||||
for _ in 0..8 { |
||||
ret.write_all(&[0]).unwrap(); |
||||
} |
||||
let (mode, a, b, c, d, e, f) = if init { |
||||
(3, 1920, 1080, 599, 1920, 1080, 120) |
||||
} |
||||
else { |
||||
(10, 0, 0, 0, 0, 0, 120) |
||||
}; |
||||
for thing in &[mode, a, b, c, d, e, f] { |
||||
ret.write_u16::<BigEndian>(*thing).unwrap(); |
||||
} |
||||
ret.write_all(&[0, 0]).unwrap(); |
||||
ret.write_u16::<BigEndian>(cur_ts).unwrap(); |
||||
ret.write_all(&[0, 1, 0, 0, 0, 0, 0x03, 0x0a]).unwrap(); |
||||
for _ in ret.len()..1024 { |
||||
ret.write_all(&[0]).unwrap(); |
||||
} |
||||
assert_eq!(ret.len(), 1024); |
||||
ret |
||||
} |
||||
|
||||
|
||||
#[test] |
||||
fn test_frame_chunker() { |
||||
let test_frame = include_bytes!("../test.mjpeg"); |
||||
let mut chonker = FrameChunker::new(0, test_frame as &[u8]); |
||||
let mut test = Vec::new(); |
||||
let mut expected_seq = 0; |
||||
while !chonker.is_empty() { |
||||
let buf = chonker.next_chunk(); |
||||
test.extend(&buf[4..]); |
||||
assert_eq!(BigEndian::read_u16(&buf[2..]) & 0b0111_1111, expected_seq); |
||||
expected_seq += 1; |
||||
} |
||||
assert_eq!(test_frame as &[u8], &test as &[u8]); |
||||
} |
||||
|
||||
fn main() { |
||||
let test_frame = include_bytes!("../test.mjpeg"); |
||||
let wakeup_frame = include_bytes!("../wakeup.dat"); |
||||
let noise_frame = include_bytes!("../noise.dat"); |
||||
let mut wakeup_mut = include_bytes!("../wakeup2.dat").to_vec(); |
||||
let mut vsync = UdpSocket::bind("192.168.168.55:2067").unwrap(); |
||||
let vsync_dest: SocketAddr = "226.2.2.2:2067".parse().unwrap(); |
||||
let mut mjpeg = UdpSocket::bind("192.168.168.55:2068").unwrap(); |
||||
let mjpeg_dest: SocketAddr = "226.2.2.2:2068".parse().unwrap(); |
||||
let mut sound = UdpSocket::bind("192.168.168.55:2065").unwrap(); |
||||
let sound_dest: SocketAddr = "226.2.2.2:2066".parse().unwrap(); |
||||
let mut wakeup = UdpSocket::bind("192.168.168.55:48689").unwrap(); |
||||
let wakeup_dest: SocketAddr = "255.255.255.255:48689".parse().unwrap(); |
||||
println!("bound"); |
||||
vsync.join_multicast_v4(&MAGIC_ADDRESS, &"192.168.168.55".parse().unwrap()).unwrap(); |
||||
mjpeg.join_multicast_v4(&MAGIC_ADDRESS, &"192.168.168.55".parse().unwrap()).unwrap(); |
||||
sound.join_multicast_v4(&MAGIC_ADDRESS, &"192.168.168.55".parse().unwrap()).unwrap(); |
||||
println!("joined"); |
||||
let mut chonker = FrameChunker::empty(); |
||||
let mut frame_no = 0; |
||||
println!("enable broadcast"); |
||||
wakeup.set_broadcast(true).unwrap(); |
||||
println!("send initial wakeup"); |
||||
wakeup.send_to(wakeup_frame, &wakeup_dest).unwrap(); |
||||
println!("{:0x}", wakeup_frame[42]); |
||||
wakeup_mut[8] = 1; |
||||
println!("spawning audio thread"); |
||||
std::thread::spawn(move || { |
||||
loop { |
||||
sound.send_to(noise_frame, &sound_dest).unwrap(); |
||||
std::thread::sleep(std::time::Duration::from_micros(2800)); |
||||
} |
||||
}); |
||||
println!("going brr"); |
||||
loop { |
||||
if (frame_no % 30) == 0 { |
||||
let cur_ts = BigEndian::read_u16(&wakeup_mut[41..]); |
||||
println!("sending wakeup: wakeup[8] = {:0x}, cur_ts = {}", wakeup_mut[8], cur_ts); |
||||
wakeup_mut[8] += 1; |
||||
BigEndian::write_u16(&mut wakeup_mut[41..], cur_ts.overflowing_add(1002).0); |
||||
wakeup.send_to(&wakeup_mut, &wakeup_dest).unwrap(); |
||||
} |
||||
chonker = FrameChunker::new(frame_no, test_frame as &[u8]); |
||||
frame_no += 1; |
||||
let vsync_pkt = make_vsync(frame_no); |
||||
vsync.send_to(&vsync_pkt, &vsync_dest).unwrap(); |
||||
while !chonker.is_empty() { |
||||
let buf = chonker.next_chunk(); |
||||
mjpeg.send_to(&buf, mjpeg_dest).unwrap(); |
||||
} |
||||
::std::thread::sleep_ms(33); |
||||
} |
||||
} |
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue