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}