burns the python library to the ground though. but I'm just doing a web service now, so it would be simpler to just use that, and for live python has the implementation as well still
288 lines
8.6 KiB
Rust
288 lines
8.6 KiB
Rust
use indicatif::ProgressBar;
|
|
use indicatif::ProgressBarIter;
|
|
use png;
|
|
use pyo3::exceptions::PyValueError;
|
|
use pyo3::prelude::*;
|
|
use std::fs::{File, OpenOptions};
|
|
use std::io::BufWriter;
|
|
use std::path::Path;
|
|
|
|
use pcap_parser::traits::PcapReaderIterator;
|
|
use pcap_parser::*;
|
|
|
|
struct PacketsIterator {
|
|
cap: PcapNGReader<ProgressBarIter<File>>,
|
|
i: usize,
|
|
size: usize,
|
|
}
|
|
|
|
impl Iterator for PacketsIterator {
|
|
type Item = Vec<u8>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let mut ret = None;
|
|
while ret.is_none() {
|
|
match self.cap.next() {
|
|
Ok((offset, packet)) => {
|
|
self.i += 1;
|
|
self.size += offset;
|
|
match packet {
|
|
PcapBlockOwned::Legacy(_block) => {
|
|
println!("dunno");
|
|
}
|
|
PcapBlockOwned::LegacyHeader(_block) => {
|
|
println!("dunnoheader");
|
|
}
|
|
PcapBlockOwned::NG(block) => {
|
|
if block.is_data_block() {
|
|
match block {
|
|
Block::EnhancedPacket(ref epb) => {
|
|
if epb.origlen == 6972 {
|
|
// remove udp header
|
|
ret = Some(epb.data[0x2a..].to_vec());
|
|
}
|
|
}
|
|
Block::SimplePacket(ref ep) => {
|
|
if ep.origlen == 6972 {
|
|
println!("found one regular");
|
|
}
|
|
}
|
|
_ => {
|
|
println!("unsupported packet");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
self.cap.consume(offset)
|
|
}
|
|
Err(PcapError::Eof) => break,
|
|
Err(PcapError::Incomplete) => {
|
|
self.cap.refill().unwrap();
|
|
}
|
|
Err(_) => {
|
|
println!("unexpected error");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ret
|
|
}
|
|
}
|
|
|
|
impl PacketsIterator {
|
|
fn new(filename: &str) -> anyhow::Result<Self> {
|
|
let file = OpenOptions::new()
|
|
.read(true)
|
|
.write(false)
|
|
.create(false)
|
|
.open(filename)?;
|
|
let pb = ProgressBar::new(file.metadata()?.len());
|
|
let wrap = pb.wrap_read(file);
|
|
let cap = PcapNGReader::new(65535, wrap)?;
|
|
Ok(PacketsIterator { cap, i: 0, size: 0 })
|
|
}
|
|
}
|
|
|
|
#[repr(C, packed)]
|
|
#[derive(Clone, Debug)]
|
|
pub struct Header {
|
|
c1: u32,
|
|
c2: u16,
|
|
pub part: u16,
|
|
a: u16,
|
|
ffaa: u16,
|
|
b: u16,
|
|
c: u16,
|
|
d: u16,
|
|
}
|
|
|
|
impl Header {
|
|
pub fn read(data: &[u8]) -> anyhow::Result<Self> {
|
|
Ok(Header {
|
|
c1: u32::from_be_bytes([data[0], data[1], data[2], data[3]]),
|
|
c2: u16::from_be_bytes([data[4], data[5]]),
|
|
part: u16::from_be_bytes([data[6], data[7]]),
|
|
a: u16::from_be_bytes([data[8], data[9]]),
|
|
ffaa: u16::from_be_bytes([data[10], data[11]]),
|
|
b: u16::from_be_bytes([data[12], data[13]]),
|
|
c: u16::from_be_bytes([data[14], data[15]]),
|
|
d: u16::from_be_bytes([data[16], data[17]]),
|
|
})
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn read_via_cast(data: &[u8]) -> anyhow::Result<&Self> {
|
|
let size = std::mem::size_of::<Self>();
|
|
if data.len() < size {
|
|
return Err(anyhow::anyhow!("not large enough"));
|
|
}
|
|
let mem = &data[..size].as_ptr();
|
|
Ok(unsafe { &*mem.cast() })
|
|
}
|
|
}
|
|
|
|
pub const HDR_SIZE: usize = std::mem::size_of::<Header>();
|
|
|
|
pub struct Frame {
|
|
#[allow(dead_code)]
|
|
pub header: Header,
|
|
pub raw: Vec<u8>,
|
|
}
|
|
|
|
impl Frame {
|
|
fn pixels(&self) -> Vec<u16> {
|
|
(0..self.raw.len())
|
|
.step_by(2)
|
|
.map(|i| u16::from_be_bytes([self.raw[i], self.raw[i + 1]]))
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
pub struct Decoder {
|
|
packiter: PacketsIterator,
|
|
parts: Vec<Vec<u8>>,
|
|
}
|
|
|
|
impl Decoder {
|
|
pub fn new(filename: &str) -> anyhow::Result<Self> {
|
|
let packiter = PacketsIterator::new(filename)?;
|
|
Ok(Decoder {
|
|
packiter,
|
|
parts: vec![],
|
|
})
|
|
}
|
|
}
|
|
|
|
impl Iterator for Decoder {
|
|
type Item = Frame;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let mut ret = None;
|
|
while let Some(packet) = self.packiter.next() {
|
|
let hdr = match Header::read(&packet) {
|
|
Ok(header) => header,
|
|
Err(e) => {
|
|
println!("error reading header: {:?}", e);
|
|
return None;
|
|
}
|
|
};
|
|
let data = packet[HDR_SIZE..].to_vec();
|
|
if hdr.part == 0 && self.parts.len() > 0 {
|
|
ret = Some(Frame {
|
|
header: hdr,
|
|
raw: self.parts.concat(),
|
|
});
|
|
self.parts.clear();
|
|
}
|
|
self.parts.push(data);
|
|
if ret.is_some() {
|
|
return ret;
|
|
}
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn write_raw_frame(name: &str, data: &[u8]) -> anyhow::Result<()> {
|
|
let path = Path::new(&name);
|
|
let file = File::create(path)?;
|
|
let ref mut w = BufWriter::new(file);
|
|
let mut encoder = png::Encoder::new(w, 288, 384);
|
|
encoder.set_color(png::ColorType::Grayscale);
|
|
encoder.set_depth(png::BitDepth::Sixteen);
|
|
let mut writer = encoder.write_header()?;
|
|
writer.write_image_data(data)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn write_calibrated_frame(name: &str, data: &[u16]) -> anyhow::Result<()> {
|
|
let path = Path::new(&name);
|
|
let file = File::create(path).unwrap();
|
|
let ref mut w = BufWriter::new(file);
|
|
let mut encoder = png::Encoder::new(w, 288, 384);
|
|
encoder.set_color(png::ColorType::Grayscale);
|
|
encoder.set_depth(png::BitDepth::Eight);
|
|
let mut writer = encoder.write_header()?;
|
|
//let samples: &[u16] = unsafe { std::slice::from_raw_parts(p.cast(), frame.len() / 2) };
|
|
let frame = data
|
|
.iter()
|
|
.copied()
|
|
.map(|x| {
|
|
let x: f64 = x.into();
|
|
let x = x / 256.0;
|
|
((-1.665884e-08) * x.powf(4.)
|
|
+ (1.347094e-05) * x.powf(3.)
|
|
+ (-4.396264e-03) * x.powf(2.)
|
|
+ (9.506939e-01) * x
|
|
+ (-6.353247e+01)) as u8
|
|
})
|
|
.collect::<Vec<u8>>();
|
|
writer.write_image_data(&frame)?;
|
|
Ok(())
|
|
}
|
|
|
|
#[pyclass]
|
|
struct PyFrameIterator {
|
|
iter: Decoder,
|
|
}
|
|
|
|
#[pymethods]
|
|
impl PyFrameIterator {
|
|
fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
|
|
slf
|
|
}
|
|
fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<PyObject> {
|
|
match slf.iter.next() {
|
|
Some(f) => {
|
|
let pixels = f.pixels();
|
|
let p = Python::with_gil(|py| pixels.into_py(py));
|
|
Some(p)
|
|
}
|
|
None => None.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[pyfunction]
|
|
fn decode(filename: &str) -> PyResult<PyFrameIterator> {
|
|
let decoder = Decoder::new(filename).map_err(|_| PyValueError::new_err("failed to read"))?;
|
|
let iter = PyFrameIterator { iter: decoder };
|
|
Ok(iter.into())
|
|
}
|
|
|
|
/// writes to frames/<basename of filename>
|
|
#[allow(dead_code)]
|
|
pub fn decode_to_files(filename: &str) -> anyhow::Result<()> {
|
|
let frameiter = Decoder::new(filename)?;
|
|
let basename = std::path::Path::new(filename)
|
|
.file_stem()
|
|
.ok_or(anyhow::anyhow!("cannot get basename"))?
|
|
.to_str()
|
|
.ok_or(anyhow::anyhow!("cannot convert to utf-8 from os name"))?;
|
|
let target_dir = format!("frames/{}", basename);
|
|
let target_dir = std::path::Path::new(&target_dir);
|
|
if !target_dir.exists() {
|
|
std::fs::create_dir(target_dir)?;
|
|
}
|
|
for (i, frame) in frameiter.enumerate() {
|
|
let name = format!("frames/{}/{:05}.png", basename, i);
|
|
if let Err(_e) = write_raw_frame(&name, &frame.raw) {
|
|
println!("skipping bad frame {}", i);
|
|
continue;
|
|
}
|
|
let name = format!("{}/temp_{:05}.png", target_dir.display(), i);
|
|
let pixels = frame.pixels();
|
|
write_calibrated_frame(&name, &pixels)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[pymodule]
|
|
fn thermaldecoder(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
|
m.add_function(wrap_pyfunction!(decode, m)?)?;
|
|
Ok(())
|
|
}
|