1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use std::{
io::{self, Write},
process::{Child, ChildStderr, ChildStdin, ChildStdout},
};
use crate::error::{Error, Result};
use crate::iter::FfmpegIterator;
/// A wrapper around [`std::process::Child`] containing a spawned FFmpeg command.
/// Provides interfaces for reading parsed metadata, progress updates, warnings and errors, and
/// piped output frames if applicable.
pub struct FfmpegChild {
inner: Child,
}
impl FfmpegChild {
/// Creates an iterator over events emitted by ffmpeg. Functions similarly to
/// `Lines` from [`std::io::BufReader`], but providing a variety of parsed
/// events:
/// - Log messages
/// - Parsed metadata
/// - Progress updates
/// - Errors and warnings
/// - Raw output frames
pub fn iter(&mut self) -> Result<FfmpegIterator> {
FfmpegIterator::new(self)
}
/// Escape hatch to manually control the process' stdout channel.
/// Calling this method takes ownership of the stdout channel, so
/// the iterator will no longer include output frames in the stream of events.
pub fn take_stdout(&mut self) -> Option<ChildStdout> {
self.inner.stdout.take()
}
/// Escape hatch to manually control the process' stderr channel.
/// This method is mutually exclusive with `events_iter`, which relies on
/// the stderr channel to parse events.
pub fn take_stderr(&mut self) -> Option<ChildStderr> {
self.inner.stderr.take()
}
/// Escape hatch to manually control the process' stdin channel.
/// This method is mutually exclusive with `send_stdin_command` and `quit`,
/// which use the stdin channel to send commands to ffmpeg.
pub fn take_stdin(&mut self) -> Option<ChildStdin> {
self.inner.stdin.take()
}
/// Send a command to ffmpeg over stdin, used during interactive mode.
///
/// This method does not validate that the command is expected or handled
/// correctly by ffmpeg. The returned `io::Result` indicates only whether the
/// command was successfully sent or not.
///
/// In a typical ffmpeg build, these are the supported commands:
///
/// ```txt
/// ? show this help
/// + increase verbosity
/// - decrease verbosity
/// c Send command to first matching filter supporting it
/// C Send/Queue command to all matching filters
/// D cycle through available debug modes
/// h dump packets/hex press to cycle through the 3 states
/// q quit
/// s Show QP histogram
/// ```
pub fn send_stdin_command(&mut self, command: &[u8]) -> Result<()> {
let mut stdin = self
.inner
.stdin
.take()
.ok_or_else(|| Error::msg("Missing child stdin"))?;
stdin.write_all(command)?;
self.inner.stdin.replace(stdin);
Ok(())
}
/// Send a `q` command to ffmpeg over stdin,
/// requesting a graceful shutdown as soon as possible.
///
/// This method returns after the command has been sent; the actual shut down
/// may take a few more frames as ffmpeg flushes its buffers and writes the
/// trailer, if applicable.
pub fn quit(&mut self) -> Result<()> {
self.send_stdin_command(b"q")
}
/// Forcibly terminate the inner child process.
///
/// Alternatively, you may choose to gracefully stop the child process by
/// sending a command over stdin, using the `quit` method.
///
/// Identical to `kill` in [`std::process::Child`].
pub fn kill(&mut self) -> io::Result<()> {
self.inner.kill()
}
/// Wrap a [`std::process::Child`] in a `FfmpegChild`. Should typically only
/// be called by `FfmpegCommand::spawn`.
///
/// ## Panics
///
/// Panics if the any of the child process's stdio channels were not piped.
/// This could be because ffmpeg was spawned with `-nostdin`, or if the
/// `Child` instance was not configured with `stdin(Stdio::piped())`.
pub(crate) fn from_inner(inner: Child) -> Self {
assert!(inner.stdin.is_some(), "stdin was not piped");
assert!(inner.stdout.is_some(), "stdout was not piped");
assert!(inner.stderr.is_some(), "stderr was not piped");
Self { inner }
}
/// Escape hatch to access the inner `Child`.
pub fn as_inner(&mut self) -> &Child {
&self.inner
}
/// Escape hatch to mutably access the inner `Child`.
pub fn as_inner_mut(&mut self) -> &mut Child {
&mut self.inner
}
}