changeset 0:57b200fad67e

Hello world!
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Thu, 19 Nov 2020 21:31:36 +0100
parents
children 4adea0a8c8dd
files .hgignore Cargo.toml src/main.rs
diffstat 3 files changed, 173 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,1 @@
+^target/
new file mode 100644
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "vxvideo"
+version = "0.1.0"
+authors = ["Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
new file mode 100644
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,163 @@
+use std::num::ParseIntError;
+use std::str::FromStr;
+use std::fmt;
+use std::fs::File;
+use std::io::{Read, BufReader, Seek, SeekFrom, Write, BufWriter};
+use std::error::Error;
+use std::env;
+use std::process;
+
+#[derive(Debug)]
+pub enum ParseError {
+    Int(ParseIntError),
+    WrongResolution,
+    UnknownCodec,
+    Custom(String),
+}
+
+impl fmt::Display for ParseError {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "{}", self)
+    }
+}
+
+impl Error for ParseError {}
+
+impl From<ParseIntError> for ParseError {
+    fn from(e: ParseIntError) -> ParseError {
+        ParseError::Int(e)
+    }
+}
+
+#[derive(Debug)]
+enum Codec {
+    H264,
+}
+
+impl FromStr for Codec {
+    type Err = ParseError;
+
+    fn from_str(s: &str) -> Result<Codec, ParseError> {
+        if s == "H.264" {
+            Ok(Codec::H264)
+        } else {
+            Err(ParseError::UnknownCodec)
+        }
+    }
+}
+
+#[derive(Debug, PartialEq)]
+struct Resolution {
+    width: u16,
+    height: u16,
+}
+
+impl FromStr for Resolution {
+    type Err = ParseError;
+
+    fn from_str(s: &str) -> Result<Resolution, ParseError> {
+        let values: Vec<_> = s.split('x').collect();
+        if let [width, height] = values.as_slice() {
+            let width = width.parse()?;
+            let height = height.parse()?;
+            Ok(Resolution {
+                width,
+                height,
+            })
+        } else {
+            Err(ParseError::WrongResolution)
+        }
+    }
+}
+
+impl fmt::Display for Resolution {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        write!(fmt, "{}×{}", self.width, self.height)
+    }
+}
+
+#[derive(Debug)]
+pub struct VxSeekLine {
+    timestamp: u64,
+    video_name: String,
+    codec: Codec,
+    resolution: Resolution,
+    unknown: u8,
+    length: u32,
+}
+
+pub fn parse_vxseek(vxseek: &str) -> Result<Vec<VxSeekLine>, ParseError> {
+    let mut content = Vec::new();
+    for line in vxseek.lines() {
+        let fields: Vec<_> = line.split(':').collect();
+        match fields.as_slice() {
+            [timestamp, video_name, codec, resolution, unknown, length] => {
+                let timestamp = timestamp.parse()?;
+                let video_name = video_name.to_string();
+                let codec = codec.parse()?;
+                let resolution = resolution.parse()?;
+                let unknown = unknown.parse()?;
+                let length = length.parse()?;
+                content.push(VxSeekLine {
+                    timestamp,
+                    video_name,
+                    codec,
+                    resolution,
+                    unknown,
+                    length,
+                });
+            }
+            _ => return Err(ParseError::Custom(String::from("Invalid line in vxseek"))),
+        }
+    }
+    Ok(content)
+}
+
+fn main() -> Result<(), Box<dyn Error>> {
+    let args: Vec<_> = env::args().collect();
+    if args.len() != 5 {
+        eprintln!("Usage: {} <input.vxseek> <input.vxvideo> <resolution> <output.h264>", args[0]);
+        process::exit(1);
+    }
+    let vxseek_filename = &args[1];
+    let vxvideo_filename = &args[2];
+    let wanted_resolution: Resolution = args[3].parse()?;
+    let output_filename = &args[4];
+
+    let vxseek = {
+        let mut vxseek = File::open(vxseek_filename).unwrap();
+        let mut contents = String::new();
+        vxseek.read_to_string(&mut contents).unwrap();
+        parse_vxseek(&contents).unwrap()
+    };
+    {
+        let vxvideo = File::open(vxvideo_filename).unwrap();
+        let mut vxvideo = BufReader::new(vxvideo);
+
+        let output = File::create(output_filename).unwrap();
+        let mut output = BufWriter::new(output);
+
+        let mut name: Option<String> = None;
+        for VxSeekLine { timestamp, video_name, codec, resolution, unknown, length } in vxseek {
+            if let Some(ref name) = &name {
+                if &video_name != name {
+                    vxvideo.seek(SeekFrom::Current(length as i64)).unwrap();
+                    continue;
+                }
+            } else {
+                if resolution != wanted_resolution {
+                    println!("Skipping {} due to resolution {}", video_name, resolution);
+                    vxvideo.seek(SeekFrom::Current(length as i64)).unwrap();
+                    continue;
+                }
+                name = Some(video_name);
+            }
+            let mut buf = Vec::with_capacity(length as usize);
+            unsafe { buf.set_len(length as usize) };
+            vxvideo.read_exact(&mut buf).unwrap();
+            output.write_all(&buf).unwrap();
+            output.flush().unwrap();
+        }
+    }
+    Ok(())
+}