]> git.nihav.org Git - nihav.git/blame - nihav-codec-support/src/test/enc_video.rs
h264: miscellaneous micro-optimisations
[nihav.git] / nihav-codec-support / src / test / enc_video.rs
CommitLineData
6def760f 1//! Routines for testing encoders and muxers.
0b257d9f
KS
2use std::fs::File;
3use nihav_core::frame::*;
4use nihav_core::codecs::*;
5use nihav_core::demuxers::*;
6use nihav_core::muxers::*;
7use nihav_core::scale::*;
8use nihav_core::soundcvt::*;
f9304550 9use super::md5::MD5;
0b257d9f 10
6def760f 11/// Parameters for the source used in the test.
0b257d9f 12pub struct DecoderTestParams {
6def760f 13 /// Demuxer name e.g. `"mov"`.
0b257d9f 14 pub demuxer: &'static str,
6def760f 15 /// Input file name.
0b257d9f 16 pub in_name: &'static str,
6def760f 17 /// Timestamp for last decoded frame.
0b257d9f 18 pub limit: Option<u64>,
6def760f 19 /// Desired input stream type (that will be decoded and fed to the encoder).
0b257d9f 20 pub stream_type: StreamType,
6def760f 21 /// Registered demuxers.
0b257d9f 22 pub dmx_reg: RegisteredDemuxers,
6def760f 23 /// Registered decoders.
0b257d9f
KS
24 pub dec_reg: RegisteredDecoders,
25}
26
6def760f 27/// Parameters for the encoding test output.
0b257d9f 28pub struct EncoderTestParams {
6def760f 29 /// Muxer name e.g. `"avi"`.
0b257d9f 30 pub muxer: &'static str,
6def760f 31 /// Encoder name.
0b257d9f 32 pub enc_name: &'static str,
6def760f 33 /// Output file name.
0b257d9f 34 pub out_name: &'static str,
6def760f 35 /// Registered muxers.
0b257d9f 36 pub mux_reg: RegisteredMuxers,
6def760f 37 /// Registered encoders.
0b257d9f
KS
38 pub enc_reg: RegisteredEncoders,
39}
40
6def760f
KS
41/// Tests muxer by making it mux raw streams from the input file.
42///
43/// Streams not fitting the output profile (e.g. a video stream or a second audio stream for WAV muxer) will be ignored.
f9304550
KS
44pub fn test_remuxing(dec_config: &DecoderTestParams, enc_config: &EncoderTestParams) {
45 let dmx_f = dec_config.dmx_reg.find_demuxer(dec_config.demuxer).unwrap();
46 let mut file = File::open(dec_config.in_name).unwrap();
47 let mut fr = FileReader::new_read(&mut file);
48 let mut br = ByteReader::new(&mut fr);
49 let mut dmx = create_demuxer(dmx_f, &mut br).unwrap();
50
51 let mux_f = enc_config.mux_reg.find_muxer(enc_config.muxer).unwrap();
52 let out_name = "assets/test_out/".to_owned() + enc_config.out_name;
53 let file = File::create(&out_name).unwrap();
54 let mut fw = FileWriter::new_write(file);
55 let mut bw = ByteWriter::new(&mut fw);
56 let mut out_sm = StreamManager::new();
57 let mux_caps = mux_f.get_capabilities();
58 let mut stream_map: Vec<Option<usize>> = Vec::new();
59 let mut has_video = false;
60 let mut has_audio = false;
61 for stream in dmx.get_streams() {
62 let mut copy_stream = false;
63 match mux_caps {
64 MuxerCapabilities::SingleVideo(_) | MuxerCapabilities::OnlyVideo => {
65 copy_stream = stream.get_media_type() == StreamType::Video;
66 has_video = true;
67 },
68 MuxerCapabilities::SingleAudio(_) | MuxerCapabilities::OnlyAudio => {
69 copy_stream = stream.get_media_type() == StreamType::Audio;
70 has_audio = true;
71 },
72 MuxerCapabilities::SingleVideoAndAudio(_, _) => {
73 if stream.get_media_type() == StreamType::Video {
74 copy_stream = !has_video;
75 has_video = true;
76 }
77 if stream.get_media_type() == StreamType::Audio {
78 copy_stream = !has_audio;
79 has_audio = true;
80 }
81 },
82 MuxerCapabilities::Universal => {
83 if stream.get_media_type() == StreamType::Video {
84 copy_stream = true;
85 has_video = true;
86 }
87 if stream.get_media_type() == StreamType::Audio {
88 copy_stream = true;
89 has_audio = true;
90 }
91 }
92 };
93 if copy_stream {
94 let streamno = out_sm.add_stream(NAStream::clone(&stream)).unwrap();
95 stream_map.push(Some(streamno));
96 match mux_caps {
97 MuxerCapabilities::SingleVideo(_) | MuxerCapabilities::SingleAudio(_) => break,
98 _ => {},
99 };
100 } else {
101 stream_map.push(None);
102 }
103 }
104 assert!(out_sm.get_num_streams() > 0);
105 let mut mux = create_muxer(mux_f, out_sm, &mut bw).unwrap();
106
107 loop {
108 let pktres = dmx.get_frame();
109 if let Err(e) = pktres {
110 if e == DemuxerError::EOF { break; }
111 panic!("error");
112 }
113 let mut pkt = pktres.unwrap();
114 println!("Got {}", pkt);
115 if let Some(new_id) = stream_map[pkt.get_stream().id as usize] {
116 pkt.reassign(mux.get_stream(new_id).unwrap(), pkt.get_time_information());
117 mux.mux_frame(pkt).unwrap();
118 }
119 }
120
121 mux.end().unwrap();
122}
123
6def760f
KS
124/// Tests muxer by making it mux raw streams from the input file and comparing MD5 hash of the result to the provided one.
125///
126/// Streams not fitting the output profile (e.g. a video stream or a second audio stream for WAV muxer) will be ignored.
f9304550
KS
127pub fn test_remuxing_md5(dec_config: &DecoderTestParams, muxer: &str, mux_reg: &RegisteredMuxers, md5_hash: [u32; 4]) {
128 let dmx_f = dec_config.dmx_reg.find_demuxer(dec_config.demuxer).unwrap();
129 let mut file = File::open(dec_config.in_name).unwrap();
130 let mut fr = FileReader::new_read(&mut file);
131 let mut br = ByteReader::new(&mut fr);
132 let mut dmx = create_demuxer(dmx_f, &mut br).unwrap();
133
134 let mux_f = mux_reg.find_muxer(muxer).unwrap();
135
03011b99 136 let mut dst = Vec::with_capacity(1 << 10);
f9304550
KS
137 let mut gw = GrowableMemoryWriter::new_write(&mut dst);
138 let mut bw = ByteWriter::new(&mut gw);
139 let mut out_sm = StreamManager::new();
140 let mux_caps = mux_f.get_capabilities();
141 let mut stream_map: Vec<Option<usize>> = Vec::new();
142 let mut has_video = false;
143 let mut has_audio = false;
144 for stream in dmx.get_streams() {
145 let mut copy_stream = false;
146 match mux_caps {
147 MuxerCapabilities::SingleVideo(_) | MuxerCapabilities::OnlyVideo => {
148 copy_stream = stream.get_media_type() == StreamType::Video;
149 has_video = true;
150 },
151 MuxerCapabilities::SingleAudio(_) | MuxerCapabilities::OnlyAudio => {
152 copy_stream = stream.get_media_type() == StreamType::Audio;
153 has_audio = true;
154 },
155 MuxerCapabilities::SingleVideoAndAudio(_, _) => {
156 if stream.get_media_type() == StreamType::Video {
157 copy_stream = !has_video;
158 has_video = true;
159 }
160 if stream.get_media_type() == StreamType::Audio {
161 copy_stream = !has_audio;
162 has_audio = true;
163 }
164 },
165 MuxerCapabilities::Universal => {
166 if stream.get_media_type() == StreamType::Video {
167 copy_stream = true;
168 has_video = true;
169 }
170 if stream.get_media_type() == StreamType::Audio {
171 copy_stream = true;
172 has_audio = true;
173 }
174 }
175 };
176 if copy_stream {
177 let streamno = out_sm.add_stream(NAStream::clone(&stream)).unwrap();
178 stream_map.push(Some(streamno));
179 match mux_caps {
180 MuxerCapabilities::SingleVideo(_) | MuxerCapabilities::SingleAudio(_) => break,
181 _ => {},
182 };
183 } else {
184 stream_map.push(None);
185 }
186 }
187 assert!(out_sm.get_num_streams() > 0);
188 let mut mux = create_muxer(mux_f, out_sm, &mut bw).unwrap();
189
190 loop {
191 let pktres = dmx.get_frame();
192 if let Err(e) = pktres {
193 if e == DemuxerError::EOF { break; }
194 panic!("error");
195 }
196 let mut pkt = pktres.unwrap();
197 println!("Got {}", pkt);
198 if let Some(new_id) = stream_map[pkt.get_stream().id as usize] {
199 pkt.reassign(mux.get_stream(new_id).unwrap(), pkt.get_time_information());
200 mux.mux_frame(pkt).unwrap();
201 }
202 }
203
204 mux.end().unwrap();
205
206 let mut hash = [0; 4];
207 MD5::calculate_hash(dst.as_slice(), &mut hash);
208 println!("output hash {:08x}{:08x}{:08x}{:08x}", hash[0], hash[1], hash[2], hash[3]);
209 assert_eq!(hash, md5_hash);
210}
211
6def760f 212/// Tests an encoder by decoding a stream from input file, feeding it to the encoder and muxing the result into output file.
b09ecc09 213pub fn test_encoding_to_file(dec_config: &DecoderTestParams, enc_config: &EncoderTestParams, mut enc_params: EncodeParameters, enc_options: &[NAOption]) {
0b257d9f
KS
214 let dmx_f = dec_config.dmx_reg.find_demuxer(dec_config.demuxer).unwrap();
215 let mut file = File::open(dec_config.in_name).unwrap();
216 let mut fr = FileReader::new_read(&mut file);
217 let mut br = ByteReader::new(&mut fr);
218 let mut dmx = create_demuxer(dmx_f, &mut br).unwrap();
219
405cec9e 220 let in_stream = dmx.get_streams().find(|strm| strm.get_media_type() == dec_config.stream_type).unwrap();
0b257d9f
KS
221 let in_stream_id = in_stream.id;
222 let decfunc = dec_config.dec_reg.find_decoder(in_stream.get_info().get_name()).unwrap();
223 let mut dec = (decfunc)();
224 let mut dsupp = Box::new(NADecoderSupport::new());
225 dec.init(&mut dsupp, in_stream.get_info()).unwrap();
226
227 let mut out_sm = StreamManager::new();
228 enc_params.tb_num = in_stream.tb_num;
229 enc_params.tb_den = in_stream.tb_den;
230
231 if let (NACodecTypeInfo::Video(ref mut vinfo), Some(ref_vinfo)) = (&mut enc_params.format, in_stream.get_info().get_properties().get_video_info()) {
232 if vinfo.width == 0 {
233 vinfo.width = ref_vinfo.width;
234 vinfo.height = ref_vinfo.height;
235 }
236 }
237 let mut dst_chmap = NAChannelMap::new();
238 if let (NACodecTypeInfo::Audio(ref mut ainfo), Some(ref_ainfo)) = (&mut enc_params.format, in_stream.get_info().get_properties().get_audio_info()) {
239 if ainfo.sample_rate == 0 {
240 ainfo.sample_rate = ref_ainfo.sample_rate;
241 }
242 if ainfo.channels == 0 {
243 ainfo.channels = ref_ainfo.channels;
244 }
245 match ainfo.channels {
246 1 => {
247 dst_chmap.add_channel(NAChannelType::C);
248 },
249 2 => {
250 dst_chmap.add_channel(NAChannelType::L);
251 dst_chmap.add_channel(NAChannelType::R);
252 },
253 _ => panic!("cannot guess channel map"),
254 }
255 }
256
257 let encfunc = enc_config.enc_reg.find_encoder(enc_config.enc_name).unwrap();
258 let mut encoder = (encfunc)();
b09ecc09 259 encoder.set_options(enc_options);
0b257d9f
KS
260 let out_str = encoder.init(0, enc_params).unwrap();
261 out_sm.add_stream(NAStream::clone(&out_str));
c8db9313 262
0b257d9f
KS
263 let mux_f = enc_config.mux_reg.find_muxer(enc_config.muxer).unwrap();
264 let out_name = "assets/test_out/".to_owned() + enc_config.out_name;
265 let file = File::create(&out_name).unwrap();
266 let mut fw = FileWriter::new_write(file);
267 let mut bw = ByteWriter::new(&mut fw);
268 let mut mux = create_muxer(mux_f, out_sm, &mut bw).unwrap();
269
270 let (mut ifmt, dst_vinfo) = if let NACodecTypeInfo::Video(vinfo) = enc_params.format {
271 (ScaleInfo { fmt: vinfo.format, width: vinfo.width, height: vinfo.height },
272 vinfo)
273 } else {
274 (ScaleInfo { fmt: YUV420_FORMAT, width: 2, height: 2 },
30940e74 275 NAVideoInfo { width: 2, height: 2, format: YUV420_FORMAT, flipped: false, bits: 12 })
0b257d9f
KS
276 };
277 let ofmt = ifmt;
278 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
279 let mut cvt_buf = alloc_video_buffer(dst_vinfo, 2).unwrap();
280 loop {
281 let pktres = dmx.get_frame();
282 if let Err(e) = pktres {
283 if e == DemuxerError::EOF { break; }
284 panic!("decoding error");
285 }
286 let pkt = pktres.unwrap();
287 if pkt.get_stream().id != in_stream_id { continue; }
288 let frm = dec.decode(&mut dsupp, &pkt).unwrap();
289 let buf = frm.get_buffer();
290 let cfrm = if let NACodecTypeInfo::Video(_) = enc_params.format {
291 let cur_ifmt = get_scale_fmt_from_pic(&buf);
292 if cur_ifmt != ifmt {
293 ifmt = cur_ifmt;
294 scaler = NAScale::new(ifmt, ofmt).unwrap();
295 }
296 scaler.convert(&buf, &mut cvt_buf).unwrap();
297 NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cvt_buf.clone())
298 } else if let NACodecTypeInfo::Audio(ref dst_ainfo) = enc_params.format {
299 let cvt_buf = convert_audio_frame(&buf, dst_ainfo, &dst_chmap).unwrap();
300 NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cvt_buf)
301 } else {
302 panic!("unexpected format");
303 };
304 encoder.encode(&cfrm).unwrap();
305 while let Ok(Some(pkt)) = encoder.get_packet() {
306 mux.mux_frame(pkt).unwrap();
307 }
308 if let Some(maxts) = dec_config.limit {
309 if frm.get_pts().unwrap_or(0) >= maxts {
310 break;
311 }
312 }
313 }
314 encoder.flush().unwrap();
315 while let Ok(Some(pkt)) = encoder.get_packet() {
316 mux.mux_frame(pkt).unwrap();
317 }
318 mux.end().unwrap();
319}
0baa0089
KS
320
321/// Tests an encoder by decoding a stream from input file, feeding it to the encoder and calculating the hash of codec information and packet data.
b09ecc09 322pub fn test_encoding_md5(dec_config: &DecoderTestParams, enc_config: &EncoderTestParams, mut enc_params: EncodeParameters, enc_options: &[NAOption], ref_hash: &[u32; 4]) {
0baa0089
KS
323 let dmx_f = dec_config.dmx_reg.find_demuxer(dec_config.demuxer).unwrap();
324 let mut file = File::open(dec_config.in_name).unwrap();
325 let mut fr = FileReader::new_read(&mut file);
326 let mut br = ByteReader::new(&mut fr);
327 let mut dmx = create_demuxer(dmx_f, &mut br).unwrap();
328
405cec9e 329 let in_stream = dmx.get_streams().find(|strm| strm.get_media_type() == dec_config.stream_type).unwrap();
0baa0089
KS
330 let in_stream_id = in_stream.id;
331 let decfunc = dec_config.dec_reg.find_decoder(in_stream.get_info().get_name()).unwrap();
332 let mut dec = (decfunc)();
333 let mut dsupp = Box::new(NADecoderSupport::new());
334 dec.init(&mut dsupp, in_stream.get_info()).unwrap();
335
336 enc_params.tb_num = in_stream.tb_num;
337 enc_params.tb_den = in_stream.tb_den;
338
339 if let (NACodecTypeInfo::Video(ref mut vinfo), Some(ref_vinfo)) = (&mut enc_params.format, in_stream.get_info().get_properties().get_video_info()) {
340 if vinfo.width == 0 {
341 vinfo.width = ref_vinfo.width;
342 vinfo.height = ref_vinfo.height;
343 }
344 }
345 let mut dst_chmap = NAChannelMap::new();
346 if let (NACodecTypeInfo::Audio(ref mut ainfo), Some(ref_ainfo)) = (&mut enc_params.format, in_stream.get_info().get_properties().get_audio_info()) {
347 if ainfo.sample_rate == 0 {
348 ainfo.sample_rate = ref_ainfo.sample_rate;
349 }
350 if ainfo.channels == 0 {
351 ainfo.channels = ref_ainfo.channels;
352 }
353 match ainfo.channels {
354 1 => {
355 dst_chmap.add_channel(NAChannelType::C);
356 },
357 2 => {
358 dst_chmap.add_channel(NAChannelType::L);
359 dst_chmap.add_channel(NAChannelType::R);
360 },
361 _ => panic!("cannot guess channel map"),
362 }
363 }
364
365 let encfunc = enc_config.enc_reg.find_encoder(enc_config.enc_name).unwrap();
366 let mut encoder = (encfunc)();
b09ecc09 367 encoder.set_options(enc_options);
0baa0089
KS
368 let out_str = encoder.init(0, enc_params).unwrap();
369
370 let mut md5 = MD5::new();
371 let info = out_str.get_info();
372 md5.update_hash(info.get_name().as_bytes());
373 match info.get_properties() {
374 NACodecTypeInfo::Video(ref vinfo) => {
375 let mut hdr = [0u8; 10];
376 hdr[0] = (vinfo.width >> 24) as u8;
377 hdr[1] = (vinfo.width >> 16) as u8;
378 hdr[2] = (vinfo.width >> 8) as u8;
379 hdr[3] = vinfo.width as u8;
380 hdr[4] = (vinfo.height >> 24) as u8;
381 hdr[5] = (vinfo.height >> 16) as u8;
382 hdr[6] = (vinfo.height >> 8) as u8;
383 hdr[7] = vinfo.height as u8;
384 hdr[8] = vinfo.flipped as u8;
385 hdr[9] = vinfo.bits;
386 md5.update_hash(&hdr);
387 },
388 NACodecTypeInfo::Audio(ref ainfo) => {
389 let mut hdr = [0u8; 10];
390 hdr[0] = (ainfo.sample_rate >> 24) as u8;
391 hdr[1] = (ainfo.sample_rate >> 16) as u8;
392 hdr[2] = (ainfo.sample_rate >> 8) as u8;
393 hdr[3] = ainfo.sample_rate as u8;
394 hdr[4] = ainfo.channels;
395 hdr[5] = ainfo.format.bits;
396 hdr[6] = (ainfo.block_len >> 24) as u8;
397 hdr[7] = (ainfo.block_len >> 16) as u8;
398 hdr[8] = (ainfo.block_len >> 8) as u8;
399 hdr[9] = ainfo.block_len as u8;
400 md5.update_hash(&hdr);
401 },
402 _ => {},
403 };
404 if let Some(ref buf) = info.get_extradata() {
405 md5.update_hash(buf.as_slice());
406 }
407
408 let (mut ifmt, dst_vinfo) = if let NACodecTypeInfo::Video(vinfo) = enc_params.format {
409 (ScaleInfo { fmt: vinfo.format, width: vinfo.width, height: vinfo.height },
410 vinfo)
411 } else {
412 (ScaleInfo { fmt: YUV420_FORMAT, width: 2, height: 2 },
413 NAVideoInfo { width: 2, height: 2, format: YUV420_FORMAT, flipped: false, bits: 12 })
414 };
415 let ofmt = ifmt;
416 let mut scaler = NAScale::new(ifmt, ofmt).unwrap();
417 let mut cvt_buf = alloc_video_buffer(dst_vinfo, 2).unwrap();
418 loop {
419 let pktres = dmx.get_frame();
420 if let Err(e) = pktres {
421 if e == DemuxerError::EOF { break; }
422 panic!("decoding error");
423 }
424 let pkt = pktres.unwrap();
425 if pkt.get_stream().id != in_stream_id { continue; }
426 let frm = dec.decode(&mut dsupp, &pkt).unwrap();
427 let buf = frm.get_buffer();
428 let cfrm = if let NACodecTypeInfo::Video(_) = enc_params.format {
429 let cur_ifmt = get_scale_fmt_from_pic(&buf);
430 if cur_ifmt != ifmt {
431 ifmt = cur_ifmt;
432 scaler = NAScale::new(ifmt, ofmt).unwrap();
433 }
434 scaler.convert(&buf, &mut cvt_buf).unwrap();
435 NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cvt_buf.clone())
436 } else if let NACodecTypeInfo::Audio(ref dst_ainfo) = enc_params.format {
437 let cvt_buf = convert_audio_frame(&buf, dst_ainfo, &dst_chmap).unwrap();
438 NAFrame::new(frm.get_time_information(), frm.frame_type, frm.key, frm.get_info(), cvt_buf)
439 } else {
440 panic!("unexpected format");
441 };
442 encoder.encode(&cfrm).unwrap();
443 while let Ok(Some(pkt)) = encoder.get_packet() {
444 md5.update_hash(pkt.get_buffer().as_slice());
445 }
446 if let Some(maxts) = dec_config.limit {
447 if frm.get_pts().unwrap_or(0) >= maxts {
448 break;
449 }
450 }
451 }
452 encoder.flush().unwrap();
453 while let Ok(Some(pkt)) = encoder.get_packet() {
454 md5.update_hash(pkt.get_buffer().as_slice());
455 }
456
457 let mut hash = [0; 4];
458 md5.finish();
459 md5.get_hash(&mut hash);
460 println!("encode hash {}", md5);
461 assert_eq!(&hash, ref_hash);
462}