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