Skip to main content

ff_encode/
error.rs

1//! Error types for encoding operations.
2
3use std::path::PathBuf;
4use thiserror::Error;
5
6/// Encoding error type.
7#[derive(Error, Debug)]
8pub enum EncodeError {
9    /// Cannot create output file
10    #[error("Cannot create output file: {path}")]
11    CannotCreateFile {
12        /// File path that failed
13        path: PathBuf,
14    },
15
16    /// Unsupported codec
17    #[error("Unsupported codec: {codec}")]
18    UnsupportedCodec {
19        /// Codec name
20        codec: String,
21    },
22
23    /// No suitable encoder found
24    #[error("No suitable encoder found for {codec} (tried: {tried:?})")]
25    NoSuitableEncoder {
26        /// Requested codec
27        codec: String,
28        /// Attempted encoders
29        tried: Vec<String>,
30    },
31
32    /// Encoding failed at specific frame
33    #[error("Encoding failed at frame {frame}: {reason}")]
34    EncodingFailed {
35        /// Frame number where encoding failed
36        frame: u64,
37        /// Failure reason
38        reason: String,
39    },
40
41    /// Invalid configuration
42    #[error("Invalid configuration: {reason}")]
43    InvalidConfig {
44        /// Configuration issue description
45        reason: String,
46    },
47
48    /// Hardware encoder unavailable
49    #[error("Hardware encoder unavailable: {encoder}")]
50    HwEncoderUnavailable {
51        /// Hardware encoder name
52        encoder: String,
53    },
54
55    /// Specific encoder is unavailable — the hint explains what is needed.
56    #[error("encoder unavailable: codec={codec} hint={hint}")]
57    EncoderUnavailable {
58        /// Requested codec name (e.g. `"h265/hevc"`).
59        codec: String,
60        /// Human-readable guidance (e.g. how to build FFmpeg with this encoder).
61        hint: String,
62    },
63
64    /// Muxing failed
65    #[error("Muxing failed: {reason}")]
66    MuxingFailed {
67        /// Failure reason
68        reason: String,
69    },
70
71    /// `FFmpeg` error
72    #[error("ffmpeg error: {message} (code={code})")]
73    Ffmpeg {
74        /// Raw `FFmpeg` error code (negative integer). `0` when no numeric code is available.
75        code: i32,
76        /// Human-readable error message from `av_strerror` or an internal description.
77        message: String,
78    },
79
80    /// IO error
81    #[error("IO error: {0}")]
82    Io(#[from] std::io::Error),
83
84    /// Invalid option value
85    #[error("Invalid option: {name} — {reason}")]
86    InvalidOption {
87        /// Option name
88        name: String,
89        /// Description of the constraint that was violated
90        reason: String,
91    },
92
93    /// Codec is incompatible with the target container format
94    #[error("codec {codec} is not supported by container {container} — {hint}")]
95    UnsupportedContainerCodecCombination {
96        /// Container format name (e.g. `"webm"`)
97        container: String,
98        /// Codec name that was rejected (e.g. `"h264"`)
99        codec: String,
100        /// Human-readable guidance on compatible codecs
101        hint: String,
102    },
103
104    /// Encoding cancelled by user
105    #[error("Encoding cancelled by user")]
106    Cancelled,
107
108    /// Async encoder worker thread panicked or disconnected unexpectedly
109    #[error("Async encoder worker panicked or disconnected")]
110    WorkerPanicked,
111}
112
113impl EncodeError {
114    /// Create an error from an FFmpeg error code.
115    ///
116    /// This is more type-safe than implementing `From<i32>` globally,
117    /// as it makes the conversion explicit and prevents accidental
118    /// conversion of arbitrary i32 values.
119    pub(crate) fn from_ffmpeg_error(errnum: i32) -> Self {
120        EncodeError::Ffmpeg {
121            code: errnum,
122            message: ff_sys::av_error_string(errnum),
123        }
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::EncodeError;
130
131    #[test]
132    fn from_ffmpeg_error_should_return_ffmpeg_variant() {
133        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::EINVAL);
134        assert!(matches!(err, EncodeError::Ffmpeg { .. }));
135    }
136
137    #[test]
138    fn from_ffmpeg_error_should_carry_numeric_code() {
139        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::EINVAL);
140        match err {
141            EncodeError::Ffmpeg { code, .. } => assert_eq!(code, ff_sys::error_codes::EINVAL),
142            _ => panic!("expected Ffmpeg variant"),
143        }
144    }
145
146    #[test]
147    fn from_ffmpeg_error_should_format_with_code_in_display() {
148        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::EINVAL);
149        let msg = err.to_string();
150        assert!(msg.contains("code=-22"), "expected 'code=-22' in '{msg}'");
151    }
152
153    #[test]
154    fn from_ffmpeg_error_message_should_be_nonempty() {
155        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::ENOMEM);
156        assert!(!err.to_string().is_empty());
157    }
158
159    #[test]
160    fn from_ffmpeg_error_eof_should_be_constructible() {
161        let err = EncodeError::from_ffmpeg_error(ff_sys::error_codes::EOF);
162        assert!(matches!(err, EncodeError::Ffmpeg { .. }));
163        assert!(!err.to_string().is_empty());
164    }
165}