Skip to main content

oxideav_core/
capabilities.rs

1//! Codec capability description.
2//!
3//! Each codec implementation registered with the codec registry attaches one
4//! of these structs to declare what it can do, what its constraints are, and
5//! how the registry should rank it against alternative implementations of
6//! the same codec id.
7//!
8//! The flag layout is a 6-column capability string (one letter per
9//! capability, `.` when absent):
10//!
11//! ```text
12//!  D..... = Decoding supported
13//!  .E.... = Encoding supported
14//!  ..V... = Video codec       ..A... = Audio       ..S... = Subtitle
15//!  ..D... = Data              ..T... = Attachment
16//!  ...I.. = Intra-frame-only codec
17//!  ....L. = Lossy compression
18//!  .....S = Lossless compression
19//! ```
20
21use std::fmt;
22
23use crate::format::{MediaType, PixelFormat};
24
25/// Default priority for software implementations. Lower numbers are preferred
26/// at resolution time, so register hardware impls with a smaller value (e.g.
27/// `10`) and software fallbacks with the default `100`.
28pub const DEFAULT_PRIORITY: i32 = 100;
29
30/// What an implementation can do plus how it ranks vs alternatives.
31#[derive(Clone, Debug)]
32pub struct CodecCapabilities {
33    /// Decoding supported by this implementation.
34    pub decode: bool,
35    /// Encoding supported by this implementation.
36    pub encode: bool,
37    /// Media type this implementation handles (audio, video, ...).
38    pub media_type: MediaType,
39    /// Every coded unit is independently decodable (no inter-frame
40    /// prediction).
41    pub intra_only: bool,
42    /// Supports lossy compression.
43    pub lossy: bool,
44    /// Supports lossless compression. `lossy` and `lossless` may both
45    /// be set for codecs that offer both modes.
46    pub lossless: bool,
47    /// Hardware-accelerated implementation (VAAPI/NVENC/QSV/VideoToolbox/...).
48    pub hardware_accelerated: bool,
49    /// Short identifier for this implementation, e.g. "flac_sw", "h264_qsv".
50    pub implementation: String,
51    /// Restrictions — `None` means "no constraint".
52    pub max_width: Option<u32>,
53    /// Maximum supported frame height in pixels; `None` = unconstrained.
54    pub max_height: Option<u32>,
55    /// Maximum supported bit rate in bits per second; `None` =
56    /// unconstrained.
57    pub max_bitrate: Option<u64>,
58    /// Maximum supported audio sample rate in Hz; `None` = unconstrained.
59    pub max_sample_rate: Option<u32>,
60    /// Maximum supported audio channel count; `None` = unconstrained.
61    pub max_channels: Option<u16>,
62    /// Lower numbers are preferred. HW impls should be ~10, SW impls ~100.
63    pub priority: i32,
64    /// Pixel formats this implementation accepts (video only). An empty
65    /// `Vec` means "any format" — resolution won't filter on it. When
66    /// populated, the registry can skip impls whose accepted set does not
67    /// include the format requested by the caller.
68    pub accepted_pixel_formats: Vec<PixelFormat>,
69}
70
71impl CodecCapabilities {
72    /// Construct a software audio decoder/encoder capability set with sensible
73    /// defaults — adjust fields after creation.
74    pub fn audio(implementation: impl Into<String>) -> Self {
75        Self {
76            decode: false,
77            encode: false,
78            media_type: MediaType::Audio,
79            intra_only: true, // audio packets are independently decodable in most codecs
80            lossy: false,
81            lossless: false,
82            hardware_accelerated: false,
83            implementation: implementation.into(),
84            max_width: None,
85            max_height: None,
86            max_bitrate: None,
87            max_sample_rate: None,
88            max_channels: None,
89            priority: DEFAULT_PRIORITY,
90            accepted_pixel_formats: Vec::new(),
91        }
92    }
93
94    /// Construct a software video decoder/encoder capability set with
95    /// sensible defaults — adjust fields after creation.
96    pub fn video(implementation: impl Into<String>) -> Self {
97        Self {
98            decode: false,
99            encode: false,
100            media_type: MediaType::Video,
101            intra_only: false,
102            lossy: false,
103            lossless: false,
104            hardware_accelerated: false,
105            implementation: implementation.into(),
106            max_width: None,
107            max_height: None,
108            max_bitrate: None,
109            max_sample_rate: None,
110            max_channels: None,
111            priority: DEFAULT_PRIORITY,
112            accepted_pixel_formats: Vec::new(),
113        }
114    }
115
116    /// 6-character capability flag string (see the module docs for the
117    /// column layout). Useful for `oxideav list`-style
118    /// output.
119    pub fn flag_string(&self) -> String {
120        let mut s = String::with_capacity(6);
121        s.push(if self.decode { 'D' } else { '.' });
122        s.push(if self.encode { 'E' } else { '.' });
123        s.push(match self.media_type {
124            MediaType::Video => 'V',
125            MediaType::Audio => 'A',
126            MediaType::Subtitle => 'S',
127            MediaType::Data => 'D',
128            MediaType::Unknown => '.',
129        });
130        s.push(if self.intra_only { 'I' } else { '.' });
131        s.push(if self.lossy { 'L' } else { '.' });
132        s.push(if self.lossless { 'S' } else { '.' });
133        s
134    }
135
136    // Builder-style helpers so registrations stay compact.
137
138    /// Mark this implementation as supporting decode.
139    pub fn with_decode(mut self) -> Self {
140        self.decode = true;
141        self
142    }
143    /// Mark this implementation as supporting encode.
144    pub fn with_encode(mut self) -> Self {
145        self.encode = true;
146        self
147    }
148    /// Set the intra-frame-only flag.
149    pub fn with_intra_only(mut self, v: bool) -> Self {
150        self.intra_only = v;
151        self
152    }
153    /// Set the lossy-compression flag.
154    pub fn with_lossy(mut self, v: bool) -> Self {
155        self.lossy = v;
156        self
157    }
158    /// Set the lossless-compression flag.
159    pub fn with_lossless(mut self, v: bool) -> Self {
160        self.lossless = v;
161        self
162    }
163    /// Set the hardware-accelerated flag.
164    pub fn with_hardware(mut self, v: bool) -> Self {
165        self.hardware_accelerated = v;
166        self
167    }
168    /// Set the registry ranking priority (lower is preferred; HW ~10,
169    /// SW ~100).
170    pub fn with_priority(mut self, p: i32) -> Self {
171        self.priority = p;
172        self
173    }
174    /// Constrain the maximum frame size to `w` × `h` pixels.
175    pub fn with_max_size(mut self, w: u32, h: u32) -> Self {
176        self.max_width = Some(w);
177        self.max_height = Some(h);
178        self
179    }
180    /// Constrain the maximum bit rate (bits per second).
181    pub fn with_max_bitrate(mut self, br: u64) -> Self {
182        self.max_bitrate = Some(br);
183        self
184    }
185    /// Constrain the maximum audio sample rate (Hz).
186    pub fn with_max_sample_rate(mut self, sr: u32) -> Self {
187        self.max_sample_rate = Some(sr);
188        self
189    }
190    /// Constrain the maximum audio channel count.
191    pub fn with_max_channels(mut self, ch: u16) -> Self {
192        self.max_channels = Some(ch);
193        self
194    }
195
196    /// Add one accepted pixel format. Appends — call multiple times to
197    /// list several.
198    pub fn with_pixel_format(mut self, fmt: PixelFormat) -> Self {
199        self.accepted_pixel_formats.push(fmt);
200        self
201    }
202
203    /// Replace the accepted pixel-format set wholesale.
204    pub fn with_pixel_formats(mut self, fmts: Vec<PixelFormat>) -> Self {
205        self.accepted_pixel_formats = fmts;
206        self
207    }
208}
209
210impl fmt::Display for CodecCapabilities {
211    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212        write!(f, "{} {}", self.flag_string(), self.implementation)
213    }
214}
215
216impl CodecCapabilities {
217    /// Whether this implementation's max-* restrictions are compatible
218    /// with the requested codec parameters. `for_encode` is reserved
219    /// for restrictions that apply asymmetrically. Used by the
220    /// registry's `make_decoder` / `make_encoder` walker and by
221    /// out-of-tree selection layers (e.g. `oxideav-pipeline`'s
222    /// `CodecPreferences` filter).
223    pub fn fits_params(&self, p: &crate::CodecParameters, for_encode: bool) -> bool {
224        let _ = for_encode;
225        if let (Some(max), Some(w)) = (self.max_width, p.width) {
226            if w > max {
227                return false;
228            }
229        }
230        if let (Some(max), Some(h)) = (self.max_height, p.height) {
231            if h > max {
232                return false;
233            }
234        }
235        if let (Some(max), Some(br)) = (self.max_bitrate, p.bit_rate) {
236            if br > max {
237                return false;
238            }
239        }
240        if let (Some(max), Some(sr)) = (self.max_sample_rate, p.sample_rate) {
241            if sr > max {
242                return false;
243            }
244        }
245        if let (Some(max), Some(ch)) = (self.max_channels, p.channels) {
246            if ch > max {
247                return false;
248            }
249        }
250        true
251    }
252}