tokio_ffmpeg_cli/
encoder.rs1use bitflags::bitflags;
2use std::str::FromStr;
3
4bitflags! {
5 #[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#[derive(Debug, thiserror::Error)]
21pub enum FromStrError {
22 #[error("missing field")]
24 Missing,
25
26 #[error("invalid field")]
28 Invalid,
29
30 #[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#[derive(Debug, thiserror::Error)]
99pub enum FromLineError {
100 #[error("missing capabilities")]
102 MissingCapabilities,
103
104 #[error("missing name")]
106 MissingName,
107
108 #[error("missing description")]
110 MissingDescription,
111
112 #[error("invalid capabilities")]
114 InvalidCapabilities(#[from] FromStrError),
115}
116
117#[derive(Debug)]
118pub struct Encoder {
119 pub capabilities: Capabilities,
121
122 pub name: Box<str>,
124
125 pub description: Box<str>,
127}
128
129impl Encoder {
130 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 pub fn is_video(&self) -> bool {
148 self.capabilities.contains(Capabilities::VIDEO)
149 }
150}