Skip to main content

StreamCopyTrimmer

Struct StreamCopyTrimmer 

Source
pub struct StreamCopyTrimmer { /* private fields */ }
Expand description

Trim a media file to a time range using stream copy (no re-encode).

Uses avformat_seek_file to seek to the start point, then copies packets until the presentation timestamp exceeds the end point. All streams (video, audio, subtitles) are copied verbatim from the input.

§Example

use ff_remux::StreamCopyTrimmer;

StreamCopyTrimmer::new("input.mp4", 2.0, 7.0, "output.mp4")
    .run()?;

Implementations§

Source§

impl StreamCopyTrimmer

Source

pub fn new( input: impl Into<PathBuf>, start_sec: f64, end_sec: f64, output: impl Into<PathBuf>, ) -> Self

Create a new StreamCopyTrimmer.

start_sec and end_sec are absolute timestamps in seconds measured from the start of the source file. run returns RemuxError::InvalidConfig if start_sec >= end_sec.

Examples found in repository?
examples/clip_trim.rs (line 67)
21fn main() {
22    let mut args = std::env::args().skip(1);
23    let mut input = None::<String>;
24    let mut output = None::<String>;
25    let mut start: f64 = 0.0;
26    let mut end: f64 = 10.0;
27
28    while let Some(flag) = args.next() {
29        match flag.as_str() {
30            "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
31            "--output" | "-o" => output = Some(args.next().unwrap_or_default()),
32            "--start" => start = args.next().unwrap_or_default().parse().unwrap_or(0.0),
33            "--end" => end = args.next().unwrap_or_default().parse().unwrap_or(10.0),
34            other => {
35                eprintln!("Unknown flag: {other}");
36                process::exit(1);
37            }
38        }
39    }
40
41    let input = input.unwrap_or_else(|| {
42        eprintln!(
43            "Usage: clip_trim --input <file> --output <file> \
44             --start <seconds> --end <seconds>"
45        );
46        process::exit(1);
47    });
48    let output = output.unwrap_or_else(|| {
49        eprintln!("--output is required");
50        process::exit(1);
51    });
52
53    let in_name = Path::new(&input)
54        .file_name()
55        .and_then(|n| n.to_str())
56        .unwrap_or(&input);
57    let out_name = Path::new(&output)
58        .file_name()
59        .and_then(|n| n.to_str())
60        .unwrap_or(&output);
61
62    println!("Input:   {in_name}");
63    println!("Trim:    {start:.3}s → {end:.3}s  (stream copy — no re-encode)");
64    println!("Output:  {out_name}");
65    println!();
66
67    if let Err(e) = StreamCopyTrimmer::new(&input, start, end, &output).run() {
68        eprintln!("Error: {e}");
69        process::exit(1);
70    }
71
72    let size_str = match std::fs::metadata(&output) {
73        Ok(m) => {
74            #[allow(clippy::cast_precision_loss)]
75            let kb = m.len() as f64 / 1024.0;
76            if kb < 1024.0 {
77                format!("{kb:.0} KB")
78            } else {
79                format!("{:.1} MB", kb / 1024.0)
80            }
81        }
82        Err(_) => "(unknown size)".to_string(),
83    };
84
85    println!("Done. {out_name}  {size_str}");
86}
More examples
Hide additional examples
examples/stream_copy_trim.rs (line 82)
23fn main() {
24    let mut args = std::env::args().skip(1);
25    let mut input = None::<String>;
26    let mut output = None::<String>;
27    let mut start_sec = 0.0_f64;
28    let mut end_sec = None::<f64>;
29
30    while let Some(flag) = args.next() {
31        match flag.as_str() {
32            "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
33            "--output" | "-o" => output = Some(args.next().unwrap_or_default()),
34            "--start" => {
35                let raw = args.next().unwrap_or_default();
36                start_sec = raw.parse().unwrap_or_else(|_| {
37                    eprintln!("Invalid start: {raw}");
38                    process::exit(1);
39                });
40            }
41            "--end" => {
42                let raw = args.next().unwrap_or_default();
43                end_sec = Some(raw.parse().unwrap_or_else(|_| {
44                    eprintln!("Invalid end: {raw}");
45                    process::exit(1);
46                }));
47            }
48            other => {
49                eprintln!("Unknown flag: {other}");
50                process::exit(1);
51            }
52        }
53    }
54
55    let input = input.unwrap_or_else(|| {
56        eprintln!(
57            "Usage: stream_copy_trim --input <file> --start <secs> --end <secs> --output <file>"
58        );
59        process::exit(1);
60    });
61    let end_sec = end_sec.unwrap_or_else(|| {
62        eprintln!("--end is required");
63        process::exit(1);
64    });
65    let output = output.unwrap_or_else(|| {
66        eprintln!("--output is required");
67        process::exit(1);
68    });
69
70    if start_sec >= end_sec {
71        eprintln!("Error: --start ({start_sec}) must be less than --end ({end_sec})");
72        process::exit(1);
73    }
74
75    let duration = end_sec - start_sec;
76    println!("Input:    {input}");
77    println!("Range:    {start_sec:.3}s – {end_sec:.3}s  ({duration:.3}s)");
78    println!("Output:   {output}");
79    println!();
80    println!("Trimming (stream-copy, no re-encode)…");
81
82    StreamCopyTrimmer::new(&input, start_sec, end_sec, &output)
83        .run()
84        .unwrap_or_else(|e| {
85            eprintln!("Error: {e}");
86            process::exit(1);
87        });
88
89    let size = match std::fs::metadata(&output) {
90        Ok(m) => {
91            #[allow(clippy::cast_precision_loss)]
92            let kb = m.len() as f64 / 1024.0;
93            if kb < 1024.0 {
94                format!("{kb:.0} KB")
95            } else {
96                format!("{:.1} MB", kb / 1024.0)
97            }
98        }
99        Err(_) => "(unknown size)".to_string(),
100    };
101
102    println!("Done. {output}  {size}");
103}
Source

