playa_ffmpeg/util/
error.rs

1use std::{error, ffi::CStr, fmt, io, str::from_utf8_unchecked};
2
3use crate::ffi::{
4    AV_ERROR_MAX_STRING_SIZE, AVERROR, AVERROR_BSF_NOT_FOUND, AVERROR_BUFFER_TOO_SMALL, AVERROR_BUG, AVERROR_BUG2, AVERROR_DECODER_NOT_FOUND, AVERROR_DEMUXER_NOT_FOUND, AVERROR_ENCODER_NOT_FOUND, AVERROR_EOF, AVERROR_EXIT, AVERROR_EXPERIMENTAL,
5    AVERROR_EXTERNAL, AVERROR_FILTER_NOT_FOUND, AVERROR_HTTP_BAD_REQUEST, AVERROR_HTTP_FORBIDDEN, AVERROR_HTTP_NOT_FOUND, AVERROR_HTTP_OTHER_4XX, AVERROR_HTTP_SERVER_ERROR, AVERROR_HTTP_UNAUTHORIZED, AVERROR_INPUT_CHANGED, AVERROR_INVALIDDATA,
6    AVERROR_MUXER_NOT_FOUND, AVERROR_OPTION_NOT_FOUND, AVERROR_OUTPUT_CHANGED, AVERROR_PATCHWELCOME, AVERROR_PROTOCOL_NOT_FOUND, AVERROR_STREAM_NOT_FOUND, AVERROR_UNKNOWN, AVUNERROR, av_strerror,
7};
8use libc::{c_char, c_int};
9
10// Export POSIX error codes so that users can do something like
11//
12//   if error == (Error::Other { errno: EAGAIN }) {
13//       ...
14//   }
15pub use libc::{
16    E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EAFNOSUPPORT, EAGAIN, EALREADY, EBADF, EBADMSG, EBUSY, ECANCELED, ECHILD, ECONNABORTED, ECONNREFUSED, ECONNRESET, EDEADLK, EDESTADDRREQ, EDOM, EEXIST, EFAULT, EFBIG, EHOSTUNREACH, EIDRM, EILSEQ,
17    EINPROGRESS, EINTR, EINVAL, EIO, EISCONN, EISDIR, ELOOP, EMFILE, EMLINK, EMSGSIZE, ENAMETOOLONG, ENETDOWN, ENETRESET, ENETUNREACH, ENFILE, ENOBUFS, ENODEV, ENOENT, ENOEXEC, ENOLCK, ENOLINK, ENOMEM, ENOMSG, ENOPROTOOPT, ENOSPC, ENOSYS, ENOTCONN,
18    ENOTDIR, ENOTEMPTY, ENOTRECOVERABLE, ENOTSOCK, ENOTSUP, ENOTTY, ENXIO, EOPNOTSUPP, EOVERFLOW, EOWNERDEAD, EPERM, EPIPE, EPROTO, EPROTONOSUPPORT, EPROTOTYPE, ERANGE, EROFS, ESPIPE, ESRCH, ETIMEDOUT, ETXTBSY, EWOULDBLOCK, EXDEV,
19};
20#[cfg(not(any(target_os = "freebsd", target_os = "wasi")))]
21pub use libc::{ENODATA, ENOSR, ENOSTR, ETIME};
22
23#[derive(Copy, Clone, PartialEq, Eq)]
24pub enum Error {
25    Bug,
26    Bug2,
27    Unknown,
28    Experimental,
29    BufferTooSmall,
30    Eof,
31    Exit,
32    External,
33    InvalidData,
34    PatchWelcome,
35
36    InputChanged,
37    OutputChanged,
38
39    BsfNotFound,
40    DecoderNotFound,
41    DemuxerNotFound,
42    EncoderNotFound,
43    OptionNotFound,
44    MuxerNotFound,
45    FilterNotFound,
46    ProtocolNotFound,
47    StreamNotFound,
48
49    HttpBadRequest,
50    HttpUnauthorized,
51    HttpForbidden,
52    HttpNotFound,
53    HttpOther4xx,
54    HttpServerError,
55
56    /// For AVERROR(e) wrapping POSIX error codes, e.g. AVERROR(EAGAIN).
57    Other {
58        errno: c_int,
59    },
60}
61
62impl From<c_int> for Error {
63    fn from(value: c_int) -> Error {
64        match value {
65            AVERROR_BSF_NOT_FOUND => Error::BsfNotFound,
66            AVERROR_BUG => Error::Bug,
67            AVERROR_BUFFER_TOO_SMALL => Error::BufferTooSmall,
68            AVERROR_DECODER_NOT_FOUND => Error::DecoderNotFound,
69            AVERROR_DEMUXER_NOT_FOUND => Error::DemuxerNotFound,
70            AVERROR_ENCODER_NOT_FOUND => Error::EncoderNotFound,
71            AVERROR_EOF => Error::Eof,
72            AVERROR_EXIT => Error::Exit,
73            AVERROR_EXTERNAL => Error::External,
74            AVERROR_FILTER_NOT_FOUND => Error::FilterNotFound,
75            AVERROR_INVALIDDATA => Error::InvalidData,
76            AVERROR_MUXER_NOT_FOUND => Error::MuxerNotFound,
77            AVERROR_OPTION_NOT_FOUND => Error::OptionNotFound,
78            AVERROR_PATCHWELCOME => Error::PatchWelcome,
79            AVERROR_PROTOCOL_NOT_FOUND => Error::ProtocolNotFound,
80            AVERROR_STREAM_NOT_FOUND => Error::StreamNotFound,
81            AVERROR_BUG2 => Error::Bug2,
82            AVERROR_UNKNOWN => Error::Unknown,
83            AVERROR_EXPERIMENTAL => Error::Experimental,
84            AVERROR_INPUT_CHANGED => Error::InputChanged,
85            AVERROR_OUTPUT_CHANGED => Error::OutputChanged,
86            AVERROR_HTTP_BAD_REQUEST => Error::HttpBadRequest,
87            AVERROR_HTTP_UNAUTHORIZED => Error::HttpUnauthorized,
88            AVERROR_HTTP_FORBIDDEN => Error::HttpForbidden,
89            AVERROR_HTTP_NOT_FOUND => Error::HttpNotFound,
90            AVERROR_HTTP_OTHER_4XX => Error::HttpOther4xx,
91            AVERROR_HTTP_SERVER_ERROR => Error::HttpServerError,
92            e => Error::Other { errno: AVUNERROR(e) },
93        }
94    }
95}
96
97impl From<Error> for c_int {
98    fn from(value: Error) -> c_int {
99        match value {
100            Error::BsfNotFound => AVERROR_BSF_NOT_FOUND,
101            Error::Bug => AVERROR_BUG,
102            Error::BufferTooSmall => AVERROR_BUFFER_TOO_SMALL,
103            Error::DecoderNotFound => AVERROR_DECODER_NOT_FOUND,
104            Error::DemuxerNotFound => AVERROR_DEMUXER_NOT_FOUND,
105            Error::EncoderNotFound => AVERROR_ENCODER_NOT_FOUND,
106            Error::Eof => AVERROR_EOF,
107            Error::Exit => AVERROR_EXIT,
108            Error::External => AVERROR_EXTERNAL,
109            Error::FilterNotFound => AVERROR_FILTER_NOT_FOUND,
110            Error::InvalidData => AVERROR_INVALIDDATA,
111            Error::MuxerNotFound => AVERROR_MUXER_NOT_FOUND,
112            Error::OptionNotFound => AVERROR_OPTION_NOT_FOUND,
113            Error::PatchWelcome => AVERROR_PATCHWELCOME,
114            Error::ProtocolNotFound => AVERROR_PROTOCOL_NOT_FOUND,
115            Error::StreamNotFound => AVERROR_STREAM_NOT_FOUND,
116            Error::Bug2 => AVERROR_BUG2,
117            Error::Unknown => AVERROR_UNKNOWN,
118            Error::Experimental => AVERROR_EXPERIMENTAL,
119            Error::InputChanged => AVERROR_INPUT_CHANGED,
120            Error::OutputChanged => AVERROR_OUTPUT_CHANGED,
121            Error::HttpBadRequest => AVERROR_HTTP_BAD_REQUEST,
122            Error::HttpUnauthorized => AVERROR_HTTP_UNAUTHORIZED,
123            Error::HttpForbidden => AVERROR_HTTP_FORBIDDEN,
124            Error::HttpNotFound => AVERROR_HTTP_NOT_FOUND,
125            Error::HttpOther4xx => AVERROR_HTTP_OTHER_4XX,
126            Error::HttpServerError => AVERROR_HTTP_SERVER_ERROR,
127            Error::Other { errno } => AVERROR(errno),
128        }
129    }
130}
131
132impl error::Error for Error {}
133
134impl From<Error> for io::Error {
135    fn from(value: Error) -> io::Error {
136        io::Error::other(value)
137    }
138}
139
140impl fmt::Display for Error {
141    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
142        f.write_str(unsafe {
143            from_utf8_unchecked(
144                CStr::from_ptr(match *self {
145                    Error::Other { errno } => libc::strerror(errno),
146                    _ => STRINGS[index(self)].as_ptr(),
147                })
148                .to_bytes(),
149            )
150        })
151    }
152}
153
154impl fmt::Debug for Error {
155    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
156        f.write_str("ffmpeg::Error(")?;
157        f.write_str(&format!("{}: ", AVUNERROR((*self).into())))?;
158        fmt::Display::fmt(self, f)?;
159        f.write_str(")")
160    }
161}
162
163#[inline(always)]
164fn index(error: &Error) -> usize {
165    match *error {
166        Error::BsfNotFound => 0,
167        Error::Bug => 1,
168        Error::BufferTooSmall => 2,
169        Error::DecoderNotFound => 3,
170        Error::DemuxerNotFound => 4,
171        Error::EncoderNotFound => 5,
172        Error::Eof => 6,
173        Error::Exit => 7,
174        Error::External => 8,
175        Error::FilterNotFound => 9,
176        Error::InvalidData => 10,
177        Error::MuxerNotFound => 11,
178        Error::OptionNotFound => 12,
179        Error::PatchWelcome => 13,
180        Error::ProtocolNotFound => 14,
181        Error::StreamNotFound => 15,
182        Error::Bug2 => 16,
183        Error::Unknown => 17,
184        Error::Experimental => 18,
185        Error::InputChanged => 19,
186        Error::OutputChanged => 20,
187        Error::HttpBadRequest => 21,
188        Error::HttpUnauthorized => 22,
189        Error::HttpForbidden => 23,
190        Error::HttpNotFound => 24,
191        Error::HttpOther4xx => 25,
192        Error::HttpServerError => 26,
193        Error::Other { errno: _ } => (-1isize) as usize,
194    }
195}
196
197// XXX: the length has to be synced with the number of errors
198static mut STRINGS: [[c_char; AV_ERROR_MAX_STRING_SIZE]; 27] = [[0; AV_ERROR_MAX_STRING_SIZE]; 27];
199
200pub fn register_all() {
201    unsafe {
202        av_strerror(Error::Bug.into(), STRINGS[index(&Error::Bug)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
203        av_strerror(Error::Bug2.into(), STRINGS[index(&Error::Bug2)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
204        av_strerror(Error::Unknown.into(), STRINGS[index(&Error::Unknown)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
205        av_strerror(Error::Experimental.into(), STRINGS[index(&Error::Experimental)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
206        av_strerror(Error::BufferTooSmall.into(), STRINGS[index(&Error::BufferTooSmall)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
207        av_strerror(Error::Eof.into(), STRINGS[index(&Error::Eof)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
208        av_strerror(Error::Exit.into(), STRINGS[index(&Error::Exit)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
209        av_strerror(Error::External.into(), STRINGS[index(&Error::External)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
210        av_strerror(Error::InvalidData.into(), STRINGS[index(&Error::InvalidData)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
211        av_strerror(Error::PatchWelcome.into(), STRINGS[index(&Error::PatchWelcome)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
212
213        av_strerror(Error::InputChanged.into(), STRINGS[index(&Error::InputChanged)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
214        av_strerror(Error::OutputChanged.into(), STRINGS[index(&Error::OutputChanged)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
215
216        av_strerror(Error::BsfNotFound.into(), STRINGS[index(&Error::BsfNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
217        av_strerror(Error::DecoderNotFound.into(), STRINGS[index(&Error::DecoderNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
218        av_strerror(Error::DemuxerNotFound.into(), STRINGS[index(&Error::DemuxerNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
219        av_strerror(Error::EncoderNotFound.into(), STRINGS[index(&Error::EncoderNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
220        av_strerror(Error::OptionNotFound.into(), STRINGS[index(&Error::OptionNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
221        av_strerror(Error::MuxerNotFound.into(), STRINGS[index(&Error::MuxerNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
222        av_strerror(Error::FilterNotFound.into(), STRINGS[index(&Error::FilterNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
223        av_strerror(Error::ProtocolNotFound.into(), STRINGS[index(&Error::ProtocolNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
224        av_strerror(Error::StreamNotFound.into(), STRINGS[index(&Error::StreamNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
225
226        av_strerror(Error::HttpBadRequest.into(), STRINGS[index(&Error::HttpBadRequest)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
227        av_strerror(Error::HttpUnauthorized.into(), STRINGS[index(&Error::HttpUnauthorized)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
228        av_strerror(Error::HttpForbidden.into(), STRINGS[index(&Error::HttpForbidden)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
229        av_strerror(Error::HttpNotFound.into(), STRINGS[index(&Error::HttpNotFound)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
230        av_strerror(Error::HttpOther4xx.into(), STRINGS[index(&Error::HttpOther4xx)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
231        av_strerror(Error::HttpServerError.into(), STRINGS[index(&Error::HttpServerError)].as_mut_ptr(), AV_ERROR_MAX_STRING_SIZE);
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238
239    #[test]
240    fn test_error_roundtrip() {
241        assert_eq!(Into::<c_int>::into(Error::from(AVERROR_EOF)), AVERROR_EOF);
242        assert_eq!(Into::<c_int>::into(Error::from(AVERROR(EAGAIN))), AVERROR(EAGAIN));
243        assert_eq!(Error::from(AVERROR(EAGAIN)), Error::Other { errno: EAGAIN });
244    }
245
246    #[cfg(any(target_os = "linux", target_os = "macos"))]
247    #[test]
248    fn test_posix_error_string() {
249        assert_eq!(Error::from(AVERROR(EAGAIN)).to_string(), "Resource temporarily unavailable")
250    }
251}