Skip to main content

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