Skip to main content

av_scenechange/analyze/
standard.rs

1use std::sync::Arc;
2
3use v_frame::{frame::Frame, math::Fixed, pixel::Pixel};
4
5use super::{SceneChangeDetector, ScenecutResult};
6use crate::{
7    analyze::{
8        importance::estimate_importance_block_difference,
9        inter::estimate_inter_costs,
10        intra::estimate_intra_costs,
11    },
12    data::motion::FrameMEStats,
13};
14
15impl<T: Pixel> SceneChangeDetector<T> {
16    /// Run a comparison between two frames to determine if they qualify for a
17    /// scenecut.
18    ///
19    /// We gather both intra and inter costs for the frames,
20    /// as well as an importance-block-based difference,
21    /// and use all three metrics.
22    pub(super) fn cost_scenecut(
23        &mut self,
24        frame1: Arc<Frame<T>>,
25        frame2: Arc<Frame<T>>,
26        input_frameno: usize,
27    ) -> ScenecutResult {
28        let frame2_inter_ref = Arc::clone(&frame2);
29        let frame1_imp_ref = Arc::clone(&frame1);
30        let frame2_imp_ref = Arc::clone(&frame2);
31
32        let mut intra_cost = 0.0;
33        let mut mv_inter_cost = 0.0;
34        let mut imp_block_cost = 0.0;
35
36        let cols = 2 * self.resolution.0.align_power_of_two_and_shift(3);
37        let rows = 2 * self.resolution.1.align_power_of_two_and_shift(3);
38
39        let buffer = if let Some(buffer) = &self.frame_me_stats_buffer {
40            Arc::clone(buffer)
41        } else {
42            let frame_me_stats = FrameMEStats::new_arc_array(cols, rows);
43            let clone = Arc::clone(&frame_me_stats);
44            self.frame_me_stats_buffer = Some(frame_me_stats);
45            clone
46        };
47
48        rayon::scope(|s| {
49            s.spawn(|_| {
50                let temp_plane = self
51                    .temp_plane
52                    .get_or_insert_with(|| frame2.planes[0].clone());
53
54                let intra_costs = estimate_intra_costs(
55                    temp_plane,
56                    &*frame2,
57                    self.bit_depth,
58                    self.cpu_feature_level,
59                );
60                if let Some(ref mut intra_cache) = self.intra_costs {
61                    intra_cache.insert(input_frameno, intra_costs.clone());
62                }
63
64                intra_cost = intra_costs.iter().map(|&cost| cost as u64).sum::<u64>() as f64
65                    / intra_costs.len() as f64;
66            });
67            s.spawn(|_| {
68                mv_inter_cost = estimate_inter_costs(
69                    frame2_inter_ref,
70                    frame1,
71                    self.bit_depth,
72                    self.frame_rate,
73                    self.chroma_sampling,
74                    buffer,
75                    self.cpu_feature_level,
76                );
77            });
78            s.spawn(|_| {
79                imp_block_cost =
80                    estimate_importance_block_difference(frame2_imp_ref, frame1_imp_ref);
81            });
82        });
83
84        // `BIAS` determines how likely we are
85        // to choose a keyframe, between 0.0-1.0.
86        // Higher values mean we are more likely to choose a keyframe.
87        // This value was chosen based on trials using the new
88        // adaptive scenecut code.
89        const BIAS: f64 = 0.7;
90        let threshold = intra_cost * (1.0 - BIAS);
91
92        ScenecutResult {
93            inter_cost: mv_inter_cost,
94            imp_block_cost,
95            threshold,
96            backward_adjusted_cost: 0.0,
97            forward_adjusted_cost: 0.0,
98        }
99    }
100}