1use nutype_enum::nutype_enum;
2
3use crate::ffi::*;
4
5#[derive(Debug, thiserror::Error, PartialEq, Eq)]
6pub enum FfmpegError {
8 #[error("failed to allocate memory")]
10 Alloc,
11 #[error("ffmpeg error: {0}")]
13 Code(#[from] FfmpegErrorCode),
14 #[error("no decoder found")]
16 NoDecoder,
17 #[error("no encoder found")]
19 NoEncoder,
20 #[error("no stream found")]
22 NoStream,
23 #[error("no filter found")]
25 NoFilter,
26 #[error("no frame found")]
28 NoFrame,
29 #[error("invalid arguments: {0}")]
31 Arguments(&'static str),
32}
33
34nutype_enum! {
35 pub enum FfmpegErrorCode(i32) {
37 Einval = AVERROR(EINVAL),
39 EndOfFile = AVERROR_EOF,
41 InvalidData = AVERROR_INVALIDDATA,
43 MuxerNotFound = AVERROR_MUXER_NOT_FOUND,
45 OptionNotFound = AVERROR_OPTION_NOT_FOUND,
47 PatchWelcome = AVERROR_PATCHWELCOME,
49 ProtocolNotFound = AVERROR_PROTOCOL_NOT_FOUND,
51 StreamNotFound = AVERROR_STREAM_NOT_FOUND,
53 BitstreamFilterNotFound = AVERROR_BSF_NOT_FOUND,
55 Bug = AVERROR_BUG,
57 Eof = AVERROR_EOF,
59 Eagain = AVERROR(EAGAIN),
61 BufferTooSmall = AVERROR_BUFFER_TOO_SMALL,
63 DecoderNotFound = AVERROR_DECODER_NOT_FOUND,
65 DemuxerNotFound = AVERROR_DEMUXER_NOT_FOUND,
67 EncoderNotFound = AVERROR_ENCODER_NOT_FOUND,
69 Exit = AVERROR_EXIT,
71 External = AVERROR_EXTERNAL,
73 FilterNotFound = AVERROR_FILTER_NOT_FOUND,
75 HttpBadRequest = AVERROR_HTTP_BAD_REQUEST,
77 HttpForbidden = AVERROR_HTTP_FORBIDDEN,
79 HttpNotFound = AVERROR_HTTP_NOT_FOUND,
81 HttpOther4xx = AVERROR_HTTP_OTHER_4XX,
83 HttpServerError = AVERROR_HTTP_SERVER_ERROR,
85 HttpUnauthorized = AVERROR_HTTP_UNAUTHORIZED,
87 Bug2 = AVERROR_BUG2,
89 Unknown = AVERROR_UNKNOWN,
91 }
92}
93
94impl FfmpegErrorCode {
95 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 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 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 for &(value, expected) in TEST_CASES {
216 let result: FfmpegErrorCode = value.into();
217 assert_eq!(result, expected, "Failed for value: {value}");
218 }
219
220 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}