Skip to main content

zvidlib_core/
api.rs

1use std::error;
2use std::fmt;
3
4/// A specialized result returned by zvidlib operations.
5pub type Result<T> = std::result::Result<T, Error>;
6
7/// Stable, backend-independent error categories.
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9#[non_exhaustive]
10pub enum ErrorKind {
11    InvalidInput,
12    Unsupported,
13    MalformedMedia,
14    ResourceLimit,
15    Io,
16    Codec,
17    Graphics,
18    Canceled,
19    InvalidState,
20    Internal,
21    /// The operation could not be satisfied without waiting, and the caller asked not to wait.
22    ///
23    /// A non-blocking source returns this rather than blocking its caller - a render thread
24    /// polling for a frame a background decoder has not produced yet - and the caller retries.
25    /// It says nothing about whether the work will succeed, only that it has not finished.
26    WouldBlock,
27}
28
29/// An error with a stable category and human-readable context.
30#[derive(Clone, Debug, Eq, PartialEq)]
31pub struct Error {
32    kind: ErrorKind,
33    message: String,
34}
35
36impl Error {
37    pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
38        Self {
39            kind,
40            message: message.into(),
41        }
42    }
43
44    pub const fn kind(&self) -> ErrorKind {
45        self.kind
46    }
47
48    pub fn message(&self) -> &str {
49        &self.message
50    }
51}
52
53impl fmt::Display for Error {
54    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(formatter, "{}", self.message)
56    }
57}
58
59impl error::Error for Error {}
60
61/// Whether a capability can be used in the current configuration.
62#[derive(Clone, Debug, Eq, PartialEq)]
63pub enum Support {
64    Available,
65    Unavailable { reason: String },
66}
67
68impl Support {
69    pub const fn is_available(&self) -> bool {
70        matches!(self, Self::Available)
71    }
72}
73
74/// The cost class for moving a video image to or from a graphics resource.
75#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
76#[non_exhaustive]
77pub enum TransferMode {
78    Shared,
79    GpuCopy,
80    CpuCopy,
81    Unsupported,
82}
83
84/// A named feature and its runtime support state.
85#[derive(Clone, Debug, Eq, PartialEq)]
86pub struct Capability {
87    pub name: String,
88    pub support: Support,
89    pub transfer_mode: Option<TransferMode>,
90}
91
92/// Resource limits applied before media-controlled allocations occur.
93#[derive(Clone, Copy, Debug, Eq, PartialEq)]
94pub struct Limits {
95    pub max_width: u32,
96    pub max_height: u32,
97    pub max_audio_channels: u16,
98    pub max_sample_rate: u32,
99    pub max_tracks: u16,
100    pub max_allocation_bytes: u64,
101    pub max_cached_frames: u32,
102    pub max_decode_samples_per_seek: u32,
103    /// Maximum number of OBUs accepted in one AV1 low-overhead unit.
104    pub max_av1_obus_per_unit: u32,
105    /// Maximum number of operating points accepted in an AV1 sequence header.
106    pub max_av1_operating_points: u8,
107    /// Maximum number of AV1 tiles accepted in one coded frame.
108    pub max_av1_tiles_per_frame: u32,
109    /// Maximum number of 4x4 AV1 reconstruction blocks decoded per frame.
110    pub max_av1_blocks_per_frame: u32,
111}
112
113impl Default for Limits {
114    fn default() -> Self {
115        Self {
116            max_width: 8_192,
117            max_height: 8_192,
118            max_audio_channels: 32,
119            max_sample_rate: 384_000,
120            max_tracks: 64,
121            max_allocation_bytes: 512 * 1024 * 1024,
122            max_cached_frames: 32,
123            max_decode_samples_per_seek: 4_096,
124            max_av1_obus_per_unit: 4_096,
125            max_av1_operating_points: 32,
126            max_av1_tiles_per_frame: 4_096,
127            max_av1_blocks_per_frame: 4_194_304,
128        }
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn errors_keep_their_stable_category() {
138        let error = Error::new(ErrorKind::MalformedMedia, "box exceeds its parent");
139        assert_eq!(error.kind(), ErrorKind::MalformedMedia);
140        assert_eq!(error.to_string(), "box exceeds its parent");
141    }
142
143    #[test]
144    fn default_limits_are_conservative_and_nonzero() {
145        let limits = Limits::default();
146        assert!(limits.max_width > 0);
147        assert!(limits.max_allocation_bytes > 0);
148        assert!(limits.max_cached_frames > 0);
149        assert!(limits.max_decode_samples_per_seek > 0);
150        assert!(limits.max_av1_obus_per_unit > 0);
151        assert!(limits.max_av1_operating_points > 0);
152        assert!(limits.max_av1_tiles_per_frame > 0);
153        assert!(limits.max_av1_blocks_per_frame > 0);
154    }
155}