1#![allow(missing_docs)]
4#![warn(clippy::doc_link_with_quotes)]
5#![warn(clippy::doc_markdown)]
6#![warn(clippy::missing_errors_doc)]
7#![warn(clippy::missing_panics_doc)]
8
9pub mod decoder;
10
11mod analyze;
12#[macro_use]
13mod cpu;
14mod data;
15#[cfg(feature = "ffmpeg")]
16pub mod ffmpeg;
17#[cfg(feature = "vapoursynth")]
18pub mod vapoursynth;
19mod y4m;
20
21use std::{
22 collections::{BTreeMap, BTreeSet},
23 io::Read,
24 sync::Arc,
25 time::Instant,
26};
27
28pub use ::y4m::Decoder as Y4mDecoder;
29use decoder::Decoder;
30pub use num_rational::Rational32;
31use v_frame::pixel::Pixel;
32
33pub use crate::{analyze::SceneChangeDetector, cpu::CpuFeatureLevel};
34
35#[derive(Debug, Clone, Copy)]
37pub struct DetectionOptions {
38 pub analysis_speed: SceneDetectionSpeed,
41 pub detect_flashes: bool,
47 pub min_scenecut_distance: Option<usize>,
49 pub max_scenecut_distance: Option<usize>,
51 pub lookahead_distance: usize,
56}
57
58impl Default for DetectionOptions {
59 #[inline]
60 fn default() -> Self {
61 DetectionOptions {
62 analysis_speed: SceneDetectionSpeed::Standard,
63 detect_flashes: true,
64 lookahead_distance: 5,
65 min_scenecut_distance: None,
66 max_scenecut_distance: None,
67 }
68 }
69}
70
71#[derive(Debug, Clone)]
73#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
74pub struct DetectionResults {
75 pub scene_changes: Vec<usize>,
77 pub frame_count: usize,
79 pub speed: f64,
81}
82
83#[inline]
87pub fn new_detector<R: Read, T: Pixel>(
88 dec: &mut Decoder<R>,
89 opts: DetectionOptions,
90) -> anyhow::Result<SceneChangeDetector<T>> {
91 let video_details = dec.get_video_details()?;
92
93 Ok(SceneChangeDetector::new(
94 (video_details.width, video_details.height),
95 video_details.bit_depth,
96 video_details.time_base.recip(),
97 video_details.chroma_sampling,
98 if opts.detect_flashes {
99 opts.lookahead_distance
100 } else {
101 1
102 },
103 opts.analysis_speed,
104 opts.min_scenecut_distance.map_or(0, |val| val),
105 opts.max_scenecut_distance
106 .map_or_else(|| u32::MAX as usize, |val| val),
107 CpuFeatureLevel::default(),
108 ))
109}
110
111#[inline]
133pub fn detect_scene_changes<R: Read, T: Pixel>(
134 dec: &mut Decoder<R>,
135 opts: DetectionOptions,
136 frame_limit: Option<usize>,
137 progress_callback: Option<&dyn Fn(usize, usize)>,
138) -> anyhow::Result<DetectionResults> {
139 assert!(opts.lookahead_distance >= 1);
140
141 let mut detector = new_detector::<R, T>(dec, opts)?;
142 let video_details = dec.get_video_details()?;
143 let mut frame_queue = BTreeMap::new();
144 let mut keyframes = BTreeSet::new();
145 keyframes.insert(0);
146
147 let start_time = Instant::now();
148 let mut frameno = 0;
149 loop {
150 let mut next_input_frameno = frame_queue.keys().last().copied().map_or(0, |key| key + 1);
151 while next_input_frameno
152 < (frameno + opts.lookahead_distance + 1).min(frame_limit.unwrap_or(usize::MAX))
153 {
154 let frame = dec.read_video_frame(&video_details);
155 if let Ok(frame) = frame {
156 frame_queue.insert(next_input_frameno, Arc::new(frame));
157 next_input_frameno += 1;
158 } else {
159 break;
161 }
162 }
163
164 let frame_set = frame_queue
166 .values()
167 .take(opts.lookahead_distance + 2)
168 .collect::<Vec<_>>();
169 if frame_set.len() < 2 {
170 break;
172 }
173 if frameno == 0
174 || detector.analyze_next_frame(
175 &frame_set,
176 frameno,
177 *keyframes
178 .iter()
179 .last()
180 .expect("at least 1 keyframe should exist"),
181 )
182 {
183 keyframes.insert(frameno);
184 };
185
186 if frameno > 0 {
187 frame_queue.remove(&(frameno - 1));
188 }
189
190 frameno += 1;
191 if let Some(progress_fn) = progress_callback {
192 progress_fn(frameno, keyframes.len());
193 }
194 if let Some(frame_limit) = frame_limit {
195 if frameno == frame_limit {
196 break;
197 }
198 }
199 }
200 Ok(DetectionResults {
201 scene_changes: keyframes.into_iter().collect(),
202 frame_count: frameno,
203 speed: frameno as f64 / start_time.elapsed().as_secs_f64(),
204 })
205}
206
207#[derive(Clone, Copy, Debug, PartialOrd, PartialEq, Eq)]
208pub enum SceneDetectionSpeed {
209 Fast,
211 Standard,
213 None,
216}