084dd3c088341ba11161b0e22114708203b40d3e
[nihav.git] / nihav-codec-support / src / test / dec_video.rs
1 //! Routines for testing decoders.
2 use std::fs::File;
3 use nihav_core::frame::*;
4 use nihav_core::codecs::*;
5 use nihav_core::demuxers::*;
6 //use nihav_core::io::byteio::*;
7 //use nihav_core::scale::*;
8 use super::wavwriter::WavWriter;
9 use super::md5::MD5;
10 use crate::imgwrite::write_pnm;
11 pub use super::ExpectedTestResult;
12
13 const OUTPUT_PREFIX: &str = "assets/test_out/";
14
15 /*fn open_wav_out(pfx: &str, strno: usize) -> WavWriter {
16 let name = format!("assets/{}out{:02}.wav", pfx, strno);
17 let mut file = File::create(name).unwrap();
18 let mut fw = FileWriter::new_write(&mut file);
19 let mut wr = ByteWriter::new(&mut fw);
20 WavWriter::new(&mut wr)
21 }*/
22
23 /// Tests decoding of provided file and optionally outputs video frames as PNM (PPM for RGB video, PGM for YUV).
24 ///
25 /// This function expects the following arguments:
26 /// * `demuxer` - container format name (used to find proper demuxer for it)
27 /// * `name` - input file name
28 /// * `limit` - optional PTS value after which decoding is stopped
29 /// * `decode_video`/`decode_audio` - flags for enabling video/audio decoding
30 /// * `video_pfx` - prefix for video frames written as pictures (if enabled then output picture names should look like `<crate_name>/assets/test_out/PFXout00_000000.ppm`
31 /// * `dmx_reg` and `dec_reg` - registered demuxers and decoders that should contain demuxer and decoder(s) needed to decode the provided file.
32 ///
33 /// Since the function is intended for tests, it will panic instead of returning an error.
34 pub fn test_file_decoding(demuxer: &str, name: &str, limit: Option<u64>,
35 decode_video: bool, decode_audio: bool,
36 video_pfx: Option<&str>,
37 dmx_reg: &RegisteredDemuxers, dec_reg: &RegisteredDecoders) {
38 let dmx_f = dmx_reg.find_demuxer(demuxer).unwrap();
39 let mut file = File::open(name).unwrap();
40 let mut fr = FileReader::new_read(&mut file);
41 let mut br = ByteReader::new(&mut fr);
42 let mut dmx = create_demuxer(dmx_f, &mut br).unwrap();
43
44 let mut decs: Vec<Option<(Box<NADecoderSupport>, Box<dyn NADecoder>)>> = Vec::new();
45 for i in 0..dmx.get_num_streams() {
46 let s = dmx.get_stream(i).unwrap();
47 let info = s.get_info();
48 let decfunc = dec_reg.find_decoder(info.get_name());
49 if let Some(df) = decfunc {
50 if (decode_video && info.is_video()) || (decode_audio && info.is_audio()) {
51 let mut dec = (df)();
52 let mut dsupp = Box::new(NADecoderSupport::new());
53 dec.init(&mut dsupp, info).unwrap();
54 decs.push(Some((dsupp, dec)));
55 } else {
56 decs.push(None);
57 }
58 } else {
59 decs.push(None);
60 }
61 }
62
63 loop {
64 let pktres = dmx.get_frame();
65 if let Err(e) = pktres {
66 if e == DemuxerError::EOF { break; }
67 panic!("error");
68 }
69 let pkt = pktres.unwrap();
70 let streamno = pkt.get_stream().get_id() as usize;
71 if let Some((ref mut dsupp, ref mut dec)) = decs[streamno] {
72 if let (Some(lim), Some(ppts)) = (limit, pkt.get_pts()) {
73 if ppts > lim { break; }
74 }
75 let frm = dec.decode(dsupp, &pkt).unwrap();
76 if pkt.get_stream().get_info().is_video() && video_pfx.is_some() && frm.get_frame_type() != FrameType::Skip {
77 let pts = if let Some(fpts) = frm.get_pts() { fpts } else { pkt.get_pts().unwrap() };
78 let pfx = OUTPUT_PREFIX.to_owned() + video_pfx.unwrap_or("") + "out";
79 write_pnm(pfx.as_str(), streamno, pts, frm).unwrap();
80 }
81 }
82 }
83 }
84
85 /// Tests audio decoder with the content in the provided file and optionally outputs decoded audio.
86 ///
87 /// The syntax is very similar to [`test_file_decoding`] except that it is intended for testing audio codecs.
88 ///
89 /// Since the function is intended for tests, it will panic instead of returning an error.
90 ///
91 /// [`test_file_decoding`]: ./fn.test_file_decoding.html
92 pub fn test_decode_audio(demuxer: &str, name: &str, limit: Option<u64>, audio_pfx: Option<&str>,
93 dmx_reg: &RegisteredDemuxers, dec_reg: &RegisteredDecoders) {
94 let dmx_f = dmx_reg.find_demuxer(demuxer).unwrap();
95 let mut file = File::open(name).unwrap();
96 let mut fr = FileReader::new_read(&mut file);
97 let mut br = ByteReader::new(&mut fr);
98 let mut dmx = create_demuxer(dmx_f, &mut br).unwrap();
99
100 let mut decs: Vec<Option<(Box<NADecoderSupport>, Box<dyn NADecoder>)>> = Vec::new();
101 for i in 0..dmx.get_num_streams() {
102 let s = dmx.get_stream(i).unwrap();
103 let info = s.get_info();
104 let decfunc = dec_reg.find_decoder(info.get_name());
105 if let Some(df) = decfunc {
106 if info.is_audio() {
107 let mut dec = (df)();
108 let mut dsupp = Box::new(NADecoderSupport::new());
109 dec.init(&mut dsupp, info).unwrap();
110 decs.push(Some((dsupp, dec)));
111 } else {
112 decs.push(None);
113 }
114 } else {
115 decs.push(None);
116 }
117 }
118
119 if let Some(audio_pfx) = audio_pfx {
120 let name = format!("{}/{}out.wav", OUTPUT_PREFIX, audio_pfx);
121 let file = File::create(name).unwrap();
122 let mut fw = FileWriter::new_write(file);
123 let mut wr = ByteWriter::new(&mut fw);
124 let mut wwr = WavWriter::new(&mut wr);
125 let mut wrote_header = false;
126
127 loop {
128 let pktres = dmx.get_frame();
129 if let Err(e) = pktres {
130 if e == DemuxerError::EOF { break; }
131 panic!("error");
132 }
133 let pkt = pktres.unwrap();
134 if limit.is_some() && pkt.get_pts().is_some() && pkt.get_pts().unwrap() > limit.unwrap() {
135 break;
136 }
137 let streamno = pkt.get_stream().get_id() as usize;
138 if let Some((ref mut dsupp, ref mut dec)) = decs[streamno] {
139 let frm = dec.decode(dsupp, &pkt).unwrap();
140 if frm.get_info().is_audio() {
141 if !wrote_header {
142 wwr.write_header(frm.get_info().as_ref().get_properties().get_audio_info().unwrap()).unwrap();
143 wrote_header = true;
144 }
145 wwr.write_frame(frm.get_buffer()).unwrap();
146 }
147 }
148 }
149 } else {
150 loop {
151 let pktres = dmx.get_frame();
152 if let Err(e) = pktres {
153 if e == DemuxerError::EOF { break; }
154 panic!("error");
155 }
156 let pkt = pktres.unwrap();
157 if limit.is_some() && pkt.get_pts().is_some() && pkt.get_pts().unwrap() > limit.unwrap() {
158 break;
159 }
160 let streamno = pkt.get_stream().get_id() as usize;
161 if let Some((ref mut dsupp, ref mut dec)) = decs[streamno] {
162 let _ = dec.decode(dsupp, &pkt).unwrap();
163 }
164 }
165 }
166 }
167
168 fn frame_checksum(md5: &mut MD5, frm: NAFrameRef) {
169 match frm.get_buffer() {
170 NABufferType::Video(ref vb) => {
171 md5.update_hash(vb.get_data());
172 },
173 NABufferType::Video16(ref vb) => {
174 let mut samp = [0u8; 2];
175 let data = vb.get_data();
176 for el in data.iter() {
177 samp[0] = (*el >> 8) as u8;
178 samp[1] = (*el >> 0) as u8;
179 md5.update_hash(&samp);
180 }
181 },
182 NABufferType::Video32(ref vb) => {
183 let mut samp = [0u8; 4];
184 let data = vb.get_data();
185 for el in data.iter() {
186 samp[0] = (*el >> 24) as u8;
187 samp[1] = (*el >> 16) as u8;
188 samp[2] = (*el >> 8) as u8;
189 samp[3] = (*el >> 0) as u8;
190 md5.update_hash(&samp);
191 }
192 },
193 NABufferType::VideoPacked(ref vb) => {
194 md5.update_hash(vb.get_data());
195 },
196 NABufferType::AudioU8(ref ab) => {
197 md5.update_hash(ab.get_data());
198 },
199 NABufferType::AudioI16(ref ab) => {
200 let mut samp = [0u8; 2];
201 let data = ab.get_data();
202 for el in data.iter() {
203 samp[0] = (*el >> 8) as u8;
204 samp[1] = (*el >> 0) as u8;
205 md5.update_hash(&samp);
206 }
207 },
208 NABufferType::AudioI32(ref ab) => {
209 let mut samp = [0u8; 4];
210 let data = ab.get_data();
211 for el in data.iter() {
212 samp[0] = (*el >> 24) as u8;
213 samp[1] = (*el >> 16) as u8;
214 samp[2] = (*el >> 8) as u8;
215 samp[3] = (*el >> 0) as u8;
216 md5.update_hash(&samp);
217 }
218 },
219 NABufferType::AudioF32(ref ab) => {
220 let mut samp = [0u8; 4];
221 let data = ab.get_data();
222 for el in data.iter() {
223 let bits = el.to_bits();
224 samp[0] = (bits >> 24) as u8;
225 samp[1] = (bits >> 16) as u8;
226 samp[2] = (bits >> 8) as u8;
227 samp[3] = (bits >> 0) as u8;
228 md5.update_hash(&samp);
229 }
230 },
231 NABufferType::AudioPacked(ref ab) => {
232 md5.update_hash(ab.get_data());
233 },
234 NABufferType::Data(ref db) => {
235 md5.update_hash(db.as_ref());
236 },
237 NABufferType::None => {},
238 };
239 }
240
241 /// Tests decoder for requested codec in provided file.
242 ///
243 /// This functions tries to decode a stream corresponding to `dec_name` codec in input file and validate the results against expected ones.
244 ///
245 /// Since the function is intended for tests, it will panic instead of returning an error.
246 ///
247 /// # Examples
248 ///
249 /// Test RealVideo 4 decoder in test stream:
250 /// ```no_run
251 /// use nihav_codec_support::test::ExpectedTestResult;
252 /// use nihav_codec_support::test::dec_video::test_decoding;
253 /// use nihav_core::codecs::RegisteredDecoders;
254 /// use nihav_core::demuxers::RegisteredDemuxers;
255 ///
256 /// let mut dmx_reg = RegisteredDemuxers::new();
257 /// let mut dec_reg = RegisteredDecoders::new();
258 /// // ... register RealMedia demuxers and RealVideo decoders ...
259 /// test_decoding("realmedia", "rv40", "assets/test_file.rmvb", None, &dmx_reg, &dec_reg, ExpectedTestResult::MD5([0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f]));
260 /// ```
261 pub fn test_decoding(demuxer: &str, dec_name: &str, filename: &str, limit: Option<u64>,
262 dmx_reg: &RegisteredDemuxers, dec_reg: &RegisteredDecoders,
263 test: ExpectedTestResult) {
264 let dmx_f = dmx_reg.find_demuxer(demuxer).unwrap();
265 let mut file = File::open(filename).unwrap();
266 let mut fr = FileReader::new_read(&mut file);
267 let mut br = ByteReader::new(&mut fr);
268 let mut dmx = create_demuxer(dmx_f, &mut br).unwrap();
269
270 let mut decs: Vec<Option<(Box<NADecoderSupport>, Box<dyn NADecoder>)>> = Vec::new();
271 let mut found = false;
272 for i in 0..dmx.get_num_streams() {
273 let s = dmx.get_stream(i).unwrap();
274 let info = s.get_info();
275 println!("stream {} codec {} / {}", i, info.get_name(), dec_name);
276 if !found && (info.get_name() == dec_name) {
277 let decfunc = dec_reg.find_decoder(info.get_name());
278 if let Some(df) = decfunc {
279 let mut dec = (df)();
280 let mut dsupp = Box::new(NADecoderSupport::new());
281 dec.init(&mut dsupp, info).unwrap();
282 decs.push(Some((dsupp, dec)));
283 found = true;
284 } else {
285 decs.push(None);
286 }
287 } else {
288 decs.push(None);
289 }
290 }
291
292 let mut md5 = MD5::new();
293 let mut frameiter = if let ExpectedTestResult::MD5Frames(ref vec) = test {
294 Some(vec.iter())
295 } else {
296 None
297 };
298 loop {
299 let pktres = dmx.get_frame();
300 if let Err(e) = pktres {
301 if e == DemuxerError::EOF { break; }
302 panic!("error");
303 }
304 let pkt = pktres.unwrap();
305 let streamno = pkt.get_stream().get_id() as usize;
306 if let Some((ref mut dsupp, ref mut dec)) = decs[streamno] {
307 if limit.is_some() && pkt.get_pts().is_some() && pkt.get_pts().unwrap() > limit.unwrap() {
308 break;
309 }
310 let frm = dec.decode(dsupp, &pkt).unwrap();
311 match &test {
312 ExpectedTestResult::Decodes => {},
313 ExpectedTestResult::MD5(_) => { frame_checksum(&mut md5, frm); },
314 ExpectedTestResult::MD5Frames(_) => {
315 md5 = MD5::new();
316 frame_checksum(&mut md5, frm);
317 md5.finish();
318 if let Some(ref mut iter) = frameiter {
319 let ret = iter.next();
320 if ret.is_none() { break; }
321 let ref_hash = ret.unwrap();
322 let mut hash = [0u32; 4];
323 md5.get_hash(&mut hash);
324 println!("frame pts {:?} hash {}", pkt.get_pts(), md5);
325 assert_eq!(&hash, ref_hash);
326 }
327 },
328 ExpectedTestResult::GenerateMD5Frames => {
329 md5 = MD5::new();
330 frame_checksum(&mut md5, frm);
331 md5.finish();
332 let mut hash = [0u32; 4];
333 md5.get_hash(&mut hash);
334 println!("frame pts {:?} hash [0x{:08x}, 0x{:08x}, 0x{:08x}, 0x{:08x}],", pkt.get_pts(), hash[0], hash[1], hash[2], hash[3]);
335 },
336 };
337 }
338 }
339 if let ExpectedTestResult::MD5(ref ref_hash) = test {
340 md5.finish();
341 let mut hash = [0u32; 4];
342 md5.get_hash(&mut hash);
343 println!("full hash {}", md5);
344 assert_eq!(&hash, ref_hash);
345 }
346 if let ExpectedTestResult::GenerateMD5Frames = test {
347 panic!("generated hashes");
348 }
349 }