pub struct KeyframeEnumerator { /* private fields */ }Expand description
Enumerates the timestamps of all keyframes in a video stream.
Reads only packet headers — no decoding is performed — making this
significantly faster than frame-by-frame decoding. By default the first
video stream is selected; call stream_index to
target a specific stream.
§Examples
ⓘ
use ff_analysis::KeyframeEnumerator;
let keyframes = KeyframeEnumerator::new("video.mp4").run()?;
for ts in &keyframes {
println!("Keyframe at {:?}", ts);
}Implementations§
Source§impl KeyframeEnumerator
impl KeyframeEnumerator
Sourcepub fn new(input: impl AsRef<Path>) -> Self
pub fn new(input: impl AsRef<Path>) -> Self
Creates a new enumerator for the given video file.
The first video stream is used by default. Call
stream_index to select a different stream.
Examples found in repository?
examples/keyframes.rs (line 53)
18fn main() {
19 let mut args = std::env::args().skip(1);
20 let mut input = None::<String>;
21 let mut stream_index = None::<usize>;
22
23 while let Some(flag) = args.next() {
24 match flag.as_str() {
25 "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
26 "--stream" | "-s" => {
27 let raw = args.next().unwrap_or_default();
28 stream_index = Some(raw.parse().unwrap_or_else(|_| {
29 eprintln!("Invalid stream index: {raw}");
30 process::exit(1);
31 }));
32 }
33 other => {
34 eprintln!("Unknown flag: {other}");
35 process::exit(1);
36 }
37 }
38 }
39
40 let input = input.unwrap_or_else(|| {
41 eprintln!("Usage: keyframes --input <video> [--stream <index>]");
42 process::exit(1);
43 });
44
45 println!("Enumerating keyframes in: {input}");
46 if let Some(idx) = stream_index {
47 println!("Stream index: {idx}");
48 } else {
49 println!("Stream: first video stream (default)");
50 }
51 println!();
52
53 let mut enumerator = KeyframeEnumerator::new(&input);
54 if let Some(idx) = stream_index {
55 enumerator = enumerator.stream_index(idx);
56 }
57
58 let keyframes = enumerator.run().unwrap_or_else(|e| {
59 eprintln!("Error: {e}");
60 process::exit(1);
61 });
62
63 println!("Found {} keyframe(s):", keyframes.len());
64
65 // Print first 30 keyframes to avoid flooding the terminal.
66 let display_count = keyframes.len().min(30);
67 for (i, ts) in keyframes.iter().take(display_count).enumerate() {
68 let h = ts.as_secs() / 3600;
69 let m = (ts.as_secs() % 3600) / 60;
70 let s = ts.as_secs() % 60;
71 let ms = ts.subsec_millis();
72 println!(" [{i:4}] {h:02}:{m:02}:{s:02}.{ms:03}");
73 }
74 if keyframes.len() > display_count {
75 println!(" … ({} more keyframes)", keyframes.len() - display_count);
76 }
77
78 if !keyframes.is_empty() {
79 let avg_interval_ms = keyframes
80 .windows(2)
81 .map(|w| w[1].saturating_sub(w[0]).as_millis())
82 .sum::<u128>()
83 .checked_div((keyframes.len() - 1) as u128)
84 .unwrap_or(0);
85 println!();
86 println!("Average keyframe interval: {avg_interval_ms} ms");
87 }
88}Sourcepub fn stream_index(self, idx: usize) -> Self
pub fn stream_index(self, idx: usize) -> Self
Selects a specific stream by zero-based index.
When not set (the default), the first video stream in the file is used.
Examples found in repository?
examples/keyframes.rs (line 55)
18fn main() {
19 let mut args = std::env::args().skip(1);
20 let mut input = None::<String>;
21 let mut stream_index = None::<usize>;
22
23 while let Some(flag) = args.next() {
24 match flag.as_str() {
25 "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
26 "--stream" | "-s" => {
27 let raw = args.next().unwrap_or_default();
28 stream_index = Some(raw.parse().unwrap_or_else(|_| {
29 eprintln!("Invalid stream index: {raw}");
30 process::exit(1);
31 }));
32 }
33 other => {
34 eprintln!("Unknown flag: {other}");
35 process::exit(1);
36 }
37 }
38 }
39
40 let input = input.unwrap_or_else(|| {
41 eprintln!("Usage: keyframes --input <video> [--stream <index>]");
42 process::exit(1);
43 });
44
45 println!("Enumerating keyframes in: {input}");
46 if let Some(idx) = stream_index {
47 println!("Stream index: {idx}");
48 } else {
49 println!("Stream: first video stream (default)");
50 }
51 println!();
52
53 let mut enumerator = KeyframeEnumerator::new(&input);
54 if let Some(idx) = stream_index {
55 enumerator = enumerator.stream_index(idx);
56 }
57
58 let keyframes = enumerator.run().unwrap_or_else(|e| {
59 eprintln!("Error: {e}");
60 process::exit(1);
61 });
62
63 println!("Found {} keyframe(s):", keyframes.len());
64
65 // Print first 30 keyframes to avoid flooding the terminal.
66 let display_count = keyframes.len().min(30);
67 for (i, ts) in keyframes.iter().take(display_count).enumerate() {
68 let h = ts.as_secs() / 3600;
69 let m = (ts.as_secs() % 3600) / 60;
70 let s = ts.as_secs() % 60;
71 let ms = ts.subsec_millis();
72 println!(" [{i:4}] {h:02}:{m:02}:{s:02}.{ms:03}");
73 }
74 if keyframes.len() > display_count {
75 println!(" … ({} more keyframes)", keyframes.len() - display_count);
76 }
77
78 if !keyframes.is_empty() {
79 let avg_interval_ms = keyframes
80 .windows(2)
81 .map(|w| w[1].saturating_sub(w[0]).as_millis())
82 .sum::<u128>()
83 .checked_div((keyframes.len() - 1) as u128)
84 .unwrap_or(0);
85 println!();
86 println!("Average keyframe interval: {avg_interval_ms} ms");
87 }
88}Sourcepub fn run(self) -> Result<Vec<Duration>, AnalysisError>
pub fn run(self) -> Result<Vec<Duration>, AnalysisError>
Enumerates keyframe timestamps and returns them in presentation order.
§Errors
AnalysisError::Failed— input file not found, no video stream exists, the requested stream index is out of range, or an internalFFmpegerror occurs.
Examples found in repository?
examples/keyframes.rs (line 58)
18fn main() {
19 let mut args = std::env::args().skip(1);
20 let mut input = None::<String>;
21 let mut stream_index = None::<usize>;
22
23 while let Some(flag) = args.next() {
24 match flag.as_str() {
25 "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
26 "--stream" | "-s" => {
27 let raw = args.next().unwrap_or_default();
28 stream_index = Some(raw.parse().unwrap_or_else(|_| {
29 eprintln!("Invalid stream index: {raw}");
30 process::exit(1);
31 }));
32 }
33 other => {
34 eprintln!("Unknown flag: {other}");
35 process::exit(1);
36 }
37 }
38 }
39
40 let input = input.unwrap_or_else(|| {
41 eprintln!("Usage: keyframes --input <video> [--stream <index>]");
42 process::exit(1);
43 });
44
45 println!("Enumerating keyframes in: {input}");
46 if let Some(idx) = stream_index {
47 println!("Stream index: {idx}");
48 } else {
49 println!("Stream: first video stream (default)");
50 }
51 println!();
52
53 let mut enumerator = KeyframeEnumerator::new(&input);
54 if let Some(idx) = stream_index {
55 enumerator = enumerator.stream_index(idx);
56 }
57
58 let keyframes = enumerator.run().unwrap_or_else(|e| {
59 eprintln!("Error: {e}");
60 process::exit(1);
61 });
62
63 println!("Found {} keyframe(s):", keyframes.len());
64
65 // Print first 30 keyframes to avoid flooding the terminal.
66 let display_count = keyframes.len().min(30);
67 for (i, ts) in keyframes.iter().take(display_count).enumerate() {
68 let h = ts.as_secs() / 3600;
69 let m = (ts.as_secs() % 3600) / 60;
70 let s = ts.as_secs() % 60;
71 let ms = ts.subsec_millis();
72 println!(" [{i:4}] {h:02}:{m:02}:{s:02}.{ms:03}");
73 }
74 if keyframes.len() > display_count {
75 println!(" … ({} more keyframes)", keyframes.len() - display_count);
76 }
77
78 if !keyframes.is_empty() {
79 let avg_interval_ms = keyframes
80 .windows(2)
81 .map(|w| w[1].saturating_sub(w[0]).as_millis())
82 .sum::<u128>()
83 .checked_div((keyframes.len() - 1) as u128)
84 .unwrap_or(0);
85 println!();
86 println!("Average keyframe interval: {avg_interval_ms} ms");
87 }
88}Auto Trait Implementations§
impl Freeze for KeyframeEnumerator
impl RefUnwindSafe for KeyframeEnumerator
impl Send for KeyframeEnumerator
impl Sync for KeyframeEnumerator
impl Unpin for KeyframeEnumerator
impl UnsafeUnpin for KeyframeEnumerator
impl UnwindSafe for KeyframeEnumerator
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more