Skip to main content

ez_ffmpeg/
error.rs

1use ffmpeg_next::ffi::AVERROR;
2use ffmpeg_sys_next::*;
3use std::ffi::NulError;
4use std::{io, result};
5
6// The `opengl` module path is deprecated as a whole (superseded by
7// `wgpu_filter`), but the crate error enum must still name its typed error;
8// importing it here, with the module-path deprecation silenced, keeps the
9// variant and thiserror's generated `From` impl warning-free.
10#[cfg(feature = "opengl")]
11#[allow(deprecated)]
12use crate::opengl::OpenGLFilterError;
13
14/// Result type of all ez-ffmpeg library calls.
15pub type Result<T, E = Error> = result::Result<T, E>;
16
17#[derive(thiserror::Error, Debug)]
18#[non_exhaustive]
19pub enum Error {
20    #[error("Scheduler is not started")]
21    NotStarted,
22
23    #[error("URL error: {0}")]
24    Url(#[from] UrlError),
25
26    #[error("Open input stream error: {0}")]
27    OpenInputStream(#[from] OpenInputError),
28
29    #[error("Find stream info error: {0}")]
30    FindStream(#[from] FindStreamError),
31
32    #[error("Decoder error: {0}")]
33    Decoder(#[from] DecoderError),
34
35    #[error("Filter graph parse error: {0}")]
36    FilterGraphParse(#[from] FilterGraphParseError),
37
38    #[error("Filter description converted to utf8 string error")]
39    FilterDescUtf8,
40
41    #[error("Filter name converted to utf8 string error")]
42    FilterNameUtf8,
43
44    #[error("A filtergraph has zero outputs, this is not supported")]
45    FilterZeroOutputs,
46
47    #[error("A filtergraph has zero inputs, this is not supported")]
48    FilterZeroInputs,
49
50    #[error("Input is not a valid number")]
51    ParseInteger,
52
53    #[error("Alloc output context error: {0}")]
54    AllocOutputContext(#[from] AllocOutputContextError),
55
56    #[error("Open output error: {0}")]
57    OpenOutput(#[from] OpenOutputError),
58
59    #[error("Output file '{0}' is the same as an input file")]
60    FileSameAsInput(String),
61
62    #[error("Find devices error: {0}")]
63    FindDevices(#[from] FindDevicesError),
64
65    #[error("Alloc frame error: {0}")]
66    AllocFrame(#[from] AllocFrameError),
67
68    #[error("Alloc packet error: {0}")]
69    AllocPacket(#[from] AllocPacketError),
70
71    #[error("Frame writable error: {0}")]
72    FrameWritable(#[from] FrameWritableError),
73
74    // ---- Muxing ----
75    #[error("Muxing operation failed {0}")]
76    Muxing(#[from] MuxingOperationError),
77
78    // ---- Open Encoder ----
79    #[error("Open encoder operation failed {0}")]
80    OpenEncoder(#[from] OpenEncoderOperationError),
81
82    // ---- Encoding ----
83    #[error("Encoding operation failed {0}")]
84    Encoding(#[from] EncodingOperationError),
85
86    // ---- FilterGraph ----
87    #[error("Filter graph operation failed {0}")]
88    FilterGraph(#[from] FilterGraphOperationError),
89
90    // ---- Open Decoder ----
91    #[error("Open decoder operation failed {0}")]
92    OpenDecoder(#[from] OpenDecoderOperationError),
93
94    // ---- Decoding ----
95    #[error("Decoding operation failed {0}")]
96    Decoding(#[from] DecodingOperationError),
97
98    // ---- Demuxing ----
99    #[error("Demuxing operation failed {0}")]
100    Demuxing(#[from] DemuxingOperationError),
101
102    // ---- Packet Scanner ----
103    #[error("Packet scanner error: {0}")]
104    PacketScanner(#[from] PacketScannerError),
105
106    // ---- Frame Filter ----
107    #[error("Frame filter init failed: {0}")]
108    FrameFilterInit(Box<dyn std::error::Error + Send + Sync>),
109
110    #[error("Frame filter process failed: {0}")]
111    FrameFilterProcess(Box<dyn std::error::Error + Send + Sync>),
112
113    #[error("Frame filter request failed: {0}")]
114    FrameFilterRequest(Box<dyn std::error::Error + Send + Sync>),
115
116    #[error("No {0} stream of the type:{1} were found while build frame pipeline")]
117    FrameFilterTypeNoMatched(String, String),
118
119    #[error("{0} stream:{1} of the type:{2} were mismatched while build frame pipeline")]
120    FrameFilterStreamTypeNoMatched(String, usize, String),
121
122    #[error("Frame filter pipeline destination already finished")]
123    FrameFilterDstFinished,
124
125    #[error("Frame filter pipeline failed to duplicate a frame for an additional destination")]
126    FrameFilterFrameDuplicateFailed,
127
128    #[error("Frame filter pipeline thread exited")]
129    FrameFilterThreadExited,
130
131    #[error("Worker thread '{0}' panicked; output may be incomplete")]
132    WorkerPanicked(String),
133
134    #[cfg(feature = "rtmp")]
135    #[error("Rtmp stream already exists with key: {0}")]
136    RtmpStreamAlreadyExists(String),
137
138    #[cfg(feature = "rtmp")]
139    #[error("Rtmp create stream failed. Check whether the server is stopped.")]
140    RtmpCreateStream,
141
142    #[cfg(feature = "rtmp")]
143    #[error("Rtmp server thread exited")]
144    RtmpThreadExited,
145
146    #[cfg(feature = "rtmp")]
147    #[error("Rtmp stream closed: the server is no longer consuming this stream")]
148    RtmpStreamClosed,
149
150    #[cfg(feature = "subtitle")]
151    #[error("Subtitle error: {0}")]
152    Subtitle(#[from] crate::subtitle::SubtitleError),
153
154    #[cfg(feature = "wgpu")]
155    #[error("Wgpu filter error: {0}")]
156    WgpuFilter(#[from] crate::wgpu_filter::WgpuFilterError),
157
158    // The allow covers the deprecation that OpenGLFilterError inherits from
159    // the deprecated `opengl` module; the variant must still carry the type.
160    // From is hand-written below the enum (a derived #[from] would re-name
161    // the type in generated code that no #[allow] on the variant reaches).
162    #[cfg(feature = "opengl")]
163    #[allow(deprecated)]
164    #[error("OpenGL filter error: {0}")]
165    OpenGLFilter(#[source] OpenGLFilterError),
166
167    #[error("IO error:{0}")]
168    IO(#[from] io::Error),
169
170    #[error("EOF")]
171    EOF,
172    #[error("Exit")]
173    Exit,
174    #[error("Bug")]
175    Bug,
176
177    #[error("Invalid recipe argument: {0}")]
178    InvalidRecipeArg(String),
179
180    #[error("Container info error: {0}")]
181    ContainerInfo(#[from] ContainerInfoError),
182}
183
184// Hand-written counterpart of the #[from] the sibling variants derive: the
185// error type inherits deprecation from the deprecated `opengl` module, so
186// the conversion is spelled out where the lint can be silenced.
187#[cfg(feature = "opengl")]
188#[allow(deprecated)]
189impl From<OpenGLFilterError> for Error {
190    fn from(err: OpenGLFilterError) -> Self {
191        Error::OpenGLFilter(err)
192    }
193}
194
195/// Errors from the `container_info` queries where the caller asked for an index
196/// outside the container's range. These are caller/argument errors — a bad index
197/// into an otherwise valid container — kept distinct from an open/probe failure
198/// (`OpenInputError` / `FindStreamError`) so retry logic, telemetry, and user
199/// messages can tell "you asked for chapter 5 of a 3-chapter file" apart from
200/// "the file is corrupt or unreadable". Each variant carries the offending
201/// `index` and the container's actual `count`.
202#[derive(thiserror::Error, Debug)]
203#[non_exhaustive]
204pub enum ContainerInfoError {
205    #[error("chapter index {index} out of range: the container has {count} chapter(s)")]
206    ChapterIndexOutOfRange { index: usize, count: usize },
207
208    #[error("stream index {index} out of range: the container has {count} stream(s)")]
209    StreamIndexOutOfRange { index: usize, count: usize },
210}
211
212/// Error type for RTMP streaming operations using StreamBuilder
213#[cfg(feature = "rtmp")]
214#[derive(thiserror::Error, Debug)]
215#[non_exhaustive]
216pub enum StreamError {
217    #[error("missing required parameter: {0}")]
218    MissingParameter(&'static str),
219
220    #[error("input path is not a valid file: {path}")]
221    InputNotFound { path: std::path::PathBuf },
222
223    #[error("ffmpeg error: {0}")]
224    Ffmpeg(#[from] crate::error::Error),
225}
226
227impl PartialEq for Error {
228    /// Structural equality for payload-less variants only. Variants carrying
229    /// an inner error compare unequal even to themselves — use matches! on
230    /// the variant when that is what you mean.
231    fn eq(&self, other: &Self) -> bool {
232        use Error::*;
233        match (self, other) {
234            (NotStarted, NotStarted)
235            | (FilterDescUtf8, FilterDescUtf8)
236            | (FilterNameUtf8, FilterNameUtf8)
237            | (FilterZeroOutputs, FilterZeroOutputs)
238            | (FilterZeroInputs, FilterZeroInputs)
239            | (ParseInteger, ParseInteger)
240            | (FrameFilterDstFinished, FrameFilterDstFinished)
241            | (FrameFilterFrameDuplicateFailed, FrameFilterFrameDuplicateFailed)
242            | (FrameFilterThreadExited, FrameFilterThreadExited)
243            | (EOF, EOF)
244            | (Exit, Exit)
245            | (Bug, Bug) => true,
246            #[cfg(feature = "rtmp")]
247            (RtmpCreateStream, RtmpCreateStream)
248            | (RtmpThreadExited, RtmpThreadExited)
249            | (RtmpStreamClosed, RtmpStreamClosed) => true,
250            _ => false,
251        }
252    }
253}
254
255// No Eq impl: variants carrying payloads are not equal to themselves, so
256// the relation is not reflexive and claiming Eq would be a lie.
257
258#[derive(thiserror::Error, Debug)]
259#[non_exhaustive]
260pub enum DemuxingOperationError {
261    #[error("while reading frame: {0}")]
262    ReadFrameError(DemuxingError),
263
264    #[error("while referencing packet: {0}")]
265    PacketRefError(DemuxingError),
266
267    #[error("while seeking file: {0}")]
268    SeekFileError(DemuxingError),
269
270    #[error("Thread exited")]
271    ThreadExited,
272}
273
274#[derive(thiserror::Error, Debug)]
275#[non_exhaustive]
276pub enum DecodingOperationError {
277    #[error("during frame reference creation: {0}")]
278    FrameRefError(DecodingError),
279
280    #[error("during frame properties copy: {0}")]
281    FrameCopyPropsError(DecodingError),
282
283    #[error("during subtitle decoding: {0}")]
284    DecodeSubtitleError(DecodingError),
285
286    #[error("during subtitle copy: {0}")]
287    CopySubtitleError(DecodingError),
288
289    #[error("during packet submission to decoder: {0}")]
290    SendPacketError(DecodingError),
291
292    #[error("during frame reception from decoder: {0}")]
293    ReceiveFrameError(DecodingError),
294
295    #[error("during frame allocation: {0}")]
296    FrameAllocationError(DecodingError),
297
298    #[error("during packet allocation: {0}")]
299    PacketAllocationError(DecodingError),
300
301    #[error("during AVSubtitle allocation: {0}")]
302    SubtitleAllocationError(DecodingError),
303
304    #[error("corrupt decoded frame")]
305    CorruptFrame,
306
307    #[error("decode error rate exceeded the maximum allowed")]
308    ErrorRateExceeded,
309
310    #[error("during retrieve data on hw: {0}")]
311    HWRetrieveDataError(DecodingError),
312
313    #[error("during cropping: {0}")]
314    CroppingError(DecodingError),
315}
316
317#[derive(thiserror::Error, Debug)]
318#[non_exhaustive]
319pub enum OpenDecoderOperationError {
320    #[error("during context allocation: {0}")]
321    ContextAllocationError(OpenDecoderError),
322
323    #[error("while applying parameters to context: {0}")]
324    ParameterApplicationError(OpenDecoderError),
325
326    #[error("while opening decoder: {0}")]
327    DecoderOpenError(OpenDecoderError),
328
329    #[error("while copying channel layout: {0}")]
330    ChannelLayoutCopyError(OpenDecoderError),
331
332    #[error("while Hw setup: {0}")]
333    HwSetupError(OpenDecoderError),
334
335    #[error("Invalid decoder name")]
336    InvalidName,
337
338    #[error("Thread exited")]
339    ThreadExited,
340}
341
342#[derive(thiserror::Error, Debug)]
343#[non_exhaustive]
344pub enum FilterGraphOperationError {
345    #[error("during requesting oldest frame: {0}")]
346    RequestOldestError(FilterGraphError),
347
348    #[error("during process frames: {0}")]
349    ProcessFramesError(FilterGraphError),
350
351    #[error("during send frames: {0}")]
352    SendFramesError(FilterGraphError),
353
354    #[error("during copying channel layout: {0}")]
355    ChannelLayoutCopyError(FilterGraphError),
356
357    #[error("during buffer source add frame: {0}")]
358    BufferSourceAddFrameError(FilterGraphError),
359
360    #[error("during closing buffer source: {0}")]
361    BufferSourceCloseError(FilterGraphError),
362
363    #[error("during replace buffer: {0}")]
364    BufferReplaceoseError(FilterGraphError),
365
366    #[error("during cloning frame side data: {0}")]
367    FrameSideDataCloneError(FilterGraphError),
368
369    #[error("during parse: {0}")]
370    ParseError(FilterGraphParseError),
371
372    #[error("The data in the frame is invalid or corrupted")]
373    InvalidData,
374
375    #[error(
376        "graph input '{0}' already holds {1} buffered frames and admitting the next \
377         one would raise the best-effort retained-memory estimate to ~{2} bytes, \
378         while another input has not yet delivered its first frame, so the filter \
379         graph cannot be configured; check that every graph input actually produces \
380         data (or produces it within the buffering window)"
381    )]
382    PreConfigQueueOverflow(String, usize, usize),
383
384    // Only constructed on the FFmpeg 8+ buffersrc side-data clone path.
385    #[cfg_attr(not(ffmpeg_8_0), allow(dead_code))]
386    #[error(
387        "graph input '{0}' would deep-copy an estimated {1} bytes of side-data \
388         metadata into the buffersrc parameters, exceeding the side-data clone \
389         estimate threshold; the frame's combined side-data metadata (across its \
390         global and downmix entries) is pathologically large"
391    )]
392    OversizedSideDataClone(String, usize),
393
394    #[error("Thread exited")]
395    ThreadExited,
396}
397
398#[derive(thiserror::Error, Debug)]
399#[non_exhaustive]
400pub enum EncodingOperationError {
401    #[error("during frame submission: {0}")]
402    SendFrameError(EncodingError),
403
404    #[error("during packet retrieval: {0}")]
405    ReceivePacketError(EncodingError),
406
407    #[error("during audio frame receive: {0}")]
408    ReceiveAudioError(EncodingError),
409
410    #[error(": Subtitle packets must have a pts")]
411    SubtitleNotPts,
412
413    #[error(": Muxer already finished")]
414    MuxerFinished,
415
416    /// An output stream buffered more packets before the muxer started than the
417    /// pre-mux queue admits (fftools `AVERROR_BUFFER_TOO_SMALL`, "Too many
418    /// packets buffered for output stream"). Unlike `MuxerFinished` this is a
419    /// hard failure — never a silent truncation — so it must reach the
420    /// scheduler error, not the graceful stop path.
421    #[error(": too many packets buffered for an output stream before the muxer started; raise Output::set_max_muxing_queue_size / Output::set_muxing_queue_data_threshold, or check that every mapped output stream receives data")]
422    MuxQueueFull,
423
424    #[error("Encode subtitle error: {0}")]
425    EncodeSubtitle(#[from] EncodeSubtitleError),
426
427    #[error(": {0}")]
428    AllocPacket(AllocPacketError),
429}
430
431#[derive(thiserror::Error, Debug)]
432#[non_exhaustive]
433pub enum MuxingOperationError {
434    #[error("during write header: {0}")]
435    WriteHeader(WriteHeaderError),
436
437    #[error("while initializing bitstream filter chain '{0}': {1}")]
438    BitstreamFilterInit(String, MuxingError),
439
440    #[error("during interleaved write: {0}")]
441    InterleavedWriteError(MuxingError),
442
443    #[error("during trailer write: {0}")]
444    TrailerWriteError(MuxingError),
445
446    #[error("during closing IO: {0}")]
447    IOCloseError(MuxingError),
448
449    #[error("Thread exited")]
450    ThreadExited,
451}
452
453#[derive(thiserror::Error, Debug)]
454#[non_exhaustive]
455pub enum OpenEncoderOperationError {
456    #[error("during frame side data cloning: {0}")]
457    FrameSideDataCloneError(OpenEncoderError),
458
459    #[error("during channel layout copying: {0}")]
460    ChannelLayoutCopyError(OpenEncoderError),
461
462    #[error("during codec opening: {0}")]
463    CodecOpenError(OpenEncoderError),
464
465    #[error("while setting codec parameters: {0}")]
466    CodecParametersError(OpenEncoderError),
467
468    #[error(": unknown format of the frame")]
469    UnknownFrameFormat,
470
471    #[error("while setting subtitle: {0}")]
472    SettingSubtitleError(OpenEncoderError),
473
474    #[error("while Hw setup: {0}")]
475    HwSetupError(OpenEncoderError),
476
477    #[error("during context allocation: {0}")]
478    ContextAllocationError(OpenEncoderError),
479
480    #[error(": no frames were received before EOF; encoder never opened")]
481    NoFramesReceived,
482
483    #[error(": unsupported media type for encoding")]
484    UnsupportedMediaType,
485
486    #[error("Thread exited")]
487    ThreadExited,
488}
489
490#[derive(thiserror::Error, Debug)]
491#[non_exhaustive]
492pub enum UrlError {
493    #[error("Null byte found in string at position {0}")]
494    NullByteError(usize),
495}
496
497impl From<NulError> for Error {
498    fn from(err: NulError) -> Self {
499        Error::Url(UrlError::NullByteError(err.nul_position()))
500    }
501}
502
503#[derive(thiserror::Error, Debug)]
504#[non_exhaustive]
505pub enum OpenInputError {
506    #[error("Memory allocation error")]
507    OutOfMemory,
508
509    #[error("Invalid argument provided")]
510    InvalidArgument,
511
512    #[error("File or stream not found")]
513    NotFound,
514
515    #[error("I/O error occurred while opening the file or stream")]
516    IOError,
517
518    #[error("Pipe error, possibly the stream or data connection was broken")]
519    PipeError,
520
521    #[error("Invalid file descriptor")]
522    BadFileDescriptor,
523
524    #[error("Functionality not implemented or unsupported input format")]
525    NotImplemented,
526
527    #[error("Operation not permitted to access the file or stream")]
528    OperationNotPermitted,
529
530    #[error("The data in the file or stream is invalid or corrupted")]
531    InvalidData,
532
533    #[error("The connection timed out while trying to open the stream")]
534    Timeout,
535
536    /// A builder option carried an invalid value (e.g. a non-positive
537    /// `set_framerate`, a non-finite `set_ts_scale`, an out-of-range
538    /// `set_io_buffer_size`). Setters store values as given and defer
539    /// validation to open time, so a bad value surfaces here instead of
540    /// panicking in the setter.
541    #[error("Invalid input option: {0}")]
542    InvalidOption(String),
543
544    #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
545    UnknownError(i32),
546
547    #[error("Invalid source provided")]
548    InvalidSource,
549
550    #[error("Invalid source format:{0}")]
551    InvalidFormat(String),
552
553    #[error("No seek callback is provided")]
554    SeekFunctionMissing,
555}
556
557impl From<i32> for OpenInputError {
558    fn from(err_code: i32) -> Self {
559        match err_code {
560            AVERROR_OUT_OF_MEMORY => OpenInputError::OutOfMemory,
561            AVERROR_INVALID_ARGUMENT => OpenInputError::InvalidArgument,
562            AVERROR_NOT_FOUND => OpenInputError::NotFound,
563            AVERROR_IO_ERROR => OpenInputError::IOError,
564            AVERROR_PIPE_ERROR => OpenInputError::PipeError,
565            AVERROR_BAD_FILE_DESCRIPTOR => OpenInputError::BadFileDescriptor,
566            AVERROR_NOT_IMPLEMENTED => OpenInputError::NotImplemented,
567            AVERROR_OPERATION_NOT_PERMITTED => OpenInputError::OperationNotPermitted,
568            AVERROR_INVALIDDATA => OpenInputError::InvalidData,
569            AVERROR_TIMEOUT => OpenInputError::Timeout,
570            _ => OpenInputError::UnknownError(err_code),
571        }
572    }
573}
574
575const AVERROR_OUT_OF_MEMORY: i32 = AVERROR(ENOMEM);
576const AVERROR_INVALID_ARGUMENT: i32 = AVERROR(EINVAL);
577const AVERROR_NOT_FOUND: i32 = AVERROR(ENOENT);
578const AVERROR_IO_ERROR: i32 = AVERROR(EIO);
579const AVERROR_PIPE_ERROR: i32 = AVERROR(EPIPE);
580const AVERROR_BAD_FILE_DESCRIPTOR: i32 = AVERROR(EBADF);
581const AVERROR_NOT_IMPLEMENTED: i32 = AVERROR(ENOSYS);
582const AVERROR_OPERATION_NOT_PERMITTED: i32 = AVERROR(EPERM);
583const AVERROR_PERMISSION_DENIED: i32 = AVERROR(EACCES);
584const AVERROR_TIMEOUT: i32 = AVERROR(ETIMEDOUT);
585const AVERROR_NOT_SOCKET: i32 = AVERROR(ENOTSOCK);
586const AVERROR_AGAIN: i32 = AVERROR(EAGAIN);
587
588#[derive(thiserror::Error, Debug)]
589#[non_exhaustive]
590pub enum FindStreamError {
591    #[error("Memory allocation error")]
592    OutOfMemory,
593
594    #[error("Invalid argument provided")]
595    InvalidArgument,
596
597    #[error("Reached end of file while looking for stream info")]
598    EndOfFile,
599
600    #[error("Timeout occurred while reading stream info")]
601    Timeout,
602
603    #[error("I/O error occurred while reading stream info")]
604    IOError,
605
606    #[error("The data in the stream is invalid or corrupted")]
607    InvalidData,
608
609    #[error("Functionality not implemented or unsupported stream format")]
610    NotImplemented,
611
612    #[error("Operation not permitted to access the file or stream")]
613    OperationNotPermitted,
614
615    #[error("No Stream found")]
616    NoStreamFound,
617
618    #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
619    UnknownError(i32),
620}
621
622impl From<i32> for FindStreamError {
623    fn from(err_code: i32) -> Self {
624        match err_code {
625            AVERROR_OUT_OF_MEMORY => FindStreamError::OutOfMemory,
626            AVERROR_INVALID_ARGUMENT => FindStreamError::InvalidArgument,
627            AVERROR_EOF => FindStreamError::EndOfFile,
628            AVERROR_TIMEOUT => FindStreamError::Timeout,
629            AVERROR_IO_ERROR => FindStreamError::IOError,
630            AVERROR_INVALIDDATA => FindStreamError::InvalidData,
631            AVERROR_NOT_IMPLEMENTED => FindStreamError::NotImplemented,
632            AVERROR_OPERATION_NOT_PERMITTED => FindStreamError::OperationNotPermitted,
633            _ => FindStreamError::UnknownError(err_code),
634        }
635    }
636}
637
638#[derive(thiserror::Error, Debug)]
639#[non_exhaustive]
640pub enum FilterGraphParseError {
641    #[error("Memory allocation error")]
642    OutOfMemory,
643
644    #[error("Invalid argument provided")]
645    InvalidArgument,
646
647    #[error("End of file reached during parsing")]
648    EndOfFile,
649
650    #[error("I/O error occurred during parsing")]
651    IOError,
652
653    #[error("Invalid data encountered during parsing")]
654    InvalidData,
655
656    #[error("Functionality not implemented or unsupported filter format")]
657    NotImplemented,
658
659    #[error("Permission denied during filter graph parsing")]
660    PermissionDenied,
661
662    #[error("Socket operation on non-socket during filter graph parsing")]
663    NotSocket,
664
665    #[error("Option not found during filter graph configuration")]
666    OptionNotFound,
667
668    #[error("Invalid file index {0} in filtergraph description {1}")]
669    InvalidFileIndexInFg(usize, String),
670
671    #[error("Invalid file index {0} in output url: {1}")]
672    InvalidFileIndexInOutput(usize, String),
673
674    #[error("Invalid filter specifier {0}")]
675    InvalidFilterSpecifier(String),
676
677    #[error("Filter '{0}' has output {1} ({2}) unconnected")]
678    OutputUnconnected(String, usize, String),
679
680    #[error("An unknown error occurred. ret: {0}")]
681    UnknownError(i32),
682}
683
684impl From<i32> for FilterGraphParseError {
685    fn from(err_code: i32) -> Self {
686        match err_code {
687            AVERROR_OUT_OF_MEMORY => FilterGraphParseError::OutOfMemory,
688            AVERROR_INVALID_ARGUMENT => FilterGraphParseError::InvalidArgument,
689            AVERROR_EOF => FilterGraphParseError::EndOfFile,
690            AVERROR_IO_ERROR => FilterGraphParseError::IOError,
691            AVERROR_INVALIDDATA => FilterGraphParseError::InvalidData,
692            AVERROR_NOT_IMPLEMENTED => FilterGraphParseError::NotImplemented,
693            AVERROR_OPTION_NOT_FOUND => FilterGraphParseError::OptionNotFound,
694            // EACCES/ENOTSOCK reach here from filters that touch files or
695            // sockets (e.g. `movie=`); map them to the variants this enum
696            // already declares instead of degrading to UnknownError.
697            AVERROR_PERMISSION_DENIED => FilterGraphParseError::PermissionDenied,
698            AVERROR_NOT_SOCKET => FilterGraphParseError::NotSocket,
699            _ => FilterGraphParseError::UnknownError(err_code),
700        }
701    }
702}
703
704#[derive(thiserror::Error, Debug)]
705#[non_exhaustive]
706pub enum AllocOutputContextError {
707    #[error("Memory allocation error")]
708    OutOfMemory,
709
710    #[error("Invalid argument provided")]
711    InvalidArgument,
712
713    #[error("File or stream not found")]
714    NotFound,
715
716    #[error("I/O error occurred while allocating the output context")]
717    IOError,
718
719    #[error("Pipe error, possibly the stream or data connection was broken")]
720    PipeError,
721
722    #[error("Invalid file descriptor")]
723    BadFileDescriptor,
724
725    #[error("Functionality not implemented or unsupported output format")]
726    NotImplemented,
727
728    #[error("Operation not permitted to allocate the output context")]
729    OperationNotPermitted,
730
731    #[error("Permission denied while allocating the output context")]
732    PermissionDenied,
733
734    #[error("The connection timed out while trying to allocate the output context")]
735    Timeout,
736
737    #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
738    UnknownError(i32),
739}
740
741impl From<i32> for AllocOutputContextError {
742    fn from(err_code: i32) -> Self {
743        match err_code {
744            AVERROR_OUT_OF_MEMORY => AllocOutputContextError::OutOfMemory,
745            AVERROR_INVALID_ARGUMENT => AllocOutputContextError::InvalidArgument,
746            AVERROR_NOT_FOUND => AllocOutputContextError::NotFound,
747            AVERROR_IO_ERROR => AllocOutputContextError::IOError,
748            AVERROR_PIPE_ERROR => AllocOutputContextError::PipeError,
749            AVERROR_BAD_FILE_DESCRIPTOR => AllocOutputContextError::BadFileDescriptor,
750            AVERROR_NOT_IMPLEMENTED => AllocOutputContextError::NotImplemented,
751            AVERROR_OPERATION_NOT_PERMITTED => AllocOutputContextError::OperationNotPermitted,
752            AVERROR_PERMISSION_DENIED => AllocOutputContextError::PermissionDenied,
753            AVERROR_TIMEOUT => AllocOutputContextError::Timeout,
754            _ => AllocOutputContextError::UnknownError(err_code),
755        }
756    }
757}
758
759#[derive(thiserror::Error, Debug)]
760#[non_exhaustive]
761pub enum OpenOutputError {
762    #[error("Memory allocation error")]
763    OutOfMemory,
764
765    #[error("Invalid argument provided")]
766    InvalidArgument,
767
768    #[error("File or stream not found")]
769    NotFound,
770
771    #[error("I/O error occurred while opening the file or stream")]
772    IOError,
773
774    #[error("Pipe error, possibly the stream or data connection was broken")]
775    PipeError,
776
777    #[error("Invalid file descriptor")]
778    BadFileDescriptor,
779
780    #[error("Functionality not implemented or unsupported output format")]
781    NotImplemented,
782
783    #[error("Operation not permitted to open the file or stream")]
784    OperationNotPermitted,
785
786    #[error("Permission denied while opening the file or stream")]
787    PermissionDenied,
788
789    #[error("The connection timed out while trying to open the file or stream")]
790    Timeout,
791
792    #[error("encoder not found")]
793    EncoderNotFound,
794
795    #[error("Stream map '{0}' matches no streams;")]
796    MatchesNoStreams(String),
797
798    #[error("Invalid label {0}")]
799    InvalidLabel(String),
800
801    #[error("not contain any stream")]
802    NotContainStream,
803
804    #[error("unknown format of the frame")]
805    UnknownFrameFormat,
806
807    #[error("Invalid file index {0} in input url: {1}")]
808    InvalidFileIndexInIntput(usize, String),
809
810    #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
811    UnknownError(i32),
812
813    #[error("Invalid sink provided")]
814    InvalidSink,
815
816    #[error("No seek callback is provided")]
817    SeekFunctionMissing,
818
819    #[error("Format '{0}' is unsupported")]
820    FormatUnsupported(String),
821
822    #[error("Unknown pixel format: '{0}'")]
823    UnknownPixelFormat(String),
824
825    #[error("Unknown sample format: '{0}'")]
826    UnknownSampleFormat(String),
827
828    /// A builder option carried an invalid value (e.g. a malformed
829    /// `set_force_key_frames` spec, an out-of-range `set_io_buffer_size`).
830    /// Setters store values as given and defer validation to open time, so
831    /// a bad value surfaces here instead of panicking in the setter or
832    /// forcing a `Result` into the middle of a builder chain.
833    #[error("Invalid output option: {0}")]
834    InvalidOption(String),
835
836    #[error("Failed to read attachment file '{0}'")]
837    AttachmentRead(String, #[source] io::Error),
838
839    #[error("Attachment file '{0}' is empty")]
840    AttachmentEmpty(String),
841
842    #[error("Attachment file '{0}' is too large ({1} bytes, limit {2} bytes)")]
843    AttachmentTooLarge(String, u64, u64),
844
845    #[error("Attachment mimetype must not be empty (file '{0}')")]
846    AttachmentEmptyMimetype(String),
847}
848
849impl From<i32> for OpenOutputError {
850    fn from(err_code: i32) -> Self {
851        match err_code {
852            AVERROR_OUT_OF_MEMORY => OpenOutputError::OutOfMemory,
853            AVERROR_INVALID_ARGUMENT => OpenOutputError::InvalidArgument,
854            AVERROR_NOT_FOUND => OpenOutputError::NotFound,
855            AVERROR_IO_ERROR => OpenOutputError::IOError,
856            AVERROR_PIPE_ERROR => OpenOutputError::PipeError,
857            AVERROR_BAD_FILE_DESCRIPTOR => OpenOutputError::BadFileDescriptor,
858            AVERROR_NOT_IMPLEMENTED => OpenOutputError::NotImplemented,
859            AVERROR_OPERATION_NOT_PERMITTED => OpenOutputError::OperationNotPermitted,
860            AVERROR_PERMISSION_DENIED => OpenOutputError::PermissionDenied,
861            AVERROR_TIMEOUT => OpenOutputError::Timeout,
862            AVERROR_ENCODER_NOT_FOUND => OpenOutputError::EncoderNotFound,
863            _ => OpenOutputError::UnknownError(err_code),
864        }
865    }
866}
867
868#[derive(thiserror::Error, Debug)]
869#[non_exhaustive]
870pub enum FindDevicesError {
871    #[error("AVCaptureDevice class not found in macOS")]
872    AVCaptureDeviceNotFound,
873
874    #[error("current media_type({0}) is not supported")]
875    MediaTypeSupported(i32),
876    #[error("current OS is not supported")]
877    OsNotSupported,
878    #[error("device_description can not to string")]
879    UTF8Error,
880
881    #[error("Memory allocation error")]
882    OutOfMemory,
883    #[error("Invalid argument provided")]
884    InvalidArgument,
885    #[error("Device or stream not found")]
886    NotFound,
887    #[error("I/O error occurred while accessing the device or stream")]
888    IOError,
889    #[error("Operation not permitted for this device or stream")]
890    OperationNotPermitted,
891    #[error("Permission denied while accessing the device or stream")]
892    PermissionDenied,
893    #[error("This functionality is not implemented")]
894    NotImplemented,
895    #[error("Bad file descriptor")]
896    BadFileDescriptor,
897    #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
898    UnknownError(i32),
899}
900
901impl From<i32> for FindDevicesError {
902    fn from(err_code: i32) -> Self {
903        match err_code {
904            AVERROR_OUT_OF_MEMORY => FindDevicesError::OutOfMemory,
905            AVERROR_INVALID_ARGUMENT => FindDevicesError::InvalidArgument,
906            AVERROR_NOT_FOUND => FindDevicesError::NotFound,
907            AVERROR_IO_ERROR => FindDevicesError::IOError,
908            AVERROR_OPERATION_NOT_PERMITTED => FindDevicesError::OperationNotPermitted,
909            AVERROR_PERMISSION_DENIED => FindDevicesError::PermissionDenied,
910            AVERROR_NOT_IMPLEMENTED => FindDevicesError::NotImplemented,
911            AVERROR_BAD_FILE_DESCRIPTOR => FindDevicesError::BadFileDescriptor,
912            _ => FindDevicesError::UnknownError(err_code),
913        }
914    }
915}
916
917#[derive(thiserror::Error, Debug)]
918#[non_exhaustive]
919pub enum WriteHeaderError {
920    #[error("Memory allocation error")]
921    OutOfMemory,
922
923    #[error("Invalid argument provided")]
924    InvalidArgument,
925
926    #[error("File or stream not found")]
927    NotFound,
928
929    #[error("I/O error occurred while writing the header")]
930    IOError,
931
932    #[error("Pipe error, possibly the stream or data connection was broken")]
933    PipeError,
934
935    #[error("Invalid file descriptor")]
936    BadFileDescriptor,
937
938    #[error("Functionality not implemented or unsupported output format")]
939    NotImplemented,
940
941    #[error("Operation not permitted to write the header")]
942    OperationNotPermitted,
943
944    #[error("Permission denied while writing the header")]
945    PermissionDenied,
946
947    #[error("The connection timed out while trying to write the header")]
948    Timeout,
949
950    #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
951    UnknownError(i32),
952}
953
954impl From<i32> for WriteHeaderError {
955    fn from(err_code: i32) -> Self {
956        match err_code {
957            AVERROR_OUT_OF_MEMORY => WriteHeaderError::OutOfMemory,
958            AVERROR_INVALID_ARGUMENT => WriteHeaderError::InvalidArgument,
959            AVERROR_NOT_FOUND => WriteHeaderError::NotFound,
960            AVERROR_IO_ERROR => WriteHeaderError::IOError,
961            AVERROR_PIPE_ERROR => WriteHeaderError::PipeError,
962            AVERROR_BAD_FILE_DESCRIPTOR => WriteHeaderError::BadFileDescriptor,
963            AVERROR_NOT_IMPLEMENTED => WriteHeaderError::NotImplemented,
964            AVERROR_OPERATION_NOT_PERMITTED => WriteHeaderError::OperationNotPermitted,
965            AVERROR_PERMISSION_DENIED => WriteHeaderError::PermissionDenied,
966            AVERROR_TIMEOUT => WriteHeaderError::Timeout,
967            _ => WriteHeaderError::UnknownError(err_code),
968        }
969    }
970}
971
972#[derive(thiserror::Error, Debug)]
973#[non_exhaustive]
974pub enum EncodeSubtitleError {
975    #[error("Memory allocation error while encoding subtitle")]
976    OutOfMemory,
977
978    #[error("Invalid argument provided for subtitle encoding")]
979    InvalidArgument,
980
981    #[error("Operation not permitted while encoding subtitle")]
982    OperationNotPermitted,
983
984    #[error("The encoding functionality is not implemented or unsupported")]
985    NotImplemented,
986
987    #[error("Encoder temporarily unable to process, please retry")]
988    TryAgain,
989
990    #[error("Subtitle encoding failed with unknown error. ret: {0}")]
991    UnknownError(i32),
992}
993
994impl From<i32> for EncodeSubtitleError {
995    fn from(err_code: i32) -> Self {
996        match err_code {
997            AVERROR_OUT_OF_MEMORY => EncodeSubtitleError::OutOfMemory,
998            AVERROR_INVALID_ARGUMENT => EncodeSubtitleError::InvalidArgument,
999            AVERROR_OPERATION_NOT_PERMITTED => EncodeSubtitleError::OperationNotPermitted,
1000            AVERROR_NOT_IMPLEMENTED => EncodeSubtitleError::NotImplemented,
1001            AVERROR_AGAIN => EncodeSubtitleError::TryAgain,
1002            _ => EncodeSubtitleError::UnknownError(err_code),
1003        }
1004    }
1005}
1006
1007#[derive(thiserror::Error, Debug)]
1008#[non_exhaustive]
1009pub enum AllocPacketError {
1010    #[error("Memory allocation error while alloc packet")]
1011    OutOfMemory,
1012}
1013
1014#[derive(thiserror::Error, Debug)]
1015#[non_exhaustive]
1016pub enum AllocFrameError {
1017    #[error("Memory allocation error while alloc frame")]
1018    OutOfMemory,
1019}
1020
1021/// Errors from [`make_frame_writable`], the safe wrapper over FFmpeg's
1022/// `av_frame_make_writable`: ensuring exclusive ownership of a frame's data
1023/// buffers may allocate new buffers and copy into them, and that underlying
1024/// call can fail. Common AVERROR codes map to named variants; anything else
1025/// carries the raw code.
1026///
1027/// [`make_frame_writable`]: crate::util::ffmpeg_utils::make_frame_writable
1028#[derive(thiserror::Error, Debug)]
1029#[non_exhaustive]
1030pub enum FrameWritableError {
1031    #[error("Memory allocation error while copying frame data")]
1032    OutOfMemory,
1033
1034    #[error("Invalid argument provided")]
1035    InvalidArgument,
1036
1037    #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1038    UnknownError(i32),
1039}
1040
1041impl From<i32> for FrameWritableError {
1042    fn from(err_code: i32) -> Self {
1043        match err_code {
1044            AVERROR_OUT_OF_MEMORY => FrameWritableError::OutOfMemory,
1045            AVERROR_INVALID_ARGUMENT => FrameWritableError::InvalidArgument,
1046            _ => FrameWritableError::UnknownError(err_code),
1047        }
1048    }
1049}
1050
1051#[derive(thiserror::Error, Debug)]
1052#[non_exhaustive]
1053pub enum MuxingError {
1054    #[error("Memory allocation error")]
1055    OutOfMemory,
1056
1057    #[error("Invalid argument provided")]
1058    InvalidArgument,
1059
1060    #[error("I/O error occurred during muxing")]
1061    IOError,
1062
1063    #[error("Broken pipe during muxing")]
1064    PipeError,
1065
1066    #[error("Bad file descriptor encountered")]
1067    BadFileDescriptor,
1068
1069    #[error("Functionality not implemented or unsupported")]
1070    NotImplemented,
1071
1072    #[error("Operation not permitted")]
1073    OperationNotPermitted,
1074
1075    #[error("Resource temporarily unavailable")]
1076    TryAgain,
1077
1078    #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1079    UnknownError(i32),
1080}
1081
1082impl From<i32> for MuxingError {
1083    fn from(err_code: i32) -> Self {
1084        match err_code {
1085            AVERROR_OUT_OF_MEMORY => MuxingError::OutOfMemory,
1086            AVERROR_INVALID_ARGUMENT => MuxingError::InvalidArgument,
1087            AVERROR_IO_ERROR => MuxingError::IOError,
1088            AVERROR_PIPE_ERROR => MuxingError::PipeError,
1089            AVERROR_BAD_FILE_DESCRIPTOR => MuxingError::BadFileDescriptor,
1090            AVERROR_NOT_IMPLEMENTED => MuxingError::NotImplemented,
1091            AVERROR_OPERATION_NOT_PERMITTED => MuxingError::OperationNotPermitted,
1092            AVERROR_AGAIN => MuxingError::TryAgain,
1093            _ => MuxingError::UnknownError(err_code),
1094        }
1095    }
1096}
1097
1098#[derive(thiserror::Error, Debug)]
1099#[non_exhaustive]
1100pub enum OpenEncoderError {
1101    #[error("Memory allocation error occurred during encoder initialization")]
1102    OutOfMemory,
1103
1104    #[error("Invalid argument provided to encoder")]
1105    InvalidArgument,
1106
1107    #[error("I/O error occurred while opening encoder")]
1108    IOError,
1109
1110    #[error("Broken pipe encountered during encoder initialization")]
1111    PipeError,
1112
1113    #[error("Bad file descriptor used in encoder")]
1114    BadFileDescriptor,
1115
1116    #[error("Encoder functionality not implemented or unsupported")]
1117    NotImplemented,
1118
1119    #[error("Operation not permitted while configuring encoder")]
1120    OperationNotPermitted,
1121
1122    #[error("Resource temporarily unavailable during encoder setup")]
1123    TryAgain,
1124
1125    #[error("An unknown error occurred in encoder setup. ret:{0}")]
1126    UnknownError(i32),
1127}
1128
1129impl From<i32> for OpenEncoderError {
1130    fn from(err_code: i32) -> Self {
1131        match err_code {
1132            AVERROR_OUT_OF_MEMORY => OpenEncoderError::OutOfMemory,
1133            AVERROR_INVALID_ARGUMENT => OpenEncoderError::InvalidArgument,
1134            AVERROR_IO_ERROR => OpenEncoderError::IOError,
1135            AVERROR_PIPE_ERROR => OpenEncoderError::PipeError,
1136            AVERROR_BAD_FILE_DESCRIPTOR => OpenEncoderError::BadFileDescriptor,
1137            AVERROR_NOT_IMPLEMENTED => OpenEncoderError::NotImplemented,
1138            AVERROR_OPERATION_NOT_PERMITTED => OpenEncoderError::OperationNotPermitted,
1139            AVERROR_AGAIN => OpenEncoderError::TryAgain,
1140            _ => OpenEncoderError::UnknownError(err_code),
1141        }
1142    }
1143}
1144
1145#[derive(thiserror::Error, Debug)]
1146#[non_exhaustive]
1147pub enum EncodingError {
1148    #[error("Memory allocation error during encoding")]
1149    OutOfMemory,
1150
1151    #[error("Invalid argument provided to encoder")]
1152    InvalidArgument,
1153
1154    #[error("I/O error occurred during encoding")]
1155    IOError,
1156
1157    #[error("Broken pipe encountered during encoding")]
1158    PipeError,
1159
1160    #[error("Bad file descriptor encountered during encoding")]
1161    BadFileDescriptor,
1162
1163    #[error("Functionality not implemented or unsupported encoding feature")]
1164    NotImplemented,
1165
1166    #[error("Operation not permitted for encoder")]
1167    OperationNotPermitted,
1168
1169    #[error("Resource temporarily unavailable, try again later")]
1170    TryAgain,
1171
1172    #[error("End of stream reached or no more frames to encode")]
1173    EndOfStream,
1174
1175    #[error("An unknown error occurred during encoding. ret: {0}")]
1176    UnknownError(i32),
1177}
1178
1179impl From<i32> for EncodingError {
1180    fn from(err_code: i32) -> Self {
1181        match err_code {
1182            AVERROR_OUT_OF_MEMORY => EncodingError::OutOfMemory,
1183            AVERROR_INVALID_ARGUMENT => EncodingError::InvalidArgument,
1184            AVERROR_IO_ERROR => EncodingError::IOError,
1185            AVERROR_PIPE_ERROR => EncodingError::PipeError,
1186            AVERROR_BAD_FILE_DESCRIPTOR => EncodingError::BadFileDescriptor,
1187            AVERROR_NOT_IMPLEMENTED => EncodingError::NotImplemented,
1188            AVERROR_OPERATION_NOT_PERMITTED => EncodingError::OperationNotPermitted,
1189            AVERROR_AGAIN => EncodingError::TryAgain,
1190            AVERROR_EOF => EncodingError::EndOfStream,
1191            _ => EncodingError::UnknownError(err_code),
1192        }
1193    }
1194}
1195
1196#[derive(thiserror::Error, Debug)]
1197#[non_exhaustive]
1198pub enum FilterGraphError {
1199    #[error("Memory allocation error during filter graph processing")]
1200    OutOfMemory,
1201
1202    #[error("Invalid argument provided to filter graph processing")]
1203    InvalidArgument,
1204
1205    #[error("I/O error occurred during filter graph processing")]
1206    IOError,
1207
1208    #[error("Broken pipe during filter graph processing")]
1209    PipeError,
1210
1211    #[error("Bad file descriptor encountered during filter graph processing")]
1212    BadFileDescriptor,
1213
1214    #[error("Functionality not implemented or unsupported during filter graph processing")]
1215    NotImplemented,
1216
1217    #[error("Operation not permitted during filter graph processing")]
1218    OperationNotPermitted,
1219
1220    #[error("Resource temporarily unavailable during filter graph processing")]
1221    TryAgain,
1222
1223    #[error("EOF")]
1224    EOF,
1225
1226    #[error("An unknown error occurred during filter graph processing. ret:{0}")]
1227    UnknownError(i32),
1228}
1229
1230impl From<i32> for FilterGraphError {
1231    fn from(err_code: i32) -> Self {
1232        match err_code {
1233            AVERROR_OUT_OF_MEMORY => FilterGraphError::OutOfMemory,
1234            AVERROR_INVALID_ARGUMENT => FilterGraphError::InvalidArgument,
1235            AVERROR_IO_ERROR => FilterGraphError::IOError,
1236            AVERROR_PIPE_ERROR => FilterGraphError::PipeError,
1237            AVERROR_BAD_FILE_DESCRIPTOR => FilterGraphError::BadFileDescriptor,
1238            AVERROR_NOT_IMPLEMENTED => FilterGraphError::NotImplemented,
1239            AVERROR_OPERATION_NOT_PERMITTED => FilterGraphError::OperationNotPermitted,
1240            AVERROR_AGAIN => FilterGraphError::TryAgain,
1241            AVERROR_EOF => FilterGraphError::EOF,
1242            _ => FilterGraphError::UnknownError(err_code),
1243        }
1244    }
1245}
1246
1247#[derive(thiserror::Error, Debug)]
1248#[non_exhaustive]
1249pub enum OpenDecoderError {
1250    #[error("Memory allocation error during decoder initialization")]
1251    OutOfMemory,
1252
1253    #[error("Invalid argument provided during decoder initialization")]
1254    InvalidArgument,
1255
1256    #[error("Functionality not implemented or unsupported during decoder initialization")]
1257    NotImplemented,
1258
1259    #[error("Resource temporarily unavailable during decoder initialization")]
1260    TryAgain,
1261
1262    #[error("I/O error occurred during decoder initialization")]
1263    IOError,
1264
1265    #[error("An unknown error occurred during decoder initialization: {0}")]
1266    UnknownError(i32),
1267}
1268
1269impl From<i32> for OpenDecoderError {
1270    fn from(err_code: i32) -> Self {
1271        match err_code {
1272            AVERROR_OUT_OF_MEMORY => OpenDecoderError::OutOfMemory,
1273            AVERROR_INVALID_ARGUMENT => OpenDecoderError::InvalidArgument,
1274            AVERROR_NOT_IMPLEMENTED => OpenDecoderError::NotImplemented,
1275            AVERROR_AGAIN => OpenDecoderError::TryAgain,
1276            AVERROR_IO_ERROR => OpenDecoderError::IOError,
1277            _ => OpenDecoderError::UnknownError(err_code),
1278        }
1279    }
1280}
1281
1282#[derive(thiserror::Error, Debug)]
1283#[non_exhaustive]
1284pub enum DecodingError {
1285    #[error("Memory allocation error")]
1286    OutOfMemory,
1287
1288    #[error("Invalid argument provided")]
1289    InvalidArgument,
1290
1291    #[error("I/O error occurred during decoding")]
1292    IOError,
1293
1294    #[error("Timeout occurred during decoding")]
1295    Timeout,
1296
1297    #[error("Broken pipe encountered during decoding")]
1298    PipeError,
1299
1300    #[error("Bad file descriptor encountered during decoding")]
1301    BadFileDescriptor,
1302
1303    #[error("Unsupported functionality or format encountered")]
1304    NotImplemented,
1305
1306    #[error("Operation not permitted")]
1307    OperationNotPermitted,
1308
1309    #[error("Resource temporarily unavailable")]
1310    TryAgain,
1311
1312    #[error("An unknown decoding error occurred. ret:{0}")]
1313    UnknownError(i32),
1314}
1315
1316impl From<i32> for DecodingError {
1317    fn from(err_code: i32) -> Self {
1318        match err_code {
1319            AVERROR_OUT_OF_MEMORY => DecodingError::OutOfMemory,
1320            AVERROR_INVALID_ARGUMENT => DecodingError::InvalidArgument,
1321            AVERROR_IO_ERROR => DecodingError::IOError,
1322            AVERROR_TIMEOUT => DecodingError::Timeout,
1323            AVERROR_PIPE_ERROR => DecodingError::PipeError,
1324            AVERROR_BAD_FILE_DESCRIPTOR => DecodingError::BadFileDescriptor,
1325            AVERROR_NOT_IMPLEMENTED => DecodingError::NotImplemented,
1326            AVERROR_OPERATION_NOT_PERMITTED => DecodingError::OperationNotPermitted,
1327            AVERROR_AGAIN => DecodingError::TryAgain,
1328            _ => DecodingError::UnknownError(err_code),
1329        }
1330    }
1331}
1332
1333#[derive(thiserror::Error, Debug)]
1334#[non_exhaustive]
1335pub enum DecoderError {
1336    #[error("decoder '{0}' not found")]
1337    NotFound(String),
1338}
1339
1340#[derive(thiserror::Error, Debug)]
1341#[non_exhaustive]
1342pub enum DemuxingError {
1343    #[error("Memory allocation error")]
1344    OutOfMemory,
1345
1346    #[error("Invalid argument provided")]
1347    InvalidArgument,
1348
1349    #[error("I/O error occurred during demuxing")]
1350    IOError,
1351
1352    #[error("End of file reached during demuxing")]
1353    EndOfFile,
1354
1355    #[error("Resource temporarily unavailable")]
1356    TryAgain,
1357
1358    #[error("Functionality not implemented or unsupported")]
1359    NotImplemented,
1360
1361    #[error("Operation not permitted")]
1362    OperationNotPermitted,
1363
1364    #[error("Bad file descriptor encountered")]
1365    BadFileDescriptor,
1366
1367    #[error("Invalid data found when processing input")]
1368    InvalidData,
1369
1370    #[error("{}. ret:{0}", crate::util::ffmpeg_utils::av_err2str(*.0))]
1371    UnknownError(i32),
1372}
1373
1374impl From<i32> for DemuxingError {
1375    fn from(err_code: i32) -> Self {
1376        match err_code {
1377            AVERROR_OUT_OF_MEMORY => DemuxingError::OutOfMemory,
1378            AVERROR_INVALID_ARGUMENT => DemuxingError::InvalidArgument,
1379            AVERROR_IO_ERROR => DemuxingError::IOError,
1380            AVERROR_EOF => DemuxingError::EndOfFile,
1381            AVERROR_AGAIN => DemuxingError::TryAgain,
1382            AVERROR_NOT_IMPLEMENTED => DemuxingError::NotImplemented,
1383            AVERROR_OPERATION_NOT_PERMITTED => DemuxingError::OperationNotPermitted,
1384            AVERROR_BAD_FILE_DESCRIPTOR => DemuxingError::BadFileDescriptor,
1385            AVERROR_INVALIDDATA => DemuxingError::InvalidData,
1386            _ => DemuxingError::UnknownError(err_code),
1387        }
1388    }
1389}
1390
1391/// Errors that can occur during packet scanning operations.
1392#[derive(thiserror::Error, Debug)]
1393#[non_exhaustive]
1394pub enum PacketScannerError {
1395    /// Failed to seek to the requested timestamp.
1396    #[error("while seeking: {0}")]
1397    SeekError(DemuxingError),
1398
1399    /// Failed to read the next packet from the demuxer.
1400    #[error("while reading packet: {0}")]
1401    ReadError(DemuxingError),
1402}
1403
1404#[cfg(test)]
1405mod tests {
1406    // Regression: FilterGraphParseError declares PermissionDenied and NotSocket,
1407    // but its From<i32> once omitted them, so an EACCES/ENOTSOCK filtergraph
1408    // error degraded to UnknownError and the two declared variants were
1409    // unreachable. Map the codes to the variants the enum already exposes.
1410    #[test]
1411    fn filter_graph_parse_error_maps_permission_and_socket_codes() {
1412        use super::{FilterGraphParseError, AVERROR_NOT_SOCKET, AVERROR_PERMISSION_DENIED};
1413        assert!(matches!(
1414            FilterGraphParseError::from(AVERROR_PERMISSION_DENIED),
1415            FilterGraphParseError::PermissionDenied
1416        ));
1417        assert!(matches!(
1418            FilterGraphParseError::from(AVERROR_NOT_SOCKET),
1419            FilterGraphParseError::NotSocket
1420        ));
1421    }
1422
1423    // make_frame_writable's failure is typed like every other AVERROR-coded
1424    // failure in this file: common codes map to named variants, the rest keep
1425    // the raw code. Pin the mapping and the user-facing Display string.
1426    #[test]
1427    fn frame_writable_error_maps_codes_and_pins_display() {
1428        use super::{Error, FrameWritableError, AVERROR_INVALID_ARGUMENT, AVERROR_OUT_OF_MEMORY};
1429        assert!(matches!(
1430            FrameWritableError::from(AVERROR_OUT_OF_MEMORY),
1431            FrameWritableError::OutOfMemory
1432        ));
1433        assert!(matches!(
1434            FrameWritableError::from(AVERROR_INVALID_ARGUMENT),
1435            FrameWritableError::InvalidArgument
1436        ));
1437        assert!(matches!(
1438            FrameWritableError::from(-99),
1439            FrameWritableError::UnknownError(-99)
1440        ));
1441        let err = Error::from(FrameWritableError::from(AVERROR_OUT_OF_MEMORY));
1442        assert_eq!(
1443            err.to_string(),
1444            "Frame writable error: Memory allocation error while copying frame data"
1445        );
1446    }
1447
1448    // The deprecated OpenGL filter's constructor failures are typed like the
1449    // wgpu successor's: they carry OpenGLFilterError and convert into
1450    // Error::OpenGLFilter. Pin the user-facing Display strings.
1451    #[cfg(feature = "opengl")]
1452    #[test]
1453    fn opengl_filter_error_pins_display() {
1454        use super::{Error, OpenGLFilterError};
1455        let err = Error::from(OpenGLFilterError::InvalidOption(
1456            "fragment shader must declare 'in vec2 TexCoord;'".to_string(),
1457        ));
1458        assert_eq!(
1459            err.to_string(),
1460            "OpenGL filter error: invalid OpenGL filter option: \
1461             fragment shader must declare 'in vec2 TexCoord;'"
1462        );
1463        let err = Error::from(OpenGLFilterError::ContextCreation(
1464            "Failed to create Surfman connection".to_string(),
1465        ));
1466        assert_eq!(
1467            err.to_string(),
1468            "OpenGL filter error: OpenGL context creation failed: \
1469             Failed to create Surfman connection"
1470        );
1471    }
1472}