scuffle_ffmpeg/
error.rs

1use nutype_enum::nutype_enum;
2
3use crate::ffi::*;
4
5#[derive(Debug, thiserror::Error, PartialEq, Eq)]
6/// An error that occurs when the ffmpeg operation fails.
7pub enum FfmpegError {
8    /// An error that occurs when the memory allocation fails.
9    #[error("failed to allocate memory")]
10    Alloc,
11    /// An error that occurs when the ffmpeg error code is not a success code.
12    #[error("ffmpeg error: {0}")]
13    Code(#[from] FfmpegErrorCode),
14    /// An error that occurs when no decoder is found.
15    #[error("no decoder found")]
16    NoDecoder,
17    /// An error that occurs when no encoder is found.
18    #[error("no encoder found")]
19    NoEncoder,
20    /// An error that occurs when no stream is found.
21    #[error("no stream found")]
22    NoStream,
23    /// An error that occurs when no filter is found.
24    #[error("no filter found")]
25    NoFilter,
26    /// An error that occurs when no frame is found.
27    #[error("no frame found")]
28    NoFrame,
29    /// An error that occurs when the arguments are invalid.
30    #[error("invalid arguments: {0}")]
31    Arguments(&'static str),
32}
33
34nutype_enum! {
35    /// An enum that represents the ffmpeg error code.
36    pub enum FfmpegErrorCode(i32) {
37        /// FFmpeg error code for invalid arguments.
38        Einval = AVERROR(EINVAL),
39        /// FFmpeg error code for end of file.
40        EndOfFile = AVERROR_EOF,
41        /// FFmpeg error code for invalid data.
42        InvalidData = AVERROR_INVALIDDATA,
43        /// FFmpeg error code for muxer not found.
44        MuxerNotFound = AVERROR_MUXER_NOT_FOUND,
45        /// FFmpeg error code for option not found.
46        OptionNotFound = AVERROR_OPTION_NOT_FOUND,
47        /// FFmpeg error code for patch welcome.
48        PatchWelcome = AVERROR_PATCHWELCOME,
49        /// FFmpeg error code for protocol not found.
50        ProtocolNotFound = AVERROR_PROTOCOL_NOT_FOUND,
51        /// FFmpeg error code for stream not found.
52        StreamNotFound = AVERROR_STREAM_NOT_FOUND,
53        /// FFmpeg error code for bitstream filter not found.
54        BitstreamFilterNotFound = AVERROR_BSF_NOT_FOUND,
55        /// FFmpeg error code for bug.
56        Bug = AVERROR_BUG,
57        /// FFmpeg error code for eof.
58        Eof = AVERROR_EOF,
59        /// FFmpeg error code for eagain.
60        Eagain = AVERROR(EAGAIN),
61        /// FFmpeg error code for buffer too small.
62        BufferTooSmall = AVERROR_BUFFER_TOO_SMALL,
63        /// FFmpeg error code for decoder not found.
64        DecoderNotFound = AVERROR_DECODER_NOT_FOUND,
65        /// FFmpeg error code for demuxer not found.
66        DemuxerNotFound = AVERROR_DEMUXER_NOT_FOUND,
67        /// FFmpeg error code for encoder not found.
68        EncoderNotFound = AVERROR_ENCODER_NOT_FOUND,
69        /// FFmpeg error code for exit.
70        Exit = AVERROR_EXIT,
71        /// FFmpeg error code for external.
72        External = AVERROR_EXTERNAL,
73        /// FFmpeg error code for filter not found.
74        FilterNotFound = AVERROR_FILTER_NOT_FOUND,
75        /// FFmpeg error code for http bad request.
76        HttpBadRequest = AVERROR_HTTP_BAD_REQUEST,
77        /// FFmpeg error code for http forbidden.
78        HttpForbidden = AVERROR_HTTP_FORBIDDEN,
79        /// FFmpeg error code for http not found.
80        HttpNotFound = AVERROR_HTTP_NOT_FOUND,
81        /// FFmpeg error code for http other 4xx.
82        HttpOther4xx = AVERROR_HTTP_OTHER_4XX,
83        /// FFmpeg error code for http server error.
84        HttpServerError = AVERROR_HTTP_SERVER_ERROR,
85        /// FFmpeg error code for http unauthorized.
86        HttpUnauthorized = AVERROR_HTTP_UNAUTHORIZED,
87        /// FFmpeg error code for bug2.
88        Bug2 = AVERROR_BUG2,
89        /// FFmpeg error code for unknown.
90        Unknown = AVERROR_UNKNOWN,
91    }
92}
93
94impl FfmpegErrorCode {
95    /// Returns the result of the error code.
96    pub const fn result(self) -> Result<i32, FfmpegError> {
97        match self {
98            code if code.is_success() => Ok(code.0),
99            _ => Err(FfmpegError::Code(self)),
100        }
101    }
102
103    /// Returns true if the error code is a success code.
104    pub const fn is_success(self) -> bool {
105        self.0 >= 0
106    }
107}
108
109impl std::fmt::Display for FfmpegErrorCode {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        match *self {
112            Self::EndOfFile => write!(f, "end of file"),
113            Self::InvalidData => write!(f, "invalid data"),
114            Self::MuxerNotFound => write!(f, "muxer not found"),
115            Self::OptionNotFound => write!(f, "option not found"),
116            Self::PatchWelcome => write!(f, "patch welcome"),
117            Self::ProtocolNotFound => write!(f, "protocol not found"),
118            Self::StreamNotFound => write!(f, "stream not found"),
119            Self::BitstreamFilterNotFound => write!(f, "bitstream filter not found"),
120            Self::Bug => write!(f, "bug"),
121            Self::BufferTooSmall => write!(f, "buffer too small"),
122            Self::DecoderNotFound => write!(f, "decoder not found"),
123            Self::DemuxerNotFound => write!(f, "demuxer not found"),
124            Self::EncoderNotFound => write!(f, "encoder not found"),
125            Self::Exit => write!(f, "exit"),
126            Self::External => write!(f, "external"),
127            Self::FilterNotFound => write!(f, "filter not found"),
128            Self::HttpBadRequest => write!(f, "http bad request"),
129            Self::HttpForbidden => write!(f, "http forbidden"),
130            Self::HttpNotFound => write!(f, "http not found"),
131            Self::HttpOther4xx => write!(f, "http other 4xx"),
132            Self::HttpServerError => write!(f, "http server error"),
133            Self::HttpUnauthorized => write!(f, "http unauthorized"),
134            Self::Bug2 => write!(f, "bug2"),
135            Self::Unknown => write!(f, "unknown"),
136            Self(ec) => write!(f, "unknown error code: {ec}"),
137        }
138    }
139}
140
141impl std::error::Error for FfmpegErrorCode {}
142
143#[cfg(test)]
144#[cfg_attr(all(test, coverage_nightly), coverage(off))]
145mod tests {
146    use super::{FfmpegError, FfmpegErrorCode};
147    use crate::error::*;
148
149    #[test]
150    fn test_ffmpeg_error_code_display() {
151        let cases = [
152            (FfmpegErrorCode::EndOfFile, "end of file"),
153            (FfmpegErrorCode::InvalidData, "invalid data"),
154            (FfmpegErrorCode::MuxerNotFound, "muxer not found"),
155            (FfmpegErrorCode::OptionNotFound, "option not found"),
156            (FfmpegErrorCode::PatchWelcome, "patch welcome"),
157            (FfmpegErrorCode::ProtocolNotFound, "protocol not found"),
158            (FfmpegErrorCode::StreamNotFound, "stream not found"),
159            (FfmpegErrorCode::BitstreamFilterNotFound, "bitstream filter not found"),
160            (FfmpegErrorCode::Bug, "bug"),
161            (FfmpegErrorCode::BufferTooSmall, "buffer too small"),
162            (FfmpegErrorCode::DecoderNotFound, "decoder not found"),
163            (FfmpegErrorCode::DemuxerNotFound, "demuxer not found"),
164            (FfmpegErrorCode::EncoderNotFound, "encoder not found"),
165            (FfmpegErrorCode::Exit, "exit"),
166            (FfmpegErrorCode::External, "external"),
167            (FfmpegErrorCode::FilterNotFound, "filter not found"),
168            (FfmpegErrorCode::HttpBadRequest, "http bad request"),
169            (FfmpegErrorCode::HttpForbidden, "http forbidden"),
170            (FfmpegErrorCode::HttpNotFound, "http not found"),
171            (FfmpegErrorCode::HttpOther4xx, "http other 4xx"),
172            (FfmpegErrorCode::HttpServerError, "http server error"),
173            (FfmpegErrorCode::HttpUnauthorized, "http unauthorized"),
174            (FfmpegErrorCode::Bug2, "bug2"),
175            (FfmpegErrorCode::Unknown, "unknown"),
176            (FfmpegErrorCode(123), "unknown error code: 123"),
177        ];
178
179        for (code, expected) in cases {
180            assert_eq!(code.to_string(), expected);
181        }
182    }
183
184    #[test]
185    fn test_ffmpeg_error_code_from_i32() {
186        // Define constants that map to the FfmpegErrorCode variants
187        const TEST_CASES: &[(i32, FfmpegErrorCode)] = &[
188            (AVERROR_EOF, FfmpegErrorCode::EndOfFile),
189            (AVERROR_INVALIDDATA, FfmpegErrorCode::InvalidData),
190            (AVERROR_MUXER_NOT_FOUND, FfmpegErrorCode::MuxerNotFound),
191            (AVERROR_OPTION_NOT_FOUND, FfmpegErrorCode::OptionNotFound),
192            (AVERROR_PATCHWELCOME, FfmpegErrorCode::PatchWelcome),
193            (AVERROR_PROTOCOL_NOT_FOUND, FfmpegErrorCode::ProtocolNotFound),
194            (AVERROR_STREAM_NOT_FOUND, FfmpegErrorCode::StreamNotFound),
195            (AVERROR_BSF_NOT_FOUND, FfmpegErrorCode::BitstreamFilterNotFound),
196            (AVERROR_BUG, FfmpegErrorCode::Bug),
197            (AVERROR_BUFFER_TOO_SMALL, FfmpegErrorCode::BufferTooSmall),
198            (AVERROR_DECODER_NOT_FOUND, FfmpegErrorCode::DecoderNotFound),
199            (AVERROR_DEMUXER_NOT_FOUND, FfmpegErrorCode::DemuxerNotFound),
200            (AVERROR_ENCODER_NOT_FOUND, FfmpegErrorCode::EncoderNotFound),
201            (AVERROR_EXIT, FfmpegErrorCode::Exit),
202            (AVERROR_EXTERNAL, FfmpegErrorCode::External),
203            (AVERROR_FILTER_NOT_FOUND, FfmpegErrorCode::FilterNotFound),
204            (AVERROR_HTTP_BAD_REQUEST, FfmpegErrorCode::HttpBadRequest),
205            (AVERROR_HTTP_FORBIDDEN, FfmpegErrorCode::HttpForbidden),
206            (AVERROR_HTTP_NOT_FOUND, FfmpegErrorCode::HttpNotFound),
207            (AVERROR_HTTP_OTHER_4XX, FfmpegErrorCode::HttpOther4xx),
208            (AVERROR_HTTP_SERVER_ERROR, FfmpegErrorCode::HttpServerError),
209            (AVERROR_HTTP_UNAUTHORIZED, FfmpegErrorCode::HttpUnauthorized),
210            (AVERROR_BUG2, FfmpegErrorCode::Bug2),
211            (AVERROR_UNKNOWN, FfmpegErrorCode::Unknown),
212        ];
213
214        // Test each case
215        for &(value, expected) in TEST_CASES {
216            let result: FfmpegErrorCode = value.into();
217            assert_eq!(result, expected, "Failed for value: {value}");
218        }
219
220        // Test an unknown error case
221        let unknown_value = 9999;
222        let result: FfmpegErrorCode = unknown_value.into();
223        assert_eq!(
224            result,
225            FfmpegErrorCode(unknown_value),
226            "Failed for unknown value: {unknown_value}"
227        );
228    }
229
230    #[test]
231    fn test_ffmpeg_error_display() {
232        let cases = [
233            (FfmpegError::Alloc, "failed to allocate memory"),
234            (FfmpegError::Code(FfmpegErrorCode::EndOfFile), "ffmpeg error: end of file"),
235            (FfmpegError::NoDecoder, "no decoder found"),
236            (FfmpegError::NoEncoder, "no encoder found"),
237            (FfmpegError::NoStream, "no stream found"),
238            (FfmpegError::NoFilter, "no filter found"),
239            (FfmpegError::NoFrame, "no frame found"),
240            (
241                FfmpegError::Arguments("invalid argument example"),
242                "invalid arguments: invalid argument example",
243            ),
244        ];
245
246        for (error, expected) in cases {
247            assert_eq!(error.to_string(), expected);
248        }
249    }
250}