ffmpeg_sidecar/command.rs
1//! Builder interface for FFmpeg commands.
2
3use crate::{child::FfmpegChild, paths::ffmpeg_path};
4use std::{
5 ffi::OsStr,
6 fmt, io,
7 process::{Command, CommandArgs, Stdio},
8};
9
10/// A wrapper around [`std::process::Command`] with some convenient preset
11/// argument sets and customization for `ffmpeg` specifically.
12///
13/// The `rustdoc` on each method includes relevant information from the FFmpeg
14/// documentation: <https://ffmpeg.org/ffmpeg.html>. Refer there for the
15/// exhaustive list of possible arguments.
16pub struct FfmpegCommand {
17 inner: Command,
18}
19
20impl FfmpegCommand {
21 //// Generic option aliases ////
22 //// https://ffmpeg.org/ffmpeg.html#Generic-options
23
24 /// alias for `-hide_banner` argument.
25 ///
26 /// Suppress printing banner.
27 ///
28 /// All FFmpeg tools will normally show a copyright notice, build options and
29 /// library versions. This option can be used to suppress printing this
30 /// information.
31 pub fn hide_banner(&mut self) -> &mut Self {
32 self.arg("-hide_banner");
33 self
34 }
35
36 //// Main option aliases
37 //// https://ffmpeg.org/ffmpeg.html#Main-options
38
39 /// Alias for `-f` argument, the format name.
40 ///
41 /// Force input or output file format. The format is normally auto detected
42 /// for input files and guessed from the file extension for output files, so
43 /// this option is not needed in most cases.
44 pub fn format<S: AsRef<str>>(&mut self, format: S) -> &mut Self {
45 self.arg("-f");
46 self.arg(format.as_ref());
47 self
48 }
49
50 /// Alias for `-i` argument, the input file path or URL.
51 ///
52 /// To take input from stdin, use the value `-` or `pipe:0`.
53 pub fn input<S: AsRef<str>>(&mut self, path_or_url: S) -> &mut Self {
54 self.arg("-i");
55 self.arg(path_or_url.as_ref());
56 self
57 }
58
59 /// Alias for the output file path or URL.
60 ///
61 /// To send output to stdout, use the value `-` or `pipe:1`.
62 ///
63 /// Since this is the last argument in the command and has no `-` flag
64 /// preceding it, it is equivalent to calling `.arg()` directly. However,
65 /// using this command helps label the purpose of the argument, and makes the
66 /// code more readable at a glance.
67 pub fn output<S: AsRef<str>>(&mut self, path_or_url: S) -> &mut Self {
68 self.arg(path_or_url.as_ref());
69 self
70 }
71
72 /// Alias for `-y` argument: overwrite output files without asking.
73 pub fn overwrite(&mut self) -> &mut Self {
74 self.arg("-y");
75 self
76 }
77
78 /// Alias for `-n` argument: do not overwrite output files, and exit
79 /// immediately if a specified output file already exists.
80 pub fn no_overwrite(&mut self) -> &mut Self {
81 self.arg("-n");
82 self
83 }
84
85 /// Alias for `-c:v` argument.
86 ///
87 /// Select an encoder (when used before an output file) or a decoder (when
88 /// used before an input file) for one or more video streams. `codec` is the
89 /// name of a decoder/encoder or a special value `copy`` (output only) to
90 /// indicate that the stream is not to be re-encoded.
91 pub fn codec_video<S: AsRef<str>>(&mut self, codec: S) -> &mut Self {
92 self.arg("-c:v");
93 self.arg(codec.as_ref());
94 self
95 }
96
97 /// Alias for `-c:a` argument.
98 ///
99 /// Select an encoder (when used before an output file) or a decoder (when
100 /// used before an input file) for one or more audio streams. `codec` is the
101 /// name of a decoder/encoder or a special value `copy` (output only) to
102 /// indicate that the stream is not to be re-encoded.
103 pub fn codec_audio<S: AsRef<str>>(&mut self, codec: S) -> &mut Self {
104 self.arg("-c:a");
105 self.arg(codec.as_ref());
106 self
107 }
108
109 /// Alias for `-c:s` argument.
110 ///
111 /// Select an encoder (when used before an output file) or a decoder (when
112 /// used before an input file) for one or more subtitle streams. `codec` is
113 /// the name of a decoder/encoder or a special value `copy` (output only) to
114 /// indicate that the stream is not to be re-encoded.
115 pub fn codec_subtitle<S: AsRef<str>>(&mut self, codec: S) -> &mut Self {
116 self.arg("-c:s");
117 self.arg(codec.as_ref());
118 self
119 }
120
121 /// Alias for `-t` argument.
122 ///
123 /// When used as an input option (before `-i`), limit the duration of data
124 /// read from the input file.
125 ///
126 /// When used as an output option (before an output url), stop writing the
127 /// output after its duration reaches duration.
128 ///
129 /// `duration` must be a time duration specification, see [(ffmpeg-utils)the
130 /// Time duration section in the ffmpeg-utils(1)
131 /// manual](https://ffmpeg.org/ffmpeg-utils.html#time-duration-syntax).
132 ///
133 /// `-to` and `-t` are mutually exclusive and -t has priority.
134 pub fn duration<S: AsRef<str>>(&mut self, duration: S) -> &mut Self {
135 self.arg("-t");
136 self.arg(duration.as_ref());
137 self
138 }
139
140 /// Alias for `-to` argument.
141 ///
142 /// Stop writing the output or reading the input at `position`. `position`
143 /// must be a time duration specification, see [(ffmpeg-utils)the Time
144 /// duration section in the ffmpeg-utils(1)
145 /// manual](https://ffmpeg.org/ffmpeg-utils.html#time-duration-syntax).
146 ///
147 /// `-to` and `-t` (aka `duration()`) are mutually exclusive and `-t` has
148 /// priority.
149 pub fn to<S: AsRef<str>>(&mut self, position: S) -> &mut Self {
150 self.arg("-to");
151 self.arg(position.as_ref());
152 self
153 }
154
155 /// Alias for `-fs` argument.
156 ///
157 /// Set the file size limit, expressed in bytes. No further chunk of bytes is
158 /// written after the limit is exceeded. The size of the output file is
159 /// slightly more than the requested file size.
160 pub fn limit_file_size(&mut self, size_in_bytes: u32) -> &mut Self {
161 self.arg("-fs");
162 self.arg(size_in_bytes.to_string());
163 self
164 }
165
166 /// Alias for `-ss` argument.
167 ///
168 /// When used as an input option (before `-i`), seeks in this input file to
169 /// position. Note that in most formats it is not possible to seek exactly, so
170 /// `ffmpeg` will seek to the closest seek point before `position`. When
171 /// transcoding and `-accurate_seek` is enabled (the default), this extra
172 /// segment between the seek point and `position` will be decoded and
173 /// discarded. When doing stream copy or when `-noaccurate_seek` is used, it
174 /// will be preserved.
175 ///
176 /// When used as an output option (before an output url), decodes but discards
177 /// input until the timestamps reach `position`.
178 ///
179 /// `position` must be a time duration specification, see [(ffmpeg-utils)the
180 /// Time duration section in the ffmpeg-utils(1)
181 /// manual](https://ffmpeg.org/ffmpeg-utils.html#time-duration-syntax).
182 pub fn seek<S: AsRef<str>>(&mut self, position: S) -> &mut Self {
183 self.arg("-ss");
184 self.arg(position.as_ref());
185 self
186 }
187
188 /// Alias for `-sseof` argument.
189 ///
190 /// Like the `-ss` option but relative to the "end of file". That is negative
191 /// values are earlier in the file, 0 is at EOF.
192 pub fn seek_eof<S: AsRef<str>>(&mut self, position: S) -> &mut Self {
193 self.arg("-sseof");
194 self.arg(position.as_ref());
195 self
196 }
197
198 /// Alias for `-filter` argument.
199 ///
200 /// Create the filtergraph specified by `filtergraph` and use it to filter the
201 /// stream.
202 ///
203 /// `filtergraph` is a description of the filtergraph to apply to the stream,
204 /// and must have a single input and a single output of the same type of the
205 /// stream. In the filtergraph, the input is associated to the label `in`, and
206 /// the output to the label `out`. See the ffmpeg-filters manual for more
207 /// information about the filtergraph syntax.
208 ///
209 /// See the [`-filter_complex`
210 /// option](https://ffmpeg.org/ffmpeg.html#filter_005fcomplex_005foption) if
211 /// you want to create filtergraphs with multiple inputs and/or outputs.
212 pub fn filter<S: AsRef<str>>(&mut self, filtergraph: S) -> &mut Self {
213 self.arg("-filter");
214 self.arg(filtergraph.as_ref());
215 self
216 }
217
218 //// Video option aliases
219 //// https://ffmpeg.org/ffmpeg.html#Video-Options
220
221 /// Alias for '-crf:v' argument.
222 ///
223 /// Set CRF (Constant Rate Factor) for quality-based VBR (Variable BitRate)
224 ///
225 /// Use this rate control mode if you want to keep the best quality and care
226 /// less about the file size. Lower values means better quality with
227 /// bigger average bitrate (0 usually means lossless).
228 ///
229 /// Possible values depend on codec:
230 /// * 0-51 for h264 (default is 23), see [ffmpeg encoding guide for h264
231 /// for more details](https://trac.ffmpeg.org/wiki/Encode/H.264#crf)
232 /// * 0-51 for h265 (default is 28), see [ffmpeg encoding guide for h265
233 /// for more details](https://trac.ffmpeg.org/wiki/Encode/H.265#ConstantRateFactorCRF)
234 /// * 0-63 for vp9 (no default, 31 is recommended for 1080p HD video),
235 /// see [ffmpeg encoding guide for vp9 for more details](https://trac.ffmpeg.org/wiki/Encode/VP9#constrainedq)
236 /// * 0-63 for av1(libaom-av1) (no default), see [ffmpeg encoding guide
237 /// for libaom for more details](https://trac.ffmpeg.org/wiki/Encode/AV1#ConstantQuality)
238 /// * 0-63 for av1(libsvtav1) (default is 30), see [ffmpeg encoding guide
239 /// for svt-av1 for mode details](https://trac.ffmpeg.org/wiki/Encode/AV1#CRF)
240 pub fn crf(&mut self, crf: u32) -> &mut Self {
241 self.arg("-crf:v");
242 self.arg(crf.to_string());
243 self
244 }
245
246 /// Alias for `-frames:v` argument.
247 ///
248 /// Stop writing to the stream after `framecount` frames.
249 ///
250 /// See also: `-frames:a` (audio), `-frames:d` (data).
251 pub fn frames(&mut self, framecount: u32) -> &mut Self {
252 self.arg("-frames:v");
253 self.arg(framecount.to_string());
254 self
255 }
256
257 /// Alias for `-preset:v` argument.
258 ///
259 /// Set preset which is basically trade-off between encoding speed and
260 /// compression ratio.
261 ///
262 /// For h264 and h265 allowed values are:
263 /// * ultrafast
264 /// * superfast
265 /// * veryfast
266 /// * faster
267 /// * medium (default preset)
268 /// * slow
269 /// * slower
270 /// * veryslow
271 /// * placebo
272 ///
273 /// For svt-av1 supported values 0-13 (higher number providing a higher
274 /// encoding speed). Prior to version 0.9.0 valid values was 0-8.
275 ///
276 /// For libaom supported values 0-11 (higher number providing a higher
277 /// encoding speed)
278 ///
279 /// VP9 has no presets
280 pub fn preset<S: AsRef<str>>(&mut self, preset: S) -> &mut Self {
281 self.arg("-preset:v");
282 self.arg(preset.as_ref());
283 self
284 }
285
286 /// Alias for `-r` argument.
287 ///
288 /// Set frame rate (Hz value, fraction or abbreviation).
289 ///
290 /// As an input option, ignore any timestamps stored in the file and instead
291 /// generate timestamps assuming constant frame rate `fps`. This is not the
292 /// same as the `-framerate` option used for some input formats like image2 or
293 /// v4l2 (it used to be the same in older versions of FFmpeg). If in doubt use
294 /// `-framerate` instead of the input option `-r`.
295 pub fn rate(&mut self, fps: f32) -> &mut Self {
296 self.arg("-r");
297 self.arg(fps.to_string());
298 self
299 }
300
301 /// Alias for `-s` argument.
302 ///
303 /// Set frame size.
304 ///
305 /// As an input option, this is a shortcut for the `video_size` private
306 /// option, recognized by some demuxers for which the frame size is either not
307 /// stored in the file or is configurable – e.g. raw video or video grabbers.
308 ///
309 /// As an output option, this inserts the `scale` video filter to the end of
310 /// the corresponding filtergraph. Please use the `scale` filter directly to
311 /// insert it at the beginning or some other place.
312 ///
313 /// The format is `'wxh'` (default - same as source).
314 pub fn size(&mut self, width: u32, height: u32) -> &mut Self {
315 self.arg("-s");
316 self.arg(format!("{width}x{height}"));
317 self
318 }
319
320 /// Alias for `-vn` argument.
321 ///
322 /// As an input option, blocks all video streams of a file from being filtered
323 /// or being automatically selected or mapped for any output. See `-discard`
324 /// option to disable streams individually.
325 ///
326 /// As an output option, disables video recording i.e. automatic selection or
327 /// mapping of any video stream. For full manual control see the `-map`
328 /// option.
329 pub fn no_video(&mut self) -> &mut Self {
330 self.arg("-vn");
331 self
332 }
333
334 //// Advanced video option aliases
335 //// https://ffmpeg.org/ffmpeg.html#Advanced-Video-options
336
337 /// Alias for `-pix_fmt` argument.
338 ///
339 /// Set pixel format. Use `-pix_fmts` to show all the supported pixel formats.
340 /// If the selected pixel format can not be selected, ffmpeg will print a
341 /// warning and select the best pixel format supported by the encoder. If
342 /// pix_fmt is prefixed by a `+`, ffmpeg will exit with an error if the
343 /// requested pixel format can not be selected, and automatic conversions
344 /// inside filtergraphs are disabled. If pix_fmt is a single `+`, ffmpeg
345 /// selects the same pixel format as the input (or graph output) and automatic
346 /// conversions are disabled.
347 pub fn pix_fmt<S: AsRef<str>>(&mut self, format: S) -> &mut Self {
348 self.arg("-pix_fmt");
349 self.arg(format.as_ref());
350 self
351 }
352
353 /// Alias for `-hwaccel` argument.
354 ///
355 /// Use hardware acceleration to decode the matching stream(s). The allowed
356 /// values of hwaccel are:
357 ///
358 /// - `none`: Do not use any hardware acceleration (the default).
359 /// - `auto`: Automatically select the hardware acceleration method.
360 /// - `vdpau`: Use VDPAU (Video Decode and Presentation API for Unix) hardware
361 /// acceleration.
362 /// - `dxva2`: Use DXVA2 (DirectX Video Acceleration) hardware acceleration.
363 /// - `d3d11va`: Use D3D11VA (DirectX Video Acceleration) hardware
364 /// acceleration.
365 /// - `vaapi`: Use VAAPI (Video Acceleration API) hardware acceleration.
366 /// - `qsv`: Use the Intel QuickSync Video acceleration for video transcoding.
367 /// - Unlike most other values, this option does not enable accelerated
368 /// decoding (that is used automatically whenever a qsv decoder is selected),
369 /// but accelerated transcoding, without copying the frames into the system
370 /// memory.
371 /// - For it to work, both the decoder and the encoder must support QSV
372 /// acceleration and no filters must be used.
373 ///
374 /// This option has no effect if the selected hwaccel is not available or not
375 /// supported by the chosen decoder.
376 ///
377 /// Note that most acceleration methods are intended for playback and will not
378 /// be faster than software decoding on modern CPUs. Additionally, `ffmpeg`
379 /// will usually need to copy the decoded frames from the GPU memory into the
380 /// system memory, resulting in further performance loss. This option is thus
381 /// mainly useful for testing.
382 pub fn hwaccel<S: AsRef<str>>(&mut self, hwaccel: S) -> &mut Self {
383 self.arg("-hwaccel");
384 self.arg(hwaccel.as_ref());
385 self
386 }
387
388 //// Audio option aliases
389 //// https://ffmpeg.org/ffmpeg.html#Audio-Options
390
391 /// Alias for `-an` argument.
392 ///
393 /// As an input option, blocks all audio streams of a file from being filtered
394 /// or being automatically selected or mapped for any output. See `-discard`
395 /// option to disable streams individually.
396 ///
397 /// As an output option, disables audio recording i.e. automatic selection or
398 /// mapping of any audio stream. For full manual control see the `-map`
399 /// option.
400 pub fn no_audio(&mut self) -> &mut Self {
401 self.arg("-an");
402 self
403 }
404
405 //// Advanced option aliases
406 //// https://ffmpeg.org/ffmpeg.html#Advanced-options
407
408 /// Alias for `-map` argument.
409 ///
410 /// Create one or more streams in the output file. This option has two forms
411 /// for specifying the data source(s): the first selects one or more streams
412 /// from some input file (specified with `-i`), the second takes an output
413 /// from some complex filtergraph (specified with `-filter_complex` or
414 /// `-filter_complex_script`).
415 ///
416 /// In the first form, an output stream is created for every stream from the
417 /// input file with the index input_file_id. If stream_specifier is given,
418 /// only those streams that match the specifier are used (see the [Stream
419 /// specifiers](https://ffmpeg.org/ffmpeg.html#Stream-specifiers) section for
420 /// the stream_specifier syntax).
421 ///
422 /// A `-` character before the stream identifier creates a "negative" mapping.
423 /// It disables matching streams from already created mappings.
424 ///
425 /// A trailing `?` after the stream index will allow the map to be optional:
426 /// if the map matches no streams the map will be ignored instead of failing.
427 /// Note the map will still fail if an invalid input file index is used; such
428 /// as if the map refers to a non-existent input.
429 ///
430 /// An alternative `[linklabel]` form will map outputs from complex filter
431 /// graphs (see the `-filter_complex` option) to the output file. `linklabel`
432 /// must correspond to a defined output link label in the graph.
433 ///
434 /// This option may be specified multiple times, each adding more streams to
435 /// the output file. Any given input stream may also be mapped any number of
436 /// times as a source for different output streams, e.g. in order to use
437 /// different encoding options and/or filters. The streams are created in the
438 /// output in the same order in which the `-map` options are given on the
439 /// commandline.
440 ///
441 /// Using this option disables the default mappings for this output file.
442 pub fn map<S: AsRef<str>>(&mut self, map_string: S) -> &mut Self {
443 self.arg("-map");
444 self.arg(map_string.as_ref());
445 self
446 }
447
448 /// Alias for `-readrate` argument.
449 ///
450 /// Limit input read speed.
451 ///
452 /// Its value is a floating-point positive number which represents the maximum
453 /// duration of media, in seconds, that should be ingested in one second of
454 /// wallclock time. Default value is zero and represents no imposed limitation
455 /// on speed of ingestion. Value `1` represents real-time speed and is
456 /// equivalent to `-re`.
457 ///
458 /// Mainly used to simulate a capture device or live input stream (e.g. when
459 /// reading from a file). Should not be used with a low value when input is an
460 /// actual capture device or live stream as it may cause packet loss.
461 ///
462 /// It is useful for when flow speed of output packets is important, such as
463 /// live streaming.
464 pub fn readrate(&mut self, speed: f32) -> &mut Self {
465 self.arg("-readrate");
466 self.arg(speed.to_string());
467 self
468 }
469
470 /// Alias for `-re`.
471 ///
472 /// Read input at native frame rate. This is equivalent to setting `-readrate
473 /// 1`.
474 pub fn realtime(&mut self) -> &mut Self {
475 self.arg("-re");
476 self
477 }
478
479 /// Alias for `-fps_mode` argument.
480 ///
481 /// Set video sync method / framerate mode. vsync is applied to all output
482 /// video streams but can be overridden for a stream by setting fps_mode.
483 /// vsync is deprecated and will be removed in the future.
484 ///
485 /// For compatibility reasons some of the values for vsync can be specified as
486 /// numbers (shown in parentheses in the following table).
487 ///
488 /// - `passthrough` (`0`): Each frame is passed with its timestamp from the
489 /// demuxer to the muxer.
490 /// - `cfr` (`1`): Frames will be duplicated and dropped to achieve exactly
491 /// the requested constant frame rate.
492 /// - `vfr` (`2`): Frames are passed through with their timestamp or dropped
493 /// so as to prevent 2 frames from having the same timestamp.
494 /// - `drop`: As passthrough but destroys all timestamps, making the muxer
495 /// generate fresh timestamps based on frame-rate.
496 /// - `auto` (`-1`): Chooses between cfr and vfr depending on muxer
497 /// capabilities. This is the default method.
498 pub fn fps_mode<S: AsRef<str>>(&mut self, parameter: S) -> &mut Self {
499 self.arg("-fps_mode");
500 self.arg(parameter.as_ref());
501 self
502 }
503
504 /// Alias for `-bsf:v` argument.
505 ///
506 /// Set bitstream filters for matching streams. `bitstream_filters` is a
507 /// comma-separated list of bitstream filters. Use the `-bsfs` option to get
508 /// the list of bitstream filters.
509 ///
510 /// See also: `-bsf:s` (subtitles), `-bsf:a` (audio), `-bsf:d` (data)
511 pub fn bitstream_filter_video<S: AsRef<str>>(&mut self, bitstream_filters: S) -> &mut Self {
512 self.arg("-bsf:v");
513 self.arg(bitstream_filters.as_ref());
514 self
515 }
516
517 /// Alias for `-filter_complex` argument.
518 ///
519 /// Define a complex filtergraph, i.e. one with arbitrary number of inputs
520 /// and/or outputs. For simple graphs – those with one input and one output of
521 /// the same type – see the `-filter` options. `filtergraph` is a description
522 /// of the filtergraph, as described in the "Filtergraph syntax" section of
523 /// the ffmpeg-filters manual.
524 ///
525 /// Input link labels must refer to input streams using the
526 /// `[file_index:stream_specifier]` syntax (i.e. the same as `-map` uses). If
527 /// `stream_specifier` matches multiple streams, the first one will be used.
528 /// An unlabeled input will be connected to the first unused input stream of
529 /// the matching type.
530 ///
531 /// Output link labels are referred to with `-map`. Unlabeled outputs are
532 /// added to the first output file.
533 ///
534 /// Note that with this option it is possible to use only lavfi sources
535 /// without normal input files.
536 pub fn filter_complex<S: AsRef<str>>(&mut self, filtergraph: S) -> &mut Self {
537 self.arg("-filter_complex");
538 self.arg(filtergraph.as_ref());
539 self
540 }
541
542 //// Preset argument sets for common use cases.
543
544 /// Generate a procedural test video. Equivalent to `ffmpeg -f lavfi -i
545 /// testsrc=duration=10`. It also inherits defaults from the `testsrc` filter
546 /// in FFmpeg: `320x240` size and `25` fps.
547 ///
548 /// [FFmpeg `testsrc` filter
549 /// documentation](https://ffmpeg.org/ffmpeg-filters.html#allrgb_002c-allyuv_002c-color_002c-colorchart_002c-colorspectrum_002c-haldclutsrc_002c-nullsrc_002c-pal75bars_002c-pal100bars_002c-rgbtestsrc_002c-smptebars_002c-smptehdbars_002c-testsrc_002c-testsrc2_002c-yuvtestsrc)
550 pub fn testsrc(&mut self) -> &mut Self {
551 self.args(["-f", "lavfi", "-i", "testsrc=duration=10"]);
552 self
553 }
554
555 /// Preset for emitting raw decoded video frames on stdout. Equivalent to `-f
556 /// rawvideo -pix_fmt rgb24 -`.
557 pub fn rawvideo(&mut self) -> &mut Self {
558 self.args(["-f", "rawvideo", "-pix_fmt", "rgb24", "-"]);
559 self
560 }
561
562 /// Configure the ffmpeg command to produce output on stdout.
563 ///
564 /// Synchronizes two changes:
565 /// 1. Pass `pipe:1` to the ffmpeg command ("output on stdout")
566 /// 2. Set the `stdout` field of the inner `Command` to `Stdio::piped()`
567 pub fn pipe_stdout(&mut self) -> &mut Self {
568 self.arg("-");
569 self.inner.stdout(Stdio::piped());
570 self
571 }
572
573 /// Automatically applied in the constructor of `FfmpegCommand`. Configures
574 /// logging with a level and format expected by the log parser.
575 ///
576 /// Equivalent to `ffmpeg -loglevel level+info`.
577 ///
578 /// The `level` flag adds a prefix to all log messages with the log level in
579 /// square brackets, allowing the parser to distinguish between ambiguous
580 /// messages like warnings vs errors.
581 ///
582 /// The `+info` flag enables the `info` log level, which is the default level.
583 ///
584 /// If this settings is manually overridden, the log parser should still work,
585 /// but lose some semantic distinction between log levels.
586 fn set_expected_loglevel(&mut self) -> &mut Self {
587 self.args(["-loglevel", "level+info"]);
588 self
589 }
590
591 //// `std::process::Command` passthrough methods
592
593 ///
594 /// Adds an argument to pass to the program.
595 ///
596 /// Identical to `arg` in [`std::process::Command`].
597 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
598 self.inner.arg(arg.as_ref());
599 self
600 }
601
602 /// Adds multiple arguments to pass to the program.
603 ///
604 /// Identical to `args` in [`std::process::Command`].
605 pub fn args<I, S>(&mut self, args: I) -> &mut Self
606 where
607 I: IntoIterator<Item = S>,
608 S: AsRef<OsStr>,
609 {
610 for arg in args {
611 self.arg(arg.as_ref());
612 }
613 self
614 }
615
616 /// Returns an iterator of the arguments that will be passed to the program.
617 ///
618 /// Identical to `get_args` in [`std::process::Command`].
619 pub fn get_args(&self) -> CommandArgs<'_> {
620 self.inner.get_args()
621 }
622
623 /// Appends `-n` (no overwrite) to the args list if needed.
624 /// The interactive "Would you like to overwrite?" prompt is problematic,
625 /// since it won't be parsed by the log parser and the process will appear
626 /// to hang indefinitely without any indication of what's happening.
627 fn prevent_overwrite_prompt(&mut self) -> &mut Self {
628 let is_overwrite_arg = |arg| arg == "-y" || arg == "-n" || arg == "-nostdin";
629 if !self.get_args().any(is_overwrite_arg) {
630 self.no_overwrite();
631 }
632 self
633 }
634
635 /// Spawn the ffmpeg command as a child process, wrapping it in a
636 /// `FfmpegChild` interface.
637 ///
638 /// Please note that if the result is not used with [`wait()`](FfmpegChild::wait)
639 /// the process is not cleaned up correctly resulting in a zombie process
640 /// until your main thread exits.
641 ///
642 /// Identical to `spawn` in [`std::process::Command`].
643 pub fn spawn(&mut self) -> io::Result<FfmpegChild> {
644 self.prevent_overwrite_prompt();
645 self.inner.spawn().map(FfmpegChild::from_inner)
646 }
647
648 /// Print a command that can be copy-pasted to run in the terminal. Requires
649 /// `&mut self` so that it chains seamlessly with other methods in the
650 /// interface. Sample output:
651 ///
652 /// ```sh
653 /// ffmpeg \
654 /// -f lavfi \
655 /// -i testsrc=duration=10 output/test.mp4
656 /// ```
657 pub fn print_command(&mut self) -> &mut Self {
658 let program = self.inner.get_program().to_str();
659 let args = self
660 .inner
661 .get_args()
662 .filter_map(|s| {
663 s.to_str().map(|s| {
664 if s.starts_with('-') {
665 format!("\\\n {s}")
666 } else {
667 s.to_owned()
668 }
669 })
670 })
671 .collect::<Vec<_>>();
672 if let Some(program) = program {
673 println!("{} {}", program, args.join(" "));
674 }
675
676 self
677 }
678
679 /// Disable creating a new console window for the spawned process on Windows.
680 /// Has no effect on other platforms. This can be useful when spawning a command
681 /// from a GUI program.
682 ///
683 /// This is called automatically in the constructor. To override, use
684 /// `CommandExt::creation_flags()` directly on the inner `Command`.
685 pub fn create_no_window(&mut self) -> &mut Self {
686 self.as_inner_mut().create_no_window();
687 self
688 }
689
690 //// Constructors
691 pub fn new() -> Self {
692 Self::new_with_path(ffmpeg_path())
693 }
694
695 pub fn new_with_path<S: AsRef<OsStr>>(path_to_ffmpeg_binary: S) -> Self {
696 // Configure `Command`
697 let mut inner = Command::new(&path_to_ffmpeg_binary);
698 inner.stdin(Stdio::piped());
699 inner.stderr(Stdio::piped());
700 inner.stdout(Stdio::piped());
701
702 // Configure `FfmpegCommand`
703 let mut ffmpeg_command = Self { inner };
704 ffmpeg_command.set_expected_loglevel();
705 ffmpeg_command.create_no_window();
706 ffmpeg_command
707 }
708
709 //// Escape hatches
710
711 /// Escape hatch to access the inner `Command`.
712 pub fn as_inner(&mut self) -> &Command {
713 &self.inner
714 }
715
716 /// Escape hatch to mutably access the inner `Command`.
717 pub fn as_inner_mut(&mut self) -> &mut Command {
718 &mut self.inner
719 }
720}
721
722impl Default for FfmpegCommand {
723 fn default() -> Self {
724 Self::new()
725 }
726}
727
728impl fmt::Debug for FfmpegCommand {
729 /// Format the program and arguments of a Command for display. Any non-utf8
730 /// data is lossily converted using the utf8 replacement character.
731 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
732 self.inner.fmt(f)
733 }
734}
735
736impl From<Command> for FfmpegCommand {
737 /// Convert a `Command` into a `FfmpegCommand`, making no guarantees about the
738 /// validity of its configured arguments and stdio. For example,
739 /// `set_expected_loglevel()` is not automatically applied, which can have
740 /// unexpected effects on log parsing.
741 fn from(inner: Command) -> Self {
742 Self { inner }
743 }
744}
745
746impl From<FfmpegCommand> for Command {
747 fn from(val: FfmpegCommand) -> Self {
748 val.inner
749 }
750}
751
752/// Verify whether ffmpeg is installed on the system. This will return true if
753/// there is an ffmpeg binary in the PATH, or in the same directory as the Rust
754/// executable.
755pub fn ffmpeg_is_installed() -> bool {
756 Command::new(ffmpeg_path())
757 .arg("-version")
758 .create_no_window()
759 .stderr(Stdio::null())
760 .stdout(Stdio::null())
761 .status()
762 .map(|s| s.success())
763 .unwrap_or_else(|_| false)
764}
765
766pub(crate) trait BackgroundCommand {
767 fn create_no_window(&mut self) -> &mut Self;
768}
769
770impl BackgroundCommand for Command {
771 /// Disable creating a new console window for the spawned process on Windows.
772 /// Has no effect on other platforms. This can be useful when spawning a command
773 /// from a GUI program.
774 fn create_no_window(&mut self) -> &mut Self {
775 #[cfg(target_os = "windows")]
776 std::os::windows::process::CommandExt::creation_flags(self, 0x08000000);
777 self
778 }
779}