pub fn video_bsf(self, spec: impl Into<String>) -> Self

Applies a bitstream filter chain to every video stream.

spec is the syntax ffmpeg -bsf takes: a comma-separated chain whose elements may carry options, e.g. "dump_extra" or "h264_metadata=level=40,extract_extradata".

This is only for filters FFmpeg does not apply on its own. libavformat already inserts the filter a container requires — copying H.264 from MP4 into MPEG-TS produces Annex B with nothing set here — so this exists for the explicit ones (extract_extradata, dump_extra, the *_metadata family). See ADR-0011.

An unregistered or malformed spec fails in run with RemuxError::InvalidConfig.

Source

pub fn audio_bsf(self, spec: impl Into<String>) -> Self

Applies a bitstream filter chain to every audio stream.

See video_bsf for the spec syntax and when to reach for this at all.

Source

pub fn run(self) -> Result<(), RemuxError>

Execute the trim operation.

§Errors
Examples found in repository?
examples/clip_trim.rs (line 67)
21fn main() {
22    let mut args = std::env::args().skip(1);
23    let mut input = None::<String>;
24    let mut output = None::<String>;
25    let mut start: f64 = 0.0;
26    let mut end: f64 = 10.0;
27
28    while let Some(flag) = args.next() {
29        match flag.as_str() {
30            "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
31            "--output" | "-o" => output = Some(args.next().unwrap_or_default()),
32            "--start" => start = args.next().unwrap_or_default().parse().unwrap_or(0.0),
33            "--end" => end = args.next().unwrap_or_default().parse().unwrap_or(10.0),
34            other => {
35                eprintln!("Unknown flag: {other}");
36                process::exit(1);
37            }
38        }
39    }
40
41    let input = input.unwrap_or_else(|| {
42        eprintln!(
43            "Usage: clip_trim --input <file> --output <file> \
44             --start <seconds> --end <seconds>"
45        );
46        process::exit(1);
47    });
48    let output = output.unwrap_or_else(|| {
49        eprintln!("--output is required");
50        process::exit(1);
51    });
52
53    let in_name = Path::new(&input)
54        .file_name()
55        .and_then(|n| n.to_str())
56        .unwrap_or(&input);
57    let out_name = Path::new(&output)
58        .file_name()
59        .and_then(|n| n.to_str())
60        .unwrap_or(&output);
61
62    println!("Input:   {in_name}");
63    println!("Trim:    {start:.3}s → {end:.3}s  (stream copy — no re-encode)");
64    println!("Output:  {out_name}");
65    println!();
66
67    if let Err(e) = StreamCopyTrimmer::new(&input, start, end, &output).run() {
68        eprintln!("Error: {e}");
69        process::exit(1);
70    }
71
72    let size_str = match std::fs::metadata(&output) {
73        Ok(m) => {
74            #[allow(clippy::cast_precision_loss)]
75            let kb = m.len() as f64 / 1024.0;
76            if kb < 1024.0 {
77                format!("{kb:.0} KB")
78            } else {
79                format!("{:.1} MB", kb / 1024.0)
80            }
81        }
82        Err(_) => "(unknown size)".to_string(),
83    };
84
85    println!("Done. {out_name}  {size_str}");
86}
More examples
Hide additional examples
examples/stream_copy_trim.rs (line 83)
23fn main() {
24    let mut args = std::env::args().skip(1);
25    let mut input = None::<String>;
26    let mut output = None::<String>;
27    let mut start_sec = 0.0_f64;
28    let mut end_sec = None::<f64>;
29
30    while let Some(flag) = args.next() {
31        match flag.as_str() {
32            "--input" | "-i" => input = Some(args.next().unwrap_or_default()),
33            "--output" | "-o" => output = Some(args.next().unwrap_or_default()),
34            "--start" => {
35                let raw = args.next().unwrap_or_default();
36                start_sec = raw.parse().unwrap_or_else(|_| {
37                    eprintln!("Invalid start: {raw}");
38                    process::exit(1);
39                });
40            }
41            "--end" => {
42                let raw = args.next().unwrap_or_default();
43                end_sec = Some(raw.parse().unwrap_or_else(|_| {
44                    eprintln!("Invalid end: {raw}");
45                    process::exit(1);
46                }));
47            }
48            other => {
49                eprintln!("Unknown flag: {other}");
50                process::exit(1);
51            }
52        }
53    }
54
55    let input = input.unwrap_or_else(|| {
56        eprintln!(
57            "Usage: stream_copy_trim --input <file> --start <secs> --end <secs> --output <file>"
58        );
59        process::exit(1);
60    });
61    let end_sec = end_sec.unwrap_or_else(|| {
62        eprintln!("--end is required");
63        process::exit(1);
64    });
65    let output = output.unwrap_or_else(|| {
66        eprintln!("--output is required");
67        process::exit(1);
68    });
69
70    if start_sec >= end_sec {
71        eprintln!("Error: --start ({start_sec}) must be less than --end ({end_sec})");
72        process::exit(1);
73    }
74
75    let duration = end_sec - start_sec;
76    println!("Input:    {input}");
77    println!("Range:    {start_sec:.3}s – {end_sec:.3}s  ({duration:.3}s)");
78    println!("Output:   {output}");
79    println!();
80    println!("Trimming (stream-copy, no re-encode)…");
81
82    StreamCopyTrimmer::new(&input, start_sec, end_sec, &output)
83        .run()
84        .unwrap_or_else(|e| {
85            eprintln!("Error: {e}");
86            process::exit(1);
87        });
88
89    let size = match std::fs::metadata(&output) {
90        Ok(m) => {
91            #[allow(clippy::cast_precision_loss)]
92            let kb = m.len() as f64 / 1024.0;
93            if kb < 1024.0 {
94                format!("{kb:.0} KB")
95            } else {
96                format!("{:.1} MB", kb / 1024.0)
97            }
98        }
99        Err(_) => "(unknown size)".to_string(),
100    };
101
102    println!("Done. {output}  {size}");
103}

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.