tokio_ffmpeg_cli/
encoder.rs

1use bitflags::bitflags;
2use std::str::FromStr;
3
4bitflags! {
5    /// Encoder capabilities
6    #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
7    pub struct Capabilities: u16 {
8        const VIDEO = 1 << 1;
9        const AUDIO = 1 << 2;
10        const SUBTITLE = 1 << 3;
11        const FRAME_LEVEL_MULTITHREADING = 1 << 4;
12        const SLICE_LEVEL_MULTITHREADING = 1 << 5;
13        const EXPERIMENTAL = 1 << 6;
14        const DRAW_HORIZ_BAND = 1 << 7;
15        const DIRECT_RENDERING_METHOD_1 = 1 << 8;
16    }
17}
18
19/// An error that may occur while parsing capabilities from a string
20#[derive(Debug, thiserror::Error)]
21pub enum FromStrError {
22    /// Missing a field
23    #[error("missing field")]
24    Missing,
25
26    /// A field had an invalid value
27    #[error("invalid field")]
28    Invalid,
29
30    /// A field was found when it was not expected
31    #[error("unexpected field")]
32    Unexpected,
33}
34
35impl FromStr for Capabilities {
36    type Err = FromStrError;
37    fn from_str(input: &str) -> Result<Self, Self::Err> {
38        let mut flags = Self::empty();
39        let mut iter = input.bytes();
40        match iter.next().ok_or(FromStrError::Missing)? {
41            b'V' => flags.insert(Capabilities::VIDEO),
42            b'A' => flags.insert(Capabilities::AUDIO),
43            b'S' => flags.insert(Capabilities::SUBTITLE),
44            _ => {
45                return Err(FromStrError::Invalid);
46            }
47        }
48
49        match iter.next().ok_or(FromStrError::Missing)? {
50            b'F' => flags.insert(Capabilities::FRAME_LEVEL_MULTITHREADING),
51            b'.' => {}
52            _ => {
53                return Err(FromStrError::Invalid);
54            }
55        }
56
57        match iter.next().ok_or(FromStrError::Missing)? {
58            b'S' => flags.insert(Capabilities::SLICE_LEVEL_MULTITHREADING),
59            b'.' => {}
60            _ => {
61                return Err(FromStrError::Invalid);
62            }
63        }
64
65        match iter.next().ok_or(FromStrError::Missing)? {
66            b'X' => flags.insert(Capabilities::EXPERIMENTAL),
67            b'.' => {}
68            _ => {
69                return Err(FromStrError::Invalid);
70            }
71        }
72
73        match iter.next().ok_or(FromStrError::Missing)? {
74            b'B' => flags.insert(Capabilities::DRAW_HORIZ_BAND),
75            b'.' => {}
76            _ => {
77                return Err(FromStrError::Invalid);
78            }
79        }
80
81        match iter.next().ok_or(FromStrError::Missing)? {
82            b'D' => flags.insert(Capabilities::DIRECT_RENDERING_METHOD_1),
83            b'.' => {}
84            _ => {
85                return Err(FromStrError::Invalid);
86            }
87        }
88
89        if iter.next().is_some() {
90            return Err(FromStrError::Unexpected);
91        }
92
93        Ok(flags)
94    }
95}
96
97/// An error that occurs while parsing an Encoder from a line
98#[derive(Debug, thiserror::Error)]
99pub enum FromLineError {
100    /// Missing the capabilities
101    #[error("missing capabilities")]
102    MissingCapabilities,
103
104    /// Missing the name
105    #[error("missing name")]
106    MissingName,
107
108    /// Missing the description
109    #[error("missing description")]
110    MissingDescription,
111
112    /// Invalid Capabilities
113    #[error("invalid capabilities")]
114    InvalidCapabilities(#[from] FromStrError),
115}
116
117#[derive(Debug)]
118pub struct Encoder {
119    /// Encoder capabilities
120    pub capabilities: Capabilities,
121
122    /// The name
123    pub name: Box<str>,
124
125    /// The description
126    pub description: Box<str>,
127}
128
129impl Encoder {
130    /// Parse an encoder from an output line
131    pub(crate) fn from_line(line: &str) -> Result<Self, FromLineError> {
132        let mut iter = line.splitn(3, char::is_whitespace);
133        let capabilities_str = iter.next().ok_or(FromLineError::MissingCapabilities)?;
134        let name = iter.next().ok_or(FromLineError::MissingName)?;
135        let description = iter.next().ok_or(FromLineError::MissingDescription)?.trim();
136
137        let capabilities = capabilities_str.parse()?;
138
139        Ok(Encoder {
140            capabilities,
141            name: name.into(),
142            description: description.into(),
143        })
144    }
145
146    /// Returns `true` if this is a video encoder
147    pub fn is_video(&self) -> bool {
148        self.capabilities.contains(Capabilities::VIDEO)
149    }
150}