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}