Skip to content

Commit

Permalink
cut out zones
Browse files Browse the repository at this point in the history
  • Loading branch information
master-of-zen committed Dec 30, 2024
1 parent a0c92b9 commit cb822e8
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 505 deletions.
91 changes: 13 additions & 78 deletions crates/av1an-core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ use crate::{
read_task_queue,
save_task_queue,
scene_detect::av_scenechange_detect,
scenes::{Scene, ZoneOptions},
scenes::Scene,
settings::{EncodeArgs, InputPixelFormat},
split::{extra_splits, segment, write_scenes_to_file},
task::Task,
Expand Down Expand Up @@ -824,8 +824,6 @@ impl Av1anContext {
}

fn calc_split_locations(&self) -> anyhow::Result<(Vec<Scene>, usize)> {
let zones = self.parse_zones()?;

Ok(match self.args.split_method {
SplitMethod::AvScenechange => av_scenechange_detect(
&self.args.input,
Expand All @@ -836,62 +834,19 @@ impl Av1anContext {
self.args.sc_pix_format,
self.args.sc_method,
self.args.sc_downscale_height,
&zones,
)?,
SplitMethod::None => {
let mut scenes = Vec::with_capacity(2 * zones.len() + 1);
let mut frames_processed = 0;
for zone in zones {
let end_frame = zone.end_frame;

if end_frame > frames_processed {
scenes.push(Scene {
start_frame: frames_processed,
end_frame: zone.start_frame,
zone_overrides: None,
});
}

scenes.push(zone);

frames_processed += end_frame;
}
if self.frames > frames_processed {
scenes.push(Scene {
start_frame: frames_processed,
end_frame: self.frames,
zone_overrides: None,
});
}

(scenes, self.args.input.frames()?)
let frames = self.args.input.frames()?;
(
vec![Scene {
start_frame: 0, end_frame: frames
}],
frames,
)
},
})
}

fn parse_zones(&self) -> anyhow::Result<Vec<Scene>> {
let mut zones = Vec::new();
if let Some(ref zones_file) = self.args.zones {
let input = fs::read_to_string(zones_file)?;
for zone_line in input
.lines()
.map(str::trim)
.filter(|line| !line.is_empty())
{
zones.push(Scene::parse_from_zone(zone_line, self)?);
}
zones.sort_unstable_by_key(|zone| zone.start_frame);
let mut segments = BTreeSet::new();
for zone in &zones {
if segments.contains(&zone.start_frame) {
bail!("Zones file contains overlapping zones");
}
segments.extend(zone.start_frame..zone.end_frame);
}
}
Ok(zones)
}

// If we are not resuming, then do scene detection. Otherwise: get scenes
// from scenes.json and return that.
fn split_routine(&mut self) -> anyhow::Result<Vec<Scene>> {
Expand Down Expand Up @@ -968,7 +923,6 @@ impl Av1anContext {
start_frame: usize,
end_frame: usize,
frame_rate: f64,
overrides: Option<ZoneOptions>,
) -> anyhow::Result<Task> {
assert!(start_frame < end_frame, "Can't make a task with <= 0 frames!");

Expand Down Expand Up @@ -1009,10 +963,7 @@ impl Av1anContext {
start_frame,
end_frame,
frame_rate,
video_params: overrides.as_ref().map_or_else(
|| self.args.video_params.clone(),
|ovr| ovr.video_params.clone(),
),
video_params: self.args.video_params.clone(),
passes: self.args.passes,
encoder: self.args.encoder,
ignore_frame_mismatch: self.args.ignore_frame_mismatch,
Expand Down Expand Up @@ -1057,10 +1008,7 @@ impl Av1anContext {
start_frame: scene.start_frame,
end_frame: scene.end_frame,
frame_rate,
video_params: scene.zone_overrides.as_ref().map_or_else(
|| self.args.video_params.clone(),
|ovr| ovr.video_params.clone(),
),
video_params: self.args.video_params.clone(),
passes: self.args.passes,
encoder: self.args.encoder,
ignore_frame_mismatch: self.args.ignore_frame_mismatch,
Expand Down Expand Up @@ -1100,7 +1048,6 @@ impl Av1anContext {
scene.start_frame,
scene.end_frame,
frame_rate,
scene.zone_overrides.clone(),
)
.unwrap()
})
Expand Down Expand Up @@ -1145,7 +1092,6 @@ impl Av1anContext {
index,
file.as_path().to_str().unwrap(),
frame_rate,
scenes[index].zone_overrides.clone(),
)
.unwrap()
})
Expand Down Expand Up @@ -1201,15 +1147,8 @@ impl Av1anContext {
.iter()
.enumerate()
.map(|(index, &(file, (start, end, scene)))| {
self.create_select_task(
index,
file,
start,
end,
frame_rate,
scene.zone_overrides.clone(),
)
.unwrap()
self.create_select_task(index, file, start, end, frame_rate)
.unwrap()
})
.collect();

Expand All @@ -1222,7 +1161,6 @@ impl Av1anContext {
index: usize,
file: &str,
frame_rate: f64,
overrides: Option<ZoneOptions>,
) -> anyhow::Result<Task> {
let ffmpeg_gen_cmd: Vec<OsString> = into_vec![
"ffmpeg",
Expand Down Expand Up @@ -1261,10 +1199,7 @@ impl Av1anContext {
start_frame: 0,
end_frame: num_frames,
frame_rate,
video_params: overrides.as_ref().map_or_else(
|| self.args.video_params.clone(),
|ovr| ovr.video_params.clone(),
),
video_params: self.args.video_params.clone(),
passes: self.args.passes,
encoder: self.args.encoder,
ignore_frame_mismatch: self.args.ignore_frame_mismatch,
Expand Down
162 changes: 47 additions & 115 deletions crates/av1an-core/src/scene_detect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ pub fn av_scenechange_detect(
sc_pix_format: Option<Pixel>,
sc_method: ScenecutMethod,
sc_downscale_height: Option<usize>,
zones: &[Scene],
) -> anyhow::Result<(Vec<Scene>, usize)> {
if verbosity != Verbosity::Quiet {
if std::io::stderr().is_terminal() {
Expand Down Expand Up @@ -74,7 +73,6 @@ pub fn av_scenechange_detect(
sc_pix_format,
sc_method,
sc_downscale_height,
zones,
)?;

let frames = frame_thread.join().unwrap();
Expand All @@ -84,8 +82,6 @@ pub fn av_scenechange_detect(
Ok((scenes, frames))
}

/// Detect scene changes using rav1e scene detector.
#[allow(clippy::option_if_let_else)]
pub fn scene_detect(
input: &Input,
encoder: Encoder,
Expand All @@ -95,128 +91,64 @@ pub fn scene_detect(
sc_pix_format: Option<Pixel>,
sc_method: ScenecutMethod,
sc_downscale_height: Option<usize>,
zones: &[Scene],
) -> anyhow::Result<Vec<Scene>> {
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 frames_read = 0;

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 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 callback = callback.map(|cb| {
|frames, _keyframes| {
cb(frames + frames_read);
}
});
let sc_result = if bit_depth > 8 {
detect_scene_changes::<_, u16>(
&mut decoder,
options,
frame_limit,
callback
.as_ref()
.map(|cb| cb as &dyn Fn(usize, usize)),
)
} else {
detect_scene_changes::<_, u8>(
&mut decoder,
options,
frame_limit,
callback
.as_ref()
.map(|cb| cb as &dyn Fn(usize, usize)),
)
}?;
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()),
});

let callback = callback.map(|cb| {
move |frames, _keyframes| {
cb(frames + frames_read);
}
});

let sc_result = if bit_depth > 8 {
detect_scene_changes::<_, u16>(
&mut decoder,
options,
None,
callback
.as_ref()
.map(|cb| cb as &dyn Fn(usize, usize)),
)
} else {
detect_scene_changes::<_, u8>(
&mut decoder,
options,
None,
callback
.as_ref()
.map(|cb| cb as &dyn Fn(usize, usize)),
)
}?;

let scene_changes = sc_result.scene_changes;
for (start, end) in scene_changes.iter().copied().tuple_windows() {
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()),
start_frame: start + frames_read,
end_frame: end + frames_read,
});
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;
}
}

scenes.push(Scene {
start_frame: scenes
.last()
.map(|scene| scene.end_frame)
.unwrap_or_default(),
end_frame: total_frames,
});

Ok(scenes)
}

Expand Down
Loading

0 comments on commit cb822e8

Please sign in to comment.