async_ffmpeg_sidecar/
command.rs

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