Mercurial > vxvideo
comparison src/main.rs @ 0:57b200fad67e
Hello world!
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Thu, 19 Nov 2020 21:31:36 +0100 |
parents | |
children | 4adea0a8c8dd |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:57b200fad67e |
---|---|
1 use std::num::ParseIntError; | |
2 use std::str::FromStr; | |
3 use std::fmt; | |
4 use std::fs::File; | |
5 use std::io::{Read, BufReader, Seek, SeekFrom, Write, BufWriter}; | |
6 use std::error::Error; | |
7 use std::env; | |
8 use std::process; | |
9 | |
10 #[derive(Debug)] | |
11 pub enum ParseError { | |
12 Int(ParseIntError), | |
13 WrongResolution, | |
14 UnknownCodec, | |
15 Custom(String), | |
16 } | |
17 | |
18 impl fmt::Display for ParseError { | |
19 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
20 write!(fmt, "{}", self) | |
21 } | |
22 } | |
23 | |
24 impl Error for ParseError {} | |
25 | |
26 impl From<ParseIntError> for ParseError { | |
27 fn from(e: ParseIntError) -> ParseError { | |
28 ParseError::Int(e) | |
29 } | |
30 } | |
31 | |
32 #[derive(Debug)] | |
33 enum Codec { | |
34 H264, | |
35 } | |
36 | |
37 impl FromStr for Codec { | |
38 type Err = ParseError; | |
39 | |
40 fn from_str(s: &str) -> Result<Codec, ParseError> { | |
41 if s == "H.264" { | |
42 Ok(Codec::H264) | |
43 } else { | |
44 Err(ParseError::UnknownCodec) | |
45 } | |
46 } | |
47 } | |
48 | |
49 #[derive(Debug, PartialEq)] | |
50 struct Resolution { | |
51 width: u16, | |
52 height: u16, | |
53 } | |
54 | |
55 impl FromStr for Resolution { | |
56 type Err = ParseError; | |
57 | |
58 fn from_str(s: &str) -> Result<Resolution, ParseError> { | |
59 let values: Vec<_> = s.split('x').collect(); | |
60 if let [width, height] = values.as_slice() { | |
61 let width = width.parse()?; | |
62 let height = height.parse()?; | |
63 Ok(Resolution { | |
64 width, | |
65 height, | |
66 }) | |
67 } else { | |
68 Err(ParseError::WrongResolution) | |
69 } | |
70 } | |
71 } | |
72 | |
73 impl fmt::Display for Resolution { | |
74 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
75 write!(fmt, "{}×{}", self.width, self.height) | |
76 } | |
77 } | |
78 | |
79 #[derive(Debug)] | |
80 pub struct VxSeekLine { | |
81 timestamp: u64, | |
82 video_name: String, | |
83 codec: Codec, | |
84 resolution: Resolution, | |
85 unknown: u8, | |
86 length: u32, | |
87 } | |
88 | |
89 pub fn parse_vxseek(vxseek: &str) -> Result<Vec<VxSeekLine>, ParseError> { | |
90 let mut content = Vec::new(); | |
91 for line in vxseek.lines() { | |
92 let fields: Vec<_> = line.split(':').collect(); | |
93 match fields.as_slice() { | |
94 [timestamp, video_name, codec, resolution, unknown, length] => { | |
95 let timestamp = timestamp.parse()?; | |
96 let video_name = video_name.to_string(); | |
97 let codec = codec.parse()?; | |
98 let resolution = resolution.parse()?; | |
99 let unknown = unknown.parse()?; | |
100 let length = length.parse()?; | |
101 content.push(VxSeekLine { | |
102 timestamp, | |
103 video_name, | |
104 codec, | |
105 resolution, | |
106 unknown, | |
107 length, | |
108 }); | |
109 } | |
110 _ => return Err(ParseError::Custom(String::from("Invalid line in vxseek"))), | |
111 } | |
112 } | |
113 Ok(content) | |
114 } | |
115 | |
116 fn main() -> Result<(), Box<dyn Error>> { | |
117 let args: Vec<_> = env::args().collect(); | |
118 if args.len() != 5 { | |
119 eprintln!("Usage: {} <input.vxseek> <input.vxvideo> <resolution> <output.h264>", args[0]); | |
120 process::exit(1); | |
121 } | |
122 let vxseek_filename = &args[1]; | |
123 let vxvideo_filename = &args[2]; | |
124 let wanted_resolution: Resolution = args[3].parse()?; | |
125 let output_filename = &args[4]; | |
126 | |
127 let vxseek = { | |
128 let mut vxseek = File::open(vxseek_filename).unwrap(); | |
129 let mut contents = String::new(); | |
130 vxseek.read_to_string(&mut contents).unwrap(); | |
131 parse_vxseek(&contents).unwrap() | |
132 }; | |
133 { | |
134 let vxvideo = File::open(vxvideo_filename).unwrap(); | |
135 let mut vxvideo = BufReader::new(vxvideo); | |
136 | |
137 let output = File::create(output_filename).unwrap(); | |
138 let mut output = BufWriter::new(output); | |
139 | |
140 let mut name: Option<String> = None; | |
141 for VxSeekLine { timestamp, video_name, codec, resolution, unknown, length } in vxseek { | |
142 if let Some(ref name) = &name { | |
143 if &video_name != name { | |
144 vxvideo.seek(SeekFrom::Current(length as i64)).unwrap(); | |
145 continue; | |
146 } | |
147 } else { | |
148 if resolution != wanted_resolution { | |
149 println!("Skipping {} due to resolution {}", video_name, resolution); | |
150 vxvideo.seek(SeekFrom::Current(length as i64)).unwrap(); | |
151 continue; | |
152 } | |
153 name = Some(video_name); | |
154 } | |
155 let mut buf = Vec::with_capacity(length as usize); | |
156 unsafe { buf.set_len(length as usize) }; | |
157 vxvideo.read_exact(&mut buf).unwrap(); | |
158 output.write_all(&buf).unwrap(); | |
159 output.flush().unwrap(); | |
160 } | |
161 } | |
162 Ok(()) | |
163 } |