ffmpeg_next/util/
error.rs

1use std::error;
2use std::ffi::CStr;
3use std::fmt;
4use std::io;
5use std::str::from_utf8_unchecked;
6
7use ffi::{
8    av_strerror, AVERROR, AVERROR_BSF_NOT_FOUND, AVERROR_BUFFER_TOO_SMALL, AVERROR_BUG,
9    AVERROR_BUG2, AVERROR_DECODER_NOT_FOUND, AVERROR_DEMUXER_NOT_FOUND, AVERROR_ENCODER_NOT_FOUND,
10    AVERROR_EOF, AVERROR_EXIT, AVERROR_EXPERIMENTAL, AVERROR_EXTERNAL, AVERROR_FILTER_NOT_FOUND,
11    AVERROR_HTTP_BAD_REQUEST, AVERROR_HTTP_FORBIDDEN, AVERROR_HTTP_NOT_FOUND,
12    AVERROR_HTTP_OTHER_4XX, AVERROR_HTTP_SERVER_ERROR, AVERROR_HTTP_UNAUTHORIZED,
13    AVERROR_INPUT_CHANGED, AVERROR_INVALIDDATA, AVERROR_MUXER_NOT_FOUND, AVERROR_OPTION_NOT_FOUND,
14    AVERROR_OUTPUT_CHANGED, AVERROR_PATCHWELCOME, AVERROR_PROTOCOL_NOT_FOUND,
15    AVERROR_STREAM_NOT_FOUND, AVERROR_UNKNOWN, AVUNERROR, AV_ERROR_MAX_STRING_SIZE,
16};
17use libc::{c_char, c_int};
18
19// Export POSIX error codes so that users can do something like
20//
21//   if error == (Error::Other { errno: EAGAIN }) {
22//       ...
23//   }
24pub use libc::{
25    E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EAFNOSUPPORT, EAGAIN, EALREADY, EBADF, EBADMSG,
26    EBUSY, ECANCELED, ECHILD, ECONNABORTED, ECONNREFUSED, ECONNRESET, EDEADLK, EDESTADDRREQ, EDOM,
27    EEXIST, EFAULT, EFBIG, EHOSTUNREACH, EIDRM, EILSEQ, EINPROGRESS, EINTR, EINVAL, EIO, EISCONN,
28    EISDIR, ELOOP, EMFILE, EMLINK, EMSGSIZE, ENAMETOOLONG, ENETDOWN, ENETRESET, ENETUNREACH,
29    ENFILE, ENOBUFS, ENODEV, ENOENT, ENOEXEC, ENOLCK, ENOLINK, ENOMEM, ENOMSG, ENOPROTOOPT, ENOSPC,
30    ENOSYS, ENOTCONN, ENOTDIR, ENOTEMPTY, ENOTRECOVERABLE, ENOTSOCK, ENOTSUP, ENOTTY, ENXIO,
31    EOPNOTSUPP, EOVERFLOW, EOWNERDEAD, EPERM, EPIPE, EPROTO, EPROTONOSUPPORT, EPROTOTYPE, ERANGE,
32    EROFS, ESPIPE, ESRCH, ETIMEDOUT, ETXTBSY, EWOULDBLOCK, EXDEV,
33};
34#[cfg(not(any(target_os = "freebsd", target_os = "wasi")))]
35pub use libc::{ENODATA, ENOSR, ENOSTR, ETIME};
36
37#[derive(Copy, Clone, PartialEq, Eq)]
38pub enum Error {
39    Bug,
40    Bug2,
41    Unknown,
42    Experimental,
43    BufferTooSmall,
44    Eof,
45    Exit,
46    External,
47    InvalidData,
48    PatchWelcome,
49
50    InputChanged,
51    OutputChanged,
52
53    BsfNotFound,
54    DecoderNotFound,
55    DemuxerNotFound,
56    EncoderNotFound,
57    OptionNotFound,
58    MuxerNotFound,
59    FilterNotFound,
60    ProtocolNotFound,
61    StreamNotFound,
62
63    HttpBadRequest,
64    HttpUnauthorized,
65    HttpForbidden,
66    HttpNotFound,
67    HttpOther4xx,
68    HttpServerError,
69
70    /// For AVERROR(e) wrapping POSIX error codes, e.g. AVERROR(EAGAIN).
71    Other {
72        errno: c_int,
73    },
74}
75
76impl From<c_int> for Error {
77    fn from(value: c_int) -> Error {
78        match value {
79            AVERROR_BSF_NOT_FOUND => Error::BsfNotFound,
80            AVERROR_BUG => Error::Bug,
81            AVERROR_BUFFER_TOO_SMALL => Error::BufferTooSmall,
82            AVERROR_DECODER_NOT_FOUND => Error::DecoderNotFound,
83            AVERROR_DEMUXER_NOT_FOUND => Error::DemuxerNotFound,
84            AVERROR_ENCODER_NOT_FOUND => Error::EncoderNotFound,
85            AVERROR_EOF => Error::Eof,
86            AVERROR_EXIT => Error::Exit,
87            AVERROR_EXTERNAL => Error::External,
88            AVERROR_FILTER_NOT_FOUND => Error::FilterNotFound,
89            AVERROR_INVALIDDATA => Error::InvalidData,
90            AVERROR_MUXER_NOT_FOUND => Error::MuxerNotFound,
91            AVERROR_OPTION_NOT_FOUND => Error::OptionNotFound,
92            AVERROR_PATCHWELCOME => Error::PatchWelcome,
93            AVERROR_PROTOCOL_NOT_FOUND => Error::ProtocolNotFound,
94            AVERROR_STREAM_NOT_FOUND => Error::StreamNotFound,
95            AVERROR_BUG2 => Error::Bug2,
96            AVERROR_UNKNOWN => Error::Unknown,
97            AVERROR_EXPERIMENTAL => Error::Experimental,
98            AVERROR_INPUT_CHANGED => Error::InputChanged,
99            AVERROR_OUTPUT_CHANGED => Error::OutputChanged,
100            AVERROR_HTTP_BAD_REQUEST => Error::HttpBadRequest,
101            AVERROR_HTTP_UNAUTHORIZED => Error::HttpUnauthorized,
102            AVERROR_HTTP_FORBIDDEN => Error::HttpForbidden,
103            AVERROR_HTTP_NOT_FOUND => Error::HttpNotFound,
104            AVERROR_HTTP_OTHER_4XX => Error::HttpOther4xx,
105            AVERROR_HTTP_SERVER_ERROR => Error::HttpServerError,
106            e => Error::Other {
107                errno: AVUNERROR(e),
108            },
109        }
110    }
111}
112
113impl From<Error> for c_int {
114    fn from(value: Error) -> c_int {
115        match value {
116            Error::BsfNotFound => AVERROR_BSF_NOT_FOUND,
117            Error::Bug => AVERROR_BUG,
118            Error::BufferTooSmall => AVERROR_BUFFER_TOO_SMALL,
119            Error::DecoderNotFound => AVERROR_DECODER_NOT_FOUND,
120            Error::DemuxerNotFound => AVERROR_DEMUXER_NOT_FOUND,
121            Error::EncoderNotFound => AVERROR_ENCODER_NOT_FOUND,
122            Error::Eof => AVERROR_EOF,
123            Error::Exit => AVERROR_EXIT,
124            Error::External => AVERROR_EXTERNAL,
125            Error::FilterNotFound => AVERROR_FILTER_NOT_FOUND,
126            Error::InvalidData => AVERROR_INVALIDDATA,
127            Error::MuxerNotFound => AVERROR_MUXER_NOT_FOUND,
128            Error::OptionNotFound => AVERROR_OPTION_NOT_FOUND,
129            Error::PatchWelcome => AVERROR_PATCHWELCOME,
130            Error::ProtocolNotFound => AVERROR_PROTOCOL_NOT_FOUND,
131            Error::StreamNotFound => AVERROR_STREAM_NOT_FOUND,
132            Error::Bug2 => AVERROR_BUG2,
133            Error::Unknown => AVERROR_UNKNOWN,
134            Error::Experimental => AVERROR_EXPERIMENTAL,
135            Error::InputChanged => AVERROR_INPUT_CHANGED,
136            Error::OutputChanged => AVERROR_OUTPUT_CHANGED,
137            Error::HttpBadRequest => AVERROR_HTTP_BAD_REQUEST,
138            Error::HttpUnauthorized => AVERROR_HTTP_UNAUTHORIZED,
139            Error::HttpForbidden => AVERROR_HTTP_FORBIDDEN,
140            Error::HttpNotFound => AVERROR_HTTP_NOT_FOUND,
141            Error::HttpOther4xx => AVERROR_HTTP_OTHER_4XX,
142            Error::HttpServerError => AVERROR_HTTP_SERVER_ERROR,
143            Error::Other { errno } => AVERROR(errno),
144        }
145    }
146}
147
148impl error::Error for Error {}
149
150impl From<Error> for io::Error {
151    fn from(value: Error) -> io::Error {
152        io::Error::new(io::ErrorKind::Other, value)
153    }
154}
155
156impl fmt::Display for Error {
157    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
158        f.write_str(unsafe {
159            from_utf8_unchecked(
160                CStr::from_ptr(match *self {
161                    Error::Other { errno } => libc::strerror(errno),
162                    _ => STRINGS[index(self)].as_ptr(),
163                })
164                .to_bytes(),
165            )
166        })
167    }
168}
169
170impl fmt::Debug for Error {
171    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
172        f.write_str("ffmpeg::Error(")?;
173        f.write_str(&format!("{}: ", AVUNERROR((*self).into())))?;
174        fmt::Display::fmt(self, f)?;
175        f.write_str(")")
176    }
177}
178
179#[inline(always)]
180fn index(error: &Error) -> usize {
181    match *error {
182        Error::BsfNotFound => 0,
183        Error::Bug => 1,
184        Error::BufferTooSmall => 2,
185        Error::DecoderNotFound => 3,
186        Error::DemuxerNotFound => 4,
187        Error::EncoderNotFound => 5,
188        Error::Eof => 6,
189        Error::Exit => 7,
190        Error::External => 8,
191        Error::FilterNotFound => 9,
192        Error::InvalidData => 10,
193        Error::MuxerNotFound => 11,
194        Error::OptionNotFound => 12,
195        Error::PatchWelcome => 13,
196        Error::ProtocolNotFound => 14,
197        Error::StreamNotFound => 15,
198        Error::Bug2 => 16,
199        Error::Unknown => 17,
200        Error::Experimental => 18,
201        Error::InputChanged => 19,
202        Error::OutputChanged => 20,
203        Error::HttpBadRequest => 21,
204        Error::HttpUnauthorized => 22,
205        Error::HttpForbidden => 23,
206        Error::HttpNotFound => 24,
207        Error::HttpOther4xx => 25,
208        Error::HttpServerError => 26,
209        Error::Other { errno: _ } => (-1isize) as usize,
210    }
211}
212
213// XXX: the length has to be synced with the number of errors
214static mut STRINGS: [[c_char; AV_ERROR_MAX_STRING_SIZE]; 27] = [[0; AV_ERROR_MAX_STRING_SIZE]; 27];
215
216pub fn register_all() {
217    unsafe {
218        av_strerror(
219            Error::Bug.into(),
220            STRINGS[index(&Error::Bug)].as_mut_ptr(),
221            AV_ERROR_MAX_STRING_SIZE,
222        );
223        av_strerror(
224            Error::Bug2.into(),
225            STRINGS[index(&Error::Bug2)].as_mut_ptr(),
226            AV_ERROR_MAX_STRING_SIZE,
227        );
228        av_strerror(
229            Error::Unknown.into(),
230            STRINGS[index(&Error::Unknown)].as_mut_ptr(),
231            AV_ERROR_MAX_STRING_SIZE,
232        );
233        av_strerror(
234            Error::Experimental.into(),
235            STRINGS[index(&Error::Experimental)].as_mut_ptr(),
236            AV_ERROR_MAX_STRING_SIZE,
237        );
238        av_strerror(
239            Error::BufferTooSmall.into(),
240            STRINGS[index(&Error::BufferTooSmall)].as_mut_ptr(),
241            AV_ERROR_MAX_STRING_SIZE,
242        );
243        av_strerror(
244            Error::Eof.into(),
245            STRINGS[index(&Error::Eof)].as_mut_ptr(),
246            AV_ERROR_MAX_STRING_SIZE,
247        );
248        av_strerror(
249            Error::Exit.into(),
250            STRINGS[index(&Error::Exit)].as_mut_ptr(),
251            AV_ERROR_MAX_STRING_SIZE,
252        );
253        av_strerror(
254            Error::External.into(),
255            STRINGS[index(&Error::External)].as_mut_ptr(),
256            AV_ERROR_MAX_STRING_SIZE,
257        );
258        av_strerror(
259            Error::InvalidData.into(),
260            STRINGS[index(&Error::InvalidData)].as_mut_ptr(),
261            AV_ERROR_MAX_STRING_SIZE,
262        );
263        av_strerror(
264            Error::PatchWelcome.into(),
265            STRINGS[index(&Error::PatchWelcome)].as_mut_ptr(),
266            AV_ERROR_MAX_STRING_SIZE,
267        );
268
269        av_strerror(
270            Error::InputChanged.into(),
271            STRINGS[index(&Error::InputChanged)].as_mut_ptr(),
272            AV_ERROR_MAX_STRING_SIZE,
273        );
274        av_strerror(
275            Error::OutputChanged.into(),
276            STRINGS[index(&Error::OutputChanged)].as_mut_ptr(),
277            AV_ERROR_MAX_STRING_SIZE,
278        );
279
280        av_strerror(
281            Error::BsfNotFound.into(),
282            STRINGS[index(&Error::BsfNotFound)].as_mut_ptr(),
283            AV_ERROR_MAX_STRING_SIZE,
284        );
285        av_strerror(
286            Error::DecoderNotFound.into(),
287            STRINGS[index(&Error::DecoderNotFound)].as_mut_ptr(),
288            AV_ERROR_MAX_STRING_SIZE,
289        );
290        av_strerror(
291            Error::DemuxerNotFound.into(),
292            STRINGS[index(&Error::DemuxerNotFound)].as_mut_ptr(),
293            AV_ERROR_MAX_STRING_SIZE,
294        );
295        av_strerror(
296            Error::EncoderNotFound.into(),
297            STRINGS[index(&Error::EncoderNotFound)].as_mut_ptr(),
298            AV_ERROR_MAX_STRING_SIZE,
299        );
300        av_strerror(
301            Error::OptionNotFound.into(),
302            STRINGS[index(&Error::OptionNotFound)].as_mut_ptr(),
303            AV_ERROR_MAX_STRING_SIZE,
304        );
305        av_strerror(
306            Error::MuxerNotFound.into(),
307            STRINGS[index(&Error::MuxerNotFound)].as_mut_ptr(),
308            AV_ERROR_MAX_STRING_SIZE,
309        );
310        av_strerror(
311            Error::FilterNotFound.into(),
312            STRINGS[index(&Error::FilterNotFound)].as_mut_ptr(),
313            AV_ERROR_MAX_STRING_SIZE,
314        );
315        av_strerror(
316            Error::ProtocolNotFound.into(),
317            STRINGS[index(&Error::ProtocolNotFound)].as_mut_ptr(),
318            AV_ERROR_MAX_STRING_SIZE,
319        );
320        av_strerror(
321            Error::StreamNotFound.into(),
322            STRINGS[index(&Error::StreamNotFound)].as_mut_ptr(),
323            AV_ERROR_MAX_STRING_SIZE,
324        );
325
326        av_strerror(
327            Error::HttpBadRequest.into(),
328            STRINGS[index(&Error::HttpBadRequest)].as_mut_ptr(),
329            AV_ERROR_MAX_STRING_SIZE,
330        );
331        av_strerror(
332            Error::HttpUnauthorized.into(),
333            STRINGS[index(&Error::HttpUnauthorized)].as_mut_ptr(),
334            AV_ERROR_MAX_STRING_SIZE,
335        );
336        av_strerror(
337            Error::HttpForbidden.into(),
338            STRINGS[index(&Error::HttpForbidden)].as_mut_ptr(),
339            AV_ERROR_MAX_STRING_SIZE,
340        );
341        av_strerror(
342            Error::HttpNotFound.into(),
343            STRINGS[index(&Error::HttpNotFound)].as_mut_ptr(),
344            AV_ERROR_MAX_STRING_SIZE,
345        );
346        av_strerror(
347            Error::HttpOther4xx.into(),
348            STRINGS[index(&Error::HttpOther4xx)].as_mut_ptr(),
349            AV_ERROR_MAX_STRING_SIZE,
350        );
351        av_strerror(
352            Error::HttpServerError.into(),
353            STRINGS[index(&Error::HttpServerError)].as_mut_ptr(),
354            AV_ERROR_MAX_STRING_SIZE,
355        );
356    }
357}
358
359#[cfg(test)]
360mod tests {
361    use super::*;
362
363    #[test]
364    fn test_error_roundtrip() {
365        assert_eq!(Into::<c_int>::into(Error::from(AVERROR_EOF)), AVERROR_EOF);
366        assert_eq!(
367            Into::<c_int>::into(Error::from(AVERROR(EAGAIN))),
368            AVERROR(EAGAIN)
369        );
370        assert_eq!(Error::from(AVERROR(EAGAIN)), Error::Other { errno: EAGAIN });
371    }
372
373    #[cfg(any(target_os = "linux", target_os = "macos"))]
374    #[test]
375    fn test_posix_error_string() {
376        assert_eq!(
377            Error::from(AVERROR(EAGAIN)).to_string(),
378            "Resource temporarily unavailable"
379        )
380    }
381}