1 // Copyright (c) 2018-2020, The rav1e contributors. All rights reserved
2 //
3 // This source code is subject to the terms of the BSD 2 Clause License and
4 // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
5 // was not distributed with this source code in the LICENSE file, you can
6 // obtain it at www.aomedia.org/license/software. If the Alliance for Open
7 // Media Patent License 1.0 was not distributed with this source code in the
8 // PATENTS file, you can obtain it at www.aomedia.org/license/patent.
9
10 use av_metrics::video::*;
11 use rav1e::data::EncoderStats;
12 use rav1e::prelude::Rational;
13 use rav1e::prelude::*;
14 use rav1e::{Packet, Pixel};
15 use rust_hawktracer::*;
16 use std::fmt;
17 use std::time::Instant;
18
19 #[derive(Debug, Clone)]
20 pub struct FrameSummary {
21 /// Frame size in bytes
22 pub size: usize,
23 pub input_frameno: u64,
24 pub frame_type: FrameType,
25 /// Contains metrics such as PSNR, SSIM, etc.
26 pub metrics: QualityMetrics,
27 /// QP selected for the frame.
28 pub qp: u8,
29 /// Block-level encoding stats for the frame
30 pub enc_stats: EncoderStats,
31 }
32
33 #[hawktracer(build_frame_summary)]
build_frame_summary<T: Pixel>( packets: Packet<T>, bit_depth: usize, chroma_sampling: ChromaSampling, metrics_cli: MetricsEnabled, ) -> FrameSummary34 pub fn build_frame_summary<T: Pixel>(
35 packets: Packet<T>, bit_depth: usize, chroma_sampling: ChromaSampling,
36 metrics_cli: MetricsEnabled,
37 ) -> FrameSummary {
38 let metrics_input_frame: &Frame<T> = packets.source.as_ref().unwrap();
39 let metrics_output_frame: &Frame<T> = packets.rec.as_ref().unwrap();
40 let encode_metrics: QualityMetrics = calculate_frame_metrics(
41 metrics_input_frame,
42 metrics_output_frame,
43 bit_depth,
44 chroma_sampling,
45 metrics_cli,
46 );
47 FrameSummary {
48 size: packets.data.len(),
49 input_frameno: packets.input_frameno,
50 frame_type: packets.frame_type,
51 metrics: encode_metrics,
52 qp: packets.qp,
53 enc_stats: packets.enc_stats,
54 }
55 }
56
57 impl fmt::Display for FrameSummary {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(
60 f,
61 "Input Frame {} - {} - {} bytes{}",
62 self.input_frameno,
63 self.frame_type,
64 self.size,
65 if let Some(psnr) = self.metrics.psnr {
66 format!(
67 " - PSNR: Y: {:.4} Cb: {:.4} Cr: {:.4}",
68 psnr.y, psnr.u, psnr.v
69 )
70 } else {
71 String::new()
72 }
73 )
74 }
75 }
76
77 #[derive(Debug, Clone)]
78 pub struct ProgressInfo {
79 // Frame rate of the video
80 frame_rate: Rational,
81 // The length of the whole video, in frames, if known
82 total_frames: Option<usize>,
83 // The time the encode was started
84 time_started: Instant,
85 // List of frames encoded so far
86 frame_info: Vec<FrameSummary>,
87 // Video size so far in bytes.
88 //
89 // This value will be updated in the CLI very frequently, so we cache the previous value
90 // to reduce the overall complexity.
91 encoded_size: usize,
92 // Which Metrics to display during and at end of encode
93 metrics_enabled: MetricsEnabled,
94 }
95
96 impl ProgressInfo {
new( frame_rate: Rational, total_frames: Option<usize>, metrics_enabled: MetricsEnabled, ) -> Self97 pub fn new(
98 frame_rate: Rational, total_frames: Option<usize>,
99 metrics_enabled: MetricsEnabled,
100 ) -> Self {
101 Self {
102 frame_rate,
103 total_frames,
104 time_started: Instant::now(),
105 frame_info: Vec::with_capacity(total_frames.unwrap_or_default()),
106 encoded_size: 0,
107 metrics_enabled,
108 }
109 }
110
add_frame(&mut self, frame: FrameSummary)111 pub fn add_frame(&mut self, frame: FrameSummary) {
112 self.encoded_size += frame.size;
113 self.frame_info.push(frame);
114 }
115
frames_encoded(&self) -> usize116 pub fn frames_encoded(&self) -> usize {
117 self.frame_info.len()
118 }
119
encoding_fps(&self) -> f64120 pub fn encoding_fps(&self) -> f64 {
121 let duration = Instant::now().duration_since(self.time_started);
122 self.frame_info.len() as f64
123 / (duration.as_secs() as f64 + duration.subsec_millis() as f64 / 1000f64)
124 }
125
video_fps(&self) -> f64126 pub fn video_fps(&self) -> f64 {
127 self.frame_rate.num as f64 / self.frame_rate.den as f64
128 }
129
130 // Returns the bitrate of the frames so far, in bits/second
bitrate(&self) -> usize131 pub fn bitrate(&self) -> usize {
132 let bits = self.encoded_size * 8;
133 let seconds = self.frame_info.len() as f64 / self.video_fps();
134 (bits as f64 / seconds) as usize
135 }
136
137 // Estimates the final filesize in bytes, if the number of frames is known
estimated_size(&self) -> usize138 pub fn estimated_size(&self) -> usize {
139 self
140 .total_frames
141 .map(|frames| self.encoded_size * frames / self.frames_encoded())
142 .unwrap_or_default()
143 }
144
145 // Estimates the remaining encoding time in seconds, if the number of frames is known
estimated_time(&self) -> u64146 pub fn estimated_time(&self) -> u64 {
147 self
148 .total_frames
149 .map(|frames| {
150 (frames - self.frames_encoded()) as f64 / self.encoding_fps()
151 })
152 .unwrap_or_default() as u64
153 }
154
155 // Elapsed time in seconds
elapsed_time(&self) -> u64156 pub fn elapsed_time(&self) -> u64 {
157 Instant::now().duration_since(self.time_started).as_secs() as u64
158 }
159
160 // Number of frames of given type which appear in the video
get_frame_type_count(&self, frame_type: FrameType) -> usize161 fn get_frame_type_count(&self, frame_type: FrameType) -> usize {
162 self
163 .frame_info
164 .iter()
165 .filter(|frame| frame.frame_type == frame_type)
166 .count()
167 }
168
get_frame_type_avg_size(&self, frame_type: FrameType) -> usize169 fn get_frame_type_avg_size(&self, frame_type: FrameType) -> usize {
170 let count = self.get_frame_type_count(frame_type);
171 if count == 0 {
172 return 0;
173 }
174 self
175 .frame_info
176 .iter()
177 .filter(|frame| frame.frame_type == frame_type)
178 .map(|frame| frame.size)
179 .sum::<usize>()
180 / count
181 }
182
get_frame_type_avg_qp(&self, frame_type: FrameType) -> f32183 fn get_frame_type_avg_qp(&self, frame_type: FrameType) -> f32 {
184 let count = self.get_frame_type_count(frame_type);
185 if count == 0 {
186 return 0.;
187 }
188 self
189 .frame_info
190 .iter()
191 .filter(|frame| frame.frame_type == frame_type)
192 .map(|frame| frame.qp as f32)
193 .sum::<f32>()
194 / count as f32
195 }
196
get_block_count_by_frame_type(&self, frame_type: FrameType) -> usize197 fn get_block_count_by_frame_type(&self, frame_type: FrameType) -> usize {
198 self
199 .frame_info
200 .iter()
201 .filter(|frame| frame.frame_type == frame_type)
202 .map(|frame| frame.enc_stats.block_size_counts.iter().sum::<usize>())
203 .sum()
204 }
205
get_tx_count_by_frame_type(&self, frame_type: FrameType) -> usize206 fn get_tx_count_by_frame_type(&self, frame_type: FrameType) -> usize {
207 self
208 .frame_info
209 .iter()
210 .filter(|frame| frame.frame_type == frame_type)
211 .map(|frame| frame.enc_stats.tx_type_counts.iter().sum::<usize>())
212 .sum()
213 }
214
get_bsize_pct_by_frame_type( &self, bsize: BlockSize, frame_type: FrameType, ) -> f32215 fn get_bsize_pct_by_frame_type(
216 &self, bsize: BlockSize, frame_type: FrameType,
217 ) -> f32 {
218 let count = self.get_block_count_by_frame_type(frame_type);
219 if count == 0 {
220 return 0.;
221 }
222 self
223 .frame_info
224 .iter()
225 .filter(|frame| frame.frame_type == frame_type)
226 .map(|frame| frame.enc_stats.block_size_counts[bsize as usize])
227 .sum::<usize>() as f32
228 / count as f32
229 * 100.
230 }
231
get_skip_pct_by_frame_type(&self, frame_type: FrameType) -> f32232 fn get_skip_pct_by_frame_type(&self, frame_type: FrameType) -> f32 {
233 let count = self.get_block_count_by_frame_type(frame_type);
234 if count == 0 {
235 return 0.;
236 }
237 self
238 .frame_info
239 .iter()
240 .filter(|frame| frame.frame_type == frame_type)
241 .map(|frame| frame.enc_stats.skip_block_count)
242 .sum::<usize>() as f32
243 / count as f32
244 * 100.
245 }
246
get_txtype_pct_by_frame_type( &self, txtype: TxType, frame_type: FrameType, ) -> f32247 fn get_txtype_pct_by_frame_type(
248 &self, txtype: TxType, frame_type: FrameType,
249 ) -> f32 {
250 let count = self.get_tx_count_by_frame_type(frame_type);
251 if count == 0 {
252 return 0.;
253 }
254 self
255 .frame_info
256 .iter()
257 .filter(|frame| frame.frame_type == frame_type)
258 .map(|frame| frame.enc_stats.tx_type_counts[txtype as usize])
259 .sum::<usize>() as f32
260 / count as f32
261 * 100.
262 }
263
get_luma_pred_count_by_frame_type(&self, frame_type: FrameType) -> usize264 fn get_luma_pred_count_by_frame_type(&self, frame_type: FrameType) -> usize {
265 self
266 .frame_info
267 .iter()
268 .filter(|frame| frame.frame_type == frame_type)
269 .map(|frame| frame.enc_stats.luma_pred_mode_counts.iter().sum::<usize>())
270 .sum()
271 }
272
get_chroma_pred_count_by_frame_type( &self, frame_type: FrameType, ) -> usize273 fn get_chroma_pred_count_by_frame_type(
274 &self, frame_type: FrameType,
275 ) -> usize {
276 self
277 .frame_info
278 .iter()
279 .filter(|frame| frame.frame_type == frame_type)
280 .map(|frame| {
281 frame.enc_stats.chroma_pred_mode_counts.iter().sum::<usize>()
282 })
283 .sum()
284 }
285
get_luma_pred_mode_pct_by_frame_type( &self, pred_mode: PredictionMode, frame_type: FrameType, ) -> f32286 fn get_luma_pred_mode_pct_by_frame_type(
287 &self, pred_mode: PredictionMode, frame_type: FrameType,
288 ) -> f32 {
289 let count = self.get_luma_pred_count_by_frame_type(frame_type);
290 if count == 0 {
291 return 0.;
292 }
293 self
294 .frame_info
295 .iter()
296 .filter(|frame| frame.frame_type == frame_type)
297 .map(|frame| frame.enc_stats.luma_pred_mode_counts[pred_mode as usize])
298 .sum::<usize>() as f32
299 / count as f32
300 * 100.
301 }
302
get_chroma_pred_mode_pct_by_frame_type( &self, pred_mode: PredictionMode, frame_type: FrameType, ) -> f32303 fn get_chroma_pred_mode_pct_by_frame_type(
304 &self, pred_mode: PredictionMode, frame_type: FrameType,
305 ) -> f32 {
306 let count = self.get_chroma_pred_count_by_frame_type(frame_type);
307 if count == 0 {
308 return 0.;
309 }
310 self
311 .frame_info
312 .iter()
313 .filter(|frame| frame.frame_type == frame_type)
314 .map(|frame| frame.enc_stats.chroma_pred_mode_counts[pred_mode as usize])
315 .sum::<usize>() as f32
316 / count as f32
317 * 100.
318 }
319
print_summary(&self, verbose: bool)320 pub fn print_summary(&self, verbose: bool) {
321 eprint!("\r");
322 info!("{}", self);
323 info!("----------");
324 self.print_frame_type_summary(FrameType::KEY);
325 self.print_frame_type_summary(FrameType::INTER);
326 self.print_frame_type_summary(FrameType::INTRA_ONLY);
327 self.print_frame_type_summary(FrameType::SWITCH);
328 if verbose {
329 self.print_block_type_summary();
330 self.print_transform_type_summary();
331 self.print_prediction_modes_summary();
332 }
333 match self.metrics_enabled {
334 MetricsEnabled::None => info!("----"),
335 MetricsEnabled::Psnr => self.print_video_psnr(),
336 MetricsEnabled::All => {
337 self.print_video_psnr();
338 self.print_video_all();
339 }
340 }
341 }
342
print_frame_type_summary(&self, frame_type: FrameType)343 fn print_frame_type_summary(&self, frame_type: FrameType) {
344 let count = self.get_frame_type_count(frame_type);
345 let size = self.get_frame_type_avg_size(frame_type);
346 let avg_qp = self.get_frame_type_avg_qp(frame_type);
347 info!(
348 "{:17} {:>6} | avg QP: {:6.2} | avg size: {:>7} B",
349 format!("{}:", frame_type),
350 count,
351 avg_qp,
352 size
353 );
354 }
355
print_video_psnr(&self)356 fn print_video_psnr(&self) {
357 info!("----------");
358 let psnr_y = sum_metric(&self.frame_info, |fi| fi.metrics.psnr.unwrap().y);
359 let psnr_u = sum_metric(&self.frame_info, |fi| fi.metrics.psnr.unwrap().u);
360 let psnr_v = sum_metric(&self.frame_info, |fi| fi.metrics.psnr.unwrap().v);
361 let psnr_avg =
362 sum_metric(&self.frame_info, |fi| fi.metrics.psnr.unwrap().avg);
363 info!(
364 "Mean PSNR: Avg: {:.4} Y: {:.4} Cb: {:.4} Cr: {:.4}",
365 psnr_avg, psnr_y, psnr_u, psnr_v
366 );
367 }
print_video_all(&self)368 fn print_video_all(&self) {
369 info!("----------");
370 let psnr_hvs =
371 sum_metric(&self.frame_info, |fi| fi.metrics.psnr_hvs.unwrap().avg);
372 let ssim = sum_metric(&self.frame_info, |fi| fi.metrics.ssim.unwrap().avg);
373 let ms_ssim =
374 sum_metric(&self.frame_info, |fi| fi.metrics.ms_ssim.unwrap().avg);
375 let ciede = sum_metric(&self.frame_info, |fi| fi.metrics.ciede.unwrap());
376 info!("PSNR HVS: {:.4}", psnr_hvs);
377 info!("SSIM: {:.4} MS SSIM: {:.4}", ssim, ms_ssim);
378 info!("CIEDE2000: {:.4}", ciede);
379 info!("----------");
380 }
381
print_block_type_summary(&self)382 fn print_block_type_summary(&self) {
383 self.print_block_type_summary_for_frame_type(FrameType::KEY, 'I');
384 self.print_block_type_summary_for_frame_type(FrameType::INTER, 'P');
385 }
386
print_block_type_summary_for_frame_type( &self, frame_type: FrameType, type_label: char, )387 fn print_block_type_summary_for_frame_type(
388 &self, frame_type: FrameType, type_label: char,
389 ) {
390 info!("----------");
391 info!(
392 "bsize {}: {:>6} {:>6} {:>6} {:>6} {:>6} {:>6}",
393 type_label, "x128", "x64", "x32", "x16", "x8", "x4"
394 );
395 info!(
396 " 128x: {:>5.1}% {:>5.1}% {}",
397 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_128X128, frame_type),
398 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_128X64, frame_type),
399 if frame_type == FrameType::INTER {
400 format!("skip: {:>5.1}%", self.get_skip_pct_by_frame_type(frame_type))
401 } else {
402 String::new()
403 }
404 );
405 info!(
406 " 64x: {:>5.1}% {:>5.1}% {:>5.1}% {:>5.1}%",
407 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_64X128, frame_type),
408 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_64X64, frame_type),
409 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_64X32, frame_type),
410 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_64X16, frame_type),
411 );
412 info!(
413 " 32x: {:>5.1}% {:>5.1}% {:>5.1}% {:>5.1}%",
414 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_32X64, frame_type),
415 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_32X32, frame_type),
416 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_32X16, frame_type),
417 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_32X8, frame_type),
418 );
419 info!(
420 " 16x: {:>5.1}% {:>5.1}% {:>5.1}% {:>5.1}% {:>5.1}%",
421 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_16X64, frame_type),
422 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_16X32, frame_type),
423 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_16X16, frame_type),
424 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_16X8, frame_type),
425 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_16X4, frame_type),
426 );
427 info!(
428 " 8x: {:>5.1}% {:>5.1}% {:>5.1}% {:>5.1}%",
429 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_8X32, frame_type),
430 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_8X16, frame_type),
431 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_8X8, frame_type),
432 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_8X4, frame_type),
433 );
434 info!(
435 " 4x: {:>5.1}% {:>5.1}% {:>5.1}%",
436 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_4X16, frame_type),
437 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_4X8, frame_type),
438 self.get_bsize_pct_by_frame_type(BlockSize::BLOCK_4X4, frame_type),
439 );
440 }
441
print_transform_type_summary(&self)442 fn print_transform_type_summary(&self) {
443 info!("----------");
444 self.print_transform_type_summary_by_frame_type(FrameType::KEY, 'I');
445 self.print_transform_type_summary_by_frame_type(FrameType::INTER, 'P');
446 }
447
print_transform_type_summary_by_frame_type( &self, frame_type: FrameType, type_label: char, )448 fn print_transform_type_summary_by_frame_type(
449 &self, frame_type: FrameType, type_label: char,
450 ) {
451 info!(
452 "txtypes {}: DCT_DCT: {:.1}% | ADST_DCT: {:.1}% | DCT_ADST: {:.1}% | ADST_ADST: {:.1}%",
453 type_label,
454 self.get_txtype_pct_by_frame_type(TxType::DCT_DCT, frame_type),
455 self.get_txtype_pct_by_frame_type(TxType::ADST_DCT, frame_type),
456 self.get_txtype_pct_by_frame_type(TxType::DCT_ADST, frame_type),
457 self.get_txtype_pct_by_frame_type(TxType::ADST_ADST, frame_type)
458 );
459 info!(
460 " IDTX: {:.1}% | V_DCT: {:.1}% | H_DCT: {:.1}%",
461 self.get_txtype_pct_by_frame_type(TxType::IDTX, frame_type),
462 self.get_txtype_pct_by_frame_type(TxType::V_DCT, frame_type),
463 self.get_txtype_pct_by_frame_type(TxType::H_DCT, frame_type),
464 )
465 }
466
print_prediction_modes_summary(&self)467 fn print_prediction_modes_summary(&self) {
468 info!("----------");
469 self.print_luma_prediction_mode_summary_by_frame_type(FrameType::KEY, 'I');
470 self
471 .print_chroma_prediction_mode_summary_by_frame_type(FrameType::KEY, 'I');
472 info!("----------");
473 self
474 .print_luma_prediction_mode_summary_by_frame_type(FrameType::INTER, 'P');
475 self.print_chroma_prediction_mode_summary_by_frame_type(
476 FrameType::INTER,
477 'P',
478 );
479 }
480
print_luma_prediction_mode_summary_by_frame_type( &self, frame_type: FrameType, type_label: char, )481 fn print_luma_prediction_mode_summary_by_frame_type(
482 &self, frame_type: FrameType, type_label: char,
483 ) {
484 if frame_type == FrameType::KEY {
485 info!(
486 "y modes {}: DC: {:.1}% | V: {:.1}% | H: {:.1}% | Paeth: {:.1}%",
487 type_label,
488 self.get_luma_pred_mode_pct_by_frame_type(
489 PredictionMode::DC_PRED,
490 frame_type
491 ),
492 self.get_luma_pred_mode_pct_by_frame_type(
493 PredictionMode::V_PRED,
494 frame_type
495 ),
496 self.get_luma_pred_mode_pct_by_frame_type(
497 PredictionMode::H_PRED,
498 frame_type
499 ),
500 self.get_luma_pred_mode_pct_by_frame_type(
501 PredictionMode::PAETH_PRED,
502 frame_type
503 ),
504 );
505 info!(
506 " Smooth: {:.1}% | Smooth V: {:.1}% | Smooth H: {:.1}%",
507 self.get_luma_pred_mode_pct_by_frame_type(
508 PredictionMode::SMOOTH_PRED,
509 frame_type
510 ),
511 self.get_luma_pred_mode_pct_by_frame_type(
512 PredictionMode::SMOOTH_V_PRED,
513 frame_type
514 ),
515 self.get_luma_pred_mode_pct_by_frame_type(
516 PredictionMode::SMOOTH_H_PRED,
517 frame_type
518 ),
519 );
520 // Keep angular order for presentation here, rather than enum order.
521 info!(
522 " D: 45: {:.1}% | 67: {:.1}% | 113: {:.1}% | 135: {:.1}% | 157: {:.1}% | 203: {:.1}%",
523 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::D45_PRED, frame_type),
524 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::D67_PRED, frame_type),
525 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::D113_PRED, frame_type),
526 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::D135_PRED, frame_type),
527 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::D157_PRED, frame_type),
528 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::D203_PRED, frame_type),
529 );
530 } else if frame_type == FrameType::INTER {
531 info!(
532 "y modes {}: Nearest: {:.1}% | Near0: {:.1}% | Near1: {:.1}% | NearNear0: {:.1}% | NearNear1: {:.1}% | NearNear2: {:.1}%",
533 type_label,
534 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::NEARESTMV, frame_type),
535 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::NEAR0MV, frame_type),
536 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::NEAR1MV, frame_type),
537 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::NEAR_NEAR0MV, frame_type),
538 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::NEAR_NEAR1MV, frame_type),
539 self.get_luma_pred_mode_pct_by_frame_type(PredictionMode::NEAR_NEAR2MV, frame_type),
540 );
541 info!(
542 "y modes {}: NearNew0: {:.1}% | NearNew1: {:.1}% | NearNew2: {:.1}%",
543 type_label,
544 self.get_luma_pred_mode_pct_by_frame_type(
545 PredictionMode::NEAR_NEW0MV,
546 frame_type
547 ),
548 self.get_luma_pred_mode_pct_by_frame_type(
549 PredictionMode::NEAR_NEW1MV,
550 frame_type
551 ),
552 self.get_luma_pred_mode_pct_by_frame_type(
553 PredictionMode::NEAR_NEW2MV,
554 frame_type
555 ),
556 );
557 info!(
558 "y modes {}: NewNear0: {:.1}% | NewNear1: {:.1}% | NewNear2: {:.1}%",
559 type_label,
560 self.get_luma_pred_mode_pct_by_frame_type(
561 PredictionMode::NEW_NEAR0MV,
562 frame_type
563 ),
564 self.get_luma_pred_mode_pct_by_frame_type(
565 PredictionMode::NEW_NEAR1MV,
566 frame_type
567 ),
568 self.get_luma_pred_mode_pct_by_frame_type(
569 PredictionMode::NEW_NEAR2MV,
570 frame_type
571 ),
572 );
573 }
574 }
575
print_chroma_prediction_mode_summary_by_frame_type( &self, frame_type: FrameType, type_label: char, )576 fn print_chroma_prediction_mode_summary_by_frame_type(
577 &self, frame_type: FrameType, type_label: char,
578 ) {
579 if frame_type == FrameType::KEY {
580 info!(
581 "uv modes {}: DC: {:.1}% | V: {:.1}% | H: {:.1}% | Paeth: {:.1}%",
582 type_label,
583 self.get_chroma_pred_mode_pct_by_frame_type(
584 PredictionMode::DC_PRED,
585 frame_type
586 ),
587 self.get_chroma_pred_mode_pct_by_frame_type(
588 PredictionMode::V_PRED,
589 frame_type
590 ),
591 self.get_chroma_pred_mode_pct_by_frame_type(
592 PredictionMode::H_PRED,
593 frame_type
594 ),
595 self.get_chroma_pred_mode_pct_by_frame_type(
596 PredictionMode::PAETH_PRED,
597 frame_type
598 ),
599 );
600 info!(
601 " Smooth: {:.1}% | Smooth V: {:.1}% | Smooth H: {:.1}% | UV CFL: {:.1}%",
602 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::SMOOTH_PRED, frame_type),
603 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::SMOOTH_V_PRED, frame_type),
604 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::SMOOTH_H_PRED, frame_type),
605 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::UV_CFL_PRED, frame_type),
606 );
607 // Keep angular order for presentation here, rather than enum order.
608 info!(
609 " D: 45: {:.1}% | 67: {:.1}% | 113: {:.1}% | 135: {:.1}% | 157: {:.1}% | 203: {:.1}%",
610 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::D45_PRED, frame_type),
611 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::D67_PRED, frame_type),
612 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::D113_PRED, frame_type),
613 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::D135_PRED, frame_type),
614 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::D157_PRED, frame_type),
615 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::D203_PRED, frame_type),
616 );
617 } else if frame_type == FrameType::INTER {
618 info!(
619 "uv modes {}: Nearest: {:.1}% | Near0: {:.1}% | Near1: {:.1}% | NearNear0: {:.1}% | NearNear1: {:.1}% | NearNear2: {:.1}%",
620 type_label,
621 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::NEARESTMV, frame_type),
622 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::NEAR0MV, frame_type),
623 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::NEAR1MV, frame_type),
624 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::NEAR_NEAR0MV, frame_type),
625 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::NEAR_NEAR1MV, frame_type),
626 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::NEAR_NEAR2MV, frame_type),
627 );
628 info!(
629 "uv modes {}: NearNew0: {:.1}% | NearNew1: {:.1}% | NearNew2: {:.1}%",
630 type_label,
631 self.get_chroma_pred_mode_pct_by_frame_type(
632 PredictionMode::NEAR_NEW0MV,
633 frame_type
634 ),
635 self.get_chroma_pred_mode_pct_by_frame_type(
636 PredictionMode::NEAR_NEW1MV,
637 frame_type
638 ),
639 self.get_chroma_pred_mode_pct_by_frame_type(
640 PredictionMode::NEAR_NEW2MV,
641 frame_type
642 ),
643 );
644 info!(
645 "uv modes {}: NewNear0: {:.1}% | NewNear1: {:.1}% | NewNear2: {:.1}%",
646 type_label,
647 self.get_chroma_pred_mode_pct_by_frame_type(
648 PredictionMode::NEW_NEAR0MV,
649 frame_type
650 ),
651 self.get_chroma_pred_mode_pct_by_frame_type(
652 PredictionMode::NEW_NEAR1MV,
653 frame_type
654 ),
655 self.get_chroma_pred_mode_pct_by_frame_type(
656 PredictionMode::NEW_NEAR2MV,
657 frame_type
658 ),
659 );
660 info!(" New: {:.1}% | NewNew: {:.1}% | NearestNearest: {:.1}% | GlobalGlobal: {:.1}%",
661 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::NEWMV, frame_type),
662 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::NEW_NEWMV, frame_type),
663 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::NEAREST_NEARESTMV, frame_type),
664 self.get_chroma_pred_mode_pct_by_frame_type(PredictionMode::GLOBAL_GLOBALMV, frame_type),);
665 }
666 }
667 }
668
sum_metric<F: FnMut(&FrameSummary) -> f64>( frame_info: &[FrameSummary], map_fn: F, ) -> f64669 fn sum_metric<F: FnMut(&FrameSummary) -> f64>(
670 frame_info: &[FrameSummary], map_fn: F,
671 ) -> f64 {
672 frame_info.iter().map(map_fn).sum::<f64>() / frame_info.len() as f64
673 }
674
675 impl fmt::Display for ProgressInfo {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result676 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
677 if let Some(total_frames) = self.total_frames {
678 write!(
679 f,
680 "encoded {}/{} frames, {:.3} fps, {:.2} Kb/s, est. size: {:.2} MB, est. time: {}, elap. time: {}",
681 self.frames_encoded(),
682 total_frames,
683 self.encoding_fps(),
684 self.bitrate() as f64 / 1000f64,
685 self.estimated_size() as f64 / (1024 * 1024) as f64,
686 secs_to_human_time(self.estimated_time()),
687 secs_to_human_time(self.elapsed_time())
688 )
689 } else {
690 write!(
691 f,
692 "encoded {} frames, {:.3} fps, {:.2} Kb/s, elap. time: {}",
693 self.frames_encoded(),
694 self.encoding_fps(),
695 self.bitrate() as f64 / 1000f64,
696 secs_to_human_time(self.elapsed_time())
697 )
698 }
699 }
700 }
701
secs_to_human_time(mut secs: u64) -> String702 fn secs_to_human_time(mut secs: u64) -> String {
703 let mut mins = secs / 60;
704 secs %= 60;
705 let hours = mins / 60;
706 mins %= 60;
707 if hours > 0 {
708 format!("{}h {}m {}s", hours, mins, secs)
709 } else if mins > 0 {
710 format!("{}m {}s", mins, secs)
711 } else {
712 format!("{}s", secs)
713 }
714 }
715
716 #[derive(Debug, Clone, Copy, Default, PartialEq)]
717 pub struct QualityMetrics {
718 /// Peak Signal-to-Noise Ratio for Y, U, and V planes
719 pub psnr: Option<PlanarMetrics>,
720 /// Peak Signal-to-Noise Ratio as perceived by the Human Visual System--
721 /// taking into account Contrast Sensitivity Function (CSF)
722 pub psnr_hvs: Option<PlanarMetrics>,
723 /// Structural Similarity
724 pub ssim: Option<PlanarMetrics>,
725 /// Multi-Scale Structural Similarity
726 pub ms_ssim: Option<PlanarMetrics>,
727 /// CIEDE 2000 color difference algorithm: https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
728 pub ciede: Option<f64>,
729 /// Aligned Peak Signal-to-Noise Ratio for Y, U, and V planes
730 pub apsnr: Option<PlanarMetrics>,
731 /// Netflix's Video Multimethod Assessment Fusion
732 pub vmaf: Option<f64>,
733 }
734
735 #[derive(Debug, Clone, Copy, PartialEq)]
736 pub enum MetricsEnabled {
737 /// Don't calculate any metrics.
738 None,
739 /// Calculate the PSNR of each plane, but no other metrics.
740 Psnr,
741 /// Calculate all implemented metrics. Currently implemented metrics match what is available via AWCY.
742 All,
743 }
744
745 #[hawktracer(calculate_frame_metrics)]
calculate_frame_metrics<T: Pixel>( frame1: &Frame<T>, frame2: &Frame<T>, bit_depth: usize, cs: ChromaSampling, metrics: MetricsEnabled, ) -> QualityMetrics746 pub fn calculate_frame_metrics<T: Pixel>(
747 frame1: &Frame<T>, frame2: &Frame<T>, bit_depth: usize, cs: ChromaSampling,
748 metrics: MetricsEnabled,
749 ) -> QualityMetrics {
750 let frame1_info = FrameInfo {
751 planes: frame1.planes.clone(),
752 bit_depth,
753 chroma_sampling: cs,
754 };
755
756 let frame2_info = FrameInfo {
757 planes: frame2.planes.clone(),
758 bit_depth,
759 chroma_sampling: cs,
760 };
761
762 match metrics {
763 MetricsEnabled::None => QualityMetrics::default(),
764 MetricsEnabled::Psnr => QualityMetrics {
765 psnr: Some(
766 psnr::calculate_frame_psnr(&frame1_info, &frame2_info).unwrap(),
767 ),
768 ..Default::default()
769 },
770 MetricsEnabled::All => {
771 let mut metrics = QualityMetrics {
772 psnr: Some(
773 psnr::calculate_frame_psnr(&frame1_info, &frame2_info).unwrap(),
774 ),
775 psnr_hvs: Some(
776 psnr_hvs::calculate_frame_psnr_hvs(&frame1_info, &frame2_info)
777 .unwrap(),
778 ),
779 ..Default::default()
780 };
781 let ssim = ssim::calculate_frame_ssim(&frame1_info, &frame2_info);
782 metrics.ssim = Some(ssim.unwrap());
783 let ms_ssim = ssim::calculate_frame_msssim(&frame1_info, &frame2_info);
784 metrics.ms_ssim = Some(ms_ssim.unwrap());
785 let ciede = ciede::calculate_frame_ciede(&frame1_info, &frame2_info);
786 metrics.ciede = Some(ciede.unwrap());
787 // TODO APSNR
788 // TODO VMAF
789 metrics
790 }
791 }
792 }
793