Warn if scene change frame count does not match (#618)

This commit is contained in:
Josh Holmer 2022-04-20 12:57:48 -04:00 committed by GitHub
parent c8d031c1b8
commit fc4bf94278
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,13 +1,13 @@
use ffmpeg::format::Pixel;
use itertools::Itertools;
use smallvec::{smallvec, SmallVec};
use crate::scenes::Scene;
use crate::Encoder;
use crate::{into_smallvec, progress_bar, Input, ScenecutMethod, Verbosity};
use ansi_term::Style;
use anyhow::bail;
use av_scenechange::{detect_scene_changes, DetectionOptions, SceneDetectionSpeed};
use crate::scenes::Scene;
use ffmpeg::format::Pixel;
use itertools::Itertools;
use smallvec::{smallvec, SmallVec};
use std::io::Read;
use std::process::{Command, Stdio};
use std::thread;
@ -79,8 +79,109 @@ pub fn scene_detect(
sc_downscale_height: Option<usize>,
zones: &[Scene],
) -> anyhow::Result<Vec<Scene>> {
let bit_depth;
let (mut decoder, bit_depth) = build_decoder(input, encoder, sc_pix_format, sc_downscale_height)?;
let mut scenes = Vec::new();
let mut cur_zone = zones.first().filter(|frame| frame.start_frame == 0);
let mut next_zone_idx = if zones.is_empty() {
None
} else if cur_zone.is_some() {
if zones.len() == 1 {
None
} else {
Some(1)
}
} else {
Some(0)
};
let mut frames_read = 0;
loop {
let mut min_scene_len = min_scene_len;
if let Some(zone) = cur_zone {
if let Some(ref overrides) = zone.zone_overrides {
min_scene_len = overrides.min_scene_len;
}
};
let options = DetectionOptions {
min_scenecut_distance: Some(min_scene_len),
analysis_speed: match sc_method {
ScenecutMethod::Fast => SceneDetectionSpeed::Fast,
ScenecutMethod::Standard => SceneDetectionSpeed::Standard,
},
..DetectionOptions::default()
};
let frame_limit = if let Some(zone) = cur_zone {
Some(zone.end_frame - zone.start_frame)
} else if let Some(next_idx) = next_zone_idx {
let zone = &zones[next_idx];
Some(zone.start_frame - frames_read)
} else {
None
};
let sc_result = if bit_depth > 8 {
detect_scene_changes::<_, u16>(&mut decoder, options, frame_limit, callback)
} else {
detect_scene_changes::<_, u8>(&mut decoder, options, frame_limit, callback)
};
if let Some(limit) = frame_limit {
if limit != sc_result.frame_count {
bail!(
"Scene change: Expected {} frames but saw {}. This may indicate an issue with the input or filters.",
limit,
sc_result.frame_count
);
}
}
let scene_changes = sc_result.scene_changes;
for (start, end) in scene_changes.iter().copied().tuple_windows() {
scenes.push(Scene {
start_frame: start + frames_read,
end_frame: end + frames_read,
zone_overrides: cur_zone.and_then(|zone| zone.zone_overrides.clone()),
});
}
scenes.push(Scene {
start_frame: scenes
.last()
.map(|scene| scene.end_frame)
.unwrap_or_default(),
end_frame: if let Some(limit) = frame_limit {
frames_read += limit;
frames_read
} else {
total_frames
},
zone_overrides: cur_zone.and_then(|zone| zone.zone_overrides.clone()),
});
if let Some(next_idx) = next_zone_idx {
if cur_zone.map_or(true, |zone| zone.end_frame == zones[next_idx].start_frame) {
cur_zone = Some(&zones[next_idx]);
next_zone_idx = if next_idx + 1 == zones.len() {
None
} else {
Some(next_idx + 1)
};
} else {
cur_zone = None;
}
} else if cur_zone.map_or(true, |zone| zone.end_frame == total_frames) {
// End of video
break;
} else {
cur_zone = None;
}
}
Ok(scenes)
}
fn build_decoder(
input: &Input,
encoder: Encoder,
sc_pix_format: Option<Pixel>,
sc_downscale_height: Option<usize>,
) -> anyhow::Result<(y4m::Decoder<impl Read>, usize)> {
let bit_depth;
let filters: SmallVec<[String; 4]> = match (sc_downscale_height, sc_pix_format) {
(Some(sdh), Some(spf)) => into_smallvec![
"-vf",
@ -95,7 +196,7 @@ pub fn scene_detect(
(None, None) => smallvec![],
};
let decoder = &mut y4m::Decoder::new(match input {
let decoder = y4m::Decoder::new(match input {
Input::VapourSynth(path) => {
bit_depth = crate::vapoursynth::bit_depth(path.as_ref())?;
let vspipe = Command::new("vspipe")
@ -142,86 +243,5 @@ pub fn scene_detect(
}
})?;
let mut scenes = Vec::new();
let mut cur_zone = zones.first().filter(|frame| frame.start_frame == 0);
let mut next_zone_idx = if zones.is_empty() {
None
} else if cur_zone.is_some() {
if zones.len() == 1 {
None
} else {
Some(1)
}
} else {
Some(0)
};
let mut frames_read = 0;
loop {
let mut min_scene_len = min_scene_len;
if let Some(zone) = cur_zone {
if let Some(ref overrides) = zone.zone_overrides {
min_scene_len = overrides.min_scene_len;
}
};
let options = DetectionOptions {
min_scenecut_distance: Some(min_scene_len),
analysis_speed: match sc_method {
ScenecutMethod::Fast => SceneDetectionSpeed::Fast,
ScenecutMethod::Standard => SceneDetectionSpeed::Standard,
},
..DetectionOptions::default()
};
let frame_limit = if let Some(zone) = cur_zone {
Some(zone.end_frame - zone.start_frame)
} else if let Some(next_idx) = next_zone_idx {
let zone = &zones[next_idx];
Some(zone.start_frame - frames_read)
} else {
None
};
let scene_changes = if bit_depth > 8 {
detect_scene_changes::<_, u16>(decoder, options, frame_limit, callback).scene_changes
} else {
detect_scene_changes::<_, u8>(decoder, options, frame_limit, callback).scene_changes
};
for (start, end) in scene_changes.iter().copied().tuple_windows() {
scenes.push(Scene {
start_frame: start + frames_read,
end_frame: end + frames_read,
zone_overrides: cur_zone.and_then(|zone| zone.zone_overrides.clone()),
});
}
scenes.push(Scene {
start_frame: scenes
.last()
.map(|scene| scene.end_frame)
.unwrap_or_default(),
end_frame: if let Some(limit) = frame_limit {
frames_read += limit;
frames_read
} else {
total_frames
},
zone_overrides: cur_zone.and_then(|zone| zone.zone_overrides.clone()),
});
if let Some(next_idx) = next_zone_idx {
if cur_zone.map_or(true, |zone| zone.end_frame == zones[next_idx].start_frame) {
cur_zone = Some(&zones[next_idx]);
next_zone_idx = if next_idx + 1 == zones.len() {
None
} else {
Some(next_idx + 1)
};
} else {
cur_zone = None;
}
} else if cur_zone.map_or(true, |zone| zone.end_frame == total_frames) {
// End of video
break;
} else {
cur_zone = None;
}
}
Ok(scenes)
Ok((decoder, bit_depth))
}