1use std::error;
2use std::ffi::CStr;
3use std::fmt;
4use std::io;
5use std::str::from_utf8_unchecked;
6
7use crate::ffi::*;
8use libc::{c_char, c_int};
9#[cfg(feature = "serialize")]
10use serde::{Deserialize, Serialize};
11
12#[derive(Copy, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
14pub enum Error {
15 Bug,
16 Bug2,
17 Unknown,
18 Experimental,
19 BufferTooSmall,
20 Eof,
21 Exit,
22 External,
23 InvalidData,
24 PatchWelcome,
25
26 InputChanged,
27 OutputChanged,
28
29 BsfNotFound,
30 DecoderNotFound,
31 DemuxerNotFound,
32 EncoderNotFound,
33 OptionNotFound,
34 MuxerNotFound,
35 FilterNotFound,
36 ProtocolNotFound,
37 StreamNotFound,
38
39 HttpBadRequest,
40 HttpUnauthorized,
41 HttpForbidden,
42 HttpNotFound,
43 #[cfg(feature = "ffmpeg_7_1")]
44 HttpTooManyRequests,
45 HttpOther4xx,
46 HttpServerError,
47
48 Other {
50 errno: c_int,
51 },
52}
53
54impl From<c_int> for Error {
55 fn from(value: c_int) -> Error {
56 match value {
57 AVERROR_BSF_NOT_FOUND => Error::BsfNotFound,
58 AVERROR_BUG => Error::Bug,
59 AVERROR_BUFFER_TOO_SMALL => Error::BufferTooSmall,
60 AVERROR_DECODER_NOT_FOUND => Error::DecoderNotFound,
61 AVERROR_DEMUXER_NOT_FOUND => Error::DemuxerNotFound,
62 AVERROR_ENCODER_NOT_FOUND => Error::EncoderNotFound,
63 AVERROR_EOF => Error::Eof,
64 AVERROR_EXIT => Error::Exit,
65 AVERROR_EXTERNAL => Error::External,
66 AVERROR_FILTER_NOT_FOUND => Error::FilterNotFound,
67 AVERROR_INVALIDDATA => Error::InvalidData,
68 AVERROR_MUXER_NOT_FOUND => Error::MuxerNotFound,
69 AVERROR_OPTION_NOT_FOUND => Error::OptionNotFound,
70 AVERROR_PATCHWELCOME => Error::PatchWelcome,
71 AVERROR_PROTOCOL_NOT_FOUND => Error::ProtocolNotFound,
72 AVERROR_STREAM_NOT_FOUND => Error::StreamNotFound,
73 AVERROR_BUG2 => Error::Bug2,
74 AVERROR_UNKNOWN => Error::Unknown,
75 AVERROR_EXPERIMENTAL => Error::Experimental,
76 AVERROR_INPUT_CHANGED => Error::InputChanged,
77 AVERROR_OUTPUT_CHANGED => Error::OutputChanged,
78 AVERROR_HTTP_BAD_REQUEST => Error::HttpBadRequest,
79 AVERROR_HTTP_UNAUTHORIZED => Error::HttpUnauthorized,
80 AVERROR_HTTP_FORBIDDEN => Error::HttpForbidden,
81 AVERROR_HTTP_NOT_FOUND => Error::HttpNotFound,
82 #[cfg(feature = "ffmpeg_7_1")]
83 AVERROR_HTTP_TOO_MANY_REQUESTS => Error::HttpTooManyRequests,
84 AVERROR_HTTP_OTHER_4XX => Error::HttpOther4xx,
85 AVERROR_HTTP_SERVER_ERROR => Error::HttpServerError,
86 e => Error::Other {
87 errno: AVUNERROR(e),
88 },
89 }
90 }
91}
92
93impl From<Error> for c_int {
94 fn from(value: Error) -> c_int {
95 match value {
96 Error::BsfNotFound => AVERROR_BSF_NOT_FOUND,
97 Error::Bug => AVERROR_BUG,
98 Error::BufferTooSmall => AVERROR_BUFFER_TOO_SMALL,
99 Error::DecoderNotFound => AVERROR_DECODER_NOT_FOUND,
100 Error::DemuxerNotFound => AVERROR_DEMUXER_NOT_FOUND,
101 Error::EncoderNotFound => AVERROR_ENCODER_NOT_FOUND,
102 Error::Eof => AVERROR_EOF,
103 Error::Exit => AVERROR_EXIT,
104 Error::External => AVERROR_EXTERNAL,
105 Error::FilterNotFound => AVERROR_FILTER_NOT_FOUND,
106 Error::InvalidData => AVERROR_INVALIDDATA,
107 Error::MuxerNotFound => AVERROR_MUXER_NOT_FOUND,
108 Error::OptionNotFound => AVERROR_OPTION_NOT_FOUND,
109 Error::PatchWelcome => AVERROR_PATCHWELCOME,
110 Error::ProtocolNotFound => AVERROR_PROTOCOL_NOT_FOUND,
111 Error::StreamNotFound => AVERROR_STREAM_NOT_FOUND,
112 Error::Bug2 => AVERROR_BUG2,
113 Error::Unknown => AVERROR_UNKNOWN,
114 Error::Experimental => AVERROR_EXPERIMENTAL,
115 Error::InputChanged => AVERROR_INPUT_CHANGED,
116 Error::OutputChanged => AVERROR_OUTPUT_CHANGED,
117 Error::HttpBadRequest => AVERROR_HTTP_BAD_REQUEST,
118 Error::HttpUnauthorized => AVERROR_HTTP_UNAUTHORIZED,
119 Error::HttpForbidden => AVERROR_HTTP_FORBIDDEN,
120 Error::HttpNotFound => AVERROR_HTTP_NOT_FOUND,
121 #[cfg(feature = "ffmpeg_7_1")]
122 Error::HttpTooManyRequests => AVERROR_HTTP_TOO_MANY_REQUESTS,
123 Error::HttpOther4xx => AVERROR_HTTP_OTHER_4XX,
124 Error::HttpServerError => AVERROR_HTTP_SERVER_ERROR,
125 Error::Other { errno } => AVERROR(errno),
126 }
127 }
128}
129
130impl error::Error for Error {}
131
132impl From<Error> for io::Error {
133 fn from(value: Error) -> io::Error {
134 io::Error::other(value)
135 }
136}
137
138impl fmt::Display for Error {
139 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
140 let mut buf = [0; AV_ERROR_MAX_STRING_SIZE];
141
142 unsafe {
143 let error_text = match *self {
144 Error::Other { errno } => os_strerror(errno, &mut buf),
145 av_err => {
146 if 0 == av_strerror(av_err.into(), buf.as_mut_ptr(), buf.len()) {
147 CStr::from_ptr(buf.as_ptr())
148 } else {
149 c"Unknown error"
150 }
151 }
152 };
153
154 f.write_str(from_utf8_unchecked(error_text.to_bytes()))
155 }
156 }
157}
158
159#[cfg(unix)]
163unsafe fn os_strerror(errno: c_int, buf: &mut [c_char; AV_ERROR_MAX_STRING_SIZE]) -> &CStr {
164 let _err = libc::strerror_r(errno, buf.as_mut_ptr(), buf.len());
165 #[cfg(test)]
168 {
169 if _err == libc::ERANGE {
170 panic!("Insufficient buffer size")
171 }
172 }
173 CStr::from_ptr(buf.as_ptr())
174}
175
176#[cfg(windows)]
177unsafe fn os_strerror(errno: c_int, _buf: &mut [c_char; AV_ERROR_MAX_STRING_SIZE]) -> &CStr {
178 CStr::from_ptr(libc::strerror(errno))
179}
180
181#[cfg(all(not(windows), not(unix)))]
182unsafe fn os_strerror(errno: c_int, buf: &mut [c_char; AV_ERROR_MAX_STRING_SIZE]) -> &CStr {
183 static MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
184 let guard = MUTEX.lock();
185 libc::strncpy(
186 buf.as_mut_ptr(),
187 libc::strerror(errno),
188 AV_ERROR_MAX_STRING_SIZE - 1,
189 );
190 drop(guard);
191 CStr::from_ptr(buf.as_ptr())
192}
193
194impl fmt::Debug for Error {
195 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
196 f.write_str("ffmpeg::Error(")?;
197 f.write_str(&format!("{}: ", AVUNERROR((*self).into())))?;
198 fmt::Display::fmt(self, f)?;
199 f.write_str(")")
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206 use libc::EAGAIN;
207
208 #[test]
209 fn test_error_roundtrip() {
210 assert_eq!(Into::<c_int>::into(Error::from(AVERROR_EOF)), AVERROR_EOF);
211 assert_eq!(
212 Into::<c_int>::into(Error::from(AVERROR(EAGAIN))),
213 AVERROR(EAGAIN)
214 );
215 assert_eq!(Error::from(AVERROR(EAGAIN)), Error::Other { errno: EAGAIN });
216 }
217
218 #[cfg(unix)]
219 #[test]
220 fn test_posix_error_string_range() {
221 let mut buf = [0; AV_ERROR_MAX_STRING_SIZE];
222 for e in 1..255 {
223 let _ = unsafe { os_strerror(e, &mut buf) };
224 }
225 }
226
227 #[cfg(unix)]
228 #[test]
229 fn test_posix_error_string() {
230 assert_eq!(
231 Error::from(AVERROR(EAGAIN)).to_string(),
232 "Resource temporarily unavailable"
233 )
234 }
235
236 #[test]
237 fn test_error_fmt() {
238 use std::fmt::Write;
239
240 let mut s = String::new();
241 write!(&mut s, "{}", Error::InvalidData).expect("can write into string");
242 assert_eq!(s, "Invalid data found when processing input");
243
244 s.clear();
245
246 write!(&mut s, "{}", Error::from(AVERROR(EAGAIN))).expect("can write into string");
247 if cfg!(unix) {
248 assert_eq!(s, "Resource temporarily unavailable");
249 }
250
251 #[cfg(feature = "ffmpeg_7_1")]
252 {
253 s.clear();
254
255 write!(&mut s, "{}", Error::HttpTooManyRequests).expect("can write into string");
256 assert_eq!(s, "Server returned 429 Too Many Requests");
257 }
258 }
259